package sk.yoz.displacing { import com.adobe.utils.AGALMiniAssembler; import com.adobe.utils.PerspectiveMatrix3D; import flash.display.BitmapData; import flash.display.Stage3D; 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.Matrix3D; import flash.geom.Rectangle; import flash.geom.Vector3D; public class SimpleDisplacerMH { public var rotX:Number = 0; public var rotY:Number = 0; public var rotationXMax:Number; public var rotationYMax:Number; private var stage3D:Stage3D; private var viewportWidth:uint; private var viewportHeight:uint; private var image:BitmapData; private var map:BitmapData; private var depth:Number; private var segmentsW:uint; private var segmentsH:uint; private var indices:Vector.; private var textureSize:uint; private var bitmapData:BitmapData; private var indexBuffer:IndexBuffer3D; private var perspective:PerspectiveMatrix3D = new PerspectiveMatrix3D; public function SimpleDisplacerMH(stage3D:Stage3D, viewportWidth:uint, viewportHeight:uint, image:BitmapData, map:BitmapData, rotationXMax:Number=10, rotationYMax:Number=10, depth:Number = 100, segmentsW:uint = 15, segmentsH:uint = 15) { this.stage3D = stage3D; this.viewportWidth = viewportWidth; this.viewportHeight = viewportHeight; this.image = image; this.textureSize = countTextureSize(image.width, image.height); this.map = map; this.rotationXMax = rotationXMax; this.rotationYMax = rotationYMax; this.segmentsW = segmentsW; this.segmentsH = segmentsH; this.depth = depth; this.bitmapData = createBitmapData(); stage3D.viewPort = new Rectangle(0, 0, viewportWidth, viewportHeight); stage3D.addEventListener(Event.CONTEXT3D_CREATE, onCreate); stage3D.requestContext3D(); } private function get context():Context3D { return stage3D.context3D; } private function countTextureSize(width:uint, height:uint):uint { var size:uint = 1; var max:uint = width > height ? width : height; while(size < max) size *= 2; return size; } private function createBitmapData():BitmapData { var bitmapData:BitmapData = new BitmapData(textureSize, textureSize); bitmapData.draw(image); return bitmapData; } private function createVertices():Vector. { var vertices:Vector. = new Vector.( (segmentsW + 1) * (segmentsH + 1) * 5, true); var color:uint, x:uint, y:uint, xs:Number, ys:Number, xyi:uint; var sizeW:Number = (image.width - 1) / segmentsW; var sizeH:Number = (image.height - 1) / segmentsH; var w:Number = image.width / 2; var h:Number = image.height / 2; var d:Number = 1 / 0xff * depth; var i:uint = 0; var scaleW:Number = image.width / textureSize / segmentsW; var scaleH:Number = image.height / textureSize / segmentsH; var colors:Vector. = map.getVector( new Rectangle(0, 0, map.width, map.height)); for(y = 0; y <= segmentsH; y++) for(x = 0; x <= segmentsW; x++) { xs = x * sizeW; ys = y * sizeH; xyi = xs + int(ys) * map.width; color = colors[xyi]; vertices[i] = xs - w; i++; // x vertices[i] = ys - h; i++; // y vertices[i] = (color >> 16) * d; i++; // z vertices[i] = x * scaleW; i++; // u vertices[i] = y * scaleH; i++; // v } return vertices; } private function createIndices():Vector. { var x:uint, y:uint, yvW:uint, y1vW:uint; var verticesW:uint = segmentsW + 1; var indices:Vector. = new Vector.( segmentsW * segmentsH * 6, true); var i:uint = 0; for(x = 0; x < segmentsW; x++) for(y = 0; y < segmentsH; y++) { yvW = y * verticesW; y1vW = (y + 1) * verticesW; indices[i] = x + yvW; i++; indices[i] = x + yvW + 1; i++; indices[i] = x + y1vW; i++; indices[i] = x + yvW + 1; i++; indices[i] = x + y1vW + 1; i++; indices[i] = x + y1vW; i++; } return indices; } private function createProgram():Program3D { var vertexAssembler:AGALMiniAssembler = new AGALMiniAssembler; vertexAssembler.assemble(Context3DProgramType.VERTEX, "m44 op, va0, vc0 \n" + // pos to clipspace "mov v0, va1"); // copy uv var fragmentAssembler:AGALMiniAssembler = new AGALMiniAssembler; fragmentAssembler.assemble(Context3DProgramType.FRAGMENT, "tex ft1, v0, fs0 <2d,linear,nomip> \n" + // sample texture "mov oc, ft1"); // copy sampled texture to output var program:Program3D = context.createProgram(); program.upload(vertexAssembler.agalcode, fragmentAssembler.agalcode); return program; } public function onCreate(event:Event):void { var vertices:Vector. = createVertices(); var vertexBuffer:VertexBuffer3D = context.createVertexBuffer( vertices.length / 5, 5); vertexBuffer.uploadFromVector(vertices, 0, vertices.length / 5); indices = createIndices(); indexBuffer = context.createIndexBuffer(indices.length); indexBuffer.uploadFromVector(indices, 0, indices.length); var texture:Texture = context.createTexture( textureSize, textureSize, Context3DTextureFormat.BGRA, false); texture.uploadFromBitmapData(bitmapData); var program:Program3D = createProgram(); var float3:String = Context3DVertexBufferFormat.FLOAT_3; var float2:String = Context3DVertexBufferFormat.FLOAT_2; context.configureBackBuffer(viewportWidth, viewportHeight, 4, true); context.setVertexBufferAt(0, vertexBuffer, 0, float3); // x, y, z context.setVertexBufferAt(1, vertexBuffer, 3, float2); // u, v context.setTextureAt(0, texture); context.setProgram(program); perspective.perspectiveFieldOfViewLH(45 * Math.PI / 180, viewportWidth / viewportHeight, 0.1, 10000); } public function render():void { var z:Number = Math.max(image.width, image.height) * 1.2; var programType:String = Context3DProgramType.VERTEX; var matrix:Matrix3D = new Matrix3D; matrix.appendRotation(-rotY, Vector3D.Y_AXIS); matrix.appendRotation(rotX + 180, Vector3D.X_AXIS); matrix.appendTranslation(0, 0, z); matrix.append(perspective); context.clear(1, 1, 1, 1); context.setProgramConstantsFromMatrix(programType, 0, matrix, true); context.drawTriangles(indexBuffer, 0, indices.length / 3); context.present(); } public function dispose():void { stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onCreate); stage3D.viewPort = null; context && context.dispose(); map.dispose(); image.dispose(); bitmapData.dispose(); indices = null; } } }