feat: Make alpha and self-illumination color animatable properties
This commit is contained in:
parent
6086aa433d
commit
1eb43b00a1
16 changed files with 385 additions and 178 deletions
|
@ -302,6 +302,7 @@ set(SCENE_HEADERS
|
|||
src/scene/animation/channel.h
|
||||
src/scene/animation/properties.h
|
||||
src/scene/animation/scenenodeanimator.h
|
||||
src/scene/animation/scenenodestate.h
|
||||
src/scene/node/cameranode.h
|
||||
src/scene/node/emitternode.h
|
||||
src/scene/node/lightnode.h
|
||||
|
|
|
@ -296,12 +296,12 @@ void JbaFile::loadAnimation() {
|
|||
for (size_t k = 0; k < boneKeyframes.size(); ++k) {
|
||||
float time = k * step;
|
||||
|
||||
ModelNode::TranslationKeyframe position;
|
||||
ModelNode::Keyframe position;
|
||||
position.time = time;
|
||||
position.translation = boneKeyframes[k].translation * _translationBase;
|
||||
node->addTranslationKeyframe(move(position));
|
||||
|
||||
ModelNode::OrientationKeyframe orientation;
|
||||
ModelNode::Keyframe orientation;
|
||||
orientation.time = time;
|
||||
orientation.orientation = skeletonNode->orientation() * boneKeyframes[k].orientation;
|
||||
node->addOrientationKeyframe(move(orientation));
|
||||
|
|
|
@ -304,9 +304,8 @@ void MdlFile::readControllers(uint32_t keyCount, uint32_t keyOffset, const vecto
|
|||
}
|
||||
break;
|
||||
case ControllerType::SelfIllumColor:
|
||||
if (node._flags & kNodeHasMesh) {
|
||||
readSelfIllumColorController(dataIndex, data, node);
|
||||
node._selfIllumEnabled = glm::dot(node._selfIllumColor, node._selfIllumColor) > 0.0f;
|
||||
if (!(node._flags & kNodeHasLight) && !(node._flags & kNodeHasEmitter)) {
|
||||
readSelfIllumColorController(rowCount, timeIndex, dataIndex, data, node);
|
||||
}
|
||||
break;
|
||||
case ControllerType::FPS:
|
||||
|
@ -330,7 +329,7 @@ void MdlFile::readControllers(uint32_t keyCount, uint32_t keyOffset, const vecto
|
|||
}
|
||||
break;
|
||||
case ControllerType::Alpha:
|
||||
readAlphaController(dataIndex, data, node);
|
||||
readAlphaController(rowCount, timeIndex, dataIndex, data, node);
|
||||
break;
|
||||
case ControllerType::Multiplier_RandomVelocity:
|
||||
if (node._flags & kNodeHasLight) {
|
||||
|
@ -428,7 +427,7 @@ void MdlFile::readPositionController(uint16_t rowCount, uint8_t columnCount, uin
|
|||
int rowTimeIdx = timeIndex + i;
|
||||
int rowDataIdx = dataIndex + i * (bezier ? 9 : 3);
|
||||
|
||||
ModelNode::TranslationKeyframe frame;
|
||||
ModelNode::Keyframe frame;
|
||||
frame.time = data[rowTimeIdx];
|
||||
frame.translation = glm::make_vec3(&data[rowDataIdx]);
|
||||
|
||||
|
@ -466,7 +465,7 @@ void MdlFile::readOrientationController(uint16_t rowCount, uint8_t columnCount,
|
|||
w = -glm::sqrt(1.0f - dot);
|
||||
}
|
||||
|
||||
ModelNode::OrientationKeyframe frame;
|
||||
ModelNode::Keyframe frame;
|
||||
frame.time = data[rowTimeIdx];
|
||||
frame.orientation = glm::quat(w, x, y, z);
|
||||
|
||||
|
@ -479,7 +478,7 @@ void MdlFile::readOrientationController(uint16_t rowCount, uint8_t columnCount,
|
|||
int rowTimeIdx = timeIndex + i;
|
||||
int rowDataIdx = dataIndex + i * 4;
|
||||
|
||||
ModelNode::OrientationKeyframe frame;
|
||||
ModelNode::Keyframe frame;
|
||||
frame.time = data[rowTimeIdx];
|
||||
frame.orientation.x = data[rowDataIdx + 0];
|
||||
frame.orientation.y = data[rowDataIdx + 1];
|
||||
|
@ -498,7 +497,7 @@ void MdlFile::readScaleController(uint16_t rowCount, uint16_t timeIndex, uint16_
|
|||
node._scaleFrames.resize(rowCount);
|
||||
|
||||
for (int i = 0; i < rowCount; ++i) {
|
||||
ModelNode::ScaleKeyframe frame;
|
||||
ModelNode::Keyframe frame;
|
||||
frame.time = data[timeIndex + i];
|
||||
frame.scale = data[dataIndex + i];
|
||||
|
||||
|
@ -512,14 +511,38 @@ void MdlFile::readColorController(uint16_t dataIndex, const vector<float> &data,
|
|||
node._color.b = data[dataIndex + 2];
|
||||
}
|
||||
|
||||
void MdlFile::readSelfIllumColorController(uint16_t dataIndex, const vector<float> &data, ModelNode &node) {
|
||||
node._selfIllumColor.r = data[dataIndex + 0];
|
||||
node._selfIllumColor.g = data[dataIndex + 1];
|
||||
node._selfIllumColor.b = data[dataIndex + 2];
|
||||
void MdlFile::readSelfIllumColorController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const vector<float> &data, ModelNode &node) {
|
||||
if (rowCount == 1) {
|
||||
node._selfIllumColor = glm::make_vec3(&data[dataIndex]);
|
||||
return;
|
||||
}
|
||||
|
||||
void MdlFile::readAlphaController(uint16_t dataIndex, const vector<float> &data, ModelNode &node) {
|
||||
node._selfIllumFrames.resize(rowCount);
|
||||
|
||||
for (uint16_t i = 0; i < rowCount; ++i) {
|
||||
ModelNode::Keyframe frame;
|
||||
frame.time = data[timeIndex + i];
|
||||
frame.selfIllumColor = glm::make_vec3(&data[dataIndex + 3 * i]);
|
||||
|
||||
node._selfIllumFrames[i] = move(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void MdlFile::readAlphaController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const vector<float> &data, ModelNode &node) {
|
||||
if (rowCount == 1) {
|
||||
node._alpha = data[dataIndex];
|
||||
return;
|
||||
}
|
||||
|
||||
node._alphaFrames.resize(rowCount);
|
||||
|
||||
for (uint16_t i = 0; i < rowCount; ++i) {
|
||||
ModelNode::Keyframe frame;
|
||||
frame.time = data[timeIndex + i];
|
||||
frame.alpha = data[dataIndex + i];
|
||||
|
||||
node._alphaFrames[i] = move(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void MdlFile::readRadiusController(uint16_t dataIndex, const vector<float> &data, ModelNode &node) {
|
||||
|
|
|
@ -64,7 +64,7 @@ private:
|
|||
|
||||
// Controllers
|
||||
|
||||
void readAlphaController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readAlphaController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readAlphaEndController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readAlphaMidController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readAlphaStartController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
|
@ -83,7 +83,7 @@ private:
|
|||
void readRadiusController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readRandomVelocityController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readScaleController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readSelfIllumColorController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readSelfIllumColorController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readSizeEndController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readSizeMidController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
void readSizeStartController(uint16_t dataIndex, const std::vector<float> &data, render::ModelNode &node);
|
||||
|
|
|
@ -76,92 +76,105 @@ void ModelNode::computeAbsoluteTransforms() {
|
|||
}
|
||||
}
|
||||
|
||||
void ModelNode::addTranslationKeyframe(TranslationKeyframe keyframe) {
|
||||
void ModelNode::addTranslationKeyframe(Keyframe keyframe) {
|
||||
_translationFrames.push_back(move(keyframe));
|
||||
}
|
||||
|
||||
void ModelNode::addOrientationKeyframe(OrientationKeyframe keyframe) {
|
||||
void ModelNode::addOrientationKeyframe(Keyframe keyframe) {
|
||||
_orientationFrames.push_back(move(keyframe));
|
||||
}
|
||||
|
||||
bool ModelNode::getTranslation(float time, glm::vec3 &translation, float scale) const {
|
||||
if (_translationFrames.empty()) return false;
|
||||
/**
|
||||
* @return false if keyframes vector is empty, true otherwise
|
||||
*/
|
||||
static bool getKeyframes(
|
||||
const vector<ModelNode::Keyframe> &frames, float time,
|
||||
const ModelNode::Keyframe *&frame1, const ModelNode::Keyframe *&frame2, float &factor) {
|
||||
|
||||
const TranslationKeyframe *left = &_translationFrames.front();
|
||||
const TranslationKeyframe *right = left;
|
||||
if (frames.empty()) return false;
|
||||
|
||||
for (auto it = _translationFrames.begin(); it != _translationFrames.end(); ++it) {
|
||||
frame1 = &frames[0];
|
||||
frame2 = &frames[0];
|
||||
|
||||
for (auto it = frames.begin(); it != frames.end(); ++it) {
|
||||
if (it->time >= time) {
|
||||
right = &*it;
|
||||
if (it != _translationFrames.begin()) left = &*(it - 1);
|
||||
frame2 = &*it;
|
||||
if (it != frames.begin()) {
|
||||
frame1 = &*(it - 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (left == right) {
|
||||
translation = left->translation * scale;
|
||||
if (frame1 == frame2) {
|
||||
factor = 0.0f;
|
||||
} else {
|
||||
factor = (time - frame1->time) / (frame2->time - frame1->time);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float factor = (time - left->time) / (right->time - left->time);
|
||||
|
||||
translation = glm::mix(left->translation, right->translation, factor) * scale;
|
||||
bool ModelNode::getTranslation(float time, glm::vec3 &translation, float scale) const {
|
||||
const Keyframe *frame1, *frame2;
|
||||
float factor;
|
||||
|
||||
if (getKeyframes(_translationFrames, time, frame1, frame2, factor)) {
|
||||
translation = glm::mix(frame1->translation, frame2->translation, factor) * scale;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelNode::getOrientation(float time, glm::quat &orientation) const {
|
||||
if (_orientationFrames.empty()) return false;
|
||||
const Keyframe *frame1, *frame2;
|
||||
float factor;
|
||||
|
||||
const OrientationKeyframe *left = &_orientationFrames.front();
|
||||
const OrientationKeyframe *right = left;
|
||||
|
||||
for (auto it = _orientationFrames.begin(); it != _orientationFrames.end(); ++it) {
|
||||
if (it->time >= time) {
|
||||
right = &*it;
|
||||
if (it != _orientationFrames.begin()) left = &*(it - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (left == right) {
|
||||
orientation = left->orientation;
|
||||
if (getKeyframes(_orientationFrames, time, frame1, frame2, factor)) {
|
||||
orientation = glm::slerp(frame1->orientation, frame2->orientation, factor);
|
||||
return true;
|
||||
}
|
||||
|
||||
float factor = (time - left->time) / (right->time - left->time);
|
||||
|
||||
orientation = glm::slerp(left->orientation, right->orientation, factor);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelNode::getScale(float time, float &scale) const {
|
||||
if (_scaleFrames.empty()) return false;
|
||||
const Keyframe *frame1, *frame2;
|
||||
float factor;
|
||||
|
||||
const ScaleKeyframe *left = &_scaleFrames.front();
|
||||
const ScaleKeyframe *right = left;
|
||||
|
||||
for (auto it = _scaleFrames.begin(); it != _scaleFrames.end(); ++it) {
|
||||
if (it->time >= time) {
|
||||
right = &*it;
|
||||
if (it != _scaleFrames.begin()) left = &*(it - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (left == right) {
|
||||
scale = left->scale;
|
||||
if (getKeyframes(_scaleFrames, time, frame1, frame2, factor)) {
|
||||
scale = glm::mix(frame1->scale, frame2->scale, factor);
|
||||
return true;
|
||||
}
|
||||
|
||||
float factor = (time - left->time) / (right->time - left->time);
|
||||
return false;
|
||||
}
|
||||
|
||||
scale = glm::mix(left->scale, right->scale, factor);
|
||||
bool ModelNode::getAlpha(float time, float &alpha) const {
|
||||
const Keyframe *frame1, *frame2;
|
||||
float factor;
|
||||
|
||||
if (getKeyframes(_alphaFrames, time, frame1, frame2, factor)) {
|
||||
alpha = glm::mix(frame1->alpha, frame2->alpha, factor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelNode::getSelfIllumColor(float time, glm::vec3 &color) const {
|
||||
const Keyframe *frame1, *frame2;
|
||||
float factor;
|
||||
|
||||
if (getKeyframes(_selfIllumFrames, time, frame1, frame2, factor)) {
|
||||
color = glm::mix(frame1->selfIllumColor, frame2->selfIllumColor, factor);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ModelNode::getTranslation(int leftFrameIdx, int rightFrameIdx, float interpolant, glm::vec3 &translation, float scale) const {
|
||||
if (leftFrameIdx < 0 || leftFrameIdx >= static_cast<int>(_translationFrames.size()) ||
|
||||
rightFrameIdx < 0 || rightFrameIdx >= static_cast<int>(_translationFrames.size())) return false;
|
||||
|
|
|
@ -33,8 +33,11 @@ namespace render {
|
|||
/**
|
||||
* 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, model nodes contain translation,
|
||||
* orientation and scale keyframes.
|
||||
* 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.
|
||||
*
|
||||
|
@ -43,19 +46,15 @@ namespace render {
|
|||
*/
|
||||
class ModelNode : boost::noncopyable {
|
||||
public:
|
||||
struct TranslationKeyframe {
|
||||
struct Keyframe {
|
||||
float time { 0.0f };
|
||||
glm::vec3 translation { 0.0f };
|
||||
union {
|
||||
glm::quat orientation { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
glm::vec3 translation;
|
||||
float scale;
|
||||
float alpha;
|
||||
glm::vec3 selfIllumColor;
|
||||
};
|
||||
|
||||
struct OrientationKeyframe {
|
||||
float time { 0.0f };
|
||||
glm::quat orientation { 1.0f, 0.0f, 0.0f, 0.0f };
|
||||
};
|
||||
|
||||
struct ScaleKeyframe {
|
||||
float time { 0.0f };
|
||||
float scale { 0.0f };
|
||||
};
|
||||
|
||||
struct Light {
|
||||
|
@ -94,15 +93,16 @@ public:
|
|||
*/
|
||||
void computeAbsoluteTransforms();
|
||||
|
||||
void addTranslationKeyframe(TranslationKeyframe keyframe);
|
||||
void addOrientationKeyframe(OrientationKeyframe keyframe);
|
||||
void addTranslationKeyframe(Keyframe keyframe);
|
||||
void addOrientationKeyframe(Keyframe keyframe);
|
||||
|
||||
bool isSelfIllumEnabled() const { return _selfIllumEnabled; }
|
||||
bool isSaber() const { return _saber; }
|
||||
|
||||
bool getTranslation(float time, glm::vec3 &translation, float scale = 1.0f) const;
|
||||
bool getOrientation(float time, glm::quat &orientation) const;
|
||||
bool getScale(float time, float &scale) const;
|
||||
bool getAlpha(float time, float &alpha) const;
|
||||
bool getSelfIllumColor(float time, glm::vec3 &color) const;
|
||||
|
||||
bool getTranslation(int leftFrameIdx, int rightFrameIdx, float interpolant, glm::vec3 &translation, float scale = 1.0f) const;
|
||||
bool getOrientation(int leftFrameIdx, int rightFrameIdx, float interpolant, glm::quat &orientation) const;
|
||||
|
@ -143,7 +143,6 @@ private:
|
|||
uint16_t _nodeNumber { 0 };
|
||||
std::string _name;
|
||||
glm::vec3 _color { 0.0f };
|
||||
bool _selfIllumEnabled { false };
|
||||
glm::vec3 _selfIllumColor { 0.0f };
|
||||
float _alpha { 1.0f };
|
||||
float _radius { 0.0f };
|
||||
|
@ -169,9 +168,11 @@ private:
|
|||
|
||||
// Keyframes
|
||||
|
||||
std::vector<TranslationKeyframe> _translationFrames;
|
||||
std::vector<OrientationKeyframe> _orientationFrames;
|
||||
std::vector<ScaleKeyframe> _scaleFrames;
|
||||
std::vector<Keyframe> _translationFrames;
|
||||
std::vector<Keyframe> _orientationFrames;
|
||||
std::vector<Keyframe> _scaleFrames;
|
||||
std::vector<Keyframe> _alphaFrames;
|
||||
std::vector<Keyframe> _selfIllumFrames;
|
||||
|
||||
// END Keyframes
|
||||
|
||||
|
|
|
@ -624,12 +624,14 @@ void main() {
|
|||
objectColor *= uWaterAlpha;
|
||||
objectAlpha *= uWaterAlpha;
|
||||
}
|
||||
fragColor = vec4(objectColor, objectAlpha);
|
||||
|
||||
vec3 brightColor = vec3(0.0);
|
||||
if (isFeatureEnabled(FEATURE_SELFILLUM)) {
|
||||
if (isFeatureEnabled(FEATURE_SELFILLUM) && !isFeatureEnabled(FEATURE_WATER)) {
|
||||
objectColor *= uSelfIllumColor.rgb;
|
||||
brightColor = smoothstep(SELFILLUM_THRESHOLD, 1.0, uSelfIllumColor.rgb * diffuseSample.rgb * objectAlpha);
|
||||
}
|
||||
|
||||
fragColor = vec4(objectColor, objectAlpha);
|
||||
fragColorBright = vec4(brightColor, 1.0);
|
||||
}
|
||||
)END";
|
||||
|
@ -769,12 +771,13 @@ void main() {
|
|||
objectColor = pow(objectColor, vec3(1.0 / GAMMA));
|
||||
}
|
||||
|
||||
fragColor = vec4(objectColor, objectAlpha);
|
||||
|
||||
vec3 brightColor = vec3(0.0);
|
||||
if (isFeatureEnabled(FEATURE_SELFILLUM)) {
|
||||
if (isFeatureEnabled(FEATURE_SELFILLUM) && !isFeatureEnabled(FEATURE_WATER)) {
|
||||
objectColor *= uSelfIllumColor.rgb;
|
||||
brightColor = smoothstep(SELFILLUM_THRESHOLD, 1.0, uSelfIllumColor.rgb * diffuseSample.rgb * objectAlpha);
|
||||
}
|
||||
|
||||
fragColor = vec4(objectColor, objectAlpha);
|
||||
fragColorBright = vec4(brightColor, 1.0);
|
||||
}
|
||||
)END";
|
||||
|
|
|
@ -83,7 +83,8 @@ void AnimationChannel::update(float dt) {
|
|||
}
|
||||
}
|
||||
|
||||
computeLocalTransforms();
|
||||
_stateByNumber.clear();
|
||||
computeSceneNodeStates(*_animation->rootNode());
|
||||
|
||||
_time = newTime;
|
||||
|
||||
|
@ -98,12 +99,7 @@ void AnimationChannel::update(float dt) {
|
|||
}
|
||||
}
|
||||
|
||||
void AnimationChannel::computeLocalTransforms() {
|
||||
_transformByNodeNumber.clear();
|
||||
computeLocalTransform(*_animation->rootNode());
|
||||
}
|
||||
|
||||
void AnimationChannel::computeLocalTransform(const ModelNode &animNode) {
|
||||
void AnimationChannel::computeSceneNodeStates(const ModelNode &animNode) {
|
||||
if (_ignoreNodes.count(animNode.name()) == 0) {
|
||||
ModelNodeSceneNode *modelNodeSceneNode = _modelSceneNode->getModelNode(animNode.name());
|
||||
if (modelNodeSceneNode) {
|
||||
|
@ -151,18 +147,31 @@ void AnimationChannel::computeLocalTransform(const ModelNode &animNode) {
|
|||
}
|
||||
}
|
||||
|
||||
SceneNodeState state;
|
||||
float alpha;
|
||||
if (animNode.getAlpha(_time, alpha)) {
|
||||
state.flags |= SceneNodeStateFlags::alpha;
|
||||
state.alpha = alpha;
|
||||
}
|
||||
glm::vec3 selfIllumColor;
|
||||
if (animNode.getSelfIllumColor(_time, selfIllumColor)) {
|
||||
state.flags |= SceneNodeStateFlags::selfIllum;
|
||||
state.selfIllumColor = move(selfIllumColor);
|
||||
}
|
||||
if (transformChanged) {
|
||||
glm::mat4 transform(1.0f);
|
||||
transform = glm::scale(transform, glm::vec3(scale));
|
||||
transform = glm::translate(transform, translation);
|
||||
transform *= glm::mat4_cast(orientation);
|
||||
_transformByNodeNumber.insert(make_pair(modelNode->nodeNumber(), move(transform)));
|
||||
state.flags |= SceneNodeStateFlags::transform;
|
||||
state.transform = move(transform);
|
||||
}
|
||||
_stateByNumber.insert(make_pair(modelNode->nodeNumber(), move(state)));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &child : animNode.children()) {
|
||||
computeLocalTransform(*child);
|
||||
computeSceneNodeStates(*child);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,18 +195,19 @@ bool AnimationChannel::isFinished() const {
|
|||
return _animation && _finished;
|
||||
}
|
||||
|
||||
bool AnimationChannel::getTransformByNodeNumber(uint16_t nodeNumber, glm::mat4 &transform) const {
|
||||
auto maybeTransform = _transformByNodeNumber.find(nodeNumber);
|
||||
if (maybeTransform != _transformByNodeNumber.end()) {
|
||||
transform = maybeTransform->second;
|
||||
}
|
||||
return maybeTransform != _transformByNodeNumber.end();
|
||||
}
|
||||
|
||||
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()) {
|
||||
state = maybeState->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AnimationChannel::setTime(float time) {
|
||||
_time = time;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "../../render/model/modelnode.h"
|
||||
|
||||
#include "properties.h"
|
||||
#include "scenenodestate.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
|
@ -72,8 +73,8 @@ public:
|
|||
*/
|
||||
bool isFinished() const;
|
||||
|
||||
bool getTransformByNodeNumber(uint16_t nodeNumber, glm::mat4 &transform) const;
|
||||
float getTransitionTime() const;
|
||||
bool getSceneNodeStateByNumber(uint16_t nodeNumber, SceneNodeState &state) const;
|
||||
|
||||
float time() const { return _time; }
|
||||
|
||||
|
@ -89,10 +90,9 @@ private:
|
|||
float _time { 0.0f };
|
||||
bool _freeze { false };
|
||||
bool _finished { false };
|
||||
std::unordered_map<uint16_t, glm::mat4> _transformByNodeNumber;
|
||||
std::unordered_map<uint16_t, SceneNodeState> _stateByNumber;
|
||||
|
||||
void computeLocalTransforms();
|
||||
void computeLocalTransform(const render::ModelNode &animNode);
|
||||
void computeSceneNodeStates(const render::ModelNode &animNode);
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
|
|
@ -65,10 +65,10 @@ void SceneNodeAnimator::update(float dt) {
|
|||
channel.update(dt);
|
||||
}
|
||||
|
||||
// Compute and apply absolute transforms to the managed model scene node
|
||||
_transformByNodeNumber.clear();
|
||||
computeAbsoluteTransforms(*_modelSceneNode->model()->rootNode());
|
||||
applyAbsoluteTransforms(*_modelSceneNode->model()->rootNode());
|
||||
// Compute and apply node states to the managed model
|
||||
_stateByNumber.clear();
|
||||
computeSceneNodeStates(*_modelSceneNode->model()->rootNode());
|
||||
applySceneNodeStates(*_modelSceneNode->model()->rootNode());
|
||||
}
|
||||
|
||||
void SceneNodeAnimator::playDefaultAnimation() {
|
||||
|
@ -81,65 +81,143 @@ bool SceneNodeAnimator::isInTransition() const {
|
|||
return _compositionMode == CompositionMode::Blend && _transition;
|
||||
}
|
||||
|
||||
void SceneNodeAnimator::computeAbsoluteTransforms(ModelNode &modelNode, glm::mat4 parentTransform) {
|
||||
void SceneNodeAnimator::computeSceneNodeStates(ModelNode &modelNode, glm::mat4 parentTransform) {
|
||||
if (modelNode.skin()) return;
|
||||
|
||||
SceneNodeState state;
|
||||
state.flags |= SceneNodeStateFlags::transform;
|
||||
|
||||
glm::mat4 localTransform(modelNode.localTransform());
|
||||
|
||||
if (isInTransition()) {
|
||||
// In the Blend mode, blend animations on the first two channels
|
||||
glm::mat4 transform1, transform2;
|
||||
bool hasTransform1 = _channels[0].getTransformByNodeNumber(modelNode.nodeNumber(), transform1);
|
||||
bool hasTransform2 = _channels[1].getTransformByNodeNumber(modelNode.nodeNumber(), transform2);
|
||||
if (hasTransform1 && hasTransform2) {
|
||||
float delta = 1.0f - (_channels[0].getTransitionTime() - _channels[0].time()) / _channels[0].getTransitionTime();
|
||||
glm::quat orientation0(glm::toQuat(transform1));
|
||||
glm::quat orientation1(glm::toQuat(transform2));
|
||||
localTransform = glm::translate(glm::mat4(1.0f), glm::vec3(transform1[3]));
|
||||
|
||||
// In the Blend mode, blend animations on the first two channels
|
||||
SceneNodeState channel0State, channel1State;
|
||||
bool hasChannel0State = _channels[0].getSceneNodeStateByNumber(modelNode.nodeNumber(), channel0State);
|
||||
bool hasChannel1State = _channels[1].getSceneNodeStateByNumber(modelNode.nodeNumber(), channel1State);
|
||||
if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::transform) &&
|
||||
hasChannel1State && (channel1State.flags & SceneNodeStateFlags::transform)) {
|
||||
|
||||
glm::quat orientation0(glm::toQuat(channel0State.transform));
|
||||
glm::quat orientation1(glm::toQuat(channel1State.transform));
|
||||
localTransform = glm::translate(glm::mat4(1.0f), glm::vec3(channel0State.transform[3]));
|
||||
localTransform *= glm::mat4_cast(glm::slerp(orientation1, orientation0, delta));
|
||||
} else if (hasTransform1) {
|
||||
localTransform = move(transform1);
|
||||
} else if (hasTransform2) {
|
||||
localTransform = move(transform2);
|
||||
|
||||
} else if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::transform)) {
|
||||
localTransform = move(channel0State.transform);
|
||||
|
||||
} else if (hasChannel1State && (channel1State.flags & SceneNodeStateFlags::transform)) {
|
||||
localTransform = move(channel1State.transform);
|
||||
}
|
||||
if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::alpha) &&
|
||||
hasChannel1State && (channel1State.flags & SceneNodeStateFlags::alpha)) {
|
||||
|
||||
state.flags |= SceneNodeStateFlags::alpha;
|
||||
state.alpha = glm::mix(channel1State.alpha, channel0State.alpha, delta);
|
||||
|
||||
} else if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::alpha)) {
|
||||
state.flags |= SceneNodeStateFlags::alpha;
|
||||
state.alpha = channel0State.alpha;
|
||||
|
||||
} else if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::alpha)) {
|
||||
state.flags |= SceneNodeStateFlags::alpha;
|
||||
state.alpha = channel1State.alpha;
|
||||
}
|
||||
if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::selfIllum) &&
|
||||
hasChannel1State && (channel1State.flags & SceneNodeStateFlags::selfIllum)) {
|
||||
|
||||
state.flags |= SceneNodeStateFlags::selfIllum;
|
||||
state.selfIllumColor = glm::mix(channel1State.selfIllumColor, channel0State.selfIllumColor, delta);
|
||||
|
||||
} else if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::selfIllum)) {
|
||||
state.flags |= SceneNodeStateFlags::selfIllum;
|
||||
state.selfIllumColor = channel0State.selfIllumColor;
|
||||
|
||||
} else if (hasChannel0State && (channel0State.flags & SceneNodeStateFlags::selfIllum)) {
|
||||
state.flags |= SceneNodeStateFlags::selfIllum;
|
||||
state.selfIllumColor = channel1State.selfIllumColor;
|
||||
}
|
||||
} else if (_compositionMode == CompositionMode::Overlay) {
|
||||
// In the Overlay mode, select the first animation channel to have a local transform for the given node
|
||||
// In the Overlay mode, for each state component select the first animation channel to have state for the given node
|
||||
for (int i = kChannelCount - 1; i >= 0; --i) {
|
||||
glm::mat4 transform;
|
||||
if (_channels[i].isActive() && _channels[i].getTransformByNodeNumber(modelNode.nodeNumber(), transform)) {
|
||||
localTransform = move(transform);
|
||||
SceneNodeState channelState;
|
||||
if (_channels[i].isActive() && _channels[i].getSceneNodeStateByNumber(modelNode.nodeNumber(), channelState)) {
|
||||
if (channelState.flags & SceneNodeStateFlags::transform) {
|
||||
localTransform = move(channelState.transform);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = kChannelCount - 1; i >= 0; --i) {
|
||||
SceneNodeState channelState;
|
||||
if (_channels[i].isActive() && _channels[i].getSceneNodeStateByNumber(modelNode.nodeNumber(), channelState)) {
|
||||
if (channelState.flags & SceneNodeStateFlags::alpha) {
|
||||
state.flags |= SceneNodeStateFlags::alpha;
|
||||
state.alpha = channelState.alpha;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = kChannelCount - 1; i >= 0; --i) {
|
||||
SceneNodeState channelState;
|
||||
if (_channels[i].isActive() && _channels[i].getSceneNodeStateByNumber(modelNode.nodeNumber(), channelState)) {
|
||||
if (channelState.flags & SceneNodeStateFlags::selfIllum) {
|
||||
state.flags |= SceneNodeStateFlags::selfIllum;
|
||||
state.selfIllumColor = move(channelState.selfIllumColor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise, select animation on the first channel
|
||||
glm::mat4 transform;
|
||||
if (_channels[0].getTransformByNodeNumber(modelNode.nodeNumber(), transform)) {
|
||||
localTransform = move(transform);
|
||||
SceneNodeState channelState;
|
||||
if (_channels[0].getSceneNodeStateByNumber(modelNode.nodeNumber(), channelState)) {
|
||||
if (channelState.flags & SceneNodeStateFlags::transform) {
|
||||
localTransform = move(channelState.transform);
|
||||
}
|
||||
if (channelState.flags & SceneNodeStateFlags::alpha) {
|
||||
state.flags |= SceneNodeStateFlags::alpha;
|
||||
state.alpha = channelState.alpha;
|
||||
}
|
||||
if (channelState.flags & SceneNodeStateFlags::selfIllum) {
|
||||
state.flags |= SceneNodeStateFlags::selfIllum;
|
||||
state.selfIllumColor = move(channelState.selfIllumColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::mat4 absTransform(parentTransform * localTransform);
|
||||
_transformByNodeNumber.insert(make_pair(modelNode.nodeNumber(), absTransform));
|
||||
state.transform = absTransform;
|
||||
_stateByNumber.insert(make_pair(modelNode.nodeNumber(), move(state)));
|
||||
|
||||
for (auto &child : modelNode.children()) {
|
||||
computeAbsoluteTransforms(*child, absTransform);
|
||||
computeSceneNodeStates(*child, absTransform);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneNodeAnimator::applyAbsoluteTransforms(ModelNode &modelNode) {
|
||||
void SceneNodeAnimator::applySceneNodeStates(ModelNode &modelNode) {
|
||||
// Do not apply transforms to skinned model nodes
|
||||
if (modelNode.skin()) return;
|
||||
|
||||
auto maybeTransform = _transformByNodeNumber.find(modelNode.nodeNumber());
|
||||
if (maybeTransform != _transformByNodeNumber.end()) {
|
||||
auto maybeState = _stateByNumber.find(modelNode.nodeNumber());
|
||||
if (maybeState != _stateByNumber.end()) {
|
||||
const SceneNodeState &state = maybeState->second;
|
||||
ModelNodeSceneNode *sceneNode = _modelSceneNode->getModelNodeByIndex(modelNode.index());
|
||||
sceneNode->setLocalTransform(maybeTransform->second);
|
||||
sceneNode->setBoneTransform(maybeTransform->second * modelNode.absoluteTransformInverse());
|
||||
if (state.flags & SceneNodeStateFlags::transform) {
|
||||
sceneNode->setLocalTransform(state.transform);
|
||||
sceneNode->setBoneTransform(state.transform * modelNode.absoluteTransformInverse());
|
||||
}
|
||||
if (state.flags & SceneNodeStateFlags::alpha) {
|
||||
sceneNode->setAlpha(state.alpha);
|
||||
}
|
||||
if (state.flags & SceneNodeStateFlags::selfIllum) {
|
||||
sceneNode->setSelfIllumColor(state.selfIllumColor);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &child : modelNode.children()) {
|
||||
applyAbsoluteTransforms(*child);
|
||||
applySceneNodeStates(*child);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "channel.h"
|
||||
#include "properties.h"
|
||||
#include "scenenodestate.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
|
@ -69,19 +70,25 @@ private:
|
|||
Blend /**< animation on the second channel is transitioned into animation on the first channel */
|
||||
};
|
||||
|
||||
struct NodeState {
|
||||
glm::mat4 transform { 1.0f };
|
||||
float alpha { 1.0f };
|
||||
glm::vec3 selfIllumColor { 0.0f };
|
||||
};
|
||||
|
||||
ModelSceneNode *_modelSceneNode;
|
||||
std::set<std::string> _ignoreNodes;
|
||||
std::vector<AnimationChannel> _channels;
|
||||
|
||||
CompositionMode _compositionMode { CompositionMode::Overlay };
|
||||
bool _transition { false }; /**< is there an animation transition going on? */
|
||||
std::unordered_map<uint16_t, glm::mat4> _transformByNodeNumber;
|
||||
std::unordered_map<uint16_t, SceneNodeState> _stateByNumber;
|
||||
|
||||
std::string _defaultAnimName;
|
||||
AnimationProperties _defaultAnimProperties;
|
||||
|
||||
void computeAbsoluteTransforms(render::ModelNode &modelNode, glm::mat4 parentTransform = glm::mat4(1.0f));
|
||||
void applyAbsoluteTransforms(render::ModelNode &modelNode);
|
||||
void computeSceneNodeStates(render::ModelNode &modelNode, glm::mat4 parentTransform = glm::mat4(1.0f));
|
||||
void applySceneNodeStates(render::ModelNode &modelNode);
|
||||
|
||||
bool isInTransition() const;
|
||||
|
||||
|
|
41
src/scene/animation/scenenodestate.h
Normal file
41
src/scene/animation/scenenodestate.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 "glm/mat4x4.hpp"
|
||||
#include "glm/vec3.hpp"
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace scene {
|
||||
|
||||
/**
|
||||
* @see SceneNodeStateFlags
|
||||
*/
|
||||
struct SceneNodeState {
|
||||
int flags { 0 };
|
||||
glm::mat4 transform { 1.0f };
|
||||
float alpha { 1.0f };
|
||||
glm::vec3 selfIllumColor { 0.0f };
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
||||
} // namespace reone
|
|
@ -58,6 +58,10 @@ ModelNodeSceneNode::ModelNodeSceneNode(SceneGraph *sceneGraph, const ModelSceneN
|
|||
if (!modelNode) {
|
||||
throw invalid_argument("modelNode must not be null");
|
||||
}
|
||||
|
||||
_alpha = _modelNode->alpha();
|
||||
_selfIllumColor = _modelNode->selfIllumColor();
|
||||
|
||||
initTextures();
|
||||
}
|
||||
|
||||
|
@ -145,7 +149,7 @@ bool ModelNodeSceneNode::isTransparent() const {
|
|||
if (_modelSceneNode->model()->classification() == Model::Classification::Character) return false;
|
||||
|
||||
// Model nodes with alpha less than 1.0 are transparent
|
||||
if (_modelNode->alpha() < 1.0f) return true;
|
||||
if (_alpha < 1.0f) return true;
|
||||
|
||||
// Model nodes without a diffuse texture are opaque
|
||||
if (!_textures.diffuse) return false;
|
||||
|
@ -173,11 +177,15 @@ static bool isLightingEnabledByClassification(ModelSceneNode::Classification cla
|
|||
return classification != ModelSceneNode::Classification::Projectile;
|
||||
}
|
||||
|
||||
static bool isReceivingShadows(const ModelSceneNode &modelSceneNode, const ModelNode &modelNode) {
|
||||
bool ModelNodeSceneNode::isSelfIlluminated() const {
|
||||
return glm::dot(_selfIllumColor, _selfIllumColor) > 0.0f;
|
||||
}
|
||||
|
||||
static bool isReceivingShadows(const ModelSceneNode &model, const ModelNodeSceneNode &modelNode) {
|
||||
// Only room models receive shadows, unless model node is self-illuminated
|
||||
return
|
||||
modelSceneNode.classification() == ModelSceneNode::Classification::Room &&
|
||||
!modelNode.isSelfIllumEnabled();
|
||||
model.classification() == ModelSceneNode::Classification::Room &&
|
||||
!modelNode.isSelfIlluminated();
|
||||
}
|
||||
|
||||
void ModelNodeSceneNode::renderSingle(bool shadowPass) {
|
||||
|
@ -191,7 +199,7 @@ void ModelNodeSceneNode::renderSingle(bool shadowPass) {
|
|||
uniforms.general.featureMask |= UniformFeatureFlags::hdr;
|
||||
}
|
||||
uniforms.general.model = _absoluteTransform;
|
||||
uniforms.general.alpha = _modelSceneNode->alpha() * _modelNode->alpha();
|
||||
uniforms.general.alpha = _modelSceneNode->alpha() * _alpha;
|
||||
uniforms.general.ambientColor = glm::vec4(_sceneGraph->ambientLightColor(), 1.0f);
|
||||
uniforms.general.exposure = _sceneGraph->exposure();
|
||||
|
||||
|
@ -231,7 +239,7 @@ void ModelNodeSceneNode::renderSingle(bool shadowPass) {
|
|||
uniforms.bumpmap.swizzled = mesh->isBumpmapSwizzled();
|
||||
}
|
||||
|
||||
bool receivesShadows = isReceivingShadows(*_modelSceneNode, *_modelNode);
|
||||
bool receivesShadows = isReceivingShadows(*_modelSceneNode, *this);
|
||||
if (receivesShadows) {
|
||||
uniforms.general.featureMask |= UniformFeatureFlags::shadows;
|
||||
}
|
||||
|
@ -254,9 +262,9 @@ void ModelNodeSceneNode::renderSingle(bool shadowPass) {
|
|||
}
|
||||
}
|
||||
|
||||
if (_modelNode->isSelfIllumEnabled()) {
|
||||
if (isSelfIlluminated()) {
|
||||
uniforms.general.featureMask |= UniformFeatureFlags::selfIllum;
|
||||
uniforms.general.selfIllumColor = glm::vec4(_modelNode->selfIllumColor(), 1.0f);
|
||||
uniforms.general.selfIllumColor = glm::vec4(_selfIllumColor, 1.0f);
|
||||
}
|
||||
if (isLightingEnabled()) {
|
||||
const vector<LightSceneNode *> &lights = _modelSceneNode->lightsAffectedBy();
|
||||
|
@ -343,8 +351,8 @@ bool ModelNodeSceneNode::isLightingEnabled() const {
|
|||
// Lighting is disabled for lightmapped models, unless dynamic room lighting is enabled
|
||||
if (_textures.lightmap && !isFeatureEnabled(Feature::DynamicRoomLighting)) return false;
|
||||
|
||||
// Lighting is disabled for self-illuminated room model nodes, e.g. the skybox
|
||||
if (_modelNode->isSelfIllumEnabled() && _modelSceneNode->classification() == ModelSceneNode::Classification::Room) return false;
|
||||
// Lighting is disabled for self-illuminated model nodes, e.g. sky boxes
|
||||
if (isSelfIlluminated()) return false;
|
||||
|
||||
// Lighting is disabled when diffuse texture is additive
|
||||
if (_textures.diffuse && _textures.diffuse->isAdditive()) return false;
|
||||
|
@ -366,6 +374,14 @@ void ModelNodeSceneNode::setDiffuseTexture(const shared_ptr<Texture> &texture) {
|
|||
refreshAdditionalTextures();
|
||||
}
|
||||
|
||||
void ModelNodeSceneNode::setAlpha(float alpha) {
|
||||
_alpha = alpha;
|
||||
}
|
||||
|
||||
void ModelNodeSceneNode::setSelfIllumColor(glm::vec3 color) {
|
||||
_selfIllumColor = move(color);
|
||||
}
|
||||
|
||||
} // namespace scene
|
||||
|
||||
} // namespace reone
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
glm::vec3 getOrigin() const override;
|
||||
|
||||
bool isTransparent() const override;
|
||||
bool isSelfIlluminated() const;
|
||||
|
||||
const ModelSceneNode *modelSceneNode() const { return _modelSceneNode; }
|
||||
const render::ModelNode *modelNode() const { return _modelNode; }
|
||||
|
@ -50,6 +51,8 @@ public:
|
|||
|
||||
void setBoneTransform(const glm::mat4 &transform);
|
||||
void setDiffuseTexture(const std::shared_ptr<render::Texture> &texture);
|
||||
void setAlpha(float alpha);
|
||||
void setSelfIllumColor(glm::vec3 color);
|
||||
|
||||
private:
|
||||
struct NodeTextures {
|
||||
|
@ -68,6 +71,8 @@ private:
|
|||
glm::vec2 _uvOffset { 0.0f };
|
||||
float _bumpmapTime { 0.0f };
|
||||
int _bumpmapFrame { 0 };
|
||||
float _alpha { 1.0f };
|
||||
glm::vec3 _selfIllumColor { 0.0f };
|
||||
|
||||
void initTextures();
|
||||
|
||||
|
|
|
@ -113,7 +113,9 @@ void ModelSceneNode::initModelNodes() {
|
|||
nodes.push(childNode.get());
|
||||
|
||||
// Optionally convert self-illuminated model nodes to light sources
|
||||
if (isFeatureEnabled(Feature::SelfIllumAsLights) && child->isSelfIllumEnabled()) {
|
||||
if (isFeatureEnabled(Feature::SelfIllumAsLights)) {
|
||||
glm::vec3 color;
|
||||
if (child->getSelfIllumColor(0.0f, color) && glm::dot(color, color) > 0.0f) {
|
||||
float radius;
|
||||
shared_ptr<ModelMesh> mesh(child->mesh());
|
||||
if (mesh) {
|
||||
|
@ -122,10 +124,11 @@ void ModelSceneNode::initModelNodes() {
|
|||
} else {
|
||||
radius = 1.0f;
|
||||
}
|
||||
auto lightNode = make_shared<LightSceneNode>(child->selfIllumColor(), kSelfIlluminatedPriority, _sceneGraph);
|
||||
auto lightNode = make_shared<LightSceneNode>(move(color), kSelfIlluminatedPriority, _sceneGraph);
|
||||
lightNode->setRadius(radius);
|
||||
childNode->addChild(lightNode);
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<ModelNode::Light> light(child->light());
|
||||
if (light) {
|
||||
|
|
|
@ -23,15 +23,21 @@ namespace scene {
|
|||
|
||||
struct AnimationFlags {
|
||||
static constexpr int loop = 1;
|
||||
static constexpr int blend = 4; /**< blend previous animation into the next one */
|
||||
static constexpr int overlay = 8; /**< overlay next animation on top of the previous one */
|
||||
static constexpr int propagateHead = 0x10; /**< propagate animation to the head model, if any */
|
||||
static constexpr int syncLipAnim = 0x20; /**< animation must be synchronized with the lip animation */
|
||||
static constexpr int blend = 2; /**< blend previous animation into the next one */
|
||||
static constexpr int overlay = 4; /**< overlay next animation on top of the previous one */
|
||||
static constexpr int propagateHead = 8; /**< propagate animation to the head model, if any */
|
||||
static constexpr int syncLipAnim = 0x10; /**< animation must be synchronized with the lip animation */
|
||||
|
||||
static constexpr int loopOverlay = loop | overlay;
|
||||
static constexpr int loopBlend = loop | blend;
|
||||
};
|
||||
|
||||
struct SceneNodeStateFlags {
|
||||
static constexpr int transform = 1;
|
||||
static constexpr int alpha = 2;
|
||||
static constexpr int selfIllum = 4;
|
||||
};
|
||||
|
||||
} // namespace scene
|
||||
|
||||
} // namespace reone
|
||||
|
|
Loading…
Reference in a new issue