package away3d.materials { import away3d.containers.*; import away3d.arcane; import away3d.core.base.*; import away3d.core.draw.*; import away3d.core.render.AbstractRenderSession; import away3d.core.utils.*; import away3d.events.*; import flash.display.*; import flash.events.*; import flash.geom.*; import flash.utils.*; use namespace arcane; /** * Container for layering multiple material objects. * Renders each material by drawing one triangle per meterial layer. * For static bitmap materials, use BitmapMaterialContainer. * * @see away3d.materials.BitmapMaterialContainer */ public class CompositeMaterial extends EventDispatcher implements ITriangleMaterial, ILayerMaterial { /** @private */ arcane var _color:uint; /** @private */ arcane var _alpha:Number; /** @private */ arcane var _colorTransform:ColorTransform = new ColorTransform(); /** @private */ arcane var _colorTransformDirty:Boolean; /** @private */ arcane var _spriteDictionary:Dictionary = new Dictionary(true); /** @private */ arcane var _sprite:Sprite; /** @private */ arcane var _source:Object3D; /** @private */ arcane var _session:AbstractRenderSession; private var _defaultColorTransform:ColorTransform = new ColorTransform(); private var _red:Number; private var _green:Number; private var _blue:Number; private var _material:ILayerMaterial; private function clearSpriteDictionary():void { for each (_sprite in _spriteDictionary) _sprite.graphics.clear(); } private function onMaterialUpdate(event:MaterialEvent):void { dispatchEvent(event); } /** * An array of bitmapmaterial objects to be overlayed sequentially. */ protected var materials:Array; /** * 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 setColorTransform():void { _colorTransformDirty = false; if (_alpha == 1 && _color == 0xFFFFFF) { _colorTransform = null; return; } else if (!_colorTransform) _colorTransform = new ColorTransform(); _colorTransform.redMultiplier = _red; _colorTransform.greenMultiplier = _green; _colorTransform.blueMultiplier = _blue; _colorTransform.alphaMultiplier = _alpha; } /** * Defines a blendMode value for the layer container. */ public var blendMode:String; /** * Defines a colored tint for the layer container. */ 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 layer container. */ 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; } /** * @inheritDoc */ public function get visible():Boolean { return true; } /** * Creates a new CompositeMaterial object. * * @param init [optional] An initialisation object for specifying default instance properties. */ public function CompositeMaterial(init:Object = null) { ini = Init.parse(init); materials = ini.getArray("materials"); blendMode = ini.getString("blendMode", BlendMode.NORMAL); alpha = ini.getNumber("alpha", 1, {min:0, max:1}); color = ini.getColor("color", 0xFFFFFF); for each (_material in materials) _material.addOnMaterialUpdate(onMaterialUpdate); _colorTransformDirty = true; } public function addMaterial(material:ILayerMaterial):void { material.addOnMaterialUpdate(onMaterialUpdate); materials.push(material); } public function removeMaterial(material:ILayerMaterial):void { var index:int = materials.indexOf(material); if (index == -1) return; material.removeOnMaterialUpdate(onMaterialUpdate); materials.splice(index, 1); } /** * @inheritDoc */ public function updateMaterial(source:Object3D, view:View3D):void { clearSpriteDictionary(); if (_colorTransformDirty) setColorTransform(); for each (_material in materials) _material.updateMaterial(source, view); } /** * @inheritDoc */ public function renderTriangle(tri:DrawTriangle):void { _source = tri.source; _session = _source.session; var level:int = 0; if (_session != tri.view.session) { //check to see if session sprite exists if (!(_sprite = _session.spriteLayers[level])) _sprite = _session.spriteLayers[level] = new Sprite(); } else { //check to see if face sprite exists if (!(_sprite = _spriteDictionary[tri.face])) _sprite = _spriteDictionary[tri.face] = new Sprite(); } if (!_session.children[_sprite]) { if (_session != tri.view.session) _session.addLayerObject(_sprite); else _session.addDisplayObject(_sprite); _sprite.filters = []; _sprite.blendMode = blendMode; if (_colorTransform) _sprite.transform.colorTransform = _colorTransform; else _sprite.transform.colorTransform = _defaultColorTransform; } //call renderLayer on each material for each (_material in materials) _material.renderLayer(tri, _sprite, ++level); } /** * @inheritDoc */ public function renderLayer(tri:DrawTriangle, layer:Sprite, level:int):void { if (!_colorTransform && blendMode == BlendMode.NORMAL) { _sprite = layer; } else { _source = tri.source; _session = _source.session; if (_session != tri.view.session) { //check to see if session sprite exists if (!(_sprite = _session.spriteLayers[level])) layer.addChild(_sprite = _session.spriteLayers[level] = new Sprite()); } else { //check to see if face sprite exists if (!(_sprite = _spriteDictionary[tri.face])) layer.addChild(_sprite = _spriteDictionary[tri.face] = new Sprite()); } _sprite.filters = []; _sprite.blendMode = blendMode; if (_colorTransform) _sprite.transform.colorTransform = _colorTransform; else _sprite.transform.colorTransform = _defaultColorTransform; } //call renderLayer on each material for each (_material in materials) _material.renderLayer(tri, _sprite, level++); } /** * @private */ public function renderBitmapLayer(tri:DrawTriangle, containerRect:Rectangle, parentFaceVO:FaceVO):FaceVO { throw new Error("Not implemented"); } /** * @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); } } }