/** * Iterative version of the Ramer-Douglas-Peucker path decimation algorithm. */ private bool Decimate(ObiList <ObiPathFrame> input, ObiList <ObiPathFrame> output, float threshold) { // no decimation, no work to do, just return: if (threshold < 0.00001f || input.Count < 3) { return(false); } float scaledThreshold = threshold * threshold * 0.01f; stack.Push(new Vector2Int(0, input.Count - 1)); var bitArray = new BitArray(input.Count, true); while (stack.Count > 0) { var range = stack.Pop(); float dmax = 0; int index = range.x; float mu = 0; for (int i = index + 1; i < range.y; ++i) { if (bitArray[i]) { float d = Vector3.SqrMagnitude(ObiUtils.ProjectPointLine(input[i].position, input[range.x].position, input[range.y].position, out mu) - input[i].position); if (d > dmax) { index = i; dmax = d; } } } if (dmax > scaledThreshold) { stack.Push(new Vector2Int(range.x, index)); stack.Push(new Vector2Int(index, range.y)); } else { for (int i = range.x + 1; i < range.y; ++i) { bitArray[i] = false; } } } output.Clear(); for (int i = 0; i < bitArray.Count; ++i) { if (bitArray[i]) { output.Add(input[i]); } } return(true); }
public ObiPathFrame GetSectionAt(float mu) { float edgeMu = smoothSections * Mathf.Clamp01(mu); int index = (int)edgeMu; float sectionMu = edgeMu - index; int counter = 0; int chunkIndex = -1; int indexInChunk = -1; for (int i = 0; i < smoothChunks.Count; ++i) { if (counter + smoothChunks[i].Count > index) { chunkIndex = i; indexInChunk = index - counter; } counter += smoothChunks[i].Count; } ObiList <ObiPathFrame> chunk = smoothChunks[chunkIndex]; ObiPathFrame s1 = chunk[indexInChunk]; ObiPathFrame s2 = chunk[Mathf.Min(indexInChunk + 1, chunk.Count - 1)]; return((1 - sectionMu) * s1 + sectionMu * s2); }
private float CalculateChunkLength(ObiList <ObiPathFrame> chunk) { float length = 0; for (int i = 1; i < chunk.Count; ++i) { length += Vector3.Distance(chunk[i].position, chunk[i - 1].position); } return(length); }
protected float CalculateCurveLength(ObiList <ObiCurveSection> curve) { float length = 0; for (int i = 1; i < curve.Count; ++i) { length += Vector3.Distance(curve[i].positionAndRadius, curve[i - 1].positionAndRadius); } return(length); }
/** * This method uses a variant of Chainkin's algorithm to produce a smooth curve from a set of control points. It is specially fast * because it directly calculates subdivision level k, instead of recursively calculating levels 1..k. */ private void Chaikin(ObiList <ObiPathFrame> input, ObiList <ObiPathFrame> output, uint k) { // no subdivision levels, no work to do. just copy the input to the output: if (k == 0 || input.Count < 3) { output.SetCount(input.Count); for (int i = 0; i < input.Count; ++i) { output[i] = input[i]; } return; } // calculate amount of new points generated by each inner control point: int pCount = (int)Mathf.Pow(2, k); // precalculate some quantities: int n0 = input.Count - 1; float twoRaisedToMinusKPlus1 = Mathf.Pow(2, -(k + 1)); float twoRaisedToMinusK = Mathf.Pow(2, -k); float twoRaisedToMinus2K = Mathf.Pow(2, -2 * k); float twoRaisedToMinus2KMinus1 = Mathf.Pow(2, -2 * k - 1); // allocate ouput: output.SetCount((n0 - 1) * pCount + 2); // calculate initial curve points: output[0] = (0.5f + twoRaisedToMinusKPlus1) * input[0] + (0.5f - twoRaisedToMinusKPlus1) * input[1]; output[pCount * n0 - pCount + 1] = (0.5f - twoRaisedToMinusKPlus1) * input[n0 - 1] + (0.5f + twoRaisedToMinusKPlus1) * input[n0]; // calculate internal points: for (int j = 1; j <= pCount; ++j) { // precalculate coefficients: float F = 0.5f - twoRaisedToMinusKPlus1 - (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2KMinus1); float G = 0.5f + twoRaisedToMinusKPlus1 + (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2K); float H = (j - 1) * j * twoRaisedToMinus2KMinus1; for (int i = 1; i < n0; ++i) { ObiPathFrame.WeightedSum(F, G, H, ref input.Data[i - 1], ref input.Data[i], ref input.Data[i + 1], ref output.Data[(i - 1) * pCount + j]); } } // make first and last curve points coincide with original points: output[0] = input[0]; output[output.Count - 1] = input[input.Count - 1]; }
/** * Generate a list of smooth curves using particles as control points. Will take into account cuts in the rope, * generating one curve for each continuous piece of rope. */ public void SmoothCurvesFromParticles() { curves.Clear(); curveSections = 0; curveLength = 0; // count amount of segments in each rope chunk: CountContinuousSegments(); ObiDistanceConstraintBatch distanceBatch = DistanceConstraints.GetFirstBatch(); Matrix4x4 w2l = transform.worldToLocalMatrix; int firstSegment = 0; // generate curve for each rope chunk: for (int i = 0; i < rawCurves.Count; ++i) { int segments = rawCurves[i].Count - 1; // allocate memory for the curve: ObiList <CurveSection> controlPoints = rawCurves[i]; // get control points position: for (int m = 0; m < segments; ++m) { int particleIndex = distanceBatch.springIndices[(firstSegment + m) * 2]; controlPoints[m] = new CurveSection(w2l.MultiplyPoint3x4(GetParticlePosition(particleIndex)), solidRadii[particleIndex], (this.colors != null && particleIndex < this.colors.Length) ? this.colors[particleIndex] : Color.white); // last segment adds its second particle too: if (m == segments - 1) { particleIndex = distanceBatch.springIndices[(firstSegment + m) * 2 + 1]; controlPoints[m + 1] = new CurveSection(w2l.MultiplyPoint3x4(GetParticlePosition(particleIndex)), solidRadii[particleIndex], (this.colors != null && particleIndex < this.colors.Length) ? this.colors[particleIndex] : Color.white); } } firstSegment += segments; // get smooth curve points: ChaikinSmoothing(controlPoints, curves[i], smoothing); // count total curve sections and total curve length: curveSections += curves[i].Count - 1; curveLength += CalculateCurveLength(curves[i]); } }
// Update is called once per frame void UpdatePlugs(ObiActor actor) { var rope = actor as ObiRopeBase; // cache the rope's transform matrix/quaternion: Matrix4x4 l2w = rope.transform.localToWorldMatrix; Quaternion l2wRot = l2w.rotation; int instanceIndex = 0; // place prefabs at the start/end of each curve: for (int c = 0; c < smoother.smoothChunks.Count; ++c) { ObiList <ObiPathFrame> curve = smoother.smoothChunks[c]; if ((plugTears && c > 0) || (plugStart && c == 0)) { var instance = GetOrCreatePrefabInstance(instanceIndex++); instance.SetActive(true); ObiPathFrame frame = curve[0]; instance.transform.position = l2w.MultiplyPoint3x4(frame.position); instance.transform.rotation = l2wRot * (Quaternion.LookRotation(-frame.tangent, frame.binormal)); instance.transform.localScale = instanceScale; } if ((plugTears && c < smoother.smoothChunks.Count - 1) || (plugEnd && c == smoother.smoothChunks.Count - 1)) { var instance = GetOrCreatePrefabInstance(instanceIndex++); instance.SetActive(true); ObiPathFrame frame = curve[curve.Count - 1]; instance.transform.position = l2w.MultiplyPoint3x4(frame.position); instance.transform.rotation = l2wRot * (Quaternion.LookRotation(frame.tangent, frame.binormal)); instance.transform.localScale = instanceScale; } } // deactivate remaining instances: for (int i = instanceIndex; i < instances.Count; ++i) { instances[i].SetActive(false); } }
/** * Generate a list of smooth curves using particles as control points. Will take into account cuts in the rope, * generating one curve for each continuous piece of rope. */ public void SmoothCurvesFromParticles() { curves.Clear(); curveSections = 0; curveLength = 0; // count amount of segments in each rope chunk: CountContinuousSegments(); Matrix4x4 w2l = transform.worldToLocalMatrix; Quaternion matrixRotation = Quaternion.LookRotation( w2l.GetColumn(2), w2l.GetColumn(1)); int firstSegment = 0; // generate curve for each rope chunk: for (int i = 0; i < rawCurves.Count; ++i) { int segments = rawCurves[i].Count - 1; // allocate memory for the curve: ObiList <ObiCurveSection> controlPoints = rawCurves[i]; // get control points position: int lastParticle = -1; int particle1 = -1, particle2 = -1; for (int m = 0; m < segments; ++m) { if (GetStructuralConstraintParticles(firstSegment + m, ref particle1, ref particle2)) { if (m == 0) { lastParticle = particle1; } // Find next and previous vectors: Vector3 nextV = GetParticlePosition(particle2) - GetParticlePosition(particle1); Vector3 prevV = GetParticlePosition(particle1) - GetParticlePosition(lastParticle); Vector3 pos = w2l.MultiplyPoint3x4(GetParticlePosition(particle1)); Quaternion orient = matrixRotation * Quaternion.SlerpUnclamped(GetParticleOrientation(lastParticle), GetParticleOrientation(particle1), 0.5f); Vector3 tangent = w2l.MultiplyVector(prevV + nextV).normalized; Color color = (this.colors != null && particle1 < this.colors.Length) ? this.colors[particle1] : Color.white; controlPoints[m] = new ObiCurveSection(new Vector4(pos.x, pos.y, pos.z, principalRadii[particle1][0]), tangent, orient * Vector3.up, color); lastParticle = particle1; } } // last segment adds its second particle too: if (segments > 0) { Vector3 pos = w2l.MultiplyPoint3x4(GetParticlePosition(particle2)); Quaternion orient = matrixRotation * GetParticleOrientation(particle1); Vector3 tangent = w2l.MultiplyVector(GetParticlePosition(particle2) - GetParticlePosition(particle1)).normalized; Color color = (this.colors != null && particle2 < this.colors.Length) ? this.colors[particle2] : Color.white; controlPoints[segments] = new ObiCurveSection(new Vector4(pos.x, pos.y, pos.z, principalRadii[particle2][0]), tangent, orient * Vector3.up, color); } firstSegment += segments; // get smooth curve points: ObiCurveFrame.Chaikin(controlPoints, curves[i], smoothing); // count total curve sections and total curve length: curveSections += curves[i].Count - 1; curveLength += CalculateCurveLength(curves[i]); } }
public void UpdateRenderer(ObiActor actor) { using (m_UpdateExtrudedRopeRendererChunksPerfMarker.Auto()) { if (section == null) { return; } var rope = actor as ObiRopeBase; CreateMeshIfNeeded(); ClearMeshData(); int sectionIndex = 0; int sectionSegments = section.Segments; int verticesPerSection = sectionSegments + 1; // the last vertex in each section must be duplicated, due to uv wraparound. float vCoord = -uvScale.y * rope.restLength * uvAnchor; // v texture coordinate. float actualToRestLengthRatio = smoother.SmoothLength / rope.restLength; Vector3 vertex = Vector3.zero, normal = Vector3.zero; Vector4 texTangent = Vector4.zero; Vector2 uv = Vector2.zero; for (int c = 0; c < smoother.smoothChunks.Count; ++c) { ObiList <ObiPathFrame> curve = smoother.smoothChunks[c]; for (int i = 0; i < curve.Count; ++i) { // Calculate previous and next curve indices: int prevIndex = Mathf.Max(i - 1, 0); // advance v texcoord: vCoord += uvScale.y * (Vector3.Distance(curve.Data[i].position, curve.Data[prevIndex].position) / (normalizeV ? smoother.SmoothLength : actualToRestLengthRatio)); // calculate section thickness and scale the basis vectors by it: float sectionThickness = curve.Data[i].thickness * thicknessScale; // Loop around each segment: int nextSectionIndex = sectionIndex + 1; for (int j = 0; j <= sectionSegments; ++j) { // make just one copy of the section vertex: Vector2 sectionVertex = section.vertices[j]; // calculate normal using section vertex, curve normal and binormal: normal.x = (sectionVertex.x * curve.Data[i].normal.x + sectionVertex.y * curve.Data[i].binormal.x) * sectionThickness; normal.y = (sectionVertex.x * curve.Data[i].normal.y + sectionVertex.y * curve.Data[i].binormal.y) * sectionThickness; normal.z = (sectionVertex.x * curve.Data[i].normal.z + sectionVertex.y * curve.Data[i].binormal.z) * sectionThickness; // offset curve position by normal: vertex.x = curve.Data[i].position.x + normal.x; vertex.y = curve.Data[i].position.y + normal.y; vertex.z = curve.Data[i].position.z + normal.z; // cross(normal, curve tangent) texTangent.x = normal.y * curve.Data[i].tangent.z - normal.z * curve.Data[i].tangent.y; texTangent.y = normal.z * curve.Data[i].tangent.x - normal.x * curve.Data[i].tangent.z; texTangent.z = normal.x * curve.Data[i].tangent.y - normal.y * curve.Data[i].tangent.x; texTangent.w = -1; uv.x = (j / (float)sectionSegments) * uvScale.x; uv.y = vCoord; vertices.Add(vertex); normals.Add(normal); tangents.Add(texTangent); vertColors.Add(curve.Data[i].color); uvs.Add(uv); if (j < sectionSegments && i < curve.Count - 1) { tris.Add(sectionIndex * verticesPerSection + j); tris.Add(nextSectionIndex * verticesPerSection + j); tris.Add(sectionIndex * verticesPerSection + (j + 1)); tris.Add(sectionIndex * verticesPerSection + (j + 1)); tris.Add(nextSectionIndex * verticesPerSection + j); tris.Add(nextSectionIndex * verticesPerSection + (j + 1)); } } sectionIndex++; } } CommitMeshData(); } }
public void UpdateRenderer(ObiActor actor) { using (m_UpdateMeshRopeRendererChunksPerfMarker.Auto()) { if (mesh == null) { return; } if (smoother.smoothChunks.Count == 0) { return; } ObiList <ObiPathFrame> curve = smoother.smoothChunks[0]; if (curve.Count < 2) { return; } var rope = actor as ObiRopeBase; float actualToRestLengthRatio = stretchWithRope ? smoother.SmoothLength / rope.restLength : 1; // squashing factor, makes mesh thinner when stretched and thicker when compresssed. float squashing = Mathf.Clamp(1 + volumeScaling * (1 / Mathf.Max(actualToRestLengthRatio, 0.01f) - 1), 0.01f, 2); // Calculate scale along swept axis so that the mesh spans the entire lenght of the rope if required. Vector3 actualScale = scale; if (spanEntireLength) { actualScale[(int)axis] = rope.restLength / meshSizeAlongAxis; } float previousVertexValue = 0; float meshLength = 0; int index = 0; int nextIndex = 1; int prevIndex = 0; float sectionMagnitude = Vector3.Distance(curve[index].position, curve[nextIndex].position); // basis matrix for deforming the mesh: Matrix4x4 basis = curve[0].ToMatrix(axis); for (int i = 0; i < orderedVertices.Length; ++i) { int vIndex = orderedVertices[i]; float vertexValue = inputVertices[vIndex][(int)axis] * actualScale[(int)axis] + offset; // Calculate how much we've advanced in the sort axis since the last vertex: meshLength += (vertexValue - previousVertexValue) * actualToRestLengthRatio; previousVertexValue = vertexValue; // If we have advanced to the next section of the curve: while (meshLength > sectionMagnitude && sectionMagnitude > Mathf.Epsilon) { meshLength -= sectionMagnitude; index = Mathf.Min(index + 1, curve.Count - 1); // Calculate previous and next curve indices: nextIndex = Mathf.Min(index + 1, curve.Count - 1); prevIndex = Mathf.Max(index - 1, 0); // Calculate current tangent as the vector between previous and next curve points: sectionMagnitude = Vector3.Distance(curve[index].position, curve[nextIndex].position); // Update basis matrix: basis = curve[index].ToMatrix(axis); } float sectionThickness = curve[index].thickness; // calculate deformed vertex position: Vector3 offsetFromCurve = Vector3.Scale(inputVertices[vIndex], actualScale * sectionThickness * squashing); offsetFromCurve[(int)axis] = meshLength; vertices[vIndex] = curve[index].position + basis.MultiplyVector(offsetFromCurve); normals[vIndex] = basis.MultiplyVector(inputNormals[vIndex]); tangents[vIndex] = basis * inputTangents[vIndex]; // avoids expensive implicit conversion from Vector4 to Vector3. tangents[vIndex].w = inputTangents[vIndex].w; } CommitMeshData(); } }
public override void UpdateRenderer(object sender, EventArgs e) { if (section == null || extrudedMesh == null) { return; } rope.SmoothCurvesFromParticles(); CreateMeshIfNeeded(); ClearMeshData(); float actualToRestLengthRatio = rope.SmoothLength / rope.RestLength; int sectionSegments = section.Segments; int verticesPerSection = sectionSegments + 1; // the last vertex in each section must be duplicated, due to uv wraparound. float vCoord = -uvScale.y * rope.RestLength * uvAnchor; // v texture coordinate. int sectionIndex = 0; int tearCount = 0; // we will define and transport a reference frame along the curve using parallel transport method: if (frame == null) { frame = new ObiRope.CurveFrame(); } frame.Reset(); frame.SetTwist(-sectionTwist * rope.SmoothSections * uvAnchor); // for closed curves, last frame of the last curve must be equal to first frame of first curve. Vector3 firstTangent = Vector3.forward; Vector4 texTangent = Vector4.zero; Vector2 uv = Vector2.zero; for (int c = 0; c < rope.curves.Count; ++c) { ObiList <ObiRope.CurveSection> curve = rope.curves[c]; // Reinitialize frame for each curve. frame.Reset(); for (int i = 0; i < curve.Count; ++i) { // Calculate previous and next curve indices: //int nextIndex = Mathf.Min(i+1,curve.Count-1); int prevIndex = Mathf.Max(i - 1, 0); // Calculate current tangent as the vector between previous and next curve points: //Vector3 nextV; // The next tangent of the last segment of the last curve in a closed rope, is the first tangent again: /*if (rope.Closed && c == rope.curves.Count-1 && i == curve.Count-1 ) * nextV = firstTangent; * else * nextV = curve[nextIndex].positionAndRadius - curve[i].positionAndRadius;*/ //Vector3 prevV = curve[i].positionAndRadius - curve[prevIndex].positionAndRadius; //Vector3 tangent = nextV + prevV; // update frame: rope.TransportFrame(frame, curve[i], sectionTwist); //curve[i].positionAndRadius,tangent,rope.sectionTwist); // update tear prefabs (first segment of not first curve, last segment of not last curve) if (c > 0 && i == 0) { rope.UpdateTearPrefab(frame, ref tearCount, false); } if (c < rope.curves.Count - 1 && i == curve.Count - 1) { rope.UpdateTearPrefab(frame, ref tearCount, true); } // update start/end prefabs: if (c == 0 && i == 0) { // store first tangent of the first curve (for closed ropes): firstTangent = frame.tangent; if (rope.startPrefabInstance != null && !rope.Closed) { rope.PlaceObjectAtCurveFrame(frame, rope.startPrefabInstance, Space.Self, false); } } else if (c == rope.curves.Count - 1 && i == curve.Count - 1 && rope.endPrefabInstance != null && !rope.Closed) { rope.PlaceObjectAtCurveFrame(frame, rope.endPrefabInstance, Space.Self, true); } // advance v texcoord: vCoord += uvScale.y * (Vector3.Distance(curve[i].positionAndRadius, curve[prevIndex].positionAndRadius) / (normalizeV ? rope.SmoothLength : actualToRestLengthRatio)); // calculate section thickness (either constant, or particle radius based): float sectionThickness = (rope.thicknessFromParticles ? curve[i].positionAndRadius.w : rope.thickness) * sectionThicknessScale; // Loop around each segment: for (int j = 0; j <= sectionSegments; ++j) { vertices.Add(frame.position + (section.vertices[j].x * frame.normal + section.vertices[j].y * frame.binormal) * sectionThickness); normals.Add(vertices[vertices.Count - 1] - frame.position); texTangent = -Vector3.Cross(normals[normals.Count - 1], frame.tangent); texTangent.w = 1; tangents.Add(texTangent); vertColors.Add(curve[i].color); uv.Set((j / (float)sectionSegments) * uvScale.x, vCoord); uvs.Add(uv); if (j < sectionSegments && i < curve.Count - 1) { tris.Add(sectionIndex * verticesPerSection + j); tris.Add(sectionIndex * verticesPerSection + (j + 1)); tris.Add((sectionIndex + 1) * verticesPerSection + j); tris.Add(sectionIndex * verticesPerSection + (j + 1)); tris.Add((sectionIndex + 1) * verticesPerSection + (j + 1)); tris.Add((sectionIndex + 1) * verticesPerSection + j); } } sectionIndex++; } } CommitMeshData(); }
public void UpdateRenderer(Camera camera) { if (camera == null || !rope.gameObject.activeInHierarchy) { return; } rope.SmoothCurvesFromParticles(); CreateMeshIfNeeded(); ClearMeshData(); float actualToRestLengthRatio = rope.SmoothLength / rope.RestLength; float vCoord = -uvScale.y * rope.RestLength * uvAnchor; // v texture coordinate. int sectionIndex = 0; int tearCount = 0; Vector3 localSpaceCamera = rope.transform.InverseTransformPoint(camera.transform.position); // we will define and transport a reference frame along the curve using parallel transport method: if (frame == null) { frame = new ObiRope.CurveFrame(); } frame.Reset(); // for closed curves, last frame of the last curve must be equal to first frame of first curve. Vector3 firstTangent = Vector3.forward; Vector4 texTangent = Vector4.zero; Vector2 uv = Vector2.zero; for (int c = 0; c < rope.curves.Count; ++c) { ObiList <ObiRope.CurveSection> curve = rope.curves[c]; // Reinitialize frame for each curve. frame.Reset(); for (int i = 0; i < curve.Count; ++i) { // Calculate previous and next curve indices: //int nextIndex = Mathf.Min(i+1,curve.Count-1); int prevIndex = Mathf.Max(i - 1, 0); // Calculate current tangent as the vector between previous and next curve points: /*Vector3 nextV; * * // The next tangent of the last segment of the last curve in a closed rope, is the first tangent again: * if (rope.Closed && c == rope.curves.Count-1 && i == curve.Count-1 ) * nextV = firstTangent; * else * nextV = curve[nextIndex].positionAndRadius - curve[i].positionAndRadius; * * Vector3 prevV = curve[i].positionAndRadius - curve[prevIndex].positionAndRadius; * Vector3 tangent = nextV + prevV;*/ // update frame: frame.Transport(curve[i], 0); // update tear prefabs: if (c > 0 && i == 0) { rope.UpdateTearPrefab(frame, ref tearCount, false); } if (c < rope.curves.Count - 1 && i == curve.Count - 1) { rope.UpdateTearPrefab(frame, ref tearCount, true); } // update start/end prefabs: if (c == 0 && i == 0) { // store first tangent of the first curve (for closed ropes): firstTangent = frame.tangent; if (rope.startPrefabInstance != null && !rope.Closed) { rope.PlaceObjectAtCurveFrame(frame, rope.startPrefabInstance, Space.Self, false); } } else if (c == rope.curves.Count - 1 && i == curve.Count - 1 && rope.endPrefabInstance != null && !rope.Closed) { rope.PlaceObjectAtCurveFrame(frame, rope.endPrefabInstance, Space.Self, true); } // advance v texcoord: vCoord += uvScale.y * (Vector3.Distance(curve[i].positionAndRadius, curve[prevIndex].positionAndRadius) / (normalizeV?rope.SmoothLength:actualToRestLengthRatio)); // calculate section thickness (either constant, or particle radius based): float sectionThickness = (rope.thicknessFromParticles ? curve[i].positionAndRadius.w : rope.thickness) * sectionThicknessScale; Vector3 normal = frame.position - localSpaceCamera; normal.Normalize(); Vector3 bitangent = Vector3.Cross(frame.tangent, normal); bitangent.Normalize(); vertices.Add(frame.position + bitangent * sectionThickness); vertices.Add(frame.position - bitangent * sectionThickness); normals.Add(-normal); normals.Add(-normal); texTangent = -bitangent; texTangent.w = 1; tangents.Add(texTangent); tangents.Add(texTangent); vertColors.Add(curve[i].color); vertColors.Add(curve[i].color); uv.Set(0, vCoord); uvs.Add(uv); uv.Set(1, vCoord); uvs.Add(uv); if (i < curve.Count - 1) { tris.Add(sectionIndex * 2); tris.Add(sectionIndex * 2 + 1); tris.Add((sectionIndex + 1) * 2); tris.Add(sectionIndex * 2 + 1); tris.Add((sectionIndex + 1) * 2 + 1); tris.Add((sectionIndex + 1) * 2); } sectionIndex++; } } CommitMeshData(); }
public void UpdateRenderer(Camera camera) { using (m_UpdateLineRopeRendererChunksPerfMarker.Auto()) { if (camera == null || !rope.gameObject.activeInHierarchy) { return; } CreateMeshIfNeeded(); ClearMeshData(); float actualToRestLengthRatio = smoother.SmoothLength / rope.restLength; float vCoord = -uvScale.y * rope.restLength * uvAnchor; // v texture coordinate. int sectionIndex = 0; Vector3 localSpaceCamera = rope.transform.InverseTransformPoint(camera.transform.position); Vector3 vertex = Vector3.zero, normal = Vector3.zero; Vector4 bitangent = Vector4.zero; Vector2 uv = Vector2.zero; for (int c = 0; c < smoother.smoothChunks.Count; ++c) { ObiList <ObiPathFrame> curve = smoother.smoothChunks[c]; for (int i = 0; i < curve.Count; ++i) { // Calculate previous and next curve indices: int prevIndex = Mathf.Max(i - 1, 0); // advance v texcoord: vCoord += uvScale.y * (Vector3.Distance(curve.Data[i].position, curve.Data[prevIndex].position) / (normalizeV ? smoother.SmoothLength : actualToRestLengthRatio)); // calculate section thickness (either constant, or particle radius based): float sectionThickness = curve.Data[i].thickness * thicknessScale; normal.x = curve.Data[i].position.x - localSpaceCamera.x; normal.y = curve.Data[i].position.y - localSpaceCamera.y; normal.z = curve.Data[i].position.z - localSpaceCamera.z; normal.Normalize(); bitangent.x = -(normal.y * curve.Data[i].tangent.z - normal.z * curve.Data[i].tangent.y); bitangent.y = -(normal.z * curve.Data[i].tangent.x - normal.x * curve.Data[i].tangent.z); bitangent.z = -(normal.x * curve.Data[i].tangent.y - normal.y * curve.Data[i].tangent.x); bitangent.w = 0; bitangent.Normalize(); vertex.x = curve.Data[i].position.x - bitangent.x * sectionThickness; vertex.y = curve.Data[i].position.y - bitangent.y * sectionThickness; vertex.z = curve.Data[i].position.z - bitangent.z * sectionThickness; vertices.Add(vertex); vertex.x = curve.Data[i].position.x + bitangent.x * sectionThickness; vertex.y = curve.Data[i].position.y + bitangent.y * sectionThickness; vertex.z = curve.Data[i].position.z + bitangent.z * sectionThickness; vertices.Add(vertex); normals.Add(-normal); normals.Add(-normal); bitangent.w = 1; tangents.Add(bitangent); tangents.Add(bitangent); vertColors.Add(curve.Data[i].color); vertColors.Add(curve.Data[i].color); uv.x = 0; uv.y = vCoord; uvs.Add(uv); uv.x = 1; uvs.Add(uv); if (i < curve.Count - 1) { tris.Add(sectionIndex * 2); tris.Add((sectionIndex + 1) * 2); tris.Add(sectionIndex * 2 + 1); tris.Add(sectionIndex * 2 + 1); tris.Add((sectionIndex + 1) * 2); tris.Add((sectionIndex + 1) * 2 + 1); } sectionIndex++; } } CommitMeshData(); } }
public override void Update(Camera camera) { if (mesh == null || rope.ropeMesh == null) { return; } rope.SmoothCurvesFromParticles(); if (rope.curves.Count == 0) { return; } ObiList <ObiRope.CurveSection> curve = rope.curves[0]; if (curve.Count < 2) { return; } float actualToRestLengthRatio = stretchWithRope ? rope.SmoothLength / rope.RestLength : 1; // Calculate scale along swept axis so that the mesh spans the entire lenght of the rope if required. Vector3 actualScale = scale; if (spanEntireLength) { actualScale[(int)axis] = rope.RestLength / meshSizeAlongAxis; } float previousVertexValue = 0; float meshLength = 0; int index = 0; int nextIndex = 1; int prevIndex = 0; Vector3 nextV = curve[nextIndex].positionAndRadius - curve[index].positionAndRadius; Vector3 prevV = curve[index].positionAndRadius - curve[prevIndex].positionAndRadius; Vector3 tangent = (nextV + prevV).normalized; float sectionMagnitude = nextV.magnitude; // we will define and transport a reference frame along the curve using parallel transport method: if (frame == null) { frame = new ObiRope.CurveFrame(); } frame.Reset(); frame.SetTwistAndTangent(-rope.sectionTwist * rope.SmoothSections * rope.uvAnchor, tangent); // set frame's initial position: frame.position = curve[index].positionAndRadius; // basis matrix for deforming the mesh, also calculate column offsets based on swept axis: Matrix4x4 basis = new Matrix4x4(); int xo = ((int)axis) % 3 * 4; int yo = ((int)axis + 1) % 3 * 4; int zo = ((int)axis + 2) % 3 * 4; basis[xo] = frame.tangent[0]; basis[xo + 1] = frame.tangent[1]; basis[xo + 2] = frame.tangent[2]; basis[yo] = frame.normal[0]; basis[yo + 1] = frame.normal[1]; basis[yo + 2] = frame.normal[2]; basis[zo] = frame.binormal[0]; basis[zo + 1] = frame.binormal[1]; basis[zo + 2] = frame.binormal[2]; for (int i = 0; i < orderedVertices.Length; ++i) { int vIndex = orderedVertices[i]; float vertexValue = inputVertices[vIndex][(int)axis] * actualScale[(int)axis] + offset; // Calculate how much we've advanced in the sort axis since the last vertex: meshLength += (vertexValue - previousVertexValue) * actualToRestLengthRatio; previousVertexValue = vertexValue; // If we have advanced to the next section of the curve: while (meshLength > sectionMagnitude && sectionMagnitude > Mathf.Epsilon) { meshLength -= sectionMagnitude; index = Mathf.Min(index + 1, curve.Count - 1); // Calculate previous and next curve indices: nextIndex = Mathf.Min(index + 1, curve.Count - 1); prevIndex = Mathf.Max(index - 1, 0); // Calculate current tangent as the vector between previous and next curve points: nextV = curve[nextIndex].positionAndRadius - curve[index].positionAndRadius; prevV = curve[index].positionAndRadius - curve[prevIndex].positionAndRadius; tangent = (nextV + prevV).normalized; sectionMagnitude = nextV.magnitude; // Transport frame: frame.Transport(curve[index].positionAndRadius, tangent, rope.sectionTwist); // Update basis matrix: basis[xo] = frame.tangent[0]; basis[xo + 1] = frame.tangent[1]; basis[xo + 2] = frame.tangent[2]; basis[yo] = frame.normal[0]; basis[yo + 1] = frame.normal[1]; basis[yo + 2] = frame.normal[2]; basis[zo] = frame.binormal[0]; basis[zo + 1] = frame.binormal[1]; basis[zo + 2] = frame.binormal[2]; } // calculate deformed vertex position: Vector3 offsetFromCurve = Vector3.Scale(inputVertices[vIndex], actualScale); offsetFromCurve[(int)axis] = meshLength; vertices[vIndex] = frame.position + basis.MultiplyVector(offsetFromCurve); normals[vIndex] = basis.MultiplyVector(inputNormals[vIndex]); tangents[vIndex] = basis * inputTangents[vIndex]; // avoids expensive implicit conversion from Vector4 to Vector3. tangents[vIndex].w = inputTangents[vIndex].w; } CommitMeshData(); }
public void UpdateRenderer(Camera camera) { using (m_UpdateLineRopeRendererChunksPerfMarker.Auto()) { if (camera == null || !rope.gameObject.activeInHierarchy) { return; } CreateMeshIfNeeded(); ClearMeshData(); float actualToRestLengthRatio = smoother.SmoothLength / rope.restLength; float vCoord = -uvScale.y * rope.restLength * uvAnchor; // v texture coordinate. int sectionIndex = 0; Vector3 localSpaceCamera = rope.transform.InverseTransformPoint(camera.transform.position); // for closed curves, last frame of the last curve must be equal to first frame of first curve. Vector3 firstTangent = Vector3.forward; Vector4 texTangent = Vector4.zero; Vector2 uv = Vector2.zero; for (int c = 0; c < smoother.smoothChunks.Count; ++c) { ObiList <ObiPathFrame> curve = smoother.smoothChunks[c]; for (int i = 0; i < curve.Count; ++i) { // Calculate previous and next curve indices: int prevIndex = Mathf.Max(i - 1, 0); // update start/end prefabs: if (c == 0 && i == 0) { // store first tangent of the first curve (for closed ropes): firstTangent = curve[i].tangent; } // advance v texcoord: vCoord += uvScale.y * (Vector3.Distance(curve[i].position, curve[prevIndex].position) / (normalizeV ? smoother.SmoothLength : actualToRestLengthRatio)); // calculate section thickness (either constant, or particle radius based): float sectionThickness = curve[i].thickness * thicknessScale; Vector3 normal = curve[i].position - localSpaceCamera; normal.Normalize(); Vector3 bitangent = Vector3.Cross(normal, curve[i].tangent); bitangent.Normalize(); vertices.Add(curve[i].position + bitangent * sectionThickness); vertices.Add(curve[i].position - bitangent * sectionThickness); normals.Add(-normal); normals.Add(-normal); texTangent = -bitangent; texTangent.w = 1; tangents.Add(texTangent); tangents.Add(texTangent); vertColors.Add(curve[i].color); vertColors.Add(curve[i].color); uv.Set(0, vCoord); uvs.Add(uv); uv.Set(1, vCoord); uvs.Add(uv); if (i < curve.Count - 1) { tris.Add(sectionIndex * 2); tris.Add((sectionIndex + 1) * 2); tris.Add(sectionIndex * 2 + 1); tris.Add(sectionIndex * 2 + 1); tris.Add((sectionIndex + 1) * 2); tris.Add((sectionIndex + 1) * 2 + 1); } sectionIndex++; } } CommitMeshData(); } }
public void UpdateRenderer(ObiActor actor) { using (m_UpdateExtrudedRopeRendererChunksPerfMarker.Auto()) { if (section == null) { return; } var rope = actor as ObiRopeBase; CreateMeshIfNeeded(); ClearMeshData(); float actualToRestLengthRatio = smoother.SmoothLength / rope.restLength; int sectionSegments = section.Segments; int verticesPerSection = sectionSegments + 1; // the last vertex in each section must be duplicated, due to uv wraparound. float vCoord = -uvScale.y * rope.restLength * uvAnchor; // v texture coordinate. int sectionIndex = 0; // for closed curves, last frame of the last curve must be equal to first frame of first curve. Vector3 vertex, normal, scaledNormal, scaledBinormal; Vector4 texTangent = Vector4.zero; Vector2 uv = Vector2.zero; for (int c = 0; c < smoother.smoothChunks.Count; ++c) { ObiList <ObiPathFrame> curve = smoother.smoothChunks[c]; for (int i = 0; i < curve.Count; ++i) { // Calculate previous and next curve indices: int prevIndex = Mathf.Max(i - 1, 0); // advance v texcoord: vCoord += uvScale.y * (Vector3.Distance(curve[i].position, curve[prevIndex].position) / (normalizeV ? smoother.SmoothLength : actualToRestLengthRatio)); // calculate section thickness and scale the basis vectors by it: float sectionThickness = curve[i].thickness * thicknessScale; scaledNormal = curve[i].normal * sectionThickness; scaledBinormal = curve[i].binormal * sectionThickness; // Loop around each segment: int nextSectionIndex = sectionIndex + 1; for (int j = 0; j <= sectionSegments; ++j) { normal = section.vertices[j].x * scaledNormal + section.vertices[j].y * scaledBinormal; vertex = curve[i].position + normal; texTangent = Vector3.Cross(normal, curve[i].tangent); texTangent.w = -1; uv.Set((j / (float)sectionSegments) * uvScale.x, vCoord); vertices.Add(vertex); normals.Add(normal); tangents.Add(texTangent); vertColors.Add(curve[i].color); uvs.Add(uv); if (j < sectionSegments && i < curve.Count - 1) { tris.Add(sectionIndex * verticesPerSection + j); tris.Add(nextSectionIndex * verticesPerSection + j); tris.Add(sectionIndex * verticesPerSection + (j + 1)); tris.Add(sectionIndex * verticesPerSection + (j + 1)); tris.Add(nextSectionIndex * verticesPerSection + j); tris.Add(nextSectionIndex * verticesPerSection + (j + 1)); } } sectionIndex++; } } CommitMeshData(); } }