STARK: Add mouse picking for 3D items

This works at the bone bounding box level, whereas the original does
pixel-perfect picking.
This commit is contained in:
Bastien Bouclet 2015-07-29 08:03:27 +02:00
parent efc23795a1
commit 7e621e13f6
11 changed files with 121 additions and 4 deletions

View file

@ -120,6 +120,19 @@ bool RenderEntry::containsPoint(const Common::Point &position, Common::Point &re
return false;
}
bool RenderEntry::intersectRay(const Math::Ray &ray) const {
if (!_visual || !_clickable) {
return false;
}
VisualActor *actor = _visual->get<VisualActor>();
if (actor) {
return actor->intersectRay(ray, _position3D, _direction3D);
}
return false;
}
VisualImageXMG *RenderEntry::getImage() const {
return _visual->get<VisualImageXMG>();
}

View file

@ -27,6 +27,7 @@
#include "common/rect.h"
#include "common/str.h"
#include "math/ray.h"
#include "math/vector3d.h"
namespace Stark {
@ -61,8 +62,18 @@ public:
/** Obtain the underlying image visual, if any */
VisualImageXMG *getImage() const;
/**
* Mouse picking test for 2D items
*
* @param position game window coordinates to test
* @param relativePosition successful hit item relative coordinates
* @return successful hit
*/
bool containsPoint(const Common::Point &position, Common::Point &relativePosition) const;
/** Mouse picking test for 3D items */
bool intersectRay(const Math::Ray &ray) const;
/** Compare two render entries by their sort keys */
static bool compare(const RenderEntry *x, const RenderEntry *y);

View file

@ -26,6 +26,8 @@
#include "engines/stark/model/skeleton.h"
#include "engines/stark/gfx/texture.h"
#include "math/aabb.h"
namespace Stark {
Model::Model() :
@ -133,6 +135,8 @@ void Model::readFromStream(ArchiveReadStream *stream) {
_meshes.push_back(node);
}
buildBonesBoundingBoxes();
}
void Model::setAnim(SkeletonAnim *anim)
@ -144,4 +148,49 @@ void Model::setTextureSet(Gfx::TextureSet *texture) {
_textureSet = texture;
}
void Model::buildBonesBoundingBoxes() {
const Common::Array<BoneNode *> &bones = _skeleton->getBones();
for (uint i = 0; i < bones.size(); i++) {
buildBoneBoundingBox(bones[i]);
}
}
void Model::buildBoneBoundingBox(BoneNode *bone) const {
bone->_boundingBox.reset();
// Add all the vertices with a non zero weight for the bone to the bone's bounding box
for (uint i = 0; i < _meshes.size(); i++) {
MeshNode *mesh = _meshes[i];
for (uint j = 0; j < mesh->_faces.size(); j++) {
FaceNode *face = mesh->_faces[j];
for (uint k = 0; k < face->_verts.size(); k++) {
VertNode *vert = face->_verts[k];
if (vert->_bone1 == bone->_idx) {
bone->_boundingBox.expand(vert->_pos1);
}
if (vert->_bone2 == bone->_idx) {
bone->_boundingBox.expand(vert->_pos2);
}
}
}
}
}
bool Model::intersectRay(const Math::Ray &ray) const {
const Common::Array<BoneNode *> &bones = _skeleton->getBones();
for (uint i = 0; i < bones.size(); i++) {
if (bones[i]->intersectRay(ray)) {
return true;
}
}
return false;
}
} // End of namespace Stark

View file

@ -26,6 +26,7 @@
#include "common/array.h"
#include "common/str.h"
#include "math/ray.h"
#include "math/vector3d.h"
namespace Stark {
@ -38,6 +39,7 @@ class ArchiveReadStream;
class Skeleton;
class SkeletonAnim;
class BoneNode;
class VertNode {
public:
@ -130,7 +132,13 @@ public:
*/
void setTextureSet(Gfx::TextureSet *textureSet);
/** Perform a collision test with a ray */
bool intersectRay(const Math::Ray &ray) const;
private:
void buildBonesBoundingBoxes();
void buildBoneBoundingBox(BoneNode *bone) const;
uint32 _u1;
float _facingDirection;

View file

@ -25,8 +25,6 @@
#include "engines/stark/model/skeleton_anim.h"
#include "engines/stark/services/archiveloader.h"
#include "common/stream.h"
namespace Stark {
Skeleton::Skeleton() :
@ -98,4 +96,11 @@ void Skeleton::animate(uint32 time) {
}
}
bool BoneNode::intersectRay(const Math::Ray &ray) const {
Math::Ray localRay = ray;
localRay.translate(-_animPos);
localRay.rotate(_animRot.inverse());
return localRay.intersectAABB(_boundingBox);
}
} // End of namespace Stark

View file

@ -26,7 +26,9 @@
#include "common/array.h"
#include "common/str.h"
#include "math/aabb.h"
#include "math/quat.h"
#include "math/ray.h"
#include "math/vector3d.h"
namespace Stark {
@ -38,14 +40,21 @@ class BoneNode {
public:
BoneNode() : _parent(-1) { }
~BoneNode() { }
/** Perform a collision test with the ray */
bool intersectRay(const Math::Ray &ray) const;
Common::String _name;
float _u1;
Common::Array<uint32> _children;
int _parent;
int _idx;
uint32 _idx;
Math::Vector3d _animPos;
Math::Quaternion _animRot;
/** Bone space bounding box */
Math::AABB _boundingBox;
};
/**

View file

@ -281,6 +281,11 @@ Visual *AnimSkeleton::getVisual() {
return _visual;
}
int AnimSkeleton::indexForPoint(const Common::Point &point) const {
// Skeleton anims only have one hotspot
return 0;
}
void AnimSkeleton::readData(Formats::XRCReadStream *stream) {
Anim::readData(stream);

View file

@ -205,6 +205,7 @@ public:
void applyToItem(Item *item) override;
void removeFromItem(Item *item) override;
Visual *getVisual() override;
int indexForPoint(const Common::Point &point) const override;
protected:
void printData() override;

View file

@ -23,6 +23,7 @@
#include "engines/stark/ui/gamewindow.h"
#include "engines/stark/cursor.h"
#include "engines/stark/scene.h"
#include "engines/stark/gfx/driver.h"
@ -160,10 +161,13 @@ void GameWindow::checkObjectAtPos(Common::Point pos, int16 selectedInventoryItem
singlePossibleAction = -1;
isDefaultAction = false;
Math::Ray ray = StarkScene->makeRayFromMouse(_cursor->getMousePosition(true));
// Render entries are sorted from the farthest to the camera to the nearest
// Loop in reverse order
for (int i = _renderEntries.size() - 1; i >= 0; i--) {
if (_renderEntries[i]->containsPoint(pos, _objectRelativePosition)) {
if (_renderEntries[i]->containsPoint(pos, _objectRelativePosition)
|| _renderEntries[i]->intersectRay(ray)) {
_objectUnderCursor = _renderEntries[i]->getOwner();
break;
}

View file

@ -74,7 +74,17 @@ Math::Matrix4 VisualActor::getModelMatrix(const Math::Vector3d& position, float
scale.setValue(2, 2, -1.0f);
return posMatrix * rot1 * rot2 * scale;
}
bool VisualActor::intersectRay(const Math::Ray &ray, const Math::Vector3d position, float direction) {
Math::Matrix4 inverseModelMatrix = getModelMatrix(position, direction);
inverseModelMatrix.inverse();
// Build an object local ray from the world ray
Math::Ray localRay = ray;
localRay.transform(inverseModelMatrix);
return _model->intersectRay(localRay);
}
} // End of namespace Stark

View file

@ -26,6 +26,7 @@
#include "common/str.h"
#include "math/matrix4.h"
#include "math/ray.h"
#include "math/vector3d.h"
#include "engines/stark/visual/visual.h"
@ -53,6 +54,7 @@ public:
void setTexture(Gfx::TextureSet *texture);
void setTime(uint32 time);
bool intersectRay(const Math::Ray &ray, const Math::Vector3d position, float direction);
virtual void render(Gfx::Driver *gfx, const Math::Vector3d position, float direction) = 0;
protected: