[Scummvm-git-logs] scummvm master -> 5ec760c55ec9e902fdaad23ace1e040e1f678685
neuromancer
noreply at scummvm.org
Sun Feb 8 14:17:25 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
5ec760c55e FREESCAPE: add a debugger.
Commit: 5ec760c55ec9e902fdaad23ace1e040e1f678685
https://github.com/scummvm/scummvm/commit/5ec760c55ec9e902fdaad23ace1e040e1f678685
Author: dhruv (dhruvranger97 at gmail.com)
Date: 2026-02-08T15:17:21+01:00
Commit Message:
FREESCAPE: add a debugger.
added a debugger class for freescape engine with basic commands like info, toggle wireframe, visualize normals, isolate objs etc., this can help to solve specific rendering related artifacts.
Changed paths:
A engines/freescape/debugger.cpp
A engines/freescape/debugger.h
engines/freescape/area.cpp
engines/freescape/freescape.cpp
engines/freescape/freescape.h
engines/freescape/gfx.cpp
engines/freescape/gfx.h
engines/freescape/gfx_opengl_shaders.cpp
engines/freescape/gfx_opengl_shaders.h
diff --git a/engines/freescape/area.cpp b/engines/freescape/area.cpp
index 7dd57ee3efd..90bb56e99b0 100644
--- a/engines/freescape/area.cpp
+++ b/engines/freescape/area.cpp
@@ -361,11 +361,33 @@ void Area::draw(Freescape::Renderer *gfx, uint32 animationTicks, Math::Vector3d
}
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);
+ }
+ }
}
for (auto &pair : offsetMap) {
- pair._key->draw(gfx, pair._value);
+ Object *obj = pair._key;
+
+ if (gfx->_debugHighlightObjectID != -1 && obj->getObjectID() != gfx->_debugHighlightObjectID)
+ continue;
+
+ obj->draw(gfx, pair._value);
+
+ // draw bounding boxes
+ if (gfx->_debugRenderBoundingBoxes) {
+ if (gfx->_debugBoundingBoxFilterID == -1 || gfx->_debugBoundingBoxFilterID == obj->getObjectID()) {
+ gfx->drawAABB(obj->_boundingBox, 0, 255, 0);
+ }
+ }
}
_lastTick = animationTicks;
diff --git a/engines/freescape/debugger.cpp b/engines/freescape/debugger.cpp
new file mode 100644
index 00000000000..f9d1e4e80d2
--- /dev/null
+++ b/engines/freescape/debugger.cpp
@@ -0,0 +1,167 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "freescape/debugger.h"
+#include "freescape/freescape.h"
+#include "freescape/gfx.h"
+#include "freescape/objects/object.h"
+#include "freescape/area.h"
+#include "math/ray.h"
+
+namespace Freescape {
+Debugger *g_debugger;
+
+Debugger::Debugger(FreescapeEngine *vm) : GUI::Debugger(), _vm(vm) {
+ g_debugger = this;
+ // register commands
+ registerCmd("info", WRAP_METHOD(Debugger, cmdInfo)); // inspect object under cursor
+ registerCmd("bbox", WRAP_METHOD(Debugger, cmdShowBBox)); // toggle bboxes
+ registerCmd("wire", WRAP_METHOD(Debugger, cmdWireframe)); // toggle wireframe
+ registerCmd("normals", WRAP_METHOD(Debugger, cmdShowNormals)); // toggle normals
+ registerCmd("iso", WRAP_METHOD(Debugger, cmdHighlightID)); // isolate object
+ 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
+}
+
+Debugger::~Debugger() {}
+
+bool Debugger::cmdShowBBox(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("Usage: bbox <0/1> [id]\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;
+}
+
+bool Debugger::cmdWireframe(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("Usage: wire <0/1>\n");
+ return true;
+ }
+ _vm->_gfx->_debugRenderWireframe = atoi(argv[1]);
+ return true;
+}
+
+bool Debugger::cmdShowNormals(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("Usage: normals <0/1>\n");
+ return true;
+ }
+ _vm->_gfx->_debugRenderNormals = atoi(argv[1]);
+ return true;
+}
+
+bool Debugger::cmdHighlightID(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("Usage: iso <id> (-1 for all)\n");
+ return true;
+ }
+ _vm->_gfx->_debugHighlightObjectID = atoi(argv[1]);
+ return true;
+}
+
+bool Debugger::cmdInfo(int argc, const char **argv) {
+ if (!_vm->_currentArea)
+ return true;
+ Math::Vector3d pos = _vm->_position;
+ Math::Vector3d dir = _vm->_cameraFront;
+
+ // shoot a long raycast
+ Math::Ray ray(pos, dir);
+ Object *obj = _vm->_currentArea->checkCollisionRay(ray, 100000);
+
+ if (obj) {
+ debugPrintf("Object %d\n", obj->getObjectID());
+ debugPrintf("Type: %d | Flags: %x\n", obj->getType(), obj->getObjectFlags());
+ debugPrintf("Origin: %f %f %f\n", obj->getOrigin().x(), obj->getOrigin().y(), obj->getOrigin().z());
+ debugPrintf("Size: %f %f %f\n", obj->getSize().x(), obj->getSize().y(), obj->getSize().z());
+ if (obj->isGeometric()) {
+ debugPrintf("BBox: min(%f %f %f) max(%f %f %f)\n",
+ obj->_boundingBox.getMin().x(),
+ obj->_boundingBox.getMin().y(),
+ obj->_boundingBox.getMin().z(),
+ obj->_boundingBox.getMax().x(),
+ obj->_boundingBox.getMax().y(),
+ obj->_boundingBox.getMax().z());
+ }
+ if (obj->_partOfGroup) {
+ debugPrintf("Grouped: Yes (Group ID: %d)\n", obj->_partOfGroup->getObjectID());
+ }
+ } else {
+ debugPrintf("Raycast hit nothing.\n");
+ }
+ return true;
+}
+
+bool Debugger::cmdObjPos(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("Usage: obj_pos <id>\n");
+ return true;
+ }
+ int id = atoi(argv[1]);
+ Object *obj = _vm->_currentArea->objectWithID(id);
+ if (!obj) {
+ debugPrintf("Object %d not found.\n", id);
+ return true;
+ }
+
+ debugPrintf("Origin: %f %f %f\n", obj->getOrigin().x(), obj->getOrigin().y(), obj->getOrigin().z());
+ return true;
+}
+
+bool Debugger::cmdSetObjPos(int argc, const char **argv) {
+ if (argc < 5) {
+ debugPrintf("Usage: obj_mov <id> <x> <y> <z>\n");
+ return true;
+ }
+ int id = atoi(argv[1]);
+ Object *obj = _vm->_currentArea->objectWithID(id);
+ if (!obj) {
+ debugPrintf("Object %d not found.\n", id);
+ return true;
+ }
+
+ Math::Vector3d newPos(atof(argv[2]), atof(argv[3]), atof(argv[4]));
+ obj->setOrigin(newPos);
+ debugPrintf("Moved Object %d to %f %f %f\n", id, newPos.x(), newPos.y(), newPos.z());
+ return true;
+}
+
+bool Debugger::cmdGoto(int argc, const char **argv) {
+ if (argc < 4) {
+ debugPrintf("Usage: goto <x> <y> <z>\n");
+ return true;
+ }
+ float x = atof(argv[1]);
+ float y = atof(argv[2]);
+ float z = atof(argv[3]);
+ _vm->_position = Math::Vector3d(x, y, z);
+ debugPrintf("Teleported to %f %f %f\n", x, y, z);
+ return true;
+}
+
+}
diff --git a/engines/freescape/debugger.h b/engines/freescape/debugger.h
new file mode 100644
index 00000000000..fa719919440
--- /dev/null
+++ b/engines/freescape/debugger.h
@@ -0,0 +1,51 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef FREESCAPE_DEBUGGER_H
+#define FREESCAPE_DEBUGGER_H
+
+#include "gui/debugger.h"
+
+namespace Freescape {
+
+class FreescapeEngine;
+
+class Debugger : public GUI::Debugger {
+public:
+ Debugger(FreescapeEngine *vm);
+ ~Debugger();
+
+private:
+ FreescapeEngine *_vm;
+ bool cmdShowBBox(int argc, const char **argv);
+ bool cmdWireframe(int argc, const char **argv);
+ bool cmdShowNormals(int argc, const char **argv);
+ bool cmdHighlightID(int agrc, const char **argv);
+
+ bool cmdInfo(int argc, const char **argv);
+ bool cmdGoto(int argc, const char **argv);
+ bool cmdObjPos(int argc, const char **argv);
+ bool cmdSetObjPos(int argc, const char **argv);
+};
+
+}
+
+#endif
diff --git a/engines/freescape/freescape.cpp b/engines/freescape/freescape.cpp
index 3bc5dc64791..3a08393d147 100644
--- a/engines/freescape/freescape.cpp
+++ b/engines/freescape/freescape.cpp
@@ -33,6 +33,7 @@
#include "freescape/objects/sensor.h"
#include "freescape/sweepAABB.h"
#include "freescape/doodle.h"
+#include "freescape/debugger.h"
namespace Freescape {
@@ -232,6 +233,8 @@ FreescapeEngine::FreescapeEngine(OSystem *syst, const ADGameDescription *gd)
ConfMan.setInt("gamepad_controller_directional_input", 1 /* kDirectionalInputDpad */, gameDomain);
#endif
g_freescape = this;
+ g_debugger = new Debugger(g_freescape);
+ setDebugger(g_debugger);
}
FreescapeEngine::~FreescapeEngine() {
diff --git a/engines/freescape/freescape.h b/engines/freescape/freescape.h
index 42b89a947a8..be13fd65f01 100644
--- a/engines/freescape/freescape.h
+++ b/engines/freescape/freescape.h
@@ -51,6 +51,7 @@ class RandomSource;
namespace Freescape {
class Renderer;
+class Debugger;
#define FREESCAPE_DATA_BUNDLE "freescape.dat"
@@ -654,6 +655,7 @@ enum GameReleaseFlags {
};
extern FreescapeEngine *g_freescape;
+extern Debugger *g_debugger;
} // namespace Freescape
diff --git a/engines/freescape/gfx.cpp b/engines/freescape/gfx.cpp
index 872b065ccc7..c777b01b514 100644
--- a/engines/freescape/gfx.cpp
+++ b/engines/freescape/gfx.cpp
@@ -50,6 +50,11 @@ Renderer::Renderer(int screenW, int screenH, Common::RenderMode renderMode, bool
_colorRemaps = nullptr;
_renderMode = renderMode;
_isAccelerated = false;
+ _debugRenderBoundingBoxes = false;
+ _debugBoundingBoxFilterID = -1;
+ _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 11354aa0eba..77895cb0258 100644
--- a/engines/freescape/gfx.h
+++ b/engines/freescape/gfx.h
@@ -267,6 +267,16 @@ public:
int _scale;
+ // debug flags
+ bool _debugRenderBoundingBoxes;
+ int _debugBoundingBoxFilterID;
+ bool _debugRenderWireframe;
+ bool _debugRenderNormals;
+ int _debugHighlightObjectID;
+
+ // for drawing bounding boxes
+ virtual void drawAABB(const Math::AABB &aabb, uint8 r, uint8 g, uint8 b) {}
+
/**
* Select the window where to render
*
diff --git a/engines/freescape/gfx_opengl_shaders.cpp b/engines/freescape/gfx_opengl_shaders.cpp
index 5dbea5080b8..d84ce9069fd 100644
--- a/engines/freescape/gfx_opengl_shaders.cpp
+++ b/engines/freescape/gfx_opengl_shaders.cpp
@@ -459,6 +459,68 @@ void OpenGLShaderRenderer::drawCelestialBody(const Math::Vector3d position, floa
_triangleShader->unbind();
}
+void OpenGLShaderRenderer::drawAABB(const Math::AABB &aabb, uint8 r, uint8 g, uint8 b) {
+ if (!aabb.isValid())
+ return;
+
+ Math::Vector3d min = aabb.getMin();
+ Math::Vector3d max = aabb.getMax();
+
+ // calculate the 8 corners of the box
+ Math::Vector3d c[8];
+ c[0] = Math::Vector3d(min.x(), min.y(), min.z());
+ c[1] = Math::Vector3d(max.x(), min.y(), min.z());
+ c[2] = Math::Vector3d(max.x(), max.y(), min.z());
+ c[3] = Math::Vector3d(min.x(), max.y(), min.z());
+ c[4] = Math::Vector3d(min.x(), min.y(), max.z());
+ c[5] = Math::Vector3d(max.x(), min.y(), max.z());
+ c[6] = Math::Vector3d(max.x(), max.y(), max.z());
+ c[7] = Math::Vector3d(min.x(), max.y(), max.z());
+
+ _triangleShader->use();
+ _triangleShader->setUniform("mvpMatrix", _mvpMatrix);
+ useColor(r, g, b);
+
+ // disable depth so we can see the box through walls
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glLineWidth(2.0f);
+
+ // build lines (12 lines * 2 vertices = 24 vertices)
+ int idx = 0;
+
+ auto pushLine = [&](int i, int j) {
+ copyToVertexArray(idx++, c[i]);
+ copyToVertexArray(idx++, c[j]);
+ };
+
+ // bottom
+ pushLine(0, 1);
+ pushLine(1, 2);
+ pushLine(2, 3);
+ pushLine(3, 0);
+ // top
+ pushLine(4, 5);
+ pushLine(5, 6);
+ pushLine(6, 7);
+ pushLine(7, 4);
+ // sides
+ pushLine(0, 4);
+ pushLine(1, 5);
+ pushLine(2, 6);
+ pushLine(3, 7);
+
+ glBindBuffer(GL_ARRAY_BUFFER, _triangleVBO);
+ glBufferData(GL_ARRAY_BUFFER, idx * 3 * sizeof(float), _verts, GL_DYNAMIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
+ glDrawArrays(GL_LINES, 0, idx);
+
+ // restore state
+ glLineWidth(1.0f);
+ glEnable(GL_DEPTH_TEST);
+}
+
void OpenGLShaderRenderer::renderCrossair(const Common::Point &crossairPosition) {
Math::Matrix4 identity;
identity(0, 0) = 1.0;
@@ -511,6 +573,11 @@ void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &verti
_triangleShader->use();
_triangleShader->setUniform("mvpMatrix", _mvpMatrix);
+ if (_debugRenderWireframe) {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ useColor(0, 255, 0);
+ }
+
if (vertices.size() == 2) {
const Math::Vector3d &v1 = vertices[1];
if (v0 == v1)
@@ -527,6 +594,8 @@ void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &verti
glDrawArrays(GL_LINES, 0, 2);
glLineWidth(1);
+ if (_debugRenderWireframe)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
return;
}
@@ -544,6 +613,32 @@ void OpenGLShaderRenderer::renderFace(const Common::Array<Math::Vector3d> &verti
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
glDrawArrays(GL_TRIANGLES, 0, vi + 3);
+
+ if (_debugRenderNormals && vertices.size() >= 3) {
+ // calculate center
+ Math::Vector3d center(0, 0, 0);
+ for (auto &v : vertices) center += v;
+ center /= (float)vertices.size();
+
+ // calculate normal vector
+ Math::Vector3d v1 = vertices[1] - vertices[0];
+ Math::Vector3d v2 = vertices[2] - vertices[0];
+ Math::Vector3d normal = Math::Vector3d::crossProduct(v1, v2);
+ normal.normalize();
+ Math::Vector3d tip = center + (normal * 50.0f);
+
+ // upload normal line
+ copyToVertexArray(0, center);
+ copyToVertexArray(1, tip);
+
+ useColor(255, 0, 255); // pink
+
+ glBufferData(GL_ARRAY_BUFFER, 2 * 3 * sizeof(float), _verts, GL_DYNAMIC_DRAW);
+ glDrawArrays(GL_LINES, 0, 2);
+ }
+
+ if (_debugRenderWireframe)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
void OpenGLShaderRenderer::depthTesting(bool enabled) {
diff --git a/engines/freescape/gfx_opengl_shaders.h b/engines/freescape/gfx_opengl_shaders.h
index a748d4dac55..c259fc5d4dc 100644
--- a/engines/freescape/gfx_opengl_shaders.h
+++ b/engines/freescape/gfx_opengl_shaders.h
@@ -73,6 +73,7 @@ public:
int _variableStippleArray[128];
virtual void init() override;
+ virtual void drawAABB(const Math::AABB &aabb, uint8 r, uint8 g, uint8 b) override;
virtual void clear(uint8 r, uint8 g, uint8 b, bool ignoreViewport = false) override;
virtual void setViewport(const Common::Rect &rect) override;
virtual Common::Point nativeResolution() override;
More information about the Scummvm-git-logs
mailing list