Major graphics/model refactoring

- Decouple Model and ModelNode from MdlReader
- Make Emitter a nested struct under ModelNode
- Read all known controller types
This commit is contained in:
Vsevolod Kremianskii 2021-05-13 14:42:29 +07:00
parent 277a94bec7
commit 366d8d739d
35 changed files with 1661 additions and 1721 deletions

View file

@ -172,11 +172,8 @@ set(GRAPHICS_HEADERS
src/engine/graphics/mesh/mesh.h
src/engine/graphics/mesh/meshes.h
src/engine/graphics/mesh/vertexattributes.h
src/engine/graphics/model/aabbnode.h
src/engine/graphics/model/animatedproperty.h
src/engine/graphics/model/animation.h
src/engine/graphics/model/emitter.h
src/engine/graphics/model/lensflare.h
src/engine/graphics/model/mdlreader.h
src/engine/graphics/model/model.h
src/engine/graphics/model/modelnode.h
@ -217,6 +214,7 @@ set(GRAPHICS_SOURCES
src/engine/graphics/mesh/meshes.cpp
src/engine/graphics/model/animation.cpp
src/engine/graphics/model/mdlreader.cpp
src/engine/graphics/model/mdlreader_controllers.cpp
src/engine/graphics/model/model.cpp
src/engine/graphics/model/modelnode.cpp
src/engine/graphics/model/models.cpp

View file

@ -256,7 +256,7 @@ glm::vec3 DialogGUI::getTalkPosition(const SpatialObject &object) const {
if (model->getNodeAbsolutePosition("talkdummy", hookPosition)) {
return object.position() + hookPosition;
}
return model->getCenterOfAABB();
return model->getWorldCenterAABB();
}
return object.position();

View file

@ -526,7 +526,7 @@ void Area::fill(SceneGraph &sceneGraph) {
if (sceneNode) {
sceneGraph.addRoot(sceneNode);
}
shared_ptr<ModelNode> aabbNode(sceneNode->model()->findAABBNode());
shared_ptr<ModelNode> aabbNode(sceneNode->model()->getAABBNode());
if (aabbNode && _grass.texture) {
glm::mat4 aabbTransform(glm::translate(aabbNode->absoluteTransform(), room.second->position()));
auto grass = make_shared<GrassSceneNode>(&sceneGraph, glm::vec2(_grass.quadSize), _grass.texture, aabbNode->mesh()->lightmap);

View file

@ -418,7 +418,7 @@ glm::vec3 Creature::getSelectablePosition() const {
shared_ptr<ModelSceneNode> model(getModelSceneNode());
if (!model) return _position;
if (_dead) return model->getCenterOfAABB();
if (_dead) return model->getWorldCenterAABB();
glm::vec3 position;
@ -426,7 +426,7 @@ glm::vec3 Creature::getSelectablePosition() const {
return model->absoluteTransform() * glm::vec4(position, 1.0f);
}
return model->getCenterOfAABB();
return model->getWorldCenterAABB();
}
float Creature::getAttackRange() const {

View file

@ -226,7 +226,7 @@ shared_ptr<Walkmesh> SpatialObject::getWalkmesh() const {
glm::vec3 SpatialObject::getSelectablePosition() const {
auto model = getModelSceneNode();
return model ? model->getCenterOfAABB() : _position;
return model ? model->getWorldCenterAABB() : _position;
}
void SpatialObject::setRoom(Room *room) {

View file

@ -29,7 +29,7 @@ LipAnimation::LipAnimation(float length, vector<Keyframe> keyframes) :
_length(length), _keyframes(move(keyframes)) {
}
bool LipAnimation::getKeyframes(float time, uint8_t &leftShape, uint8_t &rightShape, float &interpolant) const {
bool LipAnimation::getKeyframes(float time, uint8_t &leftShape, uint8_t &rightShape, float &factor) const {
if (_keyframes.empty()) return false;
const Keyframe *left = &_keyframes[0];
@ -49,9 +49,9 @@ bool LipAnimation::getKeyframes(float time, uint8_t &leftShape, uint8_t &rightSh
rightShape = right->shape;
if (&left == &right) {
interpolant = 0.0f;
factor = 0.0f;
} else {
interpolant = (time - left->time) / (right->time - left->time);
factor = (time - left->time) / (right->time - left->time);
}
return true;

View file

@ -33,7 +33,7 @@ public:
LipAnimation(float length, std::vector<Keyframe> keyframes);
bool getKeyframes(float time, uint8_t &leftShape, uint8_t &rightShape, float &interpolant) const;
bool getKeyframes(float time, uint8_t &leftShape, uint8_t &rightShape, float &factor) const;
float length() const { return _length; }
const std::vector<Keyframe> &keyframes() const { return _keyframes; }

View file

@ -36,7 +36,8 @@ Materials &Materials::instance() {
}
void Materials::init() {
if (!_inited) {
if (_inited) return;
shared_ptr<TwoDA> materials(Resources::instance().get2DA("material", false));
if (materials) {
for (int row = 0; row < materials->getRowCount(); ++row) {
@ -58,8 +59,8 @@ void Materials::init() {
_materials.insert(make_pair(tex, move(material)));
}
}
_inited = true;
}
}
Materials::~Materials() {

View file

@ -76,7 +76,6 @@ public:
const std::vector<float> &vertices() const { return _vertices; }
const std::vector<uint16_t> &indices() const { return _indices; }
const VertexAttributes &attributes() const { return _attributes; }
VertexAttributes &attributes() { return _attributes; }
const AABB &aabb() const { return _aabb; }
private:

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include "../aabb.h"
namespace reone {
namespace graphics {
struct AABBNode {
enum class Plane {
None = 0,
PositiveX = 1,
PositiveY = 2,
PositiveZ = 4
};
int faceIndex { 0 };
Plane mostSignificantPlane { Plane::None };
AABB aabb;
std::shared_ptr<AABBNode> leftChild;
std::shared_ptr<AABBNode> rightChild;
};
} // namespace graphics
} // namespace reone

View file

@ -40,22 +40,22 @@ struct SlerpInterpolator {
}
};
template <class V, class Interpolator = MixInterpolator<V>>
template <class V, class Inter = MixInterpolator<V>>
class AnimatedProperty {
public:
int getNumKeyframes() const {
return static_cast<int>(_keyframes.size());
int getNumFrames() const {
return static_cast<int>(_frames.size());
}
bool getByTime(float time, V &value) const {
if (_keyframes.empty()) return false;
if (_frames.empty()) return false;
const std::pair<float, V> *frame1 = &_keyframes[0];
const std::pair<float, V> *frame2 = &_keyframes[0];
for (auto it = _keyframes.begin(); it != _keyframes.end(); ++it) {
const std::pair<float, V> *frame1 = &_frames[0];
const std::pair<float, V> *frame2 = &_frames[0];
for (auto it = _frames.begin(); it != _frames.end(); ++it) {
if (it->first >= time) {
frame2 = &*it;
if (it != _keyframes.begin()) {
if (it != _frames.begin()) {
frame1 = &*(it - 1);
}
break;
@ -69,33 +69,33 @@ public:
factor = (time - frame1->first) / (frame2->first - frame1->first);
}
value = Interpolator::interpolate(frame1->second, frame2->second, factor);
value = Inter::interpolate(frame1->second, frame2->second, factor);
return true;
}
V getByKeyframe(int frame) const {
return _keyframes[frame].second;
V getByFrame(int frame) const {
return _frames[frame].second;
}
V getByKeyframeOrElse(int frame, V defaultValue) const {
return frame < static_cast<int>(_keyframes.size()) ?
getByKeyframe(frame) :
V getByFrameOrElse(int frame, V defaultValue) const {
return frame < static_cast<int>(_frames.size()) ?
getByFrame(frame) :
std::move(defaultValue);
}
void addKeyframe(float time, V value) {
_keyframes.push_back(std::make_pair(time, value));
void addFrame(float time, V value) {
_frames.push_back(std::make_pair(time, std::move(value)));
}
void update() {
std::sort(_keyframes.begin(), _keyframes.end(), [](auto &left, auto &right) {
std::sort(_frames.begin(), _frames.end(), [](auto &left, auto &right) {
return left.first < right.first;
});
}
private:
std::vector<std::pair<float, V>> _keyframes;
std::vector<std::pair<float, V>> _frames;
};
} // namespace graphics

View file

@ -19,6 +19,8 @@
#include <queue>
#include "../../common/collectionutil.h"
using namespace std;
namespace reone {
@ -26,22 +28,22 @@ namespace reone {
namespace graphics {
Animation::Animation(
const string &name,
string name,
float length,
float transitionTime,
vector<Event> &&events,
const shared_ptr<ModelNode> &rootNode
shared_ptr<ModelNode> rootNode,
vector<Event> &&events
) :
_name(name),
_name(move(name)),
_length(length),
_transitionTime(transitionTime),
_events(move(events)),
_rootNode(rootNode) {
_rootNode(move(rootNode)),
_events(move(events)) {
initNodeByName();
fillNodeByName();
}
void Animation::initNodeByName() {
void Animation::fillNodeByName() {
queue<shared_ptr<ModelNode>> nodes;
nodes.push(_rootNode);
@ -51,20 +53,14 @@ void Animation::initNodeByName() {
_nodeByName.insert(make_pair(node->name(), node));
const vector<shared_ptr<ModelNode>> &children = node->children();
for (auto &child : children) {
for (auto &child : node->children()) {
nodes.push(child);
}
}
}
shared_ptr<ModelNode> Animation::findNode(const string &name) const {
auto it = _nodeByName.find(name);
return it != _nodeByName.end() ? it->second : nullptr;
}
void Animation::setName(string name) {
_name = move(name);
shared_ptr<ModelNode> Animation::getNodeByName(const string &name) const {
return getFromLookupOrNull(_nodeByName, name);
}
} // namespace graphics

View file

@ -37,13 +37,13 @@ public:
};
Animation(
const std::string &name,
std::string name,
float length,
float transitionTime,
std::vector<Event> &&events,
const std::shared_ptr<ModelNode> &rootNode);
std::shared_ptr<ModelNode> rootNode,
std::vector<Event> &&events);
std::shared_ptr<ModelNode> findNode(const std::string &name) const;
std::shared_ptr<ModelNode> getNodeByName(const std::string &name) const;
const std::string &name() const { return _name; }
float length() const { return _length; }
@ -51,20 +51,16 @@ public:
std::shared_ptr<ModelNode> rootNode() const { return _rootNode; }
const std::vector<Event> &events() const { return _events; }
void setName(std::string name);
private:
std::string _name;
float _length { 0.0f };
float _transitionTime { 0.0f };
std::vector<Event> _events;
std::shared_ptr<ModelNode> _rootNode;
std::vector<Event> _events;
std::unordered_map<std::string, std::shared_ptr<ModelNode>> _nodeByName;
void initNodeByName();
friend class MdlReader;
void fillNodeByName();
};
} // namespace graphics

View file

@ -1,129 +0,0 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include "glm/vec2.hpp"
#include "../texture/texture.h"
namespace reone {
namespace graphics {
class Emitter {
public:
enum class UpdateMode {
Invalid,
Fountain,
Single,
Explosion
};
enum class RenderMode {
Invalid = 0,
Normal = 1,
BillboardToWorldZ = 2,
MotionBlur = 3,
BillboardToLocalZ = 4,
AlignedToParticleDir = 5
};
enum class BlendMode {
Invalid,
Normal,
Punch,
Lighten
};
template <class T>
struct Constraints {
T start;
T mid;
T end;
};
UpdateMode updateMode() const { return _updateMode; }
RenderMode renderMode() const { return _renderMode; }
BlendMode blendMode() const { return _blendMode; }
int renderOrder() const { return _renderOrder; }
std::shared_ptr<Texture> texture() const { return _texture; }
int gridWidth() const { return _gridWidth; }
int gridHeight() const { return _gridHeight; }
int frameStart() const { return _frameStart; }
int frameEnd() const { return _frameEnd; }
glm::vec2 &size() { return _size; }
const glm::vec2 &size() const { return _size; }
Constraints<float> &particleSize() { return _particleSize; }
const Constraints<float> &particleSize() const { return _particleSize; }
Constraints<glm::vec3> &color() { return _color; }
const Constraints<glm::vec3> &color() const { return _color; }
Constraints<float> &alpha() { return _alpha; }
const Constraints<float> &alpha() const { return _alpha; }
int birthrate() const { return _birthrate; }
int lifeExpectancy() const { return _lifeExpectancy; }
float velocity() const { return _velocity; }
float randomVelocity() const { return _randomVelocity; }
float spread() const { return _spread; }
bool loop() const { return _loop; }
int fps() const { return _fps; }
void setFrameStart(int value) { _frameStart = value; }
void setFrameEnd(int value) { _frameEnd = value; }
void setBirthrate(int value) { _birthrate = value; }
void setLifeExpectancy(int value) { _lifeExpectancy = value; }
void setVelocity(float value) { _velocity = value; }
void setRandomVelocity(float value) { _randomVelocity = value; }
void setSpread(float value) { _spread = value; }
void setLoop(bool value) { _loop = value; }
void setFPS(int value) { _fps = value; }
private:
UpdateMode _updateMode { UpdateMode::Invalid };
RenderMode _renderMode { RenderMode::Invalid };
BlendMode _blendMode { BlendMode::Invalid };
int _renderOrder { 0 };
std::shared_ptr<Texture> _texture;
int _gridWidth { 0 };
int _gridHeight { 0 };
int _frameStart { 0 };
int _frameEnd { 0 };
glm::vec2 _size { 0.0f };
int _birthrate { 0 }; /**< rate of particle birth per second */
int _lifeExpectancy { 0 }; /**< life of each particle in seconds */
float _velocity { 0.0f };
float _randomVelocity { 0.0f };
float _spread { 0.0f };
bool _loop { false };
int _fps { 0 };
Constraints<float> _particleSize;
Constraints<glm::vec3> _color;
Constraints<float> _alpha;
friend class MdlReader;
};
} // namespace graphics
} // namespace reone

View file

@ -1,39 +0,0 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <memory>
#include "glm/vec3.hpp"
#include "../texture/texture.h"
namespace reone {
namespace graphics {
struct LensFlare {
std::shared_ptr<Texture> texture;
glm::vec3 colorShift { 0.0f };
float position { 0.0f };
float size { 0.0f };
};
} // namespace graphics
} // namespace reone

File diff suppressed because it is too large Load diff

View file

@ -17,9 +17,12 @@
#pragma once
#include <functional>
#include <map>
#include <unordered_map>
#include "../../resource/format/binreader.h"
#include "aabbnode.h"
#include "model.h"
namespace reone {
@ -35,76 +38,133 @@ public:
std::shared_ptr<graphics::Model> model() const { return _model; }
private:
struct NodeFlags {
static constexpr int header = 1;
static constexpr int light = 2;
static constexpr int emitter = 4;
static constexpr int camera = 8;
static constexpr int reference = 0x10;
static constexpr int mesh = 0x20;
static constexpr int skin = 0x40;
static constexpr int anim = 0x80;
static constexpr int dangly = 0x100;
static constexpr int aabb = 0x200;
static constexpr int saber = 0x800;
};
struct ArrayDefinition {
uint32_t offset { 0 };
uint32_t count { 0 };
uint32_t count2 { 0 };
};
struct MeshHeader {
// Material
glm::vec3 ambient { 0.0f };
glm::vec3 diffuse { 0.0f };
std::string texture1;
std::string texture2;
bool render { false };
bool shadow { false };
bool backgroundGeometry { false };
int transparencyHint { 0 };
// Geometry
int numVertices { 0 };
int numFaces { 0 };
uint32_t offFaces { 0 };
uint32_t offOffIndices { 0 };
// MDX
int mdxVertexSize { 0 };
int offMdxVertices { 0 };
int offMdxNormals { 0 };
int offMdxTexCoords1 { 0 };
int offMdxTexCoords2 { 0 };
int offMdxTanSpace { 0 };
// UV animation
bool animateUV { false };
float uvDirectionX { 0.0f };
float uvDirectionY { 0.0f };
struct ControllerKey {
uint32_t type { 0 };
uint16_t numRows { 0 };
uint16_t timeIndex { 0 };
uint16_t dataIndex { 0 };
uint8_t numColumns { 0 };
};
typedef std::unordered_map<uint32_t, std::vector<uint32_t>> MaterialMap;
typedef std::function<void(const ControllerKey &, const std::vector<float> &, ModelNode &)> ControllerFn;
std::unordered_map<uint32_t, ControllerFn> _genericControllers;
std::unordered_map<uint32_t, ControllerFn> _meshControllers;
std::unordered_map<uint32_t, ControllerFn> _lightControllers;
std::unordered_map<uint32_t, ControllerFn> _emitterControllers;
std::unique_ptr<StreamReader> _mdxReader;
bool _tsl { false }; /**< is this a TSL model? */
int _nodeIndex { 0 };
std::vector<std::string> _nodeNames;
std::unordered_map<uint32_t, int> _nodeFlags;
bool _readingAnimations { false };
std::map<uint16_t, uint16_t> _nodeFlags;
std::shared_ptr<graphics::Model> _model;
void doLoad() override;
ArrayDefinition readArrayDefinition();
void readNodeNames(const std::vector<uint32_t> &offsets);
std::unique_ptr<graphics::ModelNode> readNode(uint32_t offset, graphics::ModelNode *parent);
std::unique_ptr<graphics::ModelNode> readNode(uint32_t offset, const ModelNode *parent, bool anim = false);
std::vector<std::shared_ptr<graphics::Animation>> readAnimations(const std::vector<uint32_t> &offsets);
std::unique_ptr<graphics::Animation> readAnimation(uint32_t offset);
void readControllers(bool anim, uint32_t keyOffset, uint32_t keyCount, const std::vector<float> &data, graphics::ModelNode &node);
void readControllers(int nodeFlags, uint32_t keyOffset, uint32_t keyCount, const std::vector<float> &data, graphics::ModelNode &node);
void readLight(graphics::ModelNode &node);
void readEmitter(graphics::ModelNode &node);
void readReference(graphics::ModelNode &node);
void readMesh(graphics::ModelNode &node);
void readSkin(graphics::ModelNode &node);
void readDanglymesh(graphics::ModelNode &node);
void readAABB(graphics::ModelNode &node);
void readSaber(graphics::ModelNode &node);
std::shared_ptr<ModelNode::Reference> readReference();
std::shared_ptr<ModelNode::Light> readLight();
std::shared_ptr<ModelNode::Emitter> readEmitter();
std::shared_ptr<ModelNode::TriangleMesh> readMesh(int flags);
void loadMesh(const MeshHeader &header, std::vector<float> &&vertices, std::vector<uint16_t> &&indices, VertexAttributes &&attributes, MaterialMap &&materialFaces, graphics::ModelNode &node);
MeshHeader readMeshHeader();
std::shared_ptr<AABBNode> readAABBNode(uint32_t offset);
std::shared_ptr<ModelNode::AABBTree> readAABBTree(uint32_t offset);
// Controllers
void initControllerFn();
ControllerFn getControllerFn(uint32_t type, int nodeFlags);
static void readPositionController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readOrientationController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readScaleController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readAlphaController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSelfIllumColorController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readColorController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readRadiusController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readShadowRadiusController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readVerticalDisplacementController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readMultiplierController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readAlphaEndController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readAlphaStartController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readBirthrateController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readBounceCoController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readCombineTimeController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readDragController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readFPSController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readFrameEndController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readFrameStartController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readGravController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readLifeExpController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readMassController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readP2PBezier2Controller(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readP2PBezier3Controller(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readParticleRotController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readRandVelController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSizeStartController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSizeEndController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSizeStartYController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSizeEndYController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSpreadController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readThresholdController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readVelocityController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readXSizeController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readYSizeController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readBlurLengthController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readLightingDelayController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readLightingRadiusController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readLightingScaleController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readLightingSubDivController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readLightingZigZagController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readAlphaMidController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readPercentStartController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readPercentMidController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readPercentEndController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSizeMidController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readSizeMidYController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readRandomBirthRateController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readTargetSizeController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readNumControlPtsController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readControlPtRadiusController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readControlPtDelayController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readTangentSpreadController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readTangentLengthController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readColorMidController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readColorEndController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readColorStartController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readDetonateController(const ControllerKey &key, const std::vector<float> &data, ModelNode &node);
static void readFloatController(const ControllerKey &key, const std::vector<float> &data, AnimatedProperty<float> &prop);
static void readVectorController(const ControllerKey &key, const std::vector<float> &data, AnimatedProperty<glm::vec3> &prop);
// END Controllers
};
} // namespace graphics

View file

@ -0,0 +1,443 @@
/*
* Copyright (c) 2020-2021 The reone project contributors
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "mdlreader.h"
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include "glm/ext.hpp"
#include "../../common/collectionutil.h"
using namespace std;
using namespace reone::resource;
namespace reone {
namespace graphics {
static constexpr int kFlagBezier = 16;
void MdlReader::initControllerFn() {
_genericControllers = unordered_map<uint32_t, ControllerFn> {
{ 8, &readPositionController },
{ 20, &readOrientationController },
{ 36, &readScaleController }
};
_meshControllers = unordered_map<uint32_t, ControllerFn> {
{ 100, &readSelfIllumColorController },
{ 132, &readAlphaController }
};
_lightControllers = unordered_map<uint32_t, ControllerFn> {
{ 76, &readColorController },
{ 88, &readRadiusController },
{ 96, &readShadowRadiusController },
{ 100, &readVerticalDisplacementController },
{ 140, &readMultiplierController }
};
_emitterControllers = unordered_map<uint32_t, ControllerFn> {
{ 80, &readAlphaEndController },
{ 84, &readAlphaStartController },
{ 88, &readBirthrateController },
{ 92, &readBounceCoController },
{ 96, &readCombineTimeController },
{ 100, &readDragController },
{ 104, &readFPSController },
{ 108, &readFrameEndController },
{ 112, &readFrameStartController },
{ 116, &readGravController },
{ 120, &readLifeExpController },
{ 124, &readMassController },
{ 128, &readP2PBezier2Controller },
{ 132, &readP2PBezier3Controller },
{ 136, &readParticleRotController },
{ 140, &readRandVelController },
{ 144, &readSizeStartController },
{ 148, &readSizeEndController },
{ 152, &readSizeStartYController },
{ 156, &readSizeEndYController },
{ 160, &readSpreadController },
{ 164, &readThresholdController },
{ 168, &readVelocityController },
{ 172, &readXSizeController },
{ 176, &readYSizeController },
{ 180, &readBlurLengthController },
{ 184, &readLightingDelayController },
{ 188, &readLightingRadiusController },
{ 192, &readLightingScaleController },
{ 196, &readLightingSubDivController },
{ 200, &readLightingZigZagController },
{ 216, &readAlphaMidController },
{ 220, &readPercentStartController },
{ 224, &readPercentMidController },
{ 228, &readPercentEndController },
{ 232, &readSizeMidController },
{ 236, &readSizeMidYController },
{ 240, &readRandomBirthRateController },
{ 252, &readTargetSizeController },
{ 256, &readNumControlPtsController },
{ 260, &readControlPtRadiusController },
{ 264, &readControlPtDelayController },
{ 268, &readTangentSpreadController },
{ 272, &readTangentLengthController },
{ 284, &readColorMidController },
{ 380, &readColorEndController },
{ 392, &readColorStartController },
{ 502, &readDetonateController }
};
}
MdlReader::ControllerFn MdlReader::getControllerFn(uint32_t type, int nodeFlags) {
ControllerFn fn;
if (nodeFlags & NodeFlags::mesh) {
fn = getFromLookupOrNull(_meshControllers, type);
} else if (nodeFlags & NodeFlags::light) {
fn = getFromLookupOrNull(_lightControllers, type);
} else if (nodeFlags & NodeFlags::emitter) {
fn = getFromLookupOrNull(_emitterControllers, type);
}
if (!fn) {
fn = getFromLookupOrNull(_genericControllers, type);
}
return move(fn);
}
static inline void ensureNumColumnsEquals(int type, int expected, int actual) {
if (actual != expected) {
throw runtime_error(str(boost::format("Controller %d: number of columns is %d, expected %d") % type % actual % expected));
}
}
void MdlReader::readPositionController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
bool bezier = key.numColumns & kFlagBezier;
int numColumns = key.numColumns & ~kFlagBezier;
ensureNumColumnsEquals(key.type, 3, numColumns);
for (uint16_t i = 0; i < key.numRows; ++i) {
int rowTimeIdx = key.timeIndex + i;
int rowDataIdx = key.dataIndex + (bezier ? 9 : 3) * i;
float time = data[rowTimeIdx];
glm::vec3 position(glm::make_vec3(&data[rowDataIdx]));
node.position().addFrame(time, move(position));
}
node.position().update();
}
void MdlReader::readOrientationController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
switch (key.numColumns) {
case 2:
for (uint16_t i = 0; i < key.numRows; ++i) {
int rowTimeIdx = key.timeIndex + i;
int rowDataIdx = key.dataIndex + i;
uint32_t temp = *reinterpret_cast<const uint32_t *>(&data[rowDataIdx]);
float x = 1.0f - static_cast<float>(temp & 0x7ff) / 1023.0f;
float y = 1.0f - static_cast<float>((temp >> 11) & 0x7ff) / 1023.0f;
float z = 1.0f - static_cast<float>(temp >> 22) / 511.0f;
float dot = x * x + y * y + z * z;
float w;
if (dot >= 1.0f) {
float len = glm::sqrt(dot);
x /= len;
y /= len;
z /= len;
w = 0.0f;
} else {
w = -glm::sqrt(1.0f - dot);
}
float time = data[rowTimeIdx];
glm::quat orientation(w, x, y, z);
node.orientation().addFrame(time, move(orientation));
}
break;
case 4:
for (uint16_t i = 0; i < key.numRows; ++i) {
int rowTimeIdx = key.timeIndex + i;
int rowDataIdx = key.dataIndex + 4 * i;
float time = data[rowTimeIdx];
float x = data[rowDataIdx + 0];
float y = data[rowDataIdx + 1];
float z = data[rowDataIdx + 2];
float w = data[rowDataIdx + 3];
glm::quat orientation(w, x, y, z);
node.orientation().addFrame(time, move(orientation));
}
break;
default:
throw runtime_error("Unexpected number of columns: " + to_string(key.numColumns));
}
node.orientation().update();
}
void MdlReader::readFloatController(const ControllerKey &key, const vector<float> &data, AnimatedProperty<float> &prop) {
ensureNumColumnsEquals(key.type, 1, key.numColumns);
for (uint16_t i = 0; i < key.numRows; ++i) {
float time = data[key.timeIndex + i];
float value = data[key.dataIndex + i];
prop.addFrame(time, value);
}
prop.update();
}
void MdlReader::readVectorController(const ControllerKey &key, const vector<float> &data, AnimatedProperty<glm::vec3> &prop) {
bool bezier = key.numColumns & kFlagBezier;
int numColumns = key.numColumns & ~kFlagBezier;
ensureNumColumnsEquals(key.type, 3, numColumns);
for (uint16_t i = 0; i < key.numRows; ++i) {
float time = data[key.timeIndex + i];
glm::vec3 value(glm::make_vec3(&data[key.dataIndex + (bezier ? 9 : 3) * i]));
prop.addFrame(time, value);
}
prop.update();
}
void MdlReader::readScaleController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.scale());
}
void MdlReader::readSelfIllumColorController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readVectorController(key, data, node.selfIllumColor());
}
void MdlReader::readAlphaController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.alpha());
}
void MdlReader::readColorController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readVectorController(key, data, node.color());
}
void MdlReader::readRadiusController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.radius());
}
void MdlReader::readShadowRadiusController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.shadowRadius());
}
void MdlReader::readVerticalDisplacementController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.verticalDisplacement());
}
void MdlReader::readMultiplierController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.multiplier());
}
void MdlReader::readAlphaEndController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.alphaEnd());
}
void MdlReader::readAlphaStartController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.alphaStart());
}
void MdlReader::readBirthrateController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.birthrate());
}
void MdlReader::readBounceCoController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.bounceCo());
}
void MdlReader::readCombineTimeController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.combineTime());
}
void MdlReader::readDragController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.drag());
}
void MdlReader::readFPSController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.fps());
}
void MdlReader::readFrameEndController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.frameEnd());
}
void MdlReader::readFrameStartController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.frameStart());
}
void MdlReader::readGravController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.grav());
}
void MdlReader::readLifeExpController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.lifeExp());
}
void MdlReader::readMassController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.mass());
}
void MdlReader::readP2PBezier2Controller(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.p2pBezier2());
}
void MdlReader::readP2PBezier3Controller(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.p2pBezier3());
}
void MdlReader::readParticleRotController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.particleRot());
}
void MdlReader::readRandVelController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.randVel());
}
void MdlReader::readSizeStartController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.sizeStart());
}
void MdlReader::readSizeEndController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.sizeEnd());
}
void MdlReader::readSizeStartYController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.sizeStartY());
}
void MdlReader::readSizeEndYController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.sizeEndY());
}
void MdlReader::readSpreadController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.spread());
}
void MdlReader::readThresholdController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.threshold());
}
void MdlReader::readVelocityController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.velocity());
}
void MdlReader::readXSizeController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.xSize());
}
void MdlReader::readYSizeController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.ySize());
}
void MdlReader::readBlurLengthController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.blurLength());
}
void MdlReader::readLightingDelayController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.lightingDelay());
}
void MdlReader::readLightingRadiusController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.lightingRadius());
}
void MdlReader::readLightingScaleController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.lightingScale());
}
void MdlReader::readLightingSubDivController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.lightingSubDiv());
}
void MdlReader::readLightingZigZagController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.lightingZigZag());
}
void MdlReader::readAlphaMidController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.alphaMid());
}
void MdlReader::readPercentStartController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.percentStart());
}
void MdlReader::readPercentMidController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.percentMid());
}
void MdlReader::readPercentEndController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.percentEnd());
}
void MdlReader::readSizeMidController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.sizeMid());
}
void MdlReader::readSizeMidYController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.sizeMidY());
}
void MdlReader::readRandomBirthRateController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.randomBirthRate());
}
void MdlReader::readTargetSizeController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.targetSize());
}
void MdlReader::readNumControlPtsController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.numControlPts());
}
void MdlReader::readControlPtRadiusController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.controlPtRadius());
}
void MdlReader::readControlPtDelayController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.controlPtDelay());
}
void MdlReader::readTangentSpreadController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.tangentSpread());
}
void MdlReader::readTangentLengthController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.tangentLength());
}
void MdlReader::readColorMidController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readVectorController(key, data, node.colorMid());
}
void MdlReader::readColorEndController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readVectorController(key, data, node.colorEnd());
}
void MdlReader::readColorStartController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readVectorController(key, data, node.colorStart());
}
void MdlReader::readDetonateController(const ControllerKey &key, const vector<float> &data, ModelNode &node) {
readFloatController(key, data, node.detonate());
}
} // namespace graphics
} // namespace reone

View file

@ -31,37 +31,64 @@ namespace graphics {
Model::Model(
string name,
Classification classification,
float animationScale,
shared_ptr<Model> superModel,
shared_ptr<ModelNode> rootNode,
vector<shared_ptr<Animation>> animations,
shared_ptr<Model> superModel
float animationScale
) :
_name(move(name)),
_classification(classification),
_animationScale(animationScale),
_superModel(move(superModel)),
_rootNode(rootNode),
_superModel(move(superModel)) {
_animationScale(animationScale) {
if (!rootNode) {
throw invalid_argument("rootNode must not be null");
}
for (auto &anim : animations) {
_animations.insert(make_pair(anim->name(), move(anim)));
}
initInternal(_rootNode);
fillNodeLookups(_rootNode);
fillBoneNodeId();
computeAABB();
}
void Model::initInternal(const shared_ptr<ModelNode> &node) {
_nodeByNumber.insert(make_pair(node->nodeNumber(), node));
void Model::fillNodeLookups(const shared_ptr<ModelNode> &node) {
_nodes.push_back(node);
_nodeById.insert(make_pair(node->id(), node));
_nodeByName.insert(make_pair(node->name(), node));
shared_ptr<ModelNode::Trimesh> mesh(node->mesh());
if (mesh) {
_aabb.expand(mesh->mesh->aabb() * node->absoluteTransform());
}
for (auto &child : node->children()) {
initInternal(child);
fillNodeLookups(child);
}
}
void Model::fillBoneNodeId() {
// In MDL files, bones reference node serial numbers (DFS ordering).
// We want them to reference node identifiers, for simplicity.
for (auto &node : _nodes) {
if (!node->isSkinMesh()) continue;
shared_ptr<ModelNode::TriangleMesh> mesh(node->mesh());
mesh->skin->boneNodeId.resize(mesh->skin->boneNodeSerial.size());
for (size_t i = 0; i < mesh->skin->boneNodeSerial.size(); ++i) {
uint16_t nodeSerial = mesh->skin->boneNodeSerial[i];
if (nodeSerial < static_cast<int>(_nodes.size())) {
mesh->skin->boneNodeId[i] = _nodes[nodeSerial]->id();
} else {
mesh->skin->boneNodeId[i] = 0xffff;
}
}
}
}
void Model::computeAABB() {
_aabb.reset();
for (auto &node : _nodeById) {
shared_ptr<ModelNode::TriangleMesh> mesh(node.second->mesh());
if (mesh) {
_aabb.expand(mesh->mesh->aabb() * node.second->absoluteTransform());
}
}
}
@ -69,6 +96,10 @@ void Model::init() {
_rootNode->init();
}
void Model::addAnimation(shared_ptr<Animation> animation) {
_animations.insert(make_pair(animation->name(), move(animation)));
}
vector<string> Model::getAnimationNames() const {
vector<string> result;
@ -100,17 +131,13 @@ shared_ptr<Animation> Model::getAnimation(const string &name) const {
return move(anim);
}
shared_ptr<ModelNode> Model::findNodeByNumber(uint16_t number) const {
return getFromLookupOrNull(_nodeByNumber, number);
}
shared_ptr<ModelNode> Model::findNodeByName(const string &name) const {
shared_ptr<ModelNode> Model::getNodeByName(const string &name) const {
return getFromLookupOrNull(_nodeByName, name);
}
shared_ptr<ModelNode> Model::findAABBNode() const {
for (auto &node : _nodeByNumber) {
if (node.second->isAABB()) return node.second;
shared_ptr<ModelNode> Model::getAABBNode() const {
for (auto &node : _nodeById) {
if (node.second->isAABBMesh()) return node.second;
}
return nullptr;
}

View file

@ -32,11 +32,10 @@ namespace reone {
namespace graphics {
/**
* Tree-like data structure, representing a 3D model. Contains model nodes
* and animations. Models are cached and reused between model scene nodes.
* 3D model, a tree-like data structure. Contains model nodes and animations.
*
* @see reone::graphics::ModelNode
* @see reone::graphics::Animation
* @see ModelNode
* @see Animation
*/
class Model : boost::noncopyable {
public:
@ -54,20 +53,20 @@ public:
Model(
std::string name,
Classification classification,
float animationScale,
std::shared_ptr<Model> superModel,
std::shared_ptr<ModelNode> rootNode,
std::vector<std::shared_ptr<Animation>> animations,
std::shared_ptr<Model> superModel = nullptr);
float animationScale);
void init();
void addAnimation(std::shared_ptr<Animation> animation);
bool isAffectedByFog() const { return _affectedByFog; }
std::vector<std::string> getAnimationNames() const;
std::shared_ptr<Animation> getAnimation(const std::string &name) const;
std::shared_ptr<ModelNode> findNodeByNumber(uint16_t number) const;
std::shared_ptr<ModelNode> findNodeByName(const std::string &name) const;
std::shared_ptr<ModelNode> findAABBNode() const;
std::shared_ptr<ModelNode> getNodeByName(const std::string &name) const;
std::shared_ptr<ModelNode> getAABBNode() const;
const std::string &name() const { return _name; }
Classification classification() const { return _classification; }
@ -81,19 +80,20 @@ public:
private:
std::string _name;
Classification _classification;
float _animationScale;
std::shared_ptr<Model> _superModel;
std::shared_ptr<ModelNode> _rootNode;
std::unordered_map<std::string, std::shared_ptr<Animation>> _animations;
std::shared_ptr<Model> _superModel;
float _animationScale;
bool _affectedByFog;
std::unordered_map<uint16_t, std::shared_ptr<ModelNode>> _nodeByNumber;
std::vector<std::shared_ptr<ModelNode>> _nodes;
std::unordered_map<uint16_t, std::shared_ptr<ModelNode>> _nodeById;
std::unordered_map<std::string, std::shared_ptr<ModelNode>> _nodeByName;
AABB _aabb;
bool _affectedByFog { false };
void initInternal(const std::shared_ptr<ModelNode> &node);
friend class MdlReader;
void fillNodeLookups(const std::shared_ptr<ModelNode> &node);
void fillBoneNodeId();
void computeAABB();
};
} // namespace graphics

View file

@ -17,17 +17,46 @@
#include "modelnode.h"
#include "glm/gtx/matrix_decompose.hpp"
#include "../../common/log.h"
#include "glm/gtx/transform.hpp"
using namespace std;
namespace reone {
namespace graphics {
ModelNode::ModelNode(int index, const ModelNode *parent) : _index(index), _parent(parent) {
ModelNode::ModelNode(
uint16_t id,
string name,
glm::vec3 restPosition,
glm::quat restOrientation,
const ModelNode *parent
) :
_id(id),
_name(move(name)),
_restPosition(move(restPosition)),
_restOrientation(move(restOrientation)),
_parent(parent) {
computeLocalTransform();
computeAbsoluteTransform();
}
void ModelNode::computeLocalTransform() {
_localTransform = glm::mat4(1.0f);
_localTransform *= glm::translate(_restPosition);
_localTransform *= glm::mat4_cast(_restOrientation);
}
void ModelNode::computeAbsoluteTransform() {
if (_parent) {
_absTransform = _parent->_absTransform * _localTransform;
} else {
_absTransform = _localTransform;
}
_absTransformInv = glm::inverse(_absTransform);
}
void ModelNode::init() {
@ -39,95 +68,58 @@ void ModelNode::init() {
}
}
void ModelNode::addChild(shared_ptr<ModelNode> child) {
child->_parent = this;
void ModelNode::addChild(std::shared_ptr<ModelNode> child) {
_children.push_back(move(child));
}
void ModelNode::computeLocalTransforms() {
if (_parent) {
_localTransform = glm::inverse(_parent->_absTransform) * _absTransform;
_absTransform = _parent->_absTransform * _localTransform;
_absTransformInv = glm::inverse(_absTransform);
// Extract position and orientation for use in animations.
glm::vec3 scale, skew;
glm::vec4 perspective;
glm::decompose(_localTransform, scale, _orientation, _position, skew, perspective);
} else {
_localTransform = _absTransform;
}
_absTransform = _parent ? _parent->_absTransform * _localTransform : _localTransform;
_absTransformInv = glm::inverse(_absTransform);
for (auto &child : _children) {
child->computeLocalTransforms();
}
}
void ModelNode::computeAbsoluteTransforms() {
_absTransform = _parent ? _parent->_absTransform * _localTransform : _localTransform;
_absTransformInv = glm::inverse(_absTransform);
for (auto &child : _children) {
child->computeAbsoluteTransforms();
}
}
bool ModelNode::getPosition(int leftFrameIdx, int rightFrameIdx, float factor, glm::vec3 &position) const {
if (leftFrameIdx < 0 || leftFrameIdx >= static_cast<int>(_positions.getNumKeyframes()) ||
rightFrameIdx < 0 || rightFrameIdx >= static_cast<int>(_positions.getNumKeyframes())) return false;
if (leftFrameIdx < 0 || leftFrameIdx >= static_cast<int>(_position.getNumFrames()) ||
rightFrameIdx < 0 || rightFrameIdx >= static_cast<int>(_position.getNumFrames())) return false;
if (leftFrameIdx == rightFrameIdx) {
position = _positions.getByKeyframe(leftFrameIdx);
position = _position.getByFrame(leftFrameIdx);
} else {
position = glm::mix(
_positions.getByKeyframe(leftFrameIdx),
_positions.getByKeyframe(rightFrameIdx),
_position.getByFrame(leftFrameIdx),
_position.getByFrame(rightFrameIdx),
factor);
}
return true;
}
bool ModelNode::getOrientation(int leftFrameIdx, int rightFrameIdx, float interpolant, glm::quat &orientation) const {
if (leftFrameIdx < 0 || leftFrameIdx >= static_cast<int>(_orientations.getNumKeyframes()) ||
rightFrameIdx < 0 || rightFrameIdx >= static_cast<int>(_orientations.getNumKeyframes())) return false;
bool ModelNode::getOrientation(int leftFrameIdx, int rightFrameIdx, float factor, glm::quat &orientation) const {
if (leftFrameIdx < 0 || leftFrameIdx >= static_cast<int>(_orientation.getNumFrames()) ||
rightFrameIdx < 0 || rightFrameIdx >= static_cast<int>(_orientation.getNumFrames())) return false;
if (leftFrameIdx == rightFrameIdx) {
orientation = _orientations.getByKeyframe(leftFrameIdx);
orientation = _orientation.getByFrame(leftFrameIdx);
} else {
orientation = glm::slerp(
_orientations.getByKeyframe(leftFrameIdx),
_orientations.getByKeyframe(rightFrameIdx),
interpolant);
_orientation.getByFrame(leftFrameIdx),
_orientation.getByFrame(rightFrameIdx),
factor);
}
return true;
}
bool ModelNode::getScale(int leftFrameIdx, int rightFrameIdx, float interpolant, float &scale) const {
if (leftFrameIdx < 0 || leftFrameIdx >= static_cast<int>(_scales.getNumKeyframes()) ||
rightFrameIdx < 0 || rightFrameIdx >= static_cast<int>(_scales.getNumKeyframes())) return false;
bool ModelNode::getScale(int leftFrameIdx, int rightFrameIdx, float factor, float &scale) const {
if (leftFrameIdx < 0 || leftFrameIdx >= static_cast<int>(_scale.getNumFrames()) ||
rightFrameIdx < 0 || rightFrameIdx >= static_cast<int>(_scale.getNumFrames())) return false;
if (leftFrameIdx == rightFrameIdx) {
scale = _scales.getByKeyframe(leftFrameIdx);
scale = _scale.getByFrame(leftFrameIdx);
} else {
scale = glm::mix(
_scales.getByKeyframe(leftFrameIdx),
_scales.getByKeyframe(rightFrameIdx),
interpolant);
_scale.getByFrame(leftFrameIdx),
_scale.getByFrame(rightFrameIdx),
factor);
}
return true;
}
const glm::vec3 &ModelNode::getCenterOfAABB() const {
return _mesh->mesh->aabb().center();
}
vector<uint32_t> ModelNode::getFacesByMaterial(uint32_t material) const {
if (!_mesh || _mesh->materialFaces.count(material) == 0) return vector<uint32_t>();

View file

@ -25,34 +25,102 @@
#include "glm/gtx/quaternion.hpp"
#include "../mesh/mesh.h"
#include "../texture/texture.h"
#include "aabbnode.h"
#include "animatedproperty.h"
#include "emitter.h"
#include "lensflare.h"
namespace reone {
namespace graphics {
#define DECL_ANIM_PROP(a, b, c) \
const AnimatedProperty<a> &b() const { return c; }; \
AnimatedProperty<a> &b() { return c; };
class Model;
/**
* Node of a 3D model or an animation, which are tree-like data structures.
* Model nodes have spatial properties and can have an arbitary number of
* children.
*
* When part of an animation, certain properties of a model node can be
* animated. These are position, orientation, scale, alpha and self-illumination
* color.
*
* Model nodes can be specialized to represent meshes, lights, emitters, etc.
*
* @see reone::graphics::Model
* @see reone::graphics::Animation
* Model or animation node. Can be specialized to represent a triangle mesh, a light, an emitter and etc.
*/
class ModelNode : boost::noncopyable {
public:
struct Skin {
std::vector<uint16_t> boneNodeSerial; /**< temporary, used to fill boneNodeId below */
std::vector<uint16_t> boneNodeId; /**< used in skeletal animation */
};
struct UVAnimation {
glm::vec2 dir { 0.0f };
};
struct DanglyMeshConstraint {
float multiplier { 0.0f };
glm::vec3 position { 0.0f };
};
struct DanglyMesh {
float displacement { 0.0f };
float tightness { 0.0f };
float period { 0.0f };
std::vector<DanglyMeshConstraint> constraints;
};
struct AABBTree {
enum class Plane {
None = 0,
PositiveX = 1,
PositiveY = 2,
PositiveZ = 4
};
int faceIndex { 0 };
Plane mostSignificantPlane { Plane::None };
AABB aabb;
std::shared_ptr<AABBTree> left;
std::shared_ptr<AABBTree> right;
};
struct TriangleMesh {
std::shared_ptr<Mesh> mesh;
std::unordered_map<uint32_t, std::vector<uint32_t>> materialFaces;
UVAnimation uvAnimation;
glm::vec3 diffuse { 1.0f };
glm::vec3 ambient { 1.0f };
int transparency { 0 };
// Flags
bool render { false };
bool shadow { false };
bool backgroundGeometry { false };
// END Flags
// Textures
std::shared_ptr<Texture> diffuseMap;
std::shared_ptr<Texture> lightmap;
std::shared_ptr<Texture> bumpmap;
// END Textures
// Specialization
std::shared_ptr<Skin> skin;
std::shared_ptr<DanglyMesh> danglyMesh;
std::shared_ptr<AABBTree> aabbTree;
bool saber { false };
// END Specialization
};
struct LensFlare {
std::shared_ptr<Texture> texture;
glm::vec3 colorShift { 0.0f };
float position { 0.0f };
float size { 0.0f };
};
struct Light {
int priority { 0 };
int dynamicType { 0 };
@ -63,181 +131,284 @@ public:
std::vector<LensFlare> flares;
};
struct Emitter {
enum class UpdateMode {
Invalid,
Fountain,
Single,
Explosion
};
enum class RenderMode {
Invalid = 0,
Normal = 1,
BillboardToWorldZ = 2,
MotionBlur = 3,
BillboardToLocalZ = 4,
AlignedToParticleDir = 5
};
enum class BlendMode {
Invalid,
Normal,
Punch,
Lighten
};
template <class T>
struct Constraints {
T start;
T mid;
T end;
};
UpdateMode updateMode { UpdateMode::Invalid };
RenderMode renderMode { RenderMode::Invalid };
BlendMode blendMode { BlendMode::Invalid };
int renderOrder { 0 };
std::shared_ptr<Texture> texture;
int gridWidth { 0 };
int gridHeight { 0 };
int frameStart { 0 };
int frameEnd { 0 };
glm::vec2 size { 0.0f };
float birthrate { 0.0f }; /**< rate of particle birth per second */
float lifeExpectancy { 0.0f }; /**< life of each particle in seconds */
float velocity { 0.0f };
float randomVelocity { 0.0f };
float spread { 0.0f };
bool loop { false };
float fps { 0.0f };
Constraints<float> particleSize;
Constraints<glm::vec3> color;
Constraints<float> alpha;
};
struct Reference {
std::shared_ptr<Model> model;
bool reattachable { false };
};
struct Skin {
std::unordered_map<uint16_t, uint16_t> nodeIdxByBoneIdx;
};
struct DanglymeshConstraint {
float multiplier { 0.0f };
glm::vec3 position { 0.0f };
};
struct Danglymesh {
float displacement { 0.0f };
float tightness { 0.0f };
float period { 0.0f };
std::vector<DanglymeshConstraint> constraints;
};
struct UVAnimation {
bool animated { false };
float directionX { 0.0f };
float directionY { 0.0f };
};
struct Trimesh {
std::shared_ptr<Mesh> mesh;
std::unordered_map<uint32_t, std::vector<uint32_t>> materialFaces;
UVAnimation uvAnimation;
int transparency { 0 };
glm::vec3 ambientColor { 1.0f };
glm::vec3 diffuseColor { 0.0f };
bool render { false };
bool shadow { false };
bool backgroundGeometry { false };
bool bumpmapSwizzled { false };
std::shared_ptr<Texture> diffuse;
std::shared_ptr<Texture> lightmap;
std::shared_ptr<Texture> bumpmap;
};
ModelNode(int index, const ModelNode *parent = nullptr);
ModelNode(
uint16_t id,
std::string name,
glm::vec3 restPosition,
glm::quat restOrientation,
const ModelNode *parent = nullptr);
void init();
/**
* Adds the specified node to the list of this nodes children. Also sets the
* specified nodes parent pointer to this node.
*/
void addChild(std::shared_ptr<ModelNode> node);
void addChild(std::shared_ptr<ModelNode> child);
/**
* Recursively computes the local transform, position and orientation of
* this node and its children. Absolute transform must be set prior to
* calling this method.
*/
void computeLocalTransforms();
std::vector<uint32_t> getFacesByMaterial(uint32_t material) const;
/**
* Recursively computes the absolute transform of this node and its
* children. Local transform must be set prior to calling this method.
*/
void computeAbsoluteTransforms();
uint16_t id() const { return _id; }
const std::string &name() const { return _name; }
uint16_t flags() const { return _flags; }
const ModelNode *parent() const { return _parent; }
const std::vector<std::shared_ptr<ModelNode>> &children() const { return _children; }
bool isAABB() const { return static_cast<bool>(_aabb); }
bool isSaber() const { return _saber; }
void setFlags(uint16_t flags) { _flags = flags; }
// Transformations
const glm::vec3 &restPosition() const { return _restPosition; }
const glm::quat &restOrientation() const { return _restOrientation; }
const glm::mat4 &localTransform() const { return _localTransform; }
const glm::mat4 &absoluteTransform() const { return _absTransform; }
const glm::mat4 &absoluteTransformInverse() const { return _absTransformInv; }
// END Transformations
// Specialization
bool isSkinMesh() const { return _mesh && _mesh->skin; }
bool isDanglyMesh() const { return _mesh && _mesh->danglyMesh; }
bool isAABBMesh() const { return _mesh && _mesh->aabbTree; }
bool isSaberMesh() const { return _mesh && _mesh->saber; }
std::shared_ptr<TriangleMesh> mesh() const { return _mesh; }
std::shared_ptr<Light> light() const { return _light; }
std::shared_ptr<Emitter> emitter() const { return _emitter; }
std::shared_ptr<Reference> reference() const { return _reference; }
void setMesh(std::shared_ptr<TriangleMesh> mesh) { _mesh = std::move(mesh); }
void setLight(std::shared_ptr<Light> light) { _light = std::move(light); }
void setEmitter(std::shared_ptr<Emitter> emitter) { _emitter = std::move(emitter); }
void setReference(std::shared_ptr<Reference> reference) { _reference = std::move(reference); }
// END Specialization
// Keyframes
bool getPosition(int leftFrameIdx, int rightFrameIdx, float factor, glm::vec3 &position) const;
bool getOrientation(int leftFrameIdx, int rightFrameIdx, float factor, glm::quat &orientation) const;
bool getScale(int leftFrameIx, int rightFrameIdx, float factor, float &scale) const;
const glm::vec3 &getCenterOfAABB() const;
const AnimatedProperty<glm::quat, SlerpInterpolator> &orientation() const { return _orientation; }
AnimatedProperty<glm::quat, SlerpInterpolator> &orientation() { return _orientation; }
std::vector<uint32_t> getFacesByMaterial(uint32_t material) const;
DECL_ANIM_PROP(glm::vec3, position, _position)
DECL_ANIM_PROP(float, scale, _scale)
int index() const { return _index; }
const ModelNode *parent() const { return _parent; }
uint16_t nodeNumber() const { return _nodeNumber; }
const std::string &name() const { return _name; }
const std::vector<std::shared_ptr<ModelNode>> &children() const { return _children; }
DECL_ANIM_PROP(glm::vec3, selfIllumColor, _selfIllumColor)
DECL_ANIM_PROP(float, alpha, _alpha)
// Transformation
DECL_ANIM_PROP(glm::vec3, color, _color)
DECL_ANIM_PROP(float, radius, _radius)
DECL_ANIM_PROP(float, shadowRadius, _shadowRadius)
DECL_ANIM_PROP(float, verticalDisplacement, _verticalDisplacement)
DECL_ANIM_PROP(float, multiplier, _multiplier)
const glm::vec3 &position() const { return _position; }
const glm::quat &orientation() const { return _orientation; }
const glm::mat4 &localTransform() const { return _localTransform; }
const glm::mat4 &absoluteTransform() const { return _absTransform; }
const glm::mat4 &absoluteTransformInverse() const { return _absTransformInv; }
DECL_ANIM_PROP(float, alphaEnd, _alphaEnd)
DECL_ANIM_PROP(float, alphaStart, _alphaStart)
DECL_ANIM_PROP(float, birthrate, _birthrate)
DECL_ANIM_PROP(float, bounceCo, _bounceCo)
DECL_ANIM_PROP(float, combineTime, _combineTime)
DECL_ANIM_PROP(float, drag, _drag)
DECL_ANIM_PROP(float, fps, _fps)
DECL_ANIM_PROP(float, frameEnd, _frameEnd)
DECL_ANIM_PROP(float, frameStart, _frameStart)
DECL_ANIM_PROP(float, grav, _grav)
DECL_ANIM_PROP(float, lifeExp, _lifeExp)
DECL_ANIM_PROP(float, mass, _mass)
DECL_ANIM_PROP(float, p2pBezier2, _p2pBezier2)
DECL_ANIM_PROP(float, p2pBezier3, _p2pBezier3)
DECL_ANIM_PROP(float, particleRot, _particleRot)
DECL_ANIM_PROP(float, randVel, _randVel)
DECL_ANIM_PROP(float, sizeStart, _sizeStart)
DECL_ANIM_PROP(float, sizeEnd, _sizeEnd)
DECL_ANIM_PROP(float, sizeStartY, _sizeStartY)
DECL_ANIM_PROP(float, sizeEndY, _sizeEndY)
DECL_ANIM_PROP(float, spread, _spread)
DECL_ANIM_PROP(float, threshold, _threshold)
DECL_ANIM_PROP(float, velocity, _velocity)
DECL_ANIM_PROP(float, xSize, _xSize)
DECL_ANIM_PROP(float, ySize, _ySize)
DECL_ANIM_PROP(float, blurLength, _blurLength)
DECL_ANIM_PROP(float, lightingDelay, _lightingDelay)
DECL_ANIM_PROP(float, lightingRadius, _lightingRadius)
DECL_ANIM_PROP(float, lightingScale, _lightingScale)
DECL_ANIM_PROP(float, lightingSubDiv, _lightingSubDiv)
DECL_ANIM_PROP(float, lightingZigZag, _lightingZigZag)
DECL_ANIM_PROP(float, alphaMid, _alphaMid)
DECL_ANIM_PROP(float, percentStart, _percentStart)
DECL_ANIM_PROP(float, percentMid, _percentMid)
DECL_ANIM_PROP(float, percentEnd, _percentEnd)
DECL_ANIM_PROP(float, sizeMid, _sizeMid)
DECL_ANIM_PROP(float, sizeMidY, _sizeMidY)
DECL_ANIM_PROP(float, randomBirthRate, _randomBirthRate)
DECL_ANIM_PROP(float, targetSize, _targetSize)
DECL_ANIM_PROP(float, numControlPts, _numControlPts)
DECL_ANIM_PROP(float, controlPtRadius, _controlPtRadius)
DECL_ANIM_PROP(float, controlPtDelay, _controlPtDelay)
DECL_ANIM_PROP(float, tangentSpread, _tangentSpread)
DECL_ANIM_PROP(float, tangentLength, _tangentLength)
DECL_ANIM_PROP(glm::vec3, colorMid, _colorMid)
DECL_ANIM_PROP(glm::vec3, colorEnd, _colorEnd)
DECL_ANIM_PROP(glm::vec3, colorStart, _colorStart)
DECL_ANIM_PROP(float, detonate, _detonate)
// END Transformation
// Components
std::shared_ptr<Light> light() const { return _light; }
std::shared_ptr<Emitter> emitter() const { return _emitter; }
std::shared_ptr<Reference> reference() const { return _reference; }
std::shared_ptr<Trimesh> mesh() const { return _mesh; }
std::shared_ptr<Skin> skin() const { return _skin; }
std::shared_ptr<Danglymesh> danglymesh() const { return _danglymesh; }
std::shared_ptr<AABBNode> aabb() const { return _aabb; }
// END Components
// Animation
const AnimatedProperty<glm::vec3> &positions() const { return _positions; }
AnimatedProperty<glm::vec3> &positions() { return _positions; }
const AnimatedProperty<glm::quat, SlerpInterpolator> &orientations() const { return _orientations; }
AnimatedProperty<glm::quat, SlerpInterpolator> &orientations() { return _orientations; }
const AnimatedProperty<float> &scales() const { return _scales; }
AnimatedProperty<float> &scales() { return _scales; }
const AnimatedProperty<float> &alphas() const { return _alphas; }
AnimatedProperty<float> &alphas() { return _alphas; }
const AnimatedProperty<glm::vec3> &selfIllumColors() const { return _selfIllumColors; }
AnimatedProperty<glm::vec3> &selfIllumColors() { return _selfIllumColors; }
const AnimatedProperty<glm::vec3> &lightColors() const { return _lightColors; }
AnimatedProperty<glm::vec3> &lightColors() { return _lightColors; }
const AnimatedProperty<float> &lightMultipliers() const { return _lightMultipliers; }
AnimatedProperty<float> &lightMultipliers() { return _lightMultipliers; }
const AnimatedProperty<float> &lightRadii() const { return _lightRadii; }
AnimatedProperty<float> &lightRadii() { return _lightRadii; }
// END Animation
// END Keyframes
private:
int _index;
uint16_t _id; /**< node identifier, matches id within supermodel */
std::string _name;
const ModelNode *_parent;
uint16_t _flags { 0 };
uint16_t _nodeNumber { 0 };
std::string _name;
std::vector<std::shared_ptr<ModelNode>> _children;
// Transformation
// Transformations
glm::vec3 _position { 0.0f };
glm::quat _orientation { 1.0f, 0.0f, 0.0f, 0.0f };
glm::vec3 _restPosition { 0.0f };
glm::quat _restOrientation { 1.0f, 0.0f, 0.0f, 0.0f };
glm::mat4 _localTransform { 1.0f };
glm::mat4 _absTransform { 1.0f };
glm::mat4 _absTransformInv { 1.0f };
// END Transformation
// END Transformations
// Components
// Specialization
std::shared_ptr<TriangleMesh> _mesh;
std::shared_ptr<Light> _light;
std::shared_ptr<Emitter> _emitter;
std::shared_ptr<Reference> _reference;
std::shared_ptr<Trimesh> _mesh;
std::shared_ptr<Skin> _skin;
std::shared_ptr<Danglymesh> _danglymesh;
std::shared_ptr<AABBNode> _aabb;
bool _saber { false };
// END Components
// END Specialization
// Animation
// Keyframes
AnimatedProperty<glm::vec3> _positions;
AnimatedProperty<glm::quat, SlerpInterpolator> _orientations;
AnimatedProperty<float> _scales;
AnimatedProperty<float> _alphas;
AnimatedProperty<glm::vec3> _selfIllumColors;
AnimatedProperty<glm::vec3> _lightColors;
AnimatedProperty<float> _lightMultipliers;
AnimatedProperty<float> _lightRadii;
AnimatedProperty<glm::vec3> _position;
AnimatedProperty<glm::quat, SlerpInterpolator> _orientation;
AnimatedProperty<float> _scale;
// END Animation
AnimatedProperty<glm::vec3> _selfIllumColor;
AnimatedProperty<float> _alpha;
friend class MdlReader;
AnimatedProperty<glm::vec3> _color;
AnimatedProperty<float> _radius;
AnimatedProperty<float> _shadowRadius;
AnimatedProperty<float> _verticalDisplacement;
AnimatedProperty<float> _multiplier;
AnimatedProperty<float> _alphaEnd;
AnimatedProperty<float> _alphaStart;
AnimatedProperty<float> _birthrate;
AnimatedProperty<float> _bounceCo;
AnimatedProperty<float> _combineTime;
AnimatedProperty<float> _drag;
AnimatedProperty<float> _fps;
AnimatedProperty<float> _frameEnd;
AnimatedProperty<float> _frameStart;
AnimatedProperty<float> _grav;
AnimatedProperty<float> _lifeExp;
AnimatedProperty<float> _mass;
AnimatedProperty<float> _p2pBezier2;
AnimatedProperty<float> _p2pBezier3;
AnimatedProperty<float> _particleRot;
AnimatedProperty<float> _randVel;
AnimatedProperty<float> _sizeStart;
AnimatedProperty<float> _sizeEnd;
AnimatedProperty<float> _sizeStartY;
AnimatedProperty<float> _sizeEndY;
AnimatedProperty<float> _spread;
AnimatedProperty<float> _threshold;
AnimatedProperty<float> _velocity;
AnimatedProperty<float> _xSize;
AnimatedProperty<float> _ySize;
AnimatedProperty<float> _blurLength;
AnimatedProperty<float> _lightingDelay;
AnimatedProperty<float> _lightingRadius;
AnimatedProperty<float> _lightingScale;
AnimatedProperty<float> _lightingSubDiv;
AnimatedProperty<float> _lightingZigZag;
AnimatedProperty<float> _alphaMid;
AnimatedProperty<float> _percentStart;
AnimatedProperty<float> _percentMid;
AnimatedProperty<float> _percentEnd;
AnimatedProperty<float> _sizeMid;
AnimatedProperty<float> _sizeMidY;
AnimatedProperty<float> _randomBirthRate;
AnimatedProperty<float> _targetSize;
AnimatedProperty<float> _numControlPts;
AnimatedProperty<float> _controlPtRadius;
AnimatedProperty<float> _controlPtDelay;
AnimatedProperty<float> _tangentSpread;
AnimatedProperty<float> _tangentLength;
AnimatedProperty<glm::vec3> _colorMid;
AnimatedProperty<glm::vec3> _colorEnd;
AnimatedProperty<glm::vec3> _colorStart;
AnimatedProperty<float> _detonate;
// END Keyframes
void computeLocalTransform();
void computeAbsoluteTransform();
};
} // namespace graphics

View file

@ -235,7 +235,7 @@ vec3 getNormalFromBumpmap(vec2 uv) {
if (uBumpmaps.swizzled) {
result = vec3(bumpmapSample.a, bumpmapSample.g, 1.0);
} else {
result = vec3(bumpmapSample.r, bumpmapSample.g, bumpmapSample.b);
result = bumpmapSample.rgb;
}
result = normalize(result * 2.0 - 1.0);
}

View file

@ -84,7 +84,7 @@ void AnimationChannel::update(float dt, bool visible) {
}
if (visible) {
_stateByNumber.clear();
_stateById.clear();
computeSceneNodeStates(*_animation->rootNode());
}
@ -108,42 +108,42 @@ void AnimationChannel::computeSceneNodeStates(const ModelNode &animNode) {
const ModelNode *modelNode = modelNodeSceneNode->modelNode();
bool transformChanged = false;
float scale = 1.0f;
glm::vec3 position(modelNode->position());
glm::quat orientation(modelNode->orientation());
glm::vec3 position(modelNode->restPosition());
glm::quat orientation(modelNode->restOrientation());
if (_properties.flags & AnimationFlags::syncLipAnim) {
uint8_t leftFrameIdx, rightFrameIdx;
float interpolant;
if (_lipAnimation->getKeyframes(_time, leftFrameIdx, rightFrameIdx, interpolant)) {
float factor;
if (_lipAnimation->getKeyframes(_time, leftFrameIdx, rightFrameIdx, factor)) {
float animScale;
if (animNode.getScale(leftFrameIdx, rightFrameIdx, interpolant, animScale)) {
if (animNode.getScale(leftFrameIdx, rightFrameIdx, factor, animScale)) {
scale = animScale;
transformChanged = true;
}
glm::vec3 animPosiiton;
if (animNode.getPosition(leftFrameIdx, rightFrameIdx, interpolant, animPosiiton)) {
if (animNode.getPosition(leftFrameIdx, rightFrameIdx, factor, animPosiiton)) {
position += _properties.scale * animPosiiton;
transformChanged = true;
}
glm::quat animOrientation;
if (animNode.getOrientation(leftFrameIdx, rightFrameIdx, interpolant, animOrientation)) {
if (animNode.getOrientation(leftFrameIdx, rightFrameIdx, factor, animOrientation)) {
orientation = move(animOrientation);
transformChanged = true;
}
}
} else {
float animScale;
if (animNode.scales().getByTime(_time, animScale)) {
if (animNode.scale().getByTime(_time, animScale)) {
scale = animScale;
transformChanged = true;
}
glm::vec3 animPosition;
if (animNode.positions().getByTime(_time, animPosition)) {
if (animNode.position().getByTime(_time, animPosition)) {
position += _properties.scale * animPosition;
transformChanged = true;
}
glm::quat animOrientation;
if (animNode.orientations().getByTime(_time, animOrientation)) {
if (animNode.orientation().getByTime(_time, animOrientation)) {
orientation = move(animOrientation);
transformChanged = true;
}
@ -151,27 +151,27 @@ void AnimationChannel::computeSceneNodeStates(const ModelNode &animNode) {
SceneNodeState state;
float alpha;
if (animNode.alphas().getByTime(_time, alpha)) {
if (animNode.alpha().getByTime(_time, alpha)) {
state.flags |= SceneNodeStateFlags::alpha;
state.alpha = alpha;
}
glm::vec3 selfIllumColor;
if (animNode.selfIllumColors().getByTime(_time, selfIllumColor)) {
if (animNode.selfIllumColor().getByTime(_time, selfIllumColor)) {
state.flags |= SceneNodeStateFlags::selfIllum;
state.selfIllumColor = move(selfIllumColor);
}
glm::vec3 lightColor;
if (animNode.lightColors().getByTime(_time, lightColor)) {
if (animNode.color().getByTime(_time, lightColor)) {
state.flags |= SceneNodeStateFlags::lightColor;
state.lightColor = move(lightColor);
}
float lightMultiplier;
if (animNode.lightMultipliers().getByTime(_time, lightMultiplier)) {
if (animNode.multiplier().getByTime(_time, lightMultiplier)) {
state.flags |= SceneNodeStateFlags::lightMultiplier;
state.lightMultiplier = lightMultiplier;
}
float lightRadius;
if (animNode.lightRadii().getByTime(_time, lightRadius)) {
if (animNode.radius().getByTime(_time, lightRadius)) {
state.flags |= SceneNodeStateFlags::lightRadius;
state.lightRadius = lightRadius;
}
@ -183,7 +183,7 @@ void AnimationChannel::computeSceneNodeStates(const ModelNode &animNode) {
state.flags |= SceneNodeStateFlags::transform;
state.transform = move(transform);
}
_stateByNumber.insert(make_pair(modelNode->nodeNumber(), move(state)));
_stateById.insert(make_pair(modelNode->id(), move(state)));
}
}
@ -216,9 +216,9 @@ float AnimationChannel::getTransitionTime() const {
return _animation ? _animation->transitionTime() : 0.0f;
}
bool AnimationChannel::getSceneNodeStateByNumber(uint16_t nodeNumber, SceneNodeState &state) const {
auto maybeState = _stateByNumber.find(nodeNumber);
if (maybeState != _stateByNumber.end()) {
bool AnimationChannel::getSceneNodeStateById(uint16_t nodeId, SceneNodeState &state) const {
auto maybeState = _stateById.find(nodeId);
if (maybeState != _stateById.end()) {
state = maybeState->second;
return true;
}

View file

@ -80,7 +80,7 @@ public:
bool isFinished() const;
float getTransitionTime() const;
bool getSceneNodeStateByNumber(uint16_t nodeNumber, SceneNodeState &state) const;
bool getSceneNodeStateById(uint16_t nodeId, SceneNodeState &state) const;
std::string getAnimationName() const;
float time() const { return _time; }
@ -97,7 +97,7 @@ private:
float _time { 0.0f };
bool _freeze { false };
bool _finished { false };
std::unordered_map<uint16_t, SceneNodeState> _stateByNumber;
std::unordered_map<uint16_t, SceneNodeState> _stateById;
void computeSceneNodeStates(const graphics::ModelNode &animNode);
};

View file

@ -68,7 +68,7 @@ void SceneNodeAnimator::update(float dt, bool visible) {
if (visible) {
// Compute and apply node states to the managed model
_stateByNumber.clear();
_stateById.clear();
computeSceneNodeStates(*_sceneNode->model()->rootNode());
applySceneNodeStates(*_sceneNode->model()->rootNode());
}
@ -85,7 +85,7 @@ bool SceneNodeAnimator::isInTransition() const {
}
void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 parentTransform) {
if (modelNode.skin()) return;
if (modelNode.isSkinMesh()) return;
SceneNodeState state;
state.flags |= SceneNodeStateFlags::transform;
@ -97,8 +97,8 @@ void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 p
// In the Blend mode, blend animations on the first two channels (only transforms)
SceneNodeState channel0State, channel1State;
bool hasChannel0State = _channels[0].getSceneNodeStateByNumber(modelNode.nodeNumber(), channel0State);
bool hasChannel1State = _channels[1].getSceneNodeStateByNumber(modelNode.nodeNumber(), channel1State);
bool hasChannel0State = _channels[0].getSceneNodeStateById(modelNode.id(), channel0State);
bool hasChannel1State = _channels[1].getSceneNodeStateById(modelNode.id(), channel1State);
if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::transform) &&
hasChannel1State && (channel1State.flags & SceneNodeStateFlags::transform)) {
@ -140,7 +140,7 @@ void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 p
int componentsLeft = SceneNodeStateFlags::all;
for (int i = kChannelCount - 1; i >= 0; --i) {
SceneNodeState channelState;
if (_channels[i].isActive() && _channels[i].getSceneNodeStateByNumber(modelNode.nodeNumber(), channelState)) {
if (_channels[i].isActive() && _channels[i].getSceneNodeStateById(modelNode.id(), channelState)) {
if ((channelState.flags & SceneNodeStateFlags::transform) && (componentsLeft & SceneNodeStateFlags::transform)) {
localTransform = move(channelState.transform);
componentsLeft &= ~SceneNodeStateFlags::transform;
@ -175,7 +175,7 @@ void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 p
} else {
// Otherwise, select animation on the first channel
SceneNodeState channelState;
if (_channels[0].getSceneNodeStateByNumber(modelNode.nodeNumber(), channelState)) {
if (_channels[0].getSceneNodeStateById(modelNode.id(), channelState)) {
if (channelState.flags & SceneNodeStateFlags::transform) {
localTransform = move(channelState.transform);
}
@ -204,7 +204,7 @@ void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 p
glm::mat4 absTransform(parentTransform * localTransform);
state.transform = absTransform;
_stateByNumber.insert(make_pair(modelNode.nodeNumber(), move(state)));
_stateById.insert(make_pair(modelNode.id(), move(state)));
for (auto &child : modelNode.children()) {
computeSceneNodeStates(*child, absTransform);
@ -213,12 +213,12 @@ void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 p
void SceneNodeAnimator::applySceneNodeStates(ModelNode &modelNode) {
// Do not apply transforms to skinned model nodes
if (modelNode.skin()) return;
if (modelNode.isSkinMesh()) return;
auto maybeState = _stateByNumber.find(modelNode.nodeNumber());
if (maybeState != _stateByNumber.end()) {
auto maybeState = _stateById.find(modelNode.id());
if (maybeState != _stateById.end()) {
const SceneNodeState &state = maybeState->second;
ModelNodeSceneNode *sceneNode = _sceneNode->getModelNodeByIndex(modelNode.index());
ModelNodeSceneNode *sceneNode = _sceneNode->getModelNodeById(modelNode.id());
if (state.flags & SceneNodeStateFlags::transform) {
sceneNode->setLocalTransform(state.transform);
sceneNode->setBoneTransform(state.transform * modelNode.absoluteTransformInverse());
@ -229,7 +229,7 @@ void SceneNodeAnimator::applySceneNodeStates(ModelNode &modelNode) {
if (state.flags & SceneNodeStateFlags::selfIllum) {
sceneNode->setSelfIllumColor(state.selfIllumColor);
}
LightSceneNode *light = _sceneNode->getLightNodeByNumber(modelNode.nodeNumber());
LightSceneNode *light = _sceneNode->getLightNodeById(modelNode.id());
if (light) {
if (state.flags & SceneNodeStateFlags::lightColor) {
light->setColor(state.lightColor);

View file

@ -86,7 +86,7 @@ private:
CompositionMode _compositionMode { CompositionMode::Overlay };
bool _transition { false }; /**< is there an animation transition going on? */
std::unordered_map<uint16_t, SceneNodeState> _stateByNumber;
std::unordered_map<uint16_t, SceneNodeState> _stateById;
std::string _defaultAnimName;
AnimationProperties _defaultAnimProperties;

View file

@ -43,7 +43,7 @@ namespace scene {
static constexpr float kMotionBlurStrength = 0.25f;
EmitterSceneNode::EmitterSceneNode(const ModelSceneNode *modelSceneNode, const shared_ptr<Emitter> &emitter, SceneGraph *sceneGraph) :
EmitterSceneNode::EmitterSceneNode(const ModelSceneNode *modelSceneNode, const shared_ptr<ModelNode::Emitter> &emitter, SceneGraph *sceneGraph) :
SceneNode(SceneNodeType::Emitter, sceneGraph),
_modelSceneNode(modelSceneNode),
_emitter(emitter) {
@ -59,8 +59,8 @@ EmitterSceneNode::EmitterSceneNode(const ModelSceneNode *modelSceneNode, const s
}
void EmitterSceneNode::init() {
if (_emitter->birthrate() != 0) {
_birthInterval = 1.0f / static_cast<float>(_emitter->birthrate());
if (_emitter->birthrate != 0) {
_birthInterval = 1.0f / static_cast<float>(_emitter->birthrate);
}
}
@ -88,9 +88,9 @@ void EmitterSceneNode::removeExpiredParticles(float dt) {
}
void EmitterSceneNode::spawnParticles(float dt) {
switch (_emitter->updateMode()) {
case Emitter::UpdateMode::Fountain:
if (_emitter->birthrate() != 0.0f) {
switch (_emitter->updateMode) {
case ModelNode::Emitter::UpdateMode::Fountain:
if (_emitter->birthrate != 0.0f) {
if (_birthTimer.advance(dt)) {
if (_particles.size() < kMaxParticles) {
doSpawnParticle();
@ -99,8 +99,8 @@ void EmitterSceneNode::spawnParticles(float dt) {
}
}
break;
case Emitter::UpdateMode::Single:
if (!_spawned || (_particles.empty() && _emitter->loop())) {
case ModelNode::Emitter::UpdateMode::Single:
if (!_spawned || (_particles.empty() && _emitter->loop)) {
doSpawnParticle();
_spawned = true;
}
@ -111,17 +111,17 @@ void EmitterSceneNode::spawnParticles(float dt) {
}
void EmitterSceneNode::doSpawnParticle() {
float halfW = 0.005f * _emitter->size().x;
float halfH = 0.005f * _emitter->size().y;
float halfW = 0.005f * _emitter->size.x;
float halfH = 0.005f * _emitter->size.y;
glm::vec3 position(random(-halfW, halfW), random(-halfH, halfH), 0.0f);
float sign;
if (_emitter->spread() > glm::pi<float>() && random(0, 1) != 0) {
if (_emitter->spread > glm::pi<float>() && random(0, 1) != 0) {
sign = -1.0f;
} else {
sign = 1.0f;
}
float velocity = sign * (_emitter->velocity() + random(0.0f, _emitter->randomVelocity()));
float velocity = sign * (_emitter->velocity + random(0.0f, _emitter->randomVelocity));
auto particle = make_shared<Particle>(position, velocity, this);
_particles.push_back(particle);
@ -130,20 +130,20 @@ void EmitterSceneNode::doSpawnParticle() {
void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
if (particles.empty()) return;
shared_ptr<Texture> texture(_emitter->texture());
shared_ptr<Texture> texture(_emitter->texture);
if (!texture) return;
ShaderUniforms uniforms(_sceneGraph->uniformsPrototype());
uniforms.combined.featureMask |= UniformFeatureFlags::particles;
uniforms.particles->gridSize = glm::vec2(_emitter->gridWidth(), _emitter->gridHeight());
uniforms.particles->render = static_cast<int>(_emitter->renderMode());
uniforms.particles->gridSize = glm::vec2(_emitter->gridWidth, _emitter->gridHeight);
uniforms.particles->render = static_cast<int>(_emitter->renderMode);
for (size_t i = 0; i < particles.size(); ++i) {
const Particle &particle = *particles[i];
glm::mat4 transform(_absoluteTransform);
transform = glm::translate(transform, particles[i]->position());
if (_emitter->renderMode() == Emitter::RenderMode::MotionBlur) {
if (_emitter->renderMode == ModelNode::Emitter::RenderMode::MotionBlur) {
transform = glm::scale(transform, glm::vec3((1.0f + kMotionBlurStrength * _modelSceneNode->projectileSpeed()) * particle.size(), particle.size(), particle.size()));
} else {
transform = glm::scale(transform, glm::vec3(particle.size()));
@ -161,7 +161,7 @@ void EmitterSceneNode::drawParticles(const vector<Particle *> &particles) {
StateManager::instance().setActiveTextureUnit(TextureUnits::diffuse);
texture->bind();
bool lighten = _emitter->blendMode() == Emitter::BlendMode::Lighten;
bool lighten = _emitter->blendMode == ModelNode::Emitter::BlendMode::Lighten;
if (lighten) {
StateManager::instance().withLightenBlending([&particles]() {
Meshes::instance().getBillboard()->drawInstanced(static_cast<int>(particles.size()));

View file

@ -24,7 +24,7 @@
#include "glm/vec3.hpp"
#include "../../common/timer.h"
#include "../../graphics/model/emitter.h"
#include "../../graphics/model/modelnode.h"
#include "../particle.h"
@ -36,7 +36,7 @@ class ModelSceneNode;
class EmitterSceneNode : public SceneNode {
public:
EmitterSceneNode(const ModelSceneNode *modelSceneNode, const std::shared_ptr<graphics::Emitter> &emitter, SceneGraph *sceneGraph);
EmitterSceneNode(const ModelSceneNode *modelSceneNode, const std::shared_ptr<graphics::ModelNode::Emitter> &emitter, SceneGraph *sceneGraph);
void update(float dt) override;
@ -49,12 +49,12 @@ public:
void detonate();
std::shared_ptr<graphics::Emitter> emitter() const { return _emitter; }
std::shared_ptr<graphics::ModelNode::Emitter> emitter() const { return _emitter; }
const std::vector<std::shared_ptr<Particle>> &particles() const { return _particles; }
private:
const ModelSceneNode *_modelSceneNode;
std::shared_ptr<graphics::Emitter> _emitter;
std::shared_ptr<graphics::ModelNode::Emitter> _emitter;
float _birthInterval { 0.0f };
Timer _birthTimer;

View file

@ -41,7 +41,7 @@ LightSceneNode::LightSceneNode(int priority, SceneGraph *sceneGraph) :
_priority(priority) {
}
void LightSceneNode::drawLensFlares(const LensFlare &flare) {
void LightSceneNode::drawLensFlares(const ModelNode::LensFlare &flare) {
shared_ptr<CameraSceneNode> camera(_sceneGraph->activeCamera());
if (!camera) return;

View file

@ -19,7 +19,7 @@
#include "../types.h"
#include "../../graphics/model/lensflare.h"
#include "../../graphics/model/modelnode.h"
#include "scenenode.h"
@ -31,7 +31,7 @@ class LightSceneNode : public SceneNode {
public:
LightSceneNode(int priority, SceneGraph *sceneGraph);
void drawLensFlares(const graphics::LensFlare &flare);
void drawLensFlares(const graphics::ModelNode::LensFlare &flare);
bool isShadow() const { return _shadow; }
bool isAmbientOnly() const { return _ambientOnly; }
@ -42,7 +42,7 @@ public:
float multiplier() const { return _multiplier; }
float radius() const { return _radius; }
float flareRadius() const { return _flareRadius; }
const std::vector<graphics::LensFlare> &flares() const { return _flares; }
const std::vector<graphics::ModelNode::LensFlare> &flares() const { return _flares; }
void setColor(glm::vec3 color) { _color = std::move(color); }
void setMultiplier(float multiplier) { _multiplier = multiplier; }
@ -51,7 +51,7 @@ public:
void setAmbientOnly(bool ambientOnly) { _ambientOnly = ambientOnly; }
void setDirectional(bool directional) { _directional = directional; }
void setFlareRadius(float radius) { _flareRadius = radius; }
void setFlares(std::vector<graphics::LensFlare> flares) { _flares = std::move(flares); }
void setFlares(std::vector<graphics::ModelNode::LensFlare> flares) { _flares = std::move(flares); }
private:
int _priority;
@ -66,7 +66,7 @@ private:
// Light flares
float _flareRadius { 0.0f };
std::vector<graphics::LensFlare> _flares;
std::vector<graphics::ModelNode::LensFlare> _flares;
// END Light flares
};

View file

@ -60,20 +60,20 @@ ModelNodeSceneNode::ModelNodeSceneNode(SceneGraph *sceneGraph, const ModelSceneN
if (!modelNode) {
throw invalid_argument("modelNode must not be null");
}
if (_modelNode->alphas().getNumKeyframes() > 0) {
_alpha = _modelNode->alphas().getByKeyframe(0);
if (_modelNode->alpha().getNumFrames() > 0) {
_alpha = _modelNode->alpha().getByFrame(0);
}
if (_modelNode->selfIllumColors().getNumKeyframes() > 0) {
_selfIllumColor = _modelNode->selfIllumColors().getByKeyframe(0);
if (_modelNode->selfIllumColor().getNumFrames() > 0) {
_selfIllumColor = _modelNode->selfIllumColor().getByFrame(0);
}
initTextures();
}
void ModelNodeSceneNode::initTextures() {
shared_ptr<ModelNode::Trimesh> mesh(_modelNode->mesh());
shared_ptr<ModelNode::TriangleMesh> mesh(_modelNode->mesh());
if (!mesh) return;
_textures.diffuse = mesh->diffuse;
_textures.diffuse = mesh->diffuseMap;
_textures.lightmap = mesh->lightmap;
_textures.bumpmap = mesh->bumpmap;
@ -105,13 +105,11 @@ void ModelNodeSceneNode::refreshAdditionalTextures() {
}
void ModelNodeSceneNode::update(float dt) {
shared_ptr<ModelNode::Trimesh> mesh(_modelNode->mesh());
shared_ptr<ModelNode::TriangleMesh> mesh(_modelNode->mesh());
if (mesh) {
// UV animation
const ModelNode::UVAnimation &uvAnimation = mesh->uvAnimation;
if (uvAnimation.animated) {
glm::vec2 dir(uvAnimation.directionX, uvAnimation.directionY);
_uvOffset += kUvAnimationSpeed * dir * dt;
if (mesh->uvAnimation.dir.x != 0.0f || mesh->uvAnimation.dir.y != 0.0f) {
_uvOffset += kUvAnimationSpeed * mesh->uvAnimation.dir * dt;
_uvOffset -= glm::floor(_uvOffset);
}
@ -130,20 +128,20 @@ void ModelNodeSceneNode::update(float dt) {
}
// Danglymesh animation
shared_ptr<ModelNode::Danglymesh> danglymesh(_modelNode->danglymesh());
if (danglymesh) {
shared_ptr<ModelNode::DanglyMesh> danglyMesh(mesh->danglyMesh);
if (danglyMesh) {
bool forceApplied = glm::length2(_danglymeshAnimation.force) > 0.0f;
if (forceApplied) {
// When force is applied, stride in the opposite direction from the applied force
glm::vec3 strideDir(-_danglymeshAnimation.force);
glm::vec3 maxStride(danglymesh->displacement);
_danglymeshAnimation.stride = glm::clamp(_danglymeshAnimation.stride + danglymesh->period * strideDir * dt, -maxStride, maxStride);
glm::vec3 maxStride(danglyMesh->displacement);
_danglymeshAnimation.stride = glm::clamp(_danglymeshAnimation.stride + danglyMesh->period * strideDir * dt, -maxStride, maxStride);
} else {
// When force is not applied, gradually nullify stride
float strideMag2 = glm::length2(_danglymeshAnimation.stride);
if (strideMag2 > 0.0f) {
glm::vec3 strideDir(-_danglymeshAnimation.stride);
_danglymeshAnimation.stride += danglymesh->period * strideDir * dt;
_danglymeshAnimation.stride += danglyMesh->period * strideDir * dt;
if ((strideDir.x > 0.0f && _danglymeshAnimation.stride.x > 0.0f) || (strideDir.x < 0.0f && _danglymeshAnimation.stride.x < 0.0f)) {
_danglymeshAnimation.stride.x = 0.0f;
}
@ -162,27 +160,27 @@ void ModelNodeSceneNode::update(float dt) {
}
bool ModelNodeSceneNode::shouldRender() const {
if (g_debugWalkmesh) return _modelNode->isAABB();
if (g_debugWalkmesh) return _modelNode->isAABBMesh();
shared_ptr<ModelNode::Trimesh> mesh(_modelNode->mesh());
if (!mesh || !mesh->render || _modelNode->alphas().getByKeyframeOrElse(0, 1.0f) == 0.0f) return false;
shared_ptr<ModelNode::TriangleMesh> mesh(_modelNode->mesh());
if (!mesh || !mesh->render || _modelNode->alpha().getByFrameOrElse(0, 1.0f) == 0.0f) return false;
return !_modelNode->isAABB();
return !_modelNode->isAABBMesh();
}
bool ModelNodeSceneNode::shouldCastShadows() const {
// Skin nodes must not cast shadows
if (static_cast<bool>(_modelNode->skin())) return false;
if (_modelNode->isSkinMesh()) return false;
// Meshless nodes must not cast shadows
shared_ptr<ModelNode::Trimesh> mesh(_modelNode->mesh());
shared_ptr<ModelNode::TriangleMesh> mesh(_modelNode->mesh());
if (!mesh) return false;
return mesh->shadow;
}
bool ModelNodeSceneNode::isTransparent() const {
shared_ptr<ModelNode::Trimesh> mesh(_modelNode->mesh());
shared_ptr<ModelNode::TriangleMesh> mesh(_modelNode->mesh());
if (!mesh) return false; // Meshless nodes are opaque
// Character models are opaque
@ -195,7 +193,7 @@ bool ModelNodeSceneNode::isTransparent() const {
if (!_textures.diffuse) return false;
// Model nodes with transparency hint greater than 0 are transparent
if (mesh->transparency> 0) return true;
if (mesh->transparency > 0) return true;
// Model nodes with additive diffuse texture are opaque
if (_textures.diffuse->isAdditive()) return true;
@ -226,7 +224,7 @@ static bool isReceivingShadows(const ModelSceneNode &model, const ModelNodeScene
}
void ModelNodeSceneNode::drawSingle(bool shadowPass) {
shared_ptr<ModelNode::Trimesh> mesh(_modelNode->mesh());
shared_ptr<ModelNode::TriangleMesh> mesh(_modelNode->mesh());
if (!mesh) return;
// Setup shaders
@ -285,20 +283,20 @@ void ModelNodeSceneNode::drawSingle(bool shadowPass) {
uniforms.combined.featureMask |= UniformFeatureFlags::shadows;
}
shared_ptr<ModelNode::Skin> skin(_modelNode->skin());
if (skin) {
if (mesh->skin) {
uniforms.combined.featureMask |= UniformFeatureFlags::skeletal;
for (int i = 0; i < kMaxBones; ++i) {
uniforms.skeletal->bones[i] = glm::mat4(1.0f);
}
for (auto &pair : skin->nodeIdxByBoneIdx) {
uint16_t boneIdx = pair.first;
uint16_t nodeIdx = pair.second;
ModelNodeSceneNode *bone = _modelSceneNode->getModelNodeByIndex(nodeIdx);
if (i < static_cast<int>(mesh->skin->boneNodeId.size())) {
uint16_t nodeId = mesh->skin->boneNodeId[i];
if (nodeId != 0xffff) {
ModelNodeSceneNode *bone = _modelSceneNode->getModelNodeById(nodeId);
if (bone) {
uniforms.skeletal->bones[boneIdx] = _modelNode->absoluteTransformInverse() * bone->boneTransform() * _modelNode->absoluteTransform();
uniforms.skeletal->bones[i] = _modelNode->absoluteTransformInverse() * bone->boneTransform() * _modelNode->absoluteTransform();
}
}
} else {
uniforms.skeletal->bones[i] = glm::mat4(1.0f);
}
}
}
@ -311,8 +309,8 @@ void ModelNodeSceneNode::drawSingle(bool shadowPass) {
const vector<LightSceneNode *> &lights = _sceneGraph->closestLights();
uniforms.combined.featureMask |= UniformFeatureFlags::lighting;
uniforms.combined.material.ambient = glm::vec4(mesh->ambientColor, 1.0f);
uniforms.combined.material.diffuse = glm::vec4(mesh->diffuseColor, 1.0f);
uniforms.combined.material.ambient = glm::vec4(mesh->ambient, 1.0f);
uniforms.combined.material.diffuse = glm::vec4(mesh->diffuse, 1.0f);
uniforms.combined.material.shininess = _material.shininess;
uniforms.combined.material.metallic = _material.metallic;
uniforms.combined.material.roughness = _material.roughness;
@ -347,14 +345,14 @@ void ModelNodeSceneNode::drawSingle(bool shadowPass) {
uniforms.combined.general.fogColor = glm::vec4(_sceneGraph->fogColor(), 1.0f);
}
shared_ptr<ModelNode::Danglymesh> danglymesh(_modelNode->danglymesh());
if (danglymesh) {
shared_ptr<ModelNode::DanglyMesh> danglyMesh(mesh->danglyMesh);
if (danglyMesh) {
uniforms.combined.featureMask |= UniformFeatureFlags::danglymesh;
uniforms.danglymesh->stride = glm::vec4(_danglymeshAnimation.stride, 0.0f);
uniforms.danglymesh->displacement = danglymesh->displacement;
uniforms.danglymesh->displacement = danglyMesh->displacement;
size_t i = 0;
for (i = 0; i < danglymesh->constraints.size(); ++i) {
uniforms.danglymesh->constraints[i / 4][i % 4] = danglymesh->constraints[i].multiplier;
for (i = 0; i < danglyMesh->constraints.size(); ++i) {
uniforms.danglymesh->constraints[i / 4][i % 4] = danglyMesh->constraints[i].multiplier;
}
}
}
@ -418,14 +416,14 @@ bool ModelNodeSceneNode::isLightingEnabled() const {
}
void ModelNodeSceneNode::setAppliedForce(glm::vec3 force) {
if (_modelNode->danglymesh()) {
if (_modelNode->isDanglyMesh()) {
// Convert force from world to object space
_danglymeshAnimation.force = _absoluteTransformInv * glm::vec4(force, 0.0f);
}
}
glm::vec3 ModelNodeSceneNode::getOrigin() const {
return _absoluteTransform * glm::vec4(_modelNode->getCenterOfAABB(), 1.0f);
return _absoluteTransform * glm::vec4(_modelNode->mesh()->mesh->aabb().center(), 1.0f);
}
void ModelNodeSceneNode::setBoneTransform(const glm::mat4 &transform) {

View file

@ -65,35 +65,35 @@ ModelSceneNode::ModelSceneNode(
_volumetric = true;
}
static bool validateEmitter(const Emitter &emitter) {
switch (emitter.updateMode()) {
case Emitter::UpdateMode::Fountain:
case Emitter::UpdateMode::Single:
case Emitter::UpdateMode::Explosion:
static bool validateEmitter(const ModelNode::Emitter &emitter) {
switch (emitter.updateMode) {
case ModelNode::Emitter::UpdateMode::Fountain:
case ModelNode::Emitter::UpdateMode::Single:
case ModelNode::Emitter::UpdateMode::Explosion:
break;
default:
warn("validateEmitter: unsupported update mode: " + to_string(static_cast<int>(emitter.updateMode())));
warn("validateEmitter: unsupported update mode: " + to_string(static_cast<int>(emitter.updateMode)));
return false;
}
switch (emitter.renderMode()) {
case Emitter::RenderMode::Normal:
case Emitter::RenderMode::BillboardToWorldZ:
case Emitter::RenderMode::MotionBlur:
case Emitter::RenderMode::BillboardToLocalZ:
case Emitter::RenderMode::AlignedToParticleDir:
switch (emitter.renderMode) {
case ModelNode::Emitter::RenderMode::Normal:
case ModelNode::Emitter::RenderMode::BillboardToWorldZ:
case ModelNode::Emitter::RenderMode::MotionBlur:
case ModelNode::Emitter::RenderMode::BillboardToLocalZ:
case ModelNode::Emitter::RenderMode::AlignedToParticleDir:
break;
default:
warn("validateEmitter: unsupported render mode: " + to_string(static_cast<int>(emitter.renderMode())));
warn("validateEmitter: unsupported render mode: " + to_string(static_cast<int>(emitter.renderMode)));
return false;
}
switch (emitter.blendMode()) {
case Emitter::BlendMode::Normal:
case Emitter::BlendMode::Lighten:
switch (emitter.blendMode) {
case ModelNode::Emitter::BlendMode::Normal:
case ModelNode::Emitter::BlendMode::Lighten:
break;
default:
warn("validateEmitter: unsupported blend mode: " + to_string(static_cast<int>(emitter.blendMode())));
warn("validateEmitter: unsupported blend mode: " + to_string(static_cast<int>(emitter.blendMode)));
return false;
}
@ -112,8 +112,7 @@ void ModelSceneNode::initModelNodes() {
nodes.pop();
const ModelNode *modelNode = sceneNode->modelNode();
_modelNodeByIndex.insert(make_pair(modelNode->index(), sceneNode));
_modelNodeByNumber.insert(make_pair(modelNode->nodeNumber(), sceneNode));
_modelNodeById.insert(make_pair(modelNode->id(), sceneNode));
for (auto &child : modelNode->children()) {
shared_ptr<ModelNodeSceneNode> childNode(getModelNodeSceneNode(*child));
@ -123,12 +122,12 @@ void ModelSceneNode::initModelNodes() {
shared_ptr<ModelNode::Light> light(child->light());
if (light) {
// Light is considered directional if its radius exceeds a certain threshold
float radius = child->lightRadii().getByKeyframeOrElse(0, 1.0f);
float radius = child->radius().getByFrameOrElse(0, 1.0f);
bool directional = radius >= kMinDirectionalLightRadius;
auto lightNode = make_shared<LightSceneNode>(light->priority, _sceneGraph);
lightNode->setColor(child->lightColors().getByKeyframeOrElse(0, glm::vec3(1.0f)));
lightNode->setMultiplier(child->lightMultipliers().getByKeyframeOrElse(0, 1.0f));
lightNode->setColor(child->color().getByFrameOrElse(0, glm::vec3(1.0f)));
lightNode->setMultiplier(child->multiplier().getByFrameOrElse(0, 1.0f));
lightNode->setRadius(radius);
lightNode->setShadow(light->shadow);
lightNode->setAmbientOnly(light->ambientOnly);
@ -137,10 +136,10 @@ void ModelSceneNode::initModelNodes() {
lightNode->setFlares(light->flares);
childNode->addChild(lightNode);
_lightNodeByNumber.insert(make_pair(modelNode->nodeNumber(), lightNode.get()));
_lightNodeById.insert(make_pair(modelNode->id(), lightNode.get()));
}
shared_ptr<Emitter> emitter(child->emitter());
shared_ptr<ModelNode::Emitter> emitter(child->emitter());
if (emitter && validateEmitter(*emitter)) {
auto emitterNode = make_shared<EmitterSceneNode>(this, emitter, _sceneGraph);
childNode->addChild(emitterNode);
@ -155,7 +154,7 @@ void ModelSceneNode::initModelNodes() {
}
}
refreshAABB();
computeAABB();
}
unique_ptr<ModelNodeSceneNode> ModelSceneNode::getModelNodeSceneNode(ModelNode &node) const {
@ -188,9 +187,9 @@ shared_ptr<ModelSceneNode> ModelSceneNode::attach(const string &parent, const sh
shared_ptr<ModelSceneNode> ModelSceneNode::attach(ModelNodeSceneNode &parent, const shared_ptr<Model> &model, ModelUsage usage) {
const ModelNode *parentModelNode = parent.modelNode();
uint16_t parentNumber = parentModelNode->nodeNumber();
uint16_t parentId = parentModelNode->id();
auto maybeAttached = _attachedModels.find(parentNumber);
auto maybeAttached = _attachedModels.find(parentId);
if (maybeAttached != _attachedModels.end()) {
parent.removeChild(*maybeAttached->second);
_attachedModels.erase(maybeAttached);
@ -203,7 +202,7 @@ shared_ptr<ModelSceneNode> ModelSceneNode::attach(ModelNodeSceneNode &parent, co
auto modelNode = make_shared<ModelSceneNode>(usage, model, _sceneGraph, ignoreNodes);
parent.addChild(modelNode);
return _attachedModels.insert(make_pair(parentNumber, move(modelNode))).first->second;
return _attachedModels.insert(make_pair(parentId, move(modelNode))).first->second;
}
return nullptr;
@ -211,47 +210,47 @@ shared_ptr<ModelSceneNode> ModelSceneNode::attach(ModelNodeSceneNode &parent, co
}
ModelNodeSceneNode *ModelSceneNode::getModelNode(const string &name) const {
shared_ptr<ModelNode> modelNode(_model->findNodeByName(name));
shared_ptr<ModelNode> modelNode(_model->getNodeByName(name));
if (!modelNode) return nullptr;
return getFromLookupOrNull(_modelNodeByNumber, modelNode->nodeNumber());
return getFromLookupOrNull(_modelNodeById, modelNode->id());
}
ModelNodeSceneNode *ModelSceneNode::getModelNodeByIndex(int index) const {
return getFromLookupOrNull(_modelNodeByIndex, static_cast<uint16_t>(index));
ModelNodeSceneNode *ModelSceneNode::getModelNodeById(uint16_t nodeId) const {
return getFromLookupOrNull(_modelNodeById, nodeId);
}
LightSceneNode *ModelSceneNode::getLightNodeByNumber(uint16_t nodeNumber) const {
return getFromLookupOrNull(_lightNodeByNumber, nodeNumber);
LightSceneNode *ModelSceneNode::getLightNodeById(uint16_t nodeId) const {
return getFromLookupOrNull(_lightNodeById, nodeId);
}
shared_ptr<ModelSceneNode> ModelSceneNode::getAttachedModel(const string &parent) const {
shared_ptr<ModelNode> parentModelNode(_model->findNodeByName(parent));
shared_ptr<ModelNode> parentModelNode(_model->getNodeByName(parent));
if (!parentModelNode) return nullptr;
return getFromLookupOrNull(_attachedModels, parentModelNode->nodeNumber());
return getFromLookupOrNull(_attachedModels, parentModelNode->id());
}
void ModelSceneNode::attach(const string &parent, const shared_ptr<SceneNode> &node) {
shared_ptr<ModelNode> parentModelNode(_model->findNodeByName(parent));
shared_ptr<ModelNode> parentModelNode(_model->getNodeByName(parent));
if (!parentModelNode) {
warn(boost::format("Scene node %s: model node not found: %s") % _model->name() % parent);
return;
}
uint16_t parentNumber = parentModelNode->nodeNumber();
uint16_t parentId = parentModelNode->id();
auto maybeNode = _modelNodeByNumber.find(parentNumber);
if (maybeNode != _modelNodeByNumber.end()) {
auto maybeNode = _modelNodeById.find(parentId);
if (maybeNode != _modelNodeById.end()) {
maybeNode->second->addChild(node);
}
}
bool ModelSceneNode::getNodeAbsolutePosition(const string &name, glm::vec3 &position) const {
shared_ptr<ModelNode> node(_model->findNodeByName(name));
shared_ptr<ModelNode> node(_model->getNodeByName(name));
if (!node) {
shared_ptr<Model> superModel(_model->superModel());
if (superModel) {
node = superModel->findNodeByName(name);
node = superModel->getNodeByName(name);
}
}
if (!node) return false;
@ -261,7 +260,7 @@ bool ModelSceneNode::getNodeAbsolutePosition(const string &name, glm::vec3 &posi
return true;
}
glm::vec3 ModelSceneNode::getCenterOfAABB() const {
glm::vec3 ModelSceneNode::getWorldCenterAABB() const {
return _absoluteTransform * glm::vec4(_aabb.center(), 1.0f);
}
@ -295,15 +294,7 @@ void ModelSceneNode::setAlpha(float alpha) {
}
}
void ModelSceneNode::setProjectileSpeed(float speed) {
_projectileSpeed = speed;
}
void ModelSceneNode::setWalkmesh(shared_ptr<Walkmesh> walkmesh) {
_walkmesh = move(walkmesh);
}
void ModelSceneNode::refreshAABB() {
void ModelSceneNode::computeAABB() {
_aabb.reset();
stack<SceneNode *> nodes;
@ -315,7 +306,7 @@ void ModelSceneNode::refreshAABB() {
if (node->type() == SceneNodeType::ModelNode) {
auto modelNode = static_cast<ModelNodeSceneNode *>(node);
shared_ptr<ModelNode::Trimesh> mesh(modelNode->modelNode()->mesh());
shared_ptr<ModelNode::TriangleMesh> mesh(modelNode->modelNode()->mesh());
if (mesh) {
_aabb.expand(mesh->mesh->aabb() * node->localTransform());
}
@ -340,7 +331,7 @@ void ModelSceneNode::signalEvent(const string &name) {
}
void ModelSceneNode::setAppliedForce(glm::vec3 force) {
for (auto &nodePair : _modelNodeByIndex) {
for (auto &nodePair : _modelNodeById) {
nodePair.second->setAppliedForce(force);
}
for (auto &attached : _attachedModels) {

View file

@ -50,16 +50,16 @@ public:
void update(float dt) override;
void draw() override;
void refreshAABB();
void computeAABB();
void signalEvent(const std::string &name);
void setAppliedForce(glm::vec3 force);
ModelNodeSceneNode *getModelNode(const std::string &name) const;
ModelNodeSceneNode *getModelNodeByIndex(int index) const;
LightSceneNode *getLightNodeByNumber(uint16_t nodeNumber) const;
ModelNodeSceneNode *getModelNodeById(uint16_t nodeId) const;
LightSceneNode *getLightNodeById(uint16_t nodeId) const;
std::shared_ptr<ModelSceneNode> getAttachedModel(const std::string &parent) const;
bool getNodeAbsolutePosition(const std::string &name, glm::vec3 &position) const;
glm::vec3 getCenterOfAABB() const;
glm::vec3 getWorldCenterAABB() const;
const std::string &getName() const;
ModelUsage usage() const { return _usage; }
@ -72,8 +72,8 @@ public:
void setVisible(bool visible) override;
void setDiffuseTexture(const std::shared_ptr<graphics::Texture> &texture);
void setAlpha(float alpha);
void setProjectileSpeed(float speed);
void setWalkmesh(std::shared_ptr<graphics::Walkmesh> walkmesh);
void setProjectileSpeed(float speed) { _projectileSpeed = speed; }
void setWalkmesh(std::shared_ptr<graphics::Walkmesh> walkmesh) { _walkmesh = std::move(walkmesh); }
// Attachments
@ -91,9 +91,8 @@ private:
std::shared_ptr<graphics::Walkmesh> _walkmesh;
SceneNodeAnimator _animator;
std::unordered_map<uint16_t, ModelNodeSceneNode *> _modelNodeByIndex;
std::unordered_map<uint16_t, ModelNodeSceneNode *> _modelNodeByNumber;
std::unordered_map<uint16_t, LightSceneNode *> _lightNodeByNumber;
std::unordered_map<uint16_t, ModelNodeSceneNode *> _modelNodeById;
std::unordered_map<uint16_t, LightSceneNode *> _lightNodeById;
std::vector<std::shared_ptr<EmitterSceneNode>> _emitters;
std::unordered_map<uint16_t, std::shared_ptr<ModelSceneNode>> _attachedModels;
bool _visible { true };

View file

@ -42,21 +42,21 @@ Particle::Particle(glm::vec3 position, float velocity, EmitterSceneNode *emitter
}
void Particle::init() {
shared_ptr<Emitter> emitter(_emitter->emitter());
shared_ptr<ModelNode::Emitter> emitter(_emitter->emitter());
if (emitter->fps() > 0) {
_animLength = (emitter->frameEnd() - emitter->frameStart() + 1) / static_cast<float>(emitter->fps());
if (emitter->fps > 0) {
_animLength = (emitter->frameEnd - emitter->frameStart + 1) / static_cast<float>(emitter->fps);
}
_renderOrder = emitter->renderOrder();
_frame = emitter->frameStart();
_renderOrder = emitter->renderOrder;
_frame = emitter->frameStart;
}
void Particle::update(float dt) {
shared_ptr<Emitter> emitter(_emitter->emitter());
shared_ptr<ModelNode::Emitter> emitter(_emitter->emitter());
if (emitter->lifeExpectancy() != -1) {
_lifetime = glm::min(_lifetime + dt, static_cast<float>(emitter->lifeExpectancy()));
if (emitter->lifeExpectancy != -1) {
_lifetime = glm::min(_lifetime + dt, static_cast<float>(emitter->lifeExpectancy));
} else if (_lifetime == _animLength) {
_lifetime = 0.0f;
} else {
@ -70,7 +70,7 @@ void Particle::update(float dt) {
}
template <class T>
static T interpolateConstraints(const Emitter::Constraints<T> &constraints, float t) {
static T interpolateConstraints(const ModelNode::Emitter::Constraints<T> &constraints, float t) {
T result;
if (t < 0.5f) {
float tt = 2.0f * t;
@ -83,26 +83,26 @@ static T interpolateConstraints(const Emitter::Constraints<T> &constraints, floa
}
void Particle::updateAnimation(float dt) {
shared_ptr<Emitter> emitter(_emitter->emitter());
shared_ptr<ModelNode::Emitter> emitter(_emitter->emitter());
float maturity;
if (emitter->lifeExpectancy() != -1) {
maturity = _lifetime / static_cast<float>(emitter->lifeExpectancy());
if (emitter->lifeExpectancy != -1) {
maturity = _lifetime / static_cast<float>(emitter->lifeExpectancy);
} else if (_animLength > 0.0f) {
maturity = _lifetime / _animLength;
} else {
maturity = 0.0f;
}
_frame = static_cast<int>(glm::ceil(emitter->frameStart() + maturity * (emitter->frameEnd() - emitter->frameStart())));
_size = interpolateConstraints(emitter->particleSize(), maturity);
_color = interpolateConstraints(emitter->color(), maturity);
_alpha = interpolateConstraints(emitter->alpha(), maturity);
_frame = static_cast<int>(glm::ceil(emitter->frameStart + maturity * (emitter->frameEnd - emitter->frameStart)));
_size = interpolateConstraints(emitter->particleSize, maturity);
_color = interpolateConstraints(emitter->color, maturity);
_alpha = interpolateConstraints(emitter->alpha, maturity);
}
bool Particle::isExpired() const {
shared_ptr<Emitter> emitter(_emitter->emitter());
return emitter->lifeExpectancy() != -1 && _lifetime >= emitter->lifeExpectancy();
shared_ptr<ModelNode::Emitter> emitter(_emitter->emitter());
return emitter->lifeExpectancy != -1 && _lifetime >= emitter->lifeExpectancy;
}
} // namespace scene