Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ac22ebac0 | ||
|
|
64cdddea96 | ||
|
|
65c7a9aaaf | ||
|
|
5d01dfcca2 | ||
|
|
89fedce539 | ||
|
|
f6f7e92cea | ||
|
|
d32c247cc0 |
+8
-3
@@ -45,8 +45,6 @@ set(ThirdPartyIncludeDeps
|
|||||||
|
|
||||||
# Shared code by main executable and tests
|
# Shared code by main executable and tests
|
||||||
add_library(AnimTestbedCode OBJECT
|
add_library(AnimTestbedCode OBJECT
|
||||||
src/SyncTrack.cc
|
|
||||||
src/SyncTrack.h
|
|
||||||
src/ozzutils.cc
|
src/ozzutils.cc
|
||||||
src/AnimGraph/AnimGraphNodes.cc
|
src/AnimGraph/AnimGraphNodes.cc
|
||||||
src/AnimGraph/AnimGraphNodes.h
|
src/AnimGraph/AnimGraphNodes.h
|
||||||
@@ -59,7 +57,11 @@ add_library(AnimTestbedCode OBJECT
|
|||||||
src/AnimGraph/AnimNode.cc
|
src/AnimGraph/AnimNode.cc
|
||||||
src/AnimGraph/AnimNode.h
|
src/AnimGraph/AnimNode.h
|
||||||
src/AnimGraph/AnimGraphResource.cc
|
src/AnimGraph/AnimGraphResource.cc
|
||||||
src/AnimGraph/AnimGraphResource.h)
|
src/AnimGraph/AnimGraphResource.h
|
||||||
|
src/AnimGraph/SyncTrack.cc
|
||||||
|
src/AnimGraph/SyncTrack.h
|
||||||
|
src/AnimGraph/AnimLibrary.h
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
AnimTestbedCode
|
AnimTestbedCode
|
||||||
@@ -150,6 +152,9 @@ target_sources(runtests PRIVATE
|
|||||||
tests/AnimGraphEvalTests.cc
|
tests/AnimGraphEvalTests.cc
|
||||||
tests/NodeDescriptorTests.cc
|
tests/NodeDescriptorTests.cc
|
||||||
tests/SyncTrackTests.cc
|
tests/SyncTrackTests.cc
|
||||||
|
tests/AnimDataTests.cc
|
||||||
|
tests/TestAnimData.cc
|
||||||
|
tests/TestAnimData.h
|
||||||
tests/main.cc
|
tests/main.cc
|
||||||
${ozz_offline_test_objs}
|
${ozz_offline_test_objs}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
## Graph
|
||||||
|
|
||||||
|
### 1. Support of math nodes (or non-AnimNodes in general)
|
||||||
|
|
||||||
|
* Enables animators to add custom math for blend inputs or to adjust other inputs (e.g. LookAt or IK
|
||||||
|
targets).
|
||||||
|
|
||||||
|
**Open Issues**
|
||||||
|
|
||||||
|
1. When to do the evaluation? Two types of subgraphs:
|
||||||
|
a) Instant inputs (needed for blend node inputs) that have to be evaluated before
|
||||||
|
UpdateConnections
|
||||||
|
b) Processing nodes, e.g. for extracted bones.
|
||||||
|
|
||||||
|
### 2. Support of multiple output sockets
|
||||||
|
|
||||||
|
* E.g. extract Bone transform
|
||||||
|
|
||||||
|
* Increases Node complexity:
|
||||||
|
* AnimOutput
|
||||||
|
* AnimOutput + Data
|
||||||
|
* Data
|
||||||
|
|
||||||
|
(Data = bool, float, vec3, quat, ...)
|
||||||
|
|
||||||
|
**Open Issues**
|
||||||
|
|
||||||
|
1. Unclear when this is actually needed. Using more specific nodes that perform the desired logic
|
||||||
|
may be better (
|
||||||
|
c.f. https://dev.epicgames.com/documentation/en-us/unreal-engine/animation-blueprint-bone-driven-controller-in-unreal-engine).
|
||||||
|
Likely this is not crucial so should be avoided for now.
|
||||||
|
|
||||||
|
### 3. Multi-skeleton evaluation
|
||||||
|
|
||||||
|
Use case: riding on a horse, interaction between two characters.
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#define ANIMTESTBED_ANIMGRAPHBLENDTREE_H
|
#define ANIMTESTBED_ANIMGRAPHBLENDTREE_H
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "AnimNode.h"
|
#include "AnimNode.h"
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <ozz/base/maths/soa_transform.h>
|
#include <ozz/base/maths/soa_transform.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -33,11 +32,33 @@ struct AnimDataRef {
|
|||||||
Pose* ptr = nullptr;
|
Pose* ptr = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimationFileResource {
|
struct AnimationResource {
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_filename;
|
||||||
|
|
||||||
ozz::animation::Animation* m_animation;
|
ozz::animation::Animation* m_animation;
|
||||||
SyncTrack m_sync_track;
|
SyncTrack m_sync_track;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void to_json(
|
||||||
|
nlohmann::json& j,
|
||||||
|
const AnimationResource& animation_resource) {
|
||||||
|
j["type"] = "AnimationResource";
|
||||||
|
j["name"] = animation_resource.m_name;
|
||||||
|
j["filename"] = animation_resource.m_filename;
|
||||||
|
j["synctrack"] = animation_resource.m_sync_track;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void from_json(
|
||||||
|
const nlohmann::json& j,
|
||||||
|
AnimationResource& animation_resource) {
|
||||||
|
assert(j["type"] == "AnimationResource");
|
||||||
|
|
||||||
|
animation_resource.m_name = j["name"];
|
||||||
|
animation_resource.m_filename = j["filename"];
|
||||||
|
animation_resource.m_sync_track = j["synctrack"];
|
||||||
|
}
|
||||||
|
|
||||||
struct AnimDataAllocator {
|
struct AnimDataAllocator {
|
||||||
struct PoseList {
|
struct PoseList {
|
||||||
Pose* m_anim_data = nullptr;
|
Pose* m_anim_data = nullptr;
|
||||||
@@ -109,7 +130,7 @@ struct AnimGraphContext {
|
|||||||
AnimGraph* m_graph = nullptr;
|
AnimGraph* m_graph = nullptr;
|
||||||
ozz::animation::Skeleton* m_skeleton = nullptr;
|
ozz::animation::Skeleton* m_skeleton = nullptr;
|
||||||
|
|
||||||
typedef std::map<std::string, AnimationFileResource> AnimationFileMap;
|
typedef std::map<std::string, AnimationResource> AnimationFileMap;
|
||||||
AnimationFileMap m_animation_map;
|
AnimationFileMap m_animation_map;
|
||||||
|
|
||||||
void freeAnimations() {
|
void freeAnimations() {
|
||||||
|
|||||||
@@ -151,7 +151,11 @@ bool AnimSamplerNode::Init(AnimGraphContext& context) {
|
|||||||
|
|
||||||
archive >> *m_animation;
|
archive >> *m_animation;
|
||||||
|
|
||||||
context.m_animation_map[m_filename] = {m_animation, SyncTrack()};
|
context.m_animation_map[m_filename] = {
|
||||||
|
m_filename,
|
||||||
|
m_filename,
|
||||||
|
m_animation,
|
||||||
|
SyncTrack()};
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(context.m_skeleton != nullptr);
|
assert(context.m_skeleton != nullptr);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
||||||
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "3rdparty/json/json.hpp"
|
#include "3rdparty/json/json.hpp"
|
||||||
#include "AnimGraphNodes.h"
|
#include "AnimGraphNodes.h"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
//
|
||||||
|
// Created by martin on 11.04.25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef ANIMLIBRARY_H
|
||||||
|
#define ANIMLIBRARY_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "AnimGraph/AnimGraphData.h"
|
||||||
|
#include "ozz/base/io/archive.h"
|
||||||
|
#include "ozz/base/io/stream.h"
|
||||||
|
#include "ozz/base/log.h"
|
||||||
|
|
||||||
|
/** Manage a set of animations used for an AnimGraph.
|
||||||
|
*
|
||||||
|
* By default, it behaves like a resource that allows to resolve animation names to
|
||||||
|
* AnimationResources. However, it can also trigger loading of the referenced resources from their
|
||||||
|
* filenames. This only happens on-demand.
|
||||||
|
*/
|
||||||
|
struct AnimLibrary {
|
||||||
|
typedef std::map<std::string, AnimationResource> AnimationResourceMap;
|
||||||
|
AnimationResourceMap mAnimations = {};
|
||||||
|
std::vector<ozz::animation::Animation*> mManagedAnimations = {};
|
||||||
|
static constexpr const char* EXTERNAL_ANIMATION = "<external>";
|
||||||
|
|
||||||
|
AnimLibrary() = default;
|
||||||
|
~AnimLibrary() { Reset(); }
|
||||||
|
|
||||||
|
bool AddAnimation(
|
||||||
|
const std::string& name,
|
||||||
|
ozz::animation::Animation* animation) {
|
||||||
|
AnimationResource animation_resource;
|
||||||
|
animation_resource.m_name = name;
|
||||||
|
animation_resource.m_filename = EXTERNAL_ANIMATION;
|
||||||
|
animation_resource.m_animation = animation;
|
||||||
|
mAnimations[name] = animation_resource;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AddAnimationFile(const std::string& name, const std::string& filename) {
|
||||||
|
if (mAnimations.find(name) != mAnimations.end()) {
|
||||||
|
std::cerr << "Cannot add animation '" << name
|
||||||
|
<< "' to library. Animation already exists." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationResource animation_resource;
|
||||||
|
animation_resource.m_name = name;
|
||||||
|
animation_resource.m_animation = nullptr;
|
||||||
|
animation_resource.m_filename = filename;
|
||||||
|
|
||||||
|
mAnimations[name] = animation_resource;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() {
|
||||||
|
for (ozz::animation::Animation* animation : mManagedAnimations) {
|
||||||
|
assert(animation->num_tracks() < 5);
|
||||||
|
delete animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
mManagedAnimations.clear();
|
||||||
|
mAnimations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadAnimations() {
|
||||||
|
for (AnimationResourceMap::iterator iter = mAnimations.begin();
|
||||||
|
iter != mAnimations.end();
|
||||||
|
++iter) {
|
||||||
|
if (iter->second.m_filename == EXTERNAL_ANIMATION) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iter->second.m_animation != nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!iter->second.m_filename.empty());
|
||||||
|
ozz::io::File file(iter->second.m_filename.c_str(), "rb");
|
||||||
|
if (!file.opened()) {
|
||||||
|
ozz::log::Err() << "Failed to open animation file "
|
||||||
|
<< iter->second.m_filename << "." << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ozz::io::IArchive archive(&file);
|
||||||
|
if (!archive.TestTag<ozz::animation::Animation>()) {
|
||||||
|
ozz::log::Err() << "Failed to load animation instance from file "
|
||||||
|
<< iter->second.m_filename << "." << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter->second.m_animation = new ozz::animation::Animation;
|
||||||
|
|
||||||
|
archive >> *iter->second.m_animation;
|
||||||
|
|
||||||
|
mManagedAnimations.push_back(iter->second.m_animation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void to_json(nlohmann::json& j, const AnimLibrary& animation_library) {
|
||||||
|
j["type"] = "AnimationLibrary";
|
||||||
|
|
||||||
|
for (AnimLibrary::AnimationResourceMap::const_iterator iter =
|
||||||
|
animation_library.mAnimations.cbegin();
|
||||||
|
iter != animation_library.mAnimations.cend();
|
||||||
|
++iter) {
|
||||||
|
j["animations"][iter->first] = iter->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void from_json(const nlohmann::json& j, AnimLibrary& animation_library) {
|
||||||
|
animation_library.Reset();
|
||||||
|
|
||||||
|
if (!j.contains("type") || j["type"] != "AnimationLibrary") {
|
||||||
|
std::cerr << "Invalid type. Expected 'AnimationLibrary'." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!j.contains("animations")) {
|
||||||
|
std::cerr << "Invalid AnimationLibrary. Expected 'animations' key."
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (nlohmann::json::const_iterator iter = j["animations"].begin();
|
||||||
|
iter != j["animations"].cend();
|
||||||
|
++iter) {
|
||||||
|
AnimationResource animation_resource = *iter;
|
||||||
|
|
||||||
|
if (!animation_resource.m_filename.empty()) {
|
||||||
|
animation_library.AddAnimationFile(
|
||||||
|
iter.key(),
|
||||||
|
j["animations"][iter.key()]["filename"]);
|
||||||
|
animation_library.mAnimations[iter.key()].m_sync_track =
|
||||||
|
animation_resource.m_sync_track;
|
||||||
|
} else {
|
||||||
|
animation_library.mAnimations[iter.key()] = *iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //ANIMLIBRARY_H
|
||||||
@@ -3,7 +3,3 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "SyncTrack.h"
|
#include "SyncTrack.h"
|
||||||
|
|
||||||
#include <imgui.h>
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "imnodes.h"
|
#include "imnodes.h"
|
||||||
#include "misc/cpp/imgui_stdlib.h"
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
#include "nfd.h"
|
#include "nfd.h"
|
||||||
|
#include "ozz/base/log.h"
|
||||||
#include "src/AnimGraph/AnimGraphResource.h"
|
#include "src/AnimGraph/AnimGraphResource.h"
|
||||||
|
|
||||||
struct EditorState {
|
struct EditorState {
|
||||||
@@ -154,21 +155,7 @@ void SyncTrackEditor(SyncTrack* sync_track) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("Marker:");
|
ImGui::Text("Marker:");
|
||||||
for (int i = 0; i < sync_track->m_num_intervals; i++) {
|
ImGui::Text("TODO");
|
||||||
ImGui::Text("%2d:", i);
|
|
||||||
ImGui::SameLine();
|
|
||||||
std::ostringstream marker_stream;
|
|
||||||
marker_stream << i;
|
|
||||||
ImGui::SliderFloat(
|
|
||||||
marker_stream.str().c_str(),
|
|
||||||
&sync_track->m_sync_markers[i],
|
|
||||||
0.f,
|
|
||||||
1.f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Update Intervals")) {
|
|
||||||
sync_track->CalcIntervals();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
|
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
|
||||||
|
|||||||
+5
-2
@@ -4,9 +4,13 @@
|
|||||||
|
|
||||||
#include "SkinnedMesh.h"
|
#include "SkinnedMesh.h"
|
||||||
|
|
||||||
#include <HandmadeMath.h>
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include "ozz/animation/runtime/local_to_model_job.h"
|
||||||
|
#include "ozz/base/io/archive.h"
|
||||||
|
#include "ozz/base/io/stream.h"
|
||||||
|
#include "ozz/base/log.h"
|
||||||
|
|
||||||
SkinnedMesh::~SkinnedMesh() {
|
SkinnedMesh::~SkinnedMesh() {
|
||||||
while (m_animations.size() > 0) {
|
while (m_animations.size() > 0) {
|
||||||
ozz::animation::Animation* animation_ptr =
|
ozz::animation::Animation* animation_ptr =
|
||||||
@@ -98,4 +102,3 @@ void SkinnedMesh::CalcModelMatrices() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SkinnedMesh::DrawSkeleton() {}
|
void SkinnedMesh::DrawSkeleton() {}
|
||||||
|
|
||||||
|
|||||||
+1
-9
@@ -5,21 +5,13 @@
|
|||||||
#ifndef ANIMTESTBED_SKINNEDMESH_H
|
#ifndef ANIMTESTBED_SKINNEDMESH_H
|
||||||
#define ANIMTESTBED_SKINNEDMESH_H
|
#define ANIMTESTBED_SKINNEDMESH_H
|
||||||
|
|
||||||
// ozz-animation headers
|
#include "AnimGraph/SyncTrack.h"
|
||||||
#include <cmath> // fmodf
|
|
||||||
#include <memory> // std::unique_ptr, std::make_unique
|
|
||||||
|
|
||||||
#include "SyncTrack.h"
|
|
||||||
#include "ozz/animation/runtime/animation.h"
|
#include "ozz/animation/runtime/animation.h"
|
||||||
#include "ozz/animation/runtime/local_to_model_job.h"
|
|
||||||
#include "ozz/animation/runtime/sampling_job.h"
|
#include "ozz/animation/runtime/sampling_job.h"
|
||||||
#include "ozz/animation/runtime/skeleton.h"
|
#include "ozz/animation/runtime/skeleton.h"
|
||||||
#include "ozz/base/containers/vector.h"
|
#include "ozz/base/containers/vector.h"
|
||||||
#include "ozz/base/io/archive.h"
|
|
||||||
#include "ozz/base/io/stream.h"
|
|
||||||
#include "ozz/base/log.h"
|
|
||||||
#include "ozz/base/maths/soa_transform.h"
|
#include "ozz/base/maths/soa_transform.h"
|
||||||
#include "ozz/base/maths/vec_float.h"
|
|
||||||
|
|
||||||
struct SkinnedMesh {
|
struct SkinnedMesh {
|
||||||
SkinnedMesh() : m_sync_track_override(false), m_override_anim(0.f) {}
|
SkinnedMesh() : m_sync_track_override(false), m_override_anim(0.f) {}
|
||||||
|
|||||||
@@ -5,72 +5,15 @@
|
|||||||
#include "SkinnedMeshResource.h"
|
#include "SkinnedMeshResource.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "3rdparty/json/json.hpp"
|
#include "3rdparty/json/json.hpp"
|
||||||
|
|
||||||
inline void to_json(nlohmann::json& j, const SyncTrack& syncTrack) {
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
|
||||||
j["type"] = "SyncTrack";
|
SkinnedMeshResource,
|
||||||
j["duration"] = syncTrack.m_duration;
|
m_type,
|
||||||
for (int i = 0; i < syncTrack.m_num_intervals; i++) {
|
m_resource_file,
|
||||||
j["markers"][i] = syncTrack.m_sync_markers[i];
|
m_skeleton_file);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void from_json(const nlohmann::json& j, SyncTrack& syncTrack) {
|
|
||||||
if (!j.contains("type") || j["type"] != "SyncTrack") {
|
|
||||||
std::cerr << "Unable to parse SyncTrack: wrong json type!" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
syncTrack.m_duration = j["duration"];
|
|
||||||
syncTrack.m_num_intervals = j["markers"].size();
|
|
||||||
|
|
||||||
if (syncTrack.m_num_intervals > cSyncTrackMaxIntervals) {
|
|
||||||
std::cerr << "Invalid number of sync intervals: found " << syncTrack.m_num_intervals << " maximum is " << cSyncTrackMaxIntervals << "." << std::endl;
|
|
||||||
syncTrack = SyncTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < syncTrack.m_num_intervals; i++) {
|
|
||||||
syncTrack.m_sync_markers[i] = j["markers"].at(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void to_json(nlohmann::json& j, const SkinnedMeshResource& skinnedMeshResource) {
|
|
||||||
j["type"] = "SkinnedMeshResource";
|
|
||||||
j["skeleton"]["file"] = skinnedMeshResource.m_skeleton_file;
|
|
||||||
for (int i = 0; i < skinnedMeshResource.m_animation_files.size(); i++) {
|
|
||||||
j["animations"][i]["file"] = skinnedMeshResource.m_animation_files[i];
|
|
||||||
j["animations"][i]["sync_track"] = skinnedMeshResource.m_sync_tracks[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void from_json(const nlohmann::json& j, SkinnedMeshResource& skinnedMeshResource) {
|
|
||||||
if (!j.contains("type") || j["type"] != "SkinnedMeshResource") {
|
|
||||||
std::cerr << "Unable to parse SkinnedMeshResource: wrong json type!" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!j.contains("skeleton") || !j["skeleton"].contains("file")) {
|
|
||||||
std::cerr << "Unable to parse SkinnedMeshResource: no skeleton file found!" << std::endl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
skinnedMeshResource.m_skeleton_file = j["skeleton"]["file"];
|
|
||||||
|
|
||||||
if (j.contains("animations")) {
|
|
||||||
int num_animations = j["animations"].size();
|
|
||||||
|
|
||||||
for (int i = 0; i < num_animations; i++) {
|
|
||||||
if (!j["animations"][i].contains("file") || !j["animations"][i].contains("sync_track")) {
|
|
||||||
std::cerr << "Unable to parse SkinnedMeshResource: invalid animation definition" << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
skinnedMeshResource.m_animation_files.push_back(j["animations"][i]["file"]);
|
|
||||||
skinnedMeshResource.m_sync_tracks.push_back(j["animations"][i]["sync_track"].get<SyncTrack>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SkinnedMeshResource::saveToFile(const char* filename) const {
|
bool SkinnedMeshResource::saveToFile(const char* filename) const {
|
||||||
nlohmann::json j = *this;
|
nlohmann::json j = *this;
|
||||||
@@ -103,10 +46,4 @@ bool SkinnedMeshResource::loadFromFile(const char* filename) {
|
|||||||
|
|
||||||
void SkinnedMeshResource::createInstance(SkinnedMesh& skinnedMesh) const {
|
void SkinnedMeshResource::createInstance(SkinnedMesh& skinnedMesh) const {
|
||||||
skinnedMesh.LoadSkeleton(m_skeleton_file.c_str());
|
skinnedMesh.LoadSkeleton(m_skeleton_file.c_str());
|
||||||
for (int i = 0; i < m_animation_files.size(); i++) {
|
|
||||||
skinnedMesh.LoadAnimation(m_animation_files[i].c_str());
|
|
||||||
skinnedMesh.m_animation_sync_track.back() = m_sync_tracks[i];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "SyncTrack.h"
|
#include "AnimGraph/SyncTrack.h"
|
||||||
#include "SkinnedMesh.h"
|
#include "SkinnedMesh.h"
|
||||||
|
|
||||||
struct SkinnedMeshResource {
|
struct SkinnedMeshResource {
|
||||||
|
constexpr static char TypeStr[] = "SkinnedMeshResource";
|
||||||
|
std::string m_type = TypeStr;
|
||||||
std::string m_resource_file;
|
std::string m_resource_file;
|
||||||
std::string m_skeleton_file;
|
std::string m_skeleton_file;
|
||||||
std::vector<std::string> m_animation_files;
|
|
||||||
std::vector<SyncTrack> m_sync_tracks;
|
|
||||||
|
|
||||||
bool saveToFile(const char* filename) const;
|
bool saveToFile(const char* filename) const;
|
||||||
bool loadFromFile(const char* filename);
|
bool loadFromFile(const char* filename);
|
||||||
|
|||||||
+101
-213
@@ -254,7 +254,6 @@ static void draw_imgui(ImDrawData*);
|
|||||||
#include "ozz/animation/runtime/skeleton.h"
|
#include "ozz/animation/runtime/skeleton.h"
|
||||||
#include "ozz/base/containers/vector.h"
|
#include "ozz/base/containers/vector.h"
|
||||||
#include "ozz/base/io/archive.h"
|
#include "ozz/base/io/archive.h"
|
||||||
#include "ozz/base/io/stream.h"
|
|
||||||
#include "ozz/base/log.h"
|
#include "ozz/base/log.h"
|
||||||
#include "ozz/base/maths/soa_transform.h"
|
#include "ozz/base/maths/soa_transform.h"
|
||||||
|
|
||||||
@@ -366,191 +365,80 @@ struct Viewport {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ApplicationConfig {
|
struct ApplicationConfig {
|
||||||
int window_position[2] = {100, 30};
|
constexpr static char TypeStr[] = "ApplicationConfig";
|
||||||
int window_size[2] = {1000, 600};
|
std::string type = TypeStr;
|
||||||
|
struct ScreenRect {
|
||||||
|
int position[2];
|
||||||
|
int size[2];
|
||||||
|
};
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(ScreenRect, position, size);
|
||||||
|
ScreenRect window = {.position = {100, 30}, .size = {1000, 600}};
|
||||||
|
|
||||||
struct GraphEditor {
|
struct GraphEditor {
|
||||||
|
ScreenRect rect = {.position = {20, 20}, .size = {800, 500}};
|
||||||
bool visible = false;
|
bool visible = false;
|
||||||
int position[2] = {20, 20};
|
|
||||||
int size[2] = {800, 500};
|
|
||||||
ax::NodeEditor::Config config = {};
|
ax::NodeEditor::Config config = {};
|
||||||
ax::NodeEditor::EditorContext* context = nullptr;
|
ax::NodeEditor::EditorContext* context = nullptr;
|
||||||
};
|
};
|
||||||
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(GraphEditor, rect, visible);
|
||||||
GraphEditor graph_editor;
|
GraphEditor graph_editor;
|
||||||
|
|
||||||
struct SkinnedMeshWidget {
|
struct WidgetRect {
|
||||||
|
ScreenRect rect;
|
||||||
bool visible = false;
|
bool visible = false;
|
||||||
int position[2] = {20, 20};
|
|
||||||
int size[2] = {800, 500};
|
|
||||||
};
|
};
|
||||||
SkinnedMeshWidget skinned_mesh_widget;
|
NLOHMANN_DEFINE_TYPE_INTRUSIVE(WidgetRect, rect, visible);
|
||||||
|
WidgetRect skinned_mesh_widget = {
|
||||||
|
.rect = {.position = {20, 20}, .size = {800, 500}},
|
||||||
|
.visible = false};
|
||||||
|
|
||||||
struct AnimationPlayerWidget {
|
WidgetRect animation_player_widget = {
|
||||||
bool visible = false;
|
.rect = {.position = {20, 20}, .size = {800, 500}},
|
||||||
int position[2] = {20, 20};
|
.visible = false};
|
||||||
int size[2] = {800, 500};
|
|
||||||
};
|
|
||||||
AnimationPlayerWidget animation_player_widget;
|
|
||||||
|
|
||||||
struct ViewportWidget {
|
WidgetRect viewport_widget;
|
||||||
bool visible = true;
|
|
||||||
int position[2] = {20, 20};
|
|
||||||
int size[2] = {800, 500};
|
|
||||||
};
|
|
||||||
ViewportWidget viewport_widget;
|
|
||||||
|
|
||||||
bool show_imgui_demo_window = false;
|
bool show_imgui_demo_window = false;
|
||||||
bool show_another_window = false;
|
bool show_another_window = false;
|
||||||
bool show_camera_widget = false;
|
bool show_camera_widget = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
void to_json(nlohmann::json& j, const ApplicationConfig& config) {
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
|
||||||
j["type"] = "AnimTestbedConfig";
|
ApplicationConfig,
|
||||||
|
type,
|
||||||
|
window,
|
||||||
|
graph_editor,
|
||||||
|
skinned_mesh_widget,
|
||||||
|
animation_player_widget,
|
||||||
|
viewport_widget,
|
||||||
|
show_imgui_demo_window,
|
||||||
|
show_another_window,
|
||||||
|
show_camera_widget);
|
||||||
|
|
||||||
j["main_window"]["position"][0] = config.window_position[0];
|
struct ProjectConfig {
|
||||||
j["main_window"]["position"][1] = config.window_position[1];
|
static constexpr char TypeStr[] = "ProjectConfig";
|
||||||
|
std::string type = TypeStr;
|
||||||
|
std::string animation_library_file = "";
|
||||||
|
std::string skeleton_file = "";
|
||||||
|
std::string graph_resource_file = "";
|
||||||
|
struct ViewConfig {
|
||||||
|
int pos[2];
|
||||||
|
};
|
||||||
|
ViewConfig view_config = {.pos = {300, 200}};
|
||||||
|
};
|
||||||
|
|
||||||
j["main_window"]["size"][0] = config.window_size[0];
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ProjectConfig::ViewConfig, pos);
|
||||||
j["main_window"]["size"][1] = config.window_size[1];
|
|
||||||
|
|
||||||
j["graph_editor"]["visible"] = config.graph_editor.visible;
|
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
|
||||||
j["graph_editor"]["position"][0] = config.graph_editor.position[0];
|
ProjectConfig,
|
||||||
j["graph_editor"]["position"][1] = config.graph_editor.position[1];
|
type,
|
||||||
j["graph_editor"]["size"][0] = config.graph_editor.size[0];
|
animation_library_file,
|
||||||
j["graph_editor"]["size"][1] = config.graph_editor.size[1];
|
skeleton_file,
|
||||||
|
graph_resource_file,
|
||||||
j["skinned_mesh_widget"]["visible"] = config.skinned_mesh_widget.visible;
|
view_config);
|
||||||
j["skinned_mesh_widget"]["position"][0] =
|
|
||||||
config.skinned_mesh_widget.position[0];
|
|
||||||
j["skinned_mesh_widget"]["position"][1] =
|
|
||||||
config.skinned_mesh_widget.position[1];
|
|
||||||
j["skinned_mesh_widget"]["size"][0] = config.skinned_mesh_widget.size[0];
|
|
||||||
j["skinned_mesh_widget"]["size"][1] = config.skinned_mesh_widget.size[1];
|
|
||||||
|
|
||||||
j["animation_player_widget"]["visible"] =
|
|
||||||
config.animation_player_widget.visible;
|
|
||||||
j["animation_player_widget"]["position"][0] =
|
|
||||||
config.animation_player_widget.position[0];
|
|
||||||
j["animation_player_widget"]["position"][1] =
|
|
||||||
config.animation_player_widget.position[1];
|
|
||||||
j["animation_player_widget"]["size"][0] =
|
|
||||||
config.animation_player_widget.size[0];
|
|
||||||
j["animation_player_widget"]["size"][1] =
|
|
||||||
config.animation_player_widget.size[1];
|
|
||||||
|
|
||||||
j["viewport_widget"]["visible"] = config.viewport_widget.visible;
|
|
||||||
j["viewport_widget"]["position"][0] = config.viewport_widget.position[0];
|
|
||||||
j["viewport_widget"]["position"][1] = config.viewport_widget.position[1];
|
|
||||||
j["viewport_widget"]["size"][0] = config.viewport_widget.size[0];
|
|
||||||
j["viewport_widget"]["size"][1] = config.viewport_widget.size[1];
|
|
||||||
|
|
||||||
j["camera_widget"]["visible"] = config.show_camera_widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(const nlohmann::json& j, ApplicationConfig& config) {
|
|
||||||
if (j.contains("main_window")) {
|
|
||||||
if (j["main_window"].contains("position")
|
|
||||||
and j["main_window"]["position"].size() == 2) {
|
|
||||||
config.window_position[0] = j["main_window"]["position"].at(0);
|
|
||||||
config.window_position[1] = j["main_window"]["position"].at(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["main_window"].contains("size")
|
|
||||||
and j["main_window"]["size"].size() == 2) {
|
|
||||||
config.window_size[0] = j["main_window"]["size"].at(0);
|
|
||||||
config.window_size[1] = j["main_window"]["size"].at(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("graph_editor")) {
|
|
||||||
if (j["graph_editor"].contains("visible")) {
|
|
||||||
config.graph_editor.visible = j["graph_editor"]["visible"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["graph_editor"].contains("position")
|
|
||||||
and j["graph_editor"]["position"].size() == 2) {
|
|
||||||
config.graph_editor.position[0] = j["graph_editor"]["position"].at(0);
|
|
||||||
config.graph_editor.position[1] = j["graph_editor"]["position"].at(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["graph_editor"].contains("size")
|
|
||||||
and j["graph_editor"]["size"].size() == 2) {
|
|
||||||
config.graph_editor.size[0] = j["graph_editor"]["size"].at(0);
|
|
||||||
config.graph_editor.size[1] = j["graph_editor"]["size"].at(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("skinned_mesh_widget")) {
|
|
||||||
if (j["skinned_mesh_widget"].contains("visible")) {
|
|
||||||
config.skinned_mesh_widget.visible = j["skinned_mesh_widget"]["visible"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["skinned_mesh_widget"].contains("position")
|
|
||||||
and j["skinned_mesh_widget"]["position"].size() == 2) {
|
|
||||||
config.skinned_mesh_widget.position[0] =
|
|
||||||
j["skinned_mesh_widget"]["position"].at(0);
|
|
||||||
config.skinned_mesh_widget.position[1] =
|
|
||||||
j["skinned_mesh_widget"]["position"].at(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["skinned_mesh_widget"].contains("size")
|
|
||||||
and j["skinned_mesh_widget"]["size"].size() == 2) {
|
|
||||||
config.skinned_mesh_widget.size[0] =
|
|
||||||
j["skinned_mesh_widget"]["size"].at(0);
|
|
||||||
config.skinned_mesh_widget.size[1] =
|
|
||||||
j["skinned_mesh_widget"]["size"].at(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("animation_player_widget")) {
|
|
||||||
if (j["animation_player_widget"].contains("visible")) {
|
|
||||||
config.animation_player_widget.visible =
|
|
||||||
j["animation_player_widget"]["visible"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["animation_player_widget"].contains("position")
|
|
||||||
and j["animation_player_widget"]["position"].size() == 2) {
|
|
||||||
config.animation_player_widget.position[0] =
|
|
||||||
j["animation_player_widget"]["position"].at(0);
|
|
||||||
config.animation_player_widget.position[1] =
|
|
||||||
j["animation_player_widget"]["position"].at(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["animation_player_widget"].contains("size")
|
|
||||||
and j["animation_player_widget"]["size"].size() == 2) {
|
|
||||||
config.animation_player_widget.size[0] =
|
|
||||||
j["animation_player_widget"]["size"].at(0);
|
|
||||||
config.animation_player_widget.size[1] =
|
|
||||||
j["animation_player_widget"]["size"].at(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("viewport_widget")) {
|
|
||||||
if (j["viewport_widget"].contains("visible")) {
|
|
||||||
config.viewport_widget.visible = j["viewport_widget"]["visible"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["viewport_widget"].contains("position")
|
|
||||||
and j["viewport_widget"]["position"].size() == 2) {
|
|
||||||
config.viewport_widget.position[0] =
|
|
||||||
j["viewport_widget"]["position"].at(0);
|
|
||||||
config.viewport_widget.position[1] =
|
|
||||||
j["viewport_widget"]["position"].at(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j["viewport_widget"].contains("size")
|
|
||||||
and j["viewport_widget"]["size"].size() == 2) {
|
|
||||||
config.viewport_widget.size[0] = j["viewport_widget"]["size"].at(0);
|
|
||||||
config.viewport_widget.size[1] = j["viewport_widget"]["size"].at(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j.contains("camera_widget") and j["camera_widget"].contains("visible")) {
|
|
||||||
config.show_camera_widget = j["camera_widget"]["visible"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ApplicationConfig gApplicationConfig;
|
ApplicationConfig gApplicationConfig;
|
||||||
|
ProjectConfig gProjectConfig;
|
||||||
|
|
||||||
static void draw_grid();
|
static void draw_grid();
|
||||||
|
|
||||||
@@ -592,8 +480,8 @@ void load_application_config(const char* filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (application_config.value("type", "undefined") != "AnimTestbedConfig") {
|
if (application_config.value("type", "undefined") != "AnimTestbedConfig") {
|
||||||
std::cerr
|
std::cerr << "Invalid json object. Expected type '"
|
||||||
<< "Invalid json object. Expected type 'AnimTestbedConfig' but got '"
|
<< ApplicationConfig::TypeStr << "' but got '"
|
||||||
<< application_config["type"] << "'." << std::endl;
|
<< application_config["type"] << "'." << std::endl;
|
||||||
application_config["type"] = "AnimTestbedConfig";
|
application_config["type"] = "AnimTestbedConfig";
|
||||||
}
|
}
|
||||||
@@ -640,20 +528,20 @@ int main() {
|
|||||||
glfwSwapInterval(1);
|
glfwSwapInterval(1);
|
||||||
|
|
||||||
load_application_config("animtestbed_config.json");
|
load_application_config("animtestbed_config.json");
|
||||||
if (gApplicationConfig.window_position[0] != 0
|
if (gApplicationConfig.window.position[0] != 0
|
||||||
|| gApplicationConfig.window_position[1] != 0) {
|
|| gApplicationConfig.window.position[1] != 0) {
|
||||||
glfwSetWindowPos(
|
glfwSetWindowPos(
|
||||||
window,
|
window,
|
||||||
gApplicationConfig.window_position[0],
|
gApplicationConfig.window.position[0],
|
||||||
gApplicationConfig.window_position[1]);
|
gApplicationConfig.window.position[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gApplicationConfig.window_size[0] != 0
|
if (gApplicationConfig.window.size[0] != 0
|
||||||
|| gApplicationConfig.window_size[1] != 0) {
|
|| gApplicationConfig.window.size[1] != 0) {
|
||||||
glfwSetWindowSize(
|
glfwSetWindowSize(
|
||||||
window,
|
window,
|
||||||
gApplicationConfig.window_size[0],
|
gApplicationConfig.window.size[0],
|
||||||
gApplicationConfig.window_size[1]);
|
gApplicationConfig.window.size[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
NFD_Init();
|
NFD_Init();
|
||||||
@@ -673,15 +561,11 @@ int main() {
|
|||||||
sgl_setup(&sgldesc);
|
sgl_setup(&sgldesc);
|
||||||
sgl_defaults();
|
sgl_defaults();
|
||||||
|
|
||||||
// sgl_context_desc_t sgl_context_desc = {};
|
|
||||||
// sgl_context ctx = sgl_make_context(&sgl_context_desc);
|
|
||||||
|
|
||||||
SkinnedMeshResource skinned_mesh_resource;
|
SkinnedMeshResource skinned_mesh_resource;
|
||||||
skinned_mesh_resource.loadFromFile("../media/SampleSkinnedMesh.json");
|
skinned_mesh_resource.loadFromFile("../media/SampleSkinnedMesh.json");
|
||||||
|
|
||||||
SkinnedMesh skinned_mesh;
|
SkinnedMesh skinned_mesh;
|
||||||
skinned_mesh_resource.createInstance(skinned_mesh);
|
skinned_mesh_resource.createInstance(skinned_mesh);
|
||||||
skinned_mesh.SetCurrentAnimation(0);
|
|
||||||
|
|
||||||
AnimGraphBlendTree anim_graph;
|
AnimGraphBlendTree anim_graph;
|
||||||
AnimGraphContext anim_graph_context;
|
AnimGraphContext anim_graph_context;
|
||||||
@@ -726,13 +610,13 @@ int main() {
|
|||||||
// Update window state
|
// Update window state
|
||||||
glfwGetWindowPos(
|
glfwGetWindowPos(
|
||||||
window,
|
window,
|
||||||
&gApplicationConfig.window_position[0],
|
&gApplicationConfig.window.position[0],
|
||||||
&gApplicationConfig.window_position[1]);
|
&gApplicationConfig.window.position[1]);
|
||||||
|
|
||||||
glfwGetWindowSize(
|
glfwGetWindowSize(
|
||||||
window,
|
window,
|
||||||
&gApplicationConfig.window_size[0],
|
&gApplicationConfig.window.size[0],
|
||||||
&gApplicationConfig.window_size[1]);
|
&gApplicationConfig.window.size[1]);
|
||||||
|
|
||||||
int cur_width, cur_height;
|
int cur_width, cur_height;
|
||||||
glfwGetFramebufferSize(window, &cur_width, &cur_height);
|
glfwGetFramebufferSize(window, &cur_width, &cur_height);
|
||||||
@@ -860,14 +744,16 @@ int main() {
|
|||||||
ImGui::SetNextWindowPos(
|
ImGui::SetNextWindowPos(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.viewport_widget.position[0]),
|
gApplicationConfig.viewport_widget.rect.position[0]),
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.viewport_widget.position[1])),
|
gApplicationConfig.viewport_widget.rect.position[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(
|
ImGui::SetNextWindowSize(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(gApplicationConfig.viewport_widget.size[0]),
|
static_cast<float>(
|
||||||
static_cast<float>(gApplicationConfig.viewport_widget.size[1])),
|
gApplicationConfig.viewport_widget.rect.size[0]),
|
||||||
|
static_cast<float>(
|
||||||
|
gApplicationConfig.viewport_widget.rect.size[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible);
|
ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible);
|
||||||
@@ -882,9 +768,9 @@ int main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImVec2 viewport_widget_size = ImGui::GetWindowSize();
|
ImVec2 viewport_widget_size = ImGui::GetWindowSize();
|
||||||
gApplicationConfig.viewport_widget.size[0] =
|
gApplicationConfig.viewport_widget.rect.size[0] =
|
||||||
static_cast<int>(viewport_widget_size.x);
|
static_cast<int>(viewport_widget_size.x);
|
||||||
gApplicationConfig.viewport_widget.size[1] =
|
gApplicationConfig.viewport_widget.rect.size[1] =
|
||||||
static_cast<int>(viewport_widget_size.y);
|
static_cast<int>(viewport_widget_size.y);
|
||||||
|
|
||||||
ImGui::Text(
|
ImGui::Text(
|
||||||
@@ -932,16 +818,16 @@ int main() {
|
|||||||
ImGui::SetNextWindowPos(
|
ImGui::SetNextWindowPos(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.skinned_mesh_widget.position[0]),
|
gApplicationConfig.skinned_mesh_widget.rect.position[0]),
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.skinned_mesh_widget.position[1])),
|
gApplicationConfig.skinned_mesh_widget.rect.position[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(
|
ImGui::SetNextWindowSize(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.skinned_mesh_widget.size[0]),
|
gApplicationConfig.skinned_mesh_widget.rect.size[0]),
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.skinned_mesh_widget.size[1])),
|
gApplicationConfig.skinned_mesh_widget.rect.size[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
ImGui::Begin(
|
ImGui::Begin(
|
||||||
@@ -949,15 +835,15 @@ int main() {
|
|||||||
&gApplicationConfig.skinned_mesh_widget.visible);
|
&gApplicationConfig.skinned_mesh_widget.visible);
|
||||||
|
|
||||||
ImVec2 skinned_mesh_widget_position = ImGui::GetWindowPos();
|
ImVec2 skinned_mesh_widget_position = ImGui::GetWindowPos();
|
||||||
gApplicationConfig.skinned_mesh_widget.position[0] =
|
gApplicationConfig.skinned_mesh_widget.rect.position[0] =
|
||||||
static_cast<int>(skinned_mesh_widget_position.x);
|
static_cast<int>(skinned_mesh_widget_position.x);
|
||||||
gApplicationConfig.skinned_mesh_widget.position[1] =
|
gApplicationConfig.skinned_mesh_widget.rect.position[1] =
|
||||||
static_cast<int>(skinned_mesh_widget_position.y);
|
static_cast<int>(skinned_mesh_widget_position.y);
|
||||||
|
|
||||||
ImVec2 skinned_mesh_widget_size = ImGui::GetWindowSize();
|
ImVec2 skinned_mesh_widget_size = ImGui::GetWindowSize();
|
||||||
gApplicationConfig.skinned_mesh_widget.size[0] =
|
gApplicationConfig.skinned_mesh_widget.rect.size[0] =
|
||||||
static_cast<int>(skinned_mesh_widget_size.x);
|
static_cast<int>(skinned_mesh_widget_size.x);
|
||||||
gApplicationConfig.skinned_mesh_widget.size[1] =
|
gApplicationConfig.skinned_mesh_widget.rect.size[1] =
|
||||||
static_cast<int>(skinned_mesh_widget_size.y);
|
static_cast<int>(skinned_mesh_widget_size.y);
|
||||||
|
|
||||||
SkinnedMeshWidget(&skinned_mesh);
|
SkinnedMeshWidget(&skinned_mesh);
|
||||||
@@ -968,17 +854,17 @@ int main() {
|
|||||||
if (gApplicationConfig.animation_player_widget.visible) {
|
if (gApplicationConfig.animation_player_widget.visible) {
|
||||||
ImGui::SetNextWindowPos(
|
ImGui::SetNextWindowPos(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(
|
static_cast<float>(gApplicationConfig.animation_player_widget
|
||||||
gApplicationConfig.animation_player_widget.position[0]),
|
.rect.position[0]),
|
||||||
static_cast<float>(
|
static_cast<float>(gApplicationConfig.animation_player_widget
|
||||||
gApplicationConfig.animation_player_widget.position[1])),
|
.rect.position[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(
|
ImGui::SetNextWindowSize(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.animation_player_widget.size[0]),
|
gApplicationConfig.animation_player_widget.rect.size[0]),
|
||||||
static_cast<float>(
|
static_cast<float>(
|
||||||
gApplicationConfig.animation_player_widget.size[1])),
|
gApplicationConfig.animation_player_widget.rect.size[1])),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
ImGui::Begin(
|
ImGui::Begin(
|
||||||
@@ -986,15 +872,15 @@ int main() {
|
|||||||
&gApplicationConfig.animation_player_widget.visible);
|
&gApplicationConfig.animation_player_widget.visible);
|
||||||
|
|
||||||
ImVec2 animation_player_widget_position = ImGui::GetWindowPos();
|
ImVec2 animation_player_widget_position = ImGui::GetWindowPos();
|
||||||
gApplicationConfig.animation_player_widget.position[0] =
|
gApplicationConfig.animation_player_widget.rect.position[0] =
|
||||||
static_cast<int>(animation_player_widget_position.x);
|
static_cast<int>(animation_player_widget_position.x);
|
||||||
gApplicationConfig.animation_player_widget.position[1] =
|
gApplicationConfig.animation_player_widget.rect.position[1] =
|
||||||
static_cast<int>(animation_player_widget_position.y);
|
static_cast<int>(animation_player_widget_position.y);
|
||||||
|
|
||||||
ImVec2 animation_player_widget_size = ImGui::GetWindowSize();
|
ImVec2 animation_player_widget_size = ImGui::GetWindowSize();
|
||||||
gApplicationConfig.animation_player_widget.size[0] =
|
gApplicationConfig.animation_player_widget.rect.size[0] =
|
||||||
static_cast<int>(animation_player_widget_size.x);
|
static_cast<int>(animation_player_widget_size.x);
|
||||||
gApplicationConfig.animation_player_widget.size[1] =
|
gApplicationConfig.animation_player_widget.rect.size[1] =
|
||||||
static_cast<int>(animation_player_widget_size.y);
|
static_cast<int>(animation_player_widget_size.y);
|
||||||
|
|
||||||
if (anim_graph.m_nodes.size() > 0) {
|
if (anim_graph.m_nodes.size() > 0) {
|
||||||
@@ -1096,13 +982,13 @@ int main() {
|
|||||||
if (gApplicationConfig.graph_editor.visible) {
|
if (gApplicationConfig.graph_editor.visible) {
|
||||||
ImGui::SetNextWindowPos(
|
ImGui::SetNextWindowPos(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
gApplicationConfig.graph_editor.position[0],
|
gApplicationConfig.graph_editor.rect.position[0],
|
||||||
gApplicationConfig.graph_editor.position[1]),
|
gApplicationConfig.graph_editor.rect.position[1]),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
ImGui::SetNextWindowSize(
|
ImGui::SetNextWindowSize(
|
||||||
ImVec2(
|
ImVec2(
|
||||||
gApplicationConfig.graph_editor.size[0],
|
gApplicationConfig.graph_editor.rect.size[0],
|
||||||
gApplicationConfig.graph_editor.size[1]),
|
gApplicationConfig.graph_editor.rect.size[1]),
|
||||||
ImGuiCond_FirstUseEver);
|
ImGuiCond_FirstUseEver);
|
||||||
|
|
||||||
ImGui::Begin(
|
ImGui::Begin(
|
||||||
@@ -1111,12 +997,14 @@ int main() {
|
|||||||
ImGuiWindowFlags_MenuBar);
|
ImGuiWindowFlags_MenuBar);
|
||||||
|
|
||||||
ImVec2 graph_editor_position = ImGui::GetWindowPos();
|
ImVec2 graph_editor_position = ImGui::GetWindowPos();
|
||||||
gApplicationConfig.graph_editor.position[0] = graph_editor_position.x;
|
gApplicationConfig.graph_editor.rect.position[0] =
|
||||||
gApplicationConfig.graph_editor.position[1] = graph_editor_position.y;
|
graph_editor_position.x;
|
||||||
|
gApplicationConfig.graph_editor.rect.position[1] =
|
||||||
|
graph_editor_position.y;
|
||||||
|
|
||||||
ImVec2 graph_editor_size = ImGui::GetWindowSize();
|
ImVec2 graph_editor_size = ImGui::GetWindowSize();
|
||||||
gApplicationConfig.graph_editor.size[0] = graph_editor_size.x;
|
gApplicationConfig.graph_editor.rect.size[0] = graph_editor_size.x;
|
||||||
gApplicationConfig.graph_editor.size[1] = graph_editor_size.y;
|
gApplicationConfig.graph_editor.rect.size[1] = graph_editor_size.y;
|
||||||
|
|
||||||
AnimGraphEditorUpdate(gApplicationConfig.graph_editor.context);
|
AnimGraphEditorUpdate(gApplicationConfig.graph_editor.context);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Created by martin on 11.04.25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "3rdparty/json/json.hpp"
|
||||||
|
#include "AnimGraph/AnimLibrary.h"
|
||||||
|
#include "TestAnimData.h"
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
using namespace nlohmann;
|
||||||
|
|
||||||
|
TEST_CASE("Serialize AnimLibrary", "[AnimLibrary]") {
|
||||||
|
AnimLibrary library;
|
||||||
|
|
||||||
|
TestAnimData::SingleBoneSkeleton single_bone_testdata;
|
||||||
|
|
||||||
|
REQUIRE(library.AddAnimationFile(
|
||||||
|
"translation_x",
|
||||||
|
single_bone_testdata.animation_translate_x_resource.m_filename));
|
||||||
|
REQUIRE(library.AddAnimationFile(
|
||||||
|
"translation_y",
|
||||||
|
single_bone_testdata.animation_translate_y_resource.m_filename));
|
||||||
|
|
||||||
|
// We're not actually doing anything with the animations, however loading them here ensures that
|
||||||
|
// when running valgrind a memory leak is detected.
|
||||||
|
library.LoadAnimations();
|
||||||
|
|
||||||
|
// serialize
|
||||||
|
json library_data;
|
||||||
|
library_data = library;
|
||||||
|
|
||||||
|
// deserialize
|
||||||
|
AnimLibrary library_deserialized;
|
||||||
|
library_deserialized = library_data;
|
||||||
|
|
||||||
|
CHECK(library_deserialized.mAnimations.size() == library.mAnimations.size());
|
||||||
|
for (AnimLibrary::AnimationResourceMap::const_iterator iter =
|
||||||
|
library.mAnimations.cbegin();
|
||||||
|
iter != library.mAnimations.cend();
|
||||||
|
++iter) {
|
||||||
|
CHECK(
|
||||||
|
library_deserialized.mAnimations.find(iter->first)
|
||||||
|
!= library_deserialized.mAnimations.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -199,9 +199,13 @@ TEST_CASE_METHOD(
|
|||||||
AnimGraphContext graph_context;
|
AnimGraphContext graph_context;
|
||||||
graph_context.m_skeleton = skeleton.get();
|
graph_context.m_skeleton = skeleton.get();
|
||||||
graph_context.m_animation_map["trans_x"] = {
|
graph_context.m_animation_map["trans_x"] = {
|
||||||
|
"trans_x",
|
||||||
|
"",
|
||||||
animation_translate_x.get(),
|
animation_translate_x.get(),
|
||||||
animation_translate_x_sync_track};
|
animation_translate_x_sync_track};
|
||||||
graph_context.m_animation_map["trans_y"] = {
|
graph_context.m_animation_map["trans_y"] = {
|
||||||
|
"trans_y",
|
||||||
|
"",
|
||||||
animation_translate_y.get(),
|
animation_translate_y.get(),
|
||||||
animation_translate_y_sync_track};
|
animation_translate_y_sync_track};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Created by martin on 16.11.21.
|
// Created by martin on 16.11.21.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "SyncTrack.h"
|
#include "AnimGraph/SyncTrack.h"
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
TEST_CASE("Basic", "[SyncTrack]") {
|
TEST_CASE("Basic", "[SyncTrack]") {
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
//
|
||||||
|
// Created by martin on 11.04.25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "TestAnimData.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "ozz/animation/offline/animation_builder.h"
|
||||||
|
#include "ozz/animation/offline/raw_animation.h"
|
||||||
|
#include "ozz/animation/offline/raw_skeleton.h"
|
||||||
|
#include "ozz/base/io/archive.h"
|
||||||
|
#include "ozz/base/log.h"
|
||||||
|
|
||||||
|
namespace TestAnimData {
|
||||||
|
|
||||||
|
SingleBoneSkeleton::SingleBoneSkeleton() {
|
||||||
|
using namespace ozz::animation::offline;
|
||||||
|
|
||||||
|
RawSkeleton raw_skeleton;
|
||||||
|
RawSkeleton::Joint raw_joint;
|
||||||
|
|
||||||
|
raw_joint.name = "Bone0";
|
||||||
|
raw_joint.transform.translation.x = 1.f;
|
||||||
|
raw_joint.transform.translation.y = 2.f;
|
||||||
|
raw_joint.transform.translation.z = 3.f;
|
||||||
|
|
||||||
|
raw_skeleton.roots.push_back(raw_joint);
|
||||||
|
|
||||||
|
SkeletonBuilder skeleton_builder;
|
||||||
|
skeleton = skeleton_builder(raw_skeleton);
|
||||||
|
|
||||||
|
// SingleBoneSkeleton Animations
|
||||||
|
ozz::animation::offline::RawAnimation raw_animation_translation_x;
|
||||||
|
raw_animation_translation_x.name = "TranslationX";
|
||||||
|
RawAnimation::JointTrack bone0_track;
|
||||||
|
RawAnimation::JointTrack::Translations bone0_translations;
|
||||||
|
|
||||||
|
// animation_translate_x
|
||||||
|
RawAnimation::TranslationKey translation_key;
|
||||||
|
translation_key.time = 0.f;
|
||||||
|
translation_key.value = ozz::math::Float3(0.f, 0.f, 0.f);
|
||||||
|
bone0_translations.push_back(translation_key);
|
||||||
|
|
||||||
|
translation_key.time = 1.f;
|
||||||
|
translation_key.value = ozz::math::Float3(1.f, 0.f, 0.f);
|
||||||
|
bone0_translations.push_back(translation_key);
|
||||||
|
|
||||||
|
bone0_track.translations = bone0_translations;
|
||||||
|
raw_animation_translation_x.tracks.push_back(bone0_track);
|
||||||
|
raw_animation_translation_x.duration = 1.f;
|
||||||
|
if (!raw_animation_translation_x.Validate()) {
|
||||||
|
std::cerr << "Error: could animation raw data invalid!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationBuilder animation_builder;
|
||||||
|
animation_translate_x = animation_builder(raw_animation_translation_x);
|
||||||
|
|
||||||
|
animation_translate_x_resource.m_animation = animation_translate_x.get();
|
||||||
|
SaveAnimation("single_bone_translation_z.ozz", animation_translate_x.get());
|
||||||
|
animation_translate_x_resource.m_name = "single_bone_translation_z";
|
||||||
|
animation_translate_x_resource.m_filename = "single_bone_translation_z.ozz";
|
||||||
|
|
||||||
|
// animation_translate_y
|
||||||
|
ozz::animation::offline::RawAnimation raw_animation_translation_y;
|
||||||
|
raw_animation_translation_y.name = "TranslationY";
|
||||||
|
bone0_translations.clear();
|
||||||
|
|
||||||
|
translation_key.time = 0.f;
|
||||||
|
translation_key.value = ozz::math::Float3(0.f, 0.f, 0.f);
|
||||||
|
bone0_translations.push_back(translation_key);
|
||||||
|
|
||||||
|
translation_key.time = 1.f;
|
||||||
|
translation_key.value = ozz::math::Float3(0.f, 1.f, 0.f);
|
||||||
|
bone0_translations.push_back(translation_key);
|
||||||
|
|
||||||
|
bone0_track.translations = bone0_translations;
|
||||||
|
raw_animation_translation_y.tracks.push_back(bone0_track);
|
||||||
|
raw_animation_translation_y.duration = 1.f;
|
||||||
|
if (!raw_animation_translation_y.Validate()) {
|
||||||
|
std::cerr << "Error: could animation raw data invalid!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
animation_translate_y = animation_builder(raw_animation_translation_y);
|
||||||
|
|
||||||
|
animation_translate_y_resource.m_animation = animation_translate_y.get();
|
||||||
|
SaveAnimation("single_bone_translation_y.ozz", animation_translate_y.get());
|
||||||
|
animation_translate_y_resource.m_name = "single_bone_translation_y";
|
||||||
|
animation_translate_y_resource.m_filename = "single_bone_translation_y.ozz";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleBoneSkeleton::SaveSkeleton(
|
||||||
|
const char* filename,
|
||||||
|
ozz::animation::Skeleton* skeleton) {
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SingleBoneSkeleton::SaveAnimation(
|
||||||
|
const char* filename,
|
||||||
|
ozz::animation::Animation* animation) {
|
||||||
|
ozz::io::File file(filename, "wb");
|
||||||
|
if (!file.opened()) {
|
||||||
|
ozz::log::Err() << "Failed to create animation file " << filename << "."
|
||||||
|
<< std::endl;
|
||||||
|
delete animation;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ozz::io::OArchive archive(&file);
|
||||||
|
|
||||||
|
archive << *animation;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace TestAnimData
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Created by martin on 11.04.25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef TESTANIMDATA_H
|
||||||
|
#define TESTANIMDATA_H
|
||||||
|
|
||||||
|
#include "AnimGraph/AnimGraphData.h"
|
||||||
|
#include "AnimGraph/SyncTrack.h"
|
||||||
|
#include "ozz/animation/offline/skeleton_builder.h"
|
||||||
|
#include "ozz/animation/runtime/animation.h"
|
||||||
|
#include "ozz/animation/runtime/skeleton.h"
|
||||||
|
|
||||||
|
namespace TestAnimData {
|
||||||
|
|
||||||
|
struct SingleBoneSkeleton {
|
||||||
|
SingleBoneSkeleton();
|
||||||
|
|
||||||
|
ozz::unique_ptr<ozz::animation::Skeleton> skeleton = nullptr;
|
||||||
|
|
||||||
|
ozz::unique_ptr<ozz::animation::Animation> animation_translate_x = nullptr;
|
||||||
|
AnimationResource animation_translate_x_resource;
|
||||||
|
SyncTrack animation_translate_x_sync_track = {};
|
||||||
|
|
||||||
|
ozz::unique_ptr<ozz::animation::Animation> animation_translate_y = nullptr;
|
||||||
|
AnimationResource animation_translate_y_resource;
|
||||||
|
SyncTrack animation_translate_y_sync_track = {};
|
||||||
|
|
||||||
|
bool SaveSkeleton(const char* filename, ozz::animation::Skeleton* skeleton);
|
||||||
|
bool SaveAnimation(
|
||||||
|
const char* filename,
|
||||||
|
ozz::animation::Animation* animation);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace TestAnimData
|
||||||
|
|
||||||
|
#endif //TESTANIMDATA_H
|
||||||
Reference in New Issue
Block a user