aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/Chunk.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/Chunk.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.Client/Chunk.cpp')
-rw-r--r--Minecraft.Client/Chunk.cpp1045
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(&region);
+
+ 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(&region, 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(&region, 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);
+}