the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
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