浏览代码

[scene] Refactored RenderProps handling in editor

Now uses a well defined priority system, code is more centralised, fixes a critical bug where the renderProps was lost in the fx editor when any change was made
Clément Espeute 6 月之前
父节点
当前提交
9e5dd603b7
共有 5 个文件被更改,包括 248 次插入113 次删除
  1. 1 0
      bin/defaultProps.json
  2. 212 99
      hide/comp/SceneEditor.hx
  3. 4 10
      hide/view/Model.hx
  4. 1 1
      hrt/prefab/Light.hx
  5. 30 3
      hrt/prefab/RenderProps.hx

+ 1 - 0
bin/defaultProps.json

@@ -34,6 +34,7 @@
 	"key.paste" : "Ctrl-V",
 	"key.duplicateInPlace" : "Ctrl-D",
 	"key.debugSceneRefresh" : "Ctrl-Shift-R",
+	"key.debugSelectionRefresh" : "Shift-R",
 
 	"key.duplicate" : "Ctrl-Shift-D",
 	"key.delete" : "Delete",

+ 212 - 99
hide/comp/SceneEditor.hx

@@ -692,28 +692,14 @@ class RenderPropsPopup extends Popup {
 		element.css("max-width", "300px");
 
 		var form_div = new Element("<div>").addClass("form-grid").appendTo(element);
-		var lastRenderProps:hrt.prefab.RenderProps = null;
-		var currentRenderProps = @:privateAccess editor.getAllWithRefs(@:privateAccess editor.sceneData, hrt.prefab.RenderProps);
-		for (r in currentRenderProps)
-			if (@:privateAccess r.isDefault) {
-				lastRenderProps = r;
-				break;
-			}
-		if (lastRenderProps == null)
-			lastRenderProps = currentRenderProps[0];
+		var renderProps = @:privateAccess editor.previousSceneRenderProps;
 
 		var fullPath = [];
-		var cur : hrt.prefab.Prefab = lastRenderProps;
-		var renderPropsSource = lastRenderProps?.shared.parentPrefab?.source ?? "inline";
-		renderPropsSource = StringTools.replace(renderPropsSource, "/", "<wbr>/<wbr>");
-		while (cur != null) {
-			fullPath.unshift(cur.getAbsPath());
-			cur = cur.shared.parentPrefab;
-		}
-		var fullPath = fullPath.join("<wbr>&gt;<wbr>");
 
-		if (lastRenderProps != null && !canChangeCurrRp) {
-			form_div.append(new Element('<p>A render props (<code>$renderPropsSource</code> in <code>${fullPath}</code>) is already existing in scene.</p>'));
+		if (renderProps != null && !canChangeCurrRp) {
+			var path = renderProps.getAbsPath(false, true);
+			var renderPropsSource = StringTools.replace(path, ".", "<wbr>.<wbr>");
+			form_div.append(new Element('<p>A render props (<code>$renderPropsSource</code>) already exists in the scene.</p>'));
 			return;
 		}
 
@@ -731,8 +717,17 @@ class RenderPropsPopup extends Popup {
 			cb.on('change', function(){
 				var v = cb.prop('checked');
 				Ide.inst.currentConfig.set("sceneeditor.renderprops.edit", v);
-
 				tmpView.setRenderPropsEditionVisibility(v);
+
+				@:privateAccess
+				if (editor.renderPropsRoot != null) {
+					editor.removeInstance(editor.renderPropsRoot);
+
+					// clear selection
+					editor.selectElements([]);
+					editor.renderPropsRoot = null;
+					editor.queueRefreshRenderProps();
+				}
 			});
 
 			rpEditionEl.find('label').css({ 'padding-left' : '8px' });
@@ -767,9 +762,7 @@ class RenderPropsPopup extends Popup {
 
 				input.change((e) -> {
 					editor.view.saveDisplayState("renderProps", rp);
-					editor.renderPropsRoot = null;
-					editor.refreshScene();
-					@:privateAccess editor.refreshTree();
+					@:privateAccess editor.queueRefreshRenderProps();
 				});
 
 				form_div.append(input);
@@ -910,7 +903,8 @@ class SceneEditor {
 	public var camera2D(default,set) : Bool = false;
 	public var objectAreSelectable = true;
 	public var renderPropsRoot : Reference;
-		var updates : Array<Float -> Void> = [];
+	public var previousSceneRenderProps : hrt.prefab.RenderProps;
+	var updates : Array<Float -> Void> = [];
 
 	var showGizmo = true;
 	var gizmo : hrt.tools.Gizmo;
@@ -934,6 +928,8 @@ class SceneEditor {
 	public var gridSize : Int;
 	public var showGrid = false;
 
+	var currentRenderProps: hrt.prefab.RenderProps;
+
 	var statusText : h2d.Text;
 	var ready = false;
 	var readyDelayed : Array<() -> Void> = [];
@@ -977,7 +973,6 @@ class SceneEditor {
 
 	public var view(default, null) : hide.view.FileView;
 	var sceneData : PrefabElement;
-	var lastRenderProps : hrt.prefab.RenderProps;
 
 	var customEditor : CustomEditor;
 
@@ -1024,6 +1019,7 @@ class SceneEditor {
 		view.keys.register("duplicate", {name: "Duplicate", category: "Scene"}, duplicate.bind(true));
 		view.keys.register("duplicateInPlace", {name: "Duplicate in place", category: "Scene"}, duplicate.bind(false));
 		view.keys.register("debugSceneRefresh", {name: "Refresh debug scene", category: "Scene"}, () -> {ide.quickMessage("Debug : rebuild(sceneData)"); queueRebuild(sceneData);});
+		view.keys.register("debugSelectionRefresh", {name: "Refresh debug Selection", category: "Scene"}, () -> {ide.quickMessage("Debug : rebuild(selectedPrefabs)"); for (s in selectedPrefabs) queueRebuild(s);});
 
 		view.keys.register("group", {name: "Group Selection", category: "Scene"}, groupSelection);
 		view.keys.register("delete", {name: "Delete", category: "Scene"}, () -> deleteElements(selectedPrefabs));
@@ -2097,63 +2093,166 @@ class SceneEditor {
 			ide.fileWatcher.unregister(source, w.callb);
 	}
 
-	function createRenderProps(?parent: hrt.prefab.Prefab){
-		if (renderPropsRoot == null) {
-			renderPropsRoot = new hrt.prefab.Reference(parent, parent?.shared ?? new ContextShared());
-			renderPropsRoot.setEditor(this, this.scene);
+	function teardownRenderer() {
+		scene.s3d.renderer.dispose();
+		scene.s3d.renderer = h3d.mat.MaterialSetup.current.createRenderer();
+	}
 
-			if (parent != null)
-				renderPropsRoot.parent = parent;
+	@:access(hrt.prefab.RenderProps)
+	function setRenderProps(?renderProps: hrt.prefab.RenderProps) {
+		/*
+			Order of priority for render props :
+			1. The render props passed as parameter to this function
+			2. The first currently selected render props
+			3. The first render props in the scene with isDefault == true
+			4. The first render props in the scene
+			5. The chosen default render props in the render props settings
+		*/
 
-			renderPropsRoot.name = "Render Props";
-			@:privateAccess renderPropsRoot.editMode = true;
+		function filter(p: hrt.prefab.RenderProps) : Bool {
+			return p.enabled == true && p.visible == true && !isHidden(p);
+		}
 
-			var renderProps = view.config.getLocal("scene.renderProps");
+		// 1.
+		if (renderProps == null) {
 
-			if (renderProps is String) {
-				renderPropsRoot.source = cast renderProps;
+			// 2.
+			for (prefab in selectedPrefabs) {
+				var asRenderProps = Std.downcast(prefab, hrt.prefab.RenderProps);
+				if (asRenderProps != null && filter(asRenderProps) && checkIsInWorld(asRenderProps)) {
+					renderProps = asRenderProps;
+					break;
+				}
 			}
+		}
 
-			if (renderProps is Array) {
-				var a_renderProps = cast (renderProps, Array<Dynamic>);
-				var savedRenderProp = @:privateAccess view.getDisplayState("renderProps");
-
-				// Check if the saved render prop hasn't been deleted from json
-				var isRenderPropAvailable = false;
-				for (idx in 0...a_renderProps.length) {
-					if (savedRenderProp != null && a_renderProps[idx].value == savedRenderProp.value)
-						isRenderPropAvailable = true;
+		if (renderProps == null) {
+			var all = sceneData.findAll(hrt.prefab.RenderProps, filter, true);
+			// 3.
+			for (rp in all) {
+				if (rp.isDefault == true) {
+					renderProps = rp;
+					break;
 				}
+			}
 
-				renderPropsRoot.source = view.config.getLocal("scene.renderProps")[0].value;
-				if (savedRenderProp != null && isRenderPropAvailable)
-					renderPropsRoot.source = savedRenderProp.value;
+			// 4.
+			if (renderProps == null) {
+				renderProps = all[0];
 			}
 		}
 
-		@:privateAccess renderPropsRoot.shared.root2d = renderPropsRoot.shared.current2d = root2d;
-		@:privateAccess renderPropsRoot.shared.root3d = renderPropsRoot.shared.current3d = root3d;
+		// 5.
+		if (renderProps == null) {
+			// no enabled render props was found, create an external render props (and don't show it in the scene)
+			refreshDefaultRenderProps();
+			previousSceneRenderProps = null;
+			return;
+		}
+
 
-		renderPropsRoot.make();
+		// Init a render props in the scene
 
-		/*var lights = renderPropsRoot.getAll(hrt.prefab.Light, true);
-		for (light in lights) {
-			var ctxs = ctx2.shared.getContexts(light);
-			for (ctx in ctxs) {
-				var icon = Std.downcast(ctx.custom, hrt.impl.EditorTools.EditorIcon);
-				if (icon != null) {
-					icon.remove();
-					ctx.custom = null;
-				}
+		// remove the last external render props if it exists
+		if (renderPropsRoot != null) {
+			removeInstance(renderPropsRoot);
+			renderPropsRoot = null;
+			previousSceneRenderProps = null;
+			renderPropsTree.refresh();
+		}
+
+		if (previousSceneRenderProps != renderProps)
+			teardownRenderer();
+		renderProps.applyProps(scene.s3d.renderer);
+		previousSceneRenderProps = renderProps;
+	}
+
+	function getRenderPropsPath() : String {
+		var renderProps = view.config.getLocal("scene.renderProps");
+		if (renderProps is String) {
+			return cast renderProps;
+		}
+
+		if (renderProps is Array) {
+			var a_renderProps = cast (renderProps, Array<Dynamic>);
+			var savedRenderProp = @:privateAccess view.getDisplayState("renderProps");
+
+			// Check if the saved render prop hasn't been deleted from json
+			var isRenderPropAvailable = false;
+			for (idx in 0...a_renderProps.length) {
+				if (savedRenderProp != null && a_renderProps[idx].value == savedRenderProp.value)
+					isRenderPropAvailable = true;
+			}
+
+			var source = view.config.getLocal("scene.renderProps")[0].value;
+			if (savedRenderProp != null && isRenderPropAvailable)
+				source = savedRenderProp.value;
+			return source;
+		}
+		return null;
+	}
+
+	function refreshDefaultRenderProps(){
+		var path = getRenderPropsPath();
+
+		var needTeardown = false;
+		// remove previous render props if it has changed
+		if (renderPropsRoot != null) {
+			if (renderPropsRoot.source != path) {
+				removeInstance(renderPropsRoot);
+				renderPropsRoot = null;
+				needTeardown = true;
 			}
-		}*/
+		}
+
+		if (previousSceneRenderProps != null) {
+			previousSceneRenderProps = null;
+			needTeardown = true;
+		}
+
+		if (needTeardown) {
+			teardownRenderer();
+		}
+
+		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);
+			renderPropsRoot.name = "Render Props";
+			renderPropsRoot.source = path;
 
+			@:privateAccess renderPropsRoot.shared.root2d = renderPropsRoot.shared.current2d = root2d;
+			@:privateAccess renderPropsRoot.shared.root3d = renderPropsRoot.shared.current3d = root3d;
+
+			// Needed because make will call queueRefreshRenderProps
+			refreshRenderPropsStack ++;
+
+			renderPropsRoot = renderPropsRoot.make();
+
+			refreshRenderPropsStack --;
+
+			renderPropsTree.refresh();
+
+		}
+
+		var wasSet = false;
 		if( @:privateAccess renderPropsRoot.refInstance != null ) {
 			var renderProps = @:privateAccess renderPropsRoot.refInstance.getOpt(hrt.prefab.RenderProps, true);
 			if( renderProps != null ) {
 				renderProps.applyProps(scene.s3d.renderer);
+				wasSet = true;
 			}
 		}
+
+
+		// Clear render props
+		if (!wasSet) {
+			for(fx in scene.s3d.renderer.effects)
+				if ( fx != null )
+					fx.dispose();
+
+			scene.s3d.renderer.props = scene.s3d.renderer.getDefaultProps();
+		}
 	}
 
 	public function refreshScene() {
@@ -2216,6 +2315,8 @@ class SceneEditor {
 
 		refreshTree();
 
+		setRenderProps();
+
 		onRefresh();
 	}
 
@@ -3067,6 +3168,7 @@ class SceneEditor {
 
 		recRemove(elt);
 		elt.editorRemoveObjects();
+		elt.dispose();
 
 		if (checkRebuild)
 			checkWantRebuild(parent, elt);
@@ -3382,7 +3484,24 @@ class SceneEditor {
 			if( curEdit != null )
 				curEdit.cleanup();
 			var edit = makeEditContext(elts);
+
+			var doRefreshRenderProps = false;
+			for (p in selectedPrefabs) {
+				if (Std.downcast(p, hrt.prefab.RenderProps) != null) {
+					doRefreshRenderProps = true;
+					break;
+				}
+			}
+
 			selectedPrefabs = elts;
+
+			for (p in selectedPrefabs) {
+				if (Std.downcast(p, hrt.prefab.RenderProps) != null && (p.shared.parentPrefab == null || p.shared.parentPrefab != renderPropsRoot)) {
+					doRefreshRenderProps = true;
+					break;
+				}
+			}
+
 			var old = selectedParents.copy();
 			selectedParents.clear();
 			for (parent => _ in old) {
@@ -3460,6 +3579,10 @@ class SceneEditor {
 			setupGizmo();
 
 			onSelectionChanged(elts, mode);
+
+			if (doRefreshRenderProps) {
+				queueRefreshRenderProps();
+			}
 		}
 
 		var prev : Array<PrefabElement> = null;
@@ -4045,6 +4168,10 @@ class SceneEditor {
 				var el = tree.getElement(c);
 				if( el != null ) applyTreeStyle(c, el, tree);
 				applySceneStyle(c);
+
+				if (Std.downcast(c, hrt.prefab.RenderProps) != null) {
+					queueRefreshRenderProps();
+				}
 			}
 		}
 		saveDisplayState();
@@ -4414,6 +4541,24 @@ class SceneEditor {
 		}
 	}
 
+	var queuedRefreshRenderProps = false;
+	var queuedRenderProps : hrt.prefab.RenderProps = null;
+	var refreshRenderPropsStack = 0;
+	public function queueRefreshRenderProps(?rp: hrt.prefab.RenderProps) {
+		if (refreshRenderPropsStack > 0)
+			return;
+		refreshRenderPropsStack ++;
+		if (rebuildQueue == null) {
+			setRenderProps(rp);
+			refreshRenderPropsStack --;
+			return;
+		}
+
+		queuedRefreshRenderProps = true;
+		queuedRenderProps = rp;
+		refreshRenderPropsStack --;
+	}
+
 	/** Register a callback that will be called once all the prefabs in this begin/endRebuild pair have been rebuild**/
 	public function queueRebuildCallback(callback: Void -> Void) {
 		if (rebuildEndCallbacks != null) {
@@ -4467,6 +4612,12 @@ class SceneEditor {
 		for (callback in rebuildEndCallbacks) {
 			callback();
 		}
+
+		if (queuedRefreshRenderProps) {
+			setRenderProps(queuedRenderProps);
+			queuedRenderProps = null;
+			queuedRefreshRenderProps = false;
+		}
 		rebuildQueue = null;
 		rebuildEndCallbacks = null;
 	}
@@ -4523,44 +4674,6 @@ class SceneEditor {
 			applySceneStyle(p);
 		}
 
-		if (prefab == sceneData) {
-			var previousRenderProps = lastRenderProps;
-
-			var newRenderProps = null;
-			var renderProps : Array<hrt.prefab.RenderProps> = cast getAllWithRefs(sceneData, hrt.prefab.RenderProps);
-			for( r in renderProps ) {
-				if( @:privateAccess r.isDefault ) {
-					newRenderProps = r;
-					break;
-				}
-			}
-
-			if( newRenderProps == null )
-				newRenderProps = renderProps[0];
-
-			scene.s3d.renderer.props = scene.s3d.renderer.getDefaultProps();
-
-			if (newRenderProps != previousRenderProps) {
-				if (previousRenderProps != null)
-					removeInstance(previousRenderProps);
-				lastRenderProps = newRenderProps;
-				if (lastRenderProps != null) {
-					if (renderPropsRoot != null) {
-						removeInstance(renderPropsRoot);
-						renderPropsRoot = null;
-					}
-
-					lastRenderProps.applyProps(scene.s3d.renderer);
-				} else {
-					createRenderProps();
-				}
-			}
-			else {
-				if (newRenderProps == null && renderPropsRoot == null) {
-					createRenderProps();
-				}
-			}
-		}
 		rebuildStack --;
 	}
 

+ 4 - 10
hide/view/Model.hx

@@ -1206,15 +1206,17 @@ class Model extends FileView {
 		for (c in @:privateAccess sceneEditor.sceneData.children)
 			@:privateAccess sceneEditor.sceneData.children.remove(c);
 
+		@:privateAccess sceneEditor.removeInstance(sceneEditor.renderPropsRoot);
+		sceneEditor.renderPropsRoot = null;
 
-		@:privateAccess sceneEditor.createRenderProps(@:privateAccess sceneEditor.sceneData);
+		@:privateAccess sceneEditor.queueRefreshRenderProps();
 
 		if (sceneEditor.renderPropsRoot != null && sceneEditor.renderPropsRoot.source != null)
 			root.children.push(sceneEditor.renderPropsRoot);
 
 		// Create default render props if no render props has been created yet
 		var r = root.getOpt(hrt.prefab.RenderProps, true);
-		if( r == null) {
+		if( r == null && sceneEditor.renderPropsRoot == null) {
 			var def = new hrt.prefab.Object3D(root, null);
 			def.name = "Default Ligthing";
 			var render = new hrt.prefab.RenderProps(def, null);
@@ -1238,14 +1240,6 @@ class Model extends FileView {
 			r.applyProps(scene.s3d.renderer);
 		}
 
-		// Apply render props properties on scene
-		var refPrefab = new hrt.prefab.Reference(null, null);
-		if( @:privateAccess refPrefab.refInstance != null ) {
-			var renderProps = @:privateAccess refPrefab.refInstance.getOpt(hrt.prefab.RenderProps);
-			if( renderProps != null )
-				renderProps.applyProps(scene.s3d.renderer);
-		}
-
 		plight = root.find(hrt.prefab.Light);
 		if( plight != null ) {
 			this.light = hrt.prefab.Object3D.getLocal3d(plight);

+ 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) {
+		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

+ 30 - 3
hrt/prefab/RenderProps.hx

@@ -15,8 +15,16 @@ class RenderProps extends Object3D {
 
 	override function makeObject(parent3d: h3d.scene.Object) : h3d.scene.Object {
 		return new RenderPropsObject(parent3d);
+	}
+
+	#if editor
+	override function postMakeInstance() {
+		super.postMakeInstance();
 
+		// force the refresh of the renderProps
+		shared.editor?.queueRefreshRenderProps();
 	}
+	#end
 
 	public function getProps(renderer: h3d.scene.Renderer) {
 		var p = Reflect.field(this.props, h3d.mat.MaterialSetup.current.name);
@@ -101,11 +109,28 @@ class RenderProps extends Object3D {
 		var env = getOpt(hrt.prefab.l3d.Environment);
 		if( env != null )
 			env.applyToRenderer(renderer);
+		// #if editor
+		// else {
+		// 	var r = Std.downcast(renderer, h3d.scene.pbr.Renderer);
+		// 	if( r != null )
+		// 		r.env = hide.Renderer.PbrSetup.getEnvMap();
+		// }
+		// #end
 		renderer.refreshProps();
 		return true;
 	}
 
 	#if editor
+	override function updateInstance(?propName:String) {
+		super.updateInstance(propName);
+		if (propName == "visible") {
+			shared.editor?.queueRefreshRenderProps();
+		}
+	}
+
+	override function editorRemoveInstanceObjects() {
+		shared.editor?.queueRefreshRenderProps();
+	}
 
 	override function edit(ctx) {
 		super.edit(ctx);
@@ -121,10 +146,12 @@ class RenderProps extends Object3D {
 				setProps(props);
 				needSet = false;
 			}
-			applyProps(renderer);
+			shared.editor?.queueRefreshRenderProps();
+		});
+		ctx.properties.add(new hide.Element('<dl><dt>Make Default</dt><dd><input type="checkbox" field="isDefault"/></dd></dl>'), this, (_) -> {
+			shared.editor?.queueRefreshRenderProps();
 		});
-		ctx.properties.add(new hide.Element('<dl><dt>Make Default</dt><dd><input type="checkbox" field="isDefault"/></dd></dl>'), this);
-		applyProps(renderer);
+		shared.editor?.queueRefreshRenderProps();
 	}
 
 	override function getHideProps() : hide.prefab.HideProps {