Something went wrong. Try again.
the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
Something went wrong. Try again.
35 kB · 1038 lines
C++
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039#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"//#define DISABLE_SPU_CODE
#endif
int Chunk::updates = 0;
#ifdef _LARGE_WORLDSDWORD 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-threadedTesselator *Chunk::t = Tesselator::getInstance();#endifLevelRenderer *Chunk::levelRenderer;
// TODO - 4J see how input entity vector is set up and decide what way is best to pass this to the functionChunk::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, 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::stone_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::stone_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::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 // 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::stone_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::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. 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;
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, 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; g_tileCompressDataIn.setForChunk(®ion, x0, y0, z0); RunSPURebuild(); g_rebuildDataOut.storeInTesselator(); pOutData = &g_rebuildDataOut;
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);}