// 一本道道路の生成テスト // class内に描画処理等を移動 // 木のビルボードを追加 /* @pjs preload="tex.png"; */ // 上記はProcessing.js用の記述部分 boolean FOGENABLE = false; PImage img; // テクスチャ画像 Road road; // 道路生成クラス float t; // 時間(進行度) float cameraAng; // カメラのxz角度 PVector fogVtx[]; // fog用座標値 int lastUpdateTime; float elapsedTime; // テクスチャ座標 float[] texUV = { // road A, road B 0, 2, 127, 2, 127, 63, 0, 63, 0, 64, 127, 64, 127, 125, 0, 125, // grass A, grass B 128, 2, 255, 2, 255, 63, 128, 63, 128, 64, 255, 64, 255, 125, 128, 125, // tree A, tree B 0, 129, 127, 129, 127, 255, 0, 255, 128, 129, 255, 129, 255, 255, 128, 255, }; void setup() { size(640, 480, P3D); //size(1280, 720, P3D); img = loadImage("tex.png"); textureMode(IMAGE); // textureMode(NORMAL); //lights(); noStroke(); // コレを入れないとテクスチャが透過してくれない。何故? hint(DISABLE_DEPTH_TEST); road = new Road(); t = 0; elapsedTime = 0.0; lastUpdateTime = millis(); } void draw() { int currentTime = millis(); elapsedTime = (currentTime - lastUpdateTime); lastUpdateTime = currentTime; // 時間(進行度)を進める t += (elapsedTime * 0.001) * 8; if (t >= 1.0) { road.incIndex(); t -= 1.0; } background(0, 48, 72, 255); // 道路中央の線分を得て、どのあたりに居るかを得る PVector[] l0 = road.getLinePos(road.idx + 2); PVector[] l1 = road.getLinePos(road.idx + 12); float lx0 = getLinearValue(l0[0].x, l0[1].x, t); float ly0 = getLinearValue(l0[0].y, l0[1].y, t); float lz0 = getLinearValue(l0[0].z, l0[1].z, t); float lx1 = getLinearValue(l1[0].x, l1[1].x, t); // float ly1 = getLinearValue(l1[0].y, l1[1].y, t); float lz1 = getLinearValue(l1[0].z, l1[1].z, t); pushMatrix(); // 現在の座標系を保存 translate(width / 2, height / 2, 0); // カメラ設定 float ty = 55; float cx = lx0; float cy = ly0 - ty; float cz = lz0; float ctx = lx1; float cty = ly0 - ty; float ctz = lz1; camera(cx, cy, cz, // 視点x,y,z ctx, cty, ctz, // 中心点x,y,z 0.0, 1.0, 0.0 // 天地x,y,z ); // カメラ角度を記録 cameraAng = atan2(ctz - cz, ctx - cx); // fog用座標値を記憶 if (FOGENABLE) setfogVtx(cx, cy, cz); // 道路を描画 int idx = road.idx; for (int i = idx + 72; i >= idx; i-- ) { road.getPolyData(i).draw(); // fogを描画 if ( FOGENABLE && i > idx + 24) drawFog(); } popMatrix(); // 座標系を復元 } // fog用ポリゴンを描画するための座標値を記憶 void setfogVtx(float cx, float cy, float cz) { fogVtx = new PVector[2]; cx += 200 * cos(cameraAng); cz += 200 * sin(cameraAng); float ang = cameraAng + (PI / 2); float s = 300; float ax = s * cos(ang); float ay = s; float az = s * sin(ang); float x0 = cx + ax; float y0 = cy + ay; float z0 = cz + az; float x1 = cx - ax; float y1 = cy - ay; float z1 = cz - az; fogVtx[0] = new PVector(x0, y0, z0); fogVtx[1] = new PVector(x1, y1, z1); } // fog用ポリゴンを描画 void drawFog() { beginShape(QUADS); fill(255, 255, 255, 12); vertex(fogVtx[1].x, fogVtx[1].y, fogVtx[1].z); vertex(fogVtx[0].x, fogVtx[1].y, fogVtx[0].z); vertex(fogVtx[0].x, fogVtx[0].y, fogVtx[0].z); vertex(fogVtx[1].x, fogVtx[0].y, fogVtx[1].z); endShape(); } // 一次関数の値を取得 float getLinearValue(float v_start, float v_end, float t) { return (v_start + (v_end - v_start) * t); } // 道路ポリゴンデータ用クラス class PolyData { PVector[] p; // 0,1,2,3:道路、4,0,3,5:左脇、1,6,7,2:右脇 PVector[] tree; // 木の位置情報 int[] treeTexNum; // 木のテクスチャ種類 float[] treeScale; // 木の大きさ PVector[] linePos; // 道路の線分情報 int texNumber; // 道路のテクスチャ種類 PolyData(PVector l0, float ang0, PVector l1, float ang1, int texNum, float w) { // 道路の線分情報を記録 linePos = new PVector[2]; linePos[0] = new PVector(l0.x, l0.y, l0.z); linePos[1] = new PVector(l1.x, l1.y, l1.z); // テクスチャ種類を記録 texNumber = texNum; // 道路のポリゴン頂点情報を記録 p = new PVector[8]; float degx, w0, w1, ya, yb; w0 = w; w1 = w + (w * 8); // 道路脇の幅 ya = 500; // 道路脇(左)の高さ yb = -500; // 道路脇(右)の高さ // 道路、道路脇のポリゴン座標群を記録 degx = radians(ang1 - 90); p[0] = new PVector(w0 * cos(degx) + l1.x, l1.y, w0 * sin(degx) + l1.z); p[4] = new PVector(w1 * cos(degx) + l1.x, l1.y + ya, w1 * sin(degx) + l1.z); degx = radians(ang1 + 90); p[1] = new PVector(w0 * cos(degx) + l1.x, l1.y, w0 * sin(degx) + l1.z); p[6] = new PVector(w1 * cos(degx) + l1.x, l1.y + yb, w1 * sin(degx) + l1.z); degx = radians(ang0 + 90); p[2] = new PVector(w0 * cos(degx) + l0.x, l0.y, w0 * sin(degx) + l0.z); p[7] = new PVector(w1 * cos(degx) + l0.x, l0.y + yb, w1 * sin(degx) + l0.z); degx = radians(ang0 - 90); p[3] = new PVector(w0 * cos(degx) + l0.x, l0.y, w0 * sin(degx) + l0.z); p[5] = new PVector(w1 * cos(degx) + l0.x, l0.y + ya, w1 * sin(degx) + l0.z); // 木の位置情報を記録 float tx, ty, tz; tree = new PVector[4]; // n枚分を確保 treeTexNum = new int[tree.length]; treeScale = new float[tree.length]; for (int i=0; i < tree.length; i++) { treeTexNum[i] = (random(100) > 50)? 0 : 1; // 木のテクスチャ種類 if (random(100) < 50) { // 左側に配置 w0 = random(75, 150); // 木の大きさ w1 = random(0.2, 1.00); tx = p[0].x + (p[4].x - p[0].x) * w1; ty = p[0].y + (p[4].y - p[0].y) * w1; tz = p[0].z + (p[4].z - p[0].z) * w1; } else { // 右側に配置 w0 = random(75, 200); w1 = random(0.05, 1.00); tx = p[1].x + (p[6].x - p[1].x) * w1; ty = p[1].y + (p[6].y - p[1].y) * w1; tz = p[1].z + (p[6].z - p[1].z) * w1; } treeScale[i] = w0; tree[i] = new PVector(tx, ty, tz); } } PVector getCenterPos() { return new PVector( (linePos[0].x + linePos[1].x) / 2.0, (linePos[0].y + linePos[1].y) / 2.0, (linePos[0].z + linePos[1].z) / 2.0 ); } // 描画 void draw() { // 道路脇(左)、道路脇(右) beginShape(QUADS); texture(img); { int k = 8 * (2 + texNumber); vertex(p[4].x, p[4].y, p[4].z, texUV[k+0], texUV[k+1]); vertex(p[0].x, p[0].y, p[0].z, texUV[k+2], texUV[k+3]); vertex(p[3].x, p[3].y, p[3].z, texUV[k+4], texUV[k+5]); vertex(p[5].x, p[5].y, p[5].z, texUV[k+6], texUV[k+7]); vertex(p[1].x, p[1].y, p[1].z, texUV[k+0], texUV[k+1]); vertex(p[6].x, p[6].y, p[6].z, texUV[k+2], texUV[k+3]); vertex(p[7].x, p[7].y, p[7].z, texUV[k+4], texUV[k+5]); vertex(p[2].x, p[2].y, p[2].z, texUV[k+6], texUV[k+7]); } // 道路 { int k = 8 * texNumber; vertex(p[0].x, p[0].y, p[0].z, texUV[k+0], texUV[k+1]); vertex(p[1].x, p[1].y, p[1].z, texUV[k+2], texUV[k+3]); vertex(p[2].x, p[2].y, p[2].z, texUV[k+4], texUV[k+5]); vertex(p[3].x, p[3].y, p[3].z, texUV[k+6], texUV[k+7]); } // 木 // カメラ角度を反映させてビルボードにする float ang = cameraAng + (PI / 2.0); float xx = cos(ang); float zz = sin(ang); for (int i=0; i < tree.length; i++) { float s = treeScale[i]; float ax = s * xx; float az = s * zz; float x0 = tree[i].x - ax; float y0 = tree[i].y; float z0 = tree[i].z - az; float x1 = tree[i].x + ax; float y1 = tree[i].y - s * 2; float z1 = tree[i].z + az; int k = 8 * (4 + treeTexNum[i]); vertex(x0, y1, z0, texUV[k+0], texUV[k+1]); vertex(x1, y1, z1, texUV[k+2], texUV[k+3]); vertex(x1, y0, z1, texUV[k+4], texUV[k+5]); vertex(x0, y0, z0, texUV[k+6], texUV[k+7]); } endShape(); } } // 道路生成クラス class Road { int idx, w, dist, texNum; float t, t_spd, v_dur; float startAngXZ, changeAngXZ; // 角度開始値, 角度変化量 float startAngZY, changeAngZY; float angXZ, angZY; PVector pnt; PolyData[] polyData; Road () { idx = 0; w = 160 / 2; // 道路の幅(片方の幅) dist = 64; // 道路の一片の長さ texNum = 0; // テクスチャ番号(0 or 1) t = 0.0; // 時間(進行度) v_dur = 1.0; startAngXZ = 270.0; changeAngXZ = 0; angXZ = startAngXZ; startAngZY = 0; changeAngZY = 0; angZY = startAngZY; pnt = new PVector(0, 0, 0); setNextChangeValue(); // ポリゴン座標群を生成して配列に格納 polyData = new PolyData[256]; for (int i=0; i< polyData.length; i++) { pushNewPos(i); } } // 角度の変化量と変化速度を更新 void setNextChangeValue() { changeAngXZ = random(-80, 80); changeAngZY = random(-10.0, 10.0); if ( (startAngZY + changeAngZY) > 18) changeAngZY = 18 - startAngZY; if ( (startAngZY + changeAngZY) < -18) changeAngZY = -18 - startAngZY; t_spd = random(0.015, 0.04); } // 時間(進行度)を進める void setNextTime() { t += t_spd; if (t >= 1.0) { t -= 1.0; startAngXZ += changeAngXZ; startAngZY += changeAngZY; setNextChangeValue(); } } // 道路片参照インデックス値を更新。新座標値を生成して配列に格納 void incIndex() { pushNewPos(idx); idx = (idx + 1) % polyData.length; } // 座標を生成して配列に格納 void pushNewPos(int idx) { // 道路の線分の現座標値を取得 PVector l0 = new PVector(pnt.x, pnt.y, pnt.z); float a0 = angXZ; // 道路の線分の次の座標値を取得 angXZ = easeInOutQuad(t, startAngXZ, changeAngXZ, v_dur); angZY = easeInOutQuad(t, startAngZY, changeAngZY, v_dur); float dx = radians(angXZ); float dy = radians(angZY); pnt.x = dist * cos(dx) + pnt.x; pnt.y = dist * sin(dy) + pnt.y; pnt.z = dist * sin(dx) + pnt.z; // ポリゴンデータを生成して記録 polyData[idx] = new PolyData(l0, a0, pnt, angXZ, texNum, w); // テクスチャ番号を更新 texNum = (texNum + 1) % 2; setNextTime(); } PolyData getPolyData(int i) { return polyData[i % polyData.length]; } PVector[] getLinePos(int i) { return getPolyData(i).linePos; } float easeInOutQuad(float t, float b, float c, float d) { t /= (d / 2.0); if (t < 1.0) return (c / 2.0 * t * t + b); t -= 1.0; return (-c / 2.0 * (t * (t - 2.0) - 1.0) + b); } float easeInOutCubic(float t, float b, float c, float d) { t /= (d / 2.0); if ( t < 1.0) return (c/ 2.0 * t * t * t + b); t -= 2.0; return (c / 2.0 * (t * t * t + 2.0) + b); } float easeInOutQuint(float t, float b, float c, float d) { t /= (d / 2.0); if ( t < 1.0) return (c/ 2.0 * t * t * t * t * t + b); t -= 2.0; return (c / 2.0 * (t * t * t * t * t + 2.0) + b); } float easeInOutExpo(float t, float b, float c, float d) { t /= (d / 2.0); if ( t < 1.0) return (c/ 2.0 * pow(2, 10 * (t - 1)) + b); t -= 1.0; return (c / 2.0 * ( -pow(2, -10 * t) + 2.0) + b); } // 一次関数の値を取得 float getLinearValue(float t, float v_start, float v_chg) { return (v_start + (v_chg * t)); } }