Files
mcpe/source/client/renderer/LevelRenderer.cpp
2023-12-02 21:04:21 +03:00

1340 lines
30 KiB
C++

/********************************************************************
Minecraft: Pocket Edition - Decompilation Project
Copyright (C) 2023 iProgramInCpp
The following code is licensed under the BSD 1 clause license.
SPDX-License-Identifier: BSD-1-Clause
********************************************************************/
#include "LevelRenderer.hpp"
#include "client/app/Minecraft.hpp"
#include "renderer/GL/GL.hpp"
#include "world/tile/LeafTile.hpp"
#include "world/tile/GrassTile.hpp"
LevelRenderer::LevelRenderer(Minecraft* pMC, Textures* pTexs)
{
field_4 = -9999.0f;
field_8 = -9999.0f;
field_C = -9999.0f;
field_10 = 0.0f;
field_14 = 2;
field_18 = 0;
field_1C = 0;
field_20 = 0;
field_30 = 0;
field_54 = 0;
field_58 = 0;
field_5C = 0;
field_60 = 0;
field_64 = 0;
field_68 = 0;
field_6C = 0;
field_70 = 0;
field_74 = 0;
field_78 = 0;
field_7C = 0;
field_80 = 0;
m_pLevel = nullptr;
m_chunks = nullptr;
field_98 = nullptr;
m_chunksLength = 0;
m_pTileRenderer = nullptr;
field_A4 = 0;
field_A8 = 0;
field_AC = 0;
field_B0 = 0;
field_B8 = false;
field_BC = -1;
m_ticksSinceStart = 0;
m_nBuffers = 26136;
m_pMinecraft = pMC;
m_pTextures = pTexs;
m_pBuffers = new GLuint[m_nBuffers];
xglGenBuffers(m_nBuffers, m_pBuffers);
LOG_I("numBuffers: %d", m_nBuffers);
xglGenBuffers(1, &m_skyBuffer);
generateSky(); // inlined in the 0.1.0 demo
}
void LevelRenderer::generateSky()
{
Tesselator& t = Tesselator::instance;
t.begin();
m_skyBufferCount = 0;
//float m = 16.0f;
int n = 4;
int p = 128;
for (int i = n * -p; i <= n * p; i += p)
{
for (int j = n * -p; j <= n * p; j += p)
{
t.vertex(float(i + 0.0f), 16.0f, float(j + 0.0f));
t.vertex(float(i + p) , 16.0f, float(j + 0.0f));
t.vertex(float(i + p) , 16.0f, float(j + p) );
t.vertex(float(i + 0.0f), 16.0f, float(j + p) );
m_skyBufferCount += 4;
}
}
t.end(m_skyBuffer);
}
void LevelRenderer::deleteChunks()
{
for (int i = 0; i < field_AC; i++)
{
for (int j = 0; j < field_A8; j++)
{
for (int k = 0; k < field_A4; k++)
{
int index = k + field_A4 * (j + field_A8 * i);
delete m_chunks[index];
}
}
}
if (m_chunks)
delete[] m_chunks;
m_chunks = nullptr;
if (field_98)
delete[] field_98;
field_98 = nullptr;
}
void LevelRenderer::cull(Culler* pCuller, float f)
{
for (int i = 0; i < m_chunksLength; i++)
{
Chunk* pChunk = m_chunks[i];
if (pChunk->isEmpty())
continue;
//@TODO: What does the shift do? (x % 4 == 0)?
if (!pChunk->m_bVisible || !((i + field_30) << 28))
{
pChunk->cull(pCuller);
}
}
field_30++;
}
void LevelRenderer::allChanged()
{
deleteChunks();
LeafTile* pLeaves = (LeafTile*)Tile::leaves;
pLeaves->m_bTransparent = m_pMinecraft->getOptions()->m_bFancyGraphics;
pLeaves->m_TextureFrame = !pLeaves->m_bTransparent + pLeaves->field_74;
TileRenderer::m_bFancyGrass = m_pMinecraft->getOptions()->m_bFancyGrass;
TileRenderer::m_bBiomeColors = m_pMinecraft->getOptions()->m_bBiomeColors;
field_BC = m_pMinecraft->getOptions()->m_iViewDistance;
int x1 = 64 << (3 - field_BC);
if (x1 >= 400)
x1 = 400;
field_A4 = x1 / 16 + 1;
field_AC = x1 / 16 + 1;
field_A8 = 8;
m_chunksLength = field_A8 * field_A4 * field_AC;
LOG_I("chunksLength: %d", m_chunksLength);
m_chunks = new Chunk* [m_chunksLength];
field_98 = new Chunk* [m_chunksLength];
field_6C = 0;
field_70 = 0;
field_74 = 0;
field_88.clear();
field_78 = field_A4;
field_80 = field_AC;
field_7C = field_A8;
int x2 = 0, x3 = 0;
for (int i = 0; i < field_A4; i++)
{
if (field_A8 <= 0)
continue;
for (int j = 0; j < field_A8; j++)
{
for (int k = 0; k < field_AC; k++)
{
int index = i + field_A4 * (j + field_A8 * k);
Chunk* pChunk = new Chunk(m_pLevel, 16 * i, 16 * j, 16 * k, 16, x3 + field_B0, &m_pBuffers[x3]);
if (field_B8)
pChunk->field_50 = 0;
pChunk->field_4E = false;
pChunk->field_4D = true;
pChunk->m_bVisible = true;
pChunk->field_48 = x2++;
pChunk->setDirty();
m_chunks[index] = pChunk;
field_98[index] = pChunk;
x3 += 3;
field_88.push_back(pChunk);
}
}
}
if (m_pLevel)
{
Mob* pMob = m_pMinecraft->m_pMobPersp;
if (pMob)
{
resortChunks(Mth::floor(pMob->m_pos.x), Mth::floor(pMob->m_pos.y), Mth::floor(pMob->m_pos.z));
std::sort(&field_98[0], &field_98[m_chunksLength], DistanceChunkSorter(pMob));
}
}
field_14 = 2;
}
void LevelRenderer::resortChunks(int x, int y, int z)
{
int field_A4; // r11
int z1; // r2
int field_AC; // r10
int x2; // r0
int x3; // r9
int x5; // r3
int x9; // r0
int x10; // r0
int field_78; // r3
int x11; // r7
int field_AC_1; // r5
int field_A8; // r6
int x12; // r0
int x13; // r8
int field_80; // r3
int x14; // r5
int field_A4_1; // r2
int x15; // r6
int x16; // r11
int field_7C; // r3
bool v25; // cc
Chunk** m_chunks; // r3
int x17; // r2
bool bIsDirty; // r6
int x6_1; // [sp+0h] [bp-58h]
int x8_1; // [sp+4h] [bp-54h]
int x1; // [sp+8h] [bp-50h]
int x10_1; // [sp+Ch] [bp-4Ch]
int x4; // [sp+10h] [bp-48h]
int x7; // [sp+1Ch] [bp-3Ch]
int x6; // [sp+20h] [bp-38h]
int x8; // [sp+24h] [bp-34h]
Chunk* pChunk; // [sp+2Ch] [bp-2Ch] BYREF
field_A4 = this->field_A4;
z1 = 8 - z;
this->field_6C = 0x7FFFFFFF;
this->field_70 = 0x7FFFFFFF;
this->field_74 = 0x7FFFFFFF;
this->field_78 = 0x80000000;
this->field_7C = 0x80000000;
this->field_80 = 0x80000000;
if (field_A4 > 0)
{
x1 = 16 * field_A4;
field_AC = this->field_AC;
x2 = (16 * field_A4) >> 1;
x3 = 0;
x4 = x2 + 8 - x;
x5 = 1 - 16 * field_A4;
x6 = z1 + x2;
x7 = x5 + x4;
x8 = z1 + x2 + x5;
while (1)
{
x9 = x4 & ~(x4 >> 31);
if (x4 < 0)
x9 = x7;
x10 = 16 * x3 - x1 * (x9 / x1);
field_78 = this->field_78;
x10_1 = x10;
if (x10 < this->field_6C)
this->field_6C = x10;
if (x10 > field_78)
this->field_78 = x10;
if (field_AC > 0)
break;
LABEL_32:
++x3;
x4 += 16;
x7 += 16;
if (field_A4 <= x3)
return;
}
x11 = 0;
field_AC_1 = field_AC;
field_A8 = this->field_A8;
x6_1 = x6;
x8_1 = x8;
while (1)
{
x12 = x6_1 & ~(x6_1 >> 31);
if (x6_1 < 0)
x12 = x8_1;
x13 = 16 * x11 - x1 * (x12 / x1);
field_80 = this->field_80;
if (x13 < this->field_74)
this->field_74 = x13;
if (x13 > field_80)
this->field_80 = x13;
if (field_A8 > 0)
break;
LABEL_30:
++x11;
x6_1 += 16;
x8_1 += 16;
if (field_AC_1 <= x11)
{
field_AC = field_AC_1;
goto LABEL_32;
}
}
x14 = 0;
for (field_A4_1 = field_A4; ; field_A4_1 = this->field_A4)
{
x15 = x14 + field_A8 * x11;
x16 = 16 * x14;
field_7C = this->field_7C;
if (this->field_70 > 16 * x14)
this->field_70 = x16;
v25 = field_7C < x16;
m_chunks = this->m_chunks;
x17 = x3 + field_A4_1 * x15;
if (v25)
this->field_7C = x16;
pChunk = m_chunks[x17];
bIsDirty = pChunk->isDirty();
pChunk->Chunk::setPos(x10_1, 16 * x14, x13);
if (bIsDirty || !pChunk->isDirty())
goto LABEL_19;
if (true) // need to expand
break;
++x14;
field_A8 = this->field_A8;
if (field_A8 <= x14)
{
LABEL_29:
field_A4 = this->field_A4;
field_AC_1 = this->field_AC;
goto LABEL_30;
}
LABEL_20:
;
}
this->field_88.push_back(pChunk);
LABEL_19:
field_A8 = this->field_A8;
if (field_A8 <= ++x14)
goto LABEL_29;
goto LABEL_20;
}
}
void LevelRenderer::entityAdded(Entity* pEnt)
{
// TODO
}
std::string LevelRenderer::gatherStats1()
{
//@NOTE: This data is based on the Java Edition pre-1.8 legend. This may not be accurate, but it's a good guideline.
//See https://minecraft.fandom.com/wiki/Debug_screen#Pre-1.8_legend
std::stringstream ss;
ss << "C: " << field_60 << "/" << field_54 // Number of chunk sections rendered over total number of chunks.
<< ". F: " << field_58 // Number of chunk sections loaded outside the viewing distance.
<< ", O: " << field_5C // Number of occluded chunk sections.
<< ", E: " << field_64 // Number of empty chunk sections.
<< "\n";
return ss.str();
}
void LevelRenderer::onGraphicsReset()
{
xglGenBuffers(m_nBuffers, m_pBuffers);
allChanged();
xglGenBuffers(1, &m_skyBuffer);
generateSky(); // inlined in the 0.1.0 demo
}
void LevelRenderer::render(const AABB& aabb) const
{
Tesselator& t = Tesselator::instance;
t.begin(GL_LINE_STRIP);
t.vertex(aabb.min.x, aabb.min.y, aabb.min.z);
t.vertex(aabb.max.x, aabb.min.y, aabb.min.z);
t.vertex(aabb.max.x, aabb.min.y, aabb.max.z);
t.vertex(aabb.min.x, aabb.min.y, aabb.max.z);
t.vertex(aabb.min.x, aabb.min.y, aabb.min.z);
t.draw();
t.begin(GL_LINE_STRIP);
t.vertex(aabb.min.x, aabb.max.y, aabb.min.z);
t.vertex(aabb.max.x, aabb.max.y, aabb.min.z);
t.vertex(aabb.max.x, aabb.max.y, aabb.max.z);
t.vertex(aabb.min.x, aabb.max.y, aabb.max.z);
t.vertex(aabb.min.x, aabb.max.y, aabb.min.z);
t.draw();
t.begin(GL_LINES);
t.vertex(aabb.min.x, aabb.min.y, aabb.min.z);
t.vertex(aabb.min.x, aabb.max.y, aabb.min.z);
t.vertex(aabb.max.x, aabb.min.y, aabb.min.z);
t.vertex(aabb.max.x, aabb.max.y, aabb.min.z);
t.vertex(aabb.max.x, aabb.min.y, aabb.max.z);
t.vertex(aabb.max.x, aabb.max.y, aabb.max.z);
t.vertex(aabb.min.x, aabb.min.y, aabb.max.z);
t.vertex(aabb.min.x, aabb.max.y, aabb.max.z);
t.draw();
}
void LevelRenderer::checkQueryResults(int a, int b)
{
}
void LevelRenderer::renderSameAsLast(int a, float b)
{
m_renderList.render();
}
int LevelRenderer::renderChunks(int start, int end, int a, float b)
{
field_24.clear();
int result = 0;
for (int i = start; i < end; i++)
{
Chunk* pChunk = field_98[i];
if (!a)
{
field_54++;
if (pChunk->field_1C[0])
{
field_64++;
}
else if (pChunk->m_bVisible)
{
if (!field_B8 || pChunk->field_4D)
field_60++;
else
field_5C++;
}
else
{
field_58++;
}
}
if (!pChunk->field_1C[a] && pChunk->m_bVisible && pChunk->field_4D && pChunk->getList(a) >= 0)
{
result++;
field_24.push_back(pChunk);
}
}
Mob* pMob = m_pMinecraft->m_pMobPersp;
float fPosX = pMob->field_98.x + (pMob->m_pos.x - pMob->field_98.x) * b;
float fPosY = pMob->field_98.y + (pMob->m_pos.y - pMob->field_98.y) * b;
float fPosZ = pMob->field_98.z + (pMob->m_pos.z - pMob->field_98.z) * b;
m_renderList.clear();
m_renderList.init(fPosX, fPosY, fPosZ);
for (int i = 0; i < int(field_24.size()); i++)
{
Chunk* pChk = field_24[i];
m_renderList.addR(*pChk->getRenderChunk(a));
m_renderList.field_14++;
}
renderSameAsLast(a, b);
return result;
}
void LevelRenderer::render(Mob* pMob, int a, float b)
{
for (int i = 0; i < 10; i++)
{
field_68 = (field_68 + 1) % m_chunksLength;
Chunk* pChunk = m_chunks[field_68];
if (!pChunk->m_bDirty)
continue;
std::vector<Chunk*>::iterator iter = std::find(field_88.begin(), field_88.end(), pChunk);
if (iter != field_88.end())
continue;
field_88.push_back(pChunk);
}
if (m_pMinecraft->getOptions()->m_iViewDistance != field_BC)
allChanged();
if (!a)
field_54 = field_58 = field_5C = field_60 = field_64 = 0;
//float mobX1 = pMob->m_pos.x;
float mobX2 = pMob->field_98.x + (pMob->m_pos.x - pMob->field_98.x) * b;
//float mobY1 = pMob->m_pos.y;
float mobY2 = pMob->field_98.y + (pMob->m_pos.y - pMob->field_98.y) * b;
//float mobZ1 = pMob->m_pos.z;
float mobZ2 = pMob->field_98.z + (pMob->m_pos.z - pMob->field_98.z) * b;
float dX = pMob->m_pos.x - field_4, dY = pMob->m_pos.y - field_8, dZ = pMob->m_pos.z - field_C;
if (dX * dX + dY * dY + dZ * dZ > 16.0f)
{
field_4 = pMob->m_pos.x;
field_8 = pMob->m_pos.y;
field_C = pMob->m_pos.z;
resortChunks(Mth::floor(pMob->m_pos.x), Mth::floor(pMob->m_pos.y), Mth::floor(pMob->m_pos.z));
std::sort(&field_98[0], &field_98[m_chunksLength], DistanceChunkSorter(pMob));
}
// @TODO: Fix goto hell
// @NOTE: Field_B8 doesn't appear to be used??
if (field_B8 && !a && !m_pMinecraft->getOptions()->m_bAnaglyphs)
{
checkQueryResults(0, 16);
// @HUH: why 16?
for (int i = 0; i < 16; i++)
{
field_98[i]->field_4D = true;
}
int x1 = renderChunks(0, 16, 0, b);
int x2 = 16, x3;
while (true)
{
x3 = 2 * x2;
if (x3 >= m_chunksLength)
x3 = m_chunksLength;
glDisable(GL_TEXTURE_2D);
glDisable(GL_ALPHA_TEST);
glDisable(GL_FOG);
glColorMask(false, false, false, false);
glDepthMask(false);
if (x2 < x3)
break;
label_37:
glDepthMask(true);
glColorMask(true, true, true, true);
glEnable(GL_TEXTURE_2D);
glEnable(GL_ALPHA_TEST);
glEnable(GL_FOG);
int res = renderChunks(x2, x3, 0, b);
x1 += res;
if (x3 >= m_chunksLength)
return;
x2 = x3;
}
float y1 = 0.0f;
int y2 = x2;
int y3 = x2;
float y4 = 0.0f, y5 = 0.0f;
while (true)
{
while (!field_98[y2]->isEmpty())
{
Chunk* pChunk = field_98[y2];
if (!pChunk->m_bVisible)
{
pChunk->field_4D = true;
goto label_26;
}
if (pChunk->field_4E)
goto label_26;
float y6 = pChunk->distanceToSqr(pMob);
int y7 = int(Mth::sqrt(y6) / 128.0f + 1.0f);
if (m_ticksSinceStart % y7 != y3 % y7)
goto label_26;
float fXdiff, fYdiff, fZdiff;
fXdiff = float(pChunk->m_pos.x) - mobX2 - y5;
fYdiff = float(pChunk->m_pos.y) - mobY2 - y4;
fZdiff = float(pChunk->m_pos.z) - mobZ2 - y1;
if (fXdiff != 0.0f || fYdiff != 0.0f || fZdiff != 0.0f)
{
y5 += fXdiff;
y4 += fYdiff;
y1 += fZdiff;
glTranslatef(fXdiff, fYdiff, fZdiff);
}
pChunk->renderBB();
y3++;
y2++;
//pChunk->field_4E++;
pChunk->field_4E = true;
if (y3 == x3)
goto label_37;
}
field_98[y2]->m_bVisible = 0;
label_26:
y3++;
y2++;
if (y3 == x3)
goto label_37;
continue;
}
}
renderChunks(0, m_chunksLength, a, b);
}
void LevelRenderer::setLevel(Level* level)
{
if (m_pLevel)
m_pLevel->removeListener(this);
field_4 = -9999.0f;
field_8 = -9999.0f;
field_C = -9999.0f;
EntityRenderDispatcher::getInstance()->setLevel(level);
EntityRenderDispatcher::getInstance()->setMinecraft(m_pMinecraft);
m_pLevel = level;
delete m_pTileRenderer;
m_pTileRenderer = new TileRenderer(m_pLevel);
if (level)
{
level->addListener(this);
allChanged();
}
}
void LevelRenderer::setDirty(int x1, int y1, int z1, int x2, int y2, int z2)
{
int x1c = Mth::intFloorDiv(x1, 16);
int y1c = Mth::intFloorDiv(y1, 16);
int z1c = Mth::intFloorDiv(z1, 16);
int x2c = Mth::intFloorDiv(x2, 16);
int y2c = Mth::intFloorDiv(y2, 16);
int z2c = Mth::intFloorDiv(z2, 16);
for (int x = x1c; x <= x2c; x++)
{
int x1 = x % field_A4;
if (x1 < 0)
x1 += field_A4;
for (int y = y1c; y <= y2c; y++)
{
int y1 = y % field_A8;
if (y1 < 0)
y1 += field_A8;
for (int z = z1c; z <= z2c; z++)
{
int z1 = z % field_AC;
if (z1 < 0)
z1 += field_AC;
Chunk* pChunk = m_chunks[x1 + field_A4 * (y1 + field_A8 * z1)];
if (pChunk->isDirty())
continue;
field_88.push_back(pChunk);
pChunk->setDirty();
}
}
}
}
void LevelRenderer::setTilesDirty(int x1, int y1, int z1, int x2, int y2, int z2)
{
setDirty(x1 - 1, y1 - 1, z1 - 1, x2 + 1, y2 + 1, z2 + 1);
}
void LevelRenderer::tick()
{
m_ticksSinceStart++;
}
/*
void LevelRenderer::updateDirtyChunks(Mob* pMob, bool b)
{
// @TODO This updates 16 chunks per frame. Not good.
int updated = 0;
for (int i = 0; i < 16 && i < int(field_88.size()); i++)
{
Chunk* pChk = field_88[i];
pChk->rebuild();
pChk->setClean();
updated++;
}
field_88.erase(field_88.begin(), field_88.begin() + updated);
}
*/
typedef std::vector<Chunk*> ChunkVector;
typedef ChunkVector::iterator ChunkVectorIterator;
bool LevelRenderer::updateDirtyChunks(Mob* pMob, bool b)
{
// @TODO: untangle this thing
int v3; // r4
ChunkVectorIterator field_88_Beg; // r3
size_t size; // r9
int v8; // r11
Chunk* v9; // r0
ChunkVector* xvec; // r5
int v11; // r7
ChunkVectorIterator v12; // r1
int v13; // r7
Chunk* v14; // r1
bool v15; // r0
int v16; // r3
int v17; // r7
int v18; // r2
ChunkVectorIterator v19; // r1
ChunkVectorIterator v20; // r0
size_t v21; // r8
size_t v22; // r4
ChunkVector* v23; // r10
size_t v24; // r8
Chunk* v25; // r5
int v26; // r4
int v27; // r8
Chunk* v28; // r5
bool v29; // r3
ChunkVectorIterator v31; // r1
size_t v32; // r5
int v33; // r0
int v34; // r3
Chunk* v35; // r2
Chunk** v38; // r3
Chunk* v39; // r2
ChunkVector* v40; // r0
Chunk* v42[3]; // [sp+1Ch] [bp+0h] BYREF
Chunk* a3; // [sp+28h] [bp+Ch] BYREF
//Entity* pMob_1; // [sp+2Ch] [bp+10h] BYREF
v3 = 0;
//pMob_1 = pMob;
DirtyChunkSorter dcs(pMob);
memset(v42, 0, sizeof v42);
field_88_Beg = this->field_88.begin();
size = this->field_88.end() - field_88_Beg;
if (size <= 0)
{
v8 = 0;
goto LABEL_28;
}
v8 = 0;
v9 = *field_88_Beg;
xvec = 0;
v11 = 0;
a3 = *field_88_Beg;
if (!b)
goto LABEL_11;
while (1)
{
if (!v9->m_bVisible)
goto LABEL_9;
LABEL_5:
if (!xvec)
{
++v8;
v40 = new ChunkVector;
v12 = v40->end();
xvec = v40;
LABEL_55:
xvec->insert(v12, a3);
goto LABEL_8;
}
v12 = xvec->end();
++v8;
if (true) // (v12 == xvec->capacity)
goto LABEL_55;
xvec->insert(v12, a3);
LABEL_8:
field_88[v11] = 0;
LABEL_9:
if (++v3 == size)
break;
while (1)
{
v11 = v3;
v9 = field_88[v3];
a3 = v9;
if (b)
break;
LABEL_11:
if (v9->distanceToSqr(pMob) <= 1024.0f)
goto LABEL_5;
v13 = b;
while (1)
{
v14 = v42[v13];
if (v14)
{
v15 = dcs(v14, a3);
v16 = v13;
if (!v15)
break;
}
if (++v13 == 3)
{
v17 = 2;
v18 = 1;
v16 = 3;
goto LABEL_51;
}
}
v17 = v13 - 1;
if (v17 <= 0)
goto LABEL_9;
v18 = v17 - 1;
if (v17 == 1)
{
v42[1] = a3;
goto LABEL_18;
}
LABEL_51:
v38 = &v42[v16];
v39 = v42[v18];
do
{
*(v38 - 3) = v39;
--v38;
} while (v38 != &v42[2]);
v42[v17] = a3;
LABEL_18:
if (++v3 == size)
goto LABEL_19;
}
}
LABEL_19:
if (xvec)
{
v19 = xvec->end();
v20 = xvec->begin();
v21 = v19 - xvec->begin();
if (v21 > 1)
{
std::sort(v20, v19, dcs);
v20 = xvec->begin();
v21 = xvec->end() - xvec->begin();
}
v22 = v21 - 1;
if ((int)(v21 - 1) >= 0)
{
v23 = xvec;
v24 = v21 - 1;// v21 + 0x3FFFFFFF;
while (1)
{
v25 = v20[v24--];
v25->rebuild();
v25->setClean();
if ((--v22 & 0x80000000) != 0)
break;
v20 = v23->begin();
}
xvec = v23;
}
delete xvec;
}
LABEL_28:
v26 = 2;
v27 = 0;
while (2)
{
v28 = v42[v26];
if (!v28)
{
LABEL_33:
if (v26-- == 0)
goto LABEL_34;
continue;
}
break;
}
v29 = v28->m_bVisible;
if (v28->m_bVisible || v26 == 2)
{
++v27;
v42[v26]->rebuild();
v28->setClean();
goto LABEL_33;
}
v42[v26] = (Chunk*)v29;
v42[0] = (Chunk*)v29;
LABEL_34:
v31 = this->field_88.begin();
v32 = this->field_88.end() - v31;
if (v32)
{
v33 = 0;
v34 = 0;
while (1)
{
v35 = v31[v34];
if (v35 && v42[0] != v35 && v42[1] != v35 && v42[2] != v35)
{
if (v33 != v34)
v31[v33] = v35;
++v33;
}
if (++v34 == v32)
break;
v31 = this->field_88.begin();
}
if (v34 > v33)
{
field_88.resize(v33);
}
}
return v27 + v8 == size;
}
void LevelRenderer::renderHit(Player* pPlayer, const HitResult& hr, int i, void* vp, float f)
{
glEnable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
// @BUG: possible leftover from Minecraft Classic? This is overridden anyways
glColor4f(1.0f, 1.0f, 1.0f, 0.5f * (0.4f + 0.2f * Mth::sin(float(getTimeMs()) / 100.0f)));
if (!i && field_10 > 0.0f)
{
glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
m_pTextures->loadAndBindTexture(C_TERRAIN_NAME);
glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
glPushMatrix();
Tile* pTile = nullptr;
TileID tile = m_pLevel->getTile(hr.m_tileX, hr.m_tileY, hr.m_tileZ);
if (tile > 0)
pTile = Tile::tiles[tile];
glDisable(GL_ALPHA_TEST);
glPolygonOffset(-3.0f, -3.0f);
glEnable(GL_POLYGON_OFFSET_FILL);
float px = pPlayer->field_98.x + (pPlayer->m_pos.x - pPlayer->field_98.x) * f;
float py = pPlayer->field_98.y + (pPlayer->m_pos.y - pPlayer->field_98.y) * f;
float pz = pPlayer->field_98.z + (pPlayer->m_pos.z - pPlayer->field_98.z) * f;
Tesselator& t = Tesselator::instance;
t.begin();
t.offset(-px, -py, -pz);
t.noColor();
if (!pTile)
pTile = Tile::rock;
m_pTileRenderer->tesselateInWorld(pTile, hr.m_tileX, hr.m_tileY, hr.m_tileZ, 240 + int(field_10 * 10.0f));
t.draw();
t.offset(0, 0, 0);
glPolygonOffset(0.0f, 0.0f);
glDisable(GL_POLYGON_OFFSET_FILL);
glDepthMask(true); //@HUH: What is the reason for this? You never disable the depth mask.
glPopMatrix();
}
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
}
void LevelRenderer::renderHitSelect(Player* pPlayer, const HitResult& hr, int i, void* vp, float f)
{
if (i) return;
glEnable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
glEnable(GL_DEPTH_TEST);
m_pMinecraft->m_pTextures->loadAndBindTexture(C_TERRAIN_NAME);
Tile* pTile = nullptr;
TileID tileID = m_pLevel->getTile(hr.m_tileX, hr.m_tileY, hr.m_tileZ);
if (tileID > 0)
pTile = Tile::tiles[tileID];
glDisable(GL_ALPHA_TEST);
glColor4f(0.65f, 0.65f, 0.65f, 0.65f);
glPushMatrix();
glPolygonOffset(-0.3f, -0.3f);
glEnable(GL_POLYGON_OFFSET_FILL);
float px = pPlayer->field_98.x + (pPlayer->m_pos.x - pPlayer->field_98.x) * f;
float py = pPlayer->field_98.y + (pPlayer->m_pos.y - pPlayer->field_98.y) * f;
float pz = pPlayer->field_98.z + (pPlayer->m_pos.z - pPlayer->field_98.z) * f;
Tesselator& t = Tesselator::instance;
t.begin();
t.offset(-px, -py, -pz);
t.noColor();
if (!pTile)
pTile = Tile::rock;
m_pTileRenderer->tesselateInWorld(pTile, hr.m_tileX, hr.m_tileY, hr.m_tileZ);
t.draw();
t.offset(0, 0, 0);
glPolygonOffset(0.0f, 0.0f);
glDisable(GL_POLYGON_OFFSET_FILL);
glEnable(GL_TEXTURE_2D);
glDepthMask(true);
glPopMatrix();
glEnable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
}
void LevelRenderer::renderHitOutline(Player* pPlayer, const HitResult& hr, int i, void* vp, float f)
{
if (i != 0 || hr.m_hitType != 0)
return;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
//glLineWidth(1.0f);
glDisable(GL_TEXTURE_2D);
glDepthMask(false);
// Maximize Line Width
glEnable(GL_LINE_SMOOTH);
float range[2];
glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, range);
float lineWidth = 2.0f;
if (lineWidth > range[1])
lineWidth = range[1];
glLineWidth(lineWidth);
TileID tile = m_pLevel->getTile(hr.m_tileX, hr.m_tileY, hr.m_tileZ);
if (tile > 0)
{
Tile::tiles[tile]->updateShape(
m_pLevel,
hr.m_tileX,
hr.m_tileY,
hr.m_tileZ);
float posX = pPlayer->field_98.x + ((pPlayer->m_pos.x - pPlayer->field_98.x) * f);
float posY = pPlayer->field_98.y + ((pPlayer->m_pos.y - pPlayer->field_98.y) * f);
float posZ = pPlayer->field_98.z + ((pPlayer->m_pos.z - pPlayer->field_98.z) * f);
AABB aabb, tileAABB = Tile::tiles[tile]->getTileAABB(m_pLevel, hr.m_tileX, hr.m_tileY, hr.m_tileZ);
aabb.min.y = tileAABB.min.y - 0.002f - posY;
aabb.max.y = tileAABB.max.y + 0.002f - posY;
aabb.min.z = tileAABB.min.z - 0.002f - posZ;
aabb.max.z = tileAABB.max.z + 0.002f - posZ;
aabb.min.x = tileAABB.min.x - 0.002f - posX;
aabb.max.x = tileAABB.max.x + 0.002f - posX;
render(aabb);
}
glDepthMask(true);
glEnable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
}
void LevelRenderer::tileChanged(int x, int y, int z)
{
setDirty(x - 1, y - 1, z - 1, x + 1, y + 1, z + 1);
}
void LevelRenderer::renderEntities(Vec3 pos, Culler* culler, float f)
{
if (field_14 > 0)
{
field_14--;
return;
}
Mob* mob = m_pMinecraft->m_pMobPersp;
EntityRenderDispatcher::getInstance()->prepare(m_pLevel, m_pMinecraft->m_pTextures, m_pMinecraft->m_pFont, mob, m_pMinecraft->getOptions(), f);
field_18 = 0;
field_1C = 0;
field_20 = 0;
EntityRenderDispatcher::xOff = mob->field_98.x + (mob->m_pos.x - mob->field_98.x) * f;
EntityRenderDispatcher::yOff = mob->field_98.y + (mob->m_pos.y - mob->field_98.y) * f;
EntityRenderDispatcher::zOff = mob->field_98.z + (mob->m_pos.z - mob->field_98.z) * f;
EntityVector* pVec = m_pLevel->getAllEntities();
field_18 = int(pVec->size());
for (int i = 0; i < field_18; i++)
{
Entity* pEnt = (*pVec)[i];
if (!pEnt->shouldRender(pos))
continue;
if (!culler->isVisible(pEnt->m_hitbox))
continue;
if (m_pMinecraft->m_pMobPersp == pEnt && !m_pMinecraft->getOptions()->m_bThirdPerson)
continue;
if (m_pLevel->hasChunkAt(Mth::floor(pEnt->m_pos.x), Mth::floor(pEnt->m_pos.y), Mth::floor(pEnt->m_pos.z)))
{
field_1C++;
EntityRenderDispatcher::getInstance()->render(pEnt, f);
}
}
}
extern int t_keepPic;
void LevelRenderer::takePicture(TripodCamera* pCamera, Entity* pOwner)
{
Mob* pOldMob = m_pMinecraft->m_pMobPersp;
bool bOldDontRenderGui = m_pMinecraft->getOptions()->m_bDontRenderGui;
bool bOldThirdPerson = m_pMinecraft->getOptions()->m_bThirdPerson;
#ifdef ENH_CAMERA_NO_PARTICLES
extern bool g_bDisableParticles;
g_bDisableParticles = true;
#endif
m_pMinecraft->m_pMobPersp = pCamera;
m_pMinecraft->getOptions()->m_bDontRenderGui = true;
m_pMinecraft->getOptions()->m_bThirdPerson = false; // really from the perspective of the camera
m_pMinecraft->m_pGameRenderer->render(0.0f);
m_pMinecraft->m_pMobPersp = pOldMob;
m_pMinecraft->getOptions()->m_bDontRenderGui = bOldDontRenderGui;
m_pMinecraft->getOptions()->m_bThirdPerson = bOldThirdPerson;
#ifdef ENH_CAMERA_NO_PARTICLES
g_bDisableParticles = false;
#endif
t_keepPic = -1;
static char str[256];
// @HUH: This has the potential to overwrite a file
#ifdef ORIGINAL_CODE
sprintf(str, "%s/games/com.mojang/img_%.4d.jpg", m_pMinecraft->m_externalStorageDir.c_str(), getTimeMs());
#else
sprintf(str, "img_%.4d.png", getTimeMs());
#endif
m_pMinecraft->platform()->saveScreenshot(std::string(str), Minecraft::width, Minecraft::height);
}
void LevelRenderer::addParticle(const std::string& name, float x, float y, float z, float vx, float vy, float vz)
{
// TODO: Who's the genius who decided it'd be better to check a name string rather than an enum?
if (m_pMinecraft->m_pMobPersp->distanceToSqr_inline(x, y, z) > 256.0f)
return;
ParticleEngine* pe = m_pMinecraft->m_pParticleEngine;
if (name == "bubble")
{
pe->add(new BubbleParticle(m_pLevel, x, y, z, vx, vy, vz));
return;
}
if (name == "smoke")
{
pe->add(new SmokeParticle(m_pLevel, x, y, z, vx, vy, vz, 1.0f));
return;
}
if (name == "explode")
{
pe->add(new ExplodeParticle(m_pLevel, x, y, z, vx, vy, vz));
return;
}
if (name == "flame")
{
pe->add(new FlameParticle(m_pLevel, x, y, z, vx, vy, vz));
return;
}
if (name == "lava")
{
pe->add(new LavaParticle(m_pLevel, x, y, z));
return;
}
if (name == "largesmoke")
{
pe->add(new SmokeParticle(m_pLevel, x, y, z, vx, vy, vz, 2.5f));
return;
}
if (name == "reddust")
{
pe->add(new RedDustParticle(m_pLevel, x, y, z, vx, vy, vz));
return;
}
LOG_W("Unknown particle type: %s", name.c_str());
}
void LevelRenderer::playSound(const std::string& name, float x, float y, float z, float a, float b)
{
// TODO: Who's the genius who decided it'd be better to check a name string rather than an enum?
float mult = 1.0f, dist = 16.0f;
if (a > 1.0f)
{
mult = 16.0f;
dist = a * mult;
}
if (dist * dist > m_pMinecraft->m_pMobPersp->distanceToSqr(x, y, z))
m_pMinecraft->m_pSoundEngine->play(name, x, y, z, a, b);
}
void LevelRenderer::renderSky(float f)
{
if (m_pMinecraft->m_pLevel->m_pDimension->field_C)
return;
glDisable(GL_TEXTURE_2D);
Vec3 skyColor = m_pLevel->getSkyColor(m_pMinecraft->m_pMobPersp, f);
if (m_pMinecraft->getOptions()->m_bAnaglyphs)
{
skyColor.x = (((skyColor.x * 30.0f) + (skyColor.y * 59.0f)) + (skyColor.z * 11.0f)) / 100.0f;
skyColor.y = ((skyColor.x * 30.0f) + (skyColor.y * 70.0f)) / 100.0f;
skyColor.z = ((skyColor.x * 30.0f) + (skyColor.z * 70.0f)) / 100.0f;
}
glColor4f(skyColor.x, skyColor.y, Mth::Min(1.0f, skyColor.z), 1.0f);
glDepthMask(false);
glColor4f(skyColor.x, skyColor.y, skyColor.z, 1.0f);
drawArrayVT(m_skyBuffer, m_skyBufferCount, sizeof(Tesselator::Vertex));
glEnable(GL_TEXTURE_2D);
glDepthMask(true);
}
// TODO: This should be inside of an initialized "Minecraft" instance rather than the global namespace
bool g_bAreCloudsAvailable = false; // false because 0.1 didn't have them
void LevelRenderer::renderClouds(float f)
{
if (!g_bAreCloudsAvailable)
return;
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
float yPos = Lerp(m_pMinecraft->m_pMobPersp->field_98.y, m_pMinecraft->m_pMobPersp->m_pos.y, f); // not certain if this old pos Y is used
m_pTextures->loadAndBindTexture("environment/clouds.png");
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Vec3 cloudColor = m_pLevel->getCloudColor(f);
float offX = Lerp(m_pMinecraft->m_pMobPersp->field_3C.x, m_pMinecraft->m_pMobPersp->m_pos.x, f) + (float(m_ticksSinceStart) + f) * 0.3f;
float offZ = Lerp(m_pMinecraft->m_pMobPersp->field_3C.z, m_pMinecraft->m_pMobPersp->m_pos.z, f);
int dx2048 = Mth::floor(offX / 2048.0f);
int dz2048 = Mth::floor(offZ / 2048.0f);
offX -= float(dx2048 * 2048);
offZ -= float(dz2048 * 2048);
Tesselator& t = Tesselator::instance;
float fYPos = (128.0f - yPos) + 0.33f;
offX /= 2048.0f;
offZ /= 2048.0f;
t.begin();
t.color(cloudColor.x, cloudColor.y, cloudColor.z, 0.8f);
const int incr = 32;
const int in2 = 256 / incr;
for (int x = -incr * in2; x < incr * in2; x += incr)
{
for (int z = -incr * in2; z < incr * in2; z += incr)
{
t.vertexUV(float(x) + 0.0f, fYPos, float(z) + incr, float(x + 0.0f) / 2048.0f + offX, float(z + incr) / 2048.0f + offZ);
t.vertexUV(float(x) + incr, fYPos, float(z) + incr, float(x + incr) / 2048.0f + offX, float(z + incr) / 2048.0f + offZ);
t.vertexUV(float(x) + incr, fYPos, float(z) + 0.0f, float(x + incr) / 2048.0f + offX, float(z + 0.0f) / 2048.0f + offZ);
t.vertexUV(float(x) + 0.0f, fYPos, float(z) + 0.0f, float(x + 0.0f) / 2048.0f + offX, float(z + 0.0f) / 2048.0f + offZ);
}
}
t.voidBeginAndEndCalls(false); // why??
t.draw();
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
}
void LevelRenderer::skyColorChanged()
{
for (int i = 0; i < m_chunksLength; i++)
{
Chunk* pChunk = m_chunks[i];
if (!pChunk->field_54)
continue;
if (pChunk->isDirty())
continue;
field_88.push_back(pChunk);
pChunk->setDirty();
}
}