the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at master 3774 lines 130 kB view raw
1#include "stdafx.h" 2#include "LevelRenderer.h" 3#include "Textures.h" 4#include "TextureAtlas.h" 5#include "Tesselator.h" 6#include "Chunk.h" 7#include "EntityRenderDispatcher.h" 8#include "TileEntityRenderDispatcher.h" 9#include "DistanceChunkSorter.h" 10#include "DirtyChunkSorter.h" 11#include "MobSkinTextureProcessor.h" 12#include "MobSkinMemTextureProcessor.h" 13#include "GameRenderer.h" 14#include "BubbleParticle.h" 15#include "SmokeParticle.h" 16#include "NoteParticle.h" 17#include "NetherPortalParticle.h" 18#include "EnderParticle.h" 19#include "ExplodeParticle.h" 20#include "FlameParticle.h" 21#include "LavaParticle.h" 22#include "FootstepParticle.h" 23#include "SplashParticle.h" 24#include "SmokeParticle.h" 25#include "RedDustParticle.h" 26#include "BreakingItemParticle.h" 27#include "SnowShovelParticle.h" 28#include "BreakingItemParticle.h" 29#include "HeartParticle.h" 30#include "HugeExplosionParticle.h" 31#include "HugeExplosionSeedParticle.h" 32#include "SuspendedParticle.h" 33#include "SuspendedTownParticle.h" 34#include "CritParticle2.h" 35#include "TerrainParticle.h" 36#include "SpellParticle.h" 37#include "DripParticle.h" 38#include "EchantmentTableParticle.h" 39#include "DragonBreathParticle.h" 40#include "FireworksParticles.h" 41#include "Lighting.h" 42#include "Options.h" 43#include "MultiPlayerChunkCache.h" 44#include "..\Minecraft.World\ParticleTypes.h" 45#include "..\Minecraft.World\IntCache.h" 46#include "..\Minecraft.World\IntBuffer.h" 47#include "..\Minecraft.World\JavaMath.h" 48#include "..\Minecraft.World\net.minecraft.world.level.h" 49#include "..\Minecraft.World\net.minecraft.world.level.dimension.h" 50#include "..\Minecraft.World\net.minecraft.world.level.tile.h" 51#include "..\Minecraft.World\net.minecraft.world.phys.h" 52#include "..\Minecraft.World\net.minecraft.world.entity.player.h" 53#include "..\Minecraft.World\net.minecraft.world.item.h" 54#include "..\Minecraft.World\System.h" 55#include "..\Minecraft.World\StringHelpers.h" 56#include "..\Minecraft.World\net.minecraft.world.level.chunk.h" 57#include "..\Minecraft.World\net.minecraft.world.entity.projectile.h" 58#include "..\Minecraft.World\net.minecraft.world.h" 59#include "MultiplayerLocalPlayer.h" 60#include "MultiPlayerLevel.h" 61#include "..\Minecraft.World\SoundTypes.h" 62#include "FrustumCuller.h" 63#include "..\Minecraft.World\BasicTypeContainers.h" 64 65//#define DISABLE_SPU_CODE 66 67#ifdef __PS3__ 68#include "PS3\SPU_Tasks\LevelRenderer_cull\LevelRenderer_cull.h" 69#include "PS3\SPU_Tasks\LevelRenderer_FindNearestChunk\LevelRenderer_FindNearestChunk.h" 70#include "C4JSpursJob.h" 71 72static LevelRenderer_cull_DataIn g_cullDataIn[4] __attribute__((__aligned__(16))); 73static LevelRenderer_FindNearestChunk_DataIn g_findNearestChunkDataIn __attribute__((__aligned__(16))); 74#endif 75 76ResourceLocation LevelRenderer::MOON_LOCATION = ResourceLocation(TN_TERRAIN_MOON); 77ResourceLocation LevelRenderer::MOON_PHASES_LOCATION = ResourceLocation(TN_TERRAIN_MOON_PHASES); 78ResourceLocation LevelRenderer::SUN_LOCATION = ResourceLocation(TN_TERRAIN_SUN); 79ResourceLocation LevelRenderer::CLOUDS_LOCATION = ResourceLocation(TN_ENVIRONMENT_CLOUDS); 80ResourceLocation LevelRenderer::END_SKY_LOCATION = ResourceLocation(TN_MISC_TUNNEL); 81 82const unsigned int HALO_RING_RADIUS = 100; 83 84#ifdef _LARGE_WORLDS 85Chunk LevelRenderer::permaChunk[MAX_CONCURRENT_CHUNK_REBUILDS]; 86C4JThread *LevelRenderer::rebuildThreads[MAX_CHUNK_REBUILD_THREADS]; 87C4JThread::EventArray *LevelRenderer::s_rebuildCompleteEvents; 88C4JThread::Event *LevelRenderer::s_activationEventA[MAX_CHUNK_REBUILD_THREADS]; 89 90// This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side 91// so that we can render the "infinite" sea at the edges. Currently defined as: 92const int overworldSize = LEVEL_MAX_WIDTH + LevelRenderer::PLAYER_VIEW_DISTANCE + LevelRenderer::PLAYER_VIEW_DISTANCE; 93const int netherSize = HELL_LEVEL_MAX_WIDTH + 2; // 4J Stu - The plus 2 is really just to make our total chunk count a multiple of 8 for the flags, we will never see these in the nether 94const int endSize = END_LEVEL_MAX_WIDTH; 95const int LevelRenderer::MAX_LEVEL_RENDER_SIZE[3] = { overworldSize, netherSize, endSize }; 96const int LevelRenderer::DIMENSION_OFFSETS[3] = { 0, (overworldSize * overworldSize * CHUNK_Y_COUNT) , (overworldSize * overworldSize * CHUNK_Y_COUNT) + ( netherSize * netherSize * CHUNK_Y_COUNT ) }; 97#else 98// This defines the maximum size of renderable level, must be big enough to cope with actual size of level + view distance at each side 99// so that we can render the "infinite" sea at the edges. Currently defined as: 100// Dimension idx 0 (overworld) : 80 ( = 54 + 13 + 13 ) 101// Dimension idx 1 (nether) : 44 ( = 18 + 13 + 13 ) 102// Dimension idx 2 (the end) : 44 ( = 18 + 13 + 13 ) 103 104const int LevelRenderer::MAX_LEVEL_RENDER_SIZE[3] = { 80, 44, 44 }; 105 106// Linked directly to the sizes in the previous array, these next values dictate the start offset for each dimension index into the global array for these things. 107// Each dimension uses MAX_LEVEL_RENDER_SIZE[i]^2 * 8 indices, as a MAX_LEVEL_RENDER_SIZE * MAX_LEVEL_RENDER_SIZE * 8 sized cube of references. 108 109const int LevelRenderer::DIMENSION_OFFSETS[3] = { 0, (80 * 80 * CHUNK_Y_COUNT) , (80 * 80 * CHUNK_Y_COUNT) + ( 44 * 44 * CHUNK_Y_COUNT ) }; 110#endif 111 112LevelRenderer::LevelRenderer(Minecraft *mc, Textures *textures) 113{ 114 breakingTextures = NULL; 115 116 for( int i = 0; i < 4; i++ ) 117 { 118 level[i] = NULL; 119 tileRenderer[i] = NULL; 120 xOld[i] = -9999; 121 yOld[i] = -9999; 122 zOld[i] = -9999; 123 } 124 xChunks= yChunks= zChunks = 0; 125 chunkLists = 0; 126 127 ticks = 0; 128 starList= skyList= darkList = 0; 129 xMinChunk= yMinChunk= zMinChunk = 0; 130 xMaxChunk= yMaxChunk= zMaxChunk = 0; 131 lastViewDistance = -1; 132 noEntityRenderFrames = 2; 133 totalEntities = 0; 134 renderedEntities = 0; 135 culledEntities = 0; 136 chunkFixOffs = 0; 137 frame = 0; 138 repeatList = MemoryTracker::genLists(1); 139 140 destroyProgress = 0.0f; 141 142 totalChunks= offscreenChunks= occludedChunks= renderedChunks= emptyChunks = 0; 143 for( int i = 0; i < 4; i++ ) 144 { 145 // sortedChunks[i] = NULL; // 4J - removed - not sorting our chunks anymore 146 chunks[i] = ClipChunkArray(); 147 lastPlayerCount[i] = 0; 148 } 149 150 InitializeCriticalSection(&m_csDirtyChunks); 151 InitializeCriticalSection(&m_csRenderableTileEntities); 152#ifdef _LARGE_WORLDS 153 InitializeCriticalSection(&m_csChunkFlags); 154#endif 155 156 dirtyChunkPresent = false; 157 lastDirtyChunkFound = 0; 158 159 this->mc = mc; 160 this->textures = textures; 161 162 chunkLists = MemoryTracker::genLists(getGlobalChunkCount()*2); // *2 here is because there is one renderlist per chunk here for each of the opaque & transparent layers 163 globalChunkFlags = new unsigned char[getGlobalChunkCount()]; 164 memset(globalChunkFlags, 0, getGlobalChunkCount()); 165 166 starList = MemoryTracker::genLists(4); 167 168 glPushMatrix(); 169 glNewList(starList, GL_COMPILE); 170 renderStars(); 171 glEndList(); 172 173 // 4J added - create geometry for rendering clouds 174 createCloudMesh(); 175 176 glPopMatrix(); 177 178 179 180 Tesselator *t = Tesselator::getInstance(); 181 skyList = starList + 1; 182 glNewList(skyList, GL_COMPILE); 183 glDepthMask(false); // 4J - added to get depth mask disabled within the command buffer 184 float yy; 185 int s = 64; 186 int d = 256 / s + 2; 187 yy = (float) 16; 188 for (int xx = -s * d; xx <= s * d; xx += s) 189 { 190 for (int zz = -s * d; zz <= s * d; zz += s) 191 { 192 t->begin(); 193 t->vertex((float)(xx + 0), (float)( yy), (float)( zz + 0)); 194 t->vertex((float)(xx + s), (float)( yy), (float)( zz + 0)); 195 t->vertex((float)(xx + s), (float)( yy), (float)( zz + s)); 196 t->vertex((float)(xx + 0), (float)( yy), (float)( zz + s)); 197 t->end(); 198 } 199 } 200 glEndList(); 201 202 darkList = starList + 2; 203 glNewList(darkList, GL_COMPILE); 204 yy = -(float) 16; 205 t->begin(); 206 for (int xx = -s * d; xx <= s * d; xx += s) 207 { 208 for (int zz = -s * d; zz <= s * d; zz += s) 209 { 210 t->vertex((float)(xx + s), (float)( yy), (float)( zz + 0)); 211 t->vertex((float)(xx + 0), (float)( yy), (float)( zz + 0)); 212 t->vertex((float)(xx + 0), (float)( yy), (float)( zz + s)); 213 t->vertex((float)(xx + s), (float)( yy), (float)( zz + s)); 214 } 215 } 216 t->end(); 217 glEndList(); 218 219 // HALO ring for the texture pack 220 { 221 const unsigned int ARC_SEGMENTS = 50; 222 const float VERTICAL_OFFSET = HALO_RING_RADIUS * 999/1000; // How much we raise the circle origin to make the circle curve back towards us 223 const int WIDTH = 10; 224 const float ARC_RADIANS = 2.0f*PI/ARC_SEGMENTS; 225 const float HALF_ARC_SEG = ARC_SEGMENTS/2; 226 const float WIDE_ARC_SEGS = ARC_SEGMENTS/8; 227 const float WIDE_ARC_SEGS_SQR = WIDE_ARC_SEGS * WIDE_ARC_SEGS; 228 229 float u = 0.0f; 230 float width = WIDTH; 231 232 haloRingList = starList + 3; 233 glNewList(haloRingList, GL_COMPILE); 234 t->begin(GL_TRIANGLE_STRIP); 235 t->color(0xffffff); 236 237 for(unsigned int i = 0; i <= ARC_SEGMENTS; ++i) 238 { 239 float DIFF = abs(i - HALF_ARC_SEG); 240 if(DIFF<(HALF_ARC_SEG-WIDE_ARC_SEGS)) DIFF = 0; 241 else DIFF-=(HALF_ARC_SEG-WIDE_ARC_SEGS); 242 width = 1 + ( (DIFF * DIFF) / (WIDE_ARC_SEGS_SQR) ) * WIDTH; 243 t->vertexUV((HALO_RING_RADIUS * cos(i*ARC_RADIANS)) - VERTICAL_OFFSET, (HALO_RING_RADIUS * sin(i*ARC_RADIANS)), 0-width, u, 0); 244 t->vertexUV((HALO_RING_RADIUS * cos(i*ARC_RADIANS)) - VERTICAL_OFFSET, (HALO_RING_RADIUS * sin(i*ARC_RADIANS)), 0+width, u, 1); 245 //--u; 246 u -= 0.25; 247 } 248 t->end(); 249 glEndList(); 250 } 251 252 Chunk::levelRenderer = this; 253 254 destroyedTileManager = new DestroyedTileManager(); 255 256 dirtyChunksLockFreeStack.Initialize(); 257#ifdef __PS3__ 258 m_jobPort_CullSPU = new C4JSpursJobQueue::Port("C4JSpursJob_LevelRenderer_cull"); 259 m_jobPort_FindNearestChunk = new C4JSpursJobQueue::Port("C4JSpursJob_LevelRenderer_FindNearestChunk"); 260#endif // __PS3__ 261} 262 263void LevelRenderer::renderStars() 264{ 265 Random random = Random(10842); 266 Tesselator *t = Tesselator::getInstance(); 267 t->begin(); 268 for (int i = 0; i < 1500; i++) 269 { 270 double x = random.nextFloat() * 2 - 1; 271 double y = random.nextFloat() * 2 - 1; 272 double z = random.nextFloat() * 2 - 1; 273 double ss = 0.15f + random.nextFloat() * 0.10f; 274 double d = x * x + y * y + z * z; 275 if (d < 1 && d > 0.01) 276 { 277 d = 1 / sqrt(d); 278 x *= d; 279 y *= d; 280 z *= d; 281 double xp = x * 160; // 4J - moved further away (were 100) as they were cutting through far chunks 282 double yp = y * 160; 283 double zp = z * 160; 284 285 double yRot = atan2(x, z); 286 double ySin = sin(yRot); 287 double yCos = cos(yRot); 288 289 double xRot = atan2(sqrt(x * x + z * z), y); 290 double xSin = sin(xRot); 291 double xCos = cos(xRot); 292 293 double zRot = random.nextDouble() * PI * 2; 294 double zSin = sin(zRot); 295 double zCos = cos(zRot); 296 297 for (int c = 0; c < 4; c++) 298 { 299 double ___xo = 0; 300 double ___yo = ((c & 2) - 1) * ss; 301 double ___zo = ((c + 1 & 2) - 1) * ss; 302 303 double __xo = ___xo; 304 double __yo = ___yo * zCos - ___zo * zSin; 305 double __zo = ___zo * zCos + ___yo * zSin; 306 307 double _zo = __zo; 308 double _yo = __yo * xSin + __xo * xCos; 309 double _xo = __xo * xSin - __yo * xCos; 310 311 double xo = _xo * ySin - _zo * yCos; 312 double yo = _yo; 313 double zo = _zo * ySin + _xo * yCos; 314 315 t->vertex((float)(xp + xo), (float)( yp + yo), (float)( zp + zo)); 316 } 317 } 318 } 319 t->end(); 320 321} 322 323 324void LevelRenderer::setLevel(int playerIndex, MultiPlayerLevel *level) 325{ 326 if (this->level[playerIndex] != NULL) 327 { 328 // Remove listener for this level if this is the last player referencing it 329 Level *prevLevel = this->level[playerIndex]; 330 int refCount = 0; 331 for( int i = 0; i < 4; i++ ) 332 { 333 if( this->level[i] == prevLevel ) refCount++; 334 } 335 if( refCount == 1 ) 336 { 337 this->level[playerIndex]->removeListener(this); 338 } 339 } 340 341 xOld[playerIndex] = -9999; 342 yOld[playerIndex] = -9999; 343 zOld[playerIndex] = -9999; 344 345 this->level[playerIndex] = level; 346 if( tileRenderer[playerIndex] != NULL ) 347 { 348 delete tileRenderer[playerIndex]; 349 } 350 tileRenderer[playerIndex] = new TileRenderer(level); 351 if (level != NULL) 352 { 353 // If we're the only player referencing this level, add a new listener for it 354 int refCount = 0; 355 for( int i = 0; i < 4; i++ ) 356 { 357 if( this->level[i] == level ) refCount++; 358 } 359 if( refCount == 1 ) 360 { 361 level->addListener(this); 362 } 363 364 allChanged(playerIndex); 365 } 366 else 367 { 368 // printf("NULLing player %d, chunks @ 0x%x\n",playerIndex,chunks[playerIndex]); 369 if( chunks[playerIndex].data != NULL ) 370 { 371 for (unsigned int i = 0; i < chunks[playerIndex].length; i++) 372 { 373 chunks[playerIndex][i].chunk->_delete(); 374 delete chunks[playerIndex][i].chunk; 375 } 376 delete chunks[playerIndex].data; 377 chunks[playerIndex].data = NULL; 378 chunks[playerIndex].length = 0; 379 // delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore 380 // sortedChunks[playerIndex] = NULL; // 4J - removed - not sorting our chunks anymore 381 } 382 383 // 4J Stu - If we do this for splitscreen players leaving, then all the tile entities in the world dissappear 384 // We should only do this when actually exiting the game, so only when the primary player sets there level to NULL 385 if(playerIndex == ProfileManager.GetPrimaryPad()) renderableTileEntities.clear(); 386 } 387} 388 389void LevelRenderer::AddDLCSkinsToMemTextures() 390{ 391 for(int i=0;i<app.vSkinNames.size();i++) 392 { 393 textures->addMemTexture(app.vSkinNames[i], new MobSkinMemTextureProcessor()); 394 } 395} 396 397void LevelRenderer::allChanged() 398{ 399 int playerIndex = mc->player->GetXboxPad(); // 4J added 400 allChanged(playerIndex); 401} 402 403int LevelRenderer::activePlayers() 404{ 405 int playerCount = 0; 406 for( int i = 0; i < 4; i++ ) 407 { 408 if( level[i] ) playerCount++; 409 } 410 return playerCount; 411} 412 413void LevelRenderer::allChanged(int playerIndex) 414{ 415 // 4J Stu - This was required by the threaded Minecraft::tick(). If we need to add it back then: 416 // If this CS is entered before DisableUpdateThread is called then (on 360 at least) we can get a 417 // deadlock when starting a game in splitscreen. 418 //EnterCriticalSection(&m_csDirtyChunks); 419 if( level == NULL ) 420 { 421 return; 422 } 423 424 Minecraft::GetInstance()->gameRenderer->DisableUpdateThread(); 425 426 Tile::leaves->setFancy(mc->options->fancyGraphics); 427 lastViewDistance = mc->options->viewDistance; 428 429 // Calculate size of area we can render based on number of players we need to render for 430 int dist = (int)sqrtf( (float)PLAYER_RENDER_AREA / (float)activePlayers() ); 431 432 // AP - poor little Vita just can't cope with such a big area 433#ifdef __PSVITA__ 434 dist = 10; 435#endif 436 437 lastPlayerCount[playerIndex] = activePlayers(); 438 439 xChunks = dist; 440 yChunks = Level::maxBuildHeight / CHUNK_SIZE; 441 zChunks = dist; 442 443 if( chunks[playerIndex].data != NULL ) 444 { 445 for (unsigned int i = 0; i < chunks[playerIndex].length; i++) 446 { 447 chunks[playerIndex][i].chunk->_delete(); 448 delete chunks[playerIndex][i].chunk; 449 } 450 delete chunks[playerIndex].data; 451 // delete sortedChunks[playerIndex]; // 4J - removed - not sorting our chunks anymore 452 } 453 454 chunks[playerIndex] = ClipChunkArray(xChunks * yChunks * zChunks); 455 // sortedChunks[playerIndex] = new vector<Chunk *>(xChunks * yChunks * zChunks); // 4J - removed - not sorting our chunks anymore 456 int id = 0; 457 int count = 0; 458 459 xMinChunk = 0; 460 yMinChunk = 0; 461 zMinChunk = 0; 462 xMaxChunk = xChunks; 463 yMaxChunk = yChunks; 464 zMaxChunk = zChunks; 465 466 // 4J removed - we now only fully clear this on exiting the game (setting level to NULL). Apart from that, the chunk rebuilding is responsible for maintaining this 467 // renderableTileEntities.clear(); 468 469 for (int x = 0; x < xChunks; x++) 470 { 471 for (int y = 0; y < yChunks; y++) 472 { 473 for (int z = 0; z < zChunks; z++) 474 { 475 chunks[playerIndex][(z * yChunks + y) * xChunks + x].chunk = new Chunk(level[playerIndex], renderableTileEntities, m_csRenderableTileEntities, x * CHUNK_XZSIZE, y * CHUNK_SIZE, z * CHUNK_XZSIZE, &chunks[playerIndex][(z * yChunks + y) * xChunks + x]); 476 chunks[playerIndex][(z * yChunks + y) * xChunks + x].visible = true; 477 chunks[playerIndex][(z * yChunks + y) * xChunks + x].chunk->id = count++; 478 // sortedChunks[playerIndex]->at((z * yChunks + y) * xChunks + x) = chunks[playerIndex]->at((z * yChunks + y) * xChunks + x); // 4J - removed - not sorting our chunks anymore 479 480 id += 3; 481 } 482 } 483 } 484 nonStackDirtyChunksAdded(); 485 486 if (level != NULL) 487 { 488 shared_ptr<Entity> player = mc->cameraTargetPlayer; 489 if (player != NULL) 490 { 491 this->resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); 492 // sort(sortedChunks[playerIndex]->begin(),sortedChunks[playerIndex]->end(), DistanceChunkSorter(player)); // 4J - removed - not sorting our chunks anymore 493 } 494 } 495 496 noEntityRenderFrames = 2; 497 498 Minecraft::GetInstance()->gameRenderer->EnableUpdateThread(); 499 500 // 4J Stu - Remove. See comment above. 501 //LeaveCriticalSection(&m_csDirtyChunks); 502} 503 504void LevelRenderer::renderEntities(Vec3 *cam, Culler *culler, float a) 505{ 506 int playerIndex = mc->player->GetXboxPad(); // 4J added 507 508 // 4J Stu - Set these up every time, even when not rendering as other things (like particle render) may depend on it for those frames. 509 TileEntityRenderDispatcher::instance->prepare(level[playerIndex], textures, mc->font, mc->cameraTargetPlayer, a); 510 EntityRenderDispatcher::instance->prepare(level[playerIndex], textures, mc->font, mc->cameraTargetPlayer, mc->crosshairPickMob, mc->options, a); 511 512 if (noEntityRenderFrames > 0) 513 { 514 noEntityRenderFrames--; 515 return; 516 } 517 518 totalEntities = 0; 519 renderedEntities = 0; 520 culledEntities = 0; 521 522 shared_ptr<Entity> player = mc->cameraTargetPlayer; 523 524 EntityRenderDispatcher::xOff = (player->xOld + (player->x - player->xOld) * a); 525 EntityRenderDispatcher::yOff = (player->yOld + (player->y - player->yOld) * a); 526 EntityRenderDispatcher::zOff = (player->zOld + (player->z - player->zOld) * a); 527 TileEntityRenderDispatcher::xOff = (player->xOld + (player->x - player->xOld) * a); 528 TileEntityRenderDispatcher::yOff = (player->yOld + (player->y - player->yOld) * a); 529 TileEntityRenderDispatcher::zOff = (player->zOld + (player->z - player->zOld) * a); 530 531 mc->gameRenderer->turnOnLightLayer(a); // 4J - brought forward from 1.8.2 532 533 vector<shared_ptr<Entity> > entities = level[playerIndex]->getAllEntities(); 534 totalEntities = (int)entities.size(); 535 536 AUTO_VAR(itEndGE, level[playerIndex]->globalEntities.end()); 537 for (AUTO_VAR(it, level[playerIndex]->globalEntities.begin()); it != itEndGE; it++) 538 { 539 shared_ptr<Entity> entity = *it; //level->globalEntities[i]; 540 renderedEntities++; 541 if (entity->shouldRender(cam)) EntityRenderDispatcher::instance->render(entity, a); 542 } 543 544 AUTO_VAR(itEndEnts, entities.end()); 545 for (AUTO_VAR(it, entities.begin()); it != itEndEnts; it++) 546 { 547 shared_ptr<Entity> entity = *it; //entities[i]; 548 549 bool shouldRender = (entity->shouldRender(cam) && (entity->noCulling || culler->isVisible(entity->bb))); 550 551 // Render the mob if the mob's leash holder is within the culler 552 if ( !shouldRender && entity->instanceof(eTYPE_MOB) ) 553 { 554 shared_ptr<Mob> mob = dynamic_pointer_cast<Mob>(entity); 555 if ( mob->isLeashed() && (mob->getLeashHolder() != NULL) ) 556 { 557 shared_ptr<Entity> leashHolder = mob->getLeashHolder(); 558 shouldRender = culler->isVisible(leashHolder->bb); 559 } 560 } 561 562 if (shouldRender) 563 { 564 // 4J-PB - changing this to be per player 565 //if (entity == mc->cameraTargetPlayer && !mc->options->thirdPersonView && !mc->cameraTargetPlayer->isSleeping()) continue; 566 shared_ptr<LocalPlayer> localplayer = mc->cameraTargetPlayer->instanceof(eTYPE_LOCALPLAYER) ? dynamic_pointer_cast<LocalPlayer>(mc->cameraTargetPlayer) : nullptr; 567 568 if (localplayer && entity == mc->cameraTargetPlayer && !localplayer->ThirdPersonView() && !mc->cameraTargetPlayer->isSleeping()) continue; 569 570 if (!level[playerIndex]->hasChunkAt(Mth::floor(entity->x), 0, Mth::floor(entity->z))) 571 { 572 continue; 573 } 574 renderedEntities++; 575 EntityRenderDispatcher::instance->render(entity, a); 576 } 577 } 578 579 Lighting::turnOn(); 580 // 4J - have restructed this so that the tile entities are stored within a hashmap by chunk/dimension index. The index 581 // is calculated in the same way as the global flags. 582 EnterCriticalSection(&m_csRenderableTileEntities); 583 for (AUTO_VAR(it, renderableTileEntities.begin()); it != renderableTileEntities.end(); it++) 584 { 585 int idx = it->first; 586 // Don't render if it isn't in the same dimension as this player 587 if( !isGlobalIndexInSameDimension(idx, level[playerIndex]) ) continue; 588 589 for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); it2++) 590 { 591 TileEntityRenderDispatcher::instance->render(*it2, a); 592 } 593 } 594 595 // Now consider if any of these renderable tile entities have been flagged for removal, and if so, remove 596 for (AUTO_VAR(it, renderableTileEntities.begin()); it != renderableTileEntities.end();) 597 { 598 int idx = it->first; 599 600 for( AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); ) 601 { 602 // If it has been flagged for removal, remove 603 if((*it2)->shouldRemoveForRender()) 604 { 605 it2 = it->second.erase(it2); 606 } 607 else 608 { 609 it2++; 610 } 611 } 612 613 // If there aren't any entities left for this key, then delete the key 614 if( it->second.size() == 0 ) 615 { 616 it = renderableTileEntities.erase(it); 617 } 618 else 619 { 620 it++; 621 } 622 } 623 624 LeaveCriticalSection(&m_csRenderableTileEntities); 625 626 mc->gameRenderer->turnOffLightLayer(a); // 4J - brought forward from 1.8.2 627} 628 629wstring LevelRenderer::gatherStats1() 630{ 631 return L"C: " + _toString<int>(renderedChunks) + L"/" + _toString<int>(totalChunks) + L". F: " + _toString<int>(offscreenChunks) + L", O: " + _toString<int>(occludedChunks) + L", E: " + _toString<int>(emptyChunks); 632} 633 634wstring LevelRenderer::gatherStats2() 635{ 636 return L"E: " + _toString<int>(renderedEntities) + L"/" + _toString<int>(totalEntities) + L". B: " + _toString<int>(culledEntities) + L", I: " + _toString<int>((totalEntities - culledEntities) - renderedEntities); 637} 638 639void LevelRenderer::resortChunks(int xc, int yc, int zc) 640{ 641 EnterCriticalSection(&m_csDirtyChunks); 642 xc -= CHUNK_XZSIZE / 2; 643 yc -= CHUNK_SIZE / 2; 644 zc -= CHUNK_XZSIZE / 2; 645 xMinChunk = INT_MAX; 646 yMinChunk = INT_MAX; 647 zMinChunk = INT_MAX; 648 xMaxChunk = INT_MIN; 649 yMaxChunk = INT_MIN; 650 zMaxChunk = INT_MIN; 651 652 int playerIndex = mc->player->GetXboxPad(); // 4J added 653 654 int s2 = xChunks * CHUNK_XZSIZE; 655 int s1 = s2 / 2; 656 657 for (int x = 0; x < xChunks; x++) 658 { 659 int xx = x * CHUNK_XZSIZE; 660 661 int xOff = (xx + s1 - xc); 662 if (xOff < 0) xOff -= (s2 - 1); 663 xOff /= s2; 664 xx -= xOff * s2; 665 666 if (xx < xMinChunk) xMinChunk = xx; 667 if (xx > xMaxChunk) xMaxChunk = xx; 668 669 for (int z = 0; z < zChunks; z++) 670 { 671 int zz = z * CHUNK_XZSIZE; 672 int zOff = (zz + s1 - zc); 673 if (zOff < 0) zOff -= (s2 - 1); 674 zOff /= s2; 675 zz -= zOff * s2; 676 677 if (zz < zMinChunk) zMinChunk = zz; 678 if (zz > zMaxChunk) zMaxChunk = zz; 679 680 for (int y = 0; y < yChunks; y++) 681 { 682 int yy = y * CHUNK_SIZE; 683 if (yy < yMinChunk) yMinChunk = yy; 684 if (yy > yMaxChunk) yMaxChunk = yy; 685 686 Chunk *chunk = chunks[playerIndex][(z * yChunks + y) * xChunks + x].chunk; 687 chunk->setPos(xx, yy, zz); 688 } 689 } 690 } 691 nonStackDirtyChunksAdded(); 692 LeaveCriticalSection(&m_csDirtyChunks); 693} 694 695int LevelRenderer::render(shared_ptr<LivingEntity> player, int layer, double alpha, bool updateChunks) 696{ 697 int playerIndex = mc->player->GetXboxPad(); 698 699 // 4J - added - if the number of players has changed, we need to rebuild things for the new draw distance this will require 700 if( lastPlayerCount[playerIndex] != activePlayers() ) 701 { 702 allChanged(); 703 } 704 else if (mc->options->viewDistance != lastViewDistance) 705 { 706 allChanged(); 707 } 708 709 if (layer == 0) 710 { 711 totalChunks = 0; 712 offscreenChunks = 0; 713 occludedChunks = 0; 714 renderedChunks = 0; 715 emptyChunks = 0; 716 } 717 718 double xOff = player->xOld + (player->x - player->xOld) * alpha; 719 double yOff = player->yOld + (player->y - player->yOld) * alpha; 720 double zOff = player->zOld + (player->z - player->zOld) * alpha; 721 722 double xd = player->x - xOld[playerIndex]; 723 double yd = player->y - yOld[playerIndex]; 724 double zd = player->z - zOld[playerIndex]; 725 726 if (xd * xd + yd * yd + zd * zd > 4 * 4) 727 { 728 xOld[playerIndex] = player->x; 729 yOld[playerIndex] = player->y; 730 zOld[playerIndex] = player->z; 731 732 resortChunks(Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z)); 733 // sort(sortedChunks[playerIndex]->begin(),sortedChunks[playerIndex]->end(), DistanceChunkSorter(player)); // 4J - removed - not sorting our chunks anymore 734 } 735 Lighting::turnOff(); 736 737 int count = renderChunks(0, (int)chunks[playerIndex].length, layer, alpha); 738 739 return count; 740 741} 742 743#ifdef __PSVITA__ 744#include <stdlib.h> 745 746// this is need to sort the chunks by depth 747typedef struct 748{ 749 int Index; 750 float Depth; 751} SChunckSort; 752 753int compare (const void * a, const void * b) 754{ 755 return ( ((SChunckSort*)a)->Depth - ((SChunckSort*)b)->Depth ); 756} 757 758#endif 759 760int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) 761{ 762 int playerIndex = mc->player->GetXboxPad(); // 4J added 763 764#if 1 765 // 4J - cut down version, we're not using offsetted render lists, or a sorted chunk list, anymore 766 mc->gameRenderer->turnOnLightLayer(alpha); // 4J - brought forward from 1.8.2 767 shared_ptr<LivingEntity> player = mc->cameraTargetPlayer; 768 double xOff = player->xOld + (player->x - player->xOld) * alpha; 769 double yOff = player->yOld + (player->y - player->yOld) * alpha; 770 double zOff = player->zOld + (player->z - player->zOld) * alpha; 771 772 glPushMatrix(); 773 glTranslatef((float)-xOff, (float)-yOff, (float)-zOff); 774 775#ifdef __PSVITA__ 776 // AP - also set the camera position so we can work out if a chunk is fogged or not 777 RenderManager.SetCameraPosition((float)-xOff, (float)-yOff, (float)-zOff); 778#endif 779 780#if defined __PS3__ && !defined DISABLE_SPU_CODE 781 // pre- calc'd on the SPU 782 int count = 0; 783 waitForCull_SPU(); 784 if(layer == 0) 785 { 786 count = g_cullDataIn[playerIndex].numToRender_layer0; 787 RenderManager.CBuffCallMultiple(g_cullDataIn[playerIndex].listArray_layer0, count); 788 } 789 else // layer == 1 790 { 791 count = g_cullDataIn[playerIndex].numToRender_layer1; 792 RenderManager.CBuffCallMultiple(g_cullDataIn[playerIndex].listArray_layer1, count); 793 } 794 795#else // __PS3__ 796 797#ifdef __PSVITA__ 798 // AP - alpha cut out is expensive on vita. First render all the non-alpha cut outs 799 glDisable(GL_ALPHA_TEST); 800#endif 801 802 bool first = true; 803 int count = 0; 804 ClipChunk *pClipChunk = chunks[playerIndex].data; 805 unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; 806 for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ ) 807 { 808 if( !pClipChunk->visible ) continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set 809 if( pClipChunk->globalIdx == -1 ) continue; // Not sure if we should ever encounter this... TODO check 810 if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty 811 812 // List can be calculated directly from the chunk's global idex 813 int list = pClipChunk->globalIdx * 2 + layer; 814 list += chunkLists; 815 816 if(RenderManager.CBuffCall(list, first)) 817 { 818 first = false; 819 } 820 count++; 821 } 822 823#ifdef __PSVITA__ 824 // AP - alpha cut out is expensive on vita. Now we render all the alpha cut outs 825 glEnable(GL_ALPHA_TEST); 826 RenderManager.StateSetForceLOD(0); // AP - force mipmapping off for cut outs 827 first = true; 828 pClipChunk = chunks[playerIndex].data; 829 emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; 830 for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ ) 831 { 832 if( !pClipChunk->visible ) continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set 833 if( pClipChunk->globalIdx == -1 ) continue; // Not sure if we should ever encounter this... TODO check 834 if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty 835 if( !(globalChunkFlags[pClipChunk->globalIdx] & LevelRenderer::CHUNK_FLAG_CUT_OUT) ) continue; // Does this chunk contain any cut out geometry 836 837 // List can be calculated directly from the chunk's global idex 838 int list = pClipChunk->globalIdx * 2 + layer; 839 list += chunkLists; 840 841 if(RenderManager.CBuffCallCutOut(list, first)) 842 { 843 first = false; 844 } 845 } 846 RenderManager.StateSetForceLOD(-1); // AP - back to normal mipmapping 847#endif 848 849#endif // __PS3__ 850 851 glPopMatrix(); 852 mc->gameRenderer->turnOffLightLayer(alpha); // 4J - brought forward from 1.8.2 853 854#else 855 _renderChunks.clear(); 856 // int p = 0; 857 int count = 0; 858 for (int i = from; i < to; i++) 859 { 860 if (layer == 0) 861 { 862 totalChunks++; 863 if (sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer)) emptyChunks++; 864 else if (!sortedChunks[playerIndex]->at(i)->visible) offscreenChunks++; 865 else renderedChunks++; 866 } 867 868 // if (!sortedChunks[i].empty[layer] && sortedChunks[i].visible && (sortedChunks[i].occlusion_visible)) { 869 if (!(sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer) && sortedChunks[playerIndex]->at(i)->visible )) 870 { 871 int list = sortedChunks[playerIndex]->at(i)->getList(layer); 872 if (list >= 0) 873 { 874 _renderChunks.push_back(sortedChunks[playerIndex]->at(i)); 875 count++; 876 } 877 } 878 } 879 880 shared_ptr<Mob> player = mc->cameraTargetPlayer; 881 double xOff = player->xOld + (player->x - player->xOld) * alpha; 882 double yOff = player->yOld + (player->y - player->yOld) * alpha; 883 double zOff = player->zOld + (player->z - player->zOld) * alpha; 884 885 int lists = 0; 886 for (int l = 0; l < RENDERLISTS_LENGTH; l++) 887 { 888 renderLists[l].clear(); 889 } 890 891 AUTO_VAR(itEnd, _renderChunks.end()); 892 for (AUTO_VAR(it, _renderChunks.begin()); it != itEnd; it++) 893 { 894 Chunk *chunk = *it; //_renderChunks[i]; 895 896 int list = -1; 897 for (int l = 0; l < lists; l++) 898 { 899 if (renderLists[l].isAt(chunk->xRender, chunk->yRender, chunk->zRender)) 900 { 901 list = l; 902 } 903 } 904 if (list < 0) 905 { 906 list = lists++; 907 renderLists[list].init(chunk->xRender, chunk->yRender, chunk->zRender, xOff, yOff, zOff); 908 } 909 910 renderLists[list].add(chunk->getList(layer)); 911 } 912 913 renderSameAsLast(layer, alpha); 914#endif 915 916 return count; 917 918} 919 920 921void LevelRenderer::renderSameAsLast(int layer, double alpha) 922{ 923 for (int i = 0; i < RENDERLISTS_LENGTH; i++) 924 { 925 renderLists[i].render(); 926 } 927} 928 929void LevelRenderer::tick() 930{ 931 ticks++; 932 933 if ((ticks % SharedConstants::TICKS_PER_SECOND) == 0) 934 { 935 AUTO_VAR(it , destroyingBlocks.begin()); 936 while (it != destroyingBlocks.end()) 937 { 938 BlockDestructionProgress *block = it->second; 939 940 int updatedRenderTick = block->getUpdatedRenderTick(); 941 942 if (ticks - updatedRenderTick > (SharedConstants::TICKS_PER_SECOND * 20)) 943 { 944 delete it->second; 945 it = destroyingBlocks.erase(it); 946 } 947 else 948 { 949 ++it; 950 } 951 } 952 } 953} 954 955void LevelRenderer::renderSky(float alpha) 956{ 957 if (mc->level->dimension->id == 1) 958 { 959 glDisable(GL_FOG); 960 glDisable(GL_ALPHA_TEST); 961 glEnable(GL_BLEND); 962 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 963 Lighting::turnOff(); 964 965 966 glDepthMask(false); 967 textures->bindTexture(&END_SKY_LOCATION); // 4J was L"/1_2_2/misc/tunnel.png" 968 Tesselator *t = Tesselator::getInstance(); 969 t->setMipmapEnable(false); 970 for (int i = 0; i < 6; i++) 971 { 972 glPushMatrix(); 973 if (i == 1) glRotatef(90, 1, 0, 0); 974 if (i == 2) glRotatef(-90, 1, 0, 0); 975 if (i == 3) glRotatef(180, 1, 0, 0); 976 if (i == 4) glRotatef(90, 0, 0, 1); 977 if (i == 5) glRotatef(-90, 0, 0, 1); 978 t->begin(); 979 t->color(0x282828); 980 t->vertexUV(-100, -100, -100, 0, 0); 981 t->vertexUV(-100, -100, +100, 0, 16); 982 t->vertexUV(+100, -100, +100, 16, 16); 983 t->vertexUV(+100, -100, -100, 16, 0); 984 t->end(); 985 glPopMatrix(); 986 } 987 t->setMipmapEnable(true); 988 glDepthMask(true); 989 glEnable(GL_TEXTURE_2D); 990 glEnable(GL_ALPHA_TEST); 991 992 return; 993 } 994 995 if (!mc->level->dimension->isNaturalDimension()) return; 996 997 glDisable(GL_TEXTURE_2D); 998 999 int playerIndex = mc->player->GetXboxPad(); 1000 Vec3 *sc = level[playerIndex]->getSkyColor(mc->cameraTargetPlayer, alpha); 1001 float sr = (float) sc->x; 1002 float sg = (float) sc->y; 1003 float sb = (float) sc->z; 1004 1005 if (mc->options->anaglyph3d) 1006 { 1007 float srr = (sr * 30 + sg * 59 + sb * 11) / 100; 1008 float sgg = (sr * 30 + sg * 70) / (100); 1009 float sbb = (sr * 30 + sb * 70) / (100); 1010 1011 sr = srr; 1012 sg = sgg; 1013 sb = sbb; 1014 } 1015 1016 glColor3f(sr, sg, sb); 1017 1018 Tesselator *t = Tesselator::getInstance(); 1019 1020 glDepthMask(false); 1021 1022#ifdef __PSVITA__ 1023 // AP - alpha cut out is expensive on vita. 1024 glDisable(GL_ALPHA_TEST); 1025#endif 1026 1027 glEnable(GL_FOG); 1028 glColor3f(sr, sg, sb); 1029 glCallList(skyList); 1030 1031 glDisable(GL_FOG); 1032 glDisable(GL_ALPHA_TEST); 1033 glEnable(GL_BLEND); 1034 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1035 Lighting::turnOff(); 1036 1037 float *c = level[playerIndex]->dimension->getSunriseColor(level[playerIndex]->getTimeOfDay(alpha), alpha); 1038 if (c != NULL) 1039 { 1040 glDisable(GL_TEXTURE_2D); 1041 glShadeModel(GL_SMOOTH); 1042 1043 glPushMatrix(); 1044 { 1045 glRotatef(90, 1, 0, 0); 1046 glRotatef(Mth::sin(level[playerIndex]->getSunAngle(alpha)) < 0 ? 180 : 0, 0, 0, 1); 1047 glRotatef(90, 0, 0, 1); 1048 1049 float r = c[0]; 1050 float g = c[1]; 1051 float b = c[2]; 1052 if (mc->options->anaglyph3d) 1053 { 1054 float srr = (r * 30 + g * 59 + b * 11) / 100; 1055 float sgg = (r * 30 + g * 70) / (100); 1056 float sbb = (r * 30 + b * 70) / (100); 1057 1058 r = srr; 1059 g = sgg; 1060 b = sbb; 1061 } 1062 1063 t->begin(GL_TRIANGLE_FAN); 1064 t->color(r, g, b, c[3]); 1065 1066 t->vertex((float)(0), (float)( 100), (float)( 0)); 1067 int steps = 16; 1068 t->color(c[0], c[1], c[2], 0.0f); 1069 for (int i = 0; i <= steps; i++) 1070 { 1071 float a = i * PI * 2 / steps; 1072 float _sin = Mth::sin(a); 1073 float _cos = Mth::cos(a); 1074 t->vertex((float)(_sin * 120), (float)( _cos * 120), (float)( -_cos * 40 * c[3])); 1075 } 1076 t->end(); 1077 } 1078 glPopMatrix(); 1079 glShadeModel(GL_FLAT); 1080 } 1081 1082 glEnable(GL_TEXTURE_2D); 1083 glBlendFunc(GL_SRC_ALPHA, GL_ONE); 1084 glPushMatrix(); 1085 { 1086 float rainBrightness = 1 - level[playerIndex]->getRainLevel(alpha); 1087 float xp = 0; 1088 float yp = 0; 1089 float zp = 0; 1090 glColor4f(1, 1, 1, rainBrightness); 1091 glTranslatef(xp, yp, zp); 1092 glRotatef(-90, 0, 1, 0); 1093 glRotatef(level[playerIndex]->getTimeOfDay(alpha) * 360, 1, 0, 0); 1094 float ss = 30; 1095 1096 MemSect(31); 1097 textures->bindTexture(&SUN_LOCATION); 1098 MemSect(0); 1099 t->begin(); 1100 t->vertexUV((float)(-ss), (float)( 100), (float)( -ss), (float)( 0), (float)( 0)); 1101 t->vertexUV((float)(+ss), (float)( 100), (float)( -ss), (float)( 1), (float)( 0)); 1102 t->vertexUV((float)(+ss), (float)( 100), (float)( +ss), (float)( 1), (float)( 1)); 1103 t->vertexUV((float)(-ss), (float)( 100), (float)( +ss), (float)( 0), (float)( 1)); 1104 t->end(); 1105 1106 ss = 20; 1107 textures->bindTexture(&MOON_PHASES_LOCATION); // 4J was L"/1_2_2/terrain/moon_phases.png" 1108 int phase = level[playerIndex]->getMoonPhase(); 1109 int u = phase % 4; 1110 int v = phase / 4 % 2; 1111 float u0 = (u + 0) / 4.0f; 1112 float v0 = (v + 0) / 2.0f; 1113 float u1 = (u + 1) / 4.0f; 1114 float v1 = (v + 1) / 2.0f; 1115 t->begin(); 1116 t->vertexUV(-ss, -100, +ss, u1, v1); 1117 t->vertexUV(+ss, -100, +ss, u0, v1); 1118 t->vertexUV(+ss, -100, -ss, u0, v0); 1119 t->vertexUV(-ss, -100, -ss, u1, v0); 1120 t->end(); 1121 1122 glDisable(GL_TEXTURE_2D); 1123 float br = level[playerIndex]->getStarBrightness(alpha) * rainBrightness; 1124 if (br > 0) 1125 { 1126 glColor4f(br, br, br, br); 1127 glCallList(starList); 1128 } 1129 glColor4f(1, 1, 1, 1); 1130 } 1131 glDisable(GL_BLEND); 1132 glEnable(GL_ALPHA_TEST); 1133 glEnable(GL_FOG); 1134 1135#ifdef __PSVITA__ 1136 // AP - alpha cut out is expensive on vita. 1137 glDisable(GL_ALPHA_TEST); 1138#endif 1139 1140 glPopMatrix(); 1141 glDisable(GL_TEXTURE_2D); 1142 glColor3f(0, 0, 0); 1143 1144 double yy = mc->player->getPos(alpha)->y - level[playerIndex]->getHorizonHeight(); // 4J - getHorizonHeight moved forward from 1.2.3 1145 if (yy < 0) 1146 { 1147 glPushMatrix(); 1148 glTranslatef(0, -(float) (-12), 0); 1149 glCallList(darkList); 1150 glPopMatrix(); 1151 1152 // 4J - can't work out what this big black box is for. Taking it out until someone misses it... it causes a big black box to visible appear in 3rd person mode whilst under the ground. 1153#if 0 1154 float ss = 1; 1155 float yo = -(float) (yy + 65); 1156 float y0 = -ss; 1157 float y1 = yo; 1158 1159 1160 t->begin(); 1161 t->color(0x000000, 255); 1162 t->vertex(-ss, y1, ss); 1163 t->vertex(+ss, y1, ss); 1164 t->vertex(+ss, y0, ss); 1165 t->vertex(-ss, y0, ss); 1166 1167 t->vertex(-ss, y0, -ss); 1168 t->vertex(+ss, y0, -ss); 1169 t->vertex(+ss, y1, -ss); 1170 t->vertex(-ss, y1, -ss); 1171 1172 t->vertex(+ss, y0, -ss); 1173 t->vertex(+ss, y0, +ss); 1174 t->vertex(+ss, y1, +ss); 1175 t->vertex(+ss, y1, -ss); 1176 1177 t->vertex(-ss, y1, -ss); 1178 t->vertex(-ss, y1, +ss); 1179 t->vertex(-ss, y0, +ss); 1180 t->vertex(-ss, y0, -ss); 1181 1182 t->vertex(-ss, y0, -ss); 1183 t->vertex(-ss, y0, +ss); 1184 t->vertex(+ss, y0, +ss); 1185 t->vertex(+ss, y0, -ss); 1186 t->end(); 1187#endif 1188 } 1189 1190 if (level[playerIndex]->dimension->hasGround()) 1191 { 1192 glColor3f(sr * 0.2f + 0.04f, sg * 0.2f + 0.04f, sb * 0.6f + 0.1f); 1193 } 1194 else 1195 { 1196 glColor3f(sr, sg, sb); 1197 } 1198 glPushMatrix(); 1199 glTranslatef(0, -(float) (yy - 16), 0); 1200 glCallList(darkList); 1201 glPopMatrix(); 1202 glEnable(GL_TEXTURE_2D); 1203 1204 glDepthMask(true); 1205} 1206 1207void LevelRenderer::renderHaloRing(float alpha) 1208{ 1209#if !defined(__PS3__) && !defined(__ORBIS__) && !defined(__PSVITA__) 1210 if (!mc->level->dimension->isNaturalDimension()) return; 1211 1212 glDisable(GL_ALPHA_TEST); 1213 glDisable(GL_TEXTURE_2D); 1214 glDepthMask(false); 1215 glEnable(GL_FOG); 1216 1217 int playerIndex = mc->player->GetXboxPad(); 1218 1219 Vec3 *sc = level[playerIndex]->getSkyColor(mc->cameraTargetPlayer, alpha); 1220 float sr = (float) sc->x; 1221 float sg = (float) sc->y; 1222 float sb = (float) sc->z; 1223 1224 // Rough lumninance calculation 1225 float Y = (sr+sr+sb+sg+sg+sg)/6; 1226 float br = 0.6f + (Y*0.4f); 1227 //app.DebugPrintf("Luminance = %f, brightness = %f\n", Y, br); 1228 glColor3f(br,br,br); 1229 1230 // Fog at the base near the world 1231 glFogi(GL_FOG_MODE, GL_LINEAR); 1232 glFogf(GL_FOG_START, HALO_RING_RADIUS); 1233 glFogf(GL_FOG_END, HALO_RING_RADIUS * 0.20f); 1234 1235 Lighting::turnOn(); 1236 1237 glDepthMask(false); 1238 textures->bindTexture(L"misc/haloRing.png"); // 4J was L"/1_2_2/misc/tunnel.png" 1239 Tesselator *t = Tesselator::getInstance(); 1240 bool prev = t->setMipmapEnable(true); 1241 1242 glPushMatrix(); 1243 glRotatef(-90, 1, 0, 0); 1244 glRotatef(90, 0, 1, 0); 1245 glCallList(haloRingList); 1246 glPopMatrix(); 1247 t->setMipmapEnable(prev); 1248 1249 glDepthMask(true); 1250 glEnable(GL_TEXTURE_2D); 1251 glEnable(GL_ALPHA_TEST); 1252 1253 glDisable(GL_FOG); 1254#endif 1255} 1256 1257void LevelRenderer::renderClouds(float alpha) 1258{ 1259 int iTicks=ticks; 1260 int playerIndex = mc->player->GetXboxPad(); 1261 1262 // if the primary player has clouds off, so do all players on this machine 1263 if(app.GetGameSettings(ProfileManager.GetPrimaryPad(),eGameSetting_Clouds)==0) 1264 { 1265 return; 1266 } 1267 1268 // debug setting added to keep it at day time 1269 if (!mc->level->dimension->isNaturalDimension()) return; 1270 1271 if (mc->options->fancyGraphics) 1272 { 1273 renderAdvancedClouds(alpha); 1274 return; 1275 } 1276 1277 if(app.DebugSettingsOn()) 1278 { 1279 if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_FreezeTime)) 1280 { 1281 iTicks=m_freezeticks; 1282 } 1283 } 1284 glDisable(GL_CULL_FACE); 1285 float yOffs = (float) (mc->cameraTargetPlayer->yOld + (mc->cameraTargetPlayer->y - mc->cameraTargetPlayer->yOld) * alpha); 1286 int s = 32; 1287 int d = 256 / s; 1288 Tesselator *t = Tesselator::getInstance(); 1289 1290 textures->bindTexture(&CLOUDS_LOCATION); 1291 glEnable(GL_BLEND); 1292 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1293 1294 Vec3 *cc = level[playerIndex]->getCloudColor(alpha); 1295 float cr = (float) cc->x; 1296 float cg = (float) cc->y; 1297 float cb = (float) cc->z; 1298 1299 if (mc->options->anaglyph3d) 1300 { 1301 float crr = (cr * 30 + cg * 59 + cb * 11) / 100; 1302 float cgg = (cr * 30 + cg * 70) / (100); 1303 float cbb = (cr * 30 + cb * 70) / (100); 1304 1305 cr = crr; 1306 cg = cgg; 1307 cb = cbb; 1308 } 1309 1310 1311 1312 float scale = 1 / 2048.0f; 1313 1314 double time = (ticks + alpha); 1315 double xo = mc->cameraTargetPlayer->xo + (mc->cameraTargetPlayer->x - mc->cameraTargetPlayer->xo) * alpha + time * 0.03f; 1316 double zo = mc->cameraTargetPlayer->zo + (mc->cameraTargetPlayer->z - mc->cameraTargetPlayer->zo) * alpha; 1317 int xOffs = Mth::floor(xo / 2048); 1318 int zOffs = Mth::floor(zo / 2048); 1319 xo -= xOffs * 2048; 1320 zo -= zOffs * 2048; 1321 1322 float yy = (float) (level[playerIndex]->dimension->getCloudHeight() - yOffs + 0.33f); 1323 float uo = (float) (xo * scale); 1324 float vo = (float) (zo * scale); 1325 t->begin(); 1326 1327 t->color(cr, cg, cb, 0.8f); 1328 for (int xx = -s * d; xx < +s * d; xx += s) 1329 { 1330 for (int zz = -s * d; zz < +s * d; zz += s) 1331 { 1332 t->vertexUV((float)(xx + 0), (float)( yy), (float)( zz + s), (float)( (xx + 0) * scale + uo), (float)( (zz + s) * scale + vo)); 1333 t->vertexUV((float)(xx + s), (float)( yy), (float)( zz + s), (float)( (xx + s) * scale + uo), (float)( (zz + s) * scale + vo)); 1334 t->vertexUV((float)(xx + s), (float)( yy), (float)( zz + 0), (float)( (xx + s) * scale + uo), (float)( (zz + 0) * scale + vo)); 1335 t->vertexUV((float)(xx + 0), (float)( yy), (float)( zz + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + 0) * scale + vo)); 1336 } 1337 } 1338 t->end(); 1339 1340 glColor4f(1, 1, 1, 1.0f); 1341 glDisable(GL_BLEND); 1342 glEnable(GL_CULL_FACE); 1343 1344 if(app.DebugSettingsOn()) 1345 { 1346 1347 if(!(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_FreezeTime))) 1348 { 1349 m_freezeticks=iTicks; 1350 } 1351 } 1352} 1353 1354bool LevelRenderer::isInCloud(double x, double y, double z, float alpha) 1355{ 1356 return false; 1357} 1358 1359// 4J - new geometry for clouds. This is a full array of cubes, one per texel - the original is an array of intersecting fins which aren't ever going to render perfectly. 1360// The geometry is split into 6 command buffers, one per facing direction. This is to keep rendering similar to the original, where the geometry isn't backface culled, 1361// but a decision on which sides to render is made per 8x8 chunk of sky - this keeps the cloud more solid looking when you are actually inside it. Also make a 7th 1362// list that includes all 6 directions, to make rendering of all 6 at once more optimal (we do this when the player isn't potentially inside the clouds) 1363void LevelRenderer::createCloudMesh() 1364{ 1365 cloudList = MemoryTracker::genLists(7); 1366 1367 Tesselator *t = Tesselator::getInstance(); 1368 const float h = 4.0f; 1369 const int D = 8; 1370 1371 for( int i = 0; i < 7; i++ ) 1372 { 1373 glNewList(cloudList + i, GL_COMPILE); 1374 1375 if( ( i == 0 ) || ( i == 6 ) ) 1376 { 1377 t->begin(); 1378 for( int zt = 0; zt < D; zt++ ) 1379 { 1380 for( int xt = 0; xt < D; xt++ ) 1381 { 1382 float u = (((float) xt ) + 0.5f ) / 256.0f; 1383 float v = (((float) zt ) + 0.5f ) / 256.0f; 1384 float x0 = (float)xt; 1385 float x1 = x0 + 1.0f; 1386 float y0 = 0; 1387 float y1 = h; 1388 float z0 = (float)zt; 1389 float z1 = z0 + 1.0f; 1390 t->color(0.7f, 0.7f, 0.7f, 0.8f); 1391 t->normal(0, -1, 0); 1392 t->vertexUV(x0, y0, z0, u, v ); 1393 t->vertexUV(x1, y0, z0, u, v ); 1394 t->vertexUV(x1, y0, z1, u, v ); 1395 t->vertexUV(x0, y0, z1, u, v ); 1396 } 1397 } 1398 t->end(); 1399 } 1400 if( ( i == 1 ) || ( i == 6 ) ) 1401 { 1402 t->begin(); 1403 for( int zt = 0; zt < D; zt++ ) 1404 { 1405 for( int xt = 0; xt < D; xt++ ) 1406 { 1407 float u = (((float) xt ) + 0.5f ) / 256.0f; 1408 float v = (((float) zt ) + 0.5f ) / 256.0f; 1409 float x0 = (float)xt; 1410 float x1 = x0 + 1.0f; 1411 float y0 = 0; 1412 float y1 = h; 1413 float z0 = (float)zt; 1414 float z1 = z0 + 1.0f; 1415 t->color(1.0f, 1.0f, 1.0f, 0.8f); 1416 t->normal(0, 1, 0); 1417 t->vertexUV(x0, y1, z1, u, v ); 1418 t->vertexUV(x1, y1, z1, u, v ); 1419 t->vertexUV(x1, y1, z0, u, v ); 1420 t->vertexUV(x0, y1, z0, u, v ); 1421 } 1422 } 1423 t->end(); 1424 } 1425 if( ( i == 2 ) || ( i == 6 ) ) 1426 { 1427 t->begin(); 1428 for( int zt = 0; zt < D; zt++ ) 1429 { 1430 for( int xt = 0; xt < D; xt++ ) 1431 { 1432 float u = (((float) xt ) + 0.5f ) / 256.0f; 1433 float v = (((float) zt ) + 0.5f ) / 256.0f; 1434 float x0 = (float)xt; 1435 float x1 = x0 + 1.0f; 1436 float y0 = 0; 1437 float y1 = h; 1438 float z0 = (float)zt; 1439 float z1 = z0 + 1.0f; 1440 t->color(0.9f, 0.9f, 0.9f, 0.8f); 1441 t->normal(-1, 0, 0); 1442 t->vertexUV(x0, y0, z1, u, v ); 1443 t->vertexUV(x0, y1, z1, u, v ); 1444 t->vertexUV(x0, y1, z0, u, v ); 1445 t->vertexUV(x0, y0, z0, u, v ); 1446 } 1447 } 1448 t->end(); 1449 } 1450 if( ( i == 3 ) || ( i == 6 ) ) 1451 { 1452 t->begin(); 1453 for( int zt = 0; zt < D; zt++ ) 1454 { 1455 for( int xt = 0; xt < D; xt++ ) 1456 { 1457 float u = (((float) xt ) + 0.5f ) / 256.0f; 1458 float v = (((float) zt ) + 0.5f ) / 256.0f; 1459 float x0 = (float)xt; 1460 float x1 = x0 + 1.0f; 1461 float y0 = 0; 1462 float y1 = h; 1463 float z0 = (float)zt; 1464 float z1 = z0 + 1.0f; 1465 t->color(0.9f, 0.9f, 0.9f, 0.8f); 1466 t->normal(1, 0, 0); 1467 t->vertexUV(x1, y0, z0, u, v ); 1468 t->vertexUV(x1, y1, z0, u, v ); 1469 t->vertexUV(x1, y1, z1, u, v ); 1470 t->vertexUV(x1, y0, z1, u, v ); 1471 } 1472 } 1473 t->end(); 1474 } 1475 if( ( i == 4 ) || ( i == 6 ) ) 1476 { 1477 t->begin(); 1478 for( int zt = 0; zt < D; zt++ ) 1479 { 1480 for( int xt = 0; xt < D; xt++ ) 1481 { 1482 float u = (((float) xt ) + 0.5f ) / 256.0f; 1483 float v = (((float) zt ) + 0.5f ) / 256.0f; 1484 float x0 = (float)xt; 1485 float x1 = x0 + 1.0f; 1486 float y0 = 0; 1487 float y1 = h; 1488 float z0 = (float)zt; 1489 float z1 = z0 + 1.0f; 1490 t->color(0.8f, 0.8f, 0.8f, 0.8f); 1491 t->normal(-1, 0, 0); 1492 t->vertexUV(x0, y1, z0, u, v ); 1493 t->vertexUV(x1, y1, z0, u, v ); 1494 t->vertexUV(x1, y0, z0, u, v ); 1495 t->vertexUV(x0, y0, z0, u, v ); 1496 } 1497 } 1498 t->end(); 1499 } 1500 if( ( i == 5 ) || ( i == 6 ) ) 1501 { 1502 t->begin(); 1503 for( int zt = 0; zt < D; zt++ ) 1504 { 1505 for( int xt = 0; xt < D; xt++ ) 1506 { 1507 float u = (((float) xt ) + 0.5f ) / 256.0f; 1508 float v = (((float) zt ) + 0.5f ) / 256.0f; 1509 float x0 = (float)xt; 1510 float x1 = x0 + 1.0f; 1511 float y0 = 0; 1512 float y1 = h; 1513 float z0 = (float)zt; 1514 float z1 = z0 + 1.0f; 1515 t->color(0.8f, 0.8f, 0.8f, 0.8f); 1516 t->normal(1, 0, 0); 1517 t->vertexUV(x0, y0, z1, u, v ); 1518 t->vertexUV(x1, y0, z1, u, v ); 1519 t->vertexUV(x1, y1, z1, u, v ); 1520 t->vertexUV(x0, y1, z1, u, v ); 1521 } 1522 } 1523 t->end(); 1524 } 1525 glEndList(); 1526 } 1527} 1528 1529void LevelRenderer::renderAdvancedClouds(float alpha) 1530{ 1531 // MGH - added, we were getting dark clouds sometimes on PS3, with this being setup incorrectly 1532 glMultiTexCoord2f(GL_TEXTURE1, 0, 0); 1533 1534 1535 // 4J - most of our viewports are now rendered with no clip planes but using stencilling to limit the area drawn to. Clouds have a relatively large fill area compared to 1536 // the number of vertices that they have, and so enabling clipping here to try and reduce fill rate cost. 1537 RenderManager.StateSetEnableViewportClipPlanes(true); 1538 float yOffs = (float) (mc->cameraTargetPlayer->yOld + (mc->cameraTargetPlayer->y - mc->cameraTargetPlayer->yOld) * alpha); 1539 Tesselator *t = Tesselator::getInstance(); 1540 int playerIndex = mc->player->GetXboxPad(); 1541 1542 int iTicks=ticks; 1543 1544 if(app.DebugSettingsOn()) 1545 { 1546 if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_FreezeTime)) 1547 { 1548 iTicks=m_freezeticks; 1549 } 1550 } 1551 1552 float ss = 12.0f; 1553 float h = 4.0f; 1554 1555 double time = (ticks + alpha); 1556 double xo = (mc->cameraTargetPlayer->xo + (mc->cameraTargetPlayer->x - mc->cameraTargetPlayer->xo) * alpha + time * 0.03f) / ss; 1557 double zo = (mc->cameraTargetPlayer->zo + (mc->cameraTargetPlayer->z - mc->cameraTargetPlayer->zo) * alpha) / ss + 0.33f; 1558 float yy = (float) (level[playerIndex]->dimension->getCloudHeight() - yOffs + 0.33f); 1559 int xOffs = Mth::floor(xo / 2048); 1560 int zOffs = Mth::floor(zo / 2048); 1561 xo -= xOffs * 2048; 1562 zo -= zOffs * 2048; 1563 1564 // 4J - we are now conditionally rendering the clouds in two ways 1565 // (1) if we are (by our y height) in the clouds, then we render in a mode quite like the original, with no backface culling, and 1566 // decisions on which sides of the clouds to render based on the positions of the 8x8 blocks of cloud texels 1567 // (2) if we aren't in the clouds, then we do a simpler form of rendering with backface culling on 1568 // This is because the complex sort of rendering is really there so that the clouds seem more solid when you might be in them, but it has more risk of artifacts so 1569 // we don't want to do it when not necessary 1570 1571 bool noBFCMode = ( (yy > -h - 1) && (yy <= h + 1) ); 1572 if( noBFCMode ) 1573 { 1574 glDisable(GL_CULL_FACE); 1575 } 1576 else 1577 { 1578 glEnable(GL_CULL_FACE); 1579 } 1580 1581 MemSect(31); 1582 textures->bindTexture(&CLOUDS_LOCATION); // 4J was L"/environment/clouds.png" 1583 MemSect(0); 1584 glEnable(GL_BLEND); 1585 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1586 1587 Vec3 *cc = level[playerIndex]->getCloudColor(alpha); 1588 float cr = (float) cc->x; 1589 float cg = (float) cc->y; 1590 float cb = (float) cc->z; 1591 1592 if (mc->options->anaglyph3d) 1593 { 1594 float crr = (cr * 30 + cg * 59 + cb * 11) / 100; 1595 float cgg = (cr * 30 + cg * 70) / (100); 1596 float cbb = (cr * 30 + cb * 70) / (100); 1597 1598 cr = crr; 1599 cg = cgg; 1600 cb = cbb; 1601 } 1602 1603 float uo = (float) (xo * 0); 1604 float vo = (float) (zo * 0); 1605 1606 float scale = 1 / 256.0f; 1607 1608 uo = (float) (Mth::floor(xo)) * scale; 1609 vo = (float) (Mth::floor(zo)) * scale; 1610 // 4J - keep our UVs +ve - there's a small bug in the xbox GPU that incorrectly rounds small -ve UVs (between -1/(64*size) and 0) up to 0, which leaves gaps in our clouds... 1611 while( uo < 1.0f ) uo += 1.0f; 1612 while( vo < 1.0f ) vo += 1.0f; 1613 1614 float xoffs = (float) (xo - Mth::floor(xo)); 1615 float zoffs = (float) (zo - Mth::floor(zo)); 1616 1617 int D = 8; 1618 1619 int radius = 3; 1620 if( activePlayers() > 2 ) radius = 2; // 4J - reduce the cloud render distance a bit for 3 & 4 player split screen 1621 float e = 1 / 1024.0f; 1622 glScalef(ss, 1, ss); 1623 FrustumData* pFrustumData = Frustum::getFrustum(); 1624 for (int pass = 0; pass < 2; pass++) 1625 { 1626 if (pass == 0) 1627 { 1628 // 4J - changed to use blend rather than color mask to avoid writing to frame buffer, to work with our command buffers 1629 glBlendFunc(GL_ZERO, GL_ONE); 1630 // glColorMask(false, false, false, false); 1631 } 1632 else 1633 { 1634 // 4J - changed to use blend rather than color mask to avoid writing to frame buffer, to work with our command buffers 1635 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1636 // glColorMask(true, true, true, true); 1637 } 1638 for (int xPos = -radius + 1; xPos <= radius; xPos++) 1639 { 1640 for (int zPos = -radius + 1; zPos <= radius; zPos++) 1641 { 1642 // 4J - reimplemented the clouds with full cube-per-texel geometry to get rid of seams. This is a huge amount more quads to render, so 1643 // now using command buffers to render each section to cut CPU hit. 1644#if 1 1645 float xx = (float)(xPos * D); 1646 float zz = (float)(zPos * D); 1647 float xp = xx - xoffs; 1648 float zp = zz - zoffs; 1649 1650 if( !pFrustumData->cubeInFrustum(0+xp,0+yy,0+zp, 8+xp,4+yy,8+zp) ) 1651 continue; 1652 1653 1654 1655 glMatrixMode(GL_TEXTURE); 1656 glLoadIdentity(); 1657 glTranslatef(xx / 256.0f + uo, zz / 256.0f + vo, 0); 1658 glMatrixMode(GL_MODELVIEW); 1659 glPushMatrix(); 1660 glTranslatef(xp,yy,zp); 1661 1662 glColor4f(cr, cg, cb, 1.0f ); 1663 if( noBFCMode ) 1664 { 1665 // This is the more complex form of render the clouds, based on the way that the original code picked which sides to render, with backface culling disabled. 1666 // This is to give a more solid version of the clouds for when the player might be inside them. 1667 bool draw[6] = {false,false,false,false,false,false}; 1668 1669 // These rules to decide which sides to draw are the same as the original code below 1670 if (yy > -h - 1) draw[0] = true; 1671 if (yy <= h + 1) draw[1] = true; 1672 if (xPos > -1) draw[2] = true; 1673 if (xPos <= 1) draw[3] = true; 1674 if (zPos > -1) draw[4] = true; 1675 if (zPos <= 1) draw[5] = true; 1676 1677 // Top and bottom just render when required 1678 if( draw[0] ) glCallList(cloudList); 1679 if( draw[1] ) glCallList(cloudList + 1); 1680 // For x facing sides, if we are actually in the clouds and about to draw both sides of the x sides too, then 1681 // do a little offsetting here to avoid z fighting 1682 if( draw[0] && draw[1] && draw[2] && draw[3] ) 1683 { 1684 glTranslatef(e, 0.0f, 0.0f ); 1685 glCallList(cloudList + 2); 1686 glTranslatef(-e, 0.0f, 0.0f ); 1687 glCallList(cloudList + 3); 1688 } 1689 else 1690 { 1691 if( draw[2] ) glCallList(cloudList + 2); 1692 if( draw[3] ) glCallList(cloudList + 3); 1693 } 1694 // For z facing sides, if we are actually in the clouds and about to draw both sides of the z sides too, then 1695 // do a little offsetting here to avoid z fighting 1696 if( draw[0] && draw[1] && draw[4] && draw[5] ) 1697 { 1698 glTranslatef(0.0f, 0.0f, e ); 1699 glCallList(cloudList + 4); 1700 glTranslatef(0.0f, 0.0f, -e ); 1701 glCallList(cloudList + 5); 1702 } 1703 else 1704 { 1705 if( draw[4] ) glCallList(cloudList + 4); 1706 if( draw[5] ) glCallList(cloudList + 5); 1707 } 1708 } 1709 else 1710 { 1711 // Simpler form of rendering that we can do most of the time, when we aren't potentially inside a cloud 1712 glCallList(cloudList + 6); 1713 } 1714 glPopMatrix(); 1715 glMatrixMode(GL_TEXTURE); 1716 glLoadIdentity(); 1717 glMatrixMode(GL_MODELVIEW); 1718#else 1719 1720 t->begin(); 1721 float xx = (float)(xPos * D); 1722 float zz = (float)(zPos * D); 1723 float xp = xx - xoffs; 1724 float zp = zz - zoffs; 1725 1726 1727 if (yy > -h - 1) 1728 { 1729 t->color(cr * 0.7f, cg * 0.7f, cb * 0.7f, 0.8f); 1730 t->normal(0, -1, 0); 1731 t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + D), (float)( (xx + 0) * scale + uo), (float)( (zz + D) * scale + vo)); 1732 t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + D), (float)( (xx + D) * scale + uo), (float)( (zz + D) * scale + vo)); 1733 t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + 0), (float)( (xx + D) * scale + uo), (float)( (zz + 0) * scale + vo)); 1734 t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + 0) * scale + vo)); 1735 } 1736 1737 if (yy <= h + 1) 1738 { 1739 t->color(cr, cg, cb, 0.8f); 1740 t->normal(0, 1, 0); 1741 t->vertexUV((float)(xp + 0), (float)( yy + h - e), (float)( zp + D), (float)( (xx + 0) * scale + uo), (float)( (zz + D) * scale + vo)); 1742 t->vertexUV((float)(xp + D), (float)( yy + h - e), (float)( zp + D), (float)( (xx + D) * scale + uo), (float)( (zz + D) * scale + vo)); 1743 t->vertexUV((float)(xp + D), (float)( yy + h - e), (float)( zp + 0), (float)( (xx + D) * scale + uo), (float)( (zz + 0) * scale + vo)); 1744 t->vertexUV((float)(xp + 0), (float)( yy + h - e), (float)( zp + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + 0) * scale + vo)); 1745 } 1746 1747 t->color(cr * 0.9f, cg * 0.9f, cb * 0.9f, 0.8f); 1748 if (xPos > -1) 1749 { 1750 t->normal(-1, 0, 0); 1751 for (int i = 0; i < D; i++) 1752 { 1753 t->vertexUV((float)(xp + i + 0), (float)( yy + 0), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); 1754 t->vertexUV((float)(xp + i + 0), (float)( yy + h), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); 1755 t->vertexUV((float)(xp + i + 0), (float)( yy + h), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); 1756 t->vertexUV((float)(xp + i + 0), (float)( yy + 0), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); 1757 } 1758 } 1759 1760 if (xPos <= 1) 1761 { 1762 t->normal(+1, 0, 0); 1763 for (int i = 0; i < D; i++) 1764 { 1765 t->vertexUV((float)(xp + i + 1 - e), (float)( yy + 0), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); 1766 t->vertexUV((float)(xp + i + 1 - e), (float)( yy + h), (float)( zp + D), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + D) * scale + vo)); 1767 t->vertexUV((float)(xp + i + 1 - e), (float)( yy + h), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); 1768 t->vertexUV((float)(xp + i + 1 - e), (float)( yy + 0), (float)( zp + 0), (float)( (xx + i + 0.5f) * scale + uo), (float)( (zz + 0) * scale + vo)); 1769 } 1770 } 1771 1772 t->color(cr * 0.8f, cg * 0.8f, cb * 0.8f, 0.8f); 1773 if (zPos > -1) 1774 { 1775 t->normal(0, 0, -1); 1776 for (int i = 0; i < D; i++) 1777 { 1778 t->vertexUV((float)(xp + 0), (float)( yy + h), (float)( zp + i + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1779 t->vertexUV((float)(xp + D), (float)( yy + h), (float)( zp + i + 0), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1780 t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + i + 0), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1781 t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + i + 0), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1782 } 1783 } 1784 1785 if (zPos <= 1) 1786 { 1787 t->normal(0, 0, 1); 1788 for (int i = 0; i < D; i++) 1789 { 1790 t->vertexUV((float)(xp + 0), (float)( yy + h), (float)( zp + i + 1 - e), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1791 t->vertexUV((float)(xp + D), (float)( yy + h), (float)( zp + i + 1 - e), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1792 t->vertexUV((float)(xp + D), (float)( yy + 0), (float)( zp + i + 1 - e), (float)( (xx + D) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1793 t->vertexUV((float)(xp + 0), (float)( yy + 0), (float)( zp + i + 1 - e), (float)( (xx + 0) * scale + uo), (float)( (zz + i + 0.5f) * scale + vo)); 1794 } 1795 } 1796 t->end(); 1797#endif 1798 } 1799 } 1800 } 1801 1802 glColor4f(1, 1, 1, 1.0f); 1803 glDisable(GL_BLEND); 1804 glEnable(GL_CULL_FACE); 1805 1806 1807 if(app.DebugSettingsOn()) 1808 { 1809 if(!(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<<eDebugSetting_FreezeTime))) 1810 { 1811 m_freezeticks=iTicks; 1812 } 1813 } 1814 RenderManager.StateSetEnableViewportClipPlanes(false); 1815} 1816 1817 1818bool LevelRenderer::updateDirtyChunks() 1819{ 1820#ifdef _LARGE_WORLDS 1821 std::list< std::pair<ClipChunk *, int> > nearestClipChunks; 1822#endif 1823 1824 ClipChunk *nearChunk = NULL; // Nearest chunk that is dirty 1825 int veryNearCount = 0; 1826 int minDistSq = 0x7fffffff; // Distances to this chunk 1827 1828 1829 // Set a flag if we should only rebuild existing chunks, not create anything new 1830 unsigned int memAlloc = RenderManager.CBuffSize(-1); 1831 /* 1832 static int throttle = 0; 1833 if( ( throttle % 100 ) == 0 ) 1834 { 1835 app.DebugPrintf("CBuffSize: %d\n",memAlloc/(1024*1024)); 1836 } 1837 throttle++; 1838 */ 1839 PIXAddNamedCounter(((float)memAlloc)/(1024.0f*1024.0f),"Command buffer allocations"); 1840 bool onlyRebuild = ( memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS ); 1841 EnterCriticalSection(&m_csDirtyChunks); 1842 1843 // Move any dirty chunks stored in the lock free stack into global flags 1844 int index = 0; 1845 1846 do 1847 { 1848 // See comment on dirtyChunksLockFreeStack.Push() regarding details of this casting/subtracting -2. 1849 index = (size_t)dirtyChunksLockFreeStack.Pop(); 1850#ifdef _CRITICAL_CHUNKS 1851 int oldIndex = index; 1852 index &= 0x0fffffff; // remove the top bit that marked the chunk as non-critical 1853#endif 1854 if( index == 1 ) dirtyChunkPresent = true; // 1 is a special value passed to let this thread know that a chunk which isn't on this stack has been set to dirty 1855 else if( index > 1 ) 1856 { 1857 int i2 = index - 2; 1858 if( i2 >= DIMENSION_OFFSETS[2] ) 1859 { 1860 i2 -= DIMENSION_OFFSETS[2]; 1861 int y2 = i2 & (CHUNK_Y_COUNT-1); 1862 i2 /= CHUNK_Y_COUNT; 1863 int z2 = i2 / MAX_LEVEL_RENDER_SIZE[2]; 1864 int x2 = i2 - z2 * MAX_LEVEL_RENDER_SIZE[2]; 1865 x2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; 1866 z2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; 1867 } 1868 setGlobalChunkFlag(index - 2, CHUNK_FLAG_DIRTY); 1869 1870#ifdef _CRITICAL_CHUNKS 1871 if( !(oldIndex & 0x10000000) ) // was this chunk not marked as non-critical. Ugh double negatives 1872 { 1873 setGlobalChunkFlag(index - 2, CHUNK_FLAG_CRITICAL); 1874 } 1875#endif 1876 1877 dirtyChunkPresent = true; 1878 } 1879 } while( index ); 1880 1881 // Only bother searching round all the chunks if we have some dirty chunk(s) 1882 if( dirtyChunkPresent ) 1883 { 1884 lastDirtyChunkFound = System::currentTimeMillis(); 1885 PIXBeginNamedEvent(0,"Finding nearest chunk\n"); 1886#if defined __PS3__ && !defined DISABLE_SPU_CODE 1887 // find the nearest chunk with a spu task, copy all the data over here for uploading to SPU 1888 g_findNearestChunkDataIn.numGlobalChunks = getGlobalChunkCount(); 1889 g_findNearestChunkDataIn.pGlobalChunkFlags = globalChunkFlags; 1890 g_findNearestChunkDataIn.onlyRebuild = onlyRebuild; 1891 g_findNearestChunkDataIn.lowerOffset = (int)&((LevelChunk*)0)->lowerBlocks; // dodgy bit of class structure poking, as we don't want to try and get the whole of LevelChunk copmpiling on SPU 1892 g_findNearestChunkDataIn.upperOffset = (int)&((LevelChunk*)0)->upperBlocks; 1893 g_findNearestChunkDataIn.xChunks = xChunks; 1894 g_findNearestChunkDataIn.yChunks = yChunks; 1895 g_findNearestChunkDataIn.zChunks = zChunks; 1896 1897 for(int i=0;i<4;i++) 1898 { 1899 g_findNearestChunkDataIn.chunks[i] = (LevelRenderer_FindNearestChunk_DataIn::ClipChunk*)chunks[i].data; 1900 g_findNearestChunkDataIn.chunkLengths[i] = chunks[i].length; 1901 g_findNearestChunkDataIn.level[i] = level[i]; 1902 g_findNearestChunkDataIn.playerData[i].bValid = mc->localplayers[i] != NULL; 1903 if(mc->localplayers[i] != NULL) 1904 { 1905 g_findNearestChunkDataIn.playerData[i].x = mc->localplayers[i]->x; 1906 g_findNearestChunkDataIn.playerData[i].y = mc->localplayers[i]->y; 1907 g_findNearestChunkDataIn.playerData[i].z = mc->localplayers[i]->z; 1908 1909 } 1910 if(level[i] != NULL) 1911 { 1912 g_findNearestChunkDataIn.multiplayerChunkCache[i].XZOFFSET = ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZOFFSET; 1913 g_findNearestChunkDataIn.multiplayerChunkCache[i].XZSIZE = ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZSIZE; 1914 g_findNearestChunkDataIn.multiplayerChunkCache[i].cache = (void**)((MultiPlayerChunkCache*)(level[i]->chunkSource))->cache; 1915 } 1916 1917 } 1918 1919 // assert(sizeof(LevelRenderer_FindNearestChunk_DataIn::Chunk) == sizeof(Chunk)); 1920 C4JSpursJob_LevelRenderer_FindNearestChunk findJob(&g_findNearestChunkDataIn); 1921 m_jobPort_FindNearestChunk->submitJob(&findJob); 1922 m_jobPort_FindNearestChunk->waitForCompletion(); 1923 nearChunk = (ClipChunk*)g_findNearestChunkDataIn.nearChunk; 1924 veryNearCount = g_findNearestChunkDataIn.veryNearCount; 1925#else // __PS3__ 1926 1927#ifdef _LARGE_WORLDS 1928 int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS; 1929 // 4J Stu - On XboxOne we should cut this down if in a constrained state so the saving threads get more time 1930#endif 1931 // Find nearest chunk that is dirty 1932 for( int p = 0; p < XUSER_MAX_COUNT; p++ ) 1933 { 1934 // It's possible that the localplayers member can be set to NULL on the main thread when a player chooses to exit the game 1935 // So take a reference to the player object now. As it is a shared_ptr it should live as long as we need it 1936 shared_ptr<LocalPlayer> player = mc->localplayers[p]; 1937 if( player == NULL ) continue; 1938 if( chunks[p].data == NULL ) continue; 1939 if( level[p] == NULL ) continue; 1940 if( chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT ) continue; 1941 int px = (int)player->x; 1942 int py = (int)player->y; 1943 int pz = (int)player->z; 1944 1945 // app.DebugPrintf("!! %d %d %d, %d %d %d {%d,%d} ",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, xChunks, zChunks); 1946 1947 int considered = 0; 1948 int wouldBeNearButEmpty = 0; 1949 for( int x = 0; x < xChunks; x++ ) 1950 { 1951 for( int z = 0; z < zChunks; z++ ) 1952 { 1953 for( int y = 0; y < CHUNK_Y_COUNT; y++ ) 1954 { 1955 ClipChunk *pClipChunk = &chunks[p][(z * yChunks + y) * xChunks + x]; 1956 // Get distance to this chunk - deliberately not calling the chunk's method of doing this to avoid overheads (passing entitie, type conversion etc.) that this involves 1957 int xd = pClipChunk->xm - px; 1958 int yd = pClipChunk->ym - py; 1959 int zd = pClipChunk->zm - pz; 1960 int distSq = xd * xd + yd * yd + zd * zd; 1961 int distSqWeighted = xd * xd + yd * yd * 4 + zd * zd; // Weighting against y to prioritise things in same x/z plane as player first 1962 1963 if( globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_DIRTY ) 1964 { 1965 if( (!onlyRebuild) || 1966 globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_COMPILED || 1967 ( distSq < 20 * 20 ) ) // Always rebuild really near things or else building (say) at tower up into empty blocks when we are low on memory will not create render data 1968 { 1969 considered++; 1970 // Is this chunk nearer than our nearest? 1971#ifdef _LARGE_WORLDS 1972 bool isNearer = nearestClipChunks.empty(); 1973 AUTO_VAR(itNearest, nearestClipChunks.begin()); 1974 for(; itNearest != nearestClipChunks.end(); ++itNearest) 1975 { 1976 isNearer = distSqWeighted < itNearest->second; 1977 if(isNearer) break; 1978 } 1979 isNearer = isNearer || (nearestClipChunks.size() < maxNearestChunks); 1980#else 1981 bool isNearer = distSqWeighted < minDistSq; 1982#endif 1983 1984#ifdef _CRITICAL_CHUNKS 1985 // AP - this will make sure that if a deferred grouping has started, only critical chunks go into that 1986 // grouping, even if a non-critical chunk is closer. 1987 if( (!veryNearCount && isNearer) || 1988 (distSq < 20 * 20 && (globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_CRITICAL)) ) 1989#else 1990 if( isNearer ) 1991#endif 1992 { 1993 // At this point we've got a chunk that we would like to consider for rendering, at least based on its proximity to the player(s). 1994 // Its *quite* quick to generate empty render data for render chunks, but if we let the rebuilding do that then the after rebuilding we will have 1995 // to start searching for the next nearest chunk from scratch again. Instead, its better to detect empty chunks at this stage, flag them up as not dirty 1996 // (and empty), and carry on. The levelchunk's isRenderChunkEmpty method can be quite optimal as it can make use of the chunk's data compression to detect 1997 // emptiness without actually testing as many data items as uncompressed data would. 1998 Chunk *chunk = pClipChunk->chunk; 1999 LevelChunk *lc = level[p]->getChunkAt(chunk->x,chunk->z); 2000 if( !lc->isRenderChunkEmpty(y * 16) ) 2001 { 2002 nearChunk = pClipChunk; 2003 minDistSq = distSqWeighted; 2004#ifdef _LARGE_WORLDS 2005 nearestClipChunks.insert(itNearest, std::pair<ClipChunk *, int>(nearChunk, minDistSq) ); 2006 if(nearestClipChunks.size() > maxNearestChunks) 2007 { 2008 nearestClipChunks.pop_back(); 2009 } 2010#endif 2011 } 2012 else 2013 { 2014 chunk->clearDirty(); 2015 globalChunkFlags[ pClipChunk->globalIdx ] |= CHUNK_FLAG_EMPTYBOTH; 2016 wouldBeNearButEmpty++; 2017 } 2018 } 2019 2020#ifdef _CRITICAL_CHUNKS 2021 // AP - is the chunk near and also critical 2022 if( distSq < 20 * 20 && ((globalChunkFlags[ pClipChunk->globalIdx ] & CHUNK_FLAG_CRITICAL)) ) 2023#else 2024 if( distSq < 20 * 20 ) 2025#endif 2026 { 2027 veryNearCount++; 2028 } 2029 } 2030 } 2031 } 2032 } 2033 } 2034 // app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty); 2035 } 2036#endif // __PS3__ 2037 PIXEndNamedEvent(); 2038 } 2039 2040 2041 2042 Chunk *chunk = NULL; 2043#ifdef _LARGE_WORLDS 2044 if(!nearestClipChunks.empty()) 2045 { 2046 int index = 0; 2047 for(AUTO_VAR(it, nearestClipChunks.begin()); it != nearestClipChunks.end(); ++it) 2048 { 2049 chunk = it->first->chunk; 2050 // If this chunk is very near, then move the renderer into a deferred mode. This won't commit any command buffers 2051 // for rendering until we call CBuffDeferredModeEnd(), allowing us to group any near changes into an atomic unit. This 2052 // is essential so we don't temporarily create any holes in the environment whilst updating one chunk and not the neighbours. 2053 // The "ver near" aspect of this is just a cosmetic nicety - exactly the same thing would happen further away, but we just don't 2054 // care about it so much from terms of visual impact. 2055 if( veryNearCount > 0 ) 2056 { 2057 RenderManager.CBuffDeferredModeStart(); 2058 } 2059 // Build this chunk & return false to continue processing 2060 chunk->clearDirty(); 2061 // Take a copy of the details that are required for chunk rebuilding, and rebuild That instead of the original chunk data. This is done within 2062 // the m_csDirtyChunks critical section, which means that any chunks can't be repositioned whilst we are doing this copy. The copy will then 2063 // be guaranteed to be consistent whilst rebuilding takes place outside of that critical section. 2064 permaChunk[index].makeCopyForRebuild(chunk); 2065 ++index; 2066 } 2067 LeaveCriticalSection(&m_csDirtyChunks); 2068 2069 --index; // Bring it back into 0 counted range 2070 2071 for(int i = MAX_CHUNK_REBUILD_THREADS - 1; i >= 0; --i) 2072 { 2073 // Set the events that won't run 2074 if( (i+1) > index) s_rebuildCompleteEvents->Set(i); 2075 else break; 2076 } 2077 2078 for(; index >=0; --index) 2079 { 2080 bool bAtomic = false; 2081 if((veryNearCount > 0)) 2082 bAtomic = true; //MGH - if veryNearCount, then we're trying to rebuild atomically, so do it all on the main thread 2083 2084 if( bAtomic || (index == 0) ) 2085 { 2086 //PIXBeginNamedEvent(0,"Rebuilding near chunk %d %d %d",chunk->x, chunk->y, chunk->z); 2087 // static __int64 totalTime = 0; 2088 // static __int64 countTime = 0; 2089 // __int64 startTime = System::currentTimeMillis(); 2090 2091 //app.DebugPrintf("Rebuilding permaChunk %d\n", index); 2092 2093 permaChunk[index].rebuild(); 2094 2095 if(index !=0) 2096 s_rebuildCompleteEvents->Set(index-1); // MGH - this rebuild happening on the main thread instead, mark the thread it should have been running on as complete 2097 2098 // __int64 endTime = System::currentTimeMillis(); 2099 // totalTime += (endTime - startTime); 2100 // countTime++; 2101 // printf("%d : %f\n", countTime, (float)totalTime / (float)countTime); 2102 //PIXEndNamedEvent(); 2103 } 2104 // 4J Stu - Ignore this path when in constrained mode on Xbox One 2105 else 2106 { 2107 // Activate thread to rebuild this chunk 2108 s_activationEventA[index - 1]->Set(); 2109 } 2110 } 2111 2112 // Wait for the other threads to be done as well 2113 s_rebuildCompleteEvents->WaitForAll(INFINITE); 2114 } 2115#else 2116 if( nearChunk ) 2117 { 2118 chunk = nearChunk->chunk; 2119 PIXBeginNamedEvent(0,"Rebuilding near chunk %d %d %d",chunk->x, chunk->y, chunk->z); 2120 // If this chunk is very near, then move the renderer into a deferred mode. This won't commit any command buffers 2121 // for rendering until we call CBuffDeferredModeEnd(), allowing us to group any near changes into an atomic unit. This 2122 // is essential so we don't temporarily create any holes in the environment whilst updating one chunk and not the neighbours. 2123 // The "ver near" aspect of this is just a cosmetic nicety - exactly the same thing would happen further away, but we just don't 2124 // care about it so much from terms of visual impact. 2125 if( veryNearCount > 0 ) 2126 { 2127 RenderManager.CBuffDeferredModeStart(); 2128 } 2129 // Build this chunk & return false to continue processing 2130 chunk->clearDirty(); 2131 // Take a copy of the details that are required for chunk rebuilding, and rebuild That instead of the original chunk data. This is done within 2132 // the m_csDirtyChunks critical section, which means that any chunks can't be repositioned whilst we are doing this copy. The copy will then 2133 // be guaranteed to be consistent whilst rebuilding takes place outside of that critical section. 2134 static Chunk permaChunk; 2135 permaChunk.makeCopyForRebuild(chunk); 2136 LeaveCriticalSection(&m_csDirtyChunks); 2137 // static __int64 totalTime = 0; 2138 // static __int64 countTime = 0; 2139 // __int64 startTime = System::currentTimeMillis(); 2140 permaChunk.rebuild(); 2141 // __int64 endTime = System::currentTimeMillis(); 2142 // totalTime += (endTime - startTime); 2143 // countTime++; 2144 // printf("%d : %f\n", countTime, (float)totalTime / (float)countTime); 2145 PIXEndNamedEvent(); 2146 } 2147#endif 2148 else 2149 { 2150 // Nothing to do - clear flags that there are things to process, unless it's been a while since we found any dirty chunks in which case force a check next time through 2151 if( ( System::currentTimeMillis() - lastDirtyChunkFound ) > FORCE_DIRTY_CHUNK_CHECK_PERIOD_MS ) 2152 { 2153 dirtyChunkPresent = true; 2154 } 2155 else 2156 { 2157 dirtyChunkPresent = false; 2158 } 2159 LeaveCriticalSection(&m_csDirtyChunks); 2160#ifdef __PS3__ 2161 Sleep(5); 2162#endif // __PS3__ 2163 return false; 2164 } 2165 2166 // If there was more than one very near thing found in our initial assessment, then return true so that we will keep doing the other one(s) 2167 // in an atomic unit 2168 if( veryNearCount > 1 ) 2169 { 2170 destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount ); 2171 return true; 2172 } 2173 // If the chunk we've just built was near, and it has been marked dirty at some point while we are rebuilding, also return true so 2174 // we can rebuild the same thing atomically - if its data was changed during creating render data, it may well be invalid 2175 if( ( veryNearCount == 1 ) && getGlobalChunkFlag(chunk->x, chunk->y, chunk->z, chunk->level, CHUNK_FLAG_DIRTY ) ) 2176 { 2177 destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount + 1); 2178 return true; 2179 } 2180 2181 if( nearChunk ) destroyedTileManager->updatedChunkAt(chunk->level, chunk->x, chunk->y, chunk->z, veryNearCount ); 2182 2183 return false; 2184} 2185 2186void LevelRenderer::renderHit(shared_ptr<Player> player, HitResult *h, int mode, shared_ptr<ItemInstance> inventoryItem, float a) 2187{ 2188 Tesselator *t = Tesselator::getInstance(); 2189 glEnable(GL_BLEND); 2190 glEnable(GL_ALPHA_TEST); 2191 glBlendFunc(GL_SRC_ALPHA, GL_ONE); 2192 glColor4f(1, 1, 1, ((float) (Mth::sin(Minecraft::currentTimeMillis() / 100.0f)) * 0.2f + 0.4f) * 0.5f); 2193 if (mode != 0 && inventoryItem != NULL) 2194 { 2195 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 2196 float br = (Mth::sin(Minecraft::currentTimeMillis() / 100.0f) * 0.2f + 0.8f); 2197 glColor4f(br, br, br, (Mth::sin(Minecraft::currentTimeMillis() / 200.0f) * 0.2f + 0.5f)); 2198 2199 textures->bindTexture(&TextureAtlas::LOCATION_BLOCKS); 2200 } 2201 glDisable(GL_BLEND); 2202 glDisable(GL_ALPHA_TEST); 2203} 2204 2205void LevelRenderer::renderDestroyAnimation(Tesselator *t, shared_ptr<Player> player, float a) 2206{ 2207 double xo = player->xOld + (player->x - player->xOld) * a; 2208 double yo = player->yOld + (player->y - player->yOld) * a; 2209 double zo = player->zOld + (player->z - player->zOld) * a; 2210 2211 int playerIndex = mc->player->GetXboxPad(); 2212 if (!destroyingBlocks.empty()) 2213 { 2214 glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); 2215 2216 textures->bindTexture(&TextureAtlas::LOCATION_BLOCKS); 2217 glColor4f(1, 1, 1, 0.5f); 2218 glPushMatrix(); 2219 2220 glDisable(GL_ALPHA_TEST); 2221 2222 glPolygonOffset(-3.0f, -3.0f); 2223 glEnable(GL_POLYGON_OFFSET_FILL); 2224 2225 glEnable(GL_ALPHA_TEST); 2226 t->begin(); 2227#ifdef __PSVITA__ 2228 // AP : fix for bug 4952. No amount of polygon offset will push this close enough to be seen above the second tile layer when looking straight down 2229 // so just add on a little bit of y to fix this. hacky hacky 2230 t->offset((float)-xo, (float)-yo + 0.01f,(float) -zo); 2231#else 2232 t->offset((float)-xo, (float)-yo,(float) -zo); 2233#endif 2234 t->noColor(); 2235 2236 AUTO_VAR(it, destroyingBlocks.begin()); 2237 while (it != destroyingBlocks.end()) 2238 { 2239 BlockDestructionProgress *block = it->second; 2240 double xd = block->getX() - xo; 2241 double yd = block->getY() - yo; 2242 double zd = block->getZ() - zo; 2243 2244 if (xd * xd + yd * yd + zd * zd < 32 * 32) // 4J MGH - now only culling instead of removing, as the list is shared in split screen 2245 { 2246 int iPad = mc->player->GetXboxPad(); // 4J added 2247 int tileId = level[iPad]->getTile(block->getX(), block->getY(), block->getZ()); 2248 Tile *tile = tileId > 0 ? Tile::tiles[tileId] : NULL; 2249 if (tile == NULL) tile = Tile::stone; 2250 tileRenderer[iPad]->tesselateInWorldFixedTexture(tile, block->getX(), block->getY(), block->getZ(), breakingTextures[block->getProgress()]); // 4J renamed to differentiate from tesselateInWorld 2251 } 2252 ++it; 2253 } 2254 2255 t->end(); 2256 t->offset(0, 0, 0); 2257 glDisable(GL_ALPHA_TEST); 2258 /* 2259 * for (int i = 0; i < 6; i++) { tile.renderFace(t, h.x, h.y, 2260 * h.z, i, 15 * 16 + (int) (destroyProgress * 10)); } 2261 */ 2262 glPolygonOffset(0.0f, 0.0f); 2263 glDisable(GL_POLYGON_OFFSET_FILL); 2264 glEnable(GL_ALPHA_TEST); 2265 2266 glDepthMask(true); 2267 glPopMatrix(); 2268 } 2269} 2270 2271void LevelRenderer::renderHitOutline(shared_ptr<Player> player, HitResult *h, int mode, float a) 2272{ 2273 2274 if (mode == 0 && h->type == HitResult::TILE) 2275 { 2276 int iPad = mc->player->GetXboxPad(); // 4J added 2277 2278 // 4J-PB - If Display HUD is false, don't render the hit outline 2279 if ( app.GetGameSettings(iPad,eGameSetting_DisplayHUD)==0 ) return; 2280 2281 glEnable(GL_BLEND); 2282 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 2283 glColor4f(0, 0, 0, 0.4f); 2284 glLineWidth(2.0f); 2285 glDisable(GL_TEXTURE_2D); 2286 glDepthMask(false); 2287 float ss = 0.002f; 2288 int tileId = level[iPad]->getTile(h->x, h->y, h->z); 2289 2290 if (tileId > 0) 2291 { 2292 Tile::tiles[tileId]->updateShape(level[iPad], h->x, h->y, h->z); 2293 double xo = player->xOld + (player->x - player->xOld) * a; 2294 double yo = player->yOld + (player->y - player->yOld) * a; 2295 double zo = player->zOld + (player->z - player->zOld) * a; 2296 render(Tile::tiles[tileId]->getTileAABB(level[iPad], h->x, h->y, h->z)->grow(ss, ss, ss)->cloneMove(-xo, -yo, -zo)); 2297 } 2298 glDepthMask(true); 2299 glEnable(GL_TEXTURE_2D); 2300 glDisable(GL_BLEND); 2301 } 2302} 2303 2304void LevelRenderer::render(AABB *b) 2305{ 2306 Tesselator *t = Tesselator::getInstance(); 2307 2308 t->begin(GL_LINE_STRIP); 2309 t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z0)); 2310 t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z0)); 2311 t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z1)); 2312 t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z1)); 2313 t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z0)); 2314 t->end(); 2315 2316 t->begin(GL_LINE_STRIP); 2317 t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z0)); 2318 t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z0)); 2319 t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z1)); 2320 t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z1)); 2321 t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z0)); 2322 t->end(); 2323 2324 t->begin(GL_LINES); 2325 t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z0)); 2326 t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z0)); 2327 t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z0)); 2328 t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z0)); 2329 t->vertex((float)(b->x1), (float)( b->y0), (float)( b->z1)); 2330 t->vertex((float)(b->x1), (float)( b->y1), (float)( b->z1)); 2331 t->vertex((float)(b->x0), (float)( b->y0), (float)( b->z1)); 2332 t->vertex((float)(b->x0), (float)( b->y1), (float)( b->z1)); 2333 t->end(); 2334} 2335 2336void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level) // 4J - added level param 2337{ 2338 // 4J - level is passed if this is coming from setTilesDirty, which could come from when connection is being ticked outside of normal level tick, and player won't 2339 // be set up 2340 if( level == NULL ) level = this->level[mc->player->GetXboxPad()]; 2341 // EnterCriticalSection(&m_csDirtyChunks); 2342 int _x0 = Mth::intFloorDiv(x0, CHUNK_XZSIZE); 2343 int _y0 = Mth::intFloorDiv(y0, CHUNK_SIZE); 2344 int _z0 = Mth::intFloorDiv(z0, CHUNK_XZSIZE); 2345 int _x1 = Mth::intFloorDiv(x1, CHUNK_XZSIZE); 2346 int _y1 = Mth::intFloorDiv(y1, CHUNK_SIZE); 2347 int _z1 = Mth::intFloorDiv(z1, CHUNK_XZSIZE); 2348 2349 for (int x = _x0; x <= _x1; x++) 2350 { 2351 for (int y = _y0; y <= _y1; y++) 2352 { 2353 for (int z = _z0; z <= _z1; z++) 2354 { 2355 // printf("Setting %d %d %d dirty\n",x,y,z); 2356 int index = getGlobalIndexForChunk(x * 16, y * 16, z * 16, level); 2357 // Rather than setting the flags directly, add any dirty chunks into a lock free stack - this avoids having to lock m_csDirtyChunks . 2358 // These chunks are then added to the global flags in the render update thread. 2359 // An XLockFreeQueue actually implements a queue of pointers to its templated type, and I don't want to have to go allocating ints here just to store the 2360 // pointer to them in a queue. Hence actually pretending that the int Is a pointer here. Our Index has a a valid range from 0 to something quite big, 2361 // but including zero. The lock free queue, since it thinks it is dealing with pointers, uses a NULL pointer to signify that a Pop hasn't succeeded. 2362 // We also want to reserve one special value (of 1 ) for use when multiple chunks not individually listed are made dirty. Therefore adding 2 to our 2363 // index value here to move our valid range from 1 to something quite big + 2 2364 if( index > -1 ) 2365 { 2366#ifdef _CRITICAL_CHUNKS 2367 index += 2; 2368 2369 // AP - by the time we reach this function the area passed in has a 1 block border added to it to make sure geometry and lighting is updated correctly. 2370 // Some of those blocks will only need lighting updated so it is acceptable to not have those blocks grouped in the deferral system as the mismatch 2371 // will hardly be noticable. The blocks that need geometry updated will be adjacent to the original, non-bordered area. 2372 // This bit of code will mark a chunk as 'non-critical' if all of the blocks inside it are NOT adjacent to the original area. This has the greatest effect 2373 // when digging a single block. Only 6 of the blocks out of the possible 26 are actually adjacent to the original block. The other 20 only need lighting updated. 2374 // Note I have noticed a new side effect of this system where it's possible to see into the sides of water but this is acceptable compared to seeing through 2375 // the entire landscape. 2376 // is the left or right most block just inside this chunk 2377 if( ((x0 & 15) == 15 && x == _x0) || ((x1 & 15) == 0 && x == _x1) ) 2378 { 2379 // is the front, back, top or bottom most block just inside this chunk 2380 if( ((z0 & 15) == 15 && z == _z0) || ((z1 & 15) == 0 && z == _z1) || 2381 ((y0 & 15) == 15 && y == _y0) || ((y1 & 15) == 0 && y == _y1)) 2382 { 2383 index |= 0x10000000; 2384 } 2385 } 2386 else 2387 { 2388 // is the front or back most block just inside this chunk 2389 if( ((z0 & 15) == 15 && z == _z0) || ((z1 & 15) == 0 && z == _z1) ) 2390 { 2391 // is the top or bottom most block just inside this chunk 2392 if( ((y0 & 15) == 15 && y == _y0) || ((y1 & 15) == 0 && y == _y1)) 2393 { 2394 index |= 0x10000000; 2395 } 2396 } 2397 } 2398 2399 dirtyChunksLockFreeStack.Push((int *)(index)); 2400#else 2401 dirtyChunksLockFreeStack.Push((int *)(index + 2)); 2402#endif 2403 2404#ifdef _XBOX 2405 PIXSetMarker(0,"Setting chunk %d %d %d dirty",x * 16,y * 16,z * 16); 2406#else 2407 PIXSetMarkerDeprecated(0,"Setting chunk %d %d %d dirty",x * 16,y * 16,z * 16); 2408#endif 2409 } 2410 // setGlobalChunkFlag(x * 16, y * 16, z * 16, level, CHUNK_FLAG_DIRTY); 2411 } 2412 } 2413 } 2414 // LeaveCriticalSection(&m_csDirtyChunks); 2415} 2416 2417void LevelRenderer::tileChanged(int x, int y, int z) 2418{ 2419 setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1, NULL); 2420} 2421 2422void LevelRenderer::tileLightChanged(int x, int y, int z) 2423{ 2424 setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1, NULL); 2425} 2426 2427void LevelRenderer::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level) // 4J - added level param 2428{ 2429 setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1, level); 2430} 2431 2432bool inline clip(float *bb, float *frustum) 2433{ 2434 for (int i = 0; i < 6; ++i, frustum += 4) 2435 { 2436 if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 2437 if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 2438 if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 2439 if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue; 2440 if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 2441 if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 2442 if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 2443 if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue; 2444 2445 return false; 2446 } 2447 2448 return true; 2449} 2450 2451#ifdef __PS3__ 2452int g_listArray_layer0[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); // 8000 2453int g_listArray_layer1[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); 2454float g_zDepth_layer0[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); // 8000 2455float g_zDepth_layer1[4][LevelRenderer_cull_DataIn::sc_listSize]__attribute__((__aligned__(16))); 2456 2457volatile bool g_useIdent = false; 2458volatile float g_maxDepthRender = 1000; 2459volatile float g_maxHeightRender = -1000; 2460volatile float g_offMulVal = 1; 2461 2462void LevelRenderer::cull_SPU(int playerIndex, Culler *culler, float a) 2463{ 2464 if(m_bSPUCullStarted[playerIndex]) 2465 { 2466 return; // running already 2467 } 2468 2469 FrustumCuller *fc = (FrustumCuller *)culler; 2470 FrustumData *fd = fc->frustum; 2471 float fdraw[6 * 4]; 2472 for( int i = 0; i < 6; i++ ) 2473 { 2474 double fx = fd->m_Frustum[i][0]; 2475 double fy = fd->m_Frustum[i][1]; 2476 double fz = fd->m_Frustum[i][2]; 2477 fdraw[i * 4 + 0] = (float)fx; 2478 fdraw[i * 4 + 1] = (float)fy; 2479 fdraw[i * 4 + 2] = (float)fz; 2480 fdraw[i * 4 + 3] = (float)(fd->m_Frustum[i][3] + ( fx * -fc->xOff ) + ( fy * - fc->yOff ) + ( fz * -fc->zOff )); 2481 } 2482 2483 memcpy(&g_cullDataIn[playerIndex].fdraw, fdraw, sizeof(fdraw)); 2484 g_cullDataIn[playerIndex].numClipChunks = chunks[playerIndex].length; 2485 g_cullDataIn[playerIndex].pClipChunks = (ClipChunk_SPU*)chunks[playerIndex].data; 2486 g_cullDataIn[playerIndex].numGlobalChunks = getGlobalChunkCount(); 2487 g_cullDataIn[playerIndex].pGlobalChunkFlags = globalChunkFlags; 2488 g_cullDataIn[playerIndex].chunkLists = chunkLists; 2489 g_cullDataIn[playerIndex].listArray_layer0 = g_listArray_layer0[playerIndex]; 2490 g_cullDataIn[playerIndex].listArray_layer1 = g_listArray_layer1[playerIndex]; 2491 g_cullDataIn[playerIndex].zDepth_layer0 = g_zDepth_layer0[playerIndex]; 2492 g_cullDataIn[playerIndex].zDepth_layer1 = g_zDepth_layer1[playerIndex]; 2493 g_cullDataIn[playerIndex].maxDepthRender = g_maxDepthRender; 2494 g_cullDataIn[playerIndex].maxHeightRender = g_maxHeightRender; 2495 2496 if(g_useIdent) 2497 g_cullDataIn[playerIndex].clipMat = Vectormath::Aos::Matrix4::identity(); 2498 else 2499 { 2500 memcpy(&g_cullDataIn[playerIndex].clipMat, &fc->frustum->modl[0], sizeof(float) * 16); 2501 g_cullDataIn[playerIndex].clipMat[3][0] = -fc->xOff; 2502 g_cullDataIn[playerIndex].clipMat[3][1] = -fc->yOff; 2503 g_cullDataIn[playerIndex].clipMat[3][2] = -fc->zOff; 2504 } 2505 2506 2507 C4JSpursJob_LevelRenderer_cull cullJob(&g_cullDataIn[playerIndex]); 2508 C4JSpursJob_LevelRenderer_zSort sortJob(&g_cullDataIn[playerIndex]); 2509 2510 m_jobPort_CullSPU->submitJob(&cullJob); 2511 m_jobPort_CullSPU->submitSync(); 2512 // static int doSort = false; 2513 // if(doSort) 2514 { 2515 m_jobPort_CullSPU->submitJob(&sortJob); 2516 } 2517 // doSort ^= 1; 2518 m_bSPUCullStarted[playerIndex] = true; 2519} 2520void LevelRenderer::waitForCull_SPU() 2521{ 2522 m_jobPort_CullSPU->waitForCompletion(); 2523 int playerIndex = mc->player->GetXboxPad(); // 4J added 2524 m_bSPUCullStarted[playerIndex] = false; 2525} 2526#endif // __PS3__ 2527 2528void LevelRenderer::cull(Culler *culler, float a) 2529{ 2530 int playerIndex = mc->player->GetXboxPad(); // 4J added 2531 2532#if defined __PS3__ && !defined DISABLE_SPU_CODE 2533 cull_SPU(playerIndex, culler, a); 2534 return; 2535#endif // __PS3__ 2536 2537 2538 FrustumCuller *fc = (FrustumCuller *)culler; 2539 FrustumData *fd = fc->frustum; 2540 float fdraw[6 * 4]; 2541 for( int i = 0; i < 6; i++ ) 2542 { 2543 double fx = fd->m_Frustum[i][0]; 2544 double fy = fd->m_Frustum[i][1]; 2545 double fz = fd->m_Frustum[i][2]; 2546 fdraw[i * 4 + 0] = (float)fx; 2547 fdraw[i * 4 + 1] = (float)fy; 2548 fdraw[i * 4 + 2] = (float)fz; 2549 fdraw[i * 4 + 3] = (float)(fd->m_Frustum[i][3] + ( fx * -fc->xOff ) + ( fy * - fc->yOff ) + ( fz * -fc->zOff )); 2550 } 2551 2552 ClipChunk *pClipChunk = chunks[playerIndex].data; 2553 int vis = 0; 2554 int total = 0; 2555 int numWrong = 0; 2556 for (unsigned int i = 0; i < chunks[playerIndex].length; i++) 2557 { 2558 unsigned char flags = pClipChunk->globalIdx == -1 ? 0 : globalChunkFlags[ pClipChunk->globalIdx ]; 2559 2560 if ( (flags & CHUNK_FLAG_COMPILED ) && ( ( flags & CHUNK_FLAG_EMPTYBOTH ) != CHUNK_FLAG_EMPTYBOTH ) ) 2561 { 2562 bool clipres = clip(pClipChunk->aabb, fdraw); 2563 pClipChunk->visible = clipres; 2564 if( pClipChunk->visible ) vis++; 2565 total++; 2566 } 2567 else 2568 { 2569 pClipChunk->visible = false; 2570 } 2571 pClipChunk++; 2572 } 2573} 2574 2575void LevelRenderer::playStreamingMusic(const wstring& name, int x, int y, int z) 2576{ 2577 if (name != L"") 2578 { 2579 mc->gui->setNowPlaying(L"C418 - " + name); 2580 } 2581 mc->soundEngine->playStreaming(name, (float) x, (float) y, (float) z, 1, 1); 2582} 2583 2584void LevelRenderer::playSound(int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist) 2585{ 2586 // 4J-PB - removed in 1.4 2587 2588 //float dd = 16; 2589 /*if (volume > 1) fSoundClipDist *= volume; 2590 2591 // 4J - find min distance to any players rather than just the current one 2592 float minDistSq = FLT_MAX; 2593 for( int i = 0; i < XUSER_MAX_COUNT; i++ ) 2594 { 2595 if( mc->localplayers[i] ) 2596 { 2597 float distSq = mc->localplayers[i]->distanceToSqr(x, y, z ); 2598 if( distSq < minDistSq ) 2599 { 2600 minDistSq = distSq; 2601 } 2602 } 2603 } 2604 2605 if (minDistSq < fSoundClipDist * fSoundClipDist) 2606 { 2607 mc->soundEngine->play(iSound, (float) x, (float) y, (float) z, volume, pitch); 2608 } */ 2609} 2610 2611void LevelRenderer::playSound(shared_ptr<Entity> entity,int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist) 2612{ 2613} 2614 2615void LevelRenderer::playSoundExceptPlayer(shared_ptr<Player> player, int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist) 2616{ 2617} 2618 2619// 4J-PB - original function. I've changed to an enum instead of string compares 2620// 4J removed - 2621/* 2622void LevelRenderer::addParticle(const wstring& name, double x, double y, double z, double xa, double ya, double za) 2623{ 2624if (mc == NULL || mc->cameraTargetPlayer == NULL || mc->particleEngine == NULL) return; 2625 2626double xd = mc->cameraTargetPlayer->x - x; 2627double yd = mc->cameraTargetPlayer->y - y; 2628double zd = mc->cameraTargetPlayer->z - z; 2629 2630double particleDistance = 16; 2631if (xd * xd + yd * yd + zd * zd > particleDistance * particleDistance) return; 2632 2633int playerIndex = mc->player->GetXboxPad(); // 4J added 2634 2635if (name== L"bubble") mc->particleEngine->add(shared_ptr<BubbleParticle>( new BubbleParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2636else if (name== L"smoke") mc->particleEngine->add(shared_ptr<SmokeParticle>( new SmokeParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2637else if (name== L"note") mc->particleEngine->add(shared_ptr<NoteParticle>( new NoteParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2638else if (name== L"portal") mc->particleEngine->add(shared_ptr<PortalParticle>( new PortalParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2639else if (name== L"explode") mc->particleEngine->add(shared_ptr<ExplodeParticle>( new ExplodeParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2640else if (name== L"flame") mc->particleEngine->add(shared_ptr<FlameParticle>( new FlameParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2641else if (name== L"lava") mc->particleEngine->add(shared_ptr<LavaParticle>( new LavaParticle(level[playerIndex], x, y, z) ) ); 2642else if (name== L"footstep") mc->particleEngine->add(shared_ptr<FootstepParticle>( new FootstepParticle(textures, level[playerIndex], x, y, z) ) ); 2643else if (name== L"splash") mc->particleEngine->add(shared_ptr<SplashParticle>( new SplashParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2644else if (name== L"largesmoke") mc->particleEngine->add(shared_ptr<SmokeParticle>( new SmokeParticle(level[playerIndex], x, y, z, xa, ya, za, 2.5f) ) ); 2645else if (name== L"reddust") mc->particleEngine->add(shared_ptr<RedDustParticle>( new RedDustParticle(level[playerIndex], x, y, z, (float) xa, (float) ya, (float) za) ) ); 2646else if (name== L"snowballpoof") mc->particleEngine->add(shared_ptr<BreakingItemParticle>( new BreakingItemParticle(level[playerIndex], x, y, z, Item::snowBall) ) ); 2647else if (name== L"snowshovel") mc->particleEngine->add(shared_ptr<SnowShovelParticle>( new SnowShovelParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2648else if (name== L"slime") mc->particleEngine->add(shared_ptr<BreakingItemParticle>( new BreakingItemParticle(level[playerIndex], x, y, z, Item::slimeBall)) ) ; 2649else if (name== L"heart") mc->particleEngine->add(shared_ptr<HeartParticle>( new HeartParticle(level[playerIndex], x, y, z, xa, ya, za) ) ); 2650} 2651*/ 2652 2653void LevelRenderer::addParticle(ePARTICLE_TYPE eParticleType, double x, double y, double z, double xa, double ya, double za) 2654{ 2655 addParticleInternal( eParticleType, x, y, z, xa, ya, za ); 2656} 2657 2658shared_ptr<Particle> LevelRenderer::addParticleInternal(ePARTICLE_TYPE eParticleType, double x, double y, double z, double xa, double ya, double za) 2659{ 2660 if (mc == NULL || mc->cameraTargetPlayer == NULL || mc->particleEngine == NULL) 2661 { 2662 return nullptr; 2663 } 2664 2665 // 4J added - do some explicit checking for NaN. The normal depth clipping seems to generally work for NaN (ie they get rejected), except on optimised PS3 code which 2666 // reverses the logic on the comparison with particleDistanceSquared and gets the opposite result to what you might expect. 2667 if( Double::isNaN(x) ) return nullptr; 2668 if( Double::isNaN(y) ) return nullptr; 2669 if( Double::isNaN(z) ) return nullptr; 2670 2671 int particleLevel = mc->options->particles; 2672 2673 Level *lev; 2674 int playerIndex = mc->player->GetXboxPad(); // 4J added 2675 lev = level[playerIndex]; 2676 2677 if (particleLevel == 1) 2678 { 2679 // when playing at "decreased" particle level, randomly filter 2680 // particles by setting the level to "minimal" 2681 if (level[playerIndex]->random->nextInt(3) == 0) 2682 { 2683 particleLevel = 2; 2684 } 2685 } 2686 2687 // 4J - the java code doesn't distance cull these two particle types, we need to implement this behaviour differently as our distance check is 2688 // mixed up with other things 2689 bool distCull = true; 2690 if ( (eParticleType == eParticleType_hugeexplosion) || (eParticleType == eParticleType_largeexplode) || (eParticleType == eParticleType_dragonbreath) ) 2691 { 2692 distCull = false; 2693 } 2694 2695 // 4J - this is a bit of hack to get communication through from the level itself, but if Minecraft::animateTickLevel is NULL then 2696 // we are to behave as normal, and if it is set, then we should use that as a pointer to the level the particle is to be created with 2697 // rather than try to work it out from the current player. This is because in this state we are calling from a loop that is trying 2698 // to amalgamate particle creation between all players for a particular level. Also don't do distance clipping as it isn't for a particular 2699 // player, and distance is already taken into account before we get here anyway by the code in Level::animateTickDoWork 2700 if( mc->animateTickLevel == NULL ) 2701 { 2702 double particleDistanceSquared = 16 * 16; 2703 double xd = 0.0f; 2704 double yd = 0.0f; 2705 double zd = 0.0f; 2706 2707 // 4J Stu - Changed this as we need to check all local players in case one of them is in range of this particle 2708 // Fix for #13454 - art : note blocks do not show notes 2709 bool inRange = false; 2710 for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) 2711 { 2712 shared_ptr<Player> thisPlayer = mc->localplayers[i]; 2713 if(thisPlayer != NULL && level[i] == lev) 2714 { 2715 xd = thisPlayer->x - x; 2716 yd = thisPlayer->y - y; 2717 zd = thisPlayer->z - z; 2718 if (xd * xd + yd * yd + zd * zd <= particleDistanceSquared) inRange = true; 2719 } 2720 } 2721 if( (!inRange) && distCull ) return nullptr; 2722 } 2723 else 2724 { 2725 lev = mc->animateTickLevel; 2726 } 2727 2728 if (particleLevel > 1) 2729 { 2730 // TODO: If any of the particles below are necessary even if 2731 // particles are turned off, then modify this if statement 2732 return nullptr; 2733 } 2734 2735 shared_ptr<Particle> particle; 2736 2737 switch(eParticleType) 2738 { 2739 case eParticleType_hugeexplosion: 2740 particle = shared_ptr<Particle>(new HugeExplosionSeedParticle(lev, x, y, z, xa, ya, za)); 2741 break; 2742 case eParticleType_largeexplode: 2743 particle = shared_ptr<Particle>(new HugeExplosionParticle(textures, lev, x, y, z, xa, ya, za)); 2744 break; 2745 case eParticleType_fireworksspark: 2746 particle = shared_ptr<Particle>(new FireworksParticles::FireworksSparkParticle(lev, x, y, z, xa, ya, za, mc->particleEngine)); 2747 particle->setAlpha(0.99f); 2748 break; 2749 2750 case eParticleType_bubble: 2751 particle = shared_ptr<Particle>( new BubbleParticle(lev, x, y, z, xa, ya, za) ); 2752 break; 2753 2754 case eParticleType_suspended: 2755 particle = shared_ptr<Particle>( new SuspendedParticle(lev, x, y, z, xa, ya, za) ); 2756 break; 2757 case eParticleType_depthsuspend: 2758 particle = shared_ptr<Particle>( new SuspendedTownParticle(lev, x, y, z, xa, ya, za) ); 2759 break; 2760 case eParticleType_townaura: 2761 particle = shared_ptr<Particle>( new SuspendedTownParticle(lev, x, y, z, xa, ya, za) ); 2762 break; 2763 case eParticleType_crit: 2764 { 2765 shared_ptr<CritParticle2> critParticle2 = shared_ptr<CritParticle2>(new CritParticle2(lev, x, y, z, xa, ya, za)); 2766 critParticle2->CritParticle2PostConstructor(); 2767 particle = shared_ptr<Particle>( critParticle2 ); 2768 // request from 343 to set pink for the needler in the Halo Texture Pack 2769 // Set particle colour from colour-table. 2770 unsigned int cStart = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_CritStart ); 2771 unsigned int cEnd = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_CritEnd ); 2772 2773 // If the start and end colours are the same, just set that colour, otherwise random between them 2774 if(cStart==cEnd) 2775 { 2776 critParticle2->SetAgeUniformly(); 2777 particle->setColor( ( (cStart>>16)&0xFF )/255.0f, ( (cStart>>8)&0xFF )/255.0, ( cStart&0xFF )/255.0 ); 2778 } 2779 else 2780 { 2781 float fStart=((float)(cStart&0xFF)); 2782 float fDiff=(float)((cEnd-cStart)&0xFF); 2783 2784 float fCol = (fStart + (Math::random() * fDiff))/255.0f; 2785 particle->setColor( fCol, fCol, fCol ); 2786 } 2787 } 2788 break; 2789 case eParticleType_magicCrit: 2790 { 2791 shared_ptr<CritParticle2> critParticle2 = shared_ptr<CritParticle2>(new CritParticle2(lev, x, y, z, xa, ya, za)); 2792 critParticle2->CritParticle2PostConstructor(); 2793 particle = shared_ptr<Particle>(critParticle2); 2794 particle->setColor(particle->getRedCol() * 0.3f, particle->getGreenCol() * 0.8f, particle->getBlueCol()); 2795 particle->setNextMiscAnimTex(); 2796 } 2797 break; 2798 case eParticleType_smoke: 2799 particle = shared_ptr<Particle>( new SmokeParticle(lev, x, y, z, xa, ya, za) ); 2800 break; 2801 case eParticleType_endportal: // 4J - Added. 2802 { 2803 SmokeParticle *tmp = new SmokeParticle(lev, x, y, z, xa, ya, za); 2804 2805 // 4J-JEV: Set particle colour from colour-table. 2806 unsigned int col = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Particle_EnderPortal ); 2807 tmp->setColor( ( (col>>16)&0xFF )/255.0f, ( (col>>8)&0xFF )/255.0, ( col&0xFF )/255.0 ); 2808 2809 particle = shared_ptr<Particle>(tmp); 2810 } 2811 break; 2812 case eParticleType_mobSpell: 2813 particle = shared_ptr<Particle>(new SpellParticle(lev, x, y, z, 0, 0, 0)); 2814 particle->setColor((float) xa, (float) ya, (float) za); 2815 break; 2816 case eParticleType_mobSpellAmbient: 2817 particle = shared_ptr<SpellParticle>(new SpellParticle(lev, x, y, z, 0, 0, 0)); 2818 particle->setAlpha(0.15f); 2819 particle->setColor((float) xa, (float) ya, (float) za); 2820 break; 2821 case eParticleType_spell: 2822 particle = shared_ptr<Particle>( new SpellParticle(lev, x, y, z, xa, ya, za) ); 2823 break; 2824 case eParticleType_witchMagic: 2825 { 2826 particle = shared_ptr<SpellParticle>(new SpellParticle(lev, x, y, z, xa, ya, za)); 2827 dynamic_pointer_cast<SpellParticle>(particle)->setBaseTex(9 * 16); 2828 float randBrightness = lev->random->nextFloat() * 0.5f + 0.35f; 2829 particle->setColor(1 * randBrightness, 0 * randBrightness, 1 * randBrightness); 2830 } 2831 break; 2832 case eParticleType_instantSpell: 2833 particle = shared_ptr<Particle>(new SpellParticle(lev, x, y, z, xa, ya, za)); 2834 dynamic_pointer_cast<SpellParticle>(particle)->setBaseTex(9 * 16); 2835 break; 2836 case eParticleType_note: 2837 particle = shared_ptr<Particle>( new NoteParticle(lev, x, y, z, xa, ya, za) ); 2838 break; 2839 case eParticleType_netherportal: 2840 particle = shared_ptr<Particle>( new NetherPortalParticle(lev, x, y, z, xa, ya, za) ); 2841 break; 2842 case eParticleType_ender: 2843 particle = shared_ptr<Particle>( new EnderParticle(lev, x, y, z, xa, ya, za) ); 2844 break; 2845 case eParticleType_enchantmenttable: 2846 particle = shared_ptr<Particle>(new EchantmentTableParticle(lev, x, y, z, xa, ya, za) ); 2847 break; 2848 case eParticleType_explode: 2849 particle = shared_ptr<Particle>( new ExplodeParticle(lev, x, y, z, xa, ya, za) ); 2850 break; 2851 case eParticleType_flame: 2852 particle = shared_ptr<Particle>( new FlameParticle(lev, x, y, z, xa, ya, za) ); 2853 break; 2854 case eParticleType_lava: 2855 particle = shared_ptr<Particle>( new LavaParticle(lev, x, y, z) ); 2856 break; 2857 case eParticleType_footstep: 2858 particle = shared_ptr<Particle>( new FootstepParticle(textures, lev, x, y, z) ); 2859 break; 2860 case eParticleType_splash: 2861 particle = shared_ptr<Particle>( new SplashParticle(lev, x, y, z, xa, ya, za) ); 2862 break; 2863 case eParticleType_largesmoke: 2864 particle = shared_ptr<Particle>( new SmokeParticle(lev, x, y, z, xa, ya, za, 2.5f) ); 2865 break; 2866 case eParticleType_reddust: 2867 particle = shared_ptr<Particle>( new RedDustParticle(lev, x, y, z, (float) xa, (float) ya, (float) za) ); 2868 break; 2869 case eParticleType_snowballpoof: 2870 particle = shared_ptr<Particle>( new BreakingItemParticle(lev, x, y, z, Item::snowBall, textures) ); 2871 break; 2872 case eParticleType_dripWater: 2873 particle = shared_ptr<Particle>( new DripParticle(lev, x, y, z, Material::water) ); 2874 break; 2875 case eParticleType_dripLava: 2876 particle = shared_ptr<Particle>( new DripParticle(lev, x, y, z, Material::lava) ); 2877 break; 2878 case eParticleType_snowshovel: 2879 particle = shared_ptr<Particle>( new SnowShovelParticle(lev, x, y, z, xa, ya, za) ); 2880 break; 2881 case eParticleType_slime: 2882 particle = shared_ptr<Particle>( new BreakingItemParticle(lev, x, y, z, Item::slimeBall, textures)); 2883 break; 2884 case eParticleType_heart: 2885 particle = shared_ptr<Particle>( new HeartParticle(lev, x, y, z, xa, ya, za) ); 2886 break; 2887 case eParticleType_angryVillager: 2888 particle = shared_ptr<Particle>( new HeartParticle(lev, x, y + 0.5f, z, xa, ya, za) ); 2889 particle->setMiscTex(1 + 16 * 5); 2890 particle->setColor(1, 1, 1); 2891 break; 2892 case eParticleType_happyVillager: 2893 particle = shared_ptr<Particle>( new SuspendedTownParticle(lev, x, y, z, xa, ya, za) ); 2894 particle->setMiscTex(2 + 16 * 5); 2895 particle->setColor(1, 1, 1); 2896 break; 2897 case eParticleType_dragonbreath: 2898 particle = shared_ptr<Particle>( new DragonBreathParticle(lev, x, y, z, xa, ya, za) ); 2899 break; 2900 default: 2901 if( ( eParticleType >= eParticleType_iconcrack_base ) && ( eParticleType <= eParticleType_iconcrack_last ) ) 2902 { 2903 int id = PARTICLE_CRACK_ID(eParticleType), data = PARTICLE_CRACK_DATA(eParticleType); 2904 particle = shared_ptr<Particle>(new BreakingItemParticle(lev, x, y, z, xa, ya, za, Item::items[id], textures, data)); 2905 } 2906 else if( ( eParticleType >= eParticleType_tilecrack_base ) && ( eParticleType <= eParticleType_tilecrack_last ) ) 2907 { 2908 int id = PARTICLE_CRACK_ID(eParticleType), data = PARTICLE_CRACK_DATA(eParticleType); 2909 particle = dynamic_pointer_cast<Particle>( shared_ptr<TerrainParticle>(new TerrainParticle(lev, x, y, z, xa, ya, za, Tile::tiles[id], 0, data, textures))->init(data) ); 2910 } 2911 } 2912 2913 if (particle != NULL) 2914 { 2915 mc->particleEngine->add(particle); 2916 } 2917 2918 return particle; 2919} 2920 2921void LevelRenderer::entityAdded(shared_ptr<Entity> entity) 2922{ 2923 if(entity->instanceof(eTYPE_PLAYER)) 2924 { 2925 shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity); 2926 player->prepareCustomTextures(); 2927 2928 // 4J-PB - adding these from global title storage 2929 if (player->customTextureUrl != L"") 2930 { 2931 textures->addMemTexture(player->customTextureUrl, new MobSkinMemTextureProcessor()); 2932 } 2933 if (player->customTextureUrl2 != L"") 2934 { 2935 textures->addMemTexture(player->customTextureUrl2, new MobSkinMemTextureProcessor()); 2936 } 2937 } 2938} 2939 2940void LevelRenderer::entityRemoved(shared_ptr<Entity> entity) 2941{ 2942 if(entity->instanceof(eTYPE_PLAYER)) 2943 { 2944 shared_ptr<Player> player = dynamic_pointer_cast<Player>(entity); 2945 if (player->customTextureUrl != L"") 2946 { 2947 textures->removeMemTexture(player->customTextureUrl); 2948 } 2949 if (player->customTextureUrl2 != L"") 2950 { 2951 textures->removeMemTexture(player->customTextureUrl2); 2952 } 2953 } 2954} 2955 2956void LevelRenderer::skyColorChanged() 2957{ 2958 // 4J - no longer used 2959#if 0 2960 EnterCriticalSection(&m_csDirtyChunks); 2961 for( int i = 0; i < getGlobalChunkCountForOverworld(); i++ ) 2962 { 2963 if( ( globalChunkFlags[i] & CHUNK_FLAG_NOTSKYLIT ) == 0 ) 2964 { 2965 globalChunkFlags[i] |= CHUNK_FLAG_DIRTY; 2966 } 2967 } 2968 LeaveCriticalSection(&m_csDirtyChunks); 2969#endif 2970} 2971 2972void LevelRenderer::clear() 2973{ 2974 MemoryTracker::releaseLists(chunkLists); 2975} 2976 2977void LevelRenderer::globalLevelEvent(int type, int sourceX, int sourceY, int sourceZ, int data) 2978{ 2979 Level *lev; 2980 int playerIndex = mc->player->GetXboxPad(); // 4J added 2981 lev = level[playerIndex]; 2982 2983 Random *random = lev->random; 2984 2985 switch (type) 2986 { 2987 case LevelEvent::SOUND_WITHER_BOSS_SPAWN: 2988 case LevelEvent::SOUND_DRAGON_DEATH: 2989 if (mc->cameraTargetPlayer != NULL) 2990 { 2991 // play the sound at an offset from the player 2992 double dx = sourceX - mc->cameraTargetPlayer->x; 2993 double dy = sourceY - mc->cameraTargetPlayer->y; 2994 double dz = sourceZ - mc->cameraTargetPlayer->z; 2995 2996 double len = sqrt(dx * dx + dy * dy + dz * dz); 2997 double sx = mc->cameraTargetPlayer->x; 2998 double sy = mc->cameraTargetPlayer->y; 2999 double sz = mc->cameraTargetPlayer->z; 3000 3001 if (len > 0) 3002 { 3003 sx += dx / len * 2; 3004 sy += dy / len * 2; 3005 sz += dz / len * 2; 3006 } 3007 if (type == LevelEvent::SOUND_WITHER_BOSS_SPAWN) 3008 { 3009 lev->playLocalSound(sx, sy, sz, eSoundType_MOB_WITHER_SPAWN, 1.0f, 1.0f, false); 3010 } 3011 else if (type == LevelEvent::SOUND_DRAGON_DEATH) 3012 { 3013 lev->playLocalSound(sx, sy, sz, eSoundType_MOB_ENDERDRAGON_END, 5.0f, 1.0f, false); 3014 } 3015 } 3016 break; 3017 } 3018} 3019 3020void LevelRenderer::levelEvent(shared_ptr<Player> source, int type, int x, int y, int z, int data) 3021{ 3022 int playerIndex = mc->player->GetXboxPad(); // 4J added 3023 Random *random = level[playerIndex]->random; 3024 switch (type) 3025 { 3026 //case LevelEvent::SOUND_WITHER_BOSS_SPAWN: 3027 case LevelEvent::SOUND_DRAGON_DEATH: 3028 if (mc->cameraTargetPlayer != NULL) 3029 { 3030 // play the sound at an offset from the player 3031 double dx = x - mc->cameraTargetPlayer->x; 3032 double dy = y - mc->cameraTargetPlayer->y; 3033 double dz = z - mc->cameraTargetPlayer->z; 3034 3035 double len = sqrt(dx * dx + dy * dy + dz * dz); 3036 double sx = mc->cameraTargetPlayer->x; 3037 double sy = mc->cameraTargetPlayer->y; 3038 double sz = mc->cameraTargetPlayer->z; 3039 3040 if (len > 0) 3041 { 3042 sx += (dx / len) * 2; 3043 sy += (dy / len) * 2; 3044 sz += (dz / len) * 2; 3045 } 3046 3047 level[playerIndex]->playLocalSound(sx, sy, sz, eSoundType_MOB_ENDERDRAGON_END, 5.0f, 1.0f); 3048 } 3049 break; 3050 case LevelEvent::SOUND_CLICK_FAIL: 3051 //level[playerIndex]->playSound(x, y, z, L"random.click", 1.0f, 1.2f); 3052 level[playerIndex]->playLocalSound(x, y, z, eSoundType_RANDOM_CLICK, 1.0f, 1.2f, false); 3053 break; 3054 case LevelEvent::SOUND_CLICK: 3055 level[playerIndex]->playLocalSound(x, y, z, eSoundType_RANDOM_CLICK, 1.0f, 1.0f, false); 3056 break; 3057 case LevelEvent::SOUND_LAUNCH: 3058 level[playerIndex]->playLocalSound(x, y, z, eSoundType_RANDOM_BOW, 1.0f, 1.2f, false); 3059 break; 3060 case LevelEvent::PARTICLES_SHOOT: 3061 { 3062 int xd = (data % 3) - 1; 3063 int zd = (data / 3 % 3) - 1; 3064 double xp = x + xd * 0.6 + 0.5; 3065 double yp = y + 0.5; 3066 double zp = z + zd * 0.6 + 0.5; 3067 for (int i = 0; i < 10; i++) 3068 { 3069 double pow = random->nextDouble() * 0.2 + 0.01; 3070 double xs = xp + xd * 0.01 + (random->nextDouble() - 0.5) * zd * 0.5; 3071 double ys = yp + (random->nextDouble() - 0.5) * 0.5; 3072 double zs = zp + zd * 0.01 + (random->nextDouble() - 0.5) * xd * 0.5; 3073 double xsa = xd * pow + random->nextGaussian() * 0.01; 3074 double ysa = -0.03 + random->nextGaussian() * 0.01; 3075 double zsa = zd * pow + random->nextGaussian() * 0.01; 3076 addParticle(eParticleType_smoke, xs, ys, zs, xsa, ysa, zsa); 3077 } 3078 break; 3079 } 3080 case LevelEvent::PARTICLES_EYE_OF_ENDER_DEATH: 3081 { 3082 double xp = x + 0.5; 3083 double yp = y; 3084 double zp = z + 0.5; 3085 3086 ePARTICLE_TYPE particle = PARTICLE_ICONCRACK(Item::eyeOfEnder->id,0); 3087 for (int i = 0; i < 8; i++) 3088 { 3089 addParticle(particle, xp, yp, zp, random->nextGaussian() * 0.15, random->nextDouble() * 0.2, random->nextGaussian() * .15); 3090 } 3091 for (double a = 0; a < PI * 2.0; a += PI * 0.05) 3092 { 3093 addParticle(eParticleType_ender, xp + cos(a) * 5, yp - .4, zp + sin(a) * 5, cos(a) * -5, 0, sin(a) * -5); 3094 addParticle(eParticleType_ender, xp + cos(a) * 5, yp - .4, zp + sin(a) * 5, cos(a) * -7, 0, sin(a) * -7); 3095 } 3096 3097 } 3098 break; 3099 case LevelEvent::PARTICLES_POTION_SPLASH: 3100 { 3101 double xp = x; 3102 double yp = y; 3103 double zp = z; 3104 3105 ePARTICLE_TYPE particle = PARTICLE_ICONCRACK(Item::potion->id, data); 3106 for (int i = 0; i < 8; i++) 3107 { 3108 addParticle(particle, xp, yp, zp, random->nextGaussian() * 0.15, random->nextDouble() * 0.2, random->nextGaussian() * 0.15); 3109 } 3110 3111 3112 int colorValue = Item::potion->getColor(data); 3113 3114 float red = (float) ((colorValue >> 16) & 0xff) / 255.0f; 3115 float green = (float) ((colorValue >> 8) & 0xff) / 255.0f; 3116 float blue = (float) ((colorValue >> 0) & 0xff) / 255.0f; 3117 3118 ePARTICLE_TYPE particleName = eParticleType_spell; 3119 if (Item::potion->hasInstantenousEffects(data)) 3120 { 3121 particleName = eParticleType_instantSpell; 3122 } 3123 3124 for (int i = 0; i < 100; i++) 3125 { 3126 double dist = random->nextDouble() * ThrownPotion::SPLASH_RANGE; 3127 double angle = random->nextDouble() * PI * 2; 3128 double xs = cos(angle) * dist; 3129 double ys = 0.01 + random->nextDouble() * 0.5; 3130 double zs = sin(angle) * dist; 3131 3132 shared_ptr<Particle> spellParticle = addParticleInternal(particleName, xp + xs * 0.1, yp + 0.3, zp + zs * 0.1, xs, ys, zs); 3133 if (spellParticle != NULL) 3134 { 3135 float randBrightness = 0.75f + random->nextFloat() * 0.25f; 3136 spellParticle->setColor(red * randBrightness, green * randBrightness, blue * randBrightness); 3137 spellParticle->setPower((float) dist); 3138 } 3139 } 3140 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_GLASS, 1, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f, false); 3141 } 3142 break; 3143 case LevelEvent::ENDERDRAGON_FIREBALL_SPLASH: 3144 { 3145 double xp = x; 3146 double yp = y; 3147 double zp = z; 3148 3149 ePARTICLE_TYPE particleName = eParticleType_dragonbreath; 3150 3151 for (int i = 0; i < 200; i++) 3152 { 3153 double dist = random->nextDouble() * DragonFireball::SPLASH_RANGE; 3154 double angle = random->nextDouble() * PI * 2; 3155 double xs = cos(angle) * dist; 3156 double ys = 0.01 + random->nextDouble() * 0.5; 3157 double zs = sin(angle) * dist; 3158 3159 shared_ptr<Particle> acidParticle = addParticleInternal(particleName, xp + xs * 0.1, yp + 0.3, zp + zs * 0.1, xs, ys, zs); 3160 if (acidParticle != NULL) 3161 { 3162 float randBrightness = 0.75f + random->nextFloat() * 0.25f; 3163 acidParticle->setPower((float) dist); 3164 } 3165 } 3166 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_EXPLODE, 1, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f); 3167 } 3168 break; 3169 case LevelEvent::PARTICLES_DESTROY_BLOCK: 3170 { 3171 int t = data & Tile::TILE_NUM_MASK; 3172 if (t > 0) 3173 { 3174 Tile *oldTile = Tile::tiles[t]; 3175 mc->soundEngine->play(oldTile->soundType->getBreakSound(), x + 0.5f, y + 0.5f, z + 0.5f, (oldTile->soundType->getVolume() + 1) / 2, oldTile->soundType->getPitch() * 0.8f); 3176 } 3177 3178 mc->particleEngine->destroy(x, y, z, data & Tile::TILE_NUM_MASK, (data >> Tile::TILE_NUM_SHIFT) & 0xff); 3179 break; 3180 } 3181 case LevelEvent::PARTICLES_MOBTILE_SPAWN: 3182 { 3183 for (int i = 0; i < 20; i++) 3184 { 3185 3186 double xP = x + 0.5 + (level[playerIndex]->random->nextFloat() - 0.5) * 2; 3187 double yP = y + 0.5 + (level[playerIndex]->random->nextFloat() - 0.5) * 2; 3188 double zP = z + 0.5 + (level[playerIndex]->random->nextFloat() - 0.5) * 2; 3189 3190 level[playerIndex]->addParticle(eParticleType_smoke, xP, yP, zP, 0, 0, 0); 3191 level[playerIndex]->addParticle(eParticleType_flame, xP, yP, zP, 0, 0, 0); 3192 } 3193 break; 3194 } 3195 case LevelEvent::PARTICLES_PLANT_GROWTH: 3196 DyePowderItem::addGrowthParticles(level[playerIndex], x, y, z, data); 3197 break; 3198 case LevelEvent::SOUND_OPEN_DOOR: 3199 if (Math::random() < 0.5) 3200 { 3201 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_DOOR_OPEN, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f, false); 3202 } else { 3203 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_DOOR_CLOSE, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f, false); 3204 } 3205 break; 3206 case LevelEvent::SOUND_FIZZ: 3207 level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_FIZZ, 0.5f, 2.6f + (random->nextFloat() - random->nextFloat()) * 0.8f, false); 3208 break; 3209 case LevelEvent::SOUND_ANVIL_BROKEN: 3210 level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_ANVIL_BREAK, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f, false); 3211 break; 3212 case LevelEvent::SOUND_ANVIL_USED: 3213 level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_ANVIL_USE, 1.0f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f, false); 3214 break; 3215 case LevelEvent::SOUND_ANVIL_LAND: 3216 level[playerIndex]->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_ANVIL_LAND, 0.3f, level[playerIndex]->random->nextFloat() * 0.1f + 0.9f, false); 3217 break; 3218 case LevelEvent::SOUND_PLAY_RECORDING: 3219 { 3220 RecordingItem *rci = dynamic_cast<RecordingItem *>(Item::items[data]); 3221 if (rci != NULL) 3222 { 3223 level[playerIndex]->playStreamingMusic(rci->recording, x, y, z); 3224 } 3225 else 3226 { 3227 // 4J-PB - only play streaming music if there isn't already some playing - the CD playing may have finished, and game music started playing already 3228 if(!mc->soundEngine->GetIsPlayingStreamingGameMusic()) 3229 { 3230 level[playerIndex]->playStreamingMusic(L"", x, y, z); // 4J - used to pass NULL, but using empty string here now instead 3231 } 3232 } 3233 mc->localplayers[playerIndex]->updateRichPresence(); 3234 } 3235 break; 3236 // 4J - new level event sounds brought forward from 1.2.3 3237 case LevelEvent::SOUND_GHAST_WARNING: 3238 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_GHAST_CHARGE, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f, false, 80.0f); 3239 break; 3240 case LevelEvent::SOUND_GHAST_FIREBALL: 3241 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_GHAST_FIREBALL, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f, false, 80.0f); 3242 break; 3243 case LevelEvent::SOUND_ZOMBIE_WOODEN_DOOR: 3244 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_WOOD, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); 3245 break; 3246 case LevelEvent::SOUND_ZOMBIE_DOOR_CRASH: 3247 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_WOOD_BREAK, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); 3248 break; 3249 case LevelEvent::SOUND_ZOMBIE_IRON_DOOR: 3250 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_METAL, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); 3251 break; 3252 case LevelEvent::SOUND_BLAZE_FIREBALL: 3253 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_GHAST_FIREBALL, 2, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);//, false); 3254 break; 3255 case LevelEvent::SOUND_WITHER_BOSS_SHOOT: 3256 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_WITHER_SHOOT, 2, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);//, false); 3257 break; 3258 case LevelEvent::SOUND_ZOMBIE_INFECTED: 3259 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_INFECT, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);//, false); 3260 break; 3261 case LevelEvent::SOUND_ZOMBIE_CONVERTED: 3262 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_ZOMBIE_UNFECT, 2.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f);//, false); 3263 break; 3264 // 4J Added TU9 to fix #77475 - TU9: Content: Art: Dragon egg teleport particle effect isn't present. 3265 case LevelEvent::END_EGG_TELEPORT: 3266 // 4J Added to show the paricles when the End egg teleports after being attacked 3267 EggTile::generateTeleportParticles(level[playerIndex],x,y,z,data); 3268 break; 3269 case LevelEvent::SOUND_BAT_LIFTOFF: 3270 level[playerIndex]->playLocalSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_MOB_BAT_TAKEOFF, .05f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); 3271 break; 3272 } 3273 3274} 3275 3276void LevelRenderer::destroyTileProgress(int id, int x, int y, int z, int progress) 3277{ 3278 if (progress < 0 || progress >= 10) 3279 { 3280 AUTO_VAR(it, destroyingBlocks.find(id)); 3281 if(it != destroyingBlocks.end()) 3282 { 3283 delete it->second; 3284 destroyingBlocks.erase(it); 3285 } 3286 //destroyingBlocks.remove(id); 3287 } 3288 else 3289 { 3290 BlockDestructionProgress *entry = NULL; 3291 3292 AUTO_VAR(it, destroyingBlocks.find(id)); 3293 if(it != destroyingBlocks.end()) entry = it->second; 3294 3295 if (entry == NULL || entry->getX() != x || entry->getY() != y || entry->getZ() != z) 3296 { 3297 entry = new BlockDestructionProgress(id, x, y, z); 3298 destroyingBlocks.insert( unordered_map<int, BlockDestructionProgress *>::value_type(id, entry) ); 3299 } 3300 3301 entry->setProgress(progress); 3302 entry->updateTick(ticks); 3303 } 3304} 3305 3306void LevelRenderer::registerTextures(IconRegister *iconRegister) 3307{ 3308 breakingTextures = new Icon*[10]; 3309 3310 for (int i = 0; i < 10; i++) 3311 { 3312 breakingTextures[i] = iconRegister->registerIcon(L"destroy_" + _toString(i) ); 3313 } 3314} 3315 3316// Gets a dimension index (0, 1, or 2) from an id ( 0, -1, 1) 3317int LevelRenderer::getDimensionIndexFromId(int id) 3318{ 3319 return ( 3 - id ) % 3; 3320} 3321 3322// 4J - added for new render list handling. Render lists used to be allocated per chunk, but these are now allocated per fixed chunk position 3323// in our (now finite) maps. 3324int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, Level *level) 3325{ 3326 return getGlobalIndexForChunk(x,y,z,level->dimension->id); 3327} 3328 3329int LevelRenderer::getGlobalIndexForChunk(int x, int y, int z, int dimensionId) 3330{ 3331 int dimIdx = getDimensionIndexFromId(dimensionId); 3332 int xx = ( x / CHUNK_XZSIZE ) + ( MAX_LEVEL_RENDER_SIZE[dimIdx] / 2 ); 3333 int yy = y / CHUNK_SIZE; 3334 int zz = ( z / CHUNK_XZSIZE ) + ( MAX_LEVEL_RENDER_SIZE[dimIdx] / 2 ); 3335 3336 if( ( xx < 0 ) || ( xx >= MAX_LEVEL_RENDER_SIZE[dimIdx] ) ) return -1; 3337 if( ( zz < 0 ) || ( zz >= MAX_LEVEL_RENDER_SIZE[dimIdx] ) ) return -1; 3338 if( ( yy < 0 ) || ( yy >= CHUNK_Y_COUNT ) ) return -1; 3339 3340 int dimOffset = DIMENSION_OFFSETS[dimIdx]; 3341 3342 int offset = dimOffset; // Offset caused by current dimension 3343 offset += ( zz * MAX_LEVEL_RENDER_SIZE[dimIdx] + xx ) * CHUNK_Y_COUNT; // Offset by x/z pos 3344 offset += yy; // Offset by y pos 3345 3346 return offset; 3347} 3348 3349bool LevelRenderer::isGlobalIndexInSameDimension( int idx, Level *level) 3350{ 3351 int dim = getDimensionIndexFromId(level->dimension->id); 3352 int idxDim = 0; 3353 if( idx >= DIMENSION_OFFSETS[2] ) idxDim = 2; 3354 else if ( idx >= DIMENSION_OFFSETS[1] ) idxDim = 1; 3355 return (dim == idxDim); 3356} 3357 3358int LevelRenderer::getGlobalChunkCount() 3359{ 3360 return ( MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT ) + 3361 ( MAX_LEVEL_RENDER_SIZE[1] * MAX_LEVEL_RENDER_SIZE[1] * CHUNK_Y_COUNT ) + 3362 ( MAX_LEVEL_RENDER_SIZE[2] * MAX_LEVEL_RENDER_SIZE[2] * CHUNK_Y_COUNT ); 3363} 3364 3365int LevelRenderer::getGlobalChunkCountForOverworld() 3366{ 3367 return ( MAX_LEVEL_RENDER_SIZE[0] * MAX_LEVEL_RENDER_SIZE[0] * CHUNK_Y_COUNT ); 3368} 3369 3370unsigned char LevelRenderer::getGlobalChunkFlags(int x, int y, int z, Level *level) 3371{ 3372 int index = getGlobalIndexForChunk(x, y, z, level); 3373 if( index == -1 ) 3374 { 3375 return 0; 3376 } 3377 else 3378 { 3379 return globalChunkFlags[ index ]; 3380 } 3381} 3382 3383void LevelRenderer::setGlobalChunkFlags(int x, int y, int z, Level *level, unsigned char flags) 3384{ 3385 int index = getGlobalIndexForChunk(x, y, z, level); 3386 if( index != -1 ) 3387 { 3388#ifdef _LARGE_WORLDS 3389 EnterCriticalSection(&m_csChunkFlags); 3390#endif 3391 globalChunkFlags[ index ] = flags; 3392#ifdef _LARGE_WORLDS 3393 LeaveCriticalSection(&m_csChunkFlags); 3394#endif 3395 } 3396} 3397 3398void LevelRenderer::setGlobalChunkFlag(int index, unsigned char flag, unsigned char shift) 3399{ 3400 unsigned char sflag = flag << shift; 3401 3402 if( index != -1 ) 3403 { 3404#ifdef _LARGE_WORLDS 3405 EnterCriticalSection(&m_csChunkFlags); 3406#endif 3407 globalChunkFlags[ index ] |= sflag; 3408#ifdef _LARGE_WORLDS 3409 LeaveCriticalSection(&m_csChunkFlags); 3410#endif 3411 } 3412} 3413 3414void LevelRenderer::setGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift) 3415{ 3416 unsigned char sflag = flag << shift; 3417 int index = getGlobalIndexForChunk(x, y, z, level); 3418 if( index != -1 ) 3419 { 3420#ifdef _LARGE_WORLDS 3421 EnterCriticalSection(&m_csChunkFlags); 3422#endif 3423 globalChunkFlags[ index ] |= sflag; 3424#ifdef _LARGE_WORLDS 3425 LeaveCriticalSection(&m_csChunkFlags); 3426#endif 3427 } 3428} 3429 3430void LevelRenderer::clearGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift) 3431{ 3432 unsigned char sflag = flag << shift; 3433 int index = getGlobalIndexForChunk(x, y, z, level); 3434 if( index != -1 ) 3435 { 3436#ifdef _LARGE_WORLDS 3437 EnterCriticalSection(&m_csChunkFlags); 3438#endif 3439 globalChunkFlags[ index ] &= ~sflag; 3440#ifdef _LARGE_WORLDS 3441 LeaveCriticalSection(&m_csChunkFlags); 3442#endif 3443 } 3444} 3445 3446bool LevelRenderer::getGlobalChunkFlag(int x, int y, int z, Level *level, unsigned char flag, unsigned char shift) 3447{ 3448 unsigned char sflag = flag << shift; 3449 int index = getGlobalIndexForChunk(x, y, z, level); 3450 if( index == -1 ) 3451 { 3452 return false; 3453 } 3454 else 3455 { 3456 return ( globalChunkFlags[ index ] & sflag ) == sflag; 3457 } 3458} 3459 3460unsigned char LevelRenderer::incGlobalChunkRefCount(int x, int y, int z, Level *level) 3461{ 3462 int index = getGlobalIndexForChunk(x, y, z, level); 3463 if( index != -1 ) 3464 { 3465 unsigned char flags = globalChunkFlags[ index ]; 3466 unsigned char refCount = (flags >> CHUNK_FLAG_REF_SHIFT ) & CHUNK_FLAG_REF_MASK; 3467 refCount++; 3468 flags &= ~(CHUNK_FLAG_REF_MASK<<CHUNK_FLAG_REF_SHIFT); 3469 flags |= refCount << CHUNK_FLAG_REF_SHIFT; 3470 globalChunkFlags[ index ] = flags; 3471 3472 return refCount; 3473 } 3474 else 3475 { 3476 return 0; 3477 } 3478 3479} 3480 3481unsigned char LevelRenderer::decGlobalChunkRefCount(int x, int y, int z, Level *level) 3482{ 3483 int index = getGlobalIndexForChunk(x, y, z, level); 3484 if( index != -1 ) 3485 { 3486 unsigned char flags = globalChunkFlags[ index ]; 3487 unsigned char refCount = (flags >> CHUNK_FLAG_REF_SHIFT ) & CHUNK_FLAG_REF_MASK; 3488 refCount--; 3489 flags &= ~(CHUNK_FLAG_REF_MASK<<CHUNK_FLAG_REF_SHIFT); 3490 flags |= refCount << CHUNK_FLAG_REF_SHIFT; 3491 globalChunkFlags[ index ] = flags; 3492 3493 return refCount; 3494 } 3495 else 3496 { 3497 return 0; 3498 } 3499} 3500 3501// 4J added 3502void LevelRenderer::fullyFlagRenderableTileEntitiesToBeRemoved() 3503{ 3504 EnterCriticalSection(&m_csRenderableTileEntities); 3505 AUTO_VAR(itChunkEnd, renderableTileEntities.end()); 3506 for (AUTO_VAR(it, renderableTileEntities.begin()); it != itChunkEnd; it++) 3507 { 3508 AUTO_VAR(itTEEnd, it->second.end()); 3509 for( AUTO_VAR(it2, it->second.begin()); it2 != itTEEnd; it2++ ) 3510 { 3511 (*it2)->upgradeRenderRemoveStage(); 3512 } 3513 } 3514 LeaveCriticalSection(&m_csRenderableTileEntities); 3515} 3516 3517LevelRenderer::DestroyedTileManager::RecentTile::RecentTile(int x, int y, int z, Level *level) : x(x), y(y), z(z), level(level) 3518{ 3519 timeout_ticks = 20; 3520 rebuilt = false; 3521} 3522 3523LevelRenderer::DestroyedTileManager::RecentTile::~RecentTile() 3524{ 3525 for( AUTO_VAR(it, boxes.begin()); it!= boxes.end(); it++ ) 3526 { 3527 delete *it; 3528 } 3529} 3530 3531LevelRenderer::DestroyedTileManager::DestroyedTileManager() 3532{ 3533 InitializeCriticalSection(&m_csDestroyedTiles); 3534} 3535 3536LevelRenderer::DestroyedTileManager::~DestroyedTileManager() 3537{ 3538 DeleteCriticalSection(&m_csDestroyedTiles); 3539 for( unsigned int i = 0; i < m_destroyedTiles.size(); i++ ) 3540 { 3541 delete m_destroyedTiles[i]; 3542 } 3543} 3544 3545 3546// For game to let this manager know that a tile is about to be destroyed (must be called before it actually is) 3547void LevelRenderer::DestroyedTileManager::destroyingTileAt( Level *level, int x, int y, int z ) 3548{ 3549 EnterCriticalSection(&m_csDestroyedTiles); 3550 3551 // Store a list of AABBs that the tile to be destroyed would have made, before we go and destroy it. This 3552 // is made slightly more complicated as the addAABBs method for tiles adds temporary AABBs and we need permanent 3553 // ones, so make a temporary list and then copy over 3554 3555 RecentTile *recentTile = new RecentTile(x, y, z, level); 3556 AABB *box = AABB::newTemp((float)x, (float)y, (float)z, (float)(x+1), (float)(y+1), (float)(z+1)); 3557 Tile *tile = Tile::tiles[level->getTile(x, y, z)]; 3558 3559 if (tile != NULL) 3560 { 3561 tile->addAABBs(level, x, y, z, box, &recentTile->boxes, nullptr); 3562 } 3563 3564 // Make these temporary AABBs into permanently allocated AABBs 3565 for( unsigned int i = 0; i < recentTile->boxes.size(); i++ ) 3566 { 3567 recentTile->boxes[i] = AABB::newPermanent(recentTile->boxes[i]->x0, 3568 recentTile->boxes[i]->y0, 3569 recentTile->boxes[i]->z0, 3570 recentTile->boxes[i]->x1, 3571 recentTile->boxes[i]->y1, 3572 recentTile->boxes[i]->z1); 3573 } 3574 3575 m_destroyedTiles.push_back( recentTile ); 3576 3577 LeaveCriticalSection(&m_csDestroyedTiles); 3578} 3579 3580// For chunk rebuilding to inform the manager that a chunk (a 16x16x16 tile render chunk) has been updated 3581void LevelRenderer::DestroyedTileManager::updatedChunkAt(Level *level, int x, int y, int z, int veryNearCount) 3582{ 3583 EnterCriticalSection(&m_csDestroyedTiles); 3584 3585 // There's 2 stages to this. This function is called when a renderer chunk has been rebuilt, but that chunk's render data might be grouped atomically with 3586 // changes to other very near chunks. Therefore, we don't want to consider the render data to be fully updated until the chunk that it is in has been 3587 // rebuilt, AND there aren't any very near things waiting to be rebuilt. 3588 3589 // First pass through - see if any tiles are within the chunk which is being rebuilt, and mark up by setting their rebuilt flag 3590 bool printed = false; 3591 for( unsigned int i = 0; i < m_destroyedTiles.size(); i++) 3592 { 3593 if( ( m_destroyedTiles[i]->level == level ) && 3594 ( m_destroyedTiles[i]->x >= x ) && ( m_destroyedTiles[i]->x < ( x + 16 ) ) && 3595 ( m_destroyedTiles[i]->y >= y ) && ( m_destroyedTiles[i]->y < ( y + 16 ) ) && 3596 ( m_destroyedTiles[i]->z >= z ) && ( m_destroyedTiles[i]->z < ( z + 16 ) ) ) 3597 { 3598 printed = true; 3599 m_destroyedTiles[i]->rebuilt = true; 3600 } 3601 } 3602 3603 // Now go through every tile that has been marked up as already being rebuilt, and fully remove it once there aren't going to be any more 3604 // very near chunks. This might not happen on the same call to this function that rebuilt the chunk with the tile in. 3605 if( veryNearCount <= 1 ) 3606 { 3607 for( unsigned int i = 0; i < m_destroyedTiles.size(); ) 3608 { 3609 if( m_destroyedTiles[i]->rebuilt ) 3610 { 3611 printed = true; 3612 delete m_destroyedTiles[i]; 3613 m_destroyedTiles[i] = m_destroyedTiles[m_destroyedTiles.size() - 1]; 3614 m_destroyedTiles.pop_back(); 3615 } 3616 else 3617 { 3618 i++; 3619 } 3620 } 3621 } 3622 3623 LeaveCriticalSection(&m_csDestroyedTiles); 3624} 3625 3626// For game to get any AABBs that the user should be colliding with as render data has not yet been updated 3627void LevelRenderer::DestroyedTileManager::addAABBs( Level *level, AABB *box, AABBList *boxes ) 3628{ 3629 EnterCriticalSection(&m_csDestroyedTiles); 3630 3631 for( unsigned int i = 0; i < m_destroyedTiles.size(); i++ ) 3632 { 3633 if( m_destroyedTiles[i]->level == level ) 3634 { 3635 for( unsigned int j = 0; j < m_destroyedTiles[i]->boxes.size(); j++ ) 3636 { 3637 // If we find any AABBs intersecting the region we are interested in, add them to the output list, making a temp AABB copy so that we can destroy our own copy 3638 // without worrying about the lifespan of the copy we've passed out 3639 if( m_destroyedTiles[i]->boxes[j]->intersects( box ) ) 3640 { 3641 boxes->push_back(AABB::newTemp( m_destroyedTiles[i]->boxes[j]->x0, 3642 m_destroyedTiles[i]->boxes[j]->y0, 3643 m_destroyedTiles[i]->boxes[j]->z0, 3644 m_destroyedTiles[i]->boxes[j]->x1, 3645 m_destroyedTiles[i]->boxes[j]->y1, 3646 m_destroyedTiles[i]->boxes[j]->z1 ) ); 3647 } 3648 } 3649 } 3650 } 3651 3652 LeaveCriticalSection(&m_csDestroyedTiles); 3653} 3654 3655void LevelRenderer::DestroyedTileManager::tick() 3656{ 3657 EnterCriticalSection(&m_csDestroyedTiles); 3658 3659 // Remove any tiles that have timed out 3660 for( unsigned int i = 0; i < m_destroyedTiles.size(); ) 3661 { 3662 if( --m_destroyedTiles[i]->timeout_ticks == 0 ) 3663 { 3664 delete m_destroyedTiles[i]; 3665 m_destroyedTiles[i] = m_destroyedTiles[m_destroyedTiles.size() - 1]; 3666 m_destroyedTiles.pop_back(); 3667 } 3668 else 3669 { 3670 i++; 3671 } 3672 } 3673 3674 LeaveCriticalSection(&m_csDestroyedTiles); 3675} 3676 3677#ifdef _LARGE_WORLDS 3678void LevelRenderer::staticCtor() 3679{ 3680 s_rebuildCompleteEvents = new C4JThread::EventArray(MAX_CHUNK_REBUILD_THREADS); 3681 char threadName[256]; 3682 for(unsigned int i = 0; i < MAX_CHUNK_REBUILD_THREADS; ++i) 3683 { 3684 sprintf(threadName,"Rebuild Chunk Thread %d\n",i); 3685 rebuildThreads[i] = new C4JThread(rebuildChunkThreadProc,(void *)i,threadName); 3686 3687 s_activationEventA[i] = new C4JThread::Event(); 3688 3689 // Threads 1,3 and 5 are generally idle so use them 3690 if((i%3) == 0) rebuildThreads[i]->SetProcessor(CPU_CORE_CHUNK_REBUILD_A); 3691 else if((i%3) == 1) 3692 { 3693 rebuildThreads[i]->SetProcessor(CPU_CORE_CHUNK_REBUILD_B); 3694#ifdef __ORBIS__ 3695 rebuildThreads[i]->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); // On Orbis, this core is also used for Matching 2, and that priority of that seems to be always at default no matter what we set it to. Prioritise this below Matching 2. 3696#endif 3697 } 3698 else if((i%3) == 2) rebuildThreads[i]->SetProcessor(CPU_CORE_CHUNK_REBUILD_C); 3699 3700 //ResumeThread( saveThreads[j] ); 3701 rebuildThreads[i]->Run(); 3702 } 3703} 3704 3705int LevelRenderer::rebuildChunkThreadProc(LPVOID lpParam) 3706{ 3707 Vec3::CreateNewThreadStorage(); 3708 AABB::CreateNewThreadStorage(); 3709 IntCache::CreateNewThreadStorage(); 3710 Tesselator::CreateNewThreadStorage(1024*1024); 3711 RenderManager.InitialiseContext(); 3712 Chunk::CreateNewThreadStorage(); 3713 Tile::CreateNewThreadStorage(); 3714 3715 int index = (size_t)lpParam; 3716 3717 while(true) 3718 { 3719 s_activationEventA[index]->WaitForSignal(INFINITE); 3720 3721 //app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); 3722 permaChunk[index + 1].rebuild(); 3723 3724 // Inform the producer thread that we are done with this chunk 3725 s_rebuildCompleteEvents->Set(index); 3726 } 3727 3728 return 0; 3729} 3730#endif 3731 3732// This is called when chunks require rebuilding, but they haven't been added individually to the dirtyChunksLockFreeStack. Once in this 3733// state, the rebuilding thread will keep assuming there are dirty chunks until it has had a full pass through the chunks and found no dirty ones 3734void LevelRenderer::nonStackDirtyChunksAdded() 3735{ 3736 dirtyChunksLockFreeStack.Push((int *)1); 3737} 3738 3739// 4J - for test purposes, check all chunks that are currently present for the player. Currently this is implemented to do tests to identify missing client chunks in flat worlds, but 3740// this could be extended to do other kinds of automated testing. Returns the number of chunks that are present, so that from the calling function we can determine when chunks have 3741// finished loading/generating round the current location. 3742int LevelRenderer::checkAllPresentChunks(bool *faultFound) 3743{ 3744 int playerIndex = mc->player->GetXboxPad(); // 4J added 3745 3746 int presentCount = 0; 3747 ClipChunk *pClipChunk = chunks[playerIndex].data; 3748 for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ ) 3749 { 3750 if(pClipChunk->chunk->y == 0 ) 3751 { 3752 bool chunkPresent = level[0]->reallyHasChunk(pClipChunk->chunk->x>>4,pClipChunk->chunk->z>>4); 3753 if( chunkPresent ) 3754 { 3755 presentCount++; 3756 LevelChunk *levelChunk = level[0]->getChunk(pClipChunk->chunk->x>>4,pClipChunk->chunk->z>>4); 3757 3758 for( int cx = 4; cx <= 12; cx++ ) 3759 { 3760 for( int cz = 4; cz <= 12; cz++ ) 3761 { 3762 int t0 = levelChunk->getTile(cx, 0, cz); 3763 if( ( t0 != Tile::unbreakable_Id ) && (t0 != Tile::dirt_Id) ) 3764 { 3765 *faultFound = true; 3766 } 3767 } 3768 } 3769 } 3770 } 3771 } 3772 return presentCount; 3773} 3774