diff --git a/engines/grim/actor.cpp b/engines/grim/actor.cpp index 7abcafa512f..1bdea44dc34 100644 --- a/engines/grim/actor.cpp +++ b/engines/grim/actor.cpp @@ -1668,29 +1668,30 @@ void Actor::draw() { } if (_mustPlaceText) { - int x1, y1, x2, y2; - x1 = y1 = 1000; - x2 = y2 = -1000; - if (!_costumeStack.empty()) { - g_driver->startActorDraw(this); - if (g_grim->getGameType() == GType_GRIM) { + Common::Point p1, p2; + if (g_grim->getGameType() == GType_GRIM) { + if (!_costumeStack.empty()) { + int x1 = 1000, y1 = 1000, x2 = -1000, y2 = -1000; + g_driver->startActorDraw(this); _costumeStack.back()->getBoundingBox(&x1, &y1, &x2, &y2); - } else { - EMICostume *c = static_cast(getCurrentCostume()); - if (c->_wearChore) - c->_wearChore->getMesh()->getBoundingBox(&x1, &y1, &x2, &y2); + g_driver->finishActorDraw(); + p1.x = x1; + p1.y = y1; + p2.x = x2; + p2.y = y2; } - g_driver->finishActorDraw(); + } else { + g_driver->getActorScreenBBox(this, p1, p2); } TextObject *textObject = TextObject::getPool().getObject(_sayLineText); if (textObject) { - if (x1 == 1000 || x2 == -1000 || y2 == -1000) { + if (p1.x == 1000 || p2.x == -1000 || p2.x == -1000) { textObject->setX(640 / 2); textObject->setY(463); } else { - textObject->setX((x1 + x2) / 2); - textObject->setY(y1); + textObject->setX((p1.x + p2.x) / 2); + textObject->setY(p1.y); } // Deletes the original text and rebuilds it with the newly placed text textObject->reset(); diff --git a/engines/grim/actor.h b/engines/grim/actor.h index 0227ab4cbc9..5d0ee4cbdca 100644 --- a/engines/grim/actor.h +++ b/engines/grim/actor.h @@ -558,6 +558,8 @@ public: ObjectPtr loadMaterial(const Common::String &name, bool clamp); ObjectPtr findMaterial(const Common::String &name); + void getBBoxInfo(Math::Vector3d &bboxPos, Math::Vector3d &bboxSize) const; + private: void costumeMarkerCallback(int marker); void collisionHandlerCallback(Actor *other) const; @@ -582,7 +584,6 @@ private: Math::Vector3d getSimplePuckVector() const; void calculateOrientation(const Math::Vector3d &pos, Math::Angle *pitch, Math::Angle *yaw, Math::Angle *roll); - void getBBoxInfo(Math::Vector3d &bboxPos, Math::Vector3d &bboxSize) const; bool getSphereInfo(bool adjustZ, float &size, Math::Vector3d &pos) const; Common::String _name; diff --git a/engines/grim/gfx_base.h b/engines/grim/gfx_base.h index e0c9fcff5f6..1a12ed8de75 100644 --- a/engines/grim/gfx_base.h +++ b/engines/grim/gfx_base.h @@ -29,6 +29,7 @@ #include "graphics/pixelformat.h" #include "graphics/pixelbuffer.h" #include "common/str.h" +#include "common/rect.h" #include "engines/grim/material.h" @@ -122,6 +123,7 @@ public: */ virtual void getScreenBoundingBox(const Mesh *mesh, int *x1, int *y1, int *x2, int *y2) = 0; virtual void getScreenBoundingBox(const EMIModel *mesh, int *x1, int *y1, int *x2, int *y2) = 0; + virtual void getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) = 0; virtual void startActorDraw(const Actor *act) = 0; virtual void finishActorDraw() = 0; virtual void setShadow(Shadow *shadow) = 0; diff --git a/engines/grim/gfx_opengl.cpp b/engines/grim/gfx_opengl.cpp index 307215a6155..e390eb81e5a 100644 --- a/engines/grim/gfx_opengl.cpp +++ b/engines/grim/gfx_opengl.cpp @@ -468,7 +468,7 @@ void GfxOpenGL::getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, in bottom = win.y(); } } - + double t = bottom; bottom = _gameHeight - top; top = _gameHeight - t; @@ -496,6 +496,67 @@ void GfxOpenGL::getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, in *y2 = (int)bottom; } +void GfxOpenGL::getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) { + // Get the actor's bounding box information (describes a 3D box) + Math::Vector3d bboxPos, bboxSize; + actor->getBBoxInfo(bboxPos, bboxSize); + + // Translate the bounding box to the actor's position + Math::Matrix4 m = actor->getFinalMatrix(); + bboxPos = bboxPos + actor->getWorldPos(); + + // Set up the camera coordinate system + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + Math::Matrix4 worldRot = _currentQuat.toMatrix(); + glMultMatrixf(worldRot.getData()); + glTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z()); + + // Get the current OpenGL state + GLdouble modelView[16], projection[16]; + GLint viewPort[4]; + glGetDoublev(GL_MODELVIEW_MATRIX, modelView); + glGetDoublev(GL_PROJECTION_MATRIX, projection); + glGetIntegerv(GL_VIEWPORT, viewPort); + + // Set values outside of the screen range + p1.x = 1000; + p1.y = 1000; + p2.x = -1000; + p2.y = -1000; + + // Project all of the points in the 3D bounding box + Math::Vector3d p, projected; + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + for (int z = 0; z < 2; z++) { + Math::Vector3d added(bboxSize.x() * 0.5f * (x * 2 - 1), bboxSize.y() * 0.5f * (y * 2 - 1), bboxSize.z() * 0.5f * (z * 2 - 1)); + m.transform(&added, false); + p = bboxPos + added; + Math::gluMathProject(p, modelView, projection, viewPort, projected); + + // Find the points + if (projected.x() < p1.x) + p1.x = projected.x(); + if (projected.y() < p1.y) + p1.y = projected.y(); + if (projected.x() > p2.x) + p2.x = projected.x(); + if (projected.y() > p2.y) + p2.y = projected.y(); + } + } + } + + // Swap the p1/p2 y coorindates + int16 tmp = p1.y; + p1.y = 480 - p2.y; + p2.y = 480 - tmp; + + // Restore the state + glPopMatrix(); +} + void GfxOpenGL::startActorDraw(const Actor *actor) { _currentActor = actor; glEnable(GL_TEXTURE_2D); diff --git a/engines/grim/gfx_opengl.h b/engines/grim/gfx_opengl.h index eea6e56076e..615b34c267b 100644 --- a/engines/grim/gfx_opengl.h +++ b/engines/grim/gfx_opengl.h @@ -64,6 +64,7 @@ public: void getScreenBoundingBox(const Mesh *model, int *x1, int *y1, int *x2, int *y2) override; void getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, int *x2, int *y2) override; + void getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) override; void startActorDraw(const Actor *actor) override; void finishActorDraw() override; diff --git a/engines/grim/gfx_opengl_shaders.cpp b/engines/grim/gfx_opengl_shaders.cpp index 4a1e9cf6186..ce4aafb8687 100644 --- a/engines/grim/gfx_opengl_shaders.cpp +++ b/engines/grim/gfx_opengl_shaders.cpp @@ -584,6 +584,68 @@ void GfxOpenGLS::getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, i *y2 = (int)(_gameHeight - top); } +void GfxOpenGLS::getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) { + // Get the actor's bounding box information (describes a 3D box) + Math::Vector3d bboxPos, bboxSize; + actor->getBBoxInfo(bboxPos, bboxSize); + + // Translate the bounding box to the actor's position + Math::Matrix4 m = actor->getFinalMatrix(); + bboxPos = bboxPos + actor->getWorldPos(); + + // Set up the camera coordinate system + Math::Matrix4 modelView = _currentQuat.toMatrix(); + Math::Matrix4 zScale; + zScale.setValue(2, 2, -1.0); + modelView = modelView * zScale; + modelView.transpose(); + modelView.translate(-_currentPos); + modelView.transpose(); + + // Set values outside of the screen range + p1.x = 1000; + p1.y = 1000; + p2.x = -1000; + p2.y = -1000; + + // Project all of the points in the 3D bounding box + Math::Vector3d p, projected; + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + for (int z = 0; z < 2; z++) { + Math::Vector3d added(bboxSize.x() * 0.5f * (x * 2 - 1), bboxSize.y() * 0.5f * (y * 2 - 1), bboxSize.z() * 0.5f * (z * 2 - 1)); + m.transform(&added, false); + p = bboxPos + added; + + Math::Vector4d v = Math::Vector4d(p.x(), p.y(), p.z(), 1.0f); + v = _projMatrix.transform(modelView.transform(v)); + if (v.w() == 0.0) + return; + v /= v.w(); + + double winX = (1 + v.x()) / 2.0f * _gameWidth; + double winY = (1 + v.y()) / 2.0f * _gameHeight; + + // Find the points + if (winX < p1.x) + p1.x = winX; + if (winY < p1.y) + p1.y = winY; + if (winX > p2.x) + p2.x = winX; + if (winY > p2.y) + p2.y = winY; + } + } + } + + // Swap the p1/p2 y coorindates + int16 tmp = p1.y; + p1.y = 480 - p2.y; + p2.y = 480 - tmp; +} + + void GfxOpenGLS::startActorDraw(const Actor *actor) { _currentActor = actor; _actorProgram->use(); diff --git a/engines/grim/gfx_opengl_shaders.h b/engines/grim/gfx_opengl_shaders.h index d10fcf79c80..632326ce4d9 100644 --- a/engines/grim/gfx_opengl_shaders.h +++ b/engines/grim/gfx_opengl_shaders.h @@ -27,6 +27,7 @@ #include "engines/grim/gfx_base.h" #include "graphics/opengles2/shader.h" #include "common/stack.h" +#include "common/rect.h" namespace Grim { @@ -67,6 +68,7 @@ public: virtual void getScreenBoundingBox(const Mesh *mesh, int *x1, int *y1, int *x2, int *y2) override; virtual void getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, int *x2, int *y2) override; + void getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) override; virtual void startActorDraw(const Actor *actor) override; virtual void finishActorDraw() override; diff --git a/engines/grim/gfx_tinygl.cpp b/engines/grim/gfx_tinygl.cpp index 65086478dc4..14ac4778de7 100644 --- a/engines/grim/gfx_tinygl.cpp +++ b/engines/grim/gfx_tinygl.cpp @@ -510,6 +510,70 @@ void GfxTinyGL::getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, in *y2 = (int)(_gameHeight - top); } +void GfxTinyGL::getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) { + // Get the actor's bounding box information (describes a 3D box) + Math::Vector3d bboxPos, bboxSize; + actor->getBBoxInfo(bboxPos, bboxSize); + + // Translate the bounding box to the actor's position + Math::Matrix4 m = actor->getFinalMatrix(); + bboxPos = bboxPos + actor->getWorldPos(); + + // Set up the coordinate system + tglMatrixMode(TGL_MODELVIEW); + tglPushMatrix(); + + // Apply the view transform. + Math::Matrix4 worldRot = _currentQuat.toMatrix(); + tglMultMatrixf(worldRot.getData()); + tglTranslatef(-_currentPos.x(), -_currentPos.y(), -_currentPos.z()); + + // Get the current OpenGL state + TGLfloat modelView[16], projection[16]; + TGLint viewPort[4]; + tglGetFloatv(TGL_MODELVIEW_MATRIX, modelView); + tglGetFloatv(TGL_PROJECTION_MATRIX, projection); + tglGetIntegerv(TGL_VIEWPORT, viewPort); + + // Set values outside of the screen range + p1.x = 1000; + p1.y = 1000; + p2.x = -1000; + p2.y = -1000; + + // Project all of the points in the 3D bounding box + Math::Vector3d p, projected; + for (int x = 0; x < 2; x++) { + for (int y = 0; y < 2; y++) { + for (int z = 0; z < 2; z++) { + Math::Vector3d added(bboxSize.x() * 0.5f * (x * 2 - 1), bboxSize.y() * 0.5f * (y * 2 - 1), bboxSize.z() * 0.5f * (z * 2 - 1)); + m.transform(&added, false); + p = bboxPos + added; + Math::gluMathProject(p, modelView, projection, viewPort, projected); + + // Find the points + if (projected.x() < p1.x) + p1.x = projected.x(); + if (projected.y() < p1.y) + p1.y = projected.y(); + if (projected.x() > p2.x) + p2.x = projected.x(); + if (projected.y() > p2.y) + p2.y = projected.y(); + } + } + } + + // Swap the p1/p2 y coorindates + int16 tmp = p1.y; + p1.y = 480 - p2.y; + p2.y = 480 - tmp; + + // Restore the state + tglPopMatrix(); +} + + void GfxTinyGL::startActorDraw(const Actor *actor) { _currentActor = actor; tglEnable(TGL_TEXTURE_2D); diff --git a/engines/grim/gfx_tinygl.h b/engines/grim/gfx_tinygl.h index b44785ea8ac..d3df64e42f6 100644 --- a/engines/grim/gfx_tinygl.h +++ b/engines/grim/gfx_tinygl.h @@ -62,6 +62,7 @@ public: void getScreenBoundingBox(const Mesh *model, int *x1, int *y1, int *x2, int *y2) override; void getScreenBoundingBox(const EMIModel *model, int *x1, int *y1, int *x2, int *y2) override; + void getActorScreenBBox(const Actor *actor, Common::Point &p1, Common::Point &p2) override; void startActorDraw(const Actor *actor) override; void finishActorDraw() override;