Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる〜

イントロダクション

ここ数日の戦いを経て、ようやく先に進めます。※下は戦闘履歴です。

  1. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜「惨敗」
  2. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3〜「負け越し」
  3. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜「黒星」
  4. Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5〜「引分け」
  5. Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜「白星」

家計簿作成を再開しようと思いますが、その前に戦果の方を見ていただきたく思います。

<現在>

家計簿のためのカレンダー部分が完成しました。

現状では、まだまだわかりづらいのは否めません。。。

しかし、初めに比べれば。。。

<作成当初>

家計簿作成再始動

今回はよりカレンダーらしくするために、カレンダークラスを使用してカレンダーの様にCubeを配置する様にプログラムを変更します。

まずはカレンダー作成

Javaの基本的なクラス(java.util.Calendar)を使用します。作成するクラスは「CalendarPosit」クラスです。

<要件の整理:どんな機能を持たせるか考える>

Cubeを使ってカレンダーを作成しますので。以下の要件を満たす必要があります。→プログラム設計になります。

  1. 対象月の月初の曜日を取得する
  2. 今月の最大日数を取得する
  3. Calendarクラスから取得した曜日が何曜日か判定する
  4. 日曜始まりをデフォルトとして、月曜始まりの場合でも曜日の値(Calendar.DAY_OF_WEEK)を返却する(配列の位置)

実行結果:木のCubeも移動しています

<月曜始まり>の実行結果(isStartMon=true)

<日曜始まり>の実行結果(isStartMon=false)


実装内容:ソースはこちら(Gitでみる)

isStartMonのハンドルを行なっているクラス(DummyGame#init())のソース(2018/11/10現在)

    //// 参照しているフィールド変数(抜粋) ////
    /**
     *  曜日の表示順
     *  true: 月曜始まり false: 日曜始まり
     */
    private boolean isStartMon;
    private static final String[] WEEK_TEXTURE_SUN = new String[]{"Sun", "Mon", "Tue", "Wed", "Thi", "Fri", "Sat"};

    @Override
    public void init(Window window) throws Exception {
        renderer.init(window);
        isStartMon = true;// 月曜始まり、日曜始まりは「false」
        CalendarPosit pos = new CalendarPosit();
        // 月初の曜日
        int firstDayOfWeek = pos.getStartPoint(isStartMon);
        // 今月の最大日数
        int maxDayOfMonth = pos.getMaxDayOfMonth();
        // Cubeの高さ
        ArrayList<float[]> floats = new ArrayList<>();
        floats.add(new float[] {0.0001f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.15f, 0.19f, 0.23f, 0.2f, 0.08f, 0.13f, 0.12f});
        floats.add(new float[] {0.1f, 0.2f, 0.4f, 0.001f, 0.2f, 0.05f, 0.15f});
        floats.add(new float[] {0.11f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.12f, 0.13f, 0.14f, 0.015f, 0.16f, 0.17f, 0.18f});
        floats.add(new float[] {0.12f, 0.13f, 0.14f, 0.015f, 0.16f, 0.17f, 0.18f});

        // Cubeの底面のサイズ(正方形)
        final float cubeSize = 0.1f;
        // x軸の初期値
        final float xInit = -0.5f;
        // y軸の初期値
        final float yInit = -0.8f;
        // z軸の初期値
        final float zInit = -2;
        // x軸の増減幅
        final float xWidth = 0.185f;
        // y軸の増減幅
        final float yWidth = 0.033f;
        // z軸の増減幅
        final float zWidth = 0.1f;
        ArrayList arr = new ArrayList();
        
        // マスのカウント
        int box = 0;
        // 日数
        int day = 0;
        // 初めの1回目だけ曜日のテキストプレートを作成する
        boolean isCreateTexture = false;
        // 1ヶ月分(5週間分のマスを作る)
        for(int j = 1; j <= 6; j++) {
        	// 開始点より一列ずらす
        	// X軸の開始点
        	float xStart = xInit - (0.1f * j);
        	// Y軸の開始点
        	float yStart = yInit + (0.06f * j) ;
        	// Z軸の開始点
        	float zStart = zInit - (0.16f * j);
            // 1週間分
        	float[] weekArr = floats.get(j-1);
        	
        	// 1回目だけテクスチャを作成する
        	isCreateTexture = j == 1 ? true: false;
            for(int i = 1; i <= 7; i++) {
            	float xAdd = xWidth * i;
            	float yAdd = yWidth * i;
            	float zAdd = zWidth * i;
        		// 日付をインクリメント
        		box++;
            	if (isCreateTexture) {
            		arr.add(putOnTexturePlate(xStart + xAdd + 0.01f , yStart + yAdd - 0.03f, zStart - zAdd + 0.03f, i-1));
            	}
           	    if (box < firstDayOfWeek) { System.out.println("開始: " + firstDayOfWeek); continue; } day++; if (day >= maxDayOfMonth) {
            		break;
            	}
            	float val = weekArr[i - 1];
        		arr.add(createCube(val,
        				cubeSize, xStart + xAdd, yStart + yAdd, zStart - zAdd));
            }
        }
        // 配列の要素数を指定する
        GameItem[] items = new GameItem[arr.size()];
        // 配列の取り出し
        gameItems = arr.toArray(items);
        
        // DEBUG
        //debug();
    }

ここでのポイントは月曜始まりと、日曜始まりを変更できる様にしたところです。

	/**
	 * 対象月の月初の曜日を取得する
	 * @return Calendar.MONDAY〜SUNDAY
	 */
	public int getStartPoint(boolean isStartMon) {
		int day_week = 0;
		cal.set(Calendar.DATE, 1);
		day_week = cal.get(Calendar.DAY_OF_WEEK);
		if (isStartMon) {
			day_week = day_week == 1 ? 7 : day_week - 1;
		}
		return day_week;
	}

赤字の部分が工夫の部分です、条件文を使って「if文」を使わずに1行で済むし可読性も良い。これを使わないと以下の様なコードになります。(if文を抜粋)

if (isStartMon) {
    if (day_week == 1) {
        day_week = 7;
    } else {
        day_week = day_week -1;
    }
 }

微妙なコードです、なぜかというとif文はネストすると確認しなくてはいけないことが増えるからです。→単純にコード量が多いのもあります。

一応「条件演算子」について、サンプルとして四捨五入の場合です。

/*
 * 返却する値 = 条件式 ? TRUE時の値(式) : FALSE時の値(式)
 * System.in() // 標準入力
 */
int i = System.in;
// 4以下は「0」、5以上は「10」を「response」に代入
int response = i < 5 ? 0 : 10;

そして作成したのが以下のクラス(Gitでみる)です。(ちょっとコード量が多いですが。。。)

package zenryokuservice.gui.lwjgl.kakeibo.util;
import java.util.Calendar;

/**
 * 家計簿の各数値を表示するための領域(日)を作成する
 * @author takunoji
 *
 * 2018/11/10
 */
public class CalendarPosit {
	/** 日本語の曜日 */
	private static final String[] ja_week = new String[] {"日", "月", "火", "水", "木", "金", "土"};
	/** 英語の曜日 */
	private static final String[] en_week = new String[] {"Sun", "Mon", "Tue", "Wed", "thu", "Fri", "Sat"};

	private Calendar cal = null;
	
	public CalendarPosit() {
		cal = Calendar.getInstance();
	}
	
	/**
	 * 今月の最大日数を返す
	 * @return 最大日数
	 */
	public int getMaxDayOfMonth() {
		return cal.getMaximum(Calendar.DATE);
	}

	/**
	 * 今月の最大日数を返す
	 * @move 今月の月からずらす月数
	 * move=1  -> 1ヶ月たす
	 * mobe=-1 -> 1ヶ月引く
	 * 
	 * 
	 * 現在:10月
	 * move=  1  -> 11月の最大日数
	 * move= -1 -> 9月の最大日数
	 * @return 最大日数
	 */
	public int getMaxDayOfMonth(int move) {
		Calendar tmpCal = cal;
		tmpCal.add(Calendar.MONTH, move);
		return tmpCal.getMaximum(Calendar.DATE);
	}

	/**
	 * 対象月の月初の曜日を取得する
	 * @return Calendar.MONDAY〜SUNDAY
	 */
	public int getStartPoint(boolean isStartMon) {
		int day_week = 0;
		cal.set(Calendar.DATE, 1);
		day_week = cal.get(Calendar.DAY_OF_WEEK);
		if (isStartMon) {
			day_week = day_week == 1 ? 7 : day_week - 1;
		}
		return day_week;
	}	

	/**
	 * 現在の年の指定した月初の曜日を返却する
	 * @param month 1-12を指定する
	 * @return 対象月の、月初曜日
	 */
	public int getStartPoint(int month, boolean isStartMon) {
		int day_week = 0;
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.MONTH, month - 1);
		cal.set(Calendar.DATE, 1);
		
		day_week = cal.get(Calendar.DAY_OF_WEEK);
		return day_week;
	}

	/**
	 * カレンダークラスを返却する。
	 * @return
	 */
	public Calendar getCalendar() {
		return this.cal;
	}
	/**
	 * 開始日付に対応する曜日の値を返す。
	 * @return 曜日の文字列
	 */
	public String getDayOfWeek(boolean isStartMon) {
		return ja_week[getStartPoint(isStartMon)];
	}
	/**
	 * 引数の値が何曜日か判定して返却する
	 * @param value Calendarクラスより取得した、DAY_OF_WEEK
	 * @return 対象の曜日の値
	 */
	public int chkWeek(int value, boolean isStartMon) {
		int day_of_week = 0;
		
		switch(value) {
		case Calendar.MONDAY:
			day_of_week = Calendar.MONDAY;
			break;
		case Calendar.TUESDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.WEDNESDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.THURSDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.FRIDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.SATURDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		case Calendar.SUNDAY:
			day_of_week = Calendar.TUESDAY;
			break;
		default:
			// TODO-[例外処理の実装]
			System.out.println("想定外の曜日です[day_of_week]: " + day_of_week);
			System.exit(-1);
		}

		// 日曜始まり=true, 月曜始まり=false
		if (isStartMon == false) {
			day_of_week = day_of_week < 7 ? day_of_week + 1 : 1;
		}
		return day_of_week;
	}
}

いろんなパターンに対応できる様に先にコードだけ作りました。

関連ページ一覧

<家計簿アプリ作成>

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv3〜3Dグラフ用Cube作り〜
  4. Eclipse アプリ作成 Lv4〜3Dグラフ用Cubeに高さを与える〜
  5. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜
  6. Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる

Java Basic

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Java 3DGame LWJGL Retry Lv7 遊んでみる3〜全部テクスチャにする〜

イントロダクション

ここのところ負け越しの1引分けでしたが、ついにやっつけました。

<戦闘履歴>

  1. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜「惨敗」
  2. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3〜「負け越し」
  3. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜「黒星」
  4. Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5〜「引分け」
  5. Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜「白星」

最終的に以下の様なコードができました。主にいじったのは「DummyGame」クラスです。ポイントは以下になります。

  1. メッシュを作成するときはテクスチャを使用する様に修正
  2. メッシュを作成するときの各頂点(Vertex)の定義を見直す
  3. 同様に頂点を結びつける順番(indices)を見直す

<DummyGame#init()>

    @Override
    public void init(Window window) throws Exception {
        renderer.init(window);
        // Cubeの高さ
        ArrayList<float[]> floats = new ArrayList<>();
        floats.add(new float[] {0.0001f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.15f, 0.19f, 0.23f, 0.2f, 0.08f, 0.13f, 0.12f});
        floats.add(new float[] {0.1f, 0.2f, 0.4f, 0.001f, 0.2f, 0.05f, 0.15f});
        floats.add(new float[] {0.11f, 0.12f, 0.3f, 0.001f, 0.1f, 0.25f, 0.1f});
        floats.add(new float[] {0.12f, 0.13f, 0.14f, 0.015f, 0.16f, 0.17f, 0.18f});
        // Cubeの底面のサイズ(正方形)
        final float cubeSize = 0.1f;
        // x軸の初期値
        final float xInit = -0.5f;
        // y軸の初期値
        final float yInit = -0.8f;
        // z軸の初期値
        final float zInit = -2;
        // x軸の増減幅
        final float xWidth = 0.185f;
        // y軸の増減幅
        final float yWidth = 0.033f;
        // z軸の増減幅
        final float zWidth = 0.1f;
        ArrayList arr = new ArrayList();
        
        // 初めの1回目だけ曜日のテキストプレートを作成する
        boolean isCreateTexture = false;
        // 1ヶ月分(5週間分のマスを作る)
        for(int j = 1; j <= 5; j++) {
        	// 開始点より一列文ずらす
        	// X軸の開始点
        	float xStart = xInit - (0.1f * j);
        	// Y軸の開始点
        	float yStart = yInit + (0.06f * j) ;
        	// Z軸の開始点
        	float zStart = zInit - (0.16f * j);
            // 1週間分
        	float[] weekArr = floats.get(j-1);
        	
        	// 1回目だけテクスチャを作成する
        	isCreateTexture = j == 1 ? true: false;
            for(int i = 1; i <= 7; i++) {
            	float xAdd = xWidth * i;
            	float yAdd = yWidth * i;
            	float zAdd = zWidth * i;
            	if (isCreateTexture) {
            		arr.add(putOnTexturePlate(xStart + xAdd + 0.01f , yStart + yAdd - 0.03f, zStart - zAdd + 0.03f, i-1));
            	}
            	float val = weekArr[i - 1];
        		arr.add(createCube(val,
        				cubeSize, xStart + xAdd, yStart + yAdd, zStart - zAdd));
            }
        }
        // 配列の要素数を指定する
        System.out.println("GmaeItems: " + arr.size());
        GameItem[] items = new GameItem[arr.size()];
        // 配列の取り出し
        gameItems = arr.toArray(items);
        
        // DEBUG
        //debug();
    }

<DummyGame#putOnTexturePlate()>

    /**
     * Texture作成メソッド
     * @param xPos
     * @param yPos
     * @param zPos
     * @param num 曜日の番号0:月〜7:日
     * @return GameItem
     */
    private GameItem putOnTexturePlate(float xPos, float yPos, float zPos, int num) {
    	float size = 0.08f;
    	float[] positions = new float[] {
    			-1 * size, size, size, // V0
    			-1 * size, -1 * size, size, //V1 
    			size, -1 * size, size, // V2
    			size, size, size, // V3
    			
    			-1 * size, size, -1 * size, // V4
    			size, -1 * size, size, //V5 
    			-1 * size, -1 * size, -1 * size, // V6
    			size, -1 * size, -1 * size, // V7
    			};
    	float[] textCoords = new float[]{
                0.0f, 0.0f, 
                0.0f, 1.0f,
                1.0f, 1.0f,
                1.0f, 0.0f,
            };
    	int[] indices = new int[] {
    			0, 1, 3, 3, 1, 2
			};
    	Texture texture = null;

    	try {
        	texture = new Texture("/textures/" + weekTexture[num] +  ".png");
    	} catch(Exception e) {
    		e.printStackTrace();
    	}
        TexturedMesh mesh = new TexturedMesh(positions, textCoords, indices, texture);
        GameItem gameItem = new GameItem(mesh);
        gameItem.setPosition(xPos, yPos, zPos);
        gameItem.setRotation(20, 30, 0);
        return gameItem;
    }

<DummyGame#createCube()>

    private GameItem createCube(float height, float cubeSize, float posX, float posY, float posZ) {
        // Create the Mesh
        float[] positions = new float[]{
            // VO
            -1 * cubeSize,  height,  cubeSize,
            // V1
            -1 * cubeSize, -1 * cubeSize,  cubeSize,
            // V2
            cubeSize, -1 * cubeSize,  cubeSize,
            // V3
            cubeSize,  height,  cubeSize,
            // V4
            -1 * cubeSize,  height, -1 * cubeSize,
            // V5
            cubeSize,  height, -1 * cubeSize,
            // V6
            -1 * cubeSize, -1 * cubeSize, -1 * cubeSize,
            // V7
            cubeSize, -1 * cubeSize, -1 * cubeSize,
        };
        float[] colours = new float[]{
            0.5f, 0.0f, 0.0f,
            0.0f, 0.5f, 0.0f,
            0.0f, 0.0f, 0.5f,
            0.0f, 0.5f, 0.5f,
            0.5f, 0.0f, 0.0f,
            0.0f, 0.5f, 0.0f,
            0.0f, 0.0f, 0.5f,
            0.0f, 0.5f, 0.5f,
        };
        int[] indices = new int[]{
            // Front face
            0, 1, 3, 3, 1, 2,
            // Top Face
            4, 0, 3, 5, 4, 3,
            // Right face
            3, 2, 7, 5, 3, 7,
            // Left face
            6, 1, 0, 6, 0, 4,
            // Bottom face
            2, 1, 6, 2, 6, 7,
            // Back face
            7, 6, 4, 7, 4, 5,
        };
    	Texture texture = null;
    	try {
        	texture = new Texture("/textures/wood1.png");
    	} catch(Exception e) {
    		e.printStackTrace();
    	}
        TexturedMesh mesh = new TexturedMesh(positions, colours, indices, texture);
        GameItem gameItem = new GameItem(mesh);
        gameItem.setPosition(posX, posY, posZ);
        gameItem.setRotation(20, 30, 0);
        return gameItem;
    }

上記のポイントを抑えサイドコードを直したところうまくいきました。


関連ページ一覧

<Java Basic>

  1. Java Basic for文 〜Step1_3_1〜
  2. Java Basic Level8 〜How to use for statement〜
  3. Java Basic Level 9〜Training of for statement〜
  4. Java 3DGame LWJGL GitBook chapter7-1〜Cube作成〜「動画あり」

Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜

イントロダクション

ここ数日連敗が続いていましたが、昨日引き分けにもつれ込みました。

  1. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜「惨敗」
  2. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3〜「負け越し」
  3. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜「黒星」
  4. Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5〜「引分け」
  5. Java 3DGame LWJGL Retry Lv6 遊んでみる2〜動かして理解する6〜「白星」

今日は勝つ予定です。。。

前回(「引分け」)はLWJGL GitBook Chapter7テクスチャを張り替えたり表示を変えたりして遊びました。

そして「遊んでみる方が学習効率も高い」ということを確信しました。というわけで、このまま次のチャプタをやってみようと思います。

Chapter8〜Camera〜

チャプタとしては「カメラ」と書いてありますが、ポイントとしてはテクスチャを貼り付けたオブジェクトを複数生成する場合の処理方法の理解です。

<Chapter8の実行結果>

*******************************************************************

*******************************************************************

 テクスチャありとなしのキューブを作成する

色々やったけど、こんな感じで中途半端にテクスチャが貼れています。

結論

シェーダを2種類用意していないな・・・つまり、テクスチャありの状態でCubeを作成しているので、全てのCubeにテクスチャが貼り付けられている状態です。

『本当は、「テクスチャあり」が手前の7個でそのほかは「テクスチャなし」にしようと思っております。』

<2018/11/09追記>
→この考え方が負因でした、片方はシェーダを使用する、もう片方はシェーダを使用しない。こうなるとシェーダプログラムを2つ用意してそれぞれのメッシュにもそれぞれのシェーダプログラムを適用する必要があるので、まぁ面倒だし実用的でないと判断しました。つまり全部テクスチャを使用する様に修正しました。

 

<テクスチャなし>色を指定しています。

今回は辛勝といったところでしょうか。。。

でわでわ。。。

関連ページ一覧

<今回のやっていること>

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv3〜3Dグラフ用Cube作り〜
  4. Eclipse アプリ作成 Lv4〜3Dグラフ用Cubeに高さを与える〜
  5. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜
  6. Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる〜

<Java Basic>

  1. Java Basic for文 〜Step1_3_1〜
  2. Java Basic Level8 〜How to use for statement〜
  3. Java Basic Level 9〜Training of for statement〜
  4. Java 3DGame LWJGL GitBook chapter7-1〜Cube作成〜「動画あり」

Java 3DGame LWJGL Retry Lv5 遊んでみる〜動かして理解する5〜

イントロダクション

この数日、連敗が続いている状態です。

  1. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜
  2. Java 3DGame LWJGL Retry Lv3 Texture〜動かして理解する3〜「負け越し」
  3. Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜「黒星」

こんな感じで3連敗です。。。

元々は、OpenGLで作成したCubeにテクスチャを貼り付けようとしていました。

ここで、初心に戻り動くものを弄って遊んでみようとなりました。突破口になるかもしれません。

サンプルコード:「Chapter7-2のマイクラの様なブロックを回転させる

<起動結果>


遊んでみる

First Attack: テクスチャを弄ってみる

<DummyGame#init()>元々のコード

        float[] textCoords = new float[]{
            0.0f, 0.0f,// TRY2
            0.0f, 0.5f,// TRY3
            0.5f, 0.5f,
            0.5f, 0.0f,
            
            0.0f, 0.0f,
            0.5f, 0.0f,
            0.0f, 0.5f,
            0.5f, 0.5f,
            
            // For text coords in top face
            0.0f, 0.5f,// TRY1
            0.5f, 0.5f,
            0.0f, 1.0f,
            0.5f, 1.0f,

            // For text coords in right face
            0.0f, 0.0f,
            0.0f, 0.5f,

            // For text coords in left face
            0.5f, 0.0f,
            0.5f, 0.5f,

            // For text coords in bottom face
            0.5f, 0.0f,
            1.0f, 0.0f,
            0.5f, 0.5f,
            1.0f, 0.5f,
        };

テクスチャの座標を指定している部分です。こいつを弄って遊んでみようとなりました。

TRY1-①: 「0.0f, 0.5f」→「0.0f, 0.2f」に変更。

回転しているので座標がわかりづらい。。。

ちょっとハゲができました。

TRY1-②: 「0.0f, 0.5f」→「0.5f, 0.5f」に変更。

頂点の一部がかき消された様になっています。下のコードをみてみると頂点が被っていて、実際は3点のみになっています。

            // For text coords in top face
            0.5f, 0.5f,// 同じ
            0.5f, 0.5f,// 同じ
            0.0f, 1.0f,
            0.5f, 1.0f,

なるほど、テクスチャの頂点を指定している様です。

ここからが本題

元々のコード、緑色の部分に関しては未だになんなのかわかっていませんのでこいつを弄ってみます。

「TRY2」の部分を「0.0f, 0.0f」→「0.5f, 0.5f」に変更

おそらく、V0の座標を変更したので下のイメージの半分より下の部分がテクスチャとして認識されていると思われます。

Texture coordinates front face

テクスチャのイメージファイルを変更してみます。

<実行結果>

うーんパズルの様な感じになりました。ちょいと調節すると。。。

一面のみうまく表示されています。この状態のコードは以下です。

        float[] textCoords = new float[]{
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
            
            0.0f, 0.0f,
            0.5f, 0.0f,
            0.0f, 0.5f,
            0.5f, 0.5f,
            
            // For text coords in top face
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,

            // For text coords in right face
            0.0f, 0.0f,
            0.0f, 0.5f,

            // For text coords in left face
            0.5f, 0.0f,
            0.5f, 0.5f,

            // For text coords in bottom face
            0.5f, 0.0f,
            1.0f, 0.0f,
            0.5f, 0.5f,
            1.0f, 0.5f,
        };

次のコードです。

        float[] textCoords = new float[]{
        	// Imageの取得する座標
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
            // この部分がわからない
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
            
            // For text coords in top face
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,

            // For text coords in right face
            0.0f, 0.0f,
            0.0f, 1.0f,

            // For text coords in left face
            1.0f, 0.0f,
            1.0f, 1.0f,

            // For text coords in bottom face
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
        };

一面以外はうまくいっている様です。(多分底面がうまくいっていない)

ですが、なんとなく処理の流れ(座標の取り方)がわかってきました(そんな気がする。。。)、まだ仮説状態です。

まずは頂点定義の配列を眺めます

 
        float[] positions = new float[] {
            /* ①メッシュの使用する頂点を定義 */
            // V0
            -0.5f, 0.5f, 0.5f,
            // V1
            -0.5f, -0.5f, 0.5f,
            // V2
            0.5f, -0.5f, 0.5f,
            // V3
            0.5f, 0.5f, 0.5f,
            // V4
            -0.5f, 0.5f, -0.5f,
            // V5
            0.5f, 0.5f, -0.5f,
            // V6
            -0.5f, -0.5f, -0.5f,
            // V7
            0.5f, -0.5f, -0.5f,
            /* ②テクスチャで貼り付ける面を指定する */
            // For text coords in top face
            // V8: V4 repeated
            -0.5f, 0.5f, -0.5f,
            // V9: V5 repeated
            0.5f, 0.5f, -0.5f,
            // V10: V0 repeated
            -0.5f, 0.5f, 0.5f,
            // V11: V3 repeated
            0.5f, 0.5f, 0.5f,
            /* ③横のたて(1本分)を定義する */
            // For text coords in right face
            // V12: V3 repeated
            0.5f, 0.5f, 0.5f,
            // V13: V2 repeated
            0.5f, -0.5f, 0.5f,
            /* ④横のたて(1本分)を定義する */
            // For text coords in left face
            // V14: V0 repeated
            -0.5f, 0.5f, 0.5f,
            // V15: V1 repeated
            -0.5f, -0.5f, 0.5f,
            /* ⑤面を定義する */
            // For text coords in bottom face
            // V16: V6 repeated
            -0.5f, -0.5f, -0.5f,
            // V17: V7 repeated
            0.5f, -0.5f, -0.5f,
            // V18: V1 repeated
            -0.5f, -0.5f, 0.5f,
            // V19: V2 repeated
            0.5f, -0.5f, 0.5f,
        };

 

Cube coords

頂点を定義した後に、面を定義する様に座標点を設定していきます。

Texture coordinates front face

中途半端だけど。。。

コードはこちらにアップロードしてあります。2018/11/08


 

関連ページ一覧

<今回のやっていること>

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv3〜3Dグラフ用Cube作り〜
  4. Eclipse アプリ作成 Lv4〜3Dグラフ用Cubeに高さを与える〜
  5. Eclipse アプリ作成 Lv5〜惨敗:CubeにTextureを貼る〜
  6. Eclipse アプリ作成 Lv7〜Cubeを日付順に並べる〜

<Java Basic>

  1. Java Basic for文 〜Step1_3_1〜
  2. Java Basic Level8 〜How to use for statement〜
  3. Java Basic Level 9〜Training of for statement〜
  4. Java 3DGame LWJGL GitBook chapter7-1〜Cube作成〜「動画あり」

Java 3DGame LWJGL Retry Lv4 デバック〜動かして理解する4〜

イントロダクション

前々回、惨敗を喫してしまったのでリベンジきめます。前回は3次元描画ではなく2次元描画にレベルを落として動かしてみました。PNGファイルを読んでいるところまでは、確認できているのですが。。。そのあと描画するときに指定する座標に関してはまるで判らない状態なのでそこを解決しようと思います。

デバック方法

  1. まずはコードを見直す
  2. 想定通りの結果を得るための処理として正しいか、否か?
  3. 原因を探る
  4. 解決策を打つ

参照するドキュメント:LWJGL GitBookChapterのChapter5

カスタムするコード:LWJGL GitBook Chapter5

座標に関して(ソースを見直す)

ソースコード上では以下のようになっています。

<DummyGame#createFloor()>

    /**
     * 床のような土台のメッシュ(3Dモデル)を作成します
     * @return GameItem 床型のメッシュ
     */
    private GameItem createFloor() {
        float[] positions = new float[]{
            	// V0
                0.0f, 0.25f, 0.0f,
                // V1
                -0.0f, -0.0f, 0.0f,
                // V2
                0.5f, -0.0f, 0.0f,
                // V3
                0.5f, 0.25f, 0.0f,};
            int[] indices = new int[]{
                0, 1, 3, 3, 1, 2,};
            // 追記 2018/10/27
            float[] textCoord = new float[] {
            	    0.5f, 0.0f, 0.0f,   //V0の色(赤(R))
            	    0.0f, 0.5f, 0.0f,   // V1の色(緑(G))
            	    0.0f, 0.0f, 0.5f,   // V2の色(青(B))
            	    0.8f, 0.8f, 0.0f,}; // V3の色(黄)
            return new GameItem(new Mesh(positions, indices, textCoord));
    }

上記の赤字部分が理解できていない部分です。ここの部分は、元々色を指定していた部分です。変数名が「colour」から「textCoords」に変わっただけです。

現状の理解は、上のコードにあるように各頂点の色を指定している認識です。実行結果もそのようになりました。

これがTextureに変わることで下のようになってしまいました。

ちょっとホラーな感じがします(自分だけ?)

調査開始(正しい処理か?否か?)

まずは、未着手部分→シェーダについて調べます。現状のシェーダは以下のようになっています。

<Renderer#init()>

    public void init() throws Exception {
        shaderProgram = new ShaderProgram();
        shaderProgram.createVertexShader(Utils.loadResource("/vertex5.vs"));
        shaderProgram.createFragmentShader(Utils.loadResource("/fragment5.fs"));
        shaderProgram.link();
    }

上のコードから「/vertex5.vs」「/fragment5.fs」を参照していることを確認。

そして、フォルダー構成より以下の階層を確認(ビルドパスには「resources」を追加してあります。

<vertex5.vs>

#version 330

// 追記 2018/10/27
in  vec3 exColour;
out vec4 fragColor;

void main()
{
	fragColor = vec4(exColour, 1.0);
}

<fragment5.fs>

#version 330

layout (location =0) in vec3 position;

// 追記 2018/10/27
layout(location=1) in vec3 inColour;
out vec3 exColour;

void main()
{
	gl_Position = vec4(position, 1.0);
	// 追記 2018/10/27
	exColour = inColour;
}

以前追加したコードがあるようです。

これで考える材料が揃いました。

処理を追いかけます。参照するコードはイントロダクション部分にリンクがあるのでそちらをご覧ください。

  1. Main#main(): DummyGame, GameEngineをインスタンス化してGameEngine#satrt()を起動
  2. DummyGameのコンストラクタ: Rendererクラスのインスタンス化→フィールドで保持
  3. GameEngineのコンストラクタ: 自分自身をスレッド化してWindowクラスのインスタンス化、DummyGame(インターフェース)をフィールドに保持、Timerクラスをインスタンス化してフィールドに保持
  4. 「1」でGameEngine#start()が起動される、OSの判定をして結局GameEngine#run()が起動する
  5. GameEngine#init()とgameLoop()を起動する
  6. 最後にclearnup()が起動する
  7. GameEngine#init()でフィールドに保持した「Window」「Timer」「gameLogic(DummyGame)」のinit()を起動する
  8. 今回は描画部分を調査するのでDummyGame#init()(gameLogic.init())を眺める
  9. DummyGame#init()ではRenderer#init()とGameItem(Mesh)を作成している
  10. Renderer#init(): ShaderProgram をインスタンス化、shader, fragmentプログラムを読み込み最後にShapderProgram#link()を起動する
  11. 「glLinkProgram()」でShaderをインスタンス化したときにフィールドで保持したprogramIdをリンクする(OpenGLのメソッドなので深堀はしないでおく→ハマることが多い)そのほか設定する処理を走らせる。
  12. 今回の目玉四角形を作成する

そして、ドキュメントを読むと「Meshクラスを変更して、色の代わりにテクスチャ座標を含む浮動小数点数の配列を受け入れるようにします」とあるので単純に「colour」を「textCoords」に変更した状態が今のコードです。

上記の部分を修正して実行してみると。。。

まっくろくろすけでわ、あーりませんか?

試しに背景を白くしてみます。。。とその前に今までのソースは「import static  XXXX.GL?」というクラスをstaticでインポートしているのでどのメソッドがどのクラスのメソッドかわからなかったので「static」を消します。

<Rendererクラス>

package zenryokuservice.gui.lwjgl.tutoriral.gitbook.chapter5.game;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;

import org.joml.Matrix4f;

import zenryokuservice.gui.lwjgl.tutoriral.gitbook.chapter5.engine.Utils;
import zenryokuservice.gui.lwjgl.tutoriral.gitbook.chapter5.engine.Window;
import zenryokuservice.gui.lwjgl.tutoriral.gitbook.chapter5.engine.graph.GameItem;
import zenryokuservice.gui.lwjgl.tutoriral.gitbook.chapter5.engine.graph.Mesh;
import zenryokuservice.gui.lwjgl.tutoriral.gitbook.chapter5.engine.graph.ShaderProgram;
import zenryokuservice.gui.lwjgl.tutoriral.gitbook.chapter5.engine.graph.Transformation;

public class Renderer {

    private ShaderProgram shaderProgram;
    
    // 2018/11/08
    private Transformation transformation;

    public Renderer() {
    	transformation = new Transformation();
    }

    public void init() throws Exception {
        shaderProgram = new ShaderProgram();
        shaderProgram.createVertexShader(Utils.loadResource("/vertex5.vs"));
        shaderProgram.createFragmentShader(Utils.loadResource("/fragment5.fs"));
        shaderProgram.link();
        
        // 2018/11/08追記
        shaderProgram.createUnitform("projectionMatrix");
        shaderProgram.createUnitform("worldMatrix");
        shaderProgram.createUnitform("texture_sampler");
    }

    public void clear() {
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
    }

    public void render(Window window, GameItem[] gameItems) {
        clear();

        if (window.isResized()) {
            GL11.glViewport(0, 0, window.getWidth(), window.getHeight());
            window.setResized(false);
        }

        shaderProgram.bind();
        int i = -1;
        for (GameItem item : gameItems) {
// Meshクラスに処理を移動
//            // Draw the mesh
//            glBindVertexArray(item.getMesh().getVaoId());
//            glEnableVertexAttribArray(++i);
//            // 追記 2018/10/27
//            glEnableVertexAttribArray(++i);
//            glDrawElements(GL_TRIANGLES, item.getMesh().getVertexCount(), GL_UNSIGNED_INT, 0);
        	// ワールド座標を設定する
        	Matrix4f worldMatrix = transformation.getWorldMatrix(item.getPosition(), item.getRotation(), item.getScale());
        	shaderProgram.setUniform("worldMatrix", worldMatrix);
        	item.getMesh().render();
        }

        // Restore state
        GL20.glDisableVertexAttribArray(0);
        GL30.glBindVertexArray(0);

        shaderProgram.unbind();
    }

    public void cleanup() {
        if (shaderProgram != null) {
            shaderProgram.cleanup();
        }
    }
}

今度は真っ白です、つまり何も描画していないことになります。

※背景の色を変えるのはWindow#colorの値を1.0fにしてやるとできます。

もう何がわからないので、ネットで探します。

LWJGLのWiki

色々探してみたが、Textureの座標とMeshの頂点座標の関係がわからない。。。

次は、貼れたテクスチャを解剖してみるかな?

参照するのはChapter7-1です。(Cubeの動画あり)

関連ページ一覧

<今回のやっていること>

  1. Eclipse アプリ作成 Lv1〜家計簿を作る準備〜
  2. Eclipse アプリ作成 Lv2〜家計簿を作る土台作り〜
  3. Eclipse アプリ作成 Lv3〜3Dグラフ用Cube作り〜

<Java Basic>

  1. Java Basic for文 〜Step1_3_1〜
  2. Java Basic Level8 〜How to use for statement〜
  3. Java Basic Level 9〜Training of for statement〜
  4. Java 3DGame LWJGL GitBook chapter7-1〜Cube作成〜「動画あり」

<サイトマップ>