Initial commit
This commit is contained in:
198
engines/stark/model/animhandler.cpp
Normal file
198
engines/stark/model/animhandler.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/* 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 "engines/stark/model/animhandler.h"
|
||||
|
||||
#include "engines/stark/model/model.h"
|
||||
#include "engines/stark/model/skeleton_anim.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
AnimHandler::AnimHandler() :
|
||||
_model(nullptr),
|
||||
_anim(nullptr),
|
||||
_animTime(-1),
|
||||
_framesBeforeCandidateReady(0),
|
||||
_candidateAnim(nullptr),
|
||||
_candidateAnimTime(-1),
|
||||
_blendAnim(nullptr),
|
||||
_blendAnimTime(-1),
|
||||
_blendTimeRemaining(0) {
|
||||
|
||||
}
|
||||
|
||||
AnimHandler::~AnimHandler() {
|
||||
}
|
||||
|
||||
void AnimHandler::setAnim(SkeletonAnim *anim) {
|
||||
if (_candidateAnim == anim) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_anim == anim) {
|
||||
// If we already have the correct anim, clean any candidates
|
||||
// that may have been set but not yet enacted as the active anim.
|
||||
_candidateAnim = nullptr;
|
||||
_candidateAnimTime = -1;
|
||||
_framesBeforeCandidateReady = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't use new animations the first frame they are set.
|
||||
// Scripts may change animation the very next frame,
|
||||
// causing animations to blend with animations that
|
||||
// were only visible for one frame, leading to animation
|
||||
// jumps. Instead store them as candidates.
|
||||
_framesBeforeCandidateReady = 2; // 2 because we are at the end of the frame
|
||||
_candidateAnim = anim;
|
||||
_candidateAnimTime = 0;
|
||||
}
|
||||
|
||||
void AnimHandler::setModel(Model *model) {
|
||||
_model = model;
|
||||
}
|
||||
|
||||
void AnimHandler::setNode(uint32 time, BoneNode *bone, const BoneNode *parent) {
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
|
||||
if (_blendTimeRemaining <= 0) {
|
||||
_anim->getCoordForBone(time, bone->_idx, bone->_animPos, bone->_animRot);
|
||||
} else {
|
||||
// Blend the coordinates of the previous and the current animation
|
||||
Math::Vector3d previousAnimPos, animPos;
|
||||
Math::Quaternion previousAnimRot, animRot;
|
||||
_blendAnim->getCoordForBone(_blendAnimTime, bone->_idx, previousAnimPos, previousAnimRot);
|
||||
_anim->getCoordForBone(time, bone->_idx, animPos, animRot);
|
||||
|
||||
float blendingRatio = 1.0 - _blendTimeRemaining / (float)_blendDuration;
|
||||
|
||||
bone->_animPos = previousAnimPos + (animPos - previousAnimPos) * blendingRatio;
|
||||
bone->_animRot = previousAnimRot.slerpQuat(animRot, blendingRatio);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
parent->_animRot.transform(bone->_animPos);
|
||||
|
||||
bone->_animPos = parent->_animPos + bone->_animPos;
|
||||
bone->_animRot = parent->_animRot * bone->_animRot;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < bone->_children.size(); ++i) {
|
||||
setNode(time, bones[bone->_children[i]], bone);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHandler::animate(uint32 time) {
|
||||
if (!_anim && _candidateAnim) {
|
||||
// This is the first time we animate this item.
|
||||
enactCandidate();
|
||||
}
|
||||
|
||||
if (_candidateAnim && _anim && _anim->getBoneCount() != _model->getBones().size()) {
|
||||
// We changed to an incompatible model
|
||||
enactCandidate();
|
||||
|
||||
// And the anim we were previously blending with is incompatible as well
|
||||
if (_blendAnim && _blendAnim->getBoneCount() != _model->getBones().size()) {
|
||||
stopBlending();
|
||||
}
|
||||
}
|
||||
|
||||
if (_candidateAnim && _framesBeforeCandidateReady > 0) {
|
||||
|
||||
_candidateAnimTime = time;
|
||||
_framesBeforeCandidateReady--;
|
||||
|
||||
// We need to animate here, because the model may have
|
||||
// changed from under us.
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
setNode(_animTime, bones[0], nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_candidateAnim && _framesBeforeCandidateReady <= 0) {
|
||||
if (_anim) {
|
||||
startBlending();
|
||||
}
|
||||
enactCandidate();
|
||||
}
|
||||
|
||||
int32 deltaTime = time - _animTime;
|
||||
if (deltaTime < 0 || time > _blendDuration / 2) {
|
||||
deltaTime = 33;
|
||||
}
|
||||
|
||||
updateBlending(deltaTime);
|
||||
|
||||
// Start at root bone
|
||||
// For each child
|
||||
// - Set childs animation coordinate
|
||||
// - Process that childs children
|
||||
|
||||
const Common::Array<BoneNode *> &bones = _model->getBones();
|
||||
if (deltaTime >= 0) {
|
||||
setNode(time, bones[0], nullptr);
|
||||
_animTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHandler::enactCandidate() {
|
||||
_anim = _candidateAnim;
|
||||
_animTime = _candidateAnimTime;
|
||||
_candidateAnim = nullptr;
|
||||
_candidateAnimTime = -1;
|
||||
_framesBeforeCandidateReady = 0;
|
||||
}
|
||||
|
||||
void AnimHandler::startBlending() {
|
||||
_blendTimeRemaining = _blendDuration;
|
||||
_blendAnim = _anim;
|
||||
_blendAnimTime = _animTime;
|
||||
}
|
||||
|
||||
void AnimHandler::updateBlending(int32 deltaTime) {
|
||||
_blendTimeRemaining -= deltaTime;
|
||||
if (_blendTimeRemaining > 0) {
|
||||
// If we are blending, also update the previous animation's time
|
||||
_blendAnimTime += deltaTime;
|
||||
if (_blendAnimTime >= (int32) _blendAnim->getLength()) {
|
||||
_blendAnimTime = _blendAnim->getLength() - 1;
|
||||
}
|
||||
} else {
|
||||
// Otherwise make sure blending is not enabled
|
||||
stopBlending();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimHandler::stopBlending() {
|
||||
_blendAnim = nullptr;
|
||||
_blendAnimTime = -1;
|
||||
_blendTimeRemaining = 0;
|
||||
}
|
||||
|
||||
void AnimHandler::resetBlending() {
|
||||
stopBlending();
|
||||
if (_candidateAnim) {
|
||||
enactCandidate();
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
81
engines/stark/model/animhandler.h
Normal file
81
engines/stark/model/animhandler.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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 STARK_MODEL_ANIM_HANDLER_H
|
||||
#define STARK_MODEL_ANIM_HANDLER_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class Model;
|
||||
class BoneNode;
|
||||
class SkeletonAnim;
|
||||
|
||||
/**
|
||||
* Animate a skeletal model's bones according to an animation
|
||||
*/
|
||||
class AnimHandler {
|
||||
public:
|
||||
AnimHandler();
|
||||
~AnimHandler();
|
||||
|
||||
/**
|
||||
* Increment the animation timestamp, and apply bone animations if required
|
||||
*/
|
||||
void animate(uint32 time);
|
||||
|
||||
/** Set the skeletal model to animate */
|
||||
void setModel(Model *model);
|
||||
|
||||
/** Set the skeletal animation to use */
|
||||
void setAnim(SkeletonAnim *anim);
|
||||
|
||||
/** Stop blending and forget about the previous animation */
|
||||
void resetBlending();
|
||||
|
||||
private:
|
||||
void enactCandidate();
|
||||
void startBlending();
|
||||
void updateBlending(int32 deltaTime);
|
||||
void stopBlending();
|
||||
|
||||
void setNode(uint32 time, BoneNode *bone, const BoneNode *parent);
|
||||
|
||||
static const uint32 _blendDuration = 300; // ms
|
||||
|
||||
SkeletonAnim *_anim;
|
||||
int32 _animTime;
|
||||
|
||||
int32 _framesBeforeCandidateReady;
|
||||
SkeletonAnim *_candidateAnim;
|
||||
int32 _candidateAnimTime;
|
||||
|
||||
SkeletonAnim *_blendAnim;
|
||||
int32 _blendAnimTime;
|
||||
int32 _blendTimeRemaining;
|
||||
|
||||
Model *_model;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MODEL_ANIM_HANDLER_H
|
||||
233
engines/stark/model/model.cpp
Normal file
233
engines/stark/model/model.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/* 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 "engines/stark/model/model.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
#include "engines/stark/model/animhandler.h"
|
||||
#include "engines/stark/gfx/texture.h"
|
||||
|
||||
#include "math/aabb.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
Model::Model() :
|
||||
_u1(0),
|
||||
_u2(0.0) {
|
||||
|
||||
}
|
||||
|
||||
Model::~Model() {
|
||||
for (Common::Array<VertNode *>::iterator it = _vertices.begin(); it != _vertices.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
for (Common::Array<Material *>::iterator it = _materials.begin(); it != _materials.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
for (Common::Array<Face *>::iterator it = _faces.begin(); it != _faces.end(); ++it)
|
||||
delete *it;
|
||||
|
||||
for (Common::Array<BoneNode *>::iterator it = _bones.begin(); it != _bones.end(); ++it)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
void Model::readFromStream(ArchiveReadStream *stream) {
|
||||
uint32 id = stream->readUint32LE();
|
||||
if (id != 4) {
|
||||
error("Wrong magic 1 while reading actor '%d'", id);
|
||||
}
|
||||
|
||||
uint32 format = stream->readUint32LE();
|
||||
if (format == 256) {
|
||||
_u1 = stream->readUint32LE();
|
||||
} else if (format == 16) {
|
||||
_u1 = 0;
|
||||
} else {
|
||||
error("Wrong format while reading actor '%d'", format);
|
||||
}
|
||||
|
||||
uint32 id2 = stream->readUint32LE();
|
||||
if (id2 != 0xDEADBABE) {
|
||||
error("Wrong magic 2 while reading actor '%d'", id2);
|
||||
}
|
||||
|
||||
_u2 = stream->readFloatLE();
|
||||
|
||||
uint32 numMaterials = stream->readUint32LE();
|
||||
|
||||
for (uint i = 0; i < numMaterials; ++i) {
|
||||
Material *node = new Material();
|
||||
node->name = stream->readString();
|
||||
stream->readUint32LE(); // CHECKME: Unknown data
|
||||
node->texture = stream->readString();
|
||||
node->r = stream->readFloatLE();
|
||||
node->g = stream->readFloatLE();
|
||||
node->b = stream->readFloatLE();
|
||||
_materials.push_back(node);
|
||||
}
|
||||
|
||||
uint32 numUnknowns = stream->readUint32LE();
|
||||
if (numUnknowns != 0) {
|
||||
error("Found a mesh with numUnknowns != 0");
|
||||
}
|
||||
|
||||
readBones(stream);
|
||||
|
||||
uint32 numMeshes = stream->readUint32LE();
|
||||
if (numMeshes != 1) {
|
||||
error("Found a mesh with numMeshes != 1 (%d)", numMeshes);
|
||||
}
|
||||
|
||||
_name = stream->readString();
|
||||
|
||||
uint32 numFaces = stream->readUint32LE();
|
||||
for (uint32 j = 0; j < numFaces; ++j) {
|
||||
uint faceVertexIndexOffset = _vertices.size();
|
||||
|
||||
Face *face = new Face();
|
||||
face->materialId = stream->readUint32LE();
|
||||
|
||||
uint32 numVertices = stream->readUint32LE();
|
||||
for (uint32 k = 0; k < numVertices; ++k) {
|
||||
VertNode *vert = new VertNode();
|
||||
vert->_pos1 = stream->readVector3();
|
||||
vert->_pos2 = stream->readVector3();
|
||||
vert->_normal = stream->readVector3();
|
||||
vert->_texS = stream->readFloatLE();
|
||||
vert->_texT = stream->readFloatLE();
|
||||
vert->_bone1 = stream->readUint32LE();
|
||||
vert->_bone2 = stream->readUint32LE();
|
||||
vert->_boneWeight = stream->readFloatLE();
|
||||
_vertices.push_back(vert);
|
||||
}
|
||||
|
||||
uint32 numTriangles = stream->readUint32LE();
|
||||
face->vertexIndices.resize(numTriangles * 3); // 3 vertex indices per triangle
|
||||
for (uint32 k = 0; k < numTriangles; ++k) {
|
||||
face->vertexIndices[k * 3 + 0] = stream->readUint32LE() + faceVertexIndexOffset;
|
||||
face->vertexIndices[k * 3 + 1] = stream->readUint32LE() + faceVertexIndexOffset;
|
||||
face->vertexIndices[k * 3 + 2] = stream->readUint32LE() + faceVertexIndexOffset;
|
||||
}
|
||||
|
||||
_faces.push_back(face);
|
||||
}
|
||||
|
||||
buildBonesBoundingBoxes();
|
||||
}
|
||||
|
||||
void Model::readBones(ArchiveReadStream *stream) {
|
||||
uint32 numBones = stream->readUint32LE();
|
||||
for (uint32 i = 0; i < numBones; ++i) {
|
||||
BoneNode *node = new BoneNode();
|
||||
node->_name = stream->readString();
|
||||
node->_u1 = stream->readFloatLE();
|
||||
|
||||
uint32 len = stream->readUint32LE();
|
||||
for (uint32 j = 0; j < len; ++j)
|
||||
node->_children.push_back(stream->readUint32LE());
|
||||
|
||||
node->_idx = _bones.size();
|
||||
_bones.push_back(node);
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < numBones; ++i) {
|
||||
BoneNode *node = _bones[i];
|
||||
for (uint j = 0; j < node->_children.size(); ++j) {
|
||||
_bones[node->_children[j]]->_parent = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Model::buildBonesBoundingBoxes() {
|
||||
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 k = 0; k < _vertices.size(); k++) {
|
||||
VertNode *vert = _vertices[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 {
|
||||
for (uint i = 0; i < _bones.size(); i++) {
|
||||
if (_bones[i]->intersectRay(ray)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Model::updateBoundingBox() {
|
||||
_boundingBox.reset();
|
||||
for (uint i = 0; i < _bones.size(); i++) {
|
||||
_bones[i]->expandModelSpaceBB(_boundingBox);
|
||||
}
|
||||
}
|
||||
|
||||
Math::AABB Model::getBoundingBox() const {
|
||||
return _boundingBox;
|
||||
}
|
||||
|
||||
bool BoneNode::intersectRay(const Math::Ray &ray) const {
|
||||
Math::Ray localRay = ray;
|
||||
localRay.translate(-_animPos);
|
||||
localRay.rotate(_animRot.inverse());
|
||||
|
||||
return localRay.intersectAABB(_boundingBox);
|
||||
}
|
||||
|
||||
void BoneNode::expandModelSpaceBB(Math::AABB &aabb) const {
|
||||
// Transform the bounding box
|
||||
Math::Vector3d min = _boundingBox.getMin();
|
||||
Math::Vector3d max = _boundingBox.getMax();
|
||||
|
||||
Math::Vector3d verts[8];
|
||||
verts[0].set(min.x(), min.y(), min.z());
|
||||
verts[1].set(max.x(), min.y(), min.z());
|
||||
verts[2].set(min.x(), max.y(), min.z());
|
||||
verts[3].set(min.x(), min.y(), max.z());
|
||||
verts[4].set(max.x(), max.y(), min.z());
|
||||
verts[5].set(max.x(), min.y(), max.z());
|
||||
verts[6].set(min.x(), max.y(), max.z());
|
||||
verts[7].set(max.x(), max.y(), max.z());
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_animRot.transform(verts[i]);
|
||||
verts[i] += _animPos;
|
||||
aabb.expand(verts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
133
engines/stark/model/model.h
Normal file
133
engines/stark/model/model.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* 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 STARK_MODEL_MODEL_H
|
||||
#define STARK_MODEL_MODEL_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/str.h"
|
||||
|
||||
#include "math/ray.h"
|
||||
#include "math/vector3d.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
namespace Gfx {
|
||||
class TextureSet;
|
||||
}
|
||||
|
||||
class ArchiveReadStream;
|
||||
|
||||
class VertNode {
|
||||
public:
|
||||
Math::Vector3d _pos1, _pos2;
|
||||
Math::Vector3d _normal;
|
||||
float _texS, _texT;
|
||||
uint32 _bone1, _bone2;
|
||||
float _boneWeight;
|
||||
};
|
||||
|
||||
struct Face {
|
||||
uint32 materialId;
|
||||
Common::Array<uint32> vertexIndices;
|
||||
|
||||
Face() : materialId(0) {}
|
||||
};
|
||||
|
||||
struct Material {
|
||||
Common::String name;
|
||||
Common::String texture;
|
||||
float r, g, b;
|
||||
bool doubleSided;
|
||||
|
||||
Material() : r(0), g(0), b(0), doubleSided(false) {};
|
||||
};
|
||||
|
||||
class BoneNode {
|
||||
public:
|
||||
BoneNode() : _parent(-1), _idx(0), _u1(0) {}
|
||||
~BoneNode() { }
|
||||
|
||||
/** Perform a collision test with the ray */
|
||||
bool intersectRay(const Math::Ray &ray) const;
|
||||
|
||||
/** Expand a bounding box with the model space BB of this bone */
|
||||
void expandModelSpaceBB(Math::AABB &aabb) const;
|
||||
|
||||
Common::String _name;
|
||||
float _u1;
|
||||
Common::Array<uint32> _children;
|
||||
int _parent;
|
||||
uint32 _idx;
|
||||
|
||||
Math::Vector3d _animPos;
|
||||
Math::Quaternion _animRot;
|
||||
|
||||
/** Bone space bounding box */
|
||||
Math::AABB _boundingBox;
|
||||
};
|
||||
|
||||
/**
|
||||
* A 3D Model
|
||||
*/
|
||||
class Model {
|
||||
public:
|
||||
Model();
|
||||
~Model();
|
||||
|
||||
/**
|
||||
* Try and initialise object from the specified stream
|
||||
*/
|
||||
void readFromStream(ArchiveReadStream *stream);
|
||||
|
||||
const Common::Array<VertNode *> &getVertices() const { return _vertices; }
|
||||
const Common::Array<Face *> &getFaces() const { return _faces; }
|
||||
const Common::Array<Material *> &getMaterials() const { return _materials; }
|
||||
const Common::Array<BoneNode *> &getBones() const { return _bones; };
|
||||
|
||||
/** Perform a collision test with a ray */
|
||||
bool intersectRay(const Math::Ray &ray) const;
|
||||
|
||||
/** Update the model bounding box with the current animation state */
|
||||
void updateBoundingBox();
|
||||
|
||||
/** Retrieve the model space bounding box for the current animation state */
|
||||
Math::AABB getBoundingBox() const;
|
||||
|
||||
private:
|
||||
void buildBonesBoundingBoxes();
|
||||
void buildBoneBoundingBox(BoneNode *bone) const;
|
||||
void readBones(ArchiveReadStream *stream);
|
||||
|
||||
Common::String _name;
|
||||
uint32 _u1;
|
||||
float _u2;
|
||||
|
||||
Common::Array<VertNode *> _vertices;
|
||||
Common::Array<Material *> _materials;
|
||||
Common::Array<Face *> _faces;
|
||||
Common::Array<BoneNode *> _bones;
|
||||
Math::AABB _boundingBox;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MODEL_MODEL_H
|
||||
107
engines/stark/model/skeleton_anim.cpp
Normal file
107
engines/stark/model/skeleton_anim.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* 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 "engines/stark/model/skeleton_anim.h"
|
||||
|
||||
#include "engines/stark/services/archiveloader.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
SkeletonAnim::SkeletonAnim() :
|
||||
_id(0),
|
||||
_ver(0),
|
||||
_u1(0),
|
||||
_u2(0),
|
||||
_time(0) {
|
||||
}
|
||||
|
||||
void SkeletonAnim::createFromStream(ArchiveReadStream *stream) {
|
||||
_id = stream->readUint32LE();
|
||||
_ver = stream->readUint32LE();
|
||||
if (_ver == 3) {
|
||||
_u1 = 0;
|
||||
_time = stream->readUint32LE();
|
||||
_u2 = stream->readUint32LE();
|
||||
} else {
|
||||
_u1 = stream->readUint32LE();
|
||||
_u2 = stream->readUint32LE();
|
||||
_time = stream->readUint32LE();
|
||||
}
|
||||
if (_u2 != 0xdeadbabe) {
|
||||
error("Wrong magic while reading animation");
|
||||
}
|
||||
|
||||
uint32 num = stream->readUint32LE();
|
||||
_boneAnims.resize(num);
|
||||
for (uint32 i = 0; i < num; ++i) {
|
||||
uint32 bone = stream->readUint32LE();
|
||||
uint32 numKeys = stream->readUint32LE();
|
||||
|
||||
BoneAnim &boneAnim = _boneAnims[bone];
|
||||
boneAnim._keys.resize(numKeys);
|
||||
for (uint32 j = 0; j < numKeys; ++j) {
|
||||
AnimKey &key = boneAnim._keys[j];
|
||||
key._time = stream->readUint32LE();
|
||||
key._rot = stream->readQuaternion();
|
||||
key._pos = stream->readVector3();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonAnim::getCoordForBone(uint32 time, int boneIdx, Math::Vector3d &pos, Math::Quaternion &rot) const {
|
||||
const Common::Array<AnimKey> &keys = _boneAnims[boneIdx]._keys;
|
||||
|
||||
if (keys.size() == 1) {
|
||||
// There is only one key for this bone, don't bother searching which one to use
|
||||
pos = keys[0]._pos;
|
||||
rot = keys[0]._rot;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (Common::Array<AnimKey>::const_iterator it = keys.begin(); it < keys.end(); ++it) {
|
||||
if (it->_time > time) {
|
||||
// Between two key frames, interpolate
|
||||
const AnimKey *a = it;
|
||||
--it;
|
||||
const AnimKey *b = it;
|
||||
|
||||
float t = (float)(time - b->_time) / (float)(a->_time - b->_time);
|
||||
|
||||
pos = b->_pos + (a->_pos - b->_pos) * t;
|
||||
rot = b->_rot.slerpQuat(a->_rot, t);
|
||||
|
||||
return;
|
||||
} else if (it->_time == time || it == keys.end() - 1){
|
||||
// At a key frame
|
||||
// If not right one but didn't find any, then use last one as default
|
||||
const AnimKey *key = it;
|
||||
pos = key->_pos;
|
||||
rot = key->_rot;
|
||||
if (it == keys.end() - 1) {
|
||||
warning("Unable to find keyframe for bone '%d' at %d ms, using default", boneIdx, time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Stark
|
||||
73
engines/stark/model/skeleton_anim.h
Normal file
73
engines/stark/model/skeleton_anim.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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 STARK_MODEL_SKELETON_ANIM_H
|
||||
#define STARK_MODEL_SKELETON_ANIM_H
|
||||
|
||||
#include "math/quat.h"
|
||||
#include "math/vector3d.h"
|
||||
#include "common/array.h"
|
||||
|
||||
namespace Stark {
|
||||
|
||||
class ArchiveReadStream;
|
||||
|
||||
/**
|
||||
* Data structure responsible for skeletal animation of an actor object.
|
||||
*/
|
||||
class SkeletonAnim {
|
||||
public:
|
||||
SkeletonAnim();
|
||||
|
||||
void createFromStream(ArchiveReadStream *stream);
|
||||
|
||||
/**
|
||||
* Get the interpolated bone coordinate for a given bone at a given animation timestamp
|
||||
*/
|
||||
void getCoordForBone(uint32 time, int boneIdx, Math::Vector3d &pos, Math::Quaternion &rot) const;
|
||||
|
||||
/**
|
||||
* Get total animation length (in ms)
|
||||
*/
|
||||
uint32 getLength() const { return _time; }
|
||||
|
||||
/** The number of bones a skeleton must have to play this animation */
|
||||
uint32 getBoneCount() const { return _boneAnims.size(); }
|
||||
|
||||
private:
|
||||
struct AnimKey {
|
||||
uint32 _time;
|
||||
Math::Quaternion _rot;
|
||||
Math::Vector3d _pos;
|
||||
};
|
||||
|
||||
struct BoneAnim {
|
||||
Common::Array<AnimKey> _keys;
|
||||
};
|
||||
|
||||
uint32 _id, _ver, _u1, _u2, _time;
|
||||
|
||||
Common::Array<BoneAnim> _boneAnims;
|
||||
};
|
||||
|
||||
} // End of namespace Stark
|
||||
|
||||
#endif // STARK_MODEL_SKELETON_ANIM_H
|
||||
Reference in New Issue
Block a user