private void NaiveMeshIndex(int index, int localPosition, ushort currentBlockId, bool isCurrentBlockTransparent) { Debug.Assert(currentBlockId != BlockController.AirID, $"{nameof(TraverseIndex)} should not run on air blocks."); Debug.Assert((index >= 0) && (index < GenerationConstants.CHUNK_SIZE_CUBED), $"{nameof(index)} is not within chunk bounds."); Debug.Assert(WydMath.PointToIndex(DecompressVertex(localPosition), GenerationConstants.CHUNK_SIZE) == index, $"{nameof(localPosition)} does not match given {nameof(index)}."); Debug.Assert(_MeshingBlocks[index].ID == currentBlockId, $"{currentBlockId} is not equal to block ID at given index."); Debug.Assert( BlockController.Current.CheckBlockHasProperty(currentBlockId, BlockDefinition.Property.Transparent) == isCurrentBlockTransparent, $"Given transparency state for {nameof(currentBlockId)} does not match actual block transparency."); // iterate once over all 6 faces of given cubic space for (int normalIndex = 0; normalIndex < 6; normalIndex++) { // face direction always exists on a single bit, so shift 1 by the current normalIndex (0-5) Direction faceDirection = (Direction)(1 << normalIndex); // check if current index has face already if (_MeshingBlocks[index].HasFace(faceDirection)) { continue; } // indicates whether the current face checking direction is negative or positive bool isNegativeFace = (normalIndex - 3) >= 0; // normalIndex constrained to represent the 3 axes int iModulo3 = normalIndex % 3; int iModulo3Shift = GenerationConstants.CHUNK_SIZE_BIT_SHIFT * iModulo3; // axis value of the current face check direction // example: for iteration normalIndex == 0—which is positive X—it'd be equal to localPosition.x int faceCheckAxisValue = (localPosition >> iModulo3Shift) & GenerationConstants.CHUNK_SIZE_BIT_MASK; // indicates whether or not the face check is within the current chunk bounds bool isFaceCheckOutOfBounds = (!isNegativeFace && (faceCheckAxisValue == (GenerationConstants.CHUNK_SIZE - 1))) || (isNegativeFace && (faceCheckAxisValue == 0)); if (!isFaceCheckOutOfBounds) { // amount by integer to add to current traversal index to get 3D->1D position of facing block int facedBlockIndex = index + GenerationConstants.IndexStepByNormalIndex[normalIndex]; // if so, index into block ids and set facingBlockId ushort facedBlockId = _MeshingBlocks[facedBlockIndex].ID; // if transparent, traverse so long as facing block is not the same block id // if opaque, traverse so long as facing block is transparent if (isCurrentBlockTransparent) { if (currentBlockId != facedBlockId) { continue; } } else if (!BlockController.Current.CheckBlockHasProperty(facedBlockId, BlockDefinition.Property.Transparent)) { if (!isNegativeFace) { Direction inverseFaceDirection = (Direction)(1 << ((normalIndex + 3) % 6)); _MeshingBlocks[facedBlockIndex].SetFace(inverseFaceDirection); } continue; } } else { // this block of code translates the integer local position to the local position of the neighbor at [normalIndex] int sign = isNegativeFace ? -1 : 1; int iModuloComponentMask = GenerationConstants.CHUNK_SIZE_BIT_MASK << iModulo3Shift; int finalLocalPosition = (~iModuloComponentMask & localPosition) | (WydMath.Wrap(((localPosition & iModuloComponentMask) >> iModulo3Shift) + sign, GenerationConstants.CHUNK_SIZE, 0, GenerationConstants.CHUNK_SIZE - 1) << iModulo3Shift); // index into neighbor blocks collections, call .GetPoint() with adjusted local position // remark: if there's no neighbor at the index given, then no chunk exists there (for instance, // chunks at the edge of render distance). In this case, return NullID so no face is rendered on edges. ushort facedBlockId = _NeighborBlocksCollections[normalIndex]?.GetPoint(DecompressVertex(finalLocalPosition)) ?? BlockController.NullID; if (isCurrentBlockTransparent) { if (currentBlockId != facedBlockId) { continue; } } else if (!BlockController.Current.CheckBlockHasProperty(facedBlockId, BlockDefinition.Property.Transparent)) { continue; } } _MeshingBlocks[index].SetFace(faceDirection); if (BlockController.Current.GetUVs(currentBlockId, faceDirection, out ushort textureId)) { int compressedUv = (textureId << (GenerationConstants.CHUNK_SIZE_BIT_SHIFT * 2)) ^ (1 << GenerationConstants.CHUNK_SIZE_BIT_SHIFT) ^ 1; int normals = GenerationConstants.NormalByIteration[normalIndex]; int[] compressedVertices = GenerationConstants.VerticesByIteration[normalIndex]; _MeshData.AddVertex((localPosition + compressedVertices[3]) | normals); _MeshData.AddVertex(compressedUv & (int.MaxValue << (GenerationConstants.CHUNK_SIZE_BIT_SHIFT * 2))); _MeshData.AddVertex((localPosition + compressedVertices[2]) | normals); _MeshData.AddVertex(compressedUv & (int.MaxValue << GenerationConstants.CHUNK_SIZE_BIT_SHIFT)); _MeshData.AddVertex((localPosition + compressedVertices[1]) | normals); _MeshData.AddVertex(compressedUv & ~(GenerationConstants.CHUNK_SIZE_BIT_MASK << GenerationConstants.CHUNK_SIZE_BIT_SHIFT)); _MeshData.AddVertex((localPosition + compressedVertices[0]) | normals); _MeshData.AddVertex(compressedUv & int.MaxValue); int verticesCount = _MeshData.VerticesCount / 2; int transparentAsInt = Convert.ToInt32(isCurrentBlockTransparent); _MeshData.AddTriangle(transparentAsInt, 0 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 2 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 1 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 2 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 3 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 1 + verticesCount); } else { } } }
/// <summary> /// Traverse given index of <see cref="_MeshingBlocks" /> to conditionally emit vertex data for each face. /// </summary> /// <param name="index">Current working index.</param> /// <param name="localPosition">3D projected local position of the current working index.</param> /// <param name="currentBlockId">Block ID present at the current working index.</param> /// <param name="isCurrentBlockTransparent">Whether or not this traversal uses transparent-specific conditionals.</param> private void TraverseIndex(int index, int localPosition, ushort currentBlockId, bool isCurrentBlockTransparent) { Debug.Assert(currentBlockId != BlockController.AirID, $"{nameof(TraverseIndex)} should not run on air blocks."); Debug.Assert((index >= 0) && (index < GenerationConstants.CHUNK_SIZE_CUBED), $"{nameof(index)} is not within chunk bounds."); Debug.Assert(WydMath.PointToIndex(DecompressVertex(localPosition), GenerationConstants.CHUNK_SIZE) == index, $"{nameof(localPosition)} does not match given {nameof(index)}."); Debug.Assert(_MeshingBlocks[index].ID == currentBlockId, $"{currentBlockId} is not equal to block ID at given index."); Debug.Assert( BlockController.Current.CheckBlockHasProperty(currentBlockId, BlockDefinition.Property.Transparent) == isCurrentBlockTransparent, $"Given transparency state for {nameof(currentBlockId)} does not match actual block transparency."); // iterate once over all 6 faces of given cubic space for (int normalIndex = 0; normalIndex < 6; normalIndex++) { // face direction always exists on a single bit, so shift 1 by the current normalIndex (0-5) Direction faceDirection = (Direction)(1 << normalIndex); // check if current index has face already if (_MeshingBlocks[index].HasFace(faceDirection)) { continue; } // indicates whether the current face checking direction is negative or positive bool isNegativeFace = (normalIndex - 3) >= 0; // normalIndex constrained to represent the 3 axes int iModulo3 = normalIndex % 3; int iModulo3Shift = GenerationConstants.CHUNK_SIZE_BIT_SHIFT * iModulo3; // axis value of the current face check direction // example: for iteration normalIndex == 0—which is positive X—it'd be equal to localPosition.x int faceCheckAxisValue = (localPosition >> iModulo3Shift) & GenerationConstants.CHUNK_SIZE_BIT_MASK; // indicates whether or not the face check is within the current chunk bounds bool isFaceCheckOutOfBounds = (!isNegativeFace && (faceCheckAxisValue == (GenerationConstants.CHUNK_SIZE - 1))) || (isNegativeFace && (faceCheckAxisValue == 0)); // total number of successful traversals int traversals = 0; for (int perpendicularNormalIndex = 1; perpendicularNormalIndex < 3; perpendicularNormalIndex++) { // the index of the int3 traversalNormal to traverse on int traversalNormalIndex = (iModulo3 + perpendicularNormalIndex) % 3; int traversalNormalShift = GenerationConstants.CHUNK_SIZE_BIT_SHIFT * traversalNormalIndex; // current value of the local position by traversal direction int traversalNormalAxisValue = (localPosition >> traversalNormalShift) & GenerationConstants.CHUNK_SIZE_BIT_MASK; // amount by integer to add to current index to get 3D->1D position of traversal position int traversalIndexStep = GenerationConstants.IndexStepByNormalIndex[traversalNormalIndex]; // current traversal index, which is increased by traversalIndexStep every iteration the for loop below int traversalIndex = index + (traversals * traversalIndexStep); // local start axis position + traversals int totalTraversalLength = traversalNormalAxisValue + traversals; for (; (totalTraversalLength < GenerationConstants.CHUNK_SIZE) && !_MeshingBlocks[traversalIndex].HasFace(faceDirection) && (_MeshingBlocks[traversalIndex].ID == currentBlockId); totalTraversalLength++, traversals++, // increment traversals traversalIndex += traversalIndexStep) // increment traversal index by index step to adjust local working position { // check if current facing block axis value is within the local chunk if (!isFaceCheckOutOfBounds) { // amount by integer to add to current traversal index to get 3D->1D position of facing block int facedBlockIndex = traversalIndex + GenerationConstants.IndexStepByNormalIndex[normalIndex]; // if so, index into block ids and set facingBlockId ushort facedBlockId = _MeshingBlocks[facedBlockIndex].ID; // if transparent, traverse so long as facing block is not the same block id // if opaque, traverse so long as facing block is transparent if (isCurrentBlockTransparent) { if (currentBlockId != facedBlockId) { break; } } else if (!BlockController.Current.CheckBlockHasProperty(facedBlockId, BlockDefinition.Property.Transparent)) { if (!isNegativeFace) { // we've culled this face, and faced block is opaque as well, so cull it's face inverse to the current. Direction inverseFaceDirection = (Direction)(1 << ((normalIndex + 3) % 6)); _MeshingBlocks[facedBlockIndex].SetFace(inverseFaceDirection); } break; } } else { // this block of code translates the integer local position to the local position of the neighbor at [normalIndex] int sign = isNegativeFace ? -1 : 1; int iModuloComponentMask = GenerationConstants.CHUNK_SIZE_BIT_MASK << iModulo3Shift; int translatedLocalPosition = localPosition + (traversals << traversalNormalShift); int finalLocalPosition = (~iModuloComponentMask & translatedLocalPosition) | (WydMath.Wrap(((translatedLocalPosition & iModuloComponentMask) >> iModulo3Shift) + sign, GenerationConstants.CHUNK_SIZE, 0, GenerationConstants.CHUNK_SIZE - 1) << iModulo3Shift); // index into neighbor blocks collections, call .GetPoint() with adjusted local position // remark: if there's no neighbor at the index given, then no chunk exists there (for instance, // chunks at the edge of render distance). In this case, return NullID so no face is rendered on edges. ushort facedBlockId = _NeighborBlocksCollections[normalIndex]?.GetPoint(DecompressVertex(finalLocalPosition)) ?? BlockController.NullID; if (isCurrentBlockTransparent) { if (currentBlockId != facedBlockId) { break; } } else if (!BlockController.Current.CheckBlockHasProperty(facedBlockId, BlockDefinition.Property.Transparent)) { break; } } _MeshingBlocks[traversalIndex].SetFace(faceDirection); } // if it's the first traversal and we've only made a 1x1x1 face, continue to test next axis if ((traversals == 1) && (perpendicularNormalIndex == 1)) { continue; } if ((traversals == 0) || !BlockController.Current.GetUVs(currentBlockId, faceDirection, out ushort textureId)) { break; } // add triangles int verticesCount = _MeshData.VerticesCount / 2; int transparentAsInt = Convert.ToInt32(isCurrentBlockTransparent); _MeshData.AddTriangle(transparentAsInt, 0 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 2 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 1 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 2 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 3 + verticesCount); _MeshData.AddTriangle(transparentAsInt, 1 + verticesCount); int uvShift = (iModulo3 + traversalNormalIndex) % 2; int compressedUv = (textureId << (GenerationConstants.CHUNK_SIZE_BIT_SHIFT * 2)) | (1 << (GenerationConstants.CHUNK_SIZE_BIT_SHIFT * ((uvShift + 1) % 2))) | (traversals << (GenerationConstants.CHUNK_SIZE_BIT_SHIFT * uvShift)); int traversalShiftedMask = GenerationConstants.CHUNK_SIZE_BIT_MASK << traversalNormalShift; int unaryTraversalShiftedMask = ~traversalShiftedMask; int aggregatePositionNormal = localPosition | GenerationConstants.NormalByIteration[normalIndex]; int[] compressedVertices = GenerationConstants.VerticesByIteration[normalIndex]; _MeshData.AddVertex(aggregatePositionNormal + ((unaryTraversalShiftedMask & compressedVertices[3]) | ((((compressedVertices[3] >> traversalNormalShift) * traversals) << traversalNormalShift) & traversalShiftedMask))); _MeshData.AddVertex(compressedUv & (int.MaxValue << (GenerationConstants.CHUNK_SIZE_BIT_SHIFT * 2))); _MeshData.AddVertex(aggregatePositionNormal + ((unaryTraversalShiftedMask & compressedVertices[2]) | ((((compressedVertices[2] >> traversalNormalShift) * traversals) << traversalNormalShift) & traversalShiftedMask))); _MeshData.AddVertex(compressedUv & (int.MaxValue << GenerationConstants.CHUNK_SIZE_BIT_SHIFT)); _MeshData.AddVertex(aggregatePositionNormal + ((unaryTraversalShiftedMask & compressedVertices[1]) | ((((compressedVertices[1] >> traversalNormalShift) * traversals) << traversalNormalShift) & traversalShiftedMask))); _MeshData.AddVertex(compressedUv & ~(GenerationConstants.CHUNK_SIZE_BIT_MASK << GenerationConstants.CHUNK_SIZE_BIT_SHIFT)); _MeshData.AddVertex(aggregatePositionNormal + ((unaryTraversalShiftedMask & compressedVertices[0]) | ((((compressedVertices[0] >> traversalNormalShift) * traversals) << traversalNormalShift) & traversalShiftedMask))); _MeshData.AddVertex(compressedUv & int.MaxValue); break; } } }