[Scummvm-git-logs] scummvm master -> 9b0c32ef97b5f7b5bfa9459aee734bfc461f3ecb

neuromancer noreply at scummvm.org
Sun Mar 1 13:29:01 UTC 2026


This automated email contains information about 13 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
8126c6a0f8 FREESCAPE: fix rebase conflicts in area.cpp
140485479c FREESCAPE: extended new rendering to tingyl
0fea7584de FREESCAPE: extended new rendering to shaders
de1efbe042 FREESCAPE: fix rebase conflicts in area.cpp
19acb87f0a FREESCAPE: removed depth testing code
878ae8a478 FREESCAPE: reduced artifacts in rendering thanks to @dhruv0154 fixes
32bbd7071d FREESCAPE: fix culling issues in castle master.
6413ffa6a0 FREESCAPE: fix overlap for planar objects.
5e8deecc78 FREESCAPE: fixes for renderRectangle
1fc666556b FREESCAPE: fixed invisible bottom of a chest in eclipse demo (cpc)
e7da5f1b9a FREESACPE: improved debugger.
814f7e2243 FREESCAPE: fix artifacts in castle master.
9b0c32ef97 FREESCAPE: updated credits


Commit: 8126c6a0f860aeb4f3d59037e013defe272b61dd
    https://github.com/scummvm/scummvm/commit/8126c6a0f860aeb4f3d59037e013defe272b61dd
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: fix rebase conflicts in area.cpp

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/gfx.cpp
    engines/freescape/gfx.h
    engines/freescape/gfx_opengl.cpp
    engines/freescape/gfx_opengl.h
    engines/freescape/objects/geometricobject.cpp
    engines/freescape/objects/object.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 90bb56e99b0..cba071534ad 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -84,18 +84,6 @@ Area::Area(uint16 areaID_, uint16 areaFlags_, ObjectMap *objectsByID_, ObjectMap
 		}
 	}
 
-	// sort so that those that are planar are drawn last
-	struct {
-		bool operator()(Object *object1, Object *object2) {
-			if (!object1->isPlanar() && object2->isPlanar())
-				return true;
-			if (object1->isPlanar() && !object2->isPlanar())
-				return false;
-			return object1->getObjectID() > object2->getObjectID();
-		};
-	} compareObjects;
-
-	Common::sort(_drawableObjects.begin(), _drawableObjects.end(), compareObjects);
 	_lastTick = 0;
 }
 
@@ -238,11 +226,8 @@ void Area::resetArea() {
 void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d camera, Math::Vector3d direction, bool insideWait) {
 	bool runAnimation = animationTicks != _lastTick;
 	assert(_drawableObjects.size() > 0);
-	ObjectArray planarObjects;
-	ObjectArray nonPlanarObjects;
+	ObjectArray sortedObjects;
 	Object *floor = nullptr;
-	Common::HashMap<Object *, float> sizes;
-	float offset = MAX(0.15, 1.0 / _scale);
 
 	for (auto &obj : _drawableObjects) {
 		if (!obj->isDestroyed() && !obj->isInvisible()) {
@@ -256,10 +241,7 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 				continue;
 			}
 
-			if (obj->isPlanar())
-				planarObjects.push_back(obj);
-			else
-				nonPlanarObjects.push_back(obj);
+			sortedObjects.push_back(obj);
 		}
 	}
 
@@ -269,118 +251,120 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 		gfx->depthTesting(true);
 	}
 
-	Common::HashMap<Object *, float> offsetMap;
-	for (auto &planar : planarObjects)
-		offsetMap[planar] = 0;
-
-	for (auto &planar : planarObjects) {
-		Math::Vector3d centerPlanar = planar->_boundingBox.getMin() + planar->_boundingBox.getMax();
-		centerPlanar /= 2;
-		Math::Vector3d distance;
-		for (auto &object : nonPlanarObjects) {
-			if (object->_partOfGroup)
-				continue;
-
-			distance = object->_boundingBox.distance(centerPlanar);
-			if (distance.length() > 0.0001)
-				continue;
-
-			float sizeNonPlanar = object->_boundingBox.getSize().length();
-			if (sizes[planar] >= sizeNonPlanar)
-				continue;
-
-			sizes[planar] = sizeNonPlanar;
-
-			if (planar->getSize().x() == 0) {
-				if (object->getOrigin().x() >= centerPlanar.x())
-					offsetMap[planar] = -offset;
-				else
-					offsetMap[planar] = offset;
-			} else if (planar->getSize().y() == 0) {
-				if (object->getOrigin().y() >= centerPlanar.y())
-					offsetMap[planar] = -offset;
-				else
-					offsetMap[planar] = offset;
-			} else if (planar->getSize().z() == 0) {
-				if (object->getOrigin().z() >= centerPlanar.z())
-					offsetMap[planar] = -offset;
-				else
-					offsetMap[planar] = offset;
-			} else
-				; //It was not really planar?!
-		}
-	}
-
-	for (auto &planar : planarObjects) {
-		Math::Vector3d centerPlanar = planar->_boundingBox.getMin() + planar->_boundingBox.getMax();
-		centerPlanar /= 2;
-		Math::Vector3d distance;
-		for (auto &object : planarObjects) {
-			if (object == planar)
-				continue;
-
-			distance = object->_boundingBox.distance(centerPlanar);
-			if (distance.length() > 0)
-				continue;
-
-			if (planar->getSize().x() == 0) {
-				if (object->getSize().x() > 0)
-					continue;
-			} else if (planar->getSize().y() == 0) {
-				if (object->getSize().y() > 0)
-					continue;
-			} else if (planar->getSize().z() == 0) {
-				if (object->getSize().z() > 0)
-					continue;
-			} else
-				continue;
-
-			//debug("planar object %d collides with planar object %d", planar->getObjectID(), object->getObjectID());
-			if (offsetMap[planar] == offsetMap[object] && offsetMap[object] != 0) {
-				// Nothing to do?
-			} else if (offsetMap[planar] == offsetMap[object] && offsetMap[object] == 0) {
-				if (planar->getSize().x() == 0) {
-					if (object->getOrigin().x() < centerPlanar.x())
-						offsetMap[planar] = -offset;
-					else
-						offsetMap[planar] = offset;
-				} else if (planar->getSize().y() == 0) {
-					if (object->getOrigin().y() < centerPlanar.y())
-						offsetMap[planar] = -offset;
-					else
-						offsetMap[planar] = offset;
-				} else if (planar->getSize().z() == 0) {
-					if (object->getOrigin().z() < centerPlanar.z())
-						offsetMap[planar] = -offset;
-					else
-						offsetMap[planar] = offset;
-				} else
-					; //It was not really planar?!
+	// Corresponds to L9c66 in assembly (bounding_box_axis_loop)
+	auto checkAxis = [](float minA, float maxA, float minB, float maxB) -> int {
+		if (minA >= maxB) { // A is clearly "greater" than B (L9c9b_one_object_clearly_further_than_the_other)
+			bool signMinA = minA >= 0;
+			bool signMaxA = maxA >= 0;
+			bool signMinB = minB >= 0;
+			bool signMaxB = maxB >= 0;
+
+			if (signMinA != signMaxA) // A covers 0 (L9ce6_first_object_is_closer)
+				return 1; // A is closer
+			if (signMinB != signMaxB) // B covers 0 (L9cec_second_object_is_closer)
+				return 2; // B is closer
+
+			if (signMinA != signMinB) // Different sides (L9cf3_objects_incomparable_in_this_axis)
+				return 0;
+
+			// Same side
+			if (!signMinA) { // Negative side (sign bit set in asm)
+				if (minA > minB) return 1; // A closer
+				if (minA < minB) return 2; // B closer
+				if (maxA > maxB) return 1; // A closer
+				return 2; // B closer
+			} else { // Positive side (sign bit clear in asm)
+				if (minA < minB) return 1; // A closer
+				if (minA > minB) return 2; // B closer
+				if (maxA > maxB) return 2; // B closer
+				return 1; // A closer
+			}
+		} else if (minB >= maxA) { // B is clearly "greater" than A
+			bool signMinB = minB >= 0;
+			bool signMaxB = maxB >= 0;
+			bool signMinA = minA >= 0;
+			bool signMaxA = maxA >= 0;
+
+			if (signMinB != signMaxB) // B covers 0 (L9cec_second_object_is_closer)
+				return 2; // B is closer
+			if (signMinA != signMaxA) // A covers 0 (L9ce6_first_object_is_closer)
+				return 1; // A is closer
+
+			if (signMinA != signMinB) // Different sides (L9cf3_objects_incomparable_in_this_axis)
+				return 0;
+
+			// Same side
+			if (!signMinB) { // Negative side
+				if (minB > minA) return 2; // B closer
+				if (minB < minA) return 1; // A closer
+				if (maxB > maxA) return 2; // B closer
+				return 1; // A closer
+			} else { // Positive side
+				if (minB < minA) return 2; // B closer
+				if (minB > minA) return 1; // A closer
+				if (maxB > maxA) return 1; // A closer
+				return 2; // B closer
 			}
 		}
-	}
-
-	for (auto &obj : nonPlanarObjects) {
-		if (gfx->_debugHighlightObjectID != -1 && obj->getObjectID() != gfx->_debugHighlightObjectID)
-			continue;
-
-		obj->draw(gfx);
-
-		// draw bounding boxes
-		if (gfx->_debugRenderBoundingBoxes) {
-			if (gfx->_debugBoundingBoxFilterID == -1 || gfx->_debugBoundingBoxFilterID == obj->getObjectID()) {
-				gfx->drawAABB(obj->_boundingBox, 0, 255, 0);
+		return 0; // Overlap (L9cf3_objects_incomparable_in_this_axis)
+	};
+
+	// Bubble sort as implemented in castlemaster2-annotated.asm (L9c2d_sort_objects_for_rendering)
+	// NOTE: The sorting is performed on unprojected world-space coordinates relative to the player (L847f).
+	// The rotation/view matrix (computed in L95de) is NOT applied to the bounding boxes used for sorting.
+	// It is only applied to the vertices during the projection phase (L850f/L9177).
+	int n = sortedObjects.size();
+	if (n > 1) {
+		for (int i = 0; i < n; i++) { // L9c31_whole_object_pass_loop
+			bool changed = false;
+			for (int j = 0; j < n - 1; j++) { // L9c45_objects_loop
+				Object *a = sortedObjects[j];
+				Object *b = sortedObjects[j + 1];
+
+				Math::AABB bboxA = a->_occlusionBox;
+				Math::AABB bboxB = b->_occlusionBox;
+				Math::Vector3d minA = bboxA.getMin() - camera;
+				Math::Vector3d maxA = bboxA.getMax() - camera;
+				Math::Vector3d minB = bboxB.getMin() - camera;
+				Math::Vector3d maxB = bboxB.getMax() - camera;
+
+				int result = 0;
+
+				// X axis
+				result = (result << 2) | checkAxis(minA.x(), maxA.x(), minB.x(), maxB.x());
+				// Y axis
+				result = (result << 2) | checkAxis(minA.y(), maxA.y(), minB.y(), maxB.y());
+				// Z axis
+				result = (result << 2) | checkAxis(minA.z(), maxA.z(), minB.z(), maxB.z());
+
+				bool keepOrder = false;
+				// If result indicates B is closer in at least one axis, AND A is NEVER closer in any axis, keep order (A before B)
+				// Codes where B is closer (2) and A is not (1):
+				// 2 (Z), 8 (Y), 32 (X) -> hex: 02, 08, 20
+				// 2+8=10 (0A), 2+32=34 (22), 8+32=40 (28)
+				// 2+8+32=42 (2A)
+				// L9d37_next_object (Keep order)
+				if (result == 0x02 || result == 0x08 || result == 0x20 ||
+					result == 0x0A || result == 0x22 || result == 0x28 || result == 0x2A)
+					keepOrder = true; // A before B
+
+				if (!keepOrder) {
+					// Swap objects (L9d2c_flip_objects_loop)
+					sortedObjects[j] = b;
+					sortedObjects[j + 1] = a;
+					changed = true;
+				}
 			}
+			if (!changed)
+				break;
 		}
 	}
 
-	for (auto &pair : offsetMap) {
-		Object *obj = pair._key;
-
+	for (auto &obj : sortedObjects) {
 		if (gfx->_debugHighlightObjectID != -1 && obj->getObjectID() != gfx->_debugHighlightObjectID)
 			continue;
 
-		obj->draw(gfx, pair._value);
+		obj->draw(gfx);
 
 		// draw bounding boxes
 		if (gfx->_debugRenderBoundingBoxes) {
@@ -389,7 +373,6 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 			}
 		}
 	}
-
 	_lastTick = animationTicks;
 }
 
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index 83410420e9a..d4ad2d759ed 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -850,13 +850,13 @@ void Renderer::renderCube(const Math::Vector3d &originalOrigin, const Math::Vect
 	uint color = (*colours)[0];
 	uint ecolor = ecolours ? (*ecolours)[0] : 0;
 
-	if (size.x() <= 1) {
+	/*if (size.x() <= 1) {
 		origin.x() += offset;
 	} else if (size.y() <= 1) {
 		origin.y() += offset;
 	} else if (size.z() <= 1) {
 		origin.z() += offset;
-	}
+	}*/
 
 	if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
 		setStippleData(stipple);
@@ -980,8 +980,7 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 	Math::Vector3d size = originalSize;
 	Math::Vector3d origin = originalOrigin;
 
-	if (!_isAccelerated)
-		polygonOffset(true);
+	enableCulling(false);
 
 	if (size.x() > 0 && size.y() > 0 && size.z() > 0) {
 		/* According to https://www.shdon.com/freescape/
@@ -998,17 +997,16 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 		it were a rectangle perpendicular to the Z axis.
 		TODO: fix this case.
 		*/
-		if (size.x() <= size.y() && size.x() <= size.z())
+		/*if (size.x() <= size.y() && size.x() <= size.z())
 			size.x() = 0;
 		else if (size.y() <= size.x() && size.y() <= size.z())
 			size.y() = 0;
 		else if (size.z() <= size.x() && size.z() <= size.y())
 			size.z() = 0;
 		else
-			error("Invalid size!");
+			error("Invalid size!");*/
 	}
 
-	float dx, dy, dz;
 	uint8 r1, g1, b1, r2, g2, b2;
 	byte *stipple = nullptr;
 	Common::Array<Math::Vector3d> vertices;
@@ -1031,33 +1029,36 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 		if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
 			setStippleData(stipple);
 			useColor(r1, g1, b1);
-			vertices.clear();
-			vertices.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z()));
-
-			dx = dy = dz = 0.0;
+			float d1x = 0, d1y = 0, d1z = 0;
 			if (size.x() == 0) {
-				dy = size.y();
+				d1y = size.y();
 			} else if (size.y() == 0) {
-				dx = size.x();
+				d1x = size.x();
 			} else if (size.z() == 0) {
-				dx = size.x();
+				d1x = size.x();
 			}
 
-			vertices.push_back(Math::Vector3d(origin.x() + dx, origin.y() + dy, origin.z() + dz));
-			vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
-			vertices.push_back(Math::Vector3d(origin.x(), origin.y(), origin.z()));
-
-			dx = dy = dz = 0.0;
+			float d2x = 0, d2y = 0, d2z = 0;
 			if (size.x() == 0) {
-				dz = size.z();
+				d2z = size.z();
 			} else if (size.y() == 0) {
-				dz = size.z();
+				d2z = size.z();
 			} else if (size.z() == 0) {
-				dy = size.y();
+				d2y = size.y();
 			}
 
-			vertices.push_back(Math::Vector3d(origin.x() + dx, origin.y() + dy, origin.z() + dz));
-			vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
+			vertices.clear();
+			if (i == 0) {
+				vertices.push_back(origin);
+				vertices.push_back(Math::Vector3d(origin.x() + d2x, origin.y() + d2y, origin.z() + d2z));
+				vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
+				vertices.push_back(Math::Vector3d(origin.x() + d1x, origin.y() + d1y, origin.z() + d1z));
+			} else {
+				vertices.push_back(origin);
+				vertices.push_back(Math::Vector3d(origin.x() + d1x, origin.y() + d1y, origin.z() + d1z));
+				vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
+				vertices.push_back(Math::Vector3d(origin.x() + d2x, origin.y() + d2y, origin.z() + d2z));
+			}
 			renderFace(vertices);
 			if (r1 != r2 || g1 != g2 || b1 != b2) {
 				useStipple(true);
@@ -1067,7 +1068,8 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 			}
 		}
 	}
-	polygonOffset(false);
+
+	enableCulling(true);
 }
 
 void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d &size, const Common::Array<float> *originalOrdinates, Common::Array<uint8> *colours, Common::Array<uint8> *ecolours, float offset) {
@@ -1082,9 +1084,8 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 
 	uint color = 0;
 	uint ecolor = 0;
-
+	enableCulling(false);
 	if (ordinates->size() == 6) { // Line
-		polygonOffset(true);
 		color = (*colours)[0];
 		ecolor = ecolours ? (*ecolours)[0] : 0;
 
@@ -1117,12 +1118,7 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 			renderFace(vertices);
 			useStipple(false);
 		}
-		polygonOffset(false);
 	} else {
-
-		if (!_isAccelerated)
-			polygonOffset(true);
-
 		if (size.x() == 0) {
 			for (int i = 0; i < int(ordinates->size()); i++) {
 				if (i % 3 == 0)
@@ -1177,7 +1173,7 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 		}
 	}
 
-	polygonOffset(false);
+	enableCulling(true);
 	delete(ordinates);
 }
 
diff --git a/engines/freescape/gfx.h b/engines/freescape/gfx.h
index 8f9ae18fb78..1844772ad6b 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -78,6 +78,7 @@ public:
 	virtual void flipBuffer() {}
 	virtual void useColor(uint8 r, uint8 g, uint8 b) = 0;
 	virtual void depthTesting(bool enabled) {};
+	virtual void enableCulling(bool enabled) {};
 	virtual void polygonOffset(bool enabled) = 0;
 
 	virtual Texture *createTexture(const Graphics::Surface *surface, bool is3D = false) = 0;
diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index e7c199a6172..466bb3000e7 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -77,7 +77,9 @@ void OpenGLRenderer::init() {
 
 	glDisable(GL_LIGHTING);
 	glDisable(GL_TEXTURE_2D);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
+	glFrontFace(GL_CW);
 	glEnable(GL_SCISSOR_TEST);
 	setViewport(_viewport);
 	glEnable(GL_DEPTH_CLAMP);
@@ -127,6 +129,7 @@ void OpenGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
 	glLoadIdentity();
 
 	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_CULL_FACE);
 	glEnable(GL_TEXTURE_2D);
 	glColor4f(1.0, 1.0, 1.0, 1.0);
 	glDepthMask(GL_FALSE);
@@ -154,7 +157,8 @@ void OpenGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
 
 	glDisable(GL_BLEND);
 	glDepthMask(GL_TRUE);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
 	glBindTexture(GL_TEXTURE_2D, 0); // Bind the default (empty) texture to avoid darker colors!
 	glFlush();
 }
@@ -163,6 +167,7 @@ void OpenGLRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 	OpenGLTexture *glTexture = static_cast<OpenGLTexture *>(texture);
 
 	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_CULL_FACE);
 	glEnable(GL_TEXTURE_2D);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
 
@@ -197,7 +202,8 @@ void OpenGLRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 
 	glBindTexture(GL_TEXTURE_2D, 0);
 	glDisable(GL_TEXTURE_2D);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
 	glFlush();
 }
 
@@ -331,7 +337,7 @@ void OpenGLRenderer::renderCrossair(const Common::Point &crossairPosition) {
 	glLineWidth(1);
 
 	glDisable(GL_BLEND);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -385,7 +391,7 @@ void OpenGLRenderer::renderPlayerShootRay(byte color, const Common::Point &posit
 	glLineWidth(1);
 
 	glDisable(GL_BLEND);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -456,7 +462,7 @@ void OpenGLRenderer::drawCelestialBody(Math::Vector3d position, float radius, by
 		useStipple(false);
 	}
 
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 	glPopMatrix();
 }
@@ -513,7 +519,7 @@ void OpenGLRenderer::renderPlayerShootBall(byte color, const Common::Point &posi
 	glDisableClientState(GL_VERTEX_ARRAY);
 
 	glDisable(GL_BLEND);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -554,12 +560,14 @@ void OpenGLRenderer::renderFace(const Common::Array<Math::Vector3d> &vertices) {
 }
 
 void OpenGLRenderer::depthTesting(bool enabled) {
+	glDisable(GL_DEPTH_TEST);
+}
+
+void OpenGLRenderer::enableCulling(bool enabled) {
 	if (enabled) {
-		// If we re-enable depth testing, we need to clear the depth buffer
-		glClear(GL_DEPTH_BUFFER_BIT);
-		glEnable(GL_DEPTH_TEST);
+		glEnable(GL_CULL_FACE);
 	} else {
-		glDisable(GL_DEPTH_TEST);
+		glDisable(GL_CULL_FACE);
 	}
 }
 
diff --git a/engines/freescape/gfx_opengl.h b/engines/freescape/gfx_opengl.h
index 30b1c325827..ac19acac47f 100644
--- a/engines/freescape/gfx_opengl.h
+++ b/engines/freescape/gfx_opengl.h
@@ -76,6 +76,7 @@ public:
 	virtual void polygonOffset(bool enabled) override;
 	virtual void setStippleData(byte *data) override;
 	virtual void useStipple(bool enabled) override;
+	virtual void enableCulling(bool enabled) override;
 	virtual void depthTesting(bool enabled) override;
 
 
diff --git a/engines/freescape/objects/geometricobject.cpp b/engines/freescape/objects/geometricobject.cpp
index c3647016676..48ab8db3ce2 100644
--- a/engines/freescape/objects/geometricobject.cpp
+++ b/engines/freescape/objects/geometricobject.cpp
@@ -273,6 +273,12 @@ Object *GeometricObject::duplicate() {
 
 void GeometricObject::computeBoundingBox() {
 	_boundingBox = Math::AABB();
+	_occlusionBox = Math::AABB();
+
+	// These are used for the rendered, they should NOT be refined or it will break the sorting algorithm
+	_occlusionBox.expand(_origin);
+	_occlusionBox.expand(_origin + _size);
+
 	Math::Vector3d v;
 	switch (_type) {
 	default:
diff --git a/engines/freescape/objects/object.h b/engines/freescape/objects/object.h
index 6bcc6abced1..b1b852c27b4 100644
--- a/engines/freescape/objects/object.h
+++ b/engines/freescape/objects/object.h
@@ -91,6 +91,7 @@ public:
 	uint16 _objectID;
 	Math::Vector3d _origin, _size, _rotation;
 	Math::AABB _boundingBox;
+	Math::AABB _occlusionBox;
 	Object *_partOfGroup = nullptr;
 };
 


Commit: 140485479c53666f45b5360a95aacdfdb41ae240
    https://github.com/scummvm/scummvm/commit/140485479c53666f45b5360a95aacdfdb41ae240
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: extended new rendering to tingyl

Changed paths:
    engines/freescape/gfx_tinygl.cpp
    engines/freescape/gfx_tinygl.h


diff --git a/engines/freescape/gfx_tinygl.cpp b/engines/freescape/gfx_tinygl.cpp
index b646b73c84a..7a42e0cac50 100644
--- a/engines/freescape/gfx_tinygl.cpp
+++ b/engines/freescape/gfx_tinygl.cpp
@@ -78,7 +78,8 @@ void TinyGLRenderer::init() {
 
 	tglDisable(TGL_LIGHTING);
 	tglDisable(TGL_TEXTURE_2D);
-	tglEnable(TGL_DEPTH_TEST);
+	tglEnable(TGL_CULL_FACE);
+	tglFrontFace(TGL_CW);
 	_stippleEnabled = false;
 }
 
@@ -106,6 +107,7 @@ void TinyGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
 void TinyGLRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 	TinyGL3DTexture *glTexture = static_cast<TinyGL3DTexture *>(texture);
 	tglDisable(TGL_DEPTH_TEST);
+	tglDisable(TGL_CULL_FACE);
 	tglEnable(TGL_TEXTURE_2D);
 	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_REPEAT);
 
@@ -141,7 +143,7 @@ void TinyGLRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 
 	tglBindTexture(TGL_TEXTURE_2D, 0);
 	tglDisable(TGL_TEXTURE_2D);
-	tglEnable(TGL_DEPTH_TEST);
+	tglEnable(TGL_CULL_FACE);
 	tglFlush();
 }
 
@@ -280,7 +282,6 @@ void TinyGLRenderer::renderPlayerShootBall(byte color, const Common::Point &posi
 	tglDisableClientState(TGL_VERTEX_ARRAY);
 
 	tglDisable(TGL_BLEND);
-	tglEnable(TGL_DEPTH_TEST);
 	tglDepthMask(TGL_TRUE);
 }
 
@@ -313,7 +314,6 @@ void TinyGLRenderer::renderPlayerShootRay(byte color, const Common::Point &posit
 		tglBlendFunc(TGL_ONE_MINUS_DST_COLOR, TGL_ZERO);
 	}
 
-	tglDisable(TGL_DEPTH_TEST);
 	tglDepthMask(TGL_FALSE);
 
 	tglColor4ub(r, g, b, 255);
@@ -337,7 +337,6 @@ void TinyGLRenderer::renderPlayerShootRay(byte color, const Common::Point &posit
 	tglDisableClientState(TGL_VERTEX_ARRAY);
 
 	tglDisable(TGL_BLEND);
-	tglEnable(TGL_DEPTH_TEST);
 	tglDepthMask(TGL_TRUE);
 }
 
@@ -350,7 +349,6 @@ void TinyGLRenderer::renderCrossair(const Common::Point &crossairPosition) {
 	tglEnable(TGL_BLEND);
 	tglBlendFunc(TGL_ONE_MINUS_DST_COLOR, TGL_ZERO);
 
-	tglDisable(TGL_DEPTH_TEST);
 	tglDepthMask(TGL_FALSE);
 
 	useColor(255, 255, 255);
@@ -373,7 +371,6 @@ void TinyGLRenderer::renderCrossair(const Common::Point &crossairPosition) {
 	tglDisableClientState(TGL_VERTEX_ARRAY);
 
 	tglDisable(TGL_BLEND);
-	tglEnable(TGL_DEPTH_TEST);
 	tglDepthMask(TGL_TRUE);
 }
 
@@ -463,7 +460,6 @@ void TinyGLRenderer::drawCelestialBody(Math::Vector3d position, float radius, by
 		}
 
 	tglLoadMatrixf(m);
-	tglDisable(TGL_DEPTH_TEST);
 	tglDepthMask(TGL_FALSE);
 
 	setStippleData(stipple);
@@ -490,17 +486,24 @@ void TinyGLRenderer::drawCelestialBody(Math::Vector3d position, float radius, by
 
 	useStipple(false);
 
-	tglEnable(TGL_DEPTH_TEST);
 	tglDepthMask(TGL_TRUE);
 	tglPopMatrix();
 }
 
 void TinyGLRenderer::depthTesting(bool enabled) {
-	if (enabled) {
+	/*if (enabled) {
 		tglClear(TGL_DEPTH_BUFFER_BIT);
 		tglEnable(TGL_DEPTH_TEST);
 	} else {
 		tglDisable(TGL_DEPTH_TEST);
+	}*/
+}
+
+void TinyGLRenderer::enableCulling(bool enabled) {
+	if (enabled) {
+		tglEnable(TGL_CULL_FACE);
+	} else {
+		tglDisable(TGL_CULL_FACE);
 	}
 }
 
diff --git a/engines/freescape/gfx_tinygl.h b/engines/freescape/gfx_tinygl.h
index a1a515ae88d..beed3e6f2e6 100644
--- a/engines/freescape/gfx_tinygl.h
+++ b/engines/freescape/gfx_tinygl.h
@@ -77,6 +77,7 @@ public:
 	virtual void useColor(uint8 r, uint8 g, uint8 b) override;
 	virtual void polygonOffset(bool enabled) override;
 	virtual void depthTesting(bool enabled) override;
+	virtual void enableCulling(bool enabled) override;
 	virtual void setStippleData(byte *data) override;
 	virtual void useStipple(bool enabled) override;
 


Commit: 0fea7584de738bfd41784ad50ac4acd108ea2fae
    https://github.com/scummvm/scummvm/commit/0fea7584de738bfd41784ad50ac4acd108ea2fae
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: extended new rendering to shaders

Changed paths:
    engines/freescape/gfx_opengl_shaders.cpp
    engines/freescape/gfx_opengl_shaders.h


diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 404f500a6fb..2b2eae33a36 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -118,7 +118,9 @@ void OpenGLShaderRenderer::init() {
 		_defaultShaderStippleArray[i] = _defaultStippleArray[i];
 
 	glDisable(GL_TEXTURE_2D);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
+	glFrontFace(GL_CW);
 	glEnable(GL_SCISSOR_TEST);
 	setViewport(_viewport);
 }
@@ -147,6 +149,8 @@ void OpenGLShaderRenderer::drawTexturedRect2D(const Common::Rect &screenRect, co
 	_bitmapShader->use();
 	_bitmapShader->setUniform("flipY", glTexture->_upsideDown);
 
+	glDisable(GL_CULL_FACE);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	glBindTexture(GL_TEXTURE_2D, glTexture->_id);
@@ -155,6 +159,8 @@ void OpenGLShaderRenderer::drawTexturedRect2D(const Common::Rect &screenRect, co
 
 	glDisable(GL_BLEND);
 	glDepthMask(GL_TRUE);
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
 	_bitmapShader->unbind();
 }
 
@@ -178,6 +184,7 @@ void OpenGLShaderRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 	_cubemapShader->setUniform("mvpMatrix", skyboxMVP);
 
 	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_CULL_FACE);
 
 	glBindBuffer(GL_ARRAY_BUFFER, _cubemapTexCoordVBO);
 	if (texture->_width == 1008)
@@ -194,7 +201,8 @@ void OpenGLShaderRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 
 	glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_INT, 0);
 
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
 
 	_cubemapShader->unbind();
 }
@@ -278,6 +286,7 @@ void OpenGLShaderRenderer::renderPlayerShootBall(byte color, const Common::Point
 		glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
 	}
 
+	glDisable(GL_CULL_FACE);
 	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
@@ -307,7 +316,8 @@ void OpenGLShaderRenderer::renderPlayerShootBall(byte color, const Common::Point
 	glDrawArrays(GL_TRIANGLE_FAN, 0, (triangleAmount + 2));
 
 	glDisable(GL_BLEND);
-	glEnable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -342,6 +352,7 @@ void OpenGLShaderRenderer::renderPlayerShootRay(byte color, const Common::Point
 		glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
 	}
 
+	glDisable(GL_CULL_FACE);
 	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
@@ -369,7 +380,8 @@ void OpenGLShaderRenderer::renderPlayerShootRay(byte color, const Common::Point
 	glLineWidth(1);
 
 	glDisable(GL_BLEND);
-	glEnable(GL_DEPTH_TEST);
+	glEnable(GL_CULL_FACE);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -451,7 +463,7 @@ void OpenGLShaderRenderer::drawCelestialBody(const Math::Vector3d position, floa
 	}
 
 	// === Restore state ===
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 
 	// === Cleanup binding ===
@@ -564,7 +576,7 @@ void OpenGLShaderRenderer::renderCrossair(const Common::Point &crossairPosition)
 
 	glLineWidth(1);
 	glDisable(GL_BLEND);
-	glEnable(GL_DEPTH_TEST);
+	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -650,12 +662,14 @@ void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &verti
 }
 
 void OpenGLShaderRenderer::depthTesting(bool enabled) {
+	glDisable(GL_DEPTH_TEST);
+}
+
+void OpenGLShaderRenderer::enableCulling(bool enabled) {
 	if (enabled) {
-		// If we re-enable depth testing, we need to clear the depth buffer
-		glClear(GL_DEPTH_BUFFER_BIT);
-		glEnable(GL_DEPTH_TEST);
+		glEnable(GL_CULL_FACE);
 	} else {
-		glDisable(GL_DEPTH_TEST);
+		glDisable(GL_CULL_FACE);
 	}
 }
 
diff --git a/engines/freescape/gfx_opengl_shaders.h b/engines/freescape/gfx_opengl_shaders.h
index c259fc5d4dc..b08c620f64b 100644
--- a/engines/freescape/gfx_opengl_shaders.h
+++ b/engines/freescape/gfx_opengl_shaders.h
@@ -83,6 +83,7 @@ public:
 	virtual void useColor(uint8 r, uint8 g, uint8 b) override;
 	virtual void polygonOffset(bool enabled) override;
 	virtual void depthTesting(bool enabled) override;
+	virtual void enableCulling(bool enabled) override;
 
 	virtual void setStippleData(byte *data) override;
 	virtual void useStipple(bool enabled) override;


Commit: de1efbe042f2533e90bd26da4aaf65373a4ef79f
    https://github.com/scummvm/scummvm/commit/de1efbe042f2533e90bd26da4aaf65373a4ef79f
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: fix rebase conflicts in area.cpp

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/area.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index cba071534ad..55219d84b92 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -225,8 +225,13 @@ void Area::resetArea() {
 
 void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d camera, Math::Vector3d direction, bool insideWait) {
 	bool runAnimation = animationTicks != _lastTick;
+	bool cameraChanged = camera != _lastCameraPosition;
+	bool sort = runAnimation || cameraChanged || _sortedObjects.empty();
+
 	assert(_drawableObjects.size() > 0);
-	ObjectArray sortedObjects;
+	if (sort)
+		_sortedObjects.clear();
+
 	Object *floor = nullptr;
 
 	for (auto &obj : _drawableObjects) {
@@ -241,7 +246,8 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 				continue;
 			}
 
-			sortedObjects.push_back(obj);
+			if (sort)
+				_sortedObjects.push_back(obj);
 		}
 	}
 
@@ -313,13 +319,13 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 	// NOTE: The sorting is performed on unprojected world-space coordinates relative to the player (L847f).
 	// The rotation/view matrix (computed in L95de) is NOT applied to the bounding boxes used for sorting.
 	// It is only applied to the vertices during the projection phase (L850f/L9177).
-	int n = sortedObjects.size();
-	if (n > 1) {
+	int n = _sortedObjects.size();
+	if (n > 1 && sort) {
 		for (int i = 0; i < n; i++) { // L9c31_whole_object_pass_loop
 			bool changed = false;
 			for (int j = 0; j < n - 1; j++) { // L9c45_objects_loop
-				Object *a = sortedObjects[j];
-				Object *b = sortedObjects[j + 1];
+				Object *a = _sortedObjects[j];
+				Object *b = _sortedObjects[j + 1];
 
 				Math::AABB bboxA = a->_occlusionBox;
 				Math::AABB bboxB = b->_occlusionBox;
@@ -350,8 +356,8 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 
 				if (!keepOrder) {
 					// Swap objects (L9d2c_flip_objects_loop)
-					sortedObjects[j] = b;
-					sortedObjects[j + 1] = a;
+					_sortedObjects[j] = b;
+					_sortedObjects[j + 1] = a;
 					changed = true;
 				}
 			}
@@ -360,7 +366,7 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 		}
 	}
 
-	for (auto &obj : sortedObjects) {
+	for (auto &obj : _sortedObjects) {
 		if (gfx->_debugHighlightObjectID != -1 && obj->getObjectID() != gfx->_debugHighlightObjectID)
 			continue;
 
@@ -374,6 +380,8 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 		}
 	}
 	_lastTick = animationTicks;
+	if (sort)
+		_lastCameraPosition = camera;
 }
 
 void Area::drawGroup(Freescape::Renderer *gfx, Group* group, bool runAnimation) {
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index 87bfbddb918..f65feb5ff33 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -102,6 +102,9 @@ public:
 	uint32 _lastTick;
 
 private:
+	Math::Vector3d _lastCameraPosition;
+	ObjectArray _sortedObjects;
+
 	uint16 _areaID;
 	uint16 _areaFlags;
 	ObjectMap *_objectsByID;


Commit: 19acb87f0a78a01135184c3e152839e6154eaa53
    https://github.com/scummvm/scummvm/commit/19acb87f0a78a01135184c3e152839e6154eaa53
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: removed depth testing code

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/gfx.h
    engines/freescape/gfx_opengl.cpp
    engines/freescape/gfx_opengl.h
    engines/freescape/gfx_opengl_shaders.cpp
    engines/freescape/gfx_opengl_shaders.h
    engines/freescape/gfx_tinygl.cpp
    engines/freescape/gfx_tinygl.h


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 55219d84b92..0b5b87787a2 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -252,9 +252,7 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 	}
 
 	if (floor) {
-		gfx->depthTesting(false);
 		floor->draw(gfx);
-		gfx->depthTesting(true);
 	}
 
 	// Corresponds to L9c66 in assembly (bounding_box_axis_loop)
diff --git a/engines/freescape/gfx.h b/engines/freescape/gfx.h
index 1844772ad6b..9e946a7773a 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -77,7 +77,6 @@ public:
 	 */
 	virtual void flipBuffer() {}
 	virtual void useColor(uint8 r, uint8 g, uint8 b) = 0;
-	virtual void depthTesting(bool enabled) {};
 	virtual void enableCulling(bool enabled) {};
 	virtual void polygonOffset(bool enabled) = 0;
 
diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index 466bb3000e7..920e70925f7 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -77,7 +77,6 @@ void OpenGLRenderer::init() {
 
 	glDisable(GL_LIGHTING);
 	glDisable(GL_TEXTURE_2D);
-	glDisable(GL_DEPTH_TEST);
 	glEnable(GL_CULL_FACE);
 	glFrontFace(GL_CW);
 	glEnable(GL_SCISSOR_TEST);
@@ -128,7 +127,7 @@ void OpenGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
 	glMatrixMode(GL_MODELVIEW);
 	glLoadIdentity();
 
-	glDisable(GL_DEPTH_TEST);
+
 	glDisable(GL_CULL_FACE);
 	glEnable(GL_TEXTURE_2D);
 	glColor4f(1.0, 1.0, 1.0, 1.0);
@@ -157,7 +156,6 @@ void OpenGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
 
 	glDisable(GL_BLEND);
 	glDepthMask(GL_TRUE);
-	glDisable(GL_DEPTH_TEST);
 	glEnable(GL_CULL_FACE);
 	glBindTexture(GL_TEXTURE_2D, 0); // Bind the default (empty) texture to avoid darker colors!
 	glFlush();
@@ -166,7 +164,6 @@ void OpenGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
 void OpenGLRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 	OpenGLTexture *glTexture = static_cast<OpenGLTexture *>(texture);
 
-	glDisable(GL_DEPTH_TEST);
 	glDisable(GL_CULL_FACE);
 	glEnable(GL_TEXTURE_2D);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@@ -202,7 +199,6 @@ void OpenGLRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 
 	glBindTexture(GL_TEXTURE_2D, 0);
 	glDisable(GL_TEXTURE_2D);
-	glDisable(GL_DEPTH_TEST);
 	glEnable(GL_CULL_FACE);
 	glFlush();
 }
@@ -311,7 +307,7 @@ void OpenGLRenderer::renderCrossair(const Common::Point &crossairPosition) {
 	glEnable(GL_BLEND);
 	glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
 
-	glDisable(GL_DEPTH_TEST);
+
 	glDepthMask(GL_FALSE);
 
 	useColor(255, 255, 255);
@@ -337,7 +333,6 @@ void OpenGLRenderer::renderCrossair(const Common::Point &crossairPosition) {
 	glLineWidth(1);
 
 	glDisable(GL_BLEND);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -367,7 +362,6 @@ void OpenGLRenderer::renderPlayerShootRay(byte color, const Common::Point &posit
 		glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
 	}
 
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	glColor4ub(r, g, b, 255);
@@ -391,7 +385,6 @@ void OpenGLRenderer::renderPlayerShootRay(byte color, const Common::Point &posit
 	glLineWidth(1);
 
 	glDisable(GL_BLEND);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -420,7 +413,6 @@ void OpenGLRenderer::drawCelestialBody(Math::Vector3d position, float radius, by
 		}
 
 	glLoadMatrixf(m);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	setStippleData(stipple);
@@ -462,7 +454,6 @@ void OpenGLRenderer::drawCelestialBody(Math::Vector3d position, float radius, by
 		useStipple(false);
 	}
 
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 	glPopMatrix();
 }
@@ -493,7 +484,6 @@ void OpenGLRenderer::renderPlayerShootBall(byte color, const Common::Point &posi
 		glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
 	}
 
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	glColor4ub(r, g, b, 255);
@@ -519,7 +509,6 @@ void OpenGLRenderer::renderPlayerShootBall(byte color, const Common::Point &posi
 	glDisableClientState(GL_VERTEX_ARRAY);
 
 	glDisable(GL_BLEND);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -559,10 +548,6 @@ void OpenGLRenderer::renderFace(const Common::Array<Math::Vector3d> &vertices) {
 	glDisableClientState(GL_VERTEX_ARRAY);
 }
 
-void OpenGLRenderer::depthTesting(bool enabled) {
-	glDisable(GL_DEPTH_TEST);
-}
-
 void OpenGLRenderer::enableCulling(bool enabled) {
 	if (enabled) {
 		glEnable(GL_CULL_FACE);
diff --git a/engines/freescape/gfx_opengl.h b/engines/freescape/gfx_opengl.h
index ac19acac47f..691faa42222 100644
--- a/engines/freescape/gfx_opengl.h
+++ b/engines/freescape/gfx_opengl.h
@@ -77,7 +77,6 @@ public:
 	virtual void setStippleData(byte *data) override;
 	virtual void useStipple(bool enabled) override;
 	virtual void enableCulling(bool enabled) override;
-	virtual void depthTesting(bool enabled) override;
 
 
 	Texture *createTexture(const Graphics::Surface *surface, bool is3D = false) override;
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 2b2eae33a36..adc8bda0304 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -118,7 +118,6 @@ void OpenGLShaderRenderer::init() {
 		_defaultShaderStippleArray[i] = _defaultStippleArray[i];
 
 	glDisable(GL_TEXTURE_2D);
-	glDisable(GL_DEPTH_TEST);
 	glEnable(GL_CULL_FACE);
 	glFrontFace(GL_CW);
 	glEnable(GL_SCISSOR_TEST);
@@ -150,7 +149,6 @@ void OpenGLShaderRenderer::drawTexturedRect2D(const Common::Rect &screenRect, co
 	_bitmapShader->setUniform("flipY", glTexture->_upsideDown);
 
 	glDisable(GL_CULL_FACE);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	glBindTexture(GL_TEXTURE_2D, glTexture->_id);
@@ -159,7 +157,6 @@ void OpenGLShaderRenderer::drawTexturedRect2D(const Common::Rect &screenRect, co
 
 	glDisable(GL_BLEND);
 	glDepthMask(GL_TRUE);
-	glDisable(GL_DEPTH_TEST);
 	glEnable(GL_CULL_FACE);
 	_bitmapShader->unbind();
 }
@@ -183,7 +180,6 @@ void OpenGLShaderRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 	_cubemapShader->use();
 	_cubemapShader->setUniform("mvpMatrix", skyboxMVP);
 
-	glDisable(GL_DEPTH_TEST);
 	glDisable(GL_CULL_FACE);
 
 	glBindBuffer(GL_ARRAY_BUFFER, _cubemapTexCoordVBO);
@@ -201,7 +197,6 @@ void OpenGLShaderRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 
 	glDrawElements(GL_TRIANGLES, 24, GL_UNSIGNED_INT, 0);
 
-	glDisable(GL_DEPTH_TEST);
 	glEnable(GL_CULL_FACE);
 
 	_cubemapShader->unbind();
@@ -287,7 +282,6 @@ void OpenGLShaderRenderer::renderPlayerShootBall(byte color, const Common::Point
 	}
 
 	glDisable(GL_CULL_FACE);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	useColor(r, g, b);
@@ -317,7 +311,6 @@ void OpenGLShaderRenderer::renderPlayerShootBall(byte color, const Common::Point
 
 	glDisable(GL_BLEND);
 	glEnable(GL_CULL_FACE);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -353,7 +346,6 @@ void OpenGLShaderRenderer::renderPlayerShootRay(byte color, const Common::Point
 	}
 
 	glDisable(GL_CULL_FACE);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	useColor(r, g, b);
@@ -381,7 +373,6 @@ void OpenGLShaderRenderer::renderPlayerShootRay(byte color, const Common::Point
 
 	glDisable(GL_BLEND);
 	glEnable(GL_CULL_FACE);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -449,7 +440,6 @@ void OpenGLShaderRenderer::drawCelestialBody(const Math::Vector3d position, floa
 	_triangleShader->setUniform("useStipple", false);
 
 	// === Render settings ===
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	// === Draw vertex fan ===
@@ -463,7 +453,6 @@ void OpenGLShaderRenderer::drawCelestialBody(const Math::Vector3d position, floa
 	}
 
 	// === Restore state ===
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 
 	// === Cleanup binding ===
@@ -549,7 +538,6 @@ void OpenGLShaderRenderer::renderCrossair(const Common::Point &crossairPosition)
 	glEnable(GL_BLEND);
 	glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
 
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_FALSE);
 
 	useColor(255, 255, 255);
@@ -576,7 +564,6 @@ void OpenGLShaderRenderer::renderCrossair(const Common::Point &crossairPosition)
 
 	glLineWidth(1);
 	glDisable(GL_BLEND);
-	glDisable(GL_DEPTH_TEST);
 	glDepthMask(GL_TRUE);
 }
 
@@ -661,10 +648,6 @@ void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &verti
 #endif
 }
 
-void OpenGLShaderRenderer::depthTesting(bool enabled) {
-	glDisable(GL_DEPTH_TEST);
-}
-
 void OpenGLShaderRenderer::enableCulling(bool enabled) {
 	if (enabled) {
 		glEnable(GL_CULL_FACE);
diff --git a/engines/freescape/gfx_opengl_shaders.h b/engines/freescape/gfx_opengl_shaders.h
index b08c620f64b..898bcb362ee 100644
--- a/engines/freescape/gfx_opengl_shaders.h
+++ b/engines/freescape/gfx_opengl_shaders.h
@@ -82,7 +82,6 @@ public:
 
 	virtual void useColor(uint8 r, uint8 g, uint8 b) override;
 	virtual void polygonOffset(bool enabled) override;
-	virtual void depthTesting(bool enabled) override;
 	virtual void enableCulling(bool enabled) override;
 
 	virtual void setStippleData(byte *data) override;
diff --git a/engines/freescape/gfx_tinygl.cpp b/engines/freescape/gfx_tinygl.cpp
index 7a42e0cac50..f4b094c617f 100644
--- a/engines/freescape/gfx_tinygl.cpp
+++ b/engines/freescape/gfx_tinygl.cpp
@@ -106,7 +106,6 @@ void TinyGLRenderer::drawTexturedRect2D(const Common::Rect &screenRect, const Co
 
 void TinyGLRenderer::drawSkybox(Texture *texture, Math::Vector3d camera) {
 	TinyGL3DTexture *glTexture = static_cast<TinyGL3DTexture *>(texture);
-	tglDisable(TGL_DEPTH_TEST);
 	tglDisable(TGL_CULL_FACE);
 	tglEnable(TGL_TEXTURE_2D);
 	tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_REPEAT);
@@ -256,7 +255,7 @@ void TinyGLRenderer::renderPlayerShootBall(byte color, const Common::Point &posi
 		tglBlendFunc(TGL_ONE_MINUS_DST_COLOR, TGL_ZERO);
 	}
 
-	tglDisable(TGL_DEPTH_TEST);
+
 	tglDepthMask(TGL_FALSE);
 
 	tglColor4ub(r, g, b, 255);
@@ -490,15 +489,6 @@ void TinyGLRenderer::drawCelestialBody(Math::Vector3d position, float radius, by
 	tglPopMatrix();
 }
 
-void TinyGLRenderer::depthTesting(bool enabled) {
-	/*if (enabled) {
-		tglClear(TGL_DEPTH_BUFFER_BIT);
-		tglEnable(TGL_DEPTH_TEST);
-	} else {
-		tglDisable(TGL_DEPTH_TEST);
-	}*/
-}
-
 void TinyGLRenderer::enableCulling(bool enabled) {
 	if (enabled) {
 		tglEnable(TGL_CULL_FACE);
diff --git a/engines/freescape/gfx_tinygl.h b/engines/freescape/gfx_tinygl.h
index beed3e6f2e6..cc4228018c5 100644
--- a/engines/freescape/gfx_tinygl.h
+++ b/engines/freescape/gfx_tinygl.h
@@ -76,7 +76,6 @@ public:
 
 	virtual void useColor(uint8 r, uint8 g, uint8 b) override;
 	virtual void polygonOffset(bool enabled) override;
-	virtual void depthTesting(bool enabled) override;
 	virtual void enableCulling(bool enabled) override;
 	virtual void setStippleData(byte *data) override;
 	virtual void useStipple(bool enabled) override;


Commit: 878ae8a478389b237cc22552cd71d90747e6a3a9
    https://github.com/scummvm/scummvm/commit/878ae8a478389b237cc22552cd71d90747e6a3a9
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: reduced artifacts in rendering thanks to @dhruv0154 fixes

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/gfx.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 0b5b87787a2..a515cda0399 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -257,13 +257,12 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 
 	// Corresponds to L9c66 in assembly (bounding_box_axis_loop)
 	auto checkAxis = [](float minA, float maxA, float minB, float maxB) -> int {
+		bool signMinA = minA >= 0;
+		bool signMaxA = maxA >= 0;
+		bool signMinB = minB >= 0;
+		bool signMaxB = maxB >= 0;
 		if (minA >= maxB) { // A is clearly "greater" than B (L9c9b_one_object_clearly_further_than_the_other)
-			bool signMinA = minA >= 0;
-			bool signMaxA = maxA >= 0;
-			bool signMinB = minB >= 0;
-			bool signMaxB = maxB >= 0;
-
-			if (signMinA != signMaxA) // A covers 0 (L9ce6_first_object_is_closer)
+			if (signMinA != signMaxB) // A covers 0 (L9ce6_first_object_is_closer)
 				return 1; // A is closer
 			if (signMinB != signMaxB) // B covers 0 (L9cec_second_object_is_closer)
 				return 2; // B is closer
@@ -284,11 +283,6 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 				return 1; // A closer
 			}
 		} else if (minB >= maxA) { // B is clearly "greater" than A
-			bool signMinB = minB >= 0;
-			bool signMaxB = maxB >= 0;
-			bool signMinA = minA >= 0;
-			bool signMaxA = maxA >= 0;
-
 			if (signMinB != signMaxB) // B covers 0 (L9cec_second_object_is_closer)
 				return 2; // B is closer
 			if (signMinA != signMaxA) // A covers 0 (L9ce6_first_object_is_closer)
@@ -352,6 +346,21 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 					result == 0x0A || result == 0x22 || result == 0x28 || result == 0x2A)
 					keepOrder = true; // A before B
 
+				if (result == 0) {
+					bool aInFront = minA.z() < 0;
+					bool bInFront = minB.z() < 0;
+					// if signs differ that means one object is positive in the z axis i.e. not visible in the camera
+					if (aInFront != bInFront) {
+						// one is visible, one is behind us.
+						// we want the one behind the camera (positive Z) to be drawn first
+						// so the visible object draws on top of it
+						// the one with the larger Z (positive) should come first
+						if (minA.z() > minB.z()) {
+							keepOrder = true;
+						}
+					}
+				}
+
 				if (!keepOrder) {
 					// Swap objects (L9d2c_flip_objects_loop)
 					_sortedObjects[j] = b;
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index d4ad2d759ed..c56f1d90e7c 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -980,7 +980,7 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 	Math::Vector3d size = originalSize;
 	Math::Vector3d origin = originalOrigin;
 
-	enableCulling(false);
+	//enableCulling(false);
 
 	if (size.x() > 0 && size.y() > 0 && size.z() > 0) {
 		/* According to https://www.shdon.com/freescape/
@@ -1069,7 +1069,7 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 		}
 	}
 
-	enableCulling(true);
+	//enableCulling(true);
 }
 
 void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d &size, const Common::Array<float> *originalOrdinates, Common::Array<uint8> *colours, Common::Array<uint8> *ecolours, float offset) {
@@ -1084,7 +1084,7 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 
 	uint color = 0;
 	uint ecolor = 0;
-	enableCulling(false);
+	//enableCulling(false);
 	if (ordinates->size() == 6) { // Line
 		color = (*colours)[0];
 		ecolor = ecolours ? (*ecolours)[0] : 0;
@@ -1173,7 +1173,7 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 		}
 	}
 
-	enableCulling(true);
+	//enableCulling(true);
 	delete(ordinates);
 }
 


Commit: 32bbd7071d800725e92f9a8d59a989a13da62f0a
    https://github.com/scummvm/scummvm/commit/32bbd7071d800725e92f9a8d59a989a13da62f0a
Author: dhruv (dhruvranger97 at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: fix culling issues in castle master.

Changed paths:
    engines/freescape/gfx.cpp


diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index c56f1d90e7c..0f702d0784d 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -980,8 +980,6 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 	Math::Vector3d size = originalSize;
 	Math::Vector3d origin = originalOrigin;
 
-	//enableCulling(false);
-
 	if (size.x() > 0 && size.y() > 0 && size.z() > 0) {
 		/* According to https://www.shdon.com/freescape/
 		If the bounding box is has all non-zero dimensions
@@ -1021,6 +1019,8 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 		origin.z() += offset;
 	}
 
+	bool isHorizontal = (size.y() == 0);
+
 	for (int i = 0; i < 2; i++) {
 
 		color = (*colours)[i];
@@ -1048,7 +1048,8 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 			}
 
 			vertices.clear();
-			if (i == 0) {
+			bool useFlippedWinding = (i == 0) || (isHorizontal && i == 1);
+			if (useFlippedWinding) {
 				vertices.push_back(origin);
 				vertices.push_back(Math::Vector3d(origin.x() + d2x, origin.y() + d2y, origin.z() + d2z));
 				vertices.push_back(Math::Vector3d(origin.x() + size.x(), origin.y() + size.y(), origin.z() + size.z()));
@@ -1068,11 +1069,12 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 			}
 		}
 	}
-
-	//enableCulling(true);
 }
 
-void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d &size, const Common::Array<float> *originalOrdinates, Common::Array<uint8> *colours, Common::Array<uint8> *ecolours, float offset) {
+void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d &size,
+							 const Common::Array<float> *originalOrdinates, Common::Array<uint8> *colours,
+							 Common::Array<uint8> *ecolours, float offset) {
+
 	Common::Array<float> *ordinates = new Common::Array<float>(*originalOrdinates);
 
 	uint8 r1, g1, b1, r2, g2, b2;
@@ -1084,7 +1086,7 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 
 	uint color = 0;
 	uint ecolor = 0;
-	//enableCulling(false);
+	bool isPlanar = (size.x() == 0 || size.y() == 0 || size.z() == 0);
 	if (ordinates->size() == 6) { // Line
 		color = (*colours)[0];
 		ecolor = ecolours ? (*ecolours)[0] : 0;
@@ -1142,9 +1144,10 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 		if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
 			setStippleData(stipple);
 			useColor(r1, g1, b1);
-			for (uint i = 0; i < ordinates->size(); i = i + 3) {
-				vertices.push_back(Math::Vector3d((*ordinates)[i], (*ordinates)[i + 1], (*ordinates)[i + 2]));
-			}
+			// reverse winding
+			for (int k = ordinates->size(); k > 0; k = k - 3)
+				vertices.push_back(Math::Vector3d((*ordinates)[k - 3], (*ordinates)[k - 2], (*ordinates)[k - 1]));
+
 			renderFace(vertices);
 			if (r1 != r2 || g1 != g2 || b1 != b2) {
 				useStipple(true);
@@ -1160,9 +1163,10 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 		if (getRGBAt(color, ecolor, r1, g1, b1, r2, g2, b2, stipple)) {
 			setStippleData(stipple);
 			useColor(r1, g1, b1);
-			for (int i = ordinates->size(); i > 0; i = i - 3) {
-				vertices.push_back(Math::Vector3d((*ordinates)[i - 3], (*ordinates)[i - 2], (*ordinates)[i - 1]));
-			}
+			// forward winding
+			for (uint k = 0; k < ordinates->size(); k = k + 3)
+				vertices.push_back(Math::Vector3d((*ordinates)[k], (*ordinates)[k + 1], (*ordinates)[k + 2]));
+
 			renderFace(vertices);
 			if (r1 != r2 || g1 != g2 || b1 != b2) {
 				useStipple(true);
@@ -1173,7 +1177,6 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 		}
 	}
 
-	//enableCulling(true);
 	delete(ordinates);
 }
 


Commit: 6413ffa6a04f2ec6523358cfb76cfff44524245e
    https://github.com/scummvm/scummvm/commit/6413ffa6a04f2ec6523358cfb76cfff44524245e
Author: dhruv (dhruvranger97 at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: fix overlap for planar objects.

objects like door handles were flickering from some camera position, this is solved in this commit.

Changed paths:
    engines/freescape/area.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index a515cda0399..82ea08c6692 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -261,7 +261,7 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 		bool signMaxA = maxA >= 0;
 		bool signMinB = minB >= 0;
 		bool signMaxB = maxB >= 0;
-		if (minA >= maxB) { // A is clearly "greater" than B (L9c9b_one_object_clearly_further_than_the_other)
+		if (minA >= maxB - 0.5f) { // A is clearly "greater" than B (L9c9b_one_object_clearly_further_than_the_other)
 			if (signMinA != signMaxB) // A covers 0 (L9ce6_first_object_is_closer)
 				return 1; // A is closer
 			if (signMinB != signMaxB) // B covers 0 (L9cec_second_object_is_closer)
@@ -282,7 +282,7 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 				if (maxA > maxB) return 2; // B closer
 				return 1; // A closer
 			}
-		} else if (minB >= maxA) { // B is clearly "greater" than A
+		} else if (minB >= maxA - 0.5f) { // B is clearly "greater" than A
 			if (signMinB != signMaxB) // B covers 0 (L9cec_second_object_is_closer)
 				return 2; // B is closer
 			if (signMinA != signMaxA) // A covers 0 (L9ce6_first_object_is_closer)


Commit: 5e8deecc7834e6a658d103504bc8787ff0111906
    https://github.com/scummvm/scummvm/commit/5e8deecc7834e6a658d103504bc8787ff0111906
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: fixes for renderRectangle

Changed paths:
    engines/freescape/gfx.cpp
    engines/freescape/gfx_opengl.cpp


diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index 0f702d0784d..2cf10feac71 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -1019,8 +1019,6 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 		origin.z() += offset;
 	}
 
-	bool isHorizontal = (size.y() == 0);
-
 	for (int i = 0; i < 2; i++) {
 
 		color = (*colours)[i];
@@ -1048,7 +1046,11 @@ void Renderer::renderRectangle(const Math::Vector3d &originalOrigin, const Math:
 			}
 
 			vertices.clear();
-			bool useFlippedWinding = (i == 0) || (isHorizontal && i == 1);
+			bool useFlippedWinding = (size.x() == 0) || (size.z() == 0);
+
+			if (i == 1)
+				useFlippedWinding = !useFlippedWinding;
+
 			if (useFlippedWinding) {
 				vertices.push_back(origin);
 				vertices.push_back(Math::Vector3d(origin.x() + d2x, origin.y() + d2y, origin.z() + d2z));
@@ -1086,7 +1088,6 @@ void Renderer::renderPolygon(const Math::Vector3d &origin, const Math::Vector3d
 
 	uint color = 0;
 	uint ecolor = 0;
-	bool isPlanar = (size.x() == 0 || size.y() == 0 || size.z() == 0);
 	if (ordinates->size() == 6) { // Line
 		color = (*colours)[0];
 		ecolor = ecolours ? (*ecolours)[0] : 0;
diff --git a/engines/freescape/gfx_opengl.cpp b/engines/freescape/gfx_opengl.cpp
index 920e70925f7..3bf43dcd3e0 100644
--- a/engines/freescape/gfx_opengl.cpp
+++ b/engines/freescape/gfx_opengl.cpp
@@ -79,6 +79,7 @@ void OpenGLRenderer::init() {
 	glDisable(GL_TEXTURE_2D);
 	glEnable(GL_CULL_FACE);
 	glFrontFace(GL_CW);
+	glCullFace(GL_BACK);
 	glEnable(GL_SCISSOR_TEST);
 	setViewport(_viewport);
 	glEnable(GL_DEPTH_CLAMP);


Commit: 1fc666556b6324a5953952f43a655e94bd80c5f2
    https://github.com/scummvm/scummvm/commit/1fc666556b6324a5953952f43a655e94bd80c5f2
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: fixed invisible bottom of a chest in eclipse demo (cpc)

Changed paths:
    engines/freescape/games/eclipse/cpc.cpp
    engines/freescape/objects/geometricobject.cpp
    engines/freescape/objects/geometricobject.h


diff --git a/engines/freescape/games/eclipse/cpc.cpp b/engines/freescape/games/eclipse/cpc.cpp
index 0f4dd244126..96460fe0a55 100644
--- a/engines/freescape/games/eclipse/cpc.cpp
+++ b/engines/freescape/games/eclipse/cpc.cpp
@@ -143,6 +143,12 @@ void EclipseEngine::loadAssetsCPCDemo() {
 	loadColorPalette();
 	swapPalette(1);
 
+	// This patch forces a solid color to the bottom of the chest in the area 5
+	// It was transparent in the original game
+	GeometricObject *obj = (GeometricObject *)_areaMap[5]->objectWithID(12);
+	assert(obj);
+	obj->setColor(2, 4);
+
 	_indicators.push_back(loadBundledImage("eclipse_ankh_indicator"));
 
 	for (auto &it : _indicators)
diff --git a/engines/freescape/objects/geometricobject.cpp b/engines/freescape/objects/geometricobject.cpp
index 48ab8db3ce2..bc5b1c6b80c 100644
--- a/engines/freescape/objects/geometricobject.cpp
+++ b/engines/freescape/objects/geometricobject.cpp
@@ -484,4 +484,10 @@ void GeometricObject::draw(Renderer *gfx, float offset) {
 	}
 }
 
+void GeometricObject::setColor(int idx, int color) {
+	assert(_colours);
+	assert(idx < _colours->size());
+	(*_colours)[idx] = color;
+}
+
 } // End of namespace Freescape
diff --git a/engines/freescape/objects/geometricobject.h b/engines/freescape/objects/geometricobject.h
index 2b8bd956a24..6d7d4f12117 100644
--- a/engines/freescape/objects/geometricobject.h
+++ b/engines/freescape/objects/geometricobject.h
@@ -59,6 +59,7 @@ public:
 	void computeBoundingBox();
 	bool collides(const Math::AABB &boundingBox);
 	void draw(Freescape::Renderer *gfx, float offset = 0.0) override;
+	void setColor(int idx, int color);
 	bool isDrawable() override;
 	bool isPlanar() override;
 	bool _cyclingColors;


Commit: e7da5f1b9a3f3f68d4265e3a0696b89f3e5a086b
    https://github.com/scummvm/scummvm/commit/e7da5f1b9a3f3f68d4265e3a0696b89f3e5a086b
Author: dhruv (dhruvranger97 at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESACPE: improved debugger.

added command to print sort order, show occlusion boxes and improved iso command to isolate multiple objects and only sort and render isolated objects.

Changed paths:
    engines/freescape/area.cpp
    engines/freescape/area.h
    engines/freescape/debugger.cpp
    engines/freescape/debugger.h
    engines/freescape/gfx.cpp
    engines/freescape/gfx.h
    engines/freescape/gfx_opengl_shaders.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 82ea08c6692..8c3d554ab0e 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -236,6 +236,19 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 
 	for (auto &obj : _drawableObjects) {
 		if (!obj->isDestroyed() && !obj->isInvisible()) {
+			if (!gfx->_debugHighlightObjectIDs.empty()) {
+				bool found = false;
+				for (auto id : gfx->_debugHighlightObjectIDs) {
+					if (obj->getObjectID() == id) {
+						found = true;
+						break;
+					}
+				}
+				// if this object is not in our list, skip it completely.
+				// it will not be sorted, and it will not be drawn.
+				if (!found)
+					continue;
+			}
 			if (obj->getObjectID() == 0 && _groundColor < 255 && _skyColor < 255) {
 				floor = obj;
 				continue;
@@ -374,17 +387,13 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 	}
 
 	for (auto &obj : _sortedObjects) {
-		if (gfx->_debugHighlightObjectID != -1 && obj->getObjectID() != gfx->_debugHighlightObjectID)
-			continue;
-
 		obj->draw(gfx);
 
 		// draw bounding boxes
-		if (gfx->_debugRenderBoundingBoxes) {
-			if (gfx->_debugBoundingBoxFilterID == -1 || gfx->_debugBoundingBoxFilterID == obj->getObjectID()) {
-				gfx->drawAABB(obj->_boundingBox, 0, 255, 0);
-			}
-		}
+		if (gfx->_debugRenderBoundingBoxes)
+			gfx->drawAABB(obj->_boundingBox, 0, 255, 0);
+		if (gfx->_debugRenderOcclusionBoxes)
+			gfx->drawAABB(obj->_occlusionBox, 255, 0, 0);
 	}
 	_lastTick = animationTicks;
 	if (sort)
diff --git a/engines/freescape/area.h b/engines/freescape/area.h
index f65feb5ff33..62a0106acfd 100644
--- a/engines/freescape/area.h
+++ b/engines/freescape/area.h
@@ -50,6 +50,7 @@ public:
 	void changeObjectID(uint16 objectID, uint16 newObjectID);
 	ObjectArray getSensors();
 	uint16 getAreaID();
+	Common::Array<Object *> &getSortedObjects() { return _sortedObjects; }
 	uint16 getAreaFlags();
 	uint8 getScale();
 	void remapColor(int index, int color);
diff --git a/engines/freescape/debugger.cpp b/engines/freescape/debugger.cpp
index f9d1e4e80d2..7f05fa6433e 100644
--- a/engines/freescape/debugger.cpp
+++ b/engines/freescape/debugger.cpp
@@ -40,20 +40,18 @@ Debugger::Debugger(FreescapeEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("obj_pos", WRAP_METHOD(Debugger, cmdObjPos)); // get object pos
 	registerCmd("obj_mov", WRAP_METHOD(Debugger, cmdSetObjPos)); // move object
 	registerCmd("goto", WRAP_METHOD(Debugger, cmdGoto)); // teleport to a position
+	registerCmd("sort_order", WRAP_METHOD(Debugger, cmdSortOrder)); // print current draw order of objects
+	registerCmd("occ", WRAP_METHOD(Debugger, cmdShowOcclusion)); // toggle occlussion boxes
 }
 
 Debugger::~Debugger() {}
 
 bool Debugger::cmdShowBBox(int argc, const char **argv) {
 	if (argc < 2) {
-		debugPrintf("Usage: bbox <0/1> [id]\n");
+		debugPrintf("Usage: bbox <0/1>\n");
 		return true;
 	}
 	_vm->_gfx->_debugRenderBoundingBoxes = atoi(argv[1]);
-	if (argc > 2)
-		_vm->_gfx->_debugBoundingBoxFilterID = atoi(argv[2]);
-	else
-		_vm->_gfx->_debugBoundingBoxFilterID = -1;
 	return true;
 }
 
@@ -76,11 +74,25 @@ bool Debugger::cmdShowNormals(int argc, const char **argv) {
 }
 
 bool Debugger::cmdHighlightID(int argc, const char **argv) {
+	_vm->_gfx->_debugHighlightObjectIDs.clear();
 	if (argc < 2) {
-		debugPrintf("Usage: iso <id> (-1 for all)\n");
+		debugPrintf("Isolation cleared, drawing all objects.\n");
+		debugPrintf("Usage: iso [id1] [id2] [id3] ...\n");
 		return true;
 	}
-	_vm->_gfx->_debugHighlightObjectID = atoi(argv[1]);
+	debugPrintf("Isolating Objects: ");
+	for (int i = 1; i < argc; i++) {
+		int id = atoi(argv[i]);
+		if (id == -1) {
+			_vm->_gfx->_debugHighlightObjectIDs.clear();
+			debugPrintf("All (Cleared)");
+			break;
+		}
+
+		_vm->_gfx->_debugHighlightObjectIDs.push_back(id);
+		debugPrintf("%d ", id);
+	}
+	debugPrintf("\n");
 	return true;
 }
 
@@ -164,4 +176,31 @@ bool Debugger::cmdGoto(int argc, const char **argv) {
 	return true;
 }
 
+bool Debugger::cmdSortOrder(int argc, const char** argv) {
+	if (!_vm->_currentArea) {
+		debugPrintf("No area loaded.\n");
+		return true;
+	}
+
+	Common::Array<Object *> &objs = _vm->_currentArea->getSortedObjects();
+
+	debugPrintf("Current Draw Order:\n");
+	debugPrintf("Total Objects: %d\n", objs.size());
+	for (uint i = 0; i < objs.size(); i++) {
+		Object *obj = objs[i];
+		debugPrintf("%d ", obj->getObjectID());
+	}
+	debugPrintf("\n");
+	return true;
+}
+
+bool Debugger::cmdShowOcclusion(int argc, const char **argv) {
+	if (argc < 2) {
+		debugPrintf("Usage: occ <0/1>\n");
+		return true;
+	}
+	_vm->_gfx->_debugRenderOcclusionBoxes = atoi(argv[1]);
+	return true;
+}
+
 }
diff --git a/engines/freescape/debugger.h b/engines/freescape/debugger.h
index fa719919440..1c7c549e166 100644
--- a/engines/freescape/debugger.h
+++ b/engines/freescape/debugger.h
@@ -44,6 +44,8 @@ private:
 	bool cmdGoto(int argc, const char **argv);
 	bool cmdObjPos(int argc, const char **argv);
 	bool cmdSetObjPos(int argc, const char **argv);
+	bool cmdSortOrder(int argc, const char **argv);
+	bool cmdShowOcclusion(int argc, const char **argv);
 };
 
 }
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index 2cf10feac71..49a4a6fe1c7 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -51,10 +51,9 @@ Renderer::Renderer(int screenW, int screenH, Common::RenderMode renderMode, bool
 	_renderMode = renderMode;
 	_isAccelerated = false;
 	_debugRenderBoundingBoxes = false;
-	_debugBoundingBoxFilterID = -1;
+	_debugRenderOcclusionBoxes = false;
 	_debugRenderWireframe = false;
 	_debugRenderNormals = false;
-	_debugHighlightObjectID = -1;
 	_authenticGraphics = authenticGraphics;
 
 	for (int i = 0; i < 16; i++) {
diff --git a/engines/freescape/gfx.h b/engines/freescape/gfx.h
index 9e946a7773a..ba9187c2a3a 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -294,10 +294,10 @@ public:
 
 	// debug flags
 	bool _debugRenderBoundingBoxes;
-	int _debugBoundingBoxFilterID;
+	bool _debugRenderOcclusionBoxes;
 	bool _debugRenderWireframe;
 	bool _debugRenderNormals;
-	int _debugHighlightObjectID;
+	Common::Array<uint8> _debugHighlightObjectIDs;
 
 	// for drawing bounding boxes
 	virtual void drawAABB(const Math::AABB &aabb, uint8 r, uint8 g, uint8 b) {}
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index adc8bda0304..413b9da1f28 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -521,7 +521,6 @@ void OpenGLShaderRenderer::drawAABB(const Math::AABB &aabb, uint8 r, uint8 g, ui
 
 	// restore state
 	glLineWidth(1.0f);
-	glEnable(GL_DEPTH_TEST);
 }
 
 void OpenGLShaderRenderer::renderCrossair(const Common::Point &crossairPosition) {


Commit: 814f7e22431562ddd2c57828d82513ee70313a09
    https://github.com/scummvm/scummvm/commit/814f7e22431562ddd2c57828d82513ee70313a09
Author: dhruv (dhruvranger97 at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: fix artifacts in castle master.

Changed paths:
    engines/freescape/area.cpp


diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 8c3d554ab0e..ac288f6c159 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -359,21 +359,6 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
 					result == 0x0A || result == 0x22 || result == 0x28 || result == 0x2A)
 					keepOrder = true; // A before B
 
-				if (result == 0) {
-					bool aInFront = minA.z() < 0;
-					bool bInFront = minB.z() < 0;
-					// if signs differ that means one object is positive in the z axis i.e. not visible in the camera
-					if (aInFront != bInFront) {
-						// one is visible, one is behind us.
-						// we want the one behind the camera (positive Z) to be drawn first
-						// so the visible object draws on top of it
-						// the one with the larger Z (positive) should come first
-						if (minA.z() > minB.z()) {
-							keepOrder = true;
-						}
-					}
-				}
-
 				if (!keepOrder) {
 					// Swap objects (L9d2c_flip_objects_loop)
 					_sortedObjects[j] = b;


Commit: 9b0c32ef97b5f7b5bfa9459aee734bfc461f3ecb
    https://github.com/scummvm/scummvm/commit/9b0c32ef97b5f7b5bfa9459aee734bfc461f3ecb
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-03-01T14:28:50+01:00

Commit Message:
FREESCAPE: updated credits

Changed paths:
    engines/freescape/credits.pl


diff --git a/engines/freescape/credits.pl b/engines/freescape/credits.pl
index 12652d09a78..1d9c21c4bba 100644
--- a/engines/freescape/credits.pl
+++ b/engines/freescape/credits.pl
@@ -1,4 +1,5 @@
 begin_section("Freescape");
 	add_person("Chris Allen", "", "Sound engine programming");
+	add_person("Dhruv", "", "Render engine fixes");
 	add_person("Gustavo Grieco", "neuromancer", "");
 end_section();




More information about the Scummvm-git-logs mailing list