feat: Make alpha and self-illumination color animatable properties

This commit is contained in:
Vsevolod Kremianskii 2021-02-24 21:23:29 +07:00
parent 6086aa433d
commit 1eb43b00a1
16 changed files with 385 additions and 178 deletions

View file

@ -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

View file

@ -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));

View file

@ -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;
}
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 dataIndex, const vector<float> &data, ModelNode &node) {
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) {

View file

@ -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);

View file

@ -76,90 +76,103 @@ 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;
}
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;
}
float factor = (time - left->time) / (right->time - left->time);
translation = glm::mix(left->translation, right->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 {

View file

@ -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

View file

@ -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";

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}
}

View file

@ -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;

View 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

View file

@ -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

View file

@ -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();

View file

@ -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) {

View file

@ -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