Browse Source

Revert "Add support for reference local overrides (#267)"

This reverts commit 69ee69db34cd1934dee8ec91ece5f9b00f45b142.
Clément Espeute 4 months ago
parent
commit
332095555e

+ 5 - 14
bin/style.css

@@ -1128,6 +1128,11 @@ input[type=checkbox].indeterminate:after {
   word-break: break-word;
   margin-top: 4px;
 }
+.hide-properties dd span,
+.hide-properties dt span {
+  display: inline-block;
+  width: 20px;
+}
 .hide-properties dt {
   width: calc(80px - var(--level) * 2%);
   text-align: right;
@@ -1161,9 +1166,6 @@ input[type=checkbox].indeterminate:after {
 .hide-properties input[type=checkbox] {
   width: 13px;
 }
-.hide-properties .warning {
-  color: red;
-}
 .hide-properties dt input[type=button] {
   width: 100%;
   text-align: right;
@@ -2731,17 +2733,6 @@ body.hide-subview .lm_controls {
 .jstree .inRef {
   background: #444444 !important;
 }
-.jstree .isOverride {
-  background: #25346d !important;
-}
-.jstree .isOverride.isOverriden > i,
-.jstree .isOverride.isOverriden > a {
-  color: cyan !important;
-}
-.jstree .isOverride.isOverriden.isOverridenNew i,
-.jstree .isOverride.isOverriden.isOverridenNew a {
-  color: #26c726 !important;
-}
 .jstree a.locked {
   color: #666 !important;
   font-style: italic;

+ 5 - 20
bin/style.less

@@ -1231,6 +1231,11 @@ input[type=checkbox] {
 		text-wrap: wrap;
 		word-break: break-word;
 		margin-top:4px;
+
+		span {
+			display: inline-block;
+			width: 20px;
+		}
 	}
 
 	dt {
@@ -1273,10 +1278,6 @@ input[type=checkbox] {
 		width:13px;
 	}
 
-	.warning {
-		color: red;
-	}
-
 	dt {
 		input[type=button] {
 			width: 100%;
@@ -3164,22 +3165,6 @@ body.hide-subview {
 		background: rgb(68, 68, 68) !important;
 	}
 
-	.isOverride {
-		background: #25346d !important;
-	}
-
-	.isOverride.isOverriden {
-		> i, > a {
-			color: cyan !important;
-		}
-	}
-
-	.isOverride.isOverriden.isOverridenNew {
-		i, a {
-			color: rgb(38, 199, 38) !important;
-		}
-	}
-
 	a.locked {
 		color: #666 !important;
 		font-style: italic;

+ 152 - 249
hide/comp/SceneEditor.hx

@@ -238,6 +238,10 @@ class ViewportOverlaysPopup extends hide.comp.Popup {
 			var btn = addButton("Scene Info", "info-circle", "sceneInformationToggle", () -> editor.updateStatusTextVisibility()).appendTo(group);
 			addButton("Wireframe", "connectdevelop", "wireframeToggle", () -> editor.updateWireframe()).appendTo(group);
 			addButton("Disable Scene Render", "eye-slash", "tog-scene-render", () -> {}).appendTo(group);
+
+			//btn.contextmenu(() -> {
+			//
+			//})
 		}
 
 
@@ -713,46 +717,46 @@ class ViewModePopup extends hide.comp.Popup {
 }
 
 class IconVisibilityPopup extends hide.comp.Popup {
-	 var editor : SceneEditor;
+    var editor : SceneEditor;
 
-	 public function new(?parent : Element, editor: SceneEditor) {
-		  super(parent);
-		  this.editor = editor;
+    public function new(?parent : Element, editor: SceneEditor) {
+        super(parent);
+        this.editor = editor;
 
-		  element.append(new Element("<p>Icon Visibility</p>"));
-		  element.addClass("settings-popup");
-		  element.css("max-width", "300px");
+        element.append(new Element("<p>Icon Visibility</p>"));
+        element.addClass("settings-popup");
+        element.css("max-width", "300px");
 
-		  var form_div = new Element("<div>").addClass("form-grid").appendTo(element);
+        var form_div = new Element("<div>").addClass("form-grid").appendTo(element);
 
-		  var editMode : hrt.tools.Gizmo.EditMode = @:privateAccess editor.gizmo.editMode;
+        var editMode : hrt.tools.Gizmo.EditMode = @:privateAccess editor.gizmo.editMode;
 
 		var ide = hide.Ide.inst;
-		  for (k => v in ide.show3DIconsCategory) {
-				var input = new Element('<input type="checkbox" name="snap" id="$k" value="$k"/>');
-				if (v)
-					 input.get(0).toggleAttribute("checked", true);
-				input.change((e) -> {
+        for (k => v in ide.show3DIconsCategory) {
+            var input = new Element('<input type="checkbox" name="snap" id="$k" value="$k"/>');
+            if (v)
+                input.get(0).toggleAttribute("checked", true);
+            input.change((e) -> {
 				var val = !ide.show3DIconsCategory.get(k);
 				ide.show3DIconsCategory.set(k, val);
 				js.Browser.window.localStorage.setItem(hrt.impl.EditorTools.iconVisibilityKey(k), val ? "true" : "false");
-				});
-				form_div.append(input);
-				form_div.append(new Element('<label for="$k" class="left">$k</label>'));
-		  }
-	 }
+            });
+            form_div.append(input);
+            form_div.append(new Element('<label for="$k" class="left">$k</label>'));
+        }
+    }
 }
 
 class HelpPopup extends hide.comp.Popup {
 	var editor : SceneEditor;
 
 	public function new(?parent : Element, editor: SceneEditor, ?shortcuts: Array<{name:String, shortcut:String}>) {
-		  super(parent);
-		  this.editor = editor;
+        super(parent);
+        this.editor = editor;
 
-		  element.append(new Element("<p>Shortcuts</p>"));
-		  element.addClass("settings-popup");
-		  element.css("max-width", "300px");
+        element.append(new Element("<p>Shortcuts</p>"));
+        element.addClass("settings-popup");
+        element.css("max-width", "300px");
 
 		var form_div = new Element("<div>").addClass("form-grid").appendTo(element);
 
@@ -912,9 +916,9 @@ class RenderPropsPopup extends Popup {
 @:access(hide.comp.SceneEditor)
 class CustomEditor {
 
-	 var ide(get, never) : hide.Ide;
+    var ide(get, never) : hide.Ide;
 	function get_ide() { return editor.ide; }
-	 var editor : SceneEditor;
+    var editor : SceneEditor;
 
 	var element : hide.Element;
 
@@ -922,9 +926,9 @@ class CustomEditor {
 		this.editor = editor;
 	}
 
-	 public function setElementSelected( p : hrt.prefab.Prefab, b : Bool ) {
+    public function setElementSelected( p : hrt.prefab.Prefab, b : Bool ) {
 		return true;
-	 }
+    }
 
 	public function update( dt : Float ) {
 
@@ -987,11 +991,11 @@ class SceneEditor {
 	public var curEdit(default, null) : SceneEditorContext;
 	public var snapToGround = false;
 
-	 public var snapToggle = false;
-	 public var snapMoveStep = 1.0;
-	 public var snapRotateStep = 15.0;
-	 public var snapScaleStep = 1.0;
-	 public var snapForceOnGrid = false;
+    public var snapToggle = false;
+    public var snapMoveStep = 1.0;
+    public var snapRotateStep = 15.0;
+    public var snapScaleStep = 1.0;
+    public var snapForceOnGrid = false;
 
 	public var localTransform = true;
 	public var selfOnlyTransform = false;
@@ -1078,17 +1082,11 @@ class SceneEditor {
 
 	public var lastFocusObjects : Array<Object> = [];
 
-
-	// Called when the sceneEditor scene has finished loading
-	// Use it to call setPrefab() to set the content of the scene
-	dynamic public function onSceneReady() {
-
-	}
-
-	public function new(view) {
+	public function new(view, data) {
 		ready = false;
 		ide = hide.Ide.inst;
 		this.view = view;
+		this.sceneData = data;
 
 		event = new hxd.WaitEvent();
 
@@ -1108,7 +1106,7 @@ class SceneEditor {
 		var sceneEl = new Element('<div class="heaps-scene"></div>');
 		scene = new hide.comp.Scene(view.config, null, sceneEl);
 		scene.editor = this;
-		scene.onReady = onSceneReadyInternal;
+		scene.onReady = onSceneReady;
 		scene.onResize = function() {
 			if( cameraController2D != null ) cameraController2D.toTarget();
 			onResize();
@@ -1309,23 +1307,23 @@ class SceneEditor {
 		grid.scale(1);
 		grid.material.mainPass.setPassName("overlay");
 
-		  if (snapToggle) {
-	 		gridStep = snapMoveStep;
-		  }
-		  else {
-				gridStep = ide.currentConfig.get("sceneeditor.gridStep");
-		  }
+        if (snapToggle) {
+    		gridStep = snapMoveStep;
+        }
+        else {
+            gridStep = ide.currentConfig.get("sceneeditor.gridStep");
+        }
 		gridSize = ide.currentConfig.get("sceneeditor.gridSize");
 
 		var col = h3d.Vector.fromColor(scene?.engine?.backgroundColor ?? 0);
 		var hsl = col.toColorHSL();
 
-		  var mov = 0.1;
+        var mov = 0.1;
 
-		  if (snapToggle) {
-				mov = 0.2;
-				hsl.y += (1.0-hsl.y) * 0.2;
-		  }
+        if (snapToggle) {
+            mov = 0.2;
+            hsl.y += (1.0-hsl.y) * 0.2;
+        }
 		if(hsl.z > 0.5) hsl.z -= mov;
 		else hsl.z += mov;
 
@@ -1350,12 +1348,12 @@ class SceneEditor {
 
 		var hsl = color.toColorHSL();
 
-		  var mov = 0.1;
+        var mov = 0.1;
 
-		  if (snapToggle) {
-				mov = 0.2;
-				hsl.y += (1.0-hsl.y) * 0.2;
-		  }
+        if (snapToggle) {
+            mov = 0.2;
+            hsl.y += (1.0-hsl.y) * 0.2;
+        }
 		if(hsl.z > 0.5) hsl.z -= mov;
 		else hsl.z += mov;
 
@@ -1430,16 +1428,16 @@ class SceneEditor {
 		haxe.Timer.delay(function() event.wait(0.5, updateStats), 0);
 	}
 
-	 public function getSnapStatus() : Bool {
-		  var ctrl = K.isDown(K.CTRL);
-		  return (snapToggle && !ctrl) || (!snapToggle && ctrl);
-	 };
+    public function getSnapStatus() : Bool {
+        var ctrl = K.isDown(K.CTRL);
+        return (snapToggle && !ctrl) || (!snapToggle && ctrl);
+    };
 
-	 public function snap(value: Float, step:Float) : Float {
-		  if (step > 0.0 && getSnapStatus())
-				value = hxd.Math.round(value / step) * step;
-		  return value;
-	 }
+    public function snap(value: Float, step:Float) : Float {
+        if (step > 0.0 && getSnapStatus())
+            value = hxd.Math.round(value / step) * step;
+        return value;
+    }
 
 	public function gizmoSnap(value: Float, mode: hrt.tools.Gizmo.EditMode) : Float {
 		switch(mode) {
@@ -1524,7 +1522,7 @@ class SceneEditor {
 		if (tree == null)
 			tree = this.tree;
 
-		  focusObjects(getSelectedLocal3D());
+        focusObjects(getSelectedLocal3D());
 		var selected3d = getSelectedLocal3D();
 		for(obj in selectedPrefabs)
 			tree.revealNode(obj);
@@ -1714,9 +1712,9 @@ class SceneEditor {
 			return;
 
 		var id = Std.parseInt(settings.camTypeIndex) ?? 0;
-		  var newClass = CameraControllerEditor.controllersClasses[id];
-		  if (Type.getClass(cameraController) != newClass.cl)
-				switchCamController(newClass.cl);
+        var newClass = CameraControllerEditor.controllersClasses[id];
+        if (Type.getClass(cameraController) != newClass.cl)
+            switchCamController(newClass.cl);
 
 		scene.s3d.camera.pos.set(settings.x, settings.y, settings.z);
 		scene.s3d.camera.target.set(settings.tx, settings.ty, settings.tz);
@@ -1771,45 +1769,53 @@ class SceneEditor {
 		}
 	}
 
-	function loadSnapSettings() {
-		function sanitize(value:Dynamic, def: Dynamic) {
-			if (value == null || value == 0.0)
-					return def;
-			return value;
-		}
-		@:privateAccess snapMoveStep = sanitize(view.getDisplayState("snapMoveStep"), snapMoveStep);
-		@:privateAccess snapRotateStep = sanitize(view.getDisplayState("snapRotateStep"), snapRotateStep);
-		@:privateAccess snapScaleStep = sanitize(view.getDisplayState("snapScaleStep"), snapScaleStep);
-		@:privateAccess snapForceOnGrid = view.getDisplayState("snapForceOnGrid");
-	}
-
-	public function saveSnapSettings() {
-		@:privateAccess view.saveDisplayState("snapMoveStep", snapMoveStep);
-		@:privateAccess view.saveDisplayState("snapRotateStep", snapRotateStep);
-		@:privateAccess view.saveDisplayState("snapScaleStep", snapScaleStep);
-		@:privateAccess view.saveDisplayState("snapForceOnGrid", snapForceOnGrid);
-	}
-
-	function toggleSnap(?force: Bool) {
-		if (force != null)
-			snapToggle = force;
-		else
-			snapToggle = !snapToggle;
-
-		var snap = new Element("#snap").get(0);
-		if (snap != null) {
-			snap.toggleAttribute("checked", snapToggle);
+    function loadSnapSettings() {
+        function sanitize(value:Dynamic, def: Dynamic) {
+            if (value == null || value == 0.0)
+                return def;
+            return value;
+        }
+        @:privateAccess snapMoveStep = sanitize(view.getDisplayState("snapMoveStep"), snapMoveStep);
+        @:privateAccess snapRotateStep = sanitize(view.getDisplayState("snapRotateStep"), snapRotateStep);
+        @:privateAccess snapScaleStep = sanitize(view.getDisplayState("snapScaleStep"), snapScaleStep);
+        @:privateAccess snapForceOnGrid = view.getDisplayState("snapForceOnGrid");
+    }
+
+    public function saveSnapSettings() {
+        @:privateAccess view.saveDisplayState("snapMoveStep", snapMoveStep);
+        @:privateAccess view.saveDisplayState("snapRotateStep", snapRotateStep);
+        @:privateAccess view.saveDisplayState("snapScaleStep", snapScaleStep);
+        @:privateAccess view.saveDisplayState("snapForceOnGrid", snapForceOnGrid);
+    }
+
+    function toggleSnap(?force: Bool) {
+        if (force != null)
+            snapToggle = force;
+        else
+            snapToggle = !snapToggle;
+
+        var snap = new Element("#snap").get(0);
+        if (snap != null) {
+            snap.toggleAttribute("checked", snapToggle);
+        }
+
+        updateGrid();
+    }
+
+	function onSceneReady() {
+		// Load display state
+		{
+			var all = sceneData.flatten(PrefabElement, null);
+			var list = @:privateAccess view.getDisplayState("hideList");
+			if(list != null) {
+				var m = [for(i in (list:Array<Dynamic>)) i => true];
+				for(p in all) {
+					if(m.exists(p.getAbsPath(true, true)))
+						hideList.set(p, true);
+				}
+			}
 		}
 
-		updateGrid();
-	}
-
-	public function setPrefab(prefab: hrt.prefab.Prefab) {
-		sceneData = prefab;
-		refreshScene();
-	}
-
-	function onSceneReadyInternal() {
 		tree.saveDisplayKey = view.saveDisplayKey + '/tree';
 		renderPropsTree.saveDisplayKey = view.saveDisplayKey + '/renderPropsTree';
 
@@ -1894,7 +1900,7 @@ class SceneEditor {
 				value : o,
 				text : o.name,
 				icon : "ico ico-"+icon,
-				children : o.children.length > 0 || (ref != null && ref.editMode != None),
+				children : o.children.length > 0 || (ref != null && @:privateAccess ref.editMode),
 				state: state
 			};
 			return r;
@@ -1912,7 +1918,7 @@ class SceneEditor {
 				objs = visibleObjs;
 			}
 			var ref = o == null ? null : o.to(Reference);
-			@:privateAccess if( ref != null && ref.editMode != None && ref.refInstance != null ) {
+			@:privateAccess if( ref != null && ref.editMode && ref.refInstance != null ) {
 				for( c in ref.refInstance )
 					objs.push(c);
 			}
@@ -1921,8 +1927,6 @@ class SceneEditor {
 		};
 
 		tree.get = function(o:PrefabElement) {
-			if (sceneData == null)
-				return [];
 			var objs = o == null ? sceneData.children : Lambda.array(o);
 			return getFunc(objs, o);
 		};
@@ -2133,21 +2137,17 @@ class SceneEditor {
 		tree.applyStyle = function(p, el) applyTreeStyle(p, el);
 		renderPropsTree.applyStyle = function(p, el) applyTreeStyle(p, el, renderPropsTree);
 
-		ready = true;
-
-		onSceneReady();
-
-		selectElements([], NoHistory);
+		selectElements([]);
+		refreshScene();
 		this.camera2D = camera2D;
 
 		updateViewportOverlays();
 
-
+		ready = true;
 		for (callback in readyDelayed) {
 			callback();
 		}
 		readyDelayed.empty();
-
 	}
 
 	function checkAllowParent(prefabInf:hrt.prefab.Prefab.PrefabInfo, prefabParent : PrefabElement) : Bool {
@@ -2171,17 +2171,7 @@ class SceneEditor {
 		tree.collapseAll();
 	}
 
-	var treeRefreshing = false;
-	var queueRefresh : Array<() -> Void> = null;
-
 	function refreshTree( ?callb ) {
-		if (treeRefreshing) {
-			queueRefresh ??= [];
-			if (callb != null)
-				queueRefresh.push(callb);
-			return;
-		}
-		treeRefreshing = true;
 		tree.refresh(function() {
 			var all = sceneData.flatten(PrefabElement);
 			for(elt in all) {
@@ -2193,13 +2183,6 @@ class SceneEditor {
 			tree.setSelection(selectedPrefabs);
 
 			if(callb != null) callb();
-
-			treeRefreshing = false;
-			if (queueRefresh != null) {
-				var list = queueRefresh;
-				queueRefresh = null;
-				refreshTree(() -> for (cb in list) cb());
-			}
 		});
 
 		renderPropsTree.refresh(function() {
@@ -2376,7 +2359,7 @@ class SceneEditor {
 		if (renderPropsRoot == null && path != null) {
 			renderPropsRoot = new hrt.prefab.Reference(null, new ContextShared());
 			renderPropsRoot.setEditor(this, this.scene);
-			renderPropsRoot.editMode = Ide.inst.currentConfig.get("sceneeditor.renderprops.edit", false) ? Edit : None;
+			renderPropsRoot.editMode = Ide.inst.currentConfig.get("sceneeditor.renderprops.edit", false);
 			renderPropsRoot.name = "Render Props";
 			renderPropsRoot.source = path;
 
@@ -2415,16 +2398,13 @@ class SceneEditor {
 	}
 
 	public function refreshScene() {
-
 		clearWatches();
 
 		if (root2d != null) root2d.remove();
 		if (root3d != null) root3d.remove();
 
-		if (sceneData == null)
-			return;
-
-		sceneData.dispose();
+		if (sceneData != null)
+			sceneData.dispose();
 
 		hrt.impl.Gradient.purgeEditorCache();
 
@@ -2469,19 +2449,6 @@ class SceneEditor {
 		scene.init();
 		scene.engine.backgroundColor = bgcol;
 
-		// Load display state
-		{
-			var all = sceneData.flatten(PrefabElement, null);
-			var list = @:privateAccess view.getDisplayState("hideList");
-			if(list != null) {
-				var m = [for(i in (list:Array<Dynamic>)) i => true];
-				for(p in all) {
-					if(m.exists(p.getAbsPath(true, true)))
-						hideList.set(p, true);
-				}
-			}
-		}
-
 		rebuild(sceneData);
 
 		var all = sceneData.all();
@@ -2493,8 +2460,6 @@ class SceneEditor {
 		setRenderProps();
 
 		onRefresh();
-
-
 	}
 
 	function getAllWithRefs<T:PrefabElement>( p : PrefabElement, cl : Class<T>, ?arr : Array<T>, forceLoad: Bool = false ) : Array<T> {
@@ -2525,6 +2490,10 @@ class SceneEditor {
 			if( isLocked(elt) ) toggleInteractive(elt, false);
 		}
 		var ref = Std.downcast(elt,Reference);
+		@:privateAccess if( ref != null && ref.editMode && ref.refInstance != null ) {
+			for( p in ref.refInstance.flatten() )
+				makeInteractive(p);
+		}
 	}
 
 	function toggleInteractive( e : PrefabElement, visible : Bool ) {
@@ -2691,10 +2660,8 @@ class SceneEditor {
 	}
 
 	public function refreshInteractive(elt : PrefabElement) {
-		for (p in elt.flatten(null, null)) {
-			removeInteractive(p);
-			makeInteractive(p);
-		}
+		removeInteractive(elt);
+		makeInteractive(elt);
 	}
 
 	public function removeInteractive(elt: PrefabElement) {
@@ -2761,7 +2728,7 @@ class SceneEditor {
 				if(rot != null) {
 					rot.toMatrix(transf);
 
-					 }
+                }
 				if(translate != null)
 					transf.translate(translate.x, translate.y, translate.z);
 				for(i in 0...sceneObjs.length) {
@@ -2795,21 +2762,21 @@ class SceneEditor {
 					var obj3d = objects3d[i];
 					var obj3dPrevTransform = obj3d.getTransform();
 					var euler = newMat.getEulerAngles();
-						  if (translate != null && translate.length() > 0.0001 && snapForceOnGrid) {
-								obj3d.x = snap(quantize(newMat.tx, posQuant), snapMoveStep);
-								obj3d.y = snap(quantize(newMat.ty, posQuant), snapMoveStep);
-								obj3d.z = snap(quantize(newMat.tz, posQuant), snapMoveStep);
-						  }
-						  else { // Don't snap translation if the primary action wasn't a translation (i.e. Rotation around a pivot)
+                    if (translate != null && translate.length() > 0.0001 && snapForceOnGrid) {
+                        obj3d.x = snap(quantize(newMat.tx, posQuant), snapMoveStep);
+                        obj3d.y = snap(quantize(newMat.ty, posQuant), snapMoveStep);
+                        obj3d.z = snap(quantize(newMat.tz, posQuant), snapMoveStep);
+                    }
+                    else { // Don't snap translation if the primary action wasn't a translation (i.e. Rotation around a pivot)
 						obj3d.x = quantize(newMat.tx, posQuant);
 						obj3d.y = quantize(newMat.ty, posQuant);
 						obj3d.z = quantize(newMat.tz, posQuant);
 					}
 
-						  if (rot != null) {
-								obj3d.rotationX = quantize(M.radToDeg(euler.x), rotQuant);
-								obj3d.rotationY = quantize(M.radToDeg(euler.y), rotQuant);
-								obj3d.rotationZ = quantize(M.radToDeg(euler.z), rotQuant);
+                    if (rot != null) {
+                        obj3d.rotationX = quantize(M.radToDeg(euler.x), rotQuant);
+                        obj3d.rotationY = quantize(M.radToDeg(euler.y), rotQuant);
+                        obj3d.rotationZ = quantize(M.radToDeg(euler.z), rotQuant);
 					}
 
 					if(scale != null) {
@@ -2974,10 +2941,10 @@ class SceneEditor {
 			var engine = h3d.Engine.getCurrent();
 			var ratio = 150 / engine.height;
 
-				var scale = ratio * distToCam * Math.tan(cam.fovY * 0.5 * Math.PI / 180.0);
-				if (cam.orthoBounds != null) {
-					 scale = ratio *  (cam.orthoBounds.xSize) * 0.5;
-				}
+            var scale = ratio * distToCam * Math.tan(cam.fovY * 0.5 * Math.PI / 180.0);
+            if (cam.orthoBounds != null) {
+                scale = ratio *  (cam.orthoBounds.xSize) * 0.5;
+            }
 			basis.setScale(scale);
 
 		} else {
@@ -3191,21 +3158,15 @@ class SceneEditor {
 		}
 
 		var modifiedRef = Std.downcast(p.shared.parentPrefab, hrt.prefab.Reference);
-		if (modifiedRef != null && modifiedRef.editMode == Edit) {
+		if (modifiedRef != null) {
 			var path = modifiedRef.source;
 
 			var others = sceneData.findAll(Reference, (r) -> r.source == path && r != modifiedRef, true);
 			@:privateAccess
-			if (others.length > 0) {
-				var data = modifiedRef.refInstance.serialize();
-				beginRebuild();
-				for (ref in others) {
-					removeInstance(ref.refInstance, false);
-					@:privateAccess ref.setRef(data);
-					queueRebuild(ref);
-				}
-				endRebuild();
-				refreshTree();
+			for (ref in others) {
+				removeInstance(ref.refInstance, false);
+				ref.refInstance = modifiedRef.refInstance.clone();
+				queueRebuild(ref);
 			}
 		}
 
@@ -3220,59 +3181,8 @@ class SceneEditor {
 		var obj3d  = p.to(Object3D);
 		el.toggleClass("disabled", !p.enabled);
 		var aEl = el.find("a").first();
-
-		// reference
-		var isOverride = false;
-		var isOverriden = false;
-		var isOverridenNew = false;
-		var inRef = false;
-		if (p.shared.parentPrefab != null) {
-			var parentRef = Std.downcast(p.shared.parentPrefab, Reference);
-			if (parentRef != null) {
-				if (parentRef.editMode == Override) {
-					isOverride = true;
-
-					var path = [];
-					var current = p;
-					while (current != null) {
-						path.push(current);
-						current = current.parent;
-					}
-
-					var currentOverride = @:privateAccess parentRef.computeDiffFromSource();
-
-					// skip first item in the path
-					path.pop();
-					while(currentOverride != null && path.length > 0) {
-						var current = path.pop();
-						if (currentOverride.children != null) {
-							currentOverride = Reflect.field(currentOverride.children, current.name);
-						}
-					}
-
-					if (currentOverride != null) {
-						var overridenFields = Reflect.fields(currentOverride);
-						overridenFields.remove("children");
-						if (overridenFields.length > 0) {
-							isOverriden = true;
-							if (currentOverride.type != null) {
-								isOverridenNew = true;
-							}
-						}
-					}
-
-				} else {
-					inRef = true;
-				}
-			}
-		}
-
-
-		el.toggleClass("inRef", inRef);
-		el.toggleClass("isOverride", isOverride);
-		el.toggleClass("isOverriden", isOverriden);
-		el.toggleClass("isOverridenNew", isOverridenNew);
-
+		var root = p.getRoot();
+		el.toggleClass("inRef", root != sceneData);
 
 		var tag = getTag(p);
 
@@ -4139,7 +4049,7 @@ class SceneEditor {
 	function groupSelection() {
 		if(!canGroupSelection()) {
 			return;
-		  }
+        }
 
 		// Sort the selection to match the scene order
 		var elts : Array<hrt.prefab.Prefab> = [];
@@ -4661,7 +4571,7 @@ class SceneEditor {
 			return;
 
 		var ref = Std.downcast(to, Reference);
-		@:privateAccess if( ref != null && ref.editMode != None ) to = ref.refInstance;
+		@:privateAccess if( ref != null && ref.editMode ) to = ref.refInstance;
 
 		// Sort node based on where they appear in the scene tree
 		var flat = sceneData.flatten();
@@ -4846,19 +4756,12 @@ class SceneEditor {
 		}
 	}
 
-	var beginRebuildStack = 0;
 	function beginRebuild() {
-		beginRebuildStack++;
-		if (beginRebuildStack > 1)
-			return;
 		rebuildQueue = [];
 		rebuildEndCallbacks = [];
 	}
 
 	function endRebuild() {
-		beginRebuildStack --;
-		if (beginRebuildStack > 0)
-			return;
 		for (prefab => want in rebuildQueue) {
 			switch (want) {
 				case Skip:

+ 41 - 34
hide/view/FXEditor.hx

@@ -43,11 +43,16 @@ private class FXSceneEditor extends hide.comp.SceneEditor {
 	public var is2D : Bool = false;
 
 
-	public function new(view) {
-		super(view);
+	public function new(view,  data) {
+		super(view, data);
 		parent = cast view;
 	}
 
+	override function onSceneReady() {
+		super.onSceneReady();
+		parent.onSceneReady();
+	}
+
 	override function onPrefabChange(p: PrefabElement, ?pname: String) {
 		super.onPrefabChange(p, pname);
 		parent.onPrefabChange(p, pname);
@@ -340,8 +345,6 @@ class FXEditor extends hide.view.FileView {
 	var xOffset = 0.;
 	var tlKeys: Array<{name:String, shortcut:String}> = [];
 
-	var fxprops : hide.comp.PropsEditor;
-
 	var pauseButton : hide.comp.Toolbar.ToolToggle;
 	@:isVar var currentTime(get, set) : Float;
 	var selectMin : Float;
@@ -352,6 +355,8 @@ class FXEditor extends hide.view.FileView {
 	var afterPanRefreshes : Array<Bool->Void> = [];
 	var statusText : h2d.Text;
 
+	var scriptEditor : hide.comp.ScriptEditor;
+	//var fxScriptParser : hrt.prefab.fx.FXScriptParser;
 	var cullingPreview : h3d.scene.Sphere;
 
     var viewModes : Array<String>;
@@ -388,6 +393,8 @@ class FXEditor extends hide.view.FileView {
 		var content = sys.io.File.getContent(getPath());
 		var json = haxe.Json.parse(content);
 
+
+		data = cast(PrefabElement.createFromDynamic(json), hrt.prefab.fx.BaseFX);
 		currentSign = ide.makeSignature(content);
 
 		element.html('
@@ -433,13 +440,16 @@ class FXEditor extends hide.view.FileView {
 						<div class="tab expand" name="Properties" icon="cog">
 							<div class="fx-props"></div>
 						</div>
+						<div class="tab expand" name="Script" icon="cog">
+							<div class="fx-script"></div>
+							<div class="fx-scriptParams"></div>
+						</div>
 					</div>
 				</div>
 			</div>');
 		tools = new hide.comp.Toolbar(null,element.find(".tools-buttons"));
 		var tabs = new hide.comp.Tabs(null,element.find(".tabs"));
-		sceneEditor = new FXSceneEditor(this);
-		sceneEditor.onSceneReady = onSceneReady;
+		sceneEditor = new FXSceneEditor(this, cast(data, hrt.prefab.Prefab));
 
 		for (callback in sceneReadyDelayed) {
 			sceneEditor.delayReady(callback);
@@ -505,19 +515,42 @@ class FXEditor extends hide.view.FileView {
 		element.find(".collapse-btn").click(function(e) {
 			sceneEditor.collapseTree();
 		});
-		fxprops = new hide.comp.PropsEditor(undo,null,element.find(".fx-props"));
-
+		var fxprops = new hide.comp.PropsEditor(undo,null,element.find(".fx-props"));
+		{
+			var edit = new FXEditContext(this);
+			edit.properties = fxprops;
+			edit.scene = sceneEditor.scene;
+			edit.cleanups = [];
+			cast(data, hrt.prefab.Prefab).edit(edit);
+		}
 
 		if (is2D) {
 			sceneEditor.camera2D = true;
 		}
 
+		var scriptElem = element.find(".fx-script");
+		scriptEditor = new hide.comp.ScriptEditor(data.scriptCode, null, scriptElem, scriptElem);
+		function onSaveScript() {
+			data.scriptCode = scriptEditor.code;
+			save();
+			skipNextChange = true;
+			modified = false;
+		}
+		scriptEditor.onSave = onSaveScript;
+		//fxScriptParser = new hrt.prefab.fx.FXScriptParser();
+		data.scriptCode = scriptEditor.code;
+
 		keys.register("playPause", function() { pauseButton.toggle(!pauseButton.isDown()); });
 
 		currentVersion = undo.currentID;
 		sceneEditor.tree.element.addClass("small");
 		sceneEditor.renderPropsTree.element.addClass("small");
 
+		selectMin = 0.0;
+		selectMax = 0.0;
+		previewMin = 0.0;
+		previewMax = data.duration == 0 ? 5000 : data.duration;
+
 		var rpEditionvisible = Ide.inst.currentConfig.get("sceneeditor.renderprops.edit", false);
 		setRenderPropsEditionVisibility(rpEditionvisible);
 	}
@@ -550,27 +583,6 @@ class FXEditor extends hide.view.FileView {
 		setRenderPropsEditionVisibility(Ide.inst.currentConfig.get("sceneeditor.renderprops.edit", false));
 	}
 	public function onSceneReady() {
-		data = cast(hxd.res.Loader.currentInstance.load(state.path).toPrefab().load().clone(), hrt.prefab.fx.BaseFX);
-		if (data == null) {
-			throw "Prefab is not a FX";
-			return;
-		}
-
-		sceneEditor.setPrefab(cast data);
-
-		selectMin = 0.0;
-		selectMax = 0.0;
-		previewMin = 0.0;
-		previewMax = data.duration == 0 ? 5000 : data.duration;
-
-		{
-			var edit = new FXEditContext(this);
-			edit.properties = fxprops;
-			edit.scene = sceneEditor.scene;
-			edit.cleanups = [];
-			cast(data, hrt.prefab.Prefab).edit(edit);
-		}
-
 		var axis = new h3d.scene.Graphics(scene.s3d);
 		axis.z = 0.001;
 		axis.lineStyle(2,0xFF0000); axis.lineTo(1,0,0);
@@ -655,8 +667,6 @@ class FXEditor extends hide.view.FileView {
 
 		statusText = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
 		statusText.setPosition(5, 5);
-
-		rebuildAnimPanel();
 	}
 
 	function onPrefabChange(p: PrefabElement, ?pname: String) {
@@ -1249,9 +1259,6 @@ class FXEditor extends hide.view.FileView {
 	}
 
 	function rebuildAnimPanel() {
-		if (@:privateAccess !sceneEditor.ready)
-			return;
-
 		if(element == null)
 			return;
 

+ 2 - 8
hide/view/Model.hx

@@ -80,12 +80,11 @@ class Model extends FileView {
 		tabs = new hide.comp.Tabs(null,element.find(".tabs"));
 		eventList = element.find(".event-editor");
 
+		root = new hrt.prefab.Prefab(null, null);
 		var def = new hrt.prefab.Prefab(null, null);
 		new hrt.prefab.RenderProps(def, null).name = "renderer";
 		var l = new hrt.prefab.Light(def, null);
-		sceneEditor = new hide.comp.SceneEditor(this);
-		sceneEditor.onSceneReady = onSceneReady;
-
+		sceneEditor = new hide.comp.SceneEditor(this, root);
 		sceneEditor.editorDisplay = false;
 		sceneEditor.onRefresh = onRefresh;
 		sceneEditor.onUpdate = onUpdate;
@@ -1201,11 +1200,6 @@ class Model extends FileView {
 	// Scene editor bindings
 	inline function get_scene() return sceneEditor.scene;
 
-	function onSceneReady() {
-		root = new hrt.prefab.Prefab(null, null);
-		sceneEditor.setPrefab(root);
-	}
-
 	function onRefresh() {
 		this.saveDisplayKey = "Model:" + state.path;
 

+ 10 - 8
hide/view/Prefab.hx

@@ -52,8 +52,8 @@ class FiltersPopup extends hide.comp.Popup {
 class PrefabSceneEditor extends hide.comp.SceneEditor {
 	var parent : Prefab;
 
-	public function new(view) {
-		super(view);
+	public function new(view, data) {
+		super(view, data);
 		parent = cast view;
 		this.localTransform = false; // TODO: Expose option
 	}
@@ -63,6 +63,11 @@ class PrefabSceneEditor extends hide.comp.SceneEditor {
 		parent.onUpdate(dt);
 	}
 
+	override function onSceneReady() {
+		super.onSceneReady();
+		parent.onSceneReady();
+	}
+
 	override function applyTreeStyle(p: PrefabElement, el: Element, ?pname: String, ?tree: hide.comp.IconTree<PrefabElement>) {
 		super.applyTreeStyle(p, el, pname, tree);
 		parent.applyTreeStyle(p, el, pname);
@@ -234,8 +239,7 @@ class Prefab extends hide.view.FileView {
 	}
 
 	function createEditor() {
-		sceneEditor = new PrefabSceneEditor(this);
-		sceneEditor.onSceneReady = onSceneReady;
+		sceneEditor = new PrefabSceneEditor(this, data);
 		for (callback in sceneReadyDelayed) {
 			sceneEditor.delayReady(callback);
 		}
@@ -244,8 +248,10 @@ class Prefab extends hide.view.FileView {
 
 	override function onDisplay() {
 		if( sceneEditor != null ) sceneEditor.dispose();
+
 		createData();
 		var content = sys.io.File.getContent(getPath());
+		data = hrt.prefab.Prefab.createFromDynamic(haxe.Json.parse(content));
 		currentSign = ide.makeSignature(content);
 
 
@@ -433,9 +439,6 @@ class Prefab extends hide.view.FileView {
 	}
 
 	public function onSceneReady() {
-		data = hxd.res.Loader.currentInstance.load(state.path).toPrefab().load().clone();
-		sceneEditor.setPrefab(cast data);
-
 		refreshSceneFilters();
 		refreshGraphicsFilters();
 		refreshViewModes();
@@ -575,7 +578,6 @@ class Prefab extends hide.view.FileView {
 			initGraphicsFilters();
 			initSceneFilters();
 		}
-
 	}
 
 	function resetCamera( top : Bool ) {

+ 0 - 341
hrt/prefab/Diff.hx

@@ -1,341 +0,0 @@
-package hrt.prefab;
-
-enum DiffResult {
-	/**The two object are identical, don't save anything**/
-	Skip;
-
-	/**The two objects are different, save the result as diff**/
-	Set(diff: Dynamic);
-}
-
-/**
-	Utility class to get the difference between two dynamics
-
-	There are two main functions : diff and apply, and they are reciprocal
-	diff(A,B) = D
-	apply(A,D) = B
-
-	the diff has a special support for prefab if the diffPrefab function is used :
-	the children array is handled as a special case, and prefabs that change type are
-	fully serialized in the diff instead of just the delta
-
-	Special fields prefixed by an @ can appear in the diff, they are as follow :
-
-	@removed : an array of keys name that are present in A but were removed in B
-	@index : in the prefab children data, indicate that this child has changed index between the A.children and B.children array
-
-**/
-class Diff {
-
-	/**
-		Add or Set a key/value pair to a DiffResult. If "diff" was a Skip, it will become a Set({key: value})
-	**/
-	public static function addToDiff(diff: DiffResult, key: String, value: Dynamic) : DiffResult{
-		var v = switch(diff) {
-			case Skip:
-				var v = {};
-				Reflect.setField(v, key, value);
-				return Set(v);
-			case Set(v):
-				Reflect.setField(v, key, value);
-				return diff;
-		}
-	}
-
-	public static function deepCopy(v:Dynamic) : Dynamic {
-		return haxe.Json.parse(haxe.Json.stringify(v));
-	}
-
-	/**
-		Returns the difference of two values together
-	**/
-	public static function diff(originalValue: Dynamic, modifiedValue: Dynamic) : DiffResult {
-		var originalType = Type.typeof(originalValue);
-		var modifiedType = Type.typeof(modifiedValue);
-
-		if (!originalType.equals(modifiedType)) {
-			return Set(modifiedValue);
-		}
-
-		switch (modifiedType) {
-			case TNull:
-				// The only way we get here is if both types are null, so by definition they are both null and so there is no diff
-				return Skip;
-			case TInt | TFloat | TBool:
-				if (originalValue == modifiedValue) {
-					return Skip;
-				}
-			case TObject:
-				return diffObject(originalValue, modifiedValue);
-			case TClass(subClass): {
-				switch (subClass) {
-					case String:
-						if (originalValue == modifiedValue) {
-							return Skip;
-						}
-					case Array:
-						return diffArray(originalValue, modifiedValue);
-					default:
-						throw "Can't diff class " + subClass;
-				}
-			}
-			default:
-				throw "Unhandled type " + modifiedType;
-		}
-		return Set(modifiedValue);
-	}
-
-
-	/**
-		Same as diffObject, but handles `type` and `children` fields as a special case :
-		children is serialized as an object with prefabName: diffPrefab(prefab)
-		and if original.type != modified.type, the whole modified object is copied as is
-		(because we consider that changing the type of a prefab in a diff means the prefab was destroyed then re-created)
-	**/
-	public static function diffPrefab(original: Dynamic, modified: Dynamic) : DiffResult {
-		if (original == null || modified == null) {
-			if (original == modified)
-				return Skip;
-			return Set(deepCopy(modified));
-		}
-
-		if (original.type != modified.type)
-			return Set(deepCopy(modified));
-
-		var result = diffObject(original, modified, ["children"]); // we could skip "type" but because we are sure that the type are equals they will never be serialised
-
-		var resultChildren = {};
-
-		var originalChildren = original.children ?? [];
-		var modifiedChildren = modified.children ?? [];
-
-		var childrenMap : Map<String, {originals: Array<Dynamic>, modifieds: Array<Dynamic>}> = [];
-
-		for (index => child in originalChildren) {
-			hrt.tools.MapUtils.getOrPut(childrenMap, child.name ?? "", {originals: [], modifieds: []}).originals.push({index: index, child: child});
-		}
-
-		for (index => child in modifiedChildren) {
-			hrt.tools.MapUtils.getOrPut(childrenMap, child.name ?? "", {originals: [], modifieds: []}).modifieds.push({index: index, child: child});
-		}
-
-		for (name => data in childrenMap) {
-			for (index in 0...hxd.Math.imax(data.originals.length, data.modifieds.length)) {
-				var originalChild = data.originals[index];
-				var modifiedChild = data.modifieds[index];
-				var key = name;
-
-				#if editor
-				if ((originalChild?.child != null && originalChild.child.type == null) || (modifiedChild?.child != null && modifiedChild.child.type == null)) {
-					throw "can't diff child that have a missing `type`";
-				}
-				#end
-				if (index > 0)
-					key += '@$index';
-
-				var diff = diffPrefab(originalChild?.child, modifiedChild?.child);
-
-				if (originalChild?.index != modifiedChild?.index) {
-					if (modifiedChild?.index != null) {
-						diff = addToDiff(diff, "@index", modifiedChild.index);
-					}
-				}
-
-				switch(diff) {
-					case Skip:
-					case Set(value):
-						Reflect.setField(resultChildren, key, value);
-				}
-			}
-		}
-
-		if (Reflect.fields(resultChildren).length > 0) {
-			result = addToDiff(result, "children", resultChildren);
-		}
-
-		return result;
-	}
-
-	/**
-		Returns the difference between two dynamic objects
-	**/
-	public static function diffObject(original: Dynamic, modified: Dynamic, skipFields: Array<String> = null) : DiffResult {
-		skipFields ??= [];
-		var result = {};
-		var removedFields : Array<String> = [];
-
-		if (original == null || modified == null) {
-			if (original == modified)
-				return Skip;
-			return Set(deepCopy(modified));
-		}
-
-		// Mark fields as removed
-		for (originalField in Reflect.fields(original)) {
-			if (skipFields.contains(originalField))
-				continue;
-
-			if (!Reflect.hasField(modified, originalField)) {
-				removedFields.push(originalField);
-				continue;
-			}
-		}
-
-		for (modifiedField in Reflect.fields(modified)) {
-			if (skipFields.contains(modifiedField))
-				continue;
-
-			var originalValue = Reflect.getProperty(original, modifiedField);
-			var modifiedValue = Reflect.getProperty(modified, modifiedField);
-
-			switch(diff(originalValue, modifiedValue)) {
-				case Skip:
-				case Set(v):
-					Reflect.setField(result, modifiedField, v);
-			}
-		}
-
-		if (removedFields.length > 0) {
-			Reflect.setField(result, "@removed", removedFields);
-		}
-
-		if (Reflect.fields(result).length == 0)
-			return Skip;
-		return Set(result);
-	}
-
-	/**
-		Returns the difference between two arrays. If the arrays are found to be different, a full copy of
-		modified will be returned as a Set()
-	**/
-	public static function diffArray(original: Array<Dynamic>, modified: Dynamic) : DiffResult {
-		if (original.length != modified.length) {
-			return Set(deepCopy(modified));
-		}
-
-		for (index in 0...original.length) {
-			var originalValue = original[index];
-			var modifiedValue = modified[index];
-
-			switch(diff(originalValue, modifiedValue)) {
-				case Set(_):
-					// return the whole modified object when any field is different than the original
-					return Set(deepCopy(modified));
-				case Skip:
-			}
-		}
-		return Skip;
-	}
-
-	/**
-		Modifies `target` dynamic so `apply(a, diffObject(a, b)) == b`
-	**/
-	public static function apply(target: Dynamic, diff: Dynamic) : Dynamic {
-		if (diff == null)
-			return null;
-
-		if (target == null)
-			target = {};
-
-		if (diff.type != null && diff.type != target.type) {
-			return diff;
-		}
-
-		for (field in Reflect.fields(diff)) {
-			if (field == "children")
-			{
-				var targetChildren = Reflect.field(target, "children") ?? [];
-				var diffChildren = Reflect.field(diff, "children");
-
-				for (fields in Reflect.fields(diffChildren)) {
-					var diffChild = Reflect.field(diffChildren, fields);
-					var name = fields;
-					var split = name.split("@");
-					var nthChild = 0;
-					if (split.length == 2) {
-						name = split[0];
-						nthChild = Std.parseInt(split[1]);
-					}
-
-					var targetChild = null;
-					var originalIndex = targetChildren.length; // if we don't found any children with the right name in the array, this will make sure we add the newly created children at the end of the array
-					for (index => child in targetChildren) {
-						if (name == child.name) {
-							if (nthChild == 0) {
-								targetChild = child;
-								originalIndex = index;
-								break;
-							} else {
-								nthChild --;
-							}
-						}
-					}
-
-					// Remove child if null
-					if (diffChild == null) {
-						targetChildren[originalIndex] = null;
-						continue;
-					}
-
-					// Skip diff children that don't have type if they don't
-					// modify a prefab from target object (because we can't create a prefab without a type)
-					if (targetChild == null && diffChild.type == null) {
-							continue;
-					}
-
-					targetChildren[originalIndex] = apply(targetChild, diffChild);
-				}
-
-				// Reorder the targetChildren array based on @indexes.
-				// if the @index point to a slot already taken, find the next free slot
-				// This should ensure that arrays are somewhat coherent in bad situation like
-				// the target children array has been modified since the last diff
-				var finalChildren : Array<Dynamic> = [];
-				for (index => child in targetChildren) {
-					if (child == null) continue;
-					var changedIndex = Reflect.field(child, "@index");
-					var targetIndex = if (changedIndex != null) {
-						Reflect.deleteField(child, "@index");
-						changedIndex;
-					} else {
-						index;
-					}
-					while (finalChildren[targetIndex] != null) {
-						targetIndex ++;
-					}
-					finalChildren[targetIndex] = child;
-				}
-				// If a prefab has been removed, it get inserted as a null in the childrenArray
-				// we fix that here
-				finalChildren = finalChildren.filter((f) -> f != null);
-
-				Reflect.setField(target, "children", finalChildren);
-				continue;
-			}
-
-			if (field == "@removed") {
-				var removed = Reflect.field(diff, "@removed");
-				for (field in (removed:Array<String>)) {
-					Reflect.deleteField(target, field);
-				}
-				continue;
-			}
-
-			var targetValue = Reflect.getProperty(target, field);
-			var diffValue = Reflect.getProperty(diff, field);
-
-			var targetType = Type.typeof(targetValue);
-			var diffType = Type.typeof(diffValue);
-
-			switch (targetType) {
-				case TNull | TInt | TFloat | TBool | TClass(Array) | TClass(String):
-					Reflect.setField(target, field, diffValue);
-				case TObject:
-					apply(targetValue, diffValue);
-				default:
-					throw "unhandeld type " + targetType;
-			}
-		}
-		return target;
-	}
-}

+ 1 - 1
hrt/prefab/Light.hx

@@ -179,7 +179,7 @@ class Light extends Object3D {
 		}
 
 		#if editor
-		if (shared.parentPrefab == null || (Std.downcast(shared.parentPrefab, Reference)?.editMode != None && shared.parentPrefab != shared.editor?.renderPropsRoot)) {
+		if (shared.parentPrefab == null || (Std.downcast(shared.parentPrefab, Reference)?.editMode && shared.parentPrefab != shared.editor?.renderPropsRoot)) {
 			icon = hrt.impl.EditorTools.create3DIcon(object, hide.Ide.inst.getHideResPath("icons/PointLight.png"), 0.5, Light);
 		}
 		#end

+ 17 - 51
hrt/prefab/Prefab.hx

@@ -63,11 +63,7 @@ class Prefab {
 	/**
 		The associated source file (an image, a 3D model, etc.) if the prefab type needs it.
 	**/
-	@:s public var source(default, set) : String;
-
-	public function set_source(newSource: String) {
-		return source = newSource;
-	}
+	@:s public var source : String;
 
 	/**
 		The parent of the prefab in the tree view
@@ -520,39 +516,10 @@ class Prefab {
 		return [].iterator();
 	}
 
-
-	/**
-		Returns a name that will allow to disambiguate this
-		prefabs from siblings with the same name
-		Prefab with more than one sibling with the same name
-		will have their name formated as `name-<index>` unless their index is 0.
-	**/
-	public function getUniqueName() {
-		if (parent == null) {
-			return "";
-		}
-
-		var path = name;
-		var suffix = 0;
-		for(i => c in parent.children) {
-			if(c == this)
-				break;
-			else {
-				var cname = c.name ?? "";
-				if(cname == path)
-					++suffix;
-			}
-		}
-		if(suffix > 0)
-			path += "-" + suffix;
-		return path;
-	}
-
 	/**
 		Returns the absolute name path for this prefab
 	**/
 	public function getAbsPath(unique=false, followRef : Bool = false) {
-		var origParent = parent;
 		var parent = parent;
 		if (parent != null && followRef) {
 			var ref = Std.downcast(parent.shared.parentPrefab, Reference);
@@ -565,14 +532,25 @@ class Prefab {
 		if (path == "")
 			path = hrt.prefab.Prefab.emptyNameReplacement;
 		if(unique) {
-			path = getUniqueName();
+			var suffix = 0;
+			for(i in 0...parent.children.length) {
+				var c = parent.children[i];
+				if(c == this)
+					break;
+				else {
+					var cname = c.name ?? "";
+					if(cname == path)
+						++suffix;
+				}
+			}
+			if(suffix > 0)
+				path += "-" + suffix;
 		}
 		if(parent.parent != null)
 			path = parent.getAbsPath(unique) + "." + path;
 		return path;
 	}
 
-
 	/**
 		If the prefab `props` represent CDB data, returns the sheet name of it, or null.
 	 **/
@@ -749,32 +727,20 @@ class Prefab {
 	/**
 		Finds a prefab by folowing a dot separated path like this one : `parent.child.grandchild`.
 		Returns null if the path is invalid or does not match any prefabs in the hierarchy
-		If the path contains many prefabs with the same name, they can be disambiguated in the path with `name-index`
 	**/
-	public function locatePrefab(path: String) : Null<Prefab> {
+	function locatePrefab(path: String) : Null<Prefab> {
 		if (path == null)
 			return null;
 		var parts = path.split(".");
 		var p = this;
 		while (parts.length > 0 && p != null) {
 			var name = parts.shift();
-			var subIndex = name.split("-");
-			var chooseNth = 0;
-			if (subIndex.length > 1) {
-				chooseNth = Std.parseInt(subIndex.pop()) ?? 0;
-				name = subIndex[0];
-			}
 			var found = null;
-			var currentNth = 0;
 			for (o in p.children) {
 				if (o.name == name)
 				{
-					if (currentNth == chooseNth) {
-						found = o;
-						break;
-					} else {
-						currentNth ++;
-					}
+					found = o;
+					break;
 				}
 			}
 			p = found;

+ 71 - 255
hrt/prefab/Reference.hx

@@ -1,62 +1,31 @@
 package hrt.prefab;
 
-enum EditMode {
-	/** The reference can't be edited in the editor **/
-	None;
-
-	/** The reference can be edited in the editor, and saving it will update the referenced prefab file on disk **/
-	Edit;
-
-	/** The reference can be edited, and saving it will save a diff between the original prefab and this in the `overrides` field **/
-	Override;
-}
 class Reference extends Object3D {
-	/**
-		The referenced prefab loaded by this reference
-	**/
-	public var refInstance : Prefab;
-
-	/**
-		How the reference can be edited in the editor
-	**/
-	@:s public var editMode : EditMode = None;
+	@:s public var editMode : Bool = false;
 
-	/**
-		List of all the properties that differs between this reference
-		and the original prefab data. Use the format defined by
-		hrt.prefab.Diff.diffPrefab
-	**/
-	@:s public var overrides : Dynamic = null;
+	public var refInstance : Prefab;
 
 	#if editor
 	var wasMade : Bool = false;
-
-	/**
-		Copy of the original data to use as a reference on save for overrides
-	**/
-	public var originalSource : Dynamic;
 	#end
 
-	override function set_source(newSource:String):String {
-		if (newSource != source) {
-			resetRefInstance();
+	public static function copy_overrides(from:Dynamic) : haxe.ds.StringMap<Dynamic> {
+		if (Std.isOfType(from, haxe.ds.StringMap)) {
+			return from != null ? cast(from, haxe.ds.StringMap<Dynamic>).copy() : new haxe.ds.StringMap<Dynamic>();
+		}
+		else {
+			var m = new haxe.ds.StringMap<Dynamic>();
+			for (f in Reflect.fields(from)) {
+				m.set(f, Reflect.getProperty(from ,f));
+			}
+			return m;
 		}
-		return source = newSource;
 	}
 
 	override function save() {
-		#if editor
-		if (editMode == Override && refInstance != null) {
-			this.overrides = computeDiffFromSource();
-		} else if (editMode == Edit && refInstance != null) {
-			this.overrides = null;
-		}
-		#end
-
 		var obj : Dynamic = super.save();
-
 		#if editor
-		if( editMode == Edit && refInstance != null ) {
+		if( editMode && refInstance != null ) {
 			var sheditor = Std.downcast(shared, hide.prefab.ContextShared);
 			if( sheditor.editor != null ) sheditor.editor.watchIgnoreChanges(source);
 
@@ -64,107 +33,41 @@ class Reference extends Object3D {
 			sys.io.File.saveContent(hide.Ide.inst.getPath(source), hide.Ide.inst.toJSON(s));
 		}
 		#end
-
 		return obj;
 	}
 
-	override function load(obj: Dynamic) {
-		// Backward compatibility between old bool editMode and new enum based editMode
-		if (Type.typeof(obj.editMode) == TBool) {
-			obj.editMode = "Edit";
-		}
-
-		super.load(obj);
-
-		if (source != null && shouldBeInstanciated() && hxd.res.Loader.currentInstance.exists(source)) {
-			#if editor
-			if (hasCycle())
-				return;
-			#end
-			initRefInstance();
-		}
-	}
-
-	override function copy(obj: Prefab) {
-		super.copy(obj);
-		var otherRef : Reference = cast obj;
-
-		#if editor
-		originalSource = @:privateAccess hxd.res.Loader.currentInstance.load(source).toPrefab().loadData();
-		#end
-
-		// Clone the refInstance from the original prefab on copy
-		if (source != null && shouldBeInstanciated()) {
-			if (otherRef.refInstance != null) {
-				refInstance = otherRef.refInstance.clone(new ContextShared(source, null, null, true));
-			} else {
-				initRefInstance();
-			}
-			if (refInstance != null) {
-				refInstance.shared.parentPrefab = this;
-			}
-		}
-	}
-
 	#if editor
-	function computeDiffFromSource() : Dynamic {
-		var orig = originalSource;
-		var ref = refInstance?.serialize() ?? null;
-		var diff = hrt.prefab.Diff.diffPrefab(orig, ref);
-		switch (diff) {
-			case Skip:
-				return null;
-			case Set(v):
-				return hrt.prefab.Diff.deepCopy(v);
+	override function setEditorChildren(sceneEditor:hide.comp.SceneEditor, scene: hide.comp.Scene) {
+		super.setEditorChildren(sceneEditor, scene);
+
+		if (refInstance != null) {
+			refInstance.setEditor(sceneEditor, scene);
 		}
 	}
 	#end
 
-	function initRefInstance() {
-		var refInstanceData = null;
+	function resolveRef() : Prefab {
+		if(source == null)
+			return null;
+		if (refInstance != null)
+			return refInstance;
 		#if editor
 		try {
 		#end
-			refInstanceData = @:privateAccess hxd.res.Loader.currentInstance.load(source).toPrefab().loadData();
+			var refInstance = hxd.res.Loader.currentInstance.load(source).to(hrt.prefab.Resource).load().clone();
+			refInstance.shared.parentPrefab = this;
+			this.refInstance = refInstance;
+			return refInstance;
 		#if editor
-			originalSource = @:privateAccess hxd.res.Loader.currentInstance.load(source).toPrefab().loadData();
-		} catch (e) {
-			return;
-		}
-		#end
-
-		if (overrides != null) {
-			refInstanceData = hrt.prefab.Diff.apply(refInstanceData, overrides);
+		} catch (_) {
+			return null;
 		}
-
-		refInstance = hrt.prefab.Prefab.createFromDynamic(refInstanceData, null, new ContextShared(source, null, null, true));
-		refInstance.shared.parentPrefab = this;
-	}
-
-	function resolveRef() : Prefab {
-		var shouldLoad = refInstance == null && source != null && shouldBeInstanciated();
-
-		#if editor
-		if (hasCycle())
-			shouldLoad = false;
 		#end
-
-		if (shouldLoad) {
-			initRefInstance();
-		}
-		return refInstance;
 	}
 
 	override function makeInstance() {
 		if( source == null )
 			return;
-
-
-		// in the case source has changed since the last load (can happen when creating references manually)
-		if (refInstance?.shared.currentPath != source) {
-			initRefInstance();
-			refInstance = refInstance.clone();
-		}
 		#if editor
 		if (hasCycle()) {
 			hide.Ide.inst.quickError('Reference ${getAbsPath()} to $source is creating a cycle. Please fix the reference.');
@@ -173,31 +76,32 @@ class Reference extends Object3D {
 		}
 		#end
 
+		var p = resolveRef();
 		var refLocal3d : h3d.scene.Object = null;
 
-		if (Std.downcast(refInstance, Object3D) != null) {
+		if (Std.downcast(p, Object3D) != null) {
 			refLocal3d = shared.current3d;
 		} else {
 			super.makeInstance();
 			refLocal3d = local3d;
 		}
 
-		if (refInstance == null) {
+		if (p == null) {
+			refInstance = null;
 			return;
 		}
 
-		var sh = refInstance.shared;
+		var sh = p.shared;
 		@:privateAccess sh.root3d = sh.current3d = refLocal3d;
 		@:privateAccess sh.root2d = sh.current2d = findFirstLocal2d();
 
 		#if editor
 		sh.editor = this.shared.editor;
 		sh.scene = this.shared.scene;
-		if (sh.isInstance == false)
-			throw "isInstance should be true";
 		#end
 		sh.parentPrefab = this;
 		sh.customMake = this.shared.customMake;
+		refInstance = p;
 
 		if (refInstance.to(Object3D) != null) {
 			var obj3d = refInstance.to(Object3D);
@@ -216,6 +120,7 @@ class Reference extends Object3D {
 		#end
 	}
 
+
 	override public function findRec<T:Prefab>(?cl: Class<T>, ?filter : T -> Bool, followRefs : Bool = false, includeDisabled: Bool = true) : Null<T> {
 		if (!includeDisabled && !enabled)
 			return null;
@@ -238,7 +143,7 @@ class Reference extends Object3D {
 
 	override public function flatten<T:Prefab>( ?cl : Class<T>, ?arr: Array<T>) : Array<T> {
 		arr = super.flatten(cl, arr);
-		if (editMode != None && resolveRef() != null) {
+		if (editMode && resolveRef() != null) {
 			arr = refInstance.flatten(cl, arr);
 		}
 		return arr;
@@ -250,24 +155,8 @@ class Reference extends Object3D {
 			refInstance.dispose();
 	}
 
-	function resetRefInstance() {
-		#if editor
-		editorRemoveObjects();
-		#end
-
-		refInstance = null;
-	}
-
 	#if editor
 
-	override function setEditorChildren(sceneEditor:hide.comp.SceneEditor, scene: hide.comp.Scene) {
-		super.setEditorChildren(sceneEditor, scene);
-
-		if (refInstance != null) {
-			refInstance.setEditor(sceneEditor, scene);
-		}
-	}
-
 	override public function editorRemoveObjects() : Void {
 		if (refInstance != null && wasMade) {
 			for (child in refInstance.flatten()) {
@@ -279,77 +168,42 @@ class Reference extends Object3D {
 		super.editorRemoveObjects();
 	}
 
-	/**
-		Updates the original reference data to be equal to `data`.
-		If the ref is an override, the override will be kept as is
-	**/
-	function setRef(data: Dynamic) {
-		if (data == null)
-			throw "Null data";
-
-		if (refInstance == null)
-			return;
-
-		var currentSerialization = refInstance.serialize();
-		var pristineData = hrt.prefab.Diff.deepCopy(data);
-
-		// we might have unsaved changes
-		if (editMode == Override) {
-			switch(hrt.prefab.Diff.diffPrefab(originalSource, currentSerialization)) {
-				case Skip:
-				case Set(diff):
-					pristineData = hrt.prefab.Diff.apply(pristineData, diff);
-			}
-		}
-		else if (overrides != null) {
-			pristineData = hrt.prefab.Diff.apply(pristineData, overrides);
-		}
-
-		originalSource = hrt.prefab.Diff.deepCopy(data);
-
-		refInstance = Prefab.createFromDynamic(pristineData, new ContextShared(source, true));
-		refInstance.shared.parentPrefab = this;
-	}
-
-	/**
-		Returns true if this reference has a cycle,
-		meaning that references depends on each other
-	**/
-	public function hasCycle() : Bool {
-
-		var map : Map<String, Bool> = [];
-		map.set(shared.currentPath, true);
-		return hasCyclePath(source, map);
-	}
-
-	static function hasCyclePath(path: String, seenPaths: Map<String, Bool>) : Bool {
-		if (seenPaths.get(path) == true)
-			return true;
-		if (!hxd.res.Loader.currentInstance.exists(path))
+	public function hasCycle(?seenPaths: Map<String, Bool>) : Bool {
+		if (editorOnly)
 			return false;
-
-		seenPaths.set(path, true);
-		var data = @:privateAccess hxd.res.Loader.currentInstance.load(path).toPrefab().loadData();
-		return hasCycleDynamic(data, seenPaths);
-	}
-
-	static function hasCycleDynamic(data: Dynamic, seenPaths: Map<String, Bool>) : Bool {
-		if (data.source != null) {
-			if (hasCyclePath(data.source, seenPaths.copy()))
-				return true;
+		var oldEditMode = editMode;
+		editMode = false;
+		seenPaths = seenPaths?.copy() ?? [];
+		var curPath = this.shared.currentPath;
+		if (seenPaths.get(curPath) != null) {
+			editMode = oldEditMode;
+			return true;
 		}
-		if (data.children) {
-			for (child in (data.children:Array<Dynamic>)) {
-				if (hasCycleDynamic(child, seenPaths)) {
-					return true;
+		seenPaths.set(curPath, true);
+
+		if (source != null) {
+			var ref = resolveRef();
+			if (ref != null) {
+				var root = ref;
+				if (Std.isOfType(root, hrt.prefab.fx.BaseFX)) {
+					root = hrt.prefab.fx.BaseFX.BaseFXTools.getFXRoot(root) ?? root;
+				}
+
+				var allRefs = root.flatten(Reference);
+				for (r in allRefs) {
+					if (r.hasCycle(seenPaths)){
+						editMode = oldEditMode;
+						return true;
+					}
 				}
 			}
 		}
+		editMode = oldEditMode;
 		return false;
 	}
 
 	override function makeInteractive() {
-		if( editMode != None )
+		if( editMode )
 			return null;
 		return super.makeInteractive();
 	}
@@ -359,29 +213,28 @@ class Reference extends Object3D {
 			<div class="group" name="Reference">
 			<dl>
 				<dt>Reference</dt><dd><input type="fileselect" extensions="prefab l3d fx" field="source"/></dd>
-				<dt>Edit</dt><dd><select field="editMode" class="monSelector"></select></dd>
-				<p class="warning">Warning : Edit mode enabled while there are override on this reference. Saving will cause the overrides to be applied to the original reference !</p>
+				<dt>Edit</dt><dd><input type="checkbox" field="editMode"/></dd>
 			</dl>
 			</div>');
 
-
-		var warning = element.find(".warning");
-
 		function updateProps() {
 			var input = element.find("input");
 			var found = resolveRef() != null;
 			input.toggleClass("error", !found);
-			warning.toggle(overrides != null && editMode == Edit);
 		}
 		updateProps();
 
 		var props = ctx.properties.add(element, this, function(pname) {
 			ctx.onChange(this, pname);
 			if(pname == "source" || pname == "editMode") {
+				if (pname == "source") {
+					editorRemoveObjects();
+					refInstance = null;
+				}
 				if (hasCycle()) {
 					hide.Ide.inst.quickError('Reference to $source would create a cycle. The reference change was aborted.');
-					source = null;
-					ctx.rebuildProperties();
+					ctx.properties.undo.undo();
+					@:privateAccess ctx.properties.undo.redoElts.pop();
 					return;
 				}
 				updateProps();
@@ -404,43 +257,6 @@ class Reference extends Object3D {
 		});
 
 		super.edit(ctx);
-
-		var over = new hide.Element('
-			<div class="group">
-				<dl>
-					<dt>Overrides</dt><dd><p class="override-infos"></p><fancy-button><span class="label">Clear Overrides</span></fancy-button></dd>
-				</dl>
-			</div>
-		');
-
-		var overInfos = over.find(".override-infos");
-		function refreshOverrideInfos() {
-			if (computeDiffFromSource() == null) {
-				overInfos.text("No overrides");
-			}
-			else {
-				overInfos.text("This reference has overrides");
-			}
-		}
-		refreshOverrideInfos();
-
-		over.find("fancy-button").click((_) -> {
-			var old = overrides;
-			this.overrides = null;
-			var refresh = () -> {
-				if (originalSource != null) {
-					@:privateAccess shared.editor.removeInstance(refInstance, false);
-					originalSource = null;
-					refInstance = null;
-					ctx.rebuildPrefab(this);
-					refreshOverrideInfos();
-				}
-			};
-			@:privateAccess ctx.properties.undo.change(Field(this, "overrides", old), refresh);
-			refresh();
-			//ctx.rebuildPrefab(this);
-		});
-		ctx.properties.add(over);
 	}
 
 	override function getHideProps() : hide.prefab.HideProps {

+ 2 - 4
hrt/prefab/Shader.hx

@@ -203,10 +203,8 @@ class Shader extends Prefab {
 	#if editor
 
 	override function editorRemoveInstanceObjects() : Void {
-		if (shared?.editor != null) {
-			shared.editor.queueRebuild(parent);
-			super.editorRemoveInstanceObjects();
-		}
+		shared.editor.queueRebuild(parent);
+		super.editorRemoveInstanceObjects();
 	}
 
 	function getEditProps(shaderDef: hxsl.SharedShader) : Array<hrt.prefab.Props.PropDef> {

+ 1 - 1
hrt/tools/MapUtils.hx

@@ -3,7 +3,7 @@ import haxe.macro.Expr;
 
 class MapUtils {
 	/**
-		Returns `map[key]` if key if present, else evaluate `def` and puts the result into `map[key]`, then retunrs `map[key]`
+		Returns map[key] if key if present, else execute def and puts it into map[key]
 	**/
 	macro public static function getOrPut<K, V>(map:ExprOf<Map<K, V>>, key:ExprOf<K>, def:ExprOf<V>):Expr {
 		return macro {