Compare commits
No commits in common. "ccb9bc4e9b795ba6d82b458b5809fce7bec91c49" and "1ef53d6486d68e533bfdad8d5f020969a66f5261" have entirely different histories.
ccb9bc4e9b
...
1ef53d6486
16
3rdparty/ozz-animation/CHANGES.md
vendored
16
3rdparty/ozz-animation/CHANGES.md
vendored
@ -1,19 +1,3 @@
|
|||||||
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
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
10
3rdparty/ozz-animation/CMakeLists.txt
vendored
10
3rdparty/ozz-animation/CMakeLists.txt
vendored
@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 3.24)
|
cmake_minimum_required (VERSION 3.3)
|
||||||
|
|
||||||
# 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 3)
|
set(OZZ_VERSION_PATCH 1)
|
||||||
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,7 +30,6 @@ 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()
|
||||||
@ -52,6 +51,7 @@ 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,7 +69,6 @@ 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()
|
||||||
@ -79,7 +78,6 @@ 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()
|
||||||
@ -133,4 +131,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 "share/doc/ozz-animation/")
|
DESTINATION ./)
|
||||||
|
2
3rdparty/ozz-animation/README.md
vendored
2
3rdparty/ozz-animation/README.md
vendored
@ -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, on the C and the C++ standard libraries, 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, the standard CRT 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,14 +26,13 @@ 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)
|
||||||
|
|
||||||
@ -42,15 +41,15 @@ if(ozz_build_simd_ref)
|
|||||||
add_compile_definitions(OZZ_BUILD_SIMD_REF)
|
add_compile_definitions(OZZ_BUILD_SIMD_REF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# --------------------------------------
|
|
||||||
# Modify default MSVC compilation flags
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
|
||||||
# ---------------------------
|
|
||||||
# For the common build flags
|
|
||||||
|
|
||||||
# Disables crt secure warnings
|
# Disables crt secure warnings
|
||||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||||
|
|
||||||
|
#--------------------------------------
|
||||||
|
# Modify default MSVC compilation flags
|
||||||
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
|
#---------------------------
|
||||||
|
# For the common build flags
|
||||||
|
|
||||||
# Adds support for multiple processes builds
|
# Adds support for multiple processes builds
|
||||||
add_compile_options(/MP)
|
add_compile_options(/MP)
|
||||||
|
|
||||||
@ -61,61 +60,62 @@ 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.
|
||||||
if(ozz_build_msvc_rt_dll)
|
foreach(flag ${cxx_all_flags})
|
||||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
|
if (ozz_build_msvc_rt_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,7 +124,6 @@ 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()
|
||||||
@ -133,14 +132,13 @@ 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 ".")
|
||||||
@ -148,7 +146,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,9 +67,7 @@ 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.30)
|
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20)
|
||||||
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")
|
||||||
|
@ -57,6 +57,8 @@ 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
|
||||||
|
@ -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];
|
||||||
snprintf(formatString, sizeof(formatString), "%%.%dg", precision);
|
sprintf(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
|
||||||
|
@ -66,11 +66,12 @@ 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.
|
||||||
OZZ_INLINE void _in_place_byte_swap(byte& _a, byte& _b) {
|
#define OZZ_BYTE_SWAP(_a, _b) \
|
||||||
_a = _a ^ _b;
|
do { \
|
||||||
_b = _a ^ _b;
|
const ozz::byte temp = (_a); \
|
||||||
_a = _a ^ _b;
|
(_a) = (_b); \
|
||||||
}
|
(_b) = temp; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
// EndianSwapper specialization for 1 byte types.
|
// EndianSwapper specialization for 1 byte types.
|
||||||
template <typename _Ty>
|
template <typename _Ty>
|
||||||
@ -85,12 +86,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) {
|
||||||
_in_place_byte_swap(alias[i + 0], alias[i + 1]);
|
OZZ_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);
|
||||||
_in_place_byte_swap(alias[0], alias[1]);
|
OZZ_BYTE_SWAP(alias[0], alias[1]);
|
||||||
return _ty;
|
return _ty;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -101,14 +102,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) {
|
||||||
_in_place_byte_swap(alias[i + 0], alias[i + 3]);
|
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 3]);
|
||||||
_in_place_byte_swap(alias[i + 1], alias[i + 2]);
|
OZZ_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);
|
||||||
_in_place_byte_swap(alias[0], alias[3]);
|
OZZ_BYTE_SWAP(alias[0], alias[3]);
|
||||||
_in_place_byte_swap(alias[1], alias[2]);
|
OZZ_BYTE_SWAP(alias[1], alias[2]);
|
||||||
return _ty;
|
return _ty;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -119,22 +120,25 @@ 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) {
|
||||||
_in_place_byte_swap(alias[i + 0], alias[i + 7]);
|
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 7]);
|
||||||
_in_place_byte_swap(alias[i + 1], alias[i + 6]);
|
OZZ_BYTE_SWAP(alias[i + 1], alias[i + 6]);
|
||||||
_in_place_byte_swap(alias[i + 2], alias[i + 5]);
|
OZZ_BYTE_SWAP(alias[i + 2], alias[i + 5]);
|
||||||
_in_place_byte_swap(alias[i + 3], alias[i + 4]);
|
OZZ_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);
|
||||||
_in_place_byte_swap(alias[0], alias[7]);
|
OZZ_BYTE_SWAP(alias[0], alias[7]);
|
||||||
_in_place_byte_swap(alias[1], alias[6]);
|
OZZ_BYTE_SWAP(alias[1], alias[6]);
|
||||||
_in_place_byte_swap(alias[2], alias[5]);
|
OZZ_BYTE_SWAP(alias[2], alias[5]);
|
||||||
_in_place_byte_swap(alias[3], alias[4]);
|
OZZ_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) {
|
||||||
|
@ -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::snprintf(label, sizeof(label), "Layer weight: %.2f", base_weight_);
|
std::sprintf(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,7 +259,7 @@ 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::snprintf(label, sizeof(label), "Weights\nCurl: %.2f\nSplay: %.2f",
|
std::sprintf(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.
|
||||||
|
@ -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::snprintf(label, sizeof(label), "%s (%d)",
|
std::sprintf(label, "%s (%d)", skeleton_.joint_names()[attachment_],
|
||||||
skeleton_.joint_names()[attachment_], 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:");
|
||||||
std::snprintf(label, sizeof(label), "x: %02f", offset_.x);
|
sprintf(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);
|
||||||
std::snprintf(label, sizeof(label), "y: %02f", offset_.y);
|
sprintf(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);
|
||||||
std::snprintf(label, sizeof(label), "z: %02f", offset_.z);
|
sprintf(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 third animation (ozz archive format).",
|
"Path to the second 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::snprintf(label, sizeof(label), "Blend ratio: %.2f", blend_ratio_);
|
std::sprintf(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::snprintf(label, sizeof(label), "Weight %d: %.2f", i, sampler.weight);
|
std::sprintf(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::snprintf(label, sizeof(label), "Threshold: %.2f", threshold_);
|
std::sprintf(label, "Threshold: %.2f", threshold_);
|
||||||
_im_gui->DoSlider(label, .01f, 1.f, &threshold_);
|
_im_gui->DoSlider(label, .01f, 1.f, &threshold_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 label[32];
|
char txt[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) {
|
||||||
snprintf(label, sizeof(label), "Foot height %.2g", foot_heigh_);
|
sprintf(txt, "Foot height %.2g", foot_heigh_);
|
||||||
_im_gui->DoSlider(label, 0.f, .3f, &foot_heigh_);
|
_im_gui->DoSlider(txt, 0.f, .3f, &foot_heigh_);
|
||||||
snprintf(label, sizeof(label), "Weight %.2g", weight_);
|
sprintf(txt, "Weight %.2g", weight_);
|
||||||
_im_gui->DoSlider(label, 0.f, 1.f, &weight_);
|
_im_gui->DoSlider(txt, 0.f, 1.f, &weight_);
|
||||||
snprintf(label, sizeof(label), "Soften %.2g", soften_);
|
sprintf(txt, "Soften %.2g", soften_);
|
||||||
_im_gui->DoSlider(label, 0.f, 1.f, &soften_, 1.f, two_bone_ik_);
|
_im_gui->DoSlider(txt, 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");
|
||||||
snprintf(label, sizeof(label), "x %.2g", root_translation_.x);
|
sprintf(txt, "x %.2g", root_translation_.x);
|
||||||
moved |= _im_gui->DoSlider(label, -10.f, 10.f, &root_translation_.x);
|
moved |= _im_gui->DoSlider(txt, -10.f, 10.f, &root_translation_.x);
|
||||||
snprintf(label, sizeof(label), "y %.2g", root_translation_.y);
|
sprintf(txt, "y %.2g", root_translation_.y);
|
||||||
moved |= _im_gui->DoSlider(label, 0.f, 5.f, &root_translation_.y, 1.f,
|
moved |= _im_gui->DoSlider(txt, 0.f, 5.f, &root_translation_.y, 1.f,
|
||||||
!auto_character_height_);
|
!auto_character_height_);
|
||||||
snprintf(label, sizeof(label), "z %.2g", root_translation_.z);
|
sprintf(txt, "z %.2g", root_translation_.z);
|
||||||
moved |= _im_gui->DoSlider(label, -10.f, 10.f, &root_translation_.z);
|
moved |= _im_gui->DoSlider(txt, -10.f, 10.f, &root_translation_.z);
|
||||||
|
|
||||||
// Rotation (in euler form)
|
// Rotation (in euler form)
|
||||||
_im_gui->DoLabel("Rotation");
|
_im_gui->DoLabel("Rotation");
|
||||||
snprintf(label, sizeof(label), "yaw %.3g", root_yaw_ * ozz::math::kRadianToDegree);
|
sprintf(txt, "yaw %.3g", root_yaw_ * ozz::math::kRadianToDegree);
|
||||||
moved |=
|
moved |=
|
||||||
_im_gui->DoSlider(label, -ozz::math::kPi, ozz::math::kPi, &root_yaw_);
|
_im_gui->DoSlider(txt, -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
|
||||||
|
@ -528,32 +528,31 @@ 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::snprintf(label, sizeof(label), "FPS: %.0f",
|
std::sprintf(szLabel, "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, label, &fps_open);
|
ImGui::OpenClose stats(im_gui, szLabel, &fps_open);
|
||||||
if (fps_open) {
|
if (fps_open) {
|
||||||
std::snprintf(label, sizeof(label), "Frame: %.2f ms",
|
std::sprintf(szLabel, "Frame: %.2f ms", statistics.mean);
|
||||||
statistics.mean);
|
im_gui->DoGraph(szLabel, 0.f, statistics.max, statistics.latest,
|
||||||
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::snprintf(label, sizeof(label), "Update: %.2f ms", statistics.mean);
|
std::sprintf(szLabel, "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, label, &update_open);
|
ImGui::OpenClose stats(im_gui, szLabel, &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(),
|
||||||
@ -562,9 +561,9 @@ bool Application::FrameworkGui() {
|
|||||||
}
|
}
|
||||||
{ // Render time
|
{ // Render time
|
||||||
Record::Statistics statistics = render_time_->GetStatistics();
|
Record::Statistics statistics = render_time_->GetStatistics();
|
||||||
std::snprintf(label, sizeof(label), "Render: %.2f ms", statistics.mean);
|
std::sprintf(szLabel, "Render: %.2f ms", statistics.mean);
|
||||||
static bool render_open = false;
|
static bool render_open = false;
|
||||||
ImGui::OpenClose stats(im_gui, label, &render_open);
|
ImGui::OpenClose stats(im_gui, szLabel, &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(),
|
||||||
@ -581,15 +580,18 @@ 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) {
|
||||||
std::snprintf(label, sizeof(label), "Time factor: %.2f", time_factor_);
|
char sz_factor[64];
|
||||||
im_gui->DoSlider(label, -5.f, 5.f, &time_factor_);
|
std::sprintf(sz_factor, "Time factor: %.2f", 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 {
|
||||||
std::snprintf(label, sizeof(label), "Update rate: %.0f fps",
|
char sz_fixed_update_rate[64];
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
@ -613,9 +615,10 @@ 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_);
|
||||||
std::snprintf(label, sizeof(label), "Swap interval: %d", swap_interval_);
|
char szLabel[64];
|
||||||
|
std::sprintf(szLabel, "Swap interval: %d", swap_interval_);
|
||||||
changed |=
|
changed |=
|
||||||
im_gui->DoSlider(label, 1, 4, &swap_interval_, 1.f, vertical_sync_);
|
im_gui->DoSlider(szLabel, 1, 4, &swap_interval_, 1.f, vertical_sync_);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
glfwSwapInterval(vertical_sync_ ? swap_interval_ : 0);
|
glfwSwapInterval(vertical_sync_ ? swap_interval_ : 0);
|
||||||
}
|
}
|
||||||
@ -637,9 +640,10 @@ bool Application::FrameworkGui() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::snprintf(label, sizeof(label), "Resolution: %dx%d", resolution_.width,
|
char szResolution[64];
|
||||||
|
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);
|
||||||
|
@ -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 because something went wrong.
|
kBreakFailure, // // Should stop looping beacause 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::snprintf(_string, _string_end - _string, "%.2g\n", _value);
|
std::sprintf(_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 filename[16];
|
char name[16];
|
||||||
std::snprintf(filename, sizeof(filename), "%06d.tga", shot_number_++);
|
sprintf(name, "%06d.tga", shot_number_++);
|
||||||
|
|
||||||
ozz::sample::image::WriteTGA(
|
ozz::sample::image::WriteTGA(name, shot.width, shot.height, image_format_,
|
||||||
filename, shot.width, shot.height, image_format_,
|
reinterpret_cast<const uint8_t*>(pixels),
|
||||||
reinterpret_cast<const uint8_t*>(pixels), false);
|
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));
|
||||||
|
@ -110,22 +110,21 @@ bool PlaybackController::OnGui(const animation::Animation& _animation,
|
|||||||
|
|
||||||
_im_gui->DoCheckBox("Loop", &loop_, _enabled);
|
_im_gui->DoCheckBox("Loop", &loop_, _enabled);
|
||||||
|
|
||||||
char label[64];
|
char szLabel[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::snprintf(label, sizeof(label), "Animation time: %.2f",
|
std::sprintf(szLabel, "Animation time: %.2f", ratio * _animation.duration());
|
||||||
ratio * _animation.duration());
|
if (_im_gui->DoSlider(szLabel, 0.f, 1.f, &ratio, 1.f,
|
||||||
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::snprintf(label, sizeof(label), "Playback speed: %.2f", playback_speed_);
|
std::sprintf(szLabel, "Playback speed: %.2f", playback_speed_);
|
||||||
_im_gui->DoSlider(label, -5.f, 5.f, &playback_speed_, 1.f, _enabled);
|
_im_gui->DoSlider(szLabel, -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",
|
||||||
@ -140,7 +139,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 label[255];
|
char txt[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) {
|
||||||
@ -153,23 +152,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");
|
||||||
snprintf(label, sizeof(label), "x %.2g", translation.x);
|
sprintf(txt, "x %.2g", translation.x);
|
||||||
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.x);
|
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.x);
|
||||||
snprintf(label, sizeof(label), "y %.2g", translation.y);
|
sprintf(txt, "y %.2g", translation.y);
|
||||||
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.y);
|
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.y);
|
||||||
snprintf(label, sizeof(label), "z %.2g", translation.z);
|
sprintf(txt, "z %.2g", translation.z);
|
||||||
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.z);
|
modified |= _im_gui->DoSlider(txt, -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;
|
||||||
snprintf(label, sizeof(label), "x %.3g", euler.x);
|
sprintf(txt, "x %.3g", euler.x);
|
||||||
bool euler_modified = _im_gui->DoSlider(label, -180.f, 180.f, &euler.x);
|
bool euler_modified = _im_gui->DoSlider(txt, -180.f, 180.f, &euler.x);
|
||||||
snprintf(label, sizeof(label), "y %.3g", euler.y);
|
sprintf(txt, "y %.3g", euler.y);
|
||||||
euler_modified |= _im_gui->DoSlider(label, -180.f, 180.f, &euler.y);
|
euler_modified |= _im_gui->DoSlider(txt, -180.f, 180.f, &euler.y);
|
||||||
snprintf(label, sizeof(label), "z %.3g", euler.z);
|
sprintf(txt, "z %.3g", euler.z);
|
||||||
euler_modified |= _im_gui->DoSlider(label, -180.f, 180.f, &euler.z);
|
euler_modified |= _im_gui->DoSlider(txt, -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;
|
||||||
@ -180,8 +179,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;
|
||||||
snprintf(label, sizeof(label), "%.2g", scale.x);
|
sprintf(txt, "%.2g", scale.x);
|
||||||
if (_im_gui->DoSlider(label, -1.f, 1.f, &scale.x)) {
|
if (_im_gui->DoSlider(txt, -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;
|
||||||
}
|
}
|
||||||
|
@ -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 label[64];
|
char txt[64];
|
||||||
|
|
||||||
_im_gui->DoCheckBox("Enable ik", &enable_ik_);
|
_im_gui->DoCheckBox("Enable ik", &enable_ik_);
|
||||||
snprintf(label, sizeof(label), "IK chain length: %d", chain_length_);
|
sprintf(txt, "IK chain length: %d", chain_length_);
|
||||||
_im_gui->DoSlider(label, 0, kMaxChainLength, &chain_length_);
|
_im_gui->DoSlider(txt, 0, kMaxChainLength, &chain_length_);
|
||||||
snprintf(label, sizeof(label), "Joint weight %.2g", joint_weight_);
|
sprintf(txt, "Joint weight %.2g", joint_weight_);
|
||||||
_im_gui->DoSlider(label, 0.f, 1.f, &joint_weight_);
|
_im_gui->DoSlider(txt, 0.f, 1.f, &joint_weight_);
|
||||||
snprintf(label, sizeof(label), "Chain weight %.2g", chain_weight_);
|
sprintf(txt, "Chain weight %.2g", chain_weight_);
|
||||||
_im_gui->DoSlider(label, 0.f, 1.f, &chain_weight_);
|
_im_gui->DoSlider(txt, 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");
|
||||||
snprintf(label, sizeof(label), "%.2g", target_extent_);
|
sprintf(txt, "%.2g", target_extent_);
|
||||||
_im_gui->DoSlider(label, 0.f, kTargetRange, &target_extent_);
|
_im_gui->DoSlider(txt, 0.f, kTargetRange, &target_extent_);
|
||||||
|
|
||||||
snprintf(label, sizeof(label), "x %.2g", target_offset_.x);
|
sprintf(txt, "x %.2g", target_offset_.x);
|
||||||
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.x);
|
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.x);
|
||||||
snprintf(label, sizeof(label), "y %.2g", target_offset_.y);
|
sprintf(txt, "y %.2g", target_offset_.y);
|
||||||
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.y);
|
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.y);
|
||||||
snprintf(label, sizeof(label), "z %.2g", target_offset_.z);
|
sprintf(txt, "z %.2g", target_offset_.z);
|
||||||
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.z);
|
_im_gui->DoSlider(txt, -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;
|
||||||
snprintf(label, sizeof(label), "x %.2g", eyes_offset_.x);
|
sprintf(txt, "x %.2g", eyes_offset_.x);
|
||||||
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.x);
|
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.x);
|
||||||
snprintf(label, sizeof(label), "y %.2g", eyes_offset_.y);
|
sprintf(txt, "y %.2g", eyes_offset_.y);
|
||||||
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.y);
|
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.y);
|
||||||
snprintf(label, sizeof(label), "z %.2g", eyes_offset_.z);
|
sprintf(txt, "z %.2g", eyes_offset_.z);
|
||||||
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.z);
|
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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::snprintf(label, sizeof(label), "Joints count: %d", joints);
|
std::sprintf(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 number[16];
|
char buf[16];
|
||||||
for (int i = 0; i < slice_count_; ++i) {
|
for (int i = 0; i < slice_count_; ++i) {
|
||||||
// Format joint number.
|
// Format joint number.
|
||||||
std::snprintf(number, sizeof(number), "%d", i);
|
std::sprintf(buf, "%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 += number;
|
lu.name += buf;
|
||||||
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 += number;
|
ld.name += buf;
|
||||||
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 += number;
|
lf.name += buf;
|
||||||
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 += number;
|
ru.name += buf;
|
||||||
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 += number;
|
rd.name += buf;
|
||||||
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 += number;
|
rf.name += buf;
|
||||||
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 += number;
|
sp.name += buf;
|
||||||
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::snprintf(label, sizeof(label), "Number of entities: %d", num_characters_);
|
std::sprintf(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::snprintf(label, sizeof(label), "Number of joints: %d", num_joints);
|
std::sprintf(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::snprintf(label, sizeof(label), "Grain size: %d", grain_size_);
|
std::sprintf(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::snprintf(label, sizeof(label), "Thread/task count: %d/%d", num_threads,
|
std::sprintf(label, "Thread/task count: %d/%d", num_threads,
|
||||||
monitor_.TaskCount());
|
monitor_.TaskCount());
|
||||||
_im_gui->DoLabel(label);
|
_im_gui->DoLabel(label);
|
||||||
}
|
}
|
||||||
|
@ -312,31 +312,28 @@ class OptimizeSampleApplication : public ozz::sample::Application {
|
|||||||
|
|
||||||
rebuild |= _im_gui->DoCheckBox("Enable optimizations", &optimize_);
|
rebuild |= _im_gui->DoCheckBox("Enable optimizations", &optimize_);
|
||||||
|
|
||||||
std::snprintf(label, sizeof(label), "Tolerance: %0.2f mm",
|
std::sprintf(label, "Tolerance: %0.2f mm", setting_.tolerance * 1000);
|
||||||
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::snprintf(label, sizeof(label), "Distance: %0.2f mm",
|
std::sprintf(label, "Distance: %0.2f mm", setting_.distance * 1000);
|
||||||
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::snprintf(label, sizeof(label), "%s (%d)",
|
std::sprintf(label, "%s (%d)", skeleton_.joint_names()[joint_], joint_);
|
||||||
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::snprintf(label, sizeof(label), "Tolerance: %0.2f mm",
|
std::sprintf(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::snprintf(label, sizeof(label), "Distance: %0.2f mm",
|
std::sprintf(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_);
|
||||||
@ -359,18 +356,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::snprintf(label, sizeof(label), "Original: %dKB",
|
std::sprintf(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::snprintf(label, sizeof(label), "Optimized: %dKB (%.1f:1)",
|
std::sprintf(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::snprintf(
|
std::sprintf(
|
||||||
label, sizeof(label), "Compressed: %dKB (%.1f:1)",
|
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);
|
||||||
@ -391,36 +388,37 @@ 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::snprintf(label, sizeof(label), "Median error: %.2fmm",
|
std::sprintf(szLabel, "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(label, 0.f, error_stats.max, error_stats.latest,
|
_im_gui->DoGraph(szLabel, 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::snprintf(label, sizeof(label), "Maximum error: %.2fmm",
|
std::sprintf(szLabel, "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(label, 0.f, error_stats.max, error_stats.latest,
|
_im_gui->DoGraph(szLabel, 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::snprintf(label, sizeof(label), "Joint %d error: %.2fmm", joint_,
|
std::sprintf(szLabel, "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(label, 0.f, error_stats.max, error_stats.latest,
|
_im_gui->DoGraph(szLabel, 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::snprintf(label, sizeof(label), "Upper body weight: %.2f", coeff);
|
std::sprintf(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::snprintf(label, sizeof(label), "Layer weight: %.2f",
|
std::sprintf(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::snprintf(label, sizeof(label), "Joints weight: %.2f",
|
std::sprintf(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::snprintf(label, sizeof(label), "Layer weight: %.2f",
|
std::sprintf(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::snprintf(label, sizeof(label), "Joints weight: %.2f",
|
std::sprintf(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::snprintf(label, sizeof(label), "Threshold: %.2f", threshold_);
|
std::sprintf(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::snprintf(label, sizeof(label), "%s (%d)",
|
std::sprintf(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,
|
||||||
|
@ -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];
|
||||||
std::snprintf(label, sizeof(label), "%d animated joints", skeleton_.num_joints());
|
sprintf(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());
|
||||||
}
|
}
|
||||||
std::snprintf(label, sizeof(label), "%d influences (max)", influences);
|
sprintf(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();
|
||||||
}
|
}
|
||||||
std::snprintf(label, sizeof(label), "%.1fK vertices", vertices / 1000.f);
|
sprintf(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();
|
||||||
}
|
}
|
||||||
std::snprintf(label, sizeof(label), "%.1fK triangles", indices / 3000.f);
|
sprintf(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 label[32];
|
char txt[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) {
|
||||||
snprintf(label, sizeof(label), "Soften: %.2g", soften_);
|
sprintf(txt, "Soften: %.2g", soften_);
|
||||||
_im_gui->DoSlider(label, 0.f, 1.f, &soften_, 2.f);
|
_im_gui->DoSlider(txt, 0.f, 1.f, &soften_, 2.f);
|
||||||
snprintf(label, sizeof(label), "Twist angle: %.0f",
|
sprintf(txt, "Twist angle: %.0f",
|
||||||
twist_angle_ * ozz::math::kRadianToDegree);
|
twist_angle_ * ozz::math::kRadianToDegree);
|
||||||
_im_gui->DoSlider(label, -ozz::math::kPi, ozz::math::kPi, &twist_angle_);
|
_im_gui->DoSlider(txt, -ozz::math::kPi, ozz::math::kPi, &twist_angle_);
|
||||||
snprintf(label, sizeof(label), "Weight: %.2g", weight_);
|
sprintf(txt, "Weight: %.2g", weight_);
|
||||||
_im_gui->DoSlider(label, 0.f, 1.f, &weight_);
|
_im_gui->DoSlider(txt, 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) {
|
||||||
snprintf(label, sizeof(label), "x %.2g", pole_vector.x);
|
sprintf(txt, "x %.2g", pole_vector.x);
|
||||||
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.x);
|
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.x);
|
||||||
snprintf(label, sizeof(label), "y %.2g", pole_vector.y);
|
sprintf(txt, "y %.2g", pole_vector.y);
|
||||||
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.y);
|
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.y);
|
||||||
snprintf(label, sizeof(label), "z %.2g", pole_vector.z);
|
sprintf(txt, "z %.2g", pole_vector.z);
|
||||||
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.z);
|
_im_gui->DoSlider(txt, -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");
|
||||||
snprintf(label, sizeof(label), "%.2g", target_extent_);
|
sprintf(txt, "%.2g", target_extent_);
|
||||||
_im_gui->DoSlider(label, 0.f, 1.f, &target_extent_);
|
_im_gui->DoSlider(txt, 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;
|
||||||
snprintf(label, sizeof(label), "x %.2g", target_offset_.x);
|
sprintf(txt, "x %.2g", target_offset_.x);
|
||||||
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.x);
|
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.x);
|
||||||
snprintf(label, sizeof(label), "y %.2g", target_offset_.y);
|
sprintf(txt, "y %.2g", target_offset_.y);
|
||||||
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.y);
|
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.y);
|
||||||
snprintf(label, sizeof(label), "z %.2g", target_offset_.z);
|
sprintf(txt, "z %.2g", target_offset_.z);
|
||||||
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.z);
|
_im_gui->DoSlider(txt, -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");
|
||||||
snprintf(label, sizeof(label), "x %.2g", root_translation_.x);
|
sprintf(txt, "x %.2g", root_translation_.x);
|
||||||
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.x);
|
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.x);
|
||||||
snprintf(label, sizeof(label), "y %.2g", root_translation_.y);
|
sprintf(txt, "y %.2g", root_translation_.y);
|
||||||
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.y);
|
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.y);
|
||||||
snprintf(label, sizeof(label), "z %.2g", root_translation_.z);
|
sprintf(txt, "z %.2g", root_translation_.z);
|
||||||
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.z);
|
_im_gui->DoSlider(txt, -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;
|
||||||
snprintf(label, sizeof(label), "yaw %.3g", euler.x);
|
sprintf(txt, "yaw %.3g", euler.x);
|
||||||
_im_gui->DoSlider(label, -180.f, 180.f, &euler.x);
|
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.x);
|
||||||
snprintf(label, sizeof(label), "pitch %.3g", euler.y);
|
sprintf(txt, "pitch %.3g", euler.y);
|
||||||
_im_gui->DoSlider(label, -180.f, 180.f, &euler.y);
|
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.y);
|
||||||
snprintf(label, sizeof(label), "roll %.3g", euler.z);
|
sprintf(txt, "roll %.3g", euler.z);
|
||||||
_im_gui->DoSlider(label, -180.f, 180.f, &euler.z);
|
_im_gui->DoSlider(txt, -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");
|
||||||
snprintf(label, sizeof(label), "%.2g", root_scale_);
|
sprintf(txt, "%.2g", root_scale_);
|
||||||
_im_gui->DoSlider(label, -1.f, 1.f, &root_scale_);
|
_im_gui->DoSlider(txt, -1.f, 1.f, &root_scale_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{ // Display options
|
{ // Display options
|
||||||
|
@ -62,7 +62,6 @@ 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,13 +25,15 @@
|
|||||||
// //
|
// //
|
||||||
//----------------------------------------------------------------------------//
|
//----------------------------------------------------------------------------//
|
||||||
|
|
||||||
#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;
|
||||||
@ -426,24 +428,43 @@ 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 / 2
|
{ // Pole y, twist pi
|
||||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||||
job.twist_angle = ozz::math::kPi_2;
|
job.twist_angle = ozz::math::kPi;
|
||||||
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_2);
|
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);
|
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // Pole y, twist -pi / 2
|
{ // Pole y, twist -pi
|
||||||
job.pole_vector = ozz::math::simd_float4::y_axis();
|
job.pole_vector = ozz::math::simd_float4::y_axis();
|
||||||
job.twist_angle = -ozz::math::kPi_2;
|
job.twist_angle = -ozz::math::kPi;
|
||||||
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_2);
|
ozz::math::Float3::x_axis(), -ozz::math::kPi);
|
||||||
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi.x, x_mPi.y, x_mPi.z, x_mPi.w,
|
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,8 +3,6 @@ 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")
|
||||||
@ -13,8 +11,6 @@ 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")
|
||||||
|
@ -46,22 +46,14 @@ add_library(AnimTestbedCode OBJECT
|
|||||||
src/SyncTrack.cc
|
src/SyncTrack.cc
|
||||||
src/SyncTrack.h
|
src/SyncTrack.h
|
||||||
src/ozzutils.cc
|
src/ozzutils.cc
|
||||||
# src/AnimGraph/AnimGraphBlendTreeResource.cc
|
src/AnimGraph/AnimGraphResource.cc
|
||||||
# src/AnimGraph/AnimGraphBlendTreeResource.h
|
src/AnimGraph/AnimGraphResource.h
|
||||||
src/AnimGraph/AnimGraph.cc
|
src/AnimGraph/AnimGraph.cc
|
||||||
src/AnimGraph/AnimGraph.h
|
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
|
||||||
@ -128,7 +120,7 @@ set(ozz_offline_test_objs
|
|||||||
|
|
||||||
target_sources(runtests PRIVATE
|
target_sources(runtests PRIVATE
|
||||||
tests/AnimGraphResourceTests.cc
|
tests/AnimGraphResourceTests.cc
|
||||||
# tests/AnimGraphEvalTests.cc
|
tests/AnimGraphEvalTests.cc
|
||||||
tests/NodeDescriptorTests.cc
|
tests/NodeDescriptorTests.cc
|
||||||
tests/SyncTrackTests.cc
|
tests/SyncTrackTests.cc
|
||||||
tests/main.cc
|
tests/main.cc
|
||||||
|
@ -1,3 +1,202 @@
|
|||||||
//
|
//
|
||||||
// Created by martin on 25.03.22.
|
// 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;
|
||||||
|
}
|
@ -5,34 +5,227 @@
|
|||||||
#ifndef ANIMTESTBED_ANIMGRAPH_H
|
#ifndef ANIMTESTBED_ANIMGRAPH_H
|
||||||
#define ANIMTESTBED_ANIMGRAPH_H
|
#define ANIMTESTBED_ANIMGRAPH_H
|
||||||
|
|
||||||
#include "AnimNode.h"
|
|
||||||
#include "AnimGraphData.h"
|
#include "AnimGraphData.h"
|
||||||
|
#include "AnimGraphNodes.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// AnimGraph (Runtime)
|
// AnimGraph (Runtime)
|
||||||
//
|
//
|
||||||
struct AnimGraph {
|
struct AnimGraph {
|
||||||
~AnimGraph() {}
|
AnimData m_local_transforms;
|
||||||
|
|
||||||
bool Init(AnimGraphContext& context) {
|
std::vector<AnimNode*> m_nodes;
|
||||||
m_root_node->Init(context);
|
std::vector<AnimNode*> m_eval_ordered_nodes;
|
||||||
}
|
std::vector<std::vector<AnimGraphConnection> > m_node_input_connections;
|
||||||
void UpdateTime(float dt) {
|
std::vector<std::vector<AnimGraphConnection> > m_node_output_connections;
|
||||||
m_time_last = m_time_now;
|
std::vector<AnimData*> m_animdata_blocks;
|
||||||
m_time_now = m_time_now + dt;
|
NodeDescriptorBase* m_node_descriptor = nullptr;
|
||||||
m_root_node->UpdateTime(m_time_last, m_time_now);
|
char* m_input_buffer = nullptr;
|
||||||
}
|
char* m_output_buffer = nullptr;
|
||||||
void Evaluate(AnimGraphContext& context) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimNode* m_root_node = nullptr;
|
void updateOrderedNodes();
|
||||||
|
void updateOrderedNodesRecursive(int node_index);
|
||||||
|
void markActiveNodes();
|
||||||
|
bool checkIsNodeActive(AnimNode* node) {
|
||||||
|
return node->m_state != AnimNodeEvalState::Deactivated;
|
||||||
|
}
|
||||||
|
|
||||||
float m_time_now = 0.f;
|
void evalSyncTracks();
|
||||||
float m_time_last = 0.f;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vec3 m_root_bone_translation = {};
|
Socket* getInputSocket(const std::string& name);
|
||||||
Quat m_root_bone_rotation = {};
|
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
|
#endif //ANIMTESTBED_ANIMGRAPH_H
|
||||||
|
@ -1,201 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 17.03.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "AnimGraphBlendTree.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
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++) {
|
|
||||||
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 AnimGraphBlendTree::MarkActiveInputs() {
|
|
||||||
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)) {
|
|
||||||
node->MarkActiveInputs();
|
|
||||||
size_t node_index = GetAnimNodeIndex(node);
|
|
||||||
|
|
||||||
// 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 AnimGraphBlendTree::CalcSyncTrack() {
|
|
||||||
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::Deactivated) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
node->CalcSyncTrack();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Socket* AnimGraphBlendTree::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* AnimGraphBlendTree::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* AnimGraphBlendTree::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* AnimGraphBlendTree::getOutputSocket(const std::string& name) const {
|
|
||||||
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
|
|
||||||
const AnimGraphConnection& connection = m_node_input_connections[0][i];
|
|
||||||
if (connection.m_target_socket.m_name == name) {
|
|
||||||
return &connection.m_target_socket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
@ -1,234 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 17.03.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef ANIMTESTBED_ANIMGRAPHBLENDTREE_H
|
|
||||||
#define ANIMTESTBED_ANIMGRAPHBLENDTREE_H
|
|
||||||
|
|
||||||
#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;
|
|
||||||
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() { dealloc(); }
|
|
||||||
|
|
||||||
// AnimNode overrides
|
|
||||||
bool Init(AnimGraphContext& context);
|
|
||||||
void MarkActiveInputs() override;
|
|
||||||
void CalcSyncTrack() override;
|
|
||||||
void UpdateTime(float time_last, float time_now) override;
|
|
||||||
void Evaluate(AnimGraphContext& context) override;
|
|
||||||
|
|
||||||
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_state != AnimNodeEvalState::Deactivated;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_ANIMGRAPHBLENDTREE_H
|
|
@ -1,616 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 04.02.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "3rdparty/json/json.hpp"
|
|
||||||
|
|
||||||
#include "AnimGraphBlendTreeResource.h"
|
|
||||||
#include "AnimGraphNodes.h"
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Socket <-> json
|
|
||||||
//
|
|
||||||
std::string sSocketTypeToStr(SocketType pin_type) {
|
|
||||||
if (pin_type < SocketType::SocketTypeUndefined
|
|
||||||
|| pin_type >= SocketType::SocketTypeLast) {
|
|
||||||
return "Unknown";
|
|
||||||
}
|
|
||||||
|
|
||||||
return SocketTypeNames[static_cast<int>(pin_type)];
|
|
||||||
}
|
|
||||||
|
|
||||||
json sSocketToJson(const Socket& socket) {
|
|
||||||
json result;
|
|
||||||
result["name"] = socket.m_name;
|
|
||||||
result["type"] = sSocketTypeToStr(socket.m_type);
|
|
||||||
|
|
||||||
if (socket.m_type == SocketType::SocketTypeString
|
|
||||||
&& !socket.m_value_string.empty()) {
|
|
||||||
result["value"] = socket.m_value_string;
|
|
||||||
} else if (socket.m_value.flag) {
|
|
||||||
if (socket.m_type == SocketType::SocketTypeBool) {
|
|
||||||
result["value"] = socket.m_value.flag;
|
|
||||||
} else if (socket.m_type == SocketType::SocketTypeAnimation) {
|
|
||||||
} else if (socket.m_type == SocketType::SocketTypeInt) {
|
|
||||||
result["value"] = socket.m_value.int_value;
|
|
||||||
} else if (socket.m_type == SocketType::SocketTypeFloat) {
|
|
||||||
result["value"] = socket.m_value.float_value;
|
|
||||||
} else if (socket.m_type == SocketType::SocketTypeVec3) {
|
|
||||||
result["value"][0] = socket.m_value.vec3.v[0];
|
|
||||||
result["value"][1] = socket.m_value.vec3.v[1];
|
|
||||||
result["value"][2] = socket.m_value.vec3.v[2];
|
|
||||||
} else if (socket.m_type == SocketType::SocketTypeQuat) {
|
|
||||||
result["value"][0] = socket.m_value.quat.v[0];
|
|
||||||
result["value"][1] = socket.m_value.quat.v[1];
|
|
||||||
result["value"][2] = socket.m_value.quat.v[2];
|
|
||||||
result["value"][3] = socket.m_value.quat.v[3];
|
|
||||||
} else {
|
|
||||||
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
|
|
||||||
<< "'." << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Socket sJsonToSocket(const json& json_data) {
|
|
||||||
Socket result;
|
|
||||||
result.m_type = SocketType::SocketTypeUndefined;
|
|
||||||
result.m_reference.ptr = &result.m_value.int_value;
|
|
||||||
result.m_name = json_data["name"];
|
|
||||||
|
|
||||||
std::string type_string = json_data["type"];
|
|
||||||
bool have_value = json_data.contains("value");
|
|
||||||
|
|
||||||
if (type_string == "Bool") {
|
|
||||||
result.m_type = SocketType::SocketTypeBool;
|
|
||||||
result.m_type_size = sizeof(bool);
|
|
||||||
result.m_reference.ptr = &result.m_value.int_value;
|
|
||||||
|
|
||||||
if (have_value) {
|
|
||||||
result.m_value.flag = json_data["value"];
|
|
||||||
}
|
|
||||||
} else if (type_string == "Animation") {
|
|
||||||
result.m_type = SocketType::SocketTypeAnimation;
|
|
||||||
result.m_type_size = sizeof(AnimData);
|
|
||||||
} else if (type_string == "Int") {
|
|
||||||
result.m_type = SocketType::SocketTypeInt;
|
|
||||||
result.m_type_size = sizeof(int);
|
|
||||||
if (have_value) {
|
|
||||||
result.m_value.int_value = json_data["value"];
|
|
||||||
}
|
|
||||||
} else if (type_string == "Float") {
|
|
||||||
result.m_type = SocketType::SocketTypeFloat;
|
|
||||||
result.m_type_size = sizeof(float);
|
|
||||||
if (have_value) {
|
|
||||||
result.m_value.float_value = json_data["value"];
|
|
||||||
}
|
|
||||||
} else if (type_string == "Vec3") {
|
|
||||||
result.m_type = SocketType::SocketTypeVec3;
|
|
||||||
result.m_type_size = sizeof(Vec3);
|
|
||||||
if (have_value) {
|
|
||||||
result.m_value.vec3.x = json_data["value"][0];
|
|
||||||
result.m_value.vec3.y = json_data["value"][1];
|
|
||||||
result.m_value.vec3.z = json_data["value"][2];
|
|
||||||
}
|
|
||||||
} else if (type_string == "Quat") {
|
|
||||||
result.m_type = SocketType::SocketTypeQuat;
|
|
||||||
result.m_type_size = sizeof(Quat);
|
|
||||||
if (have_value) {
|
|
||||||
result.m_value.quat.x = json_data["value"][0];
|
|
||||||
result.m_value.quat.y = json_data["value"][1];
|
|
||||||
result.m_value.quat.z = json_data["value"][2];
|
|
||||||
result.m_value.quat.w = json_data["value"][3];
|
|
||||||
}
|
|
||||||
} else if (type_string == "String") {
|
|
||||||
result.m_type = SocketType::SocketTypeString;
|
|
||||||
result.m_type_size = sizeof(std::string);
|
|
||||||
if (have_value) {
|
|
||||||
result.m_value_string = json_data["value"];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cerr << "Invalid socket type '" << type_string << "'." << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// AnimGraphNode <-> json
|
|
||||||
//
|
|
||||||
json sAnimGraphNodeToJson(
|
|
||||||
const AnimNodeResource& node,
|
|
||||||
size_t node_index,
|
|
||||||
const std::vector<AnimGraphConnectionResource>& connections) {
|
|
||||||
json result;
|
|
||||||
|
|
||||||
result["name"] = node.m_name;
|
|
||||||
result["type"] = "AnimNodeResource";
|
|
||||||
result["node_type"] = node.m_type_name;
|
|
||||||
|
|
||||||
for (size_t j = 0; j < 2; j++) {
|
|
||||||
result["position"][j] = node.m_position[j];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto & socket : node.m_socket_accessor->m_inputs) {
|
|
||||||
if (socket.m_type == SocketType::SocketTypeAnimation) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool socket_connected = false;
|
|
||||||
for (const auto & connection : connections) {
|
|
||||||
if (connection.source_node_index == node_index
|
|
||||||
&& connection.source_socket_name == socket.m_name) {
|
|
||||||
socket_connected = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!socket_connected) {
|
|
||||||
result["inputs"].push_back(sSocketToJson(socket));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto & property : node.m_socket_accessor->m_properties) {
|
|
||||||
result["properties"][property.m_name] = sSocketToJson(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimNodeResource sAnimGraphNodeFromJson(const json& json_node, size_t node_index) {
|
|
||||||
AnimNodeResource result;
|
|
||||||
|
|
||||||
result.m_name = json_node["name"];
|
|
||||||
result.m_type_name = json_node["node_type"];
|
|
||||||
result.m_position[0] = json_node["position"][0];
|
|
||||||
result.m_position[1] = json_node["position"][1];
|
|
||||||
|
|
||||||
result.m_anim_node = AnimNodeFactory(result.m_type_name);
|
|
||||||
result.m_socket_accessor =
|
|
||||||
AnimNodeDescriptorFactory(result.m_type_name, result.m_anim_node);
|
|
||||||
|
|
||||||
for (auto & property : result.m_socket_accessor->m_properties) {
|
|
||||||
property = sJsonToSocket(json_node["properties"][property.m_name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node_index != 0 && node_index != 1 && json_node.contains("inputs")) {
|
|
||||||
for (size_t j = 0, n = json_node["inputs"].size(); j < n; j++) {
|
|
||||||
assert(json_node["inputs"][j].contains("name"));
|
|
||||||
std::string input_name = json_node["inputs"][j]["name"];
|
|
||||||
Socket* input_socket =
|
|
||||||
result.m_socket_accessor->GetInputSocket(input_name.c_str());
|
|
||||||
if (input_socket == nullptr) {
|
|
||||||
std::cerr << "Could not find input socket with name " << input_name
|
|
||||||
<< " for node type " << result.m_type_name << std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
*input_socket = sJsonToSocket(json_node["inputs"][j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// AnimGraphConnectionResource <-> Json
|
|
||||||
//
|
|
||||||
json sAnimGraphConnectionToJson(
|
|
||||||
const AnimGraphConnectionResource& connection) {
|
|
||||||
json result;
|
|
||||||
|
|
||||||
result["type"] = "AnimGraphConnectionResource";
|
|
||||||
|
|
||||||
result["source_node_index"] = connection.source_node_index;
|
|
||||||
result["source_socket_name"] = connection.source_socket_name;
|
|
||||||
|
|
||||||
result["target_node_index"] = connection.target_node_index;
|
|
||||||
result["target_socket_name"] = connection.target_socket_name;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimGraphConnectionResource sAnimGraphConnectionFromJson(
|
|
||||||
const json& json_node) {
|
|
||||||
AnimGraphConnectionResource connection;
|
|
||||||
|
|
||||||
connection.source_node_index = json_node["source_node_index"];
|
|
||||||
connection.source_socket_name = json_node["source_socket_name"];
|
|
||||||
|
|
||||||
connection.target_node_index = json_node["target_node_index"];
|
|
||||||
connection.target_socket_name = json_node["target_socket_name"];
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphBlendTreeResource::clear() {
|
|
||||||
m_name = "";
|
|
||||||
|
|
||||||
clearNodes();
|
|
||||||
m_connections.clear();
|
|
||||||
|
|
||||||
initGraphConnectors();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphBlendTreeResource::clearNodes() {
|
|
||||||
for (auto & m_node : m_nodes) {
|
|
||||||
delete m_node.m_socket_accessor;
|
|
||||||
m_node.m_socket_accessor = nullptr;
|
|
||||||
delete m_node.m_anim_node;
|
|
||||||
m_node.m_anim_node = nullptr;
|
|
||||||
}
|
|
||||||
m_nodes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphBlendTreeResource::initGraphConnectors() {
|
|
||||||
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
|
|
||||||
m_nodes[0].m_name = "Outputs";
|
|
||||||
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
|
|
||||||
m_nodes[1].m_name = "Inputs";
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AnimGraphBlendTreeResource::saveToFile(const char* filename) const {
|
|
||||||
json result;
|
|
||||||
|
|
||||||
result["name"] = m_name;
|
|
||||||
result["type"] = "AnimGraphResource";
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_nodes.size(); i++) {
|
|
||||||
const AnimNodeResource& node = m_nodes[i];
|
|
||||||
result["nodes"][i] = sAnimGraphNodeToJson(node, i, m_connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_connections.size(); i++) {
|
|
||||||
const AnimGraphConnectionResource& connection = m_connections[i];
|
|
||||||
result["connections"][i] = sAnimGraphConnectionToJson(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Graph inputs and outputs
|
|
||||||
{
|
|
||||||
const AnimNodeResource& graph_output_node = m_nodes[0];
|
|
||||||
const std::vector<Socket> graph_inputs =
|
|
||||||
graph_output_node.m_socket_accessor->m_inputs;
|
|
||||||
for (size_t i = 0; i < graph_inputs.size(); i++) {
|
|
||||||
result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const AnimNodeResource& graph_input_node = m_nodes[1];
|
|
||||||
const std::vector<Socket> graph_outputs =
|
|
||||||
graph_input_node.m_socket_accessor->m_outputs;
|
|
||||||
for (size_t i = 0; i < graph_outputs.size(); i++) {
|
|
||||||
result["nodes"][1]["outputs"][i] = sSocketToJson(graph_outputs[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ofstream output_file;
|
|
||||||
output_file.open(filename);
|
|
||||||
output_file << result.dump(4, ' ') << std::endl;
|
|
||||||
output_file.close();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AnimGraphBlendTreeResource::loadFromFile(const char* filename) {
|
|
||||||
std::ifstream input_file;
|
|
||||||
input_file.open(filename);
|
|
||||||
std::stringstream buffer;
|
|
||||||
buffer << input_file.rdbuf();
|
|
||||||
|
|
||||||
json json_data = json::parse(buffer.str(), nullptr, false);
|
|
||||||
if (json_data.is_discarded()) {
|
|
||||||
std::cerr << "Error parsing json of file '" << filename << "'."
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (json_data["type"] != "AnimGraphResource") {
|
|
||||||
std::cerr
|
|
||||||
<< "Invalid json object. Expected type 'AnimGraphResource' but got '"
|
|
||||||
<< json_data["type"] << "'." << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear();
|
|
||||||
clearNodes();
|
|
||||||
|
|
||||||
m_name = json_data["name"];
|
|
||||||
|
|
||||||
// Load nodes
|
|
||||||
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
|
||||||
const json& json_node = json_data["nodes"][i];
|
|
||||||
if (json_node["type"] != "AnimNodeResource") {
|
|
||||||
std::cerr
|
|
||||||
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
|
|
||||||
<< json_node["type"] << "'." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimNodeResource node = sAnimGraphNodeFromJson(json_node, i);
|
|
||||||
m_nodes.push_back(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup graph inputs and outputs
|
|
||||||
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
|
||||||
for (const auto & graph_output : graph_outputs) {
|
|
||||||
AnimNodeResource& graph_node = m_nodes[0];
|
|
||||||
graph_node.m_socket_accessor->m_inputs.push_back(
|
|
||||||
sJsonToSocket(graph_output));
|
|
||||||
}
|
|
||||||
|
|
||||||
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
|
||||||
for (const auto & graph_input : graph_inputs) {
|
|
||||||
AnimNodeResource& graph_node = m_nodes[1];
|
|
||||||
graph_node.m_socket_accessor->m_outputs.push_back(
|
|
||||||
sJsonToSocket(graph_input));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load connections
|
|
||||||
for (const auto & json_connection : json_data["connections"]) {
|
|
||||||
if (json_connection["type"] != "AnimGraphConnectionResource") {
|
|
||||||
std::cerr
|
|
||||||
<< "Invalid json object. Expected type 'AnimGraphConnectionResource' "
|
|
||||||
"but got '"
|
|
||||||
<< json_connection["type"] << "'." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimGraphConnectionResource connection =
|
|
||||||
sAnimGraphConnectionFromJson(json_connection);
|
|
||||||
m_connections.push_back(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphBlendTreeResource::createRuntimeNodeInstances(AnimGraph& instance) const {
|
|
||||||
for (int i = 0; i < m_nodes.size(); i++) {
|
|
||||||
const AnimNodeResource& node_resource = m_nodes[i];
|
|
||||||
AnimNode* node = AnimNodeFactory(node_resource.m_type_name);
|
|
||||||
node->m_name = node_resource.m_name;
|
|
||||||
node->m_node_type_name = node_resource.m_type_name;
|
|
||||||
node->m_index = i;
|
|
||||||
instance.m_nodes.push_back(node);
|
|
||||||
|
|
||||||
// runtime node connections
|
|
||||||
instance.m_node_input_connections.emplace_back();
|
|
||||||
instance.m_node_output_connections.emplace_back();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphBlendTreeResource::prepareGraphIOData(AnimGraph& instance) const {
|
|
||||||
instance.m_node_descriptor =
|
|
||||||
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
|
|
||||||
instance.m_node_descriptor->m_outputs =
|
|
||||||
m_nodes[1].m_socket_accessor->m_outputs;
|
|
||||||
instance.m_node_descriptor->m_inputs = m_nodes[0].m_socket_accessor->m_inputs;
|
|
||||||
|
|
||||||
//
|
|
||||||
// graph inputs
|
|
||||||
//
|
|
||||||
int input_block_size = 0;
|
|
||||||
std::vector<Socket>& graph_inputs = instance.getGraphInputs();
|
|
||||||
for (int i = 0; i < graph_inputs.size(); i++) {
|
|
||||||
input_block_size += sizeof(void*);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input_block_size > 0) {
|
|
||||||
instance.m_input_buffer = new char[input_block_size];
|
|
||||||
memset(instance.m_input_buffer, 0, input_block_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int input_block_offset = 0;
|
|
||||||
for (int i = 0; i < graph_inputs.size(); i++) {
|
|
||||||
graph_inputs[i].m_reference.ptr =
|
|
||||||
(void*)&instance.m_input_buffer[input_block_offset];
|
|
||||||
instance.m_node_descriptor->m_outputs[i].m_reference.ptr =
|
|
||||||
&instance.m_input_buffer[input_block_offset];
|
|
||||||
input_block_offset += sizeof(void*);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// graph outputs
|
|
||||||
//
|
|
||||||
int output_block_size = 0;
|
|
||||||
std::vector<Socket>& graph_outputs = instance.getGraphOutputs();
|
|
||||||
for (int i = 0; i < graph_outputs.size(); i++) {
|
|
||||||
output_block_size += sizeof(void*);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output_block_size > 0) {
|
|
||||||
instance.m_output_buffer = new char[output_block_size];
|
|
||||||
memset(instance.m_output_buffer, 0, output_block_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int output_block_offset = 0;
|
|
||||||
for (int i = 0; i < graph_outputs.size(); i++) {
|
|
||||||
instance.m_node_descriptor->m_inputs[i].m_reference.ptr =
|
|
||||||
&instance.m_output_buffer[output_block_offset];
|
|
||||||
output_block_offset += sizeof(void*);
|
|
||||||
}
|
|
||||||
|
|
||||||
// connections: make source and target sockets point to the same address in the connection data storage.
|
|
||||||
// TODO: instead of every connection, only create data blocks for the source sockets and make sure every source socket gets allocated once.
|
|
||||||
size_t connection_data_storage_size = 0;
|
|
||||||
for (const auto & connection : m_connections) {
|
|
||||||
const AnimNodeResource& source_node = m_nodes[connection.source_node_index];
|
|
||||||
Socket* source_socket = source_node.m_socket_accessor->GetOutputSocket(
|
|
||||||
connection.source_socket_name.c_str());
|
|
||||||
connection_data_storage_size += source_socket->m_type_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection_data_storage_size > 0) {
|
|
||||||
instance.m_connection_data_storage = new char[connection_data_storage_size];
|
|
||||||
memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
|
||||||
m_nodes.size(),
|
|
||||||
nullptr);
|
|
||||||
for (int i = 0; i < m_nodes.size(); i++) {
|
|
||||||
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
|
||||||
m_nodes[i].m_type_name,
|
|
||||||
instance.m_nodes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
instance_node_descriptors[0]->m_inputs = instance.m_node_descriptor->m_inputs;
|
|
||||||
instance_node_descriptors[1]->m_outputs =
|
|
||||||
instance.m_node_descriptor->m_outputs;
|
|
||||||
|
|
||||||
size_t connection_data_offset = 0;
|
|
||||||
for (const auto & connection : m_connections) {
|
|
||||||
NodeDescriptorBase* source_node_descriptor =
|
|
||||||
instance_node_descriptors[connection.source_node_index];
|
|
||||||
NodeDescriptorBase* target_node_descriptor =
|
|
||||||
instance_node_descriptors[connection.target_node_index];
|
|
||||||
|
|
||||||
AnimNode* source_node = instance.m_nodes[connection.source_node_index];
|
|
||||||
AnimNode* target_node = instance.m_nodes[connection.target_node_index];
|
|
||||||
|
|
||||||
Socket* source_socket = source_node_descriptor->GetOutputSocket(
|
|
||||||
connection.source_socket_name.c_str());
|
|
||||||
Socket* target_socket = target_node_descriptor->GetInputSocket(
|
|
||||||
connection.target_socket_name.c_str());
|
|
||||||
|
|
||||||
AnimGraphConnection instance_connection;
|
|
||||||
instance_connection.m_source_node = source_node;
|
|
||||||
instance_connection.m_source_socket = *source_socket;
|
|
||||||
instance_connection.m_target_node = target_node;
|
|
||||||
instance_connection.m_target_socket = *target_socket;
|
|
||||||
instance.m_node_input_connections[connection.target_node_index].push_back(
|
|
||||||
instance_connection);
|
|
||||||
instance.m_node_output_connections[connection.source_node_index].push_back(
|
|
||||||
instance_connection);
|
|
||||||
|
|
||||||
source_node_descriptor->SetOutputUnchecked(
|
|
||||||
connection.source_socket_name.c_str(),
|
|
||||||
&instance.m_connection_data_storage[connection_data_offset]);
|
|
||||||
|
|
||||||
target_node_descriptor->SetInputUnchecked(
|
|
||||||
connection.target_socket_name.c_str(),
|
|
||||||
&instance.m_connection_data_storage[connection_data_offset]);
|
|
||||||
|
|
||||||
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
|
|
||||||
instance.m_animdata_blocks.push_back(
|
|
||||||
(AnimData*)(&instance
|
|
||||||
.m_connection_data_storage[connection_data_offset]));
|
|
||||||
}
|
|
||||||
|
|
||||||
connection_data_offset += source_socket->m_type_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// const node inputs
|
|
||||||
//
|
|
||||||
std::vector<Socket*> const_inputs =
|
|
||||||
getConstNodeInputs( instance_node_descriptors);
|
|
||||||
size_t const_node_inputs_buffer_size = 0;
|
|
||||||
for (auto & const_input : const_inputs) {
|
|
||||||
if (const_input->m_type == SocketType::SocketTypeString) {
|
|
||||||
// TODO: implement string const node input support
|
|
||||||
std::cerr << "Error: const inputs for strings not yet implemented!"
|
|
||||||
<< std::endl;
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
const_node_inputs_buffer_size += const_input->m_type_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (const_node_inputs_buffer_size > 0) {
|
|
||||||
instance.m_const_node_inputs = new char[const_node_inputs_buffer_size];
|
|
||||||
memset(instance.m_const_node_inputs, '\0', const_node_inputs_buffer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t const_input_buffer_offset = 0;
|
|
||||||
for (auto & i : const_inputs) {
|
|
||||||
Socket* const_input = i;
|
|
||||||
|
|
||||||
// TODO: implement string const node input support
|
|
||||||
assert(const_input->m_type != SocketType::SocketTypeString);
|
|
||||||
|
|
||||||
*const_input->m_reference.ptr_ptr =
|
|
||||||
&instance.m_const_node_inputs[const_input_buffer_offset];
|
|
||||||
memcpy (*const_input->m_reference.ptr_ptr, &const_input->m_value, i->m_type_size);
|
|
||||||
|
|
||||||
const_input_buffer_offset += i->m_type_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < m_nodes.size(); i++) {
|
|
||||||
delete instance_node_descriptors[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphBlendTreeResource::setRuntimeNodeProperties(AnimGraph& instance) const {
|
|
||||||
for (int i = 2; i < m_nodes.size(); i++) {
|
|
||||||
const AnimNodeResource& node_resource = m_nodes[i];
|
|
||||||
|
|
||||||
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
|
|
||||||
node_resource.m_type_name,
|
|
||||||
instance.m_nodes[i]);
|
|
||||||
|
|
||||||
std::vector<Socket>& resource_properties =
|
|
||||||
node_resource.m_socket_accessor->m_properties;
|
|
||||||
for (const auto & property : resource_properties) {
|
|
||||||
const std::string& name = property.m_name;
|
|
||||||
|
|
||||||
switch (property.m_type) {
|
|
||||||
case SocketType::SocketTypeBool:
|
|
||||||
node_instance_accessor->SetProperty(
|
|
||||||
name.c_str(),
|
|
||||||
property.m_value.flag);
|
|
||||||
break;
|
|
||||||
case SocketType::SocketTypeInt:
|
|
||||||
node_instance_accessor->SetProperty(
|
|
||||||
name.c_str(),
|
|
||||||
property.m_value.int_value);
|
|
||||||
break;
|
|
||||||
case SocketType::SocketTypeFloat:
|
|
||||||
node_instance_accessor->SetProperty(
|
|
||||||
name.c_str(),
|
|
||||||
property.m_value.float_value);
|
|
||||||
break;
|
|
||||||
case SocketType::SocketTypeVec3:
|
|
||||||
node_instance_accessor->SetProperty<Vec3>(
|
|
||||||
name.c_str(),
|
|
||||||
property.m_value.vec3);
|
|
||||||
break;
|
|
||||||
case SocketType::SocketTypeQuat:
|
|
||||||
node_instance_accessor->SetProperty(
|
|
||||||
name.c_str(),
|
|
||||||
property.m_value.quat);
|
|
||||||
break;
|
|
||||||
case SocketType::SocketTypeString:
|
|
||||||
node_instance_accessor->SetProperty(
|
|
||||||
name.c_str(),
|
|
||||||
property.m_value_string);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
std::cerr << "Invalid socket type "
|
|
||||||
<< static_cast<int>(property.m_type) << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete node_instance_accessor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Socket*> AnimGraphBlendTreeResource::getConstNodeInputs(
|
|
||||||
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;
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 04.02.22.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef ANIMTESTBED_ANIMGRAPHBLENDTREERESOURCE_H
|
|
||||||
#define ANIMTESTBED_ANIMGRAPHBLENDTREERESOURCE_H
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "AnimGraph.h"
|
|
||||||
#include "AnimGraphData.h"
|
|
||||||
#include "AnimGraphNodes.h"
|
|
||||||
#include "SyncTrack.h"
|
|
||||||
|
|
||||||
struct AnimNode;
|
|
||||||
|
|
||||||
struct AnimNodeResource {
|
|
||||||
std::string m_name;
|
|
||||||
std::string m_type_name;
|
|
||||||
AnimNode* m_anim_node = nullptr;
|
|
||||||
NodeDescriptorBase* m_socket_accessor = nullptr;
|
|
||||||
float m_position[2] = {0.f, 0.f};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// AnimGraphResource
|
|
||||||
//
|
|
||||||
struct AnimGraphConnectionResource {
|
|
||||||
size_t source_node_index = -1;
|
|
||||||
std::string source_socket_name;
|
|
||||||
size_t target_node_index = -1;
|
|
||||||
std::string target_socket_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnimGraphBlendTreeResource {
|
|
||||||
std::string m_name;
|
|
||||||
std::vector<AnimNodeResource> m_nodes;
|
|
||||||
std::vector<AnimGraphConnectionResource> m_connections;
|
|
||||||
|
|
||||||
~AnimGraphBlendTreeResource() {
|
|
||||||
for (auto & m_node : m_nodes) {
|
|
||||||
delete m_node.m_anim_node;
|
|
||||||
delete m_node.m_socket_accessor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimGraphBlendTreeResource() { clear(); }
|
|
||||||
|
|
||||||
void clear();
|
|
||||||
void clearNodes();
|
|
||||||
void initGraphConnectors();
|
|
||||||
bool saveToFile(const char* filename) const;
|
|
||||||
bool loadFromFile(const char* filename);
|
|
||||||
|
|
||||||
AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; }
|
|
||||||
AnimNodeResource& getGraphInputNode() { return m_nodes[1]; }
|
|
||||||
|
|
||||||
size_t getNodeIndex(const AnimNodeResource& node_resource) const {
|
|
||||||
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
|
|
||||||
if (&m_nodes[i] == &node_resource) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t addNode(const AnimNodeResource &node_resource) {
|
|
||||||
m_nodes.push_back(node_resource);
|
|
||||||
return m_nodes.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool connectSockets(
|
|
||||||
const AnimNodeResource& source_node,
|
|
||||||
const std::string& source_socket_name,
|
|
||||||
const AnimNodeResource& target_node,
|
|
||||||
const std::string& target_socket_name) {
|
|
||||||
size_t source_node_index = getNodeIndex(source_node);
|
|
||||||
size_t target_node_index = getNodeIndex(target_node);
|
|
||||||
|
|
||||||
if (source_node_index >= m_nodes.size()
|
|
||||||
|| target_node_index >= m_nodes.size()) {
|
|
||||||
std::cerr << "Cannot connect nodes: could not find nodes." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Socket* source_socket =
|
|
||||||
source_node.m_socket_accessor->GetOutputSocket(source_socket_name.c_str());
|
|
||||||
Socket* target_socket =
|
|
||||||
target_node.m_socket_accessor->GetInputSocket(target_socket_name.c_str());
|
|
||||||
|
|
||||||
if (source_socket == nullptr || target_socket == nullptr) {
|
|
||||||
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimGraphConnectionResource connection;
|
|
||||||
connection.source_node_index = source_node_index;
|
|
||||||
connection.source_socket_name = source_socket_name;
|
|
||||||
connection.target_node_index = target_node_index;
|
|
||||||
connection.target_socket_name = target_socket_name;
|
|
||||||
m_connections.push_back(connection);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSocketConnected(
|
|
||||||
const AnimNodeResource& node,
|
|
||||||
const std::string& socket_name) {
|
|
||||||
size_t node_index = getNodeIndex(node);
|
|
||||||
for (const auto & connection : m_connections) {
|
|
||||||
if ((connection.source_node_index == node_index
|
|
||||||
&& connection.source_socket_name == socket_name)
|
|
||||||
|| ((connection.target_node_index == node_index)
|
|
||||||
&& connection.target_socket_name == socket_name)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createInstance(AnimGraph& result) const;
|
|
||||||
|
|
||||||
void createRuntimeNodeInstances(AnimGraph& instance) const;
|
|
||||||
void prepareGraphIOData(AnimGraph& instance) const;
|
|
||||||
void setRuntimeNodeProperties(AnimGraph& instance) const;
|
|
||||||
std::vector<Socket*> getConstNodeInputs(std::vector<NodeDescriptorBase*>& instance_node_descriptors) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMGRAPHBLENDTREERESOURCE_H
|
|
@ -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;
|
||||||
@ -260,14 +260,6 @@ SocketType GetSocketType() {
|
|||||||
return SocketType::SocketTypeUndefined;
|
return SocketType::SocketTypeUndefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AnimGraphConnection {
|
|
||||||
AnimNode* m_source_node = nullptr;
|
|
||||||
Socket m_source_socket;
|
|
||||||
AnimNode* m_target_node = nullptr;
|
|
||||||
Socket m_target_socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct NodeDescriptorBase {
|
struct NodeDescriptorBase {
|
||||||
std::vector<Socket> m_inputs;
|
std::vector<Socket> m_inputs;
|
||||||
std::vector<Socket> m_outputs;
|
std::vector<Socket> m_outputs;
|
||||||
|
@ -7,14 +7,13 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
|
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
|
||||||
#include "AnimGraphBlendTreeResource.h"
|
#include "AnimGraphResource.h"
|
||||||
#include "SkinnedMesh.h"
|
#include "SkinnedMesh.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imnodes.h"
|
#include "imnodes.h"
|
||||||
#include "misc/cpp/imgui_stdlib.h"
|
#include "misc/cpp/imgui_stdlib.h"
|
||||||
|
|
||||||
static AnimGraphBlendTreeResource sGraphGresource =
|
static AnimGraphResource sGraphGresource = AnimGraphResource();
|
||||||
AnimGraphBlendTreeResource();
|
|
||||||
static bool sGraphLoadedThisFrame = false;
|
static bool sGraphLoadedThisFrame = false;
|
||||||
|
|
||||||
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
|
||||||
@ -59,7 +58,7 @@ void NodeSocketEditor(Socket& socket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RemoveConnectionsForSocket(
|
void RemoveConnectionsForSocket(
|
||||||
AnimGraphBlendTreeResource& graph_resource,
|
AnimGraphResource& graph_resource,
|
||||||
AnimNodeResource& node_resource,
|
AnimNodeResource& node_resource,
|
||||||
Socket& socket) {
|
Socket& socket) {
|
||||||
std::vector<AnimGraphConnectionResource>::iterator iter =
|
std::vector<AnimGraphConnectionResource>::iterator iter =
|
||||||
@ -160,7 +159,7 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphEditorRenderSidebar(
|
void AnimGraphEditorRenderSidebar(
|
||||||
AnimGraphBlendTreeResource& graph_resource,
|
AnimGraphResource& graph_resource,
|
||||||
AnimNodeResource& node_resource) {
|
AnimNodeResource& node_resource) {
|
||||||
ImGui::Text("[%s]", node_resource.m_type_name.c_str());
|
ImGui::Text("[%s]", node_resource.m_type_name.c_str());
|
||||||
|
|
||||||
|
@ -7,13 +7,72 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "AnimNode.h"
|
|
||||||
#include "AnimGraphData.h"
|
#include "AnimGraphData.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
|
// BlendTreeNode
|
||||||
//
|
//
|
||||||
@ -34,8 +93,8 @@ 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() override {
|
virtual void MarkActiveInputs(const std::vector<AnimGraphConnection>& inputs) override {
|
||||||
for (const auto & input : m_inputs) {
|
for (const auto & input : inputs) {
|
||||||
AnimNode* input_node = input.m_source_node;
|
AnimNode* input_node = input.m_source_node;
|
||||||
if (input_node == nullptr) {
|
if (input_node == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Created by martin on 17.03.24.
|
// Created by martin on 04.02.22.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "AnimGraphResource.h"
|
#include "AnimGraphResource.h"
|
||||||
@ -9,9 +9,6 @@
|
|||||||
|
|
||||||
#include "3rdparty/json/json.hpp"
|
#include "3rdparty/json/json.hpp"
|
||||||
|
|
||||||
#include "AnimGraphBlendTree.h"
|
|
||||||
#include "AnimGraphNodes.h"
|
|
||||||
|
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -127,7 +124,7 @@ Socket sJsonToSocket(const json& json_data) {
|
|||||||
json sAnimGraphNodeToJson(
|
json sAnimGraphNodeToJson(
|
||||||
const AnimNodeResource& node,
|
const AnimNodeResource& node,
|
||||||
size_t node_index,
|
size_t node_index,
|
||||||
const std::vector<BlendTreeConnectionResource>& connections) {
|
const std::vector<AnimGraphConnectionResource>& connections) {
|
||||||
json result;
|
json result;
|
||||||
|
|
||||||
result["name"] = node.m_name;
|
result["name"] = node.m_name;
|
||||||
@ -138,13 +135,13 @@ json sAnimGraphNodeToJson(
|
|||||||
result["position"][j] = node.m_position[j];
|
result["position"][j] = node.m_position[j];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& socket : node.m_socket_accessor->m_inputs) {
|
for (const auto & socket : node.m_socket_accessor->m_inputs) {
|
||||||
if (socket.m_type == SocketType::SocketTypeAnimation) {
|
if (socket.m_type == SocketType::SocketTypeAnimation) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool socket_connected = false;
|
bool socket_connected = false;
|
||||||
for (const auto& connection : connections) {
|
for (const auto & connection : connections) {
|
||||||
if (connection.source_node_index == node_index
|
if (connection.source_node_index == node_index
|
||||||
&& connection.source_socket_name == socket.m_name) {
|
&& connection.source_socket_name == socket.m_name) {
|
||||||
socket_connected = true;
|
socket_connected = true;
|
||||||
@ -157,16 +154,14 @@ json sAnimGraphNodeToJson(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& property : node.m_socket_accessor->m_properties) {
|
for (auto & property : node.m_socket_accessor->m_properties) {
|
||||||
result["properties"][property.m_name] = sSocketToJson(property);
|
result["properties"][property.m_name] = sSocketToJson(property);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnimNodeResource sAnimGraphNodeFromJson(
|
AnimNodeResource sAnimGraphNodeFromJson(const json& json_node, size_t node_index) {
|
||||||
const json& json_node,
|
|
||||||
size_t node_index) {
|
|
||||||
AnimNodeResource result;
|
AnimNodeResource result;
|
||||||
|
|
||||||
result.m_name = json_node["name"];
|
result.m_name = json_node["name"];
|
||||||
@ -178,7 +173,7 @@ AnimNodeResource sAnimGraphNodeFromJson(
|
|||||||
result.m_socket_accessor =
|
result.m_socket_accessor =
|
||||||
AnimNodeDescriptorFactory(result.m_type_name, result.m_anim_node);
|
AnimNodeDescriptorFactory(result.m_type_name, result.m_anim_node);
|
||||||
|
|
||||||
for (auto& property : result.m_socket_accessor->m_properties) {
|
for (auto & property : result.m_socket_accessor->m_properties) {
|
||||||
property = sJsonToSocket(json_node["properties"][property.m_name]);
|
property = sJsonToSocket(json_node["properties"][property.m_name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +198,8 @@ AnimNodeResource sAnimGraphNodeFromJson(
|
|||||||
//
|
//
|
||||||
// AnimGraphConnectionResource <-> Json
|
// AnimGraphConnectionResource <-> Json
|
||||||
//
|
//
|
||||||
json sAnimGraphConnectionToJson(const BlendTreeConnectionResource& connection) {
|
json sAnimGraphConnectionToJson(
|
||||||
|
const AnimGraphConnectionResource& connection) {
|
||||||
json result;
|
json result;
|
||||||
|
|
||||||
result["type"] = "AnimGraphConnectionResource";
|
result["type"] = "AnimGraphConnectionResource";
|
||||||
@ -217,9 +213,9 @@ json sAnimGraphConnectionToJson(const BlendTreeConnectionResource& connection) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlendTreeConnectionResource sAnimGraphConnectionFromJson(
|
AnimGraphConnectionResource sAnimGraphConnectionFromJson(
|
||||||
const json& json_node) {
|
const json& json_node) {
|
||||||
BlendTreeConnectionResource connection;
|
AnimGraphConnectionResource connection;
|
||||||
|
|
||||||
connection.source_node_index = json_node["source_node_index"];
|
connection.source_node_index = json_node["source_node_index"];
|
||||||
connection.source_socket_name = json_node["source_socket_name"];
|
connection.source_socket_name = json_node["source_socket_name"];
|
||||||
@ -230,136 +226,58 @@ BlendTreeConnectionResource sAnimGraphConnectionFromJson(
|
|||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimGraphResource::LoadFromFile(const char* filename) {
|
void AnimGraphResource::clear() {
|
||||||
std::ifstream input_file;
|
m_name = "";
|
||||||
input_file.open(filename);
|
|
||||||
std::stringstream buffer;
|
|
||||||
buffer << input_file.rdbuf();
|
|
||||||
|
|
||||||
json json_data = json::parse(buffer.str(), nullptr, false);
|
clearNodes();
|
||||||
if (json_data.is_discarded()) {
|
m_connections.clear();
|
||||||
std::cerr << "Error parsing json of file '" << filename << "'."
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
return false;
|
initGraphConnectors();
|
||||||
}
|
|
||||||
|
|
||||||
if (json_data["type"] != "AnimNodeResource") {
|
|
||||||
std::cerr
|
|
||||||
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
|
|
||||||
<< json_data["type"] << "'." << std::endl;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (json_data["node_type"] == "BlendTree") {
|
|
||||||
return LoadBlendTreeResourceFromJson(json_data);
|
|
||||||
} else if (json_data["node_type"] == "StateMachine") {
|
|
||||||
return LoadStateMachineResourceFromJson(json_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cerr << "Invalid node_type. Expected type 'BlendTree' or "
|
|
||||||
"'StateMachine' but got '"
|
|
||||||
<< json_data["node_type"] << "'." << std::endl;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimGraphResource::LoadBlendTreeResourceFromJson(nlohmann::json const& json_data) {
|
void AnimGraphResource::clearNodes() {
|
||||||
m_blend_tree_resource.Reset();
|
for (auto & m_node : m_nodes) {
|
||||||
m_type = "BlendTree";
|
delete m_node.m_socket_accessor;
|
||||||
m_name = json_data["name"];
|
m_node.m_socket_accessor = nullptr;
|
||||||
|
delete m_node.m_anim_node;
|
||||||
// Load nodes
|
m_node.m_anim_node = nullptr;
|
||||||
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
|
||||||
const json& json_node = json_data["nodes"][i];
|
|
||||||
if (json_node["type"] != "AnimNodeResource") {
|
|
||||||
std::cerr
|
|
||||||
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
|
|
||||||
<< json_node["type"] << "'." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
m_nodes.clear();
|
||||||
AnimNodeResource node = sAnimGraphNodeFromJson(json_node, i);
|
|
||||||
m_blend_tree_resource.m_nodes.push_back(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup graph inputs and outputs
|
|
||||||
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
|
||||||
for (const auto& graph_output : graph_outputs) {
|
|
||||||
AnimNodeResource& graph_node = m_blend_tree_resource.m_nodes[0];
|
|
||||||
graph_node.m_socket_accessor->m_inputs.push_back(
|
|
||||||
sJsonToSocket(graph_output));
|
|
||||||
}
|
|
||||||
|
|
||||||
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
|
||||||
for (const auto& graph_input : graph_inputs) {
|
|
||||||
AnimNodeResource& graph_node = m_blend_tree_resource.m_nodes[1];
|
|
||||||
graph_node.m_socket_accessor->m_outputs.push_back(
|
|
||||||
sJsonToSocket(graph_input));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load connections
|
|
||||||
for (const auto& json_connection : json_data["connections"]) {
|
|
||||||
if (json_connection["type"] != "AnimGraphConnectionResource") {
|
|
||||||
std::cerr << "Invalid json object. Expected type "
|
|
||||||
"'AnimGraphConnectionResource' "
|
|
||||||
"but got '"
|
|
||||||
<< json_connection["type"] << "'." << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlendTreeConnectionResource connection =
|
|
||||||
sAnimGraphConnectionFromJson(json_connection);
|
|
||||||
m_blend_tree_resource.m_connections.push_back(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimGraphResource::SaveToFile(const char* filename) const {
|
void AnimGraphResource::initGraphConnectors() {
|
||||||
if (m_type == "BlendTree") {
|
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
|
||||||
return SaveBlendTreeResourceToFile(filename);
|
m_nodes[0].m_name = "Outputs";
|
||||||
} else if (m_type == "StateMachine") {
|
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
|
||||||
return SaveStateMachineResourceToFile(filename);
|
m_nodes[1].m_name = "Inputs";
|
||||||
}
|
|
||||||
|
|
||||||
std::cerr << "Invalid AnimGraphResource type: " << m_type << "." << std::endl;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimGraphResource::SaveBlendTreeResourceToFile(
|
bool AnimGraphResource::saveToFile(const char* filename) const {
|
||||||
const char* filename) const {
|
|
||||||
json result;
|
json result;
|
||||||
|
|
||||||
result["name"] = m_name;
|
result["name"] = m_name;
|
||||||
result["type"] = "AnimNodeResource";
|
result["type"] = "AnimGraphResource";
|
||||||
result["node_type"] = "BlendTree";
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
for (size_t i = 0; i < m_nodes.size(); i++) {
|
||||||
const AnimNodeResource& node = m_blend_tree_resource.m_nodes[i];
|
const AnimNodeResource& node = m_nodes[i];
|
||||||
result["nodes"][i] =
|
result["nodes"][i] = sAnimGraphNodeToJson(node, i, m_connections);
|
||||||
sAnimGraphNodeToJson(node, i, m_blend_tree_resource.m_connections);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < m_blend_tree_resource.m_connections.size(); i++) {
|
for (size_t i = 0; i < m_connections.size(); i++) {
|
||||||
const BlendTreeConnectionResource& connection =
|
const AnimGraphConnectionResource& connection = m_connections[i];
|
||||||
m_blend_tree_resource.m_connections[i];
|
|
||||||
result["connections"][i] = sAnimGraphConnectionToJson(connection);
|
result["connections"][i] = sAnimGraphConnectionToJson(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graph inputs and outputs
|
// Graph inputs and outputs
|
||||||
{
|
{
|
||||||
const AnimNodeResource& graph_output_node =
|
const AnimNodeResource& graph_output_node = m_nodes[0];
|
||||||
m_blend_tree_resource.m_nodes[0];
|
|
||||||
const std::vector<Socket> graph_inputs =
|
const std::vector<Socket> graph_inputs =
|
||||||
graph_output_node.m_socket_accessor->m_inputs;
|
graph_output_node.m_socket_accessor->m_inputs;
|
||||||
for (size_t i = 0; i < graph_inputs.size(); i++) {
|
for (size_t i = 0; i < graph_inputs.size(); i++) {
|
||||||
result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]);
|
result["nodes"][0]["inputs"][i] = sSocketToJson(graph_inputs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const AnimNodeResource& graph_input_node = m_blend_tree_resource.m_nodes[1];
|
const AnimNodeResource& graph_input_node = m_nodes[1];
|
||||||
const std::vector<Socket> graph_outputs =
|
const std::vector<Socket> graph_outputs =
|
||||||
graph_input_node.m_socket_accessor->m_outputs;
|
graph_input_node.m_socket_accessor->m_outputs;
|
||||||
for (size_t i = 0; i < graph_outputs.size(); i++) {
|
for (size_t i = 0; i < graph_outputs.size(); i++) {
|
||||||
@ -375,45 +293,107 @@ bool AnimGraphResource::SaveBlendTreeResourceToFile(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::CreateBlendTreeInstance(
|
bool AnimGraphResource::loadFromFile(const char* filename) {
|
||||||
AnimGraphBlendTree& result) const {
|
std::ifstream input_file;
|
||||||
if (m_type != "BlendTree") {
|
input_file.open(filename);
|
||||||
std::cerr << "Invalid AnimGraphResource. Expected type 'BlendTree' but got '"
|
std::stringstream buffer;
|
||||||
<< m_type << "'." << std::endl;
|
buffer << input_file.rdbuf();
|
||||||
return;
|
|
||||||
|
json json_data = json::parse(buffer.str(), nullptr, false);
|
||||||
|
if (json_data.is_discarded()) {
|
||||||
|
std::cerr << "Error parsing json of file '" << filename << "'."
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateBlendTreeRuntimeNodeInstances(result);
|
if (json_data["type"] != "AnimGraphResource") {
|
||||||
PrepareBlendTreeIOData(result);
|
std::cerr
|
||||||
SetRuntimeNodeProperties(result);
|
<< "Invalid json object. Expected type 'AnimGraphResource' but got '"
|
||||||
|
<< json_data["type"] << "'." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
result.UpdateOrderedNodes();
|
clear();
|
||||||
result.ResetNodeStates();
|
clearNodes();
|
||||||
|
|
||||||
|
m_name = json_data["name"];
|
||||||
|
|
||||||
|
// Load nodes
|
||||||
|
for (size_t i = 0, n = json_data["nodes"].size(); i < n; i++) {
|
||||||
|
const json& json_node = json_data["nodes"][i];
|
||||||
|
if (json_node["type"] != "AnimNodeResource") {
|
||||||
|
std::cerr
|
||||||
|
<< "Invalid json object. Expected type 'AnimNodeResource' but got '"
|
||||||
|
<< json_node["type"] << "'." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimNodeResource node = sAnimGraphNodeFromJson(json_node, i);
|
||||||
|
m_nodes.push_back(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup graph inputs and outputs
|
||||||
|
const json& graph_outputs = json_data["nodes"][0]["inputs"];
|
||||||
|
for (const auto & graph_output : graph_outputs) {
|
||||||
|
AnimNodeResource& graph_node = m_nodes[0];
|
||||||
|
graph_node.m_socket_accessor->m_inputs.push_back(
|
||||||
|
sJsonToSocket(graph_output));
|
||||||
|
}
|
||||||
|
|
||||||
|
const json& graph_inputs = json_data["nodes"][1]["outputs"];
|
||||||
|
for (const auto & graph_input : graph_inputs) {
|
||||||
|
AnimNodeResource& graph_node = m_nodes[1];
|
||||||
|
graph_node.m_socket_accessor->m_outputs.push_back(
|
||||||
|
sJsonToSocket(graph_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load connections
|
||||||
|
for (const auto & json_connection : json_data["connections"]) {
|
||||||
|
if (json_connection["type"] != "AnimGraphConnectionResource") {
|
||||||
|
std::cerr
|
||||||
|
<< "Invalid json object. Expected type 'AnimGraphConnectionResource' "
|
||||||
|
"but got '"
|
||||||
|
<< json_connection["type"] << "'." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimGraphConnectionResource connection =
|
||||||
|
sAnimGraphConnectionFromJson(json_connection);
|
||||||
|
m_connections.push_back(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::CreateBlendTreeRuntimeNodeInstances(
|
void AnimGraphResource::createInstance(AnimGraph& result) const {
|
||||||
AnimGraphBlendTree& result) const {
|
createRuntimeNodeInstances(result);
|
||||||
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
prepareGraphIOData(result);
|
||||||
const AnimNodeResource& node_resource = m_blend_tree_resource.m_nodes[i];
|
setRuntimeNodeProperties(result);
|
||||||
|
|
||||||
|
result.updateOrderedNodes();
|
||||||
|
result.resetNodeStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimGraphResource::createRuntimeNodeInstances(AnimGraph& instance) const {
|
||||||
|
for (int i = 0; i < m_nodes.size(); i++) {
|
||||||
|
const AnimNodeResource& node_resource = m_nodes[i];
|
||||||
AnimNode* node = AnimNodeFactory(node_resource.m_type_name);
|
AnimNode* node = AnimNodeFactory(node_resource.m_type_name);
|
||||||
node->m_name = node_resource.m_name;
|
node->m_name = node_resource.m_name;
|
||||||
node->m_node_type_name = node_resource.m_type_name;
|
node->m_node_type_name = node_resource.m_type_name;
|
||||||
result.m_nodes.push_back(node);
|
node->m_index = i;
|
||||||
|
instance.m_nodes.push_back(node);
|
||||||
|
|
||||||
// runtime node connections
|
// runtime node connections
|
||||||
result.m_node_input_connections.emplace_back();
|
instance.m_node_input_connections.emplace_back();
|
||||||
result.m_node_output_connections.emplace_back();
|
instance.m_node_output_connections.emplace_back();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::PrepareBlendTreeIOData(
|
void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
|
||||||
AnimGraphBlendTree& instance) const {
|
|
||||||
instance.m_node_descriptor =
|
instance.m_node_descriptor =
|
||||||
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
|
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
|
||||||
instance.m_node_descriptor->m_outputs =
|
instance.m_node_descriptor->m_outputs =
|
||||||
m_blend_tree_resource.m_nodes[1].m_socket_accessor->m_outputs;
|
m_nodes[1].m_socket_accessor->m_outputs;
|
||||||
instance.m_node_descriptor->m_inputs =
|
instance.m_node_descriptor->m_inputs = m_nodes[0].m_socket_accessor->m_inputs;
|
||||||
m_blend_tree_resource.m_nodes[0].m_socket_accessor->m_inputs;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// graph inputs
|
// graph inputs
|
||||||
@ -462,9 +442,8 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
// connections: make source and target sockets point to the same address in the connection data storage.
|
// connections: make source and target sockets point to the same address in the connection data storage.
|
||||||
// TODO: instead of every connection, only create data blocks for the source sockets and make sure every source socket gets allocated once.
|
// TODO: instead of every connection, only create data blocks for the source sockets and make sure every source socket gets allocated once.
|
||||||
size_t connection_data_storage_size = 0;
|
size_t connection_data_storage_size = 0;
|
||||||
for (const auto& connection : m_blend_tree_resource.m_connections) {
|
for (const auto & connection : m_connections) {
|
||||||
const AnimNodeResource& source_node =
|
const AnimNodeResource& source_node = m_nodes[connection.source_node_index];
|
||||||
m_blend_tree_resource.m_nodes[connection.source_node_index];
|
|
||||||
Socket* source_socket = source_node.m_socket_accessor->GetOutputSocket(
|
Socket* source_socket = source_node.m_socket_accessor->GetOutputSocket(
|
||||||
connection.source_socket_name.c_str());
|
connection.source_socket_name.c_str());
|
||||||
connection_data_storage_size += source_socket->m_type_size;
|
connection_data_storage_size += source_socket->m_type_size;
|
||||||
@ -476,11 +455,11 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
||||||
m_blend_tree_resource.m_nodes.size(),
|
m_nodes.size(),
|
||||||
nullptr);
|
nullptr);
|
||||||
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
for (int i = 0; i < m_nodes.size(); i++) {
|
||||||
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
||||||
m_blend_tree_resource.m_nodes[i].m_type_name,
|
m_nodes[i].m_type_name,
|
||||||
instance.m_nodes[i]);
|
instance.m_nodes[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,7 +468,7 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
instance.m_node_descriptor->m_outputs;
|
instance.m_node_descriptor->m_outputs;
|
||||||
|
|
||||||
size_t connection_data_offset = 0;
|
size_t connection_data_offset = 0;
|
||||||
for (const auto& connection : m_blend_tree_resource.m_connections) {
|
for (const auto & connection : m_connections) {
|
||||||
NodeDescriptorBase* source_node_descriptor =
|
NodeDescriptorBase* source_node_descriptor =
|
||||||
instance_node_descriptors[connection.source_node_index];
|
instance_node_descriptors[connection.source_node_index];
|
||||||
NodeDescriptorBase* target_node_descriptor =
|
NodeDescriptorBase* target_node_descriptor =
|
||||||
@ -534,9 +513,9 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
// const node inputs
|
// const node inputs
|
||||||
//
|
//
|
||||||
std::vector<Socket*> const_inputs =
|
std::vector<Socket*> const_inputs =
|
||||||
m_blend_tree_resource.GetConstantNodeInputs(instance_node_descriptors);
|
getConstNodeInputs( instance_node_descriptors);
|
||||||
size_t const_node_inputs_buffer_size = 0;
|
size_t const_node_inputs_buffer_size = 0;
|
||||||
for (auto& const_input : const_inputs) {
|
for (auto & const_input : const_inputs) {
|
||||||
if (const_input->m_type == SocketType::SocketTypeString) {
|
if (const_input->m_type == SocketType::SocketTypeString) {
|
||||||
// TODO: implement string const node input support
|
// TODO: implement string const node input support
|
||||||
std::cerr << "Error: const inputs for strings not yet implemented!"
|
std::cerr << "Error: const inputs for strings not yet implemented!"
|
||||||
@ -552,7 +531,7 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t const_input_buffer_offset = 0;
|
size_t const_input_buffer_offset = 0;
|
||||||
for (auto& i : const_inputs) {
|
for (auto & i : const_inputs) {
|
||||||
Socket* const_input = i;
|
Socket* const_input = i;
|
||||||
|
|
||||||
// TODO: implement string const node input support
|
// TODO: implement string const node input support
|
||||||
@ -560,30 +539,27 @@ void AnimGraphResource::PrepareBlendTreeIOData(
|
|||||||
|
|
||||||
*const_input->m_reference.ptr_ptr =
|
*const_input->m_reference.ptr_ptr =
|
||||||
&instance.m_const_node_inputs[const_input_buffer_offset];
|
&instance.m_const_node_inputs[const_input_buffer_offset];
|
||||||
memcpy(
|
memcpy (*const_input->m_reference.ptr_ptr, &const_input->m_value, i->m_type_size);
|
||||||
*const_input->m_reference.ptr_ptr,
|
|
||||||
&const_input->m_value,
|
|
||||||
i->m_type_size);
|
|
||||||
|
|
||||||
const_input_buffer_offset += i->m_type_size;
|
const_input_buffer_offset += i->m_type_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
for (int i = 0; i < m_nodes.size(); i++) {
|
||||||
delete instance_node_descriptors[i];
|
delete instance_node_descriptors[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimGraphResource::SetRuntimeNodeProperties(
|
void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
|
||||||
AnimGraphBlendTree& result) const {
|
for (int i = 2; i < m_nodes.size(); i++) {
|
||||||
for (int i = 2; i < m_blend_tree_resource.m_nodes.size(); i++) {
|
const AnimNodeResource& node_resource = m_nodes[i];
|
||||||
const AnimNodeResource& node_resource = m_blend_tree_resource.m_nodes[i];
|
|
||||||
|
|
||||||
NodeDescriptorBase* node_instance_accessor =
|
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
|
||||||
AnimNodeDescriptorFactory(node_resource.m_type_name, result.m_nodes[i]);
|
node_resource.m_type_name,
|
||||||
|
instance.m_nodes[i]);
|
||||||
|
|
||||||
std::vector<Socket>& resource_properties =
|
std::vector<Socket>& resource_properties =
|
||||||
node_resource.m_socket_accessor->m_properties;
|
node_resource.m_socket_accessor->m_properties;
|
||||||
for (const auto& property : resource_properties) {
|
for (const auto & property : resource_properties) {
|
||||||
const std::string& name = property.m_name;
|
const std::string& name = property.m_name;
|
||||||
|
|
||||||
switch (property.m_type) {
|
switch (property.m_type) {
|
||||||
@ -627,15 +603,22 @@ void AnimGraphResource::SetRuntimeNodeProperties(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnimGraphResource::SaveStateMachineResourceToFile(
|
std::vector<Socket*> AnimGraphResource::getConstNodeInputs(
|
||||||
const char* filename) const {
|
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
|
||||||
assert(false && "Not yet implemented");
|
std::vector<Socket*> result;
|
||||||
|
|
||||||
return false;
|
for (size_t i = 0; i < m_nodes.size(); i++) {
|
||||||
}
|
for (size_t j = 0, num_inputs = instance_node_descriptors[i]->m_inputs.size();
|
||||||
|
j < num_inputs;
|
||||||
bool AnimGraphResource::LoadStateMachineResourceFromJson(nlohmann::json const& json_data) {
|
j++) {
|
||||||
assert(false && "Not yet implemented");
|
Socket& input = instance_node_descriptors[i]->m_inputs[j];
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
}
|
}
|
@ -1,17 +1,23 @@
|
|||||||
//
|
//
|
||||||
// Created by martin on 17.03.24.
|
// Created by martin on 04.02.22.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
||||||
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "AnimGraph.h"
|
#include "AnimGraph.h"
|
||||||
|
#include "AnimGraphData.h"
|
||||||
#include "AnimGraphNodes.h"
|
#include "AnimGraphNodes.h"
|
||||||
|
#include "SyncTrack.h"
|
||||||
|
|
||||||
#include "3rdparty/json/json.hpp"
|
struct AnimNode;
|
||||||
|
|
||||||
struct AnimGraphBlendTree;
|
|
||||||
struct AnimGraphStateMachine;
|
|
||||||
|
|
||||||
struct AnimNodeResource {
|
struct AnimNodeResource {
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
@ -31,33 +37,40 @@ static inline AnimNodeResource AnimNodeResourceFactory(
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BlendTreeConnectionResource {
|
//
|
||||||
|
// AnimGraphResource
|
||||||
|
//
|
||||||
|
struct AnimGraphConnectionResource {
|
||||||
size_t source_node_index = -1;
|
size_t source_node_index = -1;
|
||||||
std::string source_socket_name;
|
std::string source_socket_name;
|
||||||
size_t target_node_index = -1;
|
size_t target_node_index = -1;
|
||||||
std::string target_socket_name;
|
std::string target_socket_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlendTreeResource {
|
struct AnimGraphResource {
|
||||||
|
std::string m_name;
|
||||||
std::vector<AnimNodeResource> m_nodes;
|
std::vector<AnimNodeResource> m_nodes;
|
||||||
std::vector<BlendTreeConnectionResource> m_connections;
|
std::vector<AnimGraphConnectionResource> m_connections;
|
||||||
|
|
||||||
void Reset() {
|
~AnimGraphResource() {
|
||||||
m_nodes.clear();
|
for (auto & m_node : m_nodes) {
|
||||||
m_connections.clear();
|
delete m_node.m_anim_node;
|
||||||
|
delete m_node.m_socket_accessor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitGraphConnectors() {
|
AnimGraphResource() { clear(); }
|
||||||
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
|
|
||||||
m_nodes[0].m_name = "Outputs";
|
|
||||||
m_nodes.push_back(AnimNodeResourceFactory("BlendTree"));
|
|
||||||
m_nodes[1].m_name = "Inputs";
|
|
||||||
}
|
|
||||||
|
|
||||||
AnimNodeResource& GetGraphOutputNode() { return m_nodes[0]; }
|
void clear();
|
||||||
AnimNodeResource& GetGraphInputNode() { return m_nodes[1]; }
|
void clearNodes();
|
||||||
|
void initGraphConnectors();
|
||||||
|
bool saveToFile(const char* filename) const;
|
||||||
|
bool loadFromFile(const char* filename);
|
||||||
|
|
||||||
size_t GetNodeIndex(const AnimNodeResource& node_resource) const {
|
AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; }
|
||||||
|
AnimNodeResource& getGraphInputNode() { return m_nodes[1]; }
|
||||||
|
|
||||||
|
size_t 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;
|
||||||
@ -67,13 +80,18 @@ struct BlendTreeResource {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectSockets (
|
size_t addNode(const AnimNodeResource &node_resource) {
|
||||||
|
m_nodes.push_back(node_resource);
|
||||||
|
return m_nodes.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool connectSockets(
|
||||||
const AnimNodeResource& source_node,
|
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 source_node_index = getNodeIndex(source_node);
|
||||||
size_t target_node_index = GetNodeIndex(target_node);
|
size_t target_node_index = getNodeIndex(target_node);
|
||||||
|
|
||||||
if (source_node_index >= m_nodes.size()
|
if (source_node_index >= m_nodes.size()
|
||||||
|| target_node_index >= m_nodes.size()) {
|
|| target_node_index >= m_nodes.size()) {
|
||||||
@ -91,7 +109,7 @@ struct BlendTreeResource {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlendTreeConnectionResource connection;
|
AnimGraphConnectionResource connection;
|
||||||
connection.source_node_index = source_node_index;
|
connection.source_node_index = source_node_index;
|
||||||
connection.source_socket_name = source_socket_name;
|
connection.source_socket_name = source_socket_name;
|
||||||
connection.target_node_index = target_node_index;
|
connection.target_node_index = target_node_index;
|
||||||
@ -101,62 +119,28 @@ struct BlendTreeResource {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Socket*> GetConstantNodeInputs(
|
bool isSocketConnected(
|
||||||
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
|
const AnimNodeResource& node,
|
||||||
std::vector<Socket*> result;
|
const std::string& socket_name) {
|
||||||
|
size_t node_index = getNodeIndex(node);
|
||||||
for (size_t i = 0; i < m_nodes.size(); i++) {
|
for (const auto & connection : m_connections) {
|
||||||
for (size_t j = 0, num_inputs = instance_node_descriptors[i]->m_inputs.size();
|
if ((connection.source_node_index == node_index
|
||||||
j < num_inputs;
|
&& connection.source_socket_name == socket_name)
|
||||||
j++) {
|
|| ((connection.target_node_index == node_index)
|
||||||
Socket& input = instance_node_descriptors[i]->m_inputs[j];
|
&& connection.target_socket_name == socket_name)) {
|
||||||
|
return true;
|
||||||
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 false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
void createInstance(AnimGraph& result) const;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StateMachineTransitionResources {
|
void createRuntimeNodeInstances(AnimGraph& instance) const;
|
||||||
size_t source_state_index = -1;
|
void prepareGraphIOData(AnimGraph& instance) const;
|
||||||
size_t target_state_index = -1;
|
void setRuntimeNodeProperties(AnimGraph& instance) const;
|
||||||
float blend_time = 0.f;
|
std::vector<Socket*> getConstNodeInputs(std::vector<NodeDescriptorBase*>& instance_node_descriptors) const;
|
||||||
bool sync_blend = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StateMachineResource {
|
|
||||||
std::vector<AnimNodeResource> m_states;
|
|
||||||
std::vector<StateMachineTransitionResources> m_transitions;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnimGraphResource {
|
|
||||||
std::string m_type;
|
|
||||||
std::string m_name;
|
|
||||||
|
|
||||||
BlendTreeResource m_blend_tree_resource;
|
|
||||||
StateMachineResource m_state_machine_resource;
|
|
||||||
|
|
||||||
bool SaveToFile(const char* filename) const;
|
|
||||||
bool LoadFromFile(const char* filename);
|
|
||||||
|
|
||||||
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
|
|
||||||
void CreateStateMachineInstance(AnimGraphStateMachine& result) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// BlendTree
|
|
||||||
bool SaveBlendTreeResourceToFile(const char* filename) const;
|
|
||||||
bool LoadBlendTreeResourceFromJson(nlohmann::json const& json_data);
|
|
||||||
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
|
|
||||||
void PrepareBlendTreeIOData(AnimGraphBlendTree& instance) const;
|
|
||||||
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
|
|
||||||
|
|
||||||
bool SaveStateMachineResourceToFile(const char* filename) const;
|
|
||||||
bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 17.03.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "AnimGraphStateMachine.h"
|
|
||||||
|
|
||||||
bool AnimGraphStateMachine::Init(AnimGraphContext& context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphStateMachine::MarkActiveInputs() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphStateMachine::CalcSyncTrack() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphStateMachine::UpdateTime(float time_last, float time_now) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void AnimGraphStateMachine::Evaluate(AnimGraphContext& context) {
|
|
||||||
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// 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() override;
|
|
||||||
void CalcSyncTrack() override;
|
|
||||||
void UpdateTime(float time_last, float time_now) override;
|
|
||||||
void Evaluate(AnimGraphContext& context) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMGRAPHSTATEMACHINE_H
|
|
@ -1,5 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 17.03.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "AnimNode.h"
|
|
@ -1,70 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by martin on 17.03.24.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef ANIMTESTBED_ANIMNODE_H
|
|
||||||
#define ANIMTESTBED_ANIMNODE_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "SyncTrack.h"
|
|
||||||
#include "AnimGraphData.h"
|
|
||||||
|
|
||||||
struct AnimNode;
|
|
||||||
|
|
||||||
enum class AnimNodeEvalState {
|
|
||||||
Undefined,
|
|
||||||
Deactivated,
|
|
||||||
Activated,
|
|
||||||
SyncTrackUpdated,
|
|
||||||
TimeUpdated,
|
|
||||||
Evaluated
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnimNode {
|
|
||||||
std::string m_name;
|
|
||||||
std::string m_node_type_name;
|
|
||||||
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
|
|
||||||
|
|
||||||
float m_time_now = 0.f;
|
|
||||||
float m_time_last = 0.f;
|
|
||||||
SyncTrack m_sync_track;
|
|
||||||
|
|
||||||
std::vector<AnimGraphConnection> m_inputs;
|
|
||||||
|
|
||||||
virtual ~AnimNode() = default;
|
|
||||||
|
|
||||||
virtual bool Init(AnimGraphContext& context) { return true; };
|
|
||||||
|
|
||||||
virtual void MarkActiveInputs() {
|
|
||||||
for (const auto & input : m_inputs) {
|
|
||||||
AnimNode* input_node = input.m_source_node;
|
|
||||||
if (input_node != nullptr) {
|
|
||||||
input_node->m_state = AnimNodeEvalState::Activated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void CalcSyncTrack() {
|
|
||||||
for (const auto & input : m_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){};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //ANIMTESTBED_ANIMNODE_H
|
|
@ -160,10 +160,8 @@ struct Viewport {
|
|||||||
.compare = SG_COMPAREFUNC_LESS_EQUAL,
|
.compare = SG_COMPAREFUNC_LESS_EQUAL,
|
||||||
.write_enabled = true
|
.write_enabled = true
|
||||||
},
|
},
|
||||||
.cull_mode = SG_CULLMODE_BACK,
|
.cull_mode = SG_CULLMODE_BACK
|
||||||
.sample_count = cMSAASampleCount
|
|
||||||
};
|
};
|
||||||
// this->pip = sg_make_pipeline(gl_pipeline_desc);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -474,7 +472,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, cMSAASampleCount);
|
glfwWindowHint(GLFW_SAMPLES, 16);
|
||||||
GLFWwindow* w =
|
GLFWwindow* w =
|
||||||
glfwCreateWindow(Width, Height, "ATP Editor", nullptr, nullptr);
|
glfwCreateWindow(Width, Height, "ATP Editor", nullptr, nullptr);
|
||||||
glfwMakeContextCurrent(w);
|
glfwMakeContextCurrent(w);
|
||||||
@ -529,7 +527,7 @@ 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}, .context {.sample_count = cMSAASampleCount}};
|
sg_desc desc = {.logger = {.func = slog_func}};
|
||||||
sg_setup(&desc);
|
sg_setup(&desc);
|
||||||
assert(sg_isvalid());
|
assert(sg_isvalid());
|
||||||
|
|
||||||
@ -660,7 +658,6 @@ 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);
|
||||||
|
|
||||||
|
@ -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]") {
|
||||||
AnimGraphBlendTreeResource graph_resource;
|
AnimGraphResource graph_resource;
|
||||||
|
|
||||||
// Add nodes
|
// Add nodes
|
||||||
size_t trans_x_node_index =
|
size_t trans_x_node_index =
|
||||||
|
@ -3,9 +3,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "AnimGraph/AnimGraph.h"
|
#include "AnimGraph/AnimGraph.h"
|
||||||
#include "AnimGraph/AnimGraphBlendTree.h"
|
|
||||||
#include "AnimGraph/AnimGraphEditor.h"
|
#include "AnimGraph/AnimGraphEditor.h"
|
||||||
#include "AnimGraph/AnimGraphNodes.h"
|
|
||||||
#include "AnimGraph/AnimGraphResource.h"
|
#include "AnimGraph/AnimGraphResource.h"
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
#include "ozz/base/io/archive.h"
|
#include "ozz/base/io/archive.h"
|
||||||
@ -35,65 +33,61 @@ bool load_skeleton(ozz::animation::Skeleton& skeleton, const char* filename) {
|
|||||||
|
|
||||||
TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") {
|
TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") {
|
||||||
AnimGraphResource graph_resource;
|
AnimGraphResource graph_resource;
|
||||||
graph_resource.m_name = "AnimSamplerBlendTree";
|
|
||||||
graph_resource.m_type = "BlendTree";
|
|
||||||
|
|
||||||
BlendTreeResource& blend_tree_resource = graph_resource.m_blend_tree_resource;
|
graph_resource.clear();
|
||||||
blend_tree_resource.Reset();
|
graph_resource.m_name = "AnimSamplerGraph";
|
||||||
blend_tree_resource.InitGraphConnectors();
|
|
||||||
|
|
||||||
// Prepare graph inputs and outputs
|
// Prepare graph inputs and outputs
|
||||||
blend_tree_resource.m_nodes.push_back(AnimNodeResourceFactory("AnimSampler"));
|
size_t walk_node_index =
|
||||||
size_t walk_node_index = blend_tree_resource.m_nodes.size() - 1;
|
graph_resource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
||||||
|
|
||||||
AnimNodeResource& walk_node = blend_tree_resource.m_nodes[walk_node_index];
|
AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index];
|
||||||
walk_node.m_name = "WalkAnim";
|
walk_node.m_name = "WalkAnim";
|
||||||
walk_node.m_socket_accessor->SetPropertyValue(
|
walk_node.m_socket_accessor->SetPropertyValue(
|
||||||
"Filename",
|
"Filename",
|
||||||
std::string("media/Walking-loop.ozz"));
|
std::string("media/Walking-loop.ozz"));
|
||||||
|
|
||||||
AnimNodeResource& graph_node = blend_tree_resource.m_nodes[0];
|
AnimNodeResource& graph_node = graph_resource.m_nodes[0];
|
||||||
graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr);
|
graph_node.m_socket_accessor->RegisterInput<AnimData>("GraphOutput", nullptr);
|
||||||
|
|
||||||
blend_tree_resource.ConnectSockets(
|
graph_resource.connectSockets(
|
||||||
walk_node,
|
walk_node,
|
||||||
"Output",
|
"Output",
|
||||||
blend_tree_resource.GetGraphOutputNode(),
|
graph_resource.getGraphOutputNode(),
|
||||||
"GraphOutput");
|
"GraphOutput");
|
||||||
|
|
||||||
graph_resource.SaveToFile("AnimSamplerBlendTree.json");
|
graph_resource.saveToFile("AnimSamplerGraph.animgraph.json");
|
||||||
|
|
||||||
AnimGraphResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.LoadFromFile("AnimSamplerBlendTree.json");
|
graph_resource_loaded.loadFromFile("AnimSamplerGraph.animgraph.json");
|
||||||
|
|
||||||
AnimGraphBlendTree anim_graph_blend_tree;
|
AnimGraph graph;
|
||||||
graph_resource_loaded.CreateBlendTreeInstance(anim_graph_blend_tree);
|
graph_resource_loaded.createInstance(graph);
|
||||||
AnimGraphContext graph_context;
|
AnimGraphContext graph_context;
|
||||||
|
|
||||||
ozz::animation::Skeleton skeleton;
|
ozz::animation::Skeleton skeleton;
|
||||||
REQUIRE(load_skeleton(skeleton, "media/skeleton.ozz"));
|
REQUIRE(load_skeleton(skeleton, "media/skeleton.ozz"));
|
||||||
graph_context.m_skeleton = &skeleton;
|
graph_context.m_skeleton = &skeleton;
|
||||||
|
|
||||||
REQUIRE(anim_graph_blend_tree.Init(graph_context));
|
REQUIRE(graph.init(graph_context));
|
||||||
|
|
||||||
REQUIRE(anim_graph_blend_tree.m_nodes.size() == 3);
|
REQUIRE(graph.m_nodes.size() == 3);
|
||||||
REQUIRE(anim_graph_blend_tree.m_nodes[0]->m_node_type_name == "BlendTree");
|
REQUIRE(graph.m_nodes[0]->m_node_type_name == "BlendTree");
|
||||||
REQUIRE(anim_graph_blend_tree.m_nodes[1]->m_node_type_name == "BlendTree");
|
REQUIRE(graph.m_nodes[1]->m_node_type_name == "BlendTree");
|
||||||
REQUIRE(anim_graph_blend_tree.m_nodes[2]->m_node_type_name == "AnimSampler");
|
REQUIRE(graph.m_nodes[2]->m_node_type_name == "AnimSampler");
|
||||||
|
|
||||||
// connections within the graph
|
// connections within the graph
|
||||||
AnimSamplerNode* anim_sampler_walk =
|
AnimSamplerNode* anim_sampler_walk =
|
||||||
dynamic_cast<AnimSamplerNode*>(anim_graph_blend_tree.m_nodes[2]);
|
dynamic_cast<AnimSamplerNode*>(graph.m_nodes[2]);
|
||||||
|
|
||||||
BlendTreeNode* graph_output_node =
|
BlendTreeNode* graph_output_node =
|
||||||
dynamic_cast<BlendTreeNode*>(anim_graph_blend_tree.m_nodes[0]);
|
dynamic_cast<BlendTreeNode*>(graph.m_nodes[0]);
|
||||||
|
|
||||||
// check node input dependencies
|
// check node input dependencies
|
||||||
size_t anim_sampler_index = anim_graph_blend_tree.GetAnimNodeIndex(anim_sampler_walk);
|
size_t anim_sampler_index = anim_sampler_walk->m_index;
|
||||||
|
|
||||||
REQUIRE(anim_graph_blend_tree.m_node_output_connections[anim_sampler_index].size() == 1);
|
REQUIRE(graph.m_node_output_connections[anim_sampler_index].size() == 1);
|
||||||
CHECK(
|
CHECK(
|
||||||
anim_graph_blend_tree.m_node_output_connections[anim_sampler_index][0].m_target_node
|
graph.m_node_output_connections[anim_sampler_index][0].m_target_node
|
||||||
== graph_output_node);
|
== graph_output_node);
|
||||||
|
|
||||||
// Ensure animation sampler nodes use the correct files
|
// Ensure animation sampler nodes use the correct files
|
||||||
@ -103,11 +97,11 @@ TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") {
|
|||||||
// Ensure that outputs are properly propagated.
|
// Ensure that outputs are properly propagated.
|
||||||
AnimData output;
|
AnimData output;
|
||||||
output.m_local_matrices.resize(skeleton.num_soa_joints());
|
output.m_local_matrices.resize(skeleton.num_soa_joints());
|
||||||
anim_graph_blend_tree.SetOutput("GraphOutput", &output);
|
graph.SetOutput("GraphOutput", &output);
|
||||||
REQUIRE(anim_sampler_walk->o_output == &output);
|
REQUIRE(anim_sampler_walk->o_output == &output);
|
||||||
|
|
||||||
WHEN("Emulating Graph Evaluation") {
|
WHEN("Emulating Graph Evaluation") {
|
||||||
CHECK(anim_graph_blend_tree.m_anim_data_allocator.size() == 0);
|
CHECK(graph.m_anim_data_allocator.size() == 0);
|
||||||
anim_sampler_walk->Evaluate(graph_context);
|
anim_sampler_walk->Evaluate(graph_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,12 +109,10 @@ TEST_CASE("AnimSamplerGraph", "[AnimGraphResource]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* Checks that node const inputs are properly set.
|
||||||
//
|
*/
|
||||||
// Checks that node const inputs are properly set.
|
|
||||||
//
|
|
||||||
TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
||||||
AnimGraphBlendTreeResource graph_resource;
|
AnimGraphResource graph_resource;
|
||||||
|
|
||||||
graph_resource.clear();
|
graph_resource.clear();
|
||||||
graph_resource.m_name = "AnimSamplerSpeedScaleGraph";
|
graph_resource.m_name = "AnimSamplerSpeedScaleGraph";
|
||||||
@ -158,7 +150,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
|||||||
"GraphOutput");
|
"GraphOutput");
|
||||||
|
|
||||||
graph_resource.saveToFile("AnimSamplerSpeedScaleGraph.animgraph.json");
|
graph_resource.saveToFile("AnimSamplerSpeedScaleGraph.animgraph.json");
|
||||||
AnimGraphBlendTreeResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.loadFromFile(
|
graph_resource_loaded.loadFromFile(
|
||||||
"AnimSamplerSpeedScaleGraph.animgraph.json");
|
"AnimSamplerSpeedScaleGraph.animgraph.json");
|
||||||
|
|
||||||
@ -180,7 +172,7 @@ TEST_CASE("AnimSamplerSpeedScaleGraph", "[AnimGraphResource]") {
|
|||||||
|
|
||||||
|
|
||||||
TEST_CASE("Blend2Graph", "[AnimGraphResource]") {
|
TEST_CASE("Blend2Graph", "[AnimGraphResource]") {
|
||||||
AnimGraphBlendTreeResource graph_resource;
|
AnimGraphResource graph_resource;
|
||||||
|
|
||||||
graph_resource.clear();
|
graph_resource.clear();
|
||||||
graph_resource.m_name = "WalkRunBlendGraph";
|
graph_resource.m_name = "WalkRunBlendGraph";
|
||||||
@ -222,7 +214,7 @@ TEST_CASE("Blend2Graph", "[AnimGraphResource]") {
|
|||||||
"GraphOutput");
|
"GraphOutput");
|
||||||
|
|
||||||
graph_resource.saveToFile("Blend2Graph.animgraph.json");
|
graph_resource.saveToFile("Blend2Graph.animgraph.json");
|
||||||
AnimGraphBlendTreeResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.loadFromFile("Blend2Graph.animgraph.json");
|
graph_resource_loaded.loadFromFile("Blend2Graph.animgraph.json");
|
||||||
|
|
||||||
AnimGraph graph;
|
AnimGraph graph;
|
||||||
@ -319,7 +311,7 @@ TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
||||||
AnimGraphBlendTreeResource graph_resource_origin;
|
AnimGraphResource graph_resource_origin;
|
||||||
|
|
||||||
graph_resource_origin.clear();
|
graph_resource_origin.clear();
|
||||||
graph_resource_origin.m_name = "TestInputOutputGraph";
|
graph_resource_origin.m_name = "TestInputOutputGraph";
|
||||||
@ -378,7 +370,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||||||
const char* filename = "ResourceSaveLoadGraphInputs.json";
|
const char* filename = "ResourceSaveLoadGraphInputs.json";
|
||||||
graph_resource_origin.saveToFile(filename);
|
graph_resource_origin.saveToFile(filename);
|
||||||
|
|
||||||
AnimGraphBlendTreeResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.loadFromFile(filename);
|
graph_resource_loaded.loadFromFile(filename);
|
||||||
|
|
||||||
const AnimNodeResource& graph_loaded_output_node =
|
const AnimNodeResource& graph_loaded_output_node =
|
||||||
@ -452,7 +444,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
||||||
AnimGraphBlendTreeResource graph_resource_origin;
|
AnimGraphResource graph_resource_origin;
|
||||||
|
|
||||||
graph_resource_origin.clear();
|
graph_resource_origin.clear();
|
||||||
graph_resource_origin.m_name = "TestInputOutputGraph";
|
graph_resource_origin.m_name = "TestInputOutputGraph";
|
||||||
@ -537,7 +529,7 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||||||
const char* filename = "ResourceSaveLoadGraphInputs.json";
|
const char* filename = "ResourceSaveLoadGraphInputs.json";
|
||||||
graph_resource_origin.saveToFile(filename);
|
graph_resource_origin.saveToFile(filename);
|
||||||
|
|
||||||
AnimGraphBlendTreeResource graph_resource_loaded;
|
AnimGraphResource graph_resource_loaded;
|
||||||
graph_resource_loaded.loadFromFile(filename);
|
graph_resource_loaded.loadFromFile(filename);
|
||||||
|
|
||||||
const AnimNodeResource& graph_loaded_output_node =
|
const AnimNodeResource& graph_loaded_output_node =
|
||||||
@ -588,5 +580,3 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
|
Loading…
x
Reference in New Issue
Block a user