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