Reference.hx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. package hrt.prefab;
  2. class Reference extends Object3D {
  3. @:s public var editMode : Bool = false;
  4. public var refInstance : Prefab;
  5. public static function copy_overrides(from:Dynamic) : haxe.ds.StringMap<Dynamic> {
  6. if (Std.isOfType(from, haxe.ds.StringMap)) {
  7. return from != null ? cast(from, haxe.ds.StringMap<Dynamic>).copy() : new haxe.ds.StringMap<Dynamic>();
  8. }
  9. else {
  10. var m = new haxe.ds.StringMap<Dynamic>();
  11. for (f in Reflect.fields(from)) {
  12. m.set(f, Reflect.getProperty(from ,f));
  13. }
  14. return m;
  15. }
  16. }
  17. override function save() {
  18. var obj : Dynamic = super.save();
  19. #if editor
  20. if( editMode && refInstance != null ) {
  21. var sheditor = Std.downcast(shared, hide.prefab.ContextShared);
  22. if( sheditor.editor != null ) sheditor.editor.watchIgnoreChanges(source);
  23. var s = refInstance.serialize();
  24. sys.io.File.saveContent(hide.Ide.inst.getPath(source), hide.Ide.inst.toJSON(s));
  25. }
  26. #end
  27. return obj;
  28. }
  29. #if editor
  30. override function setEditorChildren(sceneEditor:hide.comp.SceneEditor, scene: hide.comp.Scene) {
  31. super.setEditorChildren(sceneEditor, scene);
  32. if (refInstance != null) {
  33. refInstance.setEditor(sceneEditor, scene);
  34. }
  35. }
  36. #end
  37. function resolveRef() : Prefab {
  38. if(source == null)
  39. return null;
  40. if (refInstance != null)
  41. return refInstance;
  42. #if editor
  43. try {
  44. #end
  45. var refInstance = hxd.res.Loader.currentInstance.load(source).to(hrt.prefab.Resource).load().clone();
  46. refInstance.shared.parentPrefab = this;
  47. return refInstance;
  48. #if editor
  49. } catch (_) {
  50. return null;
  51. }
  52. #end
  53. }
  54. override function makeInstance() {
  55. if( source == null )
  56. return;
  57. #if editor
  58. if (hasCycle()) {
  59. hide.Ide.inst.quickError('Reference ${getAbsPath()} to $source is creating a cycle. Please fix the reference.');
  60. refInstance = null;
  61. return;
  62. }
  63. #end
  64. var p = resolveRef();
  65. var refLocal3d : h3d.scene.Object = null;
  66. if (Std.downcast(p, Object3D) != null) {
  67. refLocal3d = shared.current3d;
  68. } else {
  69. super.makeInstance();
  70. refLocal3d = local3d;
  71. }
  72. if (p == null) {
  73. refInstance = null;
  74. return;
  75. }
  76. var sh = p.shared;
  77. @:privateAccess sh.root3d = sh.current3d = refLocal3d;
  78. @:privateAccess sh.root2d = sh.current2d = findFirstLocal2d();
  79. #if editor
  80. sh.editor = this.shared.editor;
  81. sh.scene = this.shared.scene;
  82. #end
  83. sh.parentPrefab = this;
  84. sh.customMake = this.shared.customMake;
  85. refInstance = p;
  86. if (refInstance.to(Object3D) != null) {
  87. var obj3d = refInstance.to(Object3D);
  88. obj3d.loadTransform(this); // apply this transform to the reference prefab
  89. obj3d.name = name;
  90. obj3d.visible = visible;
  91. refInstance.make();
  92. local3d = Object3D.getLocal3d(refInstance);
  93. }
  94. else {
  95. refInstance.make();
  96. }
  97. }
  98. override public function find<T:Prefab>(?cl: Class<T>, ?filter : T -> Bool, followRefs : Bool = false ) : Null<T> {
  99. var res = super.find(cl, filter, followRefs);
  100. if (res == null && followRefs ) {
  101. var p = resolveRef();
  102. if( p != null )
  103. return p.find(cl, filter, followRefs);
  104. }
  105. return res;
  106. }
  107. override public function getOpt<T:Prefab>( ?cl : Class<T>, ?name : String, ?followRefs : Bool ) : Null<T> {
  108. var res = super.getOpt(cl, name, followRefs);
  109. if (res == null && followRefs && refInstance != null) {
  110. return refInstance.getOpt(cl, name, followRefs);
  111. }
  112. return res;
  113. }
  114. override public function flatten<T:Prefab>( ?cl : Class<T>, ?arr: Array<T> ) : Array<T> {
  115. arr = super.flatten(cl, arr);
  116. if (editMode && refInstance != null) {
  117. arr = refInstance.flatten(cl, arr);
  118. }
  119. return arr;
  120. }
  121. override function dispose() {
  122. super.dispose();
  123. if( refInstance != null )
  124. refInstance.dispose();
  125. }
  126. #if editor
  127. override public function editorRemoveObjects() : Void {
  128. if (refInstance != null) {
  129. for (child in refInstance.flatten()) {
  130. shared.editor.removeInteractive(child);
  131. }
  132. refInstance.editorRemoveObjects();
  133. }
  134. super.editorRemoveObjects();
  135. }
  136. public function hasCycle(?seenPaths: Map<String, Bool>) : Bool {
  137. if (editorOnly)
  138. return false;
  139. var oldEditMode = editMode;
  140. editMode = false;
  141. seenPaths = seenPaths?.copy() ?? [];
  142. var curPath = this.shared.currentPath;
  143. if (seenPaths.get(curPath) != null) {
  144. editMode = oldEditMode;
  145. return true;
  146. }
  147. seenPaths.set(curPath, true);
  148. if (source != null) {
  149. var ref = resolveRef();
  150. if (ref != null) {
  151. var root = ref;
  152. if (Std.isOfType(root, hrt.prefab.fx.BaseFX)) {
  153. root = hrt.prefab.fx.BaseFX.BaseFXTools.getFXRoot(root) ?? root;
  154. }
  155. var allRefs = root.flatten(Reference);
  156. for (r in allRefs) {
  157. if (r.hasCycle(seenPaths)){
  158. editMode = oldEditMode;
  159. return true;
  160. }
  161. }
  162. }
  163. }
  164. editMode = oldEditMode;
  165. return false;
  166. }
  167. override function makeInteractive() {
  168. if( editMode )
  169. return null;
  170. return super.makeInteractive();
  171. }
  172. override function edit( ctx : hide.prefab.EditContext ) {
  173. var element = new hide.Element('
  174. <div class="group" name="Reference">
  175. <dl>
  176. <dt>Reference</dt><dd><input type="fileselect" extensions="prefab l3d fx" field="source"/></dd>
  177. <dt>Edit</dt><dd><input type="checkbox" field="editMode"/></dd>
  178. </dl>
  179. </div>');
  180. function updateProps() {
  181. var input = element.find("input");
  182. var found = resolveRef() != null;
  183. input.toggleClass("error", !found);
  184. }
  185. updateProps();
  186. var props = ctx.properties.add(element, this, function(pname) {
  187. ctx.onChange(this, pname);
  188. if(pname == "source" || pname == "editMode") {
  189. if (pname == "source") {
  190. editorRemoveObjects();
  191. refInstance = null;
  192. }
  193. if (hasCycle()) {
  194. hide.Ide.inst.quickError('Reference to $source would create a cycle. The reference change was aborted.');
  195. ctx.properties.undo.undo();
  196. @:privateAccess ctx.properties.undo.redoElts.pop();
  197. return;
  198. }
  199. updateProps();
  200. if(!ctx.properties.isTempChange) {
  201. if (pname == "source") {
  202. ctx.rebuildPrefab(this);
  203. }
  204. else {
  205. if (refInstance != null) {
  206. for (child in refInstance.flatten()) {
  207. shared.editor.removeInteractive(child);
  208. }
  209. }
  210. shared.editor.refreshInteractive(this);
  211. @:privateAccess shared.editor.refreshTree();
  212. }
  213. }
  214. }
  215. });
  216. super.edit(ctx);
  217. }
  218. override function getHideProps() : hide.prefab.HideProps {
  219. return { icon : "share", name : "Reference" };
  220. }
  221. #end
  222. public static var _ = hrt.prefab.Prefab.register("reference", Reference);
  223. }