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