ActionScript 3.0で始めるオブジェクト指向スクリプティング

第50回立方体の4面にテクスチャを貼って上下左右に回す

前回の第49回テクスチャを立方体の4面に貼るでは、水平に回る立方体にテクスチャマッピングした。そこでは4面の重ね順、厳密にいうと塗り順が問題になる。そして、裏返った面は塗らない「カリング」によって解決した。

しかし、前回予告したとおり、同じ4面を今度は上下左右に回したい。すると、裏返った面も消すことはできず、4面の塗り順に正面から取り組むことになる。

面の塗り順をどのようにして決めるか

立方体の4面を上下左右に回すムービーは、すでに第38回z座標値に応じて重ね順を変えるでつくった第38回図1再掲⁠⁠。もっとも、このとき4面はそれぞれSpriteインスタンスで、それらの重ね順を並べ替えた。今回インスタンスはひとつで、Graphics.drawTriangles()メソッドによる面の塗り順が問題だ。けれど、順序をどのようにして決めるかという考え方は使える。

第38回図1 四方に置いた面を上下左右に回す(再掲)
第38回図1 四方に置いた面を上下左右に回す(再掲)

第38回は4面の中央をx軸またはy軸が貫くように定め、それぞれの中心のz座標値で面の前後を決めた第38回図2再掲⁠⁠。

第38回図2 各面の中央をxまたはz軸が貫く(再掲)
第38回図2 各面の中央をxまたはz軸が貫く(再掲第38回図2 各面の中央をxまたはz軸が貫く(再掲)

前回のスクリプトでも、立方体の真ん中を原点に定めたので、x軸またはz軸が4面の中心を貫いている第44回図2再掲⁠⁠。したがって、各面の中心のz座標で面の前後は決められるはずだ。ただし、4面が別々のインスタンスではないため、各面の中心座標を別に管理する必要がある。

第44回図2 原点を中心に定めた立方体の8頂点座標(再掲)
第44回図2 原点を中心に定めた立方体の8頂点座標(再掲)

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⁠⁠。

第49回図3 3次元空間座標を回しても面の重ね順はそのまま(再掲)
第49回図3 3次元空間座標を回しても面の重ね順はそのまま(再掲) 第49回図3 3次元空間座標を回しても面の重ね順はそのまま(再掲)
スクリプト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]);
}

4つの面の塗り順を並べ替える

さて、4つの面の塗り順を並べ替えるのが、今回のメインイベントになる。とはいえ、面の中心のz座標値で前後を決めるという考え方は、前述のとおり第38回「z座標値に応じて重ね順を変える」と同じだ。ただ、扱うオブジェクトが目に見えるDisplayObject(のサブクラスの)インスンスから、目に見えない座標や数値のデータに変わる。したがって、それに応じて処理の仕方も工夫しなければならない。

改めて確認しておきたいのは、このテクスチャマッピングのスクリプトでは、基本的に座標値のデータは初期設定のままいじらないということだ。3次元空間の座標変換はMatrix3Dオブジェクト(worldMatrix3D)に加える(第48回テクスチャに遠近法を適用する - uvt座標の指定「Utils3D.projectVectors()メソッドを使う」参照⁠⁠。今回もこの基本は変えない。並べ替えるのは、頂点番号の組を納めたVectorオブジェクト(indices)のエレメントのみとする。

三角形の頂点番号が納められたVectorオブジェクト(indices)は、エレメントをつぎのような流れで並べ替えよう。

  1. 4面の中心座標をMatrix3Dオブジェクト(worldMatrix3D)で変換する。
  2. 変換された中心座標のzの値により四角形4面の並び順を決める。
  3. 4面の順にしたがって頂点番号のVectorオブジェクトのエレメントを並べ替える。

考えどころは上述2.だ。中心座標と四角形の4面は、それぞれ異なるVectorオブジェクト(centersとfaces)に納められている。これらをどう組み合わせるかについては、いくつかの手が考えられよう。

今回は面の数も少ないので、変換した中心座標のVector3Dオブジェクトとそれに対応する面の4頂点番号のVectorオブジェクト(uintベース型)を配列に入れ、Arrayベース型のVectorオブジェクトに納めることにする。そのうえで、Vectorオブジェクトの配列エレメントを、中心座標のzの値で並べ替えればよい。Vector.sort()メソッドの引数には、Array.sort()メソッドと同じ比較関数が渡せる(比較関数については、第24回インスタンスの管理と配列の並べ替え「インスタンスの重ね順を管理する」参照⁠⁠。三角形の頂点番号の組を並べ替える関数(xSetOrder())はつぎのとおりだ。

function xSetOrder():void {
  var transformedFaces:Vector.<Array> = new Vector.<Array>();
  for (var i:uint = 0; i < nFaces; i++) {
    var transformedVector3D:Vector3D = worldMatrix3D.transformVector(centers[i]);
    transformedFaces[i] = [transformedVector3D, faces[i]];
  }
  transformedFaces.sort(compare);
  indices.length = 0;
  for (var j:uint = 0; j < nFaces; j++) {
    addRectangleIndices(transformedFaces[j][1]);
  }
}
function compare(a:Array, b:Array):Number {
  var nA:Number = a[0].z;
  var nB:Number = b[0].z;
  if (nA < nB) {
    return 1;
  } else if (nA > nB) {
    return -1;
  } else {
    return 0;
  }
}

まず、ひとつ目のforステートメントでは、回転した後の各面の中心座標のVector3Dオブジェクト(transformedVector3D)Matrix3D.transformVector()メソッドで求め、対応する面とともに配列に入れたうえで、Arrayベース型のVectorオブジェクト(transformedFaces)に加えるMatrix3D.transformVector()メソッドについては、 第42回Vector3Dクラスの3次元空間座標とインスタンスへの描画「Vector3DインスタンスをMatrix3Dオブジェクトで座標変換する」参照⁠⁠。

つぎに、変換した中心座標と面の配列が納められたVectorオブジェクト(transformedFaces)に対して、Vector.sort()メソッドを呼出す。引数の比較関数(compare())は、エレメントの配列から中心座標のVector3Dオブジェクトを取出して、そのz座標値で並べ替えている。

そして、ふたつ目のforステートメントが、中心のz座標値で並べ替えたVectorオブジェクトから面の(頂点番号の)Vectorオブジェクトを順に取出し、関数(addRectangleIndices())により三角形の頂点番号に置き換えて、新たなVectorオブジェクト(indices)にエレメントとして加える。なお、Vector.lengthプロパティを0に設定すると、すべてのエレメントが削除されて空のVectorオブジェクトになる。

これで、Graphics.drawTriangles()メソッドの第2引数に渡す頂点番号のVectorオブジェクト(indices)は、各面のz座標値にしたがって並べ替えられる。水平に回る立方体の見た目は、前回のスクリプト2と同じだ。しかし、カリングはしていない第49回図4再掲⁠⁠。面の塗り順が正しく設定されたということだ。これで水平の回転はできあがったので、フレームアクション全体をスクリプト2に掲げる。

function xRotate(eventObject:Event):void {
  var nRotationY:Number = mySprite.mouseX * nDeceleration;
  var vertices2D:Vector.<Number> = new Vector.<Number>();
  xTransform(vertices2D, nRotationY);
  xSetOrder();   // 追加
  xDraw(vertices2D);
}
第49回図4 カリングによって手前向きの面だけが表示される(再掲)
第49回図4 カリングによって手前向きの面だけが表示される(再掲) 第49回図4 カリングによって手前向きの面だけが表示される(再掲)
スクリプト2 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();
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);
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);
  xSetOrder();   // 追加
  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]);
}
// 頂点番号で定める面の順序を並べ替える
function xSetOrder():void {
  var transformedFaces:Vector.<Array> = new Vector.<Array>();
  for (var i:uint = 0; i < nFaces; i++) {
    var transformedVector3D:Vector3D = worldMatrix3D.transformVector(centers[i]);
    transformedFaces[i] = [transformedVector3D, faces[i]];
  }
  transformedFaces.sort(compare);
  indices.length = 0;
  for (var j:uint = 0; j < nFaces; j++) {
    addRectangleIndices(transformedFaces[j][1]);
  }
}
function compare(a:Array, b:Array):Number {
  var nA:Number = a[0].z;
  var nB:Number = b[0].z;
  if (nA < nB) {
    return 1;
  } else if (nA > nB) {
    return -1;
  } else {
    return 0;
  }
}

立方体を上下左右に回す

ここまでくれば、垂直に回すのもすでに学んだことの復習に過ぎない。手を加えるのはふたつの関数だ。できあがったフレームアクション全体をスクリプト3に掲げた。

第1は、DisplayObject.enterFrameイベントのリスナー関数(xRotate())だ。マウスポインタのy座標値から、x軸で回す垂直方向の角度を求める。そして、y軸とx軸の回転角を座標変換の関数(xTransform())に引数として渡す。すると第2に、座標変換の関数に垂直回転の角度を新たな引数として加えなければならない。そして、その引数値でMatrix3Dオブジェクト(worldMatrix3D)にx軸の垂直回転を加える。また、直前の頂点座標をふたつの軸で回転するには、後から加える変換のメソッドMatrix3D.appendRotation()を使わなければならない[2]⁠。

スクリプト3 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();
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);
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 nRotationX:Number = mySprite.mouseY * nDeceleration;   // 追加
  var vertices2D:Vector.<Number> = new Vector.<Number>();
  // xTransform(vertices2D, nRotationY);
  xTransform(vertices2D, nRotationY, nRotationX);
  xSetOrder();
  xDraw(vertices2D);
}
// function xTransform(vertices2D:Vector.<Number>, myRotation:Number):void {
function xTransform(vertices2D:Vector.<Number>, myRotationY:Number, myRotationX:Number):void {
  // worldMatrix3D.prependRotation(myRotation, Vector3D.Y_AXIS);
  worldMatrix3D.appendRotation(myRotationY, Vector3D.Y_AXIS);
  worldMatrix3D.appendRotation(myRotationX, Vector3D.X_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]);
}
function xSetOrder():void {
  var transformedFaces:Vector.<Array> = new Vector.<Array>();
  for (var i:uint = 0; i < nFaces; i++) {
    var transformedVector3D:Vector3D = worldMatrix3D.transformVector(centers[i]);
    transformedFaces[i] = [transformedVector3D, faces[i]];
  }
  transformedFaces.sort(compare);
  indices.length = 0;
  for (var j:uint = 0; j < nFaces; j++) {
    addRectangleIndices(transformedFaces[j][1]);
  }
}
function compare(a:Array, b:Array):Number {
  var nA:Number = a[0].z;
  var nB:Number = b[0].z;
  if (nA < nB) {
    return 1;
  } else if (nA > nB) {
    return -1;
  } else {
    return 0;
  }
}

[ムービープレビュー]を確かめると、立方体の4面がマウスポインタの位置に応じて上下左右に回る図1⁠。この動きの見た目は、第38回「z座標値に応じて重ね順を変える」でつくったムービーと同じだ。しかし、テクスチャマッピングの方が優れている点は大きくふたつある。

第1に、球やドーナッツ型など、複雑なかたちが自在につくれる。DisplayObject(のサブクラスの)インスンスを組合わせた形状にはかぎりがある。第2は、処理の軽さだ。テクスチャマッピングの直前まで、演算はすべて座標に対して行うだけで、描画に関わらない。そして、描画するインスタンスはひとつで済む。

図1 上下左右に回した立方体に面の前後を整えて塗る
図1 上下左右に回した立方体に面の前後を整えて塗る 図1 上下左右に回した立方体に面の前後を整えて塗る

テクスチャマッピングは今回で終える。次回は3次元空間座標を回転するMatrix3Dクラスのメソッドについて、少し深堀りしてみよう。

おすすめ記事

記事・ニュース一覧