protected void Awake() { solverBatchOffsets = new List <int> [Oni.ConstraintTypeCount]; for (int i = 0; i < solverBatchOffsets.Length; ++i) { solverBatchOffsets[i] = new List <int>(); } m_PinConstraints = new ObiPinConstraintsData(); #if UNITY_EDITOR // Check if this script's GameObject is in a PrefabStage var prefabStage = UnityEditor.Experimental.SceneManagement.PrefabStageUtility.GetPrefabStage(gameObject); if (prefabStage != null) { // Only create a solver if there's not one up our hierarchy. if (GetComponentInParent <ObiSolver>() == null) { // Add our own environment root and move it to the PrefabStage scene var newParent = new GameObject("ObiSolver (Environment)", typeof(ObiSolver), typeof(ObiLateFixedUpdater)); newParent.GetComponent <ObiLateFixedUpdater>().solvers.Add(newParent.GetComponent <ObiSolver>()); UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(newParent, gameObject.scene); transform.root.parent = newParent.transform; } } #endif }
public IEnumerator Generate() { m_Empty = true; m_ActiveParticleCount = 0; distanceConstraintsData = null; bendConstraintsData = null; pinConstraintsData = null; skinConstraintsData = null; tetherConstraintsData = null; bendTwistConstraintsData = null; stretchShearConstraintsData = null; shapeMatchingConstraintsData = null; aerodynamicConstraintsData = null; chainConstraintsData = null; volumeConstraintsData = null; IEnumerator g = Initialize(); while (g.MoveNext()) { yield return(g.Current); } RecalculateBounds(); m_Empty = false; m_InitialActiveParticleCount = m_ActiveParticleCount; foreach (IObiConstraints constraints in GetConstraints()) { foreach (IObiConstraintsBatch batch in constraints.GetBatchInterfaces()) { batch.initialActiveConstraintCount = batch.activeConstraintCount; } } #if UNITY_EDITOR EditorUtility.SetDirty(this); #endif if (OnBlueprintGenerate != null) { OnBlueprintGenerate(this); } }
public ObiPinConstraintsBatch(ObiPinConstraintsData constraints = null, ObiPinConstraintsBatch source = null) : base(source) { m_Constraints = constraints; }
protected override IEnumerator Initialize() { if (inputMesh == null || !inputMesh.isReadable) { // TODO: return an error in the coroutine. Debug.LogError("The input mesh is null, or not readable."); yield break; } ClearParticleGroups(); Vector3[] vertices = inputMesh.vertices; Vector3[] normals = inputMesh.normals; vertexToParticle = new int[vertices.Length]; List <Vector3> particles = new List <Vector3>(); // Add particles to every vertex, as long as they are not too close to the already added ones: for (int i = 0; i < vertices.Length; ++i) { bool intersects = false; Vector3 vertexScaled = Vector3.Scale(scale, vertices[i]); for (int j = 0; j < particles.Count; ++j) { if (Vector3.Distance(vertexScaled, particles[j]) < particleRadius * 2 * (1 - particleOverlap)) { intersects = true; break; } } if (intersects) { continue; } particles.Add(vertexScaled); if (i % 100 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: sampling mesh...", i / (float)vertices.Length)); } } // Find out the closes particle to each vertex: for (int i = 0; i < vertices.Length; ++i) { Vector3 vertexScaled = Vector3.Scale(scale, vertices[i]); float minDistance = float.MaxValue; vertexToParticle[i] = 0; for (int j = 0; j < particles.Count; ++j) { float distance = Vector3.SqrMagnitude(vertexScaled - particles[j]); if (distance < minDistance) { minDistance = distance; vertexToParticle[i] = j; } } if (i % 100 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: mapping vertices to particles...", i / (float)vertices.Length)); } } positions = new Vector3[particles.Count]; orientations = new Quaternion[particles.Count]; restPositions = new Vector4[particles.Count]; restOrientations = new Quaternion[particles.Count]; velocities = new Vector3[particles.Count]; angularVelocities = new Vector3[particles.Count]; invMasses = new float[particles.Count]; invRotationalMasses = new float[particles.Count]; principalRadii = new Vector3[particles.Count]; phases = new int[particles.Count]; colors = new Color[particles.Count]; m_ActiveParticleCount = particles.Count; for (int i = 0; i < particles.Count; ++i) { // Perform ellipsoid fitting: Vector3 avgNormal = Vector3.zero; List <Vector3> neighbourVertices = new List <Vector3>(); for (int j = 0; j < vertices.Length; ++j) { Vector3 vertexScaled = Vector3.Scale(scale, vertices[j]); if (Vector3.Distance(vertexScaled, particles[i]) < anisotropyNeighborhood) { neighbourVertices.Add(vertexScaled); avgNormal += normals[j]; } } if (neighbourVertices.Count > 0) { avgNormal /= neighbourVertices.Count; } Vector3 centroid = particles[i]; Quaternion orientation = Quaternion.identity; Vector3 principalValues = Vector3.one; Oni.GetPointCloudAnisotropy(neighbourVertices.ToArray(), neighbourVertices.Count, maxAnisotropy, particleRadius, ref avgNormal, ref centroid, ref orientation, ref principalValues); invRotationalMasses[i] = invMasses[i] = 1.0f; positions[i] = Vector3.Lerp(particles[i], centroid, shapeSmoothing); restPositions[i] = positions[i]; restPositions[i][3] = 1; // activate rest position. orientations[i] = orientation; restOrientations[i] = orientation; principalRadii[i] = principalValues; phases[i] = Oni.MakePhase(1, oneSided ? Oni.ParticleFlags.OneSided : 0); colors[i] = Color.white; if (i % 100 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: generating particles...", i / (float)particles.Count)); } } //if (makeSolidCluster){ /*indices.Clear(); * for (int i = 0; i < particles.Count; ++i) * indices.Add(i); * shapeBatch.AddConstraint(indices.ToArray(),1,0,0,true); * }*/ IEnumerator sc = CreateShapeMatchingConstraints(particles); while (sc.MoveNext()) { yield return(sc.Current); } // Initialize pin constraints: pinConstraintsData = new ObiPinConstraintsData(); ObiPinConstraintsBatch pinBatch = new ObiPinConstraintsBatch(); pinConstraintsData.AddBatch(pinBatch); generatedMesh = inputMesh; }
public ObiPinConstraintsData(ObiActor actor = null, ObiPinConstraintsData source = null) : base(actor, source) { }
protected override IEnumerator Initialize() { if (inputMesh == null || !inputMesh.isReadable) { // TODO: return an error in the coroutine. Debug.LogError("The input mesh is null, or not readable."); yield break; } ClearParticleGroups(); List <Vector3> particles = new List <Vector3>(); List <Vector3> normals = new List <Vector3>(); // Calculate voxel size so that no more than 32^3 particles are created: Vector3 boundsSize = Vector3.Scale(inputMesh.bounds.size, Vector3.one); float voxelSize = Mathf.Max(boundsSize.x / 32.0f, boundsSize.y / 32.0f, boundsSize.z / 32.0f, particleRadius * 2 * (1 - particleOverlap)); // Voxelize mesh and calculate discrete distance field: MeshVoxelizer voxelizer = new MeshVoxelizer(inputMesh, voxelSize); VoxelDistanceField df = new VoxelDistanceField(voxelizer); voxelizer.Voxelize(scale); df.JumpFlood(); MeshVoxelizer.Voxel[,,] voxels = voxelizer.voxels; for (int x = 0; x < voxels.GetLength(0); ++x) { for (int y = 0; y < voxels.GetLength(1); ++y) { for (int z = 0; z < voxels.GetLength(2); ++z) { if (voxels[x, y, z] != MeshVoxelizer.Voxel.Outside) { particles.Add(new Vector3(voxelizer.Origin.x + x + 0.5f, voxelizer.Origin.y + y + 0.5f, voxelizer.Origin.z + z + 0.5f) * voxelSize); normals.Add(df.distanceField[x, y, z] - new Vector3Int(x, y, z)); } } } } positions = new Vector3[particles.Count]; orientations = new Quaternion[particles.Count]; restPositions = new Vector4[particles.Count]; restOrientations = new Quaternion[particles.Count]; velocities = new Vector3[particles.Count]; angularVelocities = new Vector3[particles.Count]; invMasses = new float[particles.Count]; invRotationalMasses = new float[particles.Count]; principalRadii = new Vector3[particles.Count]; phases = new int[particles.Count]; colors = new Color[particles.Count]; m_ActiveParticleCount = particles.Count; for (int i = 0; i < particles.Count; ++i) { // Perform ellipsoid fitting: Vector3 avgNormal = Vector3.zero; List <Vector3> neighbourVertices = new List <Vector3>(); Vector3 centroid = particles[i]; Quaternion orientation = Quaternion.LookRotation(normals[i]); Vector3 principalValues = Vector3.one * voxelSize * (0.5f + particleOverlap); invRotationalMasses[i] = invMasses[i] = 1.0f; positions[i] = Vector3.Lerp(particles[i], centroid, shapeSmoothing); restPositions[i] = positions[i]; orientations[i] = orientation; restOrientations[i] = orientation; restPositions[i][3] = 1; // activate rest position. principalRadii[i] = principalValues; phases[i] = ObiUtils.MakePhase(1, 0); //Oni.MakePhase(1, (selfCollisions ? Oni.ParticlePhase.SelfCollide : 0) | (oneSided ? Oni.ParticlePhase.OneSided : 0)); colors[i] = Color.white; if (i % 100 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiSoftbody: generating particles...", i / (float)particles.Count)); } } IEnumerator sc = CreateShapeMatchingConstraints(particles); while (sc.MoveNext()) { yield return(sc.Current); } // Initialize pin constraints: pinConstraintsData = new ObiPinConstraintsData(); ObiPinConstraintsBatch pinBatch = new ObiPinConstraintsBatch(); pinConstraintsData.AddBatch(pinBatch); }
protected override IEnumerator Initialize() { if (inputMesh == null || !inputMesh.isReadable) { // TODO: return an error in the coroutine. Debug.LogError("The input mesh is null, or not readable."); yield break; } ClearParticleGroups(); topology = new HalfEdgeMesh(); topology.inputMesh = inputMesh; topology.Generate(); positions = new Vector3[topology.vertices.Count]; restPositions = new Vector4[topology.vertices.Count]; velocities = new Vector3[topology.vertices.Count]; invMasses = new float[topology.vertices.Count]; principalRadii = new Vector3[topology.vertices.Count]; phases = new int[topology.vertices.Count]; colors = new Color[topology.vertices.Count]; areaContribution = new float[topology.vertices.Count]; // Create a particle for each vertex: m_ActiveParticleCount = topology.vertices.Count; for (int i = 0; i < topology.vertices.Count; i++) { HalfEdgeMesh.Vertex vertex = topology.vertices[i]; // Get the particle's area contribution. areaContribution[i] = 0; foreach (HalfEdgeMesh.Face face in topology.GetNeighbourFacesEnumerator(vertex)) { areaContribution[i] += topology.GetFaceArea(face) / 3; } // Get the shortest neighbour edge, particle radius will be half of its length. float minEdgeLength = Single.MaxValue; foreach (HalfEdgeMesh.HalfEdge edge in topology.GetNeighbourEdgesEnumerator(vertex)) { // vertices at each end of the edge: Vector3 v1 = Vector3.Scale(scale, topology.vertices[topology.GetHalfEdgeStartVertex(edge)].position); Vector3 v2 = Vector3.Scale(scale, topology.vertices[edge.endVertex].position); minEdgeLength = Mathf.Min(minEdgeLength, Vector3.Distance(v1, v2)); } invMasses[i] = (/*skinnedMeshRenderer == null &&*/ areaContribution[i] > 0) ? (1.0f / (DEFAULT_PARTICLE_MASS * areaContribution[i])) : 0; positions[i] = Vector3.Scale(scale, vertex.position); restPositions[i] = positions[i]; restPositions[i][3] = 1; // activate rest position. principalRadii[i] = Vector3.one * minEdgeLength * 0.5f; phases[i] = Oni.MakePhase(1, /*selfCollisions ? Oni.ParticlePhase.SelfCollide : 0*/ 0); colors[i] = Color.white; if (i % 500 == 0) { yield return(new CoroutineJob.ProgressInfo("ObiCloth: generating particles...", i / (float)topology.vertices.Count)); } } IEnumerator dt = GenerateDeformableTriangles(); while (dt.MoveNext()) { yield return(dt.Current); } //Create distance constraints: IEnumerator dc = CreateDistanceConstraints(); while (dc.MoveNext()) { yield return(dc.Current); } // Create aerodynamic constraints: IEnumerator ac = CreateAerodynamicConstraints(); while (ac.MoveNext()) { yield return(ac.Current); } //Create bending constraints: IEnumerator bc = CreateBendingConstraints(); while (bc.MoveNext()) { yield return(bc.Current); } // Create skin constraints: IEnumerator sc = CreateSkinConstraints(); while (sc.MoveNext()) { yield return(sc.Current); } pinConstraintsData = new ObiPinConstraintsData(); ObiPinConstraintsBatch pinBatch = new ObiPinConstraintsBatch(); pinConstraintsData.AddBatch(pinBatch); }
public ObiPinConstraintsBatch(ObiPinConstraintsData constraints = null) : base() { }
protected override IEnumerator Initialize() { path.OnPathChanged.RemoveAllListeners(); path.OnControlPointAdded.RemoveAllListeners(); path.OnControlPointRemoved.RemoveAllListeners(); path.OnControlPointRenamed.RemoveAllListeners(); path.OnPathChanged.AddListener(GenerateImmediate); path.OnControlPointAdded.AddListener(ControlPointAdded); path.OnControlPointRemoved.AddListener(ControlPointRemoved); path.OnControlPointRenamed.AddListener(ControlPointRenamed); 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.thicknesses.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] = Oni.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); } //Create pin constraints: pinConstraintsData = new ObiPinConstraintsData(); ObiPinConstraintsBatch pinBatch = new ObiPinConstraintsBatch(); pinConstraintsData.AddBatch(pinBatch); // Recalculate rest length: m_RestLength = 0; foreach (float length in restLengths) { m_RestLength += length; } }