mirror of
https://github.com/smartcmd/MinecraftConsoles.git
synced 2026-08-20 09:57:09 +00:00
Optimize rendering checks and packing as well as min/max utilisation
Tesselator.h: Branchless min/max in Bounds via Min/Max; const ref on addBounds Tesselator.cpp: clamp for color clamping; Min/Max initializer lists in packCompactQuad min/max finding and du/dv clamping Chunk.cpp: bool[256] lookup table for occluder tile IDs in occlusion culling; [[unlikely]] hints on rebuild inner loop early-outs LevelRenderer.cpp: __restrict + local caching in frustum clip(); [[likely]]/[[unlikely]] on chunk render loop continues
This commit is contained in:
parent
dc91a1dad0
commit
74a2c2e896
|
|
@ -254,6 +254,14 @@ void Chunk::rebuild()
|
||||||
// (2) if any of the tiles can be quickly determined to not need rendering because they are in the middle of other tiles and
|
// (2) if any of the tiles can be quickly determined to not need rendering because they are in the middle of other tiles and
|
||||||
// so can't be seen. A large amount (> 60% in tests) of tiles that call tesselateInWorld in the unoptimised version
|
// so can't be seen. A large amount (> 60% in tests) of tiles that call tesselateInWorld in the unoptimised version
|
||||||
// of this function fall into this category. By far the largest category of these are tiles in solid regions of rock.
|
// of this function fall into this category. By far the largest category of these are tiles in solid regions of rock.
|
||||||
|
// Build a lookup table for occluder tile IDs to replace repeated 4-way comparisons with a single table lookup per neighbor, eliminating ~24 branch comparisons per interior tile.
|
||||||
|
bool isOccluder[256];
|
||||||
|
std::memset(isOccluder, 0, sizeof(isOccluder));
|
||||||
|
isOccluder[Tile::stone_Id] = true;
|
||||||
|
isOccluder[Tile::dirt_Id] = true;
|
||||||
|
isOccluder[Tile::unbreakable_Id] = true;
|
||||||
|
isOccluder[255] = true;
|
||||||
|
|
||||||
bool empty = true;
|
bool empty = true;
|
||||||
for( int yy = y0; yy < y1; yy++ )
|
for( int yy = y0; yy < y1; yy++ )
|
||||||
{
|
{
|
||||||
|
|
@ -279,17 +287,12 @@ void Chunk::rebuild()
|
||||||
if(( xx == 0 ) || ( xx == 15 )) continue;
|
if(( xx == 0 ) || ( xx == 15 )) continue;
|
||||||
if(( zz == 0 ) || ( zz == 15 )) continue;
|
if(( zz == 0 ) || ( zz == 15 )) continue;
|
||||||
|
|
||||||
// Establish whether this tile and its neighbours are all made of rock, dirt, unbreakable tiles, or have already
|
// Establish whether this tile and its neighbours are all occluders using lookup table
|
||||||
// been determined to meet this criteria themselves and have a tile of 255 set.
|
if( !isOccluder[tileId] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx - 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
tileId = tileIds[ offset + ( ( ( xx - 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ];
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx + 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz - 1 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
tileId = tileIds[ offset + ( ( ( xx + 1 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 )) ];
|
if( !isOccluder[ tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 1 ) << 7 ) | ( indexY + 0 )) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz - 1 ) << 7 ) | ( indexY + 0 )) ];
|
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
tileId = tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 1 ) << 7 ) | ( indexY + 0 )) ];
|
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
// Treat the bottom of the world differently - we shouldn't ever be able to look up at this, so consider tiles as invisible
|
// Treat the bottom of the world differently - we shouldn't ever be able to look up at this, so consider tiles as invisible
|
||||||
// if they are surrounded on sides other than the bottom
|
// if they are surrounded on sides other than the bottom
|
||||||
if( yy > 0 )
|
if( yy > 0 )
|
||||||
|
|
@ -301,8 +304,7 @@ void Chunk::rebuild()
|
||||||
indexYMinusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
indexYMinusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
||||||
yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
||||||
}
|
}
|
||||||
tileId = tileIds[ yMinusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYMinusOne ) ];
|
if( !isOccluder[ tileIds[ yMinusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYMinusOne ) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
}
|
}
|
||||||
int indexYPlusOne = yy + 1;
|
int indexYPlusOne = yy + 1;
|
||||||
int yPlusOneOffset = 0;
|
int yPlusOneOffset = 0;
|
||||||
|
|
@ -311,8 +313,7 @@ void Chunk::rebuild()
|
||||||
indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
|
||||||
yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES;
|
||||||
}
|
}
|
||||||
tileId = tileIds[ yPlusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYPlusOne ) ];
|
if( !isOccluder[ tileIds[ yPlusOneOffset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | indexYPlusOne ) ] ] ) continue;
|
||||||
if( !( ( tileId == Tile::stone_Id ) || ( tileId == Tile::dirt_Id ) || ( tileId == Tile::unbreakable_Id ) || ( tileId == 255) ) ) continue;
|
|
||||||
|
|
||||||
// This tile is surrounded. Flag it as not requiring to be rendered by setting its id to 255.
|
// This tile is surrounded. Flag it as not requiring to be rendered by setting its id to 255.
|
||||||
tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ] = 0xff;
|
tileIds[ offset + ( ( ( xx + 0 ) << 11 ) | ( ( zz + 0 ) << 7 ) | ( indexY + 0 ) ) ] = 0xff;
|
||||||
|
|
@ -375,9 +376,9 @@ void Chunk::rebuild()
|
||||||
// 4J - get tile from those copied into our local array in earlier optimisation
|
// 4J - get tile from those copied into our local array in earlier optimisation
|
||||||
unsigned char tileId = tileIds[ offset + ( ( ( x - x0 ) << 11 ) | ( ( z - z0 ) << 7 ) | indexY) ];
|
unsigned char tileId = tileIds[ offset + ( ( ( x - x0 ) << 11 ) | ( ( z - z0 ) << 7 ) | indexY) ];
|
||||||
// If flagged as not visible, drop out straight away
|
// If flagged as not visible, drop out straight away
|
||||||
if( tileId == 0xff ) continue;
|
if( tileId == 0xff ) [[unlikely]] continue;
|
||||||
// int tileId = region->getTile(x,y,z);
|
// int tileId = region->getTile(x,y,z);
|
||||||
if (tileId > 0)
|
if (tileId > 0) [[unlikely]]
|
||||||
{
|
{
|
||||||
if (!started)
|
if (!started)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -805,8 +805,8 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha)
|
||||||
unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer;
|
unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer;
|
||||||
for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ )
|
for( int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++ )
|
||||||
{
|
{
|
||||||
if( !pClipChunk->visible ) continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set
|
if( !pClipChunk->visible ) [[likely]] continue; // This will be set if the chunk isn't visible, or isn't compiled, or has both empty flags set
|
||||||
if( pClipChunk->globalIdx == -1 ) continue; // Not sure if we should ever encounter this... TODO check
|
if( pClipChunk->globalIdx == -1 ) [[unlikely]] continue; // Not sure if we should ever encounter this... TODO check
|
||||||
if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty
|
if( ( globalChunkFlags[pClipChunk->globalIdx] & emptyFlag ) == emptyFlag ) continue; // Check that this particular layer isn't empty
|
||||||
|
|
||||||
// List can be calculated directly from the chunk's global idex
|
// List can be calculated directly from the chunk's global idex
|
||||||
|
|
@ -2426,18 +2426,23 @@ void LevelRenderer::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1
|
||||||
setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1, level);
|
setDirty(x0 - 1, y0 - 1, z0 - 1, x1 + 1, y1 + 1, z1 + 1, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inline clip(float *bb, float *frustum)
|
bool inline clip(float * __restrict bb, float * __restrict frustum)
|
||||||
{
|
{
|
||||||
|
// Pre-load AABB corners to avoid repeated memory loads
|
||||||
|
const float x0 = bb[0], y0 = bb[1], z0 = bb[2];
|
||||||
|
const float x1 = bb[3], y1 = bb[4], z1 = bb[5];
|
||||||
|
|
||||||
for (int i = 0; i < 6; ++i, frustum += 4)
|
for (int i = 0; i < 6; ++i, frustum += 4)
|
||||||
{
|
{
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
const float a = frustum[0], b = frustum[1], c = frustum[2], d = frustum[3];
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y0 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
if (a * x1 + b * y0 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[2]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y1 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x1 + b * y1 + c * z0 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[1]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y0 + c * z1 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[0]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x1 + b * y0 + c * z1 + d > 0) continue;
|
||||||
if (frustum[0] * (bb[3]) + frustum[1] * (bb[4]) + frustum[2] * (bb[5]) + frustum[3] > 0) continue;
|
if (a * x0 + b * y1 + c * z1 + d > 0) continue;
|
||||||
|
if (a * x1 + b * y1 + c * z1 + d > 0) continue;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -310,20 +310,16 @@ void Tesselator::color(int r, int g, int b)
|
||||||
|
|
||||||
void Tesselator::color(int r, int g, int b, int a)
|
void Tesselator::color(int r, int g, int b, int a)
|
||||||
{
|
{
|
||||||
if (_noColor) return;
|
if (_noColor) return;
|
||||||
|
|
||||||
if (r > 255) r = 255;
|
r = std::clamp(r, 0, 255);
|
||||||
if (g > 255) g = 255;
|
g = std::clamp(g, 0, 255);
|
||||||
if (b > 255) b = 255;
|
b = std::clamp(b, 0, 255);
|
||||||
if (a > 255) a = 255;
|
a = std::clamp(a, 0, 255);
|
||||||
if (r < 0) r = 0;
|
|
||||||
if (g < 0) g = 0;
|
|
||||||
if (b < 0) b = 0;
|
|
||||||
if (a < 0) a = 0;
|
|
||||||
|
|
||||||
hasColor = true;
|
hasColor = true;
|
||||||
// 4J - removed little-endian option
|
// 4J - removed little-endian option
|
||||||
col = (r << 24) | (g << 16) | (b << 8) | (a);
|
col = (r << 24) | (g << 16) | (b << 8) | (a);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tesselator::color(byte r, byte g, byte b)
|
void Tesselator::color(byte r, byte g, byte b)
|
||||||
|
|
@ -381,26 +377,15 @@ void Tesselator::packCompactQuad()
|
||||||
m_iz[i] += 16 * 128;
|
m_iz[i] += 16 * 128;
|
||||||
}
|
}
|
||||||
// Find min x/y/z
|
// Find min x/y/z
|
||||||
unsigned int minx = m_ix[0];
|
unsigned int minx = std::min<unsigned int>({m_ix[0], m_ix[1], m_ix[2], m_ix[3]});
|
||||||
unsigned int miny = m_iy[0];
|
unsigned int miny = std::min<unsigned int>({m_iy[0], m_iy[1], m_iy[2], m_iy[3]});
|
||||||
unsigned int minz = m_iz[0];
|
unsigned int minz = std::min<unsigned int>({m_iz[0], m_iz[1], m_iz[2], m_iz[3]});
|
||||||
for( int i = 1; i < 4; i++ )
|
|
||||||
{
|
|
||||||
if( m_ix[i] < minx ) minx = m_ix[i];
|
|
||||||
if( m_iy[i] < miny ) miny = m_iy[i];
|
|
||||||
if( m_iz[i] < minz ) minz = m_iz[i];
|
|
||||||
}
|
|
||||||
// Everything has been scaled by a factor of 128 to get it into an int, and so
|
// Everything has been scaled by a factor of 128 to get it into an int, and so
|
||||||
// the minimum now should be in the range of (0->32) * 128. Get the base x/y/z
|
// the minimum now should be in the range of (0->32) * 128. Get the base x/y/z
|
||||||
// that our quad will be referenced from now, which can be stored in 5 bits
|
// that our quad will be referenced from now, which can be stored in 5 bits
|
||||||
unsigned int basex = ( minx >> 7 );
|
unsigned int basex = std::min<unsigned int>(minx >> 7, 31u);
|
||||||
unsigned int basey = ( miny >> 7 );
|
unsigned int basey = std::min<unsigned int>(miny >> 7, 31u);
|
||||||
unsigned int basez = ( minz >> 7 );
|
unsigned int basez = std::min<unsigned int>(minz >> 7, 31u);
|
||||||
// If the min is 32, then this whole quad must be in that plane - make the min 15 instead so
|
|
||||||
// we can still offset from that with our delta to get to the exact edge
|
|
||||||
if( basex == 32 ) basex = 31;
|
|
||||||
if( basey == 32 ) basey = 31;
|
|
||||||
if( basez == 32 ) basez = 31;
|
|
||||||
// Now get deltas to each vertex - these have an 8-bit range so they can span a
|
// Now get deltas to each vertex - these have an 8-bit range so they can span a
|
||||||
// full unit range from the base position
|
// full unit range from the base position
|
||||||
for( int i = 0; i < 4; i++ )
|
for( int i = 0; i < 4; i++ )
|
||||||
|
|
@ -420,28 +405,18 @@ void Tesselator::packCompactQuad()
|
||||||
data[0] |= ( basex << 26 ) | ( basey << 21 )| ( basez << 16 );
|
data[0] |= ( basex << 26 ) | ( basey << 21 )| ( basez << 16 );
|
||||||
|
|
||||||
// Now process UVs. First find min & max U & V
|
// Now process UVs. First find min & max U & V
|
||||||
unsigned int minu = m_u[0];
|
unsigned int minu = std::min<unsigned int>({m_u[0], m_u[1], m_u[2], m_u[3]});
|
||||||
unsigned int minv = m_v[0];
|
unsigned int minv = std::min<unsigned int>({m_v[0], m_v[1], m_v[2], m_v[3]});
|
||||||
unsigned int maxu = m_u[0];
|
unsigned int maxu = std::max<unsigned int>({m_u[0], m_u[1], m_u[2], m_u[3]});
|
||||||
unsigned int maxv = m_v[0];
|
unsigned int maxv = std::max<unsigned int>({m_v[0], m_v[1], m_v[2], m_v[3]});
|
||||||
|
|
||||||
for( int i = 1; i < 4; i++ )
|
|
||||||
{
|
|
||||||
if( m_u[i] < minu ) minu = m_u[i];
|
|
||||||
if( m_v[i] < minv ) minv = m_v[i];
|
|
||||||
if( m_u[i] > maxu ) maxu = m_u[i];
|
|
||||||
if( m_v[i] > maxv ) maxv = m_v[i];
|
|
||||||
}
|
|
||||||
// In nearly all cases, all our UVs should be axis aligned for this quad. So the only values they should
|
// In nearly all cases, all our UVs should be axis aligned for this quad. So the only values they should
|
||||||
// have in each dimension should be the min/max. We're going to store:
|
// have in each dimension should be the min/max. We're going to store:
|
||||||
// (1) minu/maxu (16 bits each, only actuall needs to store 14 bits to get a 0 to 2 range for each
|
// (1) minu/maxu (16 bits each, only actuall needs to store 14 bits to get a 0 to 2 range for each
|
||||||
// (2) du/dv ( ie maxu-minu, maxv-minv) - 8 bits each, to store a range of 0 to 15.9375 texels. This
|
// (2) du/dv ( ie maxu-minu, maxv-minv) - 8 bits each, to store a range of 0 to 15.9375 texels. This
|
||||||
// should be enough to map the full UV range of a single 16x16 region of the terrain texture, since
|
// should be enough to map the full UV range of a single 16x16 region of the terrain texture, since
|
||||||
// we always pull UVs in by 1/16th of their range at the sides
|
// we always pull UVs in by 1/16th of their range at the sides
|
||||||
unsigned int du = maxu - minu;
|
unsigned int du = std::min<unsigned int>(maxu - minu, 255u);
|
||||||
unsigned int dv = maxv - minv;
|
unsigned int dv = std::min<unsigned int>(maxv - minv, 255u);
|
||||||
if( du > 255 ) du = 255;
|
|
||||||
if( dv > 255 ) dv = 255;
|
|
||||||
// Check if this quad has UVs that can be referenced this way. This should only happen for flowing water
|
// Check if this quad has UVs that can be referenced this way. This should only happen for flowing water
|
||||||
// and lava, where the texture coordinates are rotated for the top surface of the tile.
|
// and lava, where the texture coordinates are rotated for the top surface of the tile.
|
||||||
bool axisAligned = true;
|
bool axisAligned = true;
|
||||||
|
|
|
||||||
|
|
@ -93,35 +93,21 @@ public:
|
||||||
}
|
}
|
||||||
void addVert(float x, float y, float z)
|
void addVert(float x, float y, float z)
|
||||||
{
|
{
|
||||||
if(x < boundingBox[0])
|
boundingBox[0] = std::min<float>(boundingBox[0], x);
|
||||||
boundingBox[0] = x;
|
boundingBox[1] = std::min<float>(boundingBox[1], y);
|
||||||
if(y < boundingBox[1])
|
boundingBox[2] = std::min<float>(boundingBox[2], z);
|
||||||
boundingBox[1] = y;
|
boundingBox[3] = std::max<float>(boundingBox[3], x);
|
||||||
if(z < boundingBox[2])
|
boundingBox[4] = std::max<float>(boundingBox[4], y);
|
||||||
boundingBox[2] = z;
|
boundingBox[5] = std::max<float>(boundingBox[5], z);
|
||||||
|
|
||||||
if(x > boundingBox[3])
|
|
||||||
boundingBox[3] = x;
|
|
||||||
if(y > boundingBox[4])
|
|
||||||
boundingBox[4] = y;
|
|
||||||
if(z > boundingBox[5])
|
|
||||||
boundingBox[5] = z;
|
|
||||||
}
|
}
|
||||||
void addBounds(Bounds& ob)
|
void addBounds(const Bounds& ob)
|
||||||
{
|
{
|
||||||
if(ob.boundingBox[0] < boundingBox[0])
|
boundingBox[0] = std::min<float>(boundingBox[0], ob.boundingBox[0]);
|
||||||
boundingBox[0] = ob.boundingBox[0];
|
boundingBox[1] = std::min<float>(boundingBox[1], ob.boundingBox[1]);
|
||||||
if(ob.boundingBox[1] < boundingBox[1])
|
boundingBox[2] = std::min<float>(boundingBox[2], ob.boundingBox[2]);
|
||||||
boundingBox[1] = ob.boundingBox[1];
|
boundingBox[3] = std::max<float>(boundingBox[3], ob.boundingBox[3]);
|
||||||
if(ob.boundingBox[2] < boundingBox[2])
|
boundingBox[4] = std::max<float>(boundingBox[4], ob.boundingBox[4]);
|
||||||
boundingBox[2] = ob.boundingBox[2];
|
boundingBox[5] = std::max<float>(boundingBox[5], ob.boundingBox[5]);
|
||||||
|
|
||||||
if(ob.boundingBox[3] > boundingBox[3])
|
|
||||||
boundingBox[3] = ob.boundingBox[3];
|
|
||||||
if(ob.boundingBox[4] > boundingBox[4])
|
|
||||||
boundingBox[4] = ob.boundingBox[4];
|
|
||||||
if(ob.boundingBox[5] > boundingBox[5])
|
|
||||||
boundingBox[5] = ob.boundingBox[5];
|
|
||||||
}
|
}
|
||||||
float boundingBox[6]; // 4J MGH added
|
float boundingBox[6]; // 4J MGH added
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue