Renderer.hx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. package h3d.scene;
  2. class PassObjects {
  3. public var name : String;
  4. public var passes : h3d.pass.PassList;
  5. public var rendered : Bool;
  6. public function new() {
  7. passes = new h3d.pass.PassList();
  8. }
  9. }
  10. enum RenderMode{
  11. Default;
  12. LightProbe;
  13. }
  14. @:allow(hrt.prefab.rfx.RendererFX)
  15. @:allow(h3d.pass.Shadows)
  16. class Renderer extends hxd.impl.AnyProps {
  17. var defaultPass : h3d.pass.Output;
  18. var passObjects : Map<String,PassObjects>;
  19. var allPasses : Array<h3d.pass.Output>;
  20. var emptyPasses = new h3d.pass.PassList();
  21. var ctx : RenderContext;
  22. var hasSetTarget = false;
  23. var frontToBack : h3d.pass.PassList -> Void;
  24. var backToFront : h3d.pass.PassList -> Void;
  25. var debugging = false;
  26. #if editor
  27. public var showEditorGuides = false;
  28. public var showEditorOutlines = true;
  29. #end
  30. public var effects : Array<h3d.impl.RendererFX> = [];
  31. public var volumeEffects : Array<h3d.impl.RendererFXVolume> = [];
  32. var toRemove : Array<h3d.impl.RendererFX> = [];
  33. public var renderMode : RenderMode = Default;
  34. public var shadows : Bool = true;
  35. public function new() {
  36. allPasses = [];
  37. passObjects = new Map();
  38. props = getDefaultProps();
  39. // pre allocate closures
  40. frontToBack = depthSort.bind(true);
  41. backToFront = depthSort.bind(false);
  42. }
  43. public function getEffect<T:h3d.impl.RendererFX>( cl : Class<T> ) : T {
  44. for( f in effects ) {
  45. var f = Std.downcast(f, cl);
  46. if( f != null ) return f;
  47. }
  48. return null;
  49. }
  50. public function dispose() {
  51. for( p in allPasses )
  52. p.dispose();
  53. for( f in effects )
  54. f.dispose();
  55. for( v in volumeEffects )
  56. for (e in v.effects)
  57. e.dispose();
  58. if ( ctx.lightSystem != null )
  59. ctx.lightSystem.dispose();
  60. passObjects = new Map();
  61. }
  62. function mark(id: String) {
  63. }
  64. /**
  65. Inject a post process shader for the current frame. Shaders are reset after each render.
  66. **/
  67. public function addShader( s : hxsl.Shader ) {
  68. }
  69. public function getPass<T:h3d.pass.Output>( c : Class<T> ) : T {
  70. for( p in allPasses )
  71. if( Std.isOfType(p, c) )
  72. return cast p;
  73. return null;
  74. }
  75. public function getPassByName( name : String ) {
  76. for( p in allPasses )
  77. if( p.name == name )
  78. return p;
  79. return null;
  80. }
  81. function hasFeature(f) {
  82. return h3d.Engine.getCurrent().driver.hasFeature(f);
  83. }
  84. function getLightSystem() : h3d.scene.LightSystem {
  85. return ctx.scene.lightSystem;
  86. }
  87. function getDepthClearValue() {
  88. return ctx.useReverseDepth ? 0 : 1;
  89. }
  90. @:access(h3d.scene.Object)
  91. function depthSort( frontToBack, passes : h3d.pass.PassList ) {
  92. var cam = ctx.camera.m;
  93. for( p in passes ) {
  94. var z = p.obj.absPos._41 * cam._13 + p.obj.absPos._42 * cam._23 + p.obj.absPos._43 * cam._33 + cam._43;
  95. var w = p.obj.absPos._41 * cam._14 + p.obj.absPos._42 * cam._24 + p.obj.absPos._43 * cam._34 + cam._44;
  96. p.depth = w > 0.0 ? z / w : - z / w;
  97. }
  98. if( frontToBack && !ctx.camera.reverseDepth || !frontToBack && ctx.camera.reverseDepth )
  99. passes.sort(
  100. function(p1, p2) {
  101. if ( p1.pass.layer != p2.pass.layer )
  102. return p1.pass.layer - p2.pass.layer;
  103. if ( p1.depth == p2.depth )
  104. return 0;
  105. return p1.depth > p2.depth ? 1 : -1;
  106. }
  107. );
  108. else
  109. passes.sort(
  110. function(p1, p2) {
  111. if ( p1.pass.layer != p2.pass.layer )
  112. return p1.pass.layer - p2.pass.layer;
  113. if ( p1.depth == p2.depth )
  114. return 0;
  115. return p1.depth < p2.depth ? 1 : -1;
  116. }
  117. );
  118. }
  119. inline function clear( ?color, ?depth, ?stencil ) {
  120. ctx.engine.clear(color, depth, stencil);
  121. }
  122. inline function allocTarget( name : String, depth = true, size = 1., ?format ) {
  123. return ctx.textures.allocTarget(name, Math.round(ctx.engine.width * size), Math.round(ctx.engine.height * size), depth, format);
  124. }
  125. function copy( from, to, ?blend ) {
  126. h3d.pass.Copy.run(from, to, blend);
  127. }
  128. function setTarget( tex, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
  129. if( hasSetTarget ) ctx.engine.popTarget();
  130. ctx.engine.pushTarget(tex, depthBinding);
  131. hasSetTarget = true;
  132. }
  133. function setTargets<T:h3d.mat.Texture>( textures : Array<T>, depthBinding : h3d.Engine.DepthBinding = ReadWrite ) {
  134. if( hasSetTarget ) ctx.engine.popTarget();
  135. ctx.engine.pushTargets(cast textures, depthBinding);
  136. hasSetTarget = true;
  137. }
  138. function setDepth( depthBuffer : h3d.mat.Texture ) {
  139. if( hasSetTarget ) ctx.engine.popTarget();
  140. ctx.engine.pushDepth(depthBuffer);
  141. hasSetTarget = true;
  142. }
  143. function resetTarget() {
  144. if( hasSetTarget ) {
  145. ctx.engine.popTarget();
  146. hasSetTarget = false;
  147. }
  148. }
  149. function has( name : String ) {
  150. return passObjects.get(name) != null;
  151. }
  152. @:access(h3d.mat.Pass)
  153. function setPassFlags( pass : h3d.mat.Pass ) {
  154. pass.rendererFlags |= 1;
  155. }
  156. @:access(h3d.pass.PassList)
  157. function get( name : String ) {
  158. var p = passObjects.get(name);
  159. if( p == null ) return emptyPasses;
  160. p.rendered = true;
  161. return p.passes;
  162. }
  163. function draw( name : String ) {
  164. defaultPass.draw(get(name));
  165. }
  166. function render() {
  167. throw "Not implemented";
  168. }
  169. function computeStatic() {
  170. throw "Not implemented";
  171. }
  172. public function start() {
  173. }
  174. public function startEffects() {
  175. for ( e in effects )
  176. if ( e.enabled )
  177. e.start(this);
  178. }
  179. public function process( passes : Array<PassObjects> ) {
  180. hasSetTarget = false;
  181. for( p in allPasses )
  182. p.setContext(ctx);
  183. for( p in passes )
  184. passObjects.set(p.name, p);
  185. ctx.textures.begin();
  186. if( ctx.computingStatic )
  187. computeStatic();
  188. else
  189. render();
  190. resetTarget();
  191. for( p in passes )
  192. passObjects.set(p.name, null);
  193. }
  194. public function computeDispatch( shader, x = 1, y = 1, z = 1 ) {
  195. ctx.computeDispatch(shader, x, y, z);
  196. }
  197. public function processVolumetricEffects() {
  198. if (volumeEffects.length == 1) {
  199. for (e in volumeEffects[0].effects) {
  200. var newEffect = e.modulate(volumeEffects[0].getFactor(ctx.camera.pos));
  201. if (newEffect == null)
  202. continue;
  203. toRemove.push(newEffect);
  204. this.effects.push(newEffect);
  205. }
  206. }
  207. else if (volumeEffects.length >= 2) {
  208. // When there is more than 2 active volume effects, we take the top 2 prios and closer distance and
  209. // blend them
  210. volumeEffects.sort((a, b) -> {
  211. if (a.priority != b.priority)
  212. return a.priority > b.priority ? -1 : 1;
  213. var pos = ctx.camera.pos;
  214. var aDist = (a.getAbsPos().getPosition() - pos).length();
  215. var bDist = (b.getAbsPos().getPosition() - pos).length();
  216. return aDist < bDist ? -1 : 1;
  217. });
  218. var r1 = volumeEffects[0];
  219. var r2 = volumeEffects[1];
  220. function containsEffectType(volume : h3d.impl.RendererFXVolume, e : h3d.impl.RendererFX) {
  221. var cl = Type.getClass(e);
  222. for (effect in volume.effects)
  223. if (Std.isOfType(effect, cl))
  224. return true;
  225. return false;
  226. }
  227. // Push unique renderer FX from volume 1 and volume 2
  228. for (e in r1.effects) {
  229. if (!containsEffectType(r2, e)) {
  230. this.toRemove.push(e);
  231. this.effects.push(e);
  232. }
  233. }
  234. for (e in r2.effects) {
  235. if (!containsEffectType(r1, e)) {
  236. this.toRemove.push(e);
  237. this.effects.push(e);
  238. }
  239. }
  240. // Manage blending of renderer FX that are in volume 1 and volume 2
  241. // Look for which direction the blend should be (r1 -> r2 or r2 -> r1)
  242. var isR1toR2 = r1.priority < r2.priority;
  243. var volume1 = isR1toR2 ? r1 : r2;
  244. var volume2 = isR1toR2 ? r2 : r1;
  245. for (e1 in volume1.effects) {
  246. if (!containsEffectType(volume2, e1))
  247. continue;
  248. for (e2 in volume2.effects) {
  249. var newEffect = e1.transition(e1, e2, volume2.getFactor(ctx.camera.pos));
  250. if (newEffect != null) {
  251. this.toRemove.push(newEffect);
  252. this.effects.push(newEffect);
  253. }
  254. }
  255. }
  256. }
  257. }
  258. public function removeVolumetricEffects() {
  259. for (e in toRemove)
  260. effects.remove(e);
  261. }
  262. }