public void Render(Vector3 observerPos, float visibleDistance, Vector3[] frustumPlanesNormals, float[] frustumPlanesDistances) { #if DEBUG_BATCHES int batches = 0; int instancesCount = 0; #endif if (rebuild) { if (!Application.isPlaying || Time.frameCount - lastRebuildFrame > 10) { lastRebuildFrame = Time.frameCount; RebuildZoneRenderingLists(observerPos, visibleDistance); rebuild = false; } } for (int k = 0; k < batchedMeshes.count; k++) { BatchedMesh batchedMesh = batchedMeshes.values [k]; VoxelDefinition vd = batchedMesh.voxelDefinition; Mesh mesh = vd.mesh; Material material = batchedMesh.material; ShadowCastingMode shadowCastingMode = (vd.castShadows && env.enableShadows) ? ShadowCastingMode.On : ShadowCastingMode.Off; bool receiveShadows = vd.receiveShadows && env.enableShadows; for (int j = 0; j < batchedMesh.batches.count; j++) { Batch batch = batchedMesh.batches.values [j]; if (GeometryUtilityNonAlloc.TestPlanesAABB(frustumPlanesNormals, frustumPlanesDistances, ref batch.boundsMin, ref batch.boundsMax)) { Graphics.DrawMeshInstanced(mesh, 0, material, batch.matrices, batch.instancesCount, batch.materialPropertyBlock, shadowCastingMode, receiveShadows, env.layerVoxels); #if DEBUG_BATCHES batches++; instancesCount += batch.instancesCount; #endif } } } #if UNITY_EDITOR // required to fix a bug by which Draw Calls skyrocket in Stats windows when some voxel uses GPU instancing when "Render In SceneView" is enabled and the scene has just loaded in Editor UnityEditor.EditorUtility.SetDirty(env.gameObject); #endif #if DEBUG_BATCHES Debug.Log("Batches: " + batches + " Instances: " + instancesCount); #endif }
public void AddVoxel(VoxelChunk chunk, int voxelIndex, Vector3 position, Quaternion rotation, Vector3 scale) { VoxelDefinition voxelDefinition = env.voxelDefinitions [chunk.voxels [voxelIndex].typeIndex]; // Ensure there're batches for this voxel definition if (voxelDefinition.batchedIndex < 0) { BatchedMesh batchedMesh = new BatchedMesh(voxelDefinition); Material material = voxelDefinition.material; if (material == null) { material = defaultInstancingMaterial; } material.EnableKeyword(SKW_VOXELPLAY_GPU_INSTANCING); batchedMesh.material = material; voxelDefinition.batchedIndex = batchedMeshes.Add(batchedMesh); } // Add chunk and voxel to the rendering lists InstancedChunk instancedChunk; if (!instancedChunks.TryGetValue(chunk, out instancedChunk)) { instancedChunk = new InstancedChunk(chunk); instancedChunks.Add(chunk, instancedChunk); } InstancedVoxel instancedVoxel = new InstancedVoxel(); instancedVoxel.voxelDefinition = voxelDefinition; instancedVoxel.meshSize = voxelDefinition.mesh.bounds.size; instancedVoxel.position = position; instancedVoxel.matrix.SetTRS(position, rotation, scale); instancedVoxel.color = chunk.voxels [voxelIndex].color; instancedVoxel.packedLight = chunk.voxels [voxelIndex].packedLight; instancedChunk.instancedVoxels.Add(instancedVoxel); rebuild = true; }
void RebuildZoneRenderingLists(Vector3 observerPos, float visibleDistance) { // rebuild batch lists to be used in the rendering loop for (int k = 0; k < batchedMeshes.count; k++) { BatchedMesh batchedMesh = batchedMeshes.values [k]; batchedMesh.batches.Clear(); } float cullDistance = (visibleDistance * VoxelPlayEnvironment.CHUNK_SIZE) * (visibleDistance * VoxelPlayEnvironment.CHUNK_SIZE); for (int j = 0; j <= instancedChunks.lastIndex; j++) { InstancedChunk instancedChunk = instancedChunks.values [j]; if (instancedChunk == null) { continue; } // check if chunk is in area Vector3 chunkCenter = instancedChunk.chunk.position; if (FastVector.SqrDistance(ref chunkCenter, ref observerPos) > cullDistance) { continue; } // add instances to batch InstancedVoxel[] voxels = instancedChunk.instancedVoxels.values; for (int i = 0; i < instancedChunk.instancedVoxels.count; i++) { VoxelDefinition vd = voxels [i].voxelDefinition; BatchedMesh batchedMesh = batchedMeshes.values [vd.batchedIndex]; Batch batch = batchedMesh.batches.last; if (batch == null || batch.instancesCount >= Batch.MAX_INSTANCES) { batch = batchedMesh.batches.FetchDirty(); if (batch == null) { batch = new Batch(); batchedMesh.batches.Add(batch); } batch.Init(); } int pos = batch.instancesCount++; // just copying the matrix triggers lot of expensive memcpy() calls so we directly copy the fields // batch.matrices[pos] = voxels[i].matrix; batch.matrices [pos].m00 = voxels [i].matrix.m00; batch.matrices [pos].m01 = voxels [i].matrix.m01; batch.matrices [pos].m02 = voxels [i].matrix.m02; batch.matrices [pos].m03 = voxels [i].matrix.m03; batch.matrices [pos].m10 = voxels [i].matrix.m10; batch.matrices [pos].m11 = voxels [i].matrix.m11; batch.matrices [pos].m12 = voxels [i].matrix.m12; batch.matrices [pos].m13 = voxels [i].matrix.m13; batch.matrices [pos].m20 = voxels [i].matrix.m20; batch.matrices [pos].m21 = voxels [i].matrix.m21; batch.matrices [pos].m22 = voxels [i].matrix.m22; batch.matrices [pos].m23 = voxels [i].matrix.m23; batch.matrices [pos].m30 = voxels [i].matrix.m30; batch.matrices [pos].m31 = voxels [i].matrix.m31; batch.matrices [pos].m32 = voxels [i].matrix.m32; batch.matrices [pos].m33 = voxels [i].matrix.m33; batch.colorsAndLight [pos].x = voxels [i].color.r / 255f; batch.colorsAndLight [pos].y = voxels [i].color.g / 255f; batch.colorsAndLight [pos].z = voxels [i].color.b / 255f; batch.colorsAndLight [pos].w = voxels [i].packedLight; batch.UpdateBounds(voxels[i].position, voxels[i].meshSize); } } for (int k = 0; k < batchedMeshes.count; k++) { BatchedMesh batchedMesh = batchedMeshes.values [k]; for (int j = 0; j < batchedMesh.batches.count; j++) { Batch batch = batchedMesh.batches.values [j]; batch.materialPropertyBlock.SetVectorArray("_TintColor", batch.colorsAndLight); } } }