Prefab.hx 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. package hrt.prefab;
  2. /**
  3. Prefab is an data-oriented tree container capable of creating instances of Heaps objects.
  4. **/
  5. @:keepSub
  6. class Prefab {
  7. /**
  8. The type of prefab, allows to identify which class it should be loaded with.
  9. **/
  10. public var type(default, null) : String;
  11. /**
  12. The name of the prefab in the tree view
  13. **/
  14. public var name(default, set) : String;
  15. /**
  16. The parent of the prefab in the tree view
  17. **/
  18. public var parent(default, set) : Prefab;
  19. /**
  20. The associated source file (an image, a 3D model, etc.) if the prefab type needs it.
  21. **/
  22. public var source(default, set) : String;
  23. /**
  24. The list of children prefab in the tree view
  25. **/
  26. public var children(default, null) : Array<Prefab>;
  27. /**
  28. Tells if the prefab will create an instance when calling make() or be ignored. Also apply to this prefab children.
  29. **/
  30. public var enabled : Bool = true;
  31. /**
  32. A storage for some extra properties
  33. **/
  34. public var props : Any;
  35. /**
  36. Creates a new prefab with the given parent.
  37. **/
  38. public function new(?parent) {
  39. this.parent = parent;
  40. children = [];
  41. }
  42. function set_name(n) {
  43. return name = n;
  44. }
  45. function set_source(f) {
  46. return source = f;
  47. }
  48. function set_parent(p) {
  49. if( parent != null )
  50. parent.children.remove(this);
  51. parent = p;
  52. if( parent != null )
  53. parent.children.push(this);
  54. return p;
  55. }
  56. #if editor
  57. /**
  58. Allows to customize how the prefab object is edited within Hide
  59. **/
  60. public function edit( ctx : hide.prefab.EditContext ) {
  61. }
  62. /**
  63. Allows to customize how the prefab object is displayed / handled within Hide
  64. **/
  65. public function getHideProps() : hide.prefab.HideProps {
  66. return { icon : "question-circle", name : "Unknown" };
  67. }
  68. /**
  69. Allows to customize how the prefab instance changes when selected/unselected within Hide
  70. **/
  71. public function setSelected( ctx : Context, b : Bool ) {
  72. var materials = ctx.shared.getMaterials(this);
  73. if( !b ) {
  74. for( m in materials ) {
  75. m.mainPass.stencil = null;
  76. m.removePass(m.getPass("highlight"));
  77. }
  78. return;
  79. }
  80. var shader = new h3d.shader.FixedColor(0xffffff);
  81. for( m in materials ) {
  82. var p = m.allocPass("highlight");
  83. p.culling = None;
  84. p.depthWrite = false;
  85. p.addShader(shader);
  86. }
  87. }
  88. #end
  89. /**
  90. Iterate over children prefab
  91. **/
  92. public inline function iterator() : Iterator<Prefab> {
  93. return children.iterator();
  94. }
  95. /**
  96. Override to implement your custom prefab data loading
  97. **/
  98. function load( v : Dynamic ) {
  99. throw "Not implemented";
  100. }
  101. /**
  102. Override to implement your custom prefab data saving
  103. **/
  104. function save() : {} {
  105. throw "Not implemented";
  106. return null;
  107. }
  108. /**
  109. Creates an instance for this prefab only (and not its children).
  110. Use make(ctx) to creates the whole instances tree;
  111. **/
  112. public function makeInstance( ctx : Context ) : Context {
  113. return ctx;
  114. }
  115. /**
  116. Allows to customize how an instance gets updated when a property name changes.
  117. You can also call updateInstance(ctx) in order to force whole instance synchronization against current prefab data.
  118. **/
  119. public function updateInstance( ctx : Context, ?propName : String ) {
  120. }
  121. /**
  122. Removes the created instance for this prefab only (not is children).
  123. If false is returned, the instance could not be removed and the whole context scene needs to be rebuilt
  124. **/
  125. public function removeInstance( ctx : Context ) : Bool {
  126. return false;
  127. }
  128. /**
  129. Save the whole prefab data and its children.
  130. **/
  131. @:final public function saveData() : {} {
  132. var obj : Dynamic = save();
  133. obj.type = type;
  134. if( !enabled )
  135. obj.enabled = false;
  136. if( name != null )
  137. obj.name = name;
  138. if( source != null )
  139. obj.source = source;
  140. if( children.length > 0 )
  141. obj.children = [for( s in children ) s.saveData()];
  142. if( props != null && obj.props == null )
  143. obj.props = props;
  144. return obj;
  145. }
  146. /**
  147. Load the whole prefab data and creates its children.
  148. **/
  149. @:final public function loadData( v : Dynamic ) {
  150. type = v.type;
  151. name = v.name;
  152. enabled = v.enabled == null ? true : v.enabled;
  153. props = v.props;
  154. source = v.source;
  155. load(v);
  156. if( children.length > 0 )
  157. children = [];
  158. var children : Array<Dynamic> = v.children;
  159. if( children != null )
  160. for( v in children )
  161. loadPrefab(v, this);
  162. }
  163. /**
  164. Updates in-place the whole prefab data and its children.
  165. **/
  166. public function reload( p : Dynamic ) {
  167. name = p.name;
  168. enabled = p.enabled == null ? true : p.enabled;
  169. props = p.props;
  170. source = p.source;
  171. load(p);
  172. var childData : Array<Dynamic> = p.children;
  173. if( childData == null ) {
  174. if( this.children.length > 0 ) this.children = [];
  175. return;
  176. }
  177. var curChild = new Map();
  178. for( c in children )
  179. curChild.set(c.name, c);
  180. var newchild = [];
  181. for( v in childData ) {
  182. var name : String = v.name;
  183. var prev = curChild.get(name);
  184. if( prev != null && prev.type == v.type ) {
  185. curChild.remove(name);
  186. prev.reload(v);
  187. newchild.push(prev);
  188. } else {
  189. newchild.push(loadPrefab(v,this));
  190. }
  191. }
  192. children = newchild;
  193. }
  194. /**
  195. Creates the correct prefab based on v.type and load its data and children.
  196. If one the prefab in the tree is not registered, a hxd.prefab.Unkown is created instead.
  197. **/
  198. public static function loadPrefab( v : Dynamic, ?parent : Prefab ) {
  199. var pcl = @:privateAccess Library.registeredElements.get(v.type);
  200. var pcl = pcl == null ? null : pcl.cl;
  201. if( pcl == null ) pcl = Unknown;
  202. var p = Type.createInstance(pcl, [parent]);
  203. p.loadData(v);
  204. return p;
  205. }
  206. /**
  207. Creates an instance for this prefab and its children.
  208. **/
  209. public function make( ctx : Context ) : Context {
  210. if( !enabled )
  211. return ctx;
  212. if( ctx == null ) {
  213. ctx = new Context();
  214. ctx.init();
  215. }
  216. ctx = makeInstance(ctx);
  217. for( c in children )
  218. c.make(ctx);
  219. return ctx;
  220. }
  221. #if castle
  222. /**
  223. Returns which CDB model this prefab props represents
  224. **/
  225. public function getCdbModel( ?p : Prefab ) : cdb.Sheet {
  226. if( p == null )
  227. p = this;
  228. if( parent != null )
  229. return parent.getCdbModel(p);
  230. return null;
  231. }
  232. #end
  233. /**
  234. Search the prefab tree for the prefab matching the given name, returns null if not found
  235. **/
  236. public function getPrefabByName( name : String ) {
  237. if( this.name == name )
  238. return this;
  239. for( c in children ) {
  240. var p = c.getPrefabByName(name);
  241. if( p != null )
  242. return p;
  243. }
  244. return null;
  245. }
  246. /**
  247. Simlar to get() but returns null if not found.
  248. **/
  249. public function getOpt<T:Prefab>( cl : Class<T>, ?name : String ) : T {
  250. if( name == null || this.name == name ) {
  251. var cval = to(cl);
  252. if( cval != null ) return cval;
  253. }
  254. for( c in children ) {
  255. var p = c.getOpt(cl, name);
  256. if( p != null )
  257. return p;
  258. }
  259. return null;
  260. }
  261. /**
  262. Search the prefab tree for the prefab matching the given prefab class (and name, if specified).
  263. Throw an exception if not found. Uses getOpt() to return null instead.
  264. **/
  265. public function get<T:Prefab>( cl : Class<T>, ?name : String ) : T {
  266. var v = getOpt(cl, name);
  267. if( v == null )
  268. throw "Missing prefab " + (name == null ? Type.getClassName(cl) : (cl == null ? name : name+"(" + Type.getClassName(cl) + ")"));
  269. return v;
  270. }
  271. /**
  272. Return all prefabs in the tree matching the given prefab class.
  273. **/
  274. public function getAll<T:Prefab>( cl : Class<T>, ?arr: Array<T> ) : Array<T> {
  275. return findAll(function(p) return p.to(cl));
  276. }
  277. /**
  278. Find a single prefab in the tree by calling `f` on each and returning the first not-null value returned, or null if not found.
  279. **/
  280. public function find<T>( f : Prefab -> Null<T> ) : Null<T> {
  281. var v = f(this);
  282. if( v != null )
  283. return v;
  284. for( p in children ) {
  285. var v = p.find(f);
  286. if( v != null ) return v;
  287. }
  288. return null;
  289. }
  290. /**
  291. Find several prefabs in the tree by calling `f` on each and returning all the not-null values returned.
  292. **/
  293. public function findAll<T>( f : Prefab -> Null<T>, ?arr : Array<T> ) : Array<T> {
  294. if( arr == null ) arr = [];
  295. var v = f(this);
  296. if( v != null )
  297. arr.push(v);
  298. for( o in children )
  299. o.findAll(f,arr);
  300. return arr;
  301. }
  302. /**
  303. Returns all prefabs in the tree matching the specified class.
  304. **/
  305. public function flatten<T:Prefab>( ?cl : Class<T>, ?arr: Array<T> ) : Array<T> {
  306. if(arr == null)
  307. arr = [];
  308. if( cl == null )
  309. arr.push(cast this);
  310. else {
  311. var i = to(cl);
  312. if(i != null)
  313. arr.push(i);
  314. }
  315. for(c in children)
  316. c.flatten(cl, arr);
  317. return arr;
  318. }
  319. /**
  320. Returns the first parent in the tree matching the specified class or null if not found.
  321. **/
  322. public function getParent<T:Prefab>( c : Class<T> ) : Null<T> {
  323. var p = parent;
  324. while(p != null) {
  325. var inst = p.to(c);
  326. if(inst != null) return inst;
  327. p = p.parent;
  328. }
  329. return null;
  330. }
  331. /**
  332. Converts the prefab to another prefab class.
  333. Returns null if not of this type.
  334. **/
  335. public function to<T:Prefab>( c : Class<T> ) : Null<T> {
  336. return Std.instance(this, c);
  337. }
  338. /**
  339. Returns the absolute name path for this prefab
  340. **/
  341. public function getAbsPath() {
  342. var p = this;
  343. var path = [];
  344. while(p.parent != null) {
  345. var n = p.name;
  346. if( n == null ) n = getDefaultName();
  347. path.unshift(n);
  348. p = p.parent;
  349. }
  350. return path.join('.');
  351. }
  352. /**
  353. Returns the default name for this prefab
  354. **/
  355. public function getDefaultName() : String {
  356. if(source != null) {
  357. var f = new haxe.io.Path(source).file;
  358. f = f.split(" ")[0].split("-")[0];
  359. return f;
  360. }
  361. return type.split(".").pop();
  362. }
  363. /**
  364. Clone this prefab and all its children
  365. **/
  366. public function clone() : Prefab {
  367. var obj = saveData();
  368. return loadPrefab(haxe.Json.parse(haxe.Json.stringify(obj)));
  369. }
  370. }