mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 14:29:25 +00:00
Move many mods to Deprecated folder, fix spelling
This commit is contained in:
parent
5e822cec8d
commit
0042590aa6
539 changed files with 7475 additions and 3120 deletions
|
@ -1,68 +0,0 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Public API Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the specific renderer requires additional runtime checks when copying to the clone.
|
||||
/// For example, Magica Cloth modifies the sharedMesh & bones of the renderer at runtime. This is not needed
|
||||
/// for most renderers, so copying for all renderers would be inefficient.
|
||||
/// </summary>
|
||||
public void SetRendererNeedsAdditionalChecks(Renderer rend, bool needsChecks)
|
||||
{
|
||||
switch (rend)
|
||||
{
|
||||
case MeshRenderer meshRenderer:
|
||||
{
|
||||
int index = _standardRenderers.IndexOf(meshRenderer);
|
||||
if (index == -1) return;
|
||||
|
||||
if (needsChecks && !_standardRenderersNeedingChecks.Contains(index))
|
||||
{
|
||||
int insertIndex = _standardRenderersNeedingChecks.Count;
|
||||
_standardRenderersNeedingChecks.Add(index);
|
||||
_cachedSharedMeshes.Insert(insertIndex, null);
|
||||
}
|
||||
else if (!needsChecks)
|
||||
{
|
||||
int removeIndex = _standardRenderersNeedingChecks.IndexOf(index);
|
||||
if (removeIndex != -1)
|
||||
{
|
||||
_standardRenderersNeedingChecks.RemoveAt(removeIndex);
|
||||
_cachedSharedMeshes.RemoveAt(removeIndex);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
case SkinnedMeshRenderer skinnedRenderer:
|
||||
{
|
||||
int index = _skinnedRenderers.IndexOf(skinnedRenderer);
|
||||
if (index == -1) return;
|
||||
|
||||
if (needsChecks && !_skinnedRenderersNeedingChecks.Contains(index))
|
||||
{
|
||||
int insertIndex = _skinnedRenderersNeedingChecks.Count;
|
||||
_skinnedRenderersNeedingChecks.Add(index);
|
||||
_cachedSharedMeshes.Insert(_standardRenderersNeedingChecks.Count + insertIndex, null);
|
||||
_cachedSkinnedBoneCounts.Add(0);
|
||||
}
|
||||
else if (!needsChecks)
|
||||
{
|
||||
int removeIndex = _skinnedRenderersNeedingChecks.IndexOf(index);
|
||||
if (removeIndex != -1)
|
||||
{
|
||||
_skinnedRenderersNeedingChecks.RemoveAt(removeIndex);
|
||||
_cachedSharedMeshes.RemoveAt(_standardRenderersNeedingChecks.Count + removeIndex);
|
||||
_cachedSkinnedBoneCounts.RemoveAt(removeIndex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Public API Methods
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
using ABI_RC.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Clone Creation
|
||||
|
||||
private void CreateClones()
|
||||
{
|
||||
int standardCount = _standardRenderers.Count;
|
||||
_standardClones = new List<MeshRenderer>(standardCount);
|
||||
_standardCloneFilters = new List<MeshFilter>(standardCount);
|
||||
for (int i = 0; i < standardCount; i++) CreateStandardClone(i);
|
||||
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
_skinnedClones = new List<SkinnedMeshRenderer>(skinnedCount);
|
||||
for (int i = 0; i < skinnedCount; i++) CreateSkinnedClone(i);
|
||||
}
|
||||
|
||||
private void CreateStandardClone(int index)
|
||||
{
|
||||
MeshRenderer sourceRenderer = _standardRenderers[index];
|
||||
MeshFilter sourceFilter = _standardFilters[index];
|
||||
|
||||
GameObject go = new(sourceRenderer.name + "_VisualClone")
|
||||
{
|
||||
layer = CVRLayers.PlayerClone
|
||||
};
|
||||
|
||||
go.transform.SetParent(sourceRenderer.transform, false);
|
||||
//go.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
|
||||
MeshRenderer cloneRenderer = go.AddComponent<MeshRenderer>();
|
||||
MeshFilter cloneFilter = go.AddComponent<MeshFilter>();
|
||||
|
||||
// Initial setup
|
||||
cloneRenderer.sharedMaterials = sourceRenderer.sharedMaterials;
|
||||
cloneRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
cloneRenderer.probeAnchor = sourceRenderer.probeAnchor;
|
||||
cloneRenderer.localBounds = new Bounds(Vector3.zero, Vector3.positiveInfinity);
|
||||
cloneFilter.sharedMesh = sourceFilter.sharedMesh;
|
||||
|
||||
// Optimizations to enforce
|
||||
cloneRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
cloneRenderer.allowOcclusionWhenDynamic = false;
|
||||
|
||||
// Optimizations to enforce
|
||||
sourceRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
sourceRenderer.allowOcclusionWhenDynamic = false;
|
||||
|
||||
_standardClones.Add(cloneRenderer);
|
||||
_standardCloneFilters.Add(cloneFilter);
|
||||
}
|
||||
|
||||
private void CreateSkinnedClone(int index)
|
||||
{
|
||||
SkinnedMeshRenderer source = _skinnedRenderers[index];
|
||||
|
||||
GameObject go = new(source.name + "_VisualClone")
|
||||
{
|
||||
layer = CVRLayers.PlayerClone
|
||||
};
|
||||
|
||||
go.transform.SetParent(source.transform, false);
|
||||
//go.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
|
||||
SkinnedMeshRenderer clone = go.AddComponent<SkinnedMeshRenderer>();
|
||||
|
||||
// Initial setup
|
||||
clone.sharedMaterials = source.sharedMaterials;
|
||||
clone.shadowCastingMode = ShadowCastingMode.Off;
|
||||
clone.probeAnchor = source.probeAnchor;
|
||||
clone.localBounds = new Bounds(Vector3.zero, Vector3.positiveInfinity);
|
||||
clone.sharedMesh = source.sharedMesh;
|
||||
clone.rootBone = source.rootBone;
|
||||
clone.bones = source.bones;
|
||||
clone.quality = source.quality;
|
||||
clone.updateWhenOffscreen = source.updateWhenOffscreen;
|
||||
|
||||
// Optimizations to enforce
|
||||
clone.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
clone.allowOcclusionWhenDynamic = false;
|
||||
clone.updateWhenOffscreen = false;
|
||||
clone.skinnedMotionVectors = false;
|
||||
clone.forceMatrixRecalculationPerRender = false;
|
||||
clone.quality = SkinQuality.Bone4;
|
||||
|
||||
// Optimizations to enforce
|
||||
source.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
source.allowOcclusionWhenDynamic = false;
|
||||
source.updateWhenOffscreen = false;
|
||||
source.skinnedMotionVectors = false;
|
||||
source.forceMatrixRecalculationPerRender = false;
|
||||
source.quality = SkinQuality.Bone4;
|
||||
|
||||
_skinnedClones.Add(clone);
|
||||
}
|
||||
|
||||
#endregion Clone Creation
|
||||
}
|
|
@ -1,141 +0,0 @@
|
|||
using ABI.CCK.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
private readonly Dictionary<Transform, HashSet<Renderer>> _exclusionDirectRenderers = new();
|
||||
private readonly Dictionary<Transform, HashSet<Transform>> _exclusionControlledBones = new();
|
||||
|
||||
private void InitializeExclusions()
|
||||
{
|
||||
// Add head exclusion for humanoid avatars if not present
|
||||
var animator = GetComponent<Animator>();
|
||||
if (animator != null && animator.isHuman)
|
||||
{
|
||||
var headBone = animator.GetBoneTransform(HumanBodyBones.Head);
|
||||
if (headBone != null && headBone.GetComponent<FPRExclusion>() == null)
|
||||
{
|
||||
var exclusion = headBone.gameObject.AddComponent<FPRExclusion>();
|
||||
exclusion.isShown = false;
|
||||
exclusion.target = headBone;
|
||||
exclusion.shrinkToZero = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Process existing exclusions bottom-up
|
||||
var exclusions = GetComponentsInChildren<FPRExclusion>(true);
|
||||
|
||||
for (int i = exclusions.Length - 1; i >= 0; i--)
|
||||
{
|
||||
var exclusion = exclusions[i];
|
||||
if (exclusion.target == null)
|
||||
exclusion.target = exclusion.transform;
|
||||
|
||||
// Skip invalid exclusions or already processed targets
|
||||
if (exclusion.target == null || _exclusionDirectRenderers.ContainsKey(exclusion.target))
|
||||
{
|
||||
Destroy(exclusion);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialize data for this exclusion
|
||||
_exclusionDirectRenderers[exclusion.target] = new HashSet<Renderer>();
|
||||
_exclusionControlledBones[exclusion.target] = new HashSet<Transform>();
|
||||
|
||||
// Set up our behaviour
|
||||
exclusion.behaviour = new AvatarCloneExclusion(this, exclusion.target);
|
||||
|
||||
// Collect affected renderers and bones
|
||||
CollectExclusionData(exclusion.target);
|
||||
|
||||
// Initial update
|
||||
exclusion.UpdateExclusions();
|
||||
}
|
||||
}
|
||||
|
||||
private void CollectExclusionData(Transform target)
|
||||
{
|
||||
var stack = new Stack<Transform>();
|
||||
stack.Push(target);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var current = stack.Pop();
|
||||
|
||||
// Skip if this transform belongs to another exclusion
|
||||
if (current != target && current.GetComponent<FPRExclusion>() != null)
|
||||
continue;
|
||||
|
||||
_exclusionControlledBones[target].Add(current);
|
||||
|
||||
// Add renderers that will need their clone visibility toggled
|
||||
foreach (var renderer in current.GetComponents<Renderer>())
|
||||
{
|
||||
// Find corresponding clone renderer
|
||||
if (renderer is MeshRenderer meshRenderer)
|
||||
{
|
||||
int index = _standardRenderers.IndexOf(meshRenderer);
|
||||
if (index != -1)
|
||||
_exclusionDirectRenderers[target].Add(_standardClones[index]);
|
||||
}
|
||||
else if (renderer is SkinnedMeshRenderer skinnedRenderer)
|
||||
{
|
||||
int index = _skinnedRenderers.IndexOf(skinnedRenderer);
|
||||
if (index != -1)
|
||||
_exclusionDirectRenderers[target].Add(_skinnedClones[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add children to stack
|
||||
foreach (Transform child in current)
|
||||
{
|
||||
stack.Push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleExclusionUpdate(Transform target, Transform shrinkBone, bool isShown)
|
||||
{
|
||||
if (!_exclusionDirectRenderers.TryGetValue(target, out var directCloneRenderers) ||
|
||||
!_exclusionControlledBones.TryGetValue(target, out var controlledBones))
|
||||
return;
|
||||
|
||||
// Handle direct clone renderers
|
||||
foreach (var cloneRenderer in directCloneRenderers)
|
||||
{
|
||||
cloneRenderer.enabled = isShown;
|
||||
}
|
||||
|
||||
// Update bone references in clone renderers
|
||||
int cloneCount = _skinnedClones.Count;
|
||||
var cloneRenderers = _skinnedClones;
|
||||
var sourceRenderers = _skinnedRenderers;
|
||||
|
||||
for (int i = 0; i < cloneCount; i++)
|
||||
{
|
||||
var clone = cloneRenderers[i];
|
||||
var source = sourceRenderers[i];
|
||||
var sourceBones = source.bones;
|
||||
var cloneBones = clone.bones;
|
||||
int boneCount = cloneBones.Length;
|
||||
bool needsUpdate = false;
|
||||
|
||||
for (int j = 0; j < boneCount; j++)
|
||||
{
|
||||
// Check if this bone is in our controlled set
|
||||
if (controlledBones.Contains(sourceBones[j]))
|
||||
{
|
||||
cloneBones[j] = isShown ? sourceBones[j] : shrinkBone;
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsUpdate)
|
||||
{
|
||||
clone.bones = cloneBones;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
229
AvatarCloneTest/AvatarClone/AvatarClone.Exclusions.cs
Normal file
229
AvatarCloneTest/AvatarClone/AvatarClone.Exclusions.cs
Normal file
|
@ -0,0 +1,229 @@
|
|||
using ABI.CCK.Components;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Exclusions
|
||||
|
||||
private FPRExclusion[] _exclusions;
|
||||
|
||||
private void AddExclusionToHeadIfNeeded()
|
||||
{
|
||||
if (!TryGetComponent(out Animator animator)
|
||||
|| !animator.isHuman
|
||||
|| !animator.avatar
|
||||
|| !animator.avatar.isValid)
|
||||
return;
|
||||
|
||||
Transform head = animator.GetBoneTransform(HumanBodyBones.Head);
|
||||
if (!head)
|
||||
return;
|
||||
|
||||
GameObject headGo = head.gameObject;
|
||||
if (headGo.TryGetComponent(out FPRExclusion exclusion))
|
||||
return;
|
||||
|
||||
exclusion = headGo.AddComponent<FPRExclusion>();
|
||||
exclusion.target = head;
|
||||
exclusion.isShown = false;
|
||||
}
|
||||
|
||||
private void InitializeExclusions()
|
||||
{
|
||||
_exclusions = GetComponentsInChildren<FPRExclusion>(true);
|
||||
var exclusionRoots = new Dictionary<Transform, AvatarCloneExclusion>(_exclusions.Length);
|
||||
|
||||
// **1. Precompute Exclusions**
|
||||
foreach (FPRExclusion exclusion in _exclusions)
|
||||
{
|
||||
Transform target = exclusion.target ??= exclusion.transform;
|
||||
if (exclusionRoots.ContainsKey(target) || !target.gameObject.scene.IsValid())
|
||||
continue;
|
||||
|
||||
AvatarCloneExclusion behaviour = new AvatarCloneExclusion(this, target);
|
||||
exclusion.behaviour = behaviour;
|
||||
exclusionRoots.Add(target, behaviour);
|
||||
}
|
||||
|
||||
// Process Exclusion Transforms
|
||||
Renderer ourRenderer;
|
||||
|
||||
void ProcessTransformHierarchy(Transform current, Transform root, AvatarCloneExclusion behaviour)
|
||||
{
|
||||
if (exclusionRoots.ContainsKey(current) && current != root) return;
|
||||
|
||||
behaviour.affectedTransforms.Add(current);
|
||||
if (current.TryGetComponent(out ourRenderer))
|
||||
behaviour.affectedRenderers.Add(ourRenderer);
|
||||
|
||||
for (int i = 0; i < current.childCount; i++)
|
||||
{
|
||||
Transform child = current.GetChild(i);
|
||||
if (!exclusionRoots.ContainsKey(child))
|
||||
ProcessTransformHierarchy(child, root, behaviour);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entry in exclusionRoots)
|
||||
{
|
||||
Transform rootTransform = entry.Key;
|
||||
AvatarCloneExclusion behaviour = entry.Value;
|
||||
ProcessTransformHierarchy(rootTransform, rootTransform, behaviour);
|
||||
behaviour.affectedTransformSet = new HashSet<Transform>(behaviour.affectedTransforms);
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// **OPTIMIZED EXCLUSION BONE MAPPING**
|
||||
// ------------------------------
|
||||
|
||||
Dictionary<Transform, AvatarCloneExclusion>.ValueCollection exclusionBehaviours = exclusionRoots.Values;
|
||||
int skinnedCount = _skinnedClones.Count;
|
||||
|
||||
// **2. Precompute Bone-to-Exclusion Mapping**
|
||||
int estimatedBoneCount = skinnedCount * 20; // Estimated bones per skinned mesh
|
||||
var boneToExclusion = new Dictionary<Transform, List<AvatarCloneExclusion>>(estimatedBoneCount);
|
||||
|
||||
foreach (AvatarCloneExclusion behaviour in exclusionBehaviours)
|
||||
{
|
||||
foreach (Transform bone in behaviour.affectedTransformSet)
|
||||
{
|
||||
if (!boneToExclusion.TryGetValue(bone, out var list))
|
||||
{
|
||||
list = new List<AvatarCloneExclusion>(2);
|
||||
boneToExclusion[bone] = list;
|
||||
}
|
||||
list.Add(behaviour);
|
||||
}
|
||||
}
|
||||
|
||||
// **3. Process Skinned Mesh Renderers**
|
||||
for (int s = 0; s < skinnedCount; s++)
|
||||
{
|
||||
SkinnedMeshRenderer source = _skinnedRenderers[s];
|
||||
var bones = source.bones; // Cache bones array
|
||||
|
||||
SkinnedMeshRenderer smr = _skinnedClones[s];
|
||||
int boneCount = bones.Length;
|
||||
|
||||
for (int i = 0; i < boneCount; i++)
|
||||
{
|
||||
Transform bone = bones[i];
|
||||
|
||||
// **Skip if the bone isn't mapped to exclusions**
|
||||
if (!bone // Skip null bones
|
||||
|| !boneToExclusion.TryGetValue(bone, out var behaviours))
|
||||
continue;
|
||||
|
||||
// **Avoid redundant dictionary lookups**
|
||||
for (int j = 0; j < behaviours.Count; j++)
|
||||
{
|
||||
AvatarCloneExclusion behaviour = behaviours[j];
|
||||
|
||||
if (!behaviour.skinnedToBoneIndex.TryGetValue(smr, out var indices))
|
||||
{
|
||||
indices = new List<int>(4);
|
||||
behaviour.skinnedToBoneIndex[smr] = indices;
|
||||
}
|
||||
|
||||
indices.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyInitialExclusionState();
|
||||
}
|
||||
|
||||
public void ApplyInitialExclusionState()
|
||||
{
|
||||
foreach (FPRExclusion exclusion in _exclusions)
|
||||
{
|
||||
exclusion._wasShown = exclusion.isShown;
|
||||
if (!exclusion.isShown) exclusion.UpdateExclusions();
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleExclusionUpdate(AvatarCloneExclusion exclusion, bool isShown)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_UpdateExclusions.Begin();
|
||||
#endif
|
||||
|
||||
// **1. Update Renderer Visibility**
|
||||
foreach (Renderer renderer in exclusion.affectedRenderers)
|
||||
{
|
||||
if (renderer is SkinnedMeshRenderer skinned)
|
||||
{
|
||||
int index = _skinnedRenderers.IndexOf(skinned);
|
||||
if (index >= 0) _skinnedClones[index].gameObject.SetActive(isShown);
|
||||
}
|
||||
else if (renderer is MeshRenderer mesh)
|
||||
{
|
||||
int index = _meshRenderers.IndexOf(mesh);
|
||||
if (index >= 0)
|
||||
{
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
_meshClones[index].gameObject.SetActive(isShown);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other renderer (never cloned) - update shadow casting state
|
||||
_sourceShouldBeHiddenFromFPR[index] = !isShown; // When hidden, use for shadows
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (renderer)
|
||||
{
|
||||
int index = _otherRenderers.IndexOf(renderer);
|
||||
if (index >= 0)
|
||||
{
|
||||
int shadowIndex = index + (Setting_CloneMeshRenderers ? _meshRenderers.Count : 0);
|
||||
_sourceShouldBeHiddenFromFPR[shadowIndex] = !isShown; // When hidden, use for shadows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **2. Update Bone References in Skinned Mesh Renderers**
|
||||
UpdateSkinnedMeshBones(exclusion, exclusion._shrinkBone, isShown);
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_UpdateExclusions.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void UpdateSkinnedMeshBones(AvatarCloneExclusion exclusion, Transform shrinkBone, bool isShown)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_HandleBoneUpdates.Begin();
|
||||
#endif
|
||||
|
||||
foreach (var smrEntry in exclusion.skinnedToBoneIndex)
|
||||
{
|
||||
SkinnedMeshRenderer smr = smrEntry.Key;
|
||||
var indices = smrEntry.Value;
|
||||
bool needsUpdate = false;
|
||||
|
||||
var parentBones = smr.transform.parent.GetComponent<SkinnedMeshRenderer>().bones;
|
||||
var cloneBones = smr.bones;
|
||||
Array.Resize(ref cloneBones, parentBones.Length);
|
||||
|
||||
// Only modify our bones, other exclusions may have modified others
|
||||
for (int i = 0; i < indices.Count; i++)
|
||||
{
|
||||
int index = indices[i];
|
||||
if (!isShown) cloneBones[index] = shrinkBone;
|
||||
else cloneBones[index] = parentBones[index];
|
||||
needsUpdate = true;
|
||||
}
|
||||
if (needsUpdate) smr.bones = cloneBones;
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_HandleBoneUpdates.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion Exclusions
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Profile Markers
|
||||
//#if UNITY_EDITOR
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_CopyMaterials =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.CopyMaterials");
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_CopyBlendShapes =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.CopyBlendShapes");
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_CopyMeshes =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.CopyMeshes");
|
||||
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_MyOnPreRender =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.MyOnPreRender");
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_SetShadowsOnly =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.SetShadowsOnly");
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_UndoShadowsOnly =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.UndoShadowsOnly");
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_SetUiCulling =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.SetUiCulling");
|
||||
private static readonly UnityEngine.Profiling.CustomSampler s_UndoUiCulling =
|
||||
UnityEngine.Profiling.CustomSampler.Create("AvatarClone2.UndoUiCulling");
|
||||
|
||||
//#endif
|
||||
#endregion Profile Markers
|
||||
|
||||
#region Source Renderers
|
||||
private List<MeshRenderer> _standardRenderers;
|
||||
private List<MeshFilter> _standardFilters;
|
||||
private List<SkinnedMeshRenderer> _skinnedRenderers;
|
||||
private List<Renderer> _allSourceRenderers; // For shadow casting only
|
||||
#endregion Source Renderers
|
||||
|
||||
#region Clone Renderers
|
||||
private List<MeshRenderer> _standardClones;
|
||||
private List<MeshFilter> _standardCloneFilters;
|
||||
private List<SkinnedMeshRenderer> _skinnedClones;
|
||||
#endregion Clone Renderers
|
||||
|
||||
#region Dynamic Check Lists
|
||||
private List<int> _standardRenderersNeedingChecks; // Stores indices into _standardRenderers
|
||||
private List<int> _skinnedRenderersNeedingChecks; // Stores indices into _skinnedRenderers
|
||||
private List<int> _cachedSkinnedBoneCounts; // So we don't copy the bones unless they've changed
|
||||
private List<Mesh> _cachedSharedMeshes; // So we don't copy the mesh unless it's changed
|
||||
#endregion Dynamic Check Lists
|
||||
|
||||
#region Material Data
|
||||
private List<Material[]> _localMaterials;
|
||||
private List<Material[]> _cullingMaterials;
|
||||
private List<Material> _mainMaterials;
|
||||
private MaterialPropertyBlock _propertyBlock;
|
||||
#endregion Material Data
|
||||
|
||||
#region Blend Shape Data
|
||||
private List<List<float>> _blendShapeWeights;
|
||||
#endregion Blend Shape Data
|
||||
|
||||
#region Shadow and UI Culling Settings
|
||||
private bool _uiCullingActive;
|
||||
private bool _shadowsOnlyActive;
|
||||
private bool[] _originallyHadShadows;
|
||||
private bool[] _originallyWasEnabled;
|
||||
#endregion Shadow and UI Culling Settings
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
using ABI_RC.Core.Player.ShadowClone;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
|
@ -9,120 +10,295 @@ public partial class AvatarClone
|
|||
|
||||
private void InitializeCollections()
|
||||
{
|
||||
_standardRenderers = new List<MeshRenderer>();
|
||||
_standardFilters = new List<MeshFilter>();
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.Begin();
|
||||
#endif
|
||||
|
||||
// Initialize source collections
|
||||
_skinnedRenderers = new List<SkinnedMeshRenderer>();
|
||||
_allSourceRenderers = new List<Renderer>();
|
||||
_blendShapeWeights = new List<List<float>>();
|
||||
|
||||
_standardClones = new List<MeshRenderer>();
|
||||
_standardCloneFilters = new List<MeshFilter>();
|
||||
_meshRenderers = new List<MeshRenderer>();
|
||||
_meshFilters = new List<MeshFilter>();
|
||||
|
||||
_otherRenderers = new List<Renderer>();
|
||||
|
||||
// Initialize clone collections
|
||||
_skinnedClones = new List<SkinnedMeshRenderer>();
|
||||
_skinnedCloneMaterials = new List<Material[]>();
|
||||
_skinnedCloneCullingMaterials = new List<Material[]>();
|
||||
|
||||
_standardRenderersNeedingChecks = new List<int>();
|
||||
_skinnedRenderersNeedingChecks = new List<int>();
|
||||
_cachedSkinnedBoneCounts = new List<int>();
|
||||
_cachedSharedMeshes = new List<Mesh>();
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
_meshClones = new List<MeshRenderer>();
|
||||
_meshCloneFilters = new List<MeshFilter>();
|
||||
_meshCloneMaterials = new List<Material[]>();
|
||||
_meshCloneCullingMaterials = new List<Material[]>();
|
||||
}
|
||||
|
||||
_localMaterials = new List<Material[]>();
|
||||
_cullingMaterials = new List<Material[]>();
|
||||
_mainMaterials = new List<Material>();
|
||||
// Initialize shared resources
|
||||
_materialWorkingList = new List<Material>();
|
||||
_propertyBlock = new MaterialPropertyBlock();
|
||||
|
||||
_blendShapeWeights = new List<List<float>>();
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeRenderers()
|
||||
private void CollectRenderers()
|
||||
{
|
||||
var renderers = GetComponentsInChildren<Renderer>(true);
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.Begin();
|
||||
#endif
|
||||
|
||||
// Pre-size lists based on found renderers
|
||||
// _standardRenderers.Capacity = renderers.Length;
|
||||
// _standardFilters.Capacity = renderers.Length;
|
||||
// _skinnedRenderers.Capacity = renderers.Length;
|
||||
// _allSourceRenderers.Capacity = renderers.Length;
|
||||
|
||||
// Sort renderers into their respective lists
|
||||
foreach (Renderer render in renderers)
|
||||
var renderers = GetComponentsInChildren<Renderer>(true);
|
||||
var currentIndex = 0;
|
||||
var nonCloned = 0;
|
||||
|
||||
// Single pass: directly categorize renderers
|
||||
foreach (Renderer renderer in renderers)
|
||||
{
|
||||
_allSourceRenderers.Add(render);
|
||||
|
||||
switch (render)
|
||||
switch (renderer)
|
||||
{
|
||||
case MeshRenderer meshRenderer:
|
||||
{
|
||||
MeshFilter filter = meshRenderer.GetComponent<MeshFilter>();
|
||||
case SkinnedMeshRenderer skinned when skinned.sharedMesh != null:
|
||||
AddSkinnedRenderer(skinned);
|
||||
currentIndex++;
|
||||
break;
|
||||
|
||||
case MeshRenderer mesh:
|
||||
MeshFilter filter = mesh.GetComponent<MeshFilter>();
|
||||
if (filter != null && filter.sharedMesh != null)
|
||||
{
|
||||
_standardRenderers.Add(meshRenderer);
|
||||
_standardFilters.Add(filter);
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
AddMeshRenderer(mesh, filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMeshRenderer(mesh, filter);
|
||||
nonCloned++;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SkinnedMeshRenderer skinnedRenderer:
|
||||
{
|
||||
if (skinnedRenderer.sharedMesh != null) _skinnedRenderers.Add(skinnedRenderer);
|
||||
|
||||
default:
|
||||
AddOtherRenderer(renderer);
|
||||
currentIndex++;
|
||||
nonCloned++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupMaterialsAndBlendShapes()
|
||||
{
|
||||
// Cache counts
|
||||
int standardCount = _standardRenderers.Count;
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
var standardRenderers = _standardRenderers;
|
||||
var skinnedRenderers = _skinnedRenderers;
|
||||
var localMats = _localMaterials;
|
||||
var cullingMats = _cullingMaterials;
|
||||
var blendWeights = _blendShapeWeights;
|
||||
|
||||
// Setup standard renderer materials
|
||||
for (int i = 0; i < standardCount; i++)
|
||||
{
|
||||
MeshRenderer render = standardRenderers[i];
|
||||
int matCount = render.sharedMaterials.Length;
|
||||
|
||||
// Local materials array
|
||||
var localMatArray = new Material[matCount];
|
||||
for (int j = 0; j < matCount; j++) localMatArray[j] = render.sharedMaterials[j];
|
||||
localMats.Add(localMatArray);
|
||||
|
||||
// Culling materials array
|
||||
var cullingMatArray = new Material[matCount];
|
||||
for (int j = 0; j < matCount; j++) cullingMatArray[j] = ShadowCloneUtils.cullingMaterial;
|
||||
cullingMats.Add(cullingMatArray);
|
||||
}
|
||||
|
||||
// Setup skinned renderer materials and blend shapes
|
||||
for (int i = 0; i < skinnedCount; i++)
|
||||
{
|
||||
SkinnedMeshRenderer render = skinnedRenderers[i];
|
||||
int matCount = render.sharedMaterials.Length;
|
||||
|
||||
// Local materials array
|
||||
var localMatArray = new Material[matCount];
|
||||
for (int j = 0; j < matCount; j++) localMatArray[j] = render.sharedMaterials[j];
|
||||
localMats.Add(localMatArray);
|
||||
|
||||
// Culling materials array
|
||||
var cullingMatArray = new Material[matCount];
|
||||
for (int j = 0; j < matCount; j++) cullingMatArray[j] = ShadowCloneUtils.cullingMaterial;
|
||||
cullingMats.Add(cullingMatArray);
|
||||
|
||||
// Blend shape weights
|
||||
int blendShapeCount = render.sharedMesh.blendShapeCount;
|
||||
var weights = new List<float>(blendShapeCount);
|
||||
for (int j = 0; j < blendShapeCount; j++) weights.Add(0f);
|
||||
blendWeights.Add(weights);
|
||||
}
|
||||
|
||||
// Initialize renderer state arrays
|
||||
int totalRenderers = _allSourceRenderers.Count;
|
||||
_originallyHadShadows = new bool[totalRenderers];
|
||||
_originallyWasEnabled = new bool[totalRenderers];
|
||||
_rendererActiveStates = new bool[currentIndex];
|
||||
_originalShadowCastingMode = new ShadowCastingMode[currentIndex];
|
||||
_sourceShouldBeHiddenFromFPR = new bool[nonCloned];
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void AddSkinnedRenderer(SkinnedMeshRenderer renderer)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_AddRenderer.Begin();
|
||||
#endif
|
||||
|
||||
_skinnedRenderers.Add(renderer);
|
||||
|
||||
// Clone materials array for clone renderer
|
||||
var materials = renderer.sharedMaterials;
|
||||
var cloneMaterials = new Material[materials.Length];
|
||||
for (int i = 0; i < materials.Length; i++) cloneMaterials[i] = materials[i];
|
||||
_skinnedCloneMaterials.Add(cloneMaterials);
|
||||
|
||||
// Cache culling materials
|
||||
var cullingMaterialArray = new Material[materials.Length];
|
||||
#if !UNITY_EDITOR
|
||||
for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = ShadowCloneUtils.cullingMaterial;
|
||||
#else
|
||||
for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = cullingMaterial;
|
||||
#endif
|
||||
_skinnedCloneCullingMaterials.Add(cullingMaterialArray);
|
||||
|
||||
// Cache blend shape weights
|
||||
var weights = new List<float>(renderer.sharedMesh.blendShapeCount);
|
||||
for (int i = 0; i < renderer.sharedMesh.blendShapeCount; i++) weights.Add(0f);
|
||||
_blendShapeWeights.Add(weights);
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_AddRenderer.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void AddMeshRenderer(MeshRenderer renderer, MeshFilter filter)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_AddRenderer.Begin();
|
||||
#endif
|
||||
|
||||
_meshRenderers.Add(renderer);
|
||||
_meshFilters.Add(filter);
|
||||
|
||||
if (!Setting_CloneMeshRenderers) return;
|
||||
|
||||
// Clone materials array for clone renderer
|
||||
var materials = renderer.sharedMaterials;
|
||||
var cloneMaterials = new Material[materials.Length];
|
||||
for (int i = 0; i < materials.Length; i++) cloneMaterials[i] = materials[i];
|
||||
_meshCloneMaterials.Add(cloneMaterials);
|
||||
|
||||
// Cache culling materials
|
||||
var cullingMaterialArray = new Material[materials.Length];
|
||||
#if !UNITY_EDITOR
|
||||
for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = ShadowCloneUtils.cullingMaterial;
|
||||
#else
|
||||
for (int i = 0; i < materials.Length; i++) cullingMaterialArray[i] = cullingMaterial;
|
||||
#endif
|
||||
_meshCloneCullingMaterials.Add(cullingMaterialArray);
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_AddRenderer.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void AddOtherRenderer(Renderer renderer)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_AddRenderer.Begin();
|
||||
#endif
|
||||
_otherRenderers.Add(renderer);
|
||||
#if ENABLE_PROFILER
|
||||
s_AddRenderer.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void CreateClones()
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.Begin();
|
||||
#endif
|
||||
|
||||
// Always create skinned mesh clones
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
for (int i = 0; i < skinnedCount; i++)
|
||||
{
|
||||
CreateSkinnedClone(i);
|
||||
}
|
||||
|
||||
// Optionally create mesh clones
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
int meshCount = _meshRenderers.Count;
|
||||
for (int i = 0; i < meshCount; i++)
|
||||
{
|
||||
CreateMeshClone(i);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void CreateSkinnedClone(int index)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_CreateClone.Begin();
|
||||
#endif
|
||||
|
||||
SkinnedMeshRenderer source = _skinnedRenderers[index];
|
||||
|
||||
GameObject clone = new(source.name + "_Clone")
|
||||
{
|
||||
layer = CLONE_LAYER
|
||||
};
|
||||
|
||||
clone.transform.SetParent(source.transform, false);
|
||||
|
||||
SkinnedMeshRenderer cloneRenderer = clone.AddComponent<SkinnedMeshRenderer>();
|
||||
|
||||
// Basic setup
|
||||
cloneRenderer.sharedMaterials = _skinnedCloneMaterials[index];
|
||||
cloneRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
cloneRenderer.probeAnchor = source.probeAnchor;
|
||||
cloneRenderer.sharedMesh = source.sharedMesh;
|
||||
cloneRenderer.rootBone = source.rootBone;
|
||||
cloneRenderer.bones = source.bones;
|
||||
|
||||
#if !UNITY_EDITOR
|
||||
cloneRenderer.localBounds = new Bounds(source.localBounds.center, source.localBounds.size * 2f);
|
||||
#endif
|
||||
|
||||
// Quality settings
|
||||
cloneRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
cloneRenderer.allowOcclusionWhenDynamic = false;
|
||||
cloneRenderer.updateWhenOffscreen = false;
|
||||
cloneRenderer.skinnedMotionVectors = false;
|
||||
cloneRenderer.forceMatrixRecalculationPerRender = false;
|
||||
cloneRenderer.quality = SkinQuality.Bone4;
|
||||
|
||||
source.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
source.allowOcclusionWhenDynamic = false;
|
||||
source.updateWhenOffscreen = false;
|
||||
source.skinnedMotionVectors = false;
|
||||
source.forceMatrixRecalculationPerRender = false;
|
||||
source.quality = SkinQuality.Bone4;
|
||||
|
||||
// Add to clone list
|
||||
_skinnedClones.Add(cloneRenderer);
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_CreateClone.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void CreateMeshClone(int index)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_CreateClone.Begin();
|
||||
#endif
|
||||
|
||||
MeshRenderer source = _meshRenderers[index];
|
||||
MeshFilter sourceFilter = _meshFilters[index];
|
||||
|
||||
GameObject clone = new(source.name + "_Clone")
|
||||
{
|
||||
layer = CLONE_LAYER
|
||||
};
|
||||
|
||||
clone.transform.SetParent(source.transform, false);
|
||||
|
||||
MeshRenderer cloneRenderer = clone.AddComponent<MeshRenderer>();
|
||||
MeshFilter cloneFilter = clone.AddComponent<MeshFilter>();
|
||||
|
||||
// Basic setup
|
||||
cloneRenderer.sharedMaterials = _meshCloneMaterials[index];
|
||||
cloneRenderer.shadowCastingMode = ShadowCastingMode.Off;
|
||||
cloneRenderer.probeAnchor = source.probeAnchor;
|
||||
|
||||
#if !UNITY_EDITOR
|
||||
cloneRenderer.localBounds = new Bounds(source.localBounds.center, source.localBounds.size * 2f);
|
||||
#endif
|
||||
|
||||
cloneFilter.sharedMesh = sourceFilter.sharedMesh;
|
||||
|
||||
// Quality settings
|
||||
cloneRenderer.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
cloneRenderer.allowOcclusionWhenDynamic = false;
|
||||
|
||||
source.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
|
||||
source.allowOcclusionWhenDynamic = false;
|
||||
|
||||
// Add to clone lists
|
||||
_meshClones.Add(cloneRenderer);
|
||||
_meshCloneFilters.Add(cloneFilter);
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_CreateClone.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion Initialization
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
using MagicaCloth;
|
||||
using MagicaCloth2;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Magica Cloth Support
|
||||
|
||||
private void SetupMagicaClothSupport()
|
||||
{
|
||||
var magicaCloths1 = GetComponentsInChildren<MagicaRenderDeformer>(true);
|
||||
foreach (MagicaRenderDeformer magicaCloth in magicaCloths1)
|
||||
{
|
||||
// Get the renderer on the same object
|
||||
Renderer renderer = magicaCloth.gameObject.GetComponent<Renderer>();
|
||||
SetRendererNeedsAdditionalChecks(renderer, true);
|
||||
}
|
||||
|
||||
var magicaCloths2 = GetComponentsInChildren<MagicaCloth2.MagicaCloth>(true);
|
||||
foreach (MagicaCloth2.MagicaCloth magicaCloth in magicaCloths2)
|
||||
{
|
||||
if (magicaCloth.serializeData.clothType != ClothProcess.ClothType.MeshCloth)
|
||||
continue; // Only matters for cloth physics
|
||||
|
||||
// Set the affected renderers as requiring extra checks
|
||||
var renderers = magicaCloth.serializeData.sourceRenderers;
|
||||
foreach (Renderer renderer in renderers) SetRendererNeedsAdditionalChecks(renderer, true);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Magica Cloth Support
|
||||
}
|
212
AvatarCloneTest/AvatarClone/AvatarClone.RenderState.cs
Normal file
212
AvatarCloneTest/AvatarClone/AvatarClone.RenderState.cs
Normal file
|
@ -0,0 +1,212 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Render State Management
|
||||
|
||||
private void MyOnPreCull(Camera cam)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
// Scene & Preview cameras are not needed
|
||||
if (cam.cameraType != CameraType.Game)
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_PreCullUpdate.Begin();
|
||||
#endif
|
||||
|
||||
bool isOurUiCamera = IsUIInternalCamera(cam);
|
||||
bool rendersOurPlayerLayer = CameraRendersPlayerLocalLayer(cam);
|
||||
bool rendersOurCloneLayer = CameraRendersPlayerCloneLayer(cam);
|
||||
|
||||
bool rendersBothPlayerLayers = rendersOurPlayerLayer && rendersOurCloneLayer;
|
||||
|
||||
// Handle shadow casting when camera renders both layers
|
||||
if (!_sourcesSetForShadowCasting
|
||||
&& rendersBothPlayerLayers)
|
||||
{
|
||||
ConfigureSourceShadowCasting(true);
|
||||
_sourcesSetForShadowCasting = true;
|
||||
}
|
||||
else if (_sourcesSetForShadowCasting && !rendersBothPlayerLayers)
|
||||
{
|
||||
ConfigureSourceShadowCasting(false);
|
||||
_sourcesSetForShadowCasting = false;
|
||||
}
|
||||
|
||||
// Handle UI culling for clone layer
|
||||
if (!_clonesSetForUiCulling
|
||||
&& isOurUiCamera && rendersOurCloneLayer)
|
||||
{
|
||||
ConfigureCloneUICulling(true);
|
||||
_clonesSetForUiCulling = true;
|
||||
}
|
||||
else if (_clonesSetForUiCulling)
|
||||
{
|
||||
ConfigureCloneUICulling(false);
|
||||
_clonesSetForUiCulling = false;
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_PreCullUpdate.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ConfigureSourceShadowCasting(bool setSourcesToShadowCast)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_ConfigureShadowCasting.Begin();
|
||||
#endif
|
||||
|
||||
int currentIndex = 0;
|
||||
int shadowArrayIndex = 0;
|
||||
|
||||
// Handle skinned mesh renderers (always have clones)
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
for (int i = 0; i < skinnedCount; i++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex]) continue;
|
||||
|
||||
SkinnedMeshRenderer source = _skinnedRenderers[i];
|
||||
|
||||
if (setSourcesToShadowCast)
|
||||
{
|
||||
ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode;
|
||||
if (originalMode == ShadowCastingMode.Off)
|
||||
source.forceRenderingOff = true;
|
||||
else
|
||||
source.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
source.shadowCastingMode = _originalShadowCastingMode[currentIndex];
|
||||
source.forceRenderingOff = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle mesh renderers based on clone setting
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
int meshCount = _meshRenderers.Count;
|
||||
for (int i = 0; i < meshCount; i++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex]) continue;
|
||||
|
||||
MeshRenderer source = _meshRenderers[i];
|
||||
|
||||
if (setSourcesToShadowCast)
|
||||
{
|
||||
ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode;
|
||||
if (originalMode == ShadowCastingMode.Off)
|
||||
source.forceRenderingOff = true;
|
||||
else
|
||||
source.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
source.shadowCastingMode = _originalShadowCastingMode[currentIndex];
|
||||
source.forceRenderingOff = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// When not cloned, mesh renderers use the shadow casting array
|
||||
int meshCount = _meshRenderers.Count;
|
||||
for (int i = 0; i < meshCount; i++, shadowArrayIndex++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex]) continue;
|
||||
if (!_sourceShouldBeHiddenFromFPR[shadowArrayIndex]) continue;
|
||||
|
||||
MeshRenderer source = _meshRenderers[i];
|
||||
|
||||
if (setSourcesToShadowCast)
|
||||
{
|
||||
ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode;
|
||||
if (originalMode == ShadowCastingMode.Off)
|
||||
source.forceRenderingOff = true;
|
||||
else
|
||||
source.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
source.shadowCastingMode = _originalShadowCastingMode[currentIndex];
|
||||
source.forceRenderingOff = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle other renderers (never cloned)
|
||||
int otherCount = _otherRenderers.Count;
|
||||
for (int i = 0; i < otherCount; i++, shadowArrayIndex++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex]) continue;
|
||||
if (!_sourceShouldBeHiddenFromFPR[shadowArrayIndex]) continue;
|
||||
|
||||
Renderer source = _otherRenderers[i];
|
||||
|
||||
if (setSourcesToShadowCast)
|
||||
{
|
||||
ShadowCastingMode originalMode = _originalShadowCastingMode[currentIndex] = source.shadowCastingMode;
|
||||
if (originalMode == ShadowCastingMode.Off)
|
||||
source.forceRenderingOff = true;
|
||||
else
|
||||
source.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
}
|
||||
else
|
||||
{
|
||||
source.shadowCastingMode = _originalShadowCastingMode[currentIndex];
|
||||
source.forceRenderingOff = false;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_ConfigureShadowCasting.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ConfigureCloneUICulling(bool enableCulling)
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_ConfigureUICulling.Begin();
|
||||
#endif
|
||||
|
||||
// Set the materials to our culling materials
|
||||
int currentIndex = 0;
|
||||
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
for (int i = 0; i < skinnedCount; i++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex])
|
||||
continue;
|
||||
|
||||
_skinnedClones[i].sharedMaterials = enableCulling ?
|
||||
_skinnedCloneCullingMaterials[i] :
|
||||
_skinnedCloneMaterials[i];
|
||||
}
|
||||
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
int meshCount = _meshRenderers.Count;
|
||||
for (int i = 0; i < meshCount; i++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex])
|
||||
continue;
|
||||
|
||||
_meshClones[i].sharedMaterials = enableCulling ?
|
||||
_meshCloneCullingMaterials[i] :
|
||||
_meshCloneMaterials[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_ConfigureUICulling.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion Render State Management
|
||||
}
|
156
AvatarCloneTest/AvatarClone/AvatarClone.StateSync.cs
Normal file
156
AvatarCloneTest/AvatarClone/AvatarClone.StateSync.cs
Normal file
|
@ -0,0 +1,156 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region State Syncing
|
||||
|
||||
private void SyncEnabledState()
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_CopyEnabledState.Begin();
|
||||
#endif
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
// Update skinned mesh renderers
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
for (int i = 0; i < skinnedCount; i++, currentIndex++)
|
||||
{
|
||||
SkinnedMeshRenderer source = _skinnedRenderers[i];
|
||||
_skinnedClones[i].enabled = _rendererActiveStates[currentIndex] = IsRendererActive(source);
|
||||
}
|
||||
|
||||
// Update mesh renderers
|
||||
int meshCount = _meshRenderers.Count;
|
||||
for (int i = 0; i < meshCount; i++, currentIndex++)
|
||||
{
|
||||
MeshRenderer source = _meshRenderers[i];
|
||||
if (Setting_CloneMeshRenderers) _meshClones[i].enabled = _rendererActiveStates[currentIndex] = IsRendererActive(source);
|
||||
else _rendererActiveStates[currentIndex] = IsRendererActive(source);
|
||||
}
|
||||
|
||||
// Update other renderers
|
||||
int otherCount = _otherRenderers.Count;
|
||||
for (int i = 0; i < otherCount; i++, currentIndex++)
|
||||
{
|
||||
Renderer source = _otherRenderers[i];
|
||||
_rendererActiveStates[currentIndex] = IsRendererActive(source);
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_CopyEnabledState.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void SyncMaterials()
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_CopyMaterials.Begin();
|
||||
#endif
|
||||
int currentIndex = 0;
|
||||
|
||||
// Sync skinned mesh materials
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
for (int i = 0; i < skinnedCount; i++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex])
|
||||
continue;
|
||||
|
||||
CopyMaterialsAndProperties(
|
||||
_skinnedRenderers[i],
|
||||
_skinnedClones[i],
|
||||
_propertyBlock,
|
||||
_materialWorkingList,
|
||||
_skinnedCloneMaterials[i]);
|
||||
}
|
||||
|
||||
// Sync mesh materials if enabled
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
int meshCount = _meshRenderers.Count;
|
||||
for (int i = 0; i < meshCount; i++, currentIndex++)
|
||||
{
|
||||
if (!_rendererActiveStates[currentIndex])
|
||||
continue;
|
||||
|
||||
CopyMaterialsAndProperties(
|
||||
_meshRenderers[i],
|
||||
_meshClones[i],
|
||||
_propertyBlock,
|
||||
_materialWorkingList,
|
||||
_meshCloneMaterials[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_CopyMaterials.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void SyncBlendShapes()
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_CopyBlendShapes.Begin();
|
||||
#endif
|
||||
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
for (int i = 0; i < skinnedCount; i++)
|
||||
{
|
||||
SkinnedMeshRenderer source = _skinnedRenderers[i];
|
||||
if (!_rendererActiveStates[i])
|
||||
continue;
|
||||
|
||||
CopyBlendShapes(
|
||||
source,
|
||||
_skinnedClones[i],
|
||||
_blendShapeWeights[i]);
|
||||
}
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_CopyBlendShapes.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void CopyMaterialsAndProperties(
|
||||
Renderer source,
|
||||
Renderer clone,
|
||||
MaterialPropertyBlock propertyBlock,
|
||||
List<Material> workingList,
|
||||
Material[] cloneMaterials)
|
||||
{
|
||||
source.GetSharedMaterials(workingList);
|
||||
|
||||
int matCount = workingList.Count;
|
||||
bool hasChanged = false;
|
||||
|
||||
for (int i = 0; i < matCount; i++)
|
||||
{
|
||||
if (ReferenceEquals(workingList[i], cloneMaterials[i])) continue;
|
||||
cloneMaterials[i] = workingList[i];
|
||||
hasChanged = true;
|
||||
}
|
||||
if (hasChanged) clone.sharedMaterials = cloneMaterials;
|
||||
|
||||
source.GetPropertyBlock(propertyBlock);
|
||||
clone.SetPropertyBlock(propertyBlock);
|
||||
}
|
||||
|
||||
private static void CopyBlendShapes(
|
||||
SkinnedMeshRenderer source,
|
||||
SkinnedMeshRenderer clone,
|
||||
List<float> weights)
|
||||
{
|
||||
int weightCount = weights.Count;
|
||||
for (int i = 0; i < weightCount; i++)
|
||||
{
|
||||
float weight = source.GetBlendShapeWeight(i);
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
if (weight == weights[i]) continue; // Halves the work
|
||||
clone.SetBlendShapeWeight(i, weights[i] = weight);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion State Syncing
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Update Methods
|
||||
|
||||
private void UpdateStandardRenderers()
|
||||
{
|
||||
int count = _standardRenderers.Count;
|
||||
var sourceRenderers = _standardRenderers;
|
||||
var cloneRenderers = _standardClones;
|
||||
var localMats = _localMaterials;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (!IsRendererValid(sourceRenderers[i])) continue;
|
||||
CopyMaterialsAndProperties(
|
||||
sourceRenderers[i],
|
||||
cloneRenderers[i],
|
||||
_propertyBlock,
|
||||
_mainMaterials,
|
||||
localMats[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSkinnedRenderers()
|
||||
{
|
||||
int standardCount = _standardRenderers.Count;
|
||||
int count = _skinnedRenderers.Count;
|
||||
var sourceRenderers = _skinnedRenderers;
|
||||
var cloneRenderers = _skinnedClones;
|
||||
var localMats = _localMaterials;
|
||||
var blendWeights = _blendShapeWeights;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
SkinnedMeshRenderer source = sourceRenderers[i];
|
||||
if (!IsRendererValid(source)) continue;
|
||||
|
||||
SkinnedMeshRenderer clone = cloneRenderers[i];
|
||||
CopyMaterialsAndProperties(
|
||||
source,
|
||||
clone,
|
||||
_propertyBlock,
|
||||
_mainMaterials,
|
||||
localMats[i + standardCount]);
|
||||
|
||||
CopyBlendShapes(source, clone, blendWeights[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStandardRenderersWithChecks()
|
||||
{
|
||||
s_CopyMeshes.Begin();
|
||||
|
||||
var cloneFilters = _standardCloneFilters;
|
||||
var sourceFilters = _standardFilters;
|
||||
var cachedMeshes = _cachedSharedMeshes;
|
||||
var checkIndices = _standardRenderersNeedingChecks;
|
||||
int checkCount = checkIndices.Count;
|
||||
|
||||
while (cachedMeshes.Count < checkCount) cachedMeshes.Add(null);
|
||||
|
||||
for (int i = 0; i < checkCount; i++)
|
||||
{
|
||||
int rendererIndex = checkIndices[i];
|
||||
Mesh newMesh = sourceFilters[rendererIndex].sharedMesh;
|
||||
if (ReferenceEquals(newMesh, cachedMeshes[i])) continue;
|
||||
cloneFilters[rendererIndex].sharedMesh = newMesh; // expensive & allocates
|
||||
cachedMeshes[i] = newMesh;
|
||||
}
|
||||
|
||||
s_CopyMeshes.End();
|
||||
}
|
||||
|
||||
private void UpdateSkinnedRenderersWithChecks()
|
||||
{
|
||||
s_CopyMeshes.Begin();
|
||||
|
||||
var sourceRenderers = _skinnedRenderers;
|
||||
var cloneRenderers = _skinnedClones;
|
||||
var cachedMeshes = _cachedSharedMeshes;
|
||||
var cachedBoneCounts = _cachedSkinnedBoneCounts;
|
||||
var checkIndices = _skinnedRenderersNeedingChecks;
|
||||
int checkCount = checkIndices.Count;
|
||||
int meshOffset = _standardRenderersNeedingChecks.Count;
|
||||
|
||||
// Ensure cache lists are properly sized
|
||||
while (cachedMeshes.Count < meshOffset + checkCount) cachedMeshes.Add(null);
|
||||
while (cachedBoneCounts.Count < checkCount) cachedBoneCounts.Add(0);
|
||||
|
||||
for (int i = 0; i < checkCount; i++)
|
||||
{
|
||||
int rendererIndex = checkIndices[i];
|
||||
SkinnedMeshRenderer source = sourceRenderers[rendererIndex];
|
||||
SkinnedMeshRenderer clone = cloneRenderers[rendererIndex];
|
||||
|
||||
// Check mesh changes
|
||||
Mesh newMesh = source.sharedMesh; // expensive & allocates
|
||||
if (!ReferenceEquals(newMesh, cachedMeshes[meshOffset + i]))
|
||||
{
|
||||
clone.sharedMesh = newMesh;
|
||||
cachedMeshes[meshOffset + i] = newMesh;
|
||||
}
|
||||
|
||||
// Check bone changes
|
||||
var sourceBones = source.bones;
|
||||
int newBoneCount = sourceBones.Length;
|
||||
int oldBoneCount = cachedBoneCounts[i];
|
||||
if (newBoneCount == oldBoneCount)
|
||||
continue;
|
||||
|
||||
var cloneBones = clone.bones; // expensive & allocates
|
||||
if (newBoneCount > oldBoneCount)
|
||||
{
|
||||
// Resize array and copy only the new bones (Magica Cloth appends bones when enabling Mesh Cloth)
|
||||
Array.Resize(ref cloneBones, newBoneCount);
|
||||
for (int boneIndex = oldBoneCount; boneIndex < newBoneCount; boneIndex++)
|
||||
cloneBones[boneIndex] = sourceBones[boneIndex];
|
||||
clone.bones = cloneBones;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If shrinking, just set the whole array
|
||||
clone.bones = sourceBones;
|
||||
}
|
||||
|
||||
cachedBoneCounts[i] = newBoneCount;
|
||||
}
|
||||
|
||||
s_CopyMeshes.End();
|
||||
}
|
||||
|
||||
private static void CopyMaterialsAndProperties(
|
||||
Renderer source, Renderer clone,
|
||||
MaterialPropertyBlock propertyBlock,
|
||||
List<Material> mainMaterials,
|
||||
Material[] localMaterials)
|
||||
{
|
||||
s_CopyMaterials.Begin();
|
||||
|
||||
source.GetSharedMaterials(mainMaterials);
|
||||
|
||||
int matCount = mainMaterials.Count;
|
||||
bool hasChanged = false;
|
||||
for (var i = 0; i < matCount; i++)
|
||||
{
|
||||
if (ReferenceEquals(mainMaterials[i], localMaterials[i])) continue;
|
||||
localMaterials[i] = mainMaterials[i];
|
||||
hasChanged = true;
|
||||
}
|
||||
if (hasChanged) clone.sharedMaterials = localMaterials;
|
||||
|
||||
source.GetPropertyBlock(propertyBlock);
|
||||
clone.SetPropertyBlock(propertyBlock);
|
||||
|
||||
s_CopyMaterials.End();
|
||||
}
|
||||
|
||||
private static void CopyBlendShapes(
|
||||
SkinnedMeshRenderer source,
|
||||
SkinnedMeshRenderer target,
|
||||
List<float> blendShapeWeights)
|
||||
{
|
||||
s_CopyBlendShapes.Begin();
|
||||
|
||||
int weightCount = blendShapeWeights.Count;
|
||||
for (var i = 0; i < weightCount; i++)
|
||||
{
|
||||
var weight = source.GetBlendShapeWeight(i);
|
||||
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||
if (weight == blendShapeWeights[i]) continue; // Halves the work
|
||||
target.SetBlendShapeWeight(i, blendShapeWeights[i] = weight);
|
||||
}
|
||||
|
||||
s_CopyBlendShapes.End();
|
||||
}
|
||||
#endregion Update Methods
|
||||
}
|
|
@ -1,20 +1,81 @@
|
|||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Player;
|
||||
using MagicaCloth;
|
||||
using MagicaCloth2;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Utilities
|
||||
|
||||
private static bool IsRendererActive(Renderer renderer)
|
||||
=> renderer && renderer.enabled && renderer.gameObject.activeInHierarchy;
|
||||
|
||||
private static bool CameraRendersPlayerLocalLayer(Camera cam)
|
||||
=> (cam.cullingMask & (1 << CVRLayers.PlayerLocal)) != 0;
|
||||
=> (cam.cullingMask & (1 << LOCAL_LAYER)) != 0;
|
||||
|
||||
private static bool CameraRendersPlayerCloneLayer(Camera cam)
|
||||
=> (cam.cullingMask & (1 << CVRLayers.PlayerClone)) != 0;
|
||||
|
||||
=> (cam.cullingMask & (1 << CLONE_LAYER)) != 0;
|
||||
|
||||
private static bool IsUIInternalCamera(Camera cam)
|
||||
#if !UNITY_EDITOR
|
||||
=> cam == PlayerSetup.Instance.activeUiCam;
|
||||
#else
|
||||
=> cam.gameObject.layer == 15;
|
||||
#endif
|
||||
|
||||
private static bool IsRendererValid(Renderer renderer)
|
||||
=> renderer && renderer.gameObject.activeInHierarchy;
|
||||
#endregion Utilities
|
||||
|
||||
#region Magica Cloth Support
|
||||
|
||||
private void SetupMagicaClothSupport()
|
||||
{
|
||||
var magicaCloths1 = GetComponentsInChildren<BaseCloth>(true);
|
||||
foreach (BaseCloth magicaCloth1 in magicaCloths1)
|
||||
magicaCloth1.SetCullingMode(PhysicsTeam.TeamCullingMode.Off);
|
||||
|
||||
var magicaCloths2 = base.GetComponentsInChildren<MagicaCloth2.MagicaCloth>(true);
|
||||
foreach (MagicaCloth2.MagicaCloth magicaCloth2 in magicaCloths2)
|
||||
magicaCloth2.serializeData.cullingSettings.cameraCullingMode = CullingSettings.CameraCullingMode.AnimatorLinkage;
|
||||
}
|
||||
|
||||
public void OnMagicaClothMeshSwapped(Renderer render, Mesh newMesh)
|
||||
{
|
||||
switch (render)
|
||||
{
|
||||
case MeshRenderer mesh:
|
||||
{
|
||||
int index = _meshRenderers.IndexOf(mesh);
|
||||
if (index != -1) _meshCloneFilters[index].sharedMesh = newMesh;
|
||||
break;
|
||||
}
|
||||
case SkinnedMeshRenderer skinned:
|
||||
{
|
||||
int index = _skinnedRenderers.IndexOf(skinned);
|
||||
if (index != -1)
|
||||
{
|
||||
// Copy the mesh
|
||||
_skinnedClones[index].sharedMesh = newMesh;
|
||||
|
||||
// Copy appended bones if count is different
|
||||
var cloneBones = _skinnedClones[index].bones; // alloc
|
||||
var sourceBones = skinned.bones; // alloc
|
||||
|
||||
int cloneBoneCount = cloneBones.Length;
|
||||
int sourceBoneCount = sourceBones.Length;
|
||||
if (cloneBoneCount != sourceBoneCount)
|
||||
{
|
||||
// Copy the new bones only
|
||||
Array.Resize(ref cloneBones, sourceBoneCount);
|
||||
for (int i = cloneBoneCount; i < sourceBoneCount; i++) cloneBones[i] = sourceBones[i];
|
||||
_skinnedClones[index].bones = cloneBones;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Magica Cloth Support
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using NAK.AvatarCloneTest;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
|
@ -5,130 +6,142 @@ namespace NAK.AvatarCloneTest;
|
|||
|
||||
public partial class AvatarClone : MonoBehaviour
|
||||
{
|
||||
#region Constants
|
||||
|
||||
private const int LOCAL_LAYER = 8;
|
||||
private const int CLONE_LAYER = 9;
|
||||
|
||||
#endregion Constants
|
||||
|
||||
#region Profiler Markers
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
private static readonly ProfilerMarker s_CopyEnabledState = new($"{nameof(AvatarClone)}.{nameof(SyncEnabledState)}");
|
||||
private static readonly ProfilerMarker s_CopyMaterials = new($"{nameof(AvatarClone)}.{nameof(CopyMaterialsAndProperties)}");
|
||||
private static readonly ProfilerMarker s_CopyBlendShapes = new($"{nameof(AvatarClone)}.{nameof(CopyBlendShapes)}");
|
||||
private static readonly ProfilerMarker s_InitializeData = new($"{nameof(AvatarClone)}.Initialize");
|
||||
private static readonly ProfilerMarker s_UpdateExclusions = new($"{nameof(AvatarClone)}.{nameof(HandleExclusionUpdate)}");
|
||||
private static readonly ProfilerMarker s_CollectExclusionData = new($"{nameof(AvatarClone)}.{nameof(CollectExclusionData)}");
|
||||
private static readonly ProfilerMarker s_HandleBoneUpdates = new($"{nameof(AvatarClone)}.{nameof(UpdateSkinnedMeshBones)}");
|
||||
private static readonly ProfilerMarker s_PreCullUpdate = new($"{nameof(AvatarClone)}.{nameof(MyOnPreCull)}");
|
||||
private static readonly ProfilerMarker s_ConfigureShadowCasting = new($"{nameof(AvatarClone)}.{nameof(ConfigureSourceShadowCasting)}");
|
||||
private static readonly ProfilerMarker s_ConfigureUICulling = new($"{nameof(AvatarClone)}.{nameof(ConfigureCloneUICulling)}");
|
||||
private static readonly ProfilerMarker s_AddRenderer = new($"{nameof(AvatarClone)}.AddRenderer");
|
||||
private static readonly ProfilerMarker s_CreateClone = new($"{nameof(AvatarClone)}.CreateClone");
|
||||
#endif
|
||||
|
||||
#endregion Profiler Markers
|
||||
|
||||
#region Settings
|
||||
|
||||
public bool Setting_CloneMeshRenderers;
|
||||
public bool Setting_CopyMaterials = true;
|
||||
public bool Setting_CopyBlendShapes = true;
|
||||
|
||||
#endregion Settings
|
||||
|
||||
#region Source Collections - Cloned Renderers
|
||||
|
||||
// Skinned mesh renderers (always cloned)
|
||||
private List<SkinnedMeshRenderer> _skinnedRenderers;
|
||||
private List<List<float>> _blendShapeWeights;
|
||||
|
||||
// Mesh renderers (optionally cloned)
|
||||
private List<MeshRenderer> _meshRenderers;
|
||||
private List<MeshFilter> _meshFilters;
|
||||
|
||||
#endregion Source Collections - Cloned Renderers
|
||||
|
||||
#region Source Collections - Non-Cloned Renderers
|
||||
|
||||
// All other renderers (never cloned)
|
||||
private List<Renderer> _otherRenderers;
|
||||
|
||||
// True if source renderer should hide. False if source renderer should show.
|
||||
// Only used for non-cloned renderers (MeshRenderers and other Renderers).
|
||||
private bool[] _sourceShouldBeHiddenFromFPR;
|
||||
// Three states: On, ShadowsOnly, Off
|
||||
private ShadowCastingMode[] _originalShadowCastingMode;
|
||||
|
||||
#endregion Source Collections - Non-Cloned Renderers
|
||||
|
||||
#region Clone Collections
|
||||
|
||||
// Skinned mesh clones
|
||||
private List<SkinnedMeshRenderer> _skinnedClones;
|
||||
private List<Material[]> _skinnedCloneMaterials;
|
||||
private List<Material[]> _skinnedCloneCullingMaterials;
|
||||
|
||||
// Mesh clones (optional)
|
||||
private List<MeshRenderer> _meshClones;
|
||||
private List<MeshFilter> _meshCloneFilters;
|
||||
private List<Material[]> _meshCloneMaterials;
|
||||
private List<Material[]> _meshCloneCullingMaterials;
|
||||
|
||||
#endregion Clone Collections
|
||||
|
||||
#region Shared Resources
|
||||
|
||||
private List<Material> _materialWorkingList; // Used for GetSharedMaterials
|
||||
private MaterialPropertyBlock _propertyBlock;
|
||||
|
||||
#endregion Shared Resources
|
||||
|
||||
#region State
|
||||
|
||||
private bool _sourcesSetForShadowCasting;
|
||||
private bool _clonesSetForUiCulling;
|
||||
private bool[] _rendererActiveStates;
|
||||
|
||||
#endregion State
|
||||
|
||||
#region Unity Events
|
||||
|
||||
private void Start()
|
||||
{
|
||||
InitializeCollections();
|
||||
InitializeRenderers();
|
||||
SetupMaterialsAndBlendShapes();
|
||||
CreateClones();
|
||||
Setting_CloneMeshRenderers = AvatarCloneTestMod.EntryCloneMeshRenderers.Value;
|
||||
|
||||
InitializeCollections();
|
||||
CollectRenderers();
|
||||
CreateClones();
|
||||
AddExclusionToHeadIfNeeded();
|
||||
InitializeExclusions();
|
||||
SetupMagicaClothSupport();
|
||||
|
||||
Camera.onPreCull += MyOnPreRender;
|
||||
}
|
||||
// bool animatesClone = transform.Find("[ExplicitlyAnimatesVisualClones]") != null;
|
||||
// Setting_CopyMaterials = !animatesClone;
|
||||
// Setting_CopyBlendShapes = !animatesClone;
|
||||
// Animator animator = GetComponent<Animator>();
|
||||
// if (animator && animatesClone) animator.Rebind();
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Camera.onPreCull -= MyOnPreRender;
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
// Update all renderers with basic properties (Materials & BlendShapes)
|
||||
UpdateStandardRenderers();
|
||||
UpdateSkinnedRenderers();
|
||||
|
||||
// Additional pass for renderers needing extra checks (Shared Mesh & Bone Changes)
|
||||
UpdateStandardRenderersWithChecks();
|
||||
UpdateSkinnedRenderersWithChecks();
|
||||
// Likely a Unity bug with where we can touch shadowCastingMode & forceRenderingOff
|
||||
#if !UNITY_EDITOR
|
||||
Camera.onPreCull += MyOnPreCull;
|
||||
#else
|
||||
Camera.onPreRender += MyOnPreCull;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void MyOnPreRender(Camera cam)
|
||||
private void LateUpdate()
|
||||
{
|
||||
s_MyOnPreRender.Begin();
|
||||
SyncEnabledState();
|
||||
|
||||
bool isOurUiCamera = IsUIInternalCamera(cam);
|
||||
bool rendersOurPlayerLayer = CameraRendersPlayerLocalLayer(cam);
|
||||
bool rendersOurCloneLayer = CameraRendersPlayerCloneLayer(cam);
|
||||
if (Setting_CopyMaterials && AvatarCloneTestMod.EntryCopyMaterials.Value)
|
||||
SyncMaterials();
|
||||
|
||||
// Renders both player layers.
|
||||
// PlayerLocal will now act as a shadow caster, while PlayerClone will act as the actual head-hidden renderer.
|
||||
bool rendersBothPlayerLayers = rendersOurPlayerLayer && rendersOurCloneLayer;
|
||||
if (!_shadowsOnlyActive && rendersBothPlayerLayers)
|
||||
{
|
||||
s_SetShadowsOnly.Begin();
|
||||
|
||||
int sourceCount = _allSourceRenderers.Count;
|
||||
var sourceRenderers = _allSourceRenderers;
|
||||
for (int i = 0; i < sourceCount; i++)
|
||||
{
|
||||
Renderer renderer = sourceRenderers[i];
|
||||
if (!IsRendererValid(renderer)) continue;
|
||||
|
||||
bool shouldRender = renderer.shadowCastingMode != ShadowCastingMode.Off;
|
||||
_originallyWasEnabled[i] = renderer.enabled;
|
||||
_originallyHadShadows[i] = shouldRender;
|
||||
renderer.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
if (renderer.forceRenderingOff == shouldRender) renderer.forceRenderingOff = !shouldRender; // TODO: Eval if check is needed
|
||||
}
|
||||
_shadowsOnlyActive = true;
|
||||
|
||||
s_SetShadowsOnly.End();
|
||||
}
|
||||
else if (_shadowsOnlyActive && !rendersBothPlayerLayers)
|
||||
{
|
||||
s_UndoShadowsOnly.Begin();
|
||||
|
||||
int sourceCount = _allSourceRenderers.Count;
|
||||
var sourceRenderers = _allSourceRenderers;
|
||||
for (int i = 0; i < sourceCount; i++)
|
||||
{
|
||||
Renderer renderer = sourceRenderers[i];
|
||||
if (!IsRendererValid(renderer)) continue;
|
||||
|
||||
renderer.shadowCastingMode = _originallyHadShadows[i] ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
||||
if (renderer.forceRenderingOff == _originallyWasEnabled[i]) renderer.forceRenderingOff = !_originallyWasEnabled[i]; // TODO: Eval if check is needed
|
||||
}
|
||||
_shadowsOnlyActive = false;
|
||||
|
||||
s_UndoShadowsOnly.End();
|
||||
}
|
||||
|
||||
// Handle UI culling material changes
|
||||
if (isOurUiCamera && !_uiCullingActive && rendersOurCloneLayer)
|
||||
{
|
||||
s_SetUiCulling.Begin();
|
||||
|
||||
int standardCount = _standardRenderers.Count;
|
||||
var standardClones = _standardClones;
|
||||
var cullingMaterials = _cullingMaterials;
|
||||
for (int i = 0; i < standardCount; i++)
|
||||
standardClones[i].sharedMaterials = cullingMaterials[i];
|
||||
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
var skinnedClones = _skinnedClones;
|
||||
for (int i = 0; i < skinnedCount; i++)
|
||||
skinnedClones[i].sharedMaterials = cullingMaterials[i + standardCount];
|
||||
|
||||
_uiCullingActive = true;
|
||||
|
||||
s_SetUiCulling.End();
|
||||
}
|
||||
else if (!isOurUiCamera && _uiCullingActive)
|
||||
{
|
||||
s_UndoUiCulling.Begin();
|
||||
|
||||
int standardCount = _standardRenderers.Count;
|
||||
var standardClones = _standardClones;
|
||||
var localMaterials = _localMaterials;
|
||||
for (int i = 0; i < standardCount; i++)
|
||||
standardClones[i].sharedMaterials = localMaterials[i];
|
||||
|
||||
int skinnedCount = _skinnedRenderers.Count;
|
||||
var skinnedClones = _skinnedClones;
|
||||
for (int i = 0; i < skinnedCount; i++)
|
||||
skinnedClones[i].sharedMaterials = localMaterials[i + standardCount];
|
||||
|
||||
_uiCullingActive = false;
|
||||
|
||||
s_UndoUiCulling.End();
|
||||
}
|
||||
|
||||
s_MyOnPreRender.End();
|
||||
if (Setting_CopyBlendShapes && AvatarCloneTestMod.EntryCopyBlendShapes.Value)
|
||||
SyncBlendShapes();
|
||||
}
|
||||
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Likely a Unity bug with where we can touch shadowCastingMode & forceRenderingOff
|
||||
#if !UNITY_EDITOR
|
||||
Camera.onPreCull -= MyOnPreCull;
|
||||
#else
|
||||
Camera.onPreRender -= MyOnPreCull;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion Unity Events
|
||||
}
|
|
@ -5,9 +5,14 @@ namespace NAK.AvatarCloneTest;
|
|||
|
||||
public class AvatarCloneExclusion : IExclusionBehaviour
|
||||
{
|
||||
public readonly Dictionary<SkinnedMeshRenderer, List<int>> skinnedToBoneIndex = new();
|
||||
public readonly List<Transform> affectedTransforms = new();
|
||||
public readonly List<Renderer> affectedRenderers = new();
|
||||
public HashSet<Transform> affectedTransformSet = new();
|
||||
|
||||
private readonly AvatarClone _cloneSystem;
|
||||
private readonly Transform _target;
|
||||
private Transform _shrinkBone;
|
||||
internal Transform _shrinkBone;
|
||||
|
||||
public bool isImmuneToGlobalState { get; set; }
|
||||
|
||||
|
@ -19,20 +24,16 @@ public class AvatarCloneExclusion : IExclusionBehaviour
|
|||
|
||||
public void UpdateExclusions(bool isShown, bool shrinkToZero)
|
||||
{
|
||||
Debug.Log($"[AvatarClone2] Updating exclusion for {_target.name}: isShown={isShown}, shrinkToZero={shrinkToZero}");
|
||||
|
||||
if (_shrinkBone == null)
|
||||
{
|
||||
// Create shrink bone parented directly to target
|
||||
_shrinkBone = new GameObject($"{_target.name}_Shrink").transform;
|
||||
_shrinkBone.SetParent(_target, false);
|
||||
Debug.Log($"[AvatarClone2] Created shrink bone for {_target.name}");
|
||||
}
|
||||
|
||||
// Set scale based on shrink mode
|
||||
_shrinkBone.localScale = shrinkToZero ? Vector3.zero : Vector3.positiveInfinity;
|
||||
|
||||
// Let the clone system handle the update
|
||||
_cloneSystem.HandleExclusionUpdate(_target, _shrinkBone, isShown);
|
||||
// Replace the bone references with the shrink bone for the indicies we modify
|
||||
_cloneSystem.HandleExclusionUpdate(this, isShown);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.EventSystem;
|
||||
using ABI_RC.Core.Savior;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
|
||||
|
@ -15,17 +17,17 @@ public class AvatarCloneTestMod : MelonMod
|
|||
Category.CreateEntry("use_avatar_clone_test", true,
|
||||
"Use Avatar Clone", description: "Uses the Avatar Clone setup for the local avatar.");
|
||||
|
||||
// internal static readonly MelonPreferences_Entry<bool> EntryCopyBlendShapes =
|
||||
// Category.CreateEntry("copy_blend_shapes", true,
|
||||
// "Copy Blend Shapes", description: "Copies the blend shapes from the original avatar to the clone.");
|
||||
//
|
||||
// internal static readonly MelonPreferences_Entry<bool> EntryCopyMaterials =
|
||||
// Category.CreateEntry("copy_materials", true,
|
||||
// "Copy Materials", description: "Copies the materials from the original avatar to the clone.");
|
||||
//
|
||||
// internal static readonly MelonPreferences_Entry<bool> EntryCopyMeshes =
|
||||
// Category.CreateEntry("copy_meshes", true,
|
||||
// "Copy Meshes", description: "Copies the meshes from the original avatar to the clone.");
|
||||
internal static readonly MelonPreferences_Entry<bool> EntryCloneMeshRenderers =
|
||||
Category.CreateEntry("clone_mesh_renderers", false,
|
||||
"Clone Mesh Renderers", description: "Clones the mesh renderers from the original avatar to the clone.");
|
||||
|
||||
internal static readonly MelonPreferences_Entry<bool> EntryCopyBlendShapes =
|
||||
Category.CreateEntry("copy_blend_shapes", true,
|
||||
"Copy Blend Shapes", description: "Copies the blend shapes from the original avatar to the clone.");
|
||||
|
||||
internal static readonly MelonPreferences_Entry<bool> EntryCopyMaterials =
|
||||
Category.CreateEntry("copy_materials", true,
|
||||
"Copy Materials", description: "Copies the materials from the original avatar to the clone.");
|
||||
|
||||
#endregion Melon Preferences
|
||||
|
||||
|
@ -49,6 +51,13 @@ public class AvatarCloneTestMod : MelonMod
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if pressing ctrl + r, reload avatar
|
||||
if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.R))
|
||||
{
|
||||
var player = MetaPort.Instance.currentAvatarGuid;
|
||||
AssetManagement.Instance.LoadLocalAvatar(player);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Melon Events
|
||||
|
|
|
@ -18,70 +18,5 @@ public static class Patches
|
|||
avatar.AddComponent<AvatarClone>();
|
||||
return false;
|
||||
}
|
||||
|
||||
// [HarmonyPostfix]
|
||||
// [HarmonyPatch(typeof(FPRExclusion), nameof(FPRExclusion.UpdateExclusions))]
|
||||
// private static void OnUpdateExclusions(ref FPRExclusion __instance)
|
||||
// {
|
||||
// AvatarClone clone = PlayerSetup.Instance._avatar.GetComponent<AvatarClone>();
|
||||
// if (clone == null) return;
|
||||
// clone.SetBoneChainVisibility(__instance.target, !__instance.isShown, !__instance.shrinkToZero);
|
||||
// }
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(CVRMirror), nameof(CVRMirror.Start))]
|
||||
private static void OnMirrorStart(CVRMirror __instance)
|
||||
{
|
||||
if (!AvatarCloneTestMod.EntryUseAvatarCloneTest.Value)
|
||||
return;
|
||||
|
||||
// Don't reflect the player clone layer
|
||||
__instance.m_ReflectLayers &= ~(1 << CVRLayers.PlayerClone);
|
||||
}
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.Update))]
|
||||
private static void OnTransformHiderManagerUpdate(PlayerSetup __instance)
|
||||
{
|
||||
if (!AvatarCloneTestMod.EntryUseAvatarCloneTest.Value)
|
||||
return;
|
||||
|
||||
if (MetaPort.Instance.settings.GetSettingsBool("ExperimentalAvatarOverrenderUI"))
|
||||
__instance.activeUiCam.cullingMask |= 1 << CVRLayers.PlayerClone;
|
||||
else
|
||||
__instance.activeUiCam.cullingMask &= ~(1 << CVRLayers.PlayerClone);
|
||||
}
|
||||
|
||||
private static bool _wasDebugInPortableCamera;
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PortableCamera), nameof(PortableCamera.Update))]
|
||||
private static void OnPortableCameraUpdate(ref PortableCamera __instance)
|
||||
{
|
||||
if (!AvatarCloneTestMod.EntryUseAvatarCloneTest.Value)
|
||||
{
|
||||
// Show both PlayerLocal and PlayerClone
|
||||
__instance.cameraComponent.cullingMask |= 1 << CVRLayers.PlayerLocal;
|
||||
__instance.cameraComponent.cullingMask |= 1 << CVRLayers.PlayerClone;
|
||||
return;
|
||||
}
|
||||
|
||||
if (TransformHiderManager.s_DebugInPortableCamera == _wasDebugInPortableCamera)
|
||||
return;
|
||||
|
||||
if (TransformHiderManager.s_DebugInPortableCamera)
|
||||
{
|
||||
// Hide PlayerLocal, show PlayerClone
|
||||
__instance.cameraComponent.cullingMask &= ~(1 << CVRLayers.PlayerLocal);
|
||||
__instance.cameraComponent.cullingMask |= 1 << CVRLayers.PlayerClone;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show PlayerLocal, hide PlayerClone
|
||||
__instance.cameraComponent.cullingMask |= 1 << CVRLayers.PlayerLocal;
|
||||
__instance.cameraComponent.cullingMask &= ~(1 << CVRLayers.PlayerClone);
|
||||
}
|
||||
|
||||
_wasDebugInPortableCamera = TransformHiderManager.s_DebugInPortableCamera;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,6 @@ using System.Reflection;
|
|||
namespace NAK.AvatarCloneTest.Properties;
|
||||
internal static class AssemblyInfoParams
|
||||
{
|
||||
public const string Version = "1.0.0";
|
||||
public const string Version = "1.0.3";
|
||||
public const string Author = "NotAKidoS";
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue