mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 22:39:22 +00:00
[BetterShadowClone] Throwing on git before i completely overcomplicate and have to revert lmao
This commit is contained in:
parent
d155ea546e
commit
5ee7dca50b
18 changed files with 1697 additions and 0 deletions
11
BetterShadowClone/ShadowClone/IShadowClone/IShadowClone.cs
Normal file
11
BetterShadowClone/ShadowClone/IShadowClone/IShadowClone.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace NAK.BetterShadowClone;
|
||||
|
||||
public interface IShadowClone : IDisposable
|
||||
{
|
||||
bool IsValid { get; }
|
||||
bool Process();
|
||||
void RenderForShadow();
|
||||
void RenderForUiCulling();
|
||||
}
|
154
BetterShadowClone/ShadowClone/IShadowClone/MeshShadowClone.cs
Normal file
154
BetterShadowClone/ShadowClone/IShadowClone/MeshShadowClone.cs
Normal file
|
@ -0,0 +1,154 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NAK.BetterShadowClone;
|
||||
|
||||
public struct MeshShadowClone : IShadowClone
|
||||
{
|
||||
// We technically don't need a clone mesh for MeshRenderer shadow clone handling,
|
||||
// but as the shadows are also utilized for UI culling, we need to have a clone mesh.
|
||||
// If we don't stick with UI culling, we can just set the shadowCastingMode to ShadowsOnly when player camera renders.
|
||||
|
||||
// lame 2 frame init stuff
|
||||
private const int FrameInitCount = 0;
|
||||
private int _frameInitCounter;
|
||||
private bool _hasInitialized;
|
||||
|
||||
// shadow is used to cull ui, clone always exists
|
||||
private readonly bool _shouldCastShadows;
|
||||
private readonly MeshRenderer _mainMesh;
|
||||
private readonly MeshRenderer _shadowMesh;
|
||||
private readonly MeshFilter _shadowMeshFilter;
|
||||
|
||||
// material copying (unity is shit)
|
||||
private bool _hasShadowMaterials;
|
||||
private readonly Material[] _shadowMaterials;
|
||||
private readonly MaterialPropertyBlock _shadowMaterialBlock;
|
||||
|
||||
#region IShadowClone Methods
|
||||
|
||||
public bool IsValid => _mainMesh != null && _shadowMesh != null;
|
||||
|
||||
public MeshShadowClone(MeshRenderer meshRenderer)
|
||||
{
|
||||
_mainMesh = meshRenderer;
|
||||
MeshFilter _mainMeshFilter = meshRenderer.GetComponent<MeshFilter>();
|
||||
|
||||
if (_mainMesh == null
|
||||
|| _mainMesh.sharedMaterials == null
|
||||
|| _mainMesh.sharedMaterials.Length == 0
|
||||
|| _mainMeshFilter == null
|
||||
|| _mainMeshFilter.sharedMesh == null)
|
||||
{
|
||||
Dispose();
|
||||
return; // no mesh!
|
||||
}
|
||||
|
||||
_shouldCastShadows = _mainMesh.shadowCastingMode != ShadowCastingMode.Off;
|
||||
_mainMesh.shadowCastingMode = ShadowCastingMode.Off; // visual mesh doesn't cast shadows
|
||||
|
||||
(_shadowMesh, _shadowMeshFilter) = ShadowCloneManager.InstantiateShadowClone(_mainMesh);
|
||||
_shadowMesh.forceRenderingOff = true;
|
||||
|
||||
// material copying shit
|
||||
int materialCount = _mainMesh.sharedMaterials.Length;
|
||||
Material shadowMaterial = ShadowCloneHelper.shadowMaterial;
|
||||
|
||||
_shadowMaterialBlock = new MaterialPropertyBlock();
|
||||
_shadowMaterials = new Material[materialCount];
|
||||
for (int i = 0; i < materialCount; i++) _shadowMaterials[i] = shadowMaterial;
|
||||
}
|
||||
|
||||
public bool Process()
|
||||
{
|
||||
bool shouldRender = _mainMesh.enabled && _mainMesh.gameObject.activeInHierarchy;
|
||||
|
||||
// copying behaviour of SkinnedShadowClone, to visually be the same when a mesh toggles
|
||||
if (!shouldRender)
|
||||
{
|
||||
_frameInitCounter = 0;
|
||||
_hasInitialized = false;
|
||||
_shadowMesh.forceRenderingOff = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_frameInitCounter >= FrameInitCount)
|
||||
{
|
||||
if (_hasInitialized)
|
||||
return true;
|
||||
|
||||
_hasInitialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
_frameInitCounter++;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RenderForShadow()
|
||||
{
|
||||
_shadowMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
_shadowMesh.forceRenderingOff = !_shouldCastShadows;
|
||||
|
||||
// shadow casting needs clone to have original materials (uv discard)
|
||||
// we also want to respect material swaps... but this is fucking slow :(
|
||||
|
||||
if (!ShadowCloneManager.s_CopyMaterialsToShadow)
|
||||
return;
|
||||
|
||||
if (_hasShadowMaterials)
|
||||
{
|
||||
// NOTE: will not handle material swaps unless Avatar Overrender Ui is on
|
||||
_shadowMesh.sharedMaterials = _mainMesh.sharedMaterials;
|
||||
_hasShadowMaterials = false;
|
||||
}
|
||||
|
||||
UpdateCloneMaterialProperties();
|
||||
}
|
||||
|
||||
public void RenderForUiCulling()
|
||||
{
|
||||
_shadowMesh.shadowCastingMode = ShadowCastingMode.On;
|
||||
_shadowMesh.forceRenderingOff = false;
|
||||
|
||||
// UI culling needs clone to have write-to-depth shader
|
||||
if (_hasShadowMaterials) return;
|
||||
_shadowMesh.sharedMaterials = _shadowMaterials;
|
||||
_hasShadowMaterials = true;
|
||||
|
||||
// Not needed- MaterialPropertyBlock applied to renderer in RenderForShadow
|
||||
//UpdateCloneMaterialProperties();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_shadowMesh == null)
|
||||
return; // uh oh
|
||||
|
||||
// Cleanup instanced Mesh & Materials
|
||||
GameObject shadowMeshObject = _shadowMesh.gameObject;
|
||||
UnityEngine.Object.Destroy(_shadowMeshFilter.sharedMesh);
|
||||
UnityEngine.Object.Destroy(_shadowMeshFilter);
|
||||
if (!_hasShadowMaterials)
|
||||
{
|
||||
var materials = _shadowMesh.sharedMaterials;
|
||||
foreach (Material mat in materials) UnityEngine.Object.Destroy(mat);
|
||||
}
|
||||
UnityEngine.Object.Destroy(_shadowMesh);
|
||||
UnityEngine.Object.Destroy(shadowMeshObject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void UpdateCloneMaterialProperties()
|
||||
{
|
||||
// copy material properties to shadow clone materials
|
||||
_mainMesh.GetPropertyBlock(_shadowMaterialBlock);
|
||||
_shadowMesh.SetPropertyBlock(_shadowMaterialBlock);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
223
BetterShadowClone/ShadowClone/IShadowClone/SkinnedShadowClone.cs
Normal file
223
BetterShadowClone/ShadowClone/IShadowClone/SkinnedShadowClone.cs
Normal file
|
@ -0,0 +1,223 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NAK.BetterShadowClone;
|
||||
|
||||
public class SkinnedShadowClone : IShadowClone
|
||||
{
|
||||
private static readonly int s_SourceBufferId = Shader.PropertyToID("_sourceBuffer");
|
||||
private static readonly int s_TargetBufferId = Shader.PropertyToID("_targetBuffer");
|
||||
private static readonly int s_SourceBufferLayoutId = Shader.PropertyToID("_sourceBufferLayout");
|
||||
private static readonly int s_SourceRootMatrix = Shader.PropertyToID("_rootBoneMatrix");
|
||||
|
||||
// lame 2 frame init stuff
|
||||
private const int FrameInitCount = 0;
|
||||
private int _frameInitCounter;
|
||||
private bool _hasInitialized;
|
||||
|
||||
// shadow is used to cull ui, clone always exists
|
||||
private readonly bool _shouldCastShadows;
|
||||
private readonly SkinnedMeshRenderer _mainMesh;
|
||||
private readonly MeshRenderer _shadowMesh;
|
||||
private readonly MeshFilter _shadowMeshFilter;
|
||||
private readonly Transform _rootBone;
|
||||
|
||||
// clone copying
|
||||
private GraphicsBuffer _graphicsBuffer;
|
||||
private GraphicsBuffer _targetBuffer;
|
||||
private int _threadGroups;
|
||||
private int _bufferLayout;
|
||||
|
||||
// material copying (unity is shit)
|
||||
private bool _hasShadowMaterials;
|
||||
private readonly Material[] _shadowMaterials;
|
||||
private readonly MaterialPropertyBlock _shadowMaterialBlock;
|
||||
|
||||
#region IShadowClone Methods
|
||||
|
||||
// anything player can touch is suspect to death
|
||||
public bool IsValid => _mainMesh != null && _shadowMesh != null && _rootBone != null;
|
||||
|
||||
internal SkinnedShadowClone(SkinnedMeshRenderer renderer)
|
||||
{
|
||||
_mainMesh = renderer;
|
||||
|
||||
if (_mainMesh == null
|
||||
|| _mainMesh.sharedMesh == null
|
||||
|| _mainMesh.sharedMaterials == null
|
||||
|| _mainMesh.sharedMaterials.Length == 0)
|
||||
{
|
||||
Dispose();
|
||||
return; // no mesh!
|
||||
}
|
||||
|
||||
_shouldCastShadows = _mainMesh.shadowCastingMode != ShadowCastingMode.Off;
|
||||
_mainMesh.shadowCastingMode = ShadowCastingMode.Off; // visual mesh doesn't cast shadows
|
||||
|
||||
(_shadowMesh, _shadowMeshFilter) = ShadowCloneManager.InstantiateShadowClone(_mainMesh);
|
||||
_shadowMesh.forceRenderingOff = true;
|
||||
|
||||
_rootBone = _mainMesh.rootBone;
|
||||
_rootBone ??= _mainMesh.transform; // fallback to transform if no root bone
|
||||
|
||||
// material copying shit
|
||||
int materialCount = _mainMesh.sharedMaterials.Length;
|
||||
Material shadowMaterial = ShadowCloneHelper.shadowMaterial;
|
||||
|
||||
_shadowMaterialBlock = new MaterialPropertyBlock(); // TODO: check if we need one per material on renderer, idk if this is only first index
|
||||
_shadowMaterials = new Material[materialCount];
|
||||
for (int i = 0; i < materialCount; i++) _shadowMaterials[i] = shadowMaterial;
|
||||
}
|
||||
|
||||
public bool Process()
|
||||
{
|
||||
// some people animate renderer.enabled instead of gameObject.activeInHierarchy
|
||||
// do not disable shadow clone game object, it causes a flicker when re-enabled!
|
||||
bool shouldRender = _mainMesh.enabled && _mainMesh.gameObject.activeInHierarchy;
|
||||
|
||||
// GraphicsBuffer becomes stale when mesh is disabled
|
||||
if (!shouldRender)
|
||||
{
|
||||
_frameInitCounter = 0;
|
||||
_hasInitialized = false;
|
||||
_shadowMesh.forceRenderingOff = true; // force off if mesh is disabled
|
||||
return false; // TODO: dispose stale buffers
|
||||
}
|
||||
|
||||
// Unity is weird, so we need to wait 2 frames before we can get the graphics buffer
|
||||
if (_frameInitCounter >= FrameInitCount)
|
||||
{
|
||||
if (_hasInitialized)
|
||||
return true;
|
||||
|
||||
_hasInitialized = true;
|
||||
SetupGraphicsBuffer();
|
||||
return true;
|
||||
}
|
||||
|
||||
_frameInitCounter++;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RenderForShadow()
|
||||
{
|
||||
ResetShadowClone();
|
||||
RenderShadowClone();
|
||||
}
|
||||
|
||||
public void RenderForUiCulling()
|
||||
{
|
||||
ConfigureShadowCloneForUiCulling();
|
||||
RenderShadowClone();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_shadowMesh != null)
|
||||
{
|
||||
// Cleanup instanced Mesh & Materials
|
||||
GameObject shadowMeshObject = _shadowMesh.gameObject;
|
||||
UnityEngine.Object.Destroy(_shadowMeshFilter.sharedMesh);
|
||||
UnityEngine.Object.Destroy(_shadowMeshFilter);
|
||||
|
||||
if (!_hasShadowMaterials)
|
||||
{
|
||||
var materials = _shadowMesh.sharedMaterials;
|
||||
foreach (Material mat in materials) UnityEngine.Object.Destroy(mat);
|
||||
}
|
||||
|
||||
UnityEngine.Object.Destroy(_shadowMesh);
|
||||
UnityEngine.Object.Destroy(shadowMeshObject);
|
||||
}
|
||||
|
||||
_graphicsBuffer?.Dispose();
|
||||
_graphicsBuffer = null;
|
||||
_targetBuffer?.Dispose();
|
||||
_targetBuffer = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
// Unity is weird, so we need to wait 2 frames before we can get the graphics buffer
|
||||
private void SetupGraphicsBuffer()
|
||||
{
|
||||
Mesh mesh = _mainMesh.sharedMesh;
|
||||
Mesh shadowMesh = _shadowMesh.GetComponent<MeshFilter>().mesh;
|
||||
|
||||
_bufferLayout = 0;
|
||||
if (mesh.HasVertexAttribute(VertexAttribute.Position)) _bufferLayout += 3;
|
||||
if (mesh.HasVertexAttribute(VertexAttribute.Normal)) _bufferLayout += 3;
|
||||
if (mesh.HasVertexAttribute(VertexAttribute.Tangent)) _bufferLayout += 4;
|
||||
_bufferLayout *= 4; // 4 bytes per float
|
||||
|
||||
const float xThreadGroups = 64f;
|
||||
_threadGroups = Mathf.CeilToInt(mesh.vertexCount / xThreadGroups);
|
||||
|
||||
_mainMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
|
||||
shadowMesh.vertexBufferTarget |= GraphicsBuffer.Target.Raw;
|
||||
|
||||
_targetBuffer = shadowMesh.GetVertexBuffer(0);
|
||||
|
||||
//Debug.Log($"Initialized! BufferLayout: {_bufferLayout}, GraphicsBuffer: {_graphicsBuffer != null}, TargetBuffer: {_targetBuffer != null}");
|
||||
}
|
||||
|
||||
private void ResetShadowClone()
|
||||
{
|
||||
_shadowMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
_shadowMesh.forceRenderingOff = !_shouldCastShadows;
|
||||
|
||||
// shadow casting needs clone to have original materials (uv discard)
|
||||
// we also want to respect material swaps... but this is fucking slow :(
|
||||
|
||||
if (!ShadowCloneManager.s_CopyMaterialsToShadow)
|
||||
return;
|
||||
|
||||
if (_hasShadowMaterials)
|
||||
{
|
||||
_shadowMesh.sharedMaterials = _mainMesh.sharedMaterials;
|
||||
_hasShadowMaterials = false;
|
||||
}
|
||||
|
||||
UpdateCloneMaterialProperties();
|
||||
}
|
||||
|
||||
private void ConfigureShadowCloneForUiCulling()
|
||||
{
|
||||
_shadowMesh.shadowCastingMode = ShadowCastingMode.On;
|
||||
_shadowMesh.forceRenderingOff = false;
|
||||
|
||||
// UI culling needs clone to have write-to-depth shader
|
||||
if (_hasShadowMaterials) return;
|
||||
_shadowMesh.sharedMaterials = _shadowMaterials;
|
||||
_hasShadowMaterials = true;
|
||||
|
||||
// Not needed- MaterialPropertyBlock applied to renderer in RenderForShadow
|
||||
//UpdateCloneMaterialProperties();
|
||||
}
|
||||
|
||||
private void RenderShadowClone()
|
||||
{
|
||||
// thanks sdraw, i suck at matrix math
|
||||
Matrix4x4 rootMatrix = _mainMesh.localToWorldMatrix.inverse * Matrix4x4.TRS(_rootBone.position, _rootBone.rotation, Vector3.one);
|
||||
|
||||
_graphicsBuffer = _mainMesh.GetVertexBuffer();
|
||||
ShadowCloneHelper.shader.SetMatrix(s_SourceRootMatrix, rootMatrix);
|
||||
ShadowCloneHelper.shader.SetBuffer(0, s_SourceBufferId, _graphicsBuffer);
|
||||
ShadowCloneHelper.shader.SetBuffer(0, s_TargetBufferId, _targetBuffer);
|
||||
ShadowCloneHelper.shader.SetInt(s_SourceBufferLayoutId, _bufferLayout);
|
||||
ShadowCloneHelper.shader.Dispatch(0, _threadGroups, 1, 1);
|
||||
_graphicsBuffer.Release();
|
||||
}
|
||||
|
||||
private void UpdateCloneMaterialProperties()
|
||||
{
|
||||
// copy material properties to shadow clone materials
|
||||
_mainMesh.GetPropertyBlock(_shadowMaterialBlock);
|
||||
_shadowMesh.SetPropertyBlock(_shadowMaterialBlock);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
217
BetterShadowClone/ShadowClone/ShadowCloneManager.cs
Normal file
217
BetterShadowClone/ShadowClone/ShadowCloneManager.cs
Normal file
|
@ -0,0 +1,217 @@
|
|||
using System.Collections.Generic;
|
||||
using ABI_RC.Core;
|
||||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using MagicaCloth;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace NAK.BetterShadowClone;
|
||||
|
||||
public class ShadowCloneManager : MonoBehaviour
|
||||
{
|
||||
#region Singleton Implementation
|
||||
|
||||
private static ShadowCloneManager _instance;
|
||||
public static ShadowCloneManager Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance != null) return _instance;
|
||||
_instance = new GameObject("NAK.ShadowCloneManager").AddComponent<ShadowCloneManager>();
|
||||
DontDestroyOnLoad(_instance.gameObject);
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private const string ShadowClonePostfix = "_ShadowClone";
|
||||
//public const string CVRIgnoreForUiCulling = "CVRIgnoreForUiCulling"; // TODO: Shader Tag to ignore for UI culling?
|
||||
|
||||
// Game cameras
|
||||
private static Camera s_MainCamera;
|
||||
private static Camera s_UiCamera;
|
||||
|
||||
// Settings
|
||||
internal static bool s_CopyMaterialsToShadow = true;
|
||||
private static bool s_UseShadowToCullUi;
|
||||
private const string ShadowCullUiSettingName = "ExperimentalAvatarOverrenderUI";
|
||||
|
||||
// Implementation
|
||||
private bool _hasRenderedThisFrame;
|
||||
|
||||
// Shadow Clones
|
||||
private readonly List<IShadowClone> s_ShadowClones = new();
|
||||
public void AddShadowClone(IShadowClone clone)
|
||||
=> s_ShadowClones.Add(clone);
|
||||
|
||||
// Debug
|
||||
private bool _debugShadowProcessingTime;
|
||||
private readonly StopWatch _stopWatch = new();
|
||||
|
||||
#region Unity Events
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (Instance != null
|
||||
&& Instance != this)
|
||||
{
|
||||
Destroy(this);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatePlayerCameras();
|
||||
|
||||
s_CopyMaterialsToShadow = ModSettings.EntryCopyMaterialToShadow.Value;
|
||||
s_UseShadowToCullUi = MetaPort.Instance.settings.GetSettingsBool(ShadowCullUiSettingName);
|
||||
MetaPort.Instance.settings.settingBoolChanged.AddListener(OnSettingsBoolChanged);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
=> Camera.onPreCull += MyOnPreCull;
|
||||
|
||||
private void OnDisable()
|
||||
=> Camera.onPreCull -= MyOnPreCull;
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
MetaPort.Instance.settings.settingBoolChanged.RemoveListener(OnSettingsBoolChanged);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Shadow Clone Managment
|
||||
|
||||
private void Update()
|
||||
{
|
||||
_hasRenderedThisFrame = false;
|
||||
}
|
||||
|
||||
private void MyOnPreCull(Camera cam)
|
||||
{
|
||||
bool forceRenderForUiCull = s_UseShadowToCullUi && cam == s_UiCamera;
|
||||
if (_hasRenderedThisFrame && !forceRenderForUiCull)
|
||||
return;
|
||||
|
||||
_hasRenderedThisFrame = true;
|
||||
|
||||
_stopWatch.Start();
|
||||
|
||||
for (int i = s_ShadowClones.Count - 1; i >= 0; i--)
|
||||
{
|
||||
IShadowClone clone = s_ShadowClones[i];
|
||||
if (clone is not { IsValid: true })
|
||||
{
|
||||
clone?.Dispose();
|
||||
s_ShadowClones.RemoveAt(i);
|
||||
continue; // invalid or dead
|
||||
}
|
||||
|
||||
if (!clone.Process()) continue; // not ready yet or disabled
|
||||
|
||||
if (forceRenderForUiCull)
|
||||
clone.RenderForUiCulling(); // last cam to render
|
||||
else
|
||||
clone.RenderForShadow(); // first cam to render
|
||||
}
|
||||
|
||||
_stopWatch.Stop();
|
||||
if (_debugShadowProcessingTime) Debug.Log($"ShadowCloneManager.MyOnPreCull({forceRenderForUiCull}) took {_stopWatch.ElapsedMilliseconds}ms");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Game Events
|
||||
|
||||
public void OnAvatarCleared()
|
||||
{
|
||||
// Dispose all shadow clones BEFORE game unloads avatar
|
||||
// Otherwise we memory leak the shadow clones mesh & material instances!!!
|
||||
foreach (IShadowClone clone in s_ShadowClones)
|
||||
clone.Dispose();
|
||||
s_ShadowClones.Clear();
|
||||
}
|
||||
|
||||
private void OnSettingsBoolChanged(string settingName, bool settingValue)
|
||||
{
|
||||
if (settingName == ShadowCullUiSettingName)
|
||||
s_UseShadowToCullUi = settingValue;
|
||||
}
|
||||
|
||||
private void OnVRModeSwitchCompleted(bool _, Camera __)
|
||||
{
|
||||
UpdatePlayerCameras();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private static void UpdatePlayerCameras()
|
||||
{
|
||||
s_MainCamera = PlayerSetup.Instance.GetActiveCamera().GetComponent<Camera>();
|
||||
s_UiCamera = s_MainCamera.transform.Find("_UICamera").GetComponent<Camera>();
|
||||
//s_PortableCamera = PortableCamera.Instance.cameraComponent;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Helpers
|
||||
|
||||
internal static IShadowClone CreateShadowClone(Renderer renderer)
|
||||
{
|
||||
return renderer switch
|
||||
{
|
||||
SkinnedMeshRenderer skinnedMeshRenderer => new SkinnedShadowClone(skinnedMeshRenderer),
|
||||
MeshRenderer meshRenderer => new MeshShadowClone(meshRenderer),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
internal static (MeshRenderer, MeshFilter) InstantiateShadowClone(SkinnedMeshRenderer meshRenderer)
|
||||
{
|
||||
GameObject shadowClone = new (meshRenderer.name + ShadowClonePostfix) { layer = CVRLayers.PlayerClone };
|
||||
shadowClone.transform.SetParent(meshRenderer.transform, false);
|
||||
shadowClone.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
shadowClone.transform.localScale = Vector3.one;
|
||||
|
||||
MeshRenderer newMesh = shadowClone.AddComponent<MeshRenderer>();
|
||||
MeshFilter newMeshFilter = shadowClone.AddComponent<MeshFilter>();
|
||||
|
||||
ShadowCloneHelper.ConfigureRenderer(newMesh, true);
|
||||
|
||||
// only shadow clone should cast shadows
|
||||
newMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
|
||||
// copy mesh and materials
|
||||
newMeshFilter.sharedMesh = meshRenderer.sharedMesh;
|
||||
newMesh.sharedMaterials = meshRenderer.sharedMaterials;
|
||||
|
||||
return (newMesh, newMeshFilter);
|
||||
}
|
||||
|
||||
internal static (MeshRenderer, MeshFilter) InstantiateShadowClone(MeshRenderer meshRenderer)
|
||||
{
|
||||
GameObject shadowClone = new (meshRenderer.name + ShadowClonePostfix) { layer = CVRLayers.PlayerClone };
|
||||
shadowClone.transform.SetParent(meshRenderer.transform, false);
|
||||
shadowClone.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
shadowClone.transform.localScale = Vector3.one;
|
||||
|
||||
MeshRenderer newMesh = shadowClone.AddComponent<MeshRenderer>();
|
||||
MeshFilter newMeshFilter = shadowClone.AddComponent<MeshFilter>();
|
||||
|
||||
ShadowCloneHelper.ConfigureRenderer(newMesh, true);
|
||||
|
||||
// only shadow clone should cast shadows
|
||||
newMesh.shadowCastingMode = ShadowCastingMode.ShadowsOnly;
|
||||
|
||||
// copy mesh and materials
|
||||
newMeshFilter.sharedMesh = meshRenderer.GetComponent<MeshFilter>().sharedMesh;
|
||||
newMesh.sharedMaterials = meshRenderer.sharedMaterials;
|
||||
|
||||
return (newMesh, newMeshFilter);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue