diff --git a/CMakeLists.txt b/CMakeLists.txt index 64c4bfd2..54467444 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/experimental/tor/jbafile.cpp b/src/experimental/tor/jbafile.cpp index 5af5686d..0d50f1cf 100644 --- a/src/experimental/tor/jbafile.cpp +++ b/src/experimental/tor/jbafile.cpp @@ -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)); diff --git a/src/render/model/mdlfile.cpp b/src/render/model/mdlfile.cpp index da44d444..c681ef3e 100644 --- a/src/render/model/mdlfile.cpp +++ b/src/render/model/mdlfile.cpp @@ -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 &data, node._color.b = data[dataIndex + 2]; } -void MdlFile::readSelfIllumColorController(uint16_t dataIndex, const vector &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 &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 &data, ModelNode &node) { - node._alpha = data[dataIndex]; +void MdlFile::readAlphaController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const vector &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 &data, ModelNode &node) { diff --git a/src/render/model/mdlfile.h b/src/render/model/mdlfile.h index ba421529..c70bfb33 100644 --- a/src/render/model/mdlfile.h +++ b/src/render/model/mdlfile.h @@ -64,7 +64,7 @@ private: // Controllers - void readAlphaController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); + void readAlphaController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readAlphaEndController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readAlphaMidController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readAlphaStartController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); @@ -83,7 +83,7 @@ private: void readRadiusController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readRandomVelocityController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readScaleController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const std::vector &data, render::ModelNode &node); - void readSelfIllumColorController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); + void readSelfIllumColorController(uint16_t rowCount, uint16_t timeIndex, uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readSizeEndController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readSizeMidController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); void readSizeStartController(uint16_t dataIndex, const std::vector &data, render::ModelNode &node); diff --git a/src/render/model/modelnode.cpp b/src/render/model/modelnode.cpp index a641b5d1..2b858aa0 100644 --- a/src/render/model/modelnode.cpp +++ b/src/render/model/modelnode.cpp @@ -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 &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; - return true; + 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 { diff --git a/src/render/model/modelnode.h b/src/render/model/modelnode.h index 15afbed4..5224bce3 100644 --- a/src/render/model/modelnode.h +++ b/src/render/model/modelnode.h @@ -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 }; - }; - - 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 }; + union { + glm::quat orientation { 0.0f, 0.0f, 0.0f, 0.0f }; + glm::vec3 translation; + float scale; + float alpha; + glm::vec3 selfIllumColor; + }; }; 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 _translationFrames; - std::vector _orientationFrames; - std::vector _scaleFrames; + std::vector _translationFrames; + std::vector _orientationFrames; + std::vector _scaleFrames; + std::vector _alphaFrames; + std::vector _selfIllumFrames; // END Keyframes diff --git a/src/render/shaders.cpp b/src/render/shaders.cpp index f284aa68..1b2aeca1 100644 --- a/src/render/shaders.cpp +++ b/src/render/shaders.cpp @@ -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"; diff --git a/src/scene/animation/channel.cpp b/src/scene/animation/channel.cpp index 9b76aba9..58ccb077 100644 --- a/src/scene/animation/channel.cpp +++ b/src/scene/animation/channel.cpp @@ -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; } diff --git a/src/scene/animation/channel.h b/src/scene/animation/channel.h index 046b0724..a43bb307 100644 --- a/src/scene/animation/channel.h +++ b/src/scene/animation/channel.h @@ -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 _transformByNodeNumber; + std::unordered_map _stateByNumber; - void computeLocalTransforms(); - void computeLocalTransform(const render::ModelNode &animNode); + void computeSceneNodeStates(const render::ModelNode &animNode); }; } // namespace scene diff --git a/src/scene/animation/scenenodeanimator.cpp b/src/scene/animation/scenenodeanimator.cpp index 5a2e6278..c48325f5 100644 --- a/src/scene/animation/scenenodeanimator.cpp +++ b/src/scene/animation/scenenodeanimator.cpp @@ -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()) { + float delta = 1.0f - (_channels[0].getTransitionTime() - _channels[0].time()) / _channels[0].getTransitionTime(); + // 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])); + 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); - break; + 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); } } diff --git a/src/scene/animation/scenenodeanimator.h b/src/scene/animation/scenenodeanimator.h index 6414d457..e19fe8a2 100644 --- a/src/scene/animation/scenenodeanimator.h +++ b/src/scene/animation/scenenodeanimator.h @@ -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 _ignoreNodes; std::vector _channels; CompositionMode _compositionMode { CompositionMode::Overlay }; bool _transition { false }; /**< is there an animation transition going on? */ - std::unordered_map _transformByNodeNumber; + std::unordered_map _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; diff --git a/src/scene/animation/scenenodestate.h b/src/scene/animation/scenenodestate.h new file mode 100644 index 00000000..2f592ac8 --- /dev/null +++ b/src/scene/animation/scenenodestate.h @@ -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 . + */ + +#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 diff --git a/src/scene/node/modelnodescenenode.cpp b/src/scene/node/modelnodescenenode.cpp index d42cac18..892f74f4 100644 --- a/src/scene/node/modelnodescenenode.cpp +++ b/src/scene/node/modelnodescenenode.cpp @@ -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 &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) { refreshAdditionalTextures(); } +void ModelNodeSceneNode::setAlpha(float alpha) { + _alpha = alpha; +} + +void ModelNodeSceneNode::setSelfIllumColor(glm::vec3 color) { + _selfIllumColor = move(color); +} + } // namespace scene } // namespace reone diff --git a/src/scene/node/modelnodescenenode.h b/src/scene/node/modelnodescenenode.h index b024b67e..f38f6f6c 100644 --- a/src/scene/node/modelnodescenenode.h +++ b/src/scene/node/modelnodescenenode.h @@ -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 &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(); diff --git a/src/scene/node/modelscenenode.cpp b/src/scene/node/modelscenenode.cpp index abff6aaf..1abd0371 100644 --- a/src/scene/node/modelscenenode.cpp +++ b/src/scene/node/modelscenenode.cpp @@ -113,18 +113,21 @@ void ModelSceneNode::initModelNodes() { nodes.push(childNode.get()); // Optionally convert self-illuminated model nodes to light sources - if (isFeatureEnabled(Feature::SelfIllumAsLights) && child->isSelfIllumEnabled()) { - float radius; - shared_ptr mesh(child->mesh()); - if (mesh) { - glm::vec3 size(mesh->mesh()->aabb().getSize()); - radius = glm::max(1.0f, glm::sqrt(glm::dot(size, size))); - } else { - radius = 1.0f; + if (isFeatureEnabled(Feature::SelfIllumAsLights)) { + glm::vec3 color; + if (child->getSelfIllumColor(0.0f, color) && glm::dot(color, color) > 0.0f) { + float radius; + shared_ptr mesh(child->mesh()); + if (mesh) { + glm::vec3 size(mesh->mesh()->aabb().getSize()); + radius = glm::max(1.0f, glm::sqrt(glm::dot(size, size))); + } else { + radius = 1.0f; + } + auto lightNode = make_shared(move(color), kSelfIlluminatedPriority, _sceneGraph); + lightNode->setRadius(radius); + childNode->addChild(lightNode); } - auto lightNode = make_shared(child->selfIllumColor(), kSelfIlluminatedPriority, _sceneGraph); - lightNode->setRadius(radius); - childNode->addChild(lightNode); } shared_ptr light(child->light()); diff --git a/src/scene/types.h b/src/scene/types.h index 1795b0d4..2891bae5 100644 --- a/src/scene/types.h +++ b/src/scene/types.h @@ -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