public override float GenerateLayer(Chunk chunk, int layerIndex, int x, int z, float heightSoFar, float strength) { LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); NoiseItem ni = pools.noiseItems[layerIndex]; // Calculate height to add and sum it with the min height (because the height of this // layer should fluctuate between minHeight and minHeight+the max noise) and multiply // it by strength so that a fraction of the result that gets used can be decided float heightToAdd = ni.noiseGen.Interpolate(x, z, ni.lookupTable); heightToAdd += MinHeight; heightToAdd *= strength; // Absolute layers add from the minY and up but if the layer height is lower than // the existing terrain there's nothing to add so just return the initial value if (heightToAdd > heightSoFar) { SetBlocks(chunk, x, z, (int)heightSoFar, (int)heightToAdd, blockToPlace); //Return the height of this layer from minY as this is the new height of the column return(heightToAdd); } return(heightSoFar); }
public override void BuildFace(Chunk chunk, Vector3[] vertices, Color32[] palette, ref BlockFace face, bool rotated) { bool backFace = DirectionUtils.IsBackface(face.side); LocalPools pools = chunk.pools; var verts = pools.Vector3ArrayPool.PopExact(4); var cols = pools.Color32ArrayPool.PopExact(4); { verts[0] = vertices[0]; verts[1] = vertices[1]; verts[2] = vertices[2]; verts[3] = vertices[3]; cols[0] = palette[face.block.Type]; cols[1] = palette[face.block.Type]; cols[2] = palette[face.block.Type]; cols[3] = palette[face.block.Type]; BlockUtils.AdjustColors(chunk, cols, face.light); magicMeshConfig.AddFace(verts, cols, backFace); } pools.Color32ArrayPool.Push(cols); pools.Vector3ArrayPool.Push(verts); }
public override void PostProcess(Chunk chunk, int layerIndex) { LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); NoiseItem ni = pools.noiseItems[layerIndex]; pools.floatArrayPool.Push(ni.lookupTable); }
public virtual void BuildMesh( Map map, RenderGeometryBatcher batcher, int offsetX, int offsetY, int offsetZ, int minX, int maxX, int minY, int maxY, int minZ, int maxZ, int lod, LocalPools pools ) { throw new NotImplementedException(); }
public TaskPool() { Pools = new LocalPools(); m_items = new List <TaskPoolItem>(); m_event = new AutoResetEvent(false); m_thread = new Thread(ThreadFunc) { IsBackground = true }; }
public Chunk() : base(6) { Blocks = new BlockStorage(); m_threadID = Globals.WorkPool.GetThreadIDFromIndex(s_id++); Pools = Globals.WorkPool.GetPool(m_threadID); m_drawCallBatcher = new DrawCallBatcher(Globals.CubeMeshBuilder, this); BBoxVertices = new List <Vector3>(); BBoxVerticesTransformed = new List <Vector3>(); Reset(); }
/// <summary> /// Finalize the draw calls /// </summary> public void Commit() { ReleaseOldData(); // No data means there's no mesh to build if (m_renderBuffers[0].IsEmpty()) { return; } for (int i = 0; i < m_renderBuffers.Count; i++) { var go = GameObjectProvider.PopObject(GOPChunk); Assert.IsTrue(go != null); if (go != null) { Mesh mesh = Globals.Pools.MeshPool.Pop(); Assert.IsTrue(mesh.vertices.Length <= 0); m_meshBuilder.BuildMesh(mesh, m_renderBuffers[i]); MeshFilter filter = go.GetComponent <MeshFilter>(); filter.sharedMesh = null; filter.sharedMesh = mesh; filter.transform.position = Vector3.zero; m_drawCalls.Add(go); m_drawCallRenderers.Add(go.GetComponent <Renderer>()); } } // Make vertex data available again. We need to make this a task because our pooling system works on a per-thread // basis. Therefore, all Push()-es need to be called on the same thread as their respective Pop()-s. m_chunk.EnqueueGenericTask( () => { LocalPools pools = m_chunk.Pools; for (int i = 0; i < m_renderBuffers.Count; i++) { RenderBuffer buffer = m_renderBuffers[i]; for (int v = 0; v < buffer.Vertices.Count; v++) { pools.PushVertexData(buffer.Vertices[v]); } buffer.Clear(); } }); }
public Chunk() { Blocks = new BlockStorage(); // Associate Chunk with a certain thread and make use of its memory pool // This is necessary in order to have lock-free caches ThreadID = Globals.WorkPool.GetThreadIDFromIndex(s_id++); Pools = Globals.WorkPool.GetPool(ThreadID); RenderGeometryBatcher = new RenderGeometryBatcher(Globals.CubeMeshGeometryBuilder, this); BBoxVertices = new List <Vector3>(); BBoxVerticesTransformed = new List <Vector3>(); StateManager = new ChunkStateManagerClient(this); }
public override float GetHeight(Chunk chunk, int layerIndex, int x, int z, float heightSoFar, float strength) { LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); NoiseItem ni = pools.noiseItems[layerIndex]; // Calculate height to add and sum it with the min height (because the height of this // layer should fluctuate between minHeight and minHeight+the max noise) and multiply // it by strength so that a fraction of the result that gets used can be decided float heightToAdd = ni.noiseGen.Interpolate(x, z, ni.lookupTable); heightToAdd += MinHeight; heightToAdd *= strength; return(heightSoFar + heightToAdd); }
public override void PreProcess(Chunk chunk, int layerIndex) { LocalPools pools = Globals.WorkPool.GetPool(chunk.ThreadID); NoiseItem ni = pools.noiseItems[layerIndex]; ni.noiseGen.SetInterpBitStep(Env.CHUNK_SIZE_WITH_PADDING, 2); ni.lookupTable = pools.floatArrayPool.Pop((ni.noiseGen.Size + 1) * (ni.noiseGen.Size + 1)); #if (UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN) && ENABLE_FASTSIMD float[] noiseSet = pools.floatArrayPool.Pop(ni.noiseGen.Size * ni.noiseGen.Size * ni.noiseGen.Size); // Generate SIMD noise int offsetShift = Env.CHUNK_POW - ni.noiseGen.Step; int xStart = (chunk.Pos.x * Env.CHUNK_SIZE) << offsetShift; int yStart = (chunk.Pos.y * Env.CHUNK_SIZE) << offsetShift; int zStart = (chunk.Pos.z * Env.CHUNK_SIZE) << offsetShift; float scaleModifier = 1 << ni.noiseGen.Step; noiseSIMD.Noise.FillNoiseSet(noiseSet, xStart, yStart, zStart, ni.noiseGen.Size, ni.noiseGen.Size, ni.noiseGen.Size, scaleModifier); // Generate a lookup table int i = 0; for (int z = 0; z < ni.noiseGen.Size; z++) { for (int x = 0; x < ni.noiseGen.Size; x++) { ni.lookupTable[i++] = NoiseUtilsSIMD.GetNoise(noiseSet, ni.noiseGen.Size, x, 0, z, amplitude, noise.Gain); } } pools.floatArrayPool.Push(noiseSet); #else int xOffset = chunk.Pos.x; int zOffset = chunk.Pos.z; // Generate a lookup table int i = 0; for (int z = 0; z < ni.noiseGen.Size; z++) { float zf = (z << ni.noiseGen.Step) + zOffset; for (int x = 0; x < ni.noiseGen.Size; x++) { float xf = (x << ni.noiseGen.Step) + xOffset; ni.lookupTable[i++] = NoiseUtils.GetNoise(noise.Noise, xf, 0, zf, 1f, amplitude, noise.Gain); } } #endif }
public override void BuildFace(Chunk chunk, Vector3[] vertices, Color32[] palette, ref BlockFace face, bool rotated) { bool backFace = DirectionUtils.IsBackface(face.side); int d = DirectionUtils.Get(face.side); LocalPools pools = chunk.pools; var verts = pools.Vector3ArrayPool.PopExact(4); var cols = pools.Color32ArrayPool.PopExact(4); { if (vertices == null) { Vector3 pos = face.pos; verts[0] = pos + BlockUtils.PaddingOffsets[d][0]; verts[1] = pos + BlockUtils.PaddingOffsets[d][1]; verts[2] = pos + BlockUtils.PaddingOffsets[d][2]; verts[3] = pos + BlockUtils.PaddingOffsets[d][3]; cols[0] = colors[d]; cols[1] = colors[d]; cols[2] = colors[d]; cols[3] = colors[d]; } else { verts[0] = vertices[0]; verts[1] = vertices[1]; verts[2] = vertices[2]; verts[3] = vertices[3]; cols[0] = colors[d]; cols[1] = colors[d]; cols[2] = colors[d]; cols[3] = colors[d]; } BlockUtils.AdjustColors(chunk, cols, face.light); RenderGeometryBatcher batcher = chunk.GeometryHandler.Batcher; batcher.AddFace(face.materialID, verts, cols, backFace); } pools.Color32ArrayPool.Push(cols); pools.Vector3ArrayPool.Push(verts); }
public static void Build(List <Vector3> targetBuffer, ref Bounds bounds, LocalPools pools) { Vector3[] vertices = pools.PopVector3Array(24); // Vertices as they are seen in faces //Front vertices[0] = new Vector3(bounds.max.x, bounds.min.y, bounds.min.z); vertices[1] = new Vector3(bounds.max.x, bounds.max.y, bounds.min.z); vertices[2] = new Vector3(bounds.min.x, bounds.max.y, bounds.min.z); vertices[3] = new Vector3(bounds.min.x, bounds.min.y, bounds.min.z); //Back vertices[4] = new Vector3(bounds.min.x, bounds.min.y, bounds.max.z); vertices[5] = new Vector3(bounds.min.x, bounds.max.y, bounds.max.z); vertices[6] = new Vector3(bounds.max.x, bounds.max.y, bounds.max.z); vertices[7] = new Vector3(bounds.max.x, bounds.min.y, bounds.max.z); //Right vertices[8] = new Vector3(bounds.max.x, bounds.min.y, bounds.max.z); vertices[9] = new Vector3(bounds.max.x, bounds.max.y, bounds.max.z); vertices[10] = new Vector3(bounds.max.x, bounds.max.y, bounds.min.z); vertices[11] = new Vector3(bounds.max.x, bounds.min.y, bounds.min.z); //Left vertices[12] = new Vector3(bounds.min.x, bounds.min.y, bounds.min.z); vertices[13] = new Vector3(bounds.min.x, bounds.max.y, bounds.min.z); vertices[14] = new Vector3(bounds.min.x, bounds.max.y, bounds.max.z); vertices[15] = new Vector3(bounds.min.x, bounds.min.y, bounds.max.z); //Top vertices[16] = new Vector3(bounds.max.x, bounds.max.y, bounds.min.z); vertices[17] = new Vector3(bounds.max.x, bounds.max.y, bounds.max.z); vertices[18] = new Vector3(bounds.min.x, bounds.max.y, bounds.max.z); vertices[19] = new Vector3(bounds.min.x, bounds.max.y, bounds.min.z); //Bottom vertices[20] = new Vector3(bounds.min.x, bounds.min.y, bounds.min.z); vertices[21] = new Vector3(bounds.min.x, bounds.min.y, bounds.max.z); vertices[22] = new Vector3(bounds.max.x, bounds.min.y, bounds.max.z); vertices[23] = new Vector3(bounds.max.x, bounds.min.y, bounds.min.z); // Add vertices to buffer for (int i = 0; i < 24; i++) { targetBuffer.Add(vertices[i]); } pools.PushVector3Array(vertices); }
public TaskPool() { Pools = new LocalPools(); m_items = new List <ITaskPoolItem>(); m_itemsP = new List <ITaskPoolItem>(); m_itemsTmp = new List <ITaskPoolItem>(); m_itemsTmpP = new List <ITaskPoolItem>(); m_event = new AutoResetEvent(false); m_thread = new Thread(ThreadFunc) { IsBackground = true }; m_stop = false; m_hasPriorityItems = false; m_curr = m_max = m_currP = m_maxP = 0; }
public override void BuildBlock(Chunk chunk, ref Vector3Int localPos, int materialID) { LocalPools pools = chunk.pools; RenderGeometryBatcher batcher = chunk.GeometryHandler.Batcher; // Using the block positions hash is much better for random numbers than saving the offset and height in the block data int hash = localPos.GetHashCode(); float blockHeight = (hash & 63) * coef * Env.BlockSize; hash *= 39; float offsetX = (hash & 63) * coef * Env.BlockSizeHalf - Env.BlockSizeHalf * 0.5f; hash *= 39; float offsetZ = (hash & 63) * coef * Env.BlockSizeHalf - Env.BlockSizeHalf * 0.5f; // Converting the position to a vector adjusts it based on block size and gives us real world coordinates for x, y and z Vector3 vPos = localPos; vPos += new Vector3(offsetX, 0, offsetZ); float x1 = vPos.x - BlockUtils.blockPadding; float x2 = vPos.x + BlockUtils.blockPadding + Env.BlockSize; float y1 = vPos.y - BlockUtils.blockPadding; float y2 = vPos.y + BlockUtils.blockPadding + blockHeight; float z1 = vPos.z - BlockUtils.blockPadding; float z2 = vPos.z + BlockUtils.blockPadding + Env.BlockSize; var verts = pools.Vector3ArrayPool.PopExact(4); var uvs = pools.Vector2ArrayPool.PopExact(4); var colors = pools.Color32ArrayPool.PopExact(4); BlockUtils.PrepareTexture(chunk, ref localPos, uvs, Direction.north, texture, false); // TODO: How do I make sure that if I supply no color value, white is used? // TODO: These colors could be removed and memory would be saved { colors[0] = new Color32(255, 255, 255, 255); colors[1] = new Color32(255, 255, 255, 255); colors[2] = new Color32(255, 255, 255, 255); colors[3] = new Color32(255, 255, 255, 255); } { verts[0] = new Vector3(x1, y1, z2); verts[1] = new Vector3(x1, y2, z2); verts[2] = new Vector3(x2, y2, z1); verts[3] = new Vector3(x2, y1, z1); batcher.AddFace(materialID, verts, colors, uvs, false); } { verts[0] = new Vector3(x2, y1, z1); verts[1] = new Vector3(x2, y2, z1); verts[2] = new Vector3(x1, y2, z2); verts[3] = new Vector3(x1, y1, z2); batcher.AddFace(materialID, verts, colors, uvs, false); } { verts[0] = new Vector3(x2, y1, z2); verts[1] = new Vector3(x2, y2, z2); verts[2] = new Vector3(x1, y2, z1); verts[3] = new Vector3(x1, y1, z1); batcher.AddFace(materialID, verts, colors, uvs, false); } { verts[0] = new Vector3(x1, y1, z1); verts[1] = new Vector3(x1, y2, z1); verts[2] = new Vector3(x2, y2, z2); verts[3] = new Vector3(x2, y1, z2); batcher.AddFace(materialID, verts, colors, uvs, false); } pools.Color32ArrayPool.Push(colors); pools.Vector2ArrayPool.Push(uvs); pools.Vector3ArrayPool.Push(verts); }
public void Build(RenderGeometryBatcher batcher, ref BlockData block, BlockFace face, bool backFace, ref Vector3[] vecs, LocalPools pools) { int iface = (int)face; // Randomize the color a bit Color32 color = BlockDatabase.GetBlockInfo(block.BlockType).Color; if (block.BlockType != BlockType.None) { float value = m_noise.GetValue(vecs[0] + vecs[1] + vecs[2] + vecs[3]); // -1.0f..1.0f float noise = (255.0f * value) * 0.02f; // Deviation of 0.02f points from the original int n = (int)noise; byte r = (byte)Math.Max(0, Math.Min(color.r + n, 255)); byte g = (byte)Math.Max(0, Math.Min(color.g + n, 255)); byte b = (byte)Math.Max(0, Math.Min(color.b + n, 255)); color = new Color32(r, g, b, color.a); } VertexData[] vertexData = pools.PopVertexDataArray(4); VertexDataFixed[] vertexDataFixed = pools.PopVertexDataFixedArray(4); { for (int i = 0; i < 4; i++) { vertexData[i] = pools.PopVertexData(); } for (int i = 0; i < 4; i++) { vertexData[i].Vertex = vecs[i]; vertexData[i].Color = color; vertexData[i].UV = AddFaceUV(i, GetTexture(iface), backFace); vertexData[i].Normal = SNormals[iface][i]; } for (int i = 0; i < 4; i++) { vertexDataFixed[i] = VertexDataUtils.ClassToStruct(vertexData[i]); } batcher.AddFace(vertexDataFixed, backFace); for (int i = 0; i < 4; i++) { pools.PushVertexData(vertexData[i]); } } pools.PushVertexDataFixedArray(vertexDataFixed); pools.PushVertexDataArray(vertexData); }
public bool DoDecompression() { LocalPools pools = Chunk.pools; var provider = Chunk.world.blockProvider; if (IsDifferential) { if (m_positionsBytes != null && m_blocksBytes != null) { int blockPosSize = StructSerialization.TSSize <BlockPos> .ValueSize; int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; m_positionsModified = new BlockPos[m_positionsBytes.Length / blockPosSize]; m_blocksModified = new BlockData[m_blocksBytes.Length / blockDataSize]; int i, j; unsafe { // Extract positions fixed(byte *pSrc = m_positionsBytes) { for (i = 0, j = 0; j < m_positionsModified.Length; i += blockPosSize, j++) { m_positionsModified[j] = *(BlockPos *)&pSrc[i]; } } // Extract block data fixed(byte *pSrc = m_blocksBytes) { for (i = 0, j = 0; j < m_blocksModified.Length; i += blockDataSize, j++) { BlockData *bd = (BlockData *)&pSrc[i]; // Convert global block types into internal optimized version ushort type = provider.GetTypeFromTypeInConfig(bd->Type); m_blocksModified[j] = new BlockData(type, bd->Solid); } } } } } else { m_positionsBytes = null; if (m_blocksBytes != null) { int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; int requestedByteSize = Env.ChunkSizePow3 * blockDataSize; // Pop a large enough buffers from the pool var bytes = pools.ByteArrayPool.Pop(requestedByteSize); { // Decompress data int decompressedLength = CLZF2.lzf_decompress(m_blocksBytes, m_blocksBytes.Length, ref bytes); if (decompressedLength != Env.ChunkSizePow3 * blockDataSize) { m_blocksBytes = null; return(false); } // Fill chunk with decompressed data ChunkBlocks blocks = Chunk.blocks; int i = 0; unsafe { fixed(byte *pSrc = bytes) { int index = Helpers.ZeroChunkIndex; int yOffset = Env.ChunkSizeWithPaddingPow2 - Env.ChunkSize * Env.ChunkSizeWithPadding; int zOffset = Env.ChunkSizeWithPadding - Env.ChunkSize; for (int y = 0; y < Env.ChunkSize; ++y, index += yOffset) { for (int z = 0; z < Env.ChunkSize; ++z, index += zOffset) { for (int x = 0; x < Env.ChunkSize; ++x, i += blockDataSize, ++index) { BlockData *bd = (BlockData *)&pSrc[i]; // Convert global block type into internal optimized version ushort type = provider.GetTypeFromTypeInConfig(bd->Type); blocks.SetRaw(index + x, new BlockData(type, bd->Solid)); } } } } } } // Return our temporary buffer back to the pool pools.ByteArrayPool.Push(bytes); } } // We no longer need the temporary buffers m_positionsBytes = null; m_blocksBytes = null; return(true); }
public override void BuildMesh( Map map, DrawCallBatcher batcher, int offsetX, int offsetY, int offsetZ, int minX, int maxX, int minY, int maxY, int minZ, int maxZ, int lod, LocalPools pools ) { BlockFace face = 0; int stepSize = 1 << lod; Assert.IsTrue(lod <= EngineSettings.ChunkConfig.LogSize); // LOD can't be bigger than chunk size int width = EngineSettings.ChunkConfig.Size >> lod; int[] mins = { minX >> lod, minY >> lod, minZ >> lod }; int[] maxes = { maxX >> lod, maxY >> lod, maxZ >> lod }; int[] x = { 0, 0, 0 }; // Relative position of a block int[] xx = { 0, 0, 0 }; // Relative position of a block after applying lod int[] q = { 0, 0, 0 }; // Direction in which we compare neighbors when building mask (q[d] is our current direction) int[] du = { 0, 0, 0 }; // Width in a given dimension (du[u] is our current dimension) int[] dv = { 0, 0, 0 }; // Height in a given dimension (dv[v] is our current dimension) int[] s = { map.VoxelLogScaleX, map.VoxelLogScaleY, map.VoxelLogScaleZ }; // Scale in each dimension BlockData[] mask = pools.PopBlockDataArray(width * width); Vector3[] vecs = pools.PopVector3Array(4); // Iterate over 3 dimensions. Once for front faces, once for back faces for (int dd = 0; dd < 2 * 3; dd++) { int d = dd % 3; int u = (d + 1) % 3; int v = (d + 2) % 3; x[0] = 0; x[1] = 0; x[2] = 0; q[0] = 0; q[1] = 0; q[2] = 0; q[d] = stepSize << s[d]; // Determine which side we're meshing bool backFace = dd < 3; switch (dd) { case 0: face = BlockFace.Left; break; case 3: face = BlockFace.Right; break; case 1: face = BlockFace.Bottom; break; case 4: face = BlockFace.Top; break; case 2: face = BlockFace.Back; break; case 5: face = BlockFace.Front; break; } // Move through the dimension from front to back for (x[d] = mins[d] - 1; x[d] <= maxes[d];) { // Compute the mask int n = 0; for (x[v] = 0; x[v] < mins[v]; x[v]++) { for (x[u] = 0; x[u] < width; x[u]++) { mask[n++] = BlockData.Air; } } for (x[v] = mins[v]; x[v] <= maxes[v]; x[v]++) { for (x[u] = 0; x[u] < mins[u]; x[u]++) { mask[n++] = BlockData.Air; } for (x[u] = mins[u]; x[u] <= maxes[u]; x[u]++) { int realX = (x[0] << lod << s[0]) + offsetX; int realY = (x[1] << lod << s[1]) + offsetY; int realZ = (x[2] << lod << s[2]) + offsetZ; BlockData voxelFace0 = map.GetBlock(realX, realY, realZ); BlockData voxelFace1 = map.GetBlock(realX + q[0], realY + q[1], realZ + q[2]); mask[n++] = (voxelFace0.IsSolid() && voxelFace1.IsSolid()) ? BlockData.Air : (backFace ? voxelFace1 : voxelFace0); } for (x[u] = maxes[u] + 1; x[u] < width; x[u]++) { mask[n++] = BlockData.Air; } } for (x[v] = maxes[v] + 1; x[v] < width; x[v]++) { for (x[u] = 0; x[u] < width; x[u]++) { mask[n++] = BlockData.Air; } } x[d]++; n = 0; // Build faces from the mask if it's possible int j; for (j = 0; j < width; j++) { int i; for (i = 0; i < width;) { if (mask[n].IsEmpty()) { i++; n++; continue; } BlockType type = mask[n].BlockType; // Compute width int w; for (w = 1; i + w < width && mask[n + w].BlockType == type; w++) { } // Compute height bool done = false; int k; int h; for (h = 1; j + h < width; h++) { for (k = 0; k < w; k++) { if ( mask[n + k + h * width].IsEmpty() || mask[n + k + h * width].BlockType != type ) { done = true; break; } } if (done) { break; } } // Determine whether we really want to build this face // TODO: Skip bottom faces at the bottom of the world bool buildFace = true; if (buildFace) { // Prepare face coordinates and dimensions x[u] = i; x[v] = j; xx[0] = ((x[0] << lod) << s[0]) + offsetX; xx[1] = ((x[1] << lod) << s[1]) + offsetY; xx[2] = ((x[2] << lod) << s[2]) + offsetZ; du[0] = du[1] = du[2] = 0; dv[0] = dv[1] = dv[2] = 0; du[u] = (w << lod) << s[u]; dv[v] = (h << lod) << s[v]; // Face vertices Vector3Int v1 = new Vector3Int( xx[0], xx[1], xx[2] ); Vector3Int v2 = new Vector3Int( xx[0] + du[0], xx[1] + du[1], xx[2] + du[2] ); Vector3Int v3 = new Vector3Int( xx[0] + du[0] + dv[0], xx[1] + du[1] + dv[1], xx[2] + du[2] + dv[2] ); Vector3Int v4 = new Vector3Int( xx[0] + dv[0], xx[1] + dv[1], xx[2] + dv[2] ); // Face vertices transformed to world coordinates // 0--1 // | | // | | // 3--2 vecs[0] = new Vector3(v4.X, v4.Y, v4.Z); vecs[1] = new Vector3(v3.X, v3.Y, v3.Z); vecs[2] = new Vector3(v2.X, v2.Y, v2.Z); vecs[3] = new Vector3(v1.X, v1.Y, v1.Z); // Build the face IFaceBuilder builder = BlockDatabase.GetFaceBuilder(type); builder.Build(batcher, ref mask[n], face, backFace, ref vecs, pools); } // Zero out the mask int l; for (l = 0; l < h; ++l) { for (k = 0; k < w; ++k) { mask[n + k + l * width] = BlockData.Air; } } i += w; n += w; } } } } pools.PushBlockDataArray(mask); pools.PushVector3Array(vecs); }
public void Build(Chunk chunk, int minX, int maxX, int minY, int maxY, int minZ, int maxZ) { WorldBlocks blocks = chunk.world.blocks; const int stepSize = 1; const int width = Env.ChunkSize; int[] mins = { minX, minY, minZ }; int[] maxes = { maxX, maxY, maxZ }; int[] x = { 0, 0, 0 }; // Relative position of a block int[] q = { 0, 0, 0 }; // Direction in which we compare neighbors when building mask (q[d] is our current direction) int[] du = { 0, 0, 0 }; // Width in a given dimension (du[u] is our current dimension) int[] dv = { 0, 0, 0 }; // Height in a given dimension (dv[v] is our current dimension) bool[] mask = chunk.pools.PopBoolArray(width * width); Vector3[] vecs = chunk.pools.PopVector3Array(4); // Iterate over 3 dimensions. Once for front faces, once for back faces for (int dd = 0; dd < 2 * 3; dd++) { int d = dd % 3; int u = (d + 1) % 3; int v = (d + 2) % 3; x[0] = 0; x[1] = 0; x[2] = 0; q[0] = 0; q[1] = 0; q[2] = 0; q[d] = stepSize; // Determine which side we're meshing Direction dir = Direction.west; switch (dd) { // Back faces //case 0: dir = Direction.west; break; case 1: dir = Direction.down; break; case 2: dir = Direction.south; break; // Front faces case 3: dir = Direction.east; break; case 4: dir = Direction.up; break; case 5: dir = Direction.north; break; } bool backFace = DirectionUtils.Backface(dir); // Move through the dimension from front to back for (x[d] = mins[d] - 1; x[d] <= maxes[d];) { // Compute the mask int n = 0; for (x[v] = 0; x[v] < mins[v]; x[v]++) { for (x[u] = 0; x[u] < width; x[u]++) { mask[n++] = false; } } for (x[v] = mins[v]; x[v] <= maxes[v]; x[v]++) { for (x[u] = 0; x[u] < mins[u]; x[u]++) { mask[n++] = false; } for (x[u] = mins[u]; x[u] <= maxes[u]; x[u]++) { int realX = chunk.pos.x + x[0]; int realY = chunk.pos.y + x[1]; int realZ = chunk.pos.z + x[2]; bool voxelFace0 = blocks.GetBlock(new Vector3Int(realX, realY, realZ)).canBeWalkedOn; bool voxelFace1 = blocks.GetBlock(new Vector3Int(realX + q[0], realY + q[1], realZ + q[2])).canBeWalkedOn; mask[n++] = (!voxelFace0 || !voxelFace1) && (backFace ? voxelFace1 : voxelFace0); } for (x[u] = maxes[u] + 1; x[u] < width; x[u]++) { mask[n++] = false; } } for (x[v] = maxes[v] + 1; x[v] < width; x[v]++) { for (x[u] = 0; x[u] < width; x[u]++) { mask[n++] = false; } } x[d]++; n = 0; // Build faces from the mask if it's possible int j; for (j = 0; j < width; j++) { int i; for (i = 0; i < width;) { if (mask[n] == false) { i++; n++; continue; } bool type = mask[n]; // Compute width int w; for (w = 1; i + w < width && mask[n + w] == type; w++) { } // Compute height bool done = false; int k; int h; for (h = 1; j + h < width; h++) { for (k = 0; k < w; k++) { if (mask[n + k + h * width] == false || mask[n + k + h * width] != type) { done = true; break; } } if (done) { break; } } // Determine whether we really want to build this face // TODO: Skip bottom faces at the bottom of the world const bool buildFace = true; if (buildFace) { // Prepare face coordinates and dimensions x[u] = i; x[v] = j; du[0] = du[1] = du[2] = 0; dv[0] = dv[1] = dv[2] = 0; du[u] = w; dv[v] = h; // Face vertices transformed to world coordinates // 0--1 // | | // | | // 3--2 vecs[0] = new Vector3(x[0], x[1], x[2]) - BlockUtils.HalfBlockVector; vecs[1] = new Vector3(x[0] + du[0], x[1] + du[1], x[2] + du[2]) - BlockUtils.HalfBlockVector; vecs[2] = new Vector3(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2]) - BlockUtils.HalfBlockVector; vecs[3] = new Vector3(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]) - BlockUtils.HalfBlockVector; { LocalPools pool = chunk.pools; VertexData[] vertexData = pool.PopVertexDataArray(4); VertexDataFixed[] vertexDataFixed = pool.PopVertexDataFixedArray(4); { for (int ii = 0; ii < 4; ii++) { vertexData[ii] = pool.PopVertexData(); vertexData[ii].Vertex = vecs[ii]; vertexDataFixed[ii] = VertexDataUtils.ClassToStruct(vertexData[ii]); } chunk.ChunkColliderGeometryHandler.Batcher.AddFace(vertexDataFixed, backFace); for (int ii = 0; ii < 4; ii++) { pool.PushVertexData(vertexData[ii]); } } pool.PushVertexDataFixedArray(vertexDataFixed); pool.PushVertexDataArray(vertexData); } } // Zero out the mask int l; for (l = 0; l < h; ++l) { for (k = 0; k < w; ++k) { mask[n + k + l * width] = false; } } i += w; n += w; } } } } chunk.pools.PushBoolArray(mask); chunk.pools.PushVector3Array(vecs); }
public static void GenerateTangents(this RenderBuffer buffer, LocalPools pools) { var vertices = buffer.Vertices; var triangles = buffer.Triangles; var tan1 = pools.PopVector3Array(vertices.Count); var tan2 = pools.PopVector3Array(vertices.Count); for (int t = 0; t < triangles.Count; t += 3) { int i1 = triangles[t + 0]; int i2 = triangles[t + 1]; int i3 = triangles[t + 2]; VertexData vd1 = vertices[i1]; VertexData vd2 = vertices[i2]; VertexData vd3 = vertices[i3]; Vector3 v1 = vd1.Vertex; Vector3 v2 = vd2.Vertex; Vector3 v3 = vd3.Vertex; Vector2 w1 = vd1.UV; Vector2 w2 = vd2.UV; Vector2 w3 = vd3.UV; float x1 = v2.x - v1.x; float y1 = v2.y - v1.y; float z1 = v2.z - v1.z; float x2 = v3.x - v1.x; float y2 = v3.y - v1.y; float z2 = v3.z - v1.z; float s1 = w2.x - w1.x; float s2 = w3.x - w1.x; float t1 = w2.y - w1.y; float t2 = w3.y - w1.y; // Avoid division by zero float div = s1 * t2 - s2 * t1; float r = (Mathf.Abs(div) > Mathf.Epsilon) ? (1f / div) : 0f; Vector3 sdir = new Vector3((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3 tdir = new Vector3((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } for (int v = 0; v < vertices.Count; ++v) { VertexData vd = vertices[v]; Vector3 n = vd.Normal; Vector3 t = tan1[v]; //Vector3 tmp = (t - n*Vector3.Dot(n, t)).normalized; //tangents[v] = new Vector4(tmp.x, tmp.y, tmp.z); Vector3.OrthoNormalize(ref n, ref t); vd.Tangent = new Vector4( t.x, t.y, t.z, (Vector3.Dot(Vector3.Cross(n, t), tan2[v]) < 0.0f) ? -1.0f : 1.0f ); tan1[v] = Vector3.zero; tan2[v] = Vector3.zero; } pools.PushVector3Array(tan1); pools.PushVector3Array(tan2); }
public bool DoCompression() { if (Features.UseDifferentialSerialization) { if (m_blocksModified != null) { var provider = Chunk.world.blockProvider; int blockPosSize = StructSerialization.TSSize <BlockPos> .ValueSize; int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; int posLenBytes = m_blocksModified.Length * blockPosSize; int blkLenBytes = m_blocksModified.Length * blockDataSize; m_positionsBytes = new byte[posLenBytes]; m_blocksBytes = new byte[blkLenBytes]; unsafe { // Pack positions to a byte array fixed(byte *pDst = m_positionsBytes) { for (int i = 0, j = 0; i < m_blocksModified.Length; i++, j += blockPosSize) { *(BlockPos *)&pDst[j] = m_positionsModified[i]; } } // Pack block data to a byte array fixed(BlockData *pBD = m_blocksModified) fixed(byte *pDst = m_blocksBytes) { for (int i = 0, j = 0; i < m_blocksModified.Length; i++, j += blockDataSize) { BlockData *bd = &pBD[i]; // Convert block types from internal optimized version into global types ushort typeInConfig = provider.GetConfig(bd->Type).typeInConfig; *(BlockData *)&pDst[j] = new BlockData(typeInConfig, bd->Solid); } } } } else { m_positionsBytes = null; m_blocksBytes = null; } } else { LocalPools pools = Chunk.pools; var provider = Chunk.world.blockProvider; int blockDataSize = StructSerialization.TSSize <BlockData> .ValueSize; int requestedByteSize = Env.ChunkSizePow3 * blockDataSize; // Pop large enough buffers from the pool byte[] tmp = pools.ByteArrayPool.Pop(requestedByteSize); byte[] bytesCompressed = pools.ByteArrayPool.Pop(requestedByteSize); { ChunkBlocks blocks = Chunk.blocks; int i = 0; int index = Helpers.ZeroChunkIndex; int yOffset = Env.ChunkSizeWithPaddingPow2 - Env.ChunkSize * Env.ChunkSizeWithPadding; int zOffset = Env.ChunkSizeWithPadding - Env.ChunkSize; for (int y = 0; y < Env.ChunkSize; ++y, index += yOffset) { for (int z = 0; z < Env.ChunkSize; ++z, index += zOffset) { for (int x = 0; x < Env.ChunkSize; ++x, i += blockDataSize, ++index) { // Convert block types from internal optimized version into global types BlockData bd = blocks.Get(index + x); ushort typeInConfig = provider.GetConfig(bd.Type).typeInConfig; // Write updated block data to destination buffer unsafe { fixed(byte *pDst = tmp) { *(BlockData *)&pDst[i] = new BlockData(typeInConfig, bd.Solid); } } } } } // Compress bytes int blkLenBytes = CLZF2.lzf_compress(tmp, requestedByteSize, ref bytesCompressed); m_blocksBytes = new byte[blkLenBytes]; // Copy data from a temporary buffer to block buffer Array.Copy(bytesCompressed, 0, m_blocksBytes, 0, blkLenBytes); } // Return our temporary buffer back to the pool pools.ByteArrayPool.Push(bytesCompressed); pools.ByteArrayPool.Push(tmp); } return(true); }