PageRenderTime 298ms CodeModel.GetById 121ms app.highlight 91ms RepoModel.GetById 80ms app.codeStats 0ms

/src/away3d/materials/passes/MaterialPassBase.as

http://github.com/away3d/away3d-core-fp11
ActionScript | 557 lines | 353 code | 78 blank | 126 comment | 39 complexity | a7ebc61e46510dbe8827d3f1f18e8e4f MD5 | raw file
  1package away3d.materials.passes
  2{
  3	import away3d.animators.data.AnimationRegisterCache;
  4	import away3d.animators.IAnimationSet;
  5	import away3d.arcane;
  6	import away3d.cameras.Camera3D;
  7	import away3d.core.base.IRenderable;
  8	import away3d.core.managers.AGALProgram3DCache;
  9	import away3d.core.managers.Stage3DProxy;
 10	import away3d.debug.Debug;
 11	import away3d.errors.AbstractMethodError;
 12	import away3d.materials.MaterialBase;
 13	import away3d.materials.lightpickers.LightPickerBase;
 14	
 15	import flash.display.BlendMode;
 16	import flash.display3D.Context3D;
 17	import flash.display3D.Context3DBlendFactor;
 18	import flash.display3D.Context3DCompareMode;
 19	import flash.display3D.Context3DTriangleFace;
 20	import flash.display3D.Program3D;
 21	import flash.display3D.textures.TextureBase;
 22	import flash.events.Event;
 23	import flash.events.EventDispatcher;
 24	import flash.geom.Matrix3D;
 25	import flash.geom.Rectangle;
 26	
 27	use namespace arcane;
 28	
 29	/**
 30	 * MaterialPassBase provides an abstract base class for material shader passes. A material pass constitutes at least
 31	 * a render call per required renderable.
 32	 */
 33	public class MaterialPassBase extends EventDispatcher
 34	{
 35		protected var _material:MaterialBase;
 36		protected var _animationSet:IAnimationSet;
 37		
 38		arcane var _program3Ds:Vector.<Program3D> = new Vector.<Program3D>(8);
 39		arcane var _program3Dids:Vector.<int> = Vector.<int>([-1, -1, -1, -1, -1, -1, -1, -1]);
 40		private var _context3Ds:Vector.<Context3D> = new Vector.<Context3D>(8);
 41		
 42		// agal props. these NEED to be set by subclasses!
 43		// todo: can we perhaps figure these out manually by checking read operations in the bytecode, so other sources can be safely updated?
 44		protected var _numUsedStreams:uint;
 45		protected var _numUsedTextures:uint;
 46		protected var _numUsedVertexConstants:uint;
 47		protected var _numUsedFragmentConstants:uint;
 48		protected var _numUsedVaryings:uint;
 49		
 50		protected var _smooth:Boolean = true;
 51		protected var _repeat:Boolean = false;
 52		protected var _mipmap:Boolean = true;
 53		protected var _depthCompareMode:String = Context3DCompareMode.LESS_EQUAL;
 54		
 55		protected var _blendFactorSource:String = Context3DBlendFactor.ONE;
 56		protected var _blendFactorDest:String = Context3DBlendFactor.ZERO;
 57		
 58		protected var _enableBlending:Boolean;
 59		
 60		private var _bothSides:Boolean;
 61		
 62		protected var _lightPicker:LightPickerBase;
 63		protected var _animatableAttributes:Vector.<String> = Vector.<String>(["va0"]);
 64		protected var _animationTargetRegisters:Vector.<String> = Vector.<String>(["vt0"]);
 65		protected var _shadedTarget:String = "ft0";
 66		
 67		// keep track of previously rendered usage for faster cleanup of old vertex buffer streams and textures
 68		private static var _previousUsedStreams:Vector.<int> = Vector.<int>([0, 0, 0, 0, 0, 0, 0, 0]);
 69		private static var _previousUsedTexs:Vector.<int> = Vector.<int>([0, 0, 0, 0, 0, 0, 0, 0]);
 70		protected var _defaultCulling:String = Context3DTriangleFace.BACK;
 71		
 72		private var _renderToTexture:Boolean;
 73		
 74		// render state mementos for render-to-texture passes
 75		private var _oldTarget:TextureBase;
 76		private var _oldSurface:int;
 77		private var _oldDepthStencil:Boolean;
 78		private var _oldRect:Rectangle;
 79		
 80		protected var _alphaPremultiplied:Boolean;
 81		protected var _needFragmentAnimation:Boolean;
 82		protected var _needUVAnimation:Boolean;
 83		protected var _UVTarget:String;
 84		protected var _UVSource:String;
 85		
 86		protected var _writeDepth:Boolean = true;
 87		
 88		public var animationRegisterCache:AnimationRegisterCache;
 89		
 90		/**
 91		 * Creates a new MaterialPassBase object.
 92		 *
 93		 * @param renderToTexture Indicates whether this pass is a render-to-texture pass.
 94		 */
 95		public function MaterialPassBase(renderToTexture:Boolean = false)
 96		{
 97			_renderToTexture = renderToTexture;
 98			_numUsedStreams = 1;
 99			_numUsedVertexConstants = 5;
100		}
101		
102		/**
103		 * The material to which this pass belongs.
104		 */
105		public function get material():MaterialBase
106		{
107			return _material;
108		}
109		
110		public function set material(value:MaterialBase):void
111		{
112			_material = value;
113		}
114		
115		/**
116		 * Indicate whether this pass should write to the depth buffer or not. Ignored when blending is enabled.
117		 */
118		public function get writeDepth():Boolean
119		{
120			return _writeDepth;
121		}
122		
123		public function set writeDepth(value:Boolean):void
124		{
125			_writeDepth = value;
126		}
127		
128		/**
129		 * Defines whether any used textures should use mipmapping.
130		 */
131		public function get mipmap():Boolean
132		{
133			return _mipmap;
134		}
135		
136		public function set mipmap(value:Boolean):void
137		{
138			if (_mipmap == value)
139				return;
140			_mipmap = value;
141			invalidateShaderProgram();
142		}
143		
144		/**
145		 * Defines whether smoothing should be applied to any used textures.
146		 */
147		public function get smooth():Boolean
148		{
149			return _smooth;
150		}
151		
152		public function set smooth(value:Boolean):void
153		{
154			if (_smooth == value)
155				return;
156			_smooth = value;
157			invalidateShaderProgram();
158		}
159		
160		/**
161		 * Defines whether textures should be tiled.
162		 */
163		public function get repeat():Boolean
164		{
165			return _repeat;
166		}
167		
168		public function set repeat(value:Boolean):void
169		{
170			if (_repeat == value)
171				return;
172			_repeat = value;
173			invalidateShaderProgram();
174		}
175		
176		/**
177		 * Defines whether or not the material should perform backface culling.
178		 */
179		public function get bothSides():Boolean
180		{
181			return _bothSides;
182		}
183		
184		public function set bothSides(value:Boolean):void
185		{
186			_bothSides = value;
187		}
188
189		/**
190		 * The depth compare mode used to render the renderables using this material.
191		 *
192		 * @see flash.display3D.Context3DCompareMode
193		 */
194		public function get depthCompareMode():String
195		{
196			return _depthCompareMode;
197		}
198		
199		public function set depthCompareMode(value:String):void
200		{
201			_depthCompareMode = value;
202		}
203
204		/**
205		 * Returns the animation data set adding animations to the material.
206		 */
207		public function get animationSet():IAnimationSet
208		{
209			return _animationSet;
210		}
211		
212		public function set animationSet(value:IAnimationSet):void
213		{
214			if (_animationSet == value)
215				return;
216			
217			_animationSet = value;
218			
219			invalidateShaderProgram();
220		}
221		
222		/**
223		 * Specifies whether this pass renders to texture
224		 */
225		public function get renderToTexture():Boolean
226		{
227			return _renderToTexture;
228		}
229		
230		/**
231		 * Cleans up any resources used by the current object.
232		 * @param deep Indicates whether other resources should be cleaned up, that could potentially be shared across different instances.
233		 */
234		public function dispose():void
235		{
236			if (_lightPicker)
237				_lightPicker.removeEventListener(Event.CHANGE, onLightsChange);
238			
239			for (var i:uint = 0; i < 8; ++i) {
240				if (_program3Ds[i]) {
241					AGALProgram3DCache.getInstanceFromIndex(i).freeProgram3D(_program3Dids[i]);
242					_program3Ds[i] = null;
243				}
244			}
245		}
246		
247		/**
248		 * The amount of used vertex streams in the vertex code. Used by the animation code generation to know from which index on streams are available.
249		 */
250		public function get numUsedStreams():uint
251		{
252			return _numUsedStreams;
253		}
254		
255		/**
256		 * The amount of used vertex constants in the vertex code. Used by the animation code generation to know from which index on registers are available.
257		 */
258		public function get numUsedVertexConstants():uint
259		{
260			return _numUsedVertexConstants;
261		}
262		
263		public function get numUsedVaryings():uint
264		{
265			return _numUsedVaryings;
266		}
267
268		/**
269		 * The amount of used fragment constants in the fragment code. Used by the animation code generation to know from which index on registers are available.
270		 */
271		public function get numUsedFragmentConstants():uint
272		{
273			return _numUsedFragmentConstants;
274		}
275		
276		public function get needFragmentAnimation():Boolean
277		{
278			return _needFragmentAnimation;
279		}
280
281		/**
282		 * Indicates whether the pass requires any UV animatin code.
283		 */
284		public function get needUVAnimation():Boolean
285		{
286			return _needUVAnimation;
287		}
288		
289		/**
290		 * Sets up the animation state. This needs to be called before render()
291		 *
292		 * @private
293		 */
294		arcane function updateAnimationState(renderable:IRenderable, stage3DProxy:Stage3DProxy, camera:Camera3D):void
295		{
296			renderable.animator.setRenderState(stage3DProxy, renderable, _numUsedVertexConstants, _numUsedStreams, camera);
297		}
298		
299		/**
300		 * Renders an object to the current render target.
301		 *
302		 * @private
303		 */
304		arcane function render(renderable:IRenderable, stage3DProxy:Stage3DProxy, camera:Camera3D, viewProjection:Matrix3D):void
305		{
306			throw new AbstractMethodError();
307		}
308
309		/**
310		 * Returns the vertex AGAL code for the material.
311		 */
312		arcane function getVertexCode():String
313		{
314			throw new AbstractMethodError();
315		}
316
317		/**
318		 * Returns the fragment AGAL code for the material.
319		 */
320		arcane function getFragmentCode(fragmentAnimatorCode:String):String
321		{
322			throw new AbstractMethodError();
323		}
324
325		/**
326		 * The blend mode to use when drawing this renderable. The following blend modes are supported:
327		 * <ul>
328		 * <li>BlendMode.NORMAL: No blending, unless the material inherently needs it</li>
329		 * <li>BlendMode.LAYER: Force blending. This will draw the object the same as NORMAL, but without writing depth writes.</li>
330		 * <li>BlendMode.MULTIPLY</li>
331		 * <li>BlendMode.ADD</li>
332		 * <li>BlendMode.ALPHA</li>
333		 * </ul>
334		 */
335		public function setBlendMode(value:String):void
336		{
337			switch (value) {
338				case BlendMode.NORMAL:
339					_blendFactorSource = Context3DBlendFactor.ONE;
340					_blendFactorDest = Context3DBlendFactor.ZERO;
341					_enableBlending = false;
342					break;
343				case BlendMode.LAYER:
344					_blendFactorSource = Context3DBlendFactor.SOURCE_ALPHA;
345					_blendFactorDest = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;
346					_enableBlending = true;
347					break;
348				case BlendMode.MULTIPLY:
349					_blendFactorSource = Context3DBlendFactor.ZERO;
350					_blendFactorDest = Context3DBlendFactor.SOURCE_COLOR;
351					_enableBlending = true;
352					break;
353				case BlendMode.ADD:
354					_blendFactorSource = Context3DBlendFactor.SOURCE_ALPHA;
355					_blendFactorDest = Context3DBlendFactor.ONE;
356					_enableBlending = true;
357					break;
358				case BlendMode.ALPHA:
359					_blendFactorSource = Context3DBlendFactor.ZERO;
360					_blendFactorDest = Context3DBlendFactor.SOURCE_ALPHA;
361					_enableBlending = true;
362					break;
363				case BlendMode.SCREEN:
364					_blendFactorSource = Context3DBlendFactor.ONE;
365					_blendFactorDest = Context3DBlendFactor.ONE_MINUS_SOURCE_COLOR;
366					_enableBlending = true;
367					break;
368				default:
369					throw new ArgumentError("Unsupported blend mode!");
370			}
371		}
372
373		/**
374		 * Sets the render state for the pass that is independent of the rendered object. This needs to be called before
375		 * calling renderPass. Before activating a pass, the previously used pass needs to be deactivated.
376		 * @param stage3DProxy The Stage3DProxy object which is currently used for rendering.
377		 * @param camera The camera from which the scene is viewed.
378		 * @private
379		 */
380		arcane function activate(stage3DProxy:Stage3DProxy, camera:Camera3D):void
381		{
382			var contextIndex:int = stage3DProxy._stage3DIndex;
383			var context:Context3D = stage3DProxy._context3D;
384			
385			context.setDepthTest(_writeDepth && !_enableBlending, _depthCompareMode);
386			if (_enableBlending)
387				context.setBlendFactors(_blendFactorSource, _blendFactorDest);
388			
389			if (_context3Ds[contextIndex] != context || !_program3Ds[contextIndex]) {
390				_context3Ds[contextIndex] = context;
391				updateProgram(stage3DProxy);
392				dispatchEvent(new Event(Event.CHANGE));
393			}
394			
395			var prevUsed:int = _previousUsedStreams[contextIndex];
396			var i:uint;
397			for (i = _numUsedStreams; i < prevUsed; ++i)
398				context.setVertexBufferAt(i, null);
399			
400			prevUsed = _previousUsedTexs[contextIndex];
401			
402			for (i = _numUsedTextures; i < prevUsed; ++i)
403				context.setTextureAt(i, null);
404			
405			if (_animationSet && !_animationSet.usesCPU)
406				_animationSet.activate(stage3DProxy, this);
407			
408			context.setProgram(_program3Ds[contextIndex]);
409			
410			context.setCulling(_bothSides? Context3DTriangleFace.NONE : _defaultCulling);
411			
412			if (_renderToTexture) {
413				_oldTarget = stage3DProxy.renderTarget;
414				_oldSurface = stage3DProxy.renderSurfaceSelector;
415				_oldDepthStencil = stage3DProxy.enableDepthAndStencil;
416				_oldRect = stage3DProxy.scissorRect;
417			}
418		}
419
420		/**
421		 * Clears the render state for the pass. This needs to be called before activating another pass.
422		 * @param stage3DProxy The Stage3DProxy used for rendering
423		 *
424		 * @private
425		 */
426		arcane function deactivate(stage3DProxy:Stage3DProxy):void
427		{
428			var index:uint = stage3DProxy._stage3DIndex;
429			_previousUsedStreams[index] = _numUsedStreams;
430			_previousUsedTexs[index] = _numUsedTextures;
431			
432			if (_animationSet && !_animationSet.usesCPU)
433				_animationSet.deactivate(stage3DProxy, this);
434			
435			if (_renderToTexture) {
436				// kindly restore state
437				stage3DProxy.setRenderTarget(_oldTarget, _oldDepthStencil, _oldSurface);
438				stage3DProxy.scissorRect = _oldRect;
439			}
440
441			if(_enableBlending) {
442				stage3DProxy._context3D.setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ZERO);
443			}
444
445			stage3DProxy._context3D.setDepthTest(true, Context3DCompareMode.LESS_EQUAL);
446		}
447		
448		/**
449		 * Marks the shader program as invalid, so it will be recompiled before the next render.
450		 *
451		 * @param updateMaterial Indicates whether the invalidation should be performed on the entire material. Should always pass "true" unless it's called from the material itself.
452		 */
453		arcane function invalidateShaderProgram(updateMaterial:Boolean = true):void
454		{
455			for (var i:uint = 0; i < 8; ++i)
456				_program3Ds[i] = null;
457			
458			if (_material && updateMaterial)
459				_material.invalidatePasses(this);
460		}
461		
462		/**
463		 * Compiles the shader program.
464		 * @param polyOffsetReg An optional register that contains an amount by which to inflate the model (used in single object depth map rendering).
465		 */
466		arcane function updateProgram(stage3DProxy:Stage3DProxy):void
467		{
468			var animatorCode:String = "";
469			var UVAnimatorCode:String = "";
470			var fragmentAnimatorCode:String = "";
471			var vertexCode:String = getVertexCode();
472			
473			if (_animationSet && !_animationSet.usesCPU) {
474				animatorCode = _animationSet.getAGALVertexCode(this, _animatableAttributes, _animationTargetRegisters, stage3DProxy.profile);
475				if (_needFragmentAnimation)
476					fragmentAnimatorCode = _animationSet.getAGALFragmentCode(this, _shadedTarget, stage3DProxy.profile);
477				if (_needUVAnimation)
478					UVAnimatorCode = _animationSet.getAGALUVCode(this, _UVSource, _UVTarget);
479				_animationSet.doneAGALCode(this);
480			} else {
481				var len:uint = _animatableAttributes.length;
482				
483				// simply write attributes to targets, do not animate them
484				// projection will pick up on targets[0] to do the projection
485				for (var i:uint = 0; i < len; ++i)
486					animatorCode += "mov " + _animationTargetRegisters[i] + ", " + _animatableAttributes[i] + "\n";
487				if (_needUVAnimation)
488					UVAnimatorCode = "mov " + _UVTarget + "," + _UVSource + "\n";
489			}
490			
491			vertexCode = animatorCode + UVAnimatorCode + vertexCode;
492			
493			var fragmentCode:String = getFragmentCode(fragmentAnimatorCode);
494			if (Debug.active) {
495				trace("Compiling AGAL Code:");
496				trace("--------------------");
497				trace(vertexCode);
498				trace("--------------------");
499				trace(fragmentCode);
500			}
501			AGALProgram3DCache.getInstance(stage3DProxy).setProgram3D(this, vertexCode, fragmentCode);
502		}
503
504		/**
505		 * The light picker used by the material to provide lights to the material if it supports lighting.
506		 *
507		 * @see away3d.materials.lightpickers.LightPickerBase
508		 * @see away3d.materials.lightpickers.StaticLightPicker
509		 */
510		arcane function get lightPicker():LightPickerBase
511		{
512			return _lightPicker;
513		}
514		
515		arcane function set lightPicker(value:LightPickerBase):void
516		{
517			if (_lightPicker)
518				_lightPicker.removeEventListener(Event.CHANGE, onLightsChange);
519			_lightPicker = value;
520			if (_lightPicker)
521				_lightPicker.addEventListener(Event.CHANGE, onLightsChange);
522			updateLights();
523		}
524
525		/**
526		 * Called when the light picker's configuration changes.
527		 */
528		private function onLightsChange(event:Event):void
529		{
530			updateLights();
531		}
532
533		/**
534		 * Implemented by subclasses if the pass uses lights to update the shader.
535		 */
536		protected function updateLights():void
537		{
538		
539		}
540
541		/**
542		 * Indicates whether visible textures (or other pixels) used by this material have
543		 * already been premultiplied. Toggle this if you are seeing black halos around your
544		 * blended alpha edges.
545		 */
546		public function get alphaPremultiplied():Boolean
547		{
548			return _alphaPremultiplied;
549		}
550		
551		public function set alphaPremultiplied(value:Boolean):void
552		{
553			_alphaPremultiplied = value;
554			invalidateShaderProgram(false);
555		}
556	}
557}