diff --git a/BasaltPresenter/CMakeLists.txt b/BasaltPresenter/CMakeLists.txt index 7df8fa2..3926fbf 100644 --- a/BasaltPresenter/CMakeLists.txt +++ b/BasaltPresenter/CMakeLists.txt @@ -26,7 +26,7 @@ option(BASALT_OBJ_OBJECT_LOADER "Build with Wavefront OBJ 3D object loader suppo option(BASALT_GLTF_OBJECT_LOADER "Build with glTF 3D object loader support." OFF) option(BASALT_ASSIMP_OBJECT_LOADER "Build with Assimp 3D object loader support." OFF) # Camera Motion Loaders -option(BASALT_homemade_ANIME_LOADER "Build with homemade camera motion loader support." OFF) +option(BASALT_CHICKENNUGGET_ANIME_LOADER "Build with chicken nugget camera motion loader support." OFF) # Set C++ standards set(CMAKE_CXX_STANDARD 23) diff --git a/BasaltPresenter/Plugins/AnimeLoader/CMakeLists.txt b/BasaltPresenter/Plugins/AnimeLoader/CMakeLists.txt index e69de29..925cb52 100644 --- a/BasaltPresenter/Plugins/AnimeLoader/CMakeLists.txt +++ b/BasaltPresenter/Plugins/AnimeLoader/CMakeLists.txt @@ -0,0 +1,3 @@ +if (BASALT_CHICKENNUGGET_ANIME_LOADER) + add_subdirectory(ChickenNuggetAnimeLoader) +endif () \ No newline at end of file diff --git a/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/CMakeLists.txt b/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/CMakeLists.txt new file mode 100644 index 0000000..d98dd4b --- /dev/null +++ b/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/CMakeLists.txt @@ -0,0 +1,27 @@ +# Create shared library +add_library(BasaltChickenNuggetAnimeLoader SHARED "") +# Setup sources +target_sources(BasaltChickenNuggetAnimeLoader +PRIVATE + main.cpp +) +# Setup header infomation +target_include_directories(BasaltChickenNuggetAnimeLoader +PRIVATE + "${CMAKE_CURRENT_LIST_DIR}" +) +# Setup linked library infomation +target_link_libraries(BasaltChickenNuggetAnimeLoader +PRIVATE + BasaltShared +) +# Enable export macro +target_compile_definitions(BasaltChickenNuggetAnimeLoader +PRIVATE + BS_EXPORTING +) + +# Install BasaltChickenNuggetAnimeLoader only on Release mode +install(TARGETS BasaltChickenNuggetAnimeLoader + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/plugin/anime_loader" +) diff --git a/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/main.cpp b/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/main.cpp new file mode 100644 index 0000000..a4e0516 --- /dev/null +++ b/BasaltPresenter/Plugins/AnimeLoader/ChickenNuggetAnimeLoader/main.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +namespace anime_loader = ::basalt::shared::anime_loader; +using anime_loader::IAnimeLoader; +using anime_loader::KeyFrame; + +using ::basalt::shared::math::FloatPoint; +#define F(x) (static_cast(x)) + +class ChickenNuggetAnimeLoader : public IAnimeLoader { +public: + ChickenNuggetAnimeLoader() {} + virtual ~ChickenNuggetAnimeLoader() {} + +protected: + virtual void internal_load() override { + this->frames.emplace(60 * 0, KeyFrame{.position = {F(0), F(-10), F(10)}, .rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); + this->frames.emplace(60 * 1, + KeyFrame{.position = {F(-10), F(0), F(10)}, + .rotation = {F(0.270598), F(-0.270598), F(-0.653282), F(0.653282)}}); + this->frames.emplace(60 * 2, KeyFrame{.position = {F(0), F(10), F(10)}, .rotation = {F(0), F(-0.382683), F(-0.92388), F(0)}}); + this->frames.emplace(60 * 3, + KeyFrame{.position = {F(10), F(0), F(10)}, + .rotation = {F(-0.270598), F(-0.270598), F(-0.653282), F(-0.653282)}}); + this->frames.emplace(60 * 4, + KeyFrame{.position = {F(0), F(-10), F(10)}, + .rotation = {F(0.382683), F(0), F(0), F(0.92388)}}); // Same as the first + } +}; + +BS_EXPORT void* BSCreateInstance() { + return static_cast(new ChickenNuggetAnimeLoader()); +} + +BS_EXPORT void BSDestroyInstance(void* instance) { + delete dynamic_cast(static_cast(instance)); +} diff --git a/BasaltPresenter/Plugins/ObjectLoader/CMakeLists.txt b/BasaltPresenter/Plugins/ObjectLoader/CMakeLists.txt index e69de29..16ca9c0 100644 --- a/BasaltPresenter/Plugins/ObjectLoader/CMakeLists.txt +++ b/BasaltPresenter/Plugins/ObjectLoader/CMakeLists.txt @@ -0,0 +1,9 @@ +if (BASALT_OBJ_OBJECT_LOADER) + add_subdirectory(ObjObjectLoader) +endif () +# if (BASALT_GLTF_OBJECT_LOADER) +# add_subdirectory(GltfObjectLoader) +# endif () +# if (BASALT_ASSIMP_OBJECT_LOADER) +# add_subdirectory(AssimpObjectLoader) +# endif () \ No newline at end of file diff --git a/BasaltPresenter/Plugins/ObjectLoader/ObjObjectLoader/CMakeLists.txt b/BasaltPresenter/Plugins/ObjectLoader/ObjObjectLoader/CMakeLists.txt new file mode 100644 index 0000000..052ba6b --- /dev/null +++ b/BasaltPresenter/Plugins/ObjectLoader/ObjObjectLoader/CMakeLists.txt @@ -0,0 +1,27 @@ +# Create shared library +add_library(BasaltObjObjectLoader SHARED "") +# Setup sources +target_sources(BasaltObjObjectLoader +PRIVATE + main.cpp +) +# Setup header infomation +target_include_directories(BasaltObjObjectLoader +PRIVATE + "${CMAKE_CURRENT_LIST_DIR}" +) +# Setup linked library infomation +target_link_libraries(BasaltObjObjectLoader +PRIVATE + BasaltShared +) +# Enable export macro +target_compile_definitions(BasaltObjObjectLoader +PRIVATE + BS_EXPORTING +) + +# Install BasaltObjObjectLoader only on Release mode +install(TARGETS BasaltObjObjectLoader + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/plugin/object_loader" +) diff --git a/BasaltPresenter/Plugins/ObjectLoader/ObjObjectLoader/main.cpp b/BasaltPresenter/Plugins/ObjectLoader/ObjObjectLoader/main.cpp new file mode 100644 index 0000000..ab428f5 --- /dev/null +++ b/BasaltPresenter/Plugins/ObjectLoader/ObjObjectLoader/main.cpp @@ -0,0 +1,9 @@ +#include + +BS_EXPORT void* BSCreateInstance() { + return nullptr; +} + +BS_EXPORT void BSDestroyInstance(void* instance) { + return; +} diff --git a/BasaltPresenter/Shared/basalt/anime_loader.cpp b/BasaltPresenter/Shared/basalt/anime_loader.cpp index 00eb5ac..bd0f0e7 100644 --- a/BasaltPresenter/Shared/basalt/anime_loader.cpp +++ b/BasaltPresenter/Shared/basalt/anime_loader.cpp @@ -1,13 +1,70 @@ #include "anime_loader.hpp" +using ::basalt::shared::math::FloatPoint; + namespace basalt::shared::anime_loader { - IAnimeLoader::IAnimeLoader() { +#pragma region Anime Loader + IAnimeLoader::IAnimeLoader() : status(AnimeLoaderStatus::Ready), time(0), last_index(0) {} + + IAnimeLoader::~IAnimeLoader() {} + + void IAnimeLoader::load() { + if (this->status != AnimeLoaderStatus::Ready) throw std::runtime_error("unexpected anime loader status"); + + // load by user + internal_load(); + + // check empty + if (this->frames.empty()) throw std::runtime_error("no anime key frames."); + // Get last frame index + auto last_pair = this->frames.end(); + --last_pair; + this->last_index = last_pair->first; + + // change status + this->status = AnimeLoaderStatus::Loaded; } - IAnimeLoader::~IAnimeLoader() { + KeyFrameSpan IAnimeLoader::tick() { + if (this->status != AnimeLoaderStatus::Loaded) throw std::runtime_error("unexpected anime loader status"); + // Accumulate time + ++this->time; + if (this->time > this->last_index) { + this->time = 0; + } + + // 首先用lower_bound获取到不小于自身的项目(肯定能获取到) + auto lower_bound = this->frames.lower_bound(this->time); + // 获取下一帧,如果没有下一帧,就转换为自身 + auto upper_bound = lower_bound; + ++upper_bound; + if (upper_bound == this->frames.end()) { + upper_bound = lower_bound; + } + + // 计算归一化时间 + FloatPoint next_time = static_cast(this->time - lower_bound->first) + / static_cast(upper_bound->first - lower_bound->first + 1); + FloatPoint prev_time = static_cast(1) - next_time; + + // 返回结构 + return KeyFrameSpan{ + .prev_time = prev_time, + .next_time = next_time, + .prev_position = lower_bound->second.position, + .next_position = upper_bound->second.position, + .prev_rotation = lower_bound->second.rotation, + .next_rotation = upper_bound->second.rotation, + }; } -} + void IAnimeLoader::internal_load() { + throw std::logic_error("unimplemented function"); + } + +#pragma endregion + +} // namespace basalt::shared::anime_loader diff --git a/BasaltPresenter/Shared/basalt/anime_loader.hpp b/BasaltPresenter/Shared/basalt/anime_loader.hpp index cf4dd74..649a51d 100644 --- a/BasaltPresenter/Shared/basalt/anime_loader.hpp +++ b/BasaltPresenter/Shared/basalt/anime_loader.hpp @@ -1,13 +1,60 @@ #pragma once +#include "math.hpp" +#include +#include namespace basalt::shared::anime_loader { + //struct KeyFrame { + // math::Index frame_index; + // math::Matrix4x4 transform; + //}; + + //struct KeyFrameCompare { + // bool operator()(const KeyFrame& lhs, const KeyFrame& rhs) const { return lhs.time < rhs.time; } + //}; + + struct KeyFrame { + math::Vector3 position; + math::Quaternion rotation; + }; + + struct KeyFrameSpan { + math::FloatPoint prev_time; ///< 归一化的到前一帧的时间,即两者加起来为1。 + math::FloatPoint next_time; ///< 归一化的到后一帧的时间,即两者加起来为1。 + math::Vector3 prev_position; ///< 前一帧的摄像机坐标。 + math::Vector3 next_position; ///< 后一帧的摄像机坐标。 + math::Quaternion prev_rotation; ///< 前一帧的摄像机旋转。 + math::Quaternion next_rotation; ///< 后一帧的摄像机旋转。 + }; + + enum class AnimeLoaderStatus { + Ready, + Loaded, + }; + + /** + * @brief + * @details + * \li 摄像机的transform基于Blender坐标系。 + * \li 摄像机的默认状态与Blender摄像机一致,即初始指向-Z,+Y Up。 + */ class IAnimeLoader { public: IAnimeLoader(); virtual ~IAnimeLoader(); + public: + void load(); + KeyFrameSpan tick(); + protected: + virtual void internal_load(); + + protected: + AnimeLoaderStatus status; + math::Index time, last_index; + std::map frames; }; } // namespace basalt::shared::anime_loader diff --git a/BasaltPresenter/Shared/basalt/deliver.cpp b/BasaltPresenter/Shared/basalt/deliver.cpp index 6458afe..43df199 100644 --- a/BasaltPresenter/Shared/basalt/deliver.cpp +++ b/BasaltPresenter/Shared/basalt/deliver.cpp @@ -3,7 +3,7 @@ namespace basalt::shared::deliver { - IDeliver::IDeliver() {} + IDeliver::IDeliver() : status(DeliverStatus::Ready) {} IDeliver::~IDeliver() { if (this->status != DeliverStatus::Stop) { diff --git a/BasaltPresenter/Shared/basalt/math.cpp b/BasaltPresenter/Shared/basalt/math.cpp index 872c124..8868b6d 100644 --- a/BasaltPresenter/Shared/basalt/math.cpp +++ b/BasaltPresenter/Shared/basalt/math.cpp @@ -1,7 +1,37 @@ #include "math.hpp" #include // Include for std::out_of_range -namespace Basalt::Shared::Math { +namespace basalt::shared::math { + +#pragma region Triangle + + Index& Triangle::operator[](size_t index) { + switch (index) { + case 0: + return i; + case 1: + return j; + case 2: + return k; + default: + throw std::out_of_range("Triangle index out of range"); + } + } + + const Index& Triangle::operator[](size_t index) const { + switch (index) { + case 0: + return i; + case 1: + return j; + case 2: + return k; + default: + throw std::out_of_range("Triangle index out of range"); + } + } + +#pragma endregion #pragma region Vector3 @@ -33,9 +63,9 @@ namespace Basalt::Shared::Math { #pragma endregion -#pragma region Vector4 +#pragma region Quaternion - FloatPoint& Vector4::operator[](size_t index) { + FloatPoint& Quaternion::operator[](size_t index) { switch (index) { case 0: return x; @@ -46,11 +76,11 @@ namespace Basalt::Shared::Math { case 3: return w; default: - throw std::out_of_range("Vector4 index out of range"); + throw std::out_of_range("Quaternion index out of range"); } } - const FloatPoint& Vector4::operator[](size_t index) const { + const FloatPoint& Quaternion::operator[](size_t index) const { switch (index) { case 0: return x; @@ -61,22 +91,56 @@ namespace Basalt::Shared::Math { case 3: return w; default: - throw std::out_of_range("Vector4 index out of range"); + throw std::out_of_range("Quaternion index out of range"); } } #pragma endregion + // + //#pragma region Vector4 + // + // FloatPoint& Vector4::operator[](size_t index) { + // switch (index) { + // case 0: + // return x; + // case 1: + // return y; + // case 2: + // return z; + // case 3: + // return w; + // default: + // throw std::out_of_range("Vector4 index out of range"); + // } + // } + // + // const FloatPoint& Vector4::operator[](size_t index) const { + // switch (index) { + // case 0: + // return x; + // case 1: + // return y; + // case 2: + // return z; + // case 3: + // return w; + // default: + // throw std::out_of_range("Vector4 index out of range"); + // } + // } + // + //#pragma endregion + // + //#pragma region Matrix4x4 + // + // Vector4& Matrix4x4::operator[](size_t index) { + // return data.at(index); + // } + // + // const Vector4& Matrix4x4::operator[](size_t index) const { + // return data.at(index); + // } + // + //#pragma endregion -#pragma region Matrix4x4 - - Vector4& Matrix4x4::operator[](size_t index) { - return data.at(index); - } - - const Vector4& Matrix4x4::operator[](size_t index) const { - return data.at(index); - } - -#pragma endregion - -} // namespace Basalt::Shared::Math \ No newline at end of file +} // namespace basalt::shared::math \ No newline at end of file diff --git a/BasaltPresenter/Shared/basalt/math.hpp b/BasaltPresenter/Shared/basalt/math.hpp index c536d06..48fa337 100644 --- a/BasaltPresenter/Shared/basalt/math.hpp +++ b/BasaltPresenter/Shared/basalt/math.hpp @@ -1,10 +1,19 @@ #pragma once #include +#include #include // Include array for std::array::at -namespace Basalt::Shared::Math { +namespace basalt::shared::math { using FloatPoint = float; + using Index = std::uint32_t; + + struct Triangle { + Index i, j, k; + + Index& operator[](size_t index); + const Index& operator[](size_t index) const; + }; struct Vector3 { FloatPoint x, y, z; @@ -13,30 +22,37 @@ namespace Basalt::Shared::Math { const FloatPoint& operator[](size_t index) const; }; - struct Vector4 { + struct Quaternion { FloatPoint x, y, z, w; FloatPoint& operator[](size_t index); const FloatPoint& operator[](size_t index) const; }; - struct Matrix4x4 { - private: - std::array data; // Use std::array instead of raw array for .at() method + //struct Vector4 { + // FloatPoint x, y, z, w; - public: - Vector4& operator[](size_t index); - const Vector4& operator[](size_t index) const; - }; + // FloatPoint& operator[](size_t index); + // const FloatPoint& operator[](size_t index) const; + //}; -#define NOT_IMPLEMENTED throw std::logic_error("not implemented function"); + //struct Matrix4x4 { + //private: + // std::array data; // Use std::array instead of raw array for .at() method - template - struct Vector3Traits {}; + //public: + // Vector4& operator[](size_t index); + // const Vector4& operator[](size_t index) const; + //}; + // + //#define NOT_IMPLEMENTED throw std::logic_error("not implemented function"); + // + // template + // struct Vector3Traits {}; + // + // template + // struct Matrix4x4Traits {}; + // + //#undef NOT_IMPLEMENTED - template - struct Matrix4x4Traits {}; - -#undef NOT_IMPLEMENTED - -} // namespace Basalt::Shared::Math \ No newline at end of file +} // namespace basalt::shared::math \ No newline at end of file diff --git a/BasaltPresenter/Shared/basalt/object_loader.cpp b/BasaltPresenter/Shared/basalt/object_loader.cpp index 1d728bf..9d92277 100644 --- a/BasaltPresenter/Shared/basalt/object_loader.cpp +++ b/BasaltPresenter/Shared/basalt/object_loader.cpp @@ -1,13 +1,57 @@ #include "object_loader.hpp" +#include "anime_loader.hpp" + +using ::basalt::shared::math::Triangle; +using ::basalt::shared::math::Vector3; namespace basalt::shared::object_loader { - IObjectLoader::IObjectLoader() { +#pragma region Object + Object::Object(std::vector&& vertices, std::vector&& triangles) : + vertices(std::move(vertices)), triangles(std::move(triangles)) {} + + Object::~Object() {} + + size_t Object::get_vertices_count() const { + return this->vertices.size(); } - IObjectLoader::~IObjectLoader() { - + const math::Vector3* Object::get_vertices() const { + return this->vertices.data(); } -} + size_t Object::get_triangle_count() const { + return this->triangles.size(); + } + + const math::Triangle* Object::get_triangles() const { + return this->triangles.data(); + } + +#pragma endregion + +#pragma region Object Loader + + IObjectLoader::IObjectLoader() : status(ObjectLoaderStatus::Ready) {} + + IObjectLoader::~IObjectLoader() {} + + void IObjectLoader::load() { + if (this->status != ObjectLoaderStatus::Ready) throw std::runtime_error("unexpected object loader status"); + this->status = ObjectLoaderStatus::Loaded; + } + + const Object& IObjectLoader::get_object(size_t index) const { + if (this->status != ObjectLoaderStatus::Loaded) throw std::runtime_error("unexpected object loader status"); + return this->objects.at(index); + } + + size_t IObjectLoader::get_object_count() const { + if (this->status != ObjectLoaderStatus::Loaded) throw std::runtime_error("unexpected object loader status"); + return this->objects.size(); + } + +#pragma endregion + +} // namespace basalt::shared::object_loader diff --git a/BasaltPresenter/Shared/basalt/object_loader.hpp b/BasaltPresenter/Shared/basalt/object_loader.hpp index 3425b63..1e83fc0 100644 --- a/BasaltPresenter/Shared/basalt/object_loader.hpp +++ b/BasaltPresenter/Shared/basalt/object_loader.hpp @@ -1,13 +1,49 @@ #pragma once +#include "math.hpp" +#include namespace basalt::shared::object_loader { + class Object { + public: + Object(std::vector&& vertices, std::vector&& triangles); + ~Object(); + + public: + size_t get_vertices_count() const; + const math::Vector3* get_vertices() const; + size_t get_triangle_count() const; + const math::Triangle* get_triangles() const; + + private: + std::vector vertices; + std::vector triangles; + }; + + enum class ObjectLoaderStatus { + Ready, + Loaded, + }; + + /** + * @brief + * @details + * \li 加载的模型的坐标系与Blender一致。 + * \li 加载的模型的顶点顺序为CCW,即右手定则确认法线方向。 + */ class IObjectLoader { public: IObjectLoader(); virtual ~IObjectLoader(); + public: + virtual void load(); + const Object& get_object(size_t index) const; + size_t get_object_count() const; + protected: + ObjectLoaderStatus status; + std::vector objects; }; } // namespace basalt::shared::object_loader