diff options
| -rwxr-xr-x | Scripts/Fold/Editor/FoldEditorWindow.cs | 38 | ||||
| -rwxr-xr-x | vertex_deformation.slang | 29 |
2 files changed, 47 insertions, 20 deletions
diff --git a/Scripts/Fold/Editor/FoldEditorWindow.cs b/Scripts/Fold/Editor/FoldEditorWindow.cs index 48bfe2b..7cfb8aa 100755 --- a/Scripts/Fold/Editor/FoldEditorWindow.cs +++ b/Scripts/Fold/Editor/FoldEditorWindow.cs @@ -249,13 +249,16 @@ public class FoldEditorWindow : EditorWindow { EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button("Create Keyframe", GUILayout.Height(30))) + if (GUILayout.Button(new GUIContent("W", "Write keyframe at playhead"), GUILayout.Width(36), GUILayout.Height(36))) ApplyToMaterial(recordKeyframes: true); - if (GUILayout.Button("Delete Keyframe", GUILayout.Height(30), GUILayout.Width(120))) + if (GUILayout.Button(new GUIContent("R", "Read animation values at playhead into editor"), GUILayout.Width(36), GUILayout.Height(36))) + ReadFromPlayhead(); + + if (GUILayout.Button(new GUIContent("X", "Delete keyframe at playhead"), GUILayout.Width(36), GUILayout.Height(36))) DeleteKeyframeAtCurrentTime(); - if (GUILayout.Button("Clear All", GUILayout.Height(30), GUILayout.Width(80))) + if (GUILayout.Button(new GUIContent("C", "Clear all operations"), GUILayout.Width(36), GUILayout.Height(36))) { if (EditorUtility.DisplayDialog("Clear All Operations", "Remove all operations from the pipeline?", "Clear", "Cancel")) @@ -272,13 +275,13 @@ public class FoldEditorWindow : EditorWindow EditorGUILayout.Space(3); EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button(new GUIContent("<", "Retreat playhead"), GUILayout.Width(30), GUILayout.Height(20))) + if (GUILayout.Button(new GUIContent("<", "Retreat playhead"), GUILayout.Width(36), GUILayout.Height(36))) AdvancePlayhead(-frameStep); - frameStep = EditorGUILayout.IntField(frameStep, GUILayout.Width(40)); + frameStep = EditorGUILayout.IntField(frameStep, GUILayout.Width(40), GUILayout.Height(36)); if (frameStep < 1) frameStep = 1; - if (GUILayout.Button(new GUIContent(">", "Advance playhead"), GUILayout.Width(30), GUILayout.Height(20))) + if (GUILayout.Button(new GUIContent(">", "Advance playhead"), GUILayout.Width(36), GUILayout.Height(36))) AdvancePlayhead(frameStep); - if (GUILayout.Button(new GUIContent("x", "Snap to nearest keyframe"), GUILayout.Width(30), GUILayout.Height(20))) + if (GUILayout.Button(new GUIContent("Q", "Quantize: snap to nearest keyframe"), GUILayout.Width(36), GUILayout.Height(36))) SnapToNearestKeyframe(); GUILayout.FlexibleSpace(); EditorGUILayout.LabelField($"Operations: {operations.Count}/16", EditorStyles.miniLabel); @@ -376,6 +379,20 @@ public class FoldEditorWindow : EditorWindow #region Animation Recording + void ReadFromPlayhead() + { + if (!TryGetAnimationWindowState(out var clip, out float time)) + return; + + var savedExpanded = new List<int>(expandedOps); + LoadFromAnimationClip(clip, time); + expandedOps = savedExpanded; + expandedOps.RemoveAll(i => i >= operations.Count); + lastAnimTime = time; + ApplyToMaterial(); + Repaint(); + } + void RecordAnimationKeyframes(FoldPipelineBuilder builder) { if (targetObject == null || !AnimationMode.InAnimationMode()) @@ -519,7 +536,10 @@ public class FoldEditorWindow : EditorWindow if (!TryGetAnimationWindowState(out var clip, out float time)) return; float frameDuration = 1f / clip.frameRate; - SetAnimationWindowTime(time + frames * frameDuration); + float newTime = time + frames * frameDuration; + SetAnimationWindowTime(newTime); + lastAnimTime = newTime; // Suppress OnEditorUpdate from reloading animation data. + ApplyToMaterial(); // Re-apply current editor state so the scene stays in sync. Repaint(); } @@ -552,6 +572,8 @@ public class FoldEditorWindow : EditorWindow if (bestDist < float.MaxValue) { SetAnimationWindowTime(bestTime); + lastAnimTime = bestTime; // Suppress OnEditorUpdate from reloading animation data. + ApplyToMaterial(); // Re-apply current editor state so the scene stays in sync. Repaint(); } } diff --git a/vertex_deformation.slang b/vertex_deformation.slang index de53394..b02d154 100755 --- a/vertex_deformation.slang +++ b/vertex_deformation.slang @@ -330,10 +330,10 @@ public void sine_wave_normal(inout float3 xyz, inout float3 normal, inout float3 [Differentiable] public float3 norm_conversion(float3 xyz, no_diff float input_k, no_diff float output_k, no_diff float t) { - float3 xyz_abs = abs(xyz)+1e-4f; + float3 xyz_abs = dabs(xyz)+1e-4f; float lin = pow(pow(xyz_abs.x, input_k) + pow(xyz_abs.y, input_k) + pow(xyz_abs.z, input_k), 1.0f / input_k); float lout = pow(pow(xyz_abs.x, output_k) + pow(xyz_abs.y, output_k) + pow(xyz_abs.z, output_k), 1.0f / output_k); - float scale = lerp(1.0f, lout / lin, t); + float scale = dlerp(1.0f, lout / lin, t); return xyz * scale; } @@ -512,10 +512,9 @@ public float3 plane_to_hemi_octahedron(float3 xyz, // Use differentiable abs and max for smooth autodiff float y = dmax(0.0f, 1.0f - dabs(x_rot) - dabs(z_rot)); - // Normalize to unit sphere (differentiable safe normalize) float3 oct_pos = float3(x_rot, y, z_rot); float len = dot(oct_pos, oct_pos); - oct_pos = (oct_pos / sqrt(len)) * (1.0f + xyz.y); + oct_pos = oct_pos * (1.0f + xyz.y); // Rotate back by -45° around y to undo input rotation float x_unrot = (oct_pos.x - oct_pos.z) * RCP_SQRT_2; @@ -557,19 +556,22 @@ public float3 hemi_octahedron_to_plane(float3 xyz, float x_rot = (xyz.x + xyz.z) * RCP_SQRT_2; float z_rot = (xyz.z - xyz.x) * RCP_SQRT_2; - // Octahedral encode: project normalized direction to plane - float3 N = normalize(float3(x_rot, xyz.y, z_rot)); - N.y = dmax(N.y, 1e-4f); - float L1 = dabs(N.x) + dabs(N.y) + dabs(N.z); - float x_oct = N.x / L1; - float z_oct = N.z / L1; + // The forward pass scales by (1 + s_original), and the unscaled hemi-octahedron + // has L1 norm = 1, so L1 of the scaled point recovers s_original. + float L1_scaled = dabs(x_rot) + dabs(xyz.y) + dabs(z_rot); + float s_original = L1_scaled - 1.0f; + + // Remove (1 + s) scale to get the unit hemi-octahedron point, then decode. + float L1_inv = 1.0f / L1_scaled; + float x_oct = x_rot * L1_inv; + float z_oct = z_rot * L1_inv; // Undo the initial 45° rotation (x_rot = (x+z)*0.5, z_rot = (z-x)*0.5) // Inverse: x = x_rot - z_rot, z = x_rot + z_rot float x_plane = x_oct - z_oct; float z_plane = x_oct + z_oct; - float3 plane_pos = float3(x_plane, 0.0f, z_plane); + float3 plane_pos = float3(x_plane, s_original, z_plane); // Interpolate between original position and plane position float3 result = dlerp(xyz0, plane_pos, t); @@ -633,14 +635,17 @@ public float3 octahedron_to_plane(float3 xyz, xyz = mul(to_rsrxs, xyz - p); float3 xyz0 = xyz; + // The forward pass scales by (1 + s_original), and the unscaled octahedron + // has L1 norm = 1, so L1 of the scaled point recovers s_original. float l1_norm = dabs(xyz.x) + dabs(xyz.y) + dabs(xyz.z); + float s_original = l1_norm - 1.0f; xyz /= l1_norm; float2 xz_tmp = xyz.xz; if (xyz.y < 0) { xyz.x = sign(xz_tmp[0]) * (1 - dabs(xz_tmp[1])); xyz.z = sign(xz_tmp[1]) * (1 - dabs(xz_tmp[0])); } - xyz.y = 0; + xyz.y = s_original; float3 result = dlerp(xyz0, xyz, t); xyz = mul(to_cart, result) + p; |
