mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 06:19:22 +00:00
further cleanup of repo
This commit is contained in:
parent
4f8dcb0cd0
commit
323eb92f2e
140 changed files with 1 additions and 2430 deletions
|
@ -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
|
||||
}
|
304
.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Init.cs
Normal file
304
.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Init.cs
Normal file
|
@ -0,0 +1,304 @@
|
|||
using ABI_RC.Core.Player.ShadowClone;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public partial class AvatarClone
|
||||
{
|
||||
#region Initialization
|
||||
|
||||
private void InitializeCollections()
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.Begin();
|
||||
#endif
|
||||
|
||||
// Initialize source collections
|
||||
_skinnedRenderers = new List<SkinnedMeshRenderer>();
|
||||
_blendShapeWeights = new List<List<float>>();
|
||||
|
||||
_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[]>();
|
||||
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
_meshClones = new List<MeshRenderer>();
|
||||
_meshCloneFilters = new List<MeshFilter>();
|
||||
_meshCloneMaterials = new List<Material[]>();
|
||||
_meshCloneCullingMaterials = new List<Material[]>();
|
||||
}
|
||||
|
||||
// Initialize shared resources
|
||||
_materialWorkingList = new List<Material>();
|
||||
_propertyBlock = new MaterialPropertyBlock();
|
||||
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.End();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void CollectRenderers()
|
||||
{
|
||||
#if ENABLE_PROFILER
|
||||
s_InitializeData.Begin();
|
||||
#endif
|
||||
|
||||
var renderers = GetComponentsInChildren<Renderer>(true);
|
||||
var currentIndex = 0;
|
||||
var nonCloned = 0;
|
||||
|
||||
// Single pass: directly categorize renderers
|
||||
foreach (Renderer renderer in renderers)
|
||||
{
|
||||
switch (renderer)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (Setting_CloneMeshRenderers)
|
||||
{
|
||||
AddMeshRenderer(mesh, filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddMeshRenderer(mesh, filter);
|
||||
nonCloned++;
|
||||
}
|
||||
currentIndex++;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
AddOtherRenderer(renderer);
|
||||
currentIndex++;
|
||||
nonCloned++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_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
|
||||
}
|
|
@ -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
.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.StateSync.cs
Normal file
156
.Deprecated/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
|
||||
}
|
81
.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Util.cs
Normal file
81
.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.Util.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
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 << LOCAL_LAYER)) != 0;
|
||||
|
||||
private static bool CameraRendersPlayerCloneLayer(Camera cam)
|
||||
=> (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
|
||||
|
||||
#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
|
||||
}
|
147
.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.cs
Normal file
147
.Deprecated/AvatarCloneTest/AvatarClone/AvatarClone.cs
Normal file
|
@ -0,0 +1,147 @@
|
|||
using NAK.AvatarCloneTest;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
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()
|
||||
{
|
||||
Setting_CloneMeshRenderers = AvatarCloneTestMod.EntryCloneMeshRenderers.Value;
|
||||
|
||||
InitializeCollections();
|
||||
CollectRenderers();
|
||||
CreateClones();
|
||||
AddExclusionToHeadIfNeeded();
|
||||
InitializeExclusions();
|
||||
SetupMagicaClothSupport();
|
||||
|
||||
// bool animatesClone = transform.Find("[ExplicitlyAnimatesVisualClones]") != null;
|
||||
// Setting_CopyMaterials = !animatesClone;
|
||||
// Setting_CopyBlendShapes = !animatesClone;
|
||||
// Animator animator = GetComponent<Animator>();
|
||||
// if (animator && animatesClone) animator.Rebind();
|
||||
|
||||
// Likely a Unity bug with where we can touch shadowCastingMode & forceRenderingOff
|
||||
#if !UNITY_EDITOR
|
||||
Camera.onPreCull += MyOnPreCull;
|
||||
#else
|
||||
Camera.onPreRender += MyOnPreCull;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
SyncEnabledState();
|
||||
|
||||
if (Setting_CopyMaterials && AvatarCloneTestMod.EntryCopyMaterials.Value)
|
||||
SyncMaterials();
|
||||
|
||||
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
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using ABI.CCK.Components;
|
||||
using UnityEngine;
|
||||
|
||||
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;
|
||||
internal Transform _shrinkBone;
|
||||
|
||||
public bool isImmuneToGlobalState { get; set; }
|
||||
|
||||
public AvatarCloneExclusion(AvatarClone cloneSystem, Transform target)
|
||||
{
|
||||
_cloneSystem = cloneSystem;
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public void UpdateExclusions(bool isShown, bool shrinkToZero)
|
||||
{
|
||||
if (_shrinkBone == null)
|
||||
{
|
||||
// Create shrink bone parented directly to target
|
||||
_shrinkBone = new GameObject($"{_target.name}_Shrink").transform;
|
||||
_shrinkBone.SetParent(_target, false);
|
||||
}
|
||||
// Set scale based on shrink mode
|
||||
_shrinkBone.localScale = shrinkToZero ? Vector3.zero : Vector3.positiveInfinity;
|
||||
|
||||
// Replace the bone references with the shrink bone for the indicies we modify
|
||||
_cloneSystem.HandleExclusionUpdate(this, isShown);
|
||||
}
|
||||
}
|
9
.Deprecated/AvatarCloneTest/AvatarCloneTest.csproj
Normal file
9
.Deprecated/AvatarCloneTest/AvatarCloneTest.csproj
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<RootNamespace>LocalCloneFix</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DefineConstants>TRACE;TRACE;</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
81
.Deprecated/AvatarCloneTest/Main.cs
Normal file
81
.Deprecated/AvatarCloneTest/Main.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.EventSystem;
|
||||
using ABI_RC.Core.Savior;
|
||||
using MelonLoader;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public class AvatarCloneTestMod : MelonMod
|
||||
{
|
||||
#region Melon Preferences
|
||||
|
||||
private static readonly MelonPreferences_Category Category =
|
||||
MelonPreferences.CreateCategory(nameof(AvatarCloneTest));
|
||||
|
||||
internal static readonly MelonPreferences_Entry<bool> EntryUseAvatarCloneTest =
|
||||
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> 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
|
||||
|
||||
#region Melon Events
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
ApplyPatches(typeof(Patches)); // slapped together a fix cause HarmonyInstance.Patch was null ref for no reason?
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
// press f1 to find all cameras that arent tagged main and set them tno not render CVRLayers.PlayerClone
|
||||
if (Input.GetKeyDown(KeyCode.F1))
|
||||
{
|
||||
foreach (var camera in UnityEngine.Object.FindObjectsOfType<UnityEngine.Camera>())
|
||||
{
|
||||
if (camera.tag != "MainCamera")
|
||||
{
|
||||
camera.cullingMask &= ~(1 << CVRLayers.PlayerClone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
#region Melon Mod Utilities
|
||||
|
||||
private void ApplyPatches(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
HarmonyInstance.PatchAll(type);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LoggerInstance.Msg($"Failed while patching {type.Name}!");
|
||||
LoggerInstance.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Melon Mod Utilities
|
||||
}
|
22
.Deprecated/AvatarCloneTest/Patches.cs
Normal file
22
.Deprecated/AvatarCloneTest/Patches.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Player.TransformHider;
|
||||
using ABI_RC.Core.Savior;
|
||||
using ABI_RC.Systems.Camera;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarCloneTest;
|
||||
|
||||
public static class Patches
|
||||
{
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(TransformHiderUtils), nameof(TransformHiderUtils.SetupAvatar))]
|
||||
private static bool OnSetupAvatar(GameObject avatar)
|
||||
{
|
||||
if (!AvatarCloneTestMod.EntryUseAvatarCloneTest.Value) return true;
|
||||
avatar.AddComponent<AvatarClone>();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
32
.Deprecated/AvatarCloneTest/Properties/AssemblyInfo.cs
Normal file
32
.Deprecated/AvatarCloneTest/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using MelonLoader;
|
||||
using NAK.AvatarCloneTest.Properties;
|
||||
using System.Reflection;
|
||||
|
||||
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyTitle(nameof(NAK.AvatarCloneTest))]
|
||||
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||
[assembly: AssemblyProduct(nameof(NAK.AvatarCloneTest))]
|
||||
|
||||
[assembly: MelonInfo(
|
||||
typeof(NAK.AvatarCloneTest.AvatarCloneTestMod),
|
||||
nameof(NAK.AvatarCloneTest),
|
||||
AssemblyInfoParams.Version,
|
||||
AssemblyInfoParams.Author,
|
||||
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/AvatarCloneTest"
|
||||
)]
|
||||
|
||||
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||
[assembly: MelonColor(255, 246, 25, 99)] // red-pink
|
||||
[assembly: MelonAuthorColor(255, 158, 21, 32)] // red
|
||||
[assembly: HarmonyDontPatchAll]
|
||||
|
||||
namespace NAK.AvatarCloneTest.Properties;
|
||||
internal static class AssemblyInfoParams
|
||||
{
|
||||
public const string Version = "1.0.3";
|
||||
public const string Author = "NotAKidoS";
|
||||
}
|
18
.Deprecated/AvatarCloneTest/README.md
Normal file
18
.Deprecated/AvatarCloneTest/README.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# VisualCloneFix
|
||||
|
||||
Fixes the Visual Clone system and allows you to use it again.
|
||||
|
||||
Using the Visual Clone should be faster than the default Head Hiding & Shadow Clones, but will add a longer hitch on initial avatar load.
|
||||
|
||||
**NOTE:** The Visual Clone is still an experimental feature that was temporarily removed in [ChilloutVR 2024r175 Hotfix 1](https://abinteractive.net/blog/chilloutvr_2024r175_hotfix_1), so there may be bugs or issues with it.
|
||||
|
||||
---
|
||||
|
||||
Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI.
|
||||
https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games
|
||||
|
||||
> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive.
|
||||
|
||||
> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use.
|
||||
|
||||
> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive.
|
23
.Deprecated/AvatarCloneTest/format.json
Normal file
23
.Deprecated/AvatarCloneTest/format.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"_id": 221,
|
||||
"name": "VisualCloneFix",
|
||||
"modversion": "1.0.1",
|
||||
"gameversion": "2024r175",
|
||||
"loaderversion": "0.6.1",
|
||||
"modtype": "Mod",
|
||||
"author": "NotAKidoS",
|
||||
"description": "Fixes the Visual Clone system and allows you to use it again.\n\nUsing the Visual Clone should be faster than the default Head Hiding & Shadow Clones, but will add a longer hitch on initial avatar load.\n\n**NOTE:** The Visual Clone is still an experimental feature that was temporarily removed in [ChilloutVR 2024r175 Hotfix 1](https://abinteractive.net/blog/chilloutvr_2024r175_hotfix_1), so there may be bugs or issues with it.",
|
||||
"searchtags": [
|
||||
"visual",
|
||||
"clone",
|
||||
"head",
|
||||
"hiding"
|
||||
],
|
||||
"requirements": [
|
||||
"None"
|
||||
],
|
||||
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r36/VisualCloneFix.dll",
|
||||
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/VisualCloneFix/",
|
||||
"changelog": "- Fixed FPRExclusions IsShown state being inverted when toggled.\n- Fixed head FPRExclusion generation not checking for existing exclusion.\n- Sped up FindExclusionVertList by 100x by not being an idiot. This heavily reduces avatar hitch with Visual Clone active.",
|
||||
"embedcolor": "#f61963"
|
||||
}
|
24
.Deprecated/ChatBoxExtensions/ChatBoxExtensions.csproj
Normal file
24
.Deprecated/ChatBoxExtensions/ChatBoxExtensions.csproj
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Mute SDraw's funny warning, because he only compilled for x64 while most people compile for both -->
|
||||
<NoWarn>$(NoWarn);MSB3270</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Didn't put in the Directory.Build.props because it spams funny warnings... -->
|
||||
<ItemGroup>
|
||||
<Reference Include="ECM2">
|
||||
<HintPath>..\.ManagedLibs\ECM2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ml_prm">
|
||||
<HintPath>$(MsBuildThisFileDirectory)\..\.ManagedLibs\ml_prm.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ChatBox">
|
||||
<HintPath>$(MsBuildThisFileDirectory)\..\.ManagedLibs\ChatBox.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
14
.Deprecated/ChatBoxExtensions/HarmonyPatches.cs
Normal file
14
.Deprecated/ChatBoxExtensions/HarmonyPatches.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using ABI_RC.Systems.InputManagement;
|
||||
using HarmonyLib;
|
||||
|
||||
namespace NAK.ChatBoxExtensions.HarmonyPatches;
|
||||
|
||||
public class CVRInputManagerPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(CVRInputManager), nameof(CVRInputManager.Start))]
|
||||
private static void Postfix_CVRInputManager_Start(ref CVRInputManager __instance)
|
||||
{
|
||||
__instance.AddInputModule(ChatBoxExtensions.InputModule);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
using ABI_RC.Systems.InputManagement;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.ChatBoxExtensions.InputModules;
|
||||
|
||||
internal class InputModuleChatBoxExtensions : CVRInputModule
|
||||
{
|
||||
public bool jump = false;
|
||||
public float emote = -1;
|
||||
public Vector2 lookVector = Vector2.zero;
|
||||
public Vector3 movementVector = Vector3.zero;
|
||||
|
||||
public override void UpdateInput()
|
||||
{
|
||||
CVRInputManager.Instance.jump |= jump;
|
||||
CVRInputManager.Instance.movementVector += movementVector;
|
||||
CVRInputManager.Instance.lookVector += lookVector;
|
||||
if (emote > 0) CVRInputManager.Instance.emote = emote;
|
||||
|
||||
jump = false;
|
||||
emote = -1;
|
||||
lookVector = Vector3.zero;
|
||||
movementVector = Vector3.zero;
|
||||
}
|
||||
}
|
56
.Deprecated/ChatBoxExtensions/Integrations/Base.cs
Normal file
56
.Deprecated/ChatBoxExtensions/Integrations/Base.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using ABI_RC.Core.Networking;
|
||||
using ABI_RC.Core.Player;
|
||||
|
||||
namespace NAK.ChatBoxExtensions.Integrations;
|
||||
|
||||
public class CommandBase
|
||||
{
|
||||
internal static bool IsCommandForAll(string argument)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(argument)) return false;
|
||||
return argument == "*" || argument.StartsWith("@a") || argument.StartsWith("@e");
|
||||
}
|
||||
|
||||
internal static bool IsCommandForLocalPlayer(string argument)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(argument)) return false;
|
||||
if (argument.Contains("*"))
|
||||
{
|
||||
string partialName = argument.Replace("*", "").Trim();
|
||||
if (String.IsNullOrWhiteSpace(partialName)) return false;
|
||||
return AuthManager.Username.Contains(partialName);
|
||||
}
|
||||
return AuthManager.Username == argument;
|
||||
}
|
||||
|
||||
internal static void LocalCommandIgnoreOthers(string argument, Action<string[]> callback)
|
||||
{
|
||||
string[] args = argument.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).ToArray();
|
||||
|
||||
// will fail if arguments are specified which arent local player
|
||||
if (args.Length == 0 || IsCommandForAll(args[0]) || IsCommandForLocalPlayer(args[0])) callback(args);
|
||||
}
|
||||
|
||||
//remote must specify exact player, wont respawn to all
|
||||
internal static void RemoteCommandListenForSelf(string argument, Action<string[]> callback)
|
||||
{
|
||||
string[] args = argument.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).ToArray();
|
||||
|
||||
if (args.Length == 0) return;
|
||||
if (IsCommandForLocalPlayer(args[0])) callback(args);
|
||||
}
|
||||
|
||||
// remote must specify player or all, ignore commands without arguments
|
||||
internal static void RemoteCommandListenForAll(string argument, Action<string[]> callback)
|
||||
{
|
||||
string[] args = argument.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).ToArray();
|
||||
|
||||
if (args.Length == 0) return;
|
||||
if (IsCommandForAll(args[0]) || IsCommandForLocalPlayer(args[0])) callback(args);
|
||||
}
|
||||
|
||||
internal static string GetPlayerUsername(string guid)
|
||||
{
|
||||
return CVRPlayerManager.Instance.TryGetPlayerName(guid);
|
||||
}
|
||||
}
|
91
.Deprecated/ChatBoxExtensions/Integrations/ChatBox.cs
Normal file
91
.Deprecated/ChatBoxExtensions/Integrations/ChatBox.cs
Normal file
|
@ -0,0 +1,91 @@
|
|||
using Kafe.ChatBox;
|
||||
|
||||
namespace NAK.ChatBoxExtensions.Integrations;
|
||||
|
||||
internal class ChatBoxCommands : CommandBase
|
||||
{
|
||||
public static void RegisterCommands()
|
||||
{
|
||||
bool awaitingPing = false;
|
||||
DateTime pingTime = DateTime.MinValue; // store the time when "ping" command was sent
|
||||
|
||||
Commands.RegisterCommand("ping",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
pingTime = DateTime.Now;
|
||||
awaitingPing = true;
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForSelf(message, args =>
|
||||
{
|
||||
API.SendMessage("/pong " + GetPlayerUsername(sender), false, true, true);
|
||||
});
|
||||
});
|
||||
|
||||
Commands.RegisterCommand("pong",
|
||||
onCommandSent: null,
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForSelf(message, args =>
|
||||
{
|
||||
if (awaitingPing)
|
||||
{
|
||||
awaitingPing = false;
|
||||
TimeSpan timeSincePing = DateTime.Now - pingTime; // calculate the time difference
|
||||
API.SendMessage($"Time since ping: {timeSincePing.TotalMilliseconds}ms", false, true, true);
|
||||
return;
|
||||
}
|
||||
API.SendMessage($"You have to ping first, {GetPlayerUsername(sender)}!", false, true, true);
|
||||
});
|
||||
});
|
||||
|
||||
Commands.RegisterCommand("sudo",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 1)
|
||||
{
|
||||
string command = string.Join(" ", args.Skip(1));
|
||||
API.SendMessage($"/{command}", false, true, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (args.Length > 1)
|
||||
{
|
||||
string command = string.Join(" ", args.Skip(1));
|
||||
API.SendMessage($"/{command}", false, true, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Commands.RegisterCommand("say",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
string text = string.Join(" ", args);
|
||||
API.SendMessage(text, false, true, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
string text = string.Join(" ", args);
|
||||
API.SendMessage(text, false, true, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
32
.Deprecated/ChatBoxExtensions/Integrations/ChilloutVRAAS.cs
Normal file
32
.Deprecated/ChatBoxExtensions/Integrations/ChilloutVRAAS.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using ABI_RC.Core.Player;
|
||||
|
||||
namespace NAK.ChatBoxExtensions.Integrations;
|
||||
|
||||
internal class ChilloutVRAASCommands : CommandBase
|
||||
{
|
||||
public static void RegisterCommands()
|
||||
{
|
||||
// /aas [target player] [name] [value]
|
||||
Commands.RegisterCommand("aas",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 2 && float.TryParse(args[2], out float value))
|
||||
{
|
||||
PlayerSetup.Instance.ChangeAnimatorParam(args[1], value);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (args.Length > 2 && float.TryParse(args[2], out float value))
|
||||
{
|
||||
PlayerSetup.Instance.ChangeAnimatorParam(args[1], value);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
118
.Deprecated/ChatBoxExtensions/Integrations/ChilloutVRBase.cs
Normal file
118
.Deprecated/ChatBoxExtensions/Integrations/ChilloutVRBase.cs
Normal file
|
@ -0,0 +1,118 @@
|
|||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.Base;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Systems.Movement;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NAK.ChatBoxExtensions.Integrations;
|
||||
|
||||
internal class ChilloutVRBaseCommands : CommandBase
|
||||
{
|
||||
public static void RegisterCommands()
|
||||
{
|
||||
Commands.RegisterCommand("respawn",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
RootLogic.Instance.Respawn();
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, (args) =>
|
||||
{
|
||||
RootLogic.Instance.Respawn();
|
||||
});
|
||||
});
|
||||
|
||||
Commands.RegisterCommand("mute",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
AudioManagement.SetMicrophoneActive(false);
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
AudioManagement.SetMicrophoneActive(false);
|
||||
});
|
||||
});
|
||||
|
||||
// teleport [x] [y] [z]
|
||||
Commands.RegisterCommand("teleport",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 2 && float.TryParse(args[0], out float x) && float.TryParse(args[1], out float y) && float.TryParse(args[2], out float z))
|
||||
{
|
||||
BetterBetterCharacterController.Instance.TeleportPlayerTo(new Vector3(x, y, z), false, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (args.Length > 2 && float.TryParse(args[0], out float x) && float.TryParse(args[1], out float y) && float.TryParse(args[2], out float z))
|
||||
{
|
||||
BetterBetterCharacterController.Instance.TeleportPlayerTo(new Vector3(x, y, z), false, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// tp [x] [y] [z]
|
||||
Commands.RegisterCommand("tp",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 2 && float.TryParse(args[0], out float x) && float.TryParse(args[1], out float y) && float.TryParse(args[2], out float z))
|
||||
{
|
||||
BetterBetterCharacterController.Instance.TeleportPlayerTo(new Vector3(x, y, z), false, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (args.Length > 2 && float.TryParse(args[0], out float x) && float.TryParse(args[1], out float y) && float.TryParse(args[2], out float z))
|
||||
{
|
||||
BetterBetterCharacterController.Instance.TeleportPlayerTo(new Vector3(x, y, z), false, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// teleport [player]
|
||||
Commands.RegisterCommand("teleport",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
string player = args[0];
|
||||
CVRPlayerEntity playerEnt = CVRPlayerManager.Instance.NetworkPlayers.FirstOrDefault(x => x.Username == player);
|
||||
if (playerEnt != null) BetterBetterCharacterController.Instance.TeleportPlayerTo(playerEnt.PuppetMaster.transform.position, false, false);
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (args.Length > 0)
|
||||
{
|
||||
string player = args[0];
|
||||
CVRPlayerEntity playerEnt = CVRPlayerManager.Instance.NetworkPlayers.FirstOrDefault(x => x.Username == player);
|
||||
if (playerEnt != null) BetterBetterCharacterController.Instance.TeleportPlayerTo(playerEnt.PuppetMaster.transform.position, false, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
namespace NAK.ChatBoxExtensions.Integrations;
|
||||
|
||||
internal class ChilloutVRInputCommands : CommandBase
|
||||
{
|
||||
public static void RegisterCommands()
|
||||
{
|
||||
Commands.RegisterCommand("emote",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 0 && int.TryParse(args[0], out int emote))
|
||||
{
|
||||
ChatBoxExtensions.InputModule.emote = (float)emote;
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (args.Length > 1 && int.TryParse(args[1], out int emote))
|
||||
{
|
||||
ChatBoxExtensions.InputModule.emote = (float)emote;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Commands.RegisterCommand("jump",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, args =>
|
||||
{
|
||||
if (args.Length > 0 && bool.TryParse(args[0], out bool jump))
|
||||
{
|
||||
ChatBoxExtensions.InputModule.jump = jump;
|
||||
return;
|
||||
}
|
||||
ChatBoxExtensions.InputModule.jump = true;
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, args =>
|
||||
{
|
||||
if (bool.TryParse(args[0], out bool jump))
|
||||
{
|
||||
ChatBoxExtensions.InputModule.jump = jump;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
54
.Deprecated/ChatBoxExtensions/Integrations/Commands.cs
Normal file
54
.Deprecated/ChatBoxExtensions/Integrations/Commands.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
namespace NAK.ChatBoxExtensions.Integrations;
|
||||
|
||||
public static class Commands
|
||||
{
|
||||
|
||||
private const string Character = "/";
|
||||
private static readonly List<Command> CommandList = new();
|
||||
|
||||
internal static void InitializeCommandHandlers()
|
||||
{
|
||||
Kafe.ChatBox.API.OnMessageSent += msg => HandleSentCommand(msg.Message, msg.TriggerNotification, msg.DisplayOnChatBox);
|
||||
Kafe.ChatBox.API.OnMessageReceived += msg => HandleReceivedCommand(msg.SenderGuid, msg.Message, msg.TriggerNotification, msg.DisplayOnChatBox);
|
||||
}
|
||||
|
||||
internal static void RegisterCommand(string prefix, Action<string, bool, bool> onCommandSent = null, Action<string, string, bool, bool> onCommandReceived = null)
|
||||
{
|
||||
var cmd = new Command { Prefix = prefix, OnCommandSent = onCommandSent, OnCommandReceived = onCommandReceived };
|
||||
CommandList.Add(cmd);
|
||||
}
|
||||
|
||||
internal static void UnregisterCommand(string prefix)
|
||||
{
|
||||
CommandList.RemoveAll(cmd => cmd.Prefix == prefix);
|
||||
}
|
||||
|
||||
private class Command
|
||||
{
|
||||
internal string Prefix;
|
||||
|
||||
// Command Sent (message)
|
||||
internal Action<string, bool, bool> OnCommandSent;
|
||||
|
||||
// Command Sent (sender guid, message)
|
||||
internal Action<string, string, bool, bool> OnCommandReceived;
|
||||
}
|
||||
|
||||
private static void HandleSentCommand(string message, bool notification, bool displayMsg)
|
||||
{
|
||||
if (!message.StartsWith(Character)) return;
|
||||
foreach (var command in CommandList.Where(command => message.StartsWith(Character + command.Prefix)))
|
||||
{
|
||||
command.OnCommandSent?.Invoke(message, notification, displayMsg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleReceivedCommand(string sender, string message, bool notification, bool displayMsg)
|
||||
{
|
||||
if (!message.StartsWith(Character)) return;
|
||||
foreach (var command in CommandList.Where(command => message.StartsWith(Character + command.Prefix)))
|
||||
{
|
||||
command.OnCommandReceived?.Invoke(sender, message, notification, displayMsg);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
using ml_prm;
|
||||
|
||||
namespace NAK.ChatBoxExtensions.Integrations;
|
||||
|
||||
internal class PlayerRagdollModCommands : CommandBase
|
||||
{
|
||||
public static void RegisterCommands()
|
||||
{
|
||||
Commands.RegisterCommand("unragdoll",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, (args) =>
|
||||
{
|
||||
if (RagdollController.Instance.IsRagdolled())
|
||||
{
|
||||
RagdollController.Instance.SwitchRagdoll();
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, (args) =>
|
||||
{
|
||||
if (RagdollController.Instance.IsRagdolled())
|
||||
{
|
||||
RagdollController.Instance.SwitchRagdoll();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Commands.RegisterCommand("ragdoll",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, (args) =>
|
||||
{
|
||||
bool switchRagdoll = true;
|
||||
|
||||
if (args.Length > 0 && bool.TryParse(args[0], out bool state))
|
||||
{
|
||||
switchRagdoll = state != RagdollController.Instance.IsRagdolled();
|
||||
}
|
||||
|
||||
if (switchRagdoll)
|
||||
{
|
||||
RagdollController.Instance.SwitchRagdoll();
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, (args) =>
|
||||
{
|
||||
bool switchRagdoll = true;
|
||||
|
||||
if (args.Length > 1 && bool.TryParse(args[1], out bool state))
|
||||
{
|
||||
switchRagdoll = state != RagdollController.Instance.IsRagdolled();
|
||||
}
|
||||
|
||||
if (switchRagdoll)
|
||||
{
|
||||
RagdollController.Instance.SwitchRagdoll();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Commands.RegisterCommand("kill",
|
||||
onCommandSent: (message, sound, displayMsg) =>
|
||||
{
|
||||
LocalCommandIgnoreOthers(message, (args) =>
|
||||
{
|
||||
if (!RagdollController.Instance.IsRagdolled())
|
||||
{
|
||||
RagdollController.Instance.SwitchRagdoll();
|
||||
}
|
||||
});
|
||||
},
|
||||
onCommandReceived: (sender, message, sound, displayMsg) =>
|
||||
{
|
||||
RemoteCommandListenForAll(message, (args) =>
|
||||
{
|
||||
if (!RagdollController.Instance.IsRagdolled())
|
||||
{
|
||||
RagdollController.Instance.SwitchRagdoll();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
54
.Deprecated/ChatBoxExtensions/Main.cs
Normal file
54
.Deprecated/ChatBoxExtensions/Main.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using MelonLoader;
|
||||
using NAK.ChatBoxExtensions.InputModules;
|
||||
|
||||
namespace NAK.ChatBoxExtensions;
|
||||
|
||||
public class ChatBoxExtensions : MelonMod
|
||||
{
|
||||
internal static MelonLogger.Instance Logger;
|
||||
internal static InputModuleChatBoxExtensions InputModule = new();
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
Logger = LoggerInstance;
|
||||
|
||||
if (RegisteredMelons.All(it => it.Info.Name != "ChatBox"))
|
||||
{
|
||||
Logger.Error("ChatBox was not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyIntegrations();
|
||||
}
|
||||
|
||||
private void ApplyIntegrations()
|
||||
{
|
||||
|
||||
Integrations.Commands.InitializeCommandHandlers();
|
||||
Integrations.ChatBoxCommands.RegisterCommands();
|
||||
|
||||
Integrations.ChilloutVRBaseCommands.RegisterCommands();
|
||||
Integrations.ChilloutVRAASCommands.RegisterCommands();
|
||||
Integrations.ChilloutVRInputCommands.RegisterCommands();
|
||||
|
||||
ApplyPatches(typeof(HarmonyPatches.CVRInputManagerPatches));
|
||||
|
||||
if (RegisteredMelons.Any(it => it.Info.Name == "PlayerRagdollMod"))
|
||||
{
|
||||
Integrations.PlayerRagdollModCommands.RegisterCommands();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyPatches(Type type)
|
||||
{
|
||||
try
|
||||
{
|
||||
HarmonyInstance.PatchAll(type);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Msg($"Failed while patching {type.Name}!");
|
||||
Logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
33
.Deprecated/ChatBoxExtensions/Properties/AssemblyInfo.cs
Normal file
33
.Deprecated/ChatBoxExtensions/Properties/AssemblyInfo.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using ChatBoxExtensions.Properties;
|
||||
using MelonLoader;
|
||||
using System.Reflection;
|
||||
|
||||
|
||||
[assembly: AssemblyVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)]
|
||||
[assembly: AssemblyTitle(nameof(NAK.ChatBoxExtensions))]
|
||||
[assembly: AssemblyCompany(AssemblyInfoParams.Author)]
|
||||
[assembly: AssemblyProduct(nameof(NAK.ChatBoxExtensions))]
|
||||
|
||||
[assembly: MelonInfo(
|
||||
typeof(NAK.ChatBoxExtensions.ChatBoxExtensions),
|
||||
nameof(NAK.ChatBoxExtensions),
|
||||
AssemblyInfoParams.Version,
|
||||
AssemblyInfoParams.Author,
|
||||
downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ChatBoxExtensions"
|
||||
)]
|
||||
|
||||
[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")]
|
||||
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
|
||||
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
|
||||
[assembly: MelonAdditionalDependencies("ChatBox")]
|
||||
[assembly: MelonOptionalDependencies("PlayerRagdollMod")]
|
||||
[assembly: HarmonyDontPatchAll]
|
||||
|
||||
namespace ChatBoxExtensions.Properties;
|
||||
internal static class AssemblyInfoParams
|
||||
{
|
||||
public const string Version = "1.0.3";
|
||||
public const string Author = "NotAKidoS";
|
||||
}
|
23
.Deprecated/ChatBoxExtensions/format.json
Normal file
23
.Deprecated/ChatBoxExtensions/format.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"_id": 129,
|
||||
"name": "ChatBoxExtensions",
|
||||
"modversion": "1.0.1",
|
||||
"gameversion": "2022r170p1",
|
||||
"loaderversion": "0.6.1",
|
||||
"modtype": "Mod",
|
||||
"author": "NotAKidoS",
|
||||
"description": "Prevents VRIK from using toe bones in VR & optionaly FBT.\n\nVRIK calculates weird center of mass when toes are mapped, so it is sometimes desired to unmap toes to prevent an avatars feet from resting far back.\n\nPlease see the README for relevant imagery detailing the problem.",
|
||||
"searchtags": [
|
||||
"toes",
|
||||
"vrik",
|
||||
"ik",
|
||||
"feet"
|
||||
],
|
||||
"requirements": [
|
||||
"None"
|
||||
],
|
||||
"downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r3/ChatBoxExtensions.dll",
|
||||
"sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ChatBoxExtensions/",
|
||||
"changelog": "",
|
||||
"embedcolor": "#ffc700"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue