/** * Main Update Method */ protected override JobHandle OnUpdate(JobHandle inputDeps) { int numOFProjectiles = _data.Length; int batchSize = 500; var raycastCommands = new NativeArray <RaycastCommand>(numOFProjectiles, Allocator.TempJob); var raycastHits = new NativeArray <RaycastHit>(numOFProjectiles, Allocator.TempJob); var raycastCommandsJob = new PrepareRaycastCommands { DeltaTime = Time.deltaTime, Commands = raycastCommands, Projectiles = _data.Projectile }; // SCHEDULE Job for creating raycast commands JobHandle raycastDependency = raycastCommandsJob.Schedule(numOFProjectiles, batchSize); // SCHEDULE Raycast job JobHandle raycastJobHandle = RaycastCommand.ScheduleBatch(raycastCommands, raycastHits, batchSize, raycastDependency); // Update Projectiles with new state var projectileStateUpdate = new ProjectileHitUpdateJob { Projectiles = _data.Projectile, RaycastHits = raycastHits, Entities = _data.Entities, CommandBuffer = _jobBarrier.CreateCommandBuffer().ToConcurrent() }; JobHandle stateUpdateJob = projectileStateUpdate.Schedule(numOFProjectiles, batchSize, raycastJobHandle); stateUpdateJob.Complete(); raycastCommands.Dispose(); raycastHits.Dispose(); return(stateUpdateJob); }
void Update() { // only do an update if the data has been initialized if (!positions.IsCreated) { return; } var deltaTime = Time.deltaTime; // FORCE ACCUMILATION // First off lets apply gravity to all the object in our scene that arnt currently asleep var gravityJob = new GravityJob() { DeltaTime = deltaTime, Gravity = gravity, Velocities = velocities, Sleeping = sleeping }; var gravityDependency = gravityJob.Schedule(objectCount, 32); // TODO accumilate other forces here! gravity is just the simplest force to apply to our objects, // but theres no limit on the kind of forces we can simulate. We could add friction, air resistnace, // constant acceleration, joints and constrainsts, boyancy.. anything we can think of that can effect // the velocity of an object can have its own job scheduled here. // INTERGRATION AND COLLISION // Create temporary arrays for the raycast info. Lets use the TempJob allocator, // this means we have to dispose them when the job has finished - its short lived data var raycastCommands = new NativeArray <RaycastCommand>(objectCount, Allocator.TempJob); var raycastHits = new NativeArray <RaycastHit>(objectCount, Allocator.TempJob); // Lets schedule jobs to do a collision raycast for each object. One job Prepare s all the raycast commands, // the second actually does the raycasts. var setupRaycastsJob = new PrepareRaycastCommands() { DeltaTime = deltaTime, Positions = positions, Raycasts = raycastCommands, Velocities = velocities }; var setupDependency = setupRaycastsJob.Schedule(objectCount, 32, gravityDependency); // BUG| WORKAROUND - Checked against Unity 2018.1b8 // BUG| RaycastCommand seems to be ignoring any passed in dependencies. We have to explicity wait here. // BUG| This has been reported to Unity and fixed. When the fix appears in public betas this can be removed. setupDependency.Complete(); // BUG| END WORKAROUND var raycastDependency = RaycastCommand.ScheduleBatch(raycastCommands, raycastHits, 32, setupDependency); // Now we know if there is a collision along our velocity vector, its time to integrate the velocity into // our objects for the current timeset. var integrateJob = new IntegratePhysics() { DeltaTime = deltaTime, Positions = positions, Velocities = velocities, Sleeping = sleeping, Hits = raycastHits }; var integrateDependency = integrateJob.Schedule(objectCount, 32, raycastDependency); // finally, respond to any collisions that happened in the lsat update step. var collisionResponeJob = new CalculateCollisionResponse() { Hits = raycastHits, Velocities = velocities, Sleeping = sleeping }; var collisionDependency = collisionResponeJob.Schedule(objectCount, 32, integrateDependency); // Now the physics is done, we need to create a drawing matrix for every object. This simple demo dosnt // implment roation, so only the translation values in the matrix reallllly matter. var renderMatrixJob = new CalculateDrawMatricies() { positions = positions, renderMatrices = renderMatrices }; var matrixJob = renderMatrixJob.Schedule(objectCount, 32, collisionDependency); // All the jobs we want to execute have been scheduled! By calling .Complete() on the last job in the // chain, Unity makes the main thread help out with scheduled jobs untill they are all complete. // then we can move on and use the data caluclated in the jobs safely, without worry about data being changed // by other threads as we try to use it - we *know* all the work is done matrixJob.Complete(); // make sure we dispose of the temporary NativeArrays we used for raycasting raycastCommands.Dispose(); raycastHits.Dispose(); // Well, all the updating is done! lets actually issue a draw! Graphics.DrawMeshInstanced(mesh, 0, material, renderMatrices.ToArray()); // DEBUG 1 - draw red lines showing object velocity //for (int i = 0; i < objectCount; i++) //{ /// Debug.DrawLine(positions[i], positions[i] + velocities[i], Color.red, 0.016f, true); //} // DEBUG 2 - draw a trail for a few objects, really helps to visualize the bounce! //var duration = 01f; //Debug.DrawLine(positions[0], positions[0] + velocities[0], Color.red, duration, true); //Debug.DrawLine(positions[200], positions[200] + velocities[200], Color.cyan, duration, true); //Debug.DrawLine(positions[400], positions[400] + velocities[400], Color.green, duration, true); //Debug.DrawLine(positions[600], positions[600] + velocities[600], Color.magenta, duration, true); //Debug.DrawLine(positions[800], positions[800] + velocities[800], Color.yellow, duration, true); //Debug.DrawLine(positions[1000], positions[1000] + velocities[1000], Color.blue, duration, true); //Debug.DrawLine(positions[100], positions[100] + velocities[100], Color.red, duration, true); //Debug.DrawLine(positions[300], positions[300] + velocities[300], Color.cyan, duration, true); //Debug.DrawLine(positions[500], positions[500] + velocities[500], Color.green, duration, true); //Debug.DrawLine(positions[700], positions[700] + velocities[700], Color.magenta, duration, true); //Debug.DrawLine(positions[900], positions[900] + velocities[900], Color.yellow, duration, true); // finally lets respawn any object that has been asleep and stable for a while // TODO as of Unity 2018.1b6, NativeArray is the only native collection type. When // NativeQueue is included in the beta this last task can be jobified! for (int i = 0; i < objectCount; i++) { if (sleeping[i] > 15) { Respawn(i); } } }
void Update() { // only do an update if the data has been initialized if (!positions.IsCreated) { return; } sampler.Begin(); var deltaTime = Time.deltaTime; // FORCE ACCUMILATION // First off lets apply gravity to all the object in our scene that arnt currently asleep var gravityJob = new GravityJob() { DeltaTime = deltaTime, Gravity = gravity, Velocities = velocities, Sleeping = sleepingTimer }; var gravityDependency = gravityJob.Schedule(objectCount, 32); // TODO accumilate other forces here! gravity is just the simplest force to apply to our objects, // but theres no limit on the kind of forces we can simulate. We could add friction, air resistnace, // constant acceleration, joints and constrainsts, boyancy.. anything we can think of that can effect // the velocity of an object can have its own job scheduled here. // INTERGRATION AND COLLISION // Create temporary arrays for the raycast info. Lets use the TempJob allocator, // this means we have to dispose them when the job has finished - its short lived data var raycastCommands = new NativeArray <RaycastCommand>(objectCount, Allocator.TempJob); var raycastHits = new NativeArray <RaycastHit>(objectCount, Allocator.TempJob); // Lets schedule jobs to do a collision raycast for each object. One job Prepare s all the raycast commands, // the second actually does the raycasts. var setupRaycastsJob = new PrepareRaycastCommands() { DeltaTime = deltaTime, Positions = positions, Raycasts = raycastCommands, Velocities = velocities }; var setupDependency = setupRaycastsJob.Schedule(objectCount, 32, gravityDependency); var raycastDependency = RaycastCommand.ScheduleBatch(raycastCommands, raycastHits, 32, setupDependency); // Now we know if there is a collision along our velocity vector, its time to integrate the velocity into // our objects for the current timeset. var integrateJob = new IntegratePhysics() { DeltaTime = deltaTime, Positions = positions, Velocities = velocities, Sleeping = sleepingTimer, Hits = raycastHits }; var integrateDependency = integrateJob.Schedule(objectCount, 32, raycastDependency); // finally, respond to any collisions that happened in the lsat update step. var collisionResponeJob = new CalculateCollisionResponse() { Hits = raycastHits, Velocities = velocities, Sleeping = sleepingTimer }; var collisionDependency = collisionResponeJob.Schedule(objectCount, 32, integrateDependency); // Now the physics is done, we need to create a drawing matrix for every object. This simple demo dosnt // implment roation, so only the translation values in the matrix reallllly matter. var renderMatrixJob = new CalculateDrawMatricies() { positions = positions, renderMatrices = renderMatrices }; var matrixDependency = renderMatrixJob.Schedule(objectCount, 32, collisionDependency); // All the jobs we want to execute have been scheduled! By calling .Complete() on the last job in the // chain, Unity makes the main thread help out with scheduled jobs untill they are all complete. // then we can move on and use the data caluclated in the jobs safely, without worry about data being changed // by other threads as we try to use it - we *know* all the work is done matrixDependency.Complete(); // make sure we dispose of the temporary NativeArrays we used for raycasting raycastCommands.Dispose(); raycastHits.Dispose(); // lets schedule a job to figure out which objects are sleeping - this can run in the background whilst // we dispach the drawing commands for this frame. var sleepJob = new FindSleepingObjects() { Sleeping = sleepingTimer, SleepQueue = asleep.AsParallelWriter() }; var sleepDependancy = sleepJob.Schedule(objectCount, 32, matrixDependency); // lets actually issue a draw! renderMatrices.CopyTo(renderMatrixArray); // copy to a preallocated array, we would get garbage from ToArray() Graphics.DrawMeshInstanced(mesh, 0, material, renderMatrixArray); // DEBUG 1 - draw red lines showing object velocity //for (int i = 0; i < objectCount; i++) //{ /// Debug.DrawLine(positions[i], positions[i] + velocities[i], Color.red, 0.016f, true); //} // DEBUG 2 - draw a trail for a few objects, really helps to visualize the bounce! //var duration = 01f; //Debug.DrawLine(positions[0], positions[0] + velocities[0], Color.red, duration, true); //Debug.DrawLine(positions[200], positions[200] + velocities[200], Color.cyan, duration, true); //Debug.DrawLine(positions[400], positions[400] + velocities[400], Color.green, duration, true); //Debug.DrawLine(positions[600], positions[600] + velocities[600], Color.magenta, duration, true); //Debug.DrawLine(positions[800], positions[800] + velocities[800], Color.yellow, duration, true); //Debug.DrawLine(positions[1000], positions[1000] + velocities[1000], Color.blue, duration, true); //Debug.DrawLine(positions[100], positions[100] + velocities[100], Color.red, duration, true); //Debug.DrawLine(positions[300], positions[300] + velocities[300], Color.cyan, duration, true); //Debug.DrawLine(positions[500], positions[500] + velocities[500], Color.green, duration, true); //Debug.DrawLine(positions[700], positions[700] + velocities[700], Color.magenta, duration, true); //Debug.DrawLine(positions[900], positions[900] + velocities[900], Color.yellow, duration, true); // finally lets respawn any object that has been found to be asleep (ie not moved for 15 frames) // were going to respawn that object so there is a constant flow sleepDependancy.Complete(); for (int i = asleep.Count; i != 0; i--) { int index = asleep.Dequeue(); Respawn(index); } sampler.End(); }
public void Bake(int raySamples, List <Renderer> renderers, Action <SDFVolume, float[], float, object> bakeComplete, object passthrough = null) { onBakeComplete = bakeComplete; int progressInterval = _settings.CellCount / 4; int progress = 0; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); vec3 halfVoxel = _settings.HalfVoxel; float maxDistance = 0f; //adjusted to best timings from testing but it could vary by CPU int calcRayLengthBatchCount = 32; calcRayLengthBatchCount = Mathf.Clamp(calcRayLengthBatchCount, 1, raySamples); int raycastBatchCount = 8; raycastBatchCount = Mathf.Clamp(raycastBatchCount, 1, raySamples); int prepareRaysBatchCount = 64; prepareRaysBatchCount = Mathf.Clamp(prepareRaysBatchCount, 1, raySamples); int compareBatchCount = 128; //for raycast method front facing geo and flipped backfacing geo is required List <Collider> geoFront = new List <Collider>(); List <Collider> geoBack = new List <Collider>(); CreateColliders(ref renderers, ref geoFront, ref geoBack); //prepare data NativeArray <float> distances = new NativeArray <float>(_settings.CellCount, Allocator.TempJob); NativeArray <CellResults> allResults = new NativeArray <CellResults>(_settings.CellCount, Allocator.TempJob); //constant for all cells NativeArray <vec3> sphereSamples = new NativeArray <vec3>(raySamples, Allocator.TempJob); //NativeArray<vec3> randomDirections = new NativeArray<vec3>(raySamples, Allocator.TempJob); NativeArray <vec4> volumePlanes = new NativeArray <vec4>(6, Allocator.TempJob); GetUniformPointsOnSphereNormalized(ref sphereSamples); //GetRandomDirections( _halfVoxel*settings.JitterScale, settings.JitterSeed, ref randomDirections); vec3 aabbMin = BoundsWorldAABB.min; vec3 aabbMax = BoundsWorldAABB.max; //the max ray length, used to normalize all resulting distances //so they are treated as 0 to 1 within a volume float aabbMagnitude = BoundsWorldAABB.size.magnitude; Plane pl = new Plane(Vector3.right, aabbMin); Plane pr = new Plane(Vector3.left, aabbMax); Plane pd = new Plane(Vector3.up, aabbMin); Plane pu = new Plane(Vector3.down, aabbMax); Plane pb = new Plane(Vector3.forward, aabbMin); Plane pf = new Plane(Vector3.back, aabbMax); volumePlanes[0] = new vec4(pl.normal.x, pl.normal.y, pl.normal.z, pl.distance); volumePlanes[1] = new vec4(pr.normal.x, pr.normal.y, pr.normal.z, pr.distance); volumePlanes[2] = new vec4(pd.normal.x, pd.normal.y, pd.normal.z, pd.distance); volumePlanes[3] = new vec4(pu.normal.x, pu.normal.y, pu.normal.z, pu.distance); volumePlanes[4] = new vec4(pb.normal.x, pb.normal.y, pb.normal.z, pb.distance); volumePlanes[5] = new vec4(pf.normal.x, pf.normal.y, pf.normal.z, pf.distance); //iterate each cell performing raycasted samples for (int i = 0; i < _settings.CellCount; i++) { #if UNITY_EDITOR if (i % progressInterval == 0) { EditorUtility.DisplayProgressBar(strProgressTitle, strProgress, i / (float)_settings.CellCount); } #endif vec3 positionWS = _settings.ToPositionWS(i, LocalToWorldNoScale); vec3 centerVoxelWS = positionWS + halfVoxel; NativeArray <float> rayLengths = new NativeArray <float>(raySamples, Allocator.TempJob); NativeArray <RaycastCommand> allRaycastsFront = new NativeArray <RaycastCommand>(raySamples, Allocator.TempJob); NativeArray <RaycastCommand> allRaycastsBack = new NativeArray <RaycastCommand>(raySamples, Allocator.TempJob); NativeArray <RaycastHit> frontHits = new NativeArray <RaycastHit>(raySamples, Allocator.TempJob); NativeArray <RaycastHit> backHits = new NativeArray <RaycastHit>(raySamples, Allocator.TempJob); //calculate the ray lengths, just so rays are clipped within the volume when raycasting CalculateRayLengths calcRayLengths = new CalculateRayLengths { Samples = sphereSamples, VolumePlanes = volumePlanes, RayLengths = rayLengths, RayLength = aabbMagnitude, RayOrigin = centerVoxelWS }; JobHandle rayLengthHandle = calcRayLengths.Schedule(raySamples, calcRayLengthBatchCount); //prepare raycasts front PrepareRaycastCommands frontPrc = new PrepareRaycastCommands { Samples = sphereSamples, RayLengths = rayLengths, LayerMask = LAYER_MASK_FRONT, Raycasts = allRaycastsFront, RayOrigin = centerVoxelWS, }; //prepare raycasts back PrepareRaycastCommands backPrc = new PrepareRaycastCommands { Samples = sphereSamples, RayLengths = rayLengths, LayerMask = LAYER_MASK_BACK, Raycasts = allRaycastsBack, RayOrigin = centerVoxelWS, }; //schedule front raycasts JobHandle prepareFrontHandle = frontPrc.Schedule( raySamples, prepareRaysBatchCount, rayLengthHandle); JobHandle scheduleFrontHandle = RaycastCommand.ScheduleBatch( allRaycastsFront, frontHits, raycastBatchCount, prepareFrontHandle); //schedule back raycasts JobHandle prepareBackHandle = backPrc.Schedule( raySamples, prepareRaysBatchCount, rayLengthHandle); JobHandle scheduleBackHandle = RaycastCommand.ScheduleBatch( allRaycastsBack, backHits, raycastBatchCount, prepareBackHandle); //combine handles JobHandle frontBackHandle = JobHandle.CombineDependencies(scheduleFrontHandle, scheduleBackHandle); //process results and put into current cell index ProcessHits processHits = new ProcessHits { FrontHits = frontHits, BackHits = backHits, Results = allResults.Slice(i, 1), }; JobHandle cellHandle = processHits.Schedule(frontBackHandle); cellHandle.Complete(); rayLengths.Dispose(); allRaycastsFront.Dispose(); allRaycastsBack.Dispose(); frontHits.Dispose(); backHits.Dispose(); } //for each cell //final distances CompareDistances compareDistances = new CompareDistances { Distances = distances, Results = allResults }; JobHandle compareDistancesHandle = compareDistances.Schedule(_settings.CellCount, compareBatchCount); compareDistancesHandle.Complete(); stopwatch.Stop(); Debug.Log("SDF bake completed in " + stopwatch.Elapsed.ToString("mm\\:ss\\.ff")); #if UNITY_EDITOR EditorUtility.ClearProgressBar(); #endif float[] distancesOut = new float[_settings.CellCount]; distances.CopyTo(distancesOut); //cleanup all the temp arrays distances.Dispose(); allResults.Dispose(); sphereSamples.Dispose(); //randomDirections.Dispose(); volumePlanes.Dispose(); foreach (var c in geoFront) { Object.DestroyImmediate(c.gameObject); } foreach (var c in geoBack) { Object.DestroyImmediate(c.gameObject); } //NOTE do not use max distance, instead use aabbMagnitude so distance fields are interchangeable bakeComplete?.Invoke(this, distancesOut, aabbMagnitude, passthrough); }
public void Generate() { if (mesh == null) { mesh = new Mesh(); } if (useSeed) { Random.InitState(randomSeed); } Vector3[] TEMPpositions = GeneratePositions(); amount = TEMPpositions.Length; NativeArray <Vector3> rayPositions = new NativeArray <Vector3>(amount, Allocator.TempJob); NativeArray <Vector3> vertices = new NativeArray <Vector3>(amount, Allocator.TempJob); NativeArray <int> indices = new NativeArray <int>(amount, Allocator.TempJob); NativeArray <Vector3> normals = new NativeArray <Vector3>(amount, Allocator.TempJob); NativeArray <Vector4> tangents = new NativeArray <Vector4>(amount, Allocator.TempJob); NativeArray <Color> colors = new NativeArray <Color>(amount, Allocator.TempJob); //Copy generated positions rayPositions.CopyFrom(TEMPpositions); for (int i = 0; i < amount; i++) { colors[i] = new Color( Random.Range(grassHeightRange.x, grassHeightRange.y), //Height 1, //This is used by the wind, but could be saved and used before the wind is applied Random.Range(grassWidthRange.x, grassWidthRange.y)); //Width; } var raycastCommands = new NativeArray <RaycastCommand>(amount, Allocator.TempJob); NativeArray <RaycastHit> raycastHits = new NativeArray <RaycastHit>(amount, Allocator.TempJob); var setupRaycastsJob = new PrepareRaycastCommands() { Raycasts = raycastCommands, Positions = rayPositions }; var setupDependency = setupRaycastsJob.Schedule(amount, 32, default(JobHandle)); var raycastDependency = RaycastCommand.ScheduleBatch(raycastCommands, raycastHits, 32, setupDependency); raycastDependency.Complete(); var setupMeshDataJob = new PrepareMeshData() { Vertices = vertices, Indices = indices, Normals = normals, Tangents = tangents, Colors = colors, hit = raycastHits, ObjPosition = transform.position }; var setupMeshDependency = setupMeshDataJob.Schedule(amount, 32, raycastDependency); setupMeshDependency.Complete(); mesh.Clear(); #if UNITY_2019_3_OR_NEWER mesh.SetVertices(vertices); mesh.SetIndices(indices, MeshTopology.Points, 0); mesh.SetNormals(normals); mesh.SetTangents(tangents); mesh.SetColors(colors); #else mesh.vertices = vertices.ToArray(); mesh.SetIndices(indices.ToArray(), MeshTopology.Points, 0); mesh.normals = normals.ToArray(); mesh.tangents = tangents.ToArray(); mesh.colors = colors.ToArray(); #endif meshFilter.sharedMesh = mesh; raycastCommands.Dispose(); raycastHits.Dispose(); rayPositions.Dispose(); vertices.Dispose(); indices.Dispose(); normals.Dispose(); tangents.Dispose(); colors.Dispose(); }