ContextShared.hx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. package hrt.prefab;
  2. typedef ShaderDef = {
  3. var shader : hxsl.SharedShader;
  4. var inits : Array<{ variable : hxsl.Ast.TVar, value : Dynamic }>;
  5. }
  6. typedef ShaderDefCache = Map<String, ShaderDef>;
  7. class ContextShared {
  8. public var root2d : h2d.Object;
  9. public var root3d : h3d.scene.Object;
  10. public var contexts : Map<Prefab,Context>;
  11. public var currentPath : String;
  12. public var editorDisplay : Bool;
  13. /**
  14. When make() is called on prefab, it will instead call customMake on
  15. each child with current which can either intercept or call make() recursively.
  16. **/
  17. public var customMake : Context -> Prefab -> Void;
  18. /**
  19. If is a reference to another prefab file, this is the parent prefab.
  20. See refContexts for children.
  21. **/
  22. public var parent : { prefab : Prefab, shared : ContextShared };
  23. var cache : h3d.prim.ModelCache;
  24. var shaderCache : ShaderDefCache;
  25. var bakedData : Map<String, haxe.io.Bytes>;
  26. /**
  27. References to prefab within the same scene
  28. **/
  29. var sceneReferences : Map<Prefab,Array<Context>>;
  30. /**
  31. Contexts of references to other prefabs
  32. **/
  33. var refsContexts : Map<Prefab, ContextShared>;
  34. public function new( ?res : hxd.res.Resource ) {
  35. root2d = new h2d.Object();
  36. root3d = new h3d.scene.Object();
  37. contexts = new Map();
  38. cache = new h3d.prim.ModelCache();
  39. shaderCache = new ShaderDefCache();
  40. sceneReferences = new Map();
  41. refsContexts = new Map();
  42. if( res != null ) currentPath = res.entry.path;
  43. }
  44. public function onError( e : Dynamic ) {
  45. throw e;
  46. }
  47. public function elements() {
  48. return [for(e in contexts.keys()) e];
  49. }
  50. public function getContexts(p: Prefab) : Array<Context> {
  51. var ret : Array<Context> = [];
  52. var ctx = contexts.get(p);
  53. if(ctx != null)
  54. ret.push(ctx);
  55. var ctxs = sceneReferences.get(p);
  56. if( ctxs != null )
  57. for( v in ctxs )
  58. ret.push(v);
  59. for( ref in refsContexts )
  60. for( v in ref.getContexts(p) )
  61. ret.push(v);
  62. return ret;
  63. }
  64. public function find<T:hrt.prefab.Prefab>( cl : Class<T>, ?name, ?references ) : T {
  65. for( p in contexts.keys() ) {
  66. var v = Std.downcast(p,cl);
  67. if( v != null && (name == null || v.name == name) ) return v;
  68. }
  69. if( references ) {
  70. for( ref in refsContexts ) {
  71. var v = ref.find(cl, name, true);
  72. if( v != null ) return v;
  73. }
  74. }
  75. return null;
  76. }
  77. public function getRef( prefab : Prefab ) {
  78. return refsContexts.get(prefab);
  79. }
  80. public function cloneRef( prefab : Prefab, newPath : String ) {
  81. var ctx = contexts.get(prefab);
  82. if( ctx == null )
  83. throw "Prefab reference has no context created";
  84. var sh = refsContexts.get(prefab);
  85. if( sh != null ) {
  86. sh.root2d = ctx.local2d;
  87. sh.root3d = ctx.local3d;
  88. return sh;
  89. }
  90. sh = allocForRef();
  91. refsContexts.set(prefab, sh);
  92. sh.root2d = ctx.local2d;
  93. sh.root3d = ctx.local3d;
  94. // own contexts
  95. // own references
  96. sh.currentPath = newPath;
  97. sh.editorDisplay = editorDisplay;
  98. sh.parent = { shared : this, prefab : prefab };
  99. sh.cache = cache;
  100. sh.shaderCache = shaderCache;
  101. sh.customMake = customMake;
  102. // own bakedData
  103. // own refsContext
  104. return sh;
  105. }
  106. function allocForRef() {
  107. return new ContextShared();
  108. }
  109. public function loadDir(p : String, ?dir : String ) : Array<hxd.res.Any> {
  110. var datPath = new haxe.io.Path(currentPath);
  111. datPath.ext = "dat";
  112. var path = datPath.toString() + "/" + p;
  113. if(dir != null) path += "/" + dir;
  114. return try hxd.res.Loader.currentInstance.dir(path) catch( e : hxd.res.NotFound ) null;
  115. }
  116. public function loadPrefabDat(file : String, ext : String, p : String) : hxd.res.Any {
  117. var datPath = new haxe.io.Path(currentPath);
  118. datPath.ext = "dat";
  119. var path = new haxe.io.Path(datPath.toString() + "/" + p + "/" + file);
  120. path.ext = ext;
  121. return try hxd.res.Loader.currentInstance.load(path.toString()) catch( e : hxd.res.NotFound ) null;
  122. }
  123. public function savePrefabDat(file : String, ext : String, p : String, bytes : haxe.io.Bytes ) {
  124. throw "Not implemented";
  125. }
  126. public function loadPrefab( path : String ) : Prefab {
  127. return hxd.res.Loader.currentInstance.load(path).toPrefab().load();
  128. }
  129. public function loadShader( path : String ) : ShaderDef {
  130. var r = shaderCache.get(path);
  131. if(r != null)
  132. return r;
  133. var cl : Class<hxsl.Shader> = cast Type.resolveClass(path.split("/").join("."));
  134. if(cl == null) return null;
  135. // make sure to share the SharedShader instance with the real shader
  136. // so we don't get a duplicate cache of instances
  137. var shaderInst = Type.createEmptyInstance(cl);
  138. @:privateAccess shaderInst.initialize();
  139. var shader = @:privateAccess shaderInst.shader;
  140. r = {
  141. shader: shader,
  142. inits: []
  143. };
  144. shaderCache.set(path, r);
  145. return r;
  146. }
  147. public function loadModel( path : String ) {
  148. return cache.loadModel(hxd.res.Loader.currentInstance.load(path).toModel());
  149. }
  150. public function loadAnimation( path : String ) {
  151. return @:privateAccess cache.loadAnimation(hxd.res.Loader.currentInstance.load(path).toModel());
  152. }
  153. public function loadTexture( path : String ) {
  154. return cache.loadTexture(null, path);
  155. }
  156. public function loadBytes( file : String) : haxe.io.Bytes {
  157. return try hxd.res.Loader.currentInstance.load(file).entry.getBytes() catch( e : hxd.res.NotFound ) null;
  158. }
  159. public function loadBakedBytes( file : String ) {
  160. if( bakedData == null ) loadBakedData();
  161. return bakedData.get(file);
  162. }
  163. public function saveBakedBytes( file : String, bytes : haxe.io.Bytes ) {
  164. if( bakedData == null ) loadBakedData();
  165. if( bytes == null ) {
  166. if( !bakedData.remove(file) )
  167. return;
  168. } else
  169. bakedData.set(file, bytes);
  170. var keys = Lambda.array({ iterator : bakedData.keys });
  171. if( keys.length == 0 ) {
  172. saveBakedFile(null);
  173. return;
  174. }
  175. var bytes = new haxe.io.BytesOutput();
  176. bytes.writeString("BAKE");
  177. bytes.writeInt32(keys.length);
  178. var headerSize = 8;
  179. for( name in keys )
  180. headerSize += 2 + name.length + 8;
  181. for( name in keys ) {
  182. bytes.writeUInt16(name.length);
  183. bytes.writeString(name);
  184. bytes.writeInt32(headerSize);
  185. var len = bakedData.get(name).length;
  186. bytes.writeInt32(len);
  187. headerSize += len + 1;
  188. }
  189. for( name in keys ) {
  190. bytes.write(bakedData.get(name));
  191. bytes.writeByte(0xFE); // stop
  192. }
  193. saveBakedFile(bytes.getBytes());
  194. }
  195. public function saveTexture( file : String, bytes : haxe.io.Bytes , dir : String, ext : String) {
  196. throw "Don't know how to save texture";
  197. }
  198. function saveBakedFile( bytes : haxe.io.Bytes ) {
  199. throw "Don't know how to save baked file";
  200. }
  201. function loadBakedFile() {
  202. var path = new haxe.io.Path(currentPath);
  203. path.ext = "bake";
  204. return try hxd.res.Loader.currentInstance.load(path.toString()).entry.getBytes() catch( e : hxd.res.NotFound ) null;
  205. }
  206. function loadBakedData() {
  207. bakedData = new Map();
  208. var data = loadBakedFile();
  209. if( data == null )
  210. return;
  211. if( data.getString(0,4) != "BAKE" )
  212. throw "Invalid bake file";
  213. var count = data.getInt32(4);
  214. var pos = 8;
  215. for( i in 0...count ) {
  216. var len = data.getUInt16(pos);
  217. pos += 2;
  218. var name = data.getString(pos, len);
  219. pos += len;
  220. var bytesPos = data.getInt32(pos);
  221. pos += 4;
  222. var bytesLen = data.getInt32(pos);
  223. pos += 4;
  224. bakedData.set(name,data.sub(bytesPos,bytesLen));
  225. if( data.get(bytesPos+bytesLen) != 0xFE )
  226. throw "Corrupted bake file";
  227. }
  228. }
  229. function getChildrenRoots( base : h3d.scene.Object, p : Prefab, out : Array<h3d.scene.Object> ) {
  230. for( c in p.children ) {
  231. var ctx = contexts.get(c);
  232. if( ctx == null ) continue;
  233. if( ctx.local3d == base )
  234. getChildrenRoots(base, c, out);
  235. else
  236. out.push(ctx.local3d);
  237. }
  238. return out;
  239. }
  240. public function getSelfObject( p : Prefab ) : h3d.scene.Object {
  241. var ctx = contexts.get(p);
  242. if(ctx == null) return null;
  243. var parentCtx = p.parent != null ? contexts.get(p.parent) : null;
  244. if(parentCtx != null && ctx.local3d == parentCtx.local3d)
  245. return null;
  246. return ctx.local3d;
  247. }
  248. public function getObjects<T:h3d.scene.Object>( p : Prefab, c: Class<T> ) : Array<T> {
  249. var root = getSelfObject(p);
  250. if(root == null) return [];
  251. var childObjs = getChildrenRoots(root, p, []);
  252. var ret = [];
  253. function rec(o : h3d.scene.Object) {
  254. var m = Std.downcast(o, c);
  255. if(m != null) {
  256. if(ret.contains(m))
  257. throw "?!";
  258. ret.push(m);
  259. }
  260. for( child in o )
  261. if( childObjs.indexOf(child) < 0 )
  262. rec(child);
  263. }
  264. rec(root);
  265. return ret;
  266. }
  267. public function getMaterials( p : Prefab ) {
  268. var root = getSelfObject(p);
  269. if(root == null) return [];
  270. var childObjs = getChildrenRoots(root, p, []);
  271. var ret = [];
  272. function rec(o : h3d.scene.Object) {
  273. if( o.isMesh() ) {
  274. var m = o.toMesh();
  275. var multi = Std.downcast(m, h3d.scene.MultiMaterial);
  276. if( multi != null ) {
  277. for( m in multi.materials )
  278. if( m != null )
  279. ret.push(m);
  280. } else if( m.material != null )
  281. ret.push(m.material);
  282. }
  283. for( child in o )
  284. if( childObjs.indexOf(child) < 0 )
  285. rec(child);
  286. }
  287. rec(root);
  288. return ret;
  289. }
  290. }