Alternativa3D 8で頂点等の制御ができるようになるために先日のPlaneを読み解いてみるよ。
コードを再掲載しておく。
package { import alternativa.engine3d.core.Object3D; import alternativa.engine3d.core.VertexAttributes; import alternativa.engine3d.materials.Material; import alternativa.engine3d.objects.Mesh; import alternativa.engine3d.resources.Geometry; /** * ... * @author */ public class Plane extends Object3D { private var _h:uint; private var _w:uint; private var _material:Material; private var _doubleSided:Boolean; private var _numTriangles:uint; public function Plane(h:uint, w:uint, material:Material, doubleSided:Boolean = false){ this._w = w; this._h = h; this._material = material; this._doubleSided = doubleSided; this._numTriangles = (_doubleSided) ? 4 : 2; setPlane(); } private function setPlane():void { var mesh:Mesh = new Mesh() var attributes:Array = new Array(); attributes[0] = VertexAttributes.POSITION; attributes[1] = VertexAttributes.POSITION; attributes[2] = VertexAttributes.POSITION; attributes[3] = VertexAttributes.TEXCOORDS[0]; attributes[4] = VertexAttributes.TEXCOORDS[0]; var geometry:Geometry = new Geometry(); geometry.addVertexStream(attributes); geometry.numVertices = 4; var vertices:Array = [-(_w / 2), -(_h / 2), 0, -(_w / 2), (_h / 2), 0, (_w / 2), (_h / 2), 0, (_w / 2), -(_h / 2), 0]; var uvt:Array = [0, 1, 0, 1, 0, 1, 0, 1]; geometry.setAttributeValues(VertexAttributes.POSITION, Vector.<Number>(vertices)); geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], Vector.<Number>(uvt)); var arr_indices:Array = (_doubleSided) ? [0, 1, 2, 0, 2, 3, 0, 2, 1, 0, 3, 2] : [0, 1, 2, 0, 2, 3]; geometry.indices = Vector.<uint>(arr_indices); mesh.geometry = geometry; mesh.addSurface(_material, 0, _numTriangles); mesh.calculateBoundBox(); addChild(mesh); } } }
まず、Alternativa3D 8においてのMeshとは何か。
こちらの記事を読んでみる。
1-2. メッシュの構造を知る - Wonderflで始めるAlternativa3d - Tips - 丸林商店
Alternativa3D 7以前の話ではあるが設計思想は基本的に同じはず。
8.8.0にFace等のクラスはないが、記事から7と8で共通であろう部分を抜き出してみる。
こんなところだろうか。
言い換えると、ひとつのとマテリアルを貼り付ける面の単位がSurfaceなのだろう。
プリミティブであるBox(Meshのサブクラス)で使ったsetMaterialToAllSurfaces()メソッドの意味も分かってくるね。
MeshのもつすべてのSurfaceに指定した同じマテリアルを貼り付けるわけだ。
全部の面に同じマテリアルを貼り付けるメソッドしかないんだけど、他の貼り付け方はないの??
と思ったけどどうやらMeshから各SurfaceにアクセスしてSurfaceからマテリアルを設定するようだ。
Alternativa3D 8.8.0のASDocのMeshクラスを見てみると、固有のメンバはかなり少ない。
プロパティはgeometryなるGeometryクラスとSurfaceの数であろう読み取り専用のnumSurfaces。
メソッドはコンストラクタとclone()、新しいSurfaceを加えるaddSurface、
MeshからSurafaceにアクセスするgetSurface()、先ほどのsetMaterialToAllSurfaces()。
つまりMeshはSurafaceとGeometryしかもってないんだろうな。
そしてSurfaceクラスを見ると、これまたフィールドがとても少ない。
メソッドはコンストラクタとclone()のみ。コンストラクタも直接呼び出さずにaddSurface使えよって書いてある。
プロパティはひとつのSurfaceにひとつ割り当てられるmaterial、三角形(ポリゴン)の数であると思われるnumTriangles、
IndexBufferのインデックスの開始位置と書いてあるindexBegin。
この三つのプロパティはそのままaddSurface()の引数になってる。
Surfaceの持つ情報はどのマテリアルを使うか、所属する三角形はいくつか、何かよくわからんインデックスの開始位置、
の三つしかないわけだ。
内部的にこのSurfaceにアクセスしてマテリアルを貼り付けるのだとしたら、Surafaceは貼り付けるマテリアルと、どこにはりつけるかの情報をもっていなくてはならない。
おそらく頂点、ないしはポリゴンの集まりであるIndexBufferがひとつのMeshにひとつ与えられていて、
SurfaceはMeshの何番目の頂点(もしくはポリゴン)から三角形いくつぶんマテリアルを貼り付けるか、などと読み込んでいると予想。
ここでindex bufferについて調べてみるとDirectX用語(?)で
vertex bufferは頂点データを格納するバッファ
index bufferは頂点バッファ内の頂点の並び順を指定するバッファ
らしい。
ではその頂点や面の情報はどこにあるのか。
Geometryクラスしか残ってないよね。
Resourceクラスのサブクラスだ。GPUにアップロードするデータのひとつなんだろう。
Geometryクラスはなんかフィールド多いから直接コードを見て必要な部分だけ学ぼう。
やっとPlaneのコードを見るよ!
コンストラクタは引数をそのまま格納してるだけだから省略。
_numTrianglesが_doubleSided=trueの場合は4、falseの場合は2になってる。
三角形の数だね。Alternativa3Dにおいてポリゴンの単位は三角形だから、Plane(厚みのない四角形)を作る場合は三角形が2つ、両面の場合は4ついるというわけだろう。
var mesh:Mesh = new Mesh()
var attributes:Array = new Array();
attributes[0] = VertexAttributes.POSITION;
attributes[1] = VertexAttributes.POSITION;
attributes[2] = VertexAttributes.POSITION;
attributes[3] = VertexAttributes.TEXCOORDS[0];
attributes[4] = VertexAttributes.TEXCOORDS[0];
まずはMeshを作る。
attributesってなんだーー?
ALCさんが言うには特質、属性。
VertexAttributesクラスを見ると5タイプあるみたいだ。
VertexAttributes.POSITIONは3D座標タイプ
VertexAttributes.TEXCOORDSはテクスチャの座標(u,v)タイプ、添え字はチャンネルナンバーらしいが気にしないでおこう
というわけでattributes配列に[3D座標、3D座標、3D座標、UV座標、UV座標]
とタイプを指定しているみたいだ。
var geometry:Geometry = new Geometry();
geometry.addVertexStream(attributes);
geometry.numVertices = 4;
まずGeometryを作成。
geometryにvertex streamを加える。さっき作った配列だ。
よくわからないけど、一つの頂点がもつ情報をあらわしているような気がする。
x,y,zとu,vをもってますよーみたいな。
でgeometryの頂点の数を4つに指定。
var vertices:Array = [-(_w / 2), -(_h / 2), 0, -(_w / 2), (_h / 2), 0, (_w / 2), (_h / 2), 0, (_w / 2), -(_h / 2), 0];
var uvt:Array = [0, 1, 0, 1, 0, 1, 0, 1];
頂点とuvを与えられたwidthとheightから指定。
この時区切らずにそのままArrayにするみたい。
(-w/2,-h/2,0),(-w/2,h/2,0),(w/2,h/2,0),(w/2,-h/2,0)
(0,1),
(0,1),
(0,1),
(0,1)
という意味だろう。数も頂点数4にあうし。
Alternativa3Dの座標系はこうなっているので
この四つの頂点の三次元上の位置はv0から順番こうなる
geometry.setAttributeValues(VertexAttributes.POSITION, Vector.<Number>(vertices));
geometry.setAttributeValues(VertexAttributes.TEXCOORDS[0], Vector.<Number>(uvt));
var arr_indices:Array = (_doubleSided) ? [0, 1, 2, 0, 2, 3, 0, 2, 1, 0, 3, 2] : [0, 1, 2, 0, 2, 3];
geometry.indices = Vector.<uint>(arr_indices);
先ほどのxyz座標とuv座標をVector.<Number>にキャストしてgeometryの指定したattributesに与える。
arr_indicesは両面設定かどうかによって数が変わっている。これはおそらく頂点をむすぶ順番だろう。
xyz座標をセットした順番に0,1,2,3と名付けると、片面の場合は(0→1→2)と(0→2→3)の二つの三角形ができる。
そして両面の時は反対側も定義しなくてはならないらしい。
三角形ポリゴンの作り方は見える面から時計回りに3点を選ばなくてはならない、みたいな感じだろうか。
さっきの図を裏から見るとこうなり、(0→2→1)と(0→3→2)の二つの三角形が加わる
指定されたインデックスは見える面から時計回りに選ばれているのが分かる。
そしてその三角形の構成情報arr_indicesをVector.<uint>にキャストしてgeometryに設定している。
mesh.geometry = geometry;
mesh.addSurface(_material, 0, _numTriangles);
mesh.calculateBoundBox();
addChild(mesh);
最初に作ったMeshにGeometryを設定して
Surfaceを1つ追加している。Surfaceのもつ三角形は片面なら2、両面なら4だ。
1つめのSurfaceだからindexBeginは0。
これがgeometry.indicesのindexだとすると次に加えるときは片面なら0+2*3=6から、両面なら0+4*3=12から始まるのだろう。
calculateBoundBox()は空間内のmeshの領域を計算するみたいだけど返り値もないしどこで使うんだろ。
内部的にいるんだろうな。
最後にこのObject3Dの表示リストに作ったMeshを加える。これで完成だ。
多分に推測が入っているがおおまかな流れは次のようになるだろう。
ここまで分かれば頂点や面の操作はそこまで難しくなさそうだね。
3Dの知識0の推測があってればねー。
追記
謎のcalculateBoundBox()だけどObject3D.boundBoxプロパティで得られるBoundBoxを計算して作成する模様。
このBoundBoxクラスはObject3Dの持つ表示領域の矩形のxyzの最大最小値が格納されている。
交差判定などで使うみたい。
ドキュメントにはないけどupdateBoundBox()なるものもある。めんどくさそうな引数だけど。
とりあえず謎は解決。
Author:9ballsyn
ActionScriptについて
最近はMolehill