32 Commits
Author SHA1 Message Date
Martin Felis 99d5a5eb0f Minor refactor. 2024-05-07 18:50:38 +02:00
Martin Felis 698abbce4b BlendTree input and output sockets can now be registered via the AnimGraphResource. 2024-05-01 21:49:34 +02:00
Martin Felis b9789bd1e1 Minor refactor. 2024-05-01 13:17:25 +02:00
Martin Felis fd032c273b Links can now be removed by hovering link and pressing the Delete key. 2024-05-01 13:11:54 +02:00
Martin Felis da431a3879 Fixed setting location of new nodes and minor connection rejection refactor. 2024-05-01 13:03:57 +02:00
Martin Felis 84fc49af30 Properly store and restore node positions in editor. 2024-05-01 12:50:16 +02:00
Martin Felis 7c7a765455 New nodes now spawn at popup start location. 2024-05-01 11:59:30 +02:00
Martin Felis e3baa65c3b Added breadcrumb navigation for embedded graphs. 2024-05-01 10:58:33 +02:00
Martin Felis 44087d7a7c Removed legacy editor, started working on editing embedded graphs. 2024-04-30 18:40:54 +02:00
Martin Felis e8af30d10c Added embedded fonts. 2024-04-30 17:49:29 +02:00
Martin Felis 4d1990bea8 Reactivated the sidebar in the new node editor. 2024-04-25 21:40:09 +02:00
Martin Felis 5e34aaf3db Extremely basic blend tree editing. 2024-04-25 21:12:08 +02:00
Martin Felis 3fb2995b02 Fixed building of editor app, though crashes everywhere. 2024-04-24 21:58:47 +02:00
Martin Felis c267276be3 Fixed connection validation when one of the nodes is an embedded blend tree. 2024-04-24 21:38:11 +02:00
Martin Felis 53c0bff7a6 Minor cleanup. 2024-04-21 12:47:19 +02:00
Martin Felis 91e226945c Added basic connection validation and connection removal to BlendTreeResources. 2024-04-21 12:42:49 +02:00
Martin Felis d95bc9fb9c Made BlendTreeResource::m_nodes and ::m_connections private.
This is a prerequisite to properly track Node input/output connections and to compute eval order in the BlendTreeResources.
2024-04-16 22:18:11 +02:00
Martin Felis 2d5337ed1d Reduced connection data block size.
Previously we allocated a block for each connection. However when an output is reused by multiple connections this lead to duplicates. It also obfuscated which block is actually being used.
2024-04-08 21:52:03 +02:00
Martin Felis 9f9ac60f9c Cleaned up some compilation issues for the AnimGraphEditor. 2024-04-05 00:44:37 +02:00
Martin Felis 1741238a61 Fixed variable shadowing. 2024-04-05 00:43:41 +02:00
Martin Felis 8694a11416 Removed even more unused files. 2024-04-05 00:18:07 +02:00
Martin Felis 3444f8a625 Removed dead code and unused files. 2024-04-04 19:47:24 +02:00
Martin Felis cd56efca3d Only storing single socket in AnimGraphConnections and simplified wiring logic. 2024-04-04 19:36:20 +02:00
Martin Felis 28eca48a61 Added a test that checks socket propagation into an embedded BlendTree. 2024-04-01 17:59:25 +02:00
Martin Felis 76ea38f118 Added support of time updates for simple embedded graphs. 2024-04-01 12:33:23 +02:00
Martin Felis 0aebe44bd5 Started working on evaluating embedded blend trees. 2024-03-25 22:26:29 +01:00
Martin Felis 99f11e61d8 Refactored AnimGraphResourceTests such that some tests use reusable fixtures. 2024-03-25 21:10:27 +01:00
Martin Felis 116bf7699b Added support of saving and loading of embedded blend tree resources. 2024-03-24 21:50:22 +01:00
Martin Felis 3a7f470acf Fixed memory leak in ResourceSaveLoadMathGraphInputs test. 2024-03-22 12:25:42 +01:00
Martin Felis e687c9b613 Restored AnimGraphResourceTests. 2024-03-20 22:40:46 +01:00
Martin Felis ccb9bc4e9b Working on unified BlendTree and StateMachine handling. 2024-03-17 22:06:27 +01:00
Martin Felis c7d2d195a3 Updated ozz-animation to version 0.14.3 (commit 92c392b667844) 2024-03-17 12:47:11 +01:00
48 changed files with 14235 additions and 2027 deletions
+16
View File
@@ -1,3 +1,19 @@
Release version 0.14.3
----------------------
* Build pipeline
- Adds vs2022 compiler support for fbxsdk (#170)
Release version 0.14.2
----------------------
* Library
- Transitions away from sprintf to the more secure snprintf.
- #147 Works around gcc 11 error stringop-overflow which emits false positives for ozz math serialization.
* Build pipeline
- Updates CI compiler versions.
Release version 0.14.1
----------------------
+6 -4
View File
@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.3)
cmake_minimum_required(VERSION 3.24)
# Defines the project's name
project(ozz)
@@ -9,7 +9,7 @@ get_directory_property(is_sub_project PARENT_DIRECTORY)
# Current version
set(OZZ_VERSION_MAJOR 0)
set(OZZ_VERSION_MINOR 14)
set(OZZ_VERSION_PATCH 1)
set(OZZ_VERSION_PATCH 3)
set(OZZ_VERSION ${OZZ_VERSION_MAJOR}.${OZZ_VERSION_MINOR}.${OZZ_VERSION_PATCH})
# Add project build options
@@ -30,6 +30,7 @@ if(WIN32 AND BUILD_SHARED_LIBS AND NOT ozz_build_msvc_rt_dll)
message("Forcing ozz_build_msvc_rt_dll to ON as ozz is being built as dll (BUILD_SHARED_LIBS is ON).")
set(ozz_build_msvc_rt_dll ON)
endif()
if(is_sub_project)
set(ozz_build_msvc_rt_dll ${ozz_build_msvc_rt_dll} PARENT_SCOPE)
endif()
@@ -51,7 +52,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/build-utils/cm
# Detects Fbx SDK, required to build Fbx pipeline.
if(ozz_build_tools AND ozz_build_fbx)
# Select a msvc runtime compatible with ozz_build_msvc_rt_dll
set(FBX_SHARED ${BUILD_SHARED_LIBS})
set(FBX_MSVC_RT_DLL ${ozz_build_msvc_rt_dll})
@@ -69,6 +69,7 @@ else()
# Disables fbx if tools are disabled
set(ozz_build_fbx OFF)
endif()
if(is_sub_project)
set(ozz_build_fbx ${ozz_build_fbx} PARENT_SCOPE)
endif()
@@ -78,6 +79,7 @@ if(ozz_build_tools AND ozz_build_gltf)
else()
set(ozz_build_gltf OFF)
endif()
if(is_sub_project)
set(ozz_build_gltf ${ozz_build_gltf} PARENT_SCOPE)
endif()
@@ -131,4 +133,4 @@ install(FILES
${PROJECT_SOURCE_DIR}/CHANGES.md
${PROJECT_SOURCE_DIR}/LICENSE.md
${PROJECT_SOURCE_DIR}/README.md
DESTINATION ./)
DESTINATION "share/doc/ozz-animation/")
+1 -1
View File
@@ -18,7 +18,7 @@ Documentation and samples are available from [ozz-animation website](http://guil
Supported platforms
-------------------
Ozz is tested on Linux, Mac OS and Windows, for x86, x86-64 and ARM architectures. The run-time code (ozz_base, ozz_animation, ozz_geometry) depends only on c++11, the standard CRT and has no OS specific code, portability to any other platform shouldn't be an issue.
Ozz is tested on Linux, Mac OS and Windows, for x86, x86-64 and ARM architectures. The run-time code (ozz_base, ozz_animation, ozz_geometry) depends only on c++11, on the C and the C++ standard libraries, and has no OS specific code. Portability to any other platform shouldn't be an issue.
Samples, tools and tests depend on external libraries (glfw, tinygltf, Fbx SDK, jsoncpp, gtest, ...), which could limit portability.
@@ -33,6 +33,7 @@ set(cxx_all_flags
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
@@ -41,15 +42,15 @@ if(ozz_build_simd_ref)
add_compile_definitions(OZZ_BUILD_SIMD_REF)
endif()
# Disables crt secure warnings
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
# --------------------------------------
# Modify default MSVC compilation flags
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# ---------------------------
# For the common build flags
# Disables crt secure warnings
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
# Adds support for multiple processes builds
add_compile_options(/MP)
@@ -60,18 +61,15 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(/WX)
# Select whether to use the DLL version or the static library version of the Visual C++ runtime library.
foreach(flag ${cxx_all_flags})
if(ozz_build_msvc_rt_dll)
string(REGEX REPLACE "/MT" "/MD" ${flag} "${${flag}}")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
endforeach()
# --------------------------------------
# else consider the compiler as GCC compatible (inc clang)
else()
# Set the warning level to Wall
add_compile_options(-Wall)
@@ -83,18 +81,20 @@ else()
# ignored-attributes reports issue when using _m128 as template argument
check_cxx_compiler_flag("-Wignored-attributes" W_IGNORED_ATTRIBUTES)
if(W_IGNORED_ATTRIBUTES)
add_compile_options(-Wno-ignored-attributes)
endif()
# Disables c98 retrocompatibility warnings
check_cxx_compiler_flag("-Wc++98-compat-pedantic" W_98_COMPAT_PEDANTIC)
if(W_98_COMPAT_PEDANTIC)
add_compile_options(-Wno-c++98-compat-pedantic)
endif()
# Check some options availibity for the targetted compiler
check_cxx_compiler_flag("-Wunused-result" W_UNUSED_RESULT)
check_cxx_compiler_flag("-Wnull-dereference" W_NULL_DEREFERENCE)
check_cxx_compiler_flag("-Wpragma-pack" W_PRAGMA_PACK)
@@ -108,7 +108,6 @@ else()
# set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-msse2")
# endif()
endif()
endif()
# ---------------------
@@ -116,6 +115,7 @@ endif()
message(STATUS "---------------------------------------------------------")
message(STATUS "Default build type is: ${CMAKE_BUILD_TYPE}")
message(STATUS "The following compilation flags will be used:")
foreach(flag ${cxx_all_flags})
message(${flag} " ${${flag}}")
endforeach()
@@ -124,6 +124,7 @@ message(STATUS "---------------------------------------------------------")
get_directory_property(DirectoryCompileOptions DIRECTORY ${PROJECT_SOURCE_DIR} COMPILE_OPTIONS)
message(STATUS "Directory Compile Options:")
foreach(opt ${DirectoryCompileOptions})
message(STATUS ${opt})
endforeach()
@@ -132,6 +133,7 @@ message(STATUS "---------------------------------------------------------")
get_directory_property(DirectoryCompileDefinitions DIRECTORY ${PROJECT_SOURCE_DIR} COMPILE_DEFINITIONS)
message(STATUS "Directory Compile Definitions:")
foreach(def ${DirectoryCompileDefinitions})
message(STATUS ${def})
endforeach()
@@ -67,7 +67,9 @@ function(FindFbxLibrariesGeneric _FBX_ROOT_DIR _OUT_FBX_LIBRARIES _OUT_FBX_LIBRA
# Figures out matching compiler/os directory.
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20)
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.30)
set(FBX_CP_PATH "vs2022")
elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20)
set(FBX_CP_PATH "vs2019")
elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10)
set(FBX_CP_PATH "vs2017")
-2
View File
@@ -57,8 +57,6 @@ if(UNIX AND APPLE)
lib/cocoa/cocoa_joystick.m
lib/cocoa/cocoa_time.m
lib/cocoa/cocoa_window.m)
# Treats .m files as C files.
set_source_files_properties(${specific_objc_file_list} PROPERTIES LANGUAGE C)
# Disables warnings in glfw.
set_source_files_properties(${specific_objc_file_list} PROPERTIES COMPILE_FLAGS
+1 -1
View File
@@ -3887,7 +3887,7 @@ std::string valueToString(double value, bool useSpecialFloats, unsigned int prec
int len = -1;
char formatString[6];
sprintf(formatString, "%%.%dg", precision);
snprintf(formatString, sizeof(formatString), "%%.%dg", precision);
// Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distingish the
+19 -23
View File
@@ -66,12 +66,11 @@ template <typename _Ty, size_t _size = sizeof(_Ty)>
struct EndianSwapper;
// Internal macro used to swap two bytes.
#define OZZ_BYTE_SWAP(_a, _b) \
do { \
const ozz::byte temp = (_a); \
(_a) = (_b); \
(_b) = temp; \
} while (0)
OZZ_INLINE void _in_place_byte_swap(byte& _a, byte& _b) {
_a = _a ^ _b;
_b = _a ^ _b;
_a = _a ^ _b;
}
// EndianSwapper specialization for 1 byte types.
template <typename _Ty>
@@ -86,12 +85,12 @@ struct EndianSwapper<_Ty, 2> {
OZZ_INLINE static void Swap(_Ty* _ty, size_t _count) {
byte* alias = reinterpret_cast<byte*>(_ty);
for (size_t i = 0; i < _count * 2; i += 2) {
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 1]);
_in_place_byte_swap(alias[i + 0], alias[i + 1]);
}
}
OZZ_INLINE static _Ty Swap(_Ty _ty) { // Pass by copy to swap _ty in-place.
byte* alias = reinterpret_cast<byte*>(&_ty);
OZZ_BYTE_SWAP(alias[0], alias[1]);
_in_place_byte_swap(alias[0], alias[1]);
return _ty;
}
};
@@ -102,14 +101,14 @@ struct EndianSwapper<_Ty, 4> {
OZZ_INLINE static void Swap(_Ty* _ty, size_t _count) {
byte* alias = reinterpret_cast<byte*>(_ty);
for (size_t i = 0; i < _count * 4; i += 4) {
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 3]);
OZZ_BYTE_SWAP(alias[i + 1], alias[i + 2]);
_in_place_byte_swap(alias[i + 0], alias[i + 3]);
_in_place_byte_swap(alias[i + 1], alias[i + 2]);
}
}
OZZ_INLINE static _Ty Swap(_Ty _ty) { // Pass by copy to swap _ty in-place.
byte* alias = reinterpret_cast<byte*>(&_ty);
OZZ_BYTE_SWAP(alias[0], alias[3]);
OZZ_BYTE_SWAP(alias[1], alias[2]);
_in_place_byte_swap(alias[0], alias[3]);
_in_place_byte_swap(alias[1], alias[2]);
return _ty;
}
};
@@ -120,25 +119,22 @@ struct EndianSwapper<_Ty, 8> {
OZZ_INLINE static void Swap(_Ty* _ty, size_t _count) {
byte* alias = reinterpret_cast<byte*>(_ty);
for (size_t i = 0; i < _count * 8; i += 8) {
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 7]);
OZZ_BYTE_SWAP(alias[i + 1], alias[i + 6]);
OZZ_BYTE_SWAP(alias[i + 2], alias[i + 5]);
OZZ_BYTE_SWAP(alias[i + 3], alias[i + 4]);
_in_place_byte_swap(alias[i + 0], alias[i + 7]);
_in_place_byte_swap(alias[i + 1], alias[i + 6]);
_in_place_byte_swap(alias[i + 2], alias[i + 5]);
_in_place_byte_swap(alias[i + 3], alias[i + 4]);
}
}
OZZ_INLINE static _Ty Swap(_Ty _ty) { // Pass by copy to swap _ty in-place.
byte* alias = reinterpret_cast<byte*>(&_ty);
OZZ_BYTE_SWAP(alias[0], alias[7]);
OZZ_BYTE_SWAP(alias[1], alias[6]);
OZZ_BYTE_SWAP(alias[2], alias[5]);
OZZ_BYTE_SWAP(alias[3], alias[4]);
_in_place_byte_swap(alias[0], alias[7]);
_in_place_byte_swap(alias[1], alias[6]);
_in_place_byte_swap(alias[2], alias[5]);
_in_place_byte_swap(alias[3], alias[4]);
return _ty;
}
};
// OZZ_BYTE_SWAP is not useful anymore.
#undef OZZ_BYTE_SWAP
// Helper function that swaps _count elements of the array _ty in place.
template <typename _Ty>
OZZ_INLINE void EndianSwap(_Ty* _ty, size_t _count) {
+2 -2
View File
@@ -250,7 +250,7 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Blending parameters", &open);
if (open) {
_im_gui->DoLabel("Main layer:");
std::sprintf(label, "Layer weight: %.2f", base_weight_);
std::snprintf(label, sizeof(label), "Layer weight: %.2f", base_weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &base_weight_, 1.f);
_im_gui->DoLabel("Additive layer:");
@@ -259,7 +259,7 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
std::memcpy(weights.data(), additive_weigths_,
sizeof(additive_weigths_));
std::sprintf(label, "Weights\nCurl: %.2f\nSplay: %.2f",
std::snprintf(label, sizeof(label), "Weights\nCurl: %.2f\nSplay: %.2f",
additive_weigths_[kCurl], additive_weigths_[kSplay]);
if (_im_gui->DoSlider2D(label, {{0.f, 0.f}}, {{1.f, 1.f}}, &weights)) {
auto_animate_weights_ = false; // User interacted.
+5 -5
View File
@@ -161,16 +161,16 @@ class AttachSampleApplication : public ozz::sample::Application {
if (open && skeleton_.num_joints() != 0) {
_im_gui->DoLabel("Select joint:");
char label[64];
std::sprintf(label, "%s (%d)", skeleton_.joint_names()[attachment_],
attachment_);
std::snprintf(label, sizeof(label), "%s (%d)",
skeleton_.joint_names()[attachment_], attachment_);
_im_gui->DoSlider(label, 0, skeleton_.num_joints() - 1, &attachment_);
_im_gui->DoLabel("Attachment offset:");
sprintf(label, "x: %02f", offset_.x);
std::snprintf(label, sizeof(label), "x: %02f", offset_.x);
_im_gui->DoSlider(label, -1.f, 1.f, &offset_.x);
sprintf(label, "y: %02f", offset_.y);
std::snprintf(label, sizeof(label), "y: %02f", offset_.y);
_im_gui->DoSlider(label, -1.f, 1.f, &offset_.y);
sprintf(label, "z: %02f", offset_.z);
std::snprintf(label, sizeof(label), "z: %02f", offset_.z);
_im_gui->DoSlider(label, -1.f, 1.f, &offset_.z);
}
}
+4 -4
View File
@@ -58,7 +58,7 @@ OZZ_OPTIONS_DECLARE_STRING(animation2,
// Third animation archive can be specified as an option.
OZZ_OPTIONS_DECLARE_STRING(animation3,
"Path to the second animation (ozz archive format).",
"Path to the third animation (ozz archive format).",
"media/animation3.ozz", false)
class BlendSampleApplication : public ozz::sample::Application {
@@ -242,16 +242,16 @@ class BlendSampleApplication : public ozz::sample::Application {
}
char label[64];
std::sprintf(label, "Blend ratio: %.2f", blend_ratio_);
std::snprintf(label, sizeof(label), "Blend ratio: %.2f", blend_ratio_);
_im_gui->DoSlider(label, 0.f, 1.f, &blend_ratio_, 1.f, !manual_);
for (int i = 0; i < kNumLayers; ++i) {
Sampler& sampler = samplers_[i];
std::sprintf(label, "Weight %d: %.2f", i, sampler.weight);
std::snprintf(label, sizeof(label), "Weight %d: %.2f", i, sampler.weight);
_im_gui->DoSlider(label, 0.f, 1.f, &sampler.weight, 1.f, manual_);
}
std::sprintf(label, "Threshold: %.2f", threshold_);
std::snprintf(label, sizeof(label), "Threshold: %.2f", threshold_);
_im_gui->DoSlider(label, .01f, 1.f, &threshold_);
}
}
+15 -15
View File
@@ -608,7 +608,7 @@ class FootIKSampleApplication : public ozz::sample::Application {
virtual void OnDestroy() {}
virtual bool OnGui(ozz::sample::ImGui* _im_gui) {
char txt[32];
char label[32];
// Main options
{
@@ -636,12 +636,12 @@ class FootIKSampleApplication : public ozz::sample::Application {
static bool opened = true;
ozz::sample::ImGui::OpenClose oc(_im_gui, "IK settings", &opened);
if (opened) {
sprintf(txt, "Foot height %.2g", foot_heigh_);
_im_gui->DoSlider(txt, 0.f, .3f, &foot_heigh_);
sprintf(txt, "Weight %.2g", weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &weight_);
sprintf(txt, "Soften %.2g", soften_);
_im_gui->DoSlider(txt, 0.f, 1.f, &soften_, 1.f, two_bone_ik_);
snprintf(label, sizeof(label), "Foot height %.2g", foot_heigh_);
_im_gui->DoSlider(label, 0.f, .3f, &foot_heigh_);
snprintf(label, sizeof(label), "Weight %.2g", weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &weight_);
snprintf(label, sizeof(label), "Soften %.2g", soften_);
_im_gui->DoSlider(label, 0.f, 1.f, &soften_, 1.f, two_bone_ik_);
}
}
@@ -652,19 +652,19 @@ class FootIKSampleApplication : public ozz::sample::Application {
bool moved = false;
// Translation
_im_gui->DoLabel("Translation");
sprintf(txt, "x %.2g", root_translation_.x);
moved |= _im_gui->DoSlider(txt, -10.f, 10.f, &root_translation_.x);
sprintf(txt, "y %.2g", root_translation_.y);
moved |= _im_gui->DoSlider(txt, 0.f, 5.f, &root_translation_.y, 1.f,
snprintf(label, sizeof(label), "x %.2g", root_translation_.x);
moved |= _im_gui->DoSlider(label, -10.f, 10.f, &root_translation_.x);
snprintf(label, sizeof(label), "y %.2g", root_translation_.y);
moved |= _im_gui->DoSlider(label, 0.f, 5.f, &root_translation_.y, 1.f,
!auto_character_height_);
sprintf(txt, "z %.2g", root_translation_.z);
moved |= _im_gui->DoSlider(txt, -10.f, 10.f, &root_translation_.z);
snprintf(label, sizeof(label), "z %.2g", root_translation_.z);
moved |= _im_gui->DoSlider(label, -10.f, 10.f, &root_translation_.z);
// Rotation (in euler form)
_im_gui->DoLabel("Rotation");
sprintf(txt, "yaw %.3g", root_yaw_ * ozz::math::kRadianToDegree);
snprintf(label, sizeof(label), "yaw %.3g", root_yaw_ * ozz::math::kRadianToDegree);
moved |=
_im_gui->DoSlider(txt, -ozz::math::kPi, ozz::math::kPi, &root_yaw_);
_im_gui->DoSlider(label, -ozz::math::kPi, ozz::math::kPi, &root_yaw_);
// Character position shouldn't be changed after the update. In this
// case, because UI is updated after "game" update, we need to recompute
+18 -22
View File
@@ -528,31 +528,32 @@ bool Application::Gui() {
}
bool Application::FrameworkGui() {
char label[64];
// Downcast to public imgui.
ImGui* im_gui = im_gui_.get();
{ // Render statistics
static bool open = true;
ImGui::OpenClose stat_oc(im_gui, "Statistics", &open);
if (open) {
char szLabel[64];
{ // FPS
Record::Statistics statistics = fps_->GetStatistics();
std::sprintf(szLabel, "FPS: %.0f",
std::snprintf(label, sizeof(label), "FPS: %.0f",
statistics.mean == 0.f ? 0.f : 1000.f / statistics.mean);
static bool fps_open = false;
ImGui::OpenClose stats(im_gui, szLabel, &fps_open);
ImGui::OpenClose stats(im_gui, label, &fps_open);
if (fps_open) {
std::sprintf(szLabel, "Frame: %.2f ms", statistics.mean);
im_gui->DoGraph(szLabel, 0.f, statistics.max, statistics.latest,
std::snprintf(label, sizeof(label), "Frame: %.2f ms",
statistics.mean);
im_gui->DoGraph(label, 0.f, statistics.max, statistics.latest,
fps_->cursor(), fps_->record_begin(),
fps_->record_end());
}
}
{ // Update time
Record::Statistics statistics = update_time_->GetStatistics();
std::sprintf(szLabel, "Update: %.2f ms", statistics.mean);
std::snprintf(label, sizeof(label), "Update: %.2f ms", statistics.mean);
static bool update_open = true; // This is the most relevant for ozz.
ImGui::OpenClose stats(im_gui, szLabel, &update_open);
ImGui::OpenClose stats(im_gui, label, &update_open);
if (update_open) {
im_gui->DoGraph(nullptr, 0.f, statistics.max, statistics.latest,
update_time_->cursor(), update_time_->record_begin(),
@@ -561,9 +562,9 @@ bool Application::FrameworkGui() {
}
{ // Render time
Record::Statistics statistics = render_time_->GetStatistics();
std::sprintf(szLabel, "Render: %.2f ms", statistics.mean);
std::snprintf(label, sizeof(label), "Render: %.2f ms", statistics.mean);
static bool render_open = false;
ImGui::OpenClose stats(im_gui, szLabel, &render_open);
ImGui::OpenClose stats(im_gui, label, &render_open);
if (render_open) {
im_gui->DoGraph(nullptr, 0.f, statistics.max, statistics.latest,
render_time_->cursor(), render_time_->record_begin(),
@@ -580,18 +581,15 @@ bool Application::FrameworkGui() {
im_gui->DoButton("Freeze", true, &freeze_);
im_gui->DoCheckBox("Fix update rate", &fix_update_rate, true);
if (!fix_update_rate) {
char sz_factor[64];
std::sprintf(sz_factor, "Time factor: %.2f", time_factor_);
im_gui->DoSlider(sz_factor, -5.f, 5.f, &time_factor_);
std::snprintf(label, sizeof(label), "Time factor: %.2f", time_factor_);
im_gui->DoSlider(label, -5.f, 5.f, &time_factor_);
if (im_gui->DoButton("Reset time factor", time_factor_ != 1.f)) {
time_factor_ = 1.f;
}
} else {
char sz_fixed_update_rate[64];
std::sprintf(sz_fixed_update_rate, "Update rate: %.0f fps",
std::snprintf(label, sizeof(label), "Update rate: %.0f fps",
fixed_update_rate);
im_gui->DoSlider(sz_fixed_update_rate, 1.f, 200.f, &fixed_update_rate,
.5f, true);
im_gui->DoSlider(label, 1.f, 200.f, &fixed_update_rate, .5f, true);
if (im_gui->DoButton("Reset update rate", fixed_update_rate != 60.f)) {
fixed_update_rate = 60.f;
}
@@ -615,10 +613,9 @@ bool Application::FrameworkGui() {
}
// Vertical sync & swap interval
bool changed = im_gui->DoCheckBox("Vertical sync", &vertical_sync_);
char szLabel[64];
std::sprintf(szLabel, "Swap interval: %d", swap_interval_);
std::snprintf(label, sizeof(label), "Swap interval: %d", swap_interval_);
changed |=
im_gui->DoSlider(szLabel, 1, 4, &swap_interval_, 1.f, vertical_sync_);
im_gui->DoSlider(label, 1, 4, &swap_interval_, 1.f, vertical_sync_);
if (changed) {
glfwSwapInterval(vertical_sync_ ? swap_interval_ : 0);
}
@@ -640,10 +637,9 @@ bool Application::FrameworkGui() {
}
}
char szResolution[64];
std::sprintf(szResolution, "Resolution: %dx%d", resolution_.width,
std::snprintf(label, sizeof(label), "Resolution: %dx%d", resolution_.width,
resolution_.height);
if (im_gui->DoSlider(szResolution, 0, kNumPresets - 1, &preset_lookup)) {
if (im_gui->DoSlider(label, 0, kNumPresets - 1, &preset_lookup)) {
// Resolution changed.
resolution_ = resolution_presets[preset_lookup];
glfwSetWindowSize(resolution_.width, resolution_.height);
+1 -1
View File
@@ -149,7 +149,7 @@ class Application {
enum LoopStatus {
kContinue, // Can continue with next loop.
kBreak, // Should stop looping (ex: exit).
kBreakFailure, // // Should stop looping beacause something went wrong.
kBreakFailure, // Should stop looping because something went wrong.
};
LoopStatus OneLoop(int _loops);
@@ -100,7 +100,7 @@ bool FormatFloat(float _value, char* _string, const char* _string_end) {
if (!_string || _string_end - _string < 8 + precision + 1) {
return false;
}
std::sprintf(_string, "%.2g\n", _value);
std::snprintf(_string, _string_end - _string, "%.2g\n", _value);
// Removes unnecessary '0' digits in the exponent.
char* exponent = strchr(_string, 'e');
@@ -160,12 +160,12 @@ bool Shooter::Process() {
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, shot.pbo));
const void* pixels = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if (pixels) {
char name[16];
sprintf(name, "%06d.tga", shot_number_++);
char filename[16];
std::snprintf(filename, sizeof(filename), "%06d.tga", shot_number_++);
ozz::sample::image::WriteTGA(name, shot.width, shot.height, image_format_,
reinterpret_cast<const uint8_t*>(pixels),
false);
ozz::sample::image::WriteTGA(
filename, shot.width, shot.height, image_format_,
reinterpret_cast<const uint8_t*>(pixels), false);
GL(UnmapBuffer(GL_PIXEL_PACK_BUFFER));
}
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, 0));
+21 -20
View File
@@ -110,21 +110,22 @@ bool PlaybackController::OnGui(const animation::Animation& _animation,
_im_gui->DoCheckBox("Loop", &loop_, _enabled);
char szLabel[64];
char label[64];
// Uses a local copy of time_ so that set_time is used to actually apply
// changes. Otherwise previous time would be incorrect.
float ratio = time_ratio();
std::sprintf(szLabel, "Animation time: %.2f", ratio * _animation.duration());
if (_im_gui->DoSlider(szLabel, 0.f, 1.f, &ratio, 1.f,
std::snprintf(label, sizeof(label), "Animation time: %.2f",
ratio * _animation.duration());
if (_im_gui->DoSlider(label, 0.f, 1.f, &ratio, 1.f,
_enabled && _allow_set_time)) {
set_time_ratio(ratio);
// Pause the time if slider as moved.
play_ = false;
time_changed = true;
}
std::sprintf(szLabel, "Playback speed: %.2f", playback_speed_);
_im_gui->DoSlider(szLabel, -5.f, 5.f, &playback_speed_, 1.f, _enabled);
std::snprintf(label, sizeof(label), "Playback speed: %.2f", playback_speed_);
_im_gui->DoSlider(label, -5.f, 5.f, &playback_speed_, 1.f, _enabled);
// Allow to reset speed if it is not the default value.
if (_im_gui->DoButton("Reset playback speed",
@@ -139,7 +140,7 @@ bool OnRawSkeletonJointGui(
ozz::sample::ImGui* _im_gui,
ozz::animation::offline::RawSkeleton::Joint::Children* _children,
ozz::vector<bool>::iterator* _oc_state) {
char txt[255];
char label[255];
bool modified = false;
for (size_t i = 0; i < _children->size(); ++i) {
@@ -152,23 +153,23 @@ bool OnRawSkeletonJointGui(
// Translation
ozz::math::Float3& translation = joint.transform.translation;
_im_gui->DoLabel("Translation");
sprintf(txt, "x %.2g", translation.x);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.x);
sprintf(txt, "y %.2g", translation.y);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.y);
sprintf(txt, "z %.2g", translation.z);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.z);
snprintf(label, sizeof(label), "x %.2g", translation.x);
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.x);
snprintf(label, sizeof(label), "y %.2g", translation.y);
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.y);
snprintf(label, sizeof(label), "z %.2g", translation.z);
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.z);
// Rotation (in euler form)
ozz::math::Quaternion& rotation = joint.transform.rotation;
_im_gui->DoLabel("Rotation");
ozz::math::Float3 euler = ToEuler(rotation) * ozz::math::kRadianToDegree;
sprintf(txt, "x %.3g", euler.x);
bool euler_modified = _im_gui->DoSlider(txt, -180.f, 180.f, &euler.x);
sprintf(txt, "y %.3g", euler.y);
euler_modified |= _im_gui->DoSlider(txt, -180.f, 180.f, &euler.y);
sprintf(txt, "z %.3g", euler.z);
euler_modified |= _im_gui->DoSlider(txt, -180.f, 180.f, &euler.z);
snprintf(label, sizeof(label), "x %.3g", euler.x);
bool euler_modified = _im_gui->DoSlider(label, -180.f, 180.f, &euler.x);
snprintf(label, sizeof(label), "y %.3g", euler.y);
euler_modified |= _im_gui->DoSlider(label, -180.f, 180.f, &euler.y);
snprintf(label, sizeof(label), "z %.3g", euler.z);
euler_modified |= _im_gui->DoSlider(label, -180.f, 180.f, &euler.z);
if (euler_modified) {
modified = true;
ozz::math::Float3 euler_rad = euler * ozz::math::kDegreeToRadian;
@@ -179,8 +180,8 @@ bool OnRawSkeletonJointGui(
// Scale (must be uniform and not 0)
_im_gui->DoLabel("Scale");
ozz::math::Float3& scale = joint.transform.scale;
sprintf(txt, "%.2g", scale.x);
if (_im_gui->DoSlider(txt, -1.f, 1.f, &scale.x)) {
snprintf(label, sizeof(label), "%.2g", scale.x);
if (_im_gui->DoSlider(label, -1.f, 1.f, &scale.x)) {
modified = true;
scale.y = scale.z = scale.x = scale.x != 0.f ? scale.x : .01f;
}
+21 -21
View File
@@ -387,15 +387,15 @@ class LookAtSampleApplication : public ozz::sample::Application {
virtual void OnDestroy() {}
virtual bool OnGui(ozz::sample::ImGui* _im_gui) {
char txt[64];
char label[64];
_im_gui->DoCheckBox("Enable ik", &enable_ik_);
sprintf(txt, "IK chain length: %d", chain_length_);
_im_gui->DoSlider(txt, 0, kMaxChainLength, &chain_length_);
sprintf(txt, "Joint weight %.2g", joint_weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &joint_weight_);
sprintf(txt, "Chain weight %.2g", chain_weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &chain_weight_);
snprintf(label, sizeof(label), "IK chain length: %d", chain_length_);
_im_gui->DoSlider(label, 0, kMaxChainLength, &chain_length_);
snprintf(label, sizeof(label), "Joint weight %.2g", joint_weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &joint_weight_);
snprintf(label, sizeof(label), "Chain weight %.2g", chain_weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &chain_weight_);
// Exposes animation runtime playback controls.
{
@@ -413,15 +413,15 @@ class LookAtSampleApplication : public ozz::sample::Application {
const float kTargetRange = 3.f;
_im_gui->DoLabel("Animated extent");
sprintf(txt, "%.2g", target_extent_);
_im_gui->DoSlider(txt, 0.f, kTargetRange, &target_extent_);
snprintf(label, sizeof(label), "%.2g", target_extent_);
_im_gui->DoSlider(label, 0.f, kTargetRange, &target_extent_);
sprintf(txt, "x %.2g", target_offset_.x);
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.x);
sprintf(txt, "y %.2g", target_offset_.y);
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.y);
sprintf(txt, "z %.2g", target_offset_.z);
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.z);
snprintf(label, sizeof(label), "x %.2g", target_offset_.x);
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.x);
snprintf(label, sizeof(label), "y %.2g", target_offset_.y);
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.y);
snprintf(label, sizeof(label), "z %.2g", target_offset_.z);
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.z);
}
}
@@ -430,12 +430,12 @@ class LookAtSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Eyes offset", &opened);
if (opened) {
const float kOffsetRange = .5f;
sprintf(txt, "x %.2g", eyes_offset_.x);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.x);
sprintf(txt, "y %.2g", eyes_offset_.y);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.y);
sprintf(txt, "z %.2g", eyes_offset_.z);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.z);
snprintf(label, sizeof(label), "x %.2g", eyes_offset_.x);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.x);
snprintf(label, sizeof(label), "y %.2g", eyes_offset_.y);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.y);
snprintf(label, sizeof(label), "z %.2g", eyes_offset_.z);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.z);
}
}
+10 -10
View File
@@ -152,7 +152,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
// Rebuilds all if the number of joints has changed.
int joints = skeleton_->num_joints();
char label[64];
std::sprintf(label, "Joints count: %d", joints);
std::snprintf(label, sizeof(label), "Joints count: %d", joints);
// Uses an exponential scale in the slider to maintain enough precision in
// the lowest values.
@@ -219,17 +219,17 @@ class MillipedeSampleApplication : public ozz::sample::Application {
root->transform.rotation = Quaternion::identity();
root->transform.scale = Float3::one();
char buf[16];
char number[16];
for (int i = 0; i < slice_count_; ++i) {
// Format joint number.
std::sprintf(buf, "%d", i);
std::snprintf(number, sizeof(number), "%d", i);
root->children.resize(3);
// Left leg.
RawSkeleton::Joint& lu = root->children[0];
lu.name = "lu";
lu.name += buf;
lu.name += number;
lu.transform.translation = kTransUp;
lu.transform.rotation = kRotLeftUp;
lu.transform.scale = Float3::one();
@@ -237,7 +237,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
lu.children.resize(1);
RawSkeleton::Joint& ld = lu.children[0];
ld.name = "ld";
ld.name += buf;
ld.name += number;
ld.transform.translation = kTransDown;
ld.transform.rotation = kRotLeftDown;
ld.transform.scale = Float3::one();
@@ -245,7 +245,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
ld.children.resize(1);
RawSkeleton::Joint& lf = ld.children[0];
lf.name = "lf";
lf.name += buf;
lf.name += number;
lf.transform.translation = Float3::x_axis();
lf.transform.rotation = Quaternion::identity();
lf.transform.scale = Float3::one();
@@ -253,7 +253,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
// Right leg.
RawSkeleton::Joint& ru = root->children[1];
ru.name = "ru";
ru.name += buf;
ru.name += number;
ru.transform.translation = kTransUp;
ru.transform.rotation = kRotRightUp;
ru.transform.scale = Float3::one();
@@ -261,7 +261,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
ru.children.resize(1);
RawSkeleton::Joint& rd = ru.children[0];
rd.name = "rd";
rd.name += buf;
rd.name += number;
rd.transform.translation = kTransDown;
rd.transform.rotation = kRotRightDown;
rd.transform.scale = Float3::one();
@@ -269,7 +269,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
rd.children.resize(1);
RawSkeleton::Joint& rf = rd.children[0];
rf.name = "rf";
rf.name += buf;
rf.name += number;
rf.transform.translation = Float3::x_axis();
rf.transform.rotation = Quaternion::identity();
rf.transform.scale = Float3::one();
@@ -277,7 +277,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
// Spine.
RawSkeleton::Joint& sp = root->children[2];
sp.name = "sp";
sp.name += buf;
sp.name += number;
sp.transform.translation = Float3(0.f, 0.f, kSpinLength);
sp.transform.rotation = Quaternion::identity();
sp.transform.scale = Float3::one();
@@ -287,10 +287,10 @@ class MultithreadSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Sample control", &oc_open);
if (oc_open) {
char label[64];
std::sprintf(label, "Number of entities: %d", num_characters_);
std::snprintf(label, sizeof(label), "Number of entities: %d", num_characters_);
_im_gui->DoSlider(label, 1, kMaxCharacters, &num_characters_, .7f);
const int num_joints = num_characters_ * skeleton_.num_joints();
std::sprintf(label, "Number of joints: %d", num_joints);
std::snprintf(label, sizeof(label), "Number of joints: %d", num_joints);
_im_gui->DoLabel(label);
}
}
@@ -303,11 +303,11 @@ class MultithreadSampleApplication : public ozz::sample::Application {
has_threading_support_);
if (enable_theading_) {
char label[64];
std::sprintf(label, "Grain size: %d", grain_size_);
std::snprintf(label, sizeof(label), "Grain size: %d", grain_size_);
_im_gui->DoSlider(label, kMinGrainSize, kMaxCharacters, &grain_size_,
.2f);
const int num_threads = monitor_.ThreadCount();
std::sprintf(label, "Thread/task count: %d/%d", num_threads,
std::snprintf(label, sizeof(label), "Thread/task count: %d/%d", num_threads,
monitor_.TaskCount());
_im_gui->DoLabel(label);
}
+18 -16
View File
@@ -312,28 +312,31 @@ class OptimizeSampleApplication : public ozz::sample::Application {
rebuild |= _im_gui->DoCheckBox("Enable optimizations", &optimize_);
std::sprintf(label, "Tolerance: %0.2f mm", setting_.tolerance * 1000);
std::snprintf(label, sizeof(label), "Tolerance: %0.2f mm",
setting_.tolerance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, .1f, &setting_.tolerance, .5f,
optimize_);
std::sprintf(label, "Distance: %0.2f mm", setting_.distance * 1000);
std::snprintf(label, sizeof(label), "Distance: %0.2f mm",
setting_.distance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, 1.f, &setting_.distance, .5f,
optimize_);
rebuild |= _im_gui->DoCheckBox("Enable joint setting",
&joint_setting_enable_, optimize_);
std::sprintf(label, "%s (%d)", skeleton_.joint_names()[joint_], joint_);
std::snprintf(label, sizeof(label), "%s (%d)",
skeleton_.joint_names()[joint_], joint_);
rebuild |=
_im_gui->DoSlider(label, 0, skeleton_.num_joints() - 1, &joint_,
1.f, joint_setting_enable_ && optimize_);
std::sprintf(label, "Tolerance: %0.2f mm",
std::snprintf(label, sizeof(label), "Tolerance: %0.2f mm",
joint_setting_.tolerance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, .1f, &joint_setting_.tolerance,
.5f, joint_setting_enable_ && optimize_);
std::sprintf(label, "Distance: %0.2f mm",
std::snprintf(label, sizeof(label), "Distance: %0.2f mm",
joint_setting_.distance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, 1.f, &joint_setting_.distance,
.5f, joint_setting_enable_ && optimize_);
@@ -356,18 +359,18 @@ class OptimizeSampleApplication : public ozz::sample::Application {
static bool open = true;
ozz::sample::ImGui::OpenClose ocb(_im_gui, "Memory size", &open);
if (open) {
std::sprintf(label, "Original: %dKB",
std::snprintf(label, sizeof(label), "Original: %dKB",
static_cast<int>(raw_animation_.size() >> 10));
_im_gui->DoLabel(label);
std::sprintf(label, "Optimized: %dKB (%.1f:1)",
std::snprintf(label, sizeof(label), "Optimized: %dKB (%.1f:1)",
static_cast<int>(raw_optimized_animation_.size() >> 10),
static_cast<float>(raw_animation_.size()) /
raw_optimized_animation_.size());
_im_gui->DoLabel(label);
std::sprintf(
label, "Compressed: %dKB (%.1f:1)",
std::snprintf(
label, sizeof(label), "Compressed: %dKB (%.1f:1)",
static_cast<int>(animation_rt_->size() >> 10),
static_cast<float>(raw_animation_.size()) / animation_rt_->size());
_im_gui->DoLabel(label);
@@ -388,37 +391,36 @@ class OptimizeSampleApplication : public ozz::sample::Application {
// Show absolute error.
{
char szLabel[64];
static bool error_open = true;
ozz::sample::ImGui::OpenClose oc_stats(_im_gui, "Absolute error",
&error_open);
if (error_open) {
{
std::sprintf(szLabel, "Median error: %.2fmm",
std::snprintf(label, sizeof(label), "Median error: %.2fmm",
*error_record_med_.cursor());
const ozz::sample::Record::Statistics error_stats =
error_record_med_.GetStatistics();
_im_gui->DoGraph(szLabel, 0.f, error_stats.max, error_stats.latest,
_im_gui->DoGraph(label, 0.f, error_stats.max, error_stats.latest,
error_record_med_.cursor(),
error_record_med_.record_begin(),
error_record_med_.record_end());
}
{
std::sprintf(szLabel, "Maximum error: %.2fmm",
std::snprintf(label, sizeof(label), "Maximum error: %.2fmm",
*error_record_max_.cursor());
const ozz::sample::Record::Statistics error_stats =
error_record_max_.GetStatistics();
_im_gui->DoGraph(szLabel, 0.f, error_stats.max, error_stats.latest,
_im_gui->DoGraph(label, 0.f, error_stats.max, error_stats.latest,
error_record_max_.cursor(),
error_record_max_.record_begin(),
error_record_max_.record_end());
}
{
std::sprintf(szLabel, "Joint %d error: %.2fmm", joint_,
std::snprintf(label, sizeof(label), "Joint %d error: %.2fmm", joint_,
*joint_error_record_.cursor());
const ozz::sample::Record::Statistics error_stats =
joint_error_record_.GetStatistics();
_im_gui->DoGraph(szLabel, 0.f, error_stats.max, error_stats.latest,
_im_gui->DoGraph(label, 0.f, error_stats.max, error_stats.latest,
joint_error_record_.cursor(),
joint_error_record_.record_begin(),
joint_error_record_.record_end());
@@ -251,7 +251,7 @@ class PartialBlendSampleApplication : public ozz::sample::Application {
_im_gui->DoCheckBox("Use automatic blending settings", &automatic);
static float coeff = 1.f; // All power to the partial animation.
std::sprintf(label, "Upper body weight: %.2f", coeff);
std::snprintf(label, sizeof(label), "Upper body weight: %.2f", coeff);
_im_gui->DoSlider(label, 0.f, 1.f, &coeff, 1.f, automatic);
Sampler& lower_body_sampler = samplers_[kLowerBody];
@@ -267,27 +267,27 @@ class PartialBlendSampleApplication : public ozz::sample::Application {
_im_gui->DoLabel("Manual settings:");
_im_gui->DoLabel("Lower body layer:");
std::sprintf(label, "Layer weight: %.2f",
std::snprintf(label, sizeof(label), "Layer weight: %.2f",
lower_body_sampler.weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f, &lower_body_sampler.weight_setting,
1.f, !automatic);
std::sprintf(label, "Joints weight: %.2f",
std::snprintf(label, sizeof(label), "Joints weight: %.2f",
lower_body_sampler.joint_weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f,
&lower_body_sampler.joint_weight_setting, 1.f,
!automatic);
_im_gui->DoLabel("Upper body layer:");
std::sprintf(label, "Layer weight: %.2f",
std::snprintf(label, sizeof(label), "Layer weight: %.2f",
upper_body_sampler.weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f, &upper_body_sampler.weight_setting,
1.f, !automatic);
std::sprintf(label, "Joints weight: %.2f",
std::snprintf(label, sizeof(label), "Joints weight: %.2f",
upper_body_sampler.joint_weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f,
&upper_body_sampler.joint_weight_setting, 1.f,
!automatic);
_im_gui->DoLabel("Global settings:");
std::sprintf(label, "Threshold: %.2f", threshold_);
std::snprintf(label, sizeof(label), "Threshold: %.2f", threshold_);
_im_gui->DoSlider(label, .01f, 1.f, &threshold_);
SetupPerJointWeights();
@@ -301,7 +301,7 @@ class PartialBlendSampleApplication : public ozz::sample::Application {
_im_gui->DoLabel("Root of the upper body hierarchy:",
ozz::sample::ImGui::kLeft, false);
char label[64];
std::sprintf(label, "%s (%d)",
std::snprintf(label, sizeof(label), "%s (%d)",
skeleton_.joint_names()[upper_body_root_],
upper_body_root_);
if (_im_gui->DoSlider(label, 0, skeleton_.num_joints() - 1,
+4 -4
View File
@@ -186,28 +186,28 @@ class SkinningSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Model statisitics", &open);
if (open) {
char label[255];
sprintf(label, "%d animated joints", skeleton_.num_joints());
std::snprintf(label, sizeof(label), "%d animated joints", skeleton_.num_joints());
_im_gui->DoLabel(label);
int influences = 0;
for (const auto& mesh : meshes_) {
influences = ozz::math::Max(influences, mesh.max_influences_count());
}
sprintf(label, "%d influences (max)", influences);
std::snprintf(label, sizeof(label), "%d influences (max)", influences);
_im_gui->DoLabel(label);
int vertices = 0;
for (const auto& mesh : meshes_) {
vertices += mesh.vertex_count();
}
sprintf(label, "%.1fK vertices", vertices / 1000.f);
std::snprintf(label, sizeof(label), "%.1fK vertices", vertices / 1000.f);
_im_gui->DoLabel(label);
int indices = 0;
for (const auto& mesh : meshes_) {
indices += mesh.triangle_index_count();
}
sprintf(label, "%.1fK triangles", indices / 3000.f);
std::snprintf(label, sizeof(label), "%.1fK triangles", indices / 3000.f);
_im_gui->DoLabel(label);
}
}
@@ -264,7 +264,7 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
virtual void OnDestroy() {}
virtual bool OnGui(ozz::sample::ImGui* _im_gui) {
char txt[32];
char label[32];
// IK parameters
_im_gui->DoCheckBox("Fix initial transform", &fix_initial_transform_);
@@ -273,25 +273,25 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
static bool opened = true;
ozz::sample::ImGui::OpenClose oc(_im_gui, "IK parameters", &opened);
if (opened) {
sprintf(txt, "Soften: %.2g", soften_);
_im_gui->DoSlider(txt, 0.f, 1.f, &soften_, 2.f);
sprintf(txt, "Twist angle: %.0f",
snprintf(label, sizeof(label), "Soften: %.2g", soften_);
_im_gui->DoSlider(label, 0.f, 1.f, &soften_, 2.f);
snprintf(label, sizeof(label), "Twist angle: %.0f",
twist_angle_ * ozz::math::kRadianToDegree);
_im_gui->DoSlider(txt, -ozz::math::kPi, ozz::math::kPi, &twist_angle_);
sprintf(txt, "Weight: %.2g", weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &weight_);
_im_gui->DoSlider(label, -ozz::math::kPi, ozz::math::kPi, &twist_angle_);
snprintf(label, sizeof(label), "Weight: %.2g", weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &weight_);
{
// Pole vector
static bool pole_opened = true;
ozz::sample::ImGui::OpenClose oc_pole(_im_gui, "Pole vector",
&pole_opened);
if (pole_opened) {
sprintf(txt, "x %.2g", pole_vector.x);
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.x);
sprintf(txt, "y %.2g", pole_vector.y);
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.y);
sprintf(txt, "z %.2g", pole_vector.z);
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.z);
snprintf(label, sizeof(label), "x %.2g", pole_vector.x);
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.x);
snprintf(label, sizeof(label), "y %.2g", pole_vector.y);
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.y);
snprintf(label, sizeof(label), "z %.2g", pole_vector.z);
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.z);
}
}
}
@@ -301,17 +301,17 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Target position", &opened);
if (opened) {
_im_gui->DoLabel("Target animation extent");
sprintf(txt, "%.2g", target_extent_);
_im_gui->DoSlider(txt, 0.f, 1.f, &target_extent_);
snprintf(label, sizeof(label), "%.2g", target_extent_);
_im_gui->DoSlider(label, 0.f, 1.f, &target_extent_);
_im_gui->DoLabel("Target Offset");
const float kOffsetRange = 1.f;
sprintf(txt, "x %.2g", target_offset_.x);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.x);
sprintf(txt, "y %.2g", target_offset_.y);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.y);
sprintf(txt, "z %.2g", target_offset_.z);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.z);
snprintf(label, sizeof(label), "x %.2g", target_offset_.x);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.x);
snprintf(label, sizeof(label), "y %.2g", target_offset_.y);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.y);
snprintf(label, sizeof(label), "z %.2g", target_offset_.z);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.z);
}
}
{ // Root
@@ -320,28 +320,28 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
if (opened) {
// Translation
_im_gui->DoLabel("Translation");
sprintf(txt, "x %.2g", root_translation_.x);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.x);
sprintf(txt, "y %.2g", root_translation_.y);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.y);
sprintf(txt, "z %.2g", root_translation_.z);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.z);
snprintf(label, sizeof(label), "x %.2g", root_translation_.x);
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.x);
snprintf(label, sizeof(label), "y %.2g", root_translation_.y);
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.y);
snprintf(label, sizeof(label), "z %.2g", root_translation_.z);
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.z);
// Rotation (in euler form)
_im_gui->DoLabel("Rotation");
ozz::math::Float3 euler = root_euler_ * ozz::math::kRadianToDegree;
sprintf(txt, "yaw %.3g", euler.x);
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.x);
sprintf(txt, "pitch %.3g", euler.y);
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.y);
sprintf(txt, "roll %.3g", euler.z);
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.z);
snprintf(label, sizeof(label), "yaw %.3g", euler.x);
_im_gui->DoSlider(label, -180.f, 180.f, &euler.x);
snprintf(label, sizeof(label), "pitch %.3g", euler.y);
_im_gui->DoSlider(label, -180.f, 180.f, &euler.y);
snprintf(label, sizeof(label), "roll %.3g", euler.z);
_im_gui->DoSlider(label, -180.f, 180.f, &euler.z);
root_euler_ = euler * ozz::math::kDegreeToRadian;
// Scale (must be uniform and not 0)
_im_gui->DoLabel("Scale");
sprintf(txt, "%.2g", root_scale_);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_scale_);
snprintf(label, sizeof(label), "%.2g", root_scale_);
_im_gui->DoSlider(label, -1.f, 1.f, &root_scale_);
}
}
{ // Display options
+1
View File
@@ -62,6 +62,7 @@ target_compile_definitions(ozz_base
PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:OZZ_BUILD_BASE_LIB>)
target_compile_options(ozz_base PUBLIC $<$<CXX_COMPILER_ID:MSVC>:/wd4251>)
target_include_directories(ozz_base PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
@@ -25,15 +25,13 @@
// //
//----------------------------------------------------------------------------//
#include "gtest/gtest.h"
#include "ozz/animation/runtime/ik_aim_job.h"
#include "ozz/base/maths/gtest_math_helper.h"
#include "ozz/base/maths/quaternion.h"
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/maths/simd_quaternion.h"
#include "gtest/gtest.h"
#include "ozz/base/maths/gtest_math_helper.h"
TEST(JobValidity, IKAimJob) {
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
ozz::math::SimdQuaternion quat;
@@ -428,42 +426,23 @@ TEST(Twist, IKAimJob) {
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
}
{ // Pole y, twist pi
job.pole_vector = ozz::math::simd_float4::y_axis();
job.twist_angle = ozz::math::kPi;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), -ozz::math::kPi);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
}
{ // Pole y, twist -pi
job.pole_vector = ozz::math::simd_float4::y_axis();
job.twist_angle = -ozz::math::kPi;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_mPi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), -ozz::math::kPi);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi.x, x_mPi.y, x_mPi.z, x_mPi.w,
2e-3f);
}
{ // Pole y, twist pi / 2
job.pole_vector = ozz::math::simd_float4::y_axis();
job.twist_angle = ozz::math::kPi_2;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi_2.x, x_Pi_2.y, x_Pi_2.z, x_Pi_2.w,
2e-3f);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
}
{ // Pole z, twist pi/2
job.pole_vector = ozz::math::simd_float4::z_axis();
job.twist_angle = ozz::math::kPi_2;
{ // Pole y, twist -pi / 2
job.pole_vector = ozz::math::simd_float4::y_axis();
job.twist_angle = -ozz::math::kPi_2;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), ozz::math::kPi);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
const ozz::math::Quaternion x_mPi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), -ozz::math::kPi_2);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi.x, x_mPi.y, x_mPi.z, x_mPi.w,
2e-3f);
}
}
@@ -3,6 +3,8 @@ target_include_directories(test_intrusive_list
PUBLIC "${PROJECT_SOURCE_DIR}/include")
target_link_libraries(test_intrusive_list
gtest)
target_compile_options(test_intrusive_list
PRIVATE $<$<BOOL:${W_UNUSED_RESULT}>:-Wno-unused-result>)
target_copy_shared_libraries(test_intrusive_list)
add_test(NAME test_intrusive_list COMMAND test_intrusive_list)
set_target_properties(test_intrusive_list PROPERTIES FOLDER "ozz/tests/base")
@@ -11,6 +13,8 @@ add_executable(test_std_containers std_containers_tests.cc)
target_link_libraries(test_std_containers
ozz_base
gtest)
target_compile_options(test_std_containers
PRIVATE $<$<BOOL:${W_UNUSED_RESULT}>:-Wno-unused-result>)
target_copy_shared_libraries(test_std_containers)
add_test(NAME test_std_containers COMMAND test_std_containers)
set_target_properties(test_std_containers PROPERTIES FOLDER "ozz/tests/base")
+12 -6
View File
@@ -46,14 +46,18 @@ add_library(AnimTestbedCode OBJECT
src/SyncTrack.cc
src/SyncTrack.h
src/ozzutils.cc
src/AnimGraph/AnimGraphResource.cc
src/AnimGraph/AnimGraphResource.h
src/AnimGraph/AnimGraph.cc
src/AnimGraph/AnimGraph.h
src/AnimGraph/AnimGraphNodes.cc
src/AnimGraph/AnimGraphNodes.h
src/AnimGraph/AnimGraphData.cc
src/AnimGraph/AnimGraphData.h)
src/AnimGraph/AnimGraphData.h
src/AnimGraph/AnimGraphBlendTree.cc
src/AnimGraph/AnimGraphBlendTree.h
src/AnimGraph/AnimGraphStateMachine.cc
src/AnimGraph/AnimGraphStateMachine.h
src/AnimGraph/AnimNode.cc
src/AnimGraph/AnimNode.h
src/AnimGraph/AnimGraphResource.cc
src/AnimGraph/AnimGraphResource.h)
target_include_directories(
AnimTestbedCode
@@ -74,6 +78,7 @@ target_include_directories(
target_sources(AnimTestbed PRIVATE
src/main.cc
src/embedded_fonts.h
src/SkinnedMeshRenderer.cc
src/AnimGraph/AnimGraphEditor.cc
src/AnimGraph/AnimGraphEditor.h
@@ -120,7 +125,8 @@ set(ozz_offline_test_objs
target_sources(runtests PRIVATE
tests/AnimGraphResourceTests.cc
tests/AnimGraphEvalTests.cc
tests/AnimGraphEditorTests.cc
# tests/AnimGraphEvalTests.cc
tests/NodeDescriptorTests.cc
tests/SyncTrackTests.cc
tests/main.cc
-202
View File
@@ -1,202 +0,0 @@
//
// Created by martin on 25.03.22.
//
#include "AnimGraph.h"
#include <algorithm>
#include <cstring>
bool AnimGraph::init(AnimGraphContext& context) {
context.m_graph = this;
for (size_t i = 2; i < m_nodes.size(); i++) {
if (!m_nodes[i]->Init(context)) {
return false;
}
}
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
int num_soa_joints = context.m_skeleton->num_soa_joints();
m_animdata_blocks[i]->m_local_matrices.resize(num_soa_joints);
}
return true;
}
void AnimGraph::updateOrderedNodes() {
m_eval_ordered_nodes.clear();
updateOrderedNodesRecursive(0);
}
void AnimGraph::updateOrderedNodesRecursive(int node_index) {
AnimNode* node = m_nodes[node_index];
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
int input_node_index =
getAnimNodeIndex(node_input_connections.at(i).m_source_node);
if (input_node_index == 1) {
continue;
}
updateOrderedNodesRecursive(input_node_index);
}
if (node_index != 0) {
// In case we have multiple output connections from the node we here
// ensure that use the node evaluation that is the furthest away from
// the output.
std::vector<AnimNode*>::iterator find_iter = std::find(
m_eval_ordered_nodes.begin(),
m_eval_ordered_nodes.end(),
node);
if (find_iter != m_eval_ordered_nodes.end()) {
m_eval_ordered_nodes.erase(find_iter);
}
m_eval_ordered_nodes.push_back(node);
}
}
void AnimGraph::markActiveNodes() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_state = AnimNodeEvalState::Deactivated;
}
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
const AnimGraphConnection& graph_input = graph_output_inputs[i];
AnimNode* node = graph_input.m_source_node;
if (node != nullptr) {
node->m_state = AnimNodeEvalState::Activated;
}
}
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
if (checkIsNodeActive(node)) {
int node_index = node->m_index;
node->MarkActiveInputs(m_node_input_connections[node_index]);
// Non-animation data inputs are always active.
for (size_t j = 0, nj = m_node_input_connections[node_index].size();
j < nj;
j++) {
const AnimGraphConnection& input =
m_node_input_connections[node_index][j];
if (input.m_source_node != nullptr
&& input.m_target_socket.m_type
!= SocketType::SocketTypeAnimation) {
input.m_source_node->m_state = AnimNodeEvalState::Activated;
}
}
}
}
}
void AnimGraph::evalSyncTracks() {
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
int node_index = node->m_index;
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->CalcSyncTrack(m_node_input_connections[node_index]);
}
}
void AnimGraph::updateTime(float dt) {
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
AnimNode* node = graph_output_inputs[i].m_source_node;
if (node != nullptr) {
node->UpdateTime(node->m_time_now, node->m_time_now + dt);
}
}
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; --i) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state != AnimNodeEvalState::TimeUpdated) {
continue;
}
int node_index = node->m_index;
float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last;
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
AnimNode* input_node = node_input_connections[i].m_source_node;
// Only propagate time updates via animation sockets.
if (input_node != nullptr
&& node_input_connections[i].m_target_socket.m_type
== SocketType::SocketTypeAnimation
&& input_node->m_state == AnimNodeEvalState::Activated) {
input_node->UpdateTime(node_time_last, node_time_now);
}
}
}
}
void AnimGraph::evaluate(AnimGraphContext& context) {
for (int i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->Evaluate(context);
}
}
Socket* AnimGraph::getInputSocket(const std::string& name) {
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {
AnimGraphConnection& connection = m_node_output_connections[1][i];
if (connection.m_source_socket.m_name == name) {
return &connection.m_source_socket;
}
}
return nullptr;
}
Socket* AnimGraph::getOutputSocket(const std::string& name) {
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
AnimGraphConnection& connection = m_node_input_connections[0][i];
if (connection.m_target_socket.m_name == name) {
return &connection.m_target_socket;
}
}
return nullptr;
}
const Socket* AnimGraph::getInputSocket(const std::string& name) const {
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {
const AnimGraphConnection& connection = m_node_output_connections[1][i];
if (connection.m_source_socket.m_name == name) {
return &connection.m_source_socket;
}
}
return nullptr;
}
const Socket* AnimGraph::getOutputSocket(const std::string& name) const {
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
const AnimGraphConnection& connection = m_node_input_connections[0][i];
if (connection.m_target_socket.m_name == name) {
return &connection.m_target_socket;
}
}
return nullptr;
}
-231
View File
@@ -1,231 +0,0 @@
//
// Created by martin on 25.03.22.
//
#ifndef ANIMTESTBED_ANIMGRAPH_H
#define ANIMTESTBED_ANIMGRAPH_H
#include "AnimGraphData.h"
#include "AnimGraphNodes.h"
//
// AnimGraph (Runtime)
//
struct AnimGraph {
AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes;
std::vector<AnimNode*> m_eval_ordered_nodes;
std::vector<std::vector<AnimGraphConnection> > m_node_input_connections;
std::vector<std::vector<AnimGraphConnection> > m_node_output_connections;
std::vector<AnimData*> m_animdata_blocks;
NodeDescriptorBase* m_node_descriptor = nullptr;
char* m_input_buffer = nullptr;
char* m_output_buffer = nullptr;
char* m_connection_data_storage = nullptr;
char* m_const_node_inputs = nullptr;
std::vector<Socket>& getGraphOutputs() { return m_node_descriptor->m_inputs; }
std::vector<Socket>& getGraphInputs() { return m_node_descriptor->m_outputs; }
AnimDataAllocator m_anim_data_allocator;
~AnimGraph() { dealloc(); }
bool init(AnimGraphContext& context);
void dealloc() {
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
m_animdata_blocks[i]->m_local_matrices.vector::~vector();
}
m_animdata_blocks.clear();
m_node_input_connections.clear();
m_node_output_connections.clear();
delete[] m_input_buffer;
delete[] m_output_buffer;
delete[] m_connection_data_storage;
delete[] m_const_node_inputs;
for (int i = 0; i < m_nodes.size(); i++) {
delete m_nodes[i];
}
m_nodes.clear();
delete m_node_descriptor;
}
void updateOrderedNodes();
void updateOrderedNodesRecursive(int node_index);
void markActiveNodes();
bool checkIsNodeActive(AnimNode* node) {
return node->m_state != AnimNodeEvalState::Deactivated;
}
void evalSyncTracks();
void updateTime(float dt);
void evaluate(AnimGraphContext& context);
void resetNodeStates() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_time_now = 0.f;
m_nodes[i]->m_time_last = 0.f;
m_nodes[i]->m_state = AnimNodeEvalState::Undefined;
}
}
Socket* getInputSocket(const std::string& name);
Socket* getOutputSocket(const std::string& name);
const Socket* getInputSocket(const std::string& name) const;
const Socket* getOutputSocket(const std::string& name) const;
/** Sets the address that is used for the specified AnimGraph input Socket.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the input is fetched during evaluation.
*/
template <typename T>
void SetInput(const char* name, T* value_ptr) {
m_node_descriptor->SetOutput(name, value_ptr);
for (int i = 0; i < m_node_output_connections[1].size(); i++) {
const AnimGraphConnection& graph_input_connection =
m_node_output_connections[1][i];
if (graph_input_connection.m_source_socket.m_name == name) {
*graph_input_connection.m_target_socket.m_reference.ptr_ptr = value_ptr;
}
}
}
/** Sets the address that is used for the specified AnimGraph output Socket.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the graph output output is written to at the end of evaluation.
*/
template <typename T>
void SetOutput(const char* name, T* value_ptr) {
m_node_descriptor->SetInput(name, value_ptr);
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket.m_name == name) {
if (graph_output_connection.m_source_node == m_nodes[1]
&& graph_output_connection.m_target_node == m_nodes[0]) {
std::cerr << "Error: cannot set output for direct graph input to graph "
"output connections. Use GetOutptPtr for output instead!"
<< std::endl;
return;
}
*graph_output_connection.m_source_socket.m_reference.ptr_ptr =
value_ptr;
// Make sure all other output connections of this pin use the same output pointer
int source_node_index = getAnimNodeIndex(graph_output_connection.m_source_node);
for (int j = 0; j < m_node_output_connections[source_node_index].size(); j++) {
const AnimGraphConnection& source_output_connection = m_node_output_connections[source_node_index][j];
if (source_output_connection.m_target_node == m_nodes[0]) {
continue;
}
if (source_output_connection.m_source_socket.m_name == graph_output_connection.m_source_socket.m_name) {
*source_output_connection.m_target_socket.m_reference.ptr_ptr = value_ptr;
}
}
}
}
}
/** Returns the address that is used for the specified AnimGraph output Socket.
*
* This function is needed for connections that directly connect an AnimGraph
* input Socket to an output Socket of the same AnimGraph.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @return Address that is used for the specified AnimGraph output Socket.
*/
template <typename T>
T* GetOutputPtr(const char* name) {
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket.m_name == name) {
return static_cast<float*>(*graph_output_connection.m_source_socket.m_reference.ptr_ptr);
}
}
return nullptr;
}
void* getInputPtr(const std::string& name) const {
const Socket* input_socket = getInputSocket(name);
if (input_socket != nullptr) {
return input_socket->m_reference.ptr;
}
return nullptr;
}
void* getOutputPtr(const std::string& name) const {
const Socket* input_socket = getOutputSocket(name);
if (input_socket != nullptr) {
return input_socket->m_reference.ptr;
}
return nullptr;
}
int getNodeEvalOrderIndex(const AnimNode* node) {
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
if (m_eval_ordered_nodes[i] == node) {
return i;
}
}
return -1;
}
const AnimNode* getAnimNodeForInput(
size_t node_index,
const std::string& input_name) const {
assert(node_index < m_nodes.size());
const std::vector<AnimGraphConnection>& input_connection =
m_node_input_connections[node_index];
for (size_t i = 0, n = input_connection.size(); i < n; i++) {
if (input_connection[i].m_target_socket.m_name == input_name) {
return input_connection[i].m_source_node;
}
}
return nullptr;
}
AnimNode* getAnimNode(const char* name) {
for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i]->m_name == name) {
return m_nodes[i];
}
}
return nullptr;
}
size_t getAnimNodeIndex(AnimNode* node) {
for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i] == node) {
return i;
}
}
return -1;
}
};
#endif //ANIMTESTBED_ANIMGRAPH_H
+176
View File
@@ -0,0 +1,176 @@
#pragma clang diagnostic push
#pragma ide diagnostic ignored "misc-no-recursion"
//
// Created by martin on 17.03.24.
//
#include "AnimGraphBlendTree.h"
#include <algorithm>
bool AnimGraphBlendTree::Init(AnimGraphContext& context) {
for (size_t i = 2; i < m_nodes.size(); i++) {
if (!m_nodes[i]->Init(context)) {
return false;
}
}
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
int num_soa_joints = context.m_skeleton->num_soa_joints();
m_animdata_blocks[i]->m_local_matrices.resize(num_soa_joints);
}
return true;
}
void AnimGraphBlendTree::UpdateOrderedNodes() {
m_eval_ordered_nodes.clear();
UpdateOrderedNodesRecursive(0);
}
void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
AnimNode* node = m_nodes[node_index];
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
if (node_input_connections[i].m_crosses_hierarchy) {
continue;
}
int input_node_index =
GetAnimNodeIndex(node_input_connections[i].m_source_node);
if (input_node_index == 1) {
continue;
}
UpdateOrderedNodesRecursive(input_node_index);
}
if (node_index != 0) {
// In case we have multiple output connections from the node we here
// ensure that use the node evaluation that is the furthest away from
// the output.
std::vector<AnimNode*>::iterator find_iter = std::find(
m_eval_ordered_nodes.begin(),
m_eval_ordered_nodes.end(),
node);
if (find_iter != m_eval_ordered_nodes.end()) {
m_eval_ordered_nodes.erase(find_iter);
}
m_eval_ordered_nodes.push_back(node);
}
}
void AnimGraphBlendTree::MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) {
for (AnimNode* node : m_nodes) {
if (node->m_tick_number != m_tick_number) {
node->m_state = AnimNodeEvalState::Deactivated;
}
}
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
const AnimGraphConnection& graph_input = graph_output_inputs[i];
AnimNode* node = graph_input.m_source_node;
if (node != nullptr) {
node->m_tick_number = m_tick_number;
node->m_state = AnimNodeEvalState::Activated;
}
}
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
if (CheckIsNodeActive(node)) {
size_t node_index = GetAnimNodeIndex(node);
node->MarkActiveInputs(m_node_input_connections[node_index]);
// Non-animation data inputs are always active.
for (size_t j = 0, nj = m_node_input_connections[node_index].size();
j < nj;
j++) {
const AnimGraphConnection& input =
m_node_input_connections[node_index][j];
if (input.m_source_node != nullptr
&& input.m_socket.m_type != SocketType::SocketTypeAnimation) {
input.m_source_node->m_state = AnimNodeEvalState::Activated;
}
}
}
}
}
void AnimGraphBlendTree::CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) {
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->CalcSyncTrack(m_node_input_connections[GetAnimNodeIndex(node)]);
}
}
void AnimGraphBlendTree::UpdateTime(float time_last, float time_now) {
float dt = time_now - time_last;
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (const AnimGraphConnection& graph_output_input : graph_output_inputs) {
AnimNode* node = graph_output_input.m_source_node;
if (node != nullptr && node->m_state != AnimNodeEvalState::TimeUpdated) {
node->UpdateTime(time_last, time_now);
}
}
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; --i) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state != AnimNodeEvalState::TimeUpdated) {
continue;
}
PropagateTimeToNodeInputs(node);
}
m_state = AnimNodeEvalState::TimeUpdated;
}
void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
for (int i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->Evaluate(context);
}
}
void AnimGraphBlendTree::PropagateTimeToNodeInputs(const AnimNode* node) {
size_t node_index = GetAnimNodeIndex(node);
float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last;
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (const AnimGraphConnection& node_input_connection :
node_input_connections) {
AnimNode* input_node = node_input_connection.m_source_node;
// Only propagate time updates via animation sockets.
if (input_node != nullptr
&& node_input_connection.m_socket.m_type
== SocketType::SocketTypeAnimation
&& input_node->m_state == AnimNodeEvalState::Activated) {
input_node->UpdateTime(node_time_last, node_time_now);
}
}
}
#pragma clang diagnostic pop
+213
View File
@@ -0,0 +1,213 @@
//
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMGRAPHBLENDTREE_H
#define ANIMTESTBED_ANIMGRAPHBLENDTREE_H
#include <algorithm>
#include "AnimNode.h"
//
// AnimGraph (Runtime)
//
struct AnimGraphBlendTree : public AnimNode {
AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes;
std::vector<AnimNode*> m_eval_ordered_nodes;
std::vector<std::vector<AnimGraphConnection> > m_node_input_connections;
std::vector<std::vector<AnimGraphConnection> > m_node_output_connections;
[[nodiscard]] const std::vector<AnimGraphConnection>&
GetGraphInputConnections() const {
return m_node_output_connections[1];
}
[[nodiscard]] const std::vector<AnimGraphConnection>&
GetGraphOutputConnections() const {
return m_node_input_connections[0];
}
std::vector<AnimData*> m_animdata_blocks;
NodeDescriptorBase* m_node_descriptor = nullptr;
char* m_input_buffer = nullptr;
char* m_output_buffer = nullptr;
char* m_connection_data_storage = nullptr;
char* m_const_node_inputs = nullptr;
std::vector<Socket>& GetGraphOutputs() { return m_node_descriptor->m_inputs; }
std::vector<Socket>& GetGraphInputs() { return m_node_descriptor->m_outputs; }
AnimDataAllocator m_anim_data_allocator;
~AnimGraphBlendTree() override { dealloc(); }
void StartUpdateTick() { m_tick_number++; }
// AnimNode overrides
bool Init(AnimGraphContext& context) override;
void MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override;
void PropagateTimeToNodeInputs(const AnimNode* node);
void dealloc() {
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
m_animdata_blocks[i]->m_local_matrices.vector::~vector();
}
m_animdata_blocks.clear();
m_node_input_connections.clear();
m_node_output_connections.clear();
delete[] m_input_buffer;
delete[] m_output_buffer;
delete[] m_connection_data_storage;
delete[] m_const_node_inputs;
for (int i = 0; i < m_nodes.size(); i++) {
delete m_nodes[i];
}
m_nodes.clear();
delete m_node_descriptor;
}
void UpdateOrderedNodes();
void UpdateOrderedNodesRecursive(int node_index);
bool CheckIsNodeActive(AnimNode* node) {
return node->m_tick_number == m_tick_number;
}
void ResetNodeStates() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_time_now = 0.f;
m_nodes[i]->m_time_last = 0.f;
m_nodes[i]->m_state = AnimNodeEvalState::Undefined;
}
}
const Socket* GetInputSocket(const std::string& name) const;
const Socket* GetOutputSocket(const std::string& name) const;
/** Sets the address that is used for the specified AnimGraph input Socket.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the input is fetched during evaluation.
*/
template <typename T>
void SetInput(const char* name, T* value_ptr) {
m_node_descriptor->SetOutput(name, value_ptr);
std::vector<size_t> connected_node_indices;
for (int i = 0; i < m_node_output_connections[1].size(); i++) {
const AnimGraphConnection& graph_input_connection =
m_node_output_connections[1][i];
if (graph_input_connection.m_source_socket_name == name) {
*graph_input_connection.m_socket.m_reference.ptr_ptr = value_ptr;
}
}
}
/** Sets the address that is used for the specified AnimGraph output Socket.
*
* We update the pointer of the outputting node. We also have to ensure that
* all usages of that output use the same pointer.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the graph output output is written to at the end of evaluation.
*/
template <typename T>
void SetOutput(const char* name, T* value_ptr) {
const auto& graph_output_connection = std::find_if(
GetGraphOutputConnections().begin(),
GetGraphOutputConnections().end(),
[&name](const AnimGraphConnection& connection) {
return connection.m_target_socket_name == name;
});
if (graph_output_connection == GetGraphOutputConnections().end()) {
std::cerr << "Error: could not find graph connection to output socket "
<< name << "." << std::endl;
return;
}
if (graph_output_connection->m_source_node == m_nodes[1]
&& graph_output_connection->m_target_node == m_nodes[0]) {
std::cerr << "Error: cannot set output for direct graph input to graph "
"output connections. Use GetOutptPtr for output instead!"
<< std::endl;
return;
}
size_t output_node_index =
GetAnimNodeIndex(graph_output_connection->m_source_node);
const std::vector<AnimGraphConnection>& node_output_connections =
m_node_output_connections[output_node_index];
for (const AnimGraphConnection& connection : node_output_connections) {
if (connection.m_source_socket_name
== graph_output_connection->m_source_socket_name) {
*connection.m_socket.m_reference.ptr_ptr = value_ptr;
}
}
*graph_output_connection->m_socket.m_reference.ptr_ptr = value_ptr;
}
/** Returns the address that is used for the specified AnimGraph output Socket.
*
* This function is needed for connections that directly connect an AnimGraph
* input Socket to an output Socket of the same AnimGraph.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @return Address that is used for the specified AnimGraph output Socket.
*/
template <typename T>
T* GetOutputPtr(const char* name) {
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket_name == name) {
return static_cast<T*>(
*graph_output_connection.m_socket.m_reference.ptr_ptr);
}
}
return nullptr;
}
int GetAnimNodeIndex(const AnimNode* node) const {
for (int i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i] == node) {
return i;
}
}
return -1;
}
};
//
// BlendTreeSocketNode
//
struct BlendTreeSocketNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeSocketNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeSocketNode* node_) {}
};
#endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H
+29 -2
View File
@@ -22,8 +22,8 @@
//
// Data types
//
struct AnimGraph;
struct AnimNode;
struct AnimData {
ozz::vector<ozz::math::SoaTransform> m_local_matrices;
@@ -161,7 +161,7 @@ struct Socket {
SocketValue m_value = {0};
std::string m_value_string;
union SocketReference {
void* ptr;
void* ptr = nullptr;
void** ptr_ptr;
};
SocketReference m_reference = {0};
@@ -260,6 +260,15 @@ SocketType GetSocketType() {
return SocketType::SocketTypeUndefined;
}
struct AnimGraphConnection {
AnimNode* m_source_node = nullptr;
std::string m_source_socket_name = "";
AnimNode* m_target_node = nullptr;
std::string m_target_socket_name = "";
Socket m_socket;
bool m_crosses_hierarchy = false;
};
struct NodeDescriptorBase {
std::vector<Socket> m_inputs;
std::vector<Socket> m_outputs;
@@ -311,6 +320,13 @@ struct NodeDescriptorBase {
return *socket->m_reference.ptr_ptr;
}
template <typename T>
T GetInputValue(const char* name) const {
const Socket* socket = FindSocket(name, m_inputs);
assert(GetSocketType<T>() == socket->m_type);
return socket->GetValue<T>();
}
template <typename T>
void SetInput(const char* name, T* value_ptr) {
Socket* socket = FindSocket(name, m_inputs);
@@ -415,6 +431,17 @@ struct NodeDescriptorBase {
return nullptr;
}
const Socket* FindSocket(const char* name, const std::vector<Socket>& sockets)
const {
for (int i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
return &sockets[i];
}
}
return nullptr;
}
int FindSocketIndex(const char* name, std::vector<Socket>& sockets) {
for (int i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
+421 -389
View File
@@ -13,8 +13,17 @@
#include "imnodes.h"
#include "misc/cpp/imgui_stdlib.h"
static AnimGraphResource sGraphGresource = AnimGraphResource();
static bool sGraphLoadedThisFrame = false;
struct EditorState {
AnimGraphResource* rootGraphResource = nullptr;
std::vector<AnimGraphResource*> hierarchyStack;
size_t hierarchyStackIndex = 0;
bool isGraphLoadedThisFrame = false;
ImVec2 mousePopupStart = {};
};
static EditorState sEditorState;
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
switch (socket_type) {
@@ -37,44 +46,41 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
return ImNodesPinShape_Quad;
}
int GetNodeInputSocketId(int node_index, int input_socket_index) {
return node_index * 1000 + input_socket_index;
}
int GetNodeOutputSocketId(int node_index, int output_socket_index) {
return node_index * 1000 + 100 + output_socket_index;
}
void NodeSocketEditor(Socket& socket) {
bool NodeSocketEditor(Socket& socket) {
bool modified = false;
int mode_current = static_cast<int>(socket.m_type);
ImGui::InputText("Name", &socket.m_name);
if (ImGui::InputText("Name", &socket.m_name)) {
modified = true;
}
if (ImGui::Combo(
"Type",
&mode_current,
SocketTypeNames,
sizeof(SocketTypeNames) / sizeof(char*))) {
socket.m_type = static_cast<SocketType>(mode_current);
}
modified = true;
}
void RemoveConnectionsForSocket(
AnimGraphResource& graph_resource,
AnimNodeResource& node_resource,
return modified;
}
void RemoveBlendTreeConnectionsForSocket(
BlendTreeResource& blend_tree_resource,
AnimNodeResource* node_resource,
Socket& socket) {
std::vector<AnimGraphConnectionResource>::iterator iter =
graph_resource.m_connections.begin();
const BlendTreeConnectionResource* connection =
blend_tree_resource.FindConnectionForSocket(node_resource, socket.m_name);
while (connection != nullptr) {
blend_tree_resource.DisconnectSockets(
blend_tree_resource.GetNode(connection->source_node_index),
connection->source_socket_name,
blend_tree_resource.GetNode(connection->target_node_index),
connection->target_socket_name);
while (iter != graph_resource.m_connections.end()) {
// TODO adjust for refactor
assert(false);
// AnimGraphConnectionResource& connection = *iter;
// if (connection.m_source_node == &node_resource
// && connection.m_source_socket == &socket) {
// iter = sGraphGresource.m_connections.erase(iter);
// } else {
// iter++;
// }
connection = blend_tree_resource.FindConnectionForSocket(
node_resource,
socket.m_name);
}
}
@@ -159,24 +165,31 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
}
void AnimGraphEditorRenderSidebar(
AnimGraphResource& graph_resource,
AnimNodeResource& node_resource) {
ImGui::Text("[%s]", node_resource.m_type_name.c_str());
BlendTreeResource& blend_tree_resource,
AnimNodeResource* node_resource) {
ImGui::Text(
"[%s (%2.2f, %2.2f)]",
node_resource->m_node_type_name.c_str(),
node_resource->m_position[0],
node_resource->m_position[1]);
char node_name_buffer[256];
memset(node_name_buffer, 0, sizeof(node_name_buffer));
strncpy(
node_name_buffer,
node_resource.m_name.c_str(),
std::min(node_resource.m_name.size(), sizeof(node_name_buffer)));
node_resource->m_name.c_str(),
std::min(node_resource->m_name.size(), sizeof(node_name_buffer)));
if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) {
node_resource.m_name = node_name_buffer;
node_resource->m_name = node_name_buffer;
}
int num_properties = node_resource.m_socket_accessor->m_properties.size();
int num_properties = 0;
if (node_resource->m_socket_accessor != nullptr) {
num_properties = node_resource->m_socket_accessor->m_properties.size();
}
for (int i = 0; i < num_properties; i++) {
Socket& property = node_resource.m_socket_accessor->m_properties[i];
Socket& property = node_resource->m_socket_accessor->m_properties[i];
if (property.m_type == SocketType::SocketTypeInt) {
ImGui::InputInt(
property.m_name.c_str(),
@@ -211,223 +224,360 @@ void AnimGraphEditorRenderSidebar(
}
}
if (&node_resource == &graph_resource.getGraphOutputNode()) {
if (node_resource == blend_tree_resource.GetGraphOutputNode()) {
ImGui::Text("Outputs");
// Graph outputs are the inputs of the output node!
std::vector<Socket>& outputs = node_resource.m_socket_accessor->m_inputs;
std::vector<Socket>& outputs = node_resource->m_socket_accessor->m_inputs;
std::vector<Socket>::iterator iter = outputs.begin();
while (iter != outputs.end()) {
Socket& output = *iter;
ImGui::PushID(&output);
NodeSocketEditor(output);
if (NodeSocketEditor(output)) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->m_socket_accessor->m_inputs = outputs;
}
if (ImGui::Button("X")) {
RemoveConnectionsForSocket(graph_resource, node_resource, output);
RemoveBlendTreeConnectionsForSocket(
blend_tree_resource,
node_resource,
output);
iter = outputs.erase(iter);
} else {
iter++;
}
ImGui::PopID();
}
if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeOutputSocket<float>(
"GraphFloatOutput");
}
}
if (&node_resource == &graph_resource.getGraphInputNode()) {
if (node_resource == blend_tree_resource.GetGraphInputNode()) {
ImGui::Text("Inputs");
// Graph inputs are the outputs of the input node!
std::vector<Socket>& inputs = node_resource.m_socket_accessor->m_outputs;
std::vector<Socket>& inputs = node_resource->m_socket_accessor->m_outputs;
std::vector<Socket>::iterator iter = inputs.begin();
while (iter != inputs.end()) {
Socket& input = *iter;
ImGui::PushID(&input);
NodeSocketEditor(input);
if (NodeSocketEditor(input)) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->m_socket_accessor->m_inputs = inputs;
}
if (ImGui::Button("X")) {
RemoveConnectionsForSocket(graph_resource, node_resource, input);
RemoveBlendTreeConnectionsForSocket(
blend_tree_resource,
node_resource,
input);
iter = inputs.erase(iter);
} else {
iter++;
}
ImGui::PopID();
}
if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeInputSocket<float>(
"GraphFloatInput");
}
}
}
void AnimGraphEditorClear() {
if (ax::NodeEditor::GetCurrentEditor() != nullptr) {
ax::NodeEditor::ClearSelection();
}
if (sEditorState.rootGraphResource) {
delete sEditorState.rootGraphResource->m_socket_accessor;
}
delete sEditorState.rootGraphResource;
sEditorState.rootGraphResource = new AnimGraphResource();
sEditorState.rootGraphResource->m_name = "Root";
sEditorState.rootGraphResource->m_graph_type_name = "BlendTree";
sEditorState.rootGraphResource->m_blend_tree_resource.InitGraphConnectors();
sEditorState.rootGraphResource->m_socket_accessor = new NodeDescriptorBase;
sEditorState.hierarchyStack.clear();
sEditorState.hierarchyStack.push_back(sEditorState.rootGraphResource);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] =
sEditorState.hierarchyStack.back();
sEditorState.hierarchyStackIndex = 0;
}
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
ImGui::BeginMenuBar();
if (ImGui::Button("Save")) {
sGraphGresource.saveToFile("editor_graph.json");
}
if (ImGui::Button("Load")) {
sGraphGresource.loadFromFile("editor_graph.json");
sGraphLoadedThisFrame = true;
// for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
// const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
// ImNodes::SetNodeGridSpacePos(
// i,
// ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
// }
}
if (ImGui::Button("Clear")) {
sGraphGresource.clear();
}
char graph_name_buffer[256];
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy(
graph_name_buffer,
sGraphGresource.m_name.c_str(),
sizeof(graph_name_buffer));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
sGraphGresource.m_name = graph_name_buffer;
}
ImGui::EndMenuBar();
//
// Node editor canvas
//
ax::NodeEditor::SetCurrentEditor(context);
ax::NodeEditor::Begin("Graph Editor");
#if 1
for (size_t node_id = 0, n = sGraphGresource.m_nodes.size(); node_id < n;
node_id++) {
AnimNodeResource& node_resource = sGraphGresource.m_nodes[node_id];
if (node_id == 0 || node_id == 1) {
// continue;
}
if (sGraphLoadedThisFrame) {
ax::NodeEditor::SetNodePosition(
node_id,
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
}
ax::NodeEditor::BeginNode(node_id);
ImGui::Text("%s", node_resource.m_type_name.c_str());
// Inputs
std::vector<Socket>& node_inputs =
node_resource.m_socket_accessor->m_inputs;
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ax::NodeEditor::BeginPin(
GetNodeInputSocketId(static_cast<int>(node_id), static_cast<int>(j)),
ax::NodeEditor::PinKind::Input);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
// Outputs
std::vector<Socket>& node_outputs =
node_resource.m_socket_accessor->m_outputs;
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
Socket& socket = node_outputs[j];
ax::NodeEditor::BeginPin(
GetNodeOutputSocketId(static_cast<int>(node_id), static_cast<int>(j)),
ax::NodeEditor::PinKind::Output);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
ax::NodeEditor::EndNode();
}
int link_id = 0;
for (size_t connection_id = 0, n = sGraphGresource.m_connections.size(); connection_id < n;
connection_id++) {
const AnimGraphConnectionResource& connection_resource = sGraphGresource.m_connections[connection_id];
const AnimNodeResource& source_node_resource = sGraphGresource.m_nodes[connection_resource.source_node_index];
int source_socket_index = source_node_resource.m_socket_accessor->GetOutputIndex(connection_resource.source_socket_name.c_str());
const AnimNodeResource& target_node_resource = sGraphGresource.m_nodes[connection_resource.target_node_index];
int target_socket_index = target_node_resource.m_socket_accessor->GetInputIndex(connection_resource.target_socket_name.c_str());
int source_socket_id = GetNodeOutputSocketId(static_cast<int>(connection_resource.source_node_index), source_socket_index);
int target_socket_id = GetNodeInputSocketId(static_cast<int>(connection_resource.target_node_index), target_socket_index);
ax::NodeEditor::Link(link_id++, source_socket_id, target_socket_id);
}
#endif
#if 1
// Create Connections
if (ax::NodeEditor::BeginCreate()) {
ax::NodeEditor::PinId input_pin_id, output_pin_id;
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
if (input_pin_id && output_pin_id) {
if (ax::NodeEditor::AcceptNewItem()) {
}
}
}
}
ax::NodeEditor::EndCreate();
#endif
ax::NodeEditor::End();
sGraphLoadedThisFrame = false;
ax::NodeEditor::SetCurrentEditor(nullptr);
}
void LegacyAnimGraphEditorUpdate() {
//
// Menu bar
//
ImGui::BeginMenuBar();
if (ImGui::Button("Save")) {
sGraphGresource.saveToFile("editor_graph.json");
sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
}
if (ImGui::Button("Load")) {
sGraphGresource.loadFromFile("editor_graph.json");
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::SetNodeGridSpacePos(
i,
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
}
AnimGraphEditorClear();
sEditorState.rootGraphResource->LoadFromFile("editor_graph.json");
sEditorState.isGraphLoadedThisFrame = true;
}
if (ImGui::Button("Clear")) {
sGraphGresource.clear();
AnimGraphEditorClear();
}
if (ImGui::Button("Content")) {
ax::NodeEditor::NavigateToContent();
}
char graph_name_buffer[256];
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy(
graph_name_buffer,
sGraphGresource.m_name.c_str(),
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_name.c_str(),
sizeof(graph_name_buffer));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
sGraphGresource.m_name = graph_name_buffer;
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]->m_name =
graph_name_buffer;
}
ImGui::EndMenuBar();
//
// Breadcrumb navigation
//
for (size_t i = 0, n = sEditorState.hierarchyStack.size(); i < n; i++) {
AnimGraphResource* graph_resource =
dynamic_cast<AnimGraphResource*>(sEditorState.hierarchyStack[i]);
ImGui::PushID(graph_resource);
bool highlight_button = i == sEditorState.hierarchyStackIndex;
if (highlight_button) {
ImGui::PushStyleColor(
ImGuiCol_Button,
(ImVec4)ImColor::HSV(1. / 7.0f, 0.6f, 0.6f));
}
if (ImGui::Button(graph_resource->m_name.c_str())) {
sEditorState.hierarchyStackIndex = i;
}
if (highlight_button) {
ImGui::PopStyleColor(1);
}
ImGui::PopID();
if (i < n - 1) {
ImGui::SameLine();
}
}
ImGui::Columns(2);
//
// Node editor canvas
//
ImNodes::BeginNodeEditor();
ax::NodeEditor::Begin("Graph Editor");
for (size_t node_index = 0,
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNumNodes();
node_index < n;
node_index++) {
AnimNodeResource* node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(node_index);
ax::NodeEditor::NodeId node_id(node_resource);
if (sEditorState.isGraphLoadedThisFrame) {
ax::NodeEditor::SetNodePosition(
node_id,
ImVec2(node_resource->m_position[0], node_resource->m_position[1]));
}
ax::NodeEditor::BeginNode(node_id);
ImGui::Text("%s", node_resource->m_node_type_name.c_str());
// Inputs
std::vector<Socket> node_inputs =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeInputSockets(node_resource);
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ax::NodeEditor::BeginPin(
NodeIndexAndSocketIndexToInputPinId(
static_cast<int>(node_index),
static_cast<int>(j)),
ax::NodeEditor::PinKind::Input);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
// Outputs
std::vector<Socket> node_outputs =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeOutputSockets(node_resource);
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
Socket& socket = node_outputs[j];
ax::NodeEditor::BeginPin(
NodeIndexAndSocketIndexToOutputPinId(
static_cast<int>(node_index),
static_cast<int>(j)),
ax::NodeEditor::PinKind::Output);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
ax::NodeEditor::EndNode();
ImVec2 node_position = ax::NodeEditor::GetNodePosition(node_id);
node_resource->m_position[0] = node_position.x;
node_resource->m_position[1] = node_position.y;
}
int link_id = 0;
for (size_t connection_id = 0,
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNumConnections();
connection_id < n;
connection_id++) {
const BlendTreeConnectionResource* connection_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetConnection(connection_id);
const AnimNodeResource* source_node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(
connection_resource->source_node_index);
int source_socket_index =
source_node_resource->m_socket_accessor->GetOutputIndex(
connection_resource->source_socket_name.c_str());
const AnimNodeResource* target_node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(
connection_resource->target_node_index);
int target_socket_index =
target_node_resource->m_socket_accessor->GetInputIndex(
connection_resource->target_socket_name.c_str());
int source_socket_pin_id = NodeIndexAndSocketIndexToOutputPinId(
static_cast<int>(connection_resource->source_node_index),
source_socket_index);
int target_socket_pin_id = NodeIndexAndSocketIndexToInputPinId(
static_cast<int>(connection_resource->target_node_index),
target_socket_index);
ax::NodeEditor::Link(
ax::NodeEditor::LinkId(connection_resource),
source_socket_pin_id,
target_socket_pin_id);
}
// Create Connections
if (ax::NodeEditor::BeginCreate()) {
ax::NodeEditor::PinId input_pin_id, output_pin_id;
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
int source_node_index;
int source_node_socket_index;
const AnimNodeResource* source_node = nullptr;
const Socket* source_socket = nullptr;
if (input_pin_id) {
OutputPinIdToNodeIndexAndSocketIndex(
input_pin_id.Get(),
&source_node_index,
&source_node_socket_index);
source_node =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(source_node_index);
if (source_node->m_socket_accessor->m_outputs.size()
< source_node_socket_index) {
source_node_socket_index = -1;
} else {
source_socket =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeOutputSocketByIndex(
source_node,
source_node_socket_index);
}
}
int target_node_index;
int target_node_socket_index;
const AnimNodeResource* target_node = nullptr;
const Socket* target_socket = nullptr;
if (output_pin_id) {
InputPinIdToNodeIndexAndSocketIndex(
output_pin_id.Get(),
&target_node_index,
&target_node_socket_index);
target_node =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(target_node_index);
if (target_node->m_socket_accessor->m_inputs.size()
< target_node_socket_index) {
target_node_socket_index = -1;
} else {
target_socket =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeInputSocketByIndex(
target_node,
target_node_socket_index);
}
}
if (input_pin_id && output_pin_id) {
if (source_socket == nullptr || target_socket == nullptr
|| !sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.IsConnectionValid(
source_node,
source_socket->m_name,
target_node,
target_socket->m_name)) {
ax::NodeEditor::RejectNewItem();
} else if (ax::NodeEditor::AcceptNewItem()) {
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.ConnectSockets(
source_node,
source_socket->m_name,
target_node,
target_socket->m_name);
}
}
}
}
ax::NodeEditor::EndCreate();
// Popup menu
{
const bool open_popup =
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
&& ImNodes::IsEditorHovered()
&& ImGui::IsMouseReleased(ImGuiMouseButton_Right);
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
if (!ImGui::IsAnyItemHovered() && open_popup) {
if (open_popup && ImGui::IsWindowHovered()) {
ax::NodeEditor::Suspend();
ImGui::OpenPopup("add node");
ax::NodeEditor::Resume();
sEditorState.mousePopupStart = ImGui::GetMousePos();
}
ax::NodeEditor::Suspend();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
if (ImGui::BeginPopup("add node")) {
const ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup();
std::string node_type_name = "";
if (ImGui::MenuItem("AnimSampler")) {
node_type_name = "AnimSampler";
@@ -457,226 +607,108 @@ void LegacyAnimGraphEditorUpdate() {
node_type_name = "ConstScalarNode";
}
if (node_type_name != "") {
AnimNodeResource node_resource =
if (ImGui::MenuItem("BlendTree")) {
node_type_name = "BlendTree";
}
if (!node_type_name.empty()) {
AnimNodeResource* node_resource =
AnimNodeResourceFactory(node_type_name);
size_t node_id = sGraphGresource.m_nodes.size();
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
sGraphGresource.m_nodes.push_back(node_resource);
ax::NodeEditor::SetNodePosition(
ax::NodeEditor::NodeId(node_resource),
sEditorState.mousePopupStart);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.AddNode(node_resource);
}
ImGui::EndPopup();
}
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
ImGui::PopStyleVar();
ax::NodeEditor::Resume();
}
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::BeginNode(i);
ImGui::PushItemWidth(110.0f);
// Header
ImNodes::BeginNodeTitleBar();
if (&node_resource == &sGraphGresource.getGraphOutputNode()) {
ImGui::TextUnformatted("Graph Outputs");
} else if (&node_resource == &sGraphGresource.getGraphInputNode()) {
ImGui::TextUnformatted("Graph Inputs");
} else {
ImGui::TextUnformatted(node_resource.m_type_name.c_str());
}
ImNodes::EndNodeTitleBar();
// Inputs
std::vector<Socket>& node_inputs =
node_resource.m_socket_accessor->m_inputs;
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ImColor socket_color = ImColor(255, 255, 255, 255);
if (socket.m_flags & SocketFlagAffectsTime) {
socket_color = ImColor(255, 128, 128, 255);
}
ImNodes::BeginInputAttribute(
GenerateInputAttributeId(i, j),
sGetSocketShapeFromSocketType(socket.m_type),
socket_color);
ImGui::TextUnformatted(socket.m_name.c_str());
bool socket_connected =
sGraphGresource.isSocketConnected(node_resource, socket.m_name);
if (!socket_connected && (socket.m_type == SocketType::SocketTypeFloat)) {
ImGui::SameLine();
float socket_value = socket.m_value.float_value;
ImGui::PushItemWidth(
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
if (ImGui::DragFloat("##hidelabel", &socket_value, 0.01f)) {
socket.SetValue(socket_value);
}
ImGui::PopItemWidth();
}
if (!socket_connected && (socket.m_type == SocketType::SocketTypeInt)) {
ImGui::SameLine();
int socket_value = socket.m_value.int_value;
ImGui::PushItemWidth(
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
if (ImGui::InputInt("##hidelabel", &socket_value, 1)) {
socket.SetValue(socket_value);
}
ImGui::PopItemWidth();
}
ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute();
}
// Outputs
const std::vector<Socket>& node_outputs =
node_resource.m_socket_accessor->m_outputs;
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
const Socket& socket = node_outputs[j];
ImNodes::BeginOutputAttribute(
GenerateOutputAttributeId(i, j),
sGetSocketShapeFromSocketType(socket.m_type),
ImColor(255, 255, 255, 255));
ImGui::TextUnformatted(socket.m_name.c_str());
ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute();
}
// Graph output node
if (i == 0) {
if (ImGui::Button("+Output")) {
AnimNodeResource& graph_output_node =
sGraphGresource.getGraphOutputNode();
static float bla = 0.f;
std::string socket_name = "Output";
socket_name += std::to_string(
graph_output_node.m_socket_accessor->m_inputs.size());
graph_output_node.m_socket_accessor->RegisterInput<float>(
socket_name.c_str(),
nullptr);
}
} else if (i == 1) {
if (ImGui::Button("+Input")) {
AnimNodeResource& graph_input_node =
sGraphGresource.getGraphInputNode();
static float bla = 0.f;
std::string socket_name = "Input";
socket_name += std::to_string(
graph_input_node.m_socket_accessor->m_outputs.size());
graph_input_node.m_socket_accessor->RegisterOutput<float>(
socket_name.c_str(),
nullptr);
}
}
// Save state in node resource
ImVec2 node_pos = ImNodes::GetNodeGridSpacePos(i);
node_resource.m_position[0] = node_pos[0];
node_resource.m_position[1] = node_pos[1];
ImGui::PopItemWidth();
ImNodes::EndNode();
// Ensure flags such as SocketFlagAffectsTime are properly set.
node_resource.m_socket_accessor->UpdateFlags();
}
for (size_t i = 0, n = sGraphGresource.m_connections.size(); i < n; i++) {
const AnimGraphConnectionResource& connection =
sGraphGresource.m_connections[i];
int start_attr, end_attr;
const AnimNodeResource& source_node =
sGraphGresource.m_nodes[connection.source_node_index];
int source_socket_index = source_node.m_socket_accessor->GetOutputIndex(
connection.source_socket_name.c_str());
const AnimNodeResource& target_node =
sGraphGresource.m_nodes[connection.target_node_index];
int target_socket_index = target_node.m_socket_accessor->GetInputIndex(
connection.target_socket_name.c_str());
start_attr = GenerateOutputAttributeId(
connection.source_node_index,
source_socket_index);
end_attr = GenerateInputAttributeId(
connection.target_node_index,
target_socket_index);
ImNodes::Link(i, start_attr, end_attr);
}
ImNodes::EndNodeEditor();
// Handle newly created links.
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr)) {
int node_start_id;
int node_start_output_index;
SplitOutputAttributeId(
start_attr,
&node_start_id,
&node_start_output_index);
int node_end_id;
int node_end_input_index;
SplitInputAttributeId(end_attr, &node_end_id, &node_end_input_index);
AnimGraphConnectionResource connection;
connection.source_node_index = node_start_id;
const AnimNodeResource& source_node =
sGraphGresource.m_nodes[node_start_id];
connection.source_socket_name =
source_node.m_socket_accessor->m_outputs[node_start_output_index]
.m_name;
connection.target_node_index = node_end_id;
const AnimNodeResource& target_node = sGraphGresource.m_nodes[node_end_id];
connection.target_socket_name =
target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name;
sGraphGresource.m_connections.push_back(connection);
}
if (ImGui::IsKeyPressed(ImGuiKey_Delete, false)) {
std::cerr << "Delete key!" << std::endl;
}
// Handle link detachements.
int link_id = 0;
if (ImNodes::IsLinkDestroyed(&link_id)) {
sGraphGresource.m_connections.erase(
sGraphGresource.m_connections.begin() + link_id);
}
int selected_nodes[ImNodes::NumSelectedNodes()];
ImNodes::GetSelectedNodes(selected_nodes);
ax::NodeEditor::End();
//
// Sidebar
//
ImGui::NextColumn();
if (ImNodes::NumSelectedNodes() == 1) {
if (selected_nodes[0] < sGraphGresource.m_nodes.size()) {
AnimNodeResource& selected_node =
sGraphGresource.m_nodes[selected_nodes[0]];
AnimGraphEditorRenderSidebar(sGraphGresource, selected_node);
if (ax::NodeEditor::GetSelectedObjectCount() > 0) {
ax::NodeEditor::NodeId selected_node_id = 0;
ax::NodeEditor::GetSelectedNodes(&selected_node_id, 1);
if (selected_node_id.Get() != 0) {
AnimGraphEditorRenderSidebar(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource,
selected_node_id.AsPointer<AnimNodeResource>());
}
}
ImGui::Columns(1);
// Clear flag, however it may be re-set further down when handling double
// clicking into subgraphs.
sEditorState.isGraphLoadedThisFrame = false;
//
// Handle double click into subgraphs
//
ax::NodeEditor::NodeId double_clicked_node_id =
ax::NodeEditor::GetDoubleClickedNode();
if (!double_clicked_node_id.Invalid) {
AnimNodeResource* clicked_node_resource =
double_clicked_node_id.AsPointer<AnimNodeResource>();
if (clicked_node_resource != nullptr
&& clicked_node_resource->m_node_type_name == "BlendTree") {
AnimGraphResource* clicked_graph_resource =
dynamic_cast<AnimGraphResource*>(clicked_node_resource);
assert(clicked_graph_resource != nullptr);
if (sEditorState.hierarchyStack.size()
> sEditorState.hierarchyStackIndex + 1
&& sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex + 1]
== clicked_graph_resource) {
sEditorState.hierarchyStackIndex++;
} else {
sEditorState.hierarchyStack.resize(
sEditorState.hierarchyStackIndex + 1);
sEditorState.hierarchyStack.push_back(clicked_graph_resource);
sEditorState.hierarchyStackIndex++;
}
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph) {
sGraphGresource.createInstance(anim_graph);
sEditorState.isGraphLoadedThisFrame = true;
ax::NodeEditor::ClearSelection();
}
}
ax::NodeEditor::LinkId hovered_link = ax::NodeEditor::GetHoveredLink();
if (!hovered_link.Invalid) {
BlendTreeConnectionResource* connection_resource =
hovered_link.AsPointer<BlendTreeConnectionResource>();
if (connection_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
BlendTreeResource* blend_tree_resource =
&sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource;
blend_tree_resource->DisconnectSockets(
blend_tree_resource->GetNode(connection_resource->source_node_index),
connection_resource->source_socket_name,
blend_tree_resource->GetNode(connection_resource->target_node_index),
connection_resource->target_socket_name);
ax::NodeEditor::DeleteLink(hovered_link);
}
}
ax::NodeEditor::SetCurrentEditor(nullptr);
}
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree);
}
+33 -5
View File
@@ -5,13 +5,13 @@
#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H
#define ANIMTESTBED_ANIMGRAPHEDITOR_H
#include "AnimGraph.h"
namespace ax::NodeEditor {
struct EditorContext;
} // namespace ax::NodeEditor
struct SkinnedMesh;
struct AnimGraphBlendTree;
struct SyncTrack;
inline int GenerateInputAttributeId(int node_id, int input_index) {
return ((input_index + 1) << 14) + node_id;
@@ -33,14 +33,42 @@ SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
*output_index = (attribute_id >> 23) - 1;
}
inline int NodeIndexAndSocketIndexToInputPinId(
int node_index,
int input_socket_index) {
return node_index * 1000 + input_socket_index;
}
inline int NodeIndexAndSocketIndexToOutputPinId(
int node_index,
int output_socket_index) {
return node_index * 1000 + 500 + output_socket_index;
}
inline void InputPinIdToNodeIndexAndSocketIndex(
unsigned long input_pin_id,
int* node_index,
int* socket_index) {
*socket_index = input_pin_id % 1000;
*node_index = (input_pin_id - *socket_index) / 1000;
}
inline void OutputPinIdToNodeIndexAndSocketIndex(
unsigned long output_pin_id,
int* node_index,
int* socket_index) {
*socket_index = ((output_pin_id - 500) % 1000);
*node_index = (output_pin_id - *socket_index) / 1000;
}
void SyncTrackEditor(SyncTrack* sync_track);
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh);
void AnimGraphEditorClear();
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context);
void LegacyAnimGraphEditorUpdate();
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph);
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& anim_graph);
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
+63 -5
View File
@@ -4,11 +4,71 @@
#include "AnimGraphNodes.h"
#include "ozz/base/log.h"
#include "ozz/animation/runtime/blending_job.h"
#include "AnimGraphBlendTree.h"
#include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/blending_job.h"
#include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result;
if (name == "Blend2") {
result = new Blend2Node;
} else if (name == "SpeedScale") {
result = new SpeedScaleNode;
} else if (name == "AnimSampler") {
result = new AnimSamplerNode;
} else if (name == "LockTranslationNode") {
result = new LockTranslationNode;
} else if (name == "BlendTree") {
result = new AnimGraphBlendTree;
} else if (name == "BlendTreeSockets") {
result = new BlendTreeSocketNode;
} else if (name == "MathAddNode") {
result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") {
result = new MathFloatToVec3Node;
} else if (name == "ConstScalarNode") {
result = new ConstScalarNode;
}
if (result != nullptr) {
result->m_node_type_name = name;
return result;
}
std::cerr << "Invalid node type: " << name << std::endl;
return nullptr;
}
NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name,
AnimNode* node) {
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "BlendTreeSockets") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
void Blend2Node::Evaluate(AnimGraphContext& context) {
assert(i_input0 != nullptr);
@@ -38,9 +98,7 @@ void Blend2Node::Evaluate(AnimGraphContext& context) {
//
// AnimSamplerNode
//
AnimSamplerNode::~AnimSamplerNode() noexcept {
m_animation = nullptr;
}
AnimSamplerNode::~AnimSamplerNode() noexcept { m_animation = nullptr; }
bool AnimSamplerNode::Init(AnimGraphContext& context) {
assert(m_animation == nullptr);
+13 -137
View File
@@ -8,81 +8,12 @@
#include <vector>
#include "AnimGraphData.h"
#include "AnimNode.h"
#include "SyncTrack.h"
#include "ozz/animation/runtime/sampling_job.h"
struct AnimNode;
enum class AnimNodeEvalState {
Undefined,
Deactivated,
Activated,
SyncTrackUpdated,
TimeUpdated,
Evaluated
};
struct AnimGraphConnection {
AnimNode* m_source_node = nullptr;
Socket m_source_socket;
AnimNode* m_target_node = nullptr;
Socket m_target_socket;
};
struct AnimNode {
std::string m_name;
std::string m_node_type_name;
float m_time_now = 0.f;
float m_time_last = 0.f;
size_t m_index = -1;
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
SyncTrack m_sync_track;
virtual ~AnimNode() = default;
virtual bool Init(AnimGraphContext& context) { return true; };
virtual void MarkActiveInputs(const std::vector<AnimGraphConnection>& inputs) {
for (const auto & input : inputs) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr) {
input_node->m_state = AnimNodeEvalState::Activated;
}
}
}
virtual void CalcSyncTrack(const std::vector<AnimGraphConnection>& inputs) {
for (const auto & input : inputs) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr
&& input.m_source_socket.m_type == SocketType::SocketTypeAnimation
&& input_node->m_state != AnimNodeEvalState::Deactivated) {
m_sync_track = input_node->m_sync_track;
return;
}
}
}
virtual void UpdateTime(float time_last, float time_now) {
m_time_last = time_last;
m_time_now = time_now;
m_state = AnimNodeEvalState::TimeUpdated;
}
virtual void Evaluate(AnimGraphContext& context){};
};
//
// BlendTreeNode
//
struct BlendTreeNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeNode* node_) {}
};
//
// Blend2Node
//
@@ -93,26 +24,27 @@ struct Blend2Node : public AnimNode {
float* i_blend_weight = nullptr;
bool m_sync_blend = false;
virtual void MarkActiveInputs(const std::vector<AnimGraphConnection>& inputs) override {
for (const auto & input : inputs) {
void MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) override {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node;
if (input_node == nullptr) {
continue;
}
if (input.m_target_socket.m_name == "Input0" && *i_blend_weight < 0.999) {
if (input.m_target_socket_name == "Input0" && *i_blend_weight < 0.999) {
input_node->m_state = AnimNodeEvalState::Activated;
continue;
}
if (input.m_target_socket.m_name == "Input1" && *i_blend_weight > 0.001) {
if (input.m_target_socket_name == "Input1" && *i_blend_weight > 0.001) {
input_node->m_state = AnimNodeEvalState::Activated;
continue;
}
}
}
virtual void Evaluate(AnimGraphContext& context) override;
void Evaluate(AnimGraphContext& context) override;
};
template <>
@@ -139,7 +71,6 @@ struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
}
};
//
// SpeedScaleNode
//
@@ -175,7 +106,6 @@ struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
}
};
//
// AnimSamplerNode
//
@@ -185,8 +115,8 @@ struct AnimSamplerNode : public AnimNode {
ozz::animation::SamplingJob::Context m_sampling_context;
ozz::animation::Animation* m_animation = nullptr;
virtual ~AnimSamplerNode();
virtual bool Init(AnimGraphContext& context) override;
~AnimSamplerNode() noexcept override;
bool Init(AnimGraphContext& context) override;
void UpdateTime(float time_last, float time_now) override {
m_time_last = time_last;
m_time_now = time_now;
@@ -231,7 +161,6 @@ struct NodeDescriptor<LockTranslationNode> : public NodeDescriptorBase {
}
};
//
// ConstScalarNode
//
@@ -239,9 +168,7 @@ struct ConstScalarNode : public AnimNode {
float* o_value = nullptr;
float value = 0.f;
virtual void Evaluate(AnimGraphContext& context){
*o_value = value;
};
virtual void Evaluate(AnimGraphContext& context) { *o_value = value; };
};
template <>
@@ -252,7 +179,6 @@ struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
}
};
//
// MathAddNode
//
@@ -308,60 +234,10 @@ struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
}
};
AnimNode* AnimNodeFactory(const std::string& name);
static inline AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result;
if (name == "Blend2") {
result = new Blend2Node;
} else if (name == "SpeedScale") {
result = new SpeedScaleNode;
} else if (name == "AnimSampler") {
result = new AnimSamplerNode;
} else if (name == "LockTranslationNode") {
result = new LockTranslationNode;
} else if (name == "BlendTree") {
result = new BlendTreeNode;
} else if (name == "MathAddNode") {
result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") {
result = new MathFloatToVec3Node;
} else if (name == "ConstScalarNode") {
result = new ConstScalarNode;
}
if (result != nullptr) {
result->m_node_type_name = name;
return result;
}
std::cerr << "Invalid node type: " << name << std::endl;
return nullptr;
}
static inline NodeDescriptorBase* AnimNodeDescriptorFactory(
NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name,
AnimNode* node) {
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
AnimNode* node);
#endif //ANIMTESTBED_ANIMGRAPHNODES_H
File diff suppressed because it is too large Load Diff
+336 -92
View File
@@ -1,146 +1,390 @@
//
// Created by martin on 04.02.22.
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#include "AnimGraph.h"
#include "AnimGraphData.h"
#include "3rdparty/json/json.hpp"
#include "AnimGraphNodes.h"
#include "SyncTrack.h"
struct AnimNode;
struct AnimGraphBlendTree;
struct AnimGraphStateMachine;
struct AnimNodeResource {
virtual ~AnimNodeResource() = default;
std::string m_name;
std::string m_type_name;
std::string m_node_type_name;
AnimNode* m_anim_node = nullptr;
NodeDescriptorBase* m_socket_accessor = nullptr;
float m_position[2] = {0.f, 0.f};
};
static inline AnimNodeResource AnimNodeResourceFactory(
const std::string& node_type_name) {
AnimNodeResource result;
result.m_type_name = node_type_name;
result.m_anim_node = AnimNodeFactory(node_type_name);
result.m_socket_accessor =
AnimNodeDescriptorFactory(node_type_name, result.m_anim_node);
return result;
}
static inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name);
//
// AnimGraphResource
//
struct AnimGraphConnectionResource {
size_t source_node_index = -1;
struct BlendTreeConnectionResource {
int source_node_index = -1;
std::string source_socket_name;
size_t target_node_index = -1;
int target_node_index = -1;
std::string target_socket_name;
bool operator==(const BlendTreeConnectionResource& other) const {
return (
source_node_index == other.source_node_index
&& target_node_index == other.target_node_index
&& source_socket_name == other.source_socket_name
&& target_socket_name == other.target_socket_name);
}
};
struct AnimGraphResource {
std::string m_name;
std::vector<AnimNodeResource> m_nodes;
std::vector<AnimGraphConnectionResource> m_connections;
struct BlendTreeResource {
std::vector<std::vector<size_t> > m_node_input_connection_indices;
std::vector<std::vector<size_t> > m_node_inputs_subtree;
~AnimGraphResource() {
for (auto & m_node : m_nodes) {
delete m_node.m_anim_node;
delete m_node.m_socket_accessor;
}
~BlendTreeResource() { CleanupNodes(); }
void Reset() {
CleanupNodes();
m_connections.clear();
m_node_input_connection_indices.clear();
m_node_inputs_subtree.clear();
}
AnimGraphResource() { clear(); }
void CleanupNodes() {
for (AnimNodeResource* node_resource : m_nodes) {
delete node_resource->m_anim_node;
delete node_resource->m_socket_accessor;
delete node_resource;
}
void clear();
void clearNodes();
void initGraphConnectors();
bool saveToFile(const char* filename) const;
bool loadFromFile(const char* filename);
m_nodes.clear();
}
AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; }
AnimNodeResource& getGraphInputNode() { return m_nodes[1]; }
void InitGraphConnectors() {
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* output_node = GetGraphOutputNode();
output_node->m_name = "Outputs";
size_t getNodeIndex(const AnimNodeResource& node_resource) const {
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* input_node = GetGraphInputNode();
output_node->m_name = "Inputs";
}
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const {
return m_nodes[0];
}
[[nodiscard]] AnimNodeResource* GetGraphInputNode() const {
return m_nodes[1];
}
int GetNodeIndex(const AnimNodeResource* node_resource) const {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
if (&m_nodes[i] == &node_resource) {
if (m_nodes[i] == node_resource) {
return i;
}
}
std::cerr << "Error: could not find node index for node resource "
<< node_resource << std::endl;
return -1;
}
size_t addNode(const AnimNodeResource &node_resource) {
[[maybe_unused]] size_t AddNode(AnimNodeResource* node_resource) {
m_nodes.push_back(node_resource);
m_node_input_connection_indices.emplace_back();
m_node_inputs_subtree.emplace_back();
return m_nodes.size() - 1;
}
bool connectSockets(
const AnimNodeResource& source_node,
[[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); }
[[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; }
[[nodiscard]] const AnimNodeResource* GetNode(size_t i) const {
return m_nodes[i];
}
[[nodiscard]] const std::vector<AnimNodeResource*>& GetNodes() const {
return m_nodes;
}
[[nodiscard]] size_t GetNumConnections() const {
return m_connections.size();
}
[[nodiscard]] BlendTreeConnectionResource* GetConnection(size_t i) {
return &m_connections[i];
}
[[nodiscard]] const BlendTreeConnectionResource* GetConnection(
size_t i) const {
return &m_connections[i];
}
[[nodiscard]] const std::vector<BlendTreeConnectionResource>& GetConnections()
const {
return m_connections;
}
Socket* GetNodeOutputSocket(
const AnimNodeResource* node,
const std::string& output_socket_name) const;
const Socket* GetNodeOutputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_output_index) const;
Socket* GetNodeInputSocket(
const AnimNodeResource* node,
const std::string& input_socket_name) const;
const Socket* GetNodeInputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_input_index) const;
std::vector<Socket> GetNodeOutputSockets(const AnimNodeResource* node) const;
std::vector<Socket> GetNodeInputSockets(const AnimNodeResource* node) const;
bool ConnectSockets(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource& target_node,
const std::string& target_socket_name) {
size_t source_node_index = getNodeIndex(source_node);
size_t target_node_index = getNodeIndex(target_node);
const AnimNodeResource* target_node,
const std::string& target_socket_name);
if (source_node_index >= m_nodes.size()
|| target_node_index >= m_nodes.size()) {
std::cerr << "Cannot connect nodes: could not find nodes." << std::endl;
return false;
bool DisconnectSockets(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name);
bool IsConnectionValid(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name) const;
const BlendTreeConnectionResource* FindConnectionForSocket(
const AnimNodeResource* node,
const std::string& socket_name) const;
bool IsSocketConnected(
const AnimNodeResource* node,
const std::string& socket_name) const {
const BlendTreeConnectionResource* connection =
FindConnectionForSocket(node, socket_name);
return connection != nullptr;
}
Socket* source_socket =
source_node.m_socket_accessor->GetOutputSocket(source_socket_name.c_str());
Socket* target_socket =
target_node.m_socket_accessor->GetInputSocket(target_socket_name.c_str());
std::vector<Socket*> GetConstantNodeInputs(
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
std::vector<Socket*> result;
if (source_socket == nullptr || target_socket == nullptr) {
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
return false;
for (size_t i = 0; i < m_nodes.size(); i++) {
for (size_t j = 0,
num_inputs = instance_node_descriptors[i]->m_inputs.size();
j < num_inputs;
j++) {
Socket& input = instance_node_descriptors[i]->m_inputs[j];
if (*input.m_reference.ptr_ptr == nullptr) {
memcpy(
&input.m_value,
&m_nodes[i]->m_socket_accessor->m_inputs[j].m_value,
sizeof(Socket::SocketValue));
result.push_back(&input);
}
AnimGraphConnectionResource connection;
connection.source_node_index = source_node_index;
connection.source_socket_name = source_socket_name;
connection.target_node_index = target_node_index;
connection.target_socket_name = target_socket_name;
m_connections.push_back(connection);
return true;
}
bool isSocketConnected(
const AnimNodeResource& node,
const std::string& socket_name) {
size_t node_index = getNodeIndex(node);
for (const auto & connection : m_connections) {
if ((connection.source_node_index == node_index
&& connection.source_socket_name == socket_name)
|| ((connection.target_node_index == node_index)
&& connection.target_socket_name == socket_name)) {
return true;
}
}
return false;
return result;
}
void createInstance(AnimGraph& result) const;
size_t GetNodeIndexForOutputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.target_node_index == 0
&& connection.target_socket_name == socket_name) {
return connection.source_node_index;
}
}
void createRuntimeNodeInstances(AnimGraph& instance) const;
void prepareGraphIOData(AnimGraph& instance) const;
void setRuntimeNodeProperties(AnimGraph& instance) const;
std::vector<Socket*> getConstNodeInputs(std::vector<NodeDescriptorBase*>& instance_node_descriptors) const;
std::cerr << "Error: could not find a node connected to output '"
<< socket_name << "'." << std::endl;
return -1;
}
size_t GetNodeIndexForInputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.source_node_index == 1
&& connection.source_socket_name == socket_name) {
return connection.target_node_index;
}
}
std::cerr << "Error: could not find a node connected to input '"
<< socket_name << "'." << std::endl;
return -1;
}
void UpdateTreeTopologyInfo();
[[nodiscard]] const std::vector<size_t>& GetNodeEvalOrder() const {
return m_node_eval_order;
}
private:
void UpdateNodeEvalOrder() {
m_node_eval_order.clear();
UpdateNodeEvalOrderRecursive(0);
}
void UpdateNodeEvalOrderRecursive(size_t node_index);
void UpdateNodeSubtrees();
std::vector<AnimNodeResource*> m_nodes;
std::vector<BlendTreeConnectionResource> m_connections;
std::vector<size_t> m_node_eval_order;
};
struct StateMachineTransitionResources {
size_t source_state_index = -1;
size_t target_state_index = -1;
float blend_time = 0.f;
bool sync_blend = false;
};
struct StateMachineResource {
std::vector<AnimNodeResource> m_states;
std::vector<StateMachineTransitionResources> m_transitions;
};
struct AnimGraphResource : AnimNodeResource {
std::string m_graph_type_name;
BlendTreeResource m_blend_tree_resource;
typedef std::pair<const AnimNodeResource*, std::string> NodeSocketPair;
typedef std::map<NodeSocketPair, int> NodeSocketDataOffsetMap;
StateMachineResource m_state_machine_resource;
void Clear() { m_blend_tree_resource.Reset(); }
bool SaveToFile(const char* filename) const;
bool LoadFromFile(const char* filename);
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
template <typename T>
bool RegisterBlendTreeInputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeInputSocket(socket);
}
bool RegisterBlendTreeInputSocket(const Socket& socket) {
AnimNodeResource* input_node = m_blend_tree_resource.GetGraphInputNode();
std::vector<Socket> input_sockets =
input_node->m_socket_accessor->m_outputs;
std::vector<Socket>::const_iterator iter = std::find_if(
input_sockets.begin(),
input_sockets.end(),
[&socket](const Socket& input_socket) {
return socket.m_name == input_socket.m_name;
});
if (iter != input_sockets.end()) {
std::cerr << "Error: cannot register input socket as socket with name '"
<< socket.m_name << "' already exists!" << std::endl;
return false;
}
input_node->m_socket_accessor->m_outputs.push_back(socket);
m_socket_accessor->m_inputs = input_node->m_socket_accessor->m_outputs;
return true;
}
template <typename T>
bool RegisterBlendTreeOutputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeOutputSocket(socket);
}
bool RegisterBlendTreeOutputSocket(const Socket& socket) {
AnimNodeResource* output_node = m_blend_tree_resource.GetGraphOutputNode();
std::vector<Socket> output_sockets =
output_node->m_socket_accessor->m_inputs;
std::vector<Socket>::const_iterator iter = std::find_if(
output_sockets.begin(),
output_sockets.end(),
[&socket](const Socket& input_socket) {
return socket.m_name == input_socket.m_name;
});
if (iter != output_sockets.end()) {
return false;
}
output_node->m_socket_accessor->m_inputs.push_back(socket);
m_socket_accessor->m_outputs = output_node->m_socket_accessor->m_inputs;
return true;
}
void CreateStateMachineInstance(AnimGraphStateMachine& result) const;
private:
// BlendTree
bool SaveBlendTreeResourceToFile(const char* filename) const;
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
void PrepareBlendTreeIOData(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
bool SaveStateMachineResourceToFile(const char* filename) const;
bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data);
};
static inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name) {
AnimNodeResource* result;
if (node_type_name == "BlendTree") {
AnimGraphResource* blend_tree_resource = new AnimGraphResource();
blend_tree_resource->m_blend_tree_resource.InitGraphConnectors();
result = blend_tree_resource;
} else {
result = new AnimNodeResource();
}
result->m_node_type_name = node_type_name;
if (node_type_name == "BlendTreeSockets") {
result->m_anim_node = AnimNodeFactory("BlendTree");
result->m_socket_accessor = new NodeDescriptorBase();
} else {
result->m_anim_node = AnimNodeFactory(node_type_name);
result->m_socket_accessor =
AnimNodeDescriptorFactory(node_type_name, result->m_anim_node);
}
return result;
}
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H
+28
View File
@@ -0,0 +1,28 @@
//
// Created by martin on 17.03.24.
//
#include "AnimGraphStateMachine.h"
bool AnimGraphStateMachine::Init(AnimGraphContext& context) {
assert(false && !"Not yet implemented!");
return false;
}
void AnimGraphStateMachine::MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) {
assert(false && !"Not yet implemented!");
}
void AnimGraphStateMachine::CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) {
assert(false && !"Not yet implemented!");
}
void AnimGraphStateMachine::UpdateTime(float time_last, float time_now) {
assert(false && !"Not yet implemented!");
}
void AnimGraphStateMachine::Evaluate(AnimGraphContext& context) {
assert(false && !"Not yet implemented!");
}
+35
View File
@@ -0,0 +1,35 @@
//
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMGRAPHSTATEMACHINE_H
#define ANIMTESTBED_ANIMGRAPHSTATEMACHINE_H
#include "AnimGraphNodes.h"
struct Transition {
AnimNode* m_source_state = nullptr;
AnimNode* m_target_state = nullptr;
float m_blend_time = 0.f;
bool m_sync_blend = false;
};
struct AnimGraphStateMachine : public AnimNode {
std::vector<AnimNode> m_states;
std::vector<Transition> m_transitions;
std::vector<std::vector<Transition*> > m_state_out_transitions;
AnimNode* m_next_state = nullptr;
AnimNode* m_current_state = nullptr;
Transition* m_active_transition = nullptr;
bool Init(AnimGraphContext& context);
void MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override;
};
#endif //ANIMTESTBED_ANIMGRAPHSTATEMACHINE_H
+5
View File
@@ -0,0 +1,5 @@
//
// Created by martin on 17.03.24.
//
#include "AnimNode.h"
+76
View File
@@ -0,0 +1,76 @@
//
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMNODE_H
#define ANIMTESTBED_ANIMNODE_H
#include <string>
#include <vector>
#include "AnimGraphData.h"
#include "SyncTrack.h"
struct AnimNode;
enum class AnimNodeEvalState {
Undefined,
Deactivated,
Activated,
SyncTrackUpdated,
TimeUpdated,
Evaluated
};
struct AnimNode {
std::string m_name;
std::string m_node_type_name;
int m_tick_number = 0;
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
float m_time_now = 0.f;
float m_time_last = 0.f;
SyncTrack m_sync_track;
virtual ~AnimNode() = default;
virtual bool Init(AnimGraphContext& context) {
m_time_now = 0.f;
m_time_last = 0.f;
return true;
};
virtual void MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr) {
input_node->m_tick_number = m_tick_number;
input_node->m_state = AnimNodeEvalState::Activated;
}
}
}
virtual void CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr
&& input.m_socket.m_type == SocketType::SocketTypeAnimation
&& input_node->m_state != AnimNodeEvalState::Deactivated) {
m_sync_track = input_node->m_sync_track;
return;
}
}
}
virtual void UpdateTime(float time_last, float time_now) {
m_time_last = time_last;
m_time_now = time_now;
m_state = AnimNodeEvalState::TimeUpdated;
}
virtual void Evaluate(AnimGraphContext& context){};
};
#endif //ANIMTESTBED_ANIMNODE_H
+10441
View File
File diff suppressed because it is too large Load Diff
+68 -98
View File
@@ -16,13 +16,17 @@
#define GLFW_INCLUDE_NONE
#include <iostream>
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
#include "3rdparty/json/json.hpp"
#include "AnimGraph/AnimGraphBlendTree.h"
#include "AnimGraph/AnimGraphData.h"
#include "Camera.h"
#include "GLFW/glfw3.h"
#include "SkinnedMesh.h"
#include "SkinnedMeshRenderer.h"
#include "SkinnedMeshResource.h"
#include "embedded_fonts.h"
#include "src/AnimGraph/AnimGraphEditor.h"
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
const int Width = 1024;
const int Height = 768;
@@ -49,7 +53,6 @@ static void draw_imgui(ImDrawData*);
#include <cmath> // fmodf
#include <fstream>
#include "SkinnedMeshRenderer.h"
#include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/sampling_job.h"
#include "ozz/animation/runtime/skeleton.h"
@@ -156,12 +159,10 @@ struct Viewport {
this->pass = sg_make_pass(&offscreen_pass_desc);
sg_pipeline_desc gl_pipeline_desc = {
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true
},
.cull_mode = SG_CULLMODE_BACK
};
.depth = {.compare = SG_COMPAREFUNC_LESS_EQUAL, .write_enabled = true},
.cull_mode = SG_CULLMODE_BACK,
.sample_count = cMSAASampleCount};
// this->pip = sg_make_pipeline(gl_pipeline_desc);
}
};
@@ -169,13 +170,6 @@ struct ApplicationConfig {
int window_position[2] = {100, 30};
int window_size[2] = {1000, 600};
struct LegacyGraphEditor {
bool visible = false;
int position[2] = {20, 20};
int size[2] = {800, 500};
};
LegacyGraphEditor legacy_graph_editor;
struct GraphEditor {
bool visible = false;
int position[2] = {20, 20};
@@ -221,12 +215,6 @@ void to_json(nlohmann::json& j, const ApplicationConfig& config) {
j["main_window"]["size"][0] = config.window_size[0];
j["main_window"]["size"][1] = config.window_size[1];
j["legacy_graph_editor"]["visible"] = config.legacy_graph_editor.visible;
j["legacy_graph_editor"]["position"][0] = config.legacy_graph_editor.position[0];
j["legacy_graph_editor"]["position"][1] = config.legacy_graph_editor.position[1];
j["legacy_graph_editor"]["size"][0] = config.legacy_graph_editor.size[0];
j["legacy_graph_editor"]["size"][1] = config.legacy_graph_editor.size[1];
j["graph_editor"]["visible"] = config.graph_editor.visible;
j["graph_editor"]["position"][0] = config.graph_editor.position[0];
j["graph_editor"]["position"][1] = config.graph_editor.position[1];
@@ -278,24 +266,6 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) {
}
}
if (j.contains("legacy_graph_editor")) {
if (j["legacy_graph_editor"].contains("visible")) {
config.legacy_graph_editor.visible = j["legacy_graph_editor"]["visible"];
}
if (j["legacy_graph_editor"].contains("position")
and j["legacy_graph_editor"]["position"].size() == 2) {
config.legacy_graph_editor.position[0] = j["legacy_graph_editor"]["position"].at(0);
config.legacy_graph_editor.position[1] = j["legacy_graph_editor"]["position"].at(1);
}
if (j["legacy_graph_editor"].contains("size")
and j["legacy_graph_editor"]["size"].size() == 2) {
config.legacy_graph_editor.size[0] = j["legacy_graph_editor"]["size"].at(0);
config.legacy_graph_editor.size[1] = j["legacy_graph_editor"]["size"].at(1);
}
}
if (j.contains("graph_editor")) {
if (j["graph_editor"].contains("visible")) {
config.graph_editor.visible = j["graph_editor"]["visible"];
@@ -472,7 +442,7 @@ int main() {
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
glfwWindowHint(GLFW_SAMPLES, 16);
glfwWindowHint(GLFW_SAMPLES, cMSAASampleCount);
GLFWwindow* w =
glfwCreateWindow(Width, Height, "ATP Editor", nullptr, nullptr);
glfwMakeContextCurrent(w);
@@ -527,7 +497,9 @@ int main() {
// setup sokol_gfx and sokol_time
stm_setup();
sg_desc desc = {.logger = {.func = slog_func}};
sg_desc desc = {
.logger = {.func = slog_func},
.context{.sample_count = cMSAASampleCount}};
sg_setup(&desc);
assert(sg_isvalid());
@@ -548,11 +520,12 @@ int main() {
skinned_mesh_resource.createInstance(skinned_mesh);
skinned_mesh.SetCurrentAnimation(0);
AnimGraph anim_graph;
AnimGraphBlendTree anim_graph;
AnimGraphContext anim_graph_context;
AnimData anim_graph_output;
anim_graph_output.m_local_matrices.resize(
skinned_mesh.m_skeleton.num_soa_joints());
AnimGraphEditorClear();
state.time.factor = 1.0f;
@@ -564,7 +537,20 @@ int main() {
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.IniFilename = "ATPImgui.ini";
io.Fonts->AddFontDefault();
//io.Fonts->AddFontDefault();
ImFontConfig font_config;
font_config.OversampleH = 4;
font_config.OversampleV = 4;
font_config.GlyphExtraSpacing.x = 1.0f;
io.Fonts->AddFontFromMemoryCompressedTTF(
// roboto_medium_ttf_compressed_data,
// roboto_medium_ttf_compressed_size,
droid_sans_ttf_compressed_data,
droid_sans_ttf_compressed_size,
14,
&font_config);
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
@@ -658,6 +644,7 @@ int main() {
pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
pip_desc.colors[0].write_mask = SG_COLORMASK_RGB;
pip_desc.sample_count = cMSAASampleCount;
pip_desc.label = "imgui-rendering";
pip = sg_make_pipeline(&pip_desc);
@@ -670,7 +657,8 @@ int main() {
// Graph Editor
gApplicationConfig.graph_editor.config.SettingsFile = "graph_editor.json";
gApplicationConfig.graph_editor.config.NavigateButtonIndex = 2;
gApplicationConfig.graph_editor.context = ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config);
gApplicationConfig.graph_editor.context =
ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config);
// draw loop
while (!glfwWindowShouldClose(w)) {
@@ -792,9 +780,6 @@ int main() {
&gApplicationConfig.animation_player_widget.visible);
ImGui::Separator();
ImGui::Checkbox(
"Legacy Graph Editor",
&gApplicationConfig.legacy_graph_editor.visible);
ImGui::Checkbox(
"ImGui Demo",
@@ -808,11 +793,11 @@ int main() {
AnimGraphEditorGetRuntimeGraph(anim_graph);
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
anim_graph.init(anim_graph_context);
anim_graph.Init(anim_graph_context);
// For simplicity use first animation data output
const std::vector<Socket>& graph_output_sockets =
anim_graph.getGraphOutputs();
anim_graph.GetGraphOutputs();
for (const auto& output : graph_output_sockets) {
if (output.m_type == SocketType::SocketTypeAnimation) {
anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output);
@@ -842,7 +827,8 @@ int main() {
ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible);
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
if (ImGui::IsWindowHovered()
&& ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
if (gControlMode == ControlMode::ControlModeNone) {
gControlMode = ControlMode::ControlModeFPS;
Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]);
@@ -851,8 +837,10 @@ int main() {
}
ImVec2 viewport_widget_size = ImGui::GetWindowSize();
gApplicationConfig.viewport_widget.size[0] = static_cast<int>(viewport_widget_size.x);
gApplicationConfig.viewport_widget.size[1] = static_cast<int>(viewport_widget_size.y);
gApplicationConfig.viewport_widget.size[0] =
static_cast<int>(viewport_widget_size.x);
gApplicationConfig.viewport_widget.size[1] =
static_cast<int>(viewport_widget_size.y);
ImGui::Text(
"Viewport size: %d, %d",
@@ -866,12 +854,16 @@ int main() {
if (static_cast<float>(current_size[0]) != content_size[0]
|| static_cast<float>(current_size[1]) != content_size[1]
|| offscreen_viewport.pass.id == 0) {
offscreen_viewport.Resize(static_cast<int>(content_size[0]), static_cast<int>(content_size[1]));
offscreen_viewport.Resize(
static_cast<int>(content_size[0]),
static_cast<int>(content_size[1]));
}
ImGui::Image(
(ImTextureID)(uintptr_t)offscreen_viewport.color_image.id,
ImVec2(static_cast<float>(offscreen_viewport.size[0]), static_cast<float>(offscreen_viewport.size[1])),
ImVec2(
static_cast<float>(offscreen_viewport.size[0]),
static_cast<float>(offscreen_viewport.size[1])),
ImVec2(0.0f, 1.0f),
ImVec2(1.0f, 0.0f));
@@ -894,13 +886,17 @@ int main() {
if (gApplicationConfig.skinned_mesh_widget.visible) {
ImGui::SetNextWindowPos(
ImVec2(
static_cast<float>(gApplicationConfig.skinned_mesh_widget.position[0]),
static_cast<float>(gApplicationConfig.skinned_mesh_widget.position[1])),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.position[0]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.position[1])),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(
static_cast<float>(gApplicationConfig.skinned_mesh_widget.size[0]),
static_cast<float>(gApplicationConfig.skinned_mesh_widget.size[1])),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.size[0]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.size[1])),
ImGuiCond_FirstUseEver);
ImGui::Begin(
@@ -927,13 +923,17 @@ int main() {
if (gApplicationConfig.animation_player_widget.visible) {
ImGui::SetNextWindowPos(
ImVec2(
static_cast<float>(gApplicationConfig.animation_player_widget.position[0]),
static_cast<float>(gApplicationConfig.animation_player_widget.position[1])),
static_cast<float>(
gApplicationConfig.animation_player_widget.position[0]),
static_cast<float>(
gApplicationConfig.animation_player_widget.position[1])),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(
static_cast<float>(gApplicationConfig.animation_player_widget.size[0]),
static_cast<float>(gApplicationConfig.animation_player_widget.size[1])),
static_cast<float>(
gApplicationConfig.animation_player_widget.size[0]),
static_cast<float>(
gApplicationConfig.animation_player_widget.size[1])),
ImGuiCond_FirstUseEver);
ImGui::Begin(
@@ -1012,8 +1012,8 @@ int main() {
if (state.ozz.animation != nullptr) {
state.ozz.sampling_job.animation = state.ozz.animation;
state.ozz.sampling_job.ratio =
static_cast<float>(state.time.absolute) / state.ozz.animation->duration();
state.ozz.sampling_job.ratio = static_cast<float>(state.time.absolute)
/ state.ozz.animation->duration();
state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context;
state.ozz.sampling_job.output =
ozz::make_span(skinned_mesh.m_local_matrices);
@@ -1028,9 +1028,10 @@ int main() {
if (state.time.use_graph && !anim_graph.m_nodes.empty()
&& state.time.anim_update_time > 0.) {
anim_graph.markActiveNodes();
anim_graph.updateTime(state.time.anim_update_time);
anim_graph.evaluate(anim_graph_context);
// TODO: update for new API after embedding refactor
// anim_graph.MarkActiveNodes();
// anim_graph.UpdateTime()pdateTime(state.time.anim_update_time);
// anim_graph.evaluate(anim_graph_context);
skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices;
skinned_mesh.CalcModelMatrices();
@@ -1077,37 +1078,6 @@ int main() {
ImGui::End();
}
// Legacy Animation Graph Editor
if (gApplicationConfig.legacy_graph_editor.visible) {
ImGui::SetNextWindowPos(
ImVec2(
gApplicationConfig.legacy_graph_editor.position[0],
gApplicationConfig.legacy_graph_editor.position[1]),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(
gApplicationConfig.legacy_graph_editor.size[0],
gApplicationConfig.legacy_graph_editor.size[1]),
ImGuiCond_FirstUseEver);
ImGui::Begin(
"Legacy Graph Editor",
&gApplicationConfig.legacy_graph_editor.visible,
ImGuiWindowFlags_MenuBar);
ImVec2 graph_editor_position = ImGui::GetWindowPos();
gApplicationConfig.legacy_graph_editor.position[0] = graph_editor_position.x;
gApplicationConfig.legacy_graph_editor.position[1] = graph_editor_position.y;
ImVec2 graph_editor_size = ImGui::GetWindowSize();
gApplicationConfig.legacy_graph_editor.size[0] = graph_editor_size.x;
gApplicationConfig.legacy_graph_editor.size[1] = graph_editor_size.y;
LegacyAnimGraphEditorUpdate();
ImGui::End();
}
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowDemoWindow()
if (gApplicationConfig.show_imgui_demo_window) {
ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
+40
View File
@@ -0,0 +1,40 @@
#include "AnimGraph/AnimGraphEditor.h"
#include "catch.hpp"
TEST_CASE("Node Socket To InputPin Conversion", "[animGraphEditor]") {
int node_index = 321;
int socket_index = 221;
long socket_id;
socket_id = NodeIndexAndSocketIndexToInputPinId(node_index, socket_index);
int node_index_resolved;
int socket_index_resolved;
InputPinIdToNodeIndexAndSocketIndex(
socket_id,
&node_index_resolved,
&socket_index_resolved);
CHECK(node_index == node_index_resolved);
CHECK(socket_index == socket_index_resolved);
}
TEST_CASE("Node Socket To OutputPin Conversion", "[animGraphEditor]") {
int node_index = 321;
int socket_index = 221;
long socket_id;
socket_id = NodeIndexAndSocketIndexToOutputPinId(node_index, socket_index);
int node_index_resolved;
int socket_index_resolved;
OutputPinIdToNodeIndexAndSocketIndex(
socket_id,
&node_index_resolved,
&socket_index_resolved);
CHECK(node_index == node_index_resolved);
CHECK(socket_index == socket_index_resolved);
}
+2 -2
View File
@@ -3,8 +3,8 @@
//
#include "AnimGraph/AnimGraph.h"
#include "AnimGraph/AnimGraphBlendTreeResource.h"
#include "AnimGraph/AnimGraphEditor.h"
#include "AnimGraph/AnimGraphResource.h"
#include "catch.hpp"
#include "ozz/animation/offline/animation_builder.h"
#include "ozz/animation/offline/raw_animation.h"
@@ -141,7 +141,7 @@ TEST_CASE_METHOD(
SimpleAnimFixture,
"AnimGraphSimpleEval",
"[AnimGraphEvalTests]") {
AnimGraphResource graph_resource;
AnimGraphBlendTreeResource graph_resource;
// Add nodes
size_t trans_x_node_index =
File diff suppressed because it is too large Load Diff