diff options
| author | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
|---|---|---|
| committer | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
| commit | b691c43c44ff180d10e7d4a9afc83b98551ff586 (patch) | |
| tree | 3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/Chunk.cpp | |
| parent | def8cb415354ac390b7e89052a50605285f1aca9 (diff) | |
Initial commit
Diffstat (limited to 'Minecraft.Client/Chunk.cpp')
| -rw-r--r-- | Minecraft.Client/Chunk.cpp | 1045 |
1 files changed, 1045 insertions, 0 deletions
diff --git a/Minecraft.Client/Chunk.cpp b/Minecraft.Client/Chunk.cpp new file mode 100644 index 00000000..99933db3 --- /dev/null +++ b/Minecraft.Client/Chunk.cpp @@ -0,0 +1,1045 @@ +#include "stdafx.h" +#include "Chunk.h" +#include "TileRenderer.h" +#include "TileEntityRenderDispatcher.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.h" +#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" +#include "LevelRenderer.h" + +#ifdef __PS3__ +#include "PS3\SPU_Tasks\ChunkUpdate\ChunkRebuildData.h" +#include "PS3\SPU_Tasks\ChunkUpdate\TileRenderer_SPU.h" +#include "PS3\SPU_Tasks\CompressedTile\CompressedTileStorage_SPU.h" + +#include "C4JThread_SPU.h" +#include "C4JSpursJob.h" +#endif + +int Chunk::updates = 0; + +#ifdef _LARGE_WORLDS +DWORD Chunk::tlsIdx = TlsAlloc(); + +void Chunk::CreateNewThreadStorage() +{ + unsigned char *tileIds = new unsigned char[16 * 16 * Level::maxBuildHeight]; + TlsSetValue(tlsIdx, tileIds); +} + +void Chunk::ReleaseThreadStorage() +{ + unsigned char *tileIds = (unsigned char *)TlsGetValue(tlsIdx); + delete tileIds; +} + +unsigned char *Chunk::GetTileIdsStorage() +{ + unsigned char *tileIds = (unsigned char *)TlsGetValue(tlsIdx); + return tileIds; +} +#else +// 4J Stu - Don't want this when multi-threaded +Tesselator *Chunk::t = Tesselator::getInstance(); +#endif +LevelRenderer *Chunk::levelRenderer; + +// TODO - 4J see how input entity vector is set up and decide what way is best to pass this to the function +Chunk::Chunk(Level *level, LevelRenderer::rteMap &globalRenderableTileEntities, CRITICAL_SECTION& globalRenderableTileEntities_cs, int x, int y, int z, ClipChunk *clipChunk) + : globalRenderableTileEntities( &globalRenderableTileEntities ), globalRenderableTileEntities_cs(&globalRenderableTileEntities_cs) +{ + clipChunk->visible = false; + bb = NULL; + id = 0; + + this->level = level; + //this->globalRenderableTileEntities = globalRenderableTileEntities; + + assigned = false; + this->clipChunk = clipChunk; + setPos(x, y, z); +} + +void Chunk::setPos(int x, int y, int z) +{ + if(assigned && (x == this->x && y == this->y && z == this->z)) return; + + reset(); + + this->x = x; + this->y = y; + this->z = z; + xm = x + XZSIZE / 2; + ym = y + SIZE / 2; + zm = z + XZSIZE / 2; + clipChunk->xm = xm; + clipChunk->ym = ym; + clipChunk->zm = zm; + + clipChunk->globalIdx = LevelRenderer::getGlobalIndexForChunk(x, y, z, level); + +#if 1 + // 4J - we're not using offsetted renderlists anymore, so just set the full position of this chunk into x/y/zRenderOffs where + // it will be used directly in the renderlist of this chunk + xRenderOffs = x; + yRenderOffs = y; + zRenderOffs = z; + xRender = 0; + yRender = 0; + zRender = 0; +#else + xRenderOffs = x & 1023; + yRenderOffs = y; + zRenderOffs = z & 1023; + xRender = x - xRenderOffs; + yRender = y - yRenderOffs; + zRender = z - zRenderOffs; +#endif + + float g = 6.0f; + // 4J - changed to just set the value rather than make a new one, if we've already created storage + if( bb == NULL ) + { + bb = AABB::newPermanent(-g, -g, -g, XZSIZE+g, SIZE+g, XZSIZE+g); + } + else + { + // 4J MGH - bounds are relative to the position now, so the AABB will be setup already, either above, or from the tesselator bounds. +// bb->set(-g, -g, -g, SIZE+g, SIZE+g, SIZE+g); + } + clipChunk->aabb[0] = bb->x0 + x; + clipChunk->aabb[1] = bb->y0 + y; + clipChunk->aabb[2] = bb->z0 + z; + clipChunk->aabb[3] = bb->x1 + x; + clipChunk->aabb[4] = bb->y1 + y; + clipChunk->aabb[5] = bb->z1 + z; + + assigned = true; + + EnterCriticalSection(&levelRenderer->m_csDirtyChunks); + unsigned char refCount = levelRenderer->incGlobalChunkRefCount(x, y, z, level); +// printf("\t\t [inc] refcount %d at %d, %d, %d\n",refCount,x,y,z); + +// int idx = levelRenderer->getGlobalIndexForChunk(x, y, z, level); + + // If we're the first thing to be referencing this, mark it up as dirty to get rebuilt + if( refCount == 1 ) + { +// printf("Setting %d %d %d dirty [%d]\n",x,y,z, idx); + // Chunks being made dirty in this way can be very numerous (eg the full visible area of the world at start up, or a whole edge of the world when moving). + // On account of this, don't want to stick them into our lock free queue that we would normally use for letting the render update thread know about this chunk. + // Instead, just set the flag to say this is dirty, and then pass a special value of 1 through to the lock free stack which lets that thread know that at least + // one chunk other than the ones in the stack itself have been made dirty. + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY ); +#ifdef _XBOX + PIXSetMarker(0,"Non-stack event pushed"); +#else + PIXSetMarkerDeprecated(0,"Non-stack event pushed"); +#endif + } + + LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); + + +} + +void Chunk::translateToPos() +{ + glTranslatef((float)xRenderOffs, (float)yRenderOffs, (float)zRenderOffs); +} + + +Chunk::Chunk() +{ +} + +void Chunk::makeCopyForRebuild(Chunk *source) +{ + this->level = source->level; + this->x = source->x; + this->y = source->y; + this->z = source->z; + this->xRender = source->xRender; + this->yRender = source->yRender; + this->zRender = source->zRender; + this->xRenderOffs = source->xRenderOffs; + this->yRenderOffs = source->yRenderOffs; + this->zRenderOffs = source->zRenderOffs; + this->xm = source->xm; + this->ym = source->ym; + this->zm = source->zm; + this->bb = source->bb; + this->clipChunk = NULL; + this->id = source->id; + this->globalRenderableTileEntities = source->globalRenderableTileEntities; + this->globalRenderableTileEntities_cs = source->globalRenderableTileEntities_cs; +} + +void Chunk::rebuild() +{ + PIXBeginNamedEvent(0,"Rebuilding chunk %d, %d, %d", x, y, z); +#if defined __PS3__ && !defined DISABLE_SPU_CODE + rebuild_SPU(); + return; +#endif // __PS3__ + +// if (!dirty) return; + PIXBeginNamedEvent(0,"Rebuild section A"); + +#ifdef _LARGE_WORLDS + Tesselator *t = Tesselator::getInstance(); +#else + Chunk::t = Tesselator::getInstance(); // 4J - added - static initialiser being set at the wrong time +#endif + + updates++; + + int x0 = x; + int y0 = y; + int z0 = z; + int x1 = x + XZSIZE; + int y1 = y + SIZE; + int z1 = z + XZSIZE; + + LevelChunk::touchedSky = false; + +// unordered_set<shared_ptr<TileEntity> > oldTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); // 4J removed this & next line +// renderableTileEntities.clear(); + + vector<shared_ptr<TileEntity> > renderableTileEntities; // 4J - added + + int r = 1; + + int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; + lists += levelRenderer->chunkLists; + + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Rebuild section B"); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // 4J - optimisation begins. + + // Get the data for the level chunk that this render chunk is it (level chunk is 16 x 16 x 128, + // render chunk is 16 x 16 x 16. We wouldn't have to actually get all of it if the data was ordered differently, but currently + // it is ordered by x then z then y so just getting a small range of y out of it would involve getting the whole thing into + // the cache anyway. + +#ifdef _LARGE_WORLDS + unsigned char *tileIds = GetTileIdsStorage(); +#else + static unsigned char tileIds[16 * 16 * Level::maxBuildHeight]; +#endif + 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 + + LevelSource *region = new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r); + TileRenderer *tileRenderer = new TileRenderer(region, 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*)region)->setCachedTiles(tileIds, xc, zc); +#endif + + // 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 + // (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 + // of this function fall into this category. By far the largest category of these are tiles in solid regions of rock. + bool empty = true; + for( int yy = y0; yy < y1; yy++ ) + { + for( int zz = 0; zz < 16; zz++ ) + { + for( int xx = 0; xx < 16; xx++ ) + { + // 4J Stu - tile data is ordered in 128 blocks of full width, lower 128 then upper 128 + int indexY = yy; + int offset = 0; + if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + + unsigned char tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ]; + if( tileId > 0 ) empty = false; + + // Don't bother trying to work out neighbours for this tile if we are at the edge of the chunk - apart from the very + // bottom of the world where we shouldn't ever be able to see + if( yy == (Level::maxBuildHeight - 1) ) continue; + if(( xx == 0 ) || ( xx == 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 + // been determined to meet this criteria themselves and have a tile of 255 set. + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + tileId = tileIds[ offset + ( ( ( xx - 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + tileId = tileIds[ offset + ( ( ( xx + 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ]; + if( !( ( tileId == Tile::rock_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::rock_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::rock_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 + // if they are surrounded on sides other than the bottom + if( yy > 0 ) + { + int indexYMinusOne = yy - 1; + int yMinusOneOffset = 0; + if(indexYMinusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexYMinusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = tileIds[ yMinusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYMinusOne ) ]; + if( !( ( tileId == Tile::rock_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; + } + int indexYPlusOne = yy + 1; + int yPlusOneOffset = 0; + if(indexYPlusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = tileIds[ yPlusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYPlusOne ) ]; + if( !( ( tileId == Tile::rock_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. + tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ] = 0xff; + } + } + } + PIXEndNamedEvent(); + // Nothing at all to do for this chunk? + if( empty ) + { + // 4J - added - clear any renderer data associated with this + for (int currentLayer = 0; currentLayer < 2; currentLayer++) + { + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + RenderManager.CBuffClear(lists + currentLayer); + } + + delete region; + delete tileRenderer; + return; + } + // 4J - optimisation ends + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + PIXBeginNamedEvent(0,"Rebuild section C"); + Tesselator::Bounds bounds; // 4J MGH - added + { + // this was the old default clip bounds for the chunk, set in Chunk::setPos. + float g = 6.0f; + bounds.boundingBox[0] = -g; + bounds.boundingBox[1] = -g; + bounds.boundingBox[2] = -g; + bounds.boundingBox[3] = XZSIZE+g; + bounds.boundingBox[4] = SIZE+g; + bounds.boundingBox[5] = XZSIZE+g; + } + for (int currentLayer = 0; currentLayer < 2; currentLayer++) + { + bool renderNextLayer = false; + bool rendered = false; + + bool started = false; + + // 4J - changed loop order here to leave y as the innermost loop for better cache performance + for (int z = z0; z < z1; z++) + { + for (int x = x0; x < x1; x++) + { + for (int y = y0; y < y1; y++) + { + // 4J Stu - tile data is ordered in 128 blocks of full width, lower 128 then upper 128 + int indexY = y; + int offset = 0; + if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + + // 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) ]; + // If flagged as not visible, drop out straight away + if( tileId == 0xff ) continue; +// int tileId = region->getTile(x,y,z); + if (tileId > 0) + { + if (!started) + { + started = true; + + MemSect(31); + glNewList(lists + currentLayer, GL_COMPILE); + MemSect(0); + glPushMatrix(); + glDepthMask(true); // 4J added + t->useCompactVertices(true); // 4J added + translateToPos(); + float ss = 1.000001f; + // 4J - have removed this scale as I don't think we should need it, and have now optimised the vertex + // shader so it doesn't do anything other than translate with this matrix anyway +#if 0 + glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); + glScalef(ss, ss, ss); + glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); +#endif + t->begin(); + t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); + } + + Tile *tile = Tile::tiles[tileId]; + if (currentLayer == 0 && tile->isEntityTile()) + { + shared_ptr<TileEntity> et = region->getTileEntity(x, y, z); + if (TileEntityRenderDispatcher::instance->hasRenderer(et)) + { + renderableTileEntities.push_back(et); + } + } + int renderLayer = tile->getRenderLayer(); + + if (renderLayer != currentLayer) + { + renderNextLayer = true; + } + else if (renderLayer == currentLayer) + { + rendered |= tileRenderer->tesselateInWorld(tile, x, y, z); + } + } + } + } + } + +#ifdef __PSVITA__ + if( currentLayer==0 ) + { + levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_CUT_OUT); + } +#endif + + if (started) + { +#ifdef __PSVITA__ + // AP - make sure we don't attempt to render chunks without cutout geometry + if( t->getCutOutFound() ) + { + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_CUT_OUT); + } +#endif + t->end(); + bounds.addBounds(t->bounds); // 4J MGH - added + glPopMatrix(); + glEndList(); + t->useCompactVertices(false); // 4J added + t->offset(0, 0, 0); + } + else + { + rendered = false; + } + + if (rendered) + { + levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + } + else + { + // 4J - added - clear any renderer data associated with this unused list + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + RenderManager.CBuffClear(lists + currentLayer); + } + if((currentLayer==0)&&(!renderNextLayer)) + { + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY1); + RenderManager.CBuffClear(lists + 1); + break; + } + } + + // 4J MGH - added this to take the bound from the value calc'd in the tesselator + if( bb ) + { + bb->set(bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], + bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]); + } + + delete tileRenderer; + delete region; + + PIXEndNamedEvent(); + 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 + // stored globally in the levelrenderer, in a hashmap with a special key made up from the dimension and chunk position (using same index + // as is used for global flags) +#if 1 + int key = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level); + EnterCriticalSection(globalRenderableTileEntities_cs); + if( renderableTileEntities.size() ) + { + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + // We've got some renderable tile entities that we want associated with this chunk, and an existing list of things that used to be. + // We need to flag any that we don't need any more to be removed, keep those that we do, and add any new ones + + // First pass - flag everything already existing to be removed + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + + // Now go through the current list. If these are already in the list, then unflag the remove flag. If they aren't, then add + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + AUTO_VAR(it2, find( it->second.begin(), it->second.end(), renderableTileEntities[i] )); + if( it2 == it->second.end() ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + else + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageKeep); + } + } + } + else + { + // Easy case - nothing already existing for this chunk. Add them all in. + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + } + } + else + { + // Another easy case - we don't want any renderable tile entities associated with this chunk. Flag all to be removed. + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + } + } + LeaveCriticalSection(globalRenderableTileEntities_cs); + PIXEndNamedEvent(); +#else + // Find the removed ones: + + // 4J - original code for this section: + /* + Set<TileEntity> newTileEntities = new HashSet<TileEntity>(); + newTileEntities.addAll(renderableTileEntities); + newTileEntities.removeAll(oldTileEntities); + globalRenderableTileEntities.addAll(newTileEntities); + + oldTileEntities.removeAll(renderableTileEntities); + globalRenderableTileEntities.removeAll(oldTileEntities); + */ + + + unordered_set<shared_ptr<TileEntity> > newTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); + + AUTO_VAR(endIt, oldTileEntities.end()); + for( unordered_set<shared_ptr<TileEntity> >::iterator it = oldTileEntities.begin(); it != endIt; it++ ) + { + newTileEntities.erase(*it); + } + + // 4J - newTileEntities is now renderableTileEntities with any old ones from oldTileEntitesRemoved (so just new things added) + + EnterCriticalSection(globalRenderableTileEntities_cs); + endIt = newTileEntities.end(); + for( unordered_set<shared_ptr<TileEntity> >::iterator it = newTileEntities.begin(); it != endIt; it++ ) + { + globalRenderableTileEntities->push_back(*it); + } + + // 4J - All these new things added to globalRenderableTileEntities + + AUTO_VAR(endItRTE, renderableTileEntities.end()); + for( vector<shared_ptr<TileEntity> >::iterator it = renderableTileEntities.begin(); it != endItRTE; it++ ) + { + oldTileEntities.erase(*it); + } + // 4J - oldTileEntities is now the removed items + vector<shared_ptr<TileEntity> >::iterator it = globalRenderableTileEntities->begin(); + while( it != globalRenderableTileEntities->end() ) + { + if( oldTileEntities.find(*it) != oldTileEntities.end() ) + { + it = globalRenderableTileEntities->erase(it); + } + else + { + ++it; + } + } + + LeaveCriticalSection(globalRenderableTileEntities_cs); +#endif + + // 4J - These removed items are now also removed from globalRenderableTileEntities + + if( LevelChunk::touchedSky ) + { + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + else + { + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED); + PIXEndNamedEvent(); + return; + +} + + +#ifdef __PS3__ +ChunkRebuildData g_rebuildDataIn __attribute__((__aligned__(16))); +ChunkRebuildData g_rebuildDataOut __attribute__((__aligned__(16))); +TileCompressData_SPU g_tileCompressDataIn __attribute__((__aligned__(16))); +unsigned char* g_tileCompressDataOut = (unsigned char*)&g_rebuildDataIn.m_tileIds; + +#define TILE_RENDER_SPU + + +void RunSPURebuild() +{ + + static C4JSpursJobQueue::Port p("C4JSpursJob_ChunkUpdate"); + C4JSpursJob_CompressedTile tileJob(&g_tileCompressDataIn,g_tileCompressDataOut); + C4JSpursJob_ChunkUpdate chunkJob(&g_rebuildDataIn, &g_rebuildDataOut); + + if(g_rebuildDataIn.m_currentLayer == 0) // only need to create the tiles on the first layer + { + p.submitJob(&tileJob); + p.submitSync(); + } + + p.submitJob(&chunkJob); + p.waitForCompletion(); + + assert(g_rebuildDataIn.m_x0 == g_rebuildDataOut.m_x0); +} + +void Chunk::rebuild_SPU() +{ + +// if (!dirty) return; + Chunk::t = Tesselator::getInstance(); // 4J - added - static initialiser being set at the wrong time + updates++; + + int x0 = x; + int y0 = y; + int z0 = z; + int x1 = x + SIZE; + int y1 = y + SIZE; + int z1 = z + SIZE; + + LevelChunk::touchedSky = false; + +// unordered_set<shared_ptr<TileEntity> > oldTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); // 4J removed this & next line +// renderableTileEntities.clear(); + + vector<shared_ptr<TileEntity> > renderableTileEntities; // 4J - added + +// List<TileEntity> newTileEntities = new ArrayList<TileEntity>(); +// newTileEntities.clear(); +// renderableTileEntities.clear(); + + int r = 1; + + Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r); + TileRenderer tileRenderer(®ion); + + int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; + lists += levelRenderer->chunkLists; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // 4J - optimisation begins. + + // Get the data for the level chunk that this render chunk is it (level chunk is 16 x 16 x 128, + // render chunk is 16 x 16 x 16. We wouldn't have to actually get all of it if the data was ordered differently, but currently + // it is ordered by x then z then y so just getting a small range of y out of it would involve getting the whole thing into + // the cache anyway. + ChunkRebuildData* pOutData = NULL; + g_rebuildDataIn.buildForChunk(®ion, level, x0, y0, z0); + + Tesselator::Bounds bounds; + { + // this was the old default clip bounds for the chunk, set in Chunk::setPos. + float g = 6.0f; + bounds.boundingBox[0] = -g; + bounds.boundingBox[1] = -g; + bounds.boundingBox[2] = -g; + bounds.boundingBox[3] = SIZE+g; + bounds.boundingBox[4] = SIZE+g; + bounds.boundingBox[5] = SIZE+g; + } + + for (int currentLayer = 0; currentLayer < 2; currentLayer++) + { + bool rendered = false; + + { + glNewList(lists + currentLayer, GL_COMPILE); + MemSect(0); + glPushMatrix(); + glDepthMask(true); // 4J added + t->useCompactVertices(true); // 4J added + translateToPos(); + float ss = 1.000001f; + // 4J - have removed this scale as I don't think we should need it, and have now optimised the vertex + // shader so it doesn't do anything other than translate with this matrix anyway + #if 0 + glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); + glScalef(ss, ss, ss); + glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); + #endif + t->begin(); + t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); + } + + g_rebuildDataIn.copyFromTesselator(); + intArray_SPU tesselatorArray((unsigned int*)g_rebuildDataIn.m_tesselator.m_PPUArray); + g_rebuildDataIn.m_tesselator._array = &tesselatorArray; + g_rebuildDataIn.m_currentLayer = currentLayer; + #ifdef TILE_RENDER_SPU + g_tileCompressDataIn.setForChunk(®ion, x0, y0, z0); + RunSPURebuild(); + g_rebuildDataOut.storeInTesselator(); + pOutData = &g_rebuildDataOut; + #else + g_rebuildDataIn.disableUnseenTiles(); + TileRenderer_SPU *pTileRenderer = new TileRenderer_SPU(&g_rebuildDataIn); + g_rebuildDataIn.tesselateAllTiles(pTileRenderer); + g_rebuildDataIn.storeInTesselator(); + pOutData = &g_rebuildDataIn; + #endif + if(pOutData->m_flags & ChunkRebuildData::e_flag_Rendered) + rendered = true; + + // 4J - changed loop order here to leave y as the innermost loop for better cache performance + for (int z = z0; z < z1; z++) + { + for (int x = x0; x < x1; x++) + { + for (int y = y0; y < y1; y++) + { + // 4J - get tile from those copied into our local array in earlier optimisation + unsigned char tileId = pOutData->getTile(x,y,z); + if (tileId > 0) + { + if (currentLayer == 0 && Tile::tiles[tileId]->isEntityTile()) + { + shared_ptr<TileEntity> et = region.getTileEntity(x, y, z); + if (TileEntityRenderDispatcher::instance->hasRenderer(et)) + { + renderableTileEntities.push_back(et); + } + } + int flags = pOutData->getFlags(x,y,z); + if(flags & ChunkRebuildData::e_flag_SPURenderCodeMissing) + { + + Tile *tile = Tile::tiles[tileId]; + int renderLayer = tile->getRenderLayer(); + + if (renderLayer != currentLayer) + { + // renderNextLayer = true; + } + else if (renderLayer == currentLayer) + { + //if(currentLayer == 0) + // numRenderedLayer0++; + rendered |= tileRenderer.tesselateInWorld(tile, x, y, z); + } + } + } + } + } + } + + + { + t->end(); + bounds.addBounds(t->bounds); + glPopMatrix(); + glEndList(); + t->useCompactVertices(false); // 4J added + t->offset(0, 0, 0); + } + if (rendered) + { + levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + } + else + { + // 4J - added - clear any renderer data associated with this unused list + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); + RenderManager.CBuffClear(lists + currentLayer); + } + + } + + if( bb ) + { + bb->set(bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], + bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]); + } + + + if(pOutData->m_flags & ChunkRebuildData::e_flag_TouchedSky) + LevelChunk::touchedSky = true; + + + // 4J - have rewritten the way that tile entities are stored globally to make it work more easily with split screen. Chunks are now + // stored globally in the levelrenderer, in a hashmap with a special key made up from the dimension and chunk position (using same index + // as is used for global flags) +#if 1 + int key = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level); + EnterCriticalSection(globalRenderableTileEntities_cs); + if( renderableTileEntities.size() ) + { + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + // We've got some renderable tile entities that we want associated with this chunk, and an existing list of things that used to be. + // We need to flag any that we don't need any more to be removed, keep those that we do, and add any new ones + + // First pass - flag everything already existing to be removed + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + + // Now go through the current list. If these are already in the list, then unflag the remove flag. If they aren't, then add + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + AUTO_VAR(it2, find( it->second.begin(), it->second.end(), renderableTileEntities[i] )); + if( it2 == it->second.end() ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + else + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageKeep); + } + } + } + else + { + // Easy case - nothing already existing for this chunk. Add them all in. + for( int i = 0; i < renderableTileEntities.size(); i++ ) + { + (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); + } + } + } + else + { + // Another easy case - we don't want any renderable tile entities associated with this chunk. Flag all to be removed. + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if( it != globalRenderableTileEntities->end() ) + { + for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) + { + (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); + } + } + } + LeaveCriticalSection(globalRenderableTileEntities_cs); +#else + // Find the removed ones: + + // 4J - original code for this section: + /* + Set<TileEntity> newTileEntities = new HashSet<TileEntity>(); + newTileEntities.addAll(renderableTileEntities); + newTileEntities.removeAll(oldTileEntities); + globalRenderableTileEntities.addAll(newTileEntities); + + oldTileEntities.removeAll(renderableTileEntities); + globalRenderableTileEntities.removeAll(oldTileEntities); + */ + + + unordered_set<shared_ptr<TileEntity> > newTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); + + AUTO_VAR(endIt, oldTileEntities.end()); + for( unordered_set<shared_ptr<TileEntity> >::iterator it = oldTileEntities.begin(); it != endIt; it++ ) + { + newTileEntities.erase(*it); + } + + // 4J - newTileEntities is now renderableTileEntities with any old ones from oldTileEntitesRemoved (so just new things added) + + EnterCriticalSection(globalRenderableTileEntities_cs); + endIt = newTileEntities.end(); + for( unordered_set<shared_ptr<TileEntity> >::iterator it = newTileEntities.begin(); it != endIt; it++ ) + { + globalRenderableTileEntities.push_back(*it); + } + + // 4J - All these new things added to globalRenderableTileEntities + + AUTO_VAR(endItRTE, renderableTileEntities.end()); + for( vector<shared_ptr<TileEntity> >::iterator it = renderableTileEntities.begin(); it != endItRTE; it++ ) + { + oldTileEntities.erase(*it); + } + // 4J - oldTileEntities is now the removed items + vector<shared_ptr<TileEntity> >::iterator it = globalRenderableTileEntities->begin(); + while( it != globalRenderableTileEntities->end() ) + { + if( oldTileEntities.find(*it) != oldTileEntities.end() ) + { + it = globalRenderableTileEntities->erase(it); + } + else + { + ++it; + } + } + + LeaveCriticalSection(globalRenderableTileEntities_cs); +#endif + + // 4J - These removed items are now also removed from globalRenderableTileEntities + + if( LevelChunk::touchedSky ) + { + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + else + { + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); + } + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED); + return; + +} +#endif // _PS3_ + + +float Chunk::distanceToSqr(shared_ptr<Entity> player) const +{ + float xd = (float) (player->x - xm); + float yd = (float) (player->y - ym); + float zd = (float) (player->z - zm); + return xd * xd + yd * yd + zd * zd; +} + +float Chunk::squishedDistanceToSqr(shared_ptr<Entity> player) +{ + float xd = (float) (player->x - xm); + float yd = (float) (player->y - ym) * 2; + float zd = (float) (player->z - zm); + return xd * xd + yd * yd + zd * zd; +} + +void Chunk::reset() +{ + if( assigned ) + { + EnterCriticalSection(&levelRenderer->m_csDirtyChunks); + unsigned char refCount = levelRenderer->decGlobalChunkRefCount(x, y, z, level); + assigned = false; +// printf("\t\t [dec] refcount %d at %d, %d, %d\n",refCount,x,y,z); + if( refCount == 0 ) + { + int lists = levelRenderer->getGlobalIndexForChunk(x, y, z, level) * 2; + if(lists >= 0) + { + lists += levelRenderer->chunkLists; + for (int i = 0; i < 2; i++) + { + // 4J - added - clear any renderer data associated with this unused list + RenderManager.CBuffClear(lists + i); + } + levelRenderer->setGlobalChunkFlags(x, y, z, level, 0); + } + } + LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); + } + + clipChunk->visible = false; +} + +void Chunk::_delete() +{ + reset(); + level = NULL; +} + +int Chunk::getList(int layer) +{ + if (!clipChunk->visible) return -1; + + int lists = levelRenderer->getGlobalIndexForChunk(x, y, z,level) * 2; + lists += levelRenderer->chunkLists; + + bool empty = levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); + if (!empty) return lists + layer; + return -1; +} + +void Chunk::cull(Culler *culler) +{ + clipChunk->visible = culler->isVisible(bb); +} + +void Chunk::renderBB() +{ +// glCallList(lists + 2); // 4J - removed - TODO put back in +} + +bool Chunk::isEmpty() +{ + if (!levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED)) return false; + return levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTYBOTH); +} + +void Chunk::setDirty() +{ + // 4J - not used, but if this starts being used again then we'll need to investigate how best to handle it. + __debugbreak(); + levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY); +} + +void Chunk::clearDirty() +{ + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY); +#ifdef _CRITICAL_CHUNKS + levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_CRITICAL); +#endif +} + +Chunk::~Chunk() +{ + delete bb; +} + +bool Chunk::emptyFlagSet(int layer) +{ + return levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); +} |
