Skip to content

Commit

Permalink
Merge pull request #168 from anatawa12/fix-component-at-same-place
Browse files Browse the repository at this point in the history
Reimplemented Animation Mapping System Completely
  • Loading branch information
anatawa12 authored May 20, 2023
2 parents 5af8144 + 93a7f66 commit 1430e6d
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 550 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG-PRERELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog].
### Added

### Changed
- Reimplemented Animation Mapping System Completely `#168`
- This should fixes problem with objects/components at same place.

### Deprecated

Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ The format is based on [Keep a Changelog].
### Added

### Changed
- Reimplemented Animation Mapping System Completely `#168`
- This should fixes problem with objects/components at same place.

### Deprecated

Expand Down
480 changes: 0 additions & 480 deletions Editor/ObjectMapping.cs

This file was deleted.

3 changes: 3 additions & 0 deletions Editor/ObjectMapping.meta

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

125 changes: 125 additions & 0 deletions Editor/ObjectMapping/AnimationObjectMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;

namespace Anatawa12.AvatarOptimizer
{
internal class AnimationObjectMapper
{
readonly GameObject _rootGameObject;
readonly BeforeGameObjectTree _beforeGameObjectTree;
readonly ObjectMapping _objectMapping;

private readonly Dictionary<string, MappedGameObjectInfo> _pathsCache =
new Dictionary<string, MappedGameObjectInfo>();

public AnimationObjectMapper(GameObject rootGameObject, BeforeGameObjectTree beforeGameObjectTree,
ObjectMapping objectMapping)
{
_rootGameObject = rootGameObject;
_beforeGameObjectTree = beforeGameObjectTree;
_objectMapping = objectMapping;
}

// null means nothing to map
[CanBeNull]
private MappedGameObjectInfo GetGameObjectInfo(string path)
{
if (_pathsCache.TryGetValue(path, out var info)) return info;

var tree = _beforeGameObjectTree;

if (path != "")
{
foreach (var pathSegment in path.Split('/'))
{
tree = tree.Children.FirstOrDefault(x => x.Name == pathSegment);
if (tree == null) break;
}
}

if (tree == null)
{
info = null;
}
else
{
var foundGameObject = EditorUtility.InstanceIDToObject(tree.InstanceId) as GameObject;
var newPath = foundGameObject
? Utils.RelativePath(_rootGameObject.transform, foundGameObject.transform)
: null;

info = new MappedGameObjectInfo(_objectMapping, newPath, tree);
}

_pathsCache.Add(path, info);
return info;
}

class MappedGameObjectInfo
{
private ObjectMapping _objectMapping;

readonly BeforeGameObjectTree _tree;

// null means removed gameObject
[CanBeNull] public readonly string NewPath;

public MappedGameObjectInfo(ObjectMapping objectMapping, string newPath,
BeforeGameObjectTree tree)
{
_objectMapping = objectMapping;
NewPath = newPath;
_tree = tree;
}

public (int instanceId, ComponentInfo) GetComponentByType(Type type)
{
if (!_tree.ComponentInstanceIdByType.TryGetValue(type, out var instanceId))
return (instanceId, null); // Nothing to map
return (instanceId, _objectMapping.GetComponentMapping(instanceId));
}
}

public EditorCurveBinding MapBinding(EditorCurveBinding binding)
{
var gameObjectInfo = GetGameObjectInfo(binding.path);
if (gameObjectInfo == null) return binding;
var (instanceId, componentInfo) = gameObjectInfo.GetComponentByType(binding.type);

if (componentInfo != null)
{
var component = EditorUtility.InstanceIDToObject(componentInfo.MergedInto) as Component;
// there's mapping about component.
// this means the component is merged or some prop has mapping
if (!component) return default; // this means removed.

var newPath = Utils.RelativePath(_rootGameObject.transform, component.transform);
if (newPath == null) return default; // this means moved to out of the animator scope

binding.path = newPath;

if (componentInfo.PropertyMapping.TryGetValue(binding.propertyName, out var newProp))
binding.propertyName = newProp;
}
else
{
// The component is not merged & no prop mapping so process GameObject mapping

if (binding.type != typeof(GameObject))
{
var component = EditorUtility.InstanceIDToObject(instanceId) as Component;
if (!component) return default; // this means removed
}

if (gameObjectInfo.NewPath == null) return default;
binding.path = gameObjectInfo.NewPath;
}

return binding;
}
}
}
3 changes: 3 additions & 0 deletions Editor/ObjectMapping/AnimationObjectMapper.cs.meta

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

102 changes: 102 additions & 0 deletions Editor/ObjectMapping/ObjectMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using UnityEditor;
using UnityEngine;

namespace Anatawa12.AvatarOptimizer
{
internal class ObjectMapping
{
private readonly IReadOnlyDictionary<int, BeforeGameObjectTree> _beforeTree;
private readonly IReadOnlyDictionary<int, ComponentInfo> _componentMapping;

public ObjectMapping(
IReadOnlyDictionary<int, BeforeGameObjectTree> beforeTree,
IReadOnlyDictionary<int, ComponentInfo> componentMapping)
{
_beforeTree = beforeTree;
_componentMapping = componentMapping;
}

public bool MapComponentInstance(int instanceId, out Component component)
{
var mergedInto = _componentMapping.TryGetValue(instanceId, out var info) ? info.MergedInto : instanceId;

component = EditorUtility.InstanceIDToObject(mergedInto) as Component;
return instanceId != mergedInto || component == null;
}

// null means nothing to map
[CanBeNull]
public ComponentInfo GetComponentMapping(int instanceId) =>
_componentMapping.TryGetValue(instanceId, out var info) ? info : null;

[CanBeNull]
public AnimationObjectMapper CreateAnimationMapper(GameObject rootGameObject)
{
if (!_beforeTree.TryGetValue(rootGameObject.GetInstanceID(), out var beforeTree)) return null;
return new AnimationObjectMapper(rootGameObject, beforeTree, this);
}
}
class BeforeGameObjectTree
{
public readonly int InstanceId;
public readonly int ParentInstanceId;
[NotNull] public readonly string Name;
[NotNull] public readonly IReadOnlyDictionary<Type, int> ComponentInstanceIdByType;
[NotNull] public readonly int[] ComponentInstanceIds;
[NotNull] public readonly BeforeGameObjectTree[] Children;

public BeforeGameObjectTree(GameObject gameObject)
{
var parentTransform = gameObject.transform.parent;
InstanceId = gameObject.GetInstanceID();
Name = gameObject.name;
ParentInstanceId = parentTransform ? parentTransform.gameObject.GetInstanceID() : 0;
Children = new BeforeGameObjectTree[gameObject.transform.childCount];

var components = gameObject.GetComponents<Component>();
ComponentInstanceIds = components.Select(x => x.GetInstanceID()).ToArray();

var componentByType = new Dictionary<Type, int>();
foreach (var component in components)
if (!componentByType.ContainsKey(component.GetType()))
componentByType.Add(component.GetType(), component.GetInstanceID());
ComponentInstanceIdByType = componentByType;
}
}

class ComponentInfo
{
public readonly int InstanceId;
public readonly int MergedInto;
public readonly Type Type;
public readonly IReadOnlyDictionary<string, string> PropertyMapping;

public ComponentInfo(int instanceId, int mergedInto, Type type, IReadOnlyDictionary<string, string> propertyMapping)
{
InstanceId = instanceId;
MergedInto = mergedInto;
Type = type;
PropertyMapping = propertyMapping;
}
}

static class VProp
{
private const string ExtraProps = "AvatarOptimizerExtraProps";
public static string BlendShapeIndex(int index) => $"{ExtraProps}.BlendShapeIndex.{index}";

public static int ParseBlendShapeIndex(string prop)
{
if (!prop.StartsWith($"{ExtraProps}.BlendShapeIndex.", StringComparison.Ordinal))
throw new ArgumentException($"The property {prop} is not BlendShapeIndex", nameof(prop));
var indexStr = prop.Substring($"{ExtraProps}.BlendShapeIndex.".Length);
if (!int.TryParse(indexStr, out var index))
throw new ArgumentException($"The property {prop} is not BlendShapeIndex", nameof(prop));
return index;
}
}
}
File renamed without changes.
Loading

0 comments on commit 1430e6d

Please sign in to comment.