ZGroup.hx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. package h2d;
  2. @:access(h2d.RenderContext)
  3. private class State {
  4. public var depthWrite : Bool;
  5. public var depthTest : h3d.mat.Data.Compare;
  6. public var front2back : Bool;
  7. public var killAlpha : Bool;
  8. public var onBeginDraw : h2d.Drawable->Bool;
  9. public function new() { }
  10. public function loadFrom( ctx : RenderContext ) {
  11. depthWrite = ctx.pass.depthWrite;
  12. depthTest = ctx.pass.depthTest;
  13. front2back = ctx.front2back;
  14. killAlpha = ctx.killAlpha;
  15. onBeginDraw = ctx.onBeginDraw;
  16. }
  17. public function applyTo( ctx : RenderContext ) {
  18. ctx.pass.depth(depthWrite, depthTest);
  19. ctx.front2back = front2back;
  20. ctx.killAlpha = killAlpha;
  21. ctx.onBeginDraw = onBeginDraw;
  22. }
  23. }
  24. private class DepthEntry {
  25. public var spr : Object;
  26. public var depth : Float;
  27. public var keep : Bool;
  28. public var next : DepthEntry;
  29. public function new() { }
  30. }
  31. @:dox(hide)
  32. class DepthMap {
  33. var map : Map<Object, DepthEntry>;
  34. var curIndex : Int;
  35. var free : DepthEntry;
  36. var first : DepthEntry;
  37. public function new() {
  38. map = new Map();
  39. }
  40. function push(spr : Object) {
  41. var e = map.get(spr);
  42. if (e == null) {
  43. if (free != null) {
  44. e = free;
  45. free = e.next;
  46. } else {
  47. e = new DepthEntry();
  48. }
  49. e.next = first;
  50. first = e;
  51. map.set(spr, e);
  52. }
  53. e.spr = spr;
  54. e.keep = true;
  55. e.depth = curIndex++;
  56. }
  57. function populate(spr : Object) {
  58. for (c in spr) {
  59. if (!c.visible) continue;
  60. push(c);
  61. populate(c);
  62. }
  63. }
  64. public function build(spr : Object) {
  65. curIndex = 0;
  66. var e = first;
  67. while (e != null) {
  68. e.keep = false;
  69. e = e.next;
  70. }
  71. push(spr);
  72. populate(spr);
  73. var p = null;
  74. var e = first;
  75. while (e != null) {
  76. if (e.keep) {
  77. e.depth = 1 - e.depth / curIndex;
  78. p = e;
  79. e = e.next;
  80. } else {
  81. var next = e.next;
  82. map.remove(e.spr);
  83. e.spr = null;
  84. e.next = free;
  85. free = e;
  86. if (p == null) first = next;
  87. else p.next = next;
  88. e = next;
  89. }
  90. }
  91. }
  92. inline public function getDepth(spr : Object) {
  93. return map.get(spr).depth;
  94. }
  95. public function clear(){
  96. map = new Map();
  97. free = null;
  98. first = null;
  99. }
  100. }
  101. /**
  102. An advanced double-pass rendering class that utilizes a z-culling on an opaque objects.
  103. For optimization to work properly, all opaque objects should have `Object.blendMode` set to `None`.
  104. Rendering is done in two passes:
  105. * An opaque pass only renders objects with `blendeMode = None`, with `RenderContext.front2back` and `RenderContext.killAlpha` enabled.
  106. * Transparent pass renders the rest of the objects (which are not marked as opaque) as usual.
  107. That allows to perform a z-cull depth test on the objects and reduce the overall GPU strain.
  108. Additionally, ZGroup places a limitation on filter usage. They are not drawn in opaque pass, which can lead to undefined behavior.
  109. **/
  110. @:access(h2d.RenderContext)
  111. class ZGroup extends Layers
  112. {
  113. var depthMap : DepthMap;
  114. var ctx : RenderContext;
  115. var normalState : State;
  116. var transpState : State;
  117. var opaqueState : State;
  118. var onEnterFilterCached : Object -> Bool;
  119. var onLeaveFilterCached : Object -> Void;
  120. /**
  121. Create a new ZGroup instance/
  122. @param parent An optional parent `h2d.Object` instance to which ZGroup adds itself if set.
  123. **/
  124. public function new(?parent) {
  125. super(parent);
  126. depthMap = new DepthMap();
  127. opaqueState = new State();
  128. opaqueState.depthWrite = true;
  129. opaqueState.depthTest = LessEqual;
  130. opaqueState.front2back = true;
  131. opaqueState.killAlpha = true;
  132. opaqueState.onBeginDraw = onBeginOpaqueDraw;
  133. transpState = new State();
  134. transpState.depthWrite = true;
  135. transpState.depthTest = LessEqual;
  136. transpState.front2back = false;
  137. transpState.killAlpha = false;
  138. transpState.onBeginDraw = onBeginTranspDraw;
  139. normalState = new State();
  140. onEnterFilterCached = onEnterFilter;
  141. onLeaveFilterCached = onLeaveFilter;
  142. }
  143. override function drawRec(ctx:RenderContext) {
  144. if( !visible ) return;
  145. this.ctx = ctx;
  146. depthMap.build(this);
  147. ctx.engine.clear(null, 1);
  148. var oldOnEnterFilter = ctx.onEnterFilter;
  149. var oldOnLeaveFilter = ctx.onLeaveFilter;
  150. normalState.loadFrom(ctx);
  151. ctx.onEnterFilter = onEnterFilterCached;
  152. ctx.onLeaveFilter = onLeaveFilterCached;
  153. opaqueState.applyTo(ctx);
  154. super.drawRec(ctx);
  155. transpState.applyTo(ctx);
  156. super.drawRec(ctx);
  157. normalState.applyTo(ctx);
  158. ctx.onEnterFilter = oldOnEnterFilter;
  159. ctx.onLeaveFilter = oldOnLeaveFilter;
  160. }
  161. function onBeginOpaqueDraw(obj : h2d.Drawable) : Bool {
  162. if (obj.blendMode != None) return false;
  163. ctx.baseShader.zValue = depthMap.getDepth(obj);
  164. return true;
  165. }
  166. function onBeginTranspDraw(obj : h2d.Drawable) : Bool {
  167. if (obj.blendMode == None) return false;
  168. ctx.baseShader.zValue = depthMap.getDepth(obj);
  169. return true;
  170. }
  171. function onEnterFilter(spr : Object) {
  172. if (ctx.front2back) return false; // opaque pass : do not render the filter
  173. normalState.applyTo(ctx);
  174. return true;
  175. }
  176. function onLeaveFilter(spr : Object) {
  177. transpState.applyTo(ctx);
  178. }
  179. }