Skip to content

Commit

Permalink
fix: MeshRenderers have incorrect scale (#311)
Browse files Browse the repository at this point in the history
Closes: #309
  • Loading branch information
bdunderscore authored Aug 4, 2024
1 parent 1251a75 commit 88d1f7f
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- [#298] Fixed issue where the scene view was sometimes not refreshed when the pipeline build completes
- [#309] NullReferenceException from GetParametersForObject when encountering a missing component
- [#311] Fix issue where MeshRenderers are shown with incorrect scale

### Changed

Expand Down
21 changes: 16 additions & 5 deletions Editor/PreviewSystem/Rendering/ProxyObjectController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ internal bool OnPreFrame()
return false;
}

var target = _replacementRenderer;
var original = _originalRenderer;

SkinnedMeshRenderer smr = null;
if (_originalRenderer is SkinnedMeshRenderer smr_)
{
Expand All @@ -127,27 +130,35 @@ internal bool OnPreFrame()
var replacementSMR = (SkinnedMeshRenderer)_replacementRenderer;
replacementSMR.sharedMesh = smr_.sharedMesh;
replacementSMR.bones = smr_.bones;

target.transform.position = original.transform.position;
target.transform.rotation = original.transform.rotation;
}
else
{
var originalFilter = _originalRenderer.GetComponent<MeshFilter>();
var filter = _replacementRenderer.GetComponent<MeshFilter>();
filter.sharedMesh = originalFilter.sharedMesh;

var shadowBone = ShadowBoneManager.Instance.GetBone(_originalRenderer.transform).proxy;

var rendererTransform = _replacementRenderer.transform;
if (shadowBone != rendererTransform.parent)
{
rendererTransform.SetParent(shadowBone, false);
rendererTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
rendererTransform.localScale = Vector3.one;
}
}

_replacementRenderer.sharedMaterials = _originalRenderer.sharedMaterials;

var target = _replacementRenderer;
var original = _originalRenderer;

if (target.gameObject.scene != original.gameObject.scene &&
original.gameObject.scene.IsValid())
{
SceneManager.MoveGameObjectToScene(target.gameObject, original.gameObject.scene);
}

target.transform.position = original.transform.position;
target.transform.rotation = original.transform.rotation;

target.localBounds = original.localBounds;
if (target is SkinnedMeshRenderer targetSMR && original is SkinnedMeshRenderer originalSMR)
Expand Down
2 changes: 2 additions & 0 deletions Editor/PreviewSystem/Rendering/ProxySession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ public void Dispose()

public IEnumerable<(Renderer, Renderer)> OnPreCull()
{
ShadowBoneManager.Instance.Update();

bool activeIsReady = _active?.IsReady == true;
bool activeNeedsReplacement = _active?.IsInvalidated != false;

Expand Down
162 changes: 162 additions & 0 deletions Editor/PreviewSystem/Rendering/ShadowBoneManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace nadena.dev.ndmf.preview
{
// This is basically the same as ScaleAdjustedBones in ModularAvatar with slight changes, but I'm not ready to
// create a public API just yet, so copied here...
internal class ShadowBoneManager
{
internal static ShadowBoneManager Instance { get; } = new();

private static int editorFrameCount = 0;
private static int lastUpdateFrame = 0;
private static int lastMutatingUpdate = 0;
private static int mutatingUpdateCount = 0;

[InitializeOnLoadMethod]
static void Init()
{
EditorApplication.update += () => editorFrameCount++;
}

internal class BoneState
{
public Transform original;
public Transform proxy;
public int lastUsedFrame;
public BoneState parentHint;
}

private Dictionary<Component, BoneState> _bones = new(new ObjectIdentityComparer<Component>());
//private List<BoneState> _states = new List<BoneState>();

public void Clear()
{
foreach (var state in _bones.Values)
{
if (state.proxy != null) Object.DestroyImmediate(state.proxy.gameObject);
}

_bones.Clear();
}

public BoneState GetBone(Transform src, bool force = true)
{
if (src == null) return null;

if (_bones.TryGetValue(src, out var state))
{
state.lastUsedFrame = mutatingUpdateCount;
return state;
}

if (!force) return null;

var proxyObj = new GameObject(src.name);
proxyObj.hideFlags = HideFlags.HideAndDontSave;
proxyObj.AddComponent<SelfDestructComponent>().KeepAlive = this;

#if MODULAR_AVATAR_DEBUG_HIDDEN
proxyObj.hideFlags = HideFlags.DontSave;
#endif

var boneState = new BoneState();
boneState.original = src;
boneState.proxy = proxyObj.transform;
boneState.parentHint = null;
boneState.lastUsedFrame = Time.frameCount;

_bones[src] = boneState;

CheckParent(CopyState(boneState), boneState);

return boneState;
}

private List<Component> toRemove = new List<Component>();
private List<BoneState> stateList = new List<BoneState>();

public void Update()
{
if (lastUpdateFrame == editorFrameCount)
{
return;
}

lastUpdateFrame = editorFrameCount;

if (lastMutatingUpdate != editorFrameCount)
{
mutatingUpdateCount++;
lastMutatingUpdate = editorFrameCount;
}

toRemove.Clear();

stateList.Clear();
stateList.AddRange(_bones.Values);

foreach (var entry in stateList)
{
if (entry.original == null || entry.proxy == null)
{
if (entry.proxy != null)
{
Object.DestroyImmediate(entry.proxy.gameObject);
}

toRemove.Add(entry.original);
continue;
}

if (mutatingUpdateCount - entry.lastUsedFrame > 5 && entry.proxy.childCount == 0)
{
Object.DestroyImmediate(entry.proxy.gameObject);
toRemove.Add(entry.original);
continue;
}

if (entry.original.gameObject.scene != entry.proxy.gameObject.scene &&
entry.proxy.transform.parent == null)
{
SceneManager.MoveGameObjectToScene(entry.proxy.gameObject, entry.original.gameObject.scene);
}

Transform parent = CopyState(entry);

CheckParent(parent, entry);
}

foreach (var remove in toRemove)
{
_bones.Remove(remove);
}
}

private void CheckParent(Transform parent, BoneState entry)
{
if (parent != entry.parentHint?.original)
{
entry.parentHint = GetBone(parent);
entry.proxy.SetParent(entry.parentHint?.proxy, false);
}
}

private static Transform CopyState(BoneState entry)
{
Transform parent;
var t = entry.original;

parent = t.parent;

entry.proxy.localPosition = t.localPosition;
entry.proxy.localRotation = t.localRotation;
entry.proxy.localScale = t.localScale;

return parent;
}
}
}
3 changes: 3 additions & 0 deletions Editor/PreviewSystem/Rendering/ShadowBoneManager.cs.meta

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

0 comments on commit 88d1f7f

Please sign in to comment.