Scene.hx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. package hide.comp;
  2. @:access(hide.comp.Scene)
  3. class SceneLoader extends hxd.fmt.hsd.Serializer {
  4. var ide : hide.Ide;
  5. var hsdPath : String;
  6. var projectPath : String;
  7. var scene : Scene;
  8. public function new(hsdPath, scene) {
  9. ide = hide.Ide.inst;
  10. super();
  11. this.hsdPath = hsdPath;
  12. this.scene = scene;
  13. }
  14. override function initHSDPaths(resPath:String, projectPath:String) {
  15. this.resPath = resPath.split("\\").join("/");
  16. this.projectPath = projectPath == null ? null : projectPath.split("\\").join("/");
  17. }
  18. override function loadShader(name:String) : hxsl.Shader {
  19. return ide.shaderLoader.load(name);
  20. }
  21. override function loadHMD(path:String) {
  22. var path = resolvePath(path);
  23. if( path == null )
  24. throw "Missing HMD file " + path;
  25. return scene.loadHMD(path, false);
  26. }
  27. override function resolveTexture(path:String) {
  28. var path = resolvePath(path);
  29. if( path == null )
  30. return h3d.mat.Texture.fromColor(0xFF00FF);
  31. return scene.loadTexture(hsdPath, path);
  32. }
  33. function resolvePath( path : String ) {
  34. var p = null;
  35. if( projectPath != null )
  36. p = scene.resolvePath(projectPath + resPath + "/" + hsdPath.split("/").pop(), path);
  37. if( p == null )
  38. p = scene.resolvePath(hsdPath, path);
  39. return p;
  40. }
  41. }
  42. class Scene extends Component implements h3d.IDrawable {
  43. static var UID = 0;
  44. var id = ++UID;
  45. var window : hxd.Window;
  46. var canvas : js.html.CanvasElement;
  47. var hmdCache = new Map<String, hxd.fmt.hmd.Library>();
  48. var texCache = new Map<String, h3d.mat.Texture>();
  49. var pathsMap = new Map<String, String>();
  50. var cleanup = new Array<Void->Void>();
  51. var defaultCamera : h3d.Camera;
  52. public var config : hide.Config;
  53. public var engine : h3d.Engine;
  54. public var width(get, never) : Int;
  55. public var height(get, never) : Int;
  56. public var s2d : h2d.Scene;
  57. public var s3d : h3d.scene.Scene;
  58. public var sevents : hxd.SceneEvents;
  59. public var speed : Float = 1.0;
  60. public var visible(default, null) : Bool = true;
  61. public var editor : hide.comp.SceneEditor;
  62. public var refreshIfUnfocused = false;
  63. public function new(config, parent, el) {
  64. super(parent,el);
  65. this.config = config;
  66. element.addClass("hide-scene-container");
  67. canvas = cast new Element("<canvas class='hide-scene' style='width:100%;height:100%'/>").appendTo(element)[0];
  68. canvas.addEventListener("mousemove",function(_) canvas.focus());
  69. canvas.addEventListener("mouseleave",function(_) canvas.blur());
  70. canvas.oncontextmenu = function(e){
  71. e.stopPropagation();
  72. e.preventDefault();
  73. return false;
  74. };
  75. untyped canvas.__scene = this;
  76. haxe.Timer.delay(delayedInit,0); // wait canvas added to window
  77. }
  78. public function dispose() {
  79. for( c in cleanup )
  80. c();
  81. cleanup = [];
  82. engine.dispose();
  83. }
  84. function delayedInit() {
  85. canvas.id = "webgl";
  86. window = @:privateAccess new hxd.Window(canvas);
  87. window.propagateKeyEvents = true;
  88. window.setCurrent();
  89. engine = new h3d.Engine();
  90. @:privateAccess engine.resCache.set(Scene, this);
  91. engine.backgroundColor = 0xFF111111;
  92. canvas.id = null;
  93. engine.onReady = function() {
  94. new Element(canvas).on("resize", function() {
  95. @:privateAccess window.checkResize();
  96. });
  97. hxd.Key.initialize();
  98. engine.setCurrent();
  99. window.setCurrent();
  100. s2d = new h2d.Scene();
  101. s3d = new h3d.scene.Scene();
  102. sevents = new hxd.SceneEvents(window);
  103. sevents.addScene(s2d);
  104. sevents.addScene(s3d);
  105. onReady();
  106. onResize();
  107. sync();
  108. ide.registerUpdate(sync);
  109. };
  110. engine.onResized = function() {
  111. if( s2d == null ) return;
  112. visible = engine.width > 32 && engine.height > 32; // 32x32 when hidden !
  113. s2d.setFixedSize(engine.width, engine.height);
  114. onResize();
  115. };
  116. engine.init();
  117. }
  118. function get_width() {
  119. return engine.width;
  120. }
  121. function get_height() {
  122. return engine.height;
  123. }
  124. public function init( ?root : h3d.scene.Object ) {
  125. var autoHide : Array<String> = config.get("scene.autoHide");
  126. function initRec( obj : h3d.scene.Object ) {
  127. for(n in autoHide)
  128. if(obj.name != null && obj.name.indexOf(n) == 0)
  129. obj.visible = false;
  130. for( o in obj )
  131. initRec(o);
  132. }
  133. if( root == null ) {
  134. root = s3d;
  135. engine.backgroundColor = Std.parseInt("0x"+config.get("scene.backgroundColor").substr(1)) | 0xFF000000;
  136. }
  137. initRec(root);
  138. }
  139. public function setCurrent() {
  140. engine.setCurrent();
  141. window.setCurrent();
  142. }
  143. function checkCurrent() {
  144. if( h3d.Engine.getCurrent() != engine )
  145. throw "Invalid current engine : use setCurrent() first";
  146. }
  147. function sync() {
  148. if( new Element(canvas).parents("html").length == 0 ) {
  149. window.dispose();
  150. ide.unregisterUpdate(sync);
  151. return;
  152. }
  153. if( !visible || (!Ide.inst.isFocused && !refreshIfUnfocused) )
  154. return;
  155. refreshIfUnfocused = false;
  156. setCurrent();
  157. sevents.checkEvents();
  158. var dt = hxd.Timer.tmod * speed / 60;
  159. s2d.setElapsedTime(dt);
  160. s3d.setElapsedTime(dt);
  161. onUpdate(dt);
  162. engine.render(this);
  163. }
  164. function loadTextureData( img : hxd.res.Image, onReady : h3d.mat.Texture -> Void, ?target : h3d.mat.Texture ) {
  165. if( img.entry.extension == "tga" ) {
  166. // immediate read
  167. var pix = img.getPixels();
  168. if( target == null )
  169. target = new h3d.mat.Texture(pix.width, pix.height);
  170. else
  171. target.resize(pix.width, pix.height);
  172. target.uploadPixels(pix);
  173. if( onReady != null ) {
  174. onReady(target);
  175. onReady = null;
  176. }
  177. return;
  178. }
  179. var path = ide.getPath(img.entry.path);
  180. var img = new Element('<img src="file://$path" crossorigin="anonymous"/>');
  181. function onLoaded() {
  182. setCurrent();
  183. var bmp : js.html.ImageElement = cast img[0];
  184. var t;
  185. if( target == null )
  186. t = target = new h3d.mat.Texture(bmp.width, bmp.height);
  187. else {
  188. t = target;
  189. target.resize(bmp.width, bmp.height);
  190. }
  191. untyped bmp.ctx = { getImageData : function(_) return bmp, canvas : { width : 0, height : 0 } };
  192. engine.driver.uploadTextureBitmap(t, cast bmp, 0, 0);
  193. t.realloc = onLoaded;
  194. t.flags.unset(Loading);
  195. @:privateAccess if( t.waitLoads != null ) {
  196. var arr = t.waitLoads;
  197. t.waitLoads = null;
  198. for( f in arr ) f();
  199. }
  200. if( onReady != null ) {
  201. onReady(t);
  202. onReady = null;
  203. }
  204. }
  205. img.on("load", onLoaded);
  206. var w = js.node.Fs.watch(path, function(_, _) {
  207. img.attr("src", 'file://$path?t='+Date.now().getTime());
  208. });
  209. cleanup.push(w.close);
  210. }
  211. public function listAnims( path : String ) {
  212. if( StringTools.endsWith(path.toLowerCase(), ".hsd") )
  213. return [];
  214. var config = hide.Config.loadForFile(ide, path);
  215. var dirs : Array<String> = config.get("hmd.animPaths");
  216. if( dirs == null ) dirs = [];
  217. dirs = [for( d in dirs ) ide.resourceDir + d];
  218. var parts = path.split("/");
  219. parts.pop();
  220. dirs.unshift(ide.getPath(parts.join("/")));
  221. var anims = [];
  222. var lib = loadHMD(path, false);
  223. if( lib.header.animations.length > 0 )
  224. anims.push(ide.getPath(path));
  225. for( dir in dirs ) {
  226. var dir = dir;
  227. if( StringTools.endsWith(dir, "/") ) dir = dir.substr(0,-1);
  228. for( f in try sys.FileSystem.readDirectory(dir) catch( e : Dynamic ) [] ) {
  229. var file = f.toLowerCase();
  230. if( StringTools.startsWith(f,"Anim_") && (StringTools.endsWith(file,".hmd") || StringTools.endsWith(file,".fbx")) )
  231. anims.push(dir+"/"+f);
  232. }
  233. }
  234. return anims;
  235. }
  236. public function animationName( path : String ) {
  237. var name = path.split("/").pop();
  238. if( StringTools.startsWith(name, "Anim_") )
  239. name = name.substr(5);
  240. name = name.substr(0, -4);
  241. if( StringTools.endsWith(name,"_loop") )
  242. name = name.substr(0,-5);
  243. return name;
  244. }
  245. function loadHSD( path : String ) {
  246. var ctx = new SceneLoader(path,this);
  247. var fullPath = ide.getPath(path);
  248. var bytes = sys.io.File.getBytes(fullPath);
  249. var root = new h3d.scene.Object();
  250. var hsd = ctx.loadHSD(bytes);
  251. if( hsd.content.length == 1 )
  252. root = hsd.content[0];
  253. else {
  254. for( o in hsd.content )
  255. root.addChild(o);
  256. }
  257. return { root : root, camera : hsd.camera };
  258. }
  259. public function loadModel( path : String, mainScene = false ) {
  260. checkCurrent();
  261. if( StringTools.endsWith(path.toLowerCase(), ".hsd") ) {
  262. var hsd = loadHSD(path);
  263. if( mainScene ) defaultCamera = hsd.camera;
  264. return hsd.root;
  265. }
  266. var lib = loadHMD(path,false);
  267. return lib.makeObject(loadTexture.bind(path));
  268. }
  269. public function loadAnimation( path : String ) {
  270. var lib = loadHMD(path,true);
  271. return lib.loadAnimation();
  272. }
  273. function resolvePathImpl( modelPath : String, filePath : String ) {
  274. inline function exists(path) return sys.FileSystem.exists(path);
  275. var fullPath = ide.getPath(filePath);
  276. if( exists(fullPath) )
  277. return fullPath;
  278. // swap drive letter
  279. if( fullPath.charAt(1) == ":" && fullPath.charAt(0) != ide.projectDir.charAt(0) ) {
  280. fullPath = ide.projectDir.charAt(0) + fullPath.substr(1);
  281. if( exists(fullPath) )
  282. return fullPath;
  283. }
  284. if( modelPath == null )
  285. return null;
  286. filePath = filePath.split("\\").join("/");
  287. modelPath = ide.getPath(modelPath);
  288. var path = modelPath.split("/");
  289. path.pop();
  290. var relToModel = path.join("/") + "/" + filePath.split("/").pop();
  291. if( exists(relToModel) )
  292. return relToModel;
  293. return null;
  294. }
  295. function resolvePath(modelPath : String, filePath : String) {
  296. var key = modelPath + ":" + filePath;
  297. var p = pathsMap.get(key);
  298. if(p != null)
  299. return p;
  300. p = resolvePathImpl(modelPath, filePath);
  301. pathsMap.set(key, p);
  302. return p;
  303. }
  304. public function loadTextureDotPath( path : String, ?onReady ) {
  305. var path = path.split(".").join("/");
  306. var t = resolvePath(null, path + ".png");
  307. if( t == null )
  308. t = resolvePath(null, path + ".jpg");
  309. if( t == null )
  310. t = resolvePath(null, path + ".jpeg");
  311. if( t == null )
  312. t = path;
  313. return loadTexture("", t, onReady);
  314. }
  315. public function loadTexture( modelPath : String, texturePath : String, ?onReady : h3d.mat.Texture -> Void ) {
  316. checkCurrent();
  317. var path = resolvePath(modelPath, texturePath);
  318. if( path == null ) {
  319. ide.error("Could not load texture " + { modelPath : modelPath, texturePath : texturePath });
  320. return null;
  321. }
  322. var t = texCache.get(path);
  323. if( t != null ) {
  324. if( onReady != null ) haxe.Timer.delay(onReady.bind(t), 1);
  325. return t;
  326. }
  327. var bytes = sys.io.File.getBytes(path);
  328. var img = hxd.res.Any.fromBytes(path, bytes).toImage();
  329. var size = img.getSize();
  330. t = new h3d.mat.Texture(size.width,size.height);
  331. t.clear(0x102030);
  332. t.flags.set(Loading);
  333. t.name = ide.makeRelative(path);
  334. texCache.set(path, t);
  335. if( onReady == null ) onReady = function(_) {};
  336. loadTextureData(img, onReady, t);
  337. return t;
  338. }
  339. function loadHMD( path : String, isAnimation : Bool ) {
  340. checkCurrent();
  341. var fullPath = ide.getPath(path);
  342. var key = fullPath;
  343. var hmd = hmdCache.get(key);
  344. if( hmd != null )
  345. return hmd;
  346. var relPath = StringTools.startsWith(path, ide.resourceDir) ? path.substr(ide.resourceDir.length+1) : path;
  347. var e = try hxd.res.Loader.currentInstance.load(relPath) catch( e : hxd.res.NotFound ) null;
  348. if( e == null ) {
  349. var data = sys.io.File.getBytes(fullPath);
  350. if( data.get(0) != 'H'.code ) {
  351. var hmdOut = new hxd.fmt.fbx.HMDOut(fullPath);
  352. hmdOut.absoluteTexturePath = true;
  353. hmdOut.loadFile(data);
  354. var hmd = hmdOut.toHMD(null, !isAnimation);
  355. var out = new haxe.io.BytesOutput();
  356. new hxd.fmt.hmd.Writer(out).write(hmd);
  357. data = out.getBytes();
  358. }
  359. hmd = hxd.res.Any.fromBytes(path, data).toModel().toHmd();
  360. } else {
  361. hmd = e.toModel().toHmd();
  362. }
  363. hmdCache.set(key, hmd);
  364. return hmd;
  365. }
  366. public function resetCamera( ?obj : h3d.scene.Object, distanceFactor = 1. ) {
  367. if( defaultCamera != null ) {
  368. s3d.camera.load(defaultCamera);
  369. return;
  370. }
  371. if( obj == null ) obj = s3d;
  372. var b = obj.getBounds();
  373. if( b.isEmpty() )
  374. return;
  375. var dx = Math.max(Math.abs(b.xMax),Math.abs(b.xMin));
  376. var dy = Math.max(Math.abs(b.yMax),Math.abs(b.yMin));
  377. var dz = Math.max(Math.abs(b.zMax),Math.abs(b.zMin));
  378. var dist = Math.max(Math.max(dx * 6, dy * 6), dz * 4) * distanceFactor;
  379. var ang = Math.PI / 4;
  380. var zang = Math.PI * 0.4;
  381. s3d.camera.pos.set(Math.sin(zang) * Math.cos(ang) * dist, Math.sin(zang) * Math.sin(ang) * dist, Math.cos(zang) * dist);
  382. s3d.camera.target.set(0, 0, (b.zMax + b.zMin) * 0.5);
  383. }
  384. public function render( e : h3d.Engine ) {
  385. s3d.render(e);
  386. s2d.render(e);
  387. }
  388. public dynamic function onUpdate(dt:Float) {
  389. }
  390. public dynamic function onReady() {
  391. }
  392. public dynamic function onResize() {
  393. }
  394. public static function getNearest( e : Element ) : Scene {
  395. while( e.length > 0 ) {
  396. var c : Dynamic = e.find("canvas")[0];
  397. if( c != null && c.__scene != null )
  398. return c.__scene;
  399. e = e.parent();
  400. }
  401. return null;
  402. }
  403. public static function getCurrent() : Scene {
  404. return @:privateAccess h3d.Engine.getCurrent().resCache.get(Scene);
  405. }
  406. }