Interactive Voxel Terrain
Project Features Custom C++ Engine / OpenGL / Development Time - 12 Weeks
2D Perlin Noise used to randomly generate terrain
Players can add and remove voxels
Players may place light voxels, which propagate light in all directions
Dynamic lighting that is recalculated whenever blocks are added or removed
World is saved from session to session using Run-Length Encoding (RLE)
World is infinite and is populated by 16x16x128 voxel chunks
Side-Face culling for voxels that are covered by other voxels
Game runs at a smooth framerate while chunks are loaded and unloaded
Procedurally generated trees
Integrates with FMOD for audio
3D Ray tracing and AABB Physics
View frustum culling of chunks
Skybox rendering
Three camera/physics states: No Clip, Flying, and Walking
Project Overview
This was one of my first projects at Guildhall and my first exposure to 3D rendering. The goal was to create a procedurally generated voxel world using perlin noise with dynamic lighting that could be saved from session to session. It was an invaluable introduction to 3D rendering and provided interesting problems, such as the management of voxel chunks (xyz 16x16x128), data compression, and 3D physics and raycasting.
The project includes many optimizations, such as side face culling for voxels obscured by other voxels and chunk loading and unloading amortized over frames. The goal was to maintain a steady 60 FPS.
Players may switch between three different camera and physics modes.
- No Clip - Physics are turned off and the player may fly and have free roam
- Flying - Physics are turned on and the player has ability to fly but cannot fly through voxels
- Walking - Player assumes a 3D AABB and operates under all physics rules, including gravity and friction
Voxel World
The voxel world is infinite and consists of 16x16x128 chunks. Chunks are identified by their coordinates in the world. There are two 3D spheres attached to the camera which dictate which chunks are loaded and unloaded from disk.
- Chunks that intersect with neither sphere remain unloaded and not rendered
- Unloaded chunks which intersect with the outer sphere are put in a queue to be loaded
- Loaded chunks that no longer intersect with the outer sphere are put in a queue to be unloaded and saved to disc using Run-Length Encoding (RLE) compression
- Loaded chunks that interest with the outer sphere but not with the inner sphere are marked as invisible and not rendered
- Loaded chunks that intersect with the inner sphere are always rendered
- Sphere radius values are adjustable
Project Post Mortem
What Went Well
- The game ran at a steady framerate and RLE compression allowed for minimal file sizes when loading and unloading chunks. It granted a good opportunity to identify performance bottlenecks and improve performance.
- This was a great introduction to 3D graphics and helped to give me a strong understanding of Vertex Buffer Objects and the 3D graphics pipeline.
- Gained experiencing integrating a 3rd party library (FMOD) for audio playback.
What Went Wrong
- I attempted to debug many game systems, such as physics, chunk loading and unloading, and camera view frustum culling without visual debugging. This led to a lot of frustrating and trial and error attempts to get these systems to a polished state. It made me realize the importance of visual debugging tools and ensuring they are integrated into the development pipeline.
- Dynamic lighting calculations were often my bottleneck. I initially used recursive algorithms as it made the code look more elegant and readable. I realized there was often a trade off between code readability and performance, and I should have opted for the more performant option since dynamic lighting is an intensive algorithm calculated often. Early in development, planting trees or making world changes that caused massive lighting changes over multiple chunks would hitch the framerate or cause stack overflow issues due to the stack memory footprint of the recursive algorithm.
What Was Learned
- Planning and designing systems will always save time in the long run and lead to better structured code and less rework.
- Sometimes code readability should be sacrificed for more performant, less readable code, which is supplemented by comments to assist the reader. This is especially true for code that is run consistently each frame.
- If multi-threading is not an option, amortizing the cost of operations over frames is a valid alternative. This worked well for chunk loading and unloading.
- I should not be afraid to refactor or remove old code. Sometimes it is just better to admit something is bad or poorly executed and rework it from another angle. With many of my projects, I usually aim to 'get it working' first and as fast as possible. It is important to revisit this code and not label it as complete. I found it very helpful and moral improving to get systems working fast, but this was also a source of bugs when not revisited in the future or building upon the quick implementations.
Code Samples
-
WorldLightingManager.hpp
#ifndef included_WorldLightingManager #define included_WorldLightingManager #pragma once #include
#include -
WorldLightingManager.cpp
#include "WorldLightingManager.hpp" #include
#include "..\EngineCode\MathUtil.hpp" #include "..\EngineCode\IntVector2.hpp" #include "..\EngineCode\Vector3D.hpp" #include "WorldChunk.hpp" #include "VoxelBlock.hpp" #include "VoxelBlockMaterial.hpp" #include "LightingNode.hpp" WorldLightingManager::~WorldLightingManager() { } // Always Called From Private CTOR void WorldLightingManager::createRangeMappedLightingTable( float minLightingValue, float maxLightingValue ) { // Maps the lighting values to be between 0 and 1 const float newMinLightingValueRange = 0.0655f; const float newMaxLightingValueRange = 1.0f; for ( float i = minLightingValue; i <= maxLightingValue; i = i + 1.0f ) { float rangeMappedFloatValue = cbengine::rangeMapFloat( minLightingValue, maxLightingValue, newMinLightingValueRange, newMaxLightingValueRange, i ); m_lightingValueTable.insert( std::pair< float, float>( i, rangeMappedFloatValue ) ); } // end for } // end createRangeMappedLightingTable // Assumes all chunks have been created void WorldLightingManager::createInitialLightingValuesForChunks( std::map< cbengine::IntVector2, WorldChunk *> & worldChunks ) { assert( m_actionDelegate != nullptr ); // First pass to set initial lighting values from the sky std::map< cbengine::IntVector2, WorldChunk *>::iterator itChunk; size_t x = 0; size_t y = 0; int z = 0; int zMax = static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK - 1 ); size_t index = 0; for ( itChunk = worldChunks.begin(); itChunk != worldChunks.end(); ++itChunk ) { WorldChunk * chunk = itChunk->second; assert( chunk != nullptr ); for ( x = 0; x < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++x ) { for ( y = 0; y < simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK; ++y ) { float currentLightingValue = simpleminer::LIGHTING_MAX_VALUE; for ( z = zMax; z >= 0; --z ) { index = ( x & xMask ) + ( ( y << 4 ) & yMask ) + ( ( z << 8 ) & zMask ); VoxelBlock & voxelBlock = chunk->m_voxelBlocks[index]; //const VoxelBlockMaterial * voxelMaterial = voxelMaterials[ static_cast ( voxelBlock.m_blockType ) ]; // If it is air then we continue the descent if ( voxelBlock.m_blockType == simpleminer::kBLOCK_TYPE_AIR ) { voxelBlock.m_blockLightingValue = currentLightingValue; voxelBlock.m_cubeSideBitMask |= VOXEL_SURFACE_BLOCK; } else if ( voxelBlock.m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { voxelBlock.m_blockLightingValue = currentLightingValue; currentLightingValue = currentLightingValue - 1.0f; voxelBlock.m_cubeSideBitMask |= VOXEL_SURFACE_BLOCK; } else { break; } if ( currentLightingValue < 1.0f ) { // No need to continue the descent as default light coefficient is 0 for all blocks break; } } // end z for } // end y for } // end x for } // end outer for doSecondPassForAllChunksToCalculateLightForDirtyBlocks( worldChunks ); } // end function void WorldLightingManager::createInitialLightingValuesForSingleChunk( WorldChunk * worldChunk ) { assert( worldChunk != nullptr ); size_t x = 0; size_t y = 0; int z = 0; int zMax = static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK - 1 ); size_t index = 0; for ( x = 0; x < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++x ) { for ( y = 0; y < simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK; ++y ) { float currentLightingValue = simpleminer::LIGHTING_MAX_VALUE; for ( z = zMax; z >= 0; --z ) { index = ( x & xMask ) + ( ( y << 4 ) & yMask ) + ( ( z << 8 ) & zMask ); VoxelBlock & voxelBlock = worldChunk->m_voxelBlocks[index]; //const VoxelBlockMaterial * voxelMaterial = voxelMaterials[ static_cast ( voxelBlock.m_blockType ) ]; // If it is air then we continue the descent if ( voxelBlock.m_blockType == simpleminer::kBLOCK_TYPE_AIR ) { voxelBlock.m_blockLightingValue = currentLightingValue; voxelBlock.m_cubeSideBitMask |= VOXEL_SURFACE_BLOCK; } else if ( voxelBlock.m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { voxelBlock.m_blockLightingValue = currentLightingValue; currentLightingValue = currentLightingValue - 1.0f; voxelBlock.m_cubeSideBitMask |= VOXEL_SURFACE_BLOCK; } else { break; } if ( currentLightingValue < 1.0f ) { // No need to continue the descent as default light coefficient is 0 for all blocks break; } } // end z for } // end y for } // end x for // Turned off for now because there are no trees but will need to be turned on when we do chunk restoration doSecondPassForSingleChunkToCalculateLightForDiryBlocks( worldChunk ); } void WorldLightingManager::doSecondPassForSingleChunkToCalculateLightForDiryBlocks( WorldChunk * worldChunk ) { size_t x = 0; size_t y = 0; int z = 0; int zMax = static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK - 1 ); size_t index = 0; std::vector< cbengine::Vector3D > blockPositionsToRecomputeLighting; cbengine::Vector3D blockCurrentPosition; for ( x = 0; x < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++x ) { for ( y = 0; y < simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK; ++y ) { for ( z = zMax; z >= 0; --z ) { index = ( x & xMask ) + ( ( y << 4 ) & yMask ) + ( ( z << 8 ) & zMask ); VoxelBlock & voxelBlock = worldChunk->m_voxelBlocks[index]; float xPosFloat = static_cast ( x ); float yPosFloat = static_cast ( y ); float zPosFloat = static_cast ( z ); if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) { VoxelBlock * blockToMark = nullptr; // Add non surface block non opaque neighbors to the attention list // South blockCurrentPosition.x = worldChunk->m_origin.x + xPosFloat - cubeSideSize; blockCurrentPosition.y = worldChunk->m_origin.y + yPosFloat; blockCurrentPosition.z = worldChunk->m_origin.z + zPosFloat; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if // East blockCurrentPosition.x = worldChunk->m_origin.x + xPosFloat; blockCurrentPosition.y = worldChunk->m_origin.y + yPosFloat - cubeSideSize; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if // North blockCurrentPosition.x = worldChunk->m_origin.x + xPosFloat + cubeSideSize; blockCurrentPosition.y = worldChunk->m_origin.y + yPosFloat; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if // West blockCurrentPosition.x = worldChunk->m_origin.x + xPosFloat; blockCurrentPosition.y = worldChunk->m_origin.y + yPosFloat + cubeSideSize; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if } else { break; } } // end z for } // end y for } // end x for for ( size_t i = 0; i < blockPositionsToRecomputeLighting.size(); ++i ) { distributeLightForBlockChangeAtPosition( blockPositionsToRecomputeLighting[i] ); } } void WorldLightingManager::doSecondPassForAllChunksToCalculateLightForDirtyBlocks( std::map< cbengine::IntVector2, WorldChunk *> & worldChunks ) { // First pass to set initial lighting values from the sky std::map< cbengine::IntVector2, WorldChunk *>::iterator itChunk; size_t x = 0; size_t y = 0; int z = 0; int zMax = static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK - 1 ); size_t index = 0; for ( itChunk = worldChunks.begin(); itChunk != worldChunks.end(); ++itChunk ) { WorldChunk * chunk = itChunk->second; assert( chunk != nullptr ); // 2nd Pass to mark blocks that need to recompute their lighting std::vector< cbengine::Vector3D > blockPositionsToRecomputeLighting; cbengine::Vector3D blockCurrentPosition; for ( x = 0; x < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++x ) { for ( y = 0; y < simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK; ++y ) { for ( z = zMax; z >= 0; --z ) { index = ( x & xMask ) + ( ( y << 4 ) & yMask ) + ( ( z << 8 ) & zMask ); VoxelBlock & voxelBlock = chunk->m_voxelBlocks[index]; float xPosFloat = static_cast ( x ); float yPosFloat = static_cast ( y ); float zPosFloat = static_cast ( z ); if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) { VoxelBlock * blockToMark = nullptr; // Add non surface block non opaque neighbors to the attention list // South blockCurrentPosition.x = chunk->m_origin.x + xPosFloat - cubeSideSize; blockCurrentPosition.y = chunk->m_origin.y + yPosFloat; blockCurrentPosition.z = chunk->m_origin.z + zPosFloat; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if // East blockCurrentPosition.x = chunk->m_origin.x + xPosFloat; blockCurrentPosition.y = chunk->m_origin.y + yPosFloat - cubeSideSize; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if // North blockCurrentPosition.x = chunk->m_origin.x + xPosFloat + cubeSideSize; blockCurrentPosition.y = chunk->m_origin.y + yPosFloat; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if // West blockCurrentPosition.x = chunk->m_origin.x + xPosFloat; blockCurrentPosition.y = chunk->m_origin.y + yPosFloat + cubeSideSize; blockToMark = m_actionDelegate->getVoxelBlockAtPosition( blockCurrentPosition ); if ( blockToMark != nullptr ) { if ( !( ( blockToMark->m_cubeSideBitMask & VOXEL_SURFACE_BLOCK ) == VOXEL_SURFACE_BLOCK ) ) { if ( ( blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToMark->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) ) { blockPositionsToRecomputeLighting.push_back( blockCurrentPosition ); } } // end inner if } // end outer if } else { break; } } // end z for } // end y for } // end x for for ( size_t i = 0; i < blockPositionsToRecomputeLighting.size(); ++i ) { distributeLightForBlockChangeAtPosition( blockPositionsToRecomputeLighting[i] ); } } } void WorldLightingManager::determineInitialLightingValueForBlockAtPosition( float x, float y, float z, VoxelBlock & voxelBlock ) { const float lightDecrementValue = 1.0f; float currentMaxLighting = voxelBlock.m_blockLightingValue; VoxelBlock * currentBlockToCheck = nullptr; cbengine::Vector3D currentBlockPosition; // Check South currentBlockPosition.x = x - cubeSideSize; currentBlockPosition.y = y; currentBlockPosition.z = z; currentBlockToCheck = currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlockToCheck != nullptr ) { currentMaxLighting = std::max ( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) ); } // Check East currentBlockPosition.x = x; currentBlockPosition.y = y - cubeSideSize; currentBlockPosition.z = z; currentBlockToCheck = currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlockToCheck != nullptr ) { currentMaxLighting = std::max ( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) ); } // Check North currentBlockPosition.x = x + cubeSideSize; currentBlockPosition.y = y; currentBlockPosition.z = z; currentBlockToCheck = currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlockToCheck != nullptr ) { currentMaxLighting = std::max ( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) ); } // Check West currentBlockPosition.x = x; currentBlockPosition.y = y + cubeSideSize; currentBlockPosition.z = z; currentBlockToCheck = currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlockToCheck != nullptr ) { currentMaxLighting = std::max ( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) ); } // Check Bottom currentBlockPosition.x = x; currentBlockPosition.y = y; currentBlockPosition.z = z - cubeSideSize; currentBlockToCheck = currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlockToCheck != nullptr ) { currentMaxLighting = std::max ( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) ); } // Check Top currentBlockPosition.x = x; currentBlockPosition.y = y; currentBlockPosition.z = z + cubeSideSize; currentBlockToCheck = currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlockToCheck != nullptr ) { currentMaxLighting = std::max ( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) ); } voxelBlock.m_blockLightingValue = currentMaxLighting; } // end function void WorldLightingManager::determineLightingForNewlyPlacedBlock( cbengine::Vector3D & blockPosition ) { // If it is not a surface or glow stone block, then the lighting must be determined from its neighbors assert( m_actionDelegate != nullptr ); VoxelBlock * currentBlock = m_actionDelegate->getVoxelBlockAtPosition( blockPosition ); assert( currentBlock != nullptr ); cbengine::Vector3D blockPositionToCheck; blockPositionToCheck.x = blockPosition.x; blockPositionToCheck.y = blockPosition.y; bool isSurfaceBlock = true; // If the block was placed at the top... It automatically has a max light value float zPos = blockPosition.z + cubeSideSize; if ( zPos == ( static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) - 1.0f ) ) { currentBlock->m_blockLightingValue = simpleminer::LIGHTING_MAX_VALUE; return; } // First check up to see if it is a surface block for ( zPos; zPos < static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ); zPos = zPos + cubeSideSize ) { blockPositionToCheck.z = zPos; VoxelBlock * block = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { isSurfaceBlock = false; break; } } if ( isSurfaceBlock && currentBlock->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) { currentBlock->m_blockLightingValue = simpleminer::LIGHTING_MAX_VALUE; } else if ( currentBlock->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE && !(isSurfaceBlock) ) { currentBlock->m_blockLightingValue = simpleminer::LIGHTING_GLOW_STONE_DEFAULT; } else if ( currentBlock->m_blockType != simpleminer::kBLOCK_TYPE_GLOWSTONE ) { currentBlock->m_blockLightingValue = simpleminer::LIGHTING_MIN_VALUE; } // end if } // end function void WorldLightingManager::distributeLightForBlockChangeAtPosition( cbengine::Vector3D & blockPosition ) { // Assumes lighting for newly altered block is already computed assert( m_actionDelegate != nullptr ); determineDynamicLightingForAlteredBlockAtPosition( blockPosition ); } // end distribute void WorldLightingManager::determineDynamicLightingForAlteredBlockAtPosition( cbengine::Vector3D & alteredBlockPosition ) { VoxelBlock * blockToCheck = nullptr; cbengine::Vector3D blockPositionToCheck; blockPositionToCheck.x = alteredBlockPosition.x; blockPositionToCheck.y = alteredBlockPosition.y; blockPositionToCheck.z = alteredBlockPosition.z; bool blockLightingChanged = false; WorldChunk * currentChunk = nullptr; // Check South blockPositionToCheck.x = alteredBlockPosition.x - cubeSideSize; blockPositionToCheck.y = alteredBlockPosition.y; blockPositionToCheck.z = alteredBlockPosition.z; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); if ( blockToCheck != nullptr && ( blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) ) { blockLightingChanged = distributeBlockLightingIfNeededAtPosition( blockPositionToCheck, *(blockToCheck) ); if ( blockLightingChanged ) { currentChunk = m_actionDelegate->getChunkAtPosition( blockPositionToCheck ); currentChunk->m_dirty = true; markNeighboringChunksDirty( blockPositionToCheck ); determineDynamicLightingForAlteredBlockAtPosition( blockPositionToCheck ); } // end inner if } // end outer if // Check East blockPositionToCheck.x = alteredBlockPosition.x; blockPositionToCheck.y = alteredBlockPosition.y - cubeSideSize; blockPositionToCheck.z = alteredBlockPosition.z; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); if ( blockToCheck != nullptr && ( blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) ) { blockLightingChanged = distributeBlockLightingIfNeededAtPosition( blockPositionToCheck, *(blockToCheck) ); if ( blockLightingChanged ) { currentChunk = m_actionDelegate->getChunkAtPosition( blockPositionToCheck ); currentChunk->m_dirty = true; markNeighboringChunksDirty( blockPositionToCheck ); determineDynamicLightingForAlteredBlockAtPosition( blockPositionToCheck ); } // end inner if } // end outer if // Check North blockPositionToCheck.x = alteredBlockPosition.x + cubeSideSize; blockPositionToCheck.y = alteredBlockPosition.y; blockPositionToCheck.z = alteredBlockPosition.z; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); if ( blockToCheck != nullptr && ( blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) ) { blockLightingChanged = distributeBlockLightingIfNeededAtPosition( blockPositionToCheck, *(blockToCheck) ); if ( blockLightingChanged ) { currentChunk = m_actionDelegate->getChunkAtPosition( blockPositionToCheck ); currentChunk->m_dirty = true; markNeighboringChunksDirty( blockPositionToCheck ); determineDynamicLightingForAlteredBlockAtPosition( blockPositionToCheck ); } // end inner if } // end outer if // Check West blockPositionToCheck.x = alteredBlockPosition.x; blockPositionToCheck.y = alteredBlockPosition.y + cubeSideSize; blockPositionToCheck.z = alteredBlockPosition.z; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); if ( blockToCheck != nullptr && ( blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) ) { blockLightingChanged = distributeBlockLightingIfNeededAtPosition( blockPositionToCheck, *(blockToCheck) ); if ( blockLightingChanged ) { currentChunk = m_actionDelegate->getChunkAtPosition( blockPositionToCheck ); currentChunk->m_dirty = true; markNeighboringChunksDirty( blockPositionToCheck ); determineDynamicLightingForAlteredBlockAtPosition( blockPositionToCheck ); } // end inner if } // end outer if // Check Bottom blockPositionToCheck.x = alteredBlockPosition.x; blockPositionToCheck.y = alteredBlockPosition.y; blockPositionToCheck.z = alteredBlockPosition.z - cubeSideSize; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); if ( blockToCheck != nullptr && ( blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) ) { blockLightingChanged = distributeBlockLightingIfNeededAtPosition( blockPositionToCheck, *(blockToCheck) ); if ( blockLightingChanged ) { currentChunk = m_actionDelegate->getChunkAtPosition( blockPositionToCheck ); currentChunk->m_dirty = true; markNeighboringChunksDirty( blockPositionToCheck ); determineDynamicLightingForAlteredBlockAtPosition( blockPositionToCheck ); } // end inner if } // end outer if // Check Up blockPositionToCheck.x = alteredBlockPosition.x; blockPositionToCheck.y = alteredBlockPosition.y; blockPositionToCheck.z = alteredBlockPosition.z + cubeSideSize; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); if ( blockToCheck != nullptr && ( blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_AIR || blockToCheck->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) ) { blockLightingChanged = distributeBlockLightingIfNeededAtPosition( blockPositionToCheck, *(blockToCheck) ); if ( blockLightingChanged ) { currentChunk = m_actionDelegate->getChunkAtPosition( blockPositionToCheck ); currentChunk->m_dirty = true; markNeighboringChunksDirty( blockPositionToCheck ); determineDynamicLightingForAlteredBlockAtPosition( blockPositionToCheck ); } // end inner if } // end outer if } // end function void WorldLightingManager::determineLightingForDestroyedBlock( cbengine::Vector3D & blockPosition ) { assert( m_actionDelegate != nullptr ); VoxelBlock * destroyedBlock = m_actionDelegate->getVoxelBlockAtPosition( blockPosition ); // Assumption is all destroyed blocks become air assert( destroyedBlock != nullptr && destroyedBlock->m_blockType == simpleminer::kBLOCK_TYPE_AIR ); // Assumption until proven wrong destroyedBlock->m_blockLightingValue = simpleminer::LIGHTING_MAX_VALUE; // Check to see if the air block is exposed to surface light float currentZPos = blockPosition.z; VoxelBlock * currentBlock = nullptr; cbengine::Vector3D currentBlockPosition; currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y; for ( currentZPos; currentZPos < static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ); ++currentZPos ) { currentBlockPosition.z = currentZPos; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock == nullptr ) { destroyedBlock->m_blockLightingValue = simpleminer::LIGHTING_MAX_VALUE; } else if ( currentBlock->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { if ( currentBlock->m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE ) { destroyedBlock->m_blockLightingValue = currentBlock->m_blockLightingValue - 1.0f; } else { destroyedBlock->m_blockLightingValue = simpleminer::LIGHTING_MIN_VALUE; distributeBlockLightingIfNeededAtPosition( blockPosition, *(destroyedBlock) ); break; } // end if } // end if } // end for if ( destroyedBlock->m_blockLightingValue == simpleminer::LIGHTING_MAX_VALUE ) { // Need to distribute that light downwards until we hit an opaque block currentZPos = blockPosition.z; for ( currentZPos; currentZPos >= 0.0f; --currentZPos ) { currentBlockPosition.z = currentZPos; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock->m_blockType == simpleminer::kBLOCK_TYPE_AIR ) { currentBlock->m_blockLightingValue = simpleminer::LIGHTING_MAX_VALUE; determineDynamicLightingForAlteredBlockAtPosition( currentBlockPosition ); } else { break; } } } // end if } void WorldLightingManager::markNeighboringChunksDirty( const cbengine::Vector3D & blockPosition ) { cbengine::Vector3D currentBlockPosition; currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y; currentBlockPosition.z = blockPosition.z; WorldChunk * currentChunk = nullptr; // Mark South currentBlockPosition.x = currentBlockPosition.x - cubeSideSize; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } // Mark East currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y - cubeSideSize; currentBlockPosition.z = blockPosition.z; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } // Mark North currentBlockPosition.x = blockPosition.x + cubeSideSize; currentBlockPosition.y = blockPosition.y; currentBlockPosition.z = blockPosition.z; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } // Mark West currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y + cubeSideSize; currentBlockPosition.z = blockPosition.z; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } } // end mark function -
TreeGenerator.hpp
#ifndef included_TreeGenerator #define included_TreeGenerator #pragma once #include
#include "..\EngineCode\EngineMacros.hpp" #include "..\EngineCode\Vector3D.hpp" #include "ActionDelegate.hpp" const float ROOT_POSITION_INVALID = -1.0f; const float TREE_BASE_HEIGHT = 6.0f; const float TREE_MAX_ADDITIONAL_HEIGHT = 17.0f; const float TREE_MAX_HEIGHT = TREE_BASE_HEIGHT + TREE_MAX_ADDITIONAL_HEIGHT; const float TREE_WIDER_BASE_THRESHOLD = 10.0f; const float TREE_MAX_LEAVE_HEIGHT = 20.0f; const float TREE_MAX_RADIUS = 3.0f; const float TREE_MIN_RADIUS = 2.0f; class TreeGenerator { public: //Singleton accessor static TreeGenerator* sharedTreeGenerator() { static TreeGenerator treeGenerator; return &treeGenerator; } ~TreeGenerator(); void generateTreeAtPosition( const cbengine::Vector3D & blockPosition ); float findZPositionForTreeTrunk( float x, float y ); // Inline Functions void setActionDelegate( ActionDelegate * actionDelegate ); protected: private: TreeGenerator() {} bool checkIfBlockPositionIsSurfaceBlock( const cbengine::Vector3D & blockPosition ); void TreeGenerator::generateTreeLeavesAtOrigin( const cbengine::Vector3D & originPoint, float radius, float treeHeight ); // Private Variables ActionDelegate * m_actionDelegate; PREVENT_COPY_AND_ASSIGN( TreeGenerator ); }; // Inline Functions inline void TreeGenerator::setActionDelegate( ActionDelegate * actionDelegate ) { assert( actionDelegate != nullptr ); m_actionDelegate = actionDelegate; } // end set action delegate #endif -
TreeGenerator.cpp
#include "TreeGenerator.hpp" #include "..\EngineCode\MathUtil.hpp" #include "GameConstants.hpp" #include "VoxelBlock.hpp" #include "WorldChunk.hpp" #include "WorldLightingManager.hpp" TreeGenerator::~TreeGenerator() { } void TreeGenerator::generateTreeAtPosition( const cbengine::Vector3D
& blockPosition ) { bool isSurfaceBlock = checkIfBlockPositionIsSurfaceBlock( blockPosition ); if ( !isSurfaceBlock ) { // Assumption is trees cannot be generated on non surface blocks return; } float treeTrunkHeight = TREE_BASE_HEIGHT + ( cbengine::getRandomZeroToOne() * TREE_MAX_ADDITIONAL_HEIGHT ); bool drawWithLargerTrunk = ( treeTrunkHeight >= TREE_WIDER_BASE_THRESHOLD ) ? true : false; VoxelBlock * currentBlock = nullptr; WorldChunk * currentChunk = nullptr; cbengine::Vector3D currentBlockPosition; currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y; currentBlockPosition.z = blockPosition.z - cubeSideSize; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { if ( currentBlock->m_blockType == simpleminer::kBLOCK_TYPE_TREE_LEAF || currentBlock->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { return; } } float zPos = currentBlockPosition.z; float zPosMax = currentBlockPosition.z + treeTrunkHeight; if ( drawWithLargerTrunk ) { for ( zPos; zPos < zPosMax; zPos++ ) { currentBlockPosition.z = zPos; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); currentChunk->m_dirty = true; currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_TRUNK; } else { return; } currentBlockPosition.x = blockPosition.x + cubeSideSize; currentBlockPosition.y = blockPosition.y; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); currentChunk->m_dirty = true; currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_TRUNK; } currentBlockPosition.x = blockPosition.x + cubeSideSize; currentBlockPosition.y = blockPosition.y + cubeSideSize; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); currentChunk->m_dirty = true; currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_TRUNK; } currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y + cubeSideSize; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); currentChunk->m_dirty = true; currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_TRUNK; } currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); currentChunk->m_dirty = true; currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_TRUNK; } } // end for } else { for ( zPos; zPos < zPosMax; zPos++ ) { currentBlockPosition.z = zPos; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); currentChunk->m_dirty = true; currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_TRUNK; } else { return; } } } float treeLeafHeightCoefficient = cbengine::rangeMapFloat( TREE_BASE_HEIGHT, TREE_MAX_HEIGHT, 0.2f, 1.0f, treeTrunkHeight ); float treeRadius = TREE_MIN_RADIUS + ( cbengine::getRandomZeroToOne() * TREE_MAX_RADIUS ); // Generate the leaves generateTreeLeavesAtOrigin( currentBlockPosition, treeRadius, TREE_MAX_LEAVE_HEIGHT * treeLeafHeightCoefficient ); } // end generate tree at Position bool TreeGenerator::checkIfBlockPositionIsSurfaceBlock( const cbengine::Vector3D & blockPosition ) { assert( m_actionDelegate != nullptr ); // Default Assumption bool blockIsSurfaceBlock = true; cbengine::Vector3D currentBlockPosition; VoxelBlock * currentBlock = nullptr; currentBlockPosition.x = blockPosition.x; currentBlockPosition.y = blockPosition.y; float zPos = blockPosition.z; for ( zPos; zPos < simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK; ++zPos ) { currentBlockPosition.z = zPos; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock == nullptr ) { break; } else if ( currentBlock->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { blockIsSurfaceBlock = false; break; } else if ( currentBlock->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { blockIsSurfaceBlock = false; break; } } // end for return blockIsSurfaceBlock; } // end check funct float TreeGenerator::findZPositionForTreeTrunk( float x, float y ) { assert( m_actionDelegate != nullptr ); cbengine::Vector3D currentPosition( x, y, static_cast ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) ); VoxelBlock * currentBlock = nullptr; float zPos = simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK; for ( zPos; zPos >= 0.0f; --zPos ) { currentPosition.z = zPos; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentPosition ); if ( currentBlock != nullptr ) { if ( currentBlock->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { return ++zPos; } } } return ROOT_POSITION_INVALID; } void TreeGenerator::generateTreeLeavesAtOrigin( const cbengine::Vector3D & originPoint, float radius, float treeHeight ) { assert( m_actionDelegate != nullptr ); float currentHeight = 0.0f; int x = static_cast ( radius ); int y = 0; int loopCount = 0; float ranZeroToOne = cbengine::getRandomZeroToOne(); bool drawSquare = ( ranZeroToOne > 0.5f ) ? true : false; cbengine::Vector3D currentBlockPosition; currentBlockPosition.x = originPoint.x; currentBlockPosition.y = originPoint.y; currentBlockPosition.z = originPoint.z; VoxelBlock * currentBlock = nullptr; WorldChunk * currentChunk = nullptr; WorldLightingManager * sharedWorldLightingManager = WorldLightingManager::sharedWorldLightingManager(); for ( currentHeight; currentHeight < treeHeight; ++currentHeight ) { x = static_cast ( radius ) - loopCount; y = 0; int radiusError = 1 - x; if ( x < 1 ) { return; } while ( x >= y ) { for (int i = (int) originPoint.x - x; i <= ( (int) originPoint.x + x ); i++) { currentBlockPosition.x = static_cast ( i ); currentBlockPosition.y = originPoint.y - static_cast ( y ); currentBlockPosition.z = currentHeight + originPoint.z; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_LEAF; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } sharedWorldLightingManager->determineLightingForNewlyPlacedBlock( currentBlockPosition ); sharedWorldLightingManager->distributeLightForBlockChangeAtPosition( currentBlockPosition ); } currentBlockPosition.x = static_cast ( i ); currentBlockPosition.y = originPoint.y + static_cast ( y ); currentBlockPosition.z = currentHeight + originPoint.z; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_LEAF; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } sharedWorldLightingManager->determineLightingForNewlyPlacedBlock( currentBlockPosition ); sharedWorldLightingManager->distributeLightForBlockChangeAtPosition( currentBlockPosition ); } } for (int i = (int) originPoint.x - y; i <= (int) originPoint.x + y; i++) { currentBlockPosition.x = static_cast ( i ); currentBlockPosition.y = originPoint.y - static_cast ( y ); currentBlockPosition.z = currentHeight + originPoint.z; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_LEAF; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } sharedWorldLightingManager->determineLightingForNewlyPlacedBlock( currentBlockPosition ); sharedWorldLightingManager->distributeLightForBlockChangeAtPosition( currentBlockPosition ); } currentBlockPosition.x = static_cast ( i ); currentBlockPosition.y = originPoint.y + static_cast ( y ); currentBlockPosition.z = currentHeight + originPoint.z; currentBlock = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition ); if ( currentBlock != nullptr ) { currentBlock->m_blockType = simpleminer::kBLOCK_TYPE_TREE_LEAF; currentChunk = m_actionDelegate->getChunkAtPosition( currentBlockPosition ); if ( currentChunk != nullptr ) { currentChunk->m_dirty = true; } sharedWorldLightingManager->determineLightingForNewlyPlacedBlock( currentBlockPosition ); sharedWorldLightingManager->distributeLightForBlockChangeAtPosition( currentBlockPosition ); } } ++y; if (radiusError < 0) { radiusError = radiusError + ( ( 2 * y ) +1 ); } else { if ( !drawSquare ) { x--; } radiusError = radiusError + ( 2 * ( y - x + 1 ) ); } // end if } // end while loopCount++; } // end for } // end generateTreeLeaves -
VoxelBlock.hpp
#ifndef included_VoxelBlock #define included_VoxelBlock #pragma once #include "..\EngineCode\EngineMacros.hpp" // Bit Masks const int VOXEL_FRONT_SIDE_MASK = 1; const int VOXEL_RIGHT_SIDE_MASK = 2; const int VOXEL_BACK_SIDE_MASK = 4; const int VOXEL_LEFT_SIDE_MASK = 8; const int VOXEL_BOTTOM_SIDE_MASK = 16; const int VOXEL_TOP_SIDE_MASK = 32; const int VOXEL_SURFACE_BLOCK = 64; class VoxelBlock { public: ~VoxelBlock(); VoxelBlock(); unsigned char m_blockType; // 1 = render // 0 = don't render // Each side represents a bit power of 2 unsigned int m_cubeSideBitMask; float m_blockLightingValue; protected: private: PREVENT_COPY_AND_ASSIGN( VoxelBlock ); }; #endif
-
WorldChunk.hpp
#ifndef included_WorldChunk #define included_WorldChunk #pragma once #include
#include "..\EngineCode\EngineMacros.hpp" #include "..\EngineCode\Vector3D.hpp" #include "..\EngineCode\Vector2.hpp" #include "..\EngineCode\Vertex.hpp" #include "..\EngineCode\Disk2D.hpp" #include "GameConstants.hpp" #include "ActionDelegate.hpp" #include "VoxelBlock.hpp" const float rgbNorthSouthCoefficient = 0.800f; const float rgbEastWestCoefficient = 0.700f; const int xMask = 15; const int yMask = 240; const int zMask = 32512; class Texture; class WorldChunk { public: ~WorldChunk(); explicit WorldChunk( float originX = 0.0f, float originY = 0.0f, float originZ = 0.0f ); void renderChunkWithVBO() const; void createWorldChunkBlocks(); void cullChunkToChunkVoxelBlockSideFaces(); void createVertexDataForVBO(); void determineVoxelBlockSideBitMaskValues(); void recreateVBOVertexData(); // Dynamic Block Modification // Destroying ( Viewing from perspective of the origin ) void destroyBlockFrontSideAtPosition( cbengine::Vector3D & blockPosition ); void destroyBlockRightSideAtPosition( cbengine::Vector3D & blockPosition ); void destroyBlockBackSideAtPosition( cbengine::Vector3D & blockPosition ); void destroyBlockLeftSideAtPosition( cbengine::Vector3D & blockPosition ); void destroyBlockBottomSideAtPosition( cbengine::Vector3D & blockPosition ); void destroyBlockTopSideAtPosition( cbengine::Vector3D & blockPosition ); // Revealing ( Viewing from perspective of the origin ) void revealBlockFrontSideAtPosition( cbengine::Vector3D & blockPosition ); void revealBlockRightSideAtPosition( cbengine::Vector3D & blockPosition ); void revealBlockBackSideAtPosition( cbengine::Vector3D & blockPosition ); void revealBlockLeftSideAtPosition( cbengine::Vector3D & blockPosition ); void revealBlockBottomSideAtPosition( cbengine::Vector3D & blockPosition ); void revealBlockTopSideAtPosition( cbengine::Vector3D & blockPosition ); // Base Functions For Creating And Destroying Blocks void destroyBlockAtPosition( cbengine::Vector3D & blockPosition ); void createBlockAtPosition( cbengine::Vector3D & blockPosition, unsigned char blockType ); // Culling For Water Blocks void cullVoxelBlockSidesForWaterBlock( size_t index, size_t x, size_t y, size_t z ); VoxelBlock * getVoxelBlockAtPosition( const cbengine::Vector3D blockPosition ); const VoxelBlock * getVoxelBlockArray() const; VoxelBlock * getVoxelBlockArrayToModify(); // Inline Mutators void setActionDelegate( ActionDelegate * actionDelegate ); cbengine::Vector3D m_origin; cbengine::Disk2D m_boundingDisk; // Flag to mark if the chunk needs to resubmit its VBO to the GPU bool m_dirty; bool m_culledFromFrustrum; simpleminer::ChunkState m_chunkState; protected: private: // Creation With Perlin Noise void createVoxelBlocksWithPerlinNoise(); float smoothNoise( float numToSmooth ); float createNoise( int x, int y ); cbengine::Vector2 getRandomVectorAtPosition( int x, int y ); // Textures void loadChunkTextureMap(); // Culling void cullRightSidesWhichFaceOtherChunks(); void cullLeftSidesWhichFaceOtherChunks(); void cullBackSidesWhichFaceOtherChunks(); void cullFrontSidesWhichFaceOtherChunks(); void determineSideBitMasksForNewBlock( unsigned int index ); // Instance Variables ActionDelegate * m_actionDelegate; VoxelBlock m_voxelBlocks[ simpleminer::NUMBER_BLOCKS_IN_CHUNK ]; std::vector< cbengine::Vertex > m_vertsForVBO; unsigned int m_vertexBufferObjectID; // Friend Classes friend class WorldLightingManager; PREVENT_COPY_AND_ASSIGN( WorldChunk ); }; // Inline functions inline void WorldChunk::setActionDelegate( ActionDelegate * actionDelegate ) { if ( actionDelegate != nullptr ) { m_actionDelegate = actionDelegate; } // end if } // end setter inline const VoxelBlock * WorldChunk::getVoxelBlockArray() const { return m_voxelBlocks; } inline VoxelBlock * WorldChunk::getVoxelBlockArrayToModify() { return &m_voxelBlocks[0]; } #endif -
WorldChunk.cpp
#include "WorldChunk.hpp" #include
/* offsetof */ #include "../EngineCode/EngineCommon.hpp" #include "../EngineCode/OpenGLRenderer.hpp" #include "../EngineCode/Geometry3D.hpp" #include "../EngineCode/Vector3D.hpp" #include "../EngineCode/MathUtil.hpp" #include "../EngineCode/Texture.hpp" #include "GameConstants.hpp" #include "WorldLightingManager.hpp" #include "VoxelBlockMaterial.hpp" WorldChunk::~WorldChunk() { } // end dtor WorldChunk::WorldChunk( float originX, float originY, float originZ ) { m_origin.x = originX; m_origin.y = originY; m_origin.z = originZ; m_actionDelegate = nullptr; m_vertexBufferObjectID = 0; m_dirty = false; m_chunkState = simpleminer::kCHUNK_STATE_NOT_LOADED_OR_GENERATED; const float halfSizeOfXYChunk = static_cast ( simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK ) * 0.50f; m_boundingDisk.origin.x = originX + halfSizeOfXYChunk; m_boundingDisk.origin.y = originY + halfSizeOfXYChunk; m_boundingDisk.origin.z = originZ; m_boundingDisk.radius = halfSizeOfXYChunk; m_culledFromFrustrum = false; } // end ctor void WorldChunk::createWorldChunkBlocks() { createVoxelBlocksWithPerlinNoise(); determineVoxelBlockSideBitMaskValues(); m_chunkState = simpleminer::kCHUNK_STATE_INACTIVE; } void WorldChunk::renderChunkWithVBO() const { if ( m_culledFromFrustrum ) { return; } if ( m_chunkState == simpleminer::kCHUNK_STATE_ACTIVE ) { cbengine::CBRenderer * sharedRenderer = cbengine::CBRenderer::sharedCBRenderer(); sharedRenderer->pushMatrix(); sharedRenderer->translateF( m_origin ); sharedRenderer->bindVBOBuffer( m_vertexBufferObjectID ); cbengine::DataType dataType = cbengine::kTYPE_FLOAT; sharedRenderer->setVertexPointer( 3, dataType, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexPosition ) ); sharedRenderer->setColorPointer( 4, dataType, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexColor ) ); sharedRenderer->setTexCoordPointer( 2, dataType, sizeof( cbengine::Vertex ), (float*) offsetof( cbengine::Vertex, vertexTextureCoords) ); cbengine::GraphicsClientState graphicsClientState = cbengine::kVERTEX_ARRAY; sharedRenderer->enableClientState( graphicsClientState ); graphicsClientState = cbengine::kCOLOR_ARRAY; sharedRenderer->enableClientState( graphicsClientState ); graphicsClientState = cbengine::kTEXTURE_COORD_ARRAY; sharedRenderer->enableClientState( graphicsClientState ); sharedRenderer->enableCullFace(); sharedRenderer->cullFrontFaceCounterClockwise(); sharedRenderer->cullFaceBack(); const cbengine::Texture & worldTextureAtlas = m_actionDelegate->getWorldTextureAtlas(); sharedRenderer->disableLighting(); sharedRenderer->enable2DTextures(); sharedRenderer->enableDepthBuffering(); sharedRenderer->bind2DTexture( worldTextureAtlas.getGLTextureID() ); cbengine::DrawPrimitive drawPrim = cbengine::kQUADS; sharedRenderer->drawArrays( drawPrim, 0, m_vertsForVBO.size() ); graphicsClientState = graphicsClientState = cbengine::kVERTEX_ARRAY; sharedRenderer->disableClientState( graphicsClientState ); graphicsClientState = cbengine::kCOLOR_ARRAY; sharedRenderer->disableClientState( graphicsClientState ); graphicsClientState = cbengine::kTEXTURE_COORD_ARRAY; sharedRenderer->disableClientState( graphicsClientState ); sharedRenderer->disable2DTextures(); sharedRenderer->popMatrix(); } } // end renderChunkWithVBO VoxelBlock * WorldChunk::getVoxelBlockAtPosition( const cbengine::Vector3D blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { return nullptr; } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); return &( m_voxelBlocks[indexOfBlock] ); } cbengine::Vector2 WorldChunk::getRandomVectorAtPosition( int x, int y ) { float theta = createNoise( x, y ); cbengine::Vector2 randomVectorAtPosition( cos( theta * cbengine::PI ), sin( theta * cbengine::PI ) ); return randomVectorAtPosition; } float WorldChunk::createNoise( int x, int y ) { int n = x + y * 57; n = ( n << 13 ) ^ n; return static_cast ( ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 2147483647) / 1073741824.0) ); } void WorldChunk::createVoxelBlocksWithPerlinNoise() { static float baseSeaLevel = 32.0f; static float baseStoneLevel = 21.0f; static float baseDirtLevel = 32.0f; float stoneLevel = 0.0f; float dirtLevel = 0.0f; float seaLevel = 0.0f; static float angle = 0.0f; cbengine::Vector2 southWestGrid( m_origin.x, m_origin.y); cbengine::Vector2 southEastGrid( m_origin.x + 16.0f, m_origin.y ); cbengine::Vector2 northWestGrid( m_origin.x, m_origin.y + 16.0f ); cbengine::Vector2 northEastGrid( m_origin.x + 16.0f, m_origin.y + 16.0f ); cbengine::Vector2 southWestGradient = getRandomVectorAtPosition( (int) southWestGrid.x, (int) southWestGrid.y ); cbengine::Vector2 southEastGradient = getRandomVectorAtPosition( (int) southEastGrid.x, (int) southEastGrid.y ); cbengine::Vector2 northWestGradient = getRandomVectorAtPosition( (int) northWestGrid.x, (int) northWestGrid.y ); cbengine::Vector2 northEastGradient = getRandomVectorAtPosition( (int) northEastGrid.x, (int) northEastGrid.y ); size_t x = 0; size_t y = 0; size_t z = 0; for ( size_t index = 0; index < simpleminer::NUMBER_BLOCKS_IN_CHUNK; ++index ) { x = index & xMask; y = ( index & yMask ) >> 4; z = ( index & zMask ) >> 8; VoxelBlock & currentBlock = m_voxelBlocks[index]; cbengine::Vector2 vectorAtPosition; vectorAtPosition.x = m_origin.x + static_cast ( x ); vectorAtPosition.y = m_origin.y + static_cast ( y ); cbengine::Vector2 differenceVectorBottomLeft = (vectorAtPosition - southWestGrid); cbengine::Vector2 differenceVectorBottomRight = (vectorAtPosition - southEastGrid); cbengine::Vector2 differenceVectorTopLeft = (vectorAtPosition - northWestGrid); cbengine::Vector2 differenceVectorTopRight = (vectorAtPosition - northEastGrid); differenceVectorBottomLeft *= (1.f / 16.f); differenceVectorBottomRight *= (1.f / 16.f); differenceVectorTopLeft *= (1.f / 16.f); differenceVectorTopRight *= (1.f / 16.0f); float bottomLeftDotProduct = cbengine::Vector2::dotProduct( differenceVectorBottomLeft, southWestGradient ); float bottomRightDotProduct = cbengine::Vector2::dotProduct( differenceVectorBottomRight, southEastGradient ); float topLeftDotProduct = cbengine::Vector2::dotProduct( differenceVectorTopLeft, northWestGradient ); float topRightDotProduct = cbengine::Vector2::dotProduct( differenceVectorTopRight, northEastGradient ); float u = static_cast ( x ) / 16.0f; float v = static_cast ( y ) / 16.0f; u = smoothNoise( u ); v = smoothNoise( v ); float uWeightedTop = (1.f - u) * topLeftDotProduct + (u) * topRightDotProduct; float uWeightedBottom = (1.f - u) * bottomLeftDotProduct + (u) * bottomRightDotProduct; float averageOfBottomAndTop = (1.f - v) * uWeightedBottom + (v * uWeightedTop); stoneLevel = baseStoneLevel + averageOfBottomAndTop * cbengine::PI * 1.50f; dirtLevel = baseDirtLevel + averageOfBottomAndTop * cbengine::PI * 3.31f; seaLevel = baseSeaLevel; if ( z < stoneLevel ) { currentBlock.m_blockType = simpleminer::kBLOCK_TYPE_STONE; } else if ( z < dirtLevel ) { currentBlock.m_blockType = simpleminer::kBLOCK_TYPE_DIRT; } else if ( z < seaLevel ) { currentBlock.m_blockType = simpleminer::kBLOCK_TYPE_WATER; } else { currentBlock.m_blockType = simpleminer::kBLOCK_TYPE_AIR; } // end if } // end for } // end function float WorldChunk::smoothNoise( float numToSmooth ) { float smoothedNum = 3.0f * numToSmooth * numToSmooth - ( 2.0f * ( numToSmooth * numToSmooth * numToSmooth ) ); return smoothedNum; } // end smoothNoise void WorldChunk::determineVoxelBlockSideBitMaskValues() { // 6 Checks Total as cube has 6 sides size_t x = 0; size_t y = 0; size_t z = 0; for ( size_t index = 0; index < simpleminer::NUMBER_BLOCKS_IN_CHUNK; ++index ) { x = index & xMask; y = ( index & yMask ) >> 4; z = ( index & zMask ) >> 8; VoxelBlock & currentBlock = m_voxelBlocks[index]; const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); VoxelBlockMaterial * voxelMaterial = voxelMaterials[ static_cast ( currentBlock.m_blockType ) ]; if ( currentBlock.m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { cullVoxelBlockSidesForWaterBlock( index, x, y, z ); continue; } // Check the front side // Check the back side of block in front if ( x != 0 ) { VoxelBlock & adjacentBlockToCheck = m_voxelBlocks[index - 1]; voxelMaterial = voxelMaterials[ static_cast ( adjacentBlockToCheck.m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_FRONT_SIDE_MASK; } // end inner if } // end if // Determine mask for the back side // Check front side of next block if not x == 15 if ( x != ( simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK - 1 ) ) { VoxelBlock & adjacentBlockToCheck = m_voxelBlocks[index + 1]; voxelMaterial = voxelMaterials[ static_cast ( adjacentBlockToCheck.m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_BACK_SIDE_MASK; } } // Check mask for the right side // Check block to left of current block if ( y != 0 ) { VoxelBlock & adjacentBlockToCheck = m_voxelBlocks[index - simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK]; voxelMaterial = voxelMaterials[ static_cast ( adjacentBlockToCheck.m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_RIGHT_SIDE_MASK; } } // Check mask for the left side // Check the right side of the block to the left if ( y != ( simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK - 1 ) ) { VoxelBlock & adjacentBlockToCheck = m_voxelBlocks[index + simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK]; voxelMaterial = voxelMaterials[ static_cast ( adjacentBlockToCheck.m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_LEFT_SIDE_MASK; } } // Check the bottom of the block // Check the block below the current block if ( z != 0 ) { VoxelBlock & adjacentBlockToCheck = m_voxelBlocks[index - ( simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK * simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK ) ]; voxelMaterial = voxelMaterials[ static_cast ( adjacentBlockToCheck.m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_BOTTOM_SIDE_MASK; } } else { currentBlock.m_cubeSideBitMask |= VOXEL_BOTTOM_SIDE_MASK; } // Check the top of the block // Check the block above the current block if ( z != ( simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK - 1 ) ) { VoxelBlock & adjacentBlockToCheck = m_voxelBlocks[index + ( simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK * simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK ) ]; voxelMaterial = voxelMaterials[ static_cast ( adjacentBlockToCheck.m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_TOP_SIDE_MASK; } } else { currentBlock.m_cubeSideBitMask |= VOXEL_TOP_SIDE_MASK; } // end if } // end for } // end function void WorldChunk::cullVoxelBlockSidesForWaterBlock( size_t index, size_t x, size_t y, size_t z ) { VoxelBlock & currentBlock = m_voxelBlocks[index]; if ( currentBlock.m_blockType != simpleminer::kBLOCK_TYPE_WATER ) { return; } // Check the front side if ( x == 0 ) { currentBlock.m_cubeSideBitMask |= VOXEL_FRONT_SIDE_MASK; } // end if // Determine mask for the back side if ( x == ( simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK - 1 ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_BACK_SIDE_MASK; } // Check mask for the right side if ( y == 0 ) { currentBlock.m_cubeSideBitMask |= VOXEL_RIGHT_SIDE_MASK; } // Check mask for the left side if ( y == ( simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK - 1 ) ) { currentBlock.m_cubeSideBitMask |= VOXEL_LEFT_SIDE_MASK; } // Check the bottom of the block // Check the block below the current block if ( z == 0 ) { currentBlock.m_cubeSideBitMask |= VOXEL_BOTTOM_SIDE_MASK; } // Always draw the top currentBlock.m_cubeSideBitMask |= VOXEL_TOP_SIDE_MASK; } // end cull for water blocks function void WorldChunk::cullChunkToChunkVoxelBlockSideFaces() { assert( m_actionDelegate != nullptr ); // Do not need to check the top blocks or bottom blocks since we do not // have chunks on top of chunks cullRightSidesWhichFaceOtherChunks(); cullLeftSidesWhichFaceOtherChunks(); cullBackSidesWhichFaceOtherChunks(); cullFrontSidesWhichFaceOtherChunks(); } // end cullChunkToChunk void WorldChunk::cullRightSidesWhichFaceOtherChunks() { cbengine::Vector3D blockPosition; cbengine::Vector3D blockPositionToCheck; const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); VoxelBlockMaterial * voxelMaterial = nullptr; // Check right side of chunk for ( int z = 0; z < simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK; ++z ) { for ( int x = 0; x < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++x ) { blockPosition.x = static_cast ( x ); blockPosition.y = 0.0f; blockPosition.z = static_cast ( z ); blockPositionToCheck.x = m_origin.x + blockPosition.x; blockPositionToCheck.y = m_origin.y + blockPosition.y - 1.0f; blockPositionToCheck.z = m_origin.z + blockPosition.z; VoxelBlock * blockInChunkToRight = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); VoxelBlock * blockToDraw = getVoxelBlockAtPosition( blockPosition ); if ( blockInChunkToRight == nullptr ) { blockToDraw->m_cubeSideBitMask |= VOXEL_RIGHT_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockInChunkToRight->m_blockType ) ]; if ( blockToDraw->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { // We know it isn't facing the end of the world blockToDraw->m_cubeSideBitMask &= ~VOXEL_RIGHT_SIDE_MASK; } else if ( !( voxelMaterial->m_isOpaque ) ) { // Cull the side blockToDraw->m_cubeSideBitMask |= VOXEL_RIGHT_SIDE_MASK; } else { blockToDraw->m_cubeSideBitMask &= ~VOXEL_RIGHT_SIDE_MASK; } } // end if } // end for } // end for } // end cull right side void WorldChunk::cullLeftSidesWhichFaceOtherChunks() { cbengine::Vector3D blockPosition; cbengine::Vector3D blockPositionToCheck; const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); VoxelBlockMaterial * voxelMaterial = nullptr; // Check the left side of the chunk for ( int z = 0; z < simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK; ++z ) { for ( int x = 0; x < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++x ) { blockPosition.x = static_cast ( x ); blockPosition.y = 15.0f; blockPosition.z = static_cast ( z ); blockPositionToCheck.x = m_origin.x + blockPosition.x; blockPositionToCheck.y = m_origin.y + blockPosition.y + 1.0f; blockPositionToCheck.z = m_origin.z + blockPosition.z; VoxelBlock * blockInChunkToLeft = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); VoxelBlock * blockToDraw = getVoxelBlockAtPosition( blockPosition ); if ( blockInChunkToLeft == nullptr ) { // Chunk to the left doesn't exist blockToDraw->m_cubeSideBitMask |= VOXEL_LEFT_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockInChunkToLeft->m_blockType ) ]; if ( blockToDraw->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { blockToDraw->m_cubeSideBitMask &= ~VOXEL_LEFT_SIDE_MASK; } else if ( !( voxelMaterial->m_isOpaque ) ) { // Draw the side blockToDraw->m_cubeSideBitMask |= VOXEL_LEFT_SIDE_MASK; } else { blockToDraw->m_cubeSideBitMask &= ~VOXEL_LEFT_SIDE_MASK; } } // end if } // end for } // end for } // endCullLeftsides void WorldChunk::cullBackSidesWhichFaceOtherChunks() { cbengine::Vector3D blockPosition; cbengine::Vector3D blockPositionToCheck; const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); VoxelBlockMaterial * voxelMaterial = nullptr; // Check front side of chunk for ( int z = 0; z < simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK; ++z ) { for ( int y = 0; y < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++y ) { blockPosition.x = 15.0f; blockPosition.y = static_cast ( y ); blockPosition.z = static_cast ( z ); blockPositionToCheck.x = m_origin.x + blockPosition.x + 1.0f; blockPositionToCheck.y = m_origin.y + blockPosition.y; blockPositionToCheck.z = m_origin.z + blockPosition.z; VoxelBlock * blockInChunkToBack = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); VoxelBlock * blockToDraw = getVoxelBlockAtPosition( blockPosition ); if ( blockInChunkToBack == nullptr ) { blockToDraw->m_cubeSideBitMask |= VOXEL_BACK_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockInChunkToBack->m_blockType ) ]; if ( blockToDraw->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { blockToDraw->m_cubeSideBitMask &= ~VOXEL_BACK_SIDE_MASK; } else if ( !( voxelMaterial->m_isOpaque ) ) { // Cull the side blockToDraw->m_cubeSideBitMask |= VOXEL_BACK_SIDE_MASK; } else { blockToDraw->m_cubeSideBitMask &= ~ VOXEL_BACK_SIDE_MASK; } } } // end for } // end for } void WorldChunk::cullFrontSidesWhichFaceOtherChunks() { cbengine::Vector3D blockPosition; cbengine::Vector3D blockPositionToCheck; const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); VoxelBlockMaterial * voxelMaterial = nullptr; // Check front side of chunk for ( int z = 0; z < simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK; ++z ) { for ( int y = 0; y < simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK; ++y ) { blockPosition.x = 0.0f; blockPosition.y = static_cast ( y ); blockPosition.z = static_cast ( z ); blockPositionToCheck.x = m_origin.x + blockPosition.x - 1.0f; blockPositionToCheck.y = m_origin.y + blockPosition.y; blockPositionToCheck.z = m_origin.z + blockPosition.z; VoxelBlock * blockInChunkToFront = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck ); VoxelBlock * blockToDraw = getVoxelBlockAtPosition( blockPosition ); if ( blockInChunkToFront == nullptr ) { blockToDraw->m_cubeSideBitMask |= VOXEL_FRONT_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockInChunkToFront->m_blockType ) ]; if ( blockToDraw->m_blockType == simpleminer::kBLOCK_TYPE_WATER ) { blockToDraw->m_cubeSideBitMask &= ~ VOXEL_FRONT_SIDE_MASK; } else if ( !( voxelMaterial->m_isOpaque ) ) { // Cull the side blockToDraw->m_cubeSideBitMask |= VOXEL_FRONT_SIDE_MASK; } else { blockToDraw->m_cubeSideBitMask &= ~VOXEL_FRONT_SIDE_MASK; } } } // end for } // end for } // end function void WorldChunk::createVertexDataForVBO() { // Call this function after creating the vert data and culling const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); simpleminer::BlockFace currentBlockFace; size_t x = 0; size_t y = 0; size_t z = 0; for ( size_t index = 0; index < simpleminer::NUMBER_BLOCKS_IN_CHUNK; ++index ) { const VoxelBlock & voxelBlock = m_voxelBlocks[index]; const VoxelBlockMaterial * voxelMaterial = voxelMaterials[ static_cast ( voxelBlock.m_blockType ) ]; if ( voxelBlock.m_blockType == simpleminer::kBLOCK_TYPE_AIR ) { continue; } // end if WorldLightingManager * sharedWorldLightingManager = WorldLightingManager::sharedWorldLightingManager(); float normalizedLightCoefficient = 0.0f; x = index & xMask; y = ( index & yMask ) >> 4; z = ( index & zMask ) >> 8; float xPosFloat = static_cast ( x ); float yPosFloat = static_cast ( y ); float zPosFloat = static_cast ( z ); cbengine::Vector3D blockPosition( xPosFloat + m_origin.x, yPosFloat + m_origin.y, zPosFloat + m_origin.z ); cbengine::Vertex vertSouthWest; cbengine::Vertex vertSouthEast; cbengine::Vertex vertNorthEast; cbengine::Vertex vertNorthWest; // Texture Coords vertSouthEast.vertexTextureCoords.x = voxelMaterial->m_minTextureUVs.x; vertSouthEast.vertexTextureCoords.y = voxelMaterial->m_maxTextureUVs.y; vertSouthWest.vertexTextureCoords.x = voxelMaterial->m_maxTextureUVs.x; vertSouthWest.vertexTextureCoords.y = voxelMaterial->m_maxTextureUVs.y; vertNorthWest.vertexTextureCoords.x = voxelMaterial->m_maxTextureUVs.x; vertNorthWest.vertexTextureCoords.y = voxelMaterial->m_minTextureUVs.y; vertNorthEast.vertexTextureCoords.x = voxelMaterial->m_minTextureUVs.x; vertNorthEast.vertexTextureCoords.y = voxelMaterial->m_minTextureUVs.y; // Front side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_FRONT_SIDE_MASK ) == VOXEL_FRONT_SIDE_MASK ) { currentBlockFace = simpleminer::kSOUTH_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; vertSouthWest.vertexPosition.x = xPosFloat; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat; vertSouthEast.vertexPosition.x = xPosFloat; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat; vertNorthEast.vertexPosition.x = xPosFloat; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; vertNorthWest.vertexPosition.x = xPosFloat; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Right Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_RIGHT_SIDE_MASK ) == VOXEL_RIGHT_SIDE_MASK ) { currentBlockFace = simpleminer::kEAST_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbEastWestCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbEastWestCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbEastWestCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( 0.0f, 0.0f, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat; vertSouthWest.vertexPosition.y = yPosFloat; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, 0.0f, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, 0.0f, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( 0.0f, 0.0f, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat; vertNorthWest.vertexPosition.y = yPosFloat; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Back Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_BACK_SIDE_MASK ) == VOXEL_BACK_SIDE_MASK ) { currentBlockFace = simpleminer::kNORTH_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( cubeSideSizes.x, 0.0f, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthWest.vertexPosition.y = yPosFloat; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, 0.0f, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthWest.vertexPosition.y = yPosFloat; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Left Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_LEFT_SIDE_MASK ) == VOXEL_LEFT_SIDE_MASK ) { currentBlockFace = simpleminer::kWEST_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbEastWestCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbEastWestCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbEastWestCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, cubeSideSizes.y, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat; vertSouthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, cubeSideSizes.y, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat; vertNorthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Top Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_TOP_SIDE_MASK ) == VOXEL_TOP_SIDE_MASK ) { currentBlockFace = simpleminer::kTOP_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( 0.0f, cubeSideSizes.y, cubeSideSizes.z ); vertSouthWest.vertexPosition.x = xPosFloat; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( 0.0f, 0.0f, cubeSideSizes.z ); vertSouthEast.vertexPosition.x = xPosFloat; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, 0.0f, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Bottom side Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_BOTTOM_SIDE_MASK ) == VOXEL_BOTTOM_SIDE_MASK ) { currentBlockFace = simpleminer::kBOTTOM_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, 0.0f, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, 0.0f, 0.0f ); vertNorthEast.vertexPosition.x = xPosFloat; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, cubeSideSizes.y, 0.0f ); vertNorthWest.vertexPosition.x = xPosFloat; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if } // end for // Now set up the buffers cbengine::CBRenderer * sharedRenderer = cbengine::CBRenderer::sharedCBRenderer(); sharedRenderer->generateVBOBuffers( 1, &m_vertexBufferObjectID ); sharedRenderer->bindVBOBuffer( m_vertexBufferObjectID ); sharedRenderer->bufferDataForVBO( ( sizeof( cbengine::Vertex ) * m_vertsForVBO.size() ), &m_vertsForVBO.front() ); } // end function void WorldChunk::recreateVBOVertexData() { // Call this function after creating the vert data and culling m_vertsForVBO.clear(); const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); WorldLightingManager * sharedWorldLightingManager = WorldLightingManager::sharedWorldLightingManager(); float normalizedLightCoefficient = 0.0f; size_t x = 0; size_t y = 0; size_t z = 0; for ( size_t index = 0; index < simpleminer::NUMBER_BLOCKS_IN_CHUNK; ++index ) { const VoxelBlock & voxelBlock = m_voxelBlocks[index]; const VoxelBlockMaterial * voxelMaterial = voxelMaterials[ static_cast ( voxelBlock.m_blockType ) ]; if ( voxelBlock.m_blockType == simpleminer::kBLOCK_TYPE_AIR ) { continue; } // end if x = index & xMask; y = ( index & yMask ) >> 4; z = ( index & zMask ) >> 8; float xPosFloat = static_cast ( x ); float yPosFloat = static_cast ( y ); float zPosFloat = static_cast ( z ); cbengine::Vector3D blockPosition( xPosFloat + m_origin.x, yPosFloat + m_origin.y, zPosFloat + m_origin.z ); simpleminer::BlockFace currentBlockFace; cbengine::Vertex vertSouthWest; cbengine::Vertex vertSouthEast; cbengine::Vertex vertNorthEast; cbengine::Vertex vertNorthWest; // Texture Coords vertSouthEast.vertexTextureCoords.x = voxelMaterial->m_minTextureUVs.x; vertSouthEast.vertexTextureCoords.y = voxelMaterial->m_maxTextureUVs.y; vertSouthWest.vertexTextureCoords.x = voxelMaterial->m_maxTextureUVs.x; vertSouthWest.vertexTextureCoords.y = voxelMaterial->m_maxTextureUVs.y; vertNorthWest.vertexTextureCoords.x = voxelMaterial->m_maxTextureUVs.x; vertNorthWest.vertexTextureCoords.y = voxelMaterial->m_minTextureUVs.y; vertNorthEast.vertexTextureCoords.x = voxelMaterial->m_minTextureUVs.x; vertNorthEast.vertexTextureCoords.y = voxelMaterial->m_minTextureUVs.y; // Front side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_FRONT_SIDE_MASK ) == VOXEL_FRONT_SIDE_MASK ) { currentBlockFace = simpleminer::kSOUTH_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( 0.0f, cubeSideSizes.y, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, 0.0f, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, 0.0f, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( 0.0f, cubeSideSizes.y, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Right Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_RIGHT_SIDE_MASK ) == VOXEL_RIGHT_SIDE_MASK ) { currentBlockFace = simpleminer::kEAST_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbEastWestCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbEastWestCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbEastWestCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( 0.0f, 0.0f, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat; vertSouthWest.vertexPosition.y = yPosFloat; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, 0.0f, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, 0.0f, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( 0.0f, 0.0f, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat; vertNorthWest.vertexPosition.y = yPosFloat; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Back Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_BACK_SIDE_MASK ) == VOXEL_BACK_SIDE_MASK ) { currentBlockFace = simpleminer::kNORTH_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbNorthSouthCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( cubeSideSizes.x, 0.0f, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthWest.vertexPosition.y = yPosFloat; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, 0.0f, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthWest.vertexPosition.y = yPosFloat; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Left Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_LEFT_SIDE_MASK ) == VOXEL_LEFT_SIDE_MASK ) { currentBlockFace = simpleminer::kWEST_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x * rgbEastWestCoefficient; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y * rgbEastWestCoefficient; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z * rgbEastWestCoefficient; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, cubeSideSizes.y, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat; vertSouthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, cubeSideSizes.y, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat; vertNorthEast.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Top Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_TOP_SIDE_MASK ) == VOXEL_TOP_SIDE_MASK ) { currentBlockFace = simpleminer::kTOP_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( 0.0f, cubeSideSizes.y, cubeSideSizes.z ); vertSouthWest.vertexPosition.x = xPosFloat; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( 0.0f, 0.0f, cubeSideSizes.z ); vertSouthEast.vertexPosition.x = xPosFloat; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, 0.0f, cubeSideSizes.z ); vertNorthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat + cubeSideSize; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, cubeSideSizes.z ); vertNorthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat + cubeSideSize; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if // Bottom side Side if ( ( voxelBlock.m_cubeSideBitMask & VOXEL_BOTTOM_SIDE_MASK ) == VOXEL_BOTTOM_SIDE_MASK ) { currentBlockFace = simpleminer::kBOTTOM_FACE; normalizedLightCoefficient = sharedWorldLightingManager->getNormalizedLightingValueForBlockFace( blockPosition, currentBlockFace ); vertSouthWest.vertexColor.x = voxelMaterial->m_blockColor.x; vertSouthWest.vertexColor.y = voxelMaterial->m_blockColor.y; vertSouthWest.vertexColor.z = voxelMaterial->m_blockColor.z; vertSouthWest.vertexColor.w = voxelMaterial->m_alpha; vertSouthWest.applyLightingCoefficientToRGBColor( normalizedLightCoefficient ); // Make copies of color data vertSouthEast.vertexColor = vertSouthWest.vertexColor; vertNorthEast.vertexColor = vertSouthWest.vertexColor; vertNorthWest.vertexColor = vertSouthWest.vertexColor; //glVertex3f( cubeSideSizes.x, cubeSideSizes.y, 0.0f ); vertSouthWest.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertSouthWest.vertexPosition.z = zPosFloat; //glVertex3f( cubeSideSizes.x, 0.0f, 0.0f ); vertSouthEast.vertexPosition.x = xPosFloat + cubeSideSize; vertSouthEast.vertexPosition.y = yPosFloat; vertSouthEast.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, 0.0f, 0.0f ); vertNorthEast.vertexPosition.x = xPosFloat; vertNorthEast.vertexPosition.y = yPosFloat; vertNorthEast.vertexPosition.z = zPosFloat; //glVertex3f( 0.0f, cubeSideSizes.y, 0.0f ); vertNorthWest.vertexPosition.x = xPosFloat; vertNorthWest.vertexPosition.y = yPosFloat + cubeSideSize; vertNorthWest.vertexPosition.z = zPosFloat; m_vertsForVBO.push_back( vertSouthWest ); m_vertsForVBO.push_back( vertSouthEast ); m_vertsForVBO.push_back( vertNorthEast ); m_vertsForVBO.push_back( vertNorthWest ); } // end if } // end for // Delete previous buffer cbengine::CBRenderer * sharedRenderer = cbengine::CBRenderer::sharedCBRenderer(); sharedRenderer->deleteVBOBuffers( 1, &m_vertexBufferObjectID ); // Now set up the buffers sharedRenderer->generateVBOBuffers( 1, &m_vertexBufferObjectID ); sharedRenderer->bindVBOBuffer( m_vertexBufferObjectID ); sharedRenderer->bufferDataForVBO( ( sizeof( cbengine::Vertex ) * m_vertsForVBO.size() ), &m_vertsForVBO.front() ); m_dirty = false; } // end recreate void WorldChunk::createBlockAtPosition( cbengine::Vector3D & blockPosition, unsigned char blockType ) { // TODO :: Break this down into several functions // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); // Assumes localized block position VoxelBlock * blockToModify = &m_voxelBlocks[indexOfBlock]; // Sanity check assert( blockToModify != nullptr ); // This is super hacky if ( blockToModify->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { return; } blockToModify->m_blockType = blockType; blockToModify->m_cubeSideBitMask = 0; // Determine Lighting WorldLightingManager * sharedWorldLightingManager = WorldLightingManager::sharedWorldLightingManager(); cbengine::Vector3D positionOfNewBlockInWorldCoords( m_origin.x + blockPosition.x, m_origin.y + blockPosition.y, m_origin.z + blockPosition.z ); sharedWorldLightingManager->determineLightingForNewlyPlacedBlock( positionOfNewBlockInWorldCoords ); sharedWorldLightingManager->distributeLightForBlockChangeAtPosition( positionOfNewBlockInWorldCoords ); // Determine bit mask determineSideBitMasksForNewBlock( indexOfBlock ); m_dirty = true; // Now need to cull all effected chunks and their cube sides.. cbengine::Vector3D localizedBlockPosition; cbengine::Vector3D impactedBlockPosition; WorldChunk * impactedChunk = nullptr; // Check if block from behind should cull its front side impactedBlockPosition.x = m_origin.x + blockPosition.x + cubeSideSize; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->destroyBlockFrontSideAtPosition( localizedBlockPosition ); } // Check if block to right should cull its left side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y - cubeSideSize; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->destroyBlockLeftSideAtPosition( localizedBlockPosition ); } // Check if the block to front should cull its backside impactedBlockPosition.x = m_origin.x + blockPosition.x - cubeSideSize; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->destroyBlockBackSideAtPosition( localizedBlockPosition ); } // Check if block to left should reveal its right side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y + cubeSideSize; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->destroyBlockRightSideAtPosition( localizedBlockPosition ); } // Check block above to see if it should cull its bottom side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z + cubeSideSize; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->destroyBlockBottomSideAtPosition( localizedBlockPosition ); } // Check block below to see if it should cull its top side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z - cubeSideSize; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->destroyBlockTopSideAtPosition( localizedBlockPosition ); } // end if // This code seriously needs to be divided into functions if time permits } // end function void WorldChunk::destroyBlockAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); // Assumes localized block position VoxelBlock * blockToDestroy = &m_voxelBlocks[indexOfBlock]; // Sanity check assert( blockToDestroy != nullptr ); // Default destroy type blockToDestroy->m_blockType = simpleminer::kBLOCK_TYPE_AIR; //blockToDestroy->m_cubeSideBitMask = 0; // zero out the bit mask m_dirty = true; cbengine::Vector3D currentBlockWorldCoords; currentBlockWorldCoords.x = blockPosition.x + m_origin.x; currentBlockWorldCoords.y = blockPosition.y + m_origin.y; currentBlockWorldCoords.z = blockPosition.z + m_origin.z; WorldLightingManager * sharedWorldLightingManager = WorldLightingManager::sharedWorldLightingManager(); sharedWorldLightingManager->determineLightingForDestroyedBlock( currentBlockWorldCoords ); sharedWorldLightingManager->distributeLightForBlockChangeAtPosition( currentBlockWorldCoords ); // Now need to reveal all effected chunks and their cube sides.. cbengine::Vector3D localizedBlockPosition; cbengine::Vector3D impactedBlockPosition; WorldChunk * impactedChunk = nullptr; // Check if block from behind should reveal its front side impactedBlockPosition.x = m_origin.x + blockPosition.x + cubeSideSize; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->revealBlockFrontSideAtPosition( localizedBlockPosition ); } // Check if block to right should reveal its left side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y - cubeSideSize; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->revealBlockLeftSideAtPosition( localizedBlockPosition ); } // Check if the block to front should reveal its backside impactedBlockPosition.x = m_origin.x + blockPosition.x - cubeSideSize; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->revealBlockBackSideAtPosition( localizedBlockPosition ); } // Check if block to left should reveal its right side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y + cubeSideSize; impactedBlockPosition.z = m_origin.z + blockPosition.z; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->revealBlockRightSideAtPosition( localizedBlockPosition ); } // Check block above to see if it should reveal its bottom side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z + cubeSideSize; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->revealBlockBottomSideAtPosition( localizedBlockPosition ); } // Check block below to see if it should reveal its top side impactedBlockPosition.x = m_origin.x + blockPosition.x; impactedBlockPosition.y = m_origin.y + blockPosition.y; impactedBlockPosition.z = m_origin.z + blockPosition.z - cubeSideSize; impactedChunk = m_actionDelegate->getChunkAtPositionAndLocalizeBlockCoordinates( impactedBlockPosition, localizedBlockPosition ); if ( impactedChunk != nullptr ) { impactedChunk->revealBlockTopSideAtPosition( localizedBlockPosition ); } // end if // This code seriously needs to be divided into functions if time permits } // end destroyBlock // Destroying void WorldChunk::destroyBlockFrontSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { // flags &= ~mask block->m_cubeSideBitMask &= ~VOXEL_FRONT_SIDE_MASK; m_dirty = true; } } void WorldChunk::destroyBlockRightSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask &= ~VOXEL_RIGHT_SIDE_MASK; m_dirty = true; } } void WorldChunk::destroyBlockBackSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask &= ~VOXEL_BACK_SIDE_MASK; m_dirty = true; } } void WorldChunk::destroyBlockLeftSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask &= ~VOXEL_LEFT_SIDE_MASK; m_dirty = true; } } void WorldChunk::destroyBlockBottomSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask &= ~VOXEL_BOTTOM_SIDE_MASK; m_dirty = true; } } void WorldChunk::destroyBlockTopSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask &= ~VOXEL_TOP_SIDE_MASK; m_dirty = true; } } // Revealing void WorldChunk::revealBlockFrontSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask |= VOXEL_FRONT_SIDE_MASK; m_dirty = true; } } void WorldChunk::revealBlockRightSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { // |= block->m_cubeSideBitMask |= VOXEL_RIGHT_SIDE_MASK; m_dirty = true; } } void WorldChunk::revealBlockBackSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask |= VOXEL_BACK_SIDE_MASK; m_dirty = true; } } void WorldChunk::revealBlockLeftSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask |= VOXEL_LEFT_SIDE_MASK; m_dirty = true; } } void WorldChunk::revealBlockBottomSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask |= VOXEL_BOTTOM_SIDE_MASK; m_dirty = true; } } void WorldChunk::revealBlockTopSideAtPosition( cbengine::Vector3D & blockPosition ) { // Do a bounds check if ( blockPosition.x < 0 || blockPosition.x >= simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK || blockPosition.y < 0 || blockPosition.y >= simpleminer::NUMBER_BLOCKS_Y_DIMENSION_PER_CHUNK || blockPosition.z < 0 || blockPosition.z >= simpleminer::NUMBER_BLOCKS_Z_DIMENSION_PER_CHUNK ) { assert( true == true ); } // end if size_t indexOfBlock = 0; indexOfBlock = ( static_cast ( blockPosition.x ) & xMask ) + ( ( static_cast ( blockPosition.y ) << 4 ) & yMask ) + ( ( static_cast ( blockPosition.z ) << 8 ) & zMask ); VoxelBlock * block = &m_voxelBlocks[indexOfBlock]; if ( block->m_blockType != simpleminer::kBLOCK_TYPE_AIR ) { block->m_cubeSideBitMask |= VOXEL_TOP_SIDE_MASK; m_dirty = true; } } void WorldChunk::determineSideBitMasksForNewBlock( unsigned int index ) { size_t x = 0; size_t y = 0; size_t z = 0; x = index & xMask; y = ( index & yMask ) >> 4; z = ( index & zMask ) >> 8; float xPosFloat = static_cast ( x ); float yPosFloat = static_cast ( y ); float zPosFloat = static_cast ( z ); const std::vector< VoxelBlockMaterial * > & voxelMaterials = m_actionDelegate->getVoxelBlockMaterials(); VoxelBlock * currentBlock = &m_voxelBlocks[ index ]; VoxelBlockMaterial * voxelMaterial = nullptr; cbengine::Vector3D blockToCheckPosition; // Check front blockToCheckPosition.x = m_origin.x + xPosFloat - cubeSideSize; blockToCheckPosition.y = m_origin.y + yPosFloat; blockToCheckPosition.z = m_origin.z + zPosFloat; VoxelBlock * blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockToCheckPosition ); if ( blockToCheck == nullptr ) { // Cull the front as chunk doesn't exist currentBlock->m_cubeSideBitMask |= VOXEL_FRONT_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockToCheck->m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { // Cull front currentBlock->m_cubeSideBitMask |= VOXEL_FRONT_SIDE_MASK; } // end if } // end if // Check Right blockToCheckPosition.x = m_origin.x + xPosFloat; blockToCheckPosition.y = m_origin.y + yPosFloat - cubeSideSize; blockToCheckPosition.z = m_origin.z + zPosFloat; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockToCheckPosition ); if ( blockToCheck == nullptr ) { currentBlock->m_cubeSideBitMask |= VOXEL_RIGHT_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockToCheck->m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock->m_cubeSideBitMask |= VOXEL_RIGHT_SIDE_MASK; } // end if } // end if // Check Back blockToCheckPosition.x = m_origin.x + xPosFloat + cubeSideSize; blockToCheckPosition.y = m_origin.y + yPosFloat; blockToCheckPosition.z = m_origin.z + zPosFloat; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockToCheckPosition ); if ( blockToCheck == nullptr ) { currentBlock->m_cubeSideBitMask |= VOXEL_BACK_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockToCheck->m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock->m_cubeSideBitMask |= VOXEL_BACK_SIDE_MASK; } // end if } // end if // Check Left blockToCheckPosition.x = m_origin.x + xPosFloat; blockToCheckPosition.y = m_origin.y + yPosFloat + cubeSideSize; blockToCheckPosition.z = m_origin.z + zPosFloat; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockToCheckPosition ); if ( blockToCheck == nullptr ) { currentBlock->m_cubeSideBitMask |= VOXEL_LEFT_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockToCheck->m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock->m_cubeSideBitMask |= VOXEL_LEFT_SIDE_MASK; } // end if } // end if // Check Bottom blockToCheckPosition.x = m_origin.x + xPosFloat; blockToCheckPosition.y = m_origin.y + yPosFloat; blockToCheckPosition.z = m_origin.z + zPosFloat - cubeSideSize; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockToCheckPosition ); if ( blockToCheck == nullptr ) { currentBlock->m_cubeSideBitMask |= VOXEL_BOTTOM_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockToCheck->m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock->m_cubeSideBitMask |= VOXEL_BOTTOM_SIDE_MASK; } } // Check Top blockToCheckPosition.x = m_origin.x + xPosFloat; blockToCheckPosition.y = m_origin.y + yPosFloat; blockToCheckPosition.z = m_origin.z + zPosFloat + cubeSideSize; blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockToCheckPosition ); if ( blockToCheck == nullptr ) { currentBlock->m_cubeSideBitMask |= VOXEL_TOP_SIDE_MASK; } else { voxelMaterial = voxelMaterials[ static_cast ( blockToCheck->m_blockType ) ]; if ( !( voxelMaterial->m_isOpaque ) ) { currentBlock->m_cubeSideBitMask |= VOXEL_TOP_SIDE_MASK; } // end if } // end if } // end function -
ChunkResourceManager.hpp
#ifndef included_ChunkResourceManager #define included_ChunkResourceManager #pragma once #include
#include #include -
ChunkResourceManager.cpp
#include "ChunkResourceManager.hpp" #include
#include #include "GameConstants.hpp" #include "WorldChunk.hpp" #include "VoxelBlock.hpp" // Convenient way to convert int to string #define SSTR( x ) dynamic_cast< std::ostringstream & >( \ ( std::ostringstream() << std::dec << x ) ).str() WorldChunk * ChunkResourceManager::loadWorldChunkWithCoords( const cbengine::IntVector2 & chunkCoords, ActionDelegate * actionDelegate ) { WorldChunk * chunkToLoad = nullptr; std::string xCoordAsString = SSTR( chunkCoords.x ); std::string yCoordAsString = SSTR( chunkCoords.y ); std::string chunkFileName = CHUNK_FOLDER_NAME + X_POSITION_NAME + xCoordAsString + Y_POSITION_NAME + yCoordAsString + CHUNK_FILE_EXT; errno_t error; FILE * chunkFile; error = fopen_s( &chunkFile, chunkFileName.c_str(), "rb" ); if ( error != 0 || chunkFile == nullptr ) { return nullptr; } char chunkBuffer[ MAX_CHUNK_BUFFER_SIZE ]; size_t resultOfFileRead; resultOfFileRead = fread( chunkBuffer, sizeof( char ), sizeof( char ) * MAX_CHUNK_BUFFER_SIZE, chunkFile ); chunkToLoad = new WorldChunk; formChunkFromRLEEncodedFile( actionDelegate, chunkCoords, chunkBuffer, resultOfFileRead, *(chunkToLoad) ); fclose( chunkFile ); return chunkToLoad; } // end loadChunkWithCoords void ChunkResourceManager::formChunkFromRLEEncodedFile( ActionDelegate * actionDelegate, const cbengine::IntVector2 & chunkCoords, char * chunkBuffer, size_t sizeOfBuffer, WorldChunk & chunkToForm ) { UNUSED( chunkCoords ); chunkToForm.setActionDelegate( actionDelegate ); VoxelBlock * voxelBlocks = chunkToForm.getVoxelBlockArrayToModify(); std::string currentNumberString; int numberOfTypeOfBlock = 0; size_t currentIndex = 0; unsigned char blockTypeFromRLE = simpleminer::kBLOCK_TYPE_UNKNOWN; char & currentChar = chunkBuffer[0]; blockTypeFromRLE = getBlockTypeFromRLEByte( currentChar ); unsigned char previousBlockType = blockTypeFromRLE; for ( size_t i = 1; i < sizeOfBuffer; ++i ) { currentChar = chunkBuffer[i]; blockTypeFromRLE = getBlockTypeFromRLEByte( currentChar ); if ( blockTypeFromRLE == simpleminer::kBLOCK_TYPE_UNKNOWN ) { currentNumberString += currentChar; } else { // Found a new type of block... So we need to create the voxel blocks with previous known data numberOfTypeOfBlock = atoi( currentNumberString.c_str() ); currentNumberString.clear(); for ( size_t blockIndex = currentIndex; blockIndex < ( currentIndex + numberOfTypeOfBlock ); ++ blockIndex ) { voxelBlocks[ blockIndex ].m_blockType = previousBlockType; } currentIndex += static_cast ( numberOfTypeOfBlock ); previousBlockType = blockTypeFromRLE; } // end if } // end for numberOfTypeOfBlock = atoi( currentNumberString.c_str() ); for ( size_t blockIndex = currentIndex; blockIndex < ( currentIndex + numberOfTypeOfBlock ); ++ blockIndex ) { voxelBlocks[ blockIndex ].m_blockType = previousBlockType; } } // end formChunkFromRLEEncodedFile unsigned char ChunkResourceManager::getBlockTypeFromRLEByte( const char & byteFromChunkFile ) const { unsigned char blockTypeToCheck = static_cast ( byteFromChunkFile ); std::map< unsigned char, unsigned char >::const_iterator it; it = m_rleEncodingToBlockType.find( blockTypeToCheck ); if ( it != m_rleEncodingToBlockType.end() ) { return it->second; } else { return simpleminer::kBLOCK_TYPE_UNKNOWN; } } void ChunkResourceManager::cacheChunkToFileWithCoords( const WorldChunk & chunkToCache ) { const float inverseXYBlockCountPerChunk = ( 1.0f / static_cast ( simpleminer::NUMBER_BLOCKS_X_DIMENSION_PER_CHUNK ) ); std::string rleEncodingForChunk; formRLEEncodingFromChunk( chunkToCache, rleEncodingForChunk ); FILE * chunkFile; errno_t error; float xChunkPositionAsFloat = chunkToCache.m_origin.x * inverseXYBlockCountPerChunk; float yChunkPositionAsFloat = chunkToCache.m_origin.y * inverseXYBlockCountPerChunk; int xChunkPositionAsInt = static_cast ( floorf( xChunkPositionAsFloat ) ); int yChunkPositionAsInt = static_cast ( floorf( yChunkPositionAsFloat ) ); std::string xCoordAsString = SSTR( xChunkPositionAsInt ); std::string yCoordAsString = SSTR( yChunkPositionAsInt ); std::string chunkFileName = CHUNK_FOLDER_NAME + X_POSITION_NAME + xCoordAsString + Y_POSITION_NAME + yCoordAsString + CHUNK_FILE_EXT; error = fopen_s( &chunkFile, chunkFileName.c_str(), "wb" ); if ( error != 0 ) { return; } const char * buffer = rleEncodingForChunk.c_str(); fwrite( buffer, sizeof( char ), rleEncodingForChunk.size(), chunkFile ); fclose( chunkFile ); } void ChunkResourceManager::formRLEEncodingFromChunk( const WorldChunk & chunkToCache, std::string & rleEncoding ) { const VoxelBlock * voxelBlocks = chunkToCache.getVoxelBlockArray(); assert( voxelBlocks != nullptr ); const VoxelBlock & firstBlock = voxelBlocks[0]; unsigned char currentBlockType = firstBlock.m_blockType; size_t currentBlockTypeCount = 1; for ( size_t i = 1; i < simpleminer::NUMBER_BLOCKS_IN_CHUNK; ++i ) { const VoxelBlock & currentBlock = voxelBlocks[i]; if ( currentBlockType != currentBlock.m_blockType ) { // Add to string and reset count switch ( currentBlockType ) { case simpleminer::kBLOCK_TYPE_AIR: rleEncoding += RLE_AIR_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_DIRT: rleEncoding += RLE_DIRT_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_STONE: rleEncoding += RLE_STONE_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_WATER: rleEncoding += RLE_WATER_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_TREE_TRUNK: rleEncoding += RLE_TREE_TRUNK_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_TREE_LEAF: rleEncoding += RLE_TREE_LEAF_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_GLOWSTONE: rleEncoding += RLE_GLOWSTONE_MARKER + SSTR( currentBlockTypeCount ); break; default: assert( true == true ); break; } // end switch currentBlockTypeCount = 1; currentBlockType = currentBlock.m_blockType; } else { ++currentBlockTypeCount; } // end if } // end for switch ( currentBlockType ) { case simpleminer::kBLOCK_TYPE_AIR: rleEncoding += RLE_AIR_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_DIRT: rleEncoding += RLE_DIRT_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_STONE: rleEncoding += RLE_STONE_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_WATER: rleEncoding += RLE_WATER_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_TREE_TRUNK: rleEncoding += RLE_TREE_TRUNK_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_TREE_LEAF: rleEncoding += RLE_TREE_LEAF_MARKER + SSTR( currentBlockTypeCount ); break; case simpleminer::kBLOCK_TYPE_GLOWSTONE: rleEncoding += RLE_GLOWSTONE_MARKER + SSTR( currentBlockTypeCount ); break; default: assert( true == true ); break; } // end switch } // end function