using UnityEngine;
using Unity.Profiling;
using System.Collections;
using System.Collections.Generic;
namespace Obi
{
[AddComponentMenu("Physics/Obi/Obi SkinnedCloth", 902)]
[RequireComponent(typeof(SkinnedMeshRenderer))]
public class ObiSkinnedCloth : ObiClothBase, ITetherConstraintsUser, ISkinConstraintsUser
{
static ProfilerMarker m_SetTargetSkinPerfMarker = new ProfilerMarker("SetTargetSkin");
static ProfilerMarker m_SortSkinInputsPerfMarker = new ProfilerMarker("SortSkinInputs");
static ProfilerMarker m_SetSkinInputsPerfMarker = new ProfilerMarker("SetSkinInputs");
static ProfilerMarker m_BakeMeshPerfMarker = new ProfilerMarker("BakeMesh");
[SerializeField] protected ObiSkinnedClothBlueprint m_SkinnedClothBlueprint;
// tethers
[SerializeField] protected bool _tetherConstraintsEnabled = true;
[SerializeField] protected float _tetherCompliance = 0;
[SerializeField] [Range(0.1f, 2)] protected float _tetherScale = 1;
public override ObiActorBlueprint sourceBlueprint
{
get { return m_SkinnedClothBlueprint; }
}
public override ObiClothBlueprintBase clothBlueprintBase
{
get { return m_SkinnedClothBlueprint; }
}
///
/// Whether this actor's tether constraints are enabled.
///
public bool tetherConstraintsEnabled
{
get { return _tetherConstraintsEnabled; }
set { if (value != _tetherConstraintsEnabled) { _tetherConstraintsEnabled = value; SetConstraintsDirty(Oni.ConstraintType.Tether); } }
}
///
/// Compliance of this actor's tether constraints.
///
public float tetherCompliance
{
get { return _tetherCompliance; }
set { _tetherCompliance = value; SetConstraintsDirty(Oni.ConstraintType.Tether); }
}
///
/// Rest length scaling for this actor's tether constraints.
///
public float tetherScale
{
get { return _tetherScale; }
set { _tetherScale = value; SetConstraintsDirty(Oni.ConstraintType.Tether); }
}
public bool skinConstraintsEnabled { get { return true; } set { } }
public ObiSkinnedClothBlueprint skinnedClothBlueprint
{
get { return m_SkinnedClothBlueprint; }
set
{
if (m_SkinnedClothBlueprint != value)
{
RemoveFromSolver();
ClearState();
m_SkinnedClothBlueprint = value;
AddToSolver();
}
}
}
private SkinnedMeshRenderer skin;
[HideInInspector] public List bakedVertices = new List();
[HideInInspector] public List bakedNormals = new List();
[HideInInspector] public List bakedTangents = new List();
[HideInInspector] public Mesh bakedMesh; /**< Unique instance of the shared mesh, gets modified by the simulation*/
private Vector3[] sortedPoints;
private Vector3[] sortedNormals;
public override void LoadBlueprint(ObiSolver solver)
{
base.LoadBlueprint(solver);
var skinConstraints = GetConstraintsByType(Oni.ConstraintType.Skin) as ObiConstraints;
if (skinConstraints != null && skinConstraints.GetBatchCount() > 0)
{
var batch = skinConstraints.batches[0] as ObiSkinConstraintsBatch;
sortedPoints = new Vector3[batch.constraintCount];
sortedNormals = new Vector3[batch.constraintCount];
}
}
protected override void OnValidate()
{
base.OnValidate();
SetupRuntimeConstraints();
}
private void SetupRuntimeConstraints()
{
SetConstraintsDirty(Oni.ConstraintType.Distance);
SetConstraintsDirty(Oni.ConstraintType.Bending);
SetConstraintsDirty(Oni.ConstraintType.Aerodynamics);
SetConstraintsDirty(Oni.ConstraintType.Tether);
SetSelfCollisions(m_SelfCollisions);
SetSimplicesDirty();
UpdateCollisionMaterials();
}
protected override void OnEnable()
{
base.OnEnable();
skin = GetComponent();
bakedMesh = null;
CreateBakedMeshIfNeeded();
}
protected override void OnDisable()
{
base.OnDisable();
if (m_SkinnedClothBlueprint != null)
skin.sharedMesh = m_SkinnedClothBlueprint.inputMesh;
DestroyImmediate(bakedMesh);
}
public Vector3 GetSkinRadiiBackstop(ObiSkinConstraintsBatch batch, int constraintIndex)
{
return new Vector3(batch.skinRadiiBackstop[constraintIndex*3],
batch.skinRadiiBackstop[constraintIndex * 3+1],
batch.skinRadiiBackstop[constraintIndex * 3+2]);
}
public float GetSkinCompliance(ObiSkinConstraintsBatch batch, int constraintIndex)
{
return batch.skinCompliance[constraintIndex];
}
private void CreateBakedMeshIfNeeded()
{
if (bakedMesh == null && m_SkinnedClothBlueprint != null)
bakedMesh = Instantiate(m_SkinnedClothBlueprint.inputMesh);
}
private void SetTargetSkin()
{
using (m_SetTargetSkinPerfMarker.Auto())
{
var skinConstraints = GetConstraintsByType(Oni.ConstraintType.Skin) as ObiConstraints;
var batch = skinConstraints.batches[0] as ObiSkinConstraintsBatch;
var solverSkinConstraints = solver.GetConstraintsByType(Oni.ConstraintType.Skin) as ObiConstraints;
var solverBatch = solverSkinConstraints.batches[0] as ObiSkinConstraintsBatch;
using (m_SortSkinInputsPerfMarker.Auto())
{
int pointCount = bakedVertices.Count;
for (int i = 0; i < pointCount; ++i)
{
int welded = m_SkinnedClothBlueprint.topology.rawToWelded[i];
sortedPoints[welded] = bakedVertices[i];
sortedNormals[welded] = bakedNormals[i];
}
}
using (m_SetSkinInputsPerfMarker.Auto())
{
var skinScale = skin.transform.lossyScale;
var invSkinScale = Matrix4x4.Scale(new Vector3(1 / skinScale.x, 1 / skinScale.y, 1 / skinScale.z));
Matrix4x4 skinToSolver = actorLocalToSolverMatrix * invSkinScale;
int offset = solverBatchOffsets[(int)Oni.ConstraintType.Skin][0];
for (int i = 0; i < batch.activeConstraintCount; ++i)
{
int constraintIndex = offset + i;
int solverIndex = solverBatch.particleIndices[constraintIndex];
int actorIndex = solver.particleToActor[solverIndex].indexInActor;
solverBatch.skinPoints[constraintIndex] = skinToSolver.MultiplyPoint3x4(sortedPoints[actorIndex]);
solverBatch.skinNormals[constraintIndex] = skinToSolver.MultiplyVector(sortedNormals[actorIndex]);
// Rigidly transform particles with zero skin radius and zero compliance:
if (Mathf.Approximately(solverBatch.skinRadiiBackstop[constraintIndex * 3], 0) & Mathf.Approximately(solverBatch.skinCompliance[constraintIndex], 0))
{
solver.invMasses[solverIndex] = 0;
solver.positions[solverIndex] = solverBatch.skinPoints[constraintIndex];
}
}
}
}
}
public override void BeginStep(float stepTime)
{
base.BeginStep(stepTime);
CreateBakedMeshIfNeeded();
if (m_SkinnedClothBlueprint != null && bakedMesh != null && isLoaded)
{
using (m_BakeMeshPerfMarker.Auto())
{
// bake skinned vertices/normals:
skin.sharedMesh = m_SkinnedClothBlueprint.inputMesh;
skin.BakeMesh(bakedMesh);
bakedMesh.GetVertices(bakedVertices);
bakedMesh.GetNormals(bakedNormals);
bakedMesh.GetTangents(bakedTangents);
}
// update skin constraints / particles:
SetTargetSkin();
}
}
}
}