From 3f40de6a58bcb2529dcfba7f0340e1c0586d6665 Mon Sep 17 00:00:00 2001 From: NaySurGithub <96527285+NaySurGithub@users.noreply.github.com> Date: Fri, 6 Nov 2026 22:44:26 +0100 Subject: [PATCH 1/2] fix(render): honour ignore_lighting and one-sided entity materials Pack entities drawn by a render controller with ignore_lighting were lit by the world light where they stand, so a logo anchored in an unlit spot came out black. They now draw at full brightness, scaled by light_color_multiplier. Entities were also always drawn without face culling. Materials without DisableCulling (entity, entity_alphatest_one_sided, ...) now cull back faces, so inward-facing background planes no longer cover the model in front of them. The world matrix mirrors model z, so the culled side is swapped when the model matrix has a negative determinant. --- src/client/entity/EntityAssets.cpp | 4 ++++ src/client/entity/EntityAssets.hpp | 3 +++ src/client/entity/EntityRenderer.cpp | 14 ++++++++++++-- src/client/entity/EntityRenderer.hpp | 2 ++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/client/entity/EntityAssets.cpp b/src/client/entity/EntityAssets.cpp index 3d94400f..d5a7b43a 100644 --- a/src/client/entity/EntityAssets.cpp +++ b/src/client/entity/EntityAssets.cpp @@ -153,6 +153,10 @@ namespace perlin::entity { if (entry.IsObject()) for (const auto& [pattern, expr] : entry.object) rc.parts.push_back({LowerCase(pattern), ParseValue(expr)}); + if (const Json* il = def.Find("ignore_lighting"); il && il->IsBool()) + rc.ignoreLighting = il->boolean; + if (const Json* lm = def.Find("light_color_multiplier"); lm && lm->IsNumber()) + rc.lightMultiplier = static_cast(lm->number); if (const Json* c = def.Find("color"); c && c->IsObject()) { rc.hasColor = true; static const char* kKeys[4] = {"r", "g", "b", "a"}; diff --git a/src/client/entity/EntityAssets.hpp b/src/client/entity/EntityAssets.hpp index 2d6bc4cc..7ba06a3a 100644 --- a/src/client/entity/EntityAssets.hpp +++ b/src/client/entity/EntityAssets.hpp @@ -50,6 +50,9 @@ namespace perlin::entity { ExprPtr color[4]; bool hasColor = false; bool translucent = false; + // ignore_lighting: drawn at full brightness whatever the world light where the actor stands + bool ignoreLighting = false; + float lightMultiplier = 1.0f; }; class EntityAssets { diff --git a/src/client/entity/EntityRenderer.cpp b/src/client/entity/EntityRenderer.cpp index dc36e2f7..4434a9b8 100644 --- a/src/client/entity/EntityRenderer.cpp +++ b/src/client/entity/EntityRenderer.cpp @@ -1110,6 +1110,8 @@ namespace perlin::entity { } } layer.translucent = rc->translucent; + layer.ignoreLighting = rc->ignoreLighting; + layer.lightMultiplier = rc->lightMultiplier; for (const std::string& group : groups) { const bool single = groups.size() == 1; const attachable::BoneFilter groupFilter = @@ -1412,12 +1414,13 @@ namespace perlin::entity { const float layerHurt = layer.material.hurtTint ? hurt : 0.0f; const bool fullbright = layer.material.emissive == 2; const glm::vec3 tint(layer.tint[0], layer.tint[1], layer.tint[2]); + const glm::vec3 layerLight = layer.ignoreLighting ? glm::vec3(layer.lightMultiplier) : light; for (const attachable::Quad& q : layer.quads) { const glm::vec3 n = rot * q.n; const float lit = std::min( 1.0f, (std::max(0.0f, glm::dot(n, l0)) + std::max(0.0f, glm::dot(n, l1))) * 0.6f + 0.4f); const uint32_t color = fullbright || packLit ? PackColor(tint, layer.tint[3]) - : PackColor(light * lit * tint, layer.tint[3]); + : PackColor(layerLight * lit * tint, layer.tint[3]); const uint32_t base = static_cast(vertices.size()); for (int i = 0; i < 4; i++) vertices.push_back({q.p[i].x, q.p[i].y, q.p[i].z, color, q.uv[i].x, q.uv[i].y}); @@ -1454,7 +1457,7 @@ namespace perlin::entity { draw.material = DrawMaterial::Entity; draw.player = e.isPlayer; draw.entityType = e.type.c_str(); - if (fullbright) { + if (fullbright || layer.ignoreLighting) { draw.blockLight = 15; draw.skyLight = 15; } else if (levels.x >= 0) { @@ -1472,6 +1475,13 @@ namespace perlin::entity { draw.depthWrite = layer.material.depthWrite && draw.blend != 2; draw.depth = layer.material.depthTest; draw.depthFunc = layer.material.depthFunc; + // one-sided materials (no DisableCulling) hide faces seen from behind: some packs build a background + // box from inward-facing planes that must not cover the model; the world mirrors model z, which + // turns the faces' winding around + CullMode cull = layer.material.noCull ? CullMode::None : layer.material.cull; + if (cull != CullMode::None && glm::determinant(glm::mat3(model)) < 0.0f) + cull = cull == CullMode::Back ? CullMode::Front : CullMode::Back; + draw.cull = cull; draw.colorWrite = layer.material.colorWrite; draw.alphaWrite = layer.material.alphaWrite; if (layer.material.customBlend && layer.material.blend != Blend::AlphaTest && diff --git a/src/client/entity/EntityRenderer.hpp b/src/client/entity/EntityRenderer.hpp index cf48ff0d..9e9a6923 100644 --- a/src/client/entity/EntityRenderer.hpp +++ b/src/client/entity/EntityRenderer.hpp @@ -143,6 +143,8 @@ namespace perlin::entity { std::string texture; bool translucent = false; float tint[4] = {1, 1, 1, 1}; + bool ignoreLighting = false; + float lightMultiplier = 1.0f; MaterialInfo material; uint32_t maskRgb = 0xFFFFFF; // entity's own model; invisibility hides it, keeps armor and attached items From e7549ab83646ffdd6c665e8b9047acb236a1b302 Mon Sep 17 00:00:00 2001 From: NaySurGithub <96527285+NaySurGithub@users.noreply.github.com> Date: Fri, 6 Nov 2026 22:56:38 +0100 Subject: [PATCH 2/2] fix(geometry): keep a lone east or west face on the mirrored side Entity cubes take east/west UVs from the opposite face to undo Bedrock's mirrored x, but the per-face presence check still used the unswapped index. A cube listing only east (or only west) got its quad on the wrong side, facing outward: the Hive logo's inward background walls showed as dark slivers past the letters. --- src/client/attachable/Geometry.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/attachable/Geometry.cpp b/src/client/attachable/Geometry.cpp index 672fa822..23e298dc 100644 --- a/src/client/attachable/Geometry.cpp +++ b/src/client/attachable/Geometry.cpp @@ -400,13 +400,14 @@ namespace perlin::attachable { } } for (int i = 0; i < 6; i++) { - if (!c.boxUv && !c.faces[i].present) - continue; int fi = i; const bool mirrored = c.mirror != mirrorTextures; bool flip = mirrored; if (mirrored && i < 2) fi = 1 - i; + // the x mirror moves east/west to the other side: a face the cube lists sits on that other side + if (!c.boxUv && !c.faces[fi].present) + continue; const FaceDef& f = faces[i]; const glm::vec2 o = uvOrigin[fi], s = uvSize[fi]; glm::vec2 a = o, b = o + glm::vec2(s.x, 0), cc = o + s, dd = o + glm::vec2(0, s.y);