RenderContext.hx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. package h2d;
  2. class RenderContext extends h3d.impl.RenderContext {
  3. static inline var BUFFERING = false;
  4. public var globalAlpha = 1.;
  5. public var buffer : hxd.FloatBuffer;
  6. public var bufPos : Int;
  7. public var scene : h2d.Scene;
  8. public var defaultSmooth : Bool = false;
  9. public var killAlpha : Bool;
  10. public var front2back : Bool;
  11. public var onBeginDraw : h2d.Drawable->Bool; // return false to cancel drawing
  12. public var onEnterFilter : h2d.Sprite->Bool;
  13. public var onLeaveFilter : h2d.Sprite->Void;
  14. public var tmpBounds = new h2d.col.Bounds();
  15. var texture : h3d.mat.Texture;
  16. var baseShader : h3d.shader.Base2d;
  17. var manager : h3d.pass.ShaderManager;
  18. var compiledShader : hxsl.RuntimeShader;
  19. var buffers : h3d.shader.Buffers;
  20. var fixedBuffer : h3d.Buffer;
  21. var pass : h3d.mat.Pass;
  22. var currentShaders : hxsl.ShaderList;
  23. var baseShaderList : hxsl.ShaderList;
  24. var currentObj : Drawable;
  25. var stride : Int;
  26. var targetsStack : Array<{ t : h3d.mat.Texture, x : Int, y : Int, w : Int, h : Int, hasRZ : Bool, rzX:Float, rzY:Float, rzW:Float, rzH:Float }>;
  27. var targetsStackIndex : Int;
  28. var hasUVPos : Bool;
  29. var filterStack : Array<h2d.Sprite>;
  30. var inFilter : Sprite;
  31. var curX : Int;
  32. var curY : Int;
  33. var curWidth : Int;
  34. var curHeight : Int;
  35. var hasRenderZone : Bool;
  36. var renderX : Float;
  37. var renderY : Float;
  38. var renderW : Float;
  39. var renderH : Float;
  40. var currentBlend : BlendMode;
  41. public function new(scene) {
  42. super();
  43. this.scene = scene;
  44. if( BUFFERING )
  45. buffer = new hxd.FloatBuffer();
  46. bufPos = 0;
  47. manager = new h3d.pass.ShaderManager();
  48. pass = new h3d.mat.Pass("",null);
  49. pass.depth(true, Always);
  50. pass.culling = None;
  51. currentBlend = Alpha;
  52. pass.setBlendMode(currentBlend);
  53. baseShader = new h3d.shader.Base2d();
  54. baseShader.setPriority(100);
  55. baseShader.zValue = 0.;
  56. baseShaderList = new hxsl.ShaderList(baseShader);
  57. targetsStack = [];
  58. targetsStackIndex = 0;
  59. filterStack = [];
  60. }
  61. public function dispose() {
  62. textures.dispose();
  63. if( fixedBuffer != null ) fixedBuffer.dispose();
  64. }
  65. public inline function hasBuffering() {
  66. return BUFFERING;
  67. }
  68. public function begin() {
  69. texture = null;
  70. currentObj = null;
  71. bufPos = 0;
  72. stride = 0;
  73. curX = 0;
  74. curY = 0;
  75. inFilter = null;
  76. curWidth = scene.width;
  77. curHeight = scene.height;
  78. manager.globals.set("time", time);
  79. // todo : we might prefer to auto-detect this by running a test and capturing its output
  80. baseShader.pixelAlign = #if flash true #else false #end;
  81. baseShader.halfPixelInverse.set(0.5 / engine.width, 0.5 / engine.height);
  82. baseShader.viewport.set( -scene.width * 0.5, -scene.height * 0.5, 2 / scene.width, -2 / scene.height);
  83. baseShader.filterMatrixA.set(1, 0, 0);
  84. baseShader.filterMatrixB.set(0, 1, 0);
  85. baseShaderList.next = null;
  86. initShaders(baseShaderList);
  87. engine.selectMaterial(pass);
  88. textures.begin();
  89. }
  90. public function allocTarget(name, filter = false, size = 0) {
  91. var t = textures.allocTarget(name, scene.width >> size, scene.height >> size, false);
  92. t.filter = filter ? Linear : Nearest;
  93. return t;
  94. }
  95. public function clear(color) {
  96. engine.clear(color);
  97. }
  98. function initShaders( shaders ) {
  99. currentShaders = shaders;
  100. compiledShader = manager.compileShaders(shaders);
  101. if( buffers == null )
  102. buffers = new h3d.shader.Buffers(compiledShader);
  103. else
  104. buffers.grow(compiledShader);
  105. manager.fillGlobals(buffers, compiledShader);
  106. engine.selectShader(compiledShader);
  107. engine.uploadShaderBuffers(buffers, Globals);
  108. }
  109. public function end() {
  110. flush();
  111. texture = null;
  112. currentObj = null;
  113. baseShaderList.next = null;
  114. if( targetsStackIndex != 0 ) throw "Missing popTarget()";
  115. }
  116. public function pushFilter( spr : h2d.Sprite ) {
  117. if( filterStack.length == 0 && onEnterFilter != null )
  118. if( !onEnterFilter(spr) ) return false;
  119. filterStack.push(spr);
  120. inFilter = spr;
  121. return true;
  122. }
  123. public function popFilter() {
  124. var spr = filterStack.pop();
  125. if( filterStack.length > 0 ) {
  126. inFilter = filterStack[filterStack.length - 1];
  127. } else {
  128. inFilter = null;
  129. if( onLeaveFilter != null ) onLeaveFilter(spr);
  130. }
  131. }
  132. public function pushTarget( t : h3d.mat.Texture, startX = 0, startY = 0, width = -1, height = -1 ) {
  133. flush();
  134. engine.pushTarget(t);
  135. initShaders(baseShaderList);
  136. if( width < 0 ) width = t == null ? scene.width : t.width;
  137. if( height < 0 ) height = t == null ? scene.height : t.height;
  138. baseShader.halfPixelInverse.set(0.5 / (t == null ? engine.width : t.width), 0.5 / (t == null ? engine.height : t.height));
  139. baseShader.viewport.set( -width * 0.5 - startX, -height * 0.5 - startY, 2 / width, -2 / height);
  140. targetsStackIndex++;
  141. if( targetsStackIndex > targetsStack.length ){
  142. targetsStack.push( { t : t, x : startX, y : startY, w : width, h : height, hasRZ: hasRenderZone, rzX: renderX, rzY:renderY, rzW:renderW, rzH:renderH } );
  143. }else{
  144. var o = targetsStack[targetsStackIndex-1];
  145. o.t = t;
  146. o.x = startX;
  147. o.y = startY;
  148. o.w = width;
  149. o.h = height;
  150. o.hasRZ = hasRenderZone;
  151. o.rzX = renderX;
  152. o.rzY = renderY;
  153. o.rzW = renderW;
  154. o.rzH = renderH;
  155. }
  156. curX = startX;
  157. curY = startY;
  158. curWidth = width;
  159. curHeight = height;
  160. if( hasRenderZone ) clearRenderZone();
  161. }
  162. public function popTarget( restore = true ) {
  163. flush();
  164. if( targetsStackIndex <= 0 ) throw "Too many popTarget()";
  165. var pinf = targetsStack[--targetsStackIndex];
  166. engine.popTarget();
  167. if( restore ) {
  168. var tinf = targetsStack[targetsStackIndex - 1];
  169. var t = tinf == null ? null : tinf.t;
  170. var startX = tinf == null ? 0 : tinf.x;
  171. var startY = tinf == null ? 0 : tinf.y;
  172. var width = tinf == null ? scene.width : tinf.w;
  173. var height = tinf == null ? scene.height : tinf.h;
  174. initShaders(baseShaderList);
  175. baseShader.halfPixelInverse.set(0.5 / (t == null ? engine.width : t.width), 0.5 / (t == null ? engine.height : t.height));
  176. baseShader.viewport.set( -width * 0.5 - startX, -height * 0.5 - startY, 2 / width, -2 / height);
  177. curX = startX;
  178. curY = startY;
  179. curWidth = width;
  180. curHeight = height;
  181. }
  182. if( pinf.hasRZ ) setRenderZone(pinf.rzX, pinf.rzY, pinf.rzW, pinf.rzH);
  183. }
  184. public function setRenderZone( x : Float, y : Float, w : Float, h : Float ) {
  185. hasRenderZone = true;
  186. renderX = x;
  187. renderY = y;
  188. renderW = w;
  189. renderH = h;
  190. var scaleX = engine.width / scene.width;
  191. var scaleY = engine.height / scene.height;
  192. if( inFilter != null ) {
  193. var fa = baseShader.filterMatrixA;
  194. var fb = baseShader.filterMatrixB;
  195. var x2 = x + w, y2 = y + h;
  196. var rx1 = x * fa.x + y * fa.y + fa.z;
  197. var ry1 = x * fb.x + y * fb.y + fb.z;
  198. var rx2 = x2 * fa.x + y2 * fa.y + fa.z;
  199. var ry2 = x2 * fb.x + y2 * fb.y + fb.z;
  200. x = rx1;
  201. y = ry1;
  202. w = rx2 - rx1;
  203. h = ry2 - ry1;
  204. }
  205. engine.setRenderZone(Std.int((x - curX) * scaleX + 1e-10), Std.int((y - curY) * scaleY + 1e-10), Std.int(w * scaleX + 1e-10), Std.int(h * scaleY + 1e-10));
  206. }
  207. public inline function clearRenderZone() {
  208. hasRenderZone = false;
  209. engine.setRenderZone();
  210. }
  211. function drawLayer( layer : Int ) {
  212. @:privateAccess scene.drawLayer(this, layer);
  213. }
  214. public function drawScene() {
  215. @:privateAccess scene.drawRec(this);
  216. }
  217. public inline function flush() {
  218. if( hasBuffering() ) _flush();
  219. }
  220. function _flush() {
  221. if( bufPos == 0 ) return;
  222. beforeDraw();
  223. var nverts = Std.int(bufPos / stride);
  224. var tmp = new h3d.Buffer(nverts, stride, [Quads,Dynamic,RawFormat]);
  225. tmp.uploadVector(buffer, 0, nverts);
  226. engine.renderQuadBuffer(tmp);
  227. tmp.dispose();
  228. bufPos = 0;
  229. texture = null;
  230. }
  231. public function beforeDraw() {
  232. if( texture == null ) texture = h3d.mat.Texture.fromColor(0xFF00FF);
  233. baseShader.texture = texture;
  234. texture.filter = (currentObj.smooth == null ? defaultSmooth : (currentObj.smooth:Bool)) ? Linear : Nearest;
  235. texture.wrap = currentObj.tileWrap && (currentObj.filter == null || inFilter != null) ? Repeat : Clamp;
  236. var blend = currentObj.blendMode;
  237. if( inFilter == currentObj && blend == Erase ) blend = Add; // add THEN erase
  238. if( blend != currentBlend ) {
  239. currentBlend = blend;
  240. pass.setBlendMode(blend);
  241. }
  242. manager.fillParams(buffers, compiledShader, currentShaders);
  243. engine.selectMaterial(pass);
  244. engine.uploadShaderBuffers(buffers, Params);
  245. engine.uploadShaderBuffers(buffers, Textures);
  246. }
  247. @:access(h2d.Drawable)
  248. public function beginDrawObject( obj : h2d.Drawable, texture : h3d.mat.Texture ) {
  249. if ( !beginDraw(obj, texture, true) ) return false;
  250. if( inFilter == obj )
  251. baseShader.color.set(1,1,1,1);
  252. else
  253. baseShader.color.set(obj.color.r, obj.color.g, obj.color.b, obj.color.a * globalAlpha);
  254. baseShader.absoluteMatrixA.set(obj.matA, obj.matC, obj.absX);
  255. baseShader.absoluteMatrixB.set(obj.matB, obj.matD, obj.absY);
  256. beforeDraw();
  257. return true;
  258. }
  259. @:access(h2d.Drawable)
  260. public function beginDrawBatch( obj : h2d.Drawable, texture : h3d.mat.Texture ) {
  261. return beginDraw(obj, texture, false);
  262. }
  263. @:access(h2d.Drawable)
  264. public function drawTile( obj : h2d.Drawable, tile : h2d.Tile ) {
  265. var matA, matB, matC, matD, absX, absY;
  266. if( inFilter != null ) {
  267. var f1 = baseShader.filterMatrixA;
  268. var f2 = baseShader.filterMatrixB;
  269. matA = obj.matA * f1.x + obj.matB * f1.y;
  270. matB = obj.matA * f2.x + obj.matB * f2.y;
  271. matC = obj.matC * f1.x + obj.matD * f1.y;
  272. matD = obj.matC * f2.x + obj.matD * f2.y;
  273. absX = obj.absX * f1.x + obj.absY * f1.y + f1.z;
  274. absY = obj.absX * f2.x + obj.absY * f2.y + f2.z;
  275. } else {
  276. matA = obj.matA;
  277. matB = obj.matB;
  278. matC = obj.matC;
  279. matD = obj.matD;
  280. absX = obj.absX;
  281. absY = obj.absY;
  282. }
  283. // check if our tile is outside of the viewport
  284. if( matB == 0 && matC == 0 ) {
  285. var tx = tile.dx + tile.width * 0.5;
  286. var ty = tile.dy + tile.height * 0.5;
  287. var tr = (tile.width > tile.height ? tile.width : tile.height) * 1.5 * hxd.Math.max(hxd.Math.abs(obj.matA),hxd.Math.abs(obj.matD));
  288. var cx = absX + tx * matA - curX;
  289. var cy = absY + ty * matD - curY;
  290. if( cx < -tr || cy < -tr || cx - tr > curWidth || cy - tr > curHeight ) return false;
  291. } else {
  292. var xMin = 1e20, yMin = 1e20, xMax = -1e20, yMax = -1e20;
  293. inline function calc(x:Int, y:Int) {
  294. var px = (x + tile.dx) * matA + (y + tile.dy) * matC;
  295. var py = (x + tile.dx) * matB + (y + tile.dy) * matD;
  296. if( px < xMin ) xMin = px;
  297. if( px > xMax ) xMax = px;
  298. if( py < yMin ) yMin = py;
  299. if( py > yMax ) yMax = py;
  300. }
  301. var hw = tile.width * 0.5;
  302. var hh = tile.height * 0.5;
  303. calc(0, 0);
  304. calc(tile.width, 0);
  305. calc(0, tile.height);
  306. calc(tile.width, tile.height);
  307. var cx = absX - curX;
  308. var cy = absY - curY;
  309. if( cx + xMax < 0 || cy + yMax < 0 || cx + xMin > curWidth || cy + yMin > curHeight )
  310. return false;
  311. }
  312. if( !beginDraw(obj, tile.getTexture(), true, true) ) return false;
  313. if( inFilter == obj )
  314. baseShader.color.set(1, 1, 1, 1);
  315. else
  316. baseShader.color.set(obj.color.r, obj.color.g, obj.color.b, obj.color.a * globalAlpha);
  317. baseShader.absoluteMatrixA.set(tile.width * obj.matA, tile.height * obj.matC, obj.absX + tile.dx * obj.matA + tile.dy * obj.matC);
  318. baseShader.absoluteMatrixB.set(tile.width * obj.matB, tile.height * obj.matD, obj.absY + tile.dx * obj.matB + tile.dy * obj.matD);
  319. baseShader.uvPos.set(tile.u, tile.v, tile.u2 - tile.u, tile.v2 - tile.v);
  320. beforeDraw();
  321. if( fixedBuffer == null || fixedBuffer.isDisposed() ) {
  322. fixedBuffer = new h3d.Buffer(4, 8, [Quads, RawFormat]);
  323. var k = new hxd.FloatBuffer();
  324. for( v in [0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] )
  325. k.push(v);
  326. fixedBuffer.uploadVector(k, 0, 4);
  327. }
  328. engine.renderQuadBuffer(fixedBuffer);
  329. return true;
  330. }
  331. @:access(h2d.Drawable)
  332. function beginDraw( obj : h2d.Drawable, texture : h3d.mat.Texture, isRelative : Bool, hasUVPos = false ) {
  333. if( onBeginDraw != null && !onBeginDraw(obj) )
  334. return false;
  335. var stride = 8;
  336. if( hasBuffering() && currentObj != null && (texture != this.texture || stride != this.stride || obj.blendMode != currentObj.blendMode || obj.filter != currentObj.filter) )
  337. flush();
  338. var shaderChanged = false, paramsChanged = false;
  339. var objShaders = obj.shaders;
  340. var curShaders = currentShaders.next;
  341. while( objShaders != null && curShaders != null ) {
  342. var s = objShaders.s;
  343. var t = curShaders.s;
  344. objShaders = objShaders.next;
  345. curShaders = curShaders.next;
  346. var prevInst = @:privateAccess t.instance;
  347. if( s != t )
  348. paramsChanged = true;
  349. s.updateConstants(manager.globals);
  350. if( @:privateAccess s.instance != prevInst )
  351. shaderChanged = true;
  352. }
  353. if( objShaders != null || curShaders != null || baseShader.isRelative != isRelative || baseShader.hasUVPos != hasUVPos || baseShader.killAlpha != killAlpha )
  354. shaderChanged = true;
  355. if( shaderChanged ) {
  356. flush();
  357. baseShader.hasUVPos = hasUVPos;
  358. baseShader.isRelative = isRelative;
  359. baseShader.killAlpha = killAlpha;
  360. baseShader.updateConstants(manager.globals);
  361. baseShaderList.next = obj.shaders;
  362. initShaders(baseShaderList);
  363. } else if( paramsChanged ) {
  364. flush();
  365. if( currentShaders != baseShaderList ) throw "!";
  366. // the next flush will fetch their params
  367. currentShaders.next = obj.shaders;
  368. }
  369. this.texture = texture;
  370. this.stride = stride;
  371. this.currentObj = obj;
  372. return true;
  373. }
  374. }