feat/greedy-meshing: added batch of files

This commit is contained in:
JuiceyDev 2026-04-06 20:40:45 +02:00
parent 9c0dfd60d5
commit 521bd57b3a
11 changed files with 1191 additions and 133 deletions

View file

@ -3,28 +3,34 @@
#include <GL/gl.h>
#include <string.h>
#include <cmath>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "platform/sdl2/Render.h"
#include "LevelRenderer.h"
#include "app/linux/Stubs/winapi_stubs.h"
#include "app/include/FrameProfiler.h"
#include "TileRenderer.h"
#include "app/include/FrameProfiler.h"
#include "app/linux/LinuxGame.h"
#include "app/linux/Stubs/winapi_stubs.h"
#include "minecraft/SharedConstants.h"
#include "minecraft/client/renderer/GameRenderer.h"
#include "minecraft/client/renderer/Tesselator.h"
#include "minecraft/client/renderer/culling/Culler.h"
#include "minecraft/client/renderer/tileentity/TileEntityRenderDispatcher.h"
#include "minecraft/world/Icon.h"
#include "minecraft/world/entity/Entity.h"
#include "minecraft/world/level/Level.h"
#include "minecraft/world/level/LevelSource.h"
#include "minecraft/world/level/Region.h"
#include "minecraft/world/level/chunk/LevelChunk.h"
#include "minecraft/world/level/tile/LiquidTile.h"
#include "minecraft/world/level/tile/Tile.h"
#include "minecraft/world/level/tile/entity/TileEntity.h"
#include "minecraft/world/phys/AABB.h"
#include "platform/sdl2/Render.h"
int Chunk::updates = 0;
@ -428,6 +434,88 @@ void Chunk::rebuild() {
// 4J - optimisation ends
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Binary Mesh Greedy implementation
// https://gedge.ca/blog/2014-08-17-greedy-voxel-meshing/
// https://github.com/cgerikj/binary-greedy-meshing/tree/master
// https://0fps.net/2012/06/30/meshing-in-a-minecraft-game/
// also https://github.com/whleucka/voxel/blob/main/src/chunk/chunk_mesh.cpp
// ^ real useful thanks
// also known as the "spongebob! why doesn't my AO work?"
int greedyEligibleCount = 0;
std::vector<uint8_t> greedyEligible(16 * 16 * 16, 0);
std::vector<uint8_t> greedyLiquidTop(16 * 16 * 16, 0);
#if defined(ENABLE_GREEDY_MESHING)
{
const double greedyEps = 1e-6;
auto greedyIndex = [](int lx, int ly, int lz) {
return (ly << 8) | (lz << 4) | lx;
};
for (int z = z0; z < z1; z++) {
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
int indexY = y;
int offset = 0;
if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) {
indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
offset = Level::COMPRESSED_CHUNK_SECTION_TILES;
}
unsigned char tileId =
tileIds[offset +
(((x - x0) << 11) | ((z - z0) << 7) | indexY)];
if (tileId == 0 || tileId == 0xff) continue;
Tile* tile = Tile::tiles[tileId];
if (tile == nullptr) continue;
if (tile->getRenderLayer() != 0) continue;
if (tile->getRenderShape() != Tile::SHAPE_BLOCK) continue;
if (tile->isEntityTile()) continue;
if (!tile->isSolidRender()) continue;
if (Tile::transculent[tileId]) continue;
if (tileId == Tile::grass_Id) continue;
tile->updateShape(level, x, y, z, -1,
std::shared_ptr<TileEntity>());
double x0s = tile->getShapeX0();
double x1s = tile->getShapeX1();
double y0s = tile->getShapeY0();
double y1s = tile->getShapeY1();
double z0s = tile->getShapeZ0();
double z1s = tile->getShapeZ1();
if (std::fabs(x0s) > greedyEps ||
std::fabs(y0s) > greedyEps ||
std::fabs(z0s) > greedyEps ||
std::fabs(x1s - 1.0) > greedyEps ||
std::fabs(y1s - 1.0) > greedyEps ||
std::fabs(z1s - 1.0) > greedyEps)
continue;
bool ok = true;
for (int face = 0; face < 6; face++) {
if (tile->getTexture(level, x, y, z, face) == nullptr) {
ok = false;
break;
}
}
if (!ok) continue;
int lx = x - x0;
int ly = y - y0;
int lz = z - z0;
greedyEligible[greedyIndex(lx, ly, lz)] = 1;
greedyEligibleCount++;
}
}
}
}
tileRenderer->setWaterTopSkipMask(greedyLiquidTop.data());
#else
tileRenderer->setWaterTopSkipMask(nullptr);
#endif
Tesselator::Bounds bounds; // 4J MGH - added
{
// this was the old default clip bounds for the chunk, set in
@ -443,8 +531,704 @@ void Chunk::rebuild() {
for (int currentLayer = 0; currentLayer < 2; currentLayer++) {
bool renderNextLayer = false;
bool rendered = false;
// second part of the greedy mesh implementation
bool listStarted = false;
bool normalTessActive = false;
bool greedyTessActive = false;
int greedyMergedQuads = 0;
int greedyTilesEmitted = 0;
int greedyLiquidMergedQuads = 0;
int greedyLiquidTilesEmitted = 0;
bool started = false;
auto startListIfNeeded = [&]() {
if (listStarted) return;
listStarted = true;
glNewList(lists + currentLayer, GL_COMPILE);
glDepthMask(true);
};
auto beginNormal = [&]() {
if (normalTessActive) return;
startListIfNeeded();
t->useCompactVertices(true);
t->useTileUV(false);
t->begin();
t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z));
normalTessActive = true;
};
auto endNormal = [&]() {
if (!normalTessActive) return;
t->end();
bounds.addBounds(t->bounds);
t->offset(0, 0, 0);
normalTessActive = false;
};
auto beginGreedy = [&]() {
if (greedyTessActive) return;
startListIfNeeded();
t->useCompactVertices(false);
t->useTileUV(true);
t->begin();
t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z));
greedyTessActive = true;
};
auto endGreedy = [&]() {
if (!greedyTessActive) return;
t->end();
bounds.addBounds(t->bounds);
t->useTileUV(false);
t->offset(0, 0, 0);
greedyTessActive = false;
};
auto greedyIndex = [](int lx, int ly, int lz) {
return (ly << 8) | (lz << 4) | lx;
};
auto getTileIdLocal = [&](int lx, int ly, int lz) -> unsigned char {
int worldY = y0 + ly;
int indexY = worldY;
int offset = 0;
if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) {
indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT;
offset = Level::COMPRESSED_CHUNK_SECTION_TILES;
}
return tileIds[offset +
(((lx + 0) << 11) | ((lz + 0) << 7) | indexY)];
};
// bread taste better than key
auto computeFaceKey = [&](Tile* tile, int wx, int wy, int wz, int face,
uint32_t& colorKey, int& lightKey) {
int col = tile->getColor(level, wx, wy, wz);
float r = ((col >> 16) & 0xff) / 255.0f;
float g = ((col >> 8) & 0xff) / 255.0f;
float b = ((col) & 0xff) / 255.0f;
if (GameRenderer::anaglyph3d) {
// todo: this will DEF break anaglyph rendering
// do we still need it?????????
}
float shade = 1.0f;
switch (face) {
case 0:
shade = 0.5f;
break;
case 1:
shade = 1.0f;
break;
case 2:
case 3:
shade = 0.8f;
break;
case 4:
case 5:
shade = 0.6f;
break;
default:
break;
}
r *= shade;
g *= shade;
b *= shade;
if (SharedConstants::TEXTURE_LIGHTING) {
int nx = wx, ny = wy, nz = wz;
if (face == 0) ny--;
if (face == 1) ny++;
if (face == 2) nz--;
if (face == 3) nz++;
if (face == 4) nx--;
if (face == 5) nx++;
lightKey = tileRenderer->getLightColor(tile, level, nx, ny, nz);
} else {
int nx = wx, ny = wy, nz = wz;
if (face == 0) ny--;
if (face == 1) ny++;
if (face == 2) nz--;
if (face == 3) nz++;
if (face == 4) nx--;
if (face == 5) nx++;
float br = tile->getBrightness(level, nx, ny, nz);
r *= br;
g *= br;
b *= br;
lightKey = 0;
}
auto clampByte = [](float v) -> uint8_t {
int iv = (int)(v * 255.0f + 0.5f);
if (iv < 0) iv = 0;
if (iv > 255) iv = 255;
return (uint8_t)iv;
};
colorKey =
(clampByte(r) << 16) | (clampByte(g) << 8) | (clampByte(b));
};
auto shadeAt = [&](Tile* tile, int ax, int ay, int az) {
return tileRenderer->getShadeBrightness(tile, level, ax, ay, az);
};
auto aoCorner = [&](float s1, float s2, float s3) {
if (s1 < 1.0f && s2 < 1.0f) return 0.2f;
return (s1 + s2 + s3) / 3.0f;
};
auto computeFaceAO = [&](Tile* tile, int wx, int wy, int wz, int face,
float outAo[4]) {
switch (face) {
case 0: { // Down (y-1)
float sW = shadeAt(tile, wx - 1, wy - 1, wz);
float sE = shadeAt(tile, wx + 1, wy - 1, wz);
float sN = shadeAt(tile, wx, wy - 1, wz - 1);
float sS = shadeAt(tile, wx, wy - 1, wz + 1);
float sWN = shadeAt(tile, wx - 1, wy - 1, wz - 1);
float sWS = shadeAt(tile, wx - 1, wy - 1, wz + 1);
float sEN = shadeAt(tile, wx + 1, wy - 1, wz - 1);
float sES = shadeAt(tile, wx + 1, wy - 1, wz + 1);
outAo[0] = aoCorner(sW, sS, sWS);
outAo[1] = aoCorner(sW, sN, sWN);
outAo[2] = aoCorner(sE, sN, sEN);
outAo[3] = aoCorner(sE, sS, sES);
} break;
case 1: { // Up (y+1)
float sW = shadeAt(tile, wx - 1, wy + 1, wz);
float sE = shadeAt(tile, wx + 1, wy + 1, wz);
float sN = shadeAt(tile, wx, wy + 1, wz - 1);
float sS = shadeAt(tile, wx, wy + 1, wz + 1);
float sWN = shadeAt(tile, wx - 1, wy + 1, wz - 1);
float sWS = shadeAt(tile, wx - 1, wy + 1, wz + 1);
float sEN = shadeAt(tile, wx + 1, wy + 1, wz - 1);
float sES = shadeAt(tile, wx + 1, wy + 1, wz + 1);
outAo[0] = aoCorner(sE, sS, sES);
outAo[1] = aoCorner(sE, sN, sEN);
outAo[2] = aoCorner(sW, sN, sWN);
outAo[3] = aoCorner(sW, sS, sWS);
} break;
case 2: { // North (z-1)
float sW = shadeAt(tile, wx - 1, wy, wz - 1);
float sE = shadeAt(tile, wx + 1, wy, wz - 1);
float sU = shadeAt(tile, wx, wy + 1, wz - 1);
float sD = shadeAt(tile, wx, wy - 1, wz - 1);
float sWU = shadeAt(tile, wx - 1, wy + 1, wz - 1);
float sWD = shadeAt(tile, wx - 1, wy - 1, wz - 1);
float sEU = shadeAt(tile, wx + 1, wy + 1, wz - 1);
float sED = shadeAt(tile, wx + 1, wy - 1, wz - 1);
outAo[0] = aoCorner(sW, sU, sWU);
outAo[1] = aoCorner(sE, sU, sEU);
outAo[2] = aoCorner(sE, sD, sED);
outAo[3] = aoCorner(sW, sD, sWD);
} break;
case 3: { // South (z+1)
float sW = shadeAt(tile, wx - 1, wy, wz + 1);
float sE = shadeAt(tile, wx + 1, wy, wz + 1);
float sU = shadeAt(tile, wx, wy + 1, wz + 1);
float sD = shadeAt(tile, wx, wy - 1, wz + 1);
float sWU = shadeAt(tile, wx - 1, wy + 1, wz + 1);
float sWD = shadeAt(tile, wx - 1, wy - 1, wz + 1);
float sEU = shadeAt(tile, wx + 1, wy + 1, wz + 1);
float sED = shadeAt(tile, wx + 1, wy - 1, wz + 1);
outAo[0] = aoCorner(sW, sU, sWU);
outAo[1] = aoCorner(sW, sD, sWD);
outAo[2] = aoCorner(sE, sD, sED);
outAo[3] = aoCorner(sE, sU, sEU);
} break;
case 4: { // West (x-1)
float sN = shadeAt(tile, wx - 1, wy, wz - 1);
float sS = shadeAt(tile, wx - 1, wy, wz + 1);
float sU = shadeAt(tile, wx - 1, wy + 1, wz);
float sD = shadeAt(tile, wx - 1, wy - 1, wz);
float sNU = shadeAt(tile, wx - 1, wy + 1, wz - 1);
float sND = shadeAt(tile, wx - 1, wy - 1, wz - 1);
float sSU = shadeAt(tile, wx - 1, wy + 1, wz + 1);
float sSD = shadeAt(tile, wx - 1, wy - 1, wz + 1);
outAo[0] = aoCorner(sS, sU, sSU);
outAo[1] = aoCorner(sN, sU, sNU);
outAo[2] = aoCorner(sN, sD, sND);
outAo[3] = aoCorner(sS, sD, sSD);
} break;
case 5: { // East (x+1)
float sN = shadeAt(tile, wx + 1, wy, wz - 1);
float sS = shadeAt(tile, wx + 1, wy, wz + 1);
float sU = shadeAt(tile, wx + 1, wy + 1, wz);
float sD = shadeAt(tile, wx + 1, wy - 1, wz);
float sNU = shadeAt(tile, wx + 1, wy + 1, wz - 1);
float sND = shadeAt(tile, wx + 1, wy - 1, wz - 1);
float sSU = shadeAt(tile, wx + 1, wy + 1, wz + 1);
float sSD = shadeAt(tile, wx + 1, wy - 1, wz + 1);
outAo[0] = aoCorner(sS, sD, sSD);
outAo[1] = aoCorner(sN, sD, sND);
outAo[2] = aoCorner(sN, sU, sNU);
outAo[3] = aoCorner(sS, sU, sSU);
} break;
default:
outAo[0] = outAo[1] = outAo[2] = outAo[3] = 1.0f;
break;
}
};
struct GreedyFaceKey {
Tile* tile;
Icon* tex;
uint32_t colorKey;
uint32_t aoKey;
int lightKey;
bool mipmap;
bool operator==(const GreedyFaceKey& other) const {
return tile == other.tile && tex == other.tex &&
colorKey == other.colorKey && aoKey == other.aoKey &&
lightKey == other.lightKey && mipmap == other.mipmap;
}
};
auto greedyMeshFace = [&](int face) {
const int U = 16;
const int V = 16;
std::vector<uint8_t> valid(U * V, 0);
std::vector<GreedyFaceKey> keys(U * V);
bool faceRendered = false;
for (int slice = 0; slice < 16; slice++) {
memset(valid.data(), 0, valid.size());
for (int v = 0; v < V; v++) {
for (int u = 0; u < U; u++) {
int lx = 0, ly = 0, lz = 0;
switch (face) {
case 0:
case 1:
lx = u;
ly = slice;
lz = v;
break;
case 2:
case 3:
lx = u;
ly = v;
lz = slice;
break;
case 4:
case 5:
lx = slice;
ly = v;
lz = u;
break;
}
int idx = greedyIndex(lx, ly, lz);
if (greedyEligible[idx] == 0) continue;
int wx = x0 + lx;
int wy = y0 + ly;
int wz = z0 + lz;
unsigned char tileId = getTileIdLocal(lx, ly, lz);
if (tileId == 0 || tileId == 0xff) continue;
Tile* tile = Tile::tiles[tileId];
if (tile == nullptr) continue;
if (tile->getRenderLayer() != currentLayer) continue;
bool visible = false;
if (face == 0)
visible = tile->shouldRenderFace(level, wx, wy - 1,
wz, 0);
else if (face == 1)
visible = tile->shouldRenderFace(level, wx, wy + 1,
wz, 1);
else if (face == 2)
visible = tile->shouldRenderFace(level, wx, wy,
wz - 1, 2);
else if (face == 3)
visible = tile->shouldRenderFace(level, wx, wy,
wz + 1, 3);
else if (face == 4)
visible = tile->shouldRenderFace(level, wx - 1, wy,
wz, 4);
else if (face == 5)
visible = tile->shouldRenderFace(level, wx + 1, wy,
wz, 5);
if (!visible) continue;
Icon* tex = tile->getTexture(level, wx, wy, wz, face);
if (tex == nullptr) continue;
GreedyFaceKey key{};
key.tile = tile;
key.tex = tex;
key.mipmap = Tile::mipmapEnable[tileId];
uint32_t colorKey = 0;
int lightKey = 0;
computeFaceKey(tile, wx, wy, wz, face, colorKey,
lightKey);
key.colorKey = colorKey;
key.lightKey = lightKey;
float ao[4];
computeFaceAO(tile, wx, wy, wz, face, ao);
auto aoByte = [](float v) -> uint32_t {
int iv = (int)(v * 255.0f + 0.5f);
if (iv < 0) iv = 0;
if (iv > 255) iv = 255;
return (uint32_t)iv;
};
key.aoKey = (aoByte(ao[0]) << 24) |
(aoByte(ao[1]) << 16) |
(aoByte(ao[2]) << 8) | aoByte(ao[3]);
valid[u + v * U] = 1;
keys[u + v * U] = key;
}
}
for (int v = 0; v < V; v++) {
for (int u = 0; u < U; u++) {
int idx = u + v * U;
if (!valid[idx]) continue;
GreedyFaceKey key = keys[idx];
int w = 1;
while (u + w < U && valid[idx + w] &&
keys[idx + w] == key) {
w++;
}
int h = 1;
bool done = false;
while (v + h < V && !done) {
for (int k = 0; k < w; k++) {
int idx2 = (u + k) + (v + h) * U;
if (!valid[idx2] || !(keys[idx2] == key)) {
done = true;
break;
}
}
if (!done) h++;
}
for (int dv = 0; dv < h; dv++) {
for (int du = 0; du < w; du++) {
valid[(u + du) + (v + dv) * U] = 0;
}
}
greedyMergedQuads++;
greedyTilesEmitted += (w * h);
beginGreedy();
t->setMipmapEnable(key.mipmap);
if (SharedConstants::TEXTURE_LIGHTING) {
t->tex2(key.lightKey);
}
float rr = ((key.colorKey >> 16) & 0xff) / 255.0f;
float gg = ((key.colorKey >> 8) & 0xff) / 255.0f;
float bb = (key.colorKey & 0xff) / 255.0f;
float ao1 = ((key.aoKey >> 24) & 0xff) / 255.0f;
float ao2 = ((key.aoKey >> 16) & 0xff) / 255.0f;
float ao3 = ((key.aoKey >> 8) & 0xff) / 255.0f;
float ao4 = (key.aoKey & 0xff) / 255.0f;
tileRenderer->setGreedyAO(
rr, gg, bb, ao1, ao2, ao3, ao4,
SharedConstants::TEXTURE_LIGHTING ? key.lightKey
: 0);
int baseX = 0, baseY = 0, baseZ = 0;
switch (face) {
case 0:
case 1:
baseX = x0 + u;
baseY = y0 + slice;
baseZ = z0 + v;
tileRenderer->setShape(
0.0f, 0.0f, 0.0f, (float)w, 1.0f, (float)h);
if (face == 0) {
t->normal(0.0f, -1.0f, 0.0f);
tileRenderer->renderFaceDown(
key.tile, baseX, baseY, baseZ, key.tex);
} else {
t->normal(0.0f, 1.0f, 0.0f);
tileRenderer->renderFaceUp(
key.tile, baseX, baseY, baseZ, key.tex);
}
break;
case 2:
case 3:
baseX = x0 + u;
baseY = y0 + v;
baseZ = z0 + slice;
tileRenderer->setShape(
0.0f, 0.0f, 0.0f, (float)w, (float)h, 1.0f);
if (face == 2) {
t->normal(0.0f, 0.0f, -1.0f);
tileRenderer->renderNorth(
key.tile, baseX, baseY, baseZ, key.tex);
} else {
t->normal(0.0f, 0.0f, 1.0f);
tileRenderer->renderSouth(
key.tile, baseX, baseY, baseZ, key.tex);
}
break;
case 4:
case 5:
baseX = x0 + slice;
baseY = y0 + v;
baseZ = z0 + u;
tileRenderer->setShape(0.0f, 0.0f, 0.0f, 1.0f,
(float)h, (float)w);
if (face == 4) {
t->normal(-1.0f, 0.0f, 0.0f);
tileRenderer->renderWest(
key.tile, baseX, baseY, baseZ, key.tex);
} else {
t->normal(1.0f, 0.0f, 0.0f);
tileRenderer->renderEast(
key.tile, baseX, baseY, baseZ, key.tex);
}
break;
}
// who the fuck at 4j named those functions.
tileRenderer->setApplyAmbienceOcclusion(false);
faceRendered = true;
}
}
}
return faceRendered;
};
struct LiquidTopKey {
Tile* tile;
Icon* tex;
uint32_t colorKey;
int lightKey;
bool mipmap;
int heightQ;
float height;
bool operator==(const LiquidTopKey& other) const {
return tile == other.tile && tex == other.tex &&
colorKey == other.colorKey &&
lightKey == other.lightKey && mipmap == other.mipmap &&
heightQ == other.heightQ;
}
};
// liquids & oceans are usually flat, so they can definitively benefit from having this optimization.
auto greedyMeshLiquidTop = [&]() {
const int U = 16;
const int V = 16;
bool faceRendered = false;
const float hEps = 1e-4f;
for (int ly = 0; ly < 16; ly++) {
std::vector<uint8_t> valid(U * V, 0);
std::vector<LiquidTopKey> keys(U * V);
for (int lz = 0; lz < V; lz++) {
for (int lx = 0; lx < U; lx++) {
unsigned char tileId = getTileIdLocal(lx, ly, lz);
if (tileId == 0 || tileId == 0xff) continue;
Tile* tile = Tile::tiles[tileId];
if (tile == nullptr) continue;
if (tile->getRenderShape() != Tile::SHAPE_WATER)
continue;
if (tile->getRenderLayer() != currentLayer) continue;
int wx = x0 + lx;
int wy = y0 + ly;
int wz = z0 + lz;
if (!tile->shouldRenderFace(level, wx, wy + 1, wz, 1))
continue;
if (LiquidTile::getSlopeAngle(level, wx, wy, wz,
tile->material) > -999)
continue;
float h0 = tileRenderer->getWaterHeightAt(
wx, wy, wz, tile->material);
float h1 = tileRenderer->getWaterHeightAt(
wx, wy, wz + 1, tile->material);
float h2 = tileRenderer->getWaterHeightAt(
wx + 1, wy, wz + 1, tile->material);
float h3 = tileRenderer->getWaterHeightAt(
wx + 1, wy, wz, tile->material);
float hmin = h0;
if (h1 < hmin) hmin = h1;
if (h2 < hmin) hmin = h2;
if (h3 < hmin) hmin = h3;
float hmax = h0;
if (h1 > hmax) hmax = h1;
if (h2 > hmax) hmax = h2;
if (h3 > hmax) hmax = h3;
if (hmax - hmin > hEps) continue;
float height = hmax - 0.001f;
if (height < 0.0f) height = hmax;
int heightQ = (int)(height * 4096.0f + 0.5f);
int data = level->getData(wx, wy, wz);
Icon* tex = tileRenderer->getTexture(tile, 1, data);
if (tex == nullptr) continue;
LiquidTopKey key{};
key.tile = tile;
key.tex = tex;
key.mipmap = Tile::mipmapEnable[tileId];
key.heightQ = heightQ;
key.height = height;
int col = tile->getColor(level, wx, wy, wz);
float r = ((col >> 16) & 0xff) / 255.0f;
float g = ((col >> 8) & 0xff) / 255.0f;
float b = ((col) & 0xff) / 255.0f;
if (GameRenderer::anaglyph3d) {
float cr = (r * 30 + g * 59 + b * 11) / 100;
float cg = (r * 30 + g * 70) / (100);
float cb = (r * 30 + b * 70) / (100);
r = cr;
g = cg;
b = cb;
}
if (SharedConstants::TEXTURE_LIGHTING) {
key.lightKey = tileRenderer->getLightColor(
tile, level, wx, wy, wz);
} else {
float br = tile->getBrightness(level, wx, wy, wz);
r *= br;
g *= br;
b *= br;
key.lightKey = 0;
}
auto clampByte = [](float v) -> uint8_t {
int iv = (int)(v * 255.0f + 0.5f);
if (iv < 0) iv = 0;
if (iv > 255) iv = 255;
return (uint8_t)iv;
};
key.colorKey = (clampByte(r) << 16) |
(clampByte(g) << 8) | (clampByte(b));
valid[lx + lz * U] = 1;
keys[lx + lz * U] = key;
}
}
for (int v = 0; v < V; v++) {
for (int u = 0; u < U; u++) {
int idx = u + v * U;
if (!valid[idx]) continue;
LiquidTopKey key = keys[idx];
int w = 1;
while (u + w < U && valid[idx + w] &&
keys[idx + w] == key) {
w++;
}
int h = 1;
bool done = false;
while (v + h < V && !done) {
for (int k = 0; k < w; k++) {
int idx2 = (u + k) + (v + h) * U;
if (!valid[idx2] || !(keys[idx2] == key)) {
done = true;
break;
}
}
if (!done) h++;
}
for (int dv = 0; dv < h; dv++) {
for (int du = 0; du < w; du++) {
valid[(u + du) + (v + dv) * U] = 0;
}
}
greedyLiquidMergedQuads++;
greedyLiquidTilesEmitted += (w * h);
beginGreedy();
tileRenderer->setApplyAmbienceOcclusion(false);
t->setMipmapEnable(key.mipmap);
if (SharedConstants::TEXTURE_LIGHTING) {
t->tex2(key.lightKey);
}
float rr = ((key.colorKey >> 16) & 0xff) / 255.0f;
float gg = ((key.colorKey >> 8) & 0xff) / 255.0f;
float bb = (key.colorKey & 0xff) / 255.0f;
t->color(rr, gg, bb);
int baseX = x0 + u;
int baseY = y0 + ly;
int baseZ = z0 + v;
tileRenderer->setShape(0.0f, 0.0f, 0.0f, (float)w,
key.height, (float)h);
t->normal(0.0f, 1.0f, 0.0f);
tileRenderer->renderFaceUp(key.tile, baseX, baseY,
baseZ, key.tex);
for (int dv = 0; dv < h; dv++) {
for (int du = 0; du < w; du++) {
int lx = u + du;
int lz = v + dv;
int maskIdx = (ly << 8) | (lz << 4) | lx;
greedyLiquidTop[maskIdx] = 1;
}
}
faceRendered = true;
}
}
}
return faceRendered;
};
#if defined(ENABLE_GREEDY_MESHING)
rendered |= greedyMeshLiquidTop();
if (currentLayer == 0) {
tileRenderer->setApplyAmbienceOcclusion(false);
rendered |= greedyMeshFace(0);
rendered |= greedyMeshFace(1);
rendered |= greedyMeshFace(2);
rendered |= greedyMeshFace(3);
rendered |= greedyMeshFace(4);
rendered |= greedyMeshFace(5);
}
endGreedy();
#endif
// 4J - changed loop order here to leave y as the innermost loop for
// better cache performance
@ -470,17 +1254,6 @@ void Chunk::rebuild() {
// int tileId =
// region->getTile(x,y,z);
if (tileId > 0) {
if (!started) {
started = true;
glNewList(lists + currentLayer, GL_COMPILE);
glDepthMask(true); // 4J added
t->useCompactVertices(true); // 4J added
t->begin();
t->offset((float)(-this->x), (float)(-this->y),
(float)(-this->z));
}
Tile* tile = Tile::tiles[tileId];
if (currentLayer == 0 && tile->isEntityTile()) {
std::shared_ptr<TileEntity> et =
@ -495,6 +1268,19 @@ void Chunk::rebuild() {
if (renderLayer != currentLayer) {
renderNextLayer = true;
} else if (renderLayer == currentLayer) {
#if defined(ENABLE_GREEDY_MESHING)
if (currentLayer == 0) {
int lx = x - x0;
int ly = y - y0;
int lz = z - z0;
if (greedyEligible[greedyIndex(lx, ly, lz)] !=
0) {
continue;
}
}
#endif
beginNormal();
rendered |=
tileRenderer->tesselateInWorld(tile, x, y, z);
}
@ -503,12 +1289,14 @@ void Chunk::rebuild() {
}
}
if (started) {
t->end();
bounds.addBounds(t->bounds); // 4J MGH - added
#if defined(ENABLE_GREEDY_MESHING)
endGreedy();
#endif
endNormal();
if (listStarted) {
glEndList();
t->useCompactVertices(false); // 4J added
t->offset(0, 0, 0);
} else {
rendered = false;
}

View file

@ -65,6 +65,7 @@ Tesselator::Tesselator(int size) {
useCompactFormat360 = false; // 4J added
mipmapEnable = true; // 4J added
useProjectedTexturePixelShader = false; // 4J added
useTileUVEnabled = false;
this->size = size;
@ -110,7 +111,9 @@ void Tesselator::end() {
_array->data(),
useCompactFormat360
? C4JRender::VERTEX_TYPE_COMPRESSED
: C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1,
: (useTileUVEnabled
? C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TILEUV
: C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1),
useProjectedTexturePixelShader
? C4JRender::PIXEL_SHADER_TYPE_PROJECTION
: C4JRender::PIXEL_SHADER_TYPE_STANDARD);
@ -138,7 +141,9 @@ void Tesselator::end() {
RenderManager.DrawVertices(
(C4JRender::ePrimitiveType)mode, vertexCount,
_array->data(),
C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1,
useTileUVEnabled
? C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TILEUV
: C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1,
C4JRender::PIXEL_SHADER_TYPE_STANDARD);
}
}
@ -177,6 +182,8 @@ void Tesselator::useCompactVertices(bool enable) {
useCompactFormat360 = enable;
}
void Tesselator::useTileUV(bool enable) { useTileUVEnabled = enable; }
bool Tesselator::getCompactVertices() { return useCompactFormat360; }
bool Tesselator::setMipmapEnable(bool enable) {
@ -600,4 +607,4 @@ void Tesselator::addOffset(float x, float y, float z) {
zo += z;
}
bool Tesselator::hasMaxVertices() { return false; }
bool Tesselator::hasMaxVertices() { return false; }

View file

@ -33,6 +33,7 @@ private:
int p;
bool useCompactFormat360; // 4J - added
bool useProjectedTexturePixelShader; // 4J - added
bool useTileUVEnabled;
public:
int count;
@ -125,6 +126,7 @@ public:
void useCompactVertices(bool enable); // 4J added
bool getCompactVertices(); // AP added
void useProjectedTexture(bool enable); // 4J added
void useTileUV(bool enable);
void tex(float u, float v);
void tex2(int tex2); // 4J - change brought forward from 1.8.2
void color(float r, float g, float b);
@ -143,4 +145,4 @@ public:
bool setMipmapEnable(bool enable); // 4J added
bool hasMaxVertices(); // 4J Added
};
};

View file

@ -9,13 +9,12 @@
#include <cmath>
#include <numbers>
#include "platform/sdl2/Render.h"
#include "EntityTileRenderer.h"
#include "GameRenderer.h"
#include "Tesselator.h"
#include "app/common/App_enums.h"
#include "app/common/src/Colours/ColourTable.h"
#include "app/include/FrameProfiler.h"
#include "Tesselator.h"
#include "minecraft/Direction.h"
#include "minecraft/Facing.h"
#include "minecraft/SharedConstants.h"
@ -65,6 +64,7 @@
#include "minecraft/world/level/tile/piston/PistonBaseTile.h"
#include "minecraft/world/level/tile/piston/PistonExtensionTile.h"
#include "minecraft/world/phys/Vec3.h"
#include "platform/sdl2/Render.h"
bool TileRenderer::fancy = true;
@ -75,6 +75,7 @@ void TileRenderer::_init() {
xFlipTexture = false;
noCulling = false;
applyAmbienceOcclusion = false;
waterTopSkipMask = nullptr;
setColor = true;
northFlip = FLIP_NONE;
southFlip = FLIP_NONE;
@ -236,6 +237,40 @@ void TileRenderer::clearFixedTexture() { this->fixedTexture = nullptr; }
bool TileRenderer::hasFixedTexture() { return fixedTexture != nullptr; }
void TileRenderer::setApplyAmbienceOcclusion(bool enabled) {
applyAmbienceOcclusion = enabled;
}
void TileRenderer::setWaterTopSkipMask(const uint8_t* mask) {
waterTopSkipMask = mask;
}
void TileRenderer::setGreedyAO(float baseR, float baseG, float baseB, float ao1,
float ao2, float ao3, float ao4, int tex2) {
applyAmbienceOcclusion = true;
c1r = baseR * ao1;
c1g = baseG * ao1;
c1b = baseB * ao1;
c2r = baseR * ao2;
c2g = baseG * ao2;
c2b = baseB * ao2;
c3r = baseR * ao3;
c3g = baseG * ao3;
c3b = baseB * ao3;
c4r = baseR * ao4;
c4g = baseG * ao4;
c4b = baseB * ao4;
tc1 = tex2;
tc2 = tex2;
tc3 = tex2;
tc4 = tex2;
}
void TileRenderer::setShape(float x0, float y0, float z0, float x1, float y1,
float z1) {
if (!fixedShape) {
@ -4569,6 +4604,17 @@ bool TileRenderer::tesselateWaterInWorld(Tile* tt, int x, int y, int z) {
float g = (col >> 8 & 0xff) / 255.0f;
float b = (col & 0xff) / 255.0f;
bool up = tt->shouldRenderFace(level, x, y + 1, z, 1);
if (waterTopSkipMask) {
int lx = x - xMin;
int ly = y - yMin;
int lz = z - zMin;
if ((unsigned)lx < 16 && (unsigned)ly < 16 && (unsigned)lz < 16) {
int idx = (ly << 8) | (lz << 4) | lx;
if (waterTopSkipMask[idx]) {
up = false;
}
}
}
bool down = tt->shouldRenderFace(level, x, y - 1, z, 0);
bool dirs[4];
dirs[0] = tt->shouldRenderFace(level, x, y, z - 1, 2);
@ -4776,6 +4822,10 @@ bool TileRenderer::tesselateWaterInWorld(Tile* tt, int x, int y, int z) {
return changed;
}
float TileRenderer::getWaterHeightAt(int x, int y, int z, Material* m) {
return getWaterHeight(x, y, z, m);
}
float TileRenderer::getWaterHeight(int x, int y, int z, Material* m) {
int count = 0;
float h = 0;

View file

@ -1,5 +1,6 @@
#pragma once
#include <cstdint>
#include <memory>
#include <unordered_map>
@ -39,6 +40,7 @@ private:
Icon* fixedTexture;
bool xFlipTexture;
bool noCulling;
const uint8_t* waterTopSkipMask;
public:
static bool fancy;
@ -81,6 +83,10 @@ public:
void setFixedTexture(Icon* fixedTexture);
void clearFixedTexture();
bool hasFixedTexture();
void setApplyAmbienceOcclusion(bool enabled);
void setWaterTopSkipMask(const uint8_t* mask);
void setGreedyAO(float baseR, float baseG, float baseB, float ao1,
float ao2, float ao3, float ao4, int tex2);
void setShape(float x0, float y0, float z0, float x1, float y1, float z1);
void setShape(Tile* tt);
void setFixedShape(float x0, float y0, float z0, float x1, float y1,
@ -188,6 +194,7 @@ public:
void tesselateRowTexture(Tile* tt, int data, float x, float y, float z);
bool tesselateWaterInWorld(Tile* tt, int x, int y, int z);
float getWaterHeightAt(int x, int y, int z, Material* m);
private:
float getWaterHeight(int x, int y, int z, Material* m);

View file

@ -11,6 +11,7 @@ public:
VERTEX_TYPE_COMPRESSED,
VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_LIT,
VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TEXGEN,
VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TILEUV,
VERTEX_TYPE_COUNT
};

View file

@ -196,8 +196,7 @@ struct ShaderUniforms {
GLint uFogDensity = -1, uFogColor = -1, uFogEnable = -1;
GLint uLMTransform = -1, uUseLightmap = -1, uAlphaRef = -1;
GLint uTex0 = -1, uTex1 = -1, uGlobalLM = -1;
GLint uUseTexture = -1;
GLint uInvGamma = -1;
GLint uUseTexture = -1, uUseTileUV = -1, uInvGamma = -1;
GLint uChunkOffset = -1;
void build(const char* vs, const char* fs) {
@ -233,6 +232,7 @@ struct ShaderUniforms {
L(uTex1);
L(uGlobalLM);
L(uUseTexture);
L(uUseTileUV);
L(uInvGamma);
L(uChunkOffset);
#undef L
@ -380,8 +380,8 @@ static void glShadowSetDepthTest(bool e) {
}
static void glShadowSetBlendFunc(GLint s, GLint d) {
if (!(s_gl_shadow_mask & SHADOW_BLEND_FUNC) ||
s_gl_state.blendSrc != s || s_gl_state.blendDst != d) {
if (!(s_gl_shadow_mask & SHADOW_BLEND_FUNC) || s_gl_state.blendSrc != s ||
s_gl_state.blendDst != d) {
::glBlendFunc(s, d);
s_gl_state.blendSrc = s;
s_gl_state.blendDst = d;
@ -390,8 +390,7 @@ static void glShadowSetBlendFunc(GLint s, GLint d) {
}
static void glShadowSetDepthMask(GLboolean e) {
if (!(s_gl_shadow_mask & SHADOW_DEPTH_MASK) ||
s_gl_state.depthMask != e) {
if (!(s_gl_shadow_mask & SHADOW_DEPTH_MASK) || s_gl_state.depthMask != e) {
::glDepthMask(e);
s_gl_state.depthMask = e;
s_gl_shadow_mask |= SHADOW_DEPTH_MASK;
@ -502,8 +501,7 @@ static void pushRenderState() {
glUniform1f(s_shader.uFogStart, s_rs.fogStart);
glUniform1f(s_shader.uFogEnd, s_rs.fogEnd);
glUniform1f(s_shader.uFogDensity, s_rs.fogDensity);
glUniform4fv(s_shader.uFogColor, 1,
glm::value_ptr(s_rs.fogColor));
glUniform4fv(s_shader.uFogColor, 1, glm::value_ptr(s_rs.fogColor));
glUniform1i(s_shader.uFogEnable, s_rs.fogEnable ? 1 : 0);
}
if (s_rs_dirty_mask & DIRTY_TEXTURE) {
@ -515,11 +513,9 @@ static void pushRenderState() {
if (s_rs_dirty_mask & DIRTY_GAMMA)
glUniform1f(s_shader.uInvGamma, 1.0f / s_rs.gamma);
if (s_rs_dirty_mask & DIRTY_LMT)
glUniform4fv(s_shader.uLMTransform, 1,
glm::value_ptr(s_rs.lmt));
glUniform4fv(s_shader.uLMTransform, 1, glm::value_ptr(s_rs.lmt));
if (s_rs_dirty_mask & DIRTY_GLOBAL_LM)
glUniform2fv(s_shader.uGlobalLM, 1,
glm::value_ptr(s_rs.globalLM));
glUniform2fv(s_shader.uGlobalLM, 1, glm::value_ptr(s_rs.globalLM));
s_rs_dirty_mask = 0;
}
flushMatrices();
@ -556,6 +552,7 @@ struct ChunkDrawCall {
GLenum prim;
GLint first;
GLsizei count;
bool useTileUV;
};
struct ChunkBuffer {
@ -893,13 +890,17 @@ void C4JRender::DrawVertices(ePrimitiveType ptype, int count, void* dataIn,
int first = (int)(s_recVerts.size() / stride);
s_recVerts.insert(s_recVerts.end(), (const uint8_t*)dataIn,
(const uint8_t*)dataIn + bytes);
s_recDraws.push_back({glMode, first, (GLsizei)count});
bool useTileUV = (vType == VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TILEUV);
s_recDraws.push_back({glMode, first, (GLsizei)count, useTileUV});
return;
}
pthread_mutex_lock(&s_glCallMtx);
pushRenderState();
glUniform1i(s_shader.uUseTileUV,
(vType == VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1_TILEUV) ? 1 : 0);
glBindVertexArray(s_sVAO_std);
glBindBuffer(GL_ARRAY_BUFFER, s_sVBO_std);
@ -1019,7 +1020,10 @@ bool C4JRender::CBuffCall(int index, bool) {
pushRenderState();
glBindVertexArray(cb.vao);
for (const auto& dc : cb.draws) glDrawArrays(dc.prim, dc.first, dc.count);
for (const auto& dc : cb.draws) {
glUniform1i(s_shader.uUseTileUV, dc.useTileUV ? 1 : 0);
glDrawArrays(dc.prim, dc.first, dc.count);
}
glBindVertexArray(0);
pthread_mutex_unlock(&s_glCallMtx);
@ -1095,7 +1099,7 @@ void C4JRender::Set_matrixDirty() {
s_boundProgram = 0;
s_rs_dirty_mask = 0xFFFFFFFF;
s_gl_shadow_mask = 0;
s_normalMatDirty = true; // normal matrix dirt after iggy reset
s_normalMatDirty = true; // normal matrix dirt after iggy reset
s_matDirty = true;
s_chunkOffsetValid = false;
if (s_shader.prog) {
@ -1132,9 +1136,7 @@ void C4JRender::StateSetDepthMask(bool e) {
glShadowSetDepthMask(e ? GL_TRUE : GL_FALSE);
}
void C4JRender::StateSetBlendEnable(bool e) { glShadowSetBlend(e); }
void C4JRender::StateSetBlendFunc(int s, int d) {
glShadowSetBlendFunc(s, d);
}
void C4JRender::StateSetBlendFunc(int s, int d) { glShadowSetBlendFunc(s, d); }
void C4JRender::StateSetDepthFunc(int f) { ::glDepthFunc(f); }
void C4JRender::StateSetFaceCull(bool e) { glShadowSetCull(e); }
void C4JRender::StateSetFaceCullCW(bool e) {
@ -1181,7 +1183,10 @@ void C4JRender::StateSetFogEnable(bool e) {
}
}
void C4JRender::StateSetFogMode(int mode) {
int v = (mode == GL_LINEAR) ? 1 : (mode == GL_EXP) ? 2 : (mode == 0x0801) ? 3 : 0;
int v = (mode == GL_LINEAR) ? 1
: (mode == GL_EXP) ? 2
: (mode == 0x0801) ? 3
: 0;
if (s_rs.fogMode != v) {
s_rs.fogMode = v;
markDirty(DIRTY_FOG);
@ -1303,9 +1308,9 @@ void C4JRender::TextureBindVertex(int idx, bool scaleLight) {
s_rs.useLightmap = true;
markDirty(DIRTY_TEXTURE);
}
glm::vec4 newLmt =
scaleLight ? glm::vec4{1.f, 1.f, 8.f / 256.f, 8.f / 256.f}
: glm::vec4{1.f, 1.f, 0.f, 0.f};
glm::vec4 newLmt = scaleLight
? glm::vec4{1.f, 1.f, 8.f / 256.f, 8.f / 256.f}
: glm::vec4{1.f, 1.f, 0.f, 0.f};
if (s_rs.lmt != newLmt) {
s_rs.lmt = newLmt;
markDirty(DIRTY_LMT);

View file

@ -1,22 +1,113 @@
R"GLSL(
R "GLSL(
#version 330 core
uniform sampler2D uTex0;
uniform sampler2D uTex1;
uniform int uUseTexture;
uniform int uUseLightmap;
uniform int uUseTexture;
uniform int uUseTileUV;
uniform int uUseLightmap;
uniform float uAlphaRef;
uniform vec4 uFogColor;
uniform int uFogEnable;
uniform vec4 uFogColor;
uniform int uFogEnable;
uniform float uInvGamma;
in vec2 vUV0;
in vec2 vUV1;
in vec4 vColor;
in float vFogFactor;
out vec4 oColor;
in vec2 vUV0;
in vec2 vUV1;
in vec4 vColor;
in vec3 vWorldPos;
in float vFogFactor;
out vec4 oColor;
void main() {
vec4 texColor = (uUseTexture != 0) ? texture(uTex0, vUV0) : vec4(1.0);
vec2 uv = vUV0;
vec2 dUVdx = vec2(0.0);
vec2 dUVdy = vec2(0.0);
bool useGrad = false;
bool disableMipmap = false;
if (uUseTileUV != 0) {
vec2 baseUV = vUV0;
if (baseUV.x > 1.0) {
baseUV.x -= 1.0;
disableMipmap = true;
}
if (baseUV.y > 1.0) {
baseUV.y -= 1.0;
disableMipmap = true;
}
vec2 atlasSize = vec2(textureSize(uTex0, 0));
float tileSize = 16.0;
vec2 cell = floor(baseUV * atlasSize / tileSize);
vec2 uv0 = cell * tileSize / atlasSize;
vec2 cellSize = vec2(tileSize) / atlasSize;
vec3 dpdx = dFdx(vWorldPos);
vec3 dpdy = dFdy(vWorldPos);
vec3 n = normalize(cross(dpdx, dpdy));
vec3 uAxis;
vec3 vAxis;
if (abs(n.y) > abs(n.x) && abs(n.y) > abs(n.z)) {
uAxis = vec3(1.0, 0.0, 0.0);
vAxis = vec3(0.0, 0.0, 1.0);
} else if (abs(n.z) > abs(n.x)) {
uAxis = vec3(1.0, 0.0, 0.0);
vAxis = vec3(0.0, 1.0, 0.0);
} else {
uAxis = vec3(0.0, 0.0, 1.0);
vAxis = vec3(0.0, 1.0, 0.0);
}
float du = dot(vWorldPos, uAxis);
float dv = dot(vWorldPos, vAxis);
vec2 gradU = vec2(dot(dpdx, uAxis), dot(dpdy, uAxis));
vec2 gradV = vec2(dot(dpdx, vAxis), dot(dpdy, vAxis));
vec2 gradUx = vec2(dFdx(baseUV.x), dFdy(baseUV.x));
vec2 gradUy = vec2(dFdx(baseUV.y), dFdy(baseUV.y));
float corrUx = dot(gradUx, gradU);
float corrVx = dot(gradUx, gradV);
bool swap = abs(corrVx) > abs(corrUx);
float signU = swap ? corrVx : corrUx;
float corrUy = dot(gradUy, gradU);
float corrVy = dot(gradUy, gradV);
float signV = swap ? corrUy : corrVy;
float tu = swap ? dv : du;
float tv = swap ? du : dv;
float dtu_dx = swap ? dot(dpdx, vAxis) : dot(dpdx, uAxis);
float dtu_dy = swap ? dot(dpdy, vAxis) : dot(dpdy, uAxis);
float dtv_dx = swap ? dot(dpdx, uAxis) : dot(dpdx, vAxis);
float dtv_dy = swap ? dot(dpdy, uAxis) : dot(dpdy, vAxis);
if (signU < 0.0) {
tu = -tu;
dtu_dx = -dtu_dx;
dtu_dy = -dtu_dy;
}
if (signV < 0.0) {
tv = -tv;
dtv_dx = -dtv_dx;
dtv_dy = -dtv_dy;
}
uv = uv0 + fract(vec2(tu, tv)) * cellSize;
dUVdx = vec2(dtu_dx, dtv_dx) * cellSize;
dUVdy = vec2(dtu_dy, dtv_dy) * cellSize;
useGrad = true;
}
vec4 texColor = vec4(1.0);
if (uUseTexture != 0) {
if (useGrad) {
texColor = disableMipmap ? textureLod(uTex0, uv, 0.0) : textureGrad(uTex0, uv, dUVdx, dUVdy);
} else {
texColor = texture(uTex0, uv);
}
}
vec4 c = texColor * vColor;
if (c.a < uAlphaRef) discard;
if (uUseLightmap != 0) c.rgb *= texture(uTex1, vUV1).rgb;
@ -26,4 +117,5 @@ void main() {
oColor = c;
}
)GLSL";
) GLSL ";

View file

@ -1,25 +1,116 @@
R"GLSL(
R "GLSL(
#version 300 es
precision mediump float;
precision mediump int;
uniform sampler2D uTex0;
uniform sampler2D uTex1;
uniform int uUseTexture;
uniform int uUseLightmap;
uniform int uUseTexture;
uniform int uUseTileUV;
uniform int uUseLightmap;
uniform float uAlphaRef;
uniform vec4 uFogColor;
uniform int uFogEnable;
uniform vec4 uFogColor;
uniform int uFogEnable;
uniform float uInvGamma;
in vec2 vUV0;
in vec2 vUV1;
in vec4 vColor;
in float vFogFactor;
out vec4 oColor;
in vec2 vUV0;
in vec2 vUV1;
in vec4 vColor;
in vec3 vWorldPos;
in float vFogFactor;
out vec4 oColor;
void main() {
vec4 texColor = (uUseTexture != 0) ? texture(uTex0, vUV0) : vec4(1.0);
vec2 uv = vUV0;
vec2 dUVdx = vec2(0.0);
vec2 dUVdy = vec2(0.0);
bool useGrad = false;
bool disableMipmap = false;
if (uUseTileUV != 0) {
vec2 baseUV = vUV0;
if (baseUV.x > 1.0) {
baseUV.x -= 1.0;
disableMipmap = true;
}
if (baseUV.y > 1.0) {
baseUV.y -= 1.0;
disableMipmap = true;
}
vec2 atlasSize = vec2(textureSize(uTex0, 0));
float tileSize = 16.0;
vec2 cell = floor(baseUV * atlasSize / tileSize);
vec2 uv0 = cell * tileSize / atlasSize;
vec2 cellSize = vec2(tileSize) / atlasSize;
vec3 dpdx = dFdx(vWorldPos);
vec3 dpdy = dFdy(vWorldPos);
vec3 n = normalize(cross(dpdx, dpdy));
vec3 uAxis;
vec3 vAxis;
if (abs(n.y) > abs(n.x) && abs(n.y) > abs(n.z)) {
uAxis = vec3(1.0, 0.0, 0.0);
vAxis = vec3(0.0, 0.0, 1.0);
} else if (abs(n.z) > abs(n.x)) {
uAxis = vec3(1.0, 0.0, 0.0);
vAxis = vec3(0.0, 1.0, 0.0);
} else {
uAxis = vec3(0.0, 0.0, 1.0);
vAxis = vec3(0.0, 1.0, 0.0);
}
float du = dot(vWorldPos, uAxis);
float dv = dot(vWorldPos, vAxis);
vec2 gradU = vec2(dot(dpdx, uAxis), dot(dpdy, uAxis));
vec2 gradV = vec2(dot(dpdx, vAxis), dot(dpdy, vAxis));
vec2 gradUx = vec2(dFdx(baseUV.x), dFdy(baseUV.x));
vec2 gradUy = vec2(dFdx(baseUV.y), dFdy(baseUV.y));
float corrUx = dot(gradUx, gradU);
float corrVx = dot(gradUx, gradV);
bool swap = abs(corrVx) > abs(corrUx);
float signU = swap ? corrVx : corrUx;
float corrUy = dot(gradUy, gradU);
float corrVy = dot(gradUy, gradV);
float signV = swap ? corrUy : corrVy;
float tu = swap ? dv : du;
float tv = swap ? du : dv;
float dtu_dx = swap ? dot(dpdx, vAxis) : dot(dpdx, uAxis);
float dtu_dy = swap ? dot(dpdy, vAxis) : dot(dpdy, uAxis);
float dtv_dx = swap ? dot(dpdx, uAxis) : dot(dpdx, vAxis);
float dtv_dy = swap ? dot(dpdy, uAxis) : dot(dpdy, vAxis);
if (signU < 0.0) {
tu = -tu;
dtu_dx = -dtu_dx;
dtu_dy = -dtu_dy;
}
if (signV < 0.0) {
tv = -tv;
dtv_dx = -dtv_dx;
dtv_dy = -dtv_dy;
}
uv = uv0 + fract(vec2(tu, tv)) * cellSize;
dUVdx = vec2(dtu_dx, dtv_dx) * cellSize;
dUVdy = vec2(dtu_dy, dtv_dy) * cellSize;
useGrad = true;
}
vec4 texColor = vec4(1.0);
if (uUseTexture != 0) {
if (useGrad) {
texColor = disableMipmap ? textureLod(uTex0, uv, 0.0) : textureGrad(uTex0, uv, dUVdx, dUVdy);
} else {
texColor = texture(uTex0, uv);
}
}
vec4 c = texColor * vColor;
if (c.a < uAlphaRef) discard;
if (uUseLightmap != 0) c.rgb *= texture(uTex1, vUV1).rgb;
@ -29,4 +120,5 @@ void main() {
oColor = c;
}
)GLSL";
) GLSL ";

View file

@ -1,40 +1,43 @@
R"GLSL(
R "GLSL(
#version 330 core
layout(location=0) in vec3 aPos;
layout(location=1) in vec2 aUV0;
layout(location=2) in vec4 aColor;
layout(location=3) in vec3 aNormal;
layout(location=4) in ivec2 aLMraw;
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aUV0;
layout(location = 2) in vec4 aColor;
layout(location = 3) in vec3 aNormal;
layout(location = 4) in ivec2 aLMraw;
uniform mat4 uMVP;
uniform mat4 uMV;
uniform mat3 uNormalMatrix;
uniform mat4 uMVP;
uniform mat4 uMV;
uniform mat3 uNormalMatrix;
uniform float uNormalSign;
uniform mat4 uTexMat0;
uniform vec4 uBaseColor;
uniform int uLighting;
uniform vec3 uLight0Dir;
uniform vec3 uLight1Dir;
uniform vec3 uLightDiffuse;
uniform vec3 uLightAmbient;
uniform vec3 uChunkOffset;
uniform int uFogMode;
uniform mat4 uTexMat0;
uniform vec4 uBaseColor;
uniform int uLighting;
uniform vec3 uLight0Dir;
uniform vec3 uLight1Dir;
uniform vec3 uLightDiffuse;
uniform vec3 uLightAmbient;
uniform vec3 uChunkOffset;
uniform int uFogMode;
uniform float uFogStart;
uniform float uFogEnd;
uniform float uFogDensity;
uniform vec4 uLMTransform;
uniform vec2 uGlobalLM;
uniform vec4 uLMTransform;
uniform vec2 uGlobalLM;
out vec2 vUV0;
out vec2 vUV1;
out vec4 vColor;
out vec2 vUV0;
out vec2 vUV1;
out vec4 vColor;
out vec3 vWorldPos;
out float vFogFactor;
void main() {
vec4 aPos4 = vec4(aPos + uChunkOffset, 1.0);
vec4 eyePos = uMV * aPos4;
gl_Position = uMVP * aPos4;
vUV0 = (uTexMat0 * vec4(aUV0, 0.0, 1.0)).xy;
vec3 worldPos = aPos + uChunkOffset;
vec4 aPos4 = vec4(worldPos, 1.0);
vec4 eyePos = uMV * aPos4;
gl_Position = uMVP * aPos4;
vUV0 = (uTexMat0 * vec4(aUV0, 0.0, 1.0)).xy;
vWorldPos = worldPos;
vec2 lm = (aLMraw.x <= -500) ? uGlobalLM : vec2(aLMraw);
vUV1 = (lm / 256.0) * uLMTransform.xy + uLMTransform.zw;
@ -52,9 +55,13 @@ void main() {
}
float eDist = length(eyePos.xyz);
if (uFogMode == 1) vFogFactor = clamp((uFogEnd - eDist) / max(uFogEnd - uFogStart, 1e-4), 0.0, 1.0);
if (uFogMode == 1) vFogFactor = clamp((uFogEnd - eDist) / max(uFogEnd - uFogStart, 1e-4), 0.0, 1.0);
else if (uFogMode == 2) vFogFactor = clamp(exp(-uFogDensity * eDist), 0.0, 1.0);
else if (uFogMode == 3) { float d = uFogDensity * eDist; vFogFactor = clamp(exp(-d*d), 0.0, 1.0); }
else vFogFactor = 1.0;
else if (uFogMode == 3) {
float d = uFogDensity * eDist;
vFogFactor = clamp(exp(-d * d), 0.0, 1.0);
}
else vFogFactor = 1.0;
}
)GLSL";
) GLSL ";

View file

@ -1,43 +1,46 @@
R"GLSL(
R "GLSL(
#version 300 es
precision highp float;
precision highp int;
layout(location=0) in vec3 aPos;
layout(location=1) in vec2 aUV0;
layout(location=2) in vec4 aColor;
layout(location=3) in vec3 aNormal;
layout(location=4) in ivec2 aLMraw;
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aUV0;
layout(location = 2) in vec4 aColor;
layout(location = 3) in vec3 aNormal;
layout(location = 4) in ivec2 aLMraw;
uniform mat4 uMVP;
uniform mat4 uMV;
uniform mat3 uNormalMatrix;
uniform mat4 uMVP;
uniform mat4 uMV;
uniform mat3 uNormalMatrix;
uniform float uNormalSign;
uniform mat4 uTexMat0;
uniform vec4 uBaseColor;
uniform int uLighting;
uniform vec3 uLight0Dir;
uniform vec3 uLight1Dir;
uniform vec3 uLightDiffuse;
uniform vec3 uLightAmbient;
uniform vec3 uChunkOffset;
uniform int uFogMode;
uniform mat4 uTexMat0;
uniform vec4 uBaseColor;
uniform int uLighting;
uniform vec3 uLight0Dir;
uniform vec3 uLight1Dir;
uniform vec3 uLightDiffuse;
uniform vec3 uLightAmbient;
uniform vec3 uChunkOffset;
uniform int uFogMode;
uniform float uFogStart;
uniform float uFogEnd;
uniform float uFogDensity;
uniform vec4 uLMTransform;
uniform vec2 uGlobalLM;
uniform vec4 uLMTransform;
uniform vec2 uGlobalLM;
out vec2 vUV0;
out vec2 vUV1;
out vec4 vColor;
out vec2 vUV0;
out vec2 vUV1;
out vec4 vColor;
out vec3 vWorldPos;
out float vFogFactor;
void main() {
vec4 aPos4 = vec4(aPos + uChunkOffset, 1.0);
vec4 eyePos = uMV * aPos4;
gl_Position = uMVP * aPos4;
vUV0 = (uTexMat0 * vec4(aUV0, 0.0, 1.0)).xy;
vec3 worldPos = aPos + uChunkOffset;
vec4 aPos4 = vec4(worldPos, 1.0);
vec4 eyePos = uMV * aPos4;
gl_Position = uMVP * aPos4;
vUV0 = (uTexMat0 * vec4(aUV0, 0.0, 1.0)).xy;
vWorldPos = worldPos;
vec2 lm = (aLMraw.x <= -500) ? uGlobalLM : vec2(aLMraw);
vUV1 = (lm / 256.0) * uLMTransform.xy + uLMTransform.zw;
@ -55,9 +58,13 @@ void main() {
}
float eDist = length(eyePos.xyz);
if (uFogMode == 1) vFogFactor = clamp((uFogEnd - eDist) / max(uFogEnd - uFogStart, 1e-4), 0.0, 1.0);
if (uFogMode == 1) vFogFactor = clamp((uFogEnd - eDist) / max(uFogEnd - uFogStart, 1e-4), 0.0, 1.0);
else if (uFogMode == 2) vFogFactor = clamp(exp(-uFogDensity * eDist), 0.0, 1.0);
else if (uFogMode == 3) { float d = uFogDensity * eDist; vFogFactor = clamp(exp(-d*d), 0.0, 1.0); }
else vFogFactor = 1.0;
else if (uFogMode == 3) {
float d = uFogDensity * eDist;
vFogFactor = clamp(exp(-d * d), 0.0, 1.0);
}
else vFogFactor = 1.0;
}
)GLSL";
) GLSL ";