the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 1038 lines 35 kB view raw
1#include "stdafx.h" 2#include "Chunk.h" 3#include "TileRenderer.h" 4#include "TileEntityRenderDispatcher.h" 5#include "..\Minecraft.World\net.minecraft.world.level.h" 6#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" 7#include "..\Minecraft.World\net.minecraft.world.level.tile.h" 8#include "..\Minecraft.World\net.minecraft.world.level.tile.entity.h" 9#include "LevelRenderer.h" 10 11#ifdef __PS3__ 12#include "PS3\SPU_Tasks\ChunkUpdate\ChunkRebuildData.h" 13#include "PS3\SPU_Tasks\ChunkUpdate\TileRenderer_SPU.h" 14#include "PS3\SPU_Tasks\CompressedTile\CompressedTileStorage_SPU.h" 15 16#include "C4JThread_SPU.h" 17#include "C4JSpursJob.h" 18//#define DISABLE_SPU_CODE 19 20#endif 21 22int Chunk::updates = 0; 23 24#ifdef _LARGE_WORLDS 25DWORD Chunk::tlsIdx = TlsAlloc(); 26 27void Chunk::CreateNewThreadStorage() 28{ 29 unsigned char *tileIds = new unsigned char[16 * 16 * Level::maxBuildHeight]; 30 TlsSetValue(tlsIdx, tileIds); 31} 32 33void Chunk::ReleaseThreadStorage() 34{ 35 unsigned char *tileIds = (unsigned char *)TlsGetValue(tlsIdx); 36 delete tileIds; 37} 38 39unsigned char *Chunk::GetTileIdsStorage() 40{ 41 unsigned char *tileIds = (unsigned char *)TlsGetValue(tlsIdx); 42 return tileIds; 43} 44#else 45// 4J Stu - Don't want this when multi-threaded 46Tesselator *Chunk::t = Tesselator::getInstance(); 47#endif 48LevelRenderer *Chunk::levelRenderer; 49 50// TODO - 4J see how input entity vector is set up and decide what way is best to pass this to the function 51Chunk::Chunk(Level *level, LevelRenderer::rteMap &globalRenderableTileEntities, CRITICAL_SECTION& globalRenderableTileEntities_cs, int x, int y, int z, ClipChunk *clipChunk) 52 : globalRenderableTileEntities( &globalRenderableTileEntities ), globalRenderableTileEntities_cs(&globalRenderableTileEntities_cs) 53{ 54 clipChunk->visible = false; 55 bb = NULL; 56 id = 0; 57 58 this->level = level; 59 //this->globalRenderableTileEntities = globalRenderableTileEntities; 60 61 assigned = false; 62 this->clipChunk = clipChunk; 63 setPos(x, y, z); 64} 65 66void Chunk::setPos(int x, int y, int z) 67{ 68 if(assigned && (x == this->x && y == this->y && z == this->z)) return; 69 70 reset(); 71 72 this->x = x; 73 this->y = y; 74 this->z = z; 75 xm = x + XZSIZE / 2; 76 ym = y + SIZE / 2; 77 zm = z + XZSIZE / 2; 78 clipChunk->xm = xm; 79 clipChunk->ym = ym; 80 clipChunk->zm = zm; 81 82 clipChunk->globalIdx = LevelRenderer::getGlobalIndexForChunk(x, y, z, level); 83 84#if 1 85 // 4J - we're not using offsetted renderlists anymore, so just set the full position of this chunk into x/y/zRenderOffs where 86 // it will be used directly in the renderlist of this chunk 87 xRenderOffs = x; 88 yRenderOffs = y; 89 zRenderOffs = z; 90 xRender = 0; 91 yRender = 0; 92 zRender = 0; 93#else 94 xRenderOffs = x & 1023; 95 yRenderOffs = y; 96 zRenderOffs = z & 1023; 97 xRender = x - xRenderOffs; 98 yRender = y - yRenderOffs; 99 zRender = z - zRenderOffs; 100#endif 101 102 float g = 6.0f; 103 // 4J - changed to just set the value rather than make a new one, if we've already created storage 104 if( bb == NULL ) 105 { 106 bb = AABB::newPermanent(-g, -g, -g, XZSIZE+g, SIZE+g, XZSIZE+g); 107 } 108 else 109 { 110 // 4J MGH - bounds are relative to the position now, so the AABB will be setup already, either above, or from the tesselator bounds. 111// bb->set(-g, -g, -g, SIZE+g, SIZE+g, SIZE+g); 112 } 113 clipChunk->aabb[0] = bb->x0 + x; 114 clipChunk->aabb[1] = bb->y0 + y; 115 clipChunk->aabb[2] = bb->z0 + z; 116 clipChunk->aabb[3] = bb->x1 + x; 117 clipChunk->aabb[4] = bb->y1 + y; 118 clipChunk->aabb[5] = bb->z1 + z; 119 120 assigned = true; 121 122 EnterCriticalSection(&levelRenderer->m_csDirtyChunks); 123 unsigned char refCount = levelRenderer->incGlobalChunkRefCount(x, y, z, level); 124// printf("\t\t [inc] refcount %d at %d, %d, %d\n",refCount,x,y,z); 125 126// int idx = levelRenderer->getGlobalIndexForChunk(x, y, z, level); 127 128 // If we're the first thing to be referencing this, mark it up as dirty to get rebuilt 129 if( refCount == 1 ) 130 { 131// printf("Setting %d %d %d dirty [%d]\n",x,y,z, idx); 132 // 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). 133 // 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. 134 // 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 135 // one chunk other than the ones in the stack itself have been made dirty. 136 levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY ); 137#ifdef _XBOX 138 PIXSetMarker(0,"Non-stack event pushed"); 139#else 140 PIXSetMarkerDeprecated(0,"Non-stack event pushed"); 141#endif 142 } 143 144 LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); 145 146 147} 148 149void Chunk::translateToPos() 150{ 151 glTranslatef((float)xRenderOffs, (float)yRenderOffs, (float)zRenderOffs); 152} 153 154 155Chunk::Chunk() 156{ 157} 158 159void Chunk::makeCopyForRebuild(Chunk *source) 160{ 161 this->level = source->level; 162 this->x = source->x; 163 this->y = source->y; 164 this->z = source->z; 165 this->xRender = source->xRender; 166 this->yRender = source->yRender; 167 this->zRender = source->zRender; 168 this->xRenderOffs = source->xRenderOffs; 169 this->yRenderOffs = source->yRenderOffs; 170 this->zRenderOffs = source->zRenderOffs; 171 this->xm = source->xm; 172 this->ym = source->ym; 173 this->zm = source->zm; 174 this->bb = source->bb; 175 this->clipChunk = NULL; 176 this->id = source->id; 177 this->globalRenderableTileEntities = source->globalRenderableTileEntities; 178 this->globalRenderableTileEntities_cs = source->globalRenderableTileEntities_cs; 179} 180 181void Chunk::rebuild() 182{ 183 PIXBeginNamedEvent(0,"Rebuilding chunk %d, %d, %d", x, y, z); 184#if defined __PS3__ && !defined DISABLE_SPU_CODE 185 rebuild_SPU(); 186 return; 187#endif // __PS3__ 188 189// if (!dirty) return; 190 PIXBeginNamedEvent(0,"Rebuild section A"); 191 192#ifdef _LARGE_WORLDS 193 Tesselator *t = Tesselator::getInstance(); 194#else 195 Chunk::t = Tesselator::getInstance(); // 4J - added - static initialiser being set at the wrong time 196#endif 197 198 updates++; 199 200 int x0 = x; 201 int y0 = y; 202 int z0 = z; 203 int x1 = x + XZSIZE; 204 int y1 = y + SIZE; 205 int z1 = z + XZSIZE; 206 207 LevelChunk::touchedSky = false; 208 209// unordered_set<shared_ptr<TileEntity> > oldTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); // 4J removed this & next line 210// renderableTileEntities.clear(); 211 212 vector<shared_ptr<TileEntity> > renderableTileEntities; // 4J - added 213 214 int r = 1; 215 216 int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; 217 lists += levelRenderer->chunkLists; 218 219 PIXEndNamedEvent(); 220 221 PIXBeginNamedEvent(0,"Rebuild section B"); 222 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 223 // 4J - optimisation begins. 224 225 // Get the data for the level chunk that this render chunk is it (level chunk is 16 x 16 x 128, 226 // 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 227 // 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 228 // the cache anyway. 229 230#ifdef _LARGE_WORLDS 231 unsigned char *tileIds = GetTileIdsStorage(); 232#else 233 static unsigned char tileIds[16 * 16 * Level::maxBuildHeight]; 234#endif 235 byteArray tileArray = byteArray(tileIds, 16 * 16 * Level::maxBuildHeight); 236 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 237 238 LevelSource *region = new Region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r); 239 TileRenderer *tileRenderer = new TileRenderer(region, this->x, this->y, this->z, tileIds); 240 241 // AP - added a caching system for Chunk::rebuild to take advantage of 242 // Basically we're storing of copy of the tileIDs array inside the region so that calls to Region::getTile can grab data 243 // more quickly from this array rather than calling CompressedTileStorage. On the Vita the total thread time spent in 244 // Region::getTile went from 20% to 4%. 245#ifdef __PSVITA__ 246 int xc = x >> 4; 247 int zc = z >> 4; 248 ((Region*)region)->setCachedTiles(tileIds, xc, zc); 249#endif 250 251 // We now go through the vertical section of this level chunk that we are interested in and try and establish 252 // (1) if it is completely empty 253 // (2) if any of the tiles can be quickly determined to not need rendering because they are in the middle of other tiles and 254 // so can't be seen. A large amount (> 60% in tests) of tiles that call tesselateInWorld in the unoptimised version 255 // of this function fall into this category. By far the largest category of these are tiles in solid regions of rock. 256 bool empty = true; 257 for( int yy = y0; yy < y1; yy++ ) 258 { 259 for( int zz = 0; zz < 16; zz++ ) 260 { 261 for( int xx = 0; xx < 16; xx++ ) 262 { 263 // 4J Stu - tile data is ordered in 128 blocks of full width, lower 128 then upper 128 264 int indexY = yy; 265 int offset = 0; 266 if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 267 { 268 indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 269 offset = Level::COMPRESSED_CHUNK_SECTION_TILES; 270 } 271 272 unsigned char tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ]; 273 if( tileId > 0 ) empty = false; 274 275 // Don't bother trying to work out neighbours for this tile if we are at the edge of the chunk - apart from the very 276 // bottom of the world where we shouldn't ever be able to see 277 if( yy == (Level::maxBuildHeight - 1) ) continue; 278 if(( xx == 0 ) || ( xx == 15 )) continue; 279 if(( zz == 0 ) || ( zz == 15 )) continue; 280 281 // Establish whether this tile and its neighbours are all made of rock, dirt, unbreakable tiles, or have already 282 // been determined to meet this criteria themselves and have a tile of 255 set. 283 if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; 284 tileId = tileIds[ offset + ( ( ( xx - 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ]; 285 if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; 286 tileId = tileIds[ offset + ( ( ( xx + 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ]; 287 if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; 288 tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz - 1 ) << 7 ) | ( indexY + 0 )) ]; 289 if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; 290 tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 1 ) << 7 ) | ( indexY + 0 )) ]; 291 if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; 292 // Treat the bottom of the world differently - we shouldn't ever be able to look up at this, so consider tiles as invisible 293 // if they are surrounded on sides other than the bottom 294 if( yy > 0 ) 295 { 296 int indexYMinusOne = yy - 1; 297 int yMinusOneOffset = 0; 298 if(indexYMinusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 299 { 300 indexYMinusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 301 yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; 302 } 303 tileId = tileIds[ yMinusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYMinusOne ) ]; 304 if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; 305 } 306 int indexYPlusOne = yy + 1; 307 int yPlusOneOffset = 0; 308 if(indexYPlusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 309 { 310 indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 311 yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; 312 } 313 tileId = tileIds[ yPlusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYPlusOne ) ]; 314 if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue; 315 316 // This tile is surrounded. Flag it as not requiring to be rendered by setting its id to 255. 317 tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ] = 0xff; 318 } 319 } 320 } 321 PIXEndNamedEvent(); 322 // Nothing at all to do for this chunk? 323 if( empty ) 324 { 325 // 4J - added - clear any renderer data associated with this 326 for (int currentLayer = 0; currentLayer < 2; currentLayer++) 327 { 328 levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); 329 RenderManager.CBuffClear(lists + currentLayer); 330 } 331 332 delete region; 333 delete tileRenderer; 334 return; 335 } 336 // 4J - optimisation ends 337 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 338 339 PIXBeginNamedEvent(0,"Rebuild section C"); 340 Tesselator::Bounds bounds; // 4J MGH - added 341 { 342 // this was the old default clip bounds for the chunk, set in Chunk::setPos. 343 float g = 6.0f; 344 bounds.boundingBox[0] = -g; 345 bounds.boundingBox[1] = -g; 346 bounds.boundingBox[2] = -g; 347 bounds.boundingBox[3] = XZSIZE+g; 348 bounds.boundingBox[4] = SIZE+g; 349 bounds.boundingBox[5] = XZSIZE+g; 350 } 351 for (int currentLayer = 0; currentLayer < 2; currentLayer++) 352 { 353 bool renderNextLayer = false; 354 bool rendered = false; 355 356 bool started = false; 357 358 // 4J - changed loop order here to leave y as the innermost loop for better cache performance 359 for (int z = z0; z < z1; z++) 360 { 361 for (int x = x0; x < x1; x++) 362 { 363 for (int y = y0; y < y1; y++) 364 { 365 // 4J Stu - tile data is ordered in 128 blocks of full width, lower 128 then upper 128 366 int indexY = y; 367 int offset = 0; 368 if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) 369 { 370 indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; 371 offset = Level::COMPRESSED_CHUNK_SECTION_TILES; 372 } 373 374 // 4J - get tile from those copied into our local array in earlier optimisation 375 unsigned char tileId = tileIds[ offset + ( ( ( x - x0 ) << 11 ) | ( ( z - z0 ) << 7 ) | indexY) ]; 376 // If flagged as not visible, drop out straight away 377 if( tileId == 0xff ) continue; 378// int tileId = region->getTile(x,y,z); 379 if (tileId > 0) 380 { 381 if (!started) 382 { 383 started = true; 384 385 MemSect(31); 386 glNewList(lists + currentLayer, GL_COMPILE); 387 MemSect(0); 388 glPushMatrix(); 389 glDepthMask(true); // 4J added 390 t->useCompactVertices(true); // 4J added 391 translateToPos(); 392 float ss = 1.000001f; 393 // 4J - have removed this scale as I don't think we should need it, and have now optimised the vertex 394 // shader so it doesn't do anything other than translate with this matrix anyway 395#if 0 396 glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); 397 glScalef(ss, ss, ss); 398 glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); 399#endif 400 t->begin(); 401 t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); 402 } 403 404 Tile *tile = Tile::tiles[tileId]; 405 if (currentLayer == 0 && tile->isEntityTile()) 406 { 407 shared_ptr<TileEntity> et = region->getTileEntity(x, y, z); 408 if (TileEntityRenderDispatcher::instance->hasRenderer(et)) 409 { 410 renderableTileEntities.push_back(et); 411 } 412 } 413 int renderLayer = tile->getRenderLayer(); 414 415 if (renderLayer != currentLayer) 416 { 417 renderNextLayer = true; 418 } 419 else if (renderLayer == currentLayer) 420 { 421 rendered |= tileRenderer->tesselateInWorld(tile, x, y, z); 422 } 423 } 424 } 425 } 426 } 427 428#ifdef __PSVITA__ 429 if( currentLayer==0 ) 430 { 431 levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_CUT_OUT); 432 } 433#endif 434 435 if (started) 436 { 437#ifdef __PSVITA__ 438 // AP - make sure we don't attempt to render chunks without cutout geometry 439 if( t->getCutOutFound() ) 440 { 441 levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_CUT_OUT); 442 } 443#endif 444 t->end(); 445 bounds.addBounds(t->bounds); // 4J MGH - added 446 glPopMatrix(); 447 glEndList(); 448 t->useCompactVertices(false); // 4J added 449 t->offset(0, 0, 0); 450 } 451 else 452 { 453 rendered = false; 454 } 455 456 if (rendered) 457 { 458 levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); 459 } 460 else 461 { 462 // 4J - added - clear any renderer data associated with this unused list 463 levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); 464 RenderManager.CBuffClear(lists + currentLayer); 465 } 466 if((currentLayer==0)&&(!renderNextLayer)) 467 { 468 levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY1); 469 RenderManager.CBuffClear(lists + 1); 470 break; 471 } 472 } 473 474 // 4J MGH - added this to take the bound from the value calc'd in the tesselator 475 if( bb ) 476 { 477 bb->set(bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], 478 bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]); 479 } 480 481 delete tileRenderer; 482 delete region; 483 484 PIXEndNamedEvent(); 485 PIXBeginNamedEvent(0,"Rebuild section D"); 486 487 // 4J - have rewritten the way that tile entities are stored globally to make it work more easily with split screen. Chunks are now 488 // stored globally in the levelrenderer, in a hashmap with a special key made up from the dimension and chunk position (using same index 489 // as is used for global flags) 490#if 1 491 int key = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level); 492 EnterCriticalSection(globalRenderableTileEntities_cs); 493 if( renderableTileEntities.size() ) 494 { 495 AUTO_VAR(it, globalRenderableTileEntities->find(key)); 496 if( it != globalRenderableTileEntities->end() ) 497 { 498 // We've got some renderable tile entities that we want associated with this chunk, and an existing list of things that used to be. 499 // 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 500 501 // First pass - flag everything already existing to be removed 502 for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) 503 { 504 (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); 505 } 506 507 // Now go through the current list. If these are already in the list, then unflag the remove flag. If they aren't, then add 508 for( int i = 0; i < renderableTileEntities.size(); i++ ) 509 { 510 AUTO_VAR(it2, find( it->second.begin(), it->second.end(), renderableTileEntities[i] )); 511 if( it2 == it->second.end() ) 512 { 513 (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); 514 } 515 else 516 { 517 (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageKeep); 518 } 519 } 520 } 521 else 522 { 523 // Easy case - nothing already existing for this chunk. Add them all in. 524 for( int i = 0; i < renderableTileEntities.size(); i++ ) 525 { 526 (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); 527 } 528 } 529 } 530 else 531 { 532 // Another easy case - we don't want any renderable tile entities associated with this chunk. Flag all to be removed. 533 AUTO_VAR(it, globalRenderableTileEntities->find(key)); 534 if( it != globalRenderableTileEntities->end() ) 535 { 536 for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) 537 { 538 (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); 539 } 540 } 541 } 542 LeaveCriticalSection(globalRenderableTileEntities_cs); 543 PIXEndNamedEvent(); 544#else 545 // Find the removed ones: 546 547 // 4J - original code for this section: 548 /* 549 Set<TileEntity> newTileEntities = new HashSet<TileEntity>(); 550 newTileEntities.addAll(renderableTileEntities); 551 newTileEntities.removeAll(oldTileEntities); 552 globalRenderableTileEntities.addAll(newTileEntities); 553 554 oldTileEntities.removeAll(renderableTileEntities); 555 globalRenderableTileEntities.removeAll(oldTileEntities); 556 */ 557 558 559 unordered_set<shared_ptr<TileEntity> > newTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); 560 561 AUTO_VAR(endIt, oldTileEntities.end()); 562 for( unordered_set<shared_ptr<TileEntity> >::iterator it = oldTileEntities.begin(); it != endIt; it++ ) 563 { 564 newTileEntities.erase(*it); 565 } 566 567 // 4J - newTileEntities is now renderableTileEntities with any old ones from oldTileEntitesRemoved (so just new things added) 568 569 EnterCriticalSection(globalRenderableTileEntities_cs); 570 endIt = newTileEntities.end(); 571 for( unordered_set<shared_ptr<TileEntity> >::iterator it = newTileEntities.begin(); it != endIt; it++ ) 572 { 573 globalRenderableTileEntities->push_back(*it); 574 } 575 576 // 4J - All these new things added to globalRenderableTileEntities 577 578 AUTO_VAR(endItRTE, renderableTileEntities.end()); 579 for( vector<shared_ptr<TileEntity> >::iterator it = renderableTileEntities.begin(); it != endItRTE; it++ ) 580 { 581 oldTileEntities.erase(*it); 582 } 583 // 4J - oldTileEntities is now the removed items 584 vector<shared_ptr<TileEntity> >::iterator it = globalRenderableTileEntities->begin(); 585 while( it != globalRenderableTileEntities->end() ) 586 { 587 if( oldTileEntities.find(*it) != oldTileEntities.end() ) 588 { 589 it = globalRenderableTileEntities->erase(it); 590 } 591 else 592 { 593 ++it; 594 } 595 } 596 597 LeaveCriticalSection(globalRenderableTileEntities_cs); 598#endif 599 600 // 4J - These removed items are now also removed from globalRenderableTileEntities 601 602 if( LevelChunk::touchedSky ) 603 { 604 levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); 605 } 606 else 607 { 608 levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); 609 } 610 levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED); 611 PIXEndNamedEvent(); 612 return; 613 614} 615 616 617#ifdef __PS3__ 618ChunkRebuildData g_rebuildDataIn __attribute__((__aligned__(16))); 619ChunkRebuildData g_rebuildDataOut __attribute__((__aligned__(16))); 620TileCompressData_SPU g_tileCompressDataIn __attribute__((__aligned__(16))); 621unsigned char* g_tileCompressDataOut = (unsigned char*)&g_rebuildDataIn.m_tileIds; 622 623 624void RunSPURebuild() 625{ 626 627 static C4JSpursJobQueue::Port p("C4JSpursJob_ChunkUpdate"); 628 C4JSpursJob_CompressedTile tileJob(&g_tileCompressDataIn,g_tileCompressDataOut); 629 C4JSpursJob_ChunkUpdate chunkJob(&g_rebuildDataIn, &g_rebuildDataOut); 630 631 if(g_rebuildDataIn.m_currentLayer == 0) // only need to create the tiles on the first layer 632 { 633 p.submitJob(&tileJob); 634 p.submitSync(); 635 } 636 637 p.submitJob(&chunkJob); 638 p.waitForCompletion(); 639 640 assert(g_rebuildDataIn.m_x0 == g_rebuildDataOut.m_x0); 641} 642 643void Chunk::rebuild_SPU() 644{ 645 646// if (!dirty) return; 647 Chunk::t = Tesselator::getInstance(); // 4J - added - static initialiser being set at the wrong time 648 updates++; 649 650 int x0 = x; 651 int y0 = y; 652 int z0 = z; 653 int x1 = x + SIZE; 654 int y1 = y + SIZE; 655 int z1 = z + SIZE; 656 657 LevelChunk::touchedSky = false; 658 659// unordered_set<shared_ptr<TileEntity> > oldTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); // 4J removed this & next line 660// renderableTileEntities.clear(); 661 662 vector<shared_ptr<TileEntity> > renderableTileEntities; // 4J - added 663 664// List<TileEntity> newTileEntities = new ArrayList<TileEntity>(); 665// newTileEntities.clear(); 666// renderableTileEntities.clear(); 667 668 int r = 1; 669 670 Region region(level, x0 - r, y0 - r, z0 - r, x1 + r, y1 + r, z1 + r, r); 671 TileRenderer tileRenderer(&region); 672 673 int lists = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level) * 2; 674 lists += levelRenderer->chunkLists; 675 676 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 677 // 4J - optimisation begins. 678 679 // Get the data for the level chunk that this render chunk is it (level chunk is 16 x 16 x 128, 680 // 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 681 // 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 682 // the cache anyway. 683 ChunkRebuildData* pOutData = NULL; 684 g_rebuildDataIn.buildForChunk(&region, level, x0, y0, z0); 685 686 Tesselator::Bounds bounds; 687 { 688 // this was the old default clip bounds for the chunk, set in Chunk::setPos. 689 float g = 6.0f; 690 bounds.boundingBox[0] = -g; 691 bounds.boundingBox[1] = -g; 692 bounds.boundingBox[2] = -g; 693 bounds.boundingBox[3] = SIZE+g; 694 bounds.boundingBox[4] = SIZE+g; 695 bounds.boundingBox[5] = SIZE+g; 696 } 697 698 for (int currentLayer = 0; currentLayer < 2; currentLayer++) 699 { 700 bool rendered = false; 701 702 { 703 glNewList(lists + currentLayer, GL_COMPILE); 704 MemSect(0); 705 glPushMatrix(); 706 glDepthMask(true); // 4J added 707 t->useCompactVertices(true); // 4J added 708 translateToPos(); 709 float ss = 1.000001f; 710 // 4J - have removed this scale as I don't think we should need it, and have now optimised the vertex 711 // shader so it doesn't do anything other than translate with this matrix anyway 712 #if 0 713 glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); 714 glScalef(ss, ss, ss); 715 glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); 716 #endif 717 t->begin(); 718 t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); 719 } 720 721 g_rebuildDataIn.copyFromTesselator(); 722 intArray_SPU tesselatorArray((unsigned int*)g_rebuildDataIn.m_tesselator.m_PPUArray); 723 g_rebuildDataIn.m_tesselator._array = &tesselatorArray; 724 g_rebuildDataIn.m_currentLayer = currentLayer; 725 g_tileCompressDataIn.setForChunk(&region, x0, y0, z0); 726 RunSPURebuild(); 727 g_rebuildDataOut.storeInTesselator(); 728 pOutData = &g_rebuildDataOut; 729 730 if(pOutData->m_flags & ChunkRebuildData::e_flag_Rendered) 731 rendered = true; 732 733 // 4J - changed loop order here to leave y as the innermost loop for better cache performance 734 for (int z = z0; z < z1; z++) 735 { 736 for (int x = x0; x < x1; x++) 737 { 738 for (int y = y0; y < y1; y++) 739 { 740 // 4J - get tile from those copied into our local array in earlier optimisation 741 unsigned char tileId = pOutData->getTile(x,y,z); 742 if (tileId > 0) 743 { 744 if (currentLayer == 0 && Tile::tiles[tileId]->isEntityTile()) 745 { 746 shared_ptr<TileEntity> et = region.getTileEntity(x, y, z); 747 if (TileEntityRenderDispatcher::instance->hasRenderer(et)) 748 { 749 renderableTileEntities.push_back(et); 750 } 751 } 752 int flags = pOutData->getFlags(x,y,z); 753 if(flags & ChunkRebuildData::e_flag_SPURenderCodeMissing) 754 { 755 756 Tile *tile = Tile::tiles[tileId]; 757 int renderLayer = tile->getRenderLayer(); 758 759 if (renderLayer != currentLayer) 760 { 761 // renderNextLayer = true; 762 } 763 else if (renderLayer == currentLayer) 764 { 765 //if(currentLayer == 0) 766 // numRenderedLayer0++; 767 rendered |= tileRenderer.tesselateInWorld(tile, x, y, z); 768 } 769 } 770 } 771 } 772 } 773 } 774 775 776 { 777 t->end(); 778 bounds.addBounds(t->bounds); 779 glPopMatrix(); 780 glEndList(); 781 t->useCompactVertices(false); // 4J added 782 t->offset(0, 0, 0); 783 } 784 if (rendered) 785 { 786 levelRenderer->clearGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); 787 } 788 else 789 { 790 // 4J - added - clear any renderer data associated with this unused list 791 levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, currentLayer); 792 RenderManager.CBuffClear(lists + currentLayer); 793 } 794 795 } 796 797 if( bb ) 798 { 799 bb->set(bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], 800 bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]); 801 } 802 803 804 if(pOutData->m_flags & ChunkRebuildData::e_flag_TouchedSky) 805 LevelChunk::touchedSky = true; 806 807 808 // 4J - have rewritten the way that tile entities are stored globally to make it work more easily with split screen. Chunks are now 809 // stored globally in the levelrenderer, in a hashmap with a special key made up from the dimension and chunk position (using same index 810 // as is used for global flags) 811#if 1 812 int key = levelRenderer->getGlobalIndexForChunk(this->x,this->y,this->z,level); 813 EnterCriticalSection(globalRenderableTileEntities_cs); 814 if( renderableTileEntities.size() ) 815 { 816 AUTO_VAR(it, globalRenderableTileEntities->find(key)); 817 if( it != globalRenderableTileEntities->end() ) 818 { 819 // We've got some renderable tile entities that we want associated with this chunk, and an existing list of things that used to be. 820 // 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 821 822 // First pass - flag everything already existing to be removed 823 for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) 824 { 825 (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); 826 } 827 828 // Now go through the current list. If these are already in the list, then unflag the remove flag. If they aren't, then add 829 for( int i = 0; i < renderableTileEntities.size(); i++ ) 830 { 831 AUTO_VAR(it2, find( it->second.begin(), it->second.end(), renderableTileEntities[i] )); 832 if( it2 == it->second.end() ) 833 { 834 (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); 835 } 836 else 837 { 838 (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageKeep); 839 } 840 } 841 } 842 else 843 { 844 // Easy case - nothing already existing for this chunk. Add them all in. 845 for( int i = 0; i < renderableTileEntities.size(); i++ ) 846 { 847 (*globalRenderableTileEntities)[key].push_back(renderableTileEntities[i]); 848 } 849 } 850 } 851 else 852 { 853 // Another easy case - we don't want any renderable tile entities associated with this chunk. Flag all to be removed. 854 AUTO_VAR(it, globalRenderableTileEntities->find(key)); 855 if( it != globalRenderableTileEntities->end() ) 856 { 857 for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++ ) 858 { 859 (*it2)->setRenderRemoveStage(TileEntity::e_RenderRemoveStageFlaggedAtChunk); 860 } 861 } 862 } 863 LeaveCriticalSection(globalRenderableTileEntities_cs); 864#else 865 // Find the removed ones: 866 867 // 4J - original code for this section: 868 /* 869 Set<TileEntity> newTileEntities = new HashSet<TileEntity>(); 870 newTileEntities.addAll(renderableTileEntities); 871 newTileEntities.removeAll(oldTileEntities); 872 globalRenderableTileEntities.addAll(newTileEntities); 873 874 oldTileEntities.removeAll(renderableTileEntities); 875 globalRenderableTileEntities.removeAll(oldTileEntities); 876 */ 877 878 879 unordered_set<shared_ptr<TileEntity> > newTileEntities(renderableTileEntities.begin(),renderableTileEntities.end()); 880 881 AUTO_VAR(endIt, oldTileEntities.end()); 882 for( unordered_set<shared_ptr<TileEntity> >::iterator it = oldTileEntities.begin(); it != endIt; it++ ) 883 { 884 newTileEntities.erase(*it); 885 } 886 887 // 4J - newTileEntities is now renderableTileEntities with any old ones from oldTileEntitesRemoved (so just new things added) 888 889 EnterCriticalSection(globalRenderableTileEntities_cs); 890 endIt = newTileEntities.end(); 891 for( unordered_set<shared_ptr<TileEntity> >::iterator it = newTileEntities.begin(); it != endIt; it++ ) 892 { 893 globalRenderableTileEntities.push_back(*it); 894 } 895 896 // 4J - All these new things added to globalRenderableTileEntities 897 898 AUTO_VAR(endItRTE, renderableTileEntities.end()); 899 for( vector<shared_ptr<TileEntity> >::iterator it = renderableTileEntities.begin(); it != endItRTE; it++ ) 900 { 901 oldTileEntities.erase(*it); 902 } 903 // 4J - oldTileEntities is now the removed items 904 vector<shared_ptr<TileEntity> >::iterator it = globalRenderableTileEntities->begin(); 905 while( it != globalRenderableTileEntities->end() ) 906 { 907 if( oldTileEntities.find(*it) != oldTileEntities.end() ) 908 { 909 it = globalRenderableTileEntities->erase(it); 910 } 911 else 912 { 913 ++it; 914 } 915 } 916 917 LeaveCriticalSection(globalRenderableTileEntities_cs); 918#endif 919 920 // 4J - These removed items are now also removed from globalRenderableTileEntities 921 922 if( LevelChunk::touchedSky ) 923 { 924 levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); 925 } 926 else 927 { 928 levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_NOTSKYLIT); 929 } 930 levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED); 931 return; 932 933} 934#endif // _PS3_ 935 936 937float Chunk::distanceToSqr(shared_ptr<Entity> player) const 938{ 939 float xd = (float) (player->x - xm); 940 float yd = (float) (player->y - ym); 941 float zd = (float) (player->z - zm); 942 return xd * xd + yd * yd + zd * zd; 943} 944 945float Chunk::squishedDistanceToSqr(shared_ptr<Entity> player) 946{ 947 float xd = (float) (player->x - xm); 948 float yd = (float) (player->y - ym) * 2; 949 float zd = (float) (player->z - zm); 950 return xd * xd + yd * yd + zd * zd; 951} 952 953void Chunk::reset() 954{ 955 if( assigned ) 956 { 957 EnterCriticalSection(&levelRenderer->m_csDirtyChunks); 958 unsigned char refCount = levelRenderer->decGlobalChunkRefCount(x, y, z, level); 959 assigned = false; 960// printf("\t\t [dec] refcount %d at %d, %d, %d\n",refCount,x,y,z); 961 if( refCount == 0 ) 962 { 963 int lists = levelRenderer->getGlobalIndexForChunk(x, y, z, level) * 2; 964 if(lists >= 0) 965 { 966 lists += levelRenderer->chunkLists; 967 for (int i = 0; i < 2; i++) 968 { 969 // 4J - added - clear any renderer data associated with this unused list 970 RenderManager.CBuffClear(lists + i); 971 } 972 levelRenderer->setGlobalChunkFlags(x, y, z, level, 0); 973 } 974 } 975 LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); 976 } 977 978 clipChunk->visible = false; 979} 980 981void Chunk::_delete() 982{ 983 reset(); 984 level = NULL; 985} 986 987int Chunk::getList(int layer) 988{ 989 if (!clipChunk->visible) return -1; 990 991 int lists = levelRenderer->getGlobalIndexForChunk(x, y, z,level) * 2; 992 lists += levelRenderer->chunkLists; 993 994 bool empty = levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); 995 if (!empty) return lists + layer; 996 return -1; 997} 998 999void Chunk::cull(Culler *culler) 1000{ 1001 clipChunk->visible = culler->isVisible(bb); 1002} 1003 1004void Chunk::renderBB() 1005{ 1006// glCallList(lists + 2); // 4J - removed - TODO put back in 1007} 1008 1009bool Chunk::isEmpty() 1010{ 1011 if (!levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_COMPILED)) return false; 1012 return levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTYBOTH); 1013} 1014 1015void Chunk::setDirty() 1016{ 1017 // 4J - not used, but if this starts being used again then we'll need to investigate how best to handle it. 1018 __debugbreak(); 1019 levelRenderer->setGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY); 1020} 1021 1022void Chunk::clearDirty() 1023{ 1024 levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_DIRTY); 1025#ifdef _CRITICAL_CHUNKS 1026 levelRenderer->clearGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_CRITICAL); 1027#endif 1028} 1029 1030Chunk::~Chunk() 1031{ 1032 delete bb; 1033} 1034 1035bool Chunk::emptyFlagSet(int layer) 1036{ 1037 return levelRenderer->getGlobalChunkFlag(x, y, z, level, LevelRenderer::CHUNK_FLAG_EMPTY0, layer); 1038}