mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Merge 4bb8f27723 into c98153bf07
This commit is contained in:
commit
e88fa2acdb
|
|
@ -22,24 +22,21 @@
|
||||||
int Chunk::updates = 0;
|
int Chunk::updates = 0;
|
||||||
|
|
||||||
#ifdef _LARGE_WORLDS
|
#ifdef _LARGE_WORLDS
|
||||||
DWORD Chunk::tlsIdx = TlsAlloc();
|
static thread_local unsigned char s_tlsTileIds[16 * 16 * Level::maxBuildHeight];
|
||||||
|
|
||||||
void Chunk::CreateNewThreadStorage()
|
void Chunk::CreateNewThreadStorage()
|
||||||
{
|
{
|
||||||
unsigned char *tileIds = new unsigned char[16 * 16 * Level::maxBuildHeight];
|
// No-op: thread_local handles per-thread allocation automatically
|
||||||
TlsSetValue(tlsIdx, tileIds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunk::ReleaseThreadStorage()
|
void Chunk::ReleaseThreadStorage()
|
||||||
{
|
{
|
||||||
unsigned char *tileIds = static_cast<unsigned char *>(TlsGetValue(tlsIdx));
|
// No-op: thread_local handles per-thread cleanup automatically
|
||||||
delete tileIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *Chunk::GetTileIdsStorage()
|
unsigned char *Chunk::GetTileIdsStorage()
|
||||||
{
|
{
|
||||||
unsigned char *tileIds = static_cast<unsigned char *>(TlsGetValue(tlsIdx));
|
return s_tlsTileIds;
|
||||||
return tileIds;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// 4J Stu - Don't want this when multi-threaded
|
// 4J Stu - Don't want this when multi-threaded
|
||||||
|
|
@ -234,26 +231,51 @@ void Chunk::rebuild()
|
||||||
static unsigned char tileIds[16 * 16 * Level::maxBuildHeight];
|
static unsigned char tileIds[16 * 16 * Level::maxBuildHeight];
|
||||||
#endif
|
#endif
|
||||||
byteArray tileArray = byteArray(tileIds, 16 * 16 * Level::maxBuildHeight);
|
byteArray tileArray = byteArray(tileIds, 16 * 16 * Level::maxBuildHeight);
|
||||||
level->getChunkAt(x,z)->getBlockData(tileArray); // 4J - TODO - now our data has been re-arranged, we could just extra the vertical slice of this chunk rather than the whole thing
|
LevelChunk *sourceChunk = level->getChunkAt(x,z);
|
||||||
|
if( sourceChunk == nullptr )
|
||||||
LevelSource *region = new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r);
|
{
|
||||||
TileRenderer *tileRenderer = new TileRenderer(region, this->x, this->y, this->z, tileIds);
|
// Level chunk not loaded yet - treat as empty
|
||||||
|
for (int currentLayer = 0; currentLayer < 2; currentLayer++)
|
||||||
// AP - added a caching system for Chunk::rebuild to take advantage of
|
{
|
||||||
// Basically we're storing of copy of the tileIDs array inside the region so that calls to Region::getTile can grab data
|
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer);
|
||||||
// more quickly from this array rather than calling CompressedTileStorage. On the Vita the total thread time spent in
|
RenderManager.CBuffClear(lists + currentLayer);
|
||||||
// Region::getTile went from 20% to 4%.
|
}
|
||||||
#ifdef __PSVITA__
|
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT);
|
||||||
int xc = x >> 4;
|
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_COMPILED);
|
||||||
int zc = z >> 4;
|
PIXEndNamedEvent();
|
||||||
((Region*)region)->setCachedTiles(tileIds, xc, zc);
|
PIXEndNamedEvent();
|
||||||
#endif
|
return;
|
||||||
|
}
|
||||||
|
sourceChunk->getBlockData(tileArray); // 4J - TODO - now our data has been re-arranged, we could just extra the vertical slice of this chunk rather than the whole thing
|
||||||
|
|
||||||
// We now go through the vertical section of this level chunk that we are interested in and try and establish
|
// We now go through the vertical section of this level chunk that we are interested in and try and establish
|
||||||
// (1) if it is completely empty
|
// (1) if it is completely empty
|
||||||
// (2) if any of the tiles can be quickly determined to not need rendering because they are in the middle of other tiles and
|
// (2) if any of the tiles can be quickly determined to not need rendering because they are in the middle of other tiles and
|
||||||
// so can't be seen. A large amount (> 60% in tests) of tiles that call tesselateInWorld in the unoptimised version
|
// so can't be seen. A large amount (> 60% in tests) of tiles that call tesselateInWorld in the unoptimised version
|
||||||
// of this function fall into this category. By far the largest category of these are tiles in solid regions of rock.
|
// of this function fall into this category. By far the largest category of these are tiles in solid regions of rock.
|
||||||
|
// Build the occluder lookup from Tile::isSolidRender() so that ALL opaque full-cube blocks act as occluders,
|
||||||
|
// not just stone/dirt/bedrock. This catches sand, gravel, ores, cobblestone, sandstone, netherrack, wool, etc.
|
||||||
|
// which dramatically reduces tesselation work for underground/terrain-heavy chunks.
|
||||||
|
//
|
||||||
|
// thread_local: the table is built once per thread and reused across all subsequent rebuild() calls on that
|
||||||
|
// thread. Without it, a plain local would redo 255 virtual dispatches (isSolidRender) every single call -
|
||||||
|
// rebuild() runs up to MAX_CONCURRENT_CHUNK_REBUILDS times per updateDirtyChunks(), ~10 times per frame,
|
||||||
|
// so hundreds of redundant vtable lookups per second for data that never changes at runtime. A plain static
|
||||||
|
// would avoid that but introduces a data race: multiple rebuild threads run concurrently and the C++ static
|
||||||
|
// init guard (mutex) would serialize them on every entry just to check the "already initialized" flag.
|
||||||
|
// thread_local gives each thread its own copy with zero synchronization after the first call.
|
||||||
|
static thread_local auto isOccluder = [] {
|
||||||
|
std::array<bool, 256> table{};
|
||||||
|
for (int id = 1; id < 256; id++)
|
||||||
|
{
|
||||||
|
Tile *tile = Tile::tiles[id];
|
||||||
|
if (tile != nullptr && tile->isSolidRender())
|
||||||
|
table[id] = true;
|
||||||
|
}
|
||||||
|
table[255] = true; // already-marked-invisible sentinel
|
||||||
|
return table;
|
||||||
|
}();
|
||||||
|
|
||||||
bool empty = true;
|
bool empty = true;
|
||||||
for( int yy = y0; yy < y1; yy++ )
|
for( int yy = y0; yy < y1; yy++ )
|
||||||
{
|
{
|
||||||
|
|
@ -279,17 +301,12 @@ void Chunk::rebuild()
|
||||||
if(( xx == 0 ) || ( xx == 15 )) continue;
|
if(( xx == 0 ) || ( xx == 15 )) continue;
|
||||||
if(( zz == 0 ) || ( zz == 15 )) continue;
|
if(( zz == 0 ) || ( zz == 15 )) continue;
|
||||||
|
|
||||||
// Establish whether this tile and its neighbours are all made of rock, dirt, unbreakable tiles, or have already
|
// Establish whether this tile and its neighbours are all occluders using lookup table
|
||||||
// been determined to meet this criteria themselves and have a tile of 255 set.
|
if( !isOccluder[tileId] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx - 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
tileId = tileIds[ offset + ( ( ( xx - 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ];
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx + 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz - 1 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
tileId = tileIds[ offset + ( ( ( xx + 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ];
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 1 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz - 1 ) << 7 ) | ( indexY + 0 )) ];
|
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 1 ) << 7 ) | ( indexY + 0 )) ];
|
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
// Treat the bottom of the world differently - we shouldn't ever be able to look up at this, so consider tiles as invisible
|
// Treat the bottom of the world differently - we shouldn't ever be able to look up at this, so consider tiles as invisible
|
||||||
// if they are surrounded on sides other than the bottom
|
// if they are surrounded on sides other than the bottom
|
||||||
if( yy > 0 )
|
if( yy > 0 )
|
||||||
|
|
@ -301,8 +318,7 @@ void Chunk::rebuild()
|
||||||
indexYMinusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
indexYMinusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
||||||
yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
||||||
}
|
}
|
||||||
tileId = tileIds[ yMinusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYMinusOne ) ];
|
if( !isOccluder[ tileIds[ yMinusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYMinusOne ) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
}
|
}
|
||||||
int indexYPlusOne = yy + 1;
|
int indexYPlusOne = yy + 1;
|
||||||
int yPlusOneOffset = 0;
|
int yPlusOneOffset = 0;
|
||||||
|
|
@ -311,8 +327,7 @@ void Chunk::rebuild()
|
||||||
indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
||||||
yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
||||||
}
|
}
|
||||||
tileId = tileIds[ yPlusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYPlusOne ) ];
|
if( !isOccluder[ tileIds[ yPlusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYPlusOne ) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
|
|
||||||
// This tile is surrounded. Flag it as not requiring to be rendered by setting its id to 255.
|
// This tile is surrounded. Flag it as not requiring to be rendered by setting its id to 255.
|
||||||
tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ] = 0xff;
|
tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ] = 0xff;
|
||||||
|
|
@ -329,14 +344,31 @@ void Chunk::rebuild()
|
||||||
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer);
|
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer);
|
||||||
RenderManager.CBuffClear(lists + currentLayer);
|
RenderManager.CBuffClear(lists + currentLayer);
|
||||||
}
|
}
|
||||||
|
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT);
|
||||||
|
levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_COMPILED);
|
||||||
|
|
||||||
delete region;
|
PIXEndNamedEvent(); // match "Rebuilding chunk" event
|
||||||
delete tileRenderer;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 4J - optimisation ends
|
// 4J - optimisation ends
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Construct Region and TileRenderer only for non-empty chunks.
|
||||||
|
// This is deferred to here so that empty chunks (common after teleport) skip
|
||||||
|
// the Region construction (9 chunk lookups) and TileRenderer 128KB cache memset.
|
||||||
|
Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r);
|
||||||
|
TileRenderer tileRenderer(®ion, this->x, this->y, this->z, tileIds);
|
||||||
|
|
||||||
|
// AP - added a caching system for Chunk::rebuild to take advantage of
|
||||||
|
// Basically we're storing of copy of the tileIDs array inside the region so that calls to Region::getTile can grab data
|
||||||
|
// more quickly from this array rather than calling CompressedTileStorage. On the Vita the total thread time spent in
|
||||||
|
// Region::getTile went from 20% to 4%.
|
||||||
|
#ifdef __PSVITA__
|
||||||
|
int xc = x >> 4;
|
||||||
|
int zc = z >> 4;
|
||||||
|
region.setCachedTiles(tileIds, xc, zc);
|
||||||
|
#endif
|
||||||
|
|
||||||
PIXBeginNamedEvent(0,"Rebuild section C");
|
PIXBeginNamedEvent(0,"Rebuild section C");
|
||||||
Tesselator::Bounds bounds; // 4J MGH - added
|
Tesselator::Bounds bounds; // 4J MGH - added
|
||||||
{
|
{
|
||||||
|
|
@ -373,11 +405,11 @@ void Chunk::rebuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4J - get tile from those copied into our local array in earlier optimisation
|
// 4J - get tile from those copied into our local array in earlier optimisation
|
||||||
unsigned char tileId = tileIds[ offset + ( ( ( x - x0 ) << 11 ) | ( ( z - z0 ) << 7 ) | indexY) ];
|
const unsigned char tileId = tileIds[ offset + ( ( ( x - x0 ) << 11 ) | ( ( z - z0 ) << 7 ) | indexY) ];
|
||||||
// If flagged as not visible, drop out straight away
|
// If flagged as not visible, drop out straight away
|
||||||
if( tileId == 0xff ) continue;
|
if( tileId == 0xff ) [[unlikely]] continue;
|
||||||
// int tileId = region->getTile(x,y,z);
|
// int tileId = region->getTile(x,y,z);
|
||||||
if (tileId > 0)
|
if (tileId > 0) [[unlikely]]
|
||||||
{
|
{
|
||||||
if (!started)
|
if (!started)
|
||||||
{
|
{
|
||||||
|
|
@ -405,7 +437,7 @@ void Chunk::rebuild()
|
||||||
Tile *tile = Tile::tiles[tileId];
|
Tile *tile = Tile::tiles[tileId];
|
||||||
if (currentLayer == 0 && tile->isEntityTile())
|
if (currentLayer == 0 && tile->isEntityTile())
|
||||||
{
|
{
|
||||||
shared_ptr<TileEntity> et = region->getTileEntity(x, y, z);
|
shared_ptr<TileEntity> et = region.getTileEntity(x, y, z);
|
||||||
if (TileEntityRenderDispatcher::instance->hasRenderer(et))
|
if (TileEntityRenderDispatcher::instance->hasRenderer(et))
|
||||||
{
|
{
|
||||||
renderableTileEntities.push_back(et);
|
renderableTileEntities.push_back(et);
|
||||||
|
|
@ -419,7 +451,7 @@ void Chunk::rebuild()
|
||||||
}
|
}
|
||||||
else if (renderLayer == currentLayer)
|
else if (renderLayer == currentLayer)
|
||||||
{
|
{
|
||||||
rendered |= tileRenderer->tesselateInWorld(tile, x, y, z);
|
rendered |= tileRenderer.tesselateInWorld(tile, x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -479,9 +511,6 @@ void Chunk::rebuild()
|
||||||
bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]);
|
bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete tileRenderer;
|
|
||||||
delete region;
|
|
||||||
|
|
||||||
PIXEndNamedEvent();
|
PIXEndNamedEvent();
|
||||||
PIXBeginNamedEvent(0,"Rebuild section D");
|
PIXBeginNamedEvent(0,"Rebuild section D");
|
||||||
// 4J - have rewritten the way that tile entities are stored globally to make it work more easily with split screen. Chunks are now
|
// 4J - have rewritten the way that tile entities are stored globally to make it work more easily with split screen. Chunks are now
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,8 @@ public:
|
||||||
static LevelRenderer *levelRenderer;
|
static LevelRenderer *levelRenderer;
|
||||||
private:
|
private:
|
||||||
#ifndef _LARGE_WORLDS
|
#ifndef _LARGE_WORLDS
|
||||||
static Tesselator *t;
|
static Tesselator *t;
|
||||||
#else
|
#else
|
||||||
static DWORD tlsIdx;
|
|
||||||
public:
|
public:
|
||||||
static void CreateNewThreadStorage();
|
static void CreateNewThreadStorage();
|
||||||
static void ReleaseThreadStorage();
|
static void ReleaseThreadStorage();
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WINDOWS64
|
#ifdef _WINDOWS64
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include "Xbox\Resource.h"
|
#include "Xbox\Resource.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,10 @@
|
||||||
|
|
||||||
#define GDRAW_ASSERTS
|
#define GDRAW_ASSERTS
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We temporarily disable this warning for the shared interface portions
|
// We temporarily disable this warning for the shared interface portions
|
||||||
#pragma warning (push)
|
#pragma warning (push)
|
||||||
#pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union
|
#pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <d3d11_x.h> // 4J changed to use monolithic version
|
#include <d3d11_x.h> // 4J changed to use monolithic version
|
||||||
#include "gdraw.h"
|
#include "gdraw.h"
|
||||||
#include "iggy.h"
|
#include "iggy.h"
|
||||||
|
|
|
||||||
|
|
@ -367,19 +367,17 @@ void LevelRenderer::setLevel(int playerIndex, MultiPlayerLevel *level)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// printf("NULLing player %d, chunks @ 0x%x\n",playerIndex,chunks[playerIndex]);
|
// printf("NULLing player %d, chunks @ 0x%x\n",playerIndex,chunks[playerIndex]);
|
||||||
if( chunks[playerIndex].data != nullptr )
|
if (chunks[playerIndex].data != nullptr)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < chunks[playerIndex].length; i++)
|
for (unsigned int i = 0; i < chunks[playerIndex].length; i++)
|
||||||
{
|
{
|
||||||
chunks[playerIndex][i].chunk->_delete();
|
chunks[playerIndex][i].chunk->_delete();
|
||||||
delete chunks[playerIndex][i].chunk;
|
delete chunks[playerIndex][i].chunk;
|
||||||
}
|
}
|
||||||
delete chunks[playerIndex].data;
|
delete[] chunks[playerIndex].data;
|
||||||
chunks[playerIndex].data = nullptr;
|
chunks[playerIndex].data = nullptr;
|
||||||
chunks[playerIndex].length = 0;
|
chunks[playerIndex].length = 0;
|
||||||
// delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore
|
}
|
||||||
// sortedChunks[playerIndex] = nullptr; // 4J - removed - not sorting our chunks anymore
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4J Stu - If we do this for splitscreen players leaving, then all the tile entities in the world dissappear
|
// 4J Stu - If we do this for splitscreen players leaving, then all the tile entities in the world dissappear
|
||||||
// We should only do this when actually exiting the game, so only when the primary player sets there level to nullptr
|
// We should only do this when actually exiting the game, so only when the primary player sets there level to nullptr
|
||||||
|
|
@ -451,7 +449,7 @@ void LevelRenderer::allChanged(int playerIndex)
|
||||||
chunks[playerIndex][i].chunk->_delete();
|
chunks[playerIndex][i].chunk->_delete();
|
||||||
delete chunks[playerIndex][i].chunk;
|
delete chunks[playerIndex][i].chunk;
|
||||||
}
|
}
|
||||||
delete chunks[playerIndex].data;
|
delete[] chunks[playerIndex].data;
|
||||||
// delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore
|
// delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -823,8 +821,8 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha)
|
||||||
unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer;
|
unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer;
|
||||||
for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ )
|
for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ )
|
||||||
{
|
{
|
||||||
if( !pClipChunk->visible ) continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set
|
if( !pClipChunk->visible ) [[likely]] continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set
|
||||||
if( pClipChunk->globalIdx == -1 ) continue; // Not sure if we should ever encounter this... TODO check
|
if( pClipChunk->globalIdx == -1 ) [[unlikely]] continue; // Not sure if we should ever encounter this... TODO check
|
||||||
if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty
|
if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty
|
||||||
|
|
||||||
// List can be calculated directly from the chunk's global idex
|
// List can be calculated directly from the chunk's global idex
|
||||||
|
|
@ -1943,7 +1941,11 @@ bool LevelRenderer::updateDirtyChunks()
|
||||||
int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS;
|
int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS;
|
||||||
// 4J Stu - On XboxOne we should cut this down if in a constrained state so the saving threads get more time
|
// 4J Stu - On XboxOne we should cut this down if in a constrained state so the saving threads get more time
|
||||||
#endif
|
#endif
|
||||||
// Find nearest chunk that is dirty
|
// Find nearest chunk that is dirty.
|
||||||
|
// Rewritten for performance: linear scan of clip chunks (cache-friendly), with dirty flag
|
||||||
|
// checked BEFORE computing distances. The old triple-nested x/z/y loop computed distances
|
||||||
|
// for every chunk (~20K) regardless of dirty state. Now only dirty chunks (typically <1%)
|
||||||
|
// pay the cost of distance computation and nearest-selection.
|
||||||
for( int p = 0; p < XUSER_MAX_COUNT; p++ )
|
for( int p = 0; p < XUSER_MAX_COUNT; p++ )
|
||||||
{
|
{
|
||||||
// It's possible that the localplayers member can be set to nullptr on the main thread when a player chooses to exit the game
|
// It's possible that the localplayers member can be set to nullptr on the main thread when a player chooses to exit the game
|
||||||
|
|
@ -1957,96 +1959,91 @@ bool LevelRenderer::updateDirtyChunks()
|
||||||
int py = static_cast<int>(player->y);
|
int py = static_cast<int>(player->y);
|
||||||
int pz = static_cast<int>(player->z);
|
int pz = static_cast<int>(player->z);
|
||||||
|
|
||||||
// app.DebugPrintf("!! %d %d %d, %d %d %d {%d,%d} ",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, xChunks, zChunks);
|
int numClipChunks = static_cast<int>(chunks[p].length);
|
||||||
|
ClipChunk *pClipChunk = chunks[p].data;
|
||||||
int considered = 0;
|
for( int i = 0; i < numClipChunks; i++, pClipChunk++ )
|
||||||
int wouldBeNearButEmpty = 0;
|
|
||||||
for( int x = 0; x < xChunks; x++ )
|
|
||||||
{
|
{
|
||||||
for( int z = 0; z < zChunks; z++ )
|
// Fast reject: skip non-dirty chunks immediately before any distance work.
|
||||||
|
// globalIdx can be -1 for unassigned chunks.
|
||||||
|
const int gIdx = pClipChunk->globalIdx;
|
||||||
|
if (gIdx < 0)
|
||||||
|
continue;
|
||||||
|
const unsigned char flags = globalChunkFlags[gIdx];
|
||||||
|
if (!(flags & CHUNK_FLAG_DIRTY))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Batch-clear empty chunks upfront. After teleport, thousands of sky/void chunks
|
||||||
|
// are dirty. Clearing them all in one scan (rather than ~8 per call) dramatically
|
||||||
|
// reduces the dirty set for subsequent iterations.
|
||||||
|
Chunk *chunk = pClipChunk->chunk;
|
||||||
|
if (chunk == nullptr)
|
||||||
|
continue;
|
||||||
|
const int ySlice = (pClipChunk->ym - (CHUNK_SIZE / 2)) / CHUNK_SIZE;
|
||||||
|
LevelChunk *lc = level[p]->getChunkAt(chunk->x, chunk->z);
|
||||||
|
if (lc == nullptr || lc->isRenderChunkEmpty(ySlice * 16))
|
||||||
{
|
{
|
||||||
for( int y = 0; y < CHUNK_Y_COUNT; y++ )
|
chunk->clearDirty();
|
||||||
|
globalChunkFlags[gIdx] |= CHUNK_FLAG_EMPTYBOTH;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-empty dirty chunk — now compute distance
|
||||||
|
const int xd = pClipChunk->xm - px;
|
||||||
|
const int yd = pClipChunk->ym - py;
|
||||||
|
const int zd = pClipChunk->zm - pz;
|
||||||
|
const int distSq = xd * xd + yd * yd + zd * zd;
|
||||||
|
const int distSqWeighted = distSq + 3 * yd * yd; // Extra y weighting to prioritise things in same x/z plane as player first
|
||||||
|
|
||||||
|
if( (!onlyRebuild) ||
|
||||||
|
(flags & CHUNK_FLAG_COMPILED) ||
|
||||||
|
( distSq < 96 * 96 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data
|
||||||
|
{
|
||||||
|
// Is this chunk nearer than our nearest?
|
||||||
|
#ifdef _LARGE_WORLDS
|
||||||
|
bool isNearer = nearestClipChunks.empty();
|
||||||
|
auto itNearest = nearestClipChunks.begin();
|
||||||
|
for(; itNearest != nearestClipChunks.end(); ++itNearest)
|
||||||
{
|
{
|
||||||
ClipChunk *pClipChunk = &chunks[p][(z * yChunks + y) * xChunks + x];
|
isNearer = distSqWeighted < itNearest->second;
|
||||||
// Get distance to this chunk - deliberately not calling the chunk's method of doing this to avoid overheads (passing entitie, type conversion etc.) that this involves
|
if(isNearer) break;
|
||||||
int xd = pClipChunk->xm - px;
|
}
|
||||||
int yd = pClipChunk->ym - py;
|
isNearer = isNearer || (nearestClipChunks.size() < maxNearestChunks);
|
||||||
int zd = pClipChunk->zm - pz;
|
#else
|
||||||
int distSq = xd * xd + yd * yd + zd * zd;
|
bool isNearer = distSqWeighted < minDistSq;
|
||||||
int distSqWeighted = xd * xd + yd * yd * 4 + zd * zd; // Weighting against y to prioritise things in same x/z plane as player first
|
#endif
|
||||||
|
|
||||||
if( globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_DIRTY )
|
#ifdef _CRITICAL_CHUNKS
|
||||||
|
// AP - this will make sure that if a deferred grouping has started, only critical chunks go into that
|
||||||
|
// grouping, even if a non-critical chunk is closer.
|
||||||
|
if( (!veryNearCount && isNearer) ||
|
||||||
|
(distSq < 20 * 20 && (flags & CHUNK_FLAG_CRITICAL)) )
|
||||||
|
#else
|
||||||
|
if( isNearer )
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
// Non-empty (already confirmed above), add to nearest set
|
||||||
|
nearChunk = pClipChunk;
|
||||||
|
minDistSq = distSqWeighted;
|
||||||
|
#ifdef _LARGE_WORLDS
|
||||||
|
nearestClipChunks.insert(itNearest, std::make_pair(nearChunk, minDistSq) );
|
||||||
|
if(nearestClipChunks.size() > maxNearestChunks)
|
||||||
{
|
{
|
||||||
if( (!onlyRebuild) ||
|
nearestClipChunks.pop_back();
|
||||||
globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED ||
|
|
||||||
( distSq < 96 * 96 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data
|
|
||||||
{ // distSq adjusted from 20 * 20 to 96 * 96 - updated by detectiveren
|
|
||||||
considered++;
|
|
||||||
// Is this chunk nearer than our nearest?
|
|
||||||
#ifdef _LARGE_WORLDS
|
|
||||||
bool isNearer = nearestClipChunks.empty();
|
|
||||||
auto itNearest = nearestClipChunks.begin();
|
|
||||||
for(; itNearest != nearestClipChunks.end(); ++itNearest)
|
|
||||||
{
|
|
||||||
isNearer = distSqWeighted < itNearest->second;
|
|
||||||
if(isNearer) break;
|
|
||||||
}
|
|
||||||
isNearer = isNearer || (nearestClipChunks.size() < maxNearestChunks);
|
|
||||||
#else
|
|
||||||
bool isNearer = distSqWeighted < minDistSq;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _CRITICAL_CHUNKS
|
|
||||||
// AP - this will make sure that if a deferred grouping has started, only critical chunks go into that
|
|
||||||
// grouping, even if a non-critical chunk is closer.
|
|
||||||
if( (!veryNearCount && isNearer) ||
|
|
||||||
(distSq < 20 * 20 && (globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_CRITICAL)) )
|
|
||||||
#else
|
|
||||||
if( isNearer )
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// At this point we've got a chunk that we would like to consider for rendering, at least based on its proximity to the player(s).
|
|
||||||
// Its *quite* quick to generate empty render data for render chunks, but if we let the rebuilding do that then the after rebuilding we will have
|
|
||||||
// to start searching for the next nearest chunk from scratch again. Instead, its better to detect empty chunks at this stage, flag them up as not dirty
|
|
||||||
// (and empty), and carry on. The levelchunk's isRenderChunkEmpty method can be quite optimal as it can make use of the chunk's data compression to detect
|
|
||||||
// emptiness without actually testing as many data items as uncompressed data would.
|
|
||||||
Chunk *chunk = pClipChunk->chunk;
|
|
||||||
LevelChunk *lc = level[p]->getChunkAt(chunk->x,chunk->z);
|
|
||||||
if( !lc->isRenderChunkEmpty(y * 16) )
|
|
||||||
{
|
|
||||||
nearChunk = pClipChunk;
|
|
||||||
minDistSq = distSqWeighted;
|
|
||||||
#ifdef _LARGE_WORLDS
|
|
||||||
nearestClipChunks.insert(itNearest, std::make_pair(nearChunk, minDistSq) );
|
|
||||||
if(nearestClipChunks.size() > maxNearestChunks)
|
|
||||||
{
|
|
||||||
nearestClipChunks.pop_back();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
chunk->clearDirty();
|
|
||||||
globalChunkFlags[ pClipChunk->globalIdx ] |= CHUNK_FLAG_EMPTYBOTH;
|
|
||||||
wouldBeNearButEmpty++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _CRITICAL_CHUNKS
|
|
||||||
// AP - is the chunk near and also critical
|
|
||||||
if( distSq < 20 * 20 && ((globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_CRITICAL)) )
|
|
||||||
#else
|
|
||||||
if( distSq < 20 * 20 )
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
veryNearCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _CRITICAL_CHUNKS
|
||||||
|
// AP - is the chunk near and also critical
|
||||||
|
if( distSq < 20 * 20 && (flags & CHUNK_FLAG_CRITICAL) )
|
||||||
|
#else
|
||||||
|
if( distSq < 20 * 20 )
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
veryNearCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty);
|
|
||||||
}
|
}
|
||||||
#endif // __PS3__
|
#endif // __PS3__
|
||||||
PIXEndNamedEvent();
|
PIXEndNamedEvent();
|
||||||
|
|
@ -2193,7 +2190,11 @@ bool LevelRenderer::updateDirtyChunks()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( nearChunk ) destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount );
|
if( nearChunk )
|
||||||
|
{
|
||||||
|
destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount );
|
||||||
|
return dirtyChunkPresent;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -2444,18 +2445,23 @@ void LevelRenderer::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1
|
||||||
setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1, level);
|
setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inline clip(float *bb, float *frustum)
|
bool inline clip(float * __restrict bb, float * __restrict frustum)
|
||||||
{
|
{
|
||||||
|
// Pre-load AABB corners to avoid repeated memory loads
|
||||||
|
const float x0 = bb[0], y0 = bb[1], z0 = bb[2];
|
||||||
|
const float x1 = bb[3], y1 = bb[4], z1 = bb[5];
|
||||||
|
|
||||||
for (int i = 0; i < 6; ++i, frustum += 4)
|
for (int i = 0; i < 6; ++i, frustum += 4)
|
||||||
{
|
{
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
const float a = frustum[0], b = frustum[1], c = frustum[2], d = frustum[3];
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y0 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
if (a * x1 + b * y0 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y1 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x1 + b * y1 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y0 + c * z1 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x1 + b * y0 + c * z1 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y1 + c * z1 + d > 0) continue;
|
||||||
|
if (a * x1 + b * y1 + c * z1 + d > 0) continue;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,17 +24,16 @@ int normal;
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
DWORD Tesselator::tlsIdx = TlsAlloc();
|
static thread_local std::unique_ptr<Tesselator> tlsInstance;
|
||||||
|
|
||||||
Tesselator *Tesselator::getInstance()
|
Tesselator *Tesselator::getInstance()
|
||||||
{
|
{
|
||||||
return static_cast<Tesselator *>(TlsGetValue(tlsIdx));
|
return tlsInstance.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tesselator::CreateNewThreadStorage(int bytes)
|
void Tesselator::CreateNewThreadStorage(int bytes)
|
||||||
{
|
{
|
||||||
Tesselator *instance = new Tesselator(bytes/4);
|
tlsInstance = std::make_unique<Tesselator>(bytes / 4);
|
||||||
TlsSetValue(tlsIdx, instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tesselator::Tesselator(int size)
|
Tesselator::Tesselator(int size)
|
||||||
|
|
@ -310,20 +309,16 @@ void Tesselator::color(int r, int g, int b)
|
||||||
|
|
||||||
void Tesselator::color(int r, int g, int b, int a)
|
void Tesselator::color(int r, int g, int b, int a)
|
||||||
{
|
{
|
||||||
if (_noColor) return;
|
if (_noColor) return;
|
||||||
|
|
||||||
if (r > 255) r = 255;
|
r = std::clamp(r, 0, 255);
|
||||||
if (g > 255) g = 255;
|
g = std::clamp(g, 0, 255);
|
||||||
if (b > 255) b = 255;
|
b = std::clamp(b, 0, 255);
|
||||||
if (a > 255) a = 255;
|
a = std::clamp(a, 0, 255);
|
||||||
if (r < 0) r = 0;
|
|
||||||
if (g < 0) g = 0;
|
|
||||||
if (b < 0) b = 0;
|
|
||||||
if (a < 0) a = 0;
|
|
||||||
|
|
||||||
hasColor = true;
|
hasColor = true;
|
||||||
// 4J - removed little-endian option
|
// 4J - removed little-endian option
|
||||||
col = (r << 24) | (g << 16) | (b << 8) | (a);
|
col = (r << 24) | (g << 16) | (b << 8) | (a);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tesselator::color(byte r, byte g, byte b)
|
void Tesselator::color(byte r, byte g, byte b)
|
||||||
|
|
@ -381,26 +376,15 @@ void Tesselator::packCompactQuad()
|
||||||
m_iz[i] += 16 * 128;
|
m_iz[i] += 16 * 128;
|
||||||
}
|
}
|
||||||
// Find min x/y/z
|
// Find min x/y/z
|
||||||
unsigned int minx = m_ix[0];
|
unsigned int minx = std::min<unsigned int>({m_ix[0], m_ix[1], m_ix[2], m_ix[3]});
|
||||||
unsigned int miny = m_iy[0];
|
unsigned int miny = std::min<unsigned int>({m_iy[0], m_iy[1], m_iy[2], m_iy[3]});
|
||||||
unsigned int minz = m_iz[0];
|
unsigned int minz = std::min<unsigned int>({m_iz[0], m_iz[1], m_iz[2], m_iz[3]});
|
||||||
for( int i = 1; i < 4; i++ )
|
|
||||||
{
|
|
||||||
if( m_ix[i] < minx ) minx = m_ix[i];
|
|
||||||
if( m_iy[i] < miny ) miny = m_iy[i];
|
|
||||||
if( m_iz[i] < minz ) minz = m_iz[i];
|
|
||||||
}
|
|
||||||
// Everything has been scaled by a factor of 128 to get it into an int, and so
|
// Everything has been scaled by a factor of 128 to get it into an int, and so
|
||||||
// the minimum now should be in the range of (0->32) * 128. Get the base x/y/z
|
// the minimum now should be in the range of (0->32) * 128. Get the base x/y/z
|
||||||
// that our quad will be referenced from now, which can be stored in 5 bits
|
// that our quad will be referenced from now, which can be stored in 5 bits
|
||||||
unsigned int basex = ( minx >> 7 );
|
unsigned int basex = std::min<unsigned int>(minx >> 7, 31u);
|
||||||
unsigned int basey = ( miny >> 7 );
|
unsigned int basey = std::min<unsigned int>(miny >> 7, 31u);
|
||||||
unsigned int basez = ( minz >> 7 );
|
unsigned int basez = std::min<unsigned int>(minz >> 7, 31u);
|
||||||
// If the min is 32, then this whole quad must be in that plane - make the min 15 instead so
|
|
||||||
// we can still offset from that with our delta to get to the exact edge
|
|
||||||
if( basex == 32 ) basex = 31;
|
|
||||||
if( basey == 32 ) basey = 31;
|
|
||||||
if( basez == 32 ) basez = 31;
|
|
||||||
// Now get deltas to each vertex - these have an 8-bit range so they can span a
|
// Now get deltas to each vertex - these have an 8-bit range so they can span a
|
||||||
// full unit range from the base position
|
// full unit range from the base position
|
||||||
for( int i = 0; i < 4; i++ )
|
for( int i = 0; i < 4; i++ )
|
||||||
|
|
@ -420,28 +404,18 @@ void Tesselator::packCompactQuad()
|
||||||
data[0] |= ( basex << 26 ) | ( basey << 21 )| ( basez << 16 );
|
data[0] |= ( basex << 26 ) | ( basey << 21 )| ( basez << 16 );
|
||||||
|
|
||||||
// Now process UVs. First find min & max U & V
|
// Now process UVs. First find min & max U & V
|
||||||
unsigned int minu = m_u[0];
|
unsigned int minu = std::min<unsigned int>({m_u[0], m_u[1], m_u[2], m_u[3]});
|
||||||
unsigned int minv = m_v[0];
|
unsigned int minv = std::min<unsigned int>({m_v[0], m_v[1], m_v[2], m_v[3]});
|
||||||
unsigned int maxu = m_u[0];
|
unsigned int maxu = std::max<unsigned int>({m_u[0], m_u[1], m_u[2], m_u[3]});
|
||||||
unsigned int maxv = m_v[0];
|
unsigned int maxv = std::max<unsigned int>({m_v[0], m_v[1], m_v[2], m_v[3]});
|
||||||
|
|
||||||
for( int i = 1; i < 4; i++ )
|
|
||||||
{
|
|
||||||
if( m_u[i] < minu ) minu = m_u[i];
|
|
||||||
if( m_v[i] < minv ) minv = m_v[i];
|
|
||||||
if( m_u[i] > maxu ) maxu = m_u[i];
|
|
||||||
if( m_v[i] > maxv ) maxv = m_v[i];
|
|
||||||
}
|
|
||||||
// In nearly all cases, all our UVs should be axis aligned for this quad. So the only values they should
|
// In nearly all cases, all our UVs should be axis aligned for this quad. So the only values they should
|
||||||
// have in each dimension should be the min/max. We're going to store:
|
// have in each dimension should be the min/max. We're going to store:
|
||||||
// (1) minu/maxu (16 bits each, only actuall needs to store 14 bits to get a 0 to 2 range for each
|
// (1) minu/maxu (16 bits each, only actuall needs to store 14 bits to get a 0 to 2 range for each
|
||||||
// (2) du/dv ( ie maxu-minu, maxv-minv) - 8 bits each, to store a range of 0 to 15.9375 texels. This
|
// (2) du/dv ( ie maxu-minu, maxv-minv) - 8 bits each, to store a range of 0 to 15.9375 texels. This
|
||||||
// should be enough to map the full UV range of a single 16x16 region of the terrain texture, since
|
// should be enough to map the full UV range of a single 16x16 region of the terrain texture, since
|
||||||
// we always pull UVs in by 1/16th of their range at the sides
|
// we always pull UVs in by 1/16th of their range at the sides
|
||||||
unsigned int du = maxu - minu;
|
unsigned int du = std::min<unsigned int>(maxu - minu, 255u);
|
||||||
unsigned int dv = maxv - minv;
|
unsigned int dv = std::min<unsigned int>(maxv - minv, 255u);
|
||||||
if( du > 255 ) du = 255;
|
|
||||||
if( dv > 255 ) dv = 255;
|
|
||||||
// Check if this quad has UVs that can be referenced this way. This should only happen for flowing water
|
// Check if this quad has UVs that can be referenced this way. This should only happen for flowing water
|
||||||
// and lava, where the texture coordinates are rotated for the top surface of the tile.
|
// and lava, where the texture coordinates are rotated for the top surface of the tile.
|
||||||
bool axisAligned = true;
|
bool axisAligned = true;
|
||||||
|
|
|
||||||
|
|
@ -34,13 +34,9 @@ private:
|
||||||
float xoo, yoo, zoo;
|
float xoo, yoo, zoo;
|
||||||
int _normal;
|
int _normal;
|
||||||
|
|
||||||
// 4J - added for thread local storage
|
public:
|
||||||
public:
|
static void CreateNewThreadStorage(int bytes);
|
||||||
static void CreateNewThreadStorage(int bytes);
|
static Tesselator *getInstance();
|
||||||
private:
|
|
||||||
static DWORD tlsIdx;
|
|
||||||
public:
|
|
||||||
static Tesselator *getInstance();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool tesselating;
|
bool tesselating;
|
||||||
|
|
@ -52,6 +48,7 @@ private:
|
||||||
int vboCounts;
|
int vboCounts;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
|
public:
|
||||||
Tesselator(int size);
|
Tesselator(int size);
|
||||||
public:
|
public:
|
||||||
Tesselator *getUniqueInstance(int size);
|
Tesselator *getUniqueInstance(int size);
|
||||||
|
|
@ -93,35 +90,21 @@ public:
|
||||||
}
|
}
|
||||||
void addVert(float x, float y, float z)
|
void addVert(float x, float y, float z)
|
||||||
{
|
{
|
||||||
if(x < boundingBox[0])
|
boundingBox[0] = std::min<float>(boundingBox[0], x);
|
||||||
boundingBox[0] = x;
|
boundingBox[1] = std::min<float>(boundingBox[1], y);
|
||||||
if(y < boundingBox[1])
|
boundingBox[2] = std::min<float>(boundingBox[2], z);
|
||||||
boundingBox[1] = y;
|
boundingBox[3] = std::max<float>(boundingBox[3], x);
|
||||||
if(z < boundingBox[2])
|
boundingBox[4] = std::max<float>(boundingBox[4], y);
|
||||||
boundingBox[2] = z;
|
boundingBox[5] = std::max<float>(boundingBox[5], z);
|
||||||
|
|
||||||
if(x > boundingBox[3])
|
|
||||||
boundingBox[3] = x;
|
|
||||||
if(y > boundingBox[4])
|
|
||||||
boundingBox[4] = y;
|
|
||||||
if(z > boundingBox[5])
|
|
||||||
boundingBox[5] = z;
|
|
||||||
}
|
}
|
||||||
void addBounds(Bounds& ob)
|
void addBounds(const Bounds& ob)
|
||||||
{
|
{
|
||||||
if(ob.boundingBox[0] < boundingBox[0])
|
boundingBox[0] = std::min<float>(boundingBox[0], ob.boundingBox[0]);
|
||||||
boundingBox[0] = ob.boundingBox[0];
|
boundingBox[1] = std::min<float>(boundingBox[1], ob.boundingBox[1]);
|
||||||
if(ob.boundingBox[1] < boundingBox[1])
|
boundingBox[2] = std::min<float>(boundingBox[2], ob.boundingBox[2]);
|
||||||
boundingBox[1] = ob.boundingBox[1];
|
boundingBox[3] = std::max<float>(boundingBox[3], ob.boundingBox[3]);
|
||||||
if(ob.boundingBox[2] < boundingBox[2])
|
boundingBox[4] = std::max<float>(boundingBox[4], ob.boundingBox[4]);
|
||||||
boundingBox[2] = ob.boundingBox[2];
|
boundingBox[5] = std::max<float>(boundingBox[5], ob.boundingBox[5]);
|
||||||
|
|
||||||
if(ob.boundingBox[3] > boundingBox[3])
|
|
||||||
boundingBox[3] = ob.boundingBox[3];
|
|
||||||
if(ob.boundingBox[4] > boundingBox[4])
|
|
||||||
boundingBox[4] = ob.boundingBox[4];
|
|
||||||
if(ob.boundingBox[5] > boundingBox[5])
|
|
||||||
boundingBox[5] = ob.boundingBox[5];
|
|
||||||
}
|
}
|
||||||
float boundingBox[6]; // 4J MGH added
|
float boundingBox[6]; // 4J MGH added
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,9 @@ int TileRenderer::getLightColor( Tile *tt, LevelSource *level, int x, int y, int
|
||||||
return tt->getLightColor(level, x, y, z);
|
return tt->getLightColor(level, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr int TILE_RENDERER_CACHE_SIZE = 32 * 32 * 32;
|
||||||
|
static thread_local unsigned int s_tlsCache[TILE_RENDERER_CACHE_SIZE];
|
||||||
|
|
||||||
TileRenderer::TileRenderer( LevelSource* level, int xMin, int yMin, int zMin, unsigned char *tileIds )
|
TileRenderer::TileRenderer( LevelSource* level, int xMin, int yMin, int zMin, unsigned char *tileIds )
|
||||||
{
|
{
|
||||||
this->level = level;
|
this->level = level;
|
||||||
|
|
@ -153,14 +156,11 @@ TileRenderer::TileRenderer( LevelSource* level, int xMin, int yMin, int zMin, un
|
||||||
this->yMin2 = yMin-2;
|
this->yMin2 = yMin-2;
|
||||||
this->zMin2 = zMin-2;
|
this->zMin2 = zMin-2;
|
||||||
this->tileIds = tileIds;
|
this->tileIds = tileIds;
|
||||||
cache = new unsigned int[32*32*32];
|
cache = s_tlsCache;
|
||||||
XMemSet(cache,0,32*32*32*sizeof(unsigned int));
|
std::memset(cache, 0, TILE_RENDERER_CACHE_SIZE * sizeof(unsigned int));
|
||||||
}
|
}
|
||||||
|
|
||||||
TileRenderer::~TileRenderer()
|
TileRenderer::~TileRenderer() = default;
|
||||||
{
|
|
||||||
delete cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
TileRenderer::TileRenderer( LevelSource* level )
|
TileRenderer::TileRenderer( LevelSource* level )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,10 @@
|
||||||
|
|
||||||
#define GDRAW_ASSERTS
|
#define GDRAW_ASSERTS
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// We temporarily disable this warning for the shared interface portions
|
// We temporarily disable this warning for the shared interface portions
|
||||||
#pragma warning (push)
|
#pragma warning (push)
|
||||||
#pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union
|
#pragma warning (disable: 4201) // nonstandard extension used : nameless struct/union
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include "gdraw.h"
|
#include "gdraw.h"
|
||||||
#include "iggy.h"
|
#include "iggy.h"
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
#ifdef _WINDOWS64
|
#ifdef _WINDOWS64
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
class KeyboardMouseInput
|
class KeyboardMouseInput
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#ifdef _WINDOWS64
|
#ifdef _WINDOWS64
|
||||||
#define _HAS_STD_BYTE 0 // solve (std::)'byte' ambiguity with windows headers
|
#define _HAS_STD_BYTE 0 // solve (std::)'byte' ambiguity with windows headers
|
||||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
#define NOMINMAX // Exclude min/max macros from Windows headers
|
||||||
// Windows Header Files:
|
// Windows Header Files:
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
|
|
@ -132,12 +133,14 @@ typedef XUID GameSessionUID;
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <array>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<MASM>
|
<MASM>
|
||||||
<UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
|
<UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
|
||||||
|
|
@ -103,6 +104,7 @@
|
||||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<MASM>
|
<MASM>
|
||||||
<UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
|
<UseSafeExceptionHandlers>false</UseSafeExceptionHandlers>
|
||||||
|
|
@ -745,5 +747,4 @@
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
||||||
|
|
@ -12,20 +12,7 @@
|
||||||
|
|
||||||
Region::~Region()
|
Region::~Region()
|
||||||
{
|
{
|
||||||
for(unsigned int i = 0; i < chunks->length; ++i)
|
// flatChunksHeap automatically freed by unique_ptr
|
||||||
{
|
|
||||||
LevelChunkArray *lca = (*chunks)[i];
|
|
||||||
delete [] lca->data;
|
|
||||||
delete lca;
|
|
||||||
}
|
|
||||||
delete [] chunks->data;
|
|
||||||
delete chunks;
|
|
||||||
|
|
||||||
// AP - added a caching system for Chunk::rebuild to take advantage of
|
|
||||||
if( CachedTiles )
|
|
||||||
{
|
|
||||||
free(CachedTiles);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Region::Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2, int r)
|
Region::Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2, int r)
|
||||||
|
|
@ -37,7 +24,21 @@ Region::Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2, int
|
||||||
int xc2 = (x2 + r) >> 4;
|
int xc2 = (x2 + r) >> 4;
|
||||||
int zc2 = (z2 + r) >> 4;
|
int zc2 = (z2 + r) >> 4;
|
||||||
|
|
||||||
chunks = new LevelChunk2DArray(xc2 - xc1 + 1, zc2 - zc1 + 1);
|
chunksDimX = xc2 - xc1 + 1;
|
||||||
|
chunksDimZ = zc2 - zc1 + 1;
|
||||||
|
int totalChunks = chunksDimX * chunksDimZ;
|
||||||
|
|
||||||
|
// Use stack buffer for common small regions (render chunks), heap for rare large ones (pathfinding)
|
||||||
|
if( totalChunks <= MAX_STACK_CHUNKS )
|
||||||
|
{
|
||||||
|
flatChunks = flatChunks_stack;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
flatChunksHeap = std::make_unique<LevelChunk*[]>(totalChunks);
|
||||||
|
flatChunks = flatChunksHeap.get();
|
||||||
|
}
|
||||||
|
std::fill_n(flatChunks, totalChunks, nullptr);
|
||||||
|
|
||||||
allEmpty = true;
|
allEmpty = true;
|
||||||
for (int xc = xc1; xc <= xc2; xc++)
|
for (int xc = xc1; xc <= xc2; xc++)
|
||||||
|
|
@ -47,8 +48,7 @@ Region::Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2, int
|
||||||
LevelChunk *chunk = level->getChunk(xc, zc);
|
LevelChunk *chunk = level->getChunk(xc, zc);
|
||||||
if(chunk != nullptr)
|
if(chunk != nullptr)
|
||||||
{
|
{
|
||||||
LevelChunkArray *lca = (*chunks)[xc - xc1];
|
flatChunks[(xc - xc1) * chunksDimZ + (zc - zc1)] = chunk;
|
||||||
lca->data[zc - zc1] = chunk;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,8 +56,7 @@ Region::Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2, int
|
||||||
{
|
{
|
||||||
for (int zc = (z1 >> 4); zc <= (z2 >> 4); zc++)
|
for (int zc = (z1 >> 4); zc <= (z2 >> 4); zc++)
|
||||||
{
|
{
|
||||||
LevelChunkArray *lca = (*chunks)[xc - xc1];
|
LevelChunk *chunk = flatChunks[(xc - xc1) * chunksDimZ + (zc - zc1)];
|
||||||
LevelChunk *chunk = lca->data[zc - zc1];
|
|
||||||
if (chunk != nullptr)
|
if (chunk != nullptr)
|
||||||
{
|
{
|
||||||
if (!chunk->isYSpaceEmpty(y1, y2))
|
if (!chunk->isYSpaceEmpty(y1, y2))
|
||||||
|
|
@ -105,12 +104,12 @@ int Region::getTile(int x, int y, int z)
|
||||||
xc -= xc1;
|
xc -= xc1;
|
||||||
zc -= zc1;
|
zc -= zc1;
|
||||||
|
|
||||||
if (xc < 0 || xc >= static_cast<int>(chunks->length) || zc < 0 || zc >= static_cast<int>((*chunks)[xc]->length))
|
if (xc < 0 || xc >= chunksDimX || zc < 0 || zc >= chunksDimZ)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelChunk *lc = (*chunks)[xc]->data[zc];
|
LevelChunk *lc = flatChunks[xc * chunksDimZ + zc];
|
||||||
if (lc == nullptr) return 0;
|
if (lc == nullptr) return 0;
|
||||||
|
|
||||||
return lc->getTile(x & 15, y, z & 15);
|
return lc->getTile(x & 15, y, z & 15);
|
||||||
|
|
@ -122,11 +121,11 @@ void Region::setCachedTiles(unsigned char *tiles, int xc, int zc)
|
||||||
xcCached = xc;
|
xcCached = xc;
|
||||||
zcCached = zc;
|
zcCached = zc;
|
||||||
int size = 16 * 16 * Level::maxBuildHeight;
|
int size = 16 * 16 * Level::maxBuildHeight;
|
||||||
if( CachedTiles == nullptr )
|
if (!CachedTiles)
|
||||||
{
|
{
|
||||||
CachedTiles = static_cast<unsigned char *>(malloc(size));
|
CachedTiles = std::make_unique<unsigned char[]>(size);
|
||||||
}
|
}
|
||||||
memcpy(CachedTiles, tiles, size);
|
std::copy(tiles, tiles + size, CachedTiles.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelChunk* Region::getLevelChunk(int x, int y, int z)
|
LevelChunk* Region::getLevelChunk(int x, int y, int z)
|
||||||
|
|
@ -137,12 +136,12 @@ LevelChunk* Region::getLevelChunk(int x, int y, int z)
|
||||||
int xc = (x >> 4) - xc1;
|
int xc = (x >> 4) - xc1;
|
||||||
int zc = (z >> 4) - zc1;
|
int zc = (z >> 4) - zc1;
|
||||||
|
|
||||||
if (xc < 0 || xc >= static_cast<int>(chunks->length) || zc < 0 || zc >= static_cast<int>((*chunks)[xc]->length))
|
if (xc < 0 || xc >= chunksDimX || zc < 0 || zc >= chunksDimZ)
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelChunk *lc = (*chunks)[xc]->data[zc];
|
LevelChunk *lc = flatChunks[xc * chunksDimZ + zc];
|
||||||
return lc;
|
return lc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,7 +152,15 @@ shared_ptr<TileEntity> Region::getTileEntity(int x, int y, int z)
|
||||||
int xc = (x >> 4) - xc1;
|
int xc = (x >> 4) - xc1;
|
||||||
int zc = (z >> 4) - zc1;
|
int zc = (z >> 4) - zc1;
|
||||||
|
|
||||||
return (*chunks)[xc]->data[zc]->getTileEntity(x & 15, y, z & 15);
|
if (xc < 0 || xc >= chunksDimX || zc < 0 || zc >= chunksDimZ)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelChunk *lc = flatChunks[xc * chunksDimZ + zc];
|
||||||
|
if (lc == nullptr) return nullptr;
|
||||||
|
|
||||||
|
return lc->getTileEntity(x & 15, y, z & 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Region::getLightColor(int x, int y, int z, int emitt, int tileId/*=-1*/)
|
int Region::getLightColor(int x, int y, int z, int emitt, int tileId/*=-1*/)
|
||||||
|
|
@ -228,7 +235,15 @@ int Region::getRawBrightness(int x, int y, int z, bool propagate)
|
||||||
int xc = (x >> 4) - xc1;
|
int xc = (x >> 4) - xc1;
|
||||||
int zc = (z >> 4) - zc1;
|
int zc = (z >> 4) - zc1;
|
||||||
|
|
||||||
return (*chunks)[xc]->data[zc]->getRawBrightness(x & 15, y, z & 15, level->skyDarken);
|
if (xc < 0 || xc >= chunksDimX || zc < 0 || zc >= chunksDimZ)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelChunk *lc = flatChunks[xc * chunksDimZ + zc];
|
||||||
|
if (lc == nullptr) return 0;
|
||||||
|
|
||||||
|
return lc->getRawBrightness(x & 15, y, z & 15, level->skyDarken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -239,7 +254,15 @@ int Region::getData(int x, int y, int z)
|
||||||
int xc = (x >> 4) - xc1;
|
int xc = (x >> 4) - xc1;
|
||||||
int zc = (z >> 4) - zc1;
|
int zc = (z >> 4) - zc1;
|
||||||
|
|
||||||
return (*chunks)[xc]->data[zc]->getData(x & 15, y, z & 15);
|
if (xc < 0 || xc >= chunksDimX || zc < 0 || zc >= chunksDimZ)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelChunk *lc = flatChunks[xc * chunksDimZ + zc];
|
||||||
|
if (lc == nullptr) return 0;
|
||||||
|
|
||||||
|
return lc->getData(x & 15, y, z & 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
Material *Region::getMaterial(int x, int y, int z)
|
Material *Region::getMaterial(int x, int y, int z)
|
||||||
|
|
@ -321,7 +344,7 @@ int Region::getBrightnessPropagate(LightLayer::variety layer, int x, int y, int
|
||||||
// 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
|
// 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
|
||||||
// were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
|
// were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
|
||||||
// it to an int
|
// it to an int
|
||||||
return (int)layer;
|
return static_cast<int>(layer);
|
||||||
}
|
}
|
||||||
if (layer == LightLayer::Sky && level->dimension->hasCeiling)
|
if (layer == LightLayer::Sky && level->dimension->hasCeiling)
|
||||||
{
|
{
|
||||||
|
|
@ -346,7 +369,15 @@ int Region::getBrightnessPropagate(LightLayer::variety layer, int x, int y, int
|
||||||
int xc = (x >> 4) - xc1;
|
int xc = (x >> 4) - xc1;
|
||||||
int zc = (z >> 4) - zc1;
|
int zc = (z >> 4) - zc1;
|
||||||
|
|
||||||
return (*chunks)[xc]->data[zc]->getBrightness(layer, x & 15, y, z & 15);
|
if (xc < 0 || xc >= chunksDimX || zc < 0 || zc >= chunksDimZ)
|
||||||
|
{
|
||||||
|
return static_cast<int>(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelChunk *lc = flatChunks[xc * chunksDimZ + zc];
|
||||||
|
if (lc == nullptr) return static_cast<int>(layer);
|
||||||
|
|
||||||
|
return lc->getBrightness(layer, x & 15, y, z & 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4J - brought forward from 1.8.2
|
// 4J - brought forward from 1.8.2
|
||||||
|
|
@ -359,12 +390,20 @@ int Region::getBrightness(LightLayer::variety layer, int x, int y, int z)
|
||||||
// 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
|
// 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we
|
||||||
// were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
|
// were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast
|
||||||
// it to an int
|
// it to an int
|
||||||
return (int)layer;
|
return static_cast<int>(layer);
|
||||||
}
|
}
|
||||||
int xc = (x >> 4) - xc1;
|
int xc = (x >> 4) - xc1;
|
||||||
int zc = (z >> 4) - zc1;
|
int zc = (z >> 4) - zc1;
|
||||||
|
|
||||||
return (*chunks)[xc]->data[zc]->getBrightness(layer, x & 15, y, z & 15);
|
if (xc < 0 || xc >= chunksDimX || zc < 0 || zc >= chunksDimZ)
|
||||||
|
{
|
||||||
|
return static_cast<int>(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelChunk *lc = flatChunks[xc * chunksDimZ + zc];
|
||||||
|
if (lc == nullptr) return static_cast<int>(layer);
|
||||||
|
|
||||||
|
return lc->getBrightness(layer, x & 15, y, z & 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Region::getMaxBuildHeight()
|
int Region::getMaxBuildHeight()
|
||||||
|
|
|
||||||
|
|
@ -9,18 +9,29 @@ class BiomeSource;
|
||||||
class Region : public LevelSource
|
class Region : public LevelSource
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
static constexpr int MAX_STACK_CHUNKS = 16; // 4x4 covers the common render-chunk case (r=1)
|
||||||
int xc1, zc1;
|
int xc1, zc1;
|
||||||
LevelChunk2DArray *chunks;
|
LevelChunk *flatChunks_stack[MAX_STACK_CHUNKS];
|
||||||
|
LevelChunk **flatChunks; // non-owning: points to flatChunks_stack or flatChunksHeap's buffer
|
||||||
|
std::unique_ptr<LevelChunk*[]> flatChunksHeap; // owns heap allocation for large regions
|
||||||
|
int chunksDimX, chunksDimZ;
|
||||||
Level *level;
|
Level *level;
|
||||||
bool allEmpty;
|
bool allEmpty;
|
||||||
|
|
||||||
// AP - added a caching system for Chunk::rebuild to take advantage of
|
// AP - added a caching system for Chunk::rebuild to take advantage of
|
||||||
int xcCached, zcCached;
|
int xcCached, zcCached;
|
||||||
unsigned char *CachedTiles;
|
std::unique_ptr<unsigned char[]> CachedTiles;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2, int r);
|
Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2, int r);
|
||||||
virtual ~Region();
|
virtual ~Region();
|
||||||
|
|
||||||
|
// Non-copyable/movable: flatChunks may point to internal stack buffer
|
||||||
|
Region(const Region&) = delete;
|
||||||
|
Region& operator=(const Region&) = delete;
|
||||||
|
Region(Region&&) = delete;
|
||||||
|
Region& operator=(Region&&) = delete;
|
||||||
|
|
||||||
bool isAllEmpty();
|
bool isAllEmpty();
|
||||||
int getTile(int x, int y, int z);
|
int getTile(int x, int y, int z);
|
||||||
shared_ptr<TileEntity> getTileEntity(int x, int y, int z);
|
shared_ptr<TileEntity> getTileEntity(int x, int y, int z);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#ifdef _WINDOWS64
|
#ifdef _WINDOWS64
|
||||||
#define _HAS_STD_BYTE 0 // solve (std::)'byte' ambiguity with windows headers
|
#define _HAS_STD_BYTE 0 // solve (std::)'byte' ambiguity with windows headers
|
||||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
#define NOMINMAX // Exclude min/max macros from Windows headers
|
||||||
// Windows Header Files:
|
// Windows Header Files:
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
|
@ -105,6 +106,7 @@ typedef XUID GameSessionUID;
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#ifndef __PS3__ // the PS3 lib assert is rubbish, and aborts the code, we define our own in PS3Types.h
|
#ifndef __PS3__ // the PS3 lib assert is rubbish, and aborts the code, we define our own in PS3Types.h
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "lce_filesystem.h"
|
#include "lce_filesystem.h"
|
||||||
|
|
||||||
#ifdef _WINDOWS64
|
|
||||||
#include <windows.h>
|
|
||||||
#endif // TODO: More os' filesystem handling for when the project moves away from only Windows
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
bool FileOrDirectoryExists(const char* path)
|
bool FileOrDirectoryExists(const char* path)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue