void LateUpdate() { if (!isActiveAndEnabled || actor.solver == null) { return; } for (int i = 0; i < actor.solverIndices.Length; ++i) { int k = actor.solverIndices[i]; int phase = ObiUtils.GetGroupFromPhase(actor.solver.phases[k]); actor.solver.colors[k] = ObiUtils.colorAlphabet[phase % ObiUtils.colorAlphabet.Length]; } }
public override void SetSelfCollisions(bool selfCollisions) { if (solver != null && isLoaded) { Oni.ParticleFlags particlePhase = Oni.ParticleFlags.Fluid; if (emitterBlueprint != null && !(emitterBlueprint is ObiFluidEmitterBlueprint)) { particlePhase = 0; } for (int i = 0; i < solverIndices.Length; i++) { m_Solver.phases[solverIndices[i]] = ObiUtils.MakePhase(fluidPhase, (selfCollisions ? Oni.ParticleFlags.SelfCollide : 0) | particlePhase); } } }
private int FilterOutDistantContacts(Oni.Contact[] data, int count) { int filteredCount = count; // simply iterate trough all contacts, // moving the ones above the threshold to the end of the array: for (int i = count - 1; i >= 0; --i) { if (data[i].distance > distanceThreshold) { ObiUtils.Swap(ref data[i], ref data[--filteredCount]); } } return(filteredCount); }
public static float ScreenPointToCurveMu(ObiPath path, Vector2 screenPoint, Matrix4x4 referenceFrame, int samples = 30) { if (path.ControlPointCount >= 2) { samples = Mathf.Max(1, samples); float step = 1 / (float)samples; float closestMu = 0; float minDistance = float.MaxValue; for (int k = 0; k < path.GetSpanCount(); ++k) { int nextCP = (k + 1) % path.ControlPointCount; var wp1 = path.points[k]; var wp2 = path.points[nextCP]; Vector3 _p = referenceFrame.MultiplyPoint3x4(wp1.position); Vector3 p = referenceFrame.MultiplyPoint3x4(wp1.outTangentEndpoint); Vector3 p_ = referenceFrame.MultiplyPoint3x4(wp2.inTangentEndpoint); Vector3 p__ = referenceFrame.MultiplyPoint3x4(wp2.position); Vector2 lastPoint = HandleUtility.WorldToGUIPoint(path.m_Points.Evaluate(_p, p, p_, p__, 0)); for (int i = 1; i <= samples; ++i) { Vector2 currentPoint = HandleUtility.WorldToGUIPoint(path.m_Points.Evaluate(_p, p, p_, p__, i * step)); float mu; float distance = Vector2.SqrMagnitude((Vector2)ObiUtils.ProjectPointLine(screenPoint, lastPoint, currentPoint, out mu) - screenPoint); if (distance < minDistance) { minDistance = distance; closestMu = (k + (i - 1) * step + mu / samples) / (float)path.GetSpanCount(); } lastPoint = currentPoint; } } return(closestMu); } else { Debug.LogWarning("Curve needs at least 2 control points to be defined."); } return(0); }
protected override void OnAddToSolver(object info) { ObiSolver solver = actor.solver; // Set solver constraint data: int[] solverIndices = new int[aerodynamicIndices.Count]; for (int i = 0; i < aerodynamicNormals.Count; i++) { solverIndices[i] = actor.particleIndices[aerodynamicIndices[i]]; } indicesOffset = actor.solver.aerodynamicConstraints.aerodynamicNormals.Length; ObiUtils.AddRange(ref solver.aerodynamicConstraints.aerodynamicIndices, solverIndices); ObiUtils.AddRange(ref solver.aerodynamicConstraints.aerodynamicNormals, aerodynamicNormals.ToArray()); ObiUtils.AddRange(ref solver.aerodynamicConstraints.wind, wind.ToArray()); ObiUtils.AddRange(ref solver.aerodynamicConstraints.aerodynamicCoeffs, aerodynamicCoeffs.ToArray()); }
protected override void OnAddToSolver(ObiBatchedConstraints constraints) { // Set solver constraint data: solverIndices = new int[springIndices.Count]; solverRestLengths = new float[restLengths.Count]; solverStiffnesses = new Vector3[stiffnesses.Count]; int j = 0; foreach (int i in ObiUtils.BilateralInterleaved(restLengths.Count)) { solverIndices[j * 2] = constraints.Actor.particleIndices[springIndices[i * 2]]; solverIndices[j * 2 + 1] = constraints.Actor.particleIndices[springIndices[i * 2 + 1]]; solverRestLengths[j] = restLengths[i]; solverStiffnesses[j] = stiffnesses[i]; j++; } }
protected override void OnAddToSolver(object info) { ObiSolver solver = actor.solver; // Set solver constraint data: int[] solverIndices = new int[tetherIndices.Count]; for (int i = 0; i < maxLengthsScales.Count; i++) { solverIndices[i * 2] = actor.particleIndices[tetherIndices[i * 2]]; solverIndices[i * 2 + 1] = actor.particleIndices[tetherIndices[i * 2 + 1]]; } indicesOffset = actor.solver.tetherConstraints.maxLengthsScales.Length; ObiUtils.AddRange(ref solver.tetherConstraints.tetherIndices, solverIndices); ObiUtils.AddRange(ref solver.tetherConstraints.maxLengthsScales, maxLengthsScales.ToArray()); ObiUtils.AddRange(ref solver.tetherConstraints.stiffnesses, stiffnesses.ToArray()); }
protected override void OnAddToSolver(ObiBatchedConstraints constraints) { // Set solver constraint data: solverIndices = new int[springIndices.Count]; solverDarboux = new Quaternion[restDarbouxVectors.Count]; solverStiffnesses = new Vector3[stiffnesses.Count]; int j = 0; foreach (int i in ObiUtils.BilateralInterleaved(restDarbouxVectors.Count)) { solverIndices[j * 2] = constraints.Actor.particleIndices[springIndices[i * 2]]; solverIndices[j * 2 + 1] = constraints.Actor.particleIndices[springIndices[i * 2 + 1]]; solverDarboux[j] = restDarbouxVectors[i]; solverStiffnesses[j] = stiffnesses[i]; ++j; } }
protected virtual IEnumerator CreateBendingConstraints() { bendConstraintsData = new ObiBendConstraintsData(); // Add three batches: bendConstraintsData.AddBatch(new ObiBendConstraintsBatch()); bendConstraintsData.AddBatch(new ObiBendConstraintsBatch()); bendConstraintsData.AddBatch(new ObiBendConstraintsBatch()); for (int i = 0; i < totalParticles - 2; i++) { var batch = bendConstraintsData.batches[i % 3] as ObiBendConstraintsBatch; Vector3Int indices = new Vector3Int(i, i + 2, i + 1); float restBend = ObiUtils.RestBendingConstraint(restPositions[indices[0]], restPositions[indices[1]], restPositions[indices[2]]); batch.AddConstraint(indices, restBend); if (i < m_ActiveParticleCount - 2) { batch.activeConstraintCount++; } if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRope: generating structural constraints...", i / (float)(totalParticles - 2))); } } // if the path is closed, add the last, loop closing constraints to a new batch to avoid sharing particles. if (path.Closed) { var loopClosingBatch = new ObiBendConstraintsBatch(); bendConstraintsData.AddBatch(loopClosingBatch); Vector3Int indices = new Vector3Int(m_ActiveParticleCount - 2, 0, m_ActiveParticleCount - 1); loopClosingBatch.AddConstraint(indices, 0); loopClosingBatch.activeConstraintCount++; var loopClosingBatch2 = new ObiBendConstraintsBatch(); bendConstraintsData.AddBatch(loopClosingBatch2); indices = new Vector3Int(m_ActiveParticleCount - 1, 1, 0); loopClosingBatch2.AddConstraint(indices, 0); loopClosingBatch2.activeConstraintCount++; } }
public float GetClosestMuToPoint(Vector3 point, float samples) { if (controlPoints.Count >= MinPoints) { samples = Mathf.Max(1, samples); float step = 1 / (float)samples; int numSpans = GetNumSpans(); float closestMu = 0; float minDistance = float.MaxValue; Matrix4x4 l2w = transform.localToWorldMatrix; for (int k = 0; k < controlPoints.Count; ++k) { Vector3 _p = l2w.MultiplyPoint3x4(controlPoints[k].position); Vector3 p = l2w.MultiplyPoint3x4(controlPoints[k].GetOutTangent()); Vector3 p_ = l2w.MultiplyPoint3x4(controlPoints[k + 1].GetInTangent()); Vector3 p__ = l2w.MultiplyPoint3x4(controlPoints[k + 1].position); Vector3 lastPoint = Evaluate3D(_p, p, p_, p__, 0); for (int i = 1; i <= samples; ++i) { Vector2 currentPoint = Evaluate3D(_p, p, p_, p__, i * step); float mu; float distance = Vector2.SqrMagnitude(ObiUtils.ProjectPointLine(point, lastPoint, currentPoint, out mu) - point); if (distance < minDistance) { minDistance = distance; closestMu = ((k - 1) + (i - 1) * step + mu / samples) / (float)numSpans; } lastPoint = currentPoint; } } return(closestMu); } else { Debug.LogWarning("Catmull-Rom spline needs at least 4 control points to be defined."); } return(0); }
protected override void OnRemoveFromSolver(object info) { ObiSolver solver = actor.solver; // subtract our amount of constraints from other actor's offsets: for (int i = actor.actorID + 1; i < solver.actors.Count; i++) { ObiBendingConstraints bc = solver.actors[i].GetComponent <ObiBendingConstraints>(); if (bc != null) { bc.UpdateIndicesOffset(bc.indicesOffset - restBends.Count); } } ObiUtils.RemoveRange(ref solver.bendingConstraints.bendingIndices, indicesOffset * 3, restBends.Count * 3); ObiUtils.RemoveRange(ref solver.bendingConstraints.restBends, indicesOffset, restBends.Count); ObiUtils.RemoveRange(ref solver.bendingConstraints.bendingStiffnesses, indicesOffset, restBends.Count); }
protected override void OnAddToSolver(object info) { ObiSolver solver = actor.solver; // Set solver constraint data: int[] solverIndices = new int[bendingIndices.Count]; for (int i = 0; i < restBends.Count; i++) { solverIndices[i * 3] = actor.particleIndices[bendingIndices[i * 3]]; solverIndices[i * 3 + 1] = actor.particleIndices[bendingIndices[i * 3 + 1]]; solverIndices[i * 3 + 2] = actor.particleIndices[bendingIndices[i * 3 + 2]]; } indicesOffset = actor.solver.bendingConstraints.restBends.Length; ObiUtils.AddRange(ref solver.bendingConstraints.bendingIndices, solverIndices); ObiUtils.AddRange(ref solver.bendingConstraints.restBends, restBends.ToArray()); ObiUtils.AddRange(ref solver.bendingConstraints.bendingStiffnesses, bendingStiffnesses.ToArray()); }
protected override void OnRemoveFromSolver(object info) { ObiSolver solver = actor.solver; // Update following actors' indices: for (int i = actor.actorID + 1; i < solver.actors.Count; i++) { ObiTetherConstraints tc = solver.actors[i].GetComponent <ObiTetherConstraints>(); if (tc != null) { tc.UpdateIndicesOffset(tc.indicesOffset - maxLengthsScales.Count); } } ObiUtils.RemoveRange(ref solver.tetherConstraints.tetherIndices, indicesOffset * 2, maxLengthsScales.Count * 2); ObiUtils.RemoveRange(ref solver.tetherConstraints.maxLengthsScales, indicesOffset, maxLengthsScales.Count); ObiUtils.RemoveRange(ref solver.tetherConstraints.stiffnesses, indicesOffset, maxLengthsScales.Count); }
protected override void OnRemoveFromSolver(object info) { ObiSolver solver = actor.solver; // Update following actors' indices: for (int i = actor.actorID + 1; i < solver.actors.Count; i++) { ObiPinConstraints pc = solver.actors[i].GetComponent <ObiPinConstraints>(); if (pc != null) { pc.UpdateIndicesOffset(pc.indicesOffset - pinOffsets.Count); } } ObiUtils.RemoveRange(ref solver.pinConstraints.pinIndices, indicesOffset * 2, pinOffsets.Count * 2); ObiUtils.RemoveRange(ref solver.pinConstraints.pinOffsets, indicesOffset, pinOffsets.Count); ObiUtils.RemoveRange(ref solver.pinConstraints.stiffnesses, indicesOffset, pinOffsets.Count); }
protected override void OnAddToSolver(object info) { ObiSolver solver = actor.solver; // Set solver constraint data: int[] solverIndices = new int[springIndices.Count]; for (int i = 0; i < restLengths.Count; i++) { solverIndices[i * 2] = actor.particleIndices[springIndices[i * 2]]; solverIndices[i * 2 + 1] = actor.particleIndices[springIndices[i * 2 + 1]]; } indicesOffset = actor.solver.distanceConstraints.restLengths.Length; ObiUtils.AddRange(ref solver.distanceConstraints.springIndices, solverIndices); ObiUtils.AddRange(ref solver.distanceConstraints.restLengths, restLengths.ToArray()); ObiUtils.AddRange(ref solver.distanceConstraints.stiffnesses, stiffnesses.ToArray()); ObiUtils.AddRange(ref solver.distanceConstraints.stretching, stretching.ToArray()); }
public static float ScreenPointToCurveMu(ObiCurve curve, Vector2 screenPoint, int samples = 30) { if (curve.controlPoints.Count >= curve.MinPoints) { samples = Mathf.Max(1, samples); float step = 1 / (float)samples; float closestMu = 0; float minDistance = float.MaxValue; Matrix4x4 l2w = curve.transform.localToWorldMatrix; for (int k = 0; k < curve.GetNumSpans(); ++k) { Vector3 _p = l2w.MultiplyPoint3x4(curve.controlPoints[k].position); Vector3 p = l2w.MultiplyPoint3x4(curve.controlPoints[k].GetOutTangent()); Vector3 p_ = l2w.MultiplyPoint3x4(curve.controlPoints[(k + 1) % curve.controlPoints.Count].GetInTangent()); Vector3 p__ = l2w.MultiplyPoint3x4(curve.controlPoints[(k + 1) % curve.controlPoints.Count].position); Vector2 lastPoint = HandleUtility.WorldToGUIPoint(curve.Evaluate3D(_p, p, p_, p__, 0)); for (int i = 1; i <= samples; ++i) { Vector2 currentPoint = HandleUtility.WorldToGUIPoint(curve.Evaluate3D(_p, p, p_, p__, i * step)); float mu; float distance = Vector2.SqrMagnitude((Vector2)ObiUtils.ProjectPointLine(screenPoint, lastPoint, currentPoint, out mu) - screenPoint); if (distance < minDistance) { minDistance = distance; closestMu = (k + (i - 1) * step + mu / samples) / (float)curve.GetNumSpans(); } lastPoint = currentPoint; } } return(closestMu); } else { Debug.LogWarning("Curve needs at least 2 control points to be defined."); } return(0); }
protected override void OnAddToSolver(object info) { ObiSolver solver = actor.solver; // Set solver constraint data: int[] solverIndices = new int[skinIndices.Count]; for (int i = 0; i < skinIndices.Count; i++) { solverIndices[i] = actor.particleIndices[skinIndices[i]]; solverIndices[i] = actor.particleIndices[skinIndices[i]]; } indicesOffset = actor.solver.skinConstraints.skinIndices.Length; ObiUtils.AddRange(ref solver.skinConstraints.skinIndices, solverIndices); ObiUtils.AddRange(ref solver.skinConstraints.skinPoints, skinPoints.ToArray()); ObiUtils.AddRange(ref solver.skinConstraints.skinNormals, skinNormals.ToArray()); ObiUtils.AddRange(ref solver.skinConstraints.skinRadiiBackstops, skinRadiiBackstop.ToArray()); ObiUtils.AddRange(ref solver.skinConstraints.skinStiffnesses, skinStiffnesses.ToArray()); }
protected override void OnRemoveFromSolver(object info) { ObiSolver solver = actor.solver; // Update following actors' indices: for (int i = actor.actorID + 1; i < solver.actors.Count; i++) { ObiDistanceConstraints dc = solver.actors[i].GetComponent <ObiDistanceConstraints>(); if (dc != null) { dc.UpdateIndicesOffset(dc.indicesOffset - restLengths.Count); } } ObiUtils.RemoveRange(ref solver.distanceConstraints.springIndices, indicesOffset * 2, restLengths.Count * 2); ObiUtils.RemoveRange(ref solver.distanceConstraints.restLengths, indicesOffset, restLengths.Count); ObiUtils.RemoveRange(ref solver.distanceConstraints.stiffnesses, indicesOffset, restLengths.Count); ObiUtils.RemoveRange(ref solver.distanceConstraints.stretching, indicesOffset, restLengths.Count); }
private bool TopologySplitAttempt(ref int splitActorIndex, ref int intactActorIndex, out Vector3 point, out Vector3 normal, List <HalfEdgeMesh.Face> updatedFaces, HashSet <int> updatedHalfEdges) { int splitSolverIndex = solverIndices[splitActorIndex]; int intactSolverIndex = solverIndices[intactActorIndex]; // we will first try to split the particle with higher mass, so swap them if needed. if (m_Solver.invMasses[splitSolverIndex] > m_Solver.invMasses[intactSolverIndex]) { ObiUtils.Swap(ref splitSolverIndex, ref intactSolverIndex); } // Calculate the splitting plane: point = m_Solver.positions[splitSolverIndex]; Vector3 v2 = m_Solver.positions[intactSolverIndex]; normal = (v2 - point).normalized; // Try to split the vertex at that particle. // If we cannot not split the higher mass particle, try the other one. If that fails too, we cannot tear this edge. if (m_Solver.invMasses[splitSolverIndex] == 0 || !SplitTopologyAtVertex(splitActorIndex, new Plane(normal, point), updatedFaces, updatedHalfEdges)) { // Try to split the other particle: ObiUtils.Swap(ref splitActorIndex, ref intactActorIndex); ObiUtils.Swap(ref splitSolverIndex, ref intactSolverIndex); point = m_Solver.positions[splitSolverIndex]; v2 = m_Solver.positions[intactSolverIndex]; normal = (v2 - point).normalized; if (m_Solver.invMasses[splitSolverIndex] == 0 || !SplitTopologyAtVertex(splitActorIndex, new Plane(normal, point), updatedFaces, updatedHalfEdges)) { return(false); } } return(true); }
public void OnDrawGizmosSelected() { Gizmos.matrix = transform.localToWorldMatrix; Gizmos.color = new Color(0, 0.7f, 1, 1); Gizmos.DrawWireSphere(Vector3.zero, radius); float turb = GetTurbulence(1); if (!radial) { ObiUtils.DrawArrowGizmo(radius + turb, radius * 0.2f, radius * 0.3f, radius * 0.2f); } else { Gizmos.DrawLine(new Vector3(0, 0, -radius * 0.5f) * turb, new Vector3(0, 0, radius * 0.5f) * turb); Gizmos.DrawLine(new Vector3(0, -radius * 0.5f, 0) * turb, new Vector3(0, radius * 0.5f, 0) * turb); Gizmos.DrawLine(new Vector3(-radius * 0.5f, 0, 0) * turb, new Vector3(radius * 0.5f, 0, 0) * turb); } }
protected override void OnRemoveFromSolver(object info) { ObiSolver solver = actor.solver; // subtract our amount of constraints from other actor's offsets: for (int i = actor.actorID + 1; i < solver.actors.Count; i++) { ObiAerodynamicConstraints ac = solver.actors[i].GetComponent <ObiAerodynamicConstraints>(); if (ac != null) { ac.UpdateIndicesOffset(ac.indicesOffset - aerodynamicNormals.Count); } } ObiUtils.RemoveRange(ref solver.aerodynamicConstraints.aerodynamicIndices, indicesOffset, aerodynamicIndices.Count); ObiUtils.RemoveRange(ref solver.aerodynamicConstraints.aerodynamicNormals, indicesOffset, aerodynamicNormals.Count); ObiUtils.RemoveRange(ref solver.aerodynamicConstraints.wind, indicesOffset, wind.Count); ObiUtils.RemoveRange(ref solver.aerodynamicConstraints.aerodynamicCoeffs, indicesOffset * 3, aerodynamicCoeffs.Count); }
private void RefreshCutawayTexture(ObiDistanceField field) { if (field == null) { return; } Bounds b = field.FieldBounds; sampleSize = field.EffectiveSampleSize; sampleCount = (int)(b.size[0] / sampleSize) + 1; CreatePlaneMesh(field); ResizeTexture(); float sweep = (sampleCount * slice) * sampleSize; Vector3 origin = b.center - b.extents; for (int x = 0; x < sampleCount; ++x) { for (int y = 0; y < sampleCount; ++y) { Vector3 offset = Vector3.zero; switch (axis) { case Axis.X: offset = new Vector3(sweep, y * sampleSize, x * sampleSize); break; case Axis.Y: offset = new Vector3(x * sampleSize, sweep, y * sampleSize); break; case Axis.Z: offset = new Vector3(x * sampleSize, y * sampleSize, sweep); break; } Vector4 position = origin + offset; float distance = Oni.SampleDistanceField(field.OniDistanceField, position.x, position.y, position.z); float value = ObiUtils.Remap(distance, -maxDistance, maxDistance, 0, 1); cutawayTexture.SetPixel(x, y, new Color(value, 0, 0)); } } cutawayTexture.Apply(); }
protected override void OnRemoveFromSolver(object info) { ObiSolver solver = actor.solver; // subtract our amount of constraints from other actor's offsets: for (int i = actor.actorID + 1; i < solver.actors.Count; i++) { ObiSkinConstraints dc = solver.actors[i].GetComponent <ObiSkinConstraints>(); if (dc != null) { dc.UpdateIndicesOffset(dc.indicesOffset - skinIndices.Count); } } ObiUtils.RemoveRange(ref solver.skinConstraints.skinIndices, indicesOffset, skinIndices.Count); ObiUtils.RemoveRange(ref solver.skinConstraints.skinPoints, indicesOffset, skinIndices.Count); ObiUtils.RemoveRange(ref solver.skinConstraints.skinNormals, indicesOffset, skinIndices.Count); ObiUtils.RemoveRange(ref solver.skinConstraints.skinRadiiBackstops, indicesOffset * 2, skinIndices.Count * 2); ObiUtils.RemoveRange(ref solver.skinConstraints.skinStiffnesses, indicesOffset, skinIndices.Count); }
protected override void OnAddToSolver(object info) { ObiSolver solver = actor.solver; // Set solver constraint data: int[] solverIndices = new int[particleIndices.Count]; for (int i = 0; i < particleIndices.Count; i++) { solverIndices[i] = actor.particleIndices[particleIndices[i]]; } int[] solverFirstTriangle = new int[firstTriangle.Count]; for (int i = 0; i < firstTriangle.Count; i++) { solverFirstTriangle[i] = (int)actor.solver.volumeConstraints.volumeTriangleIndices.Length / 3 + firstTriangle[i]; } int[] solverFirstParticle = new int[firstParticle.Count]; for (int i = 0; i < firstParticle.Count; i++) { solverFirstParticle[i] = actor.solver.volumeConstraints.volumeParticleIndices.Length + firstParticle[i]; } indicesOffset = actor.solver.volumeConstraints.volumeRestVolumes.Length; volumeTrianglesOffset = actor.solver.volumeConstraints.volumeTriangleIndices.Length; volumeParticlesOffset = actor.solver.volumeConstraints.volumeParticleIndices.Length; ObiUtils.AddRange(ref solver.volumeConstraints.volumeTriangleIndices, triangleIndices.ToArray()); ObiUtils.AddRange(ref solver.volumeConstraints.volumeFirstTriangle, solverFirstTriangle); ObiUtils.AddRange(ref solver.volumeConstraints.volumeNumTriangles, numTriangles.ToArray()); ObiUtils.AddRange(ref solver.volumeConstraints.volumeParticleIndices, solverIndices); ObiUtils.AddRange(ref solver.volumeConstraints.volumeFirstParticle, solverFirstParticle); ObiUtils.AddRange(ref solver.volumeConstraints.volumeNumParticles, numParticles.ToArray()); ObiUtils.AddRange(ref solver.volumeConstraints.volumeRestVolumes, restVolumes.ToArray()); ObiUtils.AddRange(ref solver.volumeConstraints.volumePressureStiffnesses, pressureStiffness.ToArray()); }
protected virtual IEnumerator CreateBendTwistConstraints() { bendTwistConstraintsData = new ObiBendTwistConstraintsData(); // Add two batches: bendTwistConstraintsData.AddBatch(new ObiBendTwistConstraintsBatch()); bendTwistConstraintsData.AddBatch(new ObiBendTwistConstraintsBatch()); // the last bend constraint couples the last segment and a phantom segment past the last particle. for (int i = 0; i < totalParticles - 1; i++) { var batch = bendTwistConstraintsData.batches[i % 2] as ObiBendTwistConstraintsBatch; Vector2Int indices = new Vector2Int(i, i + 1); Quaternion darboux = keepInitialShape ? ObiUtils.RestDarboux(orientations[indices.x], orientations[indices.y]) : Quaternion.identity; batch.AddConstraint(indices, darboux); batch.activeConstraintCount++; if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRod: generating structural constraints...", i / (float)(totalParticles - 1))); } } // if the path is closed, add the last, loop closing constraints to a new batch to avoid sharing particles. if (path.Closed) { var loopClosingBatch = new ObiBendTwistConstraintsBatch(); bendTwistConstraintsData.AddBatch(loopClosingBatch); Vector2Int indices = new Vector2Int(m_ActiveParticleCount - 1, 0); Quaternion darboux = keepInitialShape ? ObiUtils.RestDarboux(orientations[indices.x], orientations[indices.y]) : Quaternion.identity; loopClosingBatch.AddConstraint(indices, darboux); loopClosingBatch.activeConstraintCount++; } }
public static float DistanceToSurface(Triangle[] triangles, Vector3[] vertices, Vector3[] normals, BIHNode node, Vector3 point) { float minDistance = float.MaxValue; int sign = 1; for (int i = node.start; i < node.start + node.count; ++i) { Triangle t = triangles[i]; Vector3 pointOnTri = ObiUtils.NearestPointOnTri(vertices[t.i1], vertices[t.i2], vertices[t.i3], point); Vector3 pointToTri = point - pointOnTri; float sqrDistance = pointToTri.sqrMagnitude; if (sqrDistance < minDistance) { Vector3 bary = Vector3.zero; ObiUtils.BarycentricCoordinates(vertices[t.i1], vertices[t.i2], vertices[t.i3], pointOnTri, ref bary); Vector3 interpolatedNormal = ObiUtils.BarycentricInterpolation(normals[t.i1], normals[t.i2], normals[t.i3], bary); sign = ObiUtils.PureSign(Vector3.Dot(pointToTri, interpolatedNormal)); minDistance = sqrDistance; } } return(Mathf.Sqrt(minDistance) * sign); }
public override void OnInspectorGUI() { serializedObject.UpdateIfRequiredOrScript(); Editor.DrawPropertiesExcluding(serializedObject, "m_Script"); // Get the particle actor editor to retrieve selected particles: ObiParticleActorEditor[] editors = (ObiParticleActorEditor[])Resources.FindObjectsOfTypeAll(typeof(ObiParticleActorEditor)); // If there's any particle actor editor active, we can show pin constraints: if (editors.Length > 0) { List <int> selectedPins = new List <int>(); List <int> removedPins = new List <int>(); if (constraints.GetFirstBatch() != null) { ObiPinConstraintBatch batch = constraints.GetFirstBatch(); // Get the list of pin constraints from the selected particles: for (int i = 0; i < batch.ConstraintCount; i++) { int particleIndex = batch.pinIndices[i]; if (particleIndex >= 0 && particleIndex < ObiParticleActorEditor.selectionStatus.Length && ObiParticleActorEditor.selectionStatus[particleIndex]) { selectedPins.Add(i); } } if (selectedPins.Count > 0) { //Iterate over all constraints: foreach (int i in selectedPins) { GUILayout.BeginVertical("box"); GUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); bool allowSceneObjects = !EditorUtility.IsPersistent(target); batch.pinBodies[i] = EditorGUILayout.ObjectField("Pinned to:", batch.pinBodies[i], typeof(ObiColliderBase), allowSceneObjects) as ObiColliderBase; // Calculate initial pin offset value after changing the rigidbody. if (EditorGUI.EndChangeCheck() && batch.pinBodies[i] != null) { batch.pinOffsets[i] = batch.pinBodies[i].transform.InverseTransformPoint(constraints.Actor.GetParticlePosition(batch.pinIndices[i])); batch.restDarbouxVectors[i] = ObiUtils.RestDarboux(constraints.Actor.GetParticleOrientation(batch.pinIndices[i]), batch.pinBodies[i].transform.rotation); } Color oldColor = GUI.color; GUI.color = Color.red; if (GUILayout.Button("X", GUILayout.Width(30))) { // Mark this constraint to be removed outside of the loop. removedPins.Add(i); continue; } GUI.color = oldColor; GUILayout.EndHorizontal(); batch.pinOffsets[i] = EditorGUILayout.Vector3Field("Offset:", batch.pinOffsets[i]); batch.pinBreakResistance[i] = EditorGUILayout.DelayedFloatField("Break Resistance:", batch.pinBreakResistance[i]); GUILayout.EndVertical(); } } else { EditorGUILayout.HelpBox("No pin constraints for the selected particles.", MessageType.Info); } if (GUILayout.Button("Remove selected")) { for (int i = 0; i < batch.ConstraintCount; i++) { int particleIndex = batch.pinIndices[i]; if (particleIndex >= 0 && particleIndex < ObiParticleActorEditor.selectionStatus.Length && ObiParticleActorEditor.selectionStatus[particleIndex]) { removedPins.Add(i); } } } if (GUILayout.Button("Add Pin Constraint")) { Undo.RecordObject(constraints, "Add pin constraints"); bool wasInSolver = constraints.InSolver; constraints.RemoveFromSolver(null); for (int i = 0; i < ObiParticleActorEditor.selectionStatus.Length; i++) { if (ObiParticleActorEditor.selectionStatus[i]) { batch.AddConstraint(i, null, Vector3.zero, Quaternion.identity, 0); } } if (wasInSolver) { constraints.AddToSolver(null); } } // Remove selected constraint outside of constraint listing loop: if (removedPins.Count > 0) { Undo.RecordObject(constraints, "Remove pin constraints"); bool wasInSolver = constraints.InSolver; constraints.RemoveFromSolver(null); // Remove from last to first, to avoid throwing off subsequent indices: foreach (int i in removedPins.OrderByDescending(i => i)) { batch.RemoveConstraint(i); } if (wasInSolver) { constraints.AddToSolver(null); } } } } // Apply changes to the serializedProperty if (GUI.changed) { serializedObject.ApplyModifiedProperties(); constraints.PushDataToSolver(); } }
protected override IEnumerator Initialize() { if (path.ControlPointCount < 2) { ClearParticleGroups(); path.InsertControlPoint(0, Vector3.left, Vector3.left * 0.25f, Vector3.right * 0.25f, Vector3.up, DEFAULT_PARTICLE_MASS, 1, 1, 1, Color.white, "control point"); path.InsertControlPoint(1, Vector3.right, Vector3.left * 0.25f, Vector3.right * 0.25f, Vector3.up, DEFAULT_PARTICLE_MASS, 1, 1, 1, Color.white, "control point"); } path.RecalculateLenght(Matrix4x4.identity, 0.00001f, 7); List <Vector3> particlePositions = new List <Vector3>(); List <float> particleThicknesses = new List <float>(); List <float> particleInvMasses = new List <float>(); List <int> particlePhases = new List <int>(); List <Color> particleColors = new List <Color>(); // In case the path is open, add a first particle. In closed paths, the last particle is also the first one. if (!path.Closed) { particlePositions.Add(path.points.GetPositionAtMu(path.Closed, 0)); particleThicknesses.Add(path.thicknesses.GetAtMu(path.Closed, 0)); particleInvMasses.Add(ObiUtils.MassToInvMass(path.masses.GetAtMu(path.Closed, 0))); particlePhases.Add(path.phases.GetAtMu(path.Closed, 0)); particleColors.Add(path.colors.GetAtMu(path.Closed, 0)); } // Create a particle group for the first control point: groups[0].particleIndices.Clear(); groups[0].particleIndices.Add(0); ReadOnlyCollection <float> lengthTable = path.ArcLengthTable; int spans = path.GetSpanCount(); for (int i = 0; i < spans; i++) { int firstArcLengthSample = i * (path.ArcLengthSamples + 1); int lastArcLengthSample = (i + 1) * (path.ArcLengthSamples + 1); float upToSpanLength = lengthTable[firstArcLengthSample]; float spanLength = lengthTable[lastArcLengthSample] - upToSpanLength; int particlesInSpan = 1 + Mathf.FloorToInt(spanLength / thickness * resolution); float distance = spanLength / particlesInSpan; for (int j = 0; j < particlesInSpan; ++j) { float mu = path.GetMuAtLenght(upToSpanLength + distance * (j + 1)); particlePositions.Add(path.points.GetPositionAtMu(path.Closed, mu)); particleThicknesses.Add(path.thicknesses.GetAtMu(path.Closed, mu)); particleInvMasses.Add(ObiUtils.MassToInvMass(path.masses.GetAtMu(path.Closed, mu))); particlePhases.Add(path.phases.GetAtMu(path.Closed, mu)); particleColors.Add(path.colors.GetAtMu(path.Closed, mu)); } // Create a particle group for each control point: if (!(path.Closed && i == spans - 1)) { groups[i + 1].particleIndices.Clear(); groups[i + 1].particleIndices.Add(particlePositions.Count - 1); } if (i % 100 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRope: generating particles...", i / (float)spans)); } } m_ActiveParticleCount = particlePositions.Count; totalParticles = m_ActiveParticleCount + pooledParticles; int numSegments = m_ActiveParticleCount - (path.Closed ? 0 : 1); if (numSegments > 0) { m_InterParticleDistance = path.Length / (float)numSegments; } else { m_InterParticleDistance = 0; } positions = new Vector3[totalParticles]; restPositions = new Vector4[totalParticles]; velocities = new Vector3[totalParticles]; invMasses = new float[totalParticles]; principalRadii = new Vector3[totalParticles]; phases = new int[totalParticles]; colors = new Color[totalParticles]; restLengths = new float[totalParticles]; for (int i = 0; i < m_ActiveParticleCount; i++) { invMasses[i] = particleInvMasses[i]; positions[i] = particlePositions[i]; restPositions[i] = positions[i]; restPositions[i][3] = 1; // activate rest position. principalRadii[i] = Vector3.one * particleThicknesses[i] * thickness; phases[i] = ObiUtils.MakePhase(particlePhases[i], 0); colors[i] = particleColors[i]; if (i % 100 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiRope: generating particles...", i / (float)m_ActiveParticleCount)); } } //Create distance constraints for the total number of particles, but only activate for the used ones. IEnumerator dc = CreateDistanceConstraints(); while (dc.MoveNext()) { yield return(dc.Current); } //Create bending constraints: IEnumerator bc = CreateBendingConstraints(); while (bc.MoveNext()) { yield return(bc.Current); } // Recalculate rest length: m_RestLength = 0; foreach (float length in restLengths) { m_RestLength += length; } }
/** * We need to find the barycentric coordinates of point such that the interpolated normal at that point passes trough our target position. * * X * \ / / * \------/--/ * * This is necessary to ensure curvature changes in the surface affect skinned points away from the face plane. * To do so, we use an iterative method similar to Newton´s method for root finding: * * - Project the point on the triangle using an initial normal. * - Get interpolated normal at projection. * - Intersect line from point and interpolated normal with triangle, to find a new projection. * - Repeat. */ BarycentricPoint FindSkinBarycentricCoords(MasterFace triangle, Vector3 position, int max_iterations, float min_convergence) { BarycentricPoint barycentricPoint = BarycentricPoint.zero; // start at center of triangle: Vector3 trusted_bary = Vector3.one / 3.0f; Vector3 temp_normal = ObiUtils.BarycentricInterpolation(triangle.n1, triangle.n2, triangle.n3, trusted_bary); int it = 0; float trust = 1.0f; float convergence = float.MaxValue; while (it++ < max_iterations) { Vector3 point; if (!Obi.ObiUtils.LinePlaneIntersection(triangle.p1, triangle.faceNormal, position, temp_normal, out point)) { break; } // get bary coords at intersection: Vector3 bary = Vector3.zero; if (!triangle.BarycentricCoords(point, ref bary)) { break; } // calculate error: Vector3 error = bary - trusted_bary; // distance from current estimation to last trusted estimation. convergence = Vector3.Dot(error, error); // get a single convergence value. // weighted sum of bary coords: trusted_bary = (1.0f - trust) * trusted_bary + trust * bary; // update normal temp_normal = ObiUtils.BarycentricInterpolation(triangle.n1, triangle.n2, triangle.n3, trusted_bary); if (convergence < min_convergence) { break; } trust *= 0.8f; } Vector3 pos_on_tri = trusted_bary[0] * triangle.p1 + trusted_bary[1] * triangle.p2 + trusted_bary[2] * triangle.p3; float height = Vector3.Dot(position - pos_on_tri, temp_normal); barycentricPoint.barycentricCoords = trusted_bary; barycentricPoint.height = height; return(barycentricPoint); }
public static List <BIHNode> Build(ref IBounded[] elements) { List <BIHNode> nodes = new List <BIHNode> { new BIHNode(0, elements.Length) }; var queue = new Queue <int>(); queue.Enqueue(0); while (queue.Count > 0) { // get current node: int index = queue.Dequeue(); var node = nodes[index]; // if this node contains enough elements, split it: if (node.count > 1) { int start = node.start; int end = start + (node.count - 1); // calculate bounding box of all elements: Aabb b = elements[start].GetBounds(); for (int k = start + 1; k <= end; ++k) { b.Encapsulate(elements[k].GetBounds()); } // determine split axis (longest one): Vector3 size = b.size; int axis = node.axis = (size.x > size.y) ? (size.x > size.z ? 0 : 2) : (size.y > size.z ? 1 : 2); // place split plane at half the longest axis: float pivot = b.min[axis] + size[axis] * 0.5f; // sort elements using the split plane (Hoare's partition algorithm): int i = start - 1; int j = end + 1; Aabb bi, bj; while (true) { // iterate over left elements, while they're smaller than the pivot. do { bi = elements[++i].GetBounds(); if (bi.center[axis] < pivot) { node.min = Mathf.Max(node.min, bi.max[axis]); } } while (bi.center[axis] < pivot); // iterate over right elements, while they're larger than the pivot. do { bj = elements[--j].GetBounds(); if (bj.center[axis] > pivot) { node.max = Mathf.Min(node.max, bj.min[axis]); } } while (bj.center[axis] > pivot); // if element i is larger than the pivot, j smaller than the pivot, swap them. if (i < j) { ObiUtils.Swap(ref elements[i], ref elements[j]); node.min = Mathf.Max(node.min, bj.max[axis]); node.max = Mathf.Min(node.max, bi.min[axis]); } else { break; } } // create two child nodes: var minChild = new BIHNode(start, j - start + 1); var maxChild = new BIHNode(j + 1, end - j); // guard against cases where all elements are on one side of the split plane, // due to all having the same or very similar bounds as the entire group. if (minChild.count > 0 && maxChild.count > 0) { node.firstChild = nodes.Count; nodes[index] = node; queue.Enqueue(nodes.Count); queue.Enqueue(nodes.Count + 1); // append child nodes to list: nodes.Add(minChild); nodes.Add(maxChild); } } } return(nodes); }