Compare commits
32
Commits
1ef53d6486
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99d5a5eb0f | ||
|
|
698abbce4b | ||
|
|
b9789bd1e1 | ||
|
|
fd032c273b | ||
|
|
da431a3879 | ||
|
|
84fc49af30 | ||
|
|
7c7a765455 | ||
|
|
e3baa65c3b | ||
|
|
44087d7a7c | ||
|
|
e8af30d10c | ||
|
|
4d1990bea8 | ||
|
|
5e34aaf3db | ||
|
|
3fb2995b02 | ||
|
|
c267276be3 | ||
|
|
53c0bff7a6 | ||
|
|
91e226945c | ||
|
|
d95bc9fb9c | ||
|
|
2d5337ed1d | ||
|
|
9f9ac60f9c | ||
|
|
1741238a61 | ||
|
|
8694a11416 | ||
|
|
3444f8a625 | ||
|
|
cd56efca3d | ||
|
|
28eca48a61 | ||
|
|
76ea38f118 | ||
|
|
0aebe44bd5 | ||
|
|
99f11e61d8 | ||
|
|
116bf7699b | ||
|
|
3a7f470acf | ||
|
|
e687c9b613 | ||
|
|
ccb9bc4e9b | ||
|
|
c7d2d195a3 |
Vendored
+16
@@ -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
|
||||
----------------------
|
||||
|
||||
|
||||
+7
-5
@@ -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,15 +69,17 @@ 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()
|
||||
|
||||
# gltf
|
||||
# gltf
|
||||
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/")
|
||||
|
||||
Vendored
+1
-1
@@ -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.
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
# Set compilers settings for all platforms/compilers.
|
||||
#---------------------------------------------------
|
||||
# ---------------------------------------------------
|
||||
|
||||
#-----------------
|
||||
# -----------------
|
||||
# Includes modules
|
||||
include(CheckIncludeFiles)
|
||||
|
||||
#------------------------------
|
||||
# ------------------------------
|
||||
# Enables IDE folders y default
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
#------------------------
|
||||
# ------------------------
|
||||
# Available build options
|
||||
|
||||
#------------------------
|
||||
# ------------------------
|
||||
# Lists all the cxx flags
|
||||
set(cxx_all_flags
|
||||
CMAKE_CXX_FLAGS
|
||||
@@ -26,13 +26,14 @@ set(cxx_all_flags
|
||||
CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_RELEASE)
|
||||
|
||||
#--------------------------------------
|
||||
# --------------------------------------
|
||||
# Cross compiler compilation flags
|
||||
|
||||
# Requires C++11
|
||||
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,62 +61,61 @@ 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}}")
|
||||
else()
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(ozz_build_msvc_rt_dll)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
||||
else()
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
endif()
|
||||
|
||||
#--------------------------------------
|
||||
# --------------------------------------
|
||||
# else consider the compiler as GCC compatible (inc clang)
|
||||
else()
|
||||
|
||||
# Set the warning level to Wall
|
||||
add_compile_options(-Wall)
|
||||
|
||||
# Enable extra level of warning
|
||||
#add_compile_options(-Wextra)
|
||||
# add_compile_options(-Wextra)
|
||||
|
||||
# Set warning as error
|
||||
add_compile_options(-Werror)
|
||||
|
||||
# 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)
|
||||
|
||||
#----------------------
|
||||
# ----------------------
|
||||
# Sets emscripten output
|
||||
if(EMSCRIPTEN)
|
||||
SET(CMAKE_EXECUTABLE_SUFFIX ".html")
|
||||
add_link_options(-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0)
|
||||
|
||||
#if(NOT ozz_build_simd_ref)
|
||||
# set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-msse2")
|
||||
#endif()
|
||||
# if(NOT ozz_build_simd_ref)
|
||||
# set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-msse2")
|
||||
# endif()
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
#---------------------
|
||||
# ---------------------
|
||||
# Prints all the flags
|
||||
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,13 +133,14 @@ message(STATUS "---------------------------------------------------------")
|
||||
|
||||
get_directory_property(DirectoryCompileDefinitions DIRECTORY ${PROJECT_SOURCE_DIR} COMPILE_DEFINITIONS)
|
||||
message(STATUS "Directory Compile Definitions:")
|
||||
|
||||
foreach(def ${DirectoryCompileDefinitions})
|
||||
message(STATUS ${def})
|
||||
endforeach()
|
||||
|
||||
message(STATUS "---------------------------------------------------------")
|
||||
|
||||
#----------------------------------------------
|
||||
# ----------------------------------------------
|
||||
# Modifies output directory for all executables
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ".")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ".")
|
||||
@@ -146,7 +148,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ".")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ".")
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ".")
|
||||
|
||||
#-------------------------------
|
||||
# -------------------------------
|
||||
# Set a postfix for output files
|
||||
if(ozz_build_postfix)
|
||||
set(CMAKE_DEBUG_POSTFIX "_d")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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,8 +259,8 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
|
||||
std::memcpy(weights.data(), additive_weigths_,
|
||||
sizeof(additive_weigths_));
|
||||
|
||||
std::sprintf(label, "Weights\nCurl: %.2f\nSplay: %.2f",
|
||||
additive_weigths_[kCurl], additive_weigths_[kSplay]);
|
||||
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.
|
||||
std::memcpy(additive_weigths_, weights.data(),
|
||||
|
||||
+5
-5
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+21
-25
@@ -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",
|
||||
statistics.mean == 0.f ? 0.f : 1000.f / statistics.mean);
|
||||
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",
|
||||
fixed_update_rate);
|
||||
im_gui->DoSlider(sz_fixed_update_rate, 1.f, 200.f, &fixed_update_rate,
|
||||
.5f, true);
|
||||
std::snprintf(label, sizeof(label), "Update rate: %.0f fps",
|
||||
fixed_update_rate);
|
||||
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,
|
||||
resolution_.height);
|
||||
if (im_gui->DoSlider(szResolution, 0, kNumPresets - 1, &preset_lookup)) {
|
||||
std::snprintf(label, sizeof(label), "Resolution: %dx%d", resolution_.width,
|
||||
resolution_.height);
|
||||
if (im_gui->DoSlider(label, 0, kNumPresets - 1, &preset_lookup)) {
|
||||
// Resolution changed.
|
||||
resolution_ = resolution_presets[preset_lookup];
|
||||
glfwSetWindowSize(resolution_.width, resolution_.height);
|
||||
|
||||
+1
-1
@@ -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));
|
||||
|
||||
+22
-21
@@ -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;
|
||||
}
|
||||
@@ -328,7 +329,7 @@ bool LoadAnimation(const char* _filename,
|
||||
}
|
||||
|
||||
bool LoadRawAnimation(const char* _filename,
|
||||
ozz::animation::offline::RawAnimation* _animation) {
|
||||
ozz::animation::offline::RawAnimation* _animation) {
|
||||
assert(_filename && _animation);
|
||||
ozz::log::Out() << "Loading raw animation archive: " << _filename << "."
|
||||
<< std::endl;
|
||||
|
||||
+21
-21
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
+27
-25
@@ -312,29 +312,32 @@ 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",
|
||||
joint_setting_.tolerance * 1000);
|
||||
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",
|
||||
joint_setting_.distance * 1000);
|
||||
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",
|
||||
static_cast<int>(raw_animation_.size() >> 10));
|
||||
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)",
|
||||
static_cast<int>(raw_optimized_animation_.size() >> 10),
|
||||
static_cast<float>(raw_animation_.size()) /
|
||||
raw_optimized_animation_.size());
|
||||
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",
|
||||
*error_record_med_.cursor());
|
||||
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",
|
||||
*error_record_max_.cursor());
|
||||
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_,
|
||||
*joint_error_record_.cursor());
|
||||
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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,43 +426,24 @@ TEST(Twist, IKAimJob) {
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole y, twist pi
|
||||
{ // Pole y, twist pi / 2
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.twist_angle = ozz::math::kPi;
|
||||
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);
|
||||
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
|
||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
|
||||
}
|
||||
|
||||
{ // Pole y, twist -pi
|
||||
{ // Pole y, twist -pi / 2
|
||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||
job.twist_angle = -ozz::math::kPi;
|
||||
job.twist_angle = -ozz::math::kPi_2;
|
||||
EXPECT_TRUE(job.Run());
|
||||
const ozz::math::Quaternion x_mPi = ozz::math::Quaternion::FromAxisAngle(
|
||||
ozz::math::Float3::x_axis(), -ozz::math::kPi);
|
||||
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);
|
||||
}
|
||||
|
||||
{ // 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(
|
||||
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);
|
||||
}
|
||||
|
||||
{ // Pole z, twist pi/2
|
||||
job.pole_vector = ozz::math::simd_float4::z_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);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AlignedTargetUp, IKAimJob) {
|
||||
|
||||
@@ -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")
|
||||
|
||||
+16
-10
@@ -39,21 +39,25 @@ set(ThirdPartyIncludeDeps
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/sokol>
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/vectorial/include>
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
|
||||
)
|
||||
)
|
||||
|
||||
# Shared code by main executable and tests
|
||||
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
|
||||
@@ -104,7 +109,7 @@ target_sources(AnimTestbed PRIVATE
|
||||
3rdparty/imgui-node-editor/imgui_extra_math.inl
|
||||
3rdparty/imgui-node-editor/crude_json.cpp
|
||||
3rdparty/imgui-node-editor/crude_json.h
|
||||
)
|
||||
)
|
||||
|
||||
target_link_libraries(AnimTestbed AnimTestbedCode glfw ozz_base ozz_geometry ozz_animation ${OPENGL_LIBRARIES})
|
||||
|
||||
@@ -116,16 +121,17 @@ set(ozz_offline_test_objs
|
||||
3rdparty/ozz-animation/src/animation/offline/raw_skeleton.cc
|
||||
3rdparty/ozz-animation/src/animation/offline/animation_builder.cc
|
||||
3rdparty/ozz-animation/src/animation/offline/raw_animation.cc
|
||||
)
|
||||
)
|
||||
|
||||
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
|
||||
${ozz_offline_test_objs}
|
||||
)
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
runtests
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -402,7 +418,7 @@ struct NodeDescriptorBase {
|
||||
return socket->GetValue<T>();
|
||||
}
|
||||
|
||||
virtual void UpdateFlags(){};
|
||||
virtual void UpdateFlags() {};
|
||||
|
||||
protected:
|
||||
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
|
||||
@@ -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) {
|
||||
|
||||
+420
-388
@@ -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;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
void RemoveConnectionsForSocket(
|
||||
AnimGraphResource& graph_resource,
|
||||
AnimNodeResource& node_resource,
|
||||
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++;
|
||||
}
|
||||
|
||||
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(AnimGraph& anim_graph) {
|
||||
sGraphGresource.createInstance(anim_graph);
|
||||
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
|
||||
sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -4,17 +4,77 @@
|
||||
|
||||
#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);
|
||||
assert (i_input1 != nullptr);
|
||||
assert (i_blend_weight != nullptr);
|
||||
assert (o_output != nullptr);
|
||||
assert(i_input0 != nullptr);
|
||||
assert(i_input1 != nullptr);
|
||||
assert(i_blend_weight != nullptr);
|
||||
assert(o_output != nullptr);
|
||||
|
||||
// perform blend
|
||||
ozz::animation::BlendingJob::Layer layers[2];
|
||||
@@ -38,12 +98,10 @@ 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);
|
||||
assert(m_animation == nullptr);
|
||||
assert(!m_filename.empty());
|
||||
|
||||
AnimGraphContext::AnimationFileMap::const_iterator animation_map_iter;
|
||||
@@ -70,14 +128,14 @@ bool AnimSamplerNode::Init(AnimGraphContext& context) {
|
||||
context.m_animation_map[m_filename] = m_animation;
|
||||
}
|
||||
|
||||
assert (context.m_skeleton != nullptr);
|
||||
assert(context.m_skeleton != nullptr);
|
||||
m_sampling_context.Resize(context.m_skeleton->num_joints());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimSamplerNode::Evaluate(AnimGraphContext& context) {
|
||||
assert (o_output != nullptr);
|
||||
assert(o_output != nullptr);
|
||||
|
||||
ozz::animation::SamplingJob sampling_job;
|
||||
sampling_job.animation = m_animation;
|
||||
|
||||
+20
-144
@@ -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
|
||||
//
|
||||
@@ -155,8 +86,8 @@ struct SpeedScaleNode : public AnimNode {
|
||||
}
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input != nullptr);
|
||||
assert (o_output != nullptr);
|
||||
assert(i_input != nullptr);
|
||||
assert(o_output != nullptr);
|
||||
|
||||
o_output->m_local_matrices = i_input->m_local_matrices;
|
||||
};
|
||||
@@ -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
|
||||
//
|
||||
@@ -262,8 +188,8 @@ struct MathAddNode : public AnimNode {
|
||||
float* o_output = nullptr;
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input0 != nullptr);
|
||||
assert (i_input1 != nullptr);
|
||||
assert(i_input0 != nullptr);
|
||||
assert(i_input1 != nullptr);
|
||||
|
||||
*o_output = *i_input0 + *i_input1;
|
||||
}
|
||||
@@ -288,9 +214,9 @@ struct MathFloatToVec3Node : public AnimNode {
|
||||
Vec3* o_output = nullptr;
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input0 != nullptr);
|
||||
assert (i_input1 != nullptr);
|
||||
assert (i_input2 != nullptr);
|
||||
assert(i_input0 != nullptr);
|
||||
assert(i_input1 != nullptr);
|
||||
assert(i_input2 != nullptr);
|
||||
|
||||
o_output->v[0] = *i_input0;
|
||||
o_output->v[1] = *i_input1;
|
||||
@@ -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
|
||||
|
||||
+814
-199
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<Socket*> GetConstantNodeInputs(
|
||||
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
|
||||
std::vector<Socket*> result;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
if (source_socket == nullptr || target_socket == nullptr) {
|
||||
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
input_node->m_socket_accessor->m_outputs.push_back(socket);
|
||||
m_socket_accessor->m_inputs = input_node->m_socket_accessor->m_outputs;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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 false;
|
||||
return RegisterBlendTreeOutputSocket(socket);
|
||||
}
|
||||
|
||||
void createInstance(AnimGraph& result) const;
|
||||
bool RegisterBlendTreeOutputSocket(const Socket& socket) {
|
||||
AnimNodeResource* output_node = m_blend_tree_resource.GetGraphOutputNode();
|
||||
|
||||
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::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
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Created by martin on 17.03.24.
|
||||
//
|
||||
|
||||
#include "AnimNode.h"
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+73
-103
@@ -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};
|
||||
@@ -184,7 +178,7 @@ struct ApplicationConfig {
|
||||
ax::NodeEditor::EditorContext* context = nullptr;
|
||||
};
|
||||
GraphEditor graph_editor;
|
||||
|
||||
|
||||
struct SkinnedMeshWidget {
|
||||
bool visible = false;
|
||||
int position[2] = {20, 20};
|
||||
@@ -221,18 +215,12 @@ 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];
|
||||
j["graph_editor"]["size"][0] = config.graph_editor.size[0];
|
||||
j["graph_editor"]["size"][1] = config.graph_editor.size[1];
|
||||
|
||||
|
||||
j["skinned_mesh_widget"]["visible"] = config.skinned_mesh_widget.visible;
|
||||
j["skinned_mesh_widget"]["position"][0] =
|
||||
config.skinned_mesh_widget.position[0];
|
||||
@@ -278,29 +266,11 @@ 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"];
|
||||
}
|
||||
|
||||
|
||||
if (j["graph_editor"].contains("position")
|
||||
and j["graph_editor"]["position"].size() == 2) {
|
||||
config.graph_editor.position[0] = j["graph_editor"]["position"].at(0);
|
||||
@@ -313,7 +283,7 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) {
|
||||
config.graph_editor.size[1] = j["graph_editor"]["size"].at(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (j.contains("skinned_mesh_widget")) {
|
||||
if (j["skinned_mesh_widget"].contains("visible")) {
|
||||
config.skinned_mesh_widget.visible = j["skinned_mesh_widget"]["visible"];
|
||||
@@ -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,12 +793,12 @@ 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();
|
||||
for (const auto & output : graph_output_sockets) {
|
||||
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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 =
|
||||
|
||||
+1130
-358
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user