diff --git a/cmake/cppcheck-suppression-list.txt b/cmake/cppcheck-suppression-list.txt index 4a7735e9e..0c8b0c7a7 100644 --- a/cmake/cppcheck-suppression-list.txt +++ b/cmake/cppcheck-suppression-list.txt @@ -191,7 +191,8 @@ duplInheritedMember:*/src/vsg/nodes/Node.cpp // suppress unhelpful warning of shadowFunction shadowFunction:*/include/vsg/maths/transform.h shadowFunction:*/src/io/Path.cpp -shadowFunction:*/src/vsg/animation/CameraAnimation.cpp +shadowFunction:*/src/vsg/animation/CameraAnimationHandler.cpp +shadowFunction:*/src/vsg/animation/CameraSampler.cpp shadowFunction:*/src/vsg/animation/TransformSampler.cpp shadowFunction:*/src/vsg/io/tile.cpp shadowFunction:*/src/vsg/io/FileSystem.cpp diff --git a/include/vsg/all.h b/include/vsg/all.h index 0bcc75651..4d514ae65 100644 --- a/include/vsg/all.h +++ b/include/vsg/all.h @@ -85,12 +85,14 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include -#include +#include +#include #include #include #include #include #include +#include // Lighting header files #include diff --git a/include/vsg/animation/CameraAnimation.h b/include/vsg/animation/CameraAnimationHandler.h similarity index 73% rename from include/vsg/animation/CameraAnimation.h rename to include/vsg/animation/CameraAnimationHandler.h index 52f45e722..09116b6f5 100644 --- a/include/vsg/animation/CameraAnimation.h +++ b/include/vsg/animation/CameraAnimationHandler.h @@ -12,7 +12,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ -#include +#include #include #include #include @@ -21,11 +21,12 @@ namespace vsg { /// event handler for controlling the playing and recording of camera animation paths - class VSG_DECLSPEC CameraAnimation : public Inherit + class VSG_DECLSPEC CameraAnimationHandler : public Inherit { public: - explicit CameraAnimation(ref_ptr in_object, const Path& in_filename = "saved_animation.vsgt", ref_ptr in_options = {}); - CameraAnimation(ref_ptr in_object, ref_ptr in_animation, const Path& in_filename = "saved_animation.vsgt", ref_ptr in_options = {}); + CameraAnimationHandler(); + CameraAnimationHandler(ref_ptr in_object, ref_ptr in_animation, const Path& in_filename = "saved_animation.vsgt", ref_ptr in_options = {}); + explicit CameraAnimationHandler(ref_ptr in_object, const Path& in_filename = "saved_animation.vsgt", ref_ptr in_options = {}); /// object to track/modify ref_ptr object; @@ -37,8 +38,8 @@ namespace vsg // animation to play/record to ref_ptr animation; - // transformSampler to play/record to - ref_ptr transformSampler; + // CameraSampler to play/record to + ref_ptr cameraSampler; KeySymbol toggleRecordingKey = KEY_r; KeySymbol togglePlaybackKey = KEY_p; @@ -61,6 +62,9 @@ namespace vsg protected: }; - VSG_type_name(vsg::CameraAnimation); + VSG_type_name(vsg::CameraAnimationHandler); + + // fallback for naming prior to VulkanSceneGraph-1.1.9. + using CameraAnimation = vsg::CameraAnimationHandler; } // namespace vsg diff --git a/include/vsg/animation/CameraSampler.h b/include/vsg/animation/CameraSampler.h new file mode 100644 index 000000000..7cc43d70a --- /dev/null +++ b/include/vsg/animation/CameraSampler.h @@ -0,0 +1,147 @@ +#pragma once + +/* + +Copyright(c) 2024 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include + +namespace vsg +{ + + using time_path = time_value; + + class VSG_DECLSPEC CameraKeyframes : public Inherit + { + public: + CameraKeyframes(); + + /// name of node + std::string name; + + // object tracking key frames + std::vector tracking; + + /// position key frames + std::vector origins; + + /// position key frames + std::vector positions; + + /// rotation key frames + std::vector rotations; + + /// field of view key frames + std::vector fieldOfViews; + + /// near/far key frames + std::vector nearFars; + + void clear() + { + origins.clear(); + positions.clear(); + rotations.clear(); + fieldOfViews.clear(); + nearFars.clear(); + } + + void add(double time, const dvec3& origin, const dvec3& position, const dquat& rotation, double fov, const dvec2& nearFar) + { + origins.push_back(VectorKey{time, origin}); + positions.push_back(VectorKey{time, position}); + rotations.push_back(QuatKey{time, rotation}); + fieldOfViews.push_back(time_double{time, fov}); + nearFars.push_back(time_dvec2{time, nearFar}); + } + + void add(double time, const dvec3& origin, const dvec3& position, const dquat& rotation, double fov) + { + origins.push_back(VectorKey{time, origin}); + positions.push_back(VectorKey{time, position}); + rotations.push_back(QuatKey{time, rotation}); + fieldOfViews.push_back(time_double{time, fov}); + } + + void add(double time, const dvec3& position, const dquat& rotation, double fov, const dvec2& nearFar) + { + positions.push_back(VectorKey{time, position}); + rotations.push_back(QuatKey{time, rotation}); + fieldOfViews.push_back(time_double{time, fov}); + nearFars.push_back(time_dvec2{time, nearFar}); + } + + void add(double time, const dvec3& position, const dquat& rotation, double fov) + { + positions.push_back(VectorKey{time, position}); + rotations.push_back(QuatKey{time, rotation}); + fieldOfViews.push_back(time_double{time, fov}); + } + + void add(double time, const dvec3& origin, const dvec3& position, const dquat& rotation) + { + origins.push_back(VectorKey{time, origin}); + positions.push_back(VectorKey{time, position}); + rotations.push_back(QuatKey{time, rotation}); + } + + void add(double time, const dvec3& position, const dquat& rotation) + { + positions.push_back(VectorKey{time, position}); + rotations.push_back(QuatKey{time, rotation}); + } + + void read(Input& input) override; + void write(Output& output) const override; + }; + VSG_type_name(vsg::CameraKeyframes); + + /// Animation sampler for sampling position, rotation and scale keyframes for setting camera view and project matrices. + class VSG_DECLSPEC CameraSampler : public Inherit + { + public: + CameraSampler(); + CameraSampler(const CameraSampler& rhs, const CopyOp& copyop = {}); + + ref_ptr keyframes; + ref_ptr object; + + // updated using keyFrames + dvec3 origin; + dvec3 position; + dquat rotation; + double fieldOfView; + dvec2 nearFar; + + void update(double time) override; + double maxTime() const override; + + inline dmat4 transform() const { return translate(position) * vsg::rotate(rotation); } + + public: + ref_ptr clone(const CopyOp& copyop = {}) const override { return CameraSampler::create(*this, copyop); } + int compare(const Object& rhs) const override; + + void read(Input& input) override; + void write(Output& output) const override; + + void apply(mat4Value& mat) override; + void apply(dmat4Value& mat) override; + void apply(LookAt& lookAt) override; + void apply(LookDirection& lookDirection) override; + void apply(Perspective& perspective) override; + void apply(Camera& camera) override; + }; + VSG_type_name(vsg::CameraSampler); + +} // namespace vsg diff --git a/include/vsg/animation/TransformSampler.h b/include/vsg/animation/TransformSampler.h index ca4b2c4ff..b8dc420af 100644 --- a/include/vsg/animation/TransformSampler.h +++ b/include/vsg/animation/TransformSampler.h @@ -13,27 +13,15 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ #include +#include #include #include namespace vsg { - struct VectorKey - { - double time; - dvec3 value; - - bool operator<(const VectorKey& rhs) const { return time < rhs.time; } - }; - - struct QuatKey - { - double time; - dquat value; - - bool operator<(const QuatKey& rhs) const { return time < rhs.time; } - }; + using VectorKey = time_dvec3; + using QuatKey = time_dquat; class VSG_DECLSPEC TransformKeyframes : public Inherit { diff --git a/include/vsg/animation/time_value.h b/include/vsg/animation/time_value.h new file mode 100644 index 000000000..303f36b96 --- /dev/null +++ b/include/vsg/animation/time_value.h @@ -0,0 +1,82 @@ +#pragma once + +/* + +Copyright(c) 2024 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include + +namespace vsg +{ + + template + struct time_value + { + using value_type = T; + double time; + value_type value; + + bool operator<(const time_value& rhs) const { return time < rhs.time; } + }; + + using time_double = time_value; + using time_dvec2 = time_value; + using time_dvec3 = time_value; + using time_dvec4 = time_value; + using time_dquat = time_value; + + template + bool sample(double time, const T& values, V& value) + { + if (values.size() == 0) return false; + + if (values.size() == 1) + { + value = values.front().value; + return true; + } + + auto pos_itr = values.begin(); + if (time <= pos_itr->time) + { + value = pos_itr->value; + return true; + } + else + { + using value_type = typename T::value_type; + pos_itr = std::lower_bound(values.begin(), values.end(), time, [](const value_type& elem, double t) -> bool { return elem.time < t; }); + + if (pos_itr == values.begin()) + { + value = values.front().value; + return true; + } + + if (pos_itr == values.end()) + { + value = values.back().value; + return true; + } + + auto before_pos_itr = pos_itr - 1; + double delta_time = (pos_itr->time - before_pos_itr->time); + double r = delta_time != 0.0 ? (time - before_pos_itr->time) / delta_time : 0.5; + + value = mix(before_pos_itr->value, pos_itr->value, r); + + return true; + } + } + +} // namespace vsg diff --git a/include/vsg/core/ConstVisitor.h b/include/vsg/core/ConstVisitor.h index 0de8d4b0a..517c96efc 100644 --- a/include/vsg/core/ConstVisitor.h +++ b/include/vsg/core/ConstVisitor.h @@ -65,6 +65,7 @@ namespace vsg class JointSampler; class MorphSampler; class TransformSampler; + class CameraSampler; class Joint; // forward declare vulkan classes @@ -157,6 +158,7 @@ namespace vsg class Viewer; class ViewMatrix; class LookAt; + class LookDirection; class RelativeViewMatrix; class TrackingViewMatrix; class ProjectionMatrix; @@ -355,6 +357,7 @@ namespace vsg virtual void apply(const JointSampler&); virtual void apply(const MorphSampler&); virtual void apply(const TransformSampler&); + virtual void apply(const CameraSampler&); virtual void apply(const Joint&); // Vulkan nodes @@ -446,6 +449,7 @@ namespace vsg virtual void apply(const Viewer&); virtual void apply(const ViewMatrix&); virtual void apply(const LookAt&); + virtual void apply(const LookDirection&); virtual void apply(const RelativeViewMatrix&); virtual void apply(const TrackingViewMatrix&); virtual void apply(const ProjectionMatrix&); diff --git a/include/vsg/core/Visitor.h b/include/vsg/core/Visitor.h index 98977bbd6..60ad7b531 100644 --- a/include/vsg/core/Visitor.h +++ b/include/vsg/core/Visitor.h @@ -63,6 +63,7 @@ namespace vsg class AnimationGroup; class AnimationSampler; class TransformSampler; + class CameraSampler; class MorphSampler; class JointSampler; class Joint; @@ -157,6 +158,7 @@ namespace vsg class Viewer; class ViewMatrix; class LookAt; + class LookDirection; class RelativeViewMatrix; class TrackingViewMatrix; class ProjectionMatrix; @@ -355,6 +357,7 @@ namespace vsg virtual void apply(JointSampler&); virtual void apply(MorphSampler&); virtual void apply(TransformSampler&); + virtual void apply(CameraSampler&); virtual void apply(Joint&); // Vulkan nodes @@ -446,6 +449,7 @@ namespace vsg virtual void apply(Viewer&); virtual void apply(ViewMatrix&); virtual void apply(LookAt&); + virtual void apply(LookDirection&); virtual void apply(RelativeViewMatrix&); virtual void apply(TrackingViewMatrix&); virtual void apply(ProjectionMatrix&); diff --git a/include/vsg/maths/transform.h b/include/vsg/maths/transform.h index bd979e79b..a07b1a131 100644 --- a/include/vsg/maths/transform.h +++ b/include/vsg/maths/transform.h @@ -263,10 +263,12 @@ namespace vsg /// usage: auto matrix = vsg::visit(nodePath).matrix; struct VSG_DECLSPEC ComputeTransform : public ConstVisitor { + dvec3 origin; dmat4 matrix; void apply(const Transform& transform) override; void apply(const MatrixTransform& mt) override; + void apply(const CoordinateFrame& cf) override; void apply(const Camera& camera) override; }; diff --git a/src/vsg/CMakeLists.txt b/src/vsg/CMakeLists.txt index dffbe08be..263122d5a 100644 --- a/src/vsg/CMakeLists.txt +++ b/src/vsg/CMakeLists.txt @@ -199,11 +199,12 @@ set(SOURCES animation/Animation.cpp animation/AnimationGroup.cpp animation/AnimationManager.cpp - animation/CameraAnimation.cpp + animation/CameraAnimationHandler.cpp animation/FindAnimations.cpp animation/Joint.cpp animation/JointSampler.cpp animation/MorphSampler.cpp + animation/CameraSampler.cpp animation/TransformSampler.cpp ui/UIEvent.cpp diff --git a/src/vsg/animation/CameraAnimation.cpp b/src/vsg/animation/CameraAnimationHandler.cpp similarity index 60% rename from src/vsg/animation/CameraAnimation.cpp rename to src/vsg/animation/CameraAnimationHandler.cpp index 8e54c68fd..de809cfb4 100644 --- a/src/vsg/animation/CameraAnimation.cpp +++ b/src/vsg/animation/CameraAnimationHandler.cpp @@ -10,7 +10,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ -#include +#include +#include #include #include #include @@ -22,7 +23,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI using namespace vsg; -CameraAnimation::CameraAnimation(ref_ptr in_object, const Path& in_filename, ref_ptr in_options) : +CameraAnimationHandler::CameraAnimationHandler() +{ +} + +CameraAnimationHandler::CameraAnimationHandler(ref_ptr in_object, const Path& in_filename, ref_ptr in_options) : object(in_object), filename(in_filename), options(in_options) @@ -35,28 +40,46 @@ CameraAnimation::CameraAnimation(ref_ptr in_object, const Path& in_filen { for (auto sampler : animation->samplers) { - if (auto ts = sampler.cast()) + if (auto cs = sampler.cast()) { - transformSampler = ts; + cameraSampler = cs; break; } } } - else if ((transformSampler = read_object.cast())) + else if ((cameraSampler = read_object.cast())) + { + animation = Animation::create(); + animation->samplers.push_back(cameraSampler); + } + else if (auto ts = read_object.cast()) { + auto tkf = ts->keyframes; + + // convert TransformSampler to CameraSampler + cameraSampler = CameraSampler::create(); + cameraSampler->name = ts->name; + const auto& ckf = cameraSampler->keyframes = CameraKeyframes::create(); + + ckf->positions = tkf->positions; + ckf->rotations = tkf->rotations; + animation = Animation::create(); - animation->samplers.push_back(transformSampler); + animation->samplers.push_back(cameraSampler); } else if (auto keyframes = read_object.cast()) { - transformSampler = TransformSampler::create(); - transformSampler->keyframes = keyframes; + cameraSampler = CameraSampler::create(); + const auto& ckf = cameraSampler->keyframes = CameraKeyframes::create(); + + ckf->positions = keyframes->positions; + ckf->rotations = keyframes->rotations; animation = Animation::create(); - animation->samplers.push_back(transformSampler); + animation->samplers.push_back(cameraSampler); } } - if (object && transformSampler) transformSampler->object = object; + if (object && cameraSampler) cameraSampler->object = object; } else { @@ -64,7 +87,7 @@ CameraAnimation::CameraAnimation(ref_ptr in_object, const Path& in_filen } } -CameraAnimation::CameraAnimation(ref_ptr in_object, ref_ptr in_animation, const Path& in_filename, ref_ptr in_options) : +CameraAnimationHandler::CameraAnimationHandler(ref_ptr in_object, ref_ptr in_animation, const Path& in_filename, ref_ptr in_options) : object(in_object), filename(in_filename), options(in_options), @@ -74,50 +97,53 @@ CameraAnimation::CameraAnimation(ref_ptr in_object, ref_ptr i { for (auto& sampler : animation->samplers) { - if (auto ts = sampler.cast()) + if (auto ts = sampler.cast()) { - transformSampler = ts; - transformSampler->object = object; + cameraSampler = ts; + cameraSampler->object = object; break; } } } } -void CameraAnimation::apply(Camera& camera) +void CameraAnimationHandler::apply(Camera& camera) { - if (transformSampler) + info("CameraAnimationHandler::apply(Camera& camera) ", cameraSampler); + + if (cameraSampler) { - auto& keyframes = transformSampler->keyframes; - if (!keyframes) keyframes = TransformKeyframes::create(); + + auto& keyframes = cameraSampler->keyframes; + if (!keyframes) keyframes = CameraKeyframes::create(); dvec3 position, scale; dquat orientation; auto matrix = camera.viewMatrix->inverse(); if (decompose(matrix, position, orientation, scale)) { - keyframes->add(simulationTime - startTime, position, orientation, scale); + keyframes->add(simulationTime - startTime, position, orientation); } } } -void CameraAnimation::apply(MatrixTransform& transform) +void CameraAnimationHandler::apply(MatrixTransform& transform) { - if (transformSampler) + if (cameraSampler) { - auto& keyframes = transformSampler->keyframes; - if (!keyframes) keyframes = TransformKeyframes::create(); + auto& keyframes = cameraSampler->keyframes; + if (!keyframes) keyframes = CameraKeyframes::create(); dvec3 position, scale; dquat orientation; if (decompose(transform.matrix, position, orientation, scale)) { - keyframes->add(simulationTime - startTime, position, orientation, scale); + keyframes->add(simulationTime - startTime, position, orientation); } } } -void CameraAnimation::play() +void CameraAnimationHandler::play() { if (playing) return; @@ -125,7 +151,7 @@ void CameraAnimation::play() if (playing) info("Starting playback."); } -void CameraAnimation::record() +void CameraAnimationHandler::record() { if (recording) return; @@ -137,25 +163,25 @@ void CameraAnimation::record() { animation = Animation::create(); } - if (!transformSampler) + if (!cameraSampler) { - transformSampler = TransformSampler::create(); - transformSampler->object = object; + cameraSampler = CameraSampler::create(); + cameraSampler->object = object; - animation->samplers.push_back(transformSampler); + animation->samplers.push_back(cameraSampler); } - if (transformSampler->keyframes) + if (cameraSampler->keyframes) { - transformSampler->keyframes->clear(); + cameraSampler->keyframes->clear(); } else { - transformSampler->keyframes = TransformKeyframes::create(); + cameraSampler->keyframes = CameraKeyframes::create(); } } -void CameraAnimation::stop() +void CameraAnimationHandler::stop() { if (playing) { @@ -179,7 +205,7 @@ void CameraAnimation::stop() } } -void CameraAnimation::apply(KeyPressEvent& keyPress) +void CameraAnimationHandler::apply(KeyPressEvent& keyPress) { if (keyPress.keyModified == togglePlaybackKey) { @@ -209,7 +235,7 @@ void CameraAnimation::apply(KeyPressEvent& keyPress) } } -void CameraAnimation::apply(FrameEvent& frame) +void CameraAnimationHandler::apply(FrameEvent& frame) { simulationTime = frame.frameStamp->simulationTime; diff --git a/src/vsg/animation/CameraSampler.cpp b/src/vsg/animation/CameraSampler.cpp new file mode 100644 index 000000000..647d0fb0d --- /dev/null +++ b/src/vsg/animation/CameraSampler.cpp @@ -0,0 +1,334 @@ +/* + +Copyright(c) 2024 Robert Osfield + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace vsg; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// CameraKeyframes +// +CameraKeyframes::CameraKeyframes() +{ +} + +void CameraKeyframes::read(Input& input) +{ + Object::read(input); + + input.read("name", name); + + // read tracking key frames + uint32_t num_tracking = input.readValue("tracking"); + tracking.resize(num_tracking); + for (auto& track : tracking) + { + input.matchPropertyName("track"); + input.read(1, &track.time); + input.readObjects("path", track.value); + } + + // read position key frames + uint32_t num_positions = input.readValue("positions"); + positions.resize(num_positions); + for (auto& position : positions) + { + input.matchPropertyName("position"); + input.read(1, &position.time); + input.read(1, &position.value); + } + + // read rotation key frames + uint32_t num_rotations = input.readValue("rotations"); + rotations.resize(num_rotations); + for (auto& rotation : rotations) + { + input.matchPropertyName("rotation"); + input.read(1, &rotation.time); + input.read(1, &rotation.value); + } + + // read field of view key frames + uint32_t num_fieldOfViews = input.readValue("fieldOfViews"); + fieldOfViews.resize(num_fieldOfViews); + for (auto& fov : fieldOfViews) + { + input.matchPropertyName("fov"); + input.read(1, &fov.time); + input.read(1, &fov.value); + } + + // read near/far key frames + uint32_t num_nearFars = input.readValue("nearFars"); + nearFars.resize(num_nearFars); + for (auto& nf : nearFars) + { + input.matchPropertyName("nearfar"); + input.read(1, &nf.time); + input.read(1, &nf.value); + } +} + +void CameraKeyframes::write(Output& output) const +{ + Object::write(output); + + output.write("name", name); + + // write position key frames + output.writeValue("tracking", tracking.size()); + for (const auto& track : tracking) + { + output.writePropertyName("track"); + output.write(1, &track.time); + output.writeEndOfLine(); + + output.writeObjects("path", track.value); + } + + // write position key frames + output.writeValue("positions", positions.size()); + for (const auto& position : positions) + { + output.writePropertyName("position"); + output.write(1, &position.time); + output.write(1, &position.value); + output.writeEndOfLine(); + } + + // write rotation key frames + output.writeValue("rotations", rotations.size()); + for (const auto& rotation : rotations) + { + output.writePropertyName("rotation"); + output.write(1, &rotation.time); + output.write(1, &rotation.value); + output.writeEndOfLine(); + } + + // write scale key frames + for (const auto& scale : fieldOfViews) + { + output.writePropertyName("fov"); + output.write(1, &scale.time); + output.write(1, &scale.value); + output.writeEndOfLine(); + } + + // write field of view key frames + output.writeValue("fieldOfViews", fieldOfViews.size()); + for (const auto& fov : fieldOfViews) + { + output.writePropertyName("fov"); + output.write(1, &fov.time); + output.write(1, &fov.value); + output.writeEndOfLine(); + } + + // read near/far key frames + output.writeValue("nearFars", nearFars.size()); + for (const auto& nf : nearFars) + { + output.writePropertyName("nearfar"); + output.write(1, &nf.time); + output.write(1, &nf.value); + output.writeEndOfLine(); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// CameraSampler +// +CameraSampler::CameraSampler() : + origin(0.0, 0.0, 0.0), + position(0.0, 0.0, 0.0), + rotation(), + fieldOfView(60.0), + nearFar(1.0, 1e10) +{ +} + +CameraSampler::CameraSampler(const CameraSampler& rhs, const CopyOp& copyop) : + Inherit(rhs, copyop), + keyframes(copyop(rhs.keyframes)), + object(copyop(rhs.object)), + position(rhs.position), + rotation(rhs.rotation), + fieldOfView(rhs.fieldOfView), + nearFar(rhs.nearFar) +{ +} + +int CameraSampler::compare(const Object& rhs_object) const +{ + int result = AnimationSampler::compare(rhs_object); + if (result != 0) return result; + + const auto& rhs = static_cast(rhs_object); + if ((result = compare_pointer(keyframes, rhs.keyframes)) != 0) return result; + return compare_pointer(object, rhs.object); +} + +void CameraSampler::update(double time) +{ + if (keyframes) + { + sample(time, keyframes->origins, origin); + sample(time, keyframes->positions, position); + sample(time, keyframes->rotations, rotation); + sample(time, keyframes->fieldOfViews, fieldOfView); + sample(time, keyframes->nearFars, nearFar); + + auto find_values = [](const RefObjectPath& path, dvec3& in_origin, dvec3& in_position, dquat& in_rotation) -> void { + ComputeTransform ct; + for (auto& obj : path) obj->accept(ct); + + in_origin = ct.origin; + + dvec3 scale; + vsg::decompose(ct.matrix, in_position, in_rotation, scale); + }; + + auto& tracking = keyframes->tracking; + if (tracking.size() == 1) + { + find_values(tracking.front().value, origin, position, rotation); + } + else if (!tracking.empty()) + { + auto pos_itr = std::lower_bound(tracking.begin(), tracking.end(), time, [](const time_path& elem, double t) -> bool { return elem.time < t; }); + if (pos_itr == tracking.begin()) + { + find_values(tracking.front().value, origin, position, rotation); + } + else if (pos_itr == tracking.end()) + { + find_values(tracking.back().value, origin, position, rotation); + } + else + { + auto before_pos_itr = pos_itr - 1; + double delta_time = (pos_itr->time - before_pos_itr->time); + double r = delta_time != 0.0 ? (time - before_pos_itr->time) / delta_time : 0.5; + + dvec3 origin_before, position_before; + dquat rotation_before; + find_values(before_pos_itr->value, origin_before, position_before, rotation_before); + + dvec3 origin_after, position_after; + dquat rotation_after; + find_values(pos_itr->value, origin_after, position_after, rotation_after); + + // convert origin input values and ratio to long double to minimize the intermediate rounding errors. + origin = mix(ldvec3(origin_before), ldvec3(origin_after), static_cast(r)); + + position = mix(position_before, position_after, r); + rotation = mix(rotation_before, rotation_after, r); + } + } + } + + if (object) object->accept(*this); +} + +double CameraSampler::maxTime() const +{ + double maxTime = 0.0; + if (keyframes) + { + if (!keyframes->tracking.empty()) maxTime = std::max(maxTime, keyframes->tracking.back().time); + if (!keyframes->origins.empty()) maxTime = std::max(maxTime, keyframes->origins.back().time); + if (!keyframes->positions.empty()) maxTime = std::max(maxTime, keyframes->positions.back().time); + if (!keyframes->rotations.empty()) maxTime = std::max(maxTime, keyframes->rotations.back().time); + if (!keyframes->fieldOfViews.empty()) maxTime = std::max(maxTime, keyframes->fieldOfViews.back().time); + if (!keyframes->nearFars.empty()) maxTime = std::max(maxTime, keyframes->nearFars.back().time); + } + + return maxTime; +} + +void CameraSampler::apply(mat4Value& matrix) +{ + matrix.set(mat4(transform())); +} + +void CameraSampler::apply(dmat4Value& matrix) +{ + matrix.set(transform()); +} + +void CameraSampler::apply(LookAt& lookAt) +{ + if (keyframes) + { + bool has_tracking = !keyframes->tracking.empty(); + if (!keyframes->origins.empty() || has_tracking) lookAt.origin = origin; + if (!keyframes->positions.empty() || !keyframes->rotations.empty() || has_tracking) + { + lookAt.set(transform()); + } + } +} + +void CameraSampler::apply(LookDirection& lookDirection) +{ + if (keyframes) + { + bool has_tracking = !keyframes->tracking.empty(); + if (!keyframes->origins.empty() || has_tracking) lookDirection.origin = origin; + if (!keyframes->positions.empty() || has_tracking) lookDirection.position = position; + if (!keyframes->rotations.empty() || has_tracking) lookDirection.rotation = rotation; + } +} + +void CameraSampler::apply(Perspective& perspective) +{ + if (keyframes && !keyframes->fieldOfViews.empty()) + { + perspective.fieldOfViewY = fieldOfView; + } + if (keyframes && !keyframes->nearFars.empty()) + { + perspective.nearDistance = nearFar[0]; + perspective.farDistance = nearFar[1]; + } +} + +void CameraSampler::apply(Camera& camera) +{ + if (camera.projectionMatrix) camera.projectionMatrix->accept(*this); + if (camera.viewMatrix) camera.viewMatrix->accept(*this); +} + +void CameraSampler::read(Input& input) +{ + AnimationSampler::read(input); + input.read("keyframes", keyframes); + input.read("object", object); +} + +void CameraSampler::write(Output& output) const +{ + AnimationSampler::write(output); + output.write("keyframes", keyframes); + output.write("object", object); +} diff --git a/src/vsg/animation/TransformSampler.cpp b/src/vsg/animation/TransformSampler.cpp index 5c4b7cc02..b81ce3629 100644 --- a/src/vsg/animation/TransformSampler.cpp +++ b/src/vsg/animation/TransformSampler.cpp @@ -104,50 +104,6 @@ void TransformKeyframes::write(Output& output) const } } -template -bool sample(double time, const T& values, V& value) -{ - if (values.size() == 0) return false; - - if (values.size() == 1) - { - value = values.front().value; - return true; - } - - auto pos_itr = values.begin(); - if (time <= pos_itr->time) - { - value = pos_itr->value; - return true; - } - else - { - using value_type = typename T::value_type; - pos_itr = std::lower_bound(values.begin(), values.end(), time, [](const value_type& elem, double t) -> bool { return elem.time < t; }); - - if (pos_itr == values.begin()) - { - value = values.front().value; - return true; - } - - if (pos_itr == values.end()) - { - value = values.back().value; - return true; - } - - auto before_pos_itr = pos_itr - 1; - double delta_time = (pos_itr->time - before_pos_itr->time); - double r = delta_time != 0.0 ? (time - before_pos_itr->time) / delta_time : 0.5; - - value = mix(before_pos_itr->value, pos_itr->value, r); - - return true; - } -} - //////////////////////////////////////////////////////////////////////////////////////////////////// // // TransformSampler @@ -176,10 +132,7 @@ int TransformSampler::compare(const Object& rhs_object) const const auto& rhs = static_cast(rhs_object); if ((result = compare_pointer(keyframes, rhs.keyframes)) != 0) return result; - if ((result = compare_pointer(object, rhs.object)) != 0) return result; - if ((result = compare_value(position, rhs.position)) != 0) return result; - if ((result = compare_value(rotation, rhs.rotation)) != 0) return result; - return compare_value(scale, rhs.scale); + return compare_pointer(object, rhs.object); } void TransformSampler::update(double time) diff --git a/src/vsg/core/ConstVisitor.cpp b/src/vsg/core/ConstVisitor.cpp index 3c85f747c..dcbc6dc5d 100644 --- a/src/vsg/core/ConstVisitor.cpp +++ b/src/vsg/core/ConstVisitor.cpp @@ -683,6 +683,10 @@ void ConstVisitor::apply(const TransformSampler& sampler) { apply(static_cast(sampler)); } +void ConstVisitor::apply(const CameraSampler& sampler) +{ + apply(static_cast(sampler)); +} void ConstVisitor::apply(const Joint& value) { apply(static_cast(value)); @@ -1032,6 +1036,10 @@ void ConstVisitor::apply(const LookAt& value) { apply(static_cast(value)); } +void ConstVisitor::apply(const LookDirection& value) +{ + apply(static_cast(value)); +} void ConstVisitor::apply(const RelativeViewMatrix& value) { apply(static_cast(value)); diff --git a/src/vsg/core/Visitor.cpp b/src/vsg/core/Visitor.cpp index a2482e5f4..146f2dace 100644 --- a/src/vsg/core/Visitor.cpp +++ b/src/vsg/core/Visitor.cpp @@ -683,6 +683,10 @@ void Visitor::apply(TransformSampler& sampler) { apply(static_cast(sampler)); } +void Visitor::apply(CameraSampler& sampler) +{ + apply(static_cast(sampler)); +} void Visitor::apply(Joint& value) { apply(static_cast(value)); @@ -1032,6 +1036,10 @@ void Visitor::apply(LookAt& value) { apply(static_cast(value)); } +void Visitor::apply(LookDirection& value) +{ + apply(static_cast(value)); +} void Visitor::apply(RelativeViewMatrix& value) { apply(static_cast(value)); diff --git a/src/vsg/io/ObjectFactory.cpp b/src/vsg/io/ObjectFactory.cpp index ad490db6a..1fe3644bb 100644 --- a/src/vsg/io/ObjectFactory.cpp +++ b/src/vsg/io/ObjectFactory.cpp @@ -300,6 +300,8 @@ ObjectFactory::ObjectFactory() // animation add(); add(); + add(); + add(); add(); add(); add(); diff --git a/src/vsg/maths/maths_transform.cpp b/src/vsg/maths/maths_transform.cpp index f7b8074a0..b36d532f2 100644 --- a/src/vsg/maths/maths_transform.cpp +++ b/src/vsg/maths/maths_transform.cpp @@ -13,6 +13,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #include #include #include +#include #include using namespace vsg; @@ -441,6 +442,12 @@ void ComputeTransform::apply(const MatrixTransform& mt) matrix = matrix * mt.matrix; } +void ComputeTransform::apply(const CoordinateFrame& cf) +{ + origin = cf.origin; + matrix = vsg::rotate(cf.rotation); +} + void ComputeTransform::apply(const Camera& camera) { if (camera.viewMatrix)