!!!3D計算で使えるVector3DとMatrix3D 3DCGでは、ベクトルと行列の扱いが必須になります。 Flex3で追加されたVector3DとMatrix3Dを使うことで、そのベクトルと行列計算を行うことができます。 !!Vector3D 「Vector3D」型は、(x, y, z, w)の要素を持ちます。 var v:Vector3D; v = new Vector3D(); とすると、(v.x, v.y, v.z, v.w)にそれぞれアクセスできます。 w要素は使わないのでしたらメモリの無駄になりますので、[[Vector|配列としてのVector型_Flex]]を使って var v:Vector.; v = new Vector.(3 * 頂点数, true); v[0] = 1.0; // X要素に相当 v[1] = 2.2; // Y要素に相当 v[2] = 3.1; // Z要素に相当 として扱うほうが節約できそうです(特に何千という頂点を一括管理するような場合)。 このVector3Dは後述するMatrix3Dや3DCGを意識した描画処理にて使われます。 Vector3D型では、以下のような関数が用意されています。 !XYZ要素の加算 var v1:Vector3D, v2:Vector3D; v1 = new Vector3D(1, 2, 3); v2 = new Vector3D(3, 6, 2); v1 = v1.add(v2); Vector3Dの加算は「add」にて行います。 この場合は、「v1 = v1 + v2」の計算を行います。 上記結果は「v1 = (4, 8, 5)」です。 !XYZ要素の減算 var v1:Vector3D, v2:Vector3D; v1 = new Vector3D(1, 2, 3); v2 = new Vector3D(3, 6, 2); v1 = v1.subtract(v2); Vector3Dの減算は「subtract」にて行います。 この場合は、「v1 = v1 - v2」の計算を行います。 上記結果は「v1 = (-2, -4, 1)」です。 !内積計算 var v1:Vector3D, v2:Vector3D; v1 = new Vector3D(1, 2, 3); v2 = new Vector3D(3, 6, 2); var dVal:Number = v1.dotProduct(v2); 内積計算は dVal = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; 同等のものを「dotProduct」にて内積計算を行います。 この場合のdValの値は21になります。 !外積計算 var v1:Vector3D, v2:Vector3D; v1 = new Vector3D(1, 2, 3); v2 = new Vector3D(3, 6, 2); var v3:Vector3D = v1.crossProduct(v2); 「crossProduct」関数にて、v1とv2に垂直なベクトルを計算します。以下の計算と同じになります。 v3.x = v1.y * v2.z - v1.z * v2.y; v3.y = v1.z * v2.x - v1.x * v2.z; v3.z = v1.x * v2.y - v1.y * v2.x; この場合の結果は「v3 = (-14, 7, 0)」となります。 !ベクトルの長さを求める var v1:Vector3D; v1 = new Vector3D(1, 2, 3); var len:Number = v1.length; ベクトルの長さは「length」にて取得します。 len = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z); と結果は同じです。 この場合の結果は「len = 3.7416573867739413」となります。 !ベクトルを正規化(単位ベクトル化) var v1:Vector3D; v1 = new Vector3D(1, 2, 3); v1.normalize(); ベクトルの長さを1.0にする正規化は「normalize」にて行います。 指定のベクトル自身が正規化されます。 len = Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z); v1.x /= len; v1.y /= len; v1.z /= len; と同じ結果になります。 この場合の結果は「v1 = (0.2672612419124244, 0.5345224838248488, 0.8017837257372732)」となります。 !!Matrix3D 4x4行列を扱う型として「Matrix3D」があります。 var m:Matrix3D; m = new Matrix3D(); !行列の各要素を取り出し 配列の要素は「Matrix3D.rawData」にて16個の要素を持つNumberの配列(Vector)として格納されています。 var m:Matrix3D; m = new Matrix3D(); var val:Number = m.rawData[2 * 4 + 1]; とした場合は、mの2行目1列でのデータがvalに入ることになります。 行列の並びは以下のようになります。 {{math m = \begin{bmatrix} 0&1&2&3\\ 4&5&6&7\\ 8&9&10&11\\ 12&13&14&15\\ \end{bmatrix} }} newした初期段階では単位行列が入ることになります。 !行列の各要素を変更 var m:Matrix3D; m = new Matrix3D(); m.rawData[2] = 5.0; として要素を変えることができる、と一瞬思ってしまいますが、実はこれがダメです。 var m:Matrix3D; m = new Matrix3D(); // 16個のNumberを持つ配列を作成 var dat:Vector.; dat = new Vector.(16, true); // 行列 m の内容を配列datに複製 dat = m.rawData; // 配列datでの値を変更 dat[2] = 5.0; // 配列datの内容を行列 m のrawDataに複製 m.rawData = dat; のように、一度Numberの配列(Vector)を作成し、配列に行列の内容(rawData)をコピーします。コピーした配列上で変更して配列ごとMatrix3DのrawDataにコピーすると反映されます。 ちと面倒ですね。 !単位行列を入れる var m:Matrix3D; m = new Matrix3D(); m.indentity(); 「Matrix3D.indentity();にて単位行列を入れます。 !行列を複製する var m:Matrix3D, m2:Matrix3D; m = new Matrix3D(); として行列 m にデータを格納したとします。 これを複製するには m2 = m.clone(); と「clone」関数を使用することで戻り値のm2に複製された値が格納されることになります。 !行列同士の乗算(ちょっと納得できない点があるので後で追記予定) m = m * m2 の行列計算は、 var m:Matrix3D, m2:Matrix3D; m = new Matrix3D(); m2 = new Matrix3D(); ... m.append(m2); のように「append」関数にて行います。この場合は、行列 m の値が変更されます。 mの値を変更したくない場合は、一度cloneしてからそれに対してappendするようにします。 http://livedocs.adobe.com/flex/3_jp/langref/index.html では、 thisMatrix = lhs * thisMatrix; となっているのですが、 thisMatrix = thisMatrix * lhs; の結果になってるようだけど、、、。 !回転行列を求める var m:Matrix3D; m = new Matrix3D(); m.appendRotation(10, Vector3D.Y_AXIS); 行列に対して、回転行列を乗算します。第一引数は回転角度(度数指定)、第二引数は 「Vector3D.X_AXIS」「Vector3D.Y_AXIS」「Vector3D.Z_AXIS」でそれぞれXYZ軸中心回転を表します。 m = m * 回転行列 の計算になります(逆かもしれん)。この順番が逆になって「回転行列 * m」となったものが「prependRotation」になります。 !ベクトルと行列の乗算 var v:Vector3D = new Vector3D(); var m:Matrix3D = new Matrix3D(); という行列があった場合、 var v2:Vector3D = m.transformVector(v); の「transformVector」関数にて、ベクトルvと行列mでの乗算が行われます。 透視変換行列などで計算して、Vector3Dでのw値が必要な場合はそのままwに対して値が入ります(普段は1.0)。