前回の第49回「テクスチャを立方体の4面に貼る」では,水平に回る立方体にテクスチャマッピングした。そこでは4面の重ね順,厳密にいうと塗り順が問題になる。そして,裏返った面は塗らない「カリング」によって解決した。
しかし,前回予告したとおり,同じ4面を今度は上下左右に回したい。すると,裏返った面も消すことはできず,4面の塗り順に正面から取り組むことになる。
面の塗り順をどのようにして決めるか
立方体の4面を上下左右に回すムービーは,すでに第38回「z座標値に応じて重ね順を変える」でつくった(第38回図1再掲)。もっとも,このとき4面はそれぞれSpriteインスタンスで,それらの重ね順を並べ替えた。今回インスタンスはひとつで,Graphics.drawTriangles()メソッドによる面の塗り順が問題だ。けれど,順序をどのようにして決めるかという考え方は使える。
第38回は4面の中央をx軸またはy軸が貫くように定め,それぞれの中心のz座標値で面の前後を決めた(第38回図2再掲)。
前回のスクリプトでも,立方体の真ん中を原点に定めたので,x軸またはz軸が4面の中心を貫いている(第44回図2再掲)。したがって,各面の中心のz座標で面の前後は決められるはずだ。ただし,4面が別々のインスタンスではないため,各面の中心座標を別に管理する必要がある。
4つの面と中心座標のデータをスクリプトに加える
今回カリングは使えないので,前回のスクリプト1に手を加えていこう。4面の前後さえ正しく整えられれば,立方体をどのように回そうと大丈夫だ。そのためには,4つの面とそれぞれの中心座標を確かめておかなければならない。立方体の各面の正方形を時計回りの4頂点番号(前掲第44回図2参照)で表せば,それぞれの中心座標は次表1のようになる。
表1 立方体の4面とその中心座標
| 面の位置 | 面の4頂点 | 中心座標 |
|---|---|---|
| 前面 | 0-1-2-3 | (0, 0, -nUnit) |
| 右面 | 1-5-6-2 | (nUnit, 0, 0) |
| 後面 | 5-4-7-6 | (0, 0, nUnit) |
| 左面 | 4-0-3-7 | (-nUnit, 0, 0) |
なお,前回のスクリプト1で,Graphics.drawTriangles()メソッドはひとつの正方形をふたつの三角形に分けて塗っている。しかし,4面の前後を決めるうえでは,ふたつの三角形の順番は問わないので,ひとつの正方形として考えて構わない。立方体の4面とそれらの中心座標は,それぞれ別の変数にVectorオブジェクトとしてもつことにする。
まず,正方形の面は4つの頂点番号をuintベース型のVectorオブジェクトに納め,4面のVectorオブジェクトはさらにVector.<uint>ベース型のVectorオブジェクト(faces)に入れて管理する(※1)。つまり,Vectorオブジェクトの入れ子にする。つぎに,各面の中心座標はVector3Dオブジェクトとし,Vector3Dベース型のVectorオブジェクト(centers)に加える。もちろん,その順序は前述の4面を納めたVectorオブジェクトのエレメントと揃える。
// 4つの面とその中心座標を納めるVectorオブジェクトの初期化
var faces:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>();
var centers:Vector.<Vector3D> = new Vector.<Vector3D>();
faces.push(new <uint>[0, 1, 2, 3]); // 前面
faces.push(new <uint>[1, 5, 6, 2]); // 右面
faces.push(new <uint>[5, 4, 7, 6]); // 後面
faces.push(new <uint>[4, 8, 9, 7]); // 左面
centers.push(new Vector3D(0, 0, -nUnit)); // 前面
centers.push(new Vector3D(nUnit, 0, 0)); // 右面
centers.push(new Vector3D(0, 0, nUnit)); // 後面
centers.push(new Vector3D(-nUnit, 0, 0)); // 前面
4面の頂点番号は変数(faces)にもたせたので,三角形の頂点番号の組に置換えてVectorオブジェクト(indices)に加える処理はforループで行える。すると,三角形の頂点番号に置換える関数(addRectangleIndices())の引数は,4頂点番号の整数からそれらを納めたひとつのVectorオブジェクト(uintベース型)に変えた方が楽だ。
var nFaces:uint = 4;
/*
addRectangleIndices(0, 1, 2, 3);
addRectangleIndices(1, 5, 6, 2);
addRectangleIndices(5, 4, 7, 6);
addRectangleIndices(4, 8, 9, 7);
*/
for (var i:uint = 0; i < nFaces; i++) {
addRectangleIndices(faces[i]);
}
// function addRectangleIndices(n0:uint, n1:uint, n2:uint, n3:uint):void {
function addRectangleIndices(face:Vector.<uint>):void {
// indices.push(n0, n1, n3);
indices.push(face[0], face[1], face[3]);
// indices.push(n1, n2, n3);
indices.push(face[1], face[2], face[3]);
}
ここまでは組み入れた新たな変数に合わせて修正を加えただけなので,動きは前回のスクリプト1と同じだ。つまり,4面の重ね順がずっと変わらない(第49回図3再掲)。一旦途中経過として,フレームアクション全体を掲げておこう(スクリプト1)。
スクリプト1 3次元空間で水平に回す立方体の4面にテクスチャマッピング(暫定)
// フレームアクション
var nUnit:Number = 100 / 2;
var mySprite:Sprite = new Sprite();
var myTexture:BitmapData = new Image();
var vertices:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var uvtData:Vector.<Number> = new Vector.<Number>();
var nDeceleration:Number = 0.3;
var myGraphics:Graphics = mySprite.graphics;
var myPerspective:PerspectiveProjection = transform.perspectiveProjection;
var worldMatrix3D:Matrix3D = new Matrix3D();
var viewMatrix3D:Matrix3D = myPerspective.toMatrix3D();
// 4つの面とその中心座標を納めるVectorオブジェクトの初期設定
var nFaces:uint = 4;
var faces:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>();
var centers:Vector.<Vector3D> = new Vector.<Vector3D>();
faces.push(new <uint>[0, 1, 2, 3]); // 前面
faces.push(new <uint>[1, 5, 6, 2]); // 右面
faces.push(new <uint>[5, 4, 7, 6]); // 後面
faces.push(new <uint>[4, 8, 9, 7]); // 左面
centers.push(new Vector3D(0, 0, -nUnit)); // 前面
centers.push(new Vector3D(nUnit, 0, 0)); // 右面
centers.push(new Vector3D(0, 0, nUnit)); // 後面
centers.push(new Vector3D(-nUnit, 0, 0)); // 前面
viewMatrix3D.prependTranslation(0, 0, myPerspective.focalLength);
mySprite.x = stage.stageWidth / 2;
mySprite.y = stage.stageHeight / 2;
vertices.push(-nUnit, -nUnit, -nUnit);
vertices.push(nUnit, -nUnit, -nUnit);
vertices.push(nUnit, nUnit, -nUnit);
vertices.push(-nUnit, nUnit, -nUnit);
vertices.push(-nUnit, -nUnit, nUnit);
vertices.push(nUnit, -nUnit, nUnit);
vertices.push(nUnit, nUnit, nUnit);
vertices.push(-nUnit, nUnit, nUnit);
vertices.push(-nUnit, -nUnit, -nUnit);
vertices.push(-nUnit, nUnit, -nUnit);
// 三角形の頂点番号の組をVectorオブジェクトに加える
for (var i:uint = 0; i < nFaces; i++) {
addRectangleIndices(faces[i]);
}
uvtData.push(0, 0, 0);
uvtData.push(1/4, 0, 0);
uvtData.push(1/4, 1, 0);
uvtData.push(0, 1, 0);
uvtData.push(3/4, 0, 0);
uvtData.push(2/4, 0, 0);
uvtData.push(2/4, 1, 0);
uvtData.push(3/4, 1, 0);
uvtData.push(1, 0, 0);
uvtData.push(1, 1, 0);
addChild(mySprite);
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
var nRotationY:Number = mySprite.mouseX * nDeceleration;
var vertices2D:Vector.<Number> = new Vector.<Number>();
xTransform(vertices2D, nRotationY);
xDraw(vertices2D);
}
function xTransform(vertices2D:Vector.<Number>, myRotation:Number):void {
worldMatrix3D.prependRotation(myRotation, Vector3D.Y_AXIS);
var myMatrix3D:Matrix3D = worldMatrix3D.clone();
myMatrix3D.append(viewMatrix3D);
Utils3D.projectVectors(myMatrix3D, vertices, vertices2D, uvtData);
}
function xDraw(vertices2D:Vector.<Number>):void {
myGraphics.clear();
myGraphics.beginBitmapFill(myTexture);
myGraphics.drawTriangles(vertices2D, indices, uvtData);
myGraphics.endFill();
}
// 四角形の頂点番号からふたつの三角形の頂点番号の組に置換える関数の引数変更
function addRectangleIndices(face:Vector.<uint>):void {
indices.push(face[0], face[1], face[3]);
indices.push(face[1], face[2], face[3]);
}
- ※1
- 頂点番号8と9は,前回uvt座標と数を合わせるため,それぞれ番号0および3と同じ座標の頂点として加えた(第49回「テクスチャを立方体の4面に貼る」の「立方体の4面にテクスチャマッピングする」参照)。


