[Scummvm-git-logs] scummvm master -> 4289094bc23006d4f414f2f96a195beb7eb2c392
mgerhardy
noreply at scummvm.org
Sun May 24 19:31:03 UTC 2026
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
9b95d14482 TWINE: LBA2: holomap and palette improvements
9641a7a19a TWINE: LBA2: implemented body parser
4289094bc2 TWINE: fixed soft lock of bulldozer driven by twinsen
Commit: 9b95d144827017541b08f3c85bb824a742e6daad
https://github.com/scummvm/scummvm/commit/9b95d144827017541b08f3c85bb824a742e6daad
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-24T21:30:55+02:00
Commit Message:
TWINE: LBA2: holomap and palette improvements
Changed paths:
engines/twine/holomap_v2.cpp
engines/twine/holomap_v2.h
engines/twine/menu/menu.cpp
engines/twine/renderer/screens.cpp
engines/twine/renderer/screens.h
engines/twine/scene/scene.cpp
engines/twine/script/script_life_v2.cpp
diff --git a/engines/twine/holomap_v2.cpp b/engines/twine/holomap_v2.cpp
index ed0c17ed4c5..6c9dc9b6835 100644
--- a/engines/twine/holomap_v2.cpp
+++ b/engines/twine/holomap_v2.cpp
@@ -20,37 +20,445 @@
*/
#include "twine/holomap_v2.h"
+#include "common/algorithm.h"
+#include "common/debug.h"
+#include "common/memstream.h"
+#include "common/stream.h"
+#include "twine/audio/sound.h"
+#include "twine/menu/interface.h"
+#include "twine/renderer/renderer.h"
+#include "twine/renderer/screens.h"
#include "twine/resources/hqr.h"
#include "twine/resources/resources.h"
+#include "twine/scene/gamestate.h"
+#include "twine/scene/scene.h"
+#include "twine/shared.h"
+#include "twine/text.h"
+#include "twine/twine.h"
namespace TwinE {
+// HQR indices in holomap.hqr for LBA2
+#define HQR_COORMAPP_HMM 0
+#define HQR_TWINSUN_HMT 1
+#define HQR_TWINSUN_HMG 2
+
+#define HOLO_FLAG_ACTIVE 1
+#define HOLO_FLAG_ASKED 2
+
+int32 HolomapV2::distance(float dist) const {
+ const float w = (float)_engine->width() / (float)_engine->originalWidth();
+ const float h = (float)_engine->height() / (float)_engine->originalHeight();
+ const float f = MIN<float>(w, h);
+ return (int32)(dist / f);
+}
+
+int32 HolomapV2::scale(float val) const {
+ const float w = (float)_engine->width() / (float)_engine->originalWidth();
+ const float h = (float)_engine->height() / (float)_engine->originalHeight();
+ const float f = MIN<float>(w, h);
+ return (int32)(val * f);
+}
+
bool HolomapV2::setHoloPos(int32 locationIdx) {
- return false;
+ assert(locationIdx >= 0 && locationIdx < HOLO_MAX_CUBE);
+ Location &loc = _locations[HOLO_MAX_OBJECTIF + locationIdx];
+ if (loc.FlagHolo & HOLO_FLAG_ASKED) {
+ return false; // already asked
+ }
+ loc.FlagHolo |= HOLO_FLAG_ACTIVE;
+ return true;
+}
+
+void HolomapV2::clrHoloPos(int32 locationIdx) {
+ assert(locationIdx >= 0 && locationIdx < HOLO_MAX_CUBE);
+ Location &loc = _locations[HOLO_MAX_OBJECTIF + locationIdx];
+ loc.FlagHolo &= ~HOLO_FLAG_ACTIVE;
+ loc.FlagHolo |= HOLO_FLAG_ASKED;
}
bool HolomapV2::loadLocations() {
- // _locations[MAX_OBJECTIF + 67].FlagHolo = 1; // Desert Globe
return HQR::getEntry((uint8 *)_locations, Resources::HQR_HOLOMAP_FILE, RESSHQR_ARROWBIN) != 0;
}
const char *HolomapV2::getLocationName(int index) const {
- if (index >= 0 && index < ARRAYSIZE(_locations)) {
- // TODO: return _locations[index].;
- }
return "";
}
-void HolomapV2::clrHoloPos(int32 locationIdx) {
+void HolomapV2::computeCoorMapping() {
+ int idx = 0;
+ for (int32 alpha = -LBAAngles::ANGLE_90; alpha <= LBAAngles::ANGLE_90; alpha += HOLO_STEP_ANGLE) {
+ for (int32 beta = 0; beta < LBAAngles::ANGLE_360; beta += HOLO_STEP_ANGLE) {
+ _projectedSurfacePositions[idx].x2 = (uint16)ruleThree32(0, 255 * 256 + 255, LBAAngles::ANGLE_360 - 1, beta);
+ if (alpha == LBAAngles::ANGLE_90) {
+ _projectedSurfacePositions[idx].y2 = 255 * 256 + 255;
+ } else {
+ _projectedSurfacePositions[idx].y2 = (uint16)(((alpha + LBAAngles::ANGLE_90) * 256) / 2);
+ }
+ ++idx;
+ }
+ // wrap column
+ _projectedSurfacePositions[idx].x2 = 255 * 256 + 255;
+ if (alpha == LBAAngles::ANGLE_90) {
+ _projectedSurfacePositions[idx].y2 = 255 * 256 + 255;
+ } else {
+ _projectedSurfacePositions[idx].y2 = (uint16)(((alpha + LBAAngles::ANGLE_90) * 256) / 2);
+ }
+ ++idx;
+ }
}
-void HolomapV2::holoTraj(int32 trajectoryIndex) {
+void HolomapV2::computeCoorGlobe(Common::SeekableReadStream *surfaceStream) {
+ int idx = 0;
+ _engine->_renderer->setAngleCamera(0, 0, 0);
+ for (int32 alpha = -LBAAngles::ANGLE_90; alpha <= LBAAngles::ANGLE_90; alpha += HOLO_STEP_ANGLE) {
+ const int64 startPos = surfaceStream->pos();
+ const int32 firstAlt = surfaceStream->readByte();
+ surfaceStream->seek(startPos, SEEK_SET);
+
+ for (int32 beta = 0; beta < LBAAngles::ANGLE_360; beta += HOLO_STEP_ANGLE) {
+ const int32 alt = surfaceStream->readByte();
+ const int32 normal = HOLO_RAYON_PLANET + alt * 2;
+ const IVec2 &rotVec = _engine->_renderer->rotate(normal, 0, alpha);
+ const IVec2 &rotVec2 = _engine->_renderer->rotate(rotVec.x, 0, beta);
+ const IVec3 &rotVec3 = _engine->_renderer->worldRotatePoint(IVec3(rotVec2.x, rotVec.y, rotVec2.y));
+ _holomapSurface[idx].x = rotVec3.x;
+ _holomapSurface[idx].y = rotVec3.y;
+ _holomapSurface[idx].z = rotVec3.z;
+ ++idx;
+ }
+ // wrap: use first altitude of this row
+ const int32 normal = HOLO_RAYON_PLANET + firstAlt * 2;
+ const IVec2 &rotVec = _engine->_renderer->rotate(normal, 0, alpha);
+ const IVec2 &rotVec2 = _engine->_renderer->rotate(rotVec.x, 0, 0);
+ const IVec3 &rotVec3 = _engine->_renderer->worldRotatePoint(IVec3(rotVec2.x, rotVec.y, rotVec2.y));
+ _holomapSurface[idx].x = rotVec3.x;
+ _holomapSurface[idx].y = rotVec3.y;
+ _holomapSurface[idx].z = rotVec3.z;
+ ++idx;
+ }
+}
+
+void HolomapV2::computeGlobeProj() {
+ int sortIdx = 0;
+ int surfIdx = 0;
+ int projIdx = 0;
+ for (int32 alpha = -LBAAngles::ANGLE_90; alpha <= LBAAngles::ANGLE_90; alpha += HOLO_STEP_ANGLE) {
+ for (int32 beta = 0; beta < HOLO_GLOBE_COLS; ++beta) {
+ const IVec3 &destPos = _engine->_renderer->worldRotatePoint(_holomapSurface[surfIdx]);
+ if (alpha != LBAAngles::ANGLE_90) {
+ _holomapSort[sortIdx].z = (int16)destPos.z;
+ _holomapSort[sortIdx].projectedPosIdx = projIdx;
+ ++sortIdx;
+ }
+ const IVec3 &projPos = _engine->_renderer->projectPoint(destPos);
+ _projectedSurfacePositions[projIdx].x1 = (int16)projPos.x;
+ _projectedSurfacePositions[projIdx].y1 = (int16)projPos.y;
+ ++projIdx;
+ ++surfIdx;
+ }
+ }
+ Common::sort(_holomapSort, _holomapSort + HOLO_GLOBE_QUADS,
+ [](const HolomapSort &a, const HolomapSort &b) { return a.z < b.z; });
+}
+
+void HolomapV2::drawHoloMap() {
+ computeGlobeProj();
+ for (int32 i = 0; i < HOLO_GLOBE_QUADS; ++i) {
+ const int base = _holomapSort[i].projectedPosIdx;
+ const HolomapProjectedPos &pos1 = _projectedSurfacePositions[base];
+ const HolomapProjectedPos &pos2 = _projectedSurfacePositions[base + HOLO_GLOBE_COLS];
+ const HolomapProjectedPos &pos3 = _projectedSurfacePositions[base + 1];
+
+ ComputedVertex vertexCoordinates[3];
+ vertexCoordinates[0].x = pos1.x1;
+ vertexCoordinates[0].y = pos1.y1;
+ vertexCoordinates[1].x = pos2.x1;
+ vertexCoordinates[1].y = pos2.y1;
+ vertexCoordinates[2].x = pos3.x1;
+ vertexCoordinates[2].y = pos3.y1;
+ if (isPolygonVisible(vertexCoordinates)) {
+ ComputedVertex textureCoordinates[3];
+ textureCoordinates[0].x = pos1.x2;
+ textureCoordinates[0].y = pos1.y2;
+ textureCoordinates[1].x = pos2.x2;
+ textureCoordinates[1].y = pos2.y2;
+ textureCoordinates[2].x = pos3.x2;
+ textureCoordinates[2].y = pos3.y2;
+ _engine->_renderer->asmTexturedTriangleNoClip(vertexCoordinates, textureCoordinates, _holomapImagePtr, _holomapImageSize);
+ }
+
+ const HolomapProjectedPos &pos4 = _projectedSurfacePositions[base + HOLO_GLOBE_COLS];
+ const HolomapProjectedPos &pos5 = _projectedSurfacePositions[base + HOLO_GLOBE_COLS + 1];
+ const HolomapProjectedPos &pos6 = _projectedSurfacePositions[base + 1];
+ vertexCoordinates[0].x = pos4.x1;
+ vertexCoordinates[0].y = pos4.y1;
+ vertexCoordinates[1].x = pos5.x1;
+ vertexCoordinates[1].y = pos5.y1;
+ vertexCoordinates[2].x = pos6.x1;
+ vertexCoordinates[2].y = pos6.y1;
+ if (isPolygonVisible(vertexCoordinates)) {
+ ComputedVertex textureCoordinates[3];
+ textureCoordinates[0].x = pos4.x2;
+ textureCoordinates[0].y = pos4.y2;
+ textureCoordinates[1].x = pos5.x2;
+ textureCoordinates[1].y = pos5.y2;
+ textureCoordinates[2].x = pos6.x2;
+ textureCoordinates[2].y = pos6.y2;
+ _engine->_renderer->asmTexturedTriangleNoClip(vertexCoordinates, textureCoordinates, _holomapImagePtr, _holomapImageSize);
+ }
+ }
+}
+
+void HolomapV2::drawListHoloGlobe(bool frontFace) {
+ struct SortEntry {
+ int32 z;
+ int32 numObj;
+ int16 xw, yw, zw;
+ };
+ SortEntry sortList[HOLO_MAX_ARROW];
+ int nbobjets = 0;
+
+ const int numCube = _engine->_scene->_numCube;
+
+ for (int n = HOLO_MAX_OBJECTIF; n < HOLO_MAX_ARROW; ++n) {
+ if (!((_locations[n].FlagHolo & HOLO_FLAG_ACTIVE) || n == (HOLO_MAX_OBJECTIF + numCube))) {
+ continue;
+ }
+ const Location &loc = _locations[n];
+ if (loc.Planet != 0) { // only Twinsun for now
+ continue;
+ }
+
+ _engine->_renderer->setAngleCamera(loc.Alpha, loc.Beta, 0);
+ const IVec3 &m = _engine->_renderer->worldRotatePoint(IVec3(0, 0, HOLO_RAYON_PLANET + loc.Alt));
+ const IVec3 &m1 = _engine->_renderer->worldRotatePoint(IVec3(0, 0, HOLO_RAYON_PLANET + 500));
+
+ _engine->_renderer->setInverseAngleCamera(_holoAlpha, _holoBeta, _holoGamma);
+ _engine->_renderer->setCameraRotation(0, 0, distance((float)_zoomPlanet));
+
+ const IVec3 &destPos = _engine->_renderer->worldRotatePoint(m);
+ const IVec3 &destPos2 = _engine->_renderer->worldRotatePoint(m1);
+
+ if (!frontFace) {
+ if (destPos2.z > destPos.z) continue;
+ } else {
+ if (destPos2.z < destPos.z) continue;
+ }
+
+ sortList[nbobjets].z = destPos.z;
+ sortList[nbobjets].numObj = n;
+ sortList[nbobjets].xw = (int16)m.x;
+ sortList[nbobjets].yw = (int16)m.y;
+ sortList[nbobjets].zw = (int16)m.z;
+ ++nbobjets;
+ }
+
+ if (nbobjets == 0) return;
+
+ // sort back to front
+ Common::sort(sortList, sortList + nbobjets,
+ [](const SortEntry &a, const SortEntry &b) { return a.z < b.z; });
+
+ for (int i = 0; i < nbobjets; ++i) {
+ const SortEntry &entry = sortList[i];
+ const Location &loc = _locations[entry.numObj];
+ Common::Rect dummy;
+ _engine->_renderer->affObjetIso(entry.xw, entry.yw, entry.zw,
+ loc.Alpha, loc.Beta, LBAAngles::ANGLE_0,
+ _engine->_resources->_holomapArrowPtr, dummy);
+ }
+}
+
+void HolomapV2::drawReticule() {
+ const int32 cx = _engine->width() / 2;
+ const int32 cy = scale(220);
+ const int32 sz = 16;
+ _engine->_menu->drawRectBorders(cx - sz, cy - sz, cx + sz, cy + sz, 15, 15);
+}
+
+bool HolomapV2::goToArrow() {
+ const int32 dt = _engine->timerRef - _moveTimer;
+ _holoAlpha = boundRuleThree(_holoAlpha, _destAlpha, 75, dt);
+ _holoBeta = boundRuleThree(_holoBeta, _destBeta, 75, dt);
+ return (_holoAlpha == _destAlpha && _holoBeta == _destBeta);
}
void HolomapV2::initHoloDatas() {
+ Common::SeekableReadStream *surfaceStream = HQR::makeReadStream(
+ TwineResource(Resources::HQR_HOLOMAP_FILE, HQR_TWINSUN_HMT));
+ if (surfaceStream == nullptr) {
+ error("Failed to load holomap surface for LBA2");
+ }
+ computeCoorMapping();
+ computeCoorGlobe(surfaceStream);
+ delete surfaceStream;
+}
+
+void HolomapV2::holoTraj(int32 trajectoryIndex) {
+ // TODO: implement trajectory animation for LBA2
+ warning("HolomapV2::holoTraj(%d) not yet implemented", trajectoryIndex);
}
void HolomapV2::holoMap() {
+ const int32 alphaLightTmp = _engine->_scene->_alphaLight;
+ const int32 betaLightTmp = _engine->_scene->_betaLight;
+
+ _engine->saveTimer(false);
+ _engine->_screens->fadeToBlack(_engine->_screens->_ptrPal);
+ _engine->_sound->stopSamples();
+ _engine->_interface->unsetClip();
+ _engine->_screens->clearScreen();
+
+ initHoloDatas();
+
+ const int32 cameraPosX = _engine->width() / 2;
+ const int32 cameraPosY = scale(220);
+ _engine->_renderer->setProjection(cameraPosX, cameraPosY, 128, 1024, 1024);
+
+ // Load globe texture
+ _holomapImagePtr = nullptr;
+ _holomapImageSize = HQR::getAllocEntry(&_holomapImagePtr,
+ TwineResource(Resources::HQR_HOLOMAP_FILE, HQR_TWINSUN_HMG));
+ if (_holomapImageSize == 0) {
+ error("Failed to load holomap image for LBA2");
+ }
+
+ // Initial camera pointing at current location
+ const int numCube = _engine->_scene->_numCube;
+ if (numCube >= 0 && numCube < HOLO_MAX_CUBE) {
+ const Location &loc = _locations[HOLO_MAX_OBJECTIF + numCube];
+ _holoAlpha = loc.Alpha & (LBAAngles::ANGLE_360 - 1);
+ _holoBeta = loc.Beta & (LBAAngles::ANGLE_360 - 1);
+ } else {
+ _holoAlpha = 0;
+ _holoBeta = 0;
+ }
+ _holoGamma = 0;
+ _zoomPlanet = HOLO_ZOOM_INIT_PLANET;
+ _zoomPlanetDest = HOLO_ZOOM_PLANET;
+ _automove = false;
+ _flagRedraw = true;
+ _flagPal = true;
+ _flagHoloEnd = false;
+ _numObjectif = -1;
+
+ _engine->_input->enableKeyMap(holomapKeyMapId);
+
+ for (;;) {
+ FrameMarker frame(_engine);
+ _engine->_input->readKeys();
+ if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
+ break;
+ }
+
+ // Navigation
+ if (!_automove) {
+ if (_engine->_input->isActionActive(TwinEActionType::HolomapUp)) {
+ _holoAlpha -= LBAAngles::ANGLE_2;
+ _holoAlpha = ClampAngle(_holoAlpha);
+ _flagRedraw = true;
+ } else if (_engine->_input->isActionActive(TwinEActionType::HolomapDown)) {
+ _holoAlpha += LBAAngles::ANGLE_2;
+ _holoAlpha = ClampAngle(_holoAlpha);
+ _flagRedraw = true;
+ }
+ if (_engine->_input->isActionActive(TwinEActionType::HolomapLeft)) {
+ _holoBeta -= LBAAngles::ANGLE_2;
+ _holoBeta = ClampAngle(_holoBeta);
+ _flagRedraw = true;
+ } else if (_engine->_input->isActionActive(TwinEActionType::HolomapRight)) {
+ _holoBeta += LBAAngles::ANGLE_2;
+ _holoBeta = ClampAngle(_holoBeta);
+ _flagRedraw = true;
+ }
+ }
+
+ if (_engine->_input->toggleActionIfActive(TwinEActionType::HolomapPrev)) {
+ // search prev active arrow
+ for (int n = HOLO_MAX_OBJECTIF; n < HOLO_MAX_ARROW; ++n) {
+ if (_locations[n].FlagHolo & HOLO_FLAG_ACTIVE) {
+ _destAlpha = _locations[n].Alpha & (LBAAngles::ANGLE_360 - 1);
+ _destBeta = _locations[n].Beta & (LBAAngles::ANGLE_360 - 1);
+ _moveTimer = _engine->timerRef;
+ _automove = true;
+ _flagRedraw = true;
+ break;
+ }
+ }
+ } else if (_engine->_input->toggleActionIfActive(TwinEActionType::HolomapNext)) {
+ // search next active arrow
+ for (int n = HOLO_MAX_ARROW - 1; n >= HOLO_MAX_OBJECTIF; --n) {
+ if (_locations[n].FlagHolo & HOLO_FLAG_ACTIVE) {
+ _destAlpha = _locations[n].Alpha & (LBAAngles::ANGLE_360 - 1);
+ _destBeta = _locations[n].Beta & (LBAAngles::ANGLE_360 - 1);
+ _moveTimer = _engine->timerRef;
+ _automove = true;
+ _flagRedraw = true;
+ break;
+ }
+ }
+ }
+
+ if (_automove) {
+ if (goToArrow()) {
+ _automove = false;
+ }
+ _flagRedraw = true;
+ }
+
+ // Zoom animation
+ if (_zoomPlanet < _zoomPlanetDest) {
+ _zoomPlanet += 100;
+ if (_zoomPlanet > _zoomPlanetDest) _zoomPlanet = _zoomPlanetDest;
+ _flagRedraw = true;
+ }
+
+ // Render
+ if (_flagRedraw) {
+ _flagRedraw = false;
+ const Common::Rect rect(0, 0, _engine->width() - 1, _engine->height() - 1);
+ _engine->_interface->box(rect, COLOR_BLACK);
+
+ _engine->_renderer->setInverseAngleCamera(_holoAlpha, _holoBeta, _holoGamma);
+ _engine->_renderer->setLightVector(_holoAlpha, _holoBeta, 0);
+
+ // Draw objects behind globe
+ drawListHoloGlobe(false);
+
+ // Draw globe
+ _engine->_renderer->setInverseAngleCamera(_holoAlpha, _holoBeta, _holoGamma);
+ _engine->_renderer->setCameraRotation(0, 0, distance((float)_zoomPlanet));
+ drawHoloMap();
+
+ // Draw objects in front of globe
+ drawListHoloGlobe(true);
+
+ if (_automove) {
+ drawReticule();
+ }
+
+ _engine->copyBlockPhys(rect);
+ }
+
+ // Fade in first time
+ if (_flagPal) {
+ _flagPal = false;
+ _engine->_screens->fadeToPal(_engine->_screens->_palettePcx);
+ }
+
+ ++_engine->timerRef;
+ }
+
+ _engine->_screens->fadeToBlack(_engine->_screens->_palettePcx);
+ _engine->_scene->_alphaLight = alphaLightTmp;
+ _engine->_scene->_betaLight = betaLightTmp;
+ _engine->_gameState->init3DGame();
+ _engine->_input->enableKeyMap(mainKeyMapId);
+ _engine->restoreTimer();
+
+ free(_holomapImagePtr);
+ _holomapImagePtr = nullptr;
}
} // namespace TwinE
diff --git a/engines/twine/holomap_v2.h b/engines/twine/holomap_v2.h
index 7984c6f990c..e3d6f0da795 100644
--- a/engines/twine/holomap_v2.h
+++ b/engines/twine/holomap_v2.h
@@ -23,63 +23,115 @@
#define TWINE_HOLOMAPV2_H
#include "twine/holomap.h"
-
-#define MAX_OBJECTIF 50
-#define MAX_CUBE 255
+#include "twine/shared.h"
namespace TwinE {
-/**
- * The Holomap shows the hero position. The arrows (@c RESSHQR_HOLOARROWMDL) represent important places in your quest - they automatically disappear once that part of
- * the quest is done (@c clrHoloPos()). You can rotate the holoamp by pressing ctrl+cursor keys - but only using the cursor keys, you can scroll through the
- * text for the visible arrows.
- */
+#define HOLO_MAX_OBJECTIF 50
+#define HOLO_MAX_CUBE 255
+#define HOLO_MAX_ARROW (HOLO_MAX_OBJECTIF + HOLO_MAX_CUBE)
+
+// Globe mesh parameters (LBA2 uses 4x angle factor)
+#define HOLO_STEP_ANGLE 128
+#define HOLO_GLOBE_ALPHA_STEPS ((LBAAngles::ANGLE_360 / HOLO_STEP_ANGLE) + 1) // 33
+#define HOLO_GLOBE_BETA_STEPS ((LBAAngles::ANGLE_180 / HOLO_STEP_ANGLE) + 1) // 17 (alpha from -90 to +90 = 180 degrees)
+// Actually: alpha from -1024 to 1024 step 128 = 17 rows, beta from 0 to 4096 step 128 = 32 cols + 1 wrap = 33
+#define HOLO_GLOBE_COLS 33
+#define HOLO_GLOBE_ROWS 17
+#define HOLO_GLOBE_VERTICES (HOLO_GLOBE_COLS * HOLO_GLOBE_ROWS)
+#define HOLO_GLOBE_QUADS ((HOLO_GLOBE_COLS - 1) * (HOLO_GLOBE_ROWS - 1)) // 512
+
+#define HOLO_RAYON_PLANET 1000
+#define HOLO_ZOOM_PLANET 8000
+#define HOLO_ZOOM_INIT_PLANET 3000
+
class HolomapV2 : public Holomap {
private:
using Super = Holomap;
public:
- HolomapV2(TwinEEngine *engine) : Super(engine) {}
- virtual ~HolomapV2() = default;
-
struct Location {
- int32 X = 0; // Position Island X Y Z
+ int32 X = 0;
int32 Y = 0;
int32 Z = 0;
- int32 Alpha = 0; // Position Planet Alpha, Beta and Altitude
+ int32 Alpha = 0;
int32 Beta = 0;
int32 Alt = 0;
int32 Mess = 0;
- int8 ObjFix = 0; // Eventual Obj Inventory 3D (FREE NOT USED!)
- uint8 FlagHolo = 0u; // Flag for Planet display, active, etc.
+ int8 ObjFix = 0;
+ uint8 FlagHolo = 0u;
uint8 Planet = 0u;
uint8 Island = 0u;
};
static_assert(sizeof(Location) == 32, "Invalid Location size");
- Location _locations[MAX_OBJECTIF + MAX_CUBE];
- /**
- * Set Holomap location position
- * @param locationIdx Scene where position must be set
- */
- bool setHoloPos(int32 locationIdx) override;
+private:
+ Location _locations[HOLO_MAX_ARROW];
- bool loadLocations() override;
+ // Globe mesh data
+ IVec3 _holomapSurface[HOLO_GLOBE_VERTICES];
- const char *getLocationName(int index) const override;
+ struct HolomapProjectedPos {
+ int16 x1 = 0; // screen X
+ int16 y1 = 0; // screen Y
+ uint16 x2 = 0; // texture U
+ uint16 y2 = 0; // texture V
+ };
+ HolomapProjectedPos _projectedSurfacePositions[HOLO_GLOBE_VERTICES];
- /**
- * Clear Holomap location position
- * @param locationIdx Scene where position must be cleared
- */
- void clrHoloPos(int32 locationIdx) override;
+ struct HolomapSort {
+ int16 z = 0;
+ uint16 projectedPosIdx = 0;
+ };
+ HolomapSort _holomapSort[HOLO_GLOBE_QUADS];
+
+ // Globe rendering state
+ int32 _holoAlpha = 0;
+ int32 _holoBeta = 0;
+ int32 _holoGamma = 0;
+ int32 _zoomPlanet = HOLO_ZOOM_INIT_PLANET;
+ int32 _zoomPlanetDest = HOLO_ZOOM_PLANET;
+
+ // Camera interpolation
+ int32 _destAlpha = 0;
+ int32 _destBeta = 0;
+ int32 _moveTimer = 0;
+ bool _automove = false;
+
+ // Objective/selection state
+ int32 _numObjectif = -1;
+ int32 _oldObjectif = -1;
+
+ // UI state
+ bool _flagRedraw = true;
+ bool _flagPal = true;
+ bool _flagHoloEnd = false;
+
+ // Holomap image
+ uint8 *_holomapImagePtr = nullptr;
+ int32 _holomapImageSize = 0;
+
+ void computeCoorMapping();
+ void computeCoorGlobe(Common::SeekableReadStream *surfaceStream);
+ void computeGlobeProj();
+ void drawHoloMap();
+ void drawListHoloGlobe(bool frontFace);
+ void drawReticule();
+ bool goToArrow();
+
+ int32 distance(float dist) const;
+ int32 scale(float val) const;
- void holoTraj(int32 trajectoryIndex) override;
+public:
+ HolomapV2(TwinEEngine *engine) : Super(engine) {}
+ virtual ~HolomapV2() = default;
- /** Load Holomap content */
+ bool setHoloPos(int32 locationIdx) override;
+ bool loadLocations() override;
+ const char *getLocationName(int index) const override;
+ void clrHoloPos(int32 locationIdx) override;
+ void holoTraj(int32 trajectoryIndex) override;
void initHoloDatas() override;
-
- /** Main holomap process loop */
void holoMap() override;
};
diff --git a/engines/twine/menu/menu.cpp b/engines/twine/menu/menu.cpp
index 1dc33c214a8..9529262e66d 100644
--- a/engines/twine/menu/menu.cpp
+++ b/engines/twine/menu/menu.cpp
@@ -251,8 +251,8 @@ void Menu::plasmaEffectRenderFrame() {
void Menu::processPlasmaEffect(const Common::Rect &rect, int32 color) {
if (_engine->isLBA2()) {
- // TODO: effects are handled differently here.
- return;
+ // LBA2 uses palette bank 12 (192) for selected items
+ color = 192;
}
const int32 max_value = color + 15;
@@ -453,7 +453,12 @@ int16 Menu::drawButtons(MenuSettings *menuSettings, bool hover) {
void Menu::menuDemo() {
// TODO: lba2 only show the credits only in the main menu and you could force it by pressing shift+c
- // TODO: lba2 has a cd audio track (2) for the credits
+ if (_engine->isLBA2()) {
+ _engine->_music->playCdTrack(2);
+ _engine->_menuOptions->showCredits();
+ _engine->_screens->loadMenuImage(false);
+ return;
+ }
_engine->_menuOptions->showCredits();
if (_engine->_movie->playMovie(FLA_DRAGON3)) {
if (!_engine->_screens->loadImageDelay(TwineImage(Resources::HQR_RESS_FILE, 15, 16), 3)) {
diff --git a/engines/twine/renderer/screens.cpp b/engines/twine/renderer/screens.cpp
index 1d801a80c93..f9d0482a85f 100644
--- a/engines/twine/renderer/screens.cpp
+++ b/engines/twine/renderer/screens.cpp
@@ -34,17 +34,24 @@
#include "twine/audio/music.h"
#include "twine/resources/hqr.h"
#include "twine/resources/resources.h"
+#include "twine/scene/gamestate.h"
+#include "twine/scene/scene.h"
#include "twine/shared.h"
#include "twine/twine.h"
namespace TwinE {
int32 Screens::mapLba2Palette(int32 palIndex) {
+ // LBA2 palette indices:
+ // 0 = PtrPalNormal (main palette from ress.hqr index 0)
+ // 1 = PtrPalCurrent (current island palette - already in _ptrPal after ChoicePalette)
+ // 2 = PtrPalEclair (lightning palette from ress.hqr index 10)
+ // 3 = PtrPalBlack (black palette from ress.hqr index 9)
static const int32 palettes[] {
RESSHQR_MAINPAL,
- -1, // TODO: current palette
- RESSHQR_BLACKPAL,
- RESSHQR_ECLAIRPAL
+ -1, // current palette (special case: use _ptrPal as-is)
+ RESSHQR_ECLAIRPAL,
+ RESSHQR_BLACKPAL
};
if (palIndex < 0 || palIndex >= ARRAYSIZE(palettes)) {
return -1;
@@ -52,6 +59,46 @@ int32 Screens::mapLba2Palette(int32 palIndex) {
return palettes[palIndex];
}
+// LBA2 XPL header structure
+#define RESS_XPL0 27
+#define RESS_XPL00 42
+
+void Screens::choicePalette() {
+ if (!_engine->isLBA2()) {
+ return;
+ }
+
+ int32 xplIndex;
+ // Interior scenes or Citadel after storm (chapter >= 2) use the interior palette
+ const bool tempeteFinie = _engine->_gameState->hasGameFlag(253) >= 2;
+ if (_engine->_scene->_island == 0 && tempeteFinie) {
+ xplIndex = RESS_XPL00;
+ } else {
+ xplIndex = RESS_XPL0 + _engine->_scene->_island;
+ }
+
+ uint8 *xplData = nullptr;
+ const int32 xplSize = HQR::getAllocEntry(&xplData, Resources::HQR_RESS_FILE, xplIndex);
+ if (xplSize == 0 || xplData == nullptr) {
+ warning("Failed to load XPL palette data for index %i", xplIndex);
+ return;
+ }
+
+ // XPL header: field at offset 4 is OffsetPalette
+ const int32 offsetPalette = READ_LE_INT32(xplData + 4);
+ if (offsetPalette + 768 > xplSize) {
+ warning("XPL palette offset out of bounds");
+ free(xplData);
+ return;
+ }
+
+ // Load palette from XPL data
+ const uint8 *palData = xplData + offsetPalette;
+ _ptrPal = Graphics::Palette(palData, NUMOFCOLORS);
+
+ free(xplData);
+}
+
bool Screens::adelineLogo() {
_engine->_music->playMidiFile(31);
diff --git a/engines/twine/renderer/screens.h b/engines/twine/renderer/screens.h
index 10505c7b612..941664953e5 100644
--- a/engines/twine/renderer/screens.h
+++ b/engines/twine/renderer/screens.h
@@ -50,6 +50,9 @@ public:
int32 mapLba2Palette(int32 palIndex);
+ /** LBA2: Load the per-island XPL palette for the current scene */
+ void choicePalette();
+
/** main palette */
Graphics::Palette _ptrPal{0};
Graphics::Palette _palettePcx{0};
diff --git a/engines/twine/scene/scene.cpp b/engines/twine/scene/scene.cpp
index d79f2562068..90ab8e99f8f 100644
--- a/engines/twine/scene/scene.cpp
+++ b/engines/twine/scene/scene.cpp
@@ -613,6 +613,11 @@ void Scene::changeCube() {
_engine->_grid->initGrid(_newCube);
+ // LBA2: load per-island palette from XPL data
+ if (_engine->isLBA2()) {
+ _engine->_screens->choicePalette();
+ }
+
if (_flagChgCube == ScenePositionType::kZone) {
_sceneStart = _zoneHeroPos;
} else if (_flagChgCube == ScenePositionType::kScene || _flagChgCube == ScenePositionType::kNoPosition) {
diff --git a/engines/twine/script/script_life_v2.cpp b/engines/twine/script/script_life_v2.cpp
index a2586d3a26d..e23289df3b9 100644
--- a/engines/twine/script/script_life_v2.cpp
+++ b/engines/twine/script/script_life_v2.cpp
@@ -204,22 +204,36 @@ int32 ScriptLifeV2::lPALETTE(TwinEEngine *engine, LifeScriptContext &ctx) {
const int32 palIndex = engine->_screens->mapLba2Palette(ctx.stream.readByte());
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::PALETTE(%i)", palIndex);
engine->saveTimer(false);
- HQR::getPaletteEntry(engine->_screens->_ptrPal, Resources::HQR_RESS_FILE, palIndex);
- engine->setPalette(engine->_screens->_ptrPal);
- engine->_screens->_flagPalettePcx = true;
+ if (palIndex == -1) {
+ // Index 1 = current palette (already in _ptrPal from ChoicePalette)
+ engine->setPalette(engine->_screens->_ptrPal);
+ } else {
+ HQR::getPaletteEntry(engine->_screens->_ptrPal, Resources::HQR_RESS_FILE, palIndex);
+ engine->setPalette(engine->_screens->_ptrPal);
+ }
engine->restoreTimer();
return 0;
}
int32 ScriptLifeV2::lFADE_TO_PAL(TwinEEngine *engine, LifeScriptContext &ctx) {
- const int32 palIndex = engine->_screens->mapLba2Palette(ctx.stream.readByte());
+ const int32 rawIndex = ctx.stream.readByte();
+ const int32 palIndex = engine->_screens->mapLba2Palette(rawIndex);
debugC(3, kDebugLevels::kDebugScriptsLife, "LIFE::FADE_TO_PAL(%i)", palIndex);
engine->saveTimer(false);
- HQR::getPaletteEntry(engine->_screens->_ptrPal, Resources::HQR_RESS_FILE, palIndex);
- engine->_screens->fadeToPal(engine->_screens->_ptrPal);
- engine->_screens->_flagPalettePcx = true;
- if (palIndex == 3) { // Black palette?
+ if (rawIndex == 3) {
+ // Black palette requested - fade to black
+ engine->_screens->fadeToBlack(engine->_screens->_ptrPal);
+ HQR::getPaletteEntry(engine->_screens->_ptrPal, Resources::HQR_RESS_FILE, palIndex);
engine->_screens->_flagFade = true;
+ } else if (palIndex == -1) {
+ // Index 1 = current palette (already in _ptrPal)
+ engine->_screens->fadeToPal(engine->_screens->_ptrPal);
+ } else {
+ // Cross-fade from current to target palette
+ Graphics::Palette targetPal{0};
+ HQR::getPaletteEntry(targetPal, Resources::HQR_RESS_FILE, palIndex);
+ engine->_screens->fadePalToPal(engine->_screens->_ptrPal, targetPal);
+ engine->_screens->_ptrPal = targetPal;
}
engine->restoreTimer();
return 0;
@@ -616,10 +630,8 @@ int32 ScriptLifeV2::lPLAY_ACF(TwinEEngine *engine, LifeScriptContext &ctx) {
engine->_screens->_flagFade = true;
engine->_movie->playMovie(movie);
- // TODO: lba2 is doing more stuff here - reset the cinema mode, init the scene and palette stuff
- // if (CubeMode==CUBE_INTERIEUR) InitGrille( NumCube ) ;
- // RazListPartFlow() ;
- // ChoicePalette() ;
+ // Restore scene palette after movie
+ engine->_screens->choicePalette();
engine->setPalette(engine->_screens->_ptrPal);
engine->_screens->_flagFade = true;
engine->restoreTimer();
Commit: 9641a7a19acc1bdc3fd869ab910f13711c726fdd
https://github.com/scummvm/scummvm/commit/9641a7a19acc1bdc3fd869ab910f13711c726fdd
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-24T21:30:55+02:00
Commit Message:
TWINE: LBA2: implemented body parser
Changed paths:
engines/twine/parser/body.cpp
diff --git a/engines/twine/parser/body.cpp b/engines/twine/parser/body.cpp
index cee214d5558..258a4e9a5de 100644
--- a/engines/twine/parser/body.cpp
+++ b/engines/twine/parser/body.cpp
@@ -26,6 +26,9 @@
#define INFO_TRI 1
#define INFO_ANIM 2
+// LBA2 body flags
+#define MASK_OBJECT_ANIMATED (1 << 8)
+
namespace TwinE {
void BodyData::reset() {
@@ -206,33 +209,140 @@ bool BodyData::loadFromStream(Common::SeekableReadStream &stream, bool lba1) {
} else {
// T_BODY_HEADER (lba2)
const uint32 flags = stream.readUint32LE();
- animated = (flags & INFO_ANIM) != 0;
- stream.skip(4); // int16 size of header and int16 dummy
+ animated = (flags & MASK_OBJECT_ANIMATED) != 0;
+ stream.skip(4); // int16 SizeHeader and int16 Dummy
bbox.mins.x = stream.readSint32LE();
bbox.maxs.x = stream.readSint32LE();
bbox.mins.y = stream.readSint32LE();
bbox.maxs.y = stream.readSint32LE();
bbox.mins.z = stream.readSint32LE();
bbox.maxs.z = stream.readSint32LE();
- stream.seek(0x20);
-#if 0
- const uint32 bonesSize = stream.readUint32LE();
- const uint32 bonesOffset = stream.readUint32LE();
- const uint32 verticesSize = stream.readUint32LE();
- const uint32 verticesOffset = stream.readUint32LE();
- const uint32 normalsSize = stream.readUint32LE();
- const uint32 normalsOffset = stream.readUint32LE();
- const uint32 unk1Size = stream.readUint32LE();
- const uint32 unk1Offset = stream.readUint32LE();
- const uint32 polygonsSize = stream.readUint32LE();
- const uint32 polygonsOffset = stream.readUint32LE();
- const uint32 linesSize = stream.readUint32LE();
- const uint32 linesOffset = stream.readUint32LE();
- const uint32 spheresSize = stream.readUint32LE();
- const uint32 spheresOffset = stream.readUint32LE();
- const uint32 uvGroupsSize = stream.readUint32LE();
- const uint32 uvGroupsOffset = stream.readUint32LE();
-#endif
+
+ // Offset table
+ const int32 nbGroupes = stream.readSint32LE();
+ const int32 offGroupes = stream.readSint32LE();
+ const int32 nbPoints = stream.readSint32LE();
+ const int32 offPoints = stream.readSint32LE();
+ const int32 nbNormals = stream.readSint32LE();
+ const int32 offNormals = stream.readSint32LE();
+ /*const int32 nbNormFaces =*/ stream.readSint32LE();
+ /*const int32 offNormFaces =*/ stream.readSint32LE();
+ const int32 nbPolys = stream.readSint32LE();
+ const int32 offPolys = stream.readSint32LE();
+ const int32 nbLines = stream.readSint32LE();
+ const int32 offLines = stream.readSint32LE();
+ const int32 nbSpheres = stream.readSint32LE();
+ const int32 offSpheres = stream.readSint32LE();
+ /*const int32 nbTextures =*/ stream.readSint32LE();
+ /*const int32 offTextures =*/ stream.readSint32LE();
+
+ // Load vertices (4 x int16 per point: x, y, z, pad)
+ stream.seek(offPoints);
+ _vertices.reserve(nbPoints);
+ for (int32 i = 0; i < nbPoints; ++i) {
+ const int16 x = stream.readSint16LE();
+ const int16 y = stream.readSint16LE();
+ const int16 z = stream.readSint16LE();
+ stream.skip(2); // padding
+ _vertices.push_back({x, y, z, 0});
+ }
+
+ // Load bones/groupes (4 x uint16: OrgGroupe, OrgPoint, NbPts, NbNorm)
+ stream.seek(offGroupes);
+ _bones.reserve(nbGroupes);
+ int16 vertexOffset = 0;
+ for (int32 i = 0; i < nbGroupes; ++i) {
+ const uint16 orgGroupe = stream.readUint16LE();
+ const uint16 orgPoint = stream.readUint16LE();
+ const uint16 nbPts = stream.readUint16LE();
+ const uint16 nbNorm = stream.readUint16LE();
+
+ BodyBone bone;
+ bone.parent = (i == 0) ? 0xffff : orgGroupe;
+ bone.vertex = orgPoint;
+ bone.firstVertex = vertexOffset;
+ bone.numVertices = nbPts;
+ bone.numNormals = nbNorm;
+ bone.initalBoneState.type = BoneType::TYPE_ROTATE;
+ bone.initalBoneState.x = 0;
+ bone.initalBoneState.y = 0;
+ bone.initalBoneState.z = 0;
+
+ for (int j = 0; j < nbPts; ++j) {
+ if (vertexOffset + j < (int)_vertices.size()) {
+ _vertices[vertexOffset + j].bone = i;
+ }
+ }
+ vertexOffset += nbPts;
+
+ _bones.push_back(bone);
+ _boneStates[i] = bone.initalBoneState;
+ }
+
+ // Load normals (4 x int16: x, y, z, prenormalizedRange)
+ stream.seek(offNormals);
+ _normals.reserve(nbNormals);
+ for (int32 i = 0; i < nbNormals; ++i) {
+ BodyNormal normal;
+ normal.x = stream.readSint16LE();
+ normal.y = stream.readSint16LE();
+ normal.z = stream.readSint16LE();
+ normal.prenormalizedRange = stream.readUint16LE();
+ _normals.push_back(normal);
+ }
+
+ // Load polygons
+ stream.seek(offPolys);
+ _polygons.reserve(nbPolys);
+ for (int32 i = 0; i < nbPolys; ++i) {
+ BodyPolygon poly;
+ poly.materialType = stream.readByte();
+ const uint8 numVerts = stream.readByte();
+ poly.intensity = stream.readSint16LE();
+
+ int16 normal = -1;
+ if (poly.materialType == MAT_FLAT || poly.materialType == MAT_GRANIT) {
+ normal = stream.readSint16LE();
+ }
+
+ poly.indices.reserve(numVerts);
+ poly.normals.reserve(numVerts);
+ for (int k = 0; k < numVerts; ++k) {
+ if (poly.materialType >= MAT_GOURAUD) {
+ normal = stream.readSint16LE();
+ }
+ const uint16 vertexIndex = stream.readUint16LE() / 6;
+ poly.indices.push_back(vertexIndex);
+ poly.normals.push_back(normal);
+ }
+ _polygons.push_back(poly);
+ }
+
+ // Load lines
+ stream.seek(offLines);
+ _lines.reserve(nbLines);
+ for (int32 i = 0; i < nbLines; ++i) {
+ BodyLine line;
+ stream.skip(1);
+ line.color = stream.readByte();
+ stream.skip(2);
+ line.vertex1 = stream.readUint16LE() / 6;
+ line.vertex2 = stream.readUint16LE() / 6;
+ _lines.push_back(line);
+ }
+
+ // Load spheres
+ stream.seek(offSpheres);
+ _spheres.reserve(nbSpheres);
+ for (int32 i = 0; i < nbSpheres; ++i) {
+ BodySphere sphere;
+ sphere.fillType = stream.readByte();
+ sphere.color = stream.readUint16LE();
+ stream.readByte();
+ sphere.radius = stream.readUint16LE();
+ sphere.vertex = stream.readUint16LE() / 6;
+ _spheres.push_back(sphere);
+ }
}
return !stream.err();
Commit: 4289094bc23006d4f414f2f96a195beb7eb2c392
https://github.com/scummvm/scummvm/commit/4289094bc23006d4f414f2f96a195beb7eb2c392
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-24T21:30:55+02:00
Commit Message:
TWINE: fixed soft lock of bulldozer driven by twinsen
https://bugs.scummvm.org/ticket/15388
Changed paths:
engines/twine/renderer/renderer.cpp
engines/twine/scene/grid.cpp
diff --git a/engines/twine/renderer/renderer.cpp b/engines/twine/renderer/renderer.cpp
index 60df7dd9171..5cf26a976e5 100644
--- a/engines/twine/renderer/renderer.cpp
+++ b/engines/twine/renderer/renderer.cpp
@@ -182,30 +182,30 @@ IVec3 Renderer::setAngleCamera(int32 alpha, int32 beta, int32 gamma) {
}
IVec3 Renderer::worldRotatePoint(const IVec3& vec) {
- const int32 vx = (_matrixWorld.row1.x * vec.x + _matrixWorld.row1.y * vec.y + _matrixWorld.row1.z * vec.z) / SCENE_SIZE_HALF;
- const int32 vy = (_matrixWorld.row2.x * vec.x + _matrixWorld.row2.y * vec.y + _matrixWorld.row2.z * vec.z) / SCENE_SIZE_HALF;
- const int32 vz = (_matrixWorld.row3.x * vec.x + _matrixWorld.row3.y * vec.y + _matrixWorld.row3.z * vec.z) / SCENE_SIZE_HALF;
+ const int32 vx = (_matrixWorld.row1.x * vec.x + _matrixWorld.row1.y * vec.y + _matrixWorld.row1.z * vec.z) >> 14;
+ const int32 vy = (_matrixWorld.row2.x * vec.x + _matrixWorld.row2.y * vec.y + _matrixWorld.row2.z * vec.z) >> 14;
+ const int32 vz = (_matrixWorld.row3.x * vec.x + _matrixWorld.row3.y * vec.y + _matrixWorld.row3.z * vec.z) >> 14;
return IVec3(vx, vy, vz);
}
IVec3 Renderer::longWorldRot(int32 x, int32 y, int32 z) {
- const int64 vx = ((int64)_matrixWorld.row1.x * (int64)x + (int64)_matrixWorld.row1.y * (int64)y + (int64)_matrixWorld.row1.z * (int64)z) / SCENE_SIZE_HALF;
- const int64 vy = ((int64)_matrixWorld.row2.x * (int64)x + (int64)_matrixWorld.row2.y * (int64)y + (int64)_matrixWorld.row2.z * (int64)z) / SCENE_SIZE_HALF;
- const int64 vz = ((int64)_matrixWorld.row3.x * (int64)x + (int64)_matrixWorld.row3.y * (int64)y + (int64)_matrixWorld.row3.z * (int64)z) / SCENE_SIZE_HALF;
+ const int64 vx = ((int64)_matrixWorld.row1.x * (int64)x + (int64)_matrixWorld.row1.y * (int64)y + (int64)_matrixWorld.row1.z * (int64)z) >> 14;
+ const int64 vy = ((int64)_matrixWorld.row2.x * (int64)x + (int64)_matrixWorld.row2.y * (int64)y + (int64)_matrixWorld.row2.z * (int64)z) >> 14;
+ const int64 vz = ((int64)_matrixWorld.row3.x * (int64)x + (int64)_matrixWorld.row3.y * (int64)y + (int64)_matrixWorld.row3.z * (int64)z) >> 14;
return IVec3((int32)vx, (int32)vy, (int32)vz);
}
IVec3 Renderer::longInverseRot(int32 x, int32 y, int32 z) {
- const int64 vx = ((int64)_matrixWorld.row1.x * (int64)x + (int64)_matrixWorld.row2.x * (int64)y + (int64)_matrixWorld.row3.x * (int64)z) / SCENE_SIZE_HALF;
- const int64 vy = ((int64)_matrixWorld.row1.y * (int64)x + (int64)_matrixWorld.row2.y * (int64)y + (int64)_matrixWorld.row3.y * (int64)z) / SCENE_SIZE_HALF;
- const int64 vz = ((int64)_matrixWorld.row1.z * (int64)x + (int64)_matrixWorld.row2.z * (int64)y + (int64)_matrixWorld.row3.z * (int64)z) / SCENE_SIZE_HALF;
+ const int64 vx = ((int64)_matrixWorld.row1.x * (int64)x + (int64)_matrixWorld.row2.x * (int64)y + (int64)_matrixWorld.row3.x * (int64)z) >> 14;
+ const int64 vy = ((int64)_matrixWorld.row1.y * (int64)x + (int64)_matrixWorld.row2.y * (int64)y + (int64)_matrixWorld.row3.y * (int64)z) >> 14;
+ const int64 vz = ((int64)_matrixWorld.row1.z * (int64)x + (int64)_matrixWorld.row2.z * (int64)y + (int64)_matrixWorld.row3.z * (int64)z) >> 14;
return IVec3((int32)vx, (int32)vy, (int32)vz);
}
IVec3 Renderer::rot(const IMatrix3x3 &matrix, int32 x, int32 y, int32 z) {
- const int32 vx = (matrix.row1.x * x + matrix.row1.y * y + matrix.row1.z * z) / SCENE_SIZE_HALF;
- const int32 vy = (matrix.row2.x * x + matrix.row2.y * y + matrix.row2.z * z) / SCENE_SIZE_HALF;
- const int32 vz = (matrix.row3.x * x + matrix.row3.y * y + matrix.row3.z * z) / SCENE_SIZE_HALF;
+ const int32 vx = (matrix.row1.x * x + matrix.row1.y * y + matrix.row1.z * z) >> 14;
+ const int32 vy = (matrix.row2.x * x + matrix.row2.y * y + matrix.row2.z * z) >> 14;
+ const int32 vz = (matrix.row3.x * x + matrix.row3.y * y + matrix.row3.z * z) >> 14;
return IVec3(vx, vy, vz);
}
@@ -246,12 +246,12 @@ void Renderer::rotMatIndex2(IMatrix3x3 *pDest, const IMatrix3x3 *pSrc, const IVe
pDest->row2.x = pSrc->row2.x;
pDest->row3.x = pSrc->row3.x;
- pDest->row1.y = (pSrc->row1.z * nSin + pSrc->row1.y * nCos) / SCENE_SIZE_HALF;
- pDest->row1.z = (pSrc->row1.z * nCos - pSrc->row1.y * nSin) / SCENE_SIZE_HALF;
- pDest->row2.y = (pSrc->row2.z * nSin + pSrc->row2.y * nCos) / SCENE_SIZE_HALF;
- pDest->row2.z = (pSrc->row2.z * nCos - pSrc->row2.y * nSin) / SCENE_SIZE_HALF;
- pDest->row3.y = (pSrc->row3.z * nSin + pSrc->row3.y * nCos) / SCENE_SIZE_HALF;
- pDest->row3.z = (pSrc->row3.z * nCos - pSrc->row3.y * nSin) / SCENE_SIZE_HALF;
+ pDest->row1.y = (pSrc->row1.z * nSin + pSrc->row1.y * nCos) >> 14;
+ pDest->row1.z = (pSrc->row1.z * nCos - pSrc->row1.y * nSin) >> 14;
+ pDest->row2.y = (pSrc->row2.z * nSin + pSrc->row2.y * nCos) >> 14;
+ pDest->row2.z = (pSrc->row2.z * nCos - pSrc->row2.y * nSin) >> 14;
+ pDest->row3.y = (pSrc->row3.z * nSin + pSrc->row3.y * nCos) >> 14;
+ pDest->row3.z = (pSrc->row3.z * nCos - pSrc->row3.y * nSin) >> 14;
pSrc = pDest;
}
@@ -263,12 +263,12 @@ void Renderer::rotMatIndex2(IMatrix3x3 *pDest, const IMatrix3x3 *pSrc, const IVe
tmp.row2.z = pSrc->row2.z;
tmp.row3.z = pSrc->row3.z;
- tmp.row1.x = (pSrc->row1.y * nSin + pSrc->row1.x * nCos) / SCENE_SIZE_HALF;
- tmp.row1.y = (pSrc->row1.y * nCos - pSrc->row1.x * nSin) / SCENE_SIZE_HALF;
- tmp.row2.x = (pSrc->row2.y * nSin + pSrc->row2.x * nCos) / SCENE_SIZE_HALF;
- tmp.row2.y = (pSrc->row2.y * nCos - pSrc->row2.x * nSin) / SCENE_SIZE_HALF;
- tmp.row3.x = (pSrc->row3.y * nSin + pSrc->row3.x * nCos) / SCENE_SIZE_HALF;
- tmp.row3.y = (pSrc->row3.y * nCos - pSrc->row3.x * nSin) / SCENE_SIZE_HALF;
+ tmp.row1.x = (pSrc->row1.y * nSin + pSrc->row1.x * nCos) >> 14;
+ tmp.row1.y = (pSrc->row1.y * nCos - pSrc->row1.x * nSin) >> 14;
+ tmp.row2.x = (pSrc->row2.y * nSin + pSrc->row2.x * nCos) >> 14;
+ tmp.row2.y = (pSrc->row2.y * nCos - pSrc->row2.x * nSin) >> 14;
+ tmp.row3.x = (pSrc->row3.y * nSin + pSrc->row3.x * nCos) >> 14;
+ tmp.row3.y = (pSrc->row3.y * nCos - pSrc->row3.x * nSin) >> 14;
pSrc = &tmp;
}
@@ -291,12 +291,12 @@ void Renderer::rotMatIndex2(IMatrix3x3 *pDest, const IMatrix3x3 *pSrc, const IVe
pDest->row3.y = pSrc->row3.y;
}
- pDest->row1.x = (pSrc->row1.x * nCos - pSrc->row1.z * nSin) / SCENE_SIZE_HALF;
- pDest->row1.z = (pSrc->row1.x * nSin + pSrc->row1.z * nCos) / SCENE_SIZE_HALF;
- pDest->row2.x = (pSrc->row2.x * nCos - pSrc->row2.z * nSin) / SCENE_SIZE_HALF;
- pDest->row2.z = (pSrc->row2.x * nSin + pSrc->row2.z * nCos) / SCENE_SIZE_HALF;
- pDest->row3.x = (pSrc->row3.x * nCos - pSrc->row3.z * nSin) / SCENE_SIZE_HALF;
- pDest->row3.z = (pSrc->row3.x * nSin + pSrc->row3.z * nCos) / SCENE_SIZE_HALF;
+ pDest->row1.x = (pSrc->row1.x * nCos - pSrc->row1.z * nSin) >> 14;
+ pDest->row1.z = (pSrc->row1.x * nSin + pSrc->row1.z * nCos) >> 14;
+ pDest->row2.x = (pSrc->row2.x * nCos - pSrc->row2.z * nSin) >> 14;
+ pDest->row2.z = (pSrc->row2.x * nSin + pSrc->row2.z * nCos) >> 14;
+ pDest->row3.x = (pSrc->row3.x * nCos - pSrc->row3.z * nSin) >> 14;
+ pDest->row3.z = (pSrc->row3.x * nSin + pSrc->row3.z * nCos) >> 14;
} else if (pSrc != pDest) {
*pDest = *pSrc;
}
@@ -314,9 +314,9 @@ bool isPolygonVisible(const ComputedVertex *vertices) { // TestVuePoly
void Renderer::rotList(const Common::Array<BodyVertex> &vertices, int32 firstPoint, int32 numPoints, I16Vec3 *destPoints, const IMatrix3x3 *rotationMatrix, const IVec3 &destPos) {
for (int32 i = 0; i < numPoints; ++i) {
const BodyVertex &vertex = vertices[i + firstPoint];
- destPoints->x = (int16)(((rotationMatrix->row1.x * vertex.x + rotationMatrix->row1.y * vertex.y + rotationMatrix->row1.z * vertex.z) / SCENE_SIZE_HALF) + destPos.x);
- destPoints->y = (int16)(((rotationMatrix->row2.x * vertex.x + rotationMatrix->row2.y * vertex.y + rotationMatrix->row2.z * vertex.z) / SCENE_SIZE_HALF) + destPos.y);
- destPoints->z = (int16)(((rotationMatrix->row3.x * vertex.x + rotationMatrix->row3.y * vertex.y + rotationMatrix->row3.z * vertex.z) / SCENE_SIZE_HALF) + destPos.z);
+ destPoints->x = (int16)(((rotationMatrix->row1.x * vertex.x + rotationMatrix->row1.y * vertex.y + rotationMatrix->row1.z * vertex.z) >> 14) + destPos.x);
+ destPoints->y = (int16)(((rotationMatrix->row2.x * vertex.x + rotationMatrix->row2.y * vertex.y + rotationMatrix->row2.z * vertex.z) >> 14) + destPos.y);
+ destPoints->z = (int16)(((rotationMatrix->row3.x * vertex.x + rotationMatrix->row3.y * vertex.y + rotationMatrix->row3.z * vertex.z) >> 14) + destPos.z);
destPoints++;
}
@@ -358,9 +358,9 @@ void Renderer::transRotList(const Common::Array<BodyVertex> &vertices, int32 fir
const int16 tmpY = (int16)(vertex.y + angleVec.y);
const int16 tmpZ = (int16)(vertex.z + angleVec.z);
- destPoints->x = ((translationMatrix->row1.x * tmpX + translationMatrix->row1.y * tmpY + translationMatrix->row1.z * tmpZ) / SCENE_SIZE_HALF) + destPos.x;
- destPoints->y = ((translationMatrix->row2.x * tmpX + translationMatrix->row2.y * tmpY + translationMatrix->row2.z * tmpZ) / SCENE_SIZE_HALF) + destPos.y;
- destPoints->z = ((translationMatrix->row3.x * tmpX + translationMatrix->row3.y * tmpY + translationMatrix->row3.z * tmpZ) / SCENE_SIZE_HALF) + destPos.z;
+ destPoints->x = ((translationMatrix->row1.x * tmpX + translationMatrix->row1.y * tmpY + translationMatrix->row1.z * tmpZ) >> 14) + destPos.x;
+ destPoints->y = ((translationMatrix->row2.x * tmpX + translationMatrix->row2.y * tmpY + translationMatrix->row2.z * tmpZ) >> 14) + destPos.y;
+ destPoints->z = ((translationMatrix->row3.x * tmpX + translationMatrix->row3.y * tmpY + translationMatrix->row3.z * tmpZ) >> 14) + destPos.z;
destPoints++;
}
diff --git a/engines/twine/scene/grid.cpp b/engines/twine/scene/grid.cpp
index 8cec35ef901..06005a19587 100644
--- a/engines/twine/scene/grid.cpp
+++ b/engines/twine/scene/grid.cpp
@@ -635,19 +635,15 @@ BlockEntry Grid::getBlockEntry(int32 xmap, int32 ymap, int32 zmap) const {
ShapeType Grid::worldColBrick(int32 x, int32 y, int32 z) {
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
- if (collision.y <= -1) {
- return ShapeType::kSolid;
- }
-
- if (collision.x < 0 || collision.x >= SIZE_CUBE_X) {
+ if (collision.x < 0 || collision.x >= SIZE_CUBE_X || collision.z < 0 || collision.z >= SIZE_CUBE_Z) {
return ShapeType::kNone;
}
- if (collision.y < 0 || collision.y >= SIZE_CUBE_Y) {
- return ShapeType::kNone;
+ if (collision.y <= -1) {
+ return ShapeType::kSolid;
}
- if (collision.z < 0 || collision.z >= SIZE_CUBE_Z) {
+ if (collision.y >= SIZE_CUBE_Y) {
return ShapeType::kNone;
}
@@ -660,9 +656,9 @@ ShapeType Grid::worldColBrick(int32 x, int32 y, int32 z) {
}
const IVec3 &Grid::updateCollisionCoordinates(int32 x, int32 y, int32 z) {
- _engine->_collision->_collision.x = (x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
- _engine->_collision->_collision.y = y / SIZE_BRICK_Y;
- _engine->_collision->_collision.z = (z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
+ _engine->_collision->_collision.x = (x + DEMI_BRICK_XZ) >> 9;
+ _engine->_collision->_collision.y = y >> 8;
+ _engine->_collision->_collision.z = (z + DEMI_BRICK_XZ) >> 9;
return _engine->_collision->_collision;
}
@@ -684,14 +680,14 @@ bool Grid::shouldCheckWaterCol(int32 actorIdx) const {
ShapeType Grid::worldColBrickFull(int32 x, int32 y, int32 z, int32 y2, int32 actorIdx) {
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
- if (collision.y <= -1) {
- return ShapeType::kSolid;
- }
-
if (collision.x < 0 || collision.x >= SIZE_CUBE_X || collision.z < 0 || collision.z >= SIZE_CUBE_Z) {
return ShapeType::kNone;
}
+ if (collision.y <= -1) {
+ return ShapeType::kSolid;
+ }
+
bool checkWater = shouldCheckWaterCol(actorIdx);
uint8 *pCube = _bufCube;
pCube += collision.x * SIZE_CUBE_Y * 2;
More information about the Scummvm-git-logs
mailing list