Hello PIYO.
2つのobjファイルを読み込んで、頂点アニメーションさせてみました。
カーソルキーの左右でカメラ位置をぐるっと一周できます。
/**
* Copyright ume ( http://wonderfl.net/user/ume )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/3KsE
*/
package {
import net.hires.debug.Stats;
import flash.display.Sprite;
import flash.text.TextField;
import flash.display.Stage;
import flash.ui.Keyboard;
import com.adobe.utils.*;
import flash.utils.getTimer;
import flash.events.*;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.display3D.*;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.display.Stage;
import flash.display.Stage3D;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.display3D.textures.Texture;
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.geom.Rectangle;
[SWF(width="465", height="465", frameRate="60")]
public class VertexAnimation extends Sprite{
private var context3D:Context3D;
private var program:Program3D;
private var vertexbuffer:VertexBuffer3D;
private var normalbuffer:VertexBuffer3D;
private var indexBuffer:IndexBuffer3D;
private var vertexbuffer2:VertexBuffer3D;
private var normalbuffer2:VertexBuffer3D;
private var constMatrix:Matrix3D;
private var modelMatrix:Matrix3D;
private var camera:Camera;
private var perspective:PerspectiveMatrix3D;
private var movieWidth:Number = 465;
private var movieHeight:Number = 465;
private var msg:TextField;
private var canRender:Boolean;
private var objList:Vector.<OBJParser>;
private var compCount:int=0;
private var picLoader:Loader;
private var _isPressLeft:Boolean = false;
private var _isPressRight:Boolean = false;
private const PIC_URL:String ="http://ciruelo.jp/assets/piyomap.png";
private const OBJ_URL_1:String ="http://ciruelo.jp/assets/piyo_1.obj";
private const OBJ_URL_2:String ="http://ciruelo.jp/assets/piyo_2.obj";
private var polyCount:int;
public function VertexAnimation() {
Wonderfl.capture_delay( 10 );
var stats:Stats = new Stats();
stage.addChild(stats);
msg = createTF();
addChild(msg);
stage.scaleMode = StageScaleMode.NO_SCALE;
canRender = false;
stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, onContext );
stage.stage3Ds[0].requestContext3D();
}
private function createTF():TextField{
var msg:TextField = new TextField();
msg.multiline = true;
msg.width = 150;
msg.height = 100;
msg.border = true;
msg.x = stage.stageWidth - msg.width - 5;
msg.y = stage.stageHeight - msg.height - 5;
msg.text = "Hello";
return msg
}
private function onContext(e:Event):void{
context3D = stage.stage3Ds[0].context3D;
context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 1, true);
addEventListener(Event.ENTER_FRAME, onRender);
picLoader = new Loader();
picLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,onCompLoadPic);
picLoader.load(new URLRequest(PIC_URL));
}
private function onCompLoadPic(e:Event):void{
picLoader.scaleX = picLoader.scaleY = 0.5;
picLoader.y = stage.height - picLoader.height;
addChild(picLoader);
objList = new Vector.<OBJParser>();
try{
objList.push(new OBJParser(OBJ_URL_1,0xFFFFFF,1));
objList.push(new OBJParser(OBJ_URL_2,0xFFFFFF,1));
objList[0].compFunc = onComp;
objList[1].compFunc = onComp;
}catch(e:Error){
msg.text = "@エラー:" + e.message;
}
}
private function onComp():void{
compCount++
if(compCount!=objList.length){
return;
}
setBuffer();
setTexture();
setProgram();
constMatrix = new Matrix3D();
modelMatrix = new Matrix3D();
camera=new Camera();
camera.lookAt(new Vector3D(0,2,8), new Vector3D(0,0,0), Vector3D.Y_AXIS);
perspective = new PerspectiveMatrix3D();
perspective.perspectiveFieldOfViewRH(45*Math.PI/180, movieWidth / movieHeight, 0.5, 1000.0);
msg.text = "モデル読み込み完了\nポリゴン数:" + objList[1].rawIndexBuffer.length/3 + "\nPOS:" + objList[0].mtlPosList + "\nPOLY:" + objList[0].mtlPolyList;
this.canRender = true;
objList[0].dispose();
objList[1].dispose();
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
stage.addEventListener(KeyboardEvent.KEY_UP, onKey);
}
private function onKey(e:KeyboardEvent):void{
var flg:Boolean = e.type==KeyboardEvent.KEY_DOWN;
switch(e.keyCode){
case Keyboard.LEFT:
_isPressLeft = flg;
break;
case Keyboard.RIGHT:
_isPressRight = flg;
break;
}
}
private function setTexture():void{
var bmd:BitmapData = Bitmap(picLoader.content).bitmapData;
var texture:Texture = context3D.createTexture(bmd.width, bmd.height, Context3DTextureFormat.BGRA, false);
texture.uploadFromBitmapData(bmd);
context3D.setTextureAt(0, texture);
}
private function setProgram():void{
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler(true);
vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"mul vt0, va0.xyz, vc11.xxx\n" +
"mul vt1, va2.xyz, vc11.yyy\n" +
"add vt0, vt0, vt1\n" +
"mov vt0.w, vc11.w\n" +
"m44 vt0, vt0, vc0\n" +
"mov op, vt0\n" +
"mul vt0, va1.xyz, vc11.xxx\n" +
"mul vt1, va3.xyz, vc11.yyy\n" +
"add vt1, vt0, vt1\n" +
"mov vt1.w, vc11.w\n" +
"m44 vt1, vt1, vc4\n" +
"dp3 vt2.x vt1, vc8\n" +
"sat vt2.x, vt2.x\n" +
"mov vt2.xyzw, vt2.x\n" +
"mov v0, vt2\n" +
"m44 vt0, va0, vc4\n" +
"sub vt3, vc10, vt0\n" +
"nrm vt3.xyz, vt3\n" +
"mov vt3.w, vc11.w\n" +
"dp3 vt3.x, vt3, vt1\n" +
"sat vt3.x, vt3.x\n" +
"mov v1, vt3\n" +
"mov v2, vc12\n"
);
var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler(true);
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
"mov ft3, v0\n" +
"mov ft3.y, v1.x\n" +
"tex ft2, ft3, fs0 <2d,linear,nomip>\n" +
"mul ft2, ft2, v2\n" +
"mov oc, ft2\n"
);
program = context3D.createProgram();
program.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
context3D.setProgram(program);
}
private function setBuffer():void{
var objData:OBJParser = objList[0];
var vertices:Vector.<Number> = objData.rawVertexBuffer;
var normals:Vector.<Number> = objData.rawNormalBuffer;
var vertices2:Vector.<Number> = objList[1].rawVertexBuffer;
var normals2:Vector.<Number> = objList[1].rawNormalBuffer;
var vertexLength:int = objData.rawVertexBuffer.length/3;
var normalLength:int = objData.rawNormalBuffer.length/3;
var indexLength:int = objData.rawIndexBuffer.length;
vertexbuffer = context3D.createVertexBuffer(vertexLength, 3);
vertexbuffer.uploadFromVector(vertices, 0, vertexLength);
vertexbuffer2= context3D.createVertexBuffer(vertexLength, 3);
vertexbuffer2.uploadFromVector(vertices2, 0, vertexLength);
normalbuffer = context3D.createVertexBuffer(normalLength,3);
normalbuffer.uploadFromVector(normals, 0, normalLength);
normalbuffer2 = context3D.createVertexBuffer(normalLength,3);
normalbuffer2.uploadFromVector(normals2, 0, normalLength);
indexBuffer = context3D.createIndexBuffer(indexLength);
indexBuffer.uploadFromVector (objData.rawIndexBuffer, 0, indexLength);
polyCount = int(objData.rawIndexBuffer.length/3);
context3D.setVertexBufferAt(0, vertexbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
//context3D.setVertexBufferAt(2, vertexbuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
context3D.setVertexBufferAt(1, normalbuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// va2,va3にトランスフォーム後の頂点・法線情報をセット
context3D.setVertexBufferAt(2, vertexbuffer2, 0, Context3DVertexBufferFormat.FLOAT_3);
context3D.setVertexBufferAt(3, normalbuffer2, 0, Context3DVertexBufferFormat.FLOAT_3);
}
private function onRender(e:Event):void{
if ( !context3D ) return;
context3D.clear ( 1, 1, 1, 1 );
modelMatrix.identity()
if(_isPressLeft){
camera.rotateY(5);
}else if(_isPressRight){
camera.rotateY(-5);
}
//modelMatrix.appendRotation(getTimer()/10,Vector3D.Y_AXIS);
constMatrix.identity();
constMatrix.append(modelMatrix);
constMatrix.append(camera.matrix);
constMatrix.append(perspective);
constMatrix.appendTranslation(0,-2,0);
/* 頂点プログラムで使用する定数 */
//vc0-vc3:WVPマトリクス
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0,constMatrix, true);
//vc4-vc7:ワールド座標変換用マトリクス
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 4,modelMatrix, true);
//vc8:ライトの法線
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 8,Vector.<Number>([0.5773502691896258, 0.5773502691896258, 0.5773502691896258, 1]));
//vc10:カメラ座標
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX,10,Vector.<Number>([camera.from.x, camera.from.y, camera.from.z, camera.from.w]));
//vc11:ブレンド比率
var v:Number = (Math.cos(getTimer()/500) +1)/2;
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX,11,Vector.<Number>([v, 1-v, 1, 1]));
/* フラグメントプログラムで使用する定数 */
//なっしん
//三角ポリゴン描画
var posList:Array = this.objList[0].mtlPosList;
var polyList:Array = this.objList[0].mtlPolyList;
var colorList:Array = this.objList[0].mtlColorList;
for(var i:int=0;i<posList.length;i++){
var color:Vector3D = colorList[i];
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX,12,Vector.<Number>([color.x,color.y,color.z,1]));
context3D.drawTriangles(indexBuffer,posList[i],polyList[i]);
}
context3D.present();
}
}
}
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
/*
なんちゃってカメラ
*/
class Camera {
private var _view:Matrix3D;
private var _from:Vector3D;
private var _at:Vector3D;
private var _up:Vector3D;
private var _degree:Number = 0;
private const n:Number = Math.PI/180;
public function Camera() {
_view = new Matrix3D();
_from = new Vector3D();
_at = new Vector3D();
_up = new Vector3D();
}
public function lookAt( from:Vector3D, at:Vector3D, up:Vector3D ) : void {
_from.x=from.x;_from.y=from.y;_from.z=from.z;
_at.x=at.x;_at.y=at.y;_at.z=at.z;
_up.x=up.x;_up.y=up.y;_up.z=up.z;
var vz:Vector3D = from.subtract( at );
vz.normalize();
var vx:Vector3D = up.crossProduct( vz );
vx.normalize();
var vy:Vector3D = vz.crossProduct( vx );
vy.normalize();
var vtx:Number = vx.dotProduct( from ) * -1;
var vty:Number = vy.dotProduct( from ) * -1;
var vtz:Number = vz.dotProduct( from ) * -1;
_view.identity();
_view.rawData = Vector.<Number>([
vx.x, vy.x, vz.x, 0,
vx.y, vy.y, vz.y, 0,
vx.z, vy.z, vz.z, 0,
vtx, vty, vtz, 1,
]);
}
public function rotateY(deg:Number):void{
_degree += deg;
_from.x = Math.sin(_degree*n)*8;
_from.z = Math.cos(_degree*n)*8;
lookAt( _from, _at, _up )
}
public function get matrix():Matrix3D {
return _view;
}
public function get from():Vector3D{
return _from;
}
}
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.SecurityErrorEvent;
import flash.events.IOErrorEvent;
import flash.system.Security;
/*
とりあえず、.objを読み込んでみて、頂点・法線・インデックス・UVをパースするクラス
*/
class OBJParser{
private const LINE_FEED:String = String.fromCharCode(10);
private const SPACE:String = String.fromCharCode(32);
private const SLASH:String = "/";
private const VERTEX:String = "v";
//private const UV:String = "vt";
private const NORMAL:String = "vn";
private const INDEX_DATA:String = "f";
private const MATERIAL:String = "usemtl";
private var _loader:URLLoader;
private var _color:uint;
private var _vertexTemp:Array;
private var _normalTemp:Array;
private var _indexTemp:Array;
public var rawIndexBuffer:Vector.<uint> = new Vector.<uint>();
public var rawVertexBuffer:Vector.<Number> = new Vector.<Number>();
public var rawNormalBuffer:Vector.<Number> = new Vector.<Number>();
private var _scale:Number;
public var mtlPosList:Array = new Array();
public var mtlPolyList:Array = new Array();
public var mtlColorList:Array = [new Vector3D(0.80000,0.80000,0.80000),new Vector3D(0.58039,0.73725,0.42039),new Vector3D(0.76178,0.67568,0.30832),new Vector3D(0.2,0.2,0.2),new Vector3D(0.80000,0.48000,0.29200)] //new Array();
private var _indexCount:int=0;
private var _polyCount:int=0;
public var compFunc:Function;
public function OBJParser(path:String, defaultColor:uint = 0xFFFFFF ,scale:Number = 0.01){
Security.loadPolicyFile("http://ciruelo.jp/assets/crossdomain.xml");
_color = defaultColor;
_scale = scale;
var req:URLRequest = new URLRequest(path);
_loader = new URLLoader(req);
_loader.addEventListener(Event.COMPLETE, _onLoadComp);
_loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,_onErr);
}
private function _onErr(e:SecurityErrorEvent):void{
var err:Error = e as Error;
throw err;
}
private function _onLoadComp(e:Event):void{
// 1行ごと取り出して読み込む
var data:String = String(_loader.data);
this._indexTemp = [];
this._vertexTemp = [];
this._normalTemp = [];
var lines:Array = data.split(LINE_FEED);
var loop:int = lines.length;
for (var i:int = 0; i < loop; ++i){
parseLine(lines[i]);
}
mtlPolyList.push(_polyCount);
mtlPolyList.shift();
rawIndexBuffer = new Vector.<uint>();
rawVertexBuffer = new Vector.<Number>();
rawNormalBuffer = new Vector.<Number>();
parseBuffer();
compFunc();
}
private function parseBuffer():void{
var vIndex:int = 0;
var nIndex:int = 0;
var v:int=3;
for(var i:int=0;i<this._indexTemp.length;i++){
var poly1:Array = _indexTemp[i][0];
var poly2:Array = _indexTemp[i][1];
var poly3:Array = _indexTemp[i][2];
rawVertexBuffer.push(
_vertexTemp[poly1[0]*v], _vertexTemp[poly1[0]*v+1], _vertexTemp[poly1[0]*v+2],
_vertexTemp[poly2[0]*v], _vertexTemp[poly2[0]*v+1], _vertexTemp[poly2[0]*v+2],
_vertexTemp[poly3[0]*v], _vertexTemp[poly3[0]*v+1], _vertexTemp[poly3[0]*v+2]
);
rawNormalBuffer.push(
_normalTemp[poly1[2]*v], _normalTemp[poly1[2]*v+1], _normalTemp[poly1[2]*v+2],
_normalTemp[poly2[2]*v], _normalTemp[poly2[2]*v+1], _normalTemp[poly2[2]*v+2],
_normalTemp[poly3[2]*v], _normalTemp[poly3[2]*v+1], _normalTemp[poly3[2]*v+2]
);
rawIndexBuffer.push(i*3, i*3+1, i*3+2);
}
}
private function parseLine(line:String):void{
var words:Array = line.split(SPACE);
if (words.length > 0){
var data:Array = words.slice(1);
}else{
return;
}
var firstWord:String = words[0];
switch (firstWord){
case VERTEX :
parseVertex(data);
break;
case NORMAL :
parseNormal(data);
break;
case INDEX_DATA :
parseIndex(data);
break;
case MATERIAL :
parseMaterial(data);
break;
}
}
private function parseMaterial(data:Array):void{
mtlPosList.push(_indexCount);
mtlPolyList.push(_polyCount);
_polyCount=0;
}
private function parseVertex(data:Array):void{
var loop:int = 3;
for (var i:uint = 0; i < loop; i++){
var element:String = data[i];
_vertexTemp.push(Number(element)*_scale);
}
}
private function parseNormal(data:Array):void{
var loop:int = 3;
for (var i:uint = 0; i < loop; i++){
var element:String = data[i];
_normalTemp.push(Number(element));
}
}
private function parseIndex(data:Array):void{
var poly:Array = [];
var loop:int = 3;
for(var i:int=0;i<loop;i++){
var v:Array = [];
var element:Array = data[i].split(SLASH);
for(var j:int=0;j<loop;j++){
v.push(Number(element[j])-1)
}
poly.push(v);
}
_indexTemp.push(poly);
_indexCount+=3;
_polyCount++;
}
public function dispose():void{
_vertexTemp = _normalTemp = _indexTemp = null;
rawVertexBuffer = rawNormalBuffer= null;
rawIndexBuffer = null;
}
}