vanCopper bio photo

vanCopper

Try Your Best.

Email Github

原创博文,转载请声明

先扯点题外话,前日又和朋友讨论加班的事情。我说我不知道别人是怎样的,我连续编码4-5个小时就会精疲力尽,如果每天我都保证4-5个小时的编码时间,那么基本上我都是可以准时下班的。我跟同事们也是这么说的,从来都没觉得工作时间的长短可以说明什么问题。如果只是以工作时间的长短来评判一个员工的好坏是不公平且不科学的。不是吐槽哦~~

OBJ是文件,先来解释下OBJ文件。随便找一个OBJ文件,用文本查看:

# Max2Obj Version 4.0 Mar 10th, 2001
#
# object (null) to come ...
#
v -0.257 0.191 0.423
v -0.115 0.29 -0.068
v -0.237 0 -0.074
v -0.237 0.071 0.646
...
# 82 vertices
vt 0.623 0.227 0
vt 0.615 0.53 0
vt 0.895 0.555 0
vt 0.825 0.246 0
vt 0.647 0.847 0
...
# 39 texture vertices
f 1/1 2/2 3/3
f 3/3 4/4 1/1
f 2/2 5/5 6/6
f 6/6 3/3 2/2
...
# 142 faces

v:是模型的顶点信息 vt:是贴图的UV坐标 f:是定义面的顶点索引和顶点对应的UV坐标索引 清楚了OBJ文件里各种数据所代表的信息,接下来就是如何把数据传递给显卡渲染。 那么我们需要一个解析OBJ文件的类(代码比较简单,注释就省了)

package com.parser
{
    import flash.utils.ByteArray;

    /**
     * OBJ文件解析器
     * @author vanCopper
     */
    public class OBJParser
    {
        private var _lines:Array;
        private const LINE_FEED:String = String.fromCharCode(10);
        private const SPACE:String = String.fromCharCode(32);
        private var _scale:Number;
        public function OBJParser(objfile:ByteArray,scale:Number = 1)
        {
            _scale = scale;
            if(objfile)
            {
                var lineStr:String = parserToStr(objfile);
                _lines = lineStr.split(LINE_FEED);
                var loop:uint = _lines.length;
                for(var i:uint = 0; i < loop; i++)
                {
                    parseLine(_lines[i]);
                }
            }   
        }

        private const VERTEX:String = 'v';
        private const UV:String = 'vt';
        private const INDEX_DATA:String = 'f';
        private function parseLine(lineStr:String):void
        {
            var data:Array = lineStr.split(SPACE);
            if(!data.length)return;
            var key:String = data[0];
            var parseData:Array = data.slice(1);
            switch(key)
            {
                case VERTEX:
                    parseVertex(parseData);
                    break;
                case UV:
                    parseUV(parseData);
                    break;
                case INDEX_DATA:
                    parseIndexData(parseData);
                    break;
            }
        }

        private var _vertices:Vector.<Number> = new Vector.<Number>();
        private function parseVertex(data:Array):void
        {
            if(data[0] == '' || data[0] == " ")
            {
                data = data.slice(1);
            }
            var loop:uint = data.length;
            for(var i:uint = 0; i < loop; i++)
            {
                var value:Number = Number(data[i]);
                _vertices.push(value*_scale);
            }
        }

        private var _uvs:Vector.<Number> = new Vector.<Number>();
        private function parseUV(data:Array):void
        {
            if(data[0] == '' || data[0] == " ")
            {
                data = data.slice(1);
            }
            var loop:uint = 2;
            for(var i:uint = 0; i < loop; i++)
            {
                var value:Number = Number(data[i]);
                _uvs.push(value*_scale);
            }
        }

        private const SLASH:String = "/";
        private var _indexData:Vector.<uint> = new Vector.<uint>();
        private var _vertexsData:Vector.<Number> = new Vector.<Number>();
        private var _uvData:Vector.<Number> = new Vector.<Number>();
        private var _faceIndex:uint;
        private function parseIndexData(data:Array):void
        {
            var index:uint = 0;
            while((data[index] == '') || (data[index] == ' '))index++;
            var loop:uint = index+3;

            var vertexIndex:int;
            var uvIndex:int;
            var normalIndex:int;
            for(var i:uint = index; i < loop; i++)
            {
                var triplet:String = data[i];
                var subdata:Array = triplet.split(SLASH);
                vertexIndex = int(subdata[0]) - 1;
                uvIndex     = int(subdata[1]) - 1;

                if(vertexIndex < 0) vertexIndex = 0;
                if(uvIndex < 0) uvIndex = 0;

                index = 3*vertexIndex;
                _vertexsData.push(_vertices[index + 0], 
                _vertices[index + 1], _vertices[index + 2]);

                index = 2*uvIndex;
                _uvData.push(1-_uvs[index+0],1-_uvs[index+1]);
            }
            _indexData.push(_faceIndex+0,_faceIndex+1,_faceIndex+2);
            _faceIndex += 3;
        }

        private function parserToStr(objFileByteArray:ByteArray):String
        {
            return objFileByteArray.readUTFBytes(objFileByteArray.bytesAvailable);
        }

        /**
         * 顶点数据 
         * @return 
         * 
         */     
        public function get vertexsData():Vector.<Number>
        {
            return _vertexsData;
        }

        /**
         * UV数据 
         * @return 
         * 
         */     
        public function get uvData():Vector.<Number>
        {
            return _uvData;
        }

        /**
         * 索引数据 
         * @return 
         * 
         */     
        public function get indexData():Vector.<uint>
        {
            return _indexData;
        }

    }
}

有了顶点信息,uv信息和面的索引信息接下来就可以把这货渲染出来了。

package
{
    import com.adobe.utils.AGALMiniAssembler;
    import com.adobe.utils.PerspectiveMatrix3D;
    import com.parser.OBJParser;

    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.Stage3D;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DTextureFormat;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.IndexBuffer3D;
    import flash.display3D.Program3D;
    import flash.display3D.VertexBuffer3D;
    import flash.display3D.textures.Texture;
    import flash.events.Event;
    import flash.geom.Matrix;
    import flash.geom.Matrix3D;
    import flash.geom.Vector3D;
    import flash.utils.ByteArray;

    [SWF(width='800',height='600',backgroundColor='0x333333',frameRate="60")]
    /**
     * 
     * @author vanCopper
     */
    public class GameTest extends Sprite
    {
        [Embed (source = "art/spaceship.obj", 
        mimeType = "application/octet-stream")] 
        private var objData:Class;

        [Embed (source = "art/spaceship_texture.jpg")] 
        private var TextureBitmap:Class;
        private var textureData:Bitmap = new TextureBitmap();

        private var _stage3D:Stage3D;
        private var _context3D:Context3D;
        private const sw:uint = 700;
        private const sh:uint = 500;

        private var _objParser:OBJParser;
        //顶点缓冲 存储顶点信息
        private var _vertexBuffer:VertexBuffer3D;
        //顶点缓冲 存储UV信息
        private var _uvBuffer:VertexBuffer3D;
        //顶点索引
        private var _indexBuffer:IndexBuffer3D;
        private var _texture:Texture;
        private var _textureSize:uint = 512;
        private var _projectionmatrix:PerspectiveMatrix3D;
        private var _viewmatrix:Matrix3D;
        private var _modelmatrix:Matrix3D = new Matrix3D();
        private var _modelViewProjection:Matrix3D = new Matrix3D();

        private var _vertexShaderAssembler:AGALMiniAssembler;
        private var _fragmentAssembler:AGALMiniAssembler;
        private var _program:Program3D;

        public function GameTest()
        {
            if(this.stage)
            {
                init();
            }else
            {
                addEventListener(Event.ADDED_TO_STAGE,init);
            }
        }

        private function init(e:Event = null):void
        {
            if(hasEventListener(Event.ADDED_TO_STAGE))removeEventListener(Event.ADDED_TO_STAGE,init);
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
            this.stage.align = StageAlign.TOP_LEFT;
            initStage3D();
        }

        private function initStage3D():void
        {
            _stage3D = this.stage.stage3Ds[0];
            if(_stage3D)
            {
                _stage3D.addEventListener(Event.CONTEXT3D_CREATE,onContext3DCreate);
                _stage3D.requestContext3D();
            }
        }

        private function onContext3DCreate(e:Event):void
        {
            _context3D = _stage3D.context3D;
            if(_context3D == null)
            {
                throw new Error("无法创建Context3D");
                return;
            }

            _stage3D.x = (this.stage.stageWidth - sw)/2;
            _stage3D.y = (this.stage.stageHeight - sh)/2;

            _context3D.configureBackBuffer(sw,sh,1);
            _context3D.clear(205,205,205);
            _context3D.enableErrorChecking = true;
            initData();
            initShader();
            this.stage.addEventListener(Event.ENTER_FRAME,onEnterFrame);
        }

        private function initData():void
        {
            var objdata:ByteArray = new objData() as ByteArray;
            _objParser = new OBJParser(objdata);

            var vertexCont:uint = _objParser.vertexsData.length/3;
            _vertexBuffer = _context3D.createVertexBuffer(vertexCont,3);
            _vertexBuffer.uploadFromVector(_objParser.vertexsData,0,vertexCont);

            var uvCont:uint = _objParser.uvData.length/2;
            _uvBuffer = _context3D.createVertexBuffer(uvCont,2);
            _uvBuffer.uploadFromVector(_objParser.uvData,0,uvCont);

            _context3D.setVertexBufferAt(0,_vertexBuffer,0,Context3DVertexBufferFormat.FLOAT_3);
            _context3D.setVertexBufferAt(1,_uvBuffer,0,Context3DVertexBufferFormat.FLOAT_2);

            var indexData:Vector.<uint> = _objParser.indexData;
            _indexBuffer = _context3D.createIndexBuffer(indexData.length);
            _indexBuffer.uploadFromVector(indexData,0,indexData.length);

            _texture = _context3D.createTexture(_textureSize,_textureSize,Context3DTextureFormat.BGRA,false);
            uploadTextureWithMipmaps(_texture,textureData.bitmapData);

            _projectionmatrix = new PerspectiveMatrix3D();
            _projectionmatrix.identity();
            // 45 degrees FOV, 640/480 aspect ratio, 0.1=near, 100=far
            _projectionmatrix.perspectiveFieldOfViewRH(
                45.0, sw / sh, 0.01, 100.0);

            _viewmatrix = new Matrix3D();
            // camera Matrix3D
            _viewmatrix.identity();
            // 移动镜头到(0,0,0)
            _viewmatrix.appendTranslation(0,0,0);
        }

        private function initShader():void
        {
            _vertexShaderAssembler = new AGALMiniAssembler();
            _vertexShaderAssembler.assemble(Context3DProgramType.VERTEX,
                "m44 op, va0, vc0\n" +
                "mov v0, va0\n" +
                "mov v1, va1\n");

            _fragmentAssembler= new AGALMiniAssembler();
            _fragmentAssembler..assemble
                ( 
                    Context3DProgramType.FRAGMENT,  
                    "tex ft0, v1, fs0 <2d,linear,repeat,miplinear>\n"+
                    "mov oc, ft0\n"
                );

            _program = _context3D.createProgram();
            _program.upload(_vertexShaderAssembler.agalcode,_fragmentAssembler.agalcode);
            _context3D.setTextureAt(0,_texture);            
            _context3D.setProgram(_program);
        }

        private var _t:Number = 0;
        private function onEnterFrame(e:Event):void
        {
            _context3D.clear(0,0,0,.7); 
            _t += 2.0;
            _modelmatrix.identity();
            //旋转模型
            _modelmatrix.appendRotation(_t*1.0, Vector3D.Y_AXIS);
            _modelmatrix.appendRotation(_t*-0.2, Vector3D.X_AXIS);
            _modelmatrix.appendRotation(_t*0.3, Vector3D.Y_AXIS);
            _modelmatrix.appendTranslation(-0.4, 0, -5);

            _modelViewProjection.identity();
            _modelViewProjection.append(_modelmatrix);
            _modelViewProjection.append(_viewmatrix);
            _modelViewProjection.append(_projectionmatrix);

            _context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _modelViewProjection, true );
            //绘制模型
            _context3D.drawTriangles(_indexBuffer, 0, _objParser.indexData.length/3);
            //呈现至屏幕
            _context3D.present();
        }

        /**
         * Mipmap 
         * @param dest
         * @param src
         * 
         */     
        private function uploadTextureWithMipmaps(dest:Texture, src:BitmapData):void
        {
            var ws:int = src.width;
            var hs:int = src.height;
            var level:int = 0;
            var tmp:BitmapData;
            var transform:Matrix = new Matrix();

            tmp = new BitmapData(src.width, src.height, true, 0);

            while ( ws >= 1 && hs >= 1 )
            { 
                tmp.draw(src, transform, null, null, null, true); 
                dest.uploadFromBitmapData(tmp, level);
                transform.scale(0.5, 0.5);
                level++;
                ws >>= 1;
                hs >>= 1;
                if (hs && ws) 
                {
                    tmp.dispose();
                    tmp = new BitmapData(ws, hs, true, 0x00000000);
                }
            }
            tmp.dispose();
        }

    }
}

需要源码的朋友可邮件索取 492214810@qq.com


扫码分享该文