protected override void _SimulationStep(float timestep) { HairStrand[] strands = this.instance.strands.cpuReference; Vector3[] vertices = this.instance.vertices.cpuReference; uint[] movability = this.instance.movability.cpuReference; for (int s = 0; s < strands.Length; s++) { HairStrand strand = strands[s]; for (int j = strand.firstVertex + 1; j <= strand.lastVertex; j++) { if (!HairMovability.IsMovable(j, movability)) { continue; } float nDist = this.lengths[j]; Vector3 p = vertices[j], pPrev = vertices[j - 1], pDir = (p - pPrev); float dist = pDir.magnitude; float distDiff = (nDist - dist); vertices[j] = p + ((pDir / dist) * (distDiff * this.stiffness * timestep)); } } this.instance.vertices.SetGPUDirty(); }
private void CalculateTransforms(HairStrand[] strands, Vector3[] vertices, NativeArray <quaternion> localTransforms, NativeArray <quaternion> globalTransforms, NativeArray <float3> referenceVectors) { for (int i = 0; i < strands.Length; i++) { HairStrand strand = strands[i]; int j; // First vertex Quaternion local; local = globalTransforms[strand.firstVertex] = localTransforms[strand.firstVertex] = Quaternion.LookRotation((vertices[strand.firstVertex + 1] - vertices[strand.firstVertex]).normalized); for (j = strand.firstVertex + 1; j < strand.lastVertex; j++) { Vector3 p1 = vertices[j - 1], p2 = vertices[j], d = (p2 - p1); Vector3 vec = Quaternion.Inverse(globalTransforms[j - 1]) * d; if (vec.magnitude < 0.001f) { local = Quaternion.identity; } else { local = Quaternion.LookRotation(vec.normalized); } referenceVectors[j] = vec; globalTransforms[j] = globalTransforms[j - 1] * local; localTransforms[j] = local; } } }
public void Execute(int index) { HairStrand strand = this.strands[index]; quaternion rotGlobal = this.globalTransform[strand.firstVertex]; for (int j = strand.firstVertex + 1; j < strand.lastVertex - 1; j++) { quaternion rotGlobalWorld = math.mul(rotation, rotGlobal); float3 p1 = vertices[j], p2 = vertices[j + 1]; float3 orgP2 = math.mul(rotGlobalWorld, this.referenceVectors[j + 1]) + p1; float3 delta = stiffness * (orgP2 - p2); if (HairMovability.IsMovable(j, movability)) { p1 -= delta; } if (HairMovability.IsMovable(j + 1, movability)) { p2 += delta; } float3 vec = (p2 - p1); rotGlobal = math.mul(rotGlobal, quaternion.LookRotation(math.normalize(math.mul(math.inverse(rotGlobalWorld), vec)), new float3(0, 1, 0))); vertices[j] = p1; vertices[j + 1] = p2; } }
protected override void _SimulationStep(float timestep) { NativeArray <float3> vertices = this.instance.vertices.CpuReference; NativeArray <uint> movability = this.instance.movability.CpuReference; NativeArray <HairStrand> strands = this.instance.strands.CpuReference; Matrix4x4 matrix = this.transform.localToWorldMatrix; for (int j = 0; j < strands.Length; j++) { HairStrand strand = strands[j]; int target = Mathf.Min(strand.firstVertex + this.vertexRange, strand.lastVertex); for (int i = strand.firstVertex; i <= target; i++) { if (!HairMovability.IsMovable(i, movability)) { continue; } Vector3 iV = this.simulation.initialVertices[i], v = vertices[i]; iV = matrix.MultiplyPoint3x4(iV); Vector3 delta = (iV - v); vertices[i] = v + (delta * this.stiffness * timestep); } } this.instance.vertices.SetGPUDirty(); }
public override void InitializeSimulation() { this.lengths = new float[this.instance.vertexCount]; HairStrand[] strands = this.instance.strands.cpuReference; Vector3[] vertices = this.instance.vertices.cpuReference; for (int s = 0; s < strands.Length; s++) { HairStrand strand = strands[s]; for (int j = strand.firstVertex + 1; j < strand.lastVertex; j++) { this.lengths[j] = Vector3.Distance(vertices[j], vertices[j - 1]); } } }
public override void InitializeSimulation() { this.lengths = new NativeArray <float>(this.instance.vertexCount, Allocator.Persistent); NativeArray <HairStrand> strands = this.instance.strands.CpuReference; NativeArray <float3> vertices = this.instance.vertices.CpuReference; for (int s = 0; s < strands.Length; s++) { HairStrand strand = strands[s]; for (int j = strand.firstVertex + 1; j < strand.lastVertex; j++) { this.lengths[j] = math.distance(vertices[j], vertices[j - 1]); } } }
protected override void _SimulationStep(float timestep) { float stiffness = .5f * Mathf.Min(this.stiffness * timestep, .95f); HairStrand[] strands = this.instance.strands.cpuReference; Vector3[] vertices = this.instance.vertices.cpuReference; uint[] movability = this.instance.movability.cpuReference; Quaternion rotation = this.transform.rotation; // Apply local shape constraint for (int i = 0; i < strands.Length; i++) { HairStrand strand = strands[i]; Quaternion rotGlobal = this.globalTransform[strand.firstVertex]; realtimeDebug[strand.firstVertex] = rotGlobal; for (int j = strand.firstVertex + 1; j < strand.lastVertex - 1; j++) { Quaternion rotGlobalWorld = rotation * rotGlobal; Vector3 p1 = vertices[j], p2 = vertices[j + 1]; Vector3 orgP2 = rotGlobalWorld * this.referenceVectors[j + 1] + p1; Vector3 delta = stiffness * (orgP2 - p2); if (HairMovability.IsMovable(j, movability)) { p1 -= delta; } if (HairMovability.IsMovable(j + 1, movability)) { p2 += delta; } Vector3 vec = (p2 - p1); rotGlobal = rotGlobal * Quaternion.LookRotation((Quaternion.Inverse(rotGlobalWorld) * vec).normalized); vertices[j] = p1; vertices[j + 1] = p2; realtimeDebug[j] = rotGlobal; } realtimeDebug[strand.lastVertex] = rotGlobal; } this.instance.vertices.SetGPUDirty(); }
public void Execute(int index) { HairStrand strand = this.strands[index]; for (int j = strand.firstVertex + 1; j <= strand.lastVertex; j++) { if (!HairMovability.IsMovable(j, movability)) { continue; } float nDist = this.lengths[j]; float3 p = vertices[j], pPrev = vertices[j - 1], pDir = (p - pPrev); float dist = math.length(pDir); float distDiff = (nDist - dist); vertices[j] = p + ((pDir / dist) * (distDiff * this.stiffness * timestep)); } }
public void Execute(int j) { HairStrand strand = strands[j]; int target = Mathf.Min(strand.firstVertex + this.vertexRange, strand.lastVertex); for (int i = strand.firstVertex; i <= target; i++) { if (!HairMovability.IsMovable(i, movability)) { continue; } float3 iV = initialVertices[i], v = vertices[i]; float4 iV4 = new float4(iV.x, iV.y, iV.z, 1); iV = math.mul(matrix, iV4).xyz; float3 delta = (iV - v); vertices[i] = v + (delta * this.stiffness * timestep); } }
private void UpdateMeshInitial() { this._indices.Clear(); this.instance.strands.cpuReference.CopyTo(this._strands, 0); int sLen = _strands.Length; for (int i = 0; i < sLen; i++) { HairStrand strand = this._strands[i]; for (int j = strand.firstVertex; j < strand.lastVertex; j++) { this._indices.Add(j); this._indices.Add(j + 1); } } UpdateMesh(); mesh.SetIndices(this._indices.ToArray(), MeshTopology.Lines, 0); }
public void Execute(int index) { HairStrand strand = this.strands[index]; float3 lastFramePosWS, lastFramePosOS, posWS, newPos; lastFramePosOS = lastFramePosWS = posWS = newPos = new float3(); // First vertex is immovable float3 initialPos = initialVertices[strand.firstVertex]; vertices[strand.firstVertex] = math.mul(matrix, new float4(initialPos.x, initialPos.y, initialPos.z, 1)).xyz; for (int i = strand.firstVertex; i <= strand.lastVertex; i++) { if (!HairMovability.IsMovable(i, movability)) { continue; } lastFramePosWS = vertices[i]; float4 lastFramePosWS4 = new float4(lastFramePosWS.x, lastFramePosWS.y, lastFramePosWS.z, 1); lastFramePosOS = math.mul(invPrevMatrix, lastFramePosWS4).xyz; float4 lastFramePosOS4 = new float4(lastFramePosOS.x, lastFramePosOS.y, lastFramePosOS.z, 1); posWS = math.mul(matrix, lastFramePosOS4).xyz; // Unoptimized: // newPos = posWS + (lastFramePosWS - posWS) + (gravity * (timestep * timestep)); // Optimized version: newPos.x = posWS.x + (lastFramePosWS.x - posWS.x) + (gravity.x * timestepSqr); newPos.y = posWS.y + (lastFramePosWS.y - posWS.y) + (gravity.y * timestepSqr); newPos.z = posWS.z + (lastFramePosWS.z - posWS.z) + (gravity.z * timestepSqr); vertices[i] = newPos; } }
/// <summary> /// Allocates and returns a copy of <see cref="strands"/> /// </summary> public HairStrand[] GetStrandData() { HairStrand[] strands = new HairStrand[this.strands.Length]; System.Array.Copy(this.strands, strands, strands.Length); return(strands); }
public void Start() { List <FollowHairStrand> followHairs = new List <FollowHairStrand>(); List <TriangulatedSegment> segments = new List <TriangulatedSegment>(); // Triangulate var strands = this.instance.strands.cpuReference; { int vCtr = 0; for (int sIndex = 0; sIndex < strands.Length; sIndex++) { HairStrand strand = strands[sIndex]; for (int vIndex = strand.firstVertex; vIndex < strand.lastVertex - 1; vIndex++) { // Build segment TriangulatedSegment segment = new TriangulatedSegment() { hairVertexIndex1 = vIndex, hairVertexIndex2 = vIndex + 1, v1 = vCtr++, v2 = vCtr++, v3 = vCtr++, v4 = vCtr++, strandIndex = sIndex, strandVertexIndex1 = vIndex - strand.firstVertex, strandVertexIndex2 = (vIndex + 1) - strand.firstVertex }; segments.Add(segment); } } } this.segments = new NativeArray <TriangulatedSegment>(segments.Count, Allocator.Persistent); this.segments.CopyFrom(segments.ToArray()); // Distribute uv and indices this.uvs = new NativeArray <float3>(segments.Count * 4, Allocator.Persistent); this.normals = new NativeArray <float3>(segments.Count * 4, Allocator.Persistent); this.triVertices = new NativeArray <float3>(segments.Count * 4, Allocator.Persistent); this.indices = new NativeArray <int>(segments.Count * 6, Allocator.Persistent); for (int i = 0; i < segments.Count; i++) { var segment = segments[i]; // Calc uv float3 uv1, uv2, uv3, uv4; uv1 = uv2 = uv3 = uv4 = float3.zero; switch (this.uvDistStrat) { case UVDistributionStrategy.ALONG_STRAND_Y: { HairStrand hs = strands[segment.strandIndex]; float y = segment.strandVertexIndex1 / (hs.lastVertex - hs.firstVertex); float y2 = segment.strandVertexIndex2 / (hs.lastVertex - hs.firstVertex); uv1 = new float3(0, y, 0); uv2 = new float3(1, y, 0); uv3 = new float3(0, y2, 0); uv4 = new float3(1, y2, 0); } break; } this.uvs[segment.v1] = uv1; this.uvs[segment.v2] = uv2; this.uvs[segment.v3] = uv3; this.uvs[segment.v4] = uv4; // Write indices this.indices[(i * 6)] = segment.v1; this.indices[(i * 6) + 1] = segment.v2; this.indices[(i * 6) + 2] = segment.v4; this.indices[(i * 6) + 3] = segment.v4; this.indices[(i * 6) + 4] = segment.v3; this.indices[(i * 6) + 5] = segment.v1; } this.uvs.CopyFrom(uvs.ToArray()); this.indices.CopyFrom(indices.ToArray()); this.splineVertices = new NativeArray <float3>(this.instance.asset.vertexCount, Allocator.Persistent); NoAllocHelpers.SetMesh(this.mesh, this.triVertices, this.uvs, this.normals, this.indices); }
public void Start() { List <TriangulatedSegment> segments = new List <TriangulatedSegment>(); // Triangulate var strands = this.instance.strands.CpuReference; { int vCtr = 0; for (int sIndex = 0; sIndex < strands.Length; sIndex++) { HairStrand strand = strands[sIndex]; for (int vIndex = strand.firstVertex; vIndex < strand.lastVertex - 1; vIndex++) { // Build segment TriangulatedSegment segment = new TriangulatedSegment() { hairVertexIndex1 = vIndex, hairVertexIndex2 = vIndex + 1, v1 = vCtr++, v2 = vCtr++, v3 = vCtr++, v4 = vCtr++, strandIndex = sIndex, strandVertexIndex1 = vIndex - strand.firstVertex, strandVertexIndex2 = (vIndex + 1) - strand.firstVertex }; segments.Add(segment); } } } this.segments = new NativeArray <TriangulatedSegment>(segments.Count, Allocator.Persistent); this.segments.CopyFrom(segments.ToArray()); // Distribute uv and indices this.uvs = new NativeArray <float2>(segments.Count * 4, Allocator.Persistent); this.normals = new NativeArray <float3>(segments.Count * 4, Allocator.Persistent); this.triVertices = new NativeArray <float3>(segments.Count * 4, Allocator.Persistent); this.indices = new NativeArray <int>(segments.Count * 6, Allocator.Persistent); for (int i = 0; i < segments.Count; i++) { var segment = segments[i]; // Calc uv float2 uv1, uv2, uv3, uv4; uv1 = uv2 = uv3 = uv4 = float2.zero; switch (this.uvDistStrat) { case UVDistributionStrategy.ALONG_STRAND_Y: { HairStrand hs = strands[segment.strandIndex]; float y = segment.strandVertexIndex1 / (hs.lastVertex - hs.firstVertex); float y2 = segment.strandVertexIndex2 / (hs.lastVertex - hs.firstVertex); uv1 = new float2(0, y); uv2 = new float2(1, y); uv3 = new float2(0, y2); uv4 = new float2(1, y2); } break; } this.uvs[segment.v1] = uv1; this.uvs[segment.v2] = uv2; this.uvs[segment.v3] = uv3; this.uvs[segment.v4] = uv4; // Write indices this.indices[(i * 6)] = segment.v1; this.indices[(i * 6) + 1] = segment.v2; this.indices[(i * 6) + 2] = segment.v4; this.indices[(i * 6) + 3] = segment.v4; this.indices[(i * 6) + 4] = segment.v3; this.indices[(i * 6) + 5] = segment.v1; } this.uvs.CopyFrom(uvs.ToArray()); this.indices.CopyFrom(indices.ToArray()); this.splineVertices = new NativeArray <float3>(this.instance.asset.VertexCount, Allocator.Persistent); // Init mesh this.mesh.SetVertices <float3>(triVertices); this.mesh.SetUVs <float2>(0, uvs); this.mesh.SetNormals <float3>(normals); this.mesh.SetIndices <int>(indices, MeshTopology.Triangles, 0); LateUpdate(); this.mesh.RecalculateBounds(); this.mesh.bounds = new Bounds(this.mesh.bounds.center, Vector3.Scale(transform.lossyScale, this.mesh.bounds.size) * 3f); // Approximation, real bounding box calculation is difficult to do fast. }