Răsfoiți Sursa

[animgraph] Improved layout of blendspace parameters

- new anim picker
Clément Espeute 8 luni în urmă
părinte
comite
ecaf05df51

+ 54 - 0
bin/style.css

@@ -4176,6 +4176,12 @@ button-2 {
 button-2:hover {
   background-color: var(--hover);
 }
+button-2 value {
+  flex-grow: 1;
+  overflow-x: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+}
 button-2:active {
   box-shadow: inset var(--sublte-shadow);
 }
@@ -4451,6 +4457,54 @@ blend-space-2d-root properties-container {
 blend-space-2d-root properties-container parameters-container {
   flex-grow: 1;
 }
+blend-space-2d-root properties-container .hide-properties {
+  padding: 1em;
+  display: flex;
+  flex-direction: column;
+  gap: 0.5em;
+}
+blend-space-2d-root properties-container .hide-properties input {
+  min-width: 0;
+  width: 0;
+}
+blend-space-2d-root properties-container .hide-properties dl {
+  display: grid;
+  grid-template-columns: 6em minmax(0, 1fr);
+  row-gap: 2px;
+  column-gap: 1em;
+  width: 100%;
+}
+blend-space-2d-root properties-container .hide-properties dl > div {
+  display: grid;
+  grid-column: 1 / -1;
+  grid-template-columns: subgrid;
+}
+blend-space-2d-root properties-container .hide-properties dl > div > dt {
+  width: unset;
+  text-align: right;
+}
+blend-space-2d-root properties-container .hide-properties dl > div > dd {
+  width: unset;
+  margin-left: 0;
+  display: flex;
+  justify-content: stretch;
+}
+blend-space-2d-root properties-container .hide-properties dl > div > dd > * {
+  flex: 1 1;
+}
+blend-space-2d-root properties-container .hide-properties dl > div .hide-range {
+  display: flex;
+}
+blend-space-2d-root properties-container .hide-properties dl > div .hide-range input[type="range"] {
+  min-width: 0;
+  width: 0;
+  flex: 1 1;
+}
+blend-space-2d-root properties-container .hide-properties dl > div .hide-range input[type="text"] {
+  min-width: 0;
+  width: 0;
+  flex: 0 0 4em;
+}
 .basic-border {
   padding: var(--basic-padding);
   border: var(--basic-border);

+ 66 - 0
bin/style.less

@@ -4934,6 +4934,13 @@ button-2 {
 		background-color: var(--hover);
 	}
 
+	value {
+		flex-grow: 1;
+		overflow-x: hidden;
+		white-space: nowrap;
+		text-overflow: ellipsis;
+	}
+
 	&:active {
 		box-shadow: inset var(--sublte-shadow);
 	}
@@ -5277,6 +5284,65 @@ blend-space-2d-root {
 		parameters-container {
 			flex-grow: 1;
 		}
+
+		.hide-properties {
+			padding: 1em;
+			display: flex;
+			flex-direction: column;
+
+			gap: 0.5em;
+
+			input {
+				min-width: 0;
+				width: 0;
+			}
+
+			dl {
+				display: grid;
+				grid-template-columns: 6em minmax(0, 1fr);
+				row-gap: 2px;
+				column-gap: 1em;
+				width: 100%;
+
+				> div {
+					display: grid;
+					grid-column: 1 / -1;
+					grid-template-columns: subgrid;
+					> dt {
+						width: unset;
+						text-align: right;
+					}
+
+					> dd {
+						width: unset;
+						margin-left: 0;
+
+						display: flex;
+						justify-content: stretch;
+
+						> * {
+							flex: 1 1;
+						}
+					}
+
+					.hide-range {
+						display: flex;
+						input[type="range"] {
+							min-width: 0;
+							width: 0;
+							flex: 1 1;
+						}
+
+						input[type="text"] {
+							min-width: 0;
+							width: 0;
+							flex: 0 0 4em;
+						}
+					}
+				}
+			}
+		}
+
 	}
 
 }

+ 28 - 4
hide/view/animgraph/BlendSpace2DEditor.hx

@@ -277,7 +277,7 @@ class BlendSpace2DEditor extends hide.view.FileView {
 
 		propertiesContainer = new hide.Element("<properties-container></properties-container>").appendTo(root);
 		{
-			var paramContainer = new Element('<parameters-container></parameters-container>').appendTo(propertiesContainer).addClass("hide-properties");
+			var paramContainer = new Element('<parameters-container></parameters-container>').appendTo(propertiesContainer);
 			new Element("<h1>Parameters</h1>").appendTo(paramContainer);
 			propsEditor = new hide.comp.PropsEditor(undo, paramContainer);
 			refreshPropertiesPannel();
@@ -346,6 +346,7 @@ class BlendSpace2DEditor extends hide.view.FileView {
 	function refreshPropertiesPannel() {
 		propsEditor.clear();
 
+
 		propsEditor.add(new hide.Element('
 		<div class="group" name="BlendSpace">
 			<dl>
@@ -358,20 +359,43 @@ class BlendSpace2DEditor extends hide.view.FileView {
 		});
 
 		if (selectedPoint != -1) {
-			propsEditor.add(new hide.Element('
+			var editor = new hide.Element('
 				<div class="group" name="Point">
 					<dl>
 						<dt>X</dt><dd><input type="range" min="0.0" max="1.0" field="x"/></dd>
 						<dt>Y</dt><dd><input type="range" min="0.0" max="1.0" field="y"/></dd>
 						<dt>Anim speed</dt><dd><input type="range" min="0.1" max="2.0" field="speed"/></dd>
-						<dt>Anim</dt><dd><input type="fileselect" extensions="fbx" field="animPath"/></dd>
 					</dl>
 				</div>
-			'), blendSpace2D.points[selectedPoint], (_) -> {
+			');
+
+			propsEditor.add(editor, blendSpace2D.points[selectedPoint], (_) -> {
 				blendSpace2D.triangulate();
 				refreshGraph();
 				refreshPreviewAnimation();
 			});
+
+			var div = new Element("<div></div>").appendTo(editor.find("dl"));
+			new Element("<dt>Anim</dt>").appendTo(div);
+			var dd = new Element("<dd>").appendTo(div);
+			var button = new hide.comp.Button(dd, null, "", {hasDropdown: true});
+			button.label = blendSpace2D.points[selectedPoint].animPath;
+			button.onClick = () -> {
+				hide.comp.ContextMenu.createDropdown(button.element.get(0), [
+					{
+						label: "Choose File ...",
+						click: () -> {
+						ide.chooseFile(["fbx"], (path) -> {
+								var old = blendSpace2D.points[selectedPoint].animPath;
+								blendSpace2D.points[selectedPoint].animPath = path;
+								undo.change(Field(blendSpace2D.points[selectedPoint], "animPath", old), () -> {
+									button.label = blendSpace2D.points[selectedPoint].animPath;
+								});
+							}, true);
+						}
+					}
+				], {search: Visible, autoWidth: true});
+			};
 		}
 
 		propsEditor.add(new hide.Element('

+ 3 - 2
hrt/animgraph/AnimGraph.hx

@@ -16,6 +16,7 @@ typedef SerializedEdge = {
 	outputId: Int,
 };
 
+
 @:access(hrt.animgraph.AnimGraphInstance)
 class AnimGraph extends hrt.prefab.Prefab {
 
@@ -34,8 +35,8 @@ class AnimGraph extends hrt.prefab.Prefab {
 		Get the animation "template" for this AnimGraph.
 		This anim should be instanciated using getInstance() after that (or use the h3d.scene.Object.playAnimation() function that does this for you)
 	**/
-	public function getAnimation(previewNode: hrt.animgraph.nodes.AnimNode = null) : AnimGraphInstance {
-		return AnimGraphInstance.fromAnimGraph(this, previewNode);
+	public function getAnimation(previewNode: hrt.animgraph.nodes.AnimNode = null, resolver: hrt.animgraph.AnimGraphInstance.AnimResolver = null) : AnimGraphInstance {
+		return AnimGraphInstance.fromAnimGraph(this, previewNode, resolver);
 	}
 
 	override function save() {

+ 19 - 6
hrt/animgraph/AnimGraphInstance.hx

@@ -10,6 +10,8 @@ class AnimGraphAnimatedObject extends h3d.anim.Animation.AnimatedObject {
 	}
 }
 
+typedef AnimResolver = (instance: AnimGraphInstance, target: h3d.scene.Object, path: String ) -> Null<String>;
+
 @:access(hrt.animgraph.AnimGraph)
 @:access(hrt.animgraph.Node)
 class AnimGraphInstance extends h3d.anim.Animation {
@@ -24,28 +26,33 @@ class AnimGraphInstance extends h3d.anim.Animation {
 	var syncCtx = new hrt.animgraph.nodes.AnimNode.GetBoneTransformContext();
 	var defaultPoseNode = new hrt.animgraph.nodes.DefaultPose();
 
+	var resolver : AnimResolver = null;
+
 	#if editor
 	var editorSkipClone : Bool = false;
 	#end
 
-	static function fromAnimGraph(animGraph:AnimGraph, outputNode: hrt.animgraph.nodes.AnimNode = null) : AnimGraphInstance {
+	static function fromAnimGraph(animGraph:AnimGraph, outputNode: hrt.animgraph.nodes.AnimNode = null, resolver: AnimResolver) : AnimGraphInstance {
 		outputNode ??= cast animGraph.nodes.find((node) -> Std.downcast(node, hrt.animgraph.nodes.Output) != null);
 		if (outputNode == null)
 			throw "Animgraph has no output node";
 
-		var inst = new AnimGraphInstance(outputNode, animGraph.name, 1000, 1/60.0);
-
+		var inst = new AnimGraphInstance(outputNode, resolver, animGraph.name, 1000, 1/60.0);
 		return inst;
 	}
 
-	public function new(rootNode: hrt.animgraph.nodes.AnimNode, name: String, framesCount: Int, sampling: Float) {
+	public function new(rootNode: hrt.animgraph.nodes.AnimNode, resolver: AnimResolver = null, name: String, framesCount: Int, sampling: Float) {
 		// Todo : Define a true length for the animation OR make so animations can have an undefined length
 		super(name, framesCount, sampling);
 		this.rootNode = rootNode;
-
+		this.resolver = resolver ?? defaultResolver;
 		defaultPoseNode = new hrt.animgraph.nodes.DefaultPose();
 	}
 
+	static function defaultResolver(instance: AnimGraphInstance, target: h3d.scene.Object, path: String) : Null<String> {
+		return path;
+	}
+
 	public function setParam(name: String, value: Float) {
 		var param = parameterMap.get(name);
 		if (param != null) {
@@ -78,7 +85,7 @@ class AnimGraphInstance extends h3d.anim.Animation {
 		#end
 		if (target != null) throw "Unexpected";
 
-		var inst = new AnimGraphInstance(null, name, frameCount, sampling);
+		var inst = new AnimGraphInstance(null, resolver, name, frameCount, sampling);
 		inst.rootNode = cast cloneRec(rootNode, inst);
 		super.clone(inst);
 		return inst;
@@ -127,6 +134,7 @@ class AnimGraphInstance extends h3d.anim.Animation {
 
 		var ctx = new hrt.animgraph.nodes.AnimNode.GetBoneContext();
 		ctx.targetObject = base;
+		ctx.resolver = resolver.bind(this, base);
 
 		var bones = getBones(ctx);
 		if (bones != null) {
@@ -223,4 +231,9 @@ class AnimGraphInstance extends h3d.anim.Animation {
 		node.tick(dt);
 		node.tickedThisFrame = true;
 	}
+
+	/**
+		Set this function in your project to programaticaly return an animation path
+	**/
+	static var customAnimResolver : (instance: AnimGraphInstance, name: String) -> Null<String> = null;
 }

+ 1 - 0
hrt/animgraph/nodes/AnimNode.hx

@@ -9,6 +9,7 @@ class GetBoneContext {
 	}
 
 	public var targetObject:h3d.scene.Object;
+	public var resolver : (path: String) -> Null<String>;
 }
 
 class GetBoneTransformContext {

+ 19 - 15
hrt/animgraph/nodes/BlendSpace2D.hx

@@ -71,26 +71,30 @@ class BlendSpace2D extends AnimNode {
 			if (blendSpacePoint.animPath != null && blendSpacePoint.animPath.length > 0) {
 				try
 				{
-					var animIndex = animMap.getOrPut(blendSpacePoint.animPath, {
-						// Create a new animation
-						var index = animInfos.length;
-						var animBase = hxd.res.Loader.currentInstance.load(blendSpacePoint.animPath).toModel().toHmd().loadAnimation();
+					var path = ctx.resolver(blendSpacePoint.animPath);
+					if (path != null) {
+						var animIndex = animMap.getOrPut(path, {
+							// Create a new animation
+							var index = animInfos.length;
+							var animBase = hxd.res.Loader.currentInstance.load(path).toModel().toHmd().loadAnimation();
 
-						var proxy = new hrt.animgraph.nodes.Input.AnimProxy(null);
-						var animInstance = animBase.createInstance(proxy);
+							var proxy = new hrt.animgraph.nodes.Input.AnimProxy(null);
+							var animInstance = animBase.createInstance(proxy);
 
-						var indexRemap : Array<Null<Int>> = [];
+							var indexRemap : Array<Null<Int>> = [];
 
-						for (boneId => obj in animInstance.getObjects()) {
-							var ourId = boneMap.getOrPut(obj.objectName, curOurBoneId++);
-							indexRemap[ourId] = boneId;
-						}
+							for (boneId => obj in animInstance.getObjects()) {
+								var ourId = boneMap.getOrPut(obj.objectName, curOurBoneId++);
+								indexRemap[ourId] = boneId;
+							}
+
+							animInfos.push({anim: animInstance, proxy: proxy, indexRemap: indexRemap});
+							index;
+						});
 
-						animInfos.push({anim: animInstance, proxy: proxy, indexRemap: indexRemap});
-						index;
-					});
+						point.animInfo = animInfos[animIndex];
+					}
 
-					point.animInfo = animInfos[animIndex];
 				} catch (e) {
 					trace('Couldn\'t load anim ${blendSpacePoint.animPath} : ${e.toString()}');
 				}