/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

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