程序化生成
程序化生成
void Chunk::GenerateBlocks()
{
//Hillness constant
constexpr int waterLevel = Z_CHUNK_SIZE / 2;
constexpr int waterDepth = 5;
constexpr int waterBed = waterLevel - waterDepth;
constexpr int maxTerrainElevation = Z_CHUNK_SIZE - waterBed;
constexpr int HALF_Z_SIZE = Z_CHUNK_SIZE / 2;
constexpr int maxOceanDownOffset = 10;
constexpr float sandThreshold = 0.4f;
constexpr float iceThreshold = 0.4f;
constexpr float treeDensityThreshold = 0.68f;
constexpr int treeParameter = 5;
constexpr int treeOutterRadius = treeParameter / 2 + 1;
constexpr int treeInnnerRadius = treeParameter / 2;
std::map blockTreeRawNoises;
std::map blockHeights;
std::map blockHumidity;
for (int localX = -treeOutterRadius; localX < X_CHUNK_SIZE + treeOutterRadius; localX++)
{
for (int localY= -treeOutterRadius; localY < Y_CHUNK_SIZE + treeOutterRadius; localY++)
{
float globalX = float( localX + m_chunkCoordinate.x * X_CHUNK_SIZE );
float globalY = float( localY + m_chunkCoordinate.y * Y_CHUNK_SIZE );
//Hillness
float hillness = rng->Compute2dPerlinNoise( globalX, globalY, 200.f, 7, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
hillness = hillness * 0.5f + 0.5f;
hillness = SmoothStep3( hillness );
//Temperature
float temperature = rng->Compute2dPerlinNoise( globalX, globalY, 200.f, 7, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
temperature = temperature * 0.5f + 0.5f;
//Oceanness
float oceanness = rng->Compute2dPerlinNoise( globalX, globalY, 500.f, 7, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
oceanness = oceanness * 0.5f + 0.5f;
oceanness = SmoothStep3( oceanness );
float oceanDownOffset = oceanness* maxOceanDownOffset;
//Humidity
float humidity = rng->Compute2dPerlinNoise( globalX, globalY, 150.f, 7, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
humidity = humidity * 0.5f + 0.5f;
blockHumidity[IntVec2( localX, localY )] = humidity;
//Tree Density
float treeDensityValue = rng->Compute2dPerlinNoise( globalX, globalY, 100.f, 5, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
treeDensityValue = treeDensityValue * 0.5f + 0.5f;
treeDensityValue = SmoothStep3( treeDensityValue );
//Terrain height above sea bed
float mountainPerlinValue = fabsf( rng->Compute2dPerlinNoise( globalX, globalY, 500.f, 5, 0.5f, 2.0f, true, m_world->m_worldSeedNumber ) );
mountainPerlinValue = SmoothStep3( mountainPerlinValue );
//mountainPerlinValue = SmoothStop3( mountainPerlinValue );
float maxRegionalTerrainElevation = maxTerrainElevation * mountainPerlinValue;
//Create ocean
maxRegionalTerrainElevation = maxRegionalTerrainElevation - oceanDownOffset;
float maxRegionalMountainElevation = maxRegionalTerrainElevation - waterDepth;
float maxRegionalTerrainHeight = maxRegionalTerrainElevation + waterBed;
if (maxRegionalTerrainHeight > waterLevel)
{
maxRegionalMountainElevation *= hillness;
}
int terrainHeightZ = int( maxRegionalMountainElevation ) + waterLevel;
blockHeights[IntVec2( localX, localY )] = terrainHeightZ;
if (localX >= 0 && localX < X_CHUNK_SIZE && localY >= 0 && localY < Y_CHUNK_SIZE)
{
for (int localZ = 0; localZ < Z_CHUNK_SIZE; localZ++)
{
int currenBlockIndex = GetBlockIndexByCoordinate( IntVec3( localX, localY, localZ ) );
if (localZ > terrainHeightZ)
{
if (localZ <= HALF_Z_SIZE)
{
if (temperature < 0.4f)
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Ice;
}
else
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Water;
}
}
else
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Air;
}
}
else if (localZ == terrainHeightZ)
{
if (humidity > sandThreshold && temperature > iceThreshold)
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Grass;
}
else if (humidity > sandThreshold && temperature < iceThreshold)
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::SnowyGrass;
}
else
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Sand;
}
}
else if (localZ < terrainHeightZ)
{
int randomDirtDepth = rng->RollRandomIntInRange( 3, 4 );
if (terrainHeightZ - localZ <= randomDirtDepth)
{
if (humidity > 0.4f)
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Dirt;
}
else
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Sand;
}
}
else
{
if (rng->RollRandomChance( 0.05f ))
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Coal;
}
else if (rng->RollRandomChance( 0.02f ))
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Iron;
}
else if (rng->RollRandomChance( 0.005f ))
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Gold;
}
else if (rng->RollRandomChance( 0.001f ))
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Diamond;
}
else
{
m_blocks[currenBlockIndex].m_blockTypeIndex = (unsigned char)BlockType::Stone;
}
}
}
m_blocks[currenBlockIndex].StartUp();
}
}
}
}
//Tree
for (int localY = -treeParameter + 1; localY < Y_CHUNK_SIZE + treeParameter - 1; localY++)
{
for (int localX = -treeParameter + 1; localX < X_CHUNK_SIZE + treeParameter - 1; localX++)
{
//Tree noise value generation
int globalX = (localX + m_chunkCoordinate.x * X_CHUNK_SIZE);
int globalY = (localY + m_chunkCoordinate.y * Y_CHUNK_SIZE);
blockTreeRawNoises[IntVec2( localX, localY )] = rng->Get2dNoiseZeroToOne( globalX, globalY, m_world->m_worldSeedNumber );
}
}
//For loop for each block check if it's a tree, if so spawn a tree template
for (int localY = -treeInnnerRadius; localY < Y_CHUNK_SIZE + treeInnnerRadius; localY++)
{
for (int localX = -treeInnnerRadius; localX < X_CHUNK_SIZE + treeInnnerRadius; localX++)
{
IntVec2 localCoordinate( localX, localY );
bool isHighestTreeValue = true;
for (int treeSearchX = -treeInnnerRadius; treeSearchX <= treeInnnerRadius; treeSearchX++)
{
for (int treeSearchY = -treeInnnerRadius; treeSearchY <= treeInnnerRadius; treeSearchY++)
{
if (treeSearchX == 0 && treeSearchY == 0)
{
continue;
}
IntVec2 currentCoordinate( localX + treeSearchX, localY + treeSearchY );
//If any raw num larger, it is not tree
if (blockTreeRawNoises[currentCoordinate] >= blockTreeRawNoises[localCoordinate])
{
isHighestTreeValue = false;
}
}
}
if (isHighestTreeValue)
{
IntVec3 currentTreeBaseCoordinate( localX, localY, blockHeights[localCoordinate] + 1 );
float globalX = float( localX + m_chunkCoordinate.x * X_CHUNK_SIZE );
float globalY = float( localY + m_chunkCoordinate.y * Y_CHUNK_SIZE );
//Tree Density
float treeDensityValue = rng->Compute2dPerlinNoise( globalX, globalY, 100.f, 5, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
treeDensityValue = treeDensityValue * 0.5f + 0.5f;
float newThreshold = RangeMapClamped( treeDensityValue, treeDensityThreshold, 1.f, 1.f, 0.85f );
//treeDensityValue = SmoothStep3( treeDensityValue );
if (currentTreeBaseCoordinate.z > waterLevel && treeDensityValue > treeDensityThreshold)//Equals to if this block is air
{
float currentTreeNoise = rng->Get2dNoiseZeroToOne( (int)globalX, (int)globalY, m_world->m_worldSeedNumber );
if (currentTreeNoise > newThreshold)
{
//Temperature
float temperature = rng->Compute2dPerlinNoise( globalX, globalY, 200.f, 7, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
temperature = temperature * 0.5f + 0.5f;
//Humidity
float humidity = rng->Compute2dPerlinNoise( globalX, globalY, 150.f, 7, 0.5f, 2.0f, true, m_world->m_worldSeedNumber );
humidity = humidity * 0.5f + 0.5f;
blockHumidity;
BlockTemplate tree;
if (humidity < sandThreshold)
{
tree = BlockTemplate::s_blockTemplates[(int)BlockTemplateType::CactusTree];
}
else if (temperature < iceThreshold)
{
tree = BlockTemplate::s_blockTemplates[(int)BlockTemplateType::PruceTree];
}
else
{
tree = BlockTemplate::s_blockTemplates[(int)BlockTemplateType::OakTree];
}
for (int i = 0; i < tree.m_blockTemplateEntries.size(); i++)
{
IntVec3 currentTreeBlockCoordinate = currentTreeBaseCoordinate + tree.m_blockTemplateEntries[i].m_relativeOffset;
if (IsBlockOutOfBound( currentTreeBlockCoordinate ))//Only change the block type in this chunk
{
continue;
}
int currentTreeBlockIndex = GetBlockIndexByCoordinate( currentTreeBlockCoordinate );
if (m_blocks[currentTreeBlockIndex].m_blockTypeIndex == (unsigned char)BlockType::Air)
{
m_blocks[currentTreeBlockIndex].m_blockTypeIndex = tree.m_blockTemplateEntries[i].m_blockTypeIndex;
m_blocks[currentTreeBlockIndex].StartUp();
}
}
}
}
}
}
}
}
每个Chunk程序生成代码
Hidden Surface Removal
通过使用背面剔除来剔除 99% 的面以优化性能,即使有1 亿个块,也能保持大约600 FPS。对于每个块,只有当相邻块为空气时,我才将顶点信息放入顶点缓冲区。
Hidden Surface Removal
void Chunk::AddVertsForBlock( std::vector& cpuMesh, std::vector& cpuMeshIndex, int blockIndex )
{
BlockDef const& currentBlockDef = BlockDef::GetBlockDefByType( (BlockType)m_blocks[blockIndex].m_blockTypeIndex );
IntVec3 blockCoordinates = GetBlockCoordinateByIndex( blockIndex );
Vec3 blockWorldPos( (float)(blockCoordinates.x + m_chunkCoordinate.x * X_CHUNK_SIZE), (float)(blockCoordinates.y + m_chunkCoordinate.y * Y_CHUNK_SIZE), (float)blockCoordinates.z );
AABB3 blockAABB3( Vec3( blockWorldPos.x, blockWorldPos.y, blockWorldPos.z ), Vec3( blockWorldPos.x + 1.f, blockWorldPos.y + 1.f, blockWorldPos.z + 1.f ) );
BlockIterator currentBlockIterator( this, blockIndex );
Block* currentBlock = currentBlockIterator.GetBlock();
Block* eastBlock = currentBlockIterator.GetEastNeighbor().GetBlock();
Block* westBlock = currentBlockIterator.GetWestNeighbor().GetBlock();
Block* northBlock = currentBlockIterator.GetNorthNeighbor().GetBlock();
Block* southBlock = currentBlockIterator.GetSouthNeighbor().GetBlock();
Block* upBlock = currentBlockIterator.GetUpNeighbor().GetBlock();
Block* downBlock = currentBlockIterator.GetDownNeighbor().GetBlock();
if (currentBlock->IsBlockVisible())
{
Rgba8 currentFaceColor;
currentFaceColor.b = 127;
if (eastBlock != nullptr && !eastBlock->IsBlockOpaque())
{
currentFaceColor.g = eastBlock->GetIndoorLight() * 17;
currentFaceColor.r = eastBlock->GetOutdoorLight() * 17;
AddVertsForForwardFace( cpuMesh, cpuMeshIndex, blockAABB3, currentFaceColor, currentBlockDef.m_sideSpriteUV );
}
if (westBlock != nullptr && !westBlock->IsBlockOpaque())
{
currentFaceColor.g = westBlock->GetIndoorLight() * 17;
currentFaceColor.r = westBlock->GetOutdoorLight() * 17;
AddVertsForBackwardFace( cpuMesh, cpuMeshIndex, blockAABB3, currentFaceColor, currentBlockDef.m_sideSpriteUV );
}
if (northBlock != nullptr && !northBlock->IsBlockOpaque())
{
currentFaceColor.g = northBlock->GetIndoorLight() * 17;
currentFaceColor.r = northBlock->GetOutdoorLight() * 17;
AddVertsForLeftFace( cpuMesh, cpuMeshIndex, blockAABB3, currentFaceColor, currentBlockDef.m_sideSpriteUV );
}
if (southBlock != nullptr && !southBlock->IsBlockOpaque())
{
currentFaceColor.g = southBlock->GetIndoorLight() * 17;
currentFaceColor.r = southBlock->GetOutdoorLight() * 17;
AddVertsForRightFace( cpuMesh, cpuMeshIndex, blockAABB3, currentFaceColor, currentBlockDef.m_sideSpriteUV );
}
if (upBlock != nullptr && !upBlock->IsBlockOpaque())
{
currentFaceColor.g = upBlock->GetIndoorLight() * 17;
currentFaceColor.r = upBlock->GetOutdoorLight() * 17;
AddVertsForTopFace( cpuMesh, cpuMeshIndex, blockAABB3, currentFaceColor, currentBlockDef.m_topSpriteUV );
}
if (downBlock != nullptr && !downBlock->IsBlockOpaque())
{
currentFaceColor.g = downBlock->GetIndoorLight() * 17;
currentFaceColor.r = downBlock->GetOutdoorLight() * 17;
AddVertsForBottomFace( cpuMesh, cpuMeshIndex, blockAABB3, currentFaceColor, currentBlockDef.m_bottomSpriteUV );
}
}
}
每个方块Hidden Surface Removal的代码
基于体素的光照
每当玩家放置一个方块时,该方块都会使用方块迭代器检查所有附近的方块(16x16),并进行光强度衰减(初始强度为 16,迭代每个方块时为 -1)重建Chunk,并根据光强度在着色器中设置亮度。
将 GlowStone 放到世界中
体素光照传播
void World::ProcessNextDirtyLightBlock()
{
BlockIterator currentBlockIterator = m_dirtyBlocks.front();
if (currentBlockIterator.m_currentChunk == nullptr)
{
return;
}
BlockIterator eastBlockIterator = currentBlockIterator.GetEastNeighbor();
BlockIterator westBlockIterator = currentBlockIterator.GetWestNeighbor();
BlockIterator northBlockIterator = currentBlockIterator.GetNorthNeighbor();
BlockIterator southBlockIterator = currentBlockIterator.GetSouthNeighbor();
BlockIterator upBlockIterator = currentBlockIterator.GetUpNeighbor();
BlockIterator downBlockIterator = currentBlockIterator.GetDownNeighbor();
Block& currentBlock = currentBlockIterator.m_currentChunk->m_blocks[currentBlockIterator.m_blockIndex];
unsigned char theoreticallyCorrectIndoorValue = 0;
unsigned char theoreticallyCorrectOutdoorValue = 0;
if (eastBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectIndoorValue < eastBlockIterator.GetBlock()->GetIndoorLight() - 1)
{
theoreticallyCorrectIndoorValue = eastBlockIterator.GetBlock()->GetIndoorLight() - 1;
}
}
if (westBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectIndoorValue < westBlockIterator.GetBlock()->GetIndoorLight() - 1)
{
theoreticallyCorrectIndoorValue = westBlockIterator.GetBlock()->GetIndoorLight() - 1;
}
}
if (northBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectIndoorValue < northBlockIterator.GetBlock()->GetIndoorLight() - 1)
{
theoreticallyCorrectIndoorValue = northBlockIterator.GetBlock()->GetIndoorLight() - 1;
}
}
if (southBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectIndoorValue < southBlockIterator.GetBlock()->GetIndoorLight() - 1)
{
theoreticallyCorrectIndoorValue = southBlockIterator.GetBlock()->GetIndoorLight() - 1;
}
}
if (upBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectIndoorValue < upBlockIterator.GetBlock()->GetIndoorLight() - 1)
{
theoreticallyCorrectIndoorValue = upBlockIterator.GetBlock()->GetIndoorLight() - 1;
}
}
if (downBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectIndoorValue < downBlockIterator.GetBlock()->GetIndoorLight() - 1)
{
theoreticallyCorrectIndoorValue = downBlockIterator.GetBlock()->GetIndoorLight() - 1;
}
}
if (currentBlock.GetIsSky())
{
theoreticallyCorrectOutdoorValue = 15;
}
else
{
if (eastBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectOutdoorValue < eastBlockIterator.GetBlock()->GetOutdoorLight() - 1)
{
theoreticallyCorrectOutdoorValue = eastBlockIterator.GetBlock()->GetOutdoorLight() - 1;
}
}
if (westBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectOutdoorValue < westBlockIterator.GetBlock()->GetOutdoorLight() - 1)
{
theoreticallyCorrectOutdoorValue = westBlockIterator.GetBlock()->GetOutdoorLight() - 1;
}
}
if (northBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectOutdoorValue < northBlockIterator.GetBlock()->GetOutdoorLight() - 1)
{
theoreticallyCorrectOutdoorValue = northBlockIterator.GetBlock()->GetOutdoorLight() - 1;
}
}
if (southBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectOutdoorValue < southBlockIterator.GetBlock()->GetOutdoorLight() - 1)
{
theoreticallyCorrectOutdoorValue = southBlockIterator.GetBlock()->GetOutdoorLight() - 1;
}
}
if (upBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectOutdoorValue < upBlockIterator.GetBlock()->GetOutdoorLight() - 1)
{
theoreticallyCorrectOutdoorValue = upBlockIterator.GetBlock()->GetOutdoorLight() - 1;
}
}
if (downBlockIterator.m_currentChunk != nullptr)
{
if (theoreticallyCorrectOutdoorValue < downBlockIterator.GetBlock()->GetOutdoorLight() - 1)
{
theoreticallyCorrectOutdoorValue = downBlockIterator.GetBlock()->GetOutdoorLight() - 1;
}
}
}
if (currentBlock.IsBlockOpaque())
{
if (currentBlock.m_blockTypeIndex == (unsigned char)BlockType::GlowStone)
{
if (currentBlock.GetIndoorLight() < 15)
{
theoreticallyCorrectIndoorValue = 15;
}
}
else
{
theoreticallyCorrectIndoorValue = 0;
theoreticallyCorrectOutdoorValue = 0;
}
}
if (currentBlockIterator.GetBlock()->GetIndoorLight() != theoreticallyCorrectIndoorValue || currentBlockIterator.GetBlock()->GetOutdoorLight() != theoreticallyCorrectOutdoorValue)
{
currentBlockIterator.GetBlock()->SetIndoorLight( theoreticallyCorrectIndoorValue );
currentBlockIterator.GetBlock()->SetOutdoorLight( theoreticallyCorrectOutdoorValue );
if (eastBlockIterator.m_currentChunk != nullptr)
{
if (!eastBlockIterator.GetBlock()->IsBlockOpaque())
{
MarkLightingDirty( eastBlockIterator );
}
}
if (westBlockIterator.m_currentChunk != nullptr)
{
if (!westBlockIterator.GetBlock()->IsBlockOpaque())
{
MarkLightingDirty( westBlockIterator );
}
}
if (northBlockIterator.m_currentChunk != nullptr)
{
if (!northBlockIterator.GetBlock()->IsBlockOpaque())
{
MarkLightingDirty( northBlockIterator );
}
}
if (southBlockIterator.m_currentChunk != nullptr)
{
if (!southBlockIterator.GetBlock()->IsBlockOpaque())
{
MarkLightingDirty( southBlockIterator );
}
}
if (upBlockIterator.m_currentChunk != nullptr)
{
if (!upBlockIterator.GetBlock()->IsBlockOpaque())
{
MarkLightingDirty( upBlockIterator );
}
}
if (downBlockIterator.m_currentChunk != nullptr)
{
if (!downBlockIterator.GetBlock()->IsBlockOpaque())
{
MarkLightingDirty( downBlockIterator );
}
}
}
//Pop out after all calculation
currentBlock.SetIsLightDirty( false );
m_dirtyBlocks.pop_front();
}
void Chunk::RebuildMesh()
{
if (m_eastNeighbor && m_westNeighbor && m_northNeighbor && m_southNeighbor)
{
if (m_isMeshDirty)
{
DeleteAllPointers();
BindNeighbors();
//Initialization
std::vector cpuMesh;
std::vector cpuMeshIndex;
cpuMesh.reserve( BLOCK_SIZE * 24 );
cpuMeshIndex.reserve( BLOCK_SIZE * 36 );
for (int i = 0; i < BLOCK_SIZE; i++)
{
AddVertsForBlock( cpuMesh, cpuMeshIndex, i );
}
m_vertexBuffer = g_theRenderer->CreateVertexBuffer( 1 );
m_indexBuffer = g_theRenderer->CreateIndexBuffer( 1 );
g_theRenderer->CopyCPUToGPU( cpuMesh.data(), (int)cpuMesh.size() * sizeof( Vertex_PCU ), m_vertexBuffer );
g_theRenderer->CopyCPUToGPU( cpuMeshIndex.data(), (int)cpuMeshIndex.size() * sizeof( unsigned int ), m_indexBuffer );
m_indexNum = (int)cpuMeshIndex.size();
m_vertsNum = (int)cpuMesh.size();
m_isMeshDirty = false;
}
}
}
放置/移除方块时把Chunk标记为Dirty并处理灯光
放置/挖掘方块
我使用了快速射线投射与方块相交,并且在此基础上,为了提高性能,我还使用方块迭代器来加速相交过程。
将方块放置到世界里
struct BlockIterator
{
public:
BlockIterator();
~BlockIterator();
BlockIterator( Chunk* currentChunk, int blockIndex );
Block* GetBlock();
IntVec3 GetBlockCoordinate();
Vec3 GetWorldCenter();
BlockIterator GetEastNeighbor() const;
BlockIterator GetWestNeighbor() const;
BlockIterator GetNorthNeighbor() const;
BlockIterator GetSouthNeighbor() const;
BlockIterator GetUpNeighbor() const;
BlockIterator GetDownNeighbor() const;
public:
Chunk* m_currentChunk = nullptr;
int m_blockIndex = 0;
};
方块迭代器类的代码
bool World::RaycastBlock3D( GameRaycastResult3D& result )
{
IntVec3 startTileCoord = GetPlayerCurrentWorldCoordinate();
Chunk* currentChunk = GetPlayerCurrentChunk();
if (currentChunk == nullptr)
{
return false;
}
int startBlockIndex = currentChunk->GetBlockIndexByCoordinate( GetCurrentBlockCoordinateOnCurrentChunk3D( startTileCoord ) );
if (currentChunk->m_blocks[startBlockIndex].IsBlockSolid())
{
if (startTileCoord.x > 0 && startTileCoord.y > 0)
{
result.m_didImpact = true;
result.m_impactDistance = 0.f;
result.m_impactPosition = result.m_rayStartPosition;
return true;
}
}
//X
float fwdDistPerXCrossing = 1.f / abs( result.m_rayDirection.x );
int tileStepDirectionX = (result.m_rayDirection.x < 0) ? -1 : 1;
float xAtFirstXCrossing = (float)(startTileCoord.x + (tileStepDirectionX + 1) / 2);
float xDistToFirstCrossing = xAtFirstXCrossing - result.m_rayStartPosition.x;
float fwdDistAtNextXCrossing = fabsf( xDistToFirstCrossing ) * fwdDistPerXCrossing;
//Y
float fwdDistPerYCrossing = 1.f / abs( result.m_rayDirection.y );
int tileStepDirectionY = (result.m_rayDirection.y < 0) ? -1 : 1;
float yAtFirstYCrossing = (float)(startTileCoord.y + (tileStepDirectionY + 1) / 2);
float yDistToFirstCrossing = yAtFirstYCrossing - result.m_rayStartPosition.y;
float fwdDistAtNextYCrossing = fabsf( yDistToFirstCrossing ) * fwdDistPerYCrossing;
//Z
float fwdDistPerZCrossing = 1.f / abs( result.m_rayDirection.z );
int tileStepDirectionZ = (result.m_rayDirection.z < 0) ? -1 : 1;
float zAtFirstZCrossing = (float)(startTileCoord.z + (tileStepDirectionZ + 1) / 2);
float zDistToFirstCrossing = zAtFirstZCrossing - result.m_rayStartPosition.z;
float fwdDistAtNextZCrossing = fabsf( zDistToFirstCrossing ) * fwdDistPerZCrossing;
BlockIterator currentBlockIterator( currentChunk, startBlockIndex );
//result
while(true)
{
if (fwdDistAtNextXCrossing < fwdDistAtNextYCrossing && fwdDistAtNextXCrossing < fwdDistAtNextZCrossing)
{
if (fwdDistAtNextXCrossing > result.m_rayLength)
{
result.m_didImpact = false;
return false;
}
//Go to next block
if (tileStepDirectionX > 0)
{
currentBlockIterator = currentBlockIterator.GetEastNeighbor();
}
else
{
currentBlockIterator = currentBlockIterator.GetWestNeighbor();
}
if (currentBlockIterator.m_currentChunk == nullptr)
{
return false;
}
if (currentBlockIterator.m_currentChunk->m_blocks[currentBlockIterator.m_blockIndex].IsBlockOpaque())
{
result.m_didImpact = true;
result.m_impactDistance = fwdDistAtNextXCrossing;
result.m_impactPosition = result.m_rayStartPosition + result.m_rayDirection * fwdDistAtNextXCrossing;
result.m_blockIterator = currentBlockIterator;
if (tileStepDirectionX > 0)
{
result.m_blockFace = 3;
}
else
{
result.m_blockFace = 0;
}
return true;
}
fwdDistAtNextXCrossing += fwdDistPerXCrossing;
}
else if (fwdDistAtNextYCrossing < fwdDistAtNextXCrossing && fwdDistAtNextYCrossing < fwdDistAtNextZCrossing)
{
if (fwdDistAtNextYCrossing > result.m_rayLength)
{
result.m_didImpact = false;
return false;
}
//Go to next block
if (tileStepDirectionY > 0)
{
currentBlockIterator = currentBlockIterator.GetNorthNeighbor();
}
else
{
currentBlockIterator = currentBlockIterator.GetSouthNeighbor();
}
if (currentBlockIterator.m_currentChunk == nullptr)
{
return false;
}
if (currentBlockIterator.m_currentChunk->m_blocks[currentBlockIterator.m_blockIndex].IsBlockOpaque())
{
result.m_didImpact = true;
result.m_impactDistance = fwdDistAtNextYCrossing;
result.m_impactPosition = result.m_rayStartPosition + result.m_rayDirection * fwdDistAtNextYCrossing;
result.m_blockIterator = currentBlockIterator;
//result.m_targetBlock = ¤tBlockIterator.m_currentChunk->m_blocks[currentBlockIterator.m_blockIndex];
if (tileStepDirectionY > 0)
{
result.m_blockFace = 4;
}
else
{
result.m_blockFace = 1;
}
return true;
}
fwdDistAtNextYCrossing += fwdDistPerYCrossing;
}
else if (fwdDistAtNextZCrossing < fwdDistAtNextXCrossing && fwdDistAtNextZCrossing < fwdDistAtNextYCrossing)
{
if (fwdDistAtNextZCrossing > result.m_rayLength)
{
result.m_didImpact = false;
return false;
}
//Go to next block
if (tileStepDirectionZ > 0)
{
currentBlockIterator = currentBlockIterator.GetUpNeighbor();
}
else
{
currentBlockIterator = currentBlockIterator.GetDownNeighbor();
}
if (currentBlockIterator.m_currentChunk == nullptr)
{
return false;
}
if (currentBlockIterator.m_currentChunk->m_blocks[currentBlockIterator.m_blockIndex].IsBlockOpaque())
{
result.m_didImpact = true;
result.m_impactDistance = fwdDistAtNextZCrossing;
result.m_impactPosition = result.m_rayStartPosition + result.m_rayDirection * fwdDistAtNextZCrossing;
result.m_blockIterator = currentBlockIterator;
if (tileStepDirectionZ > 0)
{
result.m_blockFace = 5;
}
else
{
result.m_blockFace = 2;
}
return true;
}
fwdDistAtNextZCrossing += fwdDistPerZCrossing;
}
}
}
使用方块迭代器进行射线投射
地图流式加载
我使用多线程根据玩家的世界位置每帧仅加载一个Chunk(32768个方块) ,并取消最大半径之外的Chunk。
通过距离雾来掩盖新Chunk的加载。
地图流式加载和雾
void World::UpdateChunckAmortization()
{
bool didActivate = false;
if (m_activeChunck.size() < m_maxChunks)
{
didActivate = ActivateNearestMissingChunckInRange();
}
if (!didActivate)
{
DeactivateFarestChunckOutofRange();
}
//if the job is retrieved, do light initilization and other stuff
Job* completedChunkjob = g_theJobSystem->RetriveFirstJob();
ChunkGenerateJob* generateJob = (ChunkGenerateJob*)completedChunkjob;
if (generateJob)
{
generateJob->m_currentChunk->m_chunkState = ChunkState::ACTIVATING_GENERATE_COMPLETE;
m_chunksBeingGenerated.erase( generateJob->m_currentChunk->m_chunkCoordinate );
generateJob->m_currentChunk->ActivateThisChunk();
}
}
地图流式加载代码
截图


