void Update() { Profiler.BeginSample("Schedule Softbody Work", this); int batchSize = 8; softbodyData.friction = friction; //Transform the points into world space JobHandle localToWorldHandle = new ToWorldSpaceJob() { localToWorld = transform.localToWorldMatrix, bodyVerts = softbodyData.bodyVerts }.Schedule(originalVerts.Length, batchSize); //Physics - Verlet Integration JobHandle verletHandle = new VerletIntegrateJob() { bodyVerts = softbodyData.bodyVerts, prevBodyVerts = softbodyData.prevBodyVerts, scaledGravity = softbodyData.scaledGravity }.Schedule(originalVerts.Length, batchSize, dependsOn: localToWorldHandle); JobHandle previousHandle = verletHandle; for (int i = 0; i < solverIterations; i++) { //First, ensure that the surface area is what we think it is JobHandle applyConstraints; if (parallelConstraints) { JobHandle calculateConstraints = new CalculateDistanceConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements, constraintsArray = softbodyData.connectionGraph }.Schedule(originalVerts.Length, batchSize, dependsOn: previousHandle); applyConstraints = new ApplyAccumulatedConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements }.Schedule(originalVerts.Length, batchSize, dependsOn: calculateConstraints); } else { JobHandle accumulateDistances = new AccumulateDistanceConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements, constraintsArray = softbodyData.constraintsArray }.Schedule(dependsOn: previousHandle); applyConstraints = new ApplyAccumulatedConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements }.Schedule(originalVerts.Length, batchSize, dependsOn: accumulateDistances); } //Next, set the volume of the soft body JobHandle calculateNormals; if (parallelNormals) { calculateNormals = new GatherNormalsJob() { bodyVerts = softbodyData.bodyVerts, vertexConnections = softbodyData.triangleGraph, bodyNormals = softbodyData.bodyNormals }.Schedule(originalVerts.Length, batchSize, dependsOn: applyConstraints); } else { JobHandle accumulateNormals = new AccumulateNormalsJob() { bodyVerts = softbodyData.bodyVerts, bodyTriangles = softbodyData.bodyTriangles, bodyNormals = softbodyData.bodyNormals }.Schedule(dependsOn: applyConstraints); calculateNormals = new NormalizeNormalsJob() { bodyNormals = softbodyData.bodyNormals }.Schedule(originalVerts.Length, batchSize, dependsOn: accumulateNormals); } JobHandle calculateDilationDistance; if (useAreaVolumeAccumulator) { JobHandle accumulateDilationDistance = new SurfaceAreaAndVolumeAccumulatorJob() { bodyVerts = softbodyData.bodyVerts, bodyTriangles = softbodyData.bodyTriangles, volumeAccumulator = softbodyData.volumeAccumulator, areaAccumulator = softbodyData.areaAccumulator }.Schedule(softbodyData.bodyTriangles.Length, batchSize, dependsOn: calculateNormals); calculateDilationDistance = new CalculateDilationJob() { volumeAccumulator = softbodyData.volumeAccumulator, areaAccumulator = softbodyData.areaAccumulator, initialVolume = softbodyData.initialVolume * inflationAmount, initialSurfaceArea = softbodyData.initialSurfaceArea, dilationDistance = softbodyData.dilationDistance }.Schedule(dependsOn: accumulateDilationDistance); } else { JobHandle accumulateDilationDistance = new AccumulateSurfaceAreaAndVolumeJob() { bodyVerts = softbodyData.bodyVerts, bodyTriangles = softbodyData.bodyTriangles, triangleVolumes = softbodyData.triangleVolumes, triangleSurfaceAreas = softbodyData.triangleSurfaceAreas }.Schedule(softbodyData.bodyTriangles.Length, batchSize, dependsOn: calculateNormals); //THIS IS THE LAST BIG ONE calculateDilationDistance = new CalculateSurfaceAreaAndVolumeJob() { triangleVolumes = softbodyData.triangleVolumes, triangleSurfaceAreas = softbodyData.triangleSurfaceAreas, initialVolume = softbodyData.initialVolume * inflationAmount, initialSurfaceArea = softbodyData.initialSurfaceArea, dilationDistance = softbodyData.dilationDistance }.Schedule(dependsOn: accumulateDilationDistance); } previousHandle = new ExtrudeNormalsJob() { bodyVerts = softbodyData.bodyVerts, bodyNormals = softbodyData.bodyNormals, dilationDistance = softbodyData.dilationDistance }.Schedule(originalVerts.Length, batchSize, dependsOn: calculateDilationDistance); } //Also collide with other softbodies if (collideWithOtherSoftbodies) { foreach (SoftbodyJobified softbody in s_softBodies) { if (softbody != this) { Vector3 midPoint = Vector3.Lerp(transform.position, softbody.transform.position, (radius / softbody.radius) * 0.5f); previousHandle = new VoronoiCollideJob() { bodyVerts = softbodyData.bodyVerts, prevBodyVerts = softbodyData.prevBodyVerts, planePos = midPoint, planeNormal = (transform.position - midPoint).normalized, friction = 0f, velocity = new Vector3(0f, 0f, 0f) }.Schedule(originalVerts.Length, batchSize, dependsOn: previousHandle); } } } //Prepare a batch of raycast commands JobHandle raycastPrepareHandle = new RaycastPrepareJob() { bodyVerts = softbodyData.bodyVerts, prevBodyVerts = softbodyData.prevBodyVerts, preCollisionVerts = softbodyData.preCollisionVerts, raycasts = softbodyData.raycasts, bodyNormals = softbodyData.bodyNormals, penetrationDepth = 0.5f * softbodyData.radius }.Schedule(originalVerts.Length, batchSize, dependsOn: previousHandle); // Schedule the batch of raycasts JobHandle batchRaycastHandle = RaycastCommand.ScheduleBatch(softbodyData.raycasts, softbodyData.raycastHits, batchSize, raycastPrepareHandle); //Apply the results of those raycasts JobHandle raycastCollisionHandle = new RaycastCollisionJob() { bodyVerts = softbodyData.bodyVerts, prevBodyVerts = softbodyData.prevBodyVerts, raycastHits = softbodyData.raycastHits, bodyNormals = softbodyData.bodyNormals, friction = softbodyData.friction }.Schedule(originalVerts.Length, batchSize, dependsOn: batchRaycastHandle); Profiler.EndSample(); raycastCollisionHandle.Complete(); //Calculate the impulses on PhysX objects float invDT = 1 / Time.deltaTime; float vertexMass = (mass / softbodyData.bodyVerts.Length); for (int i = 0; i < softbodyData.raycastHits.Length; i++) { Rigidbody collidingBody; if ((collidingBody = softbodyData.raycastHits[i].rigidbody) != null && !collidingBody.isKinematic) { Vector3 preCollisionVelocity = (softbodyData.preCollisionVerts[i] - softbodyData.prevBodyVerts[i]) * invDT; Vector3 postCollisionVelocity = (softbodyData.bodyVerts[i] - softbodyData.prevBodyVerts[i]) * invDT; Vector3 deltaMomentum = (postCollisionVelocity - preCollisionVelocity) * vertexMass; Vector3 netForce = -deltaMomentum * invDT; collidingBody.AddForceAtPosition(netForce, softbodyData.bodyVerts[i], ForceMode.Force); } } //Calculate the the position and rotation of the body for (int i = 0; i < softbodyData.bodyVerts.Length; i++) { softbodyData.kabschVerts[i] = new Vector4( softbodyData.bodyVerts[i].x, softbodyData.bodyVerts[i].y, softbodyData.bodyVerts[i].z, 1f); } ; softbodyData.kabschVerts.CopyTo(kabschVertsArray); Matrix4x4 toWorldSpace = kabschSolver.SolveKabsch(originalVerts, kabschVertsArray, transformFollowsRotation); transform.position = toWorldSpace.GetVector3(); transform.rotation = toWorldSpace.GetQuaternion(); //Move the points into local space for rendering JobHandle toLocalHandle = new ToLocalSpaceJob() { bodyVerts = softbodyData.bodyVerts, bodyNormals = softbodyData.bodyNormals, renderNormals = softbodyData.renderNormals, worldToLocal = transform.worldToLocalMatrix }.Schedule(originalVerts.Length, batchSize); toLocalHandle.Complete(); //Graphics softbodyData.bodyVerts.CopyTo(bodyVertsArray); softbodyData.renderNormals.CopyTo(renderNormalsArray); bodyMesh.vertices = bodyVertsArray; bodyMesh.normals = renderNormalsArray; bodyMesh.RecalculateBounds(); bodyMesh.UploadMeshData(false); }
void Update() { Profiler.BeginSample("Schedule Softbody Work", this); int batchSize = 16; //Transform the points into world space JobHandle localToWorldHandle = new ToWorldSpaceJob() { localToWorld = transform.localToWorldMatrix, bodyVerts = softbodyData.bodyVerts }.Schedule(originalVerts.Length, batchSize); //Physics - Verlet Integration JobHandle verletHandle = new VerletIntegrateJob() { bodyVerts = softbodyData.bodyVerts, prevBodyVerts = softbodyData.prevBodyVerts, scaledGravity = softbodyData.scaledGravity }.Schedule(originalVerts.Length, batchSize, dependsOn: localToWorldHandle); JobHandle previousHandle = verletHandle; for (int i = 0; i < solverIterations; i++) { //First, ensure that the surface area is what we think it is JobHandle applyConstraints; if (parallelConstraints) { JobHandle calculateConstraints = new CalculateDistanceConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements, constraintsArray = softbodyData.connectionGraph }.Schedule(originalVerts.Length, batchSize, dependsOn: previousHandle); applyConstraints = new ApplyAccumulatedConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements }.Schedule(originalVerts.Length, batchSize, dependsOn: calculateConstraints); } else { JobHandle accumulateDistances = new AccumulateDistanceConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements, constraintsArray = softbodyData.constraintsArray }.Schedule(dependsOn: previousHandle); applyConstraints = new ApplyAccumulatedConstraintsJob() { bodyVerts = softbodyData.bodyVerts, accumulatedDisplacements = softbodyData.accumulatedDisplacements }.Schedule(originalVerts.Length, batchSize, dependsOn: accumulateDistances); } //Next, set the volume of the soft body JobHandle calculateNormals; if (parallelNormals) { calculateNormals = new GatherNormalsJob() { bodyVerts = softbodyData.bodyVerts, vertexConnections = softbodyData.triangleGraph, bodyNormals = softbodyData.bodyNormals }.Schedule(originalVerts.Length, batchSize, dependsOn: applyConstraints); } else { JobHandle accumulateNormals = new AccumulateNormalsJob() { bodyVerts = softbodyData.bodyVerts, bodyTriangles = softbodyData.bodyTriangles, bodyNormals = softbodyData.bodyNormals }.Schedule(dependsOn: applyConstraints); calculateNormals = new NormalizeNormalsJob() { bodyNormals = softbodyData.bodyNormals }.Schedule(originalVerts.Length, batchSize, dependsOn: accumulateNormals); } JobHandle calculateDilationDistance; if (useAreaVolumeAccumulator) { JobHandle accumulateDilationDistance = new SurfaceAreaAndVolumeAccumulatorJob() { bodyVerts = softbodyData.bodyVerts, bodyTriangles = softbodyData.bodyTriangles, volumeAccumulator = softbodyData.volumeAccumulator, areaAccumulator = softbodyData.areaAccumulator }.Schedule(softbodyData.bodyTriangles.Length, batchSize, dependsOn: calculateNormals); calculateDilationDistance = new CalculateDilationJob() { volumeAccumulator = softbodyData.volumeAccumulator, areaAccumulator = softbodyData.areaAccumulator, initialVolume = softbodyData.initialVolume * inflationAmount, initialSurfaceArea = softbodyData.initialSurfaceArea, dilationDistance = softbodyData.dilationDistance }.Schedule(dependsOn: accumulateDilationDistance); } else { JobHandle accumulateDilationDistance = new AccumulateSurfaceAreaAndVolumeJob() { bodyVerts = softbodyData.bodyVerts, bodyTriangles = softbodyData.bodyTriangles, triangleVolumes = softbodyData.triangleVolumes, triangleSurfaceAreas = softbodyData.triangleSurfaceAreas }.Schedule(softbodyData.bodyTriangles.Length, batchSize, dependsOn: calculateNormals); //THIS IS THE LAST BIG ONE calculateDilationDistance = new CalculateSurfaceAreaAndVolumeJob() { triangleVolumes = softbodyData.triangleVolumes, triangleSurfaceAreas = softbodyData.triangleSurfaceAreas, initialVolume = softbodyData.initialVolume * inflationAmount, initialSurfaceArea = softbodyData.initialSurfaceArea, dilationDistance = softbodyData.dilationDistance }.Schedule(dependsOn: accumulateDilationDistance); } previousHandle = new ExtrudeNormalsJob() { bodyVerts = softbodyData.bodyVerts, bodyNormals = softbodyData.bodyNormals, dilationDistance = softbodyData.dilationDistance }.Schedule(originalVerts.Length, batchSize, dependsOn: calculateDilationDistance); } //Also sneak in a ground plane here: JobHandle groundPlaneHandle = new GroundCollideJob() { bodyVerts = softbodyData.bodyVerts, prevBodyVerts = softbodyData.prevBodyVerts, groundPlanePos = groundPlane.position, groundPlaneNormal = -groundPlane.forward }.Schedule(originalVerts.Length, batchSize, dependsOn: previousHandle); Profiler.EndSample(); groundPlaneHandle.Complete(); //Calculate the the position and rotation of the body for (int i = 0; i < softbodyData.bodyVerts.Length; i++) { softbodyData.kabschVerts[i] = new Vector4( softbodyData.bodyVerts[i].x, softbodyData.bodyVerts[i].y, softbodyData.bodyVerts[i].z, 1f); } ; softbodyData.kabschVerts.CopyTo(kabschVertsArray); Matrix4x4 toWorldSpace = kabschSolver.SolveKabsch(originalVerts, kabschVertsArray, transformFollowsRotation); transform.position = toWorldSpace.GetVector3(); transform.rotation = toWorldSpace.GetQuaternion(); //Move the points into local space for rendering JobHandle toLocalHandle = new ToLocalSpaceJob() { bodyVerts = softbodyData.bodyVerts, bodyNormals = softbodyData.bodyNormals, renderNormals = softbodyData.renderNormals, worldToLocal = transform.worldToLocalMatrix }.Schedule(originalVerts.Length, batchSize); toLocalHandle.Complete(); //Graphics softbodyData.bodyVerts.CopyTo(bodyVertsArray); softbodyData.renderNormals.CopyTo(renderNormalsArray); bodyMesh.vertices = bodyVertsArray; bodyMesh.normals = renderNormalsArray; bodyMesh.RecalculateBounds(); bodyMesh.UploadMeshData(false); }