Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/avoid strip roots when animated #734

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
99afd90
updated KHR_animatio_pointer to get deserialized
pfcDorn Feb 14, 2024
fbad519
added support for KHR_animationpointer import: Nodes, Lights, Skinnen…
pfcDorn Feb 14, 2024
5fe551e
changed BuildAnimationSamplers to load accessor by accessor data type…
pfcDorn Feb 14, 2024
a089ac1
updated KHR_LightsPunctualExtension to support AnimationPointers
pfcDorn Feb 14, 2024
7924d0e
added material support for animationpointer import (WIP)
pfcDorn Feb 15, 2024
418b8ba
added support for TextureTransform in animation pointer + some refact…
pfcDorn Feb 16, 2024
f33909f
added support for combined animation samples. eg. for emissive factor…
pfcDorn Feb 16, 2024
5d48669
added camera import for nodes and animationpointer
pfcDorn Feb 20, 2024
94a762d
added missing perspective clip plane import for camera animationpointer
pfcDorn Feb 20, 2024
ee9d143
added Extension Class for GLTFAccessorAttributeType to get component …
pfcDorn Feb 20, 2024
8286319
added conversion to gamme color space for KHR_light_punctual animatio…
pfcDorn Feb 20, 2024
5054a4f
fixed innerspotangle on lights when importing and exporting them as a…
pfcDorn Feb 20, 2024
ec83279
refactoring of material mapper, more comments, support for animation …
pfcDorn Feb 20, 2024
c2a0539
fixed rebased issues
pfcDorn Mar 5, 2024
3b08a81
added await YieldOnTimeoutAndThrowOnLowMemory to Import animation
pfcDorn Apr 3, 2024
95b2e04
changed BuildMaterialAnimationPointerData signature to out animationdata
pfcDorn Apr 3, 2024
167f2dd
renamed MaterialPointerPropertyMap Type from Generic to Float
pfcDorn Apr 3, 2024
26f319f
PointerPath refactoring: renaming and removed PathResolver class
pfcDorn Apr 3, 2024
34dafee
refactored AnimationPointerData and AnimPointer Extension interface
pfcDorn Apr 3, 2024
72fa8a7
fixed removing empty root objects when they are animated
pfcDorn Apr 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 78 additions & 48 deletions Editor/Scripts/GLTFImporter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,65 +317,95 @@ string GetUniqueName(string desiredName)
// Remove empty roots
if (gltfScene && _removeEmptyRootObjects)
{
// To avoid removing a Root object which is animated, we collect from all animation clips the paths that are animated.
var pathBindings = new List<string>();
foreach (var aniClip in animations)
{
var bindings = AnimationUtility.GetCurveBindings(aniClip);
var distinctPaths = bindings.Select( x => x.path).Distinct();
pathBindings.AddRange(distinctPaths);
}
pathBindings = pathBindings.Distinct().ToList();

var t = gltfScene.transform;
var existingAnimator = t.GetComponent<Animator>();
var hadAnimator = (bool)existingAnimator;
var existingAvatar = existingAnimator ? existingAnimator.avatar : default;
var rootIsAnimated = false;
if (existingAnimator)
DestroyImmediate(existingAnimator);
{
var firstChildName = t.childCount > 0 ? t.GetChild(0).gameObject.name : "";
// check if the object is animated, when true, cancel here
rootIsAnimated = firstChildName != "" && pathBindings.Contains(firstChildName);

if (!rootIsAnimated)
DestroyImmediate(existingAnimator);
}

var animationPathPrefix = "";
var originalImportName = gltfScene.name;
while (
gltfScene.transform.childCount == 1 &&
gltfScene.GetComponents<Component>().Length == 1) // Transform component
if (!rootIsAnimated)
{
var parent = gltfScene;
gltfScene = gltfScene.transform.GetChild(0).gameObject;
var importName = gltfScene.name;
t = gltfScene.transform;
t.parent = null; // To keep transform information in the new parent
DestroyImmediate(parent); // Get rid of the parent
if (animationPathPrefix != "")
animationPathPrefix += "/";
animationPathPrefix += importName;
}
animationPathPrefix += "/";
while (
gltfScene.transform.childCount == 1 &&
gltfScene.GetComponents<Component>().Length == 1) // Transform component
{
// check if the object is animated, when true, cancel here
if (pathBindings.Contains(animationPathPrefix != ""
? $"{animationPathPrefix}/{gltfScene.transform.GetChild(0).gameObject.name}"
: gltfScene.transform.GetChild(0).gameObject.name))
break;

var parent = gltfScene;
gltfScene = gltfScene.transform.GetChild(0).gameObject;
var importName = gltfScene.name;

// Re-add animator if it was removed
if (hadAnimator)
{
var newAnimator = gltfScene.AddComponent<Animator>();
newAnimator.avatar = existingAvatar;
}

// Re-target animation clips - when we strip the root, all animations also change and have a different path now.
if (animations != null)
{
foreach (var clip in animations)
{
if (clip == null) continue;

// change all animation clip targets
var bindings = AnimationUtility.GetCurveBindings(clip);
var curves = new AnimationCurve[bindings.Length];
var newBindings = new EditorCurveBinding[bindings.Length];
for (var index = 0; index < bindings.Length; index++)
{
var binding = bindings[index];
curves[index] = AnimationUtility.GetEditorCurve(clip, binding);

var newBinding = bindings[index];
if (binding.path.StartsWith(animationPathPrefix, StringComparison.Ordinal))
newBinding.path = binding.path.Substring(animationPathPrefix.Length);
newBindings[index] = newBinding;
}

var emptyCurves = new AnimationCurve[curves.Length];
AnimationUtility.SetEditorCurves(clip, bindings, emptyCurves);
AnimationUtility.SetEditorCurves(clip, newBindings, curves);
}
}
t = gltfScene.transform;
t.parent = null; // To keep transform information in the new parent
DestroyImmediate(parent); // Get rid of the parent
if (animationPathPrefix != "")
animationPathPrefix += "/";
animationPathPrefix += importName;
}

animationPathPrefix += "/";

// Re-add animator if it was removed
if (hadAnimator)
{
var newAnimator = gltfScene.AddComponent<Animator>();
newAnimator.avatar = existingAvatar;
}

// Re-target animation clips - when we strip the root, all animations also change and have a different path now.
if (animations != null)
{
foreach (var clip in animations)
{
if (clip == null) continue;

// change all animation clip targets
var bindings = AnimationUtility.GetCurveBindings(clip);
var curves = new AnimationCurve[bindings.Length];
var newBindings = new EditorCurveBinding[bindings.Length];
for (var index = 0; index < bindings.Length; index++)
{
var binding = bindings[index];
curves[index] = AnimationUtility.GetEditorCurve(clip, binding);

var newBinding = bindings[index];
if (binding.path.StartsWith(animationPathPrefix, StringComparison.Ordinal))
newBinding.path = binding.path.Substring(animationPathPrefix.Length);
newBindings[index] = newBinding;
}

var emptyCurves = new AnimationCurve[curves.Length];
AnimationUtility.SetEditorCurves(clip, bindings, emptyCurves);
AnimationUtility.SetEditorCurves(clip, newBindings, curves);
}
}
} // (!rootIsAnimated)

gltfScene.name = originalImportName;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using GLTF.Math;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using GLTF.Extensions;
using GLTF.Utilities;
using UnityEngine;
using UnityGLTF.Extensions;
using Color = GLTF.Math.Color;

namespace GLTF.Schema.KHR_lights_punctual
{
Expand Down Expand Up @@ -287,7 +290,7 @@ public IExtension Clone(GLTFRoot root)
}
}

public class KHR_LightsPunctualExtension : IExtension
public class KHR_LightsPunctualExtension : IExtension, IImportAnimationPointerRootExtension
{
public List<PunctualLight> Lights;

Expand All @@ -314,6 +317,95 @@ public JProperty Serialize()
)
);
}

private static string[] GltfLightPropertyToUnityPropertyName(string gltfPropertyName)
{
switch (gltfPropertyName)
{
case "color":
return new string[] { "m_Color.r", "m_Color.g", "m_Color.b"};
case "intensity":
return new string[] { "m_Intensity"};
case "range":
return new string[] {"m_Range"};
case "spot/innerConeAngle":
return new string[] {"m_InnerConeAngle"};
case "spot/outerConeAngle":
return new string[] {"m_OuterConeAngle"};
default:
return new string[] {gltfPropertyName};
}
}

private static AnimationPointerData.ImportValuesConversion GetConversion(string gltfPropertyName)
{
switch (gltfPropertyName)
{
case "color":
return (data, index) =>
{
var col = data.primaryData.AccessorContent.AsFloat3s[index];
var color = new Color(col[0], col[1], col[2], 1f).ToUnityColorRaw();
return new float[] { color.r, color.g, color.b };
//return new float[] { col.x, col.y, col.z };
};
case "intensity":
return (data, index) => new float[1] { data.primaryData.AccessorContent.AsFloats[index] / Mathf.PI };
case "range":
return (data, index) => new float[1] { data.primaryData.AccessorContent.AsFloats[index] };
case "spot/innerConeAngle":
return (data, index) => new float[] { data.primaryData.AccessorContent.AsFloats[index] * 2 / (Mathf.Deg2Rad * 0.8f)};
case "spot/outerConeAngle":
return (data, index) => new float[] { data.primaryData.AccessorContent.AsFloats[index] * 2 / Mathf.Deg2Rad};
default:
return null;
}
}

public bool TryGetImportAnimationPointerData(GLTFRoot root, PointerPath pointerPath, out AnimationPointerData pointerData)
{
pointerData = new AnimationPointerData();
pointerData.targetNodeIds = new int[0];
pointerData.targetType = typeof(UnityEngine.Light);

if (root.Nodes == null)
return false;

var pointId = pointerPath.FindNext(PointerPath.PathElement.Index);
if (pointId == null)
return false;

var property = pointerPath.FindNext(PointerPath.PathElement.Property);
if (property == null)
return false;

pointerData.unityPropertyNames = GltfLightPropertyToUnityPropertyName(property.elementName);
pointerData.importAccessorContentConversion = GetConversion(property.elementName);

List<int> targetNodes = new List<int>();
for (int i = 0; i < root.Nodes.Count; i++)
{
var n = root.Nodes[i];
if (n.Extensions != null && n.Extensions.TryGetValue(KHR_lights_punctualExtensionFactory.EXTENSION_NAME, out IExtension extension))
{
if (!(extension is KHR_LightsPunctualNodeExtension lightExtension))
continue;

if (lightExtension.LightId.Id == pointId.index)
{
targetNodes.Add(i);
}
}
}

if (targetNodes.Count > 0)
{
pointerData.targetNodeIds = targetNodes.ToArray();
return true;
}

return false;
}
}

public class PunctualLightId : GLTFId<PunctualLight>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public JProperty Serialize()
if (path == null && clonedFrom != null) path = clonedFrom.path;
return new JProperty(EXTENSION_NAME, new JObject(new JProperty("pointer", path)));
}

public void Deserialize(GLTFRoot root, JProperty extensionToken)
{
var extensionObject = (JObject) extensionToken.Value;
path = (string) extensionObject["pointer"];
}

public IExtension Clone(GLTFRoot root)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Newtonsoft.Json.Linq;
using UnityGLTF.Extensions;

namespace GLTF.Schema
{
public class KHR_animation_pointerExtensionFactory : ExtensionFactory
{
public const string EXTENSION_NAME = "KHR_animation_pointer";


public KHR_animation_pointerExtensionFactory()
{
ExtensionName = EXTENSION_NAME;
}

public override IExtension Deserialize(GLTFRoot root, JProperty extensionToken)
{
if (extensionToken != null)
{
var extensionObject = (JObject) extensionToken.Value;
string path = (string) extensionObject["pointer"];
return new KHR_animation_pointer
{
path = path
};
}

return null;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 20 additions & 11 deletions Runtime/Plugins/GLTFSerialization/GLTFHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using GLTF.Schema;
Expand Down Expand Up @@ -408,20 +409,28 @@ public static void BuildAnimationSamplers(ref Dictionary<string, List<AttributeA
NumericArray resultArray = attributeAccessor.AccessorContent;
LoadBufferView(attributeAccessor, out NativeArray<byte> bufferViewCache);

switch (samplerSet.Key)
switch (attributeAccessor.AccessorId.Value.Type)
{
case "time":
attributeAccessor.AccessorId.Value.AsFloatArray(ref resultArray, bufferViewCache, 0, attributeAccessor.AccessorId.Value.Normalized);
case GLTFAccessorAttributeType.SCALAR:
attributeAccessor.AccessorId.Value.AsFloatArray(ref resultArray, bufferViewCache);
break;
case "translation":
case "scale":
attributeAccessor.AccessorId.Value.AsFloat3Array(ref resultArray, bufferViewCache, 0, attributeAccessor.AccessorId.Value.Normalized);
case GLTFAccessorAttributeType.VEC2:
attributeAccessor.AccessorId.Value.AsFloat2Array(ref resultArray, bufferViewCache);
break;
case "rotation":
attributeAccessor.AccessorId.Value.AsFloat4Array(ref resultArray, bufferViewCache, 0, attributeAccessor.AccessorId.Value.Normalized);
case GLTFAccessorAttributeType.VEC3:
attributeAccessor.AccessorId.Value.AsFloat3Array(ref resultArray, bufferViewCache);
break;
case "weights":
attributeAccessor.AccessorId.Value.AsFloatArray(ref resultArray, bufferViewCache, 0, attributeAccessor.AccessorId.Value.Normalized);
case GLTFAccessorAttributeType.VEC4:
attributeAccessor.AccessorId.Value.AsFloat4Array(ref resultArray, bufferViewCache);
break;
case GLTFAccessorAttributeType.MAT2:
Debug.LogWarning("Unsupported MAT2 animation sampler type");
break;
case GLTFAccessorAttributeType.MAT3:
Debug.LogWarning("Unsupported MAT3 animation sampler type");
break;
case GLTFAccessorAttributeType.MAT4:
attributeAccessor.AccessorId.Value.AsMatrix4x4Array(ref resultArray, bufferViewCache);
break;
}

Expand Down
26 changes: 26 additions & 0 deletions Runtime/Plugins/GLTFSerialization/Schema/Accessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,32 @@ public enum GLTFAccessorAttributeType
MAT4
}

public static class GLTFAccessorAttributeTypeExtensions
{
public static int ComponentCount(this GLTFAccessorAttributeType attrType)
{
switch (attrType)
{
case GLTFAccessorAttributeType.SCALAR:
return 1;
case GLTFAccessorAttributeType.VEC2:
return 2;
case GLTFAccessorAttributeType.VEC3:
return 3;
case GLTFAccessorAttributeType.VEC4:
return 4;
case GLTFAccessorAttributeType.MAT2:
return 4;
case GLTFAccessorAttributeType.MAT3:
return 9;
case GLTFAccessorAttributeType.MAT4:
return 16;
}

return 0;
}
}

/// <summary>
[StructLayout(LayoutKind.Explicit)]
public struct NumericArray
Expand Down
Loading