Molehill API対応3Dエンジンのひとつ"Alternaiva3D 8.5.0"をさわってみる。
今回はexampleのparsersexampleだ。
外部モデルデータを読み込むサンプルだね。
モデルを作れない俺には関係なさそうだけど...。
package parsersexample { import alternativa.engine3d.controllers.SimpleObjectController; import alternativa.engine3d.core.Camera3D; import alternativa.engine3d.core.Object3D; import alternativa.engine3d.core.Renderer; import alternativa.engine3d.core.Resource; import alternativa.engine3d.core.View; import alternativa.engine3d.loaders.ParserA3D; import alternativa.engine3d.loaders.ParserCollada; import alternativa.engine3d.loaders.TexturesLoader; import alternativa.engine3d.materials.ColladaMaterial; import alternativa.engine3d.materials.TextureMaterial; import alternativa.engine3d.objects.Mesh; import alternativa.engine3d.objects.Surface; import alternativa.engine3d.resources.FileTextureResource; import alternativa.engine3d.resources.Geometry; import alternativa.engine3d.resources.TextureResource; import flash.display.Sprite; import flash.display.Stage3D; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; /** * External model parsing. * Пример работы с парсерами. */ public class ParsersExample extends Sprite { private var scene:Object3D = new Object3D(); private var camera:Camera3D; private var controller:SimpleObjectController; private var stage3D:Stage3D; public function ParsersExample() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; // Camera and view // Создание камеры и вьюпорта camera = new Camera3D(1, 1000); camera.view = new View(stage.stageWidth, stage.stageHeight); addChild(camera.view); addChild(camera.diagram); // Initial position // Установка начального положения камеры camera.rotationX = -130*Math.PI/180; camera.y = -300; camera.z = 300; controller = new SimpleObjectController(stage, camera, 200); scene.addChild(camera); // // Debug // // Режим отладки // camera.addToDebug(Debug.EDGES, Object3D); // camera.addToDebug(Debug.BOUNDS, Object3D); stage3D = stage.stage3Ds[0]; stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate); stage3D.requestContext3D(); } private function onContextCreate(e:Event):void { stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreate); // Загрузка моделей // Models loading var loaderA3D:URLLoader = new URLLoader(); loaderA3D.dataFormat = URLLoaderDataFormat.BINARY; loaderA3D.load(new URLRequest("parsersexample/model.A3D")); loaderA3D.addEventListener(Event.COMPLETE, onA3DLoad); var loaderCollada:URLLoader = new URLLoader(); loaderCollada.dataFormat = URLLoaderDataFormat.TEXT; loaderCollada.load(new URLRequest("parsersexample/model.DAE")); loaderCollada.addEventListener(Event.COMPLETE, onColladaLoad); // Listeners // Подписка на события stage.addEventListener(Event.ENTER_FRAME, onEnterFrame); stage.addEventListener(Event.RESIZE, onResize); } private function onA3DLoad(e:Event):void { // Model parsing // Парсинг модели var parser:ParserA3D = new ParserA3D(); parser.parse((e.target as URLLoader).data); trace(parser.objects); var mesh:Mesh; for each (var object:Object3D in parser.objects) { if (object.name == "Cylinder01") { mesh = object as Mesh; break; } } mesh.x -= 100; scene.addChild(mesh); uploadResources(mesh.getResources(false, Geometry)); // Setup materials // Собираем текстуры и назначаем материалы var textures:Vector.<FileTextureResource> = new Vector.<FileTextureResource>(); for (var i:int = 0; i < mesh.numSurfaces; i++) { var surface:Surface = mesh.getSurface(i); var material:ColladaMaterial = surface.material as ColladaMaterial; if (material != null) { var diffuse:FileTextureResource = material.textures["diffuse"]; if (diffuse != null) { diffuse.url = "parsersexample/" + diffuse.url; textures.push(diffuse); surface.material = new TextureMaterial(diffuse); } } } // Loading of textures // Загрузка текстур var texturesLoader:TexturesLoader = new TexturesLoader(stage3D.context3D); texturesLoader.loadResources(textures); } private function onColladaLoad(e:Event):void { // Model parsing // Парсинг модели var parser:ParserCollada = new ParserCollada(); parser.parse(XML((e.target as URLLoader).data), "parsersexample/", false /*, DaeUnits.MILIMETERS*/); trace(parser.objects); var mesh:Mesh = parser.getObjectByName("Cylinder01") as Mesh; mesh.x = 100; scene.addChild(mesh); // Загрузка ресурсов uploadResources(mesh.getResources(false, Geometry)); // Собираем текстуры и назначаем материалы var textures:Vector.<FileTextureResource> = new Vector.<FileTextureResource>(); for (var i:int = 0; i < mesh.numSurfaces; i++) { var surface:Surface = mesh.getSurface(i); var material:ColladaMaterial = surface.material as ColladaMaterial; if (material != null) { var diffuse:TextureResource = material.textures["diffuse"]; if (diffuse != null) { textures.push(diffuse); surface.material = new TextureMaterial(diffuse); } } } // Loading of textures // Загрузка текстур var texturesLoader:TexturesLoader = new TexturesLoader(stage3D.context3D); texturesLoader.loadResources(textures); } private function uploadResources(resources:Vector.<Resource>):void { for each (var resource:Resource in resources) { resource.upload(stage3D.context3D); } } private function onEnterFrame(e:Event = null):void { controller.update(); camera.render(stage3D); } private function onResize(e:Event = null):void { camera.view.width = stage.stageWidth; camera.view.height = stage.stageHeight; onEnterFrame(); } } }
// Camera and view
camera = new Camera3D(1, 1000);
camera.view = new View(stage.stageWidth, stage.stageHeight);
addChild(camera.view);
addChild(camera.diagram);
// Initial position
camera.rotationX = -130*Math.PI/180;
camera.y = -300;
camera.z = 300;
controller = new SimpleObjectController(stage, camera, 200);
scene.addChild(camera);
おなじみの初期設定。
カメラを作って、ビューを作ってカメラに設定。
ビューとデバッグ表示を2Dの表示リストに追加。
カメラの位置を設定してカメラコントローラを作り、カメラを3Dシーンに追加。
このへんは大体必要だからもうPV3DのBasicViewみたいにクラス化しちゃってもいいかもね。
// Debug
// camera.addToDebug(Debug.EDGES, Object3D);
// camera.addToDebug(Debug.BOUNDS, Object3D);
stage3D = stage.stage3Ds[0];
stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
stage3D.requestContext3D();
コメントアウトされてる部分。どうやらデバッグ用だからアンコメントしてみたら
Debugなんてクラスはなかった...。
なんなんだよもう。
Camera3D.addToDebug(debug:int, objectOrClass:*):void
第一引数はintか。
Stage3Dを取得してCONTEXT3D_CREATEイベントを登録してrequestContext3D()。
ここまでいつも通り。
private function onContextCreate(e:Event):void {
stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
// Models loading
var loaderA3D:URLLoader = new URLLoader();
loaderA3D.dataFormat = URLLoaderDataFormat.BINARY;
loaderA3D.load(new URLRequest("parsersexample/model.A3D"));
loaderA3D.addEventListener(Event.COMPLETE, onA3DLoad);
var loaderCollada:URLLoader = new URLLoader();
loaderCollada.dataFormat = URLLoaderDataFormat.TEXT;
loaderCollada.load(new URLRequest("parsersexample/model.DAE"));
loaderCollada.addEventListener(Event.COMPLETE, onColladaLoad);
// Listeners
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(Event.RESIZE, onResize);
}
CONTEXT3D_CREATEイベントハンドラ。
まずは自分のイベントを外して
URLLoaderでA3Dファイルを読み込み。
この際のデータフォーマットはBINARYで。
A3Dファイルってなんだ...?
って検索してもなかなか出てこない。
DAEとかそういうのの仲間だと思ったんだけど。
でAlternativa3Dのことだと気づく。
3DS Max 2010 pluginなるものが公式ページにあった。
どうやら3DSでAlternativa3Dの独自形式で書き出すものらしい。
URLLoaderは非同期読み込みなのでCOMPLETEイベントを登録。
もうひとつモデルを今度はDAEで読み込む。
データフォーマットはTEXTだ。
Colladaはxmlだからテキストで読み込むわけだ。
ってことはA3Dはバイナリで情報書き込んであるんだね。
モデルが違うかもだから単純に比較していいかわからないけれどmodel.A3Dとmodel.DAEでは3倍ほどA3Dのほうが容量が軽い。
でも独自形式よりはColladaだよなあ。
で、ENTER_FRAMEとRESIZEイベントを登録する。
この二つのイベントハンドラは前回と同じだから省略。
// Model parsing
var parser:ParserA3D = new ParserA3D();
parser.parse((e.target as URLLoader).data);
trace(parser.objects);
var mesh:Mesh;
for each (var object:Object3D in parser.objects) {
if (object.name == "Cylinder01") {
mesh = object as Mesh;
break;
}
}
mesh.x -= 100;
scene.addChild(mesh);
uploadResources(mesh.getResources(false, Geometry));
A3Dロードのイベントハンドラ。
ParserA3Dクラスインスタンスを作成してparse()でモデルをパース
public function parse(input:ByteArray):void
Fills objects and hierarchy lists from loaded byteArray
ByteArrayからfills objectsとhierarchy listsを読みだしてくれるそうな。
読んだ結果はobjectsプロパティにVector.<Object3D>で入ってる。
parser.objectsからnameプロパティがCylinder01のものを見つけてMeshとして変数meshに入れる。
MeshはプリミティブのBoxのスーパークラスで、Object3Dのサブクラス。頂点とか面を持つクラスの基本クラスかな。
これをシーンに配置してuploadResourcesを呼び出す。
private function uploadResources(resources:Vector.<Resource>):void {
for each (var resource:Resource in resources) {
resource.upload(stage3D.context3D);
}
}
引数はVector.<Resource>。
今渡したのはmesh.getResources(false, Geometry)
public function getResources(hierarchy:Boolean = false, resourceType:Class = null):Vector
hierarchy:Boolean (default = false) — flag for collecting resources in children
resourceType:Class (default = null) — To collect resources only the given type
hierarchyは子のリソースも含めるか、resourceTypeは指定されたタイプのリソースのみ得るから
meshのみのGeometryデータのみをVectorに入れて渡した感じだ。
渡したリソースをcontex3Dにアップロードしますよと。
つまり今回は読み込んだA3DファイルのGeometryをアップロード。
戻って次はマテリアルの設定。
// Setup materials
var textures:Vector.<FileTextureResource> = new Vector.<FileTextureResource>();
for (var i:int = 0; i < mesh.numSurfaces; i++) {
var surface:Surface = mesh.getSurface(i);
var material:ColladaMaterial = surface.material as ColladaMaterial;
if (material != null) {
var diffuse:FileTextureResource = material.textures["diffuse"];
if (diffuse != null) {
diffuse.url = "parsersexample/" + diffuse.url;
textures.push(diffuse);
surface.material = new TextureMaterial(diffuse);
}
}
}
ますFileTextureResourceのVectorを作って
meshから面を取り出して面のマテリアルをColladaMaterialとして得る。
これがnullでなければ
マテリアルからリソースを取り出す。
public var textures:Object
List of FileTextureResource, which can be loaded with TexturesLoader. Object keys are names of channels. Possible variants: ambient, emission, diffuse, specular, shininess, reflective, transparent, bump
ColladaMaterialのtexturesプロパティはFileTextureResourceをプロパティに持つObject型で与えられてキーは8種類。
その中の"diffuse"をFileTextureResourceとして取り出す。
これがまたnullでなければリソースの相対パスを直して(A3Dファイルとテクスチャの画像ファイルは同階層に書き出されているのでA3Dファイルからのurlはそうなっているが、このswfファイルはそれら3Dモデルのファイルの一つ上の階層にあるため画像ファイルを参照するためにはフォルダ名が必要になるから)、最初に作ったVectorに格納。
この面のマテリアルを新たに作ったTextureMaterialにする。
これらの作業をmeshの面の数だけ繰り返し。
// Loading of textures
var texturesLoader:TexturesLoader = new TexturesLoader(stage3D.context3D);
texturesLoader.loadResources(textures);
で、そのリソースをContext3Dから読み込む。
読み込むの??リソースはすでにContext3Dにアップロードされてるってこと?
なんか俺の読み方が間違ってる気がする。てかややこしい。
全部の面からColladaMaterialを取り出してそこからFileTextreResourceを取り出してVectorにいれつつ、パスを直して面のマテリアルをTextureResourceから作ったTextureMaterialに差し替え。
リソースのVectorをContext3Dにロード。
ロードって読み込みだよなあ。TextureLoaderって言うくらいだし。
これでA3Dファイルからの3Dモデルの読み込みは完了。
// Model parsing
var parser:ParserCollada = new ParserCollada();
parser.parse(XML((e.target as URLLoader).data), "parsersexample/", false /*, DaeUnits.MILIMETERS*/);
trace(parser.objects);
var mesh:Mesh = parser.getObjectByName("Cylinder01") as Mesh;
mesh.x = 100;
scene.addChild(mesh);
uploadResources(mesh.getResources(false, Geometry));
次はColladaからの読み込み。
今度はParserColladaになってる。
URLLoaderが読み込んだデータをXMLとしてパーサーに渡す。
他の引数はURL指定絡みみたいだ。
MeshをgetObjectByName()で取り出してシーンに追加。
MeshのGeometryデータリソースをContext3Dにアップロード。
var textures:Vector.<FileTextureResource> = new Vector.<FileTextureResource>();
for (var i:int = 0; i < mesh.numSurfaces; i++) {
var surface:Surface = mesh.getSurface(i);
var material:ColladaMaterial = surface.material as ColladaMaterial;
if (material != null) {
var diffuse:TextureResource = material.textures["diffuse"];
if (diffuse != null) {
textures.push(diffuse);
surface.material = new TextureMaterial(diffuse);
}
}
}
// Loading of textures
var texturesLoader:TexturesLoader = new TexturesLoader(stage3D.context3D);
texturesLoader.loadResources(textures);
ここはまったくいっしょ。
というわけでA3DとColladaからモデルを読み込めるようになった。
意味が分からないところがまだあるけどまあ真似すればおkおk
Author:9ballsyn
ActionScriptについて
最近はMolehill