Engine.hx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. package h3d;
  2. import h3d.mat.Data;
  3. private class TargetTmp {
  4. public var t : h3d.mat.Texture;
  5. public var next : TargetTmp;
  6. public function new(t, n) {
  7. this.t = t;
  8. this.next = n;
  9. }
  10. }
  11. class Engine {
  12. public var driver(default,null) : h3d.impl.Driver;
  13. public var mem(default,null) : h3d.impl.MemoryManager;
  14. public var hardware(default, null) : Bool;
  15. public var width(default, null) : Int;
  16. public var height(default, null) : Int;
  17. public var debug(default, set) : Bool;
  18. public var drawTriangles(default, null) : Int;
  19. public var drawCalls(default, null) : Int;
  20. public var shaderSwitches(default, null) : Int;
  21. public var backgroundColor : Null<Int> = 0xFF000000;
  22. public var autoResize : Bool;
  23. public var fullScreen(default, set) : Bool;
  24. public var fps(get, never) : Float;
  25. public var frameCount : Int = 0;
  26. var realFps : Float;
  27. var lastTime : Float;
  28. var antiAlias : Int;
  29. var tmpVector = new h3d.Vector();
  30. var stage : hxd.Stage;
  31. var targetTmp : TargetTmp;
  32. var targetStack : TargetTmp;
  33. var currentTarget : h3d.mat.Texture;
  34. var needFlushTarget : Bool;
  35. var nullTexture : h3d.mat.Texture;
  36. var textureColorCache = new Map<Int,h3d.mat.Texture>();
  37. @:allow(hxd.res) var resCache = new Map<{},Dynamic>();
  38. @:access(hxd.Stage)
  39. public function new( hardware = true, aa = 0 ) {
  40. this.hardware = hardware;
  41. this.antiAlias = aa;
  42. this.autoResize = true;
  43. fullScreen = !hxd.System.getValue(IsWindowed);
  44. stage = hxd.Stage.getInstance();
  45. realFps = hxd.System.getDefaultFrameRate();
  46. lastTime = haxe.Timer.stamp();
  47. stage.addResizeEvent(onStageResize);
  48. #if (js || cpp || hlsdl || psgl)
  49. driver = new h3d.impl.GlDriver(antiAlias);
  50. #elseif flash
  51. driver = new h3d.impl.Stage3dDriver(antiAlias);
  52. #elseif hldx
  53. driver = new h3d.impl.DirectXDriver();
  54. #else
  55. #if sys Sys.println #else trace #end("No output driver available.");
  56. driver = new h3d.impl.LogDriver(new h3d.impl.NullDriver());
  57. driver.logEnable = true;
  58. #end
  59. setCurrent();
  60. }
  61. static var CURRENT : Engine = null;
  62. public function setDriver(d) {
  63. driver = d;
  64. if( mem != null ) mem.driver = d;
  65. }
  66. public static inline function getCurrent() {
  67. return CURRENT;
  68. }
  69. public inline function setCurrent() {
  70. CURRENT = this;
  71. }
  72. public function init() {
  73. driver.init(onCreate, !hardware);
  74. }
  75. public function driverName(details=false) {
  76. return driver.getDriverName(details);
  77. }
  78. public function selectShader( shader : hxsl.RuntimeShader ) {
  79. flushTarget();
  80. if( driver.selectShader(shader) )
  81. shaderSwitches++;
  82. }
  83. public function selectMaterial( pass : h3d.mat.Pass ) {
  84. driver.selectMaterial(pass);
  85. }
  86. public function uploadShaderBuffers(buffers, which) {
  87. driver.uploadShaderBuffers(buffers, which);
  88. }
  89. function selectBuffer( buf : Buffer ) {
  90. if( buf.isDisposed() )
  91. return false;
  92. flushTarget();
  93. driver.selectBuffer(buf);
  94. return true;
  95. }
  96. public inline function renderTriBuffer( b : Buffer, start = 0, max = -1 ) {
  97. return renderBuffer(b, mem.triIndexes, 3, start, max);
  98. }
  99. public inline function renderQuadBuffer( b : Buffer, start = 0, max = -1 ) {
  100. return renderBuffer(b, mem.quadIndexes, 2, start, max);
  101. }
  102. // we use preallocated indexes so all the triangles are stored inside our buffers
  103. function renderBuffer( b : Buffer, indexes : Indexes, vertPerTri : Int, startTri = 0, drawTri = -1 ) {
  104. if( indexes.isDisposed() )
  105. return;
  106. do {
  107. var ntri = Std.int(b.vertices / vertPerTri);
  108. var pos = Std.int(b.position / vertPerTri);
  109. if( startTri > 0 ) {
  110. if( startTri >= ntri ) {
  111. startTri -= ntri;
  112. b = b.next;
  113. continue;
  114. }
  115. pos += startTri;
  116. ntri -= startTri;
  117. startTri = 0;
  118. }
  119. if( drawTri >= 0 ) {
  120. if( drawTri == 0 ) return;
  121. drawTri -= ntri;
  122. if( drawTri < 0 ) {
  123. ntri += drawTri;
  124. drawTri = 0;
  125. }
  126. }
  127. if( ntri > 0 && selectBuffer(b) ) {
  128. // *3 because it's the position in indexes which are always by 3
  129. driver.draw(indexes.ibuf, pos * 3, ntri);
  130. drawTriangles += ntri;
  131. drawCalls++;
  132. }
  133. b = b.next;
  134. } while( b != null );
  135. }
  136. // we use custom indexes, so the number of triangles is the number of indexes/3
  137. public function renderIndexed( b : Buffer, indexes : Indexes, startTri = 0, drawTri = -1 ) {
  138. if( b.next != null )
  139. throw "Buffer is split";
  140. if( indexes.isDisposed() )
  141. return;
  142. var maxTri = Std.int(indexes.count / 3);
  143. if( drawTri < 0 ) drawTri = maxTri - startTri;
  144. if( drawTri > 0 && selectBuffer(b) ) {
  145. // *3 because it's the position in indexes which are always by 3
  146. driver.draw(indexes.ibuf, startTri * 3, drawTri);
  147. drawTriangles += drawTri;
  148. drawCalls++;
  149. }
  150. }
  151. public function renderMultiBuffers( buffers : Buffer.BufferOffset, indexes : Indexes, startTri = 0, drawTri = -1 ) {
  152. var maxTri = Std.int(indexes.count / 3);
  153. if( maxTri <= 0 ) return;
  154. flushTarget();
  155. driver.selectMultiBuffers(buffers);
  156. if( indexes.isDisposed() )
  157. return;
  158. if( drawTri < 0 ) drawTri = maxTri - startTri;
  159. if( drawTri > 0 ) {
  160. // render
  161. driver.draw(indexes.ibuf, startTri * 3, drawTri);
  162. drawTriangles += drawTri;
  163. drawCalls++;
  164. }
  165. }
  166. function set_debug(d) {
  167. debug = d;
  168. driver.setDebug(debug);
  169. return d;
  170. }
  171. function onCreate( disposed ) {
  172. setCurrent();
  173. if( autoResize ) {
  174. width = stage.width;
  175. height = stage.height;
  176. }
  177. if( disposed )
  178. mem.onContextLost();
  179. else {
  180. mem = new h3d.impl.MemoryManager(driver);
  181. mem.init();
  182. }
  183. hardware = driver.hasFeature(HardwareAccelerated);
  184. set_debug(debug);
  185. set_fullScreen(fullScreen);
  186. resize(width, height);
  187. if( disposed )
  188. onContextLost();
  189. else
  190. onReady();
  191. }
  192. public dynamic function onContextLost() {
  193. }
  194. public dynamic function onReady() {
  195. }
  196. function onStageResize() {
  197. if( autoResize && !driver.isDisposed() ) {
  198. var w = stage.width, h = stage.height;
  199. if( w != width || h != height )
  200. resize(w, h);
  201. onResized();
  202. }
  203. }
  204. function set_fullScreen(v) {
  205. fullScreen = v;
  206. if( mem != null && hxd.System.getValue(IsWindowed) )
  207. stage.setFullScreen(v);
  208. return v;
  209. }
  210. public dynamic function onResized() {
  211. }
  212. public function resize(width, height) {
  213. // minimum 32x32 size
  214. if( width < 32 ) width = 32;
  215. if( height < 32 ) height = 32;
  216. this.width = width;
  217. this.height = height;
  218. if( !driver.isDisposed() ) driver.resize(width, height);
  219. }
  220. public function begin() {
  221. if( driver.isDisposed() )
  222. return false;
  223. // init
  224. frameCount++;
  225. drawTriangles = 0;
  226. shaderSwitches = 0;
  227. drawCalls = 0;
  228. targetStack = null;
  229. needFlushTarget = currentTarget != null;
  230. driver.begin(frameCount);
  231. if( backgroundColor != null ) clear(backgroundColor, 1, 0);
  232. return true;
  233. }
  234. public function hasFeature(f) {
  235. return driver.hasFeature(f);
  236. }
  237. public function end() {
  238. driver.present();
  239. }
  240. public function getCurrentTarget() {
  241. return targetStack == null ? null : targetStack.t;
  242. }
  243. public function pushTarget( tex : h3d.mat.Texture ) {
  244. var c = targetTmp;
  245. if( c == null )
  246. c = new TargetTmp(tex, targetStack);
  247. else {
  248. targetTmp = c.next;
  249. c.t = tex;
  250. c.next = targetStack;
  251. }
  252. targetStack = c;
  253. needFlushTarget = currentTarget != tex;
  254. }
  255. public function pushTargets( textures : Array<h3d.mat.Texture> ) {
  256. if( nullTexture == null ) nullTexture = new h3d.mat.Texture(0, 0, [NoAlloc]);
  257. pushTarget(nullTexture);
  258. driver.setRenderTargets(textures);
  259. currentTarget = nullTexture;
  260. needFlushTarget = false;
  261. }
  262. public function popTarget() {
  263. var c = targetStack;
  264. if( c == null )
  265. throw "popTarget() with no matching pushTarget()";
  266. targetStack = c.next;
  267. var tex = targetStack == null ? null : targetStack.t;
  268. needFlushTarget = currentTarget != tex;
  269. // recycle
  270. c.t = null;
  271. c.next = targetTmp;
  272. targetTmp = c;
  273. }
  274. inline function flushTarget() {
  275. if( needFlushTarget ) doFlushTarget();
  276. }
  277. function doFlushTarget() {
  278. var tex = targetStack == null ? null : targetStack.t;
  279. currentTarget = tex;
  280. driver.setRenderTarget(tex);
  281. needFlushTarget = false;
  282. }
  283. public function clear( ?color : Int, ?depth : Float, ?stencil : Int ) {
  284. if( color != null )
  285. tmpVector.setColor(color);
  286. flushTarget();
  287. driver.clear(color == null ? null : tmpVector, depth, stencil);
  288. }
  289. /**
  290. * Sets up a scissored zone to eliminate pixels outside the given range.
  291. * Call with no parameters to reset to full viewport.
  292. */
  293. public function setRenderZone( x = 0, y = 0, ?width = -1, ?height = -1 ) : Void {
  294. driver.setRenderZone(x, y, width, height);
  295. }
  296. public function render( obj : { function render( engine : Engine ) : Void; } ) {
  297. if( !begin() ) return false;
  298. obj.render(this);
  299. end();
  300. var delta = haxe.Timer.stamp() - lastTime;
  301. lastTime += delta;
  302. if( delta > 0 ) {
  303. var curFps = 1. / delta;
  304. if( curFps > realFps * 2 ) curFps = realFps * 2 else if( curFps < realFps * 0.5 ) curFps = realFps * 0.5;
  305. var f = delta / .5;
  306. if( f > 0.3 ) f = 0.3;
  307. realFps = realFps * (1 - f) + curFps * f; // smooth a bit the fps
  308. }
  309. return true;
  310. }
  311. public function dispose() {
  312. driver.dispose();
  313. stage.removeResizeEvent(onStageResize);
  314. }
  315. function get_fps() {
  316. return Math.ceil(realFps * 100) / 100;
  317. }
  318. }