package away3d.materials { import away3d.containers.*; import away3d.arcane; import away3d.core.base.*; import away3d.core.draw.*; import away3d.core.render.*; import away3d.core.utils.*; import away3d.events.*; import flash.display.*; import flash.events.*; import flash.geom.*; import flash.utils.*; use namespace arcane; /** * Dispatched when the bitmapData used for the material texture is resized. * * @eventType away3d.events.AnimationEvent */ [Event(name="materialResize",type="away3d.events.MaterialEvent")] /** * Basic bitmap material */ public class BitmapMaterial extends EventDispatcher implements ITriangleMaterial, IUVMaterial, ILayerMaterial { /** @private */ arcane var _texturemapping:Matrix; /** @private */ arcane var _bitmap:BitmapData; /** @private */ arcane var _faceDirty:Boolean; /** @private */ arcane var _renderBitmap:BitmapData; /** @private */ arcane var _bitmapDirty:Boolean; /** @private */ arcane var _colorTransform:ColorTransform; /** @private */ arcane var _colorTransformDirty:Boolean; /** @private */ arcane var _blendMode:String; /** @private */ arcane var _blendModeDirty:Boolean; /** @private */ arcane var _color:uint = 0xFFFFFF; /** @private */ arcane var _red:Number = 1; /** @private */ arcane var _green:Number = 1; /** @private */ arcane var _blue:Number = 1; /** @private */ arcane var _alpha:Number = 1; /** @private */ arcane var _faceDictionary:Dictionary = new Dictionary(true); /** @private */ arcane var _zeroPoint:Point = new Point(0, 0); /** @private */ arcane var _faceVO:FaceVO; /** @private */ arcane var _mapping:Matrix; /** @private */ arcane var _s:Shape = new Shape(); /** @private */ arcane var _graphics:Graphics; /** @private */ arcane var _bitmapRect:Rectangle; /** @private */ arcane var _sourceVO:FaceVO; /** @private */ arcane var _session:AbstractRenderSession; /** @private */ arcane function notifyMaterialUpdate():void { if (!hasEventListener(MaterialEvent.MATERIAL_UPDATED)) return; if (_materialupdated == null) _materialupdated = new MaterialEvent(MaterialEvent.MATERIAL_UPDATED, this); dispatchEvent(_materialupdated); } /** @private */ arcane function clearShapeDictionary():void { for each (_shape in _shapeDictionary) _shape.graphics.clear(); } /** @private */ arcane function clearFaceDictionary():void { _faceDirty = false; notifyMaterialUpdate(); for each (_faceVO in _faceDictionary) { if (!_faceVO.cleared) _faceVO.clear(); _faceVO.invalidated = true; } } /** @private */ arcane function renderSource(source:Object3D, containerRect:Rectangle, mapping:Matrix):void { //check to see if sourceDictionary exists if (!(_sourceVO = _faceDictionary[source])) _sourceVO = _faceDictionary[source] = new FaceVO(); _sourceVO.resize(containerRect.width, containerRect.height); //check to see if rendering can be skipped if (_sourceVO.invalidated) { //calulate scale matrix mapping.scale(containerRect.width/width, containerRect.height/height); //reset booleans _sourceVO.invalidated = false; _sourceVO.cleared = false; _sourceVO.updated = true; //draw the bitmap if (mapping.a == 1 && mapping.d == 1 && mapping.b == 0 && mapping.c == 0 && mapping.tx == 0 && mapping.ty == 0) { //speedier version for non-transformed bitmap _sourceVO.bitmap.copyPixels(_bitmap, containerRect, _zeroPoint); }else { _graphics = _s.graphics; _graphics.clear(); _graphics.beginBitmapFill(_bitmap, mapping, repeat, smooth); _graphics.drawRect(0, 0, containerRect.width, containerRect.height); _graphics.endFill(); _sourceVO.bitmap.draw(_s, null, _colorTransform, _blendMode, _sourceVO.bitmap.rect); } } } private var _view:View3D; private var _smooth:Boolean; private var _debug:Boolean; private var _repeat:Boolean; private var _precision:Number; private var _shapeDictionary:Dictionary = new Dictionary(true); private var _shape:Shape; private var _materialupdated:MaterialEvent; private var focus:Number; private var map:Matrix = new Matrix(); private var triangle:DrawTriangle = new DrawTriangle(); private var svArray:Array = new Array(); private var x:Number; private var y:Number; private var faz:Number; private var fbz:Number; private var fcz:Number; private var mabz:Number; private var mbcz:Number; private var mcaz:Number; private var mabx:Number; private var maby:Number; private var mbcx:Number; private var mbcy:Number; private var mcax:Number; private var mcay:Number; private var dabx:Number; private var daby:Number; private var dbcx:Number; private var dbcy:Number; private var dcax:Number; private var dcay:Number; private var dsab:Number; private var dsbc:Number; private var dsca:Number; private var dmax:Number; private var ax:Number; private var ay:Number; private var az:Number; private var bx:Number; private var by:Number; private var bz:Number; private var cx:Number; private var cy:Number; private var cz:Number; private function createVertexArray():void { var index:Number = 100; while (index--) { svArray.push(new ScreenVertex()); } } private function renderRec(a:ScreenVertex, b:ScreenVertex, c:ScreenVertex, index:Number):void { ax = a.x; ay = a.y; az = a.z; bx = b.x; by = b.y; bz = b.z; cx = c.x; cy = c.y; cz = c.z; if (!_view.clip.rect(Math.min(ax, Math.min(bx, cx)), Math.min(ay, Math.min(by, cy)), Math.max(ax, Math.max(bx, cx)), Math.max(ay, Math.max(by, cy)))) return; if ((az <= 0) && (bz <= 0) && (cz <= 0)) return; if (index >= 100 || (focus == Infinity) || (Math.max(Math.max(ax, bx), cx) - Math.min(Math.min(ax, bx), cx) < 10) || (Math.max(Math.max(ay, by), cy) - Math.min(Math.min(ay, by), cy) < 10)) { _session.renderTriangleBitmap(_renderBitmap, map, a, b, c, smooth, repeat, _graphics); if (debug) _session.renderTriangleLine(1, 0x00FF00, 1, a, b, c); return; } faz = focus + az; fbz = focus + bz; fcz = focus + cz; mabz = 2 / (faz + fbz); mbcz = 2 / (fbz + fcz); mcaz = 2 / (fcz + faz); dabx = ax + bx - (mabx = (ax*faz + bx*fbz)*mabz); daby = ay + by - (maby = (ay*faz + by*fbz)*mabz); dbcx = bx + cx - (mbcx = (bx*fbz + cx*fcz)*mbcz); dbcy = by + cy - (mbcy = (by*fbz + cy*fcz)*mbcz); dcax = cx + ax - (mcax = (cx*fcz + ax*faz)*mcaz); dcay = cy + ay - (mcay = (cy*fcz + ay*faz)*mcaz); dsab = (dabx*dabx + daby*daby); dsbc = (dbcx*dbcx + dbcy*dbcy); dsca = (dcax*dcax + dcay*dcay); if ((dsab <= precision) && (dsca <= precision) && (dsbc <= precision)) { _session.renderTriangleBitmap(_renderBitmap, map, a, b, c, smooth, repeat, _graphics); if (debug) _session.renderTriangleLine(1, 0x00FF00, 1, a, b, c); return; } var map_a:Number = map.a; var map_b:Number = map.b; var map_c:Number = map.c; var map_d:Number = map.d; var map_tx:Number = map.tx; var map_ty:Number = map.ty; var sv1:ScreenVertex; var sv2:ScreenVertex; var sv3:ScreenVertex = svArray[index++]; sv3.x = mbcx/2; sv3.y = mbcy/2; sv3.z = (bz+cz)/2; if ((dsab > precision) && (dsca > precision) && (dsbc > precision)) { sv1 = svArray[index++]; sv1.x = mabx/2; sv1.y = maby/2; sv1.z = (az+bz)/2; sv2 = svArray[index++]; sv2.x = mcax/2; sv2.y = mcay/2; sv2.z = (cz+az)/2; map.a = map_a*=2; map.b = map_b*=2; map.c = map_c*=2; map.d = map_d*=2; map.tx = map_tx*=2; map.ty = map_ty*=2; renderRec(a, sv1, sv2, index); map.a = map_a; map.b = map_b; map.c = map_c; map.d = map_d; map.tx = map_tx-1; map.ty = map_ty; renderRec(sv1, b, sv3, index); map.a = map_a; map.b = map_b; map.c = map_c; map.d = map_d; map.tx = map_tx; map.ty = map_ty-1; renderRec(sv2, sv3, c, index); map.a = -map_a; map.b = -map_b; map.c = -map_c; map.d = -map_d; map.tx = 1-map_tx; map.ty = 1-map_ty; renderRec(sv3, sv2, sv1, index); return; } dmax = Math.max(dsab, Math.max(dsca, dsbc)); if (dsab == dmax) { sv1 = svArray[index++]; sv1.x = mabx/2; sv1.y = maby/2; sv1.z = (az+bz)/2; map.a = map_a*=2; map.c = map_c*=2; map.tx = map_tx*=2; renderRec(a, sv1, c, index); map.a = map_a + map_b; map.b = map_b; map.c = map_c + map_d; map.d = map_d; map.tx = map_tx + map_ty - 1; map.ty = map_ty; renderRec(sv1, b, c, index); return; } if (dsca == dmax) { sv2 = svArray[index++]; sv2.x = mcax/2; sv2.y = mcay/2; sv2.z = (cz+az)/2; map.b = map_b*=2; map.d = map_d*=2; map.ty = map_ty*=2; renderRec(a, b, sv2, index); map.a = map_a; map.b = map_b + map_a; map.c = map_c; map.d = map_d + map_c; map.tx = map_tx; map.ty = map_ty + map_tx - 1; renderRec(sv2, b, c, index); return; } map.a = map_a - map_b; map.b = map_b*2; map.c = map_c - map_d; map.d = map_d*2; map.tx = map_tx - map_ty; map.ty = map_ty*2; renderRec(a, b, sv3, index); map.a = map_a*2; map.b = map_b - map_a; map.c = map_c*2; map.d = map_d - map_c; map.tx = map_tx*2; map.ty = map_ty - map_tx; renderRec(a, sv3, c, index); } /** * Instance of the Init object used to hold and parse default property values * specified by the initialiser object in the 3d object constructor. */ protected var ini:Init; /** * Updates the colortransform object applied to the texture from the color and alpha properties. * * @see color * @see alpha */ protected function updateColorTransform():void { _colorTransformDirty = false; _bitmapDirty = true; _faceDirty = true; if (_alpha == 1 && _color == 0xFFFFFF) { _renderBitmap = _bitmap; _colorTransform = null; return; } else if (!_colorTransform) _colorTransform = new ColorTransform(); _colorTransform.redMultiplier = _red; _colorTransform.greenMultiplier = _green; _colorTransform.blueMultiplier = _blue; _colorTransform.alphaMultiplier = _alpha; if (_alpha == 0) { _renderBitmap = null; return; } } /** * Updates the texture bitmapData with the colortransform determined from the color and alpha properties. * * @see color * @see alpha * @see setColorTransform() */ protected function updateRenderBitmap():void { _bitmapDirty = false; if (_colorTransform) { if (!_bitmap.transparent && _alpha != 1) { _renderBitmap = new BitmapData(_bitmap.width, _bitmap.height, true); _renderBitmap.draw(_bitmap); } else { _renderBitmap = _bitmap.clone(); } _renderBitmap.colorTransform(_renderBitmap.rect, _colorTransform); } else { _renderBitmap = _bitmap.clone(); } _faceDirty = true; } /** * Calculates the mapping matrix required to draw the triangle texture to screen. * * @param tri The data object holding all information about the triangle to be drawn. * @return The required matrix object. */ protected function getMapping(tri:DrawTriangle):Matrix { if (tri.generated) { _texturemapping = tri.transformUV(this).clone(); _texturemapping.invert(); return _texturemapping; } _faceVO = getFaceVO(tri.face, tri.source, tri.view); if (_faceVO.texturemapping) return _faceVO.texturemapping; _texturemapping = tri.transformUV(this).clone(); _texturemapping.invert(); return _faceVO.texturemapping = _texturemapping; } /** * Determines if texture bitmap is smoothed (bilinearly filtered) when drawn to screen. */ public function get smooth():Boolean { return _smooth; } public function set smooth(val:Boolean):void { if (_smooth == val) return _smooth = val; _faceDirty = true; } /** * Toggles debug mode: textured triangles are drawn with white outlines, precision correction triangles are drawn with blue outlines. */ public function get debug():Boolean { return _debug; } public function set debug(val:Boolean):void { if (_debug == val) return _debug = val; _faceDirty = true; } /** * Determines if texture bitmap will tile in uv-space */ public function get repeat():Boolean { return _repeat; } public function set repeat(val:Boolean):void { if (_repeat == val) return _repeat = val; _faceDirty = true; } /** * Corrects distortion caused by the affine transformation (non-perpective) of textures. * The number refers to the pixel correction value - ie. a value of 2 means a distorion correction to within 2 pixels of the correct perspective distortion. * 0 performs no precision. */ public function get precision():Number { return _precision; } public function set precision(val:Number):void { _precision = val*val*1.4; _faceDirty = true; } /** * @inheritDoc */ public function get width():Number { return _bitmap.width; } /** * @inheritDoc */ public function get height():Number { return _bitmap.height; } /** * @inheritDoc */ public function get bitmap():BitmapData { return _bitmap; } public function set bitmap(val:BitmapData):void { _bitmap = val; _bitmapDirty = true; } /** * @inheritDoc */ public function getPixel32(u:Number, v:Number):uint { if (repeat) { x = u%1; y = (1 - v%1); } else { x = u; y = (1 - v); } return _bitmap.getPixel32(x*_bitmap.width, y*_bitmap.height); } /** * Defines a colored tint for the texture bitmap. */ public function get color():uint { return _color; } public function set color(val:uint):void { if (_color == val) return; _color = val; _red = ((_color & 0xFF0000) >> 16)/255; _green = ((_color & 0x00FF00) >> 8)/255; _blue = (_color & 0x0000FF)/255; _colorTransformDirty = true; } /** * Defines an alpha value for the texture bitmap. */ public function get alpha():Number { return _alpha; } public function set alpha(value:Number):void { if (value > 1) value = 1; if (value < 0) value = 0; if (_alpha == value) return; _alpha = value; _colorTransformDirty = true; } /** * Defines a blendMode value for the texture bitmap. * Applies to materials rendered as children of BitmapMaterialContainer or CompositeMaterial. * * @see away3d.materials.BitmapMaterialContainer * @see away3d.materials.CompositeMaterial */ public function get blendMode():String { return _blendMode; } public function set blendMode(val:String):void { if (_blendMode == val) return; _blendMode = val; _blendModeDirty = true; } /** * Creates a new BitmapMaterial object. * * @param bitmap The bitmapData object to be used as the material's texture. * @param init [optional] An initialisation object for specifying default instance properties. */ public function BitmapMaterial(bitmap:BitmapData, init:Object = null) { _bitmap = bitmap; ini = Init.parse(init); smooth = ini.getBoolean("smooth", false); debug = ini.getBoolean("debug", false); repeat = ini.getBoolean("repeat", false); precision = ini.getNumber("precision", 0); _blendMode = ini.getString("blendMode", BlendMode.NORMAL); alpha = ini.getNumber("alpha", _alpha, {min:0, max:1}); color = ini.getColor("color", _color); _colorTransformDirty = true; createVertexArray(); } /** * @inheritDoc */ public function updateMaterial(source:Object3D, view:View3D):void { _graphics = null; clearShapeDictionary(); if (_colorTransformDirty) updateColorTransform(); if (_bitmapDirty) updateRenderBitmap(); if (_faceDirty || _blendModeDirty) clearFaceDictionary(); _blendModeDirty = false; } public function getFaceVO(face:Face, source:Object3D, view:View3D = null):FaceVO { if ((_faceVO = _faceDictionary[face])) return _faceVO; return _faceDictionary[face] = new FaceVO(); } public function removeFaceDictionary():void { _faceDictionary = new Dictionary(true); } /** * @inheritDoc */ public function renderLayer(tri:DrawTriangle, layer:Sprite, level:int):void { if (blendMode == BlendMode.NORMAL) { _graphics = layer.graphics; } else { _session = tri.source.session; if (_session != tri.view.session) { //check to see if source shape exists if (!(_shape = _shapeDictionary[_session])) layer.addChild(_shape = _shapeDictionary[_session] = new Shape()); } else { //check to see if face shape exists if (!(_shape = _shapeDictionary[tri.face])) layer.addChild(_shape = _shapeDictionary[tri.face] = new Shape()); } _shape.blendMode = _blendMode; _graphics = _shape.graphics; } renderTriangle(tri); } /** * @inheritDoc */ public function renderTriangle(tri:DrawTriangle):void { _mapping = getMapping(tri); _session = tri.source.session; _view = tri.view; if (!_graphics && _session != tri.view.session && _session.newLayer) _graphics = _session.newLayer.graphics; if (precision) { focus = tri.view.camera.focus; map.a = _mapping.a; map.b = _mapping.b; map.c = _mapping.c; map.d = _mapping.d; map.tx = _mapping.tx; map.ty = _mapping.ty; renderRec(tri.v0, tri.v1, tri.v2, 0); } else { _session.renderTriangleBitmap(_renderBitmap, _mapping, tri.v0, tri.v1, tri.v2, smooth, repeat, _graphics); } if (debug) _session.renderTriangleLine(0, 0x0000FF, 1, tri.v0, tri.v1, tri.v2); } /** * @inheritDoc */ public function renderBitmapLayer(tri:DrawTriangle, containerRect:Rectangle, parentFaceVO:FaceVO):FaceVO { //draw the bitmap once renderSource(tri.source, containerRect, new Matrix()); //check to see if faceDictionary exists if (!(_faceVO = _faceDictionary[tri])) _faceVO = _faceDictionary[tri] = new FaceVO(); //pass on resize value if (parentFaceVO.resized) { parentFaceVO.resized = false; _faceVO.resized = true; } //pass on invtexturemapping value _faceVO.invtexturemapping = parentFaceVO.invtexturemapping; //check to see if face update can be skipped if (parentFaceVO.updated || _faceVO.invalidated) { parentFaceVO.updated = false; //reset booleans _faceVO.invalidated = false; _faceVO.cleared = false; _faceVO.updated = true; //store a clone _faceVO.bitmap = parentFaceVO.bitmap.clone(); //draw into faceBitmap _faceVO.bitmap.copyPixels(_sourceVO.bitmap, tri.face.bitmapRect, _zeroPoint, null, null, true); } return _faceVO; } /** * @inheritDoc */ public function get visible():Boolean { return _alpha > 0; } /** * @inheritDoc */ public function addOnMaterialResize(listener:Function):void { addEventListener(MaterialEvent.MATERIAL_RESIZED, listener, false, 0, true); } /** * @inheritDoc */ public function removeOnMaterialResize(listener:Function):void { removeEventListener(MaterialEvent.MATERIAL_RESIZED, listener, false); } /** * @inheritDoc */ public function addOnMaterialUpdate(listener:Function):void { addEventListener(MaterialEvent.MATERIAL_UPDATED, listener, false, 0, true); } /** * @inheritDoc */ public function removeOnMaterialUpdate(listener:Function):void { removeEventListener(MaterialEvent.MATERIAL_UPDATED, listener, false); } } }