Files
HauntedBloodlines/Assets/Obi/Scripts/Cloth/Rendering/ObiClothProxy.cs
2025-05-29 22:31:40 +03:00

144 lines
5.3 KiB
C#

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Obi
{
/**
* This class allows a mesh (skinned or not) to follow the simulation performed by a different cloth object. The most
* common use case is having a high-poly mesh mimic the movement of a low-poly cloth simulation.
*/
[DisallowMultipleComponent]
[AddComponentMenu("Physics/Obi/Obi Cloth Proxy", 906)]
public class ObiClothProxy : MonoBehaviour
{
[SerializeField] [HideInInspector] private ObiClothRendererBase m_Master;
public ObiTriangleSkinMap skinMap;
private Mesh slaveMesh;
private Vector3[] slavePositions;
private Vector3[] slaveNormals;
private Vector4[] slaveTangents;
private MeshFilter meshFilter;
public ObiClothRendererBase master
{
set
{
if (m_Master != null)
m_Master.OnRendererUpdated -= UpdateSkinning;
m_Master = value;
if (m_Master != null && isActiveAndEnabled)
m_Master.OnRendererUpdated += UpdateSkinning;
}
get { return m_Master; }
}
public void OnEnable()
{
meshFilter = GetComponent<MeshFilter>();
if (m_Master != null)
m_Master.OnRendererUpdated += UpdateSkinning;
}
public void OnDisable()
{
if (m_Master != null)
m_Master.OnRendererUpdated -= UpdateSkinning;
}
private void GetSlaveMeshIfNeeded()
{
if (slaveMesh == null)
{
// If the proxy is used on the cloth itself, pick the mesh from the master renderer.
if (gameObject == m_Master.gameObject)
{
slaveMesh = m_Master.clothMesh;
}
else if (meshFilter != null)// Try to pick the mesh from a mesh filter.
{
slaveMesh = meshFilter.mesh;
}
}
}
public void UpdateSkinning(ObiActor actor)
{
GetSlaveMeshIfNeeded();
ObiClothBase masterCloth = m_Master.GetComponent<ObiClothBase>();
if (skinMap.bound && slaveMesh != null && masterCloth.isLoaded)
{
var solver = masterCloth.solver;
Matrix4x4 s2l;
if (gameObject == m_Master.gameObject)
s2l = m_Master.renderMatrix * solver.transform.localToWorldMatrix;
else
s2l = transform.worldToLocalMatrix * solver.transform.localToWorldMatrix;
slavePositions = slaveMesh.vertices;
slaveNormals = slaveMesh.normals;
slaveTangents = slaveMesh.tangents;
Vector3 skinnedPos = Vector3.zero;
Vector3 skinnedNormal = Vector3.zero;
Vector3 skinnedTangent = Vector3.zero;
for (int i = 0; i < skinMap.skinnedVertices.Count; ++i)
{
var data = skinMap.skinnedVertices[i];
int firstVertex = data.masterTriangleIndex;
int t1 = masterCloth.clothBlueprintBase.deformableTriangles[firstVertex];
int t2 = masterCloth.clothBlueprintBase.deformableTriangles[firstVertex + 1];
int t3 = masterCloth.clothBlueprintBase.deformableTriangles[firstVertex + 2];
// get solver indices for each particle:
int s1 = masterCloth.solverIndices[t1];
int s2 = masterCloth.solverIndices[t2];
int s3 = masterCloth.solverIndices[t3];
// get master particle positions/normals:
Vector3 p1 = solver.renderablePositions[s1];
Vector3 p2 = solver.renderablePositions[s2];
Vector3 p3 = solver.renderablePositions[s3];
Vector3 n1 = solver.normals[s1];
Vector3 n2 = solver.normals[s2];
Vector3 n3 = solver.normals[s3];
ObiVectorMath.BarycentricInterpolation(p1, p2, p3, n1, n2, n3, data.position.barycentricCoords, data.position.height, ref skinnedPos);
ObiVectorMath.BarycentricInterpolation(p1, p2, p3, n1, n2, n3, data.normal.barycentricCoords, data.normal.height, ref skinnedNormal);
ObiVectorMath.BarycentricInterpolation(p1, p2, p3, n1, n2, n3, data.tangent.barycentricCoords, data.tangent.height, ref skinnedTangent);
// update slave data arrays:
slavePositions[data.slaveIndex] = s2l.MultiplyPoint3x4(skinnedPos);
slaveNormals[data.slaveIndex] = s2l.MultiplyVector(skinnedNormal - skinnedPos);
Vector3 tangent = s2l.MultiplyVector(skinnedTangent - skinnedPos);
slaveTangents[data.slaveIndex] = new Vector4(tangent.x, tangent.y, tangent.z, slaveTangents[data.slaveIndex].w);
}
slaveMesh.vertices = slavePositions;
slaveMesh.normals = slaveNormals;
slaveMesh.tangents = slaveTangents;
slaveMesh.RecalculateBounds();
}
}
}
}