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

 

 

Underground area lit by a glow stone

Underground area with minimal lighting


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 
    #include 
    #include 
    
    #include "..\EngineCode\EngineMacros.hpp"
    #include "..\EngineCode\Vector3D.hpp"
    
    #include "GameConstants.hpp"
    #include "VoxelBlock.hpp"
    #include "ActionDelegate.hpp"
    
    const bool	DO_SMOOTH_LIGHTING_COEFFICIENT = true;
    
    class IntVector2;
    class WorldChunk;
    class LightingNode;
    
    class WorldLightingManager {
    
    public:
    
    	static WorldLightingManager * sharedWorldLightingManager() {
    
    		static WorldLightingManager * worldLightingManager = new WorldLightingManager;
    		return worldLightingManager;
    
    	}
    
    	~WorldLightingManager();
    
    	// Assumes all chunks have been created
    	void createInitialLightingValuesForChunks( std::map< cbengine::IntVector2, WorldChunk *> & worldChunks );
    	void createInitialLightingValuesForSingleChunk( WorldChunk * worldChunk );
    	void determineInitialLightingValueForBlockAtPosition( float x, float y, float z, VoxelBlock & voxelBlock );
    
    	void determineLightingForNewlyPlacedBlock( cbengine::Vector3D & blockPosition );
    	void distributeLightForBlockChangeAtPosition( cbengine::Vector3D & blockPosition );
    	void determineLightingForDestroyedBlock( cbengine::Vector3D & blockPosition );
    
    	// Inline Mutators
    	float getNormalizedLightingValueForBlockFace( const cbengine::Vector3D & blockPosition, 
    		simpleminer::BlockFace blockFace  ) const;
    	void setActionDelegate( ActionDelegate * actionDelegate );
    
    protected:
    
    
    private:
    
    	WorldLightingManager() {
    
    		createRangeMappedLightingTable( simpleminer::LIGHTING_MIN_VALUE, simpleminer::LIGHTING_MAX_VALUE );
    
    		m_actionDelegate = nullptr;
    		m_visitedBlocks = nullptr;
    
    	} // end private ctor
    
    	// Private Functions
    	void createRangeMappedLightingTable( float minLightingValue, float maxLightingValue );
    
    	void determineDynamicLightingForAlteredBlockAtPosition( cbengine::Vector3D & alteredBlockPosition );
    	bool distributeBlockLightingIfNeededAtPosition( cbengine::Vector3D & blockPosition, VoxelBlock & voxelBlock );
    
    	void markNeighboringChunksDirty( const cbengine::Vector3D & blockPosition ); 
    
    	void doSecondPassForSingleChunkToCalculateLightForDiryBlocks( WorldChunk * worldChunk );
    	void doSecondPassForAllChunksToCalculateLightForDirtyBlocks( std::map< cbengine::IntVector2, WorldChunk *> & worldChunks );
    
    	// Private Variables
    	std::map< float, float >										m_lightingValueTable;
    	std::map< cbengine::Vector3D, LightingNode * > *			m_directedGraphNodes;
    	std::set< cbengine::Vector3D > *							m_visitedBlocks;
    	ActionDelegate *												m_actionDelegate;
    
    	PREVENT_COPY_AND_ASSIGN( WorldLightingManager );
    };
    
    
    // Inline Functions
    inline float WorldLightingManager::getNormalizedLightingValueForBlockFace(  
    	const cbengine::Vector3D & blockPosition, 
    	simpleminer::BlockFace blockFace ) const 
    {
    
    	assert( m_actionDelegate != nullptr );
    	float lightValue = 0.0f;
    	
    	cbengine::Vector3D blockPositionToCheck;
    	VoxelBlock * blockToCheck = nullptr;
    
    	if ( blockFace == simpleminer::kSOUTH_FACE ) {
    
    		blockPositionToCheck.x = blockPosition.x - cubeSideSize;
    		blockPositionToCheck.y = blockPosition.y;
    		blockPositionToCheck.z = blockPosition.z;
    
    		blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck );
    
    		if ( blockToCheck != nullptr ) {
    
    			if ( blockToCheck->m_blockLightingValue > simpleminer::LIGHTING_MIN_VALUE ) {
    
    				lightValue = blockToCheck->m_blockLightingValue - 1.0f;
    
    			}
    
    		}
    
    	} else if ( blockFace == simpleminer::kEAST_FACE ) {
    
    		blockPositionToCheck.x = blockPosition.x;
    		blockPositionToCheck.y = blockPosition.y - cubeSideSize;
    		blockPositionToCheck.z = blockPosition.z;
    
    		blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck );
    
    		if ( blockToCheck != nullptr ) {
    
    			if ( blockToCheck->m_blockLightingValue > simpleminer::LIGHTING_MIN_VALUE ) {
    
    				lightValue = blockToCheck->m_blockLightingValue - 1.0f;
    
    			}
    
    		}
    
    	} else if ( blockFace == simpleminer::kNORTH_FACE ) {
    
    		blockPositionToCheck.x = blockPosition.x + cubeSideSize;
    		blockPositionToCheck.y = blockPosition.y;
    		blockPositionToCheck.z = blockPosition.z;
    
    		blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck );
    
    		if ( blockToCheck != nullptr ) {
    
    			if ( blockToCheck->m_blockLightingValue > simpleminer::LIGHTING_MIN_VALUE ) {
    
    				lightValue = blockToCheck->m_blockLightingValue - 1.0f;
    
    			}
    
    		}
    
    
    	} else if ( blockFace == simpleminer::kWEST_FACE ) {
    
    		blockPositionToCheck.x = blockPosition.x;
    		blockPositionToCheck.y = blockPosition.y + cubeSideSize;
    		blockPositionToCheck.z = blockPosition.z;
    
    		blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck );
    
    		if ( blockToCheck != nullptr ) {
    
    			if ( blockToCheck->m_blockLightingValue > simpleminer::LIGHTING_MIN_VALUE ) {
    
    				lightValue = blockToCheck->m_blockLightingValue - 1.0f;
    
    			}
    
    		}
    
    	} else if ( blockFace == simpleminer::kBOTTOM_FACE ) {
    
    		blockPositionToCheck.x = blockPosition.x;
    		blockPositionToCheck.y = blockPosition.y;
    		blockPositionToCheck.z = blockPosition.z - cubeSideSize;
    
    		blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck );
    
    		if ( blockToCheck != nullptr ) {
    
    			if ( blockToCheck->m_blockLightingValue > simpleminer::LIGHTING_MIN_VALUE ) {
    
    				lightValue = blockToCheck->m_blockLightingValue - 1.0f;
    
    			}
    
    		}
    
    
    	} else if ( blockFace == simpleminer::kTOP_FACE ) {
    
    		blockPositionToCheck.x = blockPosition.x;
    		blockPositionToCheck.y = blockPosition.y;
    		blockPositionToCheck.z = blockPosition.z + cubeSideSize;
    
    		blockToCheck = m_actionDelegate->getVoxelBlockAtPosition( blockPositionToCheck );
    
    		if ( blockToCheck != nullptr ) {
    
    			if ( blockToCheck->m_blockLightingValue > simpleminer::LIGHTING_MIN_VALUE ) {
    
    				lightValue = blockToCheck->m_blockLightingValue - 1.0f;
    
    			}
    
    		}
    	}
    
    	float normalizedLightValue = 0.0f;
    	std::map::const_iterator it;
    	it = m_lightingValueTable.find( lightValue );
    
    	if ( it == m_lightingValueTable.end() ) {
    		// Default if there is an error
    		normalizedLightValue = simpleminer::LIGHTING_MIN_VALUE;
    		return normalizedLightValue;
    	}
    
    	normalizedLightValue = it->second;
    
    	return normalizedLightValue;
    
    } // end 
    
    
    inline void WorldLightingManager::setActionDelegate( ActionDelegate * actionDelegate ) {
    
    	assert( actionDelegate != nullptr );
    
    	m_actionDelegate = actionDelegate;
    
    } // end setActionDelegate
    
    
    inline bool WorldLightingManager::distributeBlockLightingIfNeededAtPosition( cbengine::Vector3D & blockPosition, VoxelBlock & voxelBlock ) {
    
    	const float lightDecrementValue = 1.0f;
    	float previousBlockLighting = voxelBlock.m_blockLightingValue;
    	float currentMaxLighting = 0.0f;
    	VoxelBlock * currentBlockToCheck = nullptr;
    	cbengine::Vector3D currentBlockPosition;
    
    
    	// Check South
    	currentBlockPosition.x = blockPosition.x - cubeSideSize;
    	currentBlockPosition.y = blockPosition.y;
    	currentBlockPosition.z = blockPosition.z;
    	currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition );
    
    	if ( currentBlockToCheck != nullptr ) {
    		currentMaxLighting = std::max( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) );
    	} 
    
    	// Check East
    	currentBlockPosition.x = blockPosition.x;
    	currentBlockPosition.y = blockPosition.y - cubeSideSize;
    	currentBlockPosition.z = blockPosition.z;
    	currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition );
    
    	if ( currentBlockToCheck != nullptr ) {
    		currentMaxLighting = std::max( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) );
    	} 
    
    	// Check North
    	currentBlockPosition.x = blockPosition.x + cubeSideSize;
    	currentBlockPosition.y = blockPosition.y;
    	currentBlockPosition.z = blockPosition.z;
    	currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition );
    
    	if ( currentBlockToCheck != nullptr ) {
    		currentMaxLighting = std::max( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) );
    	} 
    
    	// Check West
    	currentBlockPosition.x = blockPosition.x;
    	currentBlockPosition.y = blockPosition.y + cubeSideSize;
    	currentBlockPosition.z = blockPosition.z;
    	currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition );
    
    	if ( currentBlockToCheck != nullptr ) {
    		currentMaxLighting = std::max( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) );
    	} 
    
    	// Check Bottom
    	currentBlockPosition.x = blockPosition.x;
    	currentBlockPosition.y = blockPosition.y;
    	currentBlockPosition.z = blockPosition.z - cubeSideSize;
    	currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition );
    
    	if ( currentBlockToCheck != nullptr ) {
    		currentMaxLighting = std::max( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) );
    	} 
    
    	// Check Top
    	currentBlockPosition.x = blockPosition.x;
    	currentBlockPosition.y = blockPosition.y;
    	currentBlockPosition.z = blockPosition.z + cubeSideSize;
    	currentBlockToCheck = m_actionDelegate->getVoxelBlockAtPosition( currentBlockPosition );
    
    	if ( currentBlockToCheck != nullptr ) {
    
    		// Paul :: Trying this out 
    		// This seems like a unique case
    		if ( currentBlockToCheck->m_blockLightingValue == simpleminer::LIGHTING_MAX_VALUE ) {
    			currentMaxLighting = simpleminer::LIGHTING_MAX_VALUE;
    		} else {
    			currentMaxLighting = std::max( currentMaxLighting, ( currentBlockToCheck->m_blockLightingValue - lightDecrementValue ) );
    		}
    
    	} 
    
    	if ( voxelBlock.m_blockType == simpleminer::kBLOCK_TYPE_GLOWSTONE && currentMaxLighting < simpleminer::LIGHTING_GLOW_STONE_DEFAULT ) {
    
    		currentMaxLighting = simpleminer::LIGHTING_GLOW_STONE_DEFAULT;
    
    	}
    
    	voxelBlock.m_blockLightingValue = currentMaxLighting;
    
    	if ( currentMaxLighting != previousBlockLighting ) {
    		return true;
    	} else {
    		return false;
    	}
    
    }
    
    #endif
    							
  • 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 
    
    #include "..\EngineCode\EngineMacros.hpp"
    
    #include "..\EngineCode\IntVector2.hpp"
    
    #include "ActionDelegate.hpp"
    
    const std::string CHUNK_FOLDER_NAME			= "SMChunks\\";
    const std::string X_POSITION_NAME			= "X";
    const std::string Y_POSITION_NAME			= "Y";
    const std::string CHUNK_FILE_EXT			= ".smchunk";
    const unsigned int MAX_CHUNK_BUFFER_SIZE	= 32768;
    
    // Ugh this is crap... If time permits, find something better
    // For now... Have to increase this each time a new block comes into existence
    const std::string RLE_DIRT_MARKER					= "D";
    const std::string RLE_STONE_MARKER					= "S";
    const std::string RLE_AIR_MARKER					= "A";
    const std::string RLE_WATER_MARKER					= "W";
    const std::string RLE_GLOWSTONE_MARKER				= "G";
    const std::string RLE_TREE_TRUNK_MARKER				= "T";
    const std::string RLE_TREE_LEAF_MARKER				= "L";
    
    
    class WorldChunk;
    
    class ChunkResourceManager {
    public:
    	static ChunkResourceManager * sharedChunkResourceManager() {
    
    		static ChunkResourceManager chunkResourceManager;
    		return &chunkResourceManager;
    	}
    
    	// Scoped Struct
    	struct ChunkBuffer {
    
    		unsigned char		blockType;
    		short				blockTypeCount;
    	};
    
    	// Returns nullptr if the chunk does not exist as a file
    	WorldChunk * loadWorldChunkWithCoords( const cbengine::IntVector2 & chunkCoords, ActionDelegate * actionDelegate );
    	void cacheChunkToFileWithCoords( const WorldChunk & chunkToCache );
    
    	~ChunkResourceManager() {}
    
    protected:
    
    private:
    	ChunkResourceManager() {
    
    		unsigned char RLE_DIRT_MARKER_AS_CHAR		= *(RLE_DIRT_MARKER.c_str());
    		unsigned char RLE_STONE_MARKER_AS_CHAR		= *(RLE_STONE_MARKER.c_str());
    		unsigned char RLE_AIR_MARKER_AS_CHAR		= *(RLE_AIR_MARKER.c_str());
    		unsigned char RLE_WATER_MARKER_AS_CHAR		= *(RLE_WATER_MARKER.c_str());
    		unsigned char RLE_GLOWSTONE_MARKER_AS_CHAR	= *(RLE_GLOWSTONE_MARKER.c_str());
    		unsigned char RLE_TREE_TRUNK_MARKER_AS_CHAR	= *(RLE_TREE_TRUNK_MARKER.c_str());
    		unsigned char RLE_TREE_LEAF_MARKER_AS_CHAR	= *(RLE_TREE_LEAF_MARKER.c_str());
    
    		m_rleEncodingToBlockType.insert( std::pair< unsigned char, unsigned char>( RLE_DIRT_MARKER_AS_CHAR, simpleminer::kBLOCK_TYPE_DIRT ) );
    		m_rleEncodingToBlockType.insert( std::pair< unsigned char, unsigned char>( RLE_STONE_MARKER_AS_CHAR, simpleminer::kBLOCK_TYPE_STONE ) );
    		m_rleEncodingToBlockType.insert( std::pair< unsigned char, unsigned char>( RLE_AIR_MARKER_AS_CHAR, simpleminer::kBLOCK_TYPE_AIR ) );
    		m_rleEncodingToBlockType.insert( std::pair< unsigned char, unsigned char>( RLE_WATER_MARKER_AS_CHAR, simpleminer::kBLOCK_TYPE_WATER ) );
    		m_rleEncodingToBlockType.insert( std::pair< unsigned char, unsigned char>( RLE_GLOWSTONE_MARKER_AS_CHAR, simpleminer::kBLOCK_TYPE_GLOWSTONE ) );
    		m_rleEncodingToBlockType.insert( std::pair< unsigned char, unsigned char>( RLE_TREE_TRUNK_MARKER_AS_CHAR, simpleminer::kBLOCK_TYPE_TREE_TRUNK ) );
    		m_rleEncodingToBlockType.insert( std::pair< unsigned char, unsigned char>( RLE_TREE_LEAF_MARKER_AS_CHAR, simpleminer::kBLOCK_TYPE_TREE_LEAF ) );
    	}
    
    	void formRLEEncodingFromChunk( const WorldChunk & chunkToCache, std::string & rleEncoding );
    	void formChunkFromRLEEncodedFile( ActionDelegate * actionDelegate, 
    		const cbengine::IntVector2 & chunkCoords, 
    		char * chunkBuffer,
    		size_t sizeOfBuffer,
    		WorldChunk & chunkToForm );
    
    	unsigned char getBlockTypeFromRLEByte( const char & byteFromChunkFile ) const;
    
    	std::map< unsigned char, unsigned char >			m_rleEncodingToBlockType;
    };
    
    #endif
    							
  • 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