39 Commits
Author SHA1 Message Date
Martin Felis 99d5a5eb0f Minor refactor. 2024-05-07 18:50:38 +02:00
Martin Felis 698abbce4b BlendTree input and output sockets can now be registered via the AnimGraphResource. 2024-05-01 21:49:34 +02:00
Martin Felis b9789bd1e1 Minor refactor. 2024-05-01 13:17:25 +02:00
Martin Felis fd032c273b Links can now be removed by hovering link and pressing the Delete key. 2024-05-01 13:11:54 +02:00
Martin Felis da431a3879 Fixed setting location of new nodes and minor connection rejection refactor. 2024-05-01 13:03:57 +02:00
Martin Felis 84fc49af30 Properly store and restore node positions in editor. 2024-05-01 12:50:16 +02:00
Martin Felis 7c7a765455 New nodes now spawn at popup start location. 2024-05-01 11:59:30 +02:00
Martin Felis e3baa65c3b Added breadcrumb navigation for embedded graphs. 2024-05-01 10:58:33 +02:00
Martin Felis 44087d7a7c Removed legacy editor, started working on editing embedded graphs. 2024-04-30 18:40:54 +02:00
Martin Felis e8af30d10c Added embedded fonts. 2024-04-30 17:49:29 +02:00
Martin Felis 4d1990bea8 Reactivated the sidebar in the new node editor. 2024-04-25 21:40:09 +02:00
Martin Felis 5e34aaf3db Extremely basic blend tree editing. 2024-04-25 21:12:08 +02:00
Martin Felis 3fb2995b02 Fixed building of editor app, though crashes everywhere. 2024-04-24 21:58:47 +02:00
Martin Felis c267276be3 Fixed connection validation when one of the nodes is an embedded blend tree. 2024-04-24 21:38:11 +02:00
Martin Felis 53c0bff7a6 Minor cleanup. 2024-04-21 12:47:19 +02:00
Martin Felis 91e226945c Added basic connection validation and connection removal to BlendTreeResources. 2024-04-21 12:42:49 +02:00
Martin Felis d95bc9fb9c Made BlendTreeResource::m_nodes and ::m_connections private.
This is a prerequisite to properly track Node input/output connections and to compute eval order in the BlendTreeResources.
2024-04-16 22:18:11 +02:00
Martin Felis 2d5337ed1d Reduced connection data block size.
Previously we allocated a block for each connection. However when an output is reused by multiple connections this lead to duplicates. It also obfuscated which block is actually being used.
2024-04-08 21:52:03 +02:00
Martin Felis 9f9ac60f9c Cleaned up some compilation issues for the AnimGraphEditor. 2024-04-05 00:44:37 +02:00
Martin Felis 1741238a61 Fixed variable shadowing. 2024-04-05 00:43:41 +02:00
Martin Felis 8694a11416 Removed even more unused files. 2024-04-05 00:18:07 +02:00
Martin Felis 3444f8a625 Removed dead code and unused files. 2024-04-04 19:47:24 +02:00
Martin Felis cd56efca3d Only storing single socket in AnimGraphConnections and simplified wiring logic. 2024-04-04 19:36:20 +02:00
Martin Felis 28eca48a61 Added a test that checks socket propagation into an embedded BlendTree. 2024-04-01 17:59:25 +02:00
Martin Felis 76ea38f118 Added support of time updates for simple embedded graphs. 2024-04-01 12:33:23 +02:00
Martin Felis 0aebe44bd5 Started working on evaluating embedded blend trees. 2024-03-25 22:26:29 +01:00
Martin Felis 99f11e61d8 Refactored AnimGraphResourceTests such that some tests use reusable fixtures. 2024-03-25 21:10:27 +01:00
Martin Felis 116bf7699b Added support of saving and loading of embedded blend tree resources. 2024-03-24 21:50:22 +01:00
Martin Felis 3a7f470acf Fixed memory leak in ResourceSaveLoadMathGraphInputs test. 2024-03-22 12:25:42 +01:00
Martin Felis e687c9b613 Restored AnimGraphResourceTests. 2024-03-20 22:40:46 +01:00
Martin Felis ccb9bc4e9b Working on unified BlendTree and StateMachine handling. 2024-03-17 22:06:27 +01:00
Martin Felis c7d2d195a3 Updated ozz-animation to version 0.14.3 (commit 92c392b667844) 2024-03-17 12:47:11 +01:00
Martin Felis 1ef53d6486 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	src/AnimGraph/AnimGraphEditor.cc
2024-03-16 20:08:45 +01:00
Martin Felis fdb2b6ffc5 Some progress with new graph editor. 2024-03-16 19:49:41 +01:00
Martin Felis a5af917f4b Updated imgui-node-editor to commit 32dc92e 2024-03-03 20:22:06 +01:00
Martin Felis a79ffaa420 WIP: new anim graph editor can show loaded graph. 2024-03-03 20:20:24 +01:00
Martin Felis e61180c4a7 WIP: implementation of new anim graph editor. 2024-03-01 14:26:52 +01:00
Martin Felis 8388fbfe3e Better separated 3D view and graph editor navigation. 2024-03-01 14:00:14 +01:00
Martin Felis dc958dcabd Fixed some clang-tidy warnings. 2024-03-01 13:59:00 +01:00
71 changed files with 15277 additions and 2580 deletions
+1 -14
View File
@@ -1,11 +1,3 @@
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
// Crude implementation of JSON value object and parser.
//
// VERSION 0.1
@@ -569,7 +561,7 @@ private:
if (end != hex.c_str() + hex.size())
return false;
c = v;
c = static_cast<int>(v);
return true;
}
@@ -896,8 +888,3 @@ bool value::save(const string& path, const int indent, const char indent_char) c
# endif
} // namespace crude_json
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
+2 -13
View File
@@ -1,10 +1,4 @@
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#endif
// Crude implementation of JSON value object and parser.
// Crude implementation of JSON value object and parser.
//
// VERSION 0.1
//
@@ -253,9 +247,4 @@ template <> inline number* value::get_ptr<number>() { if (m_Type =
} // namespace crude_json
# endif // __CRUDE_JSON_H__
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
# endif // __CRUDE_JSON_H__
+67 -1
View File
@@ -1,4 +1,70 @@
v0.9.1 (WIP):
v0.9.4 (WIP):
NEW: Editor: Add smooth zoom (#266)
BUGFIX: Canvas: Remember index of first command buffer to not miss updating any used (#260)
BUGFIX: Editor: Don't duplicated ImVec2/ImVec3 == != operators defined since ImGui r19002 (#268)
BUGFIX: Examples: Use imgui_impl_opengl3_loader.h instead of gl3w (#264)
v0.9.3 (2023-10-14):
CHANGE: Canvas: Use ImDrawCallback_ImCanvas macro as draw callback sentinel (#256), thanks @nspitko
BUGFIX: Canvas: Ensure SentinelDrawCallback cleanup (#255)
BUGFIX: Editor: Don't call Reasume/Suspend on invisible canvas (#255)
v0.9.2 (2023-09-01):
NEW: Editor: Add offset of hover/select to style (thanks @MultiPain)
NEW: Editor: Add IMGUI_NODE_EDITOR_API to support building editor as a shared library (#189)
NEW: Canvas: Add IMGUIEX_CANVAS_API to support building canvas as a shared library (#189)
CHANGE: Editor: Support ImGui r18836 after SetItemUsingMouseWheel removal (#218), thanks @ocornut
CHANGE: Editor: Define IMGUI_DEFINE_MATH_OPERATORS before <imgui.h> (#209), thanks @ocornut
CHANGE: Examples: Define IMGUI_DEFINE_MATH_OPERATORS before <imgui.h> (#209), thanks @ocornut
CHANGE: Canvas: Don't use deprecated SetItemAllowOverlap (#250)
CHANGE: Examples: Don't use deprecated SetItemAllowOverlap (#250)
CHANGE: Editor: Define IMGUI_DEFINE_MATH_OPERATORS before <imgui.h> (#209), thanks @ocornut
CHANGE: Editor: Unary operator- for ImVec2 is defined by ImGui since r18955 (#248)
BUGFIX: Editor: Correctly initialize 'width' for view resize code (thx @gnif)
BUGFIX: Examples: Handle node deletion before links (#182)
Deleting node queue connected links for deletion.
BUGFIX: Examples: Simplify and fix drawing of node header line (#180)
BUGFIX: Editor: Cleanup tabs.
BUGFIX: Editor: Use ImGuiKey directly with ImGui r18822 (#183)
BUGFIX: Examples: Use ImGuiKey directly with ImGui r18822 (#183)
BUGFIX: Examples: Use ImGuiKey_KeypadEnter with ImGui r18604 (#183)
BUGFIX: Examples: Add missing <cstdint> include for std::intptr_t (#199)
BUGFIX: Examples: Don't use empty string as identifier
BUGFIX: Editor: Clean long to int implicit cast warning in crude_json
BUGFIX: Canvas: Ensure canvas draw commands are separated from other ImGui draw commands (#205, #250)
BUGFIX: Editor: Don't call Canvas.End() when Canvas.Begin() failed (#186), thanks @pthom, @TheZoc
v0.9.1 (2022-08-27):
CHANGE: Remove unwanted extra frame height from node bottom
@@ -69,15 +69,12 @@ endif()
if (OpenGL_FOUND)
set(HAVE_OPENGL YES)
find_package(gl3w REQUIRED)
# Explicitly select embedded GL3W loader
target_compile_definitions(application PRIVATE IMGUI_IMPL_OPENGL_LOADER_GL3W)
target_include_directories(application PRIVATE ${OPENGL_INCLUDE_DIR})
target_link_libraries(application PRIVATE ${OPENGL_gl_LIBRARY} gl3w)
target_link_libraries(application PRIVATE ${OPENGL_gl_LIBRARY})
list(APPEND _Application_Sources
source/imgui_impl_opengl3.cpp
source/imgui_impl_opengl3.h
source/imgui_impl_opengl3_loader.h
)
endif()
@@ -1,5 +1,8 @@
# pragma once
# include <imgui.h>
# if !defined(IMGUI_VERSION_NUM) || (IMGUI_VERSION_NUM < 18822)
# include <type_traits>
// https://stackoverflow.com/a/8597498
@@ -46,3 +49,17 @@ static inline int GetEnumValueForD()
{
return KeyTester_ImGuiKey_D::Get<ImGuiKey_>(nullptr);
}
# else
static inline ImGuiKey GetEnumValueForF()
{
return ImGuiKey_F;
}
static inline ImGuiKey GetEnumValueForD()
{
return ImGuiKey_D;
}
# endif
File diff suppressed because it is too large Load Diff
@@ -1,31 +1,36 @@
// dear imgui: Renderer for modern OpenGL with shaders / programmatic pipeline
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices.
// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this.
// If you are new to dear imgui, read examples/README.txt and read the documentation at the top of imgui.cpp.
// https://github.com/ocornut/imgui
// About WebGL/ES:
// - You need to '#define IMGUI_IMPL_OPENGL_ES2' or '#define IMGUI_IMPL_OPENGL_ES3' to use WebGL or OpenGL ES.
// - This is done automatically on iOS, Android and Emscripten targets.
// - For other targets, the define needs to be visible from the imgui_impl_opengl3.cpp compilation unit. If unsure, define globally or in imconfig.h.
// About Desktop OpenGL function loaders:
// Modern Desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are supporting a few common ones (gl3w, glew, glad).
// You may use another loader/header of your choice (glext, glLoadGen, etc.), or chose to manually implement your own.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#ifndef IMGUI_DISABLE
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
@@ -40,48 +45,22 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// Attempt to auto-detect the default Desktop GL loader based on available header files.
// If auto-detection fails or doesn't select the same GL loader file as used by your application,
// you are likely to get a crash in ImGui_ImplOpenGL3_Init().
// You can explicitly select a loader by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
&& !defined(IMGUI_IMPL_OPENGL_ES3) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GL3W) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLEW) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLAD2) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING2) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_GLBINDING3) \
&& !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
&& !defined(IMGUI_IMPL_OPENGL_ES3)
// Try to detect GLES on matching platforms
#if defined(__APPLE__)
#include "TargetConditionals.h"
#include <TargetConditionals.h>
#endif
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#elif defined(__EMSCRIPTEN__)
#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
// Otherwise try to detect supported Desktop OpenGL loaders..
#elif defined(__has_include)
#if __has_include(<GL/glew.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLEW
#elif __has_include(<glad/glad.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD
#elif __has_include(<glad/gl.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLAD2
#elif __has_include(<GL/gl3w.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GL3W
#elif __has_include(<glbinding/glbinding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING3
#elif __has_include(<glbinding/Binding.h>)
#define IMGUI_IMPL_OPENGL_LOADER_GLBINDING2
#else
#error "Cannot detect OpenGL loader!"
#endif
#else
#define IMGUI_IMPL_OPENGL_LOADER_GL3W // Default to GL3W embedded in our repository
// Otherwise imgui_impl_opengl3_loader.h will be used.
#endif
#endif
#endif // #ifndef IMGUI_DISABLE
@@ -93,7 +93,11 @@ bool ImGui_ImplWin32_Init(void* hwnd)
io.KeyMap[ImGuiKey_Space] = VK_SPACE;
io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
# if defined(IMGUI_VERSION_NUM) && (IMGUI_VERSION_NUM >= 18604)
io.KeyMap[ImGuiKey_KeypadEnter] = VK_RETURN;
# else
io.KeyMap[ImGuiKey_KeyPadEnter] = VK_RETURN;
# endif
io.KeyMap[ImGuiKey_A] = 'A';
io.KeyMap[ImGuiKey_C] = 'C';
io.KeyMap[ImGuiKey_V] = 'V';
@@ -4,6 +4,7 @@
# include "platform.h"
# include <algorithm>
# include <cstdint> // std::intptr_t
# if PLATFORM(WINDOWS)
# define NOMINMAX
@@ -31,8 +32,10 @@ using namespace gl;
# include <glbinding/glbinding.h>// Initialize with glbinding::initialize()
# include <glbinding/gl/gl.h>
using namespace gl;
# else
# elif defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
# include IMGUI_IMPL_OPENGL_LOADER_CUSTOM
# else
# include "imgui_impl_opengl3_loader.h"
# endif
struct ImTexture
@@ -1,10 +1,9 @@
#define IMGUI_DEFINE_MATH_OPERATORS
#include <application.h>
#include "utilities/builders.h"
#include "utilities/widgets.h"
#include <imgui_node_editor.h>
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui_internal.h>
#include <string>
@@ -648,7 +647,9 @@ struct Example:
ImGui::DragFloat("Node Rounding", &editorStyle.NodeRounding, 0.1f, 0.0f, 40.0f);
ImGui::DragFloat("Node Border Width", &editorStyle.NodeBorderWidth, 0.1f, 0.0f, 15.0f);
ImGui::DragFloat("Hovered Node Border Width", &editorStyle.HoveredNodeBorderWidth, 0.1f, 0.0f, 15.0f);
ImGui::DragFloat("Hovered Node Border Offset", &editorStyle.HoverNodeBorderOffset, 0.1f, -40.0f, 40.0f);
ImGui::DragFloat("Selected Node Border Width", &editorStyle.SelectedNodeBorderWidth, 0.1f, 0.0f, 15.0f);
ImGui::DragFloat("Selected Node Border Offset", &editorStyle.SelectedNodeBorderOffset, 0.1f, -40.0f, 40.0f);
ImGui::DragFloat("Pin Rounding", &editorStyle.PinRounding, 0.1f, 0.0f, 40.0f);
ImGui::DragFloat("Pin Border Width", &editorStyle.PinBorderWidth, 0.1f, 0.0f, 15.0f);
ImGui::DragFloat("Link Strength", &editorStyle.LinkStrength, 1.0f, 0.0f, 500.0f);
@@ -682,7 +683,7 @@ struct Example:
ImGui::EndHorizontal();
static ImGuiTextFilter filter;
filter.Draw("", paneWidth);
filter.Draw("##filter", paneWidth);
ImGui::Spacing();
@@ -765,6 +766,9 @@ struct Example:
}
bool isSelected = std::find(selectedNodes.begin(), selectedNodes.end(), node.ID) != selectedNodes.end();
# if IMGUI_VERSION_NUM >= 18967
ImGui::SetNextItemAllowOverlap();
# endif
if (ImGui::Selectable((node.Name + "##" + std::to_string(reinterpret_cast<uintptr_t>(node.ID.AsPointer()))).c_str(), &isSelected))
{
if (io.KeyCtrl)
@@ -793,7 +797,11 @@ struct Example:
auto drawList = ImGui::GetWindowDrawList();
ImGui::SetCursorScreenPos(iconPanelPos);
# if IMGUI_VERSION_NUM < 18967
ImGui::SetItemAllowOverlap();
# else
ImGui::SetNextItemAllowOverlap();
# endif
if (node.SavedState.empty())
{
if (ImGui::InvisibleButton("save", ImVec2((float)saveIconWidth, (float)saveIconHeight)))
@@ -813,7 +821,11 @@ struct Example:
}
ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
# if IMGUI_VERSION_NUM < 18967
ImGui::SetItemAllowOverlap();
# else
ImGui::SetNextItemAllowOverlap();
# endif
if (!node.SavedState.empty())
{
if (ImGui::InvisibleButton("restore", ImVec2((float)restoreIconWidth, (float)restoreIconHeight)))
@@ -837,7 +849,9 @@ struct Example:
}
ImGui::SameLine(0, 0);
# if IMGUI_VERSION_NUM < 18967
ImGui::SetItemAllowOverlap();
# endif
ImGui::Dummy(ImVec2(0, (float)restoreIconHeight));
ImGui::PopID();
@@ -1537,17 +1551,6 @@ struct Example:
if (ed::BeginDelete())
{
ed::LinkId linkId = 0;
while (ed::QueryDeletedLink(&linkId))
{
if (ed::AcceptDeletedItem())
{
auto id = std::find_if(m_Links.begin(), m_Links.end(), [linkId](auto& link) { return link.ID == linkId; });
if (id != m_Links.end())
m_Links.erase(id);
}
}
ed::NodeId nodeId = 0;
while (ed::QueryDeletedNode(&nodeId))
{
@@ -1558,6 +1561,17 @@ struct Example:
m_Nodes.erase(id);
}
}
ed::LinkId linkId = 0;
while (ed::QueryDeletedLink(&linkId))
{
if (ed::AcceptDeletedItem())
{
auto id = std::find_if(m_Links.begin(), m_Links.end(), [linkId](auto& link) { return link.ID == linkId; });
if (id != m_Links.end())
m_Links.erase(id);
}
}
}
ed::EndDelete();
}
@@ -7,8 +7,8 @@
// CREDITS
// Written by Michal Cichon
//------------------------------------------------------------------------------
# include "builders.h"
# define IMGUI_DEFINE_MATH_OPERATORS
# include "builders.h"
# include <imgui_internal.h>
@@ -72,15 +72,11 @@ void util::BlueprintNodeBuilder::End()
headerColor, GetStyle().NodeRounding, 1 | 2);
#endif
auto headerSeparatorMin = ImVec2(HeaderMin.x, HeaderMax.y);
auto headerSeparatorMax = ImVec2(HeaderMax.x, HeaderMin.y);
if ((headerSeparatorMax.x > headerSeparatorMin.x) && (headerSeparatorMax.y > headerSeparatorMin.y))
if (ContentMin.y > HeaderMax.y)
{
drawList->AddLine(
headerSeparatorMin + ImVec2(-(8 - halfBorderWidth), -0.5f),
headerSeparatorMax + ImVec2( (8 - halfBorderWidth), -0.5f),
ImVec2(HeaderMin.x - (8 - halfBorderWidth), HeaderMax.y - 0.5f),
ImVec2(HeaderMax.x + (8 - halfBorderWidth), HeaderMax.y - 0.5f),
ImColor(255, 255, 255, 96 * alpha / (3 * 255)), 1.0f);
}
}
@@ -1,5 +1,5 @@
# include "drawing.h"
# define IMGUI_DEFINE_MATH_OPERATORS
# include "drawing.h"
# include <imgui_internal.h>
void ax::Drawing::DrawIcon(ImDrawList* drawList, const ImVec2& a, const ImVec2& b, IconType type, bool filled, ImU32 color, ImU32 innerColor)
@@ -1,5 +1,5 @@
# include "widgets.h"
# define IMGUI_DEFINE_MATH_OPERATORS
# include "widgets.h"
# include <imgui_internal.h>
void ax::Widgets::Icon(const ImVec2& size, IconType type, bool filled, const ImVec4& color/* = ImVec4(1, 1, 1, 1)*/, const ImVec4& innerColor/* = ImVec4(0, 0, 0, 0)*/)
@@ -1,5 +1,5 @@
# include <imgui.h>
# define IMGUI_DEFINE_MATH_OPERATORS
# include <imgui.h>
# include <imgui_internal.h>
# include <imgui_canvas.h>
# include <application.h>
+11 -25
View File
@@ -1,11 +1,3 @@
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//------------------------------------------------------------------------------
// VERSION 0.1
//
@@ -403,19 +395,17 @@ inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImVec2& p0,
return count;
};
/*
https://github.com/kaishiqi/Geometric-Bezier/blob/master/GeometricBezier/src/kaishiqi/geometric/intersection/Intersection.as
Start with Bezier using Bernstein polynomials for weighting functions:
(1-t^3)P0 + 3t(1-t)^2P1 + 3t^2(1-t)P2 + t^3P3
Expand and collect terms to form linear combinations of original Bezier
controls. This ends up with a vector cubic in t:
(-P0+3P1-3P2+P3)t^3 + (3P0-6P1+3P2)t^2 + (-3P0+3P1)t + P0
/\ /\ /\ /\
|| || || ||
c3 c2 c1 c0
*/
// https://github.com/kaishiqi/Geometric-Bezier/blob/master/GeometricBezier/src/kaishiqi/geometric/intersection/Intersection.as
//
// Start with Bezier using Bernstein polynomials for weighting functions:
// (1-t^3)P0 + 3t(1-t)^2P1 + 3t^2(1-t)P2 + t^3P3
//
// Expand and collect terms to form linear combinations of original Bezier
// controls. This ends up with a vector cubic in t:
// (-P0+3P1-3P2+P3)t^3 + (3P0-6P1+3P2)t^2 + (-3P0+3P1)t + P0
// /\ /\ /\ /\
// || || || ||
// c3 c2 c1 c0
// Calculate the coefficients
auto c3 = -p0 + 3 * p1 - 3 * p2 + p3;
@@ -683,7 +673,3 @@ inline void ImCubicBezierFixedStep(F& callback, const ImCubicBezierPoints& curve
//------------------------------------------------------------------------------
# endif // __IMGUI_BEZIER_MATH_INL__
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
+37 -13
View File
@@ -1,4 +1,6 @@
# define IMGUI_DEFINE_MATH_OPERATORS
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include "imgui_canvas.h"
# include <type_traits>
@@ -25,6 +27,11 @@
static constexpr bool value = (sizeof(yes_type) == sizeof(test<mixin>(0))); \
}
// Special sentinel value. This needs to be unique, so allow it to be overridden in the user's ImGui config
# ifndef ImDrawCallback_ImCanvas
# define ImDrawCallback_ImCanvas (ImDrawCallback)(-2)
# endif
namespace ImCanvasDetails {
DECLARE_HAS_MEMBER(HasFringeScale, _FringeScale);
@@ -115,7 +122,7 @@ bool ImGuiEx::Canvas::Begin(ImGuiID id, const ImVec2& size)
// #debug: Canvas content.
//m_DrawList->AddRectFilled(m_StartPos, m_StartPos + m_CurrentSize, IM_COL32(0, 0, 0, 64));
m_DrawList->AddRect(m_WidgetRect.Min, m_WidgetRect.Max, IM_COL32(255, 0, 255, 64));
//m_DrawList->AddRect(m_WidgetRect.Min, m_WidgetRect.Max, IM_COL32(255, 0, 255, 64));
ImGui::SetCursorScreenPos(ImVec2(0.0f, 0.0f));
@@ -131,6 +138,10 @@ bool ImGuiEx::Canvas::Begin(ImGuiID id, const ImVec2& size)
EnterLocalSpace();
# if IMGUI_VERSION_NUM >= 18967
ImGui::SetNextItemAllowOverlap();
# endif
// Emit dummy widget matching bounds of the canvas.
ImGui::SetCursorScreenPos(m_ViewRect.Min);
ImGui::Dummy(m_ViewRect.GetSize());
@@ -162,7 +173,9 @@ void ImGuiEx::Canvas::End()
ImGui::GetCurrentWindow()->DC.CursorMaxPos = m_WindowCursorMaxBackup;
# if IMGUI_VERSION_NUM < 18967
ImGui::SetItemAllowOverlap();
# endif
// Emit dummy widget matching bounds of the canvas.
ImGui::SetCursorScreenPos(m_WidgetPosition);
@@ -407,6 +420,15 @@ void ImGuiEx::Canvas::EnterLocalSpace()
auto clipped_clip_rect = m_DrawList->_ClipRectStack.back();
ImGui::PopClipRect();
# if IMGUI_EX_CANVAS_DEFERED()
m_Ranges.resize(m_Ranges.Size + 1);
m_CurrentRange = &m_Ranges.back();
m_CurrentRange->BeginComandIndex = ImMax(m_DrawList->CmdBuffer.Size, 0);
m_CurrentRange->BeginVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
# endif
m_DrawListCommadBufferSize = ImMax(m_DrawList->CmdBuffer.Size, 0);
m_DrawListStartVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
// Make sure we do not share draw command with anyone. We don't want to mess
// with someones clip rectangle.
@@ -421,16 +443,9 @@ void ImGuiEx::Canvas::EnterLocalSpace()
//
// More investigation is needed. To get to the bottom of this.
if ((!m_DrawList->CmdBuffer.empty() && m_DrawList->CmdBuffer.back().ElemCount > 0) || m_DrawList->_Splitter._Count > 1)
m_DrawList->AddDrawCmd();
m_DrawList->AddCallback(ImDrawCallback_ImCanvas, nullptr);
# if IMGUI_EX_CANVAS_DEFERED()
m_Ranges.resize(m_Ranges.Size + 1);
m_CurrentRange = &m_Ranges.back();
m_CurrentRange->BeginComandIndex = ImMax(m_DrawList->CmdBuffer.Size - 1, 0);
m_CurrentRange->BeginVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
# endif
m_DrawListCommadBufferSize = ImMax(m_DrawList->CmdBuffer.Size - 1, 0);
m_DrawListStartVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
m_DrawListFirstCommandIndex = ImMax(m_DrawList->CmdBuffer.Size - 1, 0);
# if defined(IMGUI_HAS_VIEWPORT)
auto window = ImGui::GetCurrentWindow();
@@ -510,7 +525,7 @@ void ImGuiEx::Canvas::LeaveLocalSpace()
}
// Move clip rectangles to screen space.
for (int i = m_DrawListCommadBufferSize; i < m_DrawList->CmdBuffer.size(); ++i)
for (int i = m_DrawListFirstCommandIndex; i < m_DrawList->CmdBuffer.size(); ++i)
{
auto& command = m_DrawList->CmdBuffer[i];
command.ClipRect.x = command.ClipRect.x * m_View.Scale + m_ViewTransformPosition.x;
@@ -529,7 +544,7 @@ void ImGuiEx::Canvas::LeaveLocalSpace()
}
// Move clip rectangles to screen space.
for (int i = m_DrawListCommadBufferSize; i < m_DrawList->CmdBuffer.size(); ++i)
for (int i = m_DrawListFirstCommandIndex; i < m_DrawList->CmdBuffer.size(); ++i)
{
auto& command = m_DrawList->CmdBuffer[i];
command.ClipRect.x = command.ClipRect.x + m_ViewTransformPosition.x;
@@ -539,6 +554,15 @@ void ImGuiEx::Canvas::LeaveLocalSpace()
}
}
// Remove sentinel draw command if present
if (m_DrawListCommadBufferSize > 0)
{
if (m_DrawList->CmdBuffer.size() > m_DrawListCommadBufferSize && m_DrawList->CmdBuffer[m_DrawListCommadBufferSize].UserCallback == ImDrawCallback_ImCanvas)
m_DrawList->CmdBuffer.erase(m_DrawList->CmdBuffer.Data + m_DrawListCommadBufferSize);
else if (m_DrawList->CmdBuffer.size() >= m_DrawListCommadBufferSize && m_DrawList->CmdBuffer[m_DrawListCommadBufferSize - 1].UserCallback == ImDrawCallback_ImCanvas)
m_DrawList->CmdBuffer.erase(m_DrawList->CmdBuffer.Data + m_DrawListCommadBufferSize - 1);
}
auto& fringeScale = ImFringeScaleRef(m_DrawList);
fringeScale = m_LastFringeScale;
+25 -20
View File
@@ -51,6 +51,10 @@
# include <imgui.h>
# include <imgui_internal.h> // ImRect, ImFloor
#ifndef IMGUIEX_CANVAS_API
#define IMGUIEX_CANVAS_API
#endif
namespace ImGuiEx {
struct CanvasView
@@ -106,13 +110,13 @@ struct Canvas
//
// You can query size of the canvas while it is being drawn
// by calling Rect().
bool Begin(const char* id, const ImVec2& size);
bool Begin(ImGuiID id, const ImVec2& size);
IMGUIEX_CANVAS_API bool Begin(const char* id, const ImVec2& size);
IMGUIEX_CANVAS_API bool Begin(ImGuiID id, const ImVec2& size);
// Ends interaction with canvas plane.
//
// Must be called only when Begin() retuned true.
void End();
IMGUIEX_CANVAS_API void End();
// Sets visible region of canvas plane.
//
@@ -120,50 +124,50 @@ struct Canvas
// corner of the canvas.
//
// Scale greater than 1 make canvas content be bigger, less than 1 smaller.
void SetView(const ImVec2& origin, float scale);
void SetView(const CanvasView& view);
IMGUIEX_CANVAS_API void SetView(const ImVec2& origin, float scale);
IMGUIEX_CANVAS_API void SetView(const CanvasView& view);
// Centers view over specific point on canvas plane.
//
// View will be centered on specific point by changing origin
// but not scale.
void CenterView(const ImVec2& canvasPoint);
IMGUIEX_CANVAS_API void CenterView(const ImVec2& canvasPoint);
// Calculates view over specific point on canvas plane.
CanvasView CalcCenterView(const ImVec2& canvasPoint) const;
IMGUIEX_CANVAS_API CanvasView CalcCenterView(const ImVec2& canvasPoint) const;
// Centers view over specific rectangle on canvas plane.
//
// Whole rectangle will fit in canvas view. This will affect both
// origin and scale.
void CenterView(const ImRect& canvasRect);
IMGUIEX_CANVAS_API void CenterView(const ImRect& canvasRect);
// Calculates view over specific rectangle on canvas plane.
CanvasView CalcCenterView(const ImRect& canvasRect) const;
IMGUIEX_CANVAS_API CanvasView CalcCenterView(const ImRect& canvasRect) const;
// Suspends canvas by returning to normal ImGui transformation space.
// While suspended UI will not be drawn on canvas plane.
//
// Calls to Suspend()/Resume() are symetrical. Each call to Suspend()
// must be matched with call to Resume().
void Suspend();
void Resume();
IMGUIEX_CANVAS_API void Suspend();
IMGUIEX_CANVAS_API void Resume();
// Transforms point from canvas plane to ImGui.
ImVec2 FromLocal(const ImVec2& point) const;
ImVec2 FromLocal(const ImVec2& point, const CanvasView& view) const;
IMGUIEX_CANVAS_API ImVec2 FromLocal(const ImVec2& point) const;
IMGUIEX_CANVAS_API ImVec2 FromLocal(const ImVec2& point, const CanvasView& view) const;
// Transforms vector from canvas plant to ImGui.
ImVec2 FromLocalV(const ImVec2& vector) const;
ImVec2 FromLocalV(const ImVec2& vector, const CanvasView& view) const;
IMGUIEX_CANVAS_API ImVec2 FromLocalV(const ImVec2& vector) const;
IMGUIEX_CANVAS_API ImVec2 FromLocalV(const ImVec2& vector, const CanvasView& view) const;
// Transforms point from ImGui to canvas plane.
ImVec2 ToLocal(const ImVec2& point) const;
ImVec2 ToLocal(const ImVec2& point, const CanvasView& view) const;
IMGUIEX_CANVAS_API ImVec2 ToLocal(const ImVec2& point) const;
IMGUIEX_CANVAS_API ImVec2 ToLocal(const ImVec2& point, const CanvasView& view) const;
// Transforms vector from ImGui to canvas plane.
ImVec2 ToLocalV(const ImVec2& vector) const;
ImVec2 ToLocalV(const ImVec2& vector, const CanvasView& view) const;
IMGUIEX_CANVAS_API ImVec2 ToLocalV(const ImVec2& vector) const;
IMGUIEX_CANVAS_API ImVec2 ToLocalV(const ImVec2& vector, const CanvasView& view) const;
// Returns widget bounds.
//
@@ -175,7 +179,7 @@ struct Canvas
const ImRect& ViewRect() const { return m_ViewRect; }
// Calculates visible region for view.
ImRect CalcViewRect(const CanvasView& view) const;
IMGUIEX_CANVAS_API ImRect CalcViewRect(const CanvasView& view) const;
// Returns current view.
const CanvasView& View() const { return m_View; }
@@ -232,6 +236,7 @@ private:
Range* m_CurrentRange = nullptr;
# endif
int m_DrawListFirstCommandIndex = 0;
int m_DrawListCommadBufferSize = 0;
int m_DrawListStartVertexIndex = 0;
+5 -1
View File
@@ -15,10 +15,10 @@
//------------------------------------------------------------------------------
# include <imgui.h>
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include <imgui.h>
# include <imgui_internal.h>
@@ -30,10 +30,14 @@ struct ImLine
//------------------------------------------------------------------------------
# if IMGUI_VERSION_NUM < 19002
inline bool operator==(const ImVec2& lhs, const ImVec2& rhs);
inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs);
# endif
inline ImVec2 operator*(const float lhs, const ImVec2& rhs);
# if IMGUI_VERSION_NUM < 18955
inline ImVec2 operator-(const ImVec2& lhs);
# endif
//------------------------------------------------------------------------------
+4
View File
@@ -19,6 +19,7 @@
//------------------------------------------------------------------------------
# if IMGUI_VERSION_NUM < 19002
inline bool operator==(const ImVec2& lhs, const ImVec2& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y;
@@ -28,16 +29,19 @@ inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs)
{
return lhs.x != rhs.x || lhs.y != rhs.y;
}
# endif
inline ImVec2 operator*(const float lhs, const ImVec2& rhs)
{
return ImVec2(lhs * rhs.x, lhs * rhs.y);
}
# if IMGUI_VERSION_NUM < 18955
inline ImVec2 operator-(const ImVec2& lhs)
{
return ImVec2(-lhs.x, -lhs.y);
}
# endif
//------------------------------------------------------------------------------
+119 -44
View File
@@ -1,10 +1,3 @@
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
@@ -42,6 +35,58 @@
static bool const value = sizeof(test<T>(0)) == sizeof(yes); \
};
namespace ax {
namespace NodeEditor {
namespace Detail {
# if !defined(IMGUI_VERSION_NUM) || (IMGUI_VERSION_NUM < 18822)
# define DECLARE_KEY_TESTER(Key) \
DECLARE_HAS_NESTED(Key, Key) \
struct KeyTester_ ## Key \
{ \
template <typename T> \
static int Get(typename std::enable_if<has_nested_ ## Key<ImGuiKey_>::value, T>::type*) \
{ \
return ImGui::GetKeyIndex(T::Key); \
} \
\
template <typename T> \
static int Get(typename std::enable_if<!has_nested_ ## Key<ImGuiKey_>::value, T>::type*) \
{ \
return -1; \
} \
}
DECLARE_KEY_TESTER(ImGuiKey_F);
DECLARE_KEY_TESTER(ImGuiKey_D);
static inline int GetKeyIndexForF()
{
return KeyTester_ImGuiKey_F::Get<ImGuiKey_>(nullptr);
}
static inline int GetKeyIndexForD()
{
return KeyTester_ImGuiKey_D::Get<ImGuiKey_>(nullptr);
}
# else
static inline ImGuiKey GetKeyIndexForF()
{
return ImGuiKey_F;
}
static inline ImGuiKey GetKeyIndexForD()
{
return ImGuiKey_D;
}
# endif
} // namespace Detail
} // namespace NodeEditor
} // namespace ax
//------------------------------------------------------------------------------
namespace ed = ax::NodeEditor::Detail;
@@ -169,34 +214,34 @@ static void ImDrawListSplitter_Grow(ImDrawList* draw_list, ImDrawListSplitter* s
static void ImDrawList_ChannelsGrow(ImDrawList* draw_list, int channels_count)
{
ImDrawListSplitter_Grow(draw_list, &draw_list->_Splitter, channels_count);
ImDrawListSplitter_Grow(draw_list, &draw_list->_Splitter, channels_count);
}
static void ImDrawListSplitter_SwapChannels(ImDrawListSplitter* splitter, int left, int right)
{
IM_ASSERT(left < splitter->_Count && right < splitter->_Count);
if (left == right)
return;
IM_ASSERT(left < splitter->_Count && right < splitter->_Count);
if (left == right)
return;
auto currentChannel = splitter->_Current;
auto currentChannel = splitter->_Current;
auto* leftCmdBuffer = &splitter->_Channels[left]._CmdBuffer;
auto* leftIdxBuffer = &splitter->_Channels[left]._IdxBuffer;
auto* rightCmdBuffer = &splitter->_Channels[right]._CmdBuffer;
auto* rightIdxBuffer = &splitter->_Channels[right]._IdxBuffer;
auto* leftCmdBuffer = &splitter->_Channels[left]._CmdBuffer;
auto* leftIdxBuffer = &splitter->_Channels[left]._IdxBuffer;
auto* rightCmdBuffer = &splitter->_Channels[right]._CmdBuffer;
auto* rightIdxBuffer = &splitter->_Channels[right]._IdxBuffer;
leftCmdBuffer->swap(*rightCmdBuffer);
leftIdxBuffer->swap(*rightIdxBuffer);
leftCmdBuffer->swap(*rightCmdBuffer);
leftIdxBuffer->swap(*rightIdxBuffer);
if (currentChannel == left)
splitter->_Current = right;
else if (currentChannel == right)
splitter->_Current = left;
if (currentChannel == left)
splitter->_Current = right;
else if (currentChannel == right)
splitter->_Current = left;
}
static void ImDrawList_SwapChannels(ImDrawList* drawList, int left, int right)
{
ImDrawListSplitter_SwapChannels(&drawList->_Splitter, left, right);
ImDrawListSplitter_SwapChannels(&drawList->_Splitter, left, right);
}
static void ImDrawList_SwapSplitter(ImDrawList* drawList, ImDrawListSplitter& splitter)
@@ -655,7 +700,7 @@ void ed::Node::Draw(ImDrawList* drawList, DrawFlags flags)
drawList->ChannelsSetCurrent(m_Channel + c_NodeBaseChannel);
DrawBorder(drawList, borderColor, editorStyle.SelectedNodeBorderWidth);
DrawBorder(drawList, borderColor, editorStyle.SelectedNodeBorderWidth, editorStyle.SelectedNodeBorderOffset);
}
else if (!IsGroup(this) && (flags & Hovered))
{
@@ -664,16 +709,18 @@ void ed::Node::Draw(ImDrawList* drawList, DrawFlags flags)
drawList->ChannelsSetCurrent(m_Channel + c_NodeBaseChannel);
DrawBorder(drawList, borderColor, editorStyle.HoveredNodeBorderWidth);
DrawBorder(drawList, borderColor, editorStyle.HoveredNodeBorderWidth, editorStyle.HoverNodeBorderOffset);
}
}
void ed::Node::DrawBorder(ImDrawList* drawList, ImU32 color, float thickness)
void ed::Node::DrawBorder(ImDrawList* drawList, ImU32 color, float thickness, float offset)
{
if (thickness > 0.0f)
{
drawList->AddRect(m_Bounds.Min, m_Bounds.Max,
color, m_Rounding, c_AllRoundCornersFlags, thickness);
const ImVec2 extraOffset = ImVec2(offset, offset);
drawList->AddRect(m_Bounds.Min - extraOffset, m_Bounds.Max + extraOffset,
color, ImMax(0.0f, m_Rounding + offset), c_AllRoundCornersFlags, thickness);
}
}
@@ -1090,9 +1137,9 @@ void ed::EditorContext::Begin(const char* id, const ImVec2& size)
if (!m_IsInitialized)
{
// Cycle canvas so it has a change to setup its size before settings are loaded
m_Canvas.Begin(id, canvasSize);
m_Canvas.End();
// Cycle canvas, so it has a chance to initialize its size before settings are loaded
if (m_Canvas.Begin(id, canvasSize))
m_Canvas.End();
LoadSettings();
m_IsInitialized = true;
@@ -1161,7 +1208,7 @@ void ed::EditorContext::Begin(const char* id, const ImVec2& size)
auto centerY = (previousVisibleRect.Max.y + previousVisibleRect.Min.y) * 0.5f;
auto currentVisibleRect = m_Canvas.ViewRect();
auto currentAspectRatio = currentVisibleRect.GetHeight() ? (currentVisibleRect.GetWidth() / currentVisibleRect.GetHeight()) : 0.0f;
auto width = previousVisibleRect.GetHeight();
auto width = previousVisibleRect.GetWidth();
auto height = previousVisibleRect.GetHeight();
if (m_Config.CanvasSizeMode == ax::NodeEditor::CanvasSizeMode::FitVerticalView)
@@ -1918,7 +1965,8 @@ void ed::EditorContext::Suspend(SuspendFlags flags)
IM_ASSERT(m_DrawList != nullptr && "Suspend was called outiside of Begin/End.");
auto lastChannel = m_DrawList->_Splitter._Current;
m_DrawList->ChannelsSetCurrent(m_ExternalChannel);
m_Canvas.Suspend();
if (m_IsCanvasVisible)
m_Canvas.Suspend();
m_DrawList->ChannelsSetCurrent(lastChannel);
if ((flags & SuspendFlags::KeepSplitter) != SuspendFlags::KeepSplitter)
ImDrawList_SwapSplitter(m_DrawList, m_Splitter);
@@ -1931,13 +1979,14 @@ void ed::EditorContext::Resume(SuspendFlags flags)
ImDrawList_SwapSplitter(m_DrawList, m_Splitter);
auto lastChannel = m_DrawList->_Splitter._Current;
m_DrawList->ChannelsSetCurrent(m_ExternalChannel);
m_Canvas.Resume();
if (m_IsCanvasVisible)
m_Canvas.Resume();
m_DrawList->ChannelsSetCurrent(lastChannel);
}
bool ed::EditorContext::IsSuspended()
{
return m_Canvas.IsSuspended();
return m_Canvas.IsSuspended();
}
bool ed::EditorContext::IsFocused()
@@ -2513,9 +2562,12 @@ ed::Control ed::EditorContext::BuildControl(bool allowOffscreen)
if (!allowOffscreen && !m_IsHovered)
return Control();
# if IMGUI_VERSION_NUM >= 17909
# if IMGUI_VERSION_NUM >= 18836
if (m_IsHoveredWithoutOverlapp)
ImGui::SetItemKeyOwner(ImGuiKey_MouseWheelY);
# elif IMGUI_VERSION_NUM >= 17909
if (m_IsHoveredWithoutOverlapp)
ImGui::SetItemUsingMouseWheel();
# endif
return Control(hotObject, activeObject, clickedObject, doubleClickedObject,
@@ -3281,7 +3333,7 @@ ed::EditorAction::AcceptResult ed::NavigateAction::Accept(const Control& control
auto& io = ImGui::GetIO();
if (Editor->CanAcceptUserInput() && ImGui::IsKeyPressed(ImGuiKey_F) && Editor->AreShortcutsEnabled())
if (Editor->CanAcceptUserInput() && ImGui::IsKeyPressed(GetKeyIndexForF()) && Editor->AreShortcutsEnabled())
{
const auto zoomMode = io.KeyShift ? NavigateAction::ZoomMode::WithMargin : NavigateAction::ZoomMode::None;
@@ -3397,8 +3449,7 @@ bool ed::NavigateAction::HandleZoom(const Control& control)
m_Animation.Finish();
auto mousePos = io.MousePos;
auto steps = (int)io.MouseWheel;
auto newZoom = MatchZoom(steps, m_ZoomLevels[steps < 0 ? 0 : m_ZoomLevelCount - 1]);
auto newZoom = GetNextZoom(io.MouseWheel);
auto oldView = GetView();
m_Zoom = newZoom;
@@ -3574,6 +3625,32 @@ ImRect ed::NavigateAction::GetViewRect() const
return m_Canvas.CalcViewRect(GetView());
}
float ed::NavigateAction::GetNextZoom(float steps)
{
if (this->Editor->GetConfig().EnableSmoothZoom)
{
return MatchSmoothZoom(steps);
}
else
{
auto fixedSteps = (int)steps;
return MatchZoom(fixedSteps, m_ZoomLevels[fixedSteps < 0 ? 0 : m_ZoomLevelCount - 1]);
}
}
float ed::NavigateAction::MatchSmoothZoom(float steps)
{
const auto power = Editor->GetConfig().SmoothZoomPower;
const auto newZoom = m_Zoom * powf(power, steps);
if (newZoom < m_ZoomLevels[0])
return m_ZoomLevels[0];
else if (newZoom > m_ZoomLevels[m_ZoomLevelCount - 1])
return m_ZoomLevels[m_ZoomLevelCount - 1];
else
return newZoom;
}
float ed::NavigateAction::MatchZoom(int steps, float fallbackZoom)
{
auto currentZoomIndex = MatchZoomIndex(steps);
@@ -4320,7 +4397,7 @@ ed::EditorAction::AcceptResult ed::ShortcutAction::Accept(const Control& control
candidateAction = Copy;
if (io.KeyCtrl && !io.KeyShift && !io.KeyAlt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_V)))
candidateAction = Paste;
if (io.KeyCtrl && !io.KeyShift && !io.KeyAlt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_D)))
if (io.KeyCtrl && !io.KeyShift && !io.KeyAlt && ImGui::IsKeyPressed(GetKeyIndexForD()))
candidateAction = Duplicate;
if (!io.KeyCtrl && !io.KeyShift && !io.KeyAlt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Space)))
candidateAction = CreateNode;
@@ -5650,6 +5727,8 @@ float* ed::Style::GetVarFloatAddr(StyleVar idx)
case StyleVar_GroupBorderWidth: return &GroupBorderWidth;
case StyleVar_HighlightConnectedLinks: return &HighlightConnectedLinks;
case StyleVar_SnapLinkToPinDir: return &SnapLinkToPinDir;
case StyleVar_HoveredNodeBorderOffset: return &HoverNodeBorderOffset;
case StyleVar_SelectedNodeBorderOffset: return &SelectedNodeBorderOffset;
default: return nullptr;
}
}
@@ -5774,7 +5853,3 @@ void ed::Config::EndSave()
if (EndSaveSession)
EndSaveSession(UserPointer);
}
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
+163 -144
View File
@@ -15,15 +15,20 @@
//------------------------------------------------------------------------------
#define IMGUI_DEFINE_MATH_OPERATORS
# include <imgui.h>
# include <cstdint> // std::uintXX_t
# include <utility> // std::move
//------------------------------------------------------------------------------
# define IMGUI_NODE_EDITOR_VERSION "0.9.2"
# define IMGUI_NODE_EDITOR_VERSION_NUM 000902
# define IMGUI_NODE_EDITOR_VERSION "0.9.4"
# define IMGUI_NODE_EDITOR_VERSION_NUM 000904
//------------------------------------------------------------------------------
#ifndef IMGUI_NODE_EDITOR_API
#define IMGUI_NODE_EDITOR_API
#endif
//------------------------------------------------------------------------------
@@ -100,6 +105,8 @@ struct Config
int SelectButtonIndex; // Mouse button index select action will react to (0-left, 1-right, 2-middle)
int NavigateButtonIndex; // Mouse button index navigate action will react to (0-left, 1-right, 2-middle)
int ContextMenuButtonIndex; // Mouse button index context menu action will react to (0-left, 1-right, 2-middle)
bool EnableSmoothZoom;
float SmoothZoomPower;
Config()
: SettingsFile("NodeEditor.json")
@@ -116,6 +123,12 @@ struct Config
, SelectButtonIndex(0)
, NavigateButtonIndex(1)
, ContextMenuButtonIndex(1)
, EnableSmoothZoom(false)
# ifdef __APPLE__
, SmoothZoomPower(1.1f)
# else
, SmoothZoomPower(1.3f)
# endif
{
}
};
@@ -174,6 +187,8 @@ enum StyleVar
StyleVar_GroupBorderWidth,
StyleVar_HighlightConnectedLinks,
StyleVar_SnapLinkToPinDir,
StyleVar_HoveredNodeBorderOffset,
StyleVar_SelectedNodeBorderOffset,
StyleVar_Count
};
@@ -184,7 +199,9 @@ struct Style
float NodeRounding;
float NodeBorderWidth;
float HoveredNodeBorderWidth;
float HoverNodeBorderOffset;
float SelectedNodeBorderWidth;
float SelectedNodeBorderOffset;
float PinRounding;
float PinBorderWidth;
float LinkStrength;
@@ -209,35 +226,37 @@ struct Style
Style()
{
NodePadding = ImVec4(8, 8, 8, 8);
NodeRounding = 12.0f;
NodeBorderWidth = 1.5f;
HoveredNodeBorderWidth = 3.5f;
SelectedNodeBorderWidth = 3.5f;
PinRounding = 4.0f;
PinBorderWidth = 0.0f;
LinkStrength = 100.0f;
SourceDirection = ImVec2(1.0f, 0.0f);
TargetDirection = ImVec2(-1.0f, 0.0f);
ScrollDuration = 0.35f;
FlowMarkerDistance = 30.0f;
FlowSpeed = 150.0f;
FlowDuration = 2.0f;
PivotAlignment = ImVec2(0.5f, 0.5f);
PivotSize = ImVec2(0.0f, 0.0f);
PivotScale = ImVec2(1, 1);
NodePadding = ImVec4(8, 8, 8, 8);
NodeRounding = 12.0f;
NodeBorderWidth = 1.5f;
HoveredNodeBorderWidth = 3.5f;
HoverNodeBorderOffset = 0.0f;
SelectedNodeBorderWidth = 3.5f;
SelectedNodeBorderOffset = 0.0f;
PinRounding = 4.0f;
PinBorderWidth = 0.0f;
LinkStrength = 100.0f;
SourceDirection = ImVec2(1.0f, 0.0f);
TargetDirection = ImVec2(-1.0f, 0.0f);
ScrollDuration = 0.35f;
FlowMarkerDistance = 30.0f;
FlowSpeed = 150.0f;
FlowDuration = 2.0f;
PivotAlignment = ImVec2(0.5f, 0.5f);
PivotSize = ImVec2(0.0f, 0.0f);
PivotScale = ImVec2(1, 1);
#if IMGUI_VERSION_NUM > 18101
PinCorners = ImDrawFlags_RoundCornersAll;
PinCorners = ImDrawFlags_RoundCornersAll;
#else
PinCorners = ImDrawCornerFlags_All;
PinCorners = ImDrawCornerFlags_All;
#endif
PinRadius = 0.0f;
PinArrowSize = 0.0f;
PinArrowWidth = 0.0f;
GroupRounding = 6.0f;
GroupBorderWidth = 1.0f;
HighlightConnectedLinks = 0.0f;
SnapLinkToPinDir = 0.0f;
PinRadius = 0.0f;
PinArrowSize = 0.0f;
PinArrowWidth = 0.0f;
GroupRounding = 6.0f;
GroupBorderWidth = 1.0f;
HighlightConnectedLinks = 0.0f;
SnapLinkToPinDir = 0.0f;
Colors[StyleColor_Bg] = ImColor( 60, 60, 70, 200);
Colors[StyleColor_Grid] = ImColor(120, 120, 120, 40);
@@ -267,150 +286,150 @@ struct EditorContext;
//------------------------------------------------------------------------------
void SetCurrentEditor(EditorContext* ctx);
EditorContext* GetCurrentEditor();
EditorContext* CreateEditor(const Config* config = nullptr);
void DestroyEditor(EditorContext* ctx);
const Config& GetConfig(EditorContext* ctx = nullptr);
IMGUI_NODE_EDITOR_API void SetCurrentEditor(EditorContext* ctx);
IMGUI_NODE_EDITOR_API EditorContext* GetCurrentEditor();
IMGUI_NODE_EDITOR_API EditorContext* CreateEditor(const Config* config = nullptr);
IMGUI_NODE_EDITOR_API void DestroyEditor(EditorContext* ctx);
IMGUI_NODE_EDITOR_API const Config& GetConfig(EditorContext* ctx = nullptr);
Style& GetStyle();
const char* GetStyleColorName(StyleColor colorIndex);
IMGUI_NODE_EDITOR_API Style& GetStyle();
IMGUI_NODE_EDITOR_API const char* GetStyleColorName(StyleColor colorIndex);
void PushStyleColor(StyleColor colorIndex, const ImVec4& color);
void PopStyleColor(int count = 1);
IMGUI_NODE_EDITOR_API void PushStyleColor(StyleColor colorIndex, const ImVec4& color);
IMGUI_NODE_EDITOR_API void PopStyleColor(int count = 1);
void PushStyleVar(StyleVar varIndex, float value);
void PushStyleVar(StyleVar varIndex, const ImVec2& value);
void PushStyleVar(StyleVar varIndex, const ImVec4& value);
void PopStyleVar(int count = 1);
IMGUI_NODE_EDITOR_API void PushStyleVar(StyleVar varIndex, float value);
IMGUI_NODE_EDITOR_API void PushStyleVar(StyleVar varIndex, const ImVec2& value);
IMGUI_NODE_EDITOR_API void PushStyleVar(StyleVar varIndex, const ImVec4& value);
IMGUI_NODE_EDITOR_API void PopStyleVar(int count = 1);
void Begin(const char* id, const ImVec2& size = ImVec2(0, 0));
void End();
IMGUI_NODE_EDITOR_API void Begin(const char* id, const ImVec2& size = ImVec2(0, 0));
IMGUI_NODE_EDITOR_API void End();
void BeginNode(NodeId id);
void BeginPin(PinId id, PinKind kind);
void PinRect(const ImVec2& a, const ImVec2& b);
void PinPivotRect(const ImVec2& a, const ImVec2& b);
void PinPivotSize(const ImVec2& size);
void PinPivotScale(const ImVec2& scale);
void PinPivotAlignment(const ImVec2& alignment);
void EndPin();
void Group(const ImVec2& size);
void EndNode();
IMGUI_NODE_EDITOR_API void BeginNode(NodeId id);
IMGUI_NODE_EDITOR_API void BeginPin(PinId id, PinKind kind);
IMGUI_NODE_EDITOR_API void PinRect(const ImVec2& a, const ImVec2& b);
IMGUI_NODE_EDITOR_API void PinPivotRect(const ImVec2& a, const ImVec2& b);
IMGUI_NODE_EDITOR_API void PinPivotSize(const ImVec2& size);
IMGUI_NODE_EDITOR_API void PinPivotScale(const ImVec2& scale);
IMGUI_NODE_EDITOR_API void PinPivotAlignment(const ImVec2& alignment);
IMGUI_NODE_EDITOR_API void EndPin();
IMGUI_NODE_EDITOR_API void Group(const ImVec2& size);
IMGUI_NODE_EDITOR_API void EndNode();
bool BeginGroupHint(NodeId nodeId);
ImVec2 GetGroupMin();
ImVec2 GetGroupMax();
ImDrawList* GetHintForegroundDrawList();
ImDrawList* GetHintBackgroundDrawList();
void EndGroupHint();
IMGUI_NODE_EDITOR_API bool BeginGroupHint(NodeId nodeId);
IMGUI_NODE_EDITOR_API ImVec2 GetGroupMin();
IMGUI_NODE_EDITOR_API ImVec2 GetGroupMax();
IMGUI_NODE_EDITOR_API ImDrawList* GetHintForegroundDrawList();
IMGUI_NODE_EDITOR_API ImDrawList* GetHintBackgroundDrawList();
IMGUI_NODE_EDITOR_API void EndGroupHint();
// TODO: Add a way to manage node background channels
ImDrawList* GetNodeBackgroundDrawList(NodeId nodeId);
IMGUI_NODE_EDITOR_API ImDrawList* GetNodeBackgroundDrawList(NodeId nodeId);
bool Link(LinkId id, PinId startPinId, PinId endPinId, const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
IMGUI_NODE_EDITOR_API bool Link(LinkId id, PinId startPinId, PinId endPinId, const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
void Flow(LinkId linkId, FlowDirection direction = FlowDirection::Forward);
IMGUI_NODE_EDITOR_API void Flow(LinkId linkId, FlowDirection direction = FlowDirection::Forward);
bool BeginCreate(const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
bool QueryNewLink(PinId* startId, PinId* endId);
bool QueryNewLink(PinId* startId, PinId* endId, const ImVec4& color, float thickness = 1.0f);
bool QueryNewNode(PinId* pinId);
bool QueryNewNode(PinId* pinId, const ImVec4& color, float thickness = 1.0f);
bool AcceptNewItem();
bool AcceptNewItem(const ImVec4& color, float thickness = 1.0f);
void RejectNewItem();
void RejectNewItem(const ImVec4& color, float thickness = 1.0f);
void EndCreate();
IMGUI_NODE_EDITOR_API bool BeginCreate(const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
IMGUI_NODE_EDITOR_API bool QueryNewLink(PinId* startId, PinId* endId);
IMGUI_NODE_EDITOR_API bool QueryNewLink(PinId* startId, PinId* endId, const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API bool QueryNewNode(PinId* pinId);
IMGUI_NODE_EDITOR_API bool QueryNewNode(PinId* pinId, const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API bool AcceptNewItem();
IMGUI_NODE_EDITOR_API bool AcceptNewItem(const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API void RejectNewItem();
IMGUI_NODE_EDITOR_API void RejectNewItem(const ImVec4& color, float thickness = 1.0f);
IMGUI_NODE_EDITOR_API void EndCreate();
bool BeginDelete();
bool QueryDeletedLink(LinkId* linkId, PinId* startId = nullptr, PinId* endId = nullptr);
bool QueryDeletedNode(NodeId* nodeId);
bool AcceptDeletedItem(bool deleteDependencies = true);
void RejectDeletedItem();
void EndDelete();
IMGUI_NODE_EDITOR_API bool BeginDelete();
IMGUI_NODE_EDITOR_API bool QueryDeletedLink(LinkId* linkId, PinId* startId = nullptr, PinId* endId = nullptr);
IMGUI_NODE_EDITOR_API bool QueryDeletedNode(NodeId* nodeId);
IMGUI_NODE_EDITOR_API bool AcceptDeletedItem(bool deleteDependencies = true);
IMGUI_NODE_EDITOR_API void RejectDeletedItem();
IMGUI_NODE_EDITOR_API void EndDelete();
void SetNodePosition(NodeId nodeId, const ImVec2& editorPosition);
void SetGroupSize(NodeId nodeId, const ImVec2& size);
ImVec2 GetNodePosition(NodeId nodeId);
ImVec2 GetNodeSize(NodeId nodeId);
void CenterNodeOnScreen(NodeId nodeId);
void SetNodeZPosition(NodeId nodeId, float z); // Sets node z position, nodes with higher value are drawn over nodes with lower value
float GetNodeZPosition(NodeId nodeId); // Returns node z position, defaults is 0.0f
IMGUI_NODE_EDITOR_API void SetNodePosition(NodeId nodeId, const ImVec2& editorPosition);
IMGUI_NODE_EDITOR_API void SetGroupSize(NodeId nodeId, const ImVec2& size);
IMGUI_NODE_EDITOR_API ImVec2 GetNodePosition(NodeId nodeId);
IMGUI_NODE_EDITOR_API ImVec2 GetNodeSize(NodeId nodeId);
IMGUI_NODE_EDITOR_API void CenterNodeOnScreen(NodeId nodeId);
IMGUI_NODE_EDITOR_API void SetNodeZPosition(NodeId nodeId, float z); // Sets node z position, nodes with higher value are drawn over nodes with lower value
IMGUI_NODE_EDITOR_API float GetNodeZPosition(NodeId nodeId); // Returns node z position, defaults is 0.0f
void RestoreNodeState(NodeId nodeId);
IMGUI_NODE_EDITOR_API void RestoreNodeState(NodeId nodeId);
void Suspend();
void Resume();
bool IsSuspended();
IMGUI_NODE_EDITOR_API void Suspend();
IMGUI_NODE_EDITOR_API void Resume();
IMGUI_NODE_EDITOR_API bool IsSuspended();
bool IsActive();
IMGUI_NODE_EDITOR_API bool IsActive();
bool HasSelectionChanged();
int GetSelectedObjectCount();
int GetSelectedNodes(NodeId* nodes, int size);
int GetSelectedLinks(LinkId* links, int size);
bool IsNodeSelected(NodeId nodeId);
bool IsLinkSelected(LinkId linkId);
void ClearSelection();
void SelectNode(NodeId nodeId, bool append = false);
void SelectLink(LinkId linkId, bool append = false);
void DeselectNode(NodeId nodeId);
void DeselectLink(LinkId linkId);
IMGUI_NODE_EDITOR_API bool HasSelectionChanged();
IMGUI_NODE_EDITOR_API int GetSelectedObjectCount();
IMGUI_NODE_EDITOR_API int GetSelectedNodes(NodeId* nodes, int size);
IMGUI_NODE_EDITOR_API int GetSelectedLinks(LinkId* links, int size);
IMGUI_NODE_EDITOR_API bool IsNodeSelected(NodeId nodeId);
IMGUI_NODE_EDITOR_API bool IsLinkSelected(LinkId linkId);
IMGUI_NODE_EDITOR_API void ClearSelection();
IMGUI_NODE_EDITOR_API void SelectNode(NodeId nodeId, bool append = false);
IMGUI_NODE_EDITOR_API void SelectLink(LinkId linkId, bool append = false);
IMGUI_NODE_EDITOR_API void DeselectNode(NodeId nodeId);
IMGUI_NODE_EDITOR_API void DeselectLink(LinkId linkId);
bool DeleteNode(NodeId nodeId);
bool DeleteLink(LinkId linkId);
IMGUI_NODE_EDITOR_API bool DeleteNode(NodeId nodeId);
IMGUI_NODE_EDITOR_API bool DeleteLink(LinkId linkId);
bool HasAnyLinks(NodeId nodeId); // Returns true if node has any link connected
bool HasAnyLinks(PinId pinId); // Return true if pin has any link connected
int BreakLinks(NodeId nodeId); // Break all links connected to this node
int BreakLinks(PinId pinId); // Break all links connected to this pin
IMGUI_NODE_EDITOR_API bool HasAnyLinks(NodeId nodeId); // Returns true if node has any link connected
IMGUI_NODE_EDITOR_API bool HasAnyLinks(PinId pinId); // Return true if pin has any link connected
IMGUI_NODE_EDITOR_API int BreakLinks(NodeId nodeId); // Break all links connected to this node
IMGUI_NODE_EDITOR_API int BreakLinks(PinId pinId); // Break all links connected to this pin
void NavigateToContent(float duration = -1);
void NavigateToSelection(bool zoomIn = false, float duration = -1);
IMGUI_NODE_EDITOR_API void NavigateToContent(float duration = -1);
IMGUI_NODE_EDITOR_API void NavigateToSelection(bool zoomIn = false, float duration = -1);
bool ShowNodeContextMenu(NodeId* nodeId);
bool ShowPinContextMenu(PinId* pinId);
bool ShowLinkContextMenu(LinkId* linkId);
bool ShowBackgroundContextMenu();
IMGUI_NODE_EDITOR_API bool ShowNodeContextMenu(NodeId* nodeId);
IMGUI_NODE_EDITOR_API bool ShowPinContextMenu(PinId* pinId);
IMGUI_NODE_EDITOR_API bool ShowLinkContextMenu(LinkId* linkId);
IMGUI_NODE_EDITOR_API bool ShowBackgroundContextMenu();
void EnableShortcuts(bool enable);
bool AreShortcutsEnabled();
IMGUI_NODE_EDITOR_API void EnableShortcuts(bool enable);
IMGUI_NODE_EDITOR_API bool AreShortcutsEnabled();
bool BeginShortcut();
bool AcceptCut();
bool AcceptCopy();
bool AcceptPaste();
bool AcceptDuplicate();
bool AcceptCreateNode();
int GetActionContextSize();
int GetActionContextNodes(NodeId* nodes, int size);
int GetActionContextLinks(LinkId* links, int size);
void EndShortcut();
IMGUI_NODE_EDITOR_API bool BeginShortcut();
IMGUI_NODE_EDITOR_API bool AcceptCut();
IMGUI_NODE_EDITOR_API bool AcceptCopy();
IMGUI_NODE_EDITOR_API bool AcceptPaste();
IMGUI_NODE_EDITOR_API bool AcceptDuplicate();
IMGUI_NODE_EDITOR_API bool AcceptCreateNode();
IMGUI_NODE_EDITOR_API int GetActionContextSize();
IMGUI_NODE_EDITOR_API int GetActionContextNodes(NodeId* nodes, int size);
IMGUI_NODE_EDITOR_API int GetActionContextLinks(LinkId* links, int size);
IMGUI_NODE_EDITOR_API void EndShortcut();
float GetCurrentZoom();
IMGUI_NODE_EDITOR_API float GetCurrentZoom();
NodeId GetHoveredNode();
PinId GetHoveredPin();
LinkId GetHoveredLink();
NodeId GetDoubleClickedNode();
PinId GetDoubleClickedPin();
LinkId GetDoubleClickedLink();
bool IsBackgroundClicked();
bool IsBackgroundDoubleClicked();
ImGuiMouseButton GetBackgroundClickButtonIndex(); // -1 if none
ImGuiMouseButton GetBackgroundDoubleClickButtonIndex(); // -1 if none
IMGUI_NODE_EDITOR_API NodeId GetHoveredNode();
IMGUI_NODE_EDITOR_API PinId GetHoveredPin();
IMGUI_NODE_EDITOR_API LinkId GetHoveredLink();
IMGUI_NODE_EDITOR_API NodeId GetDoubleClickedNode();
IMGUI_NODE_EDITOR_API PinId GetDoubleClickedPin();
IMGUI_NODE_EDITOR_API LinkId GetDoubleClickedLink();
IMGUI_NODE_EDITOR_API bool IsBackgroundClicked();
IMGUI_NODE_EDITOR_API bool IsBackgroundDoubleClicked();
IMGUI_NODE_EDITOR_API ImGuiMouseButton GetBackgroundClickButtonIndex(); // -1 if none
IMGUI_NODE_EDITOR_API ImGuiMouseButton GetBackgroundDoubleClickButtonIndex(); // -1 if none
bool GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId); // pass nullptr if particular pin do not interest you
IMGUI_NODE_EDITOR_API bool GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId); // pass nullptr if particular pin do not interest you
bool PinHadAnyLinks(PinId pinId);
IMGUI_NODE_EDITOR_API bool PinHadAnyLinks(PinId pinId);
ImVec2 GetScreenSize();
ImVec2 ScreenToCanvas(const ImVec2& pos);
ImVec2 CanvasToScreen(const ImVec2& pos);
IMGUI_NODE_EDITOR_API ImVec2 GetScreenSize();
IMGUI_NODE_EDITOR_API ImVec2 ScreenToCanvas(const ImVec2& pos);
IMGUI_NODE_EDITOR_API ImVec2 CanvasToScreen(const ImVec2& pos);
int GetNodeCount(); // Returns number of submitted nodes since Begin() call
int GetOrderedNodeIds(NodeId* nodes, int size); // Fills an array with node id's in order they're drawn; up to 'size` elements are set. Returns actual size of filled id's.
IMGUI_NODE_EDITOR_API int GetNodeCount(); // Returns number of submitted nodes since Begin() call
IMGUI_NODE_EDITOR_API int GetOrderedNodeIds(NodeId* nodes, int size); // Fills an array with node id's in order they're drawn; up to 'size` elements are set. Returns actual size of filled id's.
-12
View File
@@ -1,10 +1,3 @@
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
@@ -767,8 +760,3 @@ int ax::NodeEditor::GetOrderedNodeIds(NodeId* nodes, int size)
{
return s_Editor->GetNodeIds(nodes, size);
}
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
+6 -14
View File
@@ -1,10 +1,3 @@
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//------------------------------------------------------------------------------
// VERSION 0.9.1
//
@@ -22,12 +15,14 @@
//------------------------------------------------------------------------------
# ifndef IMGUI_DEFINE_MATH_OPERATORS
# define IMGUI_DEFINE_MATH_OPERATORS
# endif
# include "imgui_node_editor.h"
//------------------------------------------------------------------------------
# include <imgui.h>
# define IMGUI_DEFINE_MATH_OPERATORS
# include <imgui_internal.h>
# include "imgui_extra_math.h"
# include "imgui_bezier_math.h"
@@ -440,7 +435,7 @@ struct Node final: Object
virtual bool IsSelectable() override { return true; }
virtual void Draw(ImDrawList* drawList, DrawFlags flags = None) override final;
void DrawBorder(ImDrawList* drawList, ImU32 color, float thickness = 1.0f);
void DrawBorder(ImDrawList* drawList, ImU32 color, float thickness = 1.0f, float offset = 0.0f);
void GetGroupedNodes(std::vector<Node*>& result, bool append = false);
@@ -891,6 +886,8 @@ private:
void NavigateTo(const ImRect& target, float duration = -1.0f, NavigationReason reason = NavigationReason::Unknown);
float GetNextZoom(float steps);
float MatchSmoothZoom(float steps);
float MatchZoom(int steps, float fallbackZoom);
int MatchZoomIndex(int direction);
@@ -1561,8 +1558,3 @@ private:
//------------------------------------------------------------------------------
# endif // __IMGUI_NODE_EDITOR_INTERNAL_H__
//Disable a bunch of warnings for now
#ifndef _MSC_VER
#pragma GCC diagnostic pop
#endif
+16
View File
@@ -1,3 +1,19 @@
Release version 0.14.3
----------------------
* Build pipeline
- Adds vs2022 compiler support for fbxsdk (#170)
Release version 0.14.2
----------------------
* Library
- Transitions away from sprintf to the more secure snprintf.
- #147 Works around gcc 11 error stringop-overflow which emits false positives for ozz math serialization.
* Build pipeline
- Updates CI compiler versions.
Release version 0.14.1
----------------------
+7 -5
View File
@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.3)
cmake_minimum_required(VERSION 3.24)
# Defines the project's name
project(ozz)
@@ -9,7 +9,7 @@ get_directory_property(is_sub_project PARENT_DIRECTORY)
# Current version
set(OZZ_VERSION_MAJOR 0)
set(OZZ_VERSION_MINOR 14)
set(OZZ_VERSION_PATCH 1)
set(OZZ_VERSION_PATCH 3)
set(OZZ_VERSION ${OZZ_VERSION_MAJOR}.${OZZ_VERSION_MINOR}.${OZZ_VERSION_PATCH})
# Add project build options
@@ -30,6 +30,7 @@ if(WIN32 AND BUILD_SHARED_LIBS AND NOT ozz_build_msvc_rt_dll)
message("Forcing ozz_build_msvc_rt_dll to ON as ozz is being built as dll (BUILD_SHARED_LIBS is ON).")
set(ozz_build_msvc_rt_dll ON)
endif()
if(is_sub_project)
set(ozz_build_msvc_rt_dll ${ozz_build_msvc_rt_dll} PARENT_SCOPE)
endif()
@@ -51,7 +52,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/build-utils/cm
# Detects Fbx SDK, required to build Fbx pipeline.
if(ozz_build_tools AND ozz_build_fbx)
# Select a msvc runtime compatible with ozz_build_msvc_rt_dll
set(FBX_SHARED ${BUILD_SHARED_LIBS})
set(FBX_MSVC_RT_DLL ${ozz_build_msvc_rt_dll})
@@ -69,15 +69,17 @@ else()
# Disables fbx if tools are disabled
set(ozz_build_fbx OFF)
endif()
if(is_sub_project)
set(ozz_build_fbx ${ozz_build_fbx} PARENT_SCOPE)
endif()
# gltf
# gltf
if(ozz_build_tools AND ozz_build_gltf)
else()
set(ozz_build_gltf OFF)
endif()
if(is_sub_project)
set(ozz_build_gltf ${ozz_build_gltf} PARENT_SCOPE)
endif()
@@ -131,4 +133,4 @@ install(FILES
${PROJECT_SOURCE_DIR}/CHANGES.md
${PROJECT_SOURCE_DIR}/LICENSE.md
${PROJECT_SOURCE_DIR}/README.md
DESTINATION ./)
DESTINATION "share/doc/ozz-animation/")
+1 -1
View File
@@ -18,7 +18,7 @@ Documentation and samples are available from [ozz-animation website](http://guil
Supported platforms
-------------------
Ozz is tested on Linux, Mac OS and Windows, for x86, x86-64 and ARM architectures. The run-time code (ozz_base, ozz_animation, ozz_geometry) depends only on c++11, the standard CRT and has no OS specific code, portability to any other platform shouldn't be an issue.
Ozz is tested on Linux, Mac OS and Windows, for x86, x86-64 and ARM architectures. The run-time code (ozz_base, ozz_animation, ozz_geometry) depends only on c++11, on the C and the C++ standard libraries, and has no OS specific code. Portability to any other platform shouldn't be an issue.
Samples, tools and tests depend on external libraries (glfw, tinygltf, Fbx SDK, jsoncpp, gtest, ...), which could limit portability.
@@ -1,18 +1,18 @@
# Set compilers settings for all platforms/compilers.
#---------------------------------------------------
# ---------------------------------------------------
#-----------------
# -----------------
# Includes modules
include(CheckIncludeFiles)
#------------------------------
# ------------------------------
# Enables IDE folders y default
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
#------------------------
# ------------------------
# Available build options
#------------------------
# ------------------------
# Lists all the cxx flags
set(cxx_all_flags
CMAKE_CXX_FLAGS
@@ -26,13 +26,14 @@ set(cxx_all_flags
CMAKE_CXX_FLAGS_RELEASE
CMAKE_C_FLAGS_RELEASE)
#--------------------------------------
# --------------------------------------
# Cross compiler compilation flags
# Requires C++11
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
@@ -41,15 +42,15 @@ if(ozz_build_simd_ref)
add_compile_definitions(OZZ_BUILD_SIMD_REF)
endif()
# Disables crt secure warnings
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
#--------------------------------------
# --------------------------------------
# Modify default MSVC compilation flags
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
#---------------------------
# ---------------------------
# For the common build flags
# Disables crt secure warnings
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
# Adds support for multiple processes builds
add_compile_options(/MP)
@@ -60,62 +61,61 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
add_compile_options(/WX)
# Select whether to use the DLL version or the static library version of the Visual C++ runtime library.
foreach(flag ${cxx_all_flags})
if (ozz_build_msvc_rt_dll)
string(REGEX REPLACE "/MT" "/MD" ${flag} "${${flag}}")
else()
string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}")
endif()
endforeach()
if(ozz_build_msvc_rt_dll)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
else()
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
#--------------------------------------
# --------------------------------------
# else consider the compiler as GCC compatible (inc clang)
else()
# Set the warning level to Wall
add_compile_options(-Wall)
# Enable extra level of warning
#add_compile_options(-Wextra)
# add_compile_options(-Wextra)
# Set warning as error
add_compile_options(-Werror)
# ignored-attributes reports issue when using _m128 as template argument
check_cxx_compiler_flag("-Wignored-attributes" W_IGNORED_ATTRIBUTES)
if(W_IGNORED_ATTRIBUTES)
add_compile_options(-Wno-ignored-attributes)
endif()
# Disables c98 retrocompatibility warnings
check_cxx_compiler_flag("-Wc++98-compat-pedantic" W_98_COMPAT_PEDANTIC)
if(W_98_COMPAT_PEDANTIC)
add_compile_options(-Wno-c++98-compat-pedantic)
endif()
# Check some options availibity for the targetted compiler
check_cxx_compiler_flag("-Wunused-result" W_UNUSED_RESULT)
check_cxx_compiler_flag("-Wnull-dereference" W_NULL_DEREFERENCE)
check_cxx_compiler_flag("-Wpragma-pack" W_PRAGMA_PACK)
#----------------------
# ----------------------
# Sets emscripten output
if(EMSCRIPTEN)
SET(CMAKE_EXECUTABLE_SUFFIX ".html")
add_link_options(-s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0)
#if(NOT ozz_build_simd_ref)
# set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-msse2")
#endif()
# if(NOT ozz_build_simd_ref)
# set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-msse2")
# endif()
endif()
endif()
#---------------------
# ---------------------
# Prints all the flags
message(STATUS "---------------------------------------------------------")
message(STATUS "Default build type is: ${CMAKE_BUILD_TYPE}")
message(STATUS "The following compilation flags will be used:")
foreach(flag ${cxx_all_flags})
message(${flag} " ${${flag}}")
endforeach()
@@ -124,6 +124,7 @@ message(STATUS "---------------------------------------------------------")
get_directory_property(DirectoryCompileOptions DIRECTORY ${PROJECT_SOURCE_DIR} COMPILE_OPTIONS)
message(STATUS "Directory Compile Options:")
foreach(opt ${DirectoryCompileOptions})
message(STATUS ${opt})
endforeach()
@@ -132,13 +133,14 @@ message(STATUS "---------------------------------------------------------")
get_directory_property(DirectoryCompileDefinitions DIRECTORY ${PROJECT_SOURCE_DIR} COMPILE_DEFINITIONS)
message(STATUS "Directory Compile Definitions:")
foreach(def ${DirectoryCompileDefinitions})
message(STATUS ${def})
endforeach()
message(STATUS "---------------------------------------------------------")
#----------------------------------------------
# ----------------------------------------------
# Modifies output directory for all executables
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ".")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ".")
@@ -146,7 +148,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ".")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ".")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ".")
#-------------------------------
# -------------------------------
# Set a postfix for output files
if(ozz_build_postfix)
set(CMAKE_DEBUG_POSTFIX "_d")
@@ -67,7 +67,9 @@ function(FindFbxLibrariesGeneric _FBX_ROOT_DIR _OUT_FBX_LIBRARIES _OUT_FBX_LIBRA
# Figures out matching compiler/os directory.
if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20)
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.30)
set(FBX_CP_PATH "vs2022")
elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.20)
set(FBX_CP_PATH "vs2019")
elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10)
set(FBX_CP_PATH "vs2017")
-2
View File
@@ -57,8 +57,6 @@ if(UNIX AND APPLE)
lib/cocoa/cocoa_joystick.m
lib/cocoa/cocoa_time.m
lib/cocoa/cocoa_window.m)
# Treats .m files as C files.
set_source_files_properties(${specific_objc_file_list} PROPERTIES LANGUAGE C)
# Disables warnings in glfw.
set_source_files_properties(${specific_objc_file_list} PROPERTIES COMPILE_FLAGS
+1 -1
View File
@@ -3887,7 +3887,7 @@ std::string valueToString(double value, bool useSpecialFloats, unsigned int prec
int len = -1;
char formatString[6];
sprintf(formatString, "%%.%dg", precision);
snprintf(formatString, sizeof(formatString), "%%.%dg", precision);
// Print into the buffer. We need not request the alternative representation
// that always has a decimal point because JSON doesn't distingish the
+19 -23
View File
@@ -66,12 +66,11 @@ template <typename _Ty, size_t _size = sizeof(_Ty)>
struct EndianSwapper;
// Internal macro used to swap two bytes.
#define OZZ_BYTE_SWAP(_a, _b) \
do { \
const ozz::byte temp = (_a); \
(_a) = (_b); \
(_b) = temp; \
} while (0)
OZZ_INLINE void _in_place_byte_swap(byte& _a, byte& _b) {
_a = _a ^ _b;
_b = _a ^ _b;
_a = _a ^ _b;
}
// EndianSwapper specialization for 1 byte types.
template <typename _Ty>
@@ -86,12 +85,12 @@ struct EndianSwapper<_Ty, 2> {
OZZ_INLINE static void Swap(_Ty* _ty, size_t _count) {
byte* alias = reinterpret_cast<byte*>(_ty);
for (size_t i = 0; i < _count * 2; i += 2) {
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 1]);
_in_place_byte_swap(alias[i + 0], alias[i + 1]);
}
}
OZZ_INLINE static _Ty Swap(_Ty _ty) { // Pass by copy to swap _ty in-place.
byte* alias = reinterpret_cast<byte*>(&_ty);
OZZ_BYTE_SWAP(alias[0], alias[1]);
_in_place_byte_swap(alias[0], alias[1]);
return _ty;
}
};
@@ -102,14 +101,14 @@ struct EndianSwapper<_Ty, 4> {
OZZ_INLINE static void Swap(_Ty* _ty, size_t _count) {
byte* alias = reinterpret_cast<byte*>(_ty);
for (size_t i = 0; i < _count * 4; i += 4) {
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 3]);
OZZ_BYTE_SWAP(alias[i + 1], alias[i + 2]);
_in_place_byte_swap(alias[i + 0], alias[i + 3]);
_in_place_byte_swap(alias[i + 1], alias[i + 2]);
}
}
OZZ_INLINE static _Ty Swap(_Ty _ty) { // Pass by copy to swap _ty in-place.
byte* alias = reinterpret_cast<byte*>(&_ty);
OZZ_BYTE_SWAP(alias[0], alias[3]);
OZZ_BYTE_SWAP(alias[1], alias[2]);
_in_place_byte_swap(alias[0], alias[3]);
_in_place_byte_swap(alias[1], alias[2]);
return _ty;
}
};
@@ -120,25 +119,22 @@ struct EndianSwapper<_Ty, 8> {
OZZ_INLINE static void Swap(_Ty* _ty, size_t _count) {
byte* alias = reinterpret_cast<byte*>(_ty);
for (size_t i = 0; i < _count * 8; i += 8) {
OZZ_BYTE_SWAP(alias[i + 0], alias[i + 7]);
OZZ_BYTE_SWAP(alias[i + 1], alias[i + 6]);
OZZ_BYTE_SWAP(alias[i + 2], alias[i + 5]);
OZZ_BYTE_SWAP(alias[i + 3], alias[i + 4]);
_in_place_byte_swap(alias[i + 0], alias[i + 7]);
_in_place_byte_swap(alias[i + 1], alias[i + 6]);
_in_place_byte_swap(alias[i + 2], alias[i + 5]);
_in_place_byte_swap(alias[i + 3], alias[i + 4]);
}
}
OZZ_INLINE static _Ty Swap(_Ty _ty) { // Pass by copy to swap _ty in-place.
byte* alias = reinterpret_cast<byte*>(&_ty);
OZZ_BYTE_SWAP(alias[0], alias[7]);
OZZ_BYTE_SWAP(alias[1], alias[6]);
OZZ_BYTE_SWAP(alias[2], alias[5]);
OZZ_BYTE_SWAP(alias[3], alias[4]);
_in_place_byte_swap(alias[0], alias[7]);
_in_place_byte_swap(alias[1], alias[6]);
_in_place_byte_swap(alias[2], alias[5]);
_in_place_byte_swap(alias[3], alias[4]);
return _ty;
}
};
// OZZ_BYTE_SWAP is not useful anymore.
#undef OZZ_BYTE_SWAP
// Helper function that swaps _count elements of the array _ty in place.
template <typename _Ty>
OZZ_INLINE void EndianSwap(_Ty* _ty, size_t _count) {
+3 -3
View File
@@ -250,7 +250,7 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Blending parameters", &open);
if (open) {
_im_gui->DoLabel("Main layer:");
std::sprintf(label, "Layer weight: %.2f", base_weight_);
std::snprintf(label, sizeof(label), "Layer weight: %.2f", base_weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &base_weight_, 1.f);
_im_gui->DoLabel("Additive layer:");
@@ -259,8 +259,8 @@ class AdditiveBlendSampleApplication : public ozz::sample::Application {
std::memcpy(weights.data(), additive_weigths_,
sizeof(additive_weigths_));
std::sprintf(label, "Weights\nCurl: %.2f\nSplay: %.2f",
additive_weigths_[kCurl], additive_weigths_[kSplay]);
std::snprintf(label, sizeof(label), "Weights\nCurl: %.2f\nSplay: %.2f",
additive_weigths_[kCurl], additive_weigths_[kSplay]);
if (_im_gui->DoSlider2D(label, {{0.f, 0.f}}, {{1.f, 1.f}}, &weights)) {
auto_animate_weights_ = false; // User interacted.
std::memcpy(additive_weigths_, weights.data(),
+5 -5
View File
@@ -161,16 +161,16 @@ class AttachSampleApplication : public ozz::sample::Application {
if (open && skeleton_.num_joints() != 0) {
_im_gui->DoLabel("Select joint:");
char label[64];
std::sprintf(label, "%s (%d)", skeleton_.joint_names()[attachment_],
attachment_);
std::snprintf(label, sizeof(label), "%s (%d)",
skeleton_.joint_names()[attachment_], attachment_);
_im_gui->DoSlider(label, 0, skeleton_.num_joints() - 1, &attachment_);
_im_gui->DoLabel("Attachment offset:");
sprintf(label, "x: %02f", offset_.x);
std::snprintf(label, sizeof(label), "x: %02f", offset_.x);
_im_gui->DoSlider(label, -1.f, 1.f, &offset_.x);
sprintf(label, "y: %02f", offset_.y);
std::snprintf(label, sizeof(label), "y: %02f", offset_.y);
_im_gui->DoSlider(label, -1.f, 1.f, &offset_.y);
sprintf(label, "z: %02f", offset_.z);
std::snprintf(label, sizeof(label), "z: %02f", offset_.z);
_im_gui->DoSlider(label, -1.f, 1.f, &offset_.z);
}
}
+4 -4
View File
@@ -58,7 +58,7 @@ OZZ_OPTIONS_DECLARE_STRING(animation2,
// Third animation archive can be specified as an option.
OZZ_OPTIONS_DECLARE_STRING(animation3,
"Path to the second animation (ozz archive format).",
"Path to the third animation (ozz archive format).",
"media/animation3.ozz", false)
class BlendSampleApplication : public ozz::sample::Application {
@@ -242,16 +242,16 @@ class BlendSampleApplication : public ozz::sample::Application {
}
char label[64];
std::sprintf(label, "Blend ratio: %.2f", blend_ratio_);
std::snprintf(label, sizeof(label), "Blend ratio: %.2f", blend_ratio_);
_im_gui->DoSlider(label, 0.f, 1.f, &blend_ratio_, 1.f, !manual_);
for (int i = 0; i < kNumLayers; ++i) {
Sampler& sampler = samplers_[i];
std::sprintf(label, "Weight %d: %.2f", i, sampler.weight);
std::snprintf(label, sizeof(label), "Weight %d: %.2f", i, sampler.weight);
_im_gui->DoSlider(label, 0.f, 1.f, &sampler.weight, 1.f, manual_);
}
std::sprintf(label, "Threshold: %.2f", threshold_);
std::snprintf(label, sizeof(label), "Threshold: %.2f", threshold_);
_im_gui->DoSlider(label, .01f, 1.f, &threshold_);
}
}
+15 -15
View File
@@ -608,7 +608,7 @@ class FootIKSampleApplication : public ozz::sample::Application {
virtual void OnDestroy() {}
virtual bool OnGui(ozz::sample::ImGui* _im_gui) {
char txt[32];
char label[32];
// Main options
{
@@ -636,12 +636,12 @@ class FootIKSampleApplication : public ozz::sample::Application {
static bool opened = true;
ozz::sample::ImGui::OpenClose oc(_im_gui, "IK settings", &opened);
if (opened) {
sprintf(txt, "Foot height %.2g", foot_heigh_);
_im_gui->DoSlider(txt, 0.f, .3f, &foot_heigh_);
sprintf(txt, "Weight %.2g", weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &weight_);
sprintf(txt, "Soften %.2g", soften_);
_im_gui->DoSlider(txt, 0.f, 1.f, &soften_, 1.f, two_bone_ik_);
snprintf(label, sizeof(label), "Foot height %.2g", foot_heigh_);
_im_gui->DoSlider(label, 0.f, .3f, &foot_heigh_);
snprintf(label, sizeof(label), "Weight %.2g", weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &weight_);
snprintf(label, sizeof(label), "Soften %.2g", soften_);
_im_gui->DoSlider(label, 0.f, 1.f, &soften_, 1.f, two_bone_ik_);
}
}
@@ -652,19 +652,19 @@ class FootIKSampleApplication : public ozz::sample::Application {
bool moved = false;
// Translation
_im_gui->DoLabel("Translation");
sprintf(txt, "x %.2g", root_translation_.x);
moved |= _im_gui->DoSlider(txt, -10.f, 10.f, &root_translation_.x);
sprintf(txt, "y %.2g", root_translation_.y);
moved |= _im_gui->DoSlider(txt, 0.f, 5.f, &root_translation_.y, 1.f,
snprintf(label, sizeof(label), "x %.2g", root_translation_.x);
moved |= _im_gui->DoSlider(label, -10.f, 10.f, &root_translation_.x);
snprintf(label, sizeof(label), "y %.2g", root_translation_.y);
moved |= _im_gui->DoSlider(label, 0.f, 5.f, &root_translation_.y, 1.f,
!auto_character_height_);
sprintf(txt, "z %.2g", root_translation_.z);
moved |= _im_gui->DoSlider(txt, -10.f, 10.f, &root_translation_.z);
snprintf(label, sizeof(label), "z %.2g", root_translation_.z);
moved |= _im_gui->DoSlider(label, -10.f, 10.f, &root_translation_.z);
// Rotation (in euler form)
_im_gui->DoLabel("Rotation");
sprintf(txt, "yaw %.3g", root_yaw_ * ozz::math::kRadianToDegree);
snprintf(label, sizeof(label), "yaw %.3g", root_yaw_ * ozz::math::kRadianToDegree);
moved |=
_im_gui->DoSlider(txt, -ozz::math::kPi, ozz::math::kPi, &root_yaw_);
_im_gui->DoSlider(label, -ozz::math::kPi, ozz::math::kPi, &root_yaw_);
// Character position shouldn't be changed after the update. In this
// case, because UI is updated after "game" update, we need to recompute
+21 -25
View File
@@ -528,31 +528,32 @@ bool Application::Gui() {
}
bool Application::FrameworkGui() {
char label[64];
// Downcast to public imgui.
ImGui* im_gui = im_gui_.get();
{ // Render statistics
static bool open = true;
ImGui::OpenClose stat_oc(im_gui, "Statistics", &open);
if (open) {
char szLabel[64];
{ // FPS
Record::Statistics statistics = fps_->GetStatistics();
std::sprintf(szLabel, "FPS: %.0f",
statistics.mean == 0.f ? 0.f : 1000.f / statistics.mean);
std::snprintf(label, sizeof(label), "FPS: %.0f",
statistics.mean == 0.f ? 0.f : 1000.f / statistics.mean);
static bool fps_open = false;
ImGui::OpenClose stats(im_gui, szLabel, &fps_open);
ImGui::OpenClose stats(im_gui, label, &fps_open);
if (fps_open) {
std::sprintf(szLabel, "Frame: %.2f ms", statistics.mean);
im_gui->DoGraph(szLabel, 0.f, statistics.max, statistics.latest,
std::snprintf(label, sizeof(label), "Frame: %.2f ms",
statistics.mean);
im_gui->DoGraph(label, 0.f, statistics.max, statistics.latest,
fps_->cursor(), fps_->record_begin(),
fps_->record_end());
}
}
{ // Update time
Record::Statistics statistics = update_time_->GetStatistics();
std::sprintf(szLabel, "Update: %.2f ms", statistics.mean);
std::snprintf(label, sizeof(label), "Update: %.2f ms", statistics.mean);
static bool update_open = true; // This is the most relevant for ozz.
ImGui::OpenClose stats(im_gui, szLabel, &update_open);
ImGui::OpenClose stats(im_gui, label, &update_open);
if (update_open) {
im_gui->DoGraph(nullptr, 0.f, statistics.max, statistics.latest,
update_time_->cursor(), update_time_->record_begin(),
@@ -561,9 +562,9 @@ bool Application::FrameworkGui() {
}
{ // Render time
Record::Statistics statistics = render_time_->GetStatistics();
std::sprintf(szLabel, "Render: %.2f ms", statistics.mean);
std::snprintf(label, sizeof(label), "Render: %.2f ms", statistics.mean);
static bool render_open = false;
ImGui::OpenClose stats(im_gui, szLabel, &render_open);
ImGui::OpenClose stats(im_gui, label, &render_open);
if (render_open) {
im_gui->DoGraph(nullptr, 0.f, statistics.max, statistics.latest,
render_time_->cursor(), render_time_->record_begin(),
@@ -580,18 +581,15 @@ bool Application::FrameworkGui() {
im_gui->DoButton("Freeze", true, &freeze_);
im_gui->DoCheckBox("Fix update rate", &fix_update_rate, true);
if (!fix_update_rate) {
char sz_factor[64];
std::sprintf(sz_factor, "Time factor: %.2f", time_factor_);
im_gui->DoSlider(sz_factor, -5.f, 5.f, &time_factor_);
std::snprintf(label, sizeof(label), "Time factor: %.2f", time_factor_);
im_gui->DoSlider(label, -5.f, 5.f, &time_factor_);
if (im_gui->DoButton("Reset time factor", time_factor_ != 1.f)) {
time_factor_ = 1.f;
}
} else {
char sz_fixed_update_rate[64];
std::sprintf(sz_fixed_update_rate, "Update rate: %.0f fps",
fixed_update_rate);
im_gui->DoSlider(sz_fixed_update_rate, 1.f, 200.f, &fixed_update_rate,
.5f, true);
std::snprintf(label, sizeof(label), "Update rate: %.0f fps",
fixed_update_rate);
im_gui->DoSlider(label, 1.f, 200.f, &fixed_update_rate, .5f, true);
if (im_gui->DoButton("Reset update rate", fixed_update_rate != 60.f)) {
fixed_update_rate = 60.f;
}
@@ -615,10 +613,9 @@ bool Application::FrameworkGui() {
}
// Vertical sync & swap interval
bool changed = im_gui->DoCheckBox("Vertical sync", &vertical_sync_);
char szLabel[64];
std::sprintf(szLabel, "Swap interval: %d", swap_interval_);
std::snprintf(label, sizeof(label), "Swap interval: %d", swap_interval_);
changed |=
im_gui->DoSlider(szLabel, 1, 4, &swap_interval_, 1.f, vertical_sync_);
im_gui->DoSlider(label, 1, 4, &swap_interval_, 1.f, vertical_sync_);
if (changed) {
glfwSwapInterval(vertical_sync_ ? swap_interval_ : 0);
}
@@ -640,10 +637,9 @@ bool Application::FrameworkGui() {
}
}
char szResolution[64];
std::sprintf(szResolution, "Resolution: %dx%d", resolution_.width,
resolution_.height);
if (im_gui->DoSlider(szResolution, 0, kNumPresets - 1, &preset_lookup)) {
std::snprintf(label, sizeof(label), "Resolution: %dx%d", resolution_.width,
resolution_.height);
if (im_gui->DoSlider(label, 0, kNumPresets - 1, &preset_lookup)) {
// Resolution changed.
resolution_ = resolution_presets[preset_lookup];
glfwSetWindowSize(resolution_.width, resolution_.height);
+1 -1
View File
@@ -149,7 +149,7 @@ class Application {
enum LoopStatus {
kContinue, // Can continue with next loop.
kBreak, // Should stop looping (ex: exit).
kBreakFailure, // // Should stop looping beacause something went wrong.
kBreakFailure, // Should stop looping because something went wrong.
};
LoopStatus OneLoop(int _loops);
@@ -100,7 +100,7 @@ bool FormatFloat(float _value, char* _string, const char* _string_end) {
if (!_string || _string_end - _string < 8 + precision + 1) {
return false;
}
std::sprintf(_string, "%.2g\n", _value);
std::snprintf(_string, _string_end - _string, "%.2g\n", _value);
// Removes unnecessary '0' digits in the exponent.
char* exponent = strchr(_string, 'e');
@@ -160,12 +160,12 @@ bool Shooter::Process() {
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, shot.pbo));
const void* pixels = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if (pixels) {
char name[16];
sprintf(name, "%06d.tga", shot_number_++);
char filename[16];
std::snprintf(filename, sizeof(filename), "%06d.tga", shot_number_++);
ozz::sample::image::WriteTGA(name, shot.width, shot.height, image_format_,
reinterpret_cast<const uint8_t*>(pixels),
false);
ozz::sample::image::WriteTGA(
filename, shot.width, shot.height, image_format_,
reinterpret_cast<const uint8_t*>(pixels), false);
GL(UnmapBuffer(GL_PIXEL_PACK_BUFFER));
}
GL(BindBuffer(GL_PIXEL_PACK_BUFFER, 0));
+22 -21
View File
@@ -110,21 +110,22 @@ bool PlaybackController::OnGui(const animation::Animation& _animation,
_im_gui->DoCheckBox("Loop", &loop_, _enabled);
char szLabel[64];
char label[64];
// Uses a local copy of time_ so that set_time is used to actually apply
// changes. Otherwise previous time would be incorrect.
float ratio = time_ratio();
std::sprintf(szLabel, "Animation time: %.2f", ratio * _animation.duration());
if (_im_gui->DoSlider(szLabel, 0.f, 1.f, &ratio, 1.f,
std::snprintf(label, sizeof(label), "Animation time: %.2f",
ratio * _animation.duration());
if (_im_gui->DoSlider(label, 0.f, 1.f, &ratio, 1.f,
_enabled && _allow_set_time)) {
set_time_ratio(ratio);
// Pause the time if slider as moved.
play_ = false;
time_changed = true;
}
std::sprintf(szLabel, "Playback speed: %.2f", playback_speed_);
_im_gui->DoSlider(szLabel, -5.f, 5.f, &playback_speed_, 1.f, _enabled);
std::snprintf(label, sizeof(label), "Playback speed: %.2f", playback_speed_);
_im_gui->DoSlider(label, -5.f, 5.f, &playback_speed_, 1.f, _enabled);
// Allow to reset speed if it is not the default value.
if (_im_gui->DoButton("Reset playback speed",
@@ -139,7 +140,7 @@ bool OnRawSkeletonJointGui(
ozz::sample::ImGui* _im_gui,
ozz::animation::offline::RawSkeleton::Joint::Children* _children,
ozz::vector<bool>::iterator* _oc_state) {
char txt[255];
char label[255];
bool modified = false;
for (size_t i = 0; i < _children->size(); ++i) {
@@ -152,23 +153,23 @@ bool OnRawSkeletonJointGui(
// Translation
ozz::math::Float3& translation = joint.transform.translation;
_im_gui->DoLabel("Translation");
sprintf(txt, "x %.2g", translation.x);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.x);
sprintf(txt, "y %.2g", translation.y);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.y);
sprintf(txt, "z %.2g", translation.z);
modified |= _im_gui->DoSlider(txt, -1.f, 1.f, &translation.z);
snprintf(label, sizeof(label), "x %.2g", translation.x);
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.x);
snprintf(label, sizeof(label), "y %.2g", translation.y);
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.y);
snprintf(label, sizeof(label), "z %.2g", translation.z);
modified |= _im_gui->DoSlider(label, -1.f, 1.f, &translation.z);
// Rotation (in euler form)
ozz::math::Quaternion& rotation = joint.transform.rotation;
_im_gui->DoLabel("Rotation");
ozz::math::Float3 euler = ToEuler(rotation) * ozz::math::kRadianToDegree;
sprintf(txt, "x %.3g", euler.x);
bool euler_modified = _im_gui->DoSlider(txt, -180.f, 180.f, &euler.x);
sprintf(txt, "y %.3g", euler.y);
euler_modified |= _im_gui->DoSlider(txt, -180.f, 180.f, &euler.y);
sprintf(txt, "z %.3g", euler.z);
euler_modified |= _im_gui->DoSlider(txt, -180.f, 180.f, &euler.z);
snprintf(label, sizeof(label), "x %.3g", euler.x);
bool euler_modified = _im_gui->DoSlider(label, -180.f, 180.f, &euler.x);
snprintf(label, sizeof(label), "y %.3g", euler.y);
euler_modified |= _im_gui->DoSlider(label, -180.f, 180.f, &euler.y);
snprintf(label, sizeof(label), "z %.3g", euler.z);
euler_modified |= _im_gui->DoSlider(label, -180.f, 180.f, &euler.z);
if (euler_modified) {
modified = true;
ozz::math::Float3 euler_rad = euler * ozz::math::kDegreeToRadian;
@@ -179,8 +180,8 @@ bool OnRawSkeletonJointGui(
// Scale (must be uniform and not 0)
_im_gui->DoLabel("Scale");
ozz::math::Float3& scale = joint.transform.scale;
sprintf(txt, "%.2g", scale.x);
if (_im_gui->DoSlider(txt, -1.f, 1.f, &scale.x)) {
snprintf(label, sizeof(label), "%.2g", scale.x);
if (_im_gui->DoSlider(label, -1.f, 1.f, &scale.x)) {
modified = true;
scale.y = scale.z = scale.x = scale.x != 0.f ? scale.x : .01f;
}
@@ -328,7 +329,7 @@ bool LoadAnimation(const char* _filename,
}
bool LoadRawAnimation(const char* _filename,
ozz::animation::offline::RawAnimation* _animation) {
ozz::animation::offline::RawAnimation* _animation) {
assert(_filename && _animation);
ozz::log::Out() << "Loading raw animation archive: " << _filename << "."
<< std::endl;
+21 -21
View File
@@ -387,15 +387,15 @@ class LookAtSampleApplication : public ozz::sample::Application {
virtual void OnDestroy() {}
virtual bool OnGui(ozz::sample::ImGui* _im_gui) {
char txt[64];
char label[64];
_im_gui->DoCheckBox("Enable ik", &enable_ik_);
sprintf(txt, "IK chain length: %d", chain_length_);
_im_gui->DoSlider(txt, 0, kMaxChainLength, &chain_length_);
sprintf(txt, "Joint weight %.2g", joint_weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &joint_weight_);
sprintf(txt, "Chain weight %.2g", chain_weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &chain_weight_);
snprintf(label, sizeof(label), "IK chain length: %d", chain_length_);
_im_gui->DoSlider(label, 0, kMaxChainLength, &chain_length_);
snprintf(label, sizeof(label), "Joint weight %.2g", joint_weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &joint_weight_);
snprintf(label, sizeof(label), "Chain weight %.2g", chain_weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &chain_weight_);
// Exposes animation runtime playback controls.
{
@@ -413,15 +413,15 @@ class LookAtSampleApplication : public ozz::sample::Application {
const float kTargetRange = 3.f;
_im_gui->DoLabel("Animated extent");
sprintf(txt, "%.2g", target_extent_);
_im_gui->DoSlider(txt, 0.f, kTargetRange, &target_extent_);
snprintf(label, sizeof(label), "%.2g", target_extent_);
_im_gui->DoSlider(label, 0.f, kTargetRange, &target_extent_);
sprintf(txt, "x %.2g", target_offset_.x);
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.x);
sprintf(txt, "y %.2g", target_offset_.y);
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.y);
sprintf(txt, "z %.2g", target_offset_.z);
_im_gui->DoSlider(txt, -kTargetRange, kTargetRange, &target_offset_.z);
snprintf(label, sizeof(label), "x %.2g", target_offset_.x);
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.x);
snprintf(label, sizeof(label), "y %.2g", target_offset_.y);
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.y);
snprintf(label, sizeof(label), "z %.2g", target_offset_.z);
_im_gui->DoSlider(label, -kTargetRange, kTargetRange, &target_offset_.z);
}
}
@@ -430,12 +430,12 @@ class LookAtSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Eyes offset", &opened);
if (opened) {
const float kOffsetRange = .5f;
sprintf(txt, "x %.2g", eyes_offset_.x);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.x);
sprintf(txt, "y %.2g", eyes_offset_.y);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.y);
sprintf(txt, "z %.2g", eyes_offset_.z);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &eyes_offset_.z);
snprintf(label, sizeof(label), "x %.2g", eyes_offset_.x);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.x);
snprintf(label, sizeof(label), "y %.2g", eyes_offset_.y);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.y);
snprintf(label, sizeof(label), "z %.2g", eyes_offset_.z);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &eyes_offset_.z);
}
}
+10 -10
View File
@@ -152,7 +152,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
// Rebuilds all if the number of joints has changed.
int joints = skeleton_->num_joints();
char label[64];
std::sprintf(label, "Joints count: %d", joints);
std::snprintf(label, sizeof(label), "Joints count: %d", joints);
// Uses an exponential scale in the slider to maintain enough precision in
// the lowest values.
@@ -219,17 +219,17 @@ class MillipedeSampleApplication : public ozz::sample::Application {
root->transform.rotation = Quaternion::identity();
root->transform.scale = Float3::one();
char buf[16];
char number[16];
for (int i = 0; i < slice_count_; ++i) {
// Format joint number.
std::sprintf(buf, "%d", i);
std::snprintf(number, sizeof(number), "%d", i);
root->children.resize(3);
// Left leg.
RawSkeleton::Joint& lu = root->children[0];
lu.name = "lu";
lu.name += buf;
lu.name += number;
lu.transform.translation = kTransUp;
lu.transform.rotation = kRotLeftUp;
lu.transform.scale = Float3::one();
@@ -237,7 +237,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
lu.children.resize(1);
RawSkeleton::Joint& ld = lu.children[0];
ld.name = "ld";
ld.name += buf;
ld.name += number;
ld.transform.translation = kTransDown;
ld.transform.rotation = kRotLeftDown;
ld.transform.scale = Float3::one();
@@ -245,7 +245,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
ld.children.resize(1);
RawSkeleton::Joint& lf = ld.children[0];
lf.name = "lf";
lf.name += buf;
lf.name += number;
lf.transform.translation = Float3::x_axis();
lf.transform.rotation = Quaternion::identity();
lf.transform.scale = Float3::one();
@@ -253,7 +253,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
// Right leg.
RawSkeleton::Joint& ru = root->children[1];
ru.name = "ru";
ru.name += buf;
ru.name += number;
ru.transform.translation = kTransUp;
ru.transform.rotation = kRotRightUp;
ru.transform.scale = Float3::one();
@@ -261,7 +261,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
ru.children.resize(1);
RawSkeleton::Joint& rd = ru.children[0];
rd.name = "rd";
rd.name += buf;
rd.name += number;
rd.transform.translation = kTransDown;
rd.transform.rotation = kRotRightDown;
rd.transform.scale = Float3::one();
@@ -269,7 +269,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
rd.children.resize(1);
RawSkeleton::Joint& rf = rd.children[0];
rf.name = "rf";
rf.name += buf;
rf.name += number;
rf.transform.translation = Float3::x_axis();
rf.transform.rotation = Quaternion::identity();
rf.transform.scale = Float3::one();
@@ -277,7 +277,7 @@ class MillipedeSampleApplication : public ozz::sample::Application {
// Spine.
RawSkeleton::Joint& sp = root->children[2];
sp.name = "sp";
sp.name += buf;
sp.name += number;
sp.transform.translation = Float3(0.f, 0.f, kSpinLength);
sp.transform.rotation = Quaternion::identity();
sp.transform.scale = Float3::one();
@@ -287,10 +287,10 @@ class MultithreadSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Sample control", &oc_open);
if (oc_open) {
char label[64];
std::sprintf(label, "Number of entities: %d", num_characters_);
std::snprintf(label, sizeof(label), "Number of entities: %d", num_characters_);
_im_gui->DoSlider(label, 1, kMaxCharacters, &num_characters_, .7f);
const int num_joints = num_characters_ * skeleton_.num_joints();
std::sprintf(label, "Number of joints: %d", num_joints);
std::snprintf(label, sizeof(label), "Number of joints: %d", num_joints);
_im_gui->DoLabel(label);
}
}
@@ -303,11 +303,11 @@ class MultithreadSampleApplication : public ozz::sample::Application {
has_threading_support_);
if (enable_theading_) {
char label[64];
std::sprintf(label, "Grain size: %d", grain_size_);
std::snprintf(label, sizeof(label), "Grain size: %d", grain_size_);
_im_gui->DoSlider(label, kMinGrainSize, kMaxCharacters, &grain_size_,
.2f);
const int num_threads = monitor_.ThreadCount();
std::sprintf(label, "Thread/task count: %d/%d", num_threads,
std::snprintf(label, sizeof(label), "Thread/task count: %d/%d", num_threads,
monitor_.TaskCount());
_im_gui->DoLabel(label);
}
+27 -25
View File
@@ -312,29 +312,32 @@ class OptimizeSampleApplication : public ozz::sample::Application {
rebuild |= _im_gui->DoCheckBox("Enable optimizations", &optimize_);
std::sprintf(label, "Tolerance: %0.2f mm", setting_.tolerance * 1000);
std::snprintf(label, sizeof(label), "Tolerance: %0.2f mm",
setting_.tolerance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, .1f, &setting_.tolerance, .5f,
optimize_);
std::sprintf(label, "Distance: %0.2f mm", setting_.distance * 1000);
std::snprintf(label, sizeof(label), "Distance: %0.2f mm",
setting_.distance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, 1.f, &setting_.distance, .5f,
optimize_);
rebuild |= _im_gui->DoCheckBox("Enable joint setting",
&joint_setting_enable_, optimize_);
std::sprintf(label, "%s (%d)", skeleton_.joint_names()[joint_], joint_);
std::snprintf(label, sizeof(label), "%s (%d)",
skeleton_.joint_names()[joint_], joint_);
rebuild |=
_im_gui->DoSlider(label, 0, skeleton_.num_joints() - 1, &joint_,
1.f, joint_setting_enable_ && optimize_);
std::sprintf(label, "Tolerance: %0.2f mm",
joint_setting_.tolerance * 1000);
std::snprintf(label, sizeof(label), "Tolerance: %0.2f mm",
joint_setting_.tolerance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, .1f, &joint_setting_.tolerance,
.5f, joint_setting_enable_ && optimize_);
std::sprintf(label, "Distance: %0.2f mm",
joint_setting_.distance * 1000);
std::snprintf(label, sizeof(label), "Distance: %0.2f mm",
joint_setting_.distance * 1000);
rebuild |= _im_gui->DoSlider(label, 0.f, 1.f, &joint_setting_.distance,
.5f, joint_setting_enable_ && optimize_);
@@ -356,18 +359,18 @@ class OptimizeSampleApplication : public ozz::sample::Application {
static bool open = true;
ozz::sample::ImGui::OpenClose ocb(_im_gui, "Memory size", &open);
if (open) {
std::sprintf(label, "Original: %dKB",
static_cast<int>(raw_animation_.size() >> 10));
std::snprintf(label, sizeof(label), "Original: %dKB",
static_cast<int>(raw_animation_.size() >> 10));
_im_gui->DoLabel(label);
std::sprintf(label, "Optimized: %dKB (%.1f:1)",
static_cast<int>(raw_optimized_animation_.size() >> 10),
static_cast<float>(raw_animation_.size()) /
raw_optimized_animation_.size());
std::snprintf(label, sizeof(label), "Optimized: %dKB (%.1f:1)",
static_cast<int>(raw_optimized_animation_.size() >> 10),
static_cast<float>(raw_animation_.size()) /
raw_optimized_animation_.size());
_im_gui->DoLabel(label);
std::sprintf(
label, "Compressed: %dKB (%.1f:1)",
std::snprintf(
label, sizeof(label), "Compressed: %dKB (%.1f:1)",
static_cast<int>(animation_rt_->size() >> 10),
static_cast<float>(raw_animation_.size()) / animation_rt_->size());
_im_gui->DoLabel(label);
@@ -388,37 +391,36 @@ class OptimizeSampleApplication : public ozz::sample::Application {
// Show absolute error.
{
char szLabel[64];
static bool error_open = true;
ozz::sample::ImGui::OpenClose oc_stats(_im_gui, "Absolute error",
&error_open);
if (error_open) {
{
std::sprintf(szLabel, "Median error: %.2fmm",
*error_record_med_.cursor());
std::snprintf(label, sizeof(label), "Median error: %.2fmm",
*error_record_med_.cursor());
const ozz::sample::Record::Statistics error_stats =
error_record_med_.GetStatistics();
_im_gui->DoGraph(szLabel, 0.f, error_stats.max, error_stats.latest,
_im_gui->DoGraph(label, 0.f, error_stats.max, error_stats.latest,
error_record_med_.cursor(),
error_record_med_.record_begin(),
error_record_med_.record_end());
}
{
std::sprintf(szLabel, "Maximum error: %.2fmm",
*error_record_max_.cursor());
std::snprintf(label, sizeof(label), "Maximum error: %.2fmm",
*error_record_max_.cursor());
const ozz::sample::Record::Statistics error_stats =
error_record_max_.GetStatistics();
_im_gui->DoGraph(szLabel, 0.f, error_stats.max, error_stats.latest,
_im_gui->DoGraph(label, 0.f, error_stats.max, error_stats.latest,
error_record_max_.cursor(),
error_record_max_.record_begin(),
error_record_max_.record_end());
}
{
std::sprintf(szLabel, "Joint %d error: %.2fmm", joint_,
*joint_error_record_.cursor());
std::snprintf(label, sizeof(label), "Joint %d error: %.2fmm", joint_,
*joint_error_record_.cursor());
const ozz::sample::Record::Statistics error_stats =
joint_error_record_.GetStatistics();
_im_gui->DoGraph(szLabel, 0.f, error_stats.max, error_stats.latest,
_im_gui->DoGraph(label, 0.f, error_stats.max, error_stats.latest,
joint_error_record_.cursor(),
joint_error_record_.record_begin(),
joint_error_record_.record_end());
@@ -251,7 +251,7 @@ class PartialBlendSampleApplication : public ozz::sample::Application {
_im_gui->DoCheckBox("Use automatic blending settings", &automatic);
static float coeff = 1.f; // All power to the partial animation.
std::sprintf(label, "Upper body weight: %.2f", coeff);
std::snprintf(label, sizeof(label), "Upper body weight: %.2f", coeff);
_im_gui->DoSlider(label, 0.f, 1.f, &coeff, 1.f, automatic);
Sampler& lower_body_sampler = samplers_[kLowerBody];
@@ -267,27 +267,27 @@ class PartialBlendSampleApplication : public ozz::sample::Application {
_im_gui->DoLabel("Manual settings:");
_im_gui->DoLabel("Lower body layer:");
std::sprintf(label, "Layer weight: %.2f",
std::snprintf(label, sizeof(label), "Layer weight: %.2f",
lower_body_sampler.weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f, &lower_body_sampler.weight_setting,
1.f, !automatic);
std::sprintf(label, "Joints weight: %.2f",
std::snprintf(label, sizeof(label), "Joints weight: %.2f",
lower_body_sampler.joint_weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f,
&lower_body_sampler.joint_weight_setting, 1.f,
!automatic);
_im_gui->DoLabel("Upper body layer:");
std::sprintf(label, "Layer weight: %.2f",
std::snprintf(label, sizeof(label), "Layer weight: %.2f",
upper_body_sampler.weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f, &upper_body_sampler.weight_setting,
1.f, !automatic);
std::sprintf(label, "Joints weight: %.2f",
std::snprintf(label, sizeof(label), "Joints weight: %.2f",
upper_body_sampler.joint_weight_setting);
_im_gui->DoSlider(label, 0.f, 1.f,
&upper_body_sampler.joint_weight_setting, 1.f,
!automatic);
_im_gui->DoLabel("Global settings:");
std::sprintf(label, "Threshold: %.2f", threshold_);
std::snprintf(label, sizeof(label), "Threshold: %.2f", threshold_);
_im_gui->DoSlider(label, .01f, 1.f, &threshold_);
SetupPerJointWeights();
@@ -301,7 +301,7 @@ class PartialBlendSampleApplication : public ozz::sample::Application {
_im_gui->DoLabel("Root of the upper body hierarchy:",
ozz::sample::ImGui::kLeft, false);
char label[64];
std::sprintf(label, "%s (%d)",
std::snprintf(label, sizeof(label), "%s (%d)",
skeleton_.joint_names()[upper_body_root_],
upper_body_root_);
if (_im_gui->DoSlider(label, 0, skeleton_.num_joints() - 1,
+4 -4
View File
@@ -186,28 +186,28 @@ class SkinningSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Model statisitics", &open);
if (open) {
char label[255];
sprintf(label, "%d animated joints", skeleton_.num_joints());
std::snprintf(label, sizeof(label), "%d animated joints", skeleton_.num_joints());
_im_gui->DoLabel(label);
int influences = 0;
for (const auto& mesh : meshes_) {
influences = ozz::math::Max(influences, mesh.max_influences_count());
}
sprintf(label, "%d influences (max)", influences);
std::snprintf(label, sizeof(label), "%d influences (max)", influences);
_im_gui->DoLabel(label);
int vertices = 0;
for (const auto& mesh : meshes_) {
vertices += mesh.vertex_count();
}
sprintf(label, "%.1fK vertices", vertices / 1000.f);
std::snprintf(label, sizeof(label), "%.1fK vertices", vertices / 1000.f);
_im_gui->DoLabel(label);
int indices = 0;
for (const auto& mesh : meshes_) {
indices += mesh.triangle_index_count();
}
sprintf(label, "%.1fK triangles", indices / 3000.f);
std::snprintf(label, sizeof(label), "%.1fK triangles", indices / 3000.f);
_im_gui->DoLabel(label);
}
}
@@ -264,7 +264,7 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
virtual void OnDestroy() {}
virtual bool OnGui(ozz::sample::ImGui* _im_gui) {
char txt[32];
char label[32];
// IK parameters
_im_gui->DoCheckBox("Fix initial transform", &fix_initial_transform_);
@@ -273,25 +273,25 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
static bool opened = true;
ozz::sample::ImGui::OpenClose oc(_im_gui, "IK parameters", &opened);
if (opened) {
sprintf(txt, "Soften: %.2g", soften_);
_im_gui->DoSlider(txt, 0.f, 1.f, &soften_, 2.f);
sprintf(txt, "Twist angle: %.0f",
snprintf(label, sizeof(label), "Soften: %.2g", soften_);
_im_gui->DoSlider(label, 0.f, 1.f, &soften_, 2.f);
snprintf(label, sizeof(label), "Twist angle: %.0f",
twist_angle_ * ozz::math::kRadianToDegree);
_im_gui->DoSlider(txt, -ozz::math::kPi, ozz::math::kPi, &twist_angle_);
sprintf(txt, "Weight: %.2g", weight_);
_im_gui->DoSlider(txt, 0.f, 1.f, &weight_);
_im_gui->DoSlider(label, -ozz::math::kPi, ozz::math::kPi, &twist_angle_);
snprintf(label, sizeof(label), "Weight: %.2g", weight_);
_im_gui->DoSlider(label, 0.f, 1.f, &weight_);
{
// Pole vector
static bool pole_opened = true;
ozz::sample::ImGui::OpenClose oc_pole(_im_gui, "Pole vector",
&pole_opened);
if (pole_opened) {
sprintf(txt, "x %.2g", pole_vector.x);
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.x);
sprintf(txt, "y %.2g", pole_vector.y);
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.y);
sprintf(txt, "z %.2g", pole_vector.z);
_im_gui->DoSlider(txt, -1.f, 1.f, &pole_vector.z);
snprintf(label, sizeof(label), "x %.2g", pole_vector.x);
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.x);
snprintf(label, sizeof(label), "y %.2g", pole_vector.y);
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.y);
snprintf(label, sizeof(label), "z %.2g", pole_vector.z);
_im_gui->DoSlider(label, -1.f, 1.f, &pole_vector.z);
}
}
}
@@ -301,17 +301,17 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
ozz::sample::ImGui::OpenClose oc(_im_gui, "Target position", &opened);
if (opened) {
_im_gui->DoLabel("Target animation extent");
sprintf(txt, "%.2g", target_extent_);
_im_gui->DoSlider(txt, 0.f, 1.f, &target_extent_);
snprintf(label, sizeof(label), "%.2g", target_extent_);
_im_gui->DoSlider(label, 0.f, 1.f, &target_extent_);
_im_gui->DoLabel("Target Offset");
const float kOffsetRange = 1.f;
sprintf(txt, "x %.2g", target_offset_.x);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.x);
sprintf(txt, "y %.2g", target_offset_.y);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.y);
sprintf(txt, "z %.2g", target_offset_.z);
_im_gui->DoSlider(txt, -kOffsetRange, kOffsetRange, &target_offset_.z);
snprintf(label, sizeof(label), "x %.2g", target_offset_.x);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.x);
snprintf(label, sizeof(label), "y %.2g", target_offset_.y);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.y);
snprintf(label, sizeof(label), "z %.2g", target_offset_.z);
_im_gui->DoSlider(label, -kOffsetRange, kOffsetRange, &target_offset_.z);
}
}
{ // Root
@@ -320,28 +320,28 @@ class TwoBoneIKSampleApplication : public ozz::sample::Application {
if (opened) {
// Translation
_im_gui->DoLabel("Translation");
sprintf(txt, "x %.2g", root_translation_.x);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.x);
sprintf(txt, "y %.2g", root_translation_.y);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.y);
sprintf(txt, "z %.2g", root_translation_.z);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_translation_.z);
snprintf(label, sizeof(label), "x %.2g", root_translation_.x);
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.x);
snprintf(label, sizeof(label), "y %.2g", root_translation_.y);
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.y);
snprintf(label, sizeof(label), "z %.2g", root_translation_.z);
_im_gui->DoSlider(label, -1.f, 1.f, &root_translation_.z);
// Rotation (in euler form)
_im_gui->DoLabel("Rotation");
ozz::math::Float3 euler = root_euler_ * ozz::math::kRadianToDegree;
sprintf(txt, "yaw %.3g", euler.x);
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.x);
sprintf(txt, "pitch %.3g", euler.y);
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.y);
sprintf(txt, "roll %.3g", euler.z);
_im_gui->DoSlider(txt, -180.f, 180.f, &euler.z);
snprintf(label, sizeof(label), "yaw %.3g", euler.x);
_im_gui->DoSlider(label, -180.f, 180.f, &euler.x);
snprintf(label, sizeof(label), "pitch %.3g", euler.y);
_im_gui->DoSlider(label, -180.f, 180.f, &euler.y);
snprintf(label, sizeof(label), "roll %.3g", euler.z);
_im_gui->DoSlider(label, -180.f, 180.f, &euler.z);
root_euler_ = euler * ozz::math::kDegreeToRadian;
// Scale (must be uniform and not 0)
_im_gui->DoLabel("Scale");
sprintf(txt, "%.2g", root_scale_);
_im_gui->DoSlider(txt, -1.f, 1.f, &root_scale_);
snprintf(label, sizeof(label), "%.2g", root_scale_);
_im_gui->DoSlider(label, -1.f, 1.f, &root_scale_);
}
}
{ // Display options
+1
View File
@@ -62,6 +62,7 @@ target_compile_definitions(ozz_base
PRIVATE $<$<BOOL:${BUILD_SHARED_LIBS}>:OZZ_BUILD_BASE_LIB>)
target_compile_options(ozz_base PUBLIC $<$<CXX_COMPILER_ID:MSVC>:/wd4251>)
target_include_directories(ozz_base PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
@@ -25,15 +25,13 @@
// //
//----------------------------------------------------------------------------//
#include "gtest/gtest.h"
#include "ozz/animation/runtime/ik_aim_job.h"
#include "ozz/base/maths/gtest_math_helper.h"
#include "ozz/base/maths/quaternion.h"
#include "ozz/base/maths/simd_math.h"
#include "ozz/base/maths/simd_quaternion.h"
#include "gtest/gtest.h"
#include "ozz/base/maths/gtest_math_helper.h"
TEST(JobValidity, IKAimJob) {
const ozz::math::Float4x4 joint = ozz::math::Float4x4::identity();
ozz::math::SimdQuaternion quat;
@@ -428,43 +426,24 @@ TEST(Twist, IKAimJob) {
EXPECT_SIMDQUATERNION_EQ_TOL(quat, 0.f, 0.f, 0.f, 1.f, 2e-3f);
}
{ // Pole y, twist pi
{ // Pole y, twist pi / 2
job.pole_vector = ozz::math::simd_float4::y_axis();
job.twist_angle = ozz::math::kPi;
job.twist_angle = ozz::math::kPi_2;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), -ozz::math::kPi);
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
}
{ // Pole y, twist -pi
{ // Pole y, twist -pi / 2
job.pole_vector = ozz::math::simd_float4::y_axis();
job.twist_angle = -ozz::math::kPi;
job.twist_angle = -ozz::math::kPi_2;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_mPi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), -ozz::math::kPi);
ozz::math::Float3::x_axis(), -ozz::math::kPi_2);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_mPi.x, x_mPi.y, x_mPi.z, x_mPi.w,
2e-3f);
}
{ // Pole y, twist pi/2
job.pole_vector = ozz::math::simd_float4::y_axis();
job.twist_angle = ozz::math::kPi_2;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_Pi_2 = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), ozz::math::kPi_2);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi_2.x, x_Pi_2.y, x_Pi_2.z, x_Pi_2.w,
2e-3f);
}
{ // Pole z, twist pi/2
job.pole_vector = ozz::math::simd_float4::z_axis();
job.twist_angle = ozz::math::kPi_2;
EXPECT_TRUE(job.Run());
const ozz::math::Quaternion x_Pi = ozz::math::Quaternion::FromAxisAngle(
ozz::math::Float3::x_axis(), ozz::math::kPi);
EXPECT_SIMDQUATERNION_EQ_TOL(quat, x_Pi.x, x_Pi.y, x_Pi.z, x_Pi.w, 2e-3f);
}
}
TEST(AlignedTargetUp, IKAimJob) {
@@ -3,6 +3,8 @@ target_include_directories(test_intrusive_list
PUBLIC "${PROJECT_SOURCE_DIR}/include")
target_link_libraries(test_intrusive_list
gtest)
target_compile_options(test_intrusive_list
PRIVATE $<$<BOOL:${W_UNUSED_RESULT}>:-Wno-unused-result>)
target_copy_shared_libraries(test_intrusive_list)
add_test(NAME test_intrusive_list COMMAND test_intrusive_list)
set_target_properties(test_intrusive_list PROPERTIES FOLDER "ozz/tests/base")
@@ -11,6 +13,8 @@ add_executable(test_std_containers std_containers_tests.cc)
target_link_libraries(test_std_containers
ozz_base
gtest)
target_compile_options(test_std_containers
PRIVATE $<$<BOOL:${W_UNUSED_RESULT}>:-Wno-unused-result>)
target_copy_shared_libraries(test_std_containers)
add_test(NAME test_std_containers COMMAND test_std_containers)
set_target_properties(test_std_containers PROPERTIES FOLDER "ozz/tests/base")
+16 -10
View File
@@ -39,21 +39,25 @@ set(ThirdPartyIncludeDeps
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/sokol>
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/vectorial/include>
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>
)
)
# Shared code by main executable and tests
add_library(AnimTestbedCode OBJECT
src/SyncTrack.cc
src/SyncTrack.h
src/ozzutils.cc
src/AnimGraph/AnimGraphResource.cc
src/AnimGraph/AnimGraphResource.h
src/AnimGraph/AnimGraph.cc
src/AnimGraph/AnimGraph.h
src/AnimGraph/AnimGraphNodes.cc
src/AnimGraph/AnimGraphNodes.h
src/AnimGraph/AnimGraphData.cc
src/AnimGraph/AnimGraphData.h)
src/AnimGraph/AnimGraphData.h
src/AnimGraph/AnimGraphBlendTree.cc
src/AnimGraph/AnimGraphBlendTree.h
src/AnimGraph/AnimGraphStateMachine.cc
src/AnimGraph/AnimGraphStateMachine.h
src/AnimGraph/AnimNode.cc
src/AnimGraph/AnimNode.h
src/AnimGraph/AnimGraphResource.cc
src/AnimGraph/AnimGraphResource.h)
target_include_directories(
AnimTestbedCode
@@ -74,6 +78,7 @@ target_include_directories(
target_sources(AnimTestbed PRIVATE
src/main.cc
src/embedded_fonts.h
src/SkinnedMeshRenderer.cc
src/AnimGraph/AnimGraphEditor.cc
src/AnimGraph/AnimGraphEditor.h
@@ -104,7 +109,7 @@ target_sources(AnimTestbed PRIVATE
3rdparty/imgui-node-editor/imgui_extra_math.inl
3rdparty/imgui-node-editor/crude_json.cpp
3rdparty/imgui-node-editor/crude_json.h
)
)
target_link_libraries(AnimTestbed AnimTestbedCode glfw ozz_base ozz_geometry ozz_animation ${OPENGL_LIBRARIES})
@@ -116,16 +121,17 @@ set(ozz_offline_test_objs
3rdparty/ozz-animation/src/animation/offline/raw_skeleton.cc
3rdparty/ozz-animation/src/animation/offline/animation_builder.cc
3rdparty/ozz-animation/src/animation/offline/raw_animation.cc
)
)
target_sources(runtests PRIVATE
tests/AnimGraphResourceTests.cc
tests/AnimGraphEvalTests.cc
tests/AnimGraphEditorTests.cc
# tests/AnimGraphEvalTests.cc
tests/NodeDescriptorTests.cc
tests/SyncTrackTests.cc
tests/main.cc
${ozz_offline_test_objs}
)
)
target_include_directories(
runtests
-202
View File
@@ -1,202 +0,0 @@
//
// Created by martin on 25.03.22.
//
#include "AnimGraph.h"
#include <algorithm>
#include <cstring>
bool AnimGraph::init(AnimGraphContext& context) {
context.m_graph = this;
for (size_t i = 2; i < m_nodes.size(); i++) {
if (!m_nodes[i]->Init(context)) {
return false;
}
}
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
int num_soa_joints = context.m_skeleton->num_soa_joints();
m_animdata_blocks[i]->m_local_matrices.resize(num_soa_joints);
}
return true;
}
void AnimGraph::updateOrderedNodes() {
m_eval_ordered_nodes.clear();
updateOrderedNodesRecursive(0);
}
void AnimGraph::updateOrderedNodesRecursive(int node_index) {
AnimNode* node = m_nodes[node_index];
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
int input_node_index =
getAnimNodeIndex(node_input_connections.at(i).m_source_node);
if (input_node_index == 1) {
continue;
}
updateOrderedNodesRecursive(input_node_index);
}
if (node_index != 0) {
// In case we have multiple output connections from the node we here
// ensure that use the node evaluation that is the furthest away from
// the output.
std::vector<AnimNode*>::iterator find_iter = std::find(
m_eval_ordered_nodes.begin(),
m_eval_ordered_nodes.end(),
node);
if (find_iter != m_eval_ordered_nodes.end()) {
m_eval_ordered_nodes.erase(find_iter);
}
m_eval_ordered_nodes.push_back(node);
}
}
void AnimGraph::markActiveNodes() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_state = AnimNodeEvalState::Deactivated;
}
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
const AnimGraphConnection& graph_input = graph_output_inputs[i];
AnimNode* node = graph_input.m_source_node;
if (node != nullptr) {
node->m_state = AnimNodeEvalState::Activated;
}
}
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
if (checkIsNodeActive(node)) {
int node_index = node->m_index;
node->MarkActiveInputs(m_node_input_connections[node_index]);
// Non-animation data inputs are always active.
for (size_t j = 0, nj = m_node_input_connections[node_index].size();
j < nj;
j++) {
const AnimGraphConnection& input =
m_node_input_connections[node_index][j];
if (input.m_source_node != nullptr
&& input.m_target_socket.m_type
!= SocketType::SocketTypeAnimation) {
input.m_source_node->m_state = AnimNodeEvalState::Activated;
}
}
}
}
}
void AnimGraph::evalSyncTracks() {
for (size_t i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
int node_index = node->m_index;
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->CalcSyncTrack(m_node_input_connections[node_index]);
}
}
void AnimGraph::updateTime(float dt) {
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
AnimNode* node = graph_output_inputs[i].m_source_node;
if (node != nullptr) {
node->UpdateTime(node->m_time_now, node->m_time_now + dt);
}
}
for (size_t i = m_eval_ordered_nodes.size() - 1; i > 0; --i) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state != AnimNodeEvalState::TimeUpdated) {
continue;
}
int node_index = node->m_index;
float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last;
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
AnimNode* input_node = node_input_connections[i].m_source_node;
// Only propagate time updates via animation sockets.
if (input_node != nullptr
&& node_input_connections[i].m_target_socket.m_type
== SocketType::SocketTypeAnimation
&& input_node->m_state == AnimNodeEvalState::Activated) {
input_node->UpdateTime(node_time_last, node_time_now);
}
}
}
}
void AnimGraph::evaluate(AnimGraphContext& context) {
for (int i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->Evaluate(context);
}
}
Socket* AnimGraph::getInputSocket(const std::string& name) {
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {
AnimGraphConnection& connection = m_node_output_connections[1][i];
if (connection.m_source_socket.m_name == name) {
return &connection.m_source_socket;
}
}
return nullptr;
}
Socket* AnimGraph::getOutputSocket(const std::string& name) {
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
AnimGraphConnection& connection = m_node_input_connections[0][i];
if (connection.m_target_socket.m_name == name) {
return &connection.m_target_socket;
}
}
return nullptr;
}
const Socket* AnimGraph::getInputSocket(const std::string& name) const {
for (size_t i = 0, n = m_node_output_connections[1].size(); i < n; i++) {
const AnimGraphConnection& connection = m_node_output_connections[1][i];
if (connection.m_source_socket.m_name == name) {
return &connection.m_source_socket;
}
}
return nullptr;
}
const Socket* AnimGraph::getOutputSocket(const std::string& name) const {
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
const AnimGraphConnection& connection = m_node_input_connections[0][i];
if (connection.m_target_socket.m_name == name) {
return &connection.m_target_socket;
}
}
return nullptr;
}
-231
View File
@@ -1,231 +0,0 @@
//
// Created by martin on 25.03.22.
//
#ifndef ANIMTESTBED_ANIMGRAPH_H
#define ANIMTESTBED_ANIMGRAPH_H
#include "AnimGraphData.h"
#include "AnimGraphNodes.h"
//
// AnimGraph (Runtime)
//
struct AnimGraph {
AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes;
std::vector<AnimNode*> m_eval_ordered_nodes;
std::vector<std::vector<AnimGraphConnection> > m_node_input_connections;
std::vector<std::vector<AnimGraphConnection> > m_node_output_connections;
std::vector<AnimData*> m_animdata_blocks;
NodeDescriptorBase* m_node_descriptor = nullptr;
char* m_input_buffer = nullptr;
char* m_output_buffer = nullptr;
char* m_connection_data_storage = nullptr;
char* m_const_node_inputs = nullptr;
std::vector<Socket>& getGraphOutputs() { return m_node_descriptor->m_inputs; }
std::vector<Socket>& getGraphInputs() { return m_node_descriptor->m_outputs; }
AnimDataAllocator m_anim_data_allocator;
~AnimGraph() { dealloc(); }
bool init(AnimGraphContext& context);
void dealloc() {
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
m_animdata_blocks[i]->m_local_matrices.vector::~vector();
}
m_animdata_blocks.clear();
m_node_input_connections.clear();
m_node_output_connections.clear();
delete[] m_input_buffer;
delete[] m_output_buffer;
delete[] m_connection_data_storage;
delete[] m_const_node_inputs;
for (int i = 0; i < m_nodes.size(); i++) {
delete m_nodes[i];
}
m_nodes.clear();
delete m_node_descriptor;
}
void updateOrderedNodes();
void updateOrderedNodesRecursive(int node_index);
void markActiveNodes();
bool checkIsNodeActive(AnimNode* node) {
return node->m_state != AnimNodeEvalState::Deactivated;
}
void evalSyncTracks();
void updateTime(float dt);
void evaluate(AnimGraphContext& context);
void resetNodeStates() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_time_now = 0.f;
m_nodes[i]->m_time_last = 0.f;
m_nodes[i]->m_state = AnimNodeEvalState::Undefined;
}
}
Socket* getInputSocket(const std::string& name);
Socket* getOutputSocket(const std::string& name);
const Socket* getInputSocket(const std::string& name) const;
const Socket* getOutputSocket(const std::string& name) const;
/** Sets the address that is used for the specified AnimGraph input Socket.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the input is fetched during evaluation.
*/
template <typename T>
void SetInput(const char* name, T* value_ptr) {
m_node_descriptor->SetOutput(name, value_ptr);
for (int i = 0; i < m_node_output_connections[1].size(); i++) {
const AnimGraphConnection& graph_input_connection =
m_node_output_connections[1][i];
if (graph_input_connection.m_source_socket.m_name == name) {
*graph_input_connection.m_target_socket.m_reference.ptr_ptr = value_ptr;
}
}
}
/** Sets the address that is used for the specified AnimGraph output Socket.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the graph output output is written to at the end of evaluation.
*/
template <typename T>
void SetOutput(const char* name, T* value_ptr) {
m_node_descriptor->SetInput(name, value_ptr);
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket.m_name == name) {
if (graph_output_connection.m_source_node == m_nodes[1]
&& graph_output_connection.m_target_node == m_nodes[0]) {
std::cerr << "Error: cannot set output for direct graph input to graph "
"output connections. Use GetOutptPtr for output instead!"
<< std::endl;
return;
}
*graph_output_connection.m_source_socket.m_reference.ptr_ptr =
value_ptr;
// Make sure all other output connections of this pin use the same output pointer
int source_node_index = getAnimNodeIndex(graph_output_connection.m_source_node);
for (int j = 0; j < m_node_output_connections[source_node_index].size(); j++) {
const AnimGraphConnection& source_output_connection = m_node_output_connections[source_node_index][j];
if (source_output_connection.m_target_node == m_nodes[0]) {
continue;
}
if (source_output_connection.m_source_socket.m_name == graph_output_connection.m_source_socket.m_name) {
*source_output_connection.m_target_socket.m_reference.ptr_ptr = value_ptr;
}
}
}
}
}
/** Returns the address that is used for the specified AnimGraph output Socket.
*
* This function is needed for connections that directly connect an AnimGraph
* input Socket to an output Socket of the same AnimGraph.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @return Address that is used for the specified AnimGraph output Socket.
*/
template <typename T>
T* GetOutputPtr(const char* name) {
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket.m_name == name) {
return static_cast<float*>(*graph_output_connection.m_source_socket.m_reference.ptr_ptr);
}
}
return nullptr;
}
void* getInputPtr(const std::string& name) const {
const Socket* input_socket = getInputSocket(name);
if (input_socket != nullptr) {
return input_socket->m_reference.ptr;
}
return nullptr;
}
void* getOutputPtr(const std::string& name) const {
const Socket* input_socket = getOutputSocket(name);
if (input_socket != nullptr) {
return input_socket->m_reference.ptr;
}
return nullptr;
}
int getNodeEvalOrderIndex(const AnimNode* node) {
for (size_t i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
if (m_eval_ordered_nodes[i] == node) {
return i;
}
}
return -1;
}
const AnimNode* getAnimNodeForInput(
size_t node_index,
const std::string& input_name) const {
assert(node_index < m_nodes.size());
const std::vector<AnimGraphConnection>& input_connection =
m_node_input_connections[node_index];
for (size_t i = 0, n = input_connection.size(); i < n; i++) {
if (input_connection[i].m_target_socket.m_name == input_name) {
return input_connection[i].m_source_node;
}
}
return nullptr;
}
AnimNode* getAnimNode(const char* name) {
for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i]->m_name == name) {
return m_nodes[i];
}
}
return nullptr;
}
size_t getAnimNodeIndex(AnimNode* node) {
for (size_t i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i] == node) {
return i;
}
}
return -1;
}
};
#endif //ANIMTESTBED_ANIMGRAPH_H
+176
View File
@@ -0,0 +1,176 @@
#pragma clang diagnostic push
#pragma ide diagnostic ignored "misc-no-recursion"
//
// Created by martin on 17.03.24.
//
#include "AnimGraphBlendTree.h"
#include <algorithm>
bool AnimGraphBlendTree::Init(AnimGraphContext& context) {
for (size_t i = 2; i < m_nodes.size(); i++) {
if (!m_nodes[i]->Init(context)) {
return false;
}
}
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
int num_soa_joints = context.m_skeleton->num_soa_joints();
m_animdata_blocks[i]->m_local_matrices.resize(num_soa_joints);
}
return true;
}
void AnimGraphBlendTree::UpdateOrderedNodes() {
m_eval_ordered_nodes.clear();
UpdateOrderedNodesRecursive(0);
}
void AnimGraphBlendTree::UpdateOrderedNodesRecursive(int node_index) {
AnimNode* node = m_nodes[node_index];
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (size_t i = 0, n = node_input_connections.size(); i < n; i++) {
if (node_input_connections[i].m_crosses_hierarchy) {
continue;
}
int input_node_index =
GetAnimNodeIndex(node_input_connections[i].m_source_node);
if (input_node_index == 1) {
continue;
}
UpdateOrderedNodesRecursive(input_node_index);
}
if (node_index != 0) {
// In case we have multiple output connections from the node we here
// ensure that use the node evaluation that is the furthest away from
// the output.
std::vector<AnimNode*>::iterator find_iter = std::find(
m_eval_ordered_nodes.begin(),
m_eval_ordered_nodes.end(),
node);
if (find_iter != m_eval_ordered_nodes.end()) {
m_eval_ordered_nodes.erase(find_iter);
}
m_eval_ordered_nodes.push_back(node);
}
}
void AnimGraphBlendTree::MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) {
for (AnimNode* node : m_nodes) {
if (node->m_tick_number != m_tick_number) {
node->m_state = AnimNodeEvalState::Deactivated;
}
}
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (size_t i = 0, n = graph_output_inputs.size(); i < n; i++) {
const AnimGraphConnection& graph_input = graph_output_inputs[i];
AnimNode* node = graph_input.m_source_node;
if (node != nullptr) {
node->m_tick_number = m_tick_number;
node->m_state = AnimNodeEvalState::Activated;
}
}
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
if (CheckIsNodeActive(node)) {
size_t node_index = GetAnimNodeIndex(node);
node->MarkActiveInputs(m_node_input_connections[node_index]);
// Non-animation data inputs are always active.
for (size_t j = 0, nj = m_node_input_connections[node_index].size();
j < nj;
j++) {
const AnimGraphConnection& input =
m_node_input_connections[node_index][j];
if (input.m_source_node != nullptr
&& input.m_socket.m_type != SocketType::SocketTypeAnimation) {
input.m_source_node->m_state = AnimNodeEvalState::Activated;
}
}
}
}
}
void AnimGraphBlendTree::CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) {
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; i--) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->CalcSyncTrack(m_node_input_connections[GetAnimNodeIndex(node)]);
}
}
void AnimGraphBlendTree::UpdateTime(float time_last, float time_now) {
float dt = time_now - time_last;
const std::vector<AnimGraphConnection>& graph_output_inputs =
m_node_input_connections[0];
for (const AnimGraphConnection& graph_output_input : graph_output_inputs) {
AnimNode* node = graph_output_input.m_source_node;
if (node != nullptr && node->m_state != AnimNodeEvalState::TimeUpdated) {
node->UpdateTime(time_last, time_now);
}
}
for (int i = m_eval_ordered_nodes.size() - 1; i >= 0; --i) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state != AnimNodeEvalState::TimeUpdated) {
continue;
}
PropagateTimeToNodeInputs(node);
}
m_state = AnimNodeEvalState::TimeUpdated;
}
void AnimGraphBlendTree::Evaluate(AnimGraphContext& context) {
for (int i = 0, n = m_eval_ordered_nodes.size(); i < n; i++) {
AnimNode* node = m_eval_ordered_nodes[i];
if (node->m_state == AnimNodeEvalState::Deactivated) {
continue;
}
node->Evaluate(context);
}
}
void AnimGraphBlendTree::PropagateTimeToNodeInputs(const AnimNode* node) {
size_t node_index = GetAnimNodeIndex(node);
float node_time_now = node->m_time_now;
float node_time_last = node->m_time_last;
const std::vector<AnimGraphConnection>& node_input_connections =
m_node_input_connections[node_index];
for (const AnimGraphConnection& node_input_connection :
node_input_connections) {
AnimNode* input_node = node_input_connection.m_source_node;
// Only propagate time updates via animation sockets.
if (input_node != nullptr
&& node_input_connection.m_socket.m_type
== SocketType::SocketTypeAnimation
&& input_node->m_state == AnimNodeEvalState::Activated) {
input_node->UpdateTime(node_time_last, node_time_now);
}
}
}
#pragma clang diagnostic pop
+213
View File
@@ -0,0 +1,213 @@
//
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMGRAPHBLENDTREE_H
#define ANIMTESTBED_ANIMGRAPHBLENDTREE_H
#include <algorithm>
#include "AnimNode.h"
//
// AnimGraph (Runtime)
//
struct AnimGraphBlendTree : public AnimNode {
AnimData m_local_transforms;
std::vector<AnimNode*> m_nodes;
std::vector<AnimNode*> m_eval_ordered_nodes;
std::vector<std::vector<AnimGraphConnection> > m_node_input_connections;
std::vector<std::vector<AnimGraphConnection> > m_node_output_connections;
[[nodiscard]] const std::vector<AnimGraphConnection>&
GetGraphInputConnections() const {
return m_node_output_connections[1];
}
[[nodiscard]] const std::vector<AnimGraphConnection>&
GetGraphOutputConnections() const {
return m_node_input_connections[0];
}
std::vector<AnimData*> m_animdata_blocks;
NodeDescriptorBase* m_node_descriptor = nullptr;
char* m_input_buffer = nullptr;
char* m_output_buffer = nullptr;
char* m_connection_data_storage = nullptr;
char* m_const_node_inputs = nullptr;
std::vector<Socket>& GetGraphOutputs() { return m_node_descriptor->m_inputs; }
std::vector<Socket>& GetGraphInputs() { return m_node_descriptor->m_outputs; }
AnimDataAllocator m_anim_data_allocator;
~AnimGraphBlendTree() override { dealloc(); }
void StartUpdateTick() { m_tick_number++; }
// AnimNode overrides
bool Init(AnimGraphContext& context) override;
void MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override;
void PropagateTimeToNodeInputs(const AnimNode* node);
void dealloc() {
for (size_t i = 0; i < m_animdata_blocks.size(); i++) {
m_animdata_blocks[i]->m_local_matrices.vector::~vector();
}
m_animdata_blocks.clear();
m_node_input_connections.clear();
m_node_output_connections.clear();
delete[] m_input_buffer;
delete[] m_output_buffer;
delete[] m_connection_data_storage;
delete[] m_const_node_inputs;
for (int i = 0; i < m_nodes.size(); i++) {
delete m_nodes[i];
}
m_nodes.clear();
delete m_node_descriptor;
}
void UpdateOrderedNodes();
void UpdateOrderedNodesRecursive(int node_index);
bool CheckIsNodeActive(AnimNode* node) {
return node->m_tick_number == m_tick_number;
}
void ResetNodeStates() {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
m_nodes[i]->m_time_now = 0.f;
m_nodes[i]->m_time_last = 0.f;
m_nodes[i]->m_state = AnimNodeEvalState::Undefined;
}
}
const Socket* GetInputSocket(const std::string& name) const;
const Socket* GetOutputSocket(const std::string& name) const;
/** Sets the address that is used for the specified AnimGraph input Socket.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the input is fetched during evaluation.
*/
template <typename T>
void SetInput(const char* name, T* value_ptr) {
m_node_descriptor->SetOutput(name, value_ptr);
std::vector<size_t> connected_node_indices;
for (int i = 0; i < m_node_output_connections[1].size(); i++) {
const AnimGraphConnection& graph_input_connection =
m_node_output_connections[1][i];
if (graph_input_connection.m_source_socket_name == name) {
*graph_input_connection.m_socket.m_reference.ptr_ptr = value_ptr;
}
}
}
/** Sets the address that is used for the specified AnimGraph output Socket.
*
* We update the pointer of the outputting node. We also have to ensure that
* all usages of that output use the same pointer.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @param value_ptr Pointer where the graph output output is written to at the end of evaluation.
*/
template <typename T>
void SetOutput(const char* name, T* value_ptr) {
const auto& graph_output_connection = std::find_if(
GetGraphOutputConnections().begin(),
GetGraphOutputConnections().end(),
[&name](const AnimGraphConnection& connection) {
return connection.m_target_socket_name == name;
});
if (graph_output_connection == GetGraphOutputConnections().end()) {
std::cerr << "Error: could not find graph connection to output socket "
<< name << "." << std::endl;
return;
}
if (graph_output_connection->m_source_node == m_nodes[1]
&& graph_output_connection->m_target_node == m_nodes[0]) {
std::cerr << "Error: cannot set output for direct graph input to graph "
"output connections. Use GetOutptPtr for output instead!"
<< std::endl;
return;
}
size_t output_node_index =
GetAnimNodeIndex(graph_output_connection->m_source_node);
const std::vector<AnimGraphConnection>& node_output_connections =
m_node_output_connections[output_node_index];
for (const AnimGraphConnection& connection : node_output_connections) {
if (connection.m_source_socket_name
== graph_output_connection->m_source_socket_name) {
*connection.m_socket.m_reference.ptr_ptr = value_ptr;
}
}
*graph_output_connection->m_socket.m_reference.ptr_ptr = value_ptr;
}
/** Returns the address that is used for the specified AnimGraph output Socket.
*
* This function is needed for connections that directly connect an AnimGraph
* input Socket to an output Socket of the same AnimGraph.
*
* @tparam T Type of the Socket.
* @param name Name of the Socket.
* @return Address that is used for the specified AnimGraph output Socket.
*/
template <typename T>
T* GetOutputPtr(const char* name) {
for (int i = 0; i < m_node_input_connections[0].size(); i++) {
const AnimGraphConnection& graph_output_connection =
m_node_input_connections[0][i];
if (graph_output_connection.m_target_socket_name == name) {
return static_cast<T*>(
*graph_output_connection.m_socket.m_reference.ptr_ptr);
}
}
return nullptr;
}
int GetAnimNodeIndex(const AnimNode* node) const {
for (int i = 0; i < m_nodes.size(); i++) {
if (m_nodes[i] == node) {
return i;
}
}
return -1;
}
};
//
// BlendTreeSocketNode
//
struct BlendTreeSocketNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeSocketNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeSocketNode* node_) {}
};
#endif //ANIMTESTBED_ANIMGRAPHBLENDTREE_H
+30 -3
View File
@@ -22,8 +22,8 @@
//
// Data types
//
struct AnimGraph;
struct AnimNode;
struct AnimData {
ozz::vector<ozz::math::SoaTransform> m_local_matrices;
@@ -161,7 +161,7 @@ struct Socket {
SocketValue m_value = {0};
std::string m_value_string;
union SocketReference {
void* ptr;
void* ptr = nullptr;
void** ptr_ptr;
};
SocketReference m_reference = {0};
@@ -260,6 +260,15 @@ SocketType GetSocketType() {
return SocketType::SocketTypeUndefined;
}
struct AnimGraphConnection {
AnimNode* m_source_node = nullptr;
std::string m_source_socket_name = "";
AnimNode* m_target_node = nullptr;
std::string m_target_socket_name = "";
Socket m_socket;
bool m_crosses_hierarchy = false;
};
struct NodeDescriptorBase {
std::vector<Socket> m_inputs;
std::vector<Socket> m_outputs;
@@ -311,6 +320,13 @@ struct NodeDescriptorBase {
return *socket->m_reference.ptr_ptr;
}
template <typename T>
T GetInputValue(const char* name) const {
const Socket* socket = FindSocket(name, m_inputs);
assert(GetSocketType<T>() == socket->m_type);
return socket->GetValue<T>();
}
template <typename T>
void SetInput(const char* name, T* value_ptr) {
Socket* socket = FindSocket(name, m_inputs);
@@ -402,7 +418,7 @@ struct NodeDescriptorBase {
return socket->GetValue<T>();
}
virtual void UpdateFlags(){};
virtual void UpdateFlags() {};
protected:
Socket* FindSocket(const char* name, std::vector<Socket>& sockets) {
@@ -415,6 +431,17 @@ struct NodeDescriptorBase {
return nullptr;
}
const Socket* FindSocket(const char* name, const std::vector<Socket>& sockets)
const {
for (int i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
return &sockets[i];
}
}
return nullptr;
}
int FindSocketIndex(const char* name, std::vector<Socket>& sockets) {
for (int i = 0, n = sockets.size(); i < n; i++) {
if (sockets[i].m_name == name) {
+425 -262
View File
@@ -6,13 +6,24 @@
#include <sstream>
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
#include "AnimGraphResource.h"
#include "SkinnedMesh.h"
#include "imgui.h"
#include "imnodes.h"
#include "misc/cpp/imgui_stdlib.h"
static AnimGraphResource sGraphGresource = AnimGraphResource();
struct EditorState {
AnimGraphResource* rootGraphResource = nullptr;
std::vector<AnimGraphResource*> hierarchyStack;
size_t hierarchyStackIndex = 0;
bool isGraphLoadedThisFrame = false;
ImVec2 mousePopupStart = {};
};
static EditorState sEditorState;
ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
switch (socket_type) {
@@ -35,36 +46,41 @@ ImNodesPinShape sGetSocketShapeFromSocketType(const SocketType& socket_type) {
return ImNodesPinShape_Quad;
}
void NodeSocketEditor(Socket& socket) {
bool NodeSocketEditor(Socket& socket) {
bool modified = false;
int mode_current = static_cast<int>(socket.m_type);
ImGui::InputText("Name", &socket.m_name);
if (ImGui::InputText("Name", &socket.m_name)) {
modified = true;
}
if (ImGui::Combo(
"Type",
&mode_current,
SocketTypeNames,
sizeof(SocketTypeNames) / sizeof(char*))) {
socket.m_type = static_cast<SocketType>(mode_current);
modified = true;
}
return modified;
}
void RemoveConnectionsForSocket(
AnimGraphResource& graph_resource,
AnimNodeResource& node_resource,
void RemoveBlendTreeConnectionsForSocket(
BlendTreeResource& blend_tree_resource,
AnimNodeResource* node_resource,
Socket& socket) {
std::vector<AnimGraphConnectionResource>::iterator iter =
graph_resource.m_connections.begin();
const BlendTreeConnectionResource* connection =
blend_tree_resource.FindConnectionForSocket(node_resource, socket.m_name);
while (connection != nullptr) {
blend_tree_resource.DisconnectSockets(
blend_tree_resource.GetNode(connection->source_node_index),
connection->source_socket_name,
blend_tree_resource.GetNode(connection->target_node_index),
connection->target_socket_name);
while (iter != graph_resource.m_connections.end()) {
// TODO adjust for refactor
assert(false);
// AnimGraphConnectionResource& connection = *iter;
// if (connection.m_source_node == &node_resource
// && connection.m_source_socket == &socket) {
// iter = sGraphGresource.m_connections.erase(iter);
// } else {
// iter++;
// }
connection = blend_tree_resource.FindConnectionForSocket(
node_resource,
socket.m_name);
}
}
@@ -149,24 +165,31 @@ void SkinnedMeshWidget(SkinnedMesh* skinned_mesh) {
}
void AnimGraphEditorRenderSidebar(
AnimGraphResource& graph_resource,
AnimNodeResource& node_resource) {
ImGui::Text("[%s]", node_resource.m_type_name.c_str());
BlendTreeResource& blend_tree_resource,
AnimNodeResource* node_resource) {
ImGui::Text(
"[%s (%2.2f, %2.2f)]",
node_resource->m_node_type_name.c_str(),
node_resource->m_position[0],
node_resource->m_position[1]);
char node_name_buffer[256];
memset(node_name_buffer, 0, sizeof(node_name_buffer));
strncpy(
node_name_buffer,
node_resource.m_name.c_str(),
std::min(node_resource.m_name.size(), sizeof(node_name_buffer)));
node_resource->m_name.c_str(),
std::min(node_resource->m_name.size(), sizeof(node_name_buffer)));
if (ImGui::InputText("Name", node_name_buffer, sizeof(node_name_buffer))) {
node_resource.m_name = node_name_buffer;
node_resource->m_name = node_name_buffer;
}
int num_properties = node_resource.m_socket_accessor->m_properties.size();
int num_properties = 0;
if (node_resource->m_socket_accessor != nullptr) {
num_properties = node_resource->m_socket_accessor->m_properties.size();
}
for (int i = 0; i < num_properties; i++) {
Socket& property = node_resource.m_socket_accessor->m_properties[i];
Socket& property = node_resource->m_socket_accessor->m_properties[i];
if (property.m_type == SocketType::SocketTypeInt) {
ImGui::InputInt(
property.m_name.c_str(),
@@ -180,9 +203,7 @@ void AnimGraphEditorRenderSidebar(
100.f);
} else if (property.m_type == SocketType::SocketTypeBool) {
bool flag_value = property.GetValue<bool>();
if (ImGui::Checkbox(
property.m_name.c_str(),
&flag_value)) {
if (ImGui::Checkbox(property.m_name.c_str(), &flag_value)) {
property.SetValue(flag_value);
}
} else if (property.m_type == SocketType::SocketTypeString) {
@@ -203,100 +224,360 @@ void AnimGraphEditorRenderSidebar(
}
}
if (&node_resource == &graph_resource.getGraphOutputNode()) {
if (node_resource == blend_tree_resource.GetGraphOutputNode()) {
ImGui::Text("Outputs");
// Graph outputs are the inputs of the output node!
std::vector<Socket>& outputs = node_resource.m_socket_accessor->m_inputs;
std::vector<Socket>& outputs = node_resource->m_socket_accessor->m_inputs;
std::vector<Socket>::iterator iter = outputs.begin();
while (iter != outputs.end()) {
Socket& output = *iter;
ImGui::PushID(&output);
NodeSocketEditor(output);
if (NodeSocketEditor(output)) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->m_socket_accessor->m_inputs = outputs;
}
if (ImGui::Button("X")) {
RemoveConnectionsForSocket(graph_resource, node_resource, output);
RemoveBlendTreeConnectionsForSocket(
blend_tree_resource,
node_resource,
output);
iter = outputs.erase(iter);
} else {
iter++;
}
ImGui::PopID();
}
if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeOutputSocket<float>(
"GraphFloatOutput");
}
}
if (&node_resource == &graph_resource.getGraphInputNode()) {
if (node_resource == blend_tree_resource.GetGraphInputNode()) {
ImGui::Text("Inputs");
// Graph inputs are the outputs of the input node!
std::vector<Socket>& inputs = node_resource.m_socket_accessor->m_outputs;
std::vector<Socket>& inputs = node_resource->m_socket_accessor->m_outputs;
std::vector<Socket>::iterator iter = inputs.begin();
while (iter != inputs.end()) {
Socket& input = *iter;
ImGui::PushID(&input);
NodeSocketEditor(input);
if (NodeSocketEditor(input)) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->m_socket_accessor->m_inputs = inputs;
}
if (ImGui::Button("X")) {
RemoveConnectionsForSocket(graph_resource, node_resource, input);
RemoveBlendTreeConnectionsForSocket(
blend_tree_resource,
node_resource,
input);
iter = inputs.erase(iter);
} else {
iter++;
}
ImGui::PopID();
}
if (ImGui::Button("+")) {
AnimGraphResource* current_graph_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex];
current_graph_resource->RegisterBlendTreeInputSocket<float>(
"GraphFloatInput");
}
}
}
void AnimGraphEditorUpdate() {
void AnimGraphEditorClear() {
if (ax::NodeEditor::GetCurrentEditor() != nullptr) {
ax::NodeEditor::ClearSelection();
}
if (sEditorState.rootGraphResource) {
delete sEditorState.rootGraphResource->m_socket_accessor;
}
delete sEditorState.rootGraphResource;
sEditorState.rootGraphResource = new AnimGraphResource();
sEditorState.rootGraphResource->m_name = "Root";
sEditorState.rootGraphResource->m_graph_type_name = "BlendTree";
sEditorState.rootGraphResource->m_blend_tree_resource.InitGraphConnectors();
sEditorState.rootGraphResource->m_socket_accessor = new NodeDescriptorBase;
sEditorState.hierarchyStack.clear();
sEditorState.hierarchyStack.push_back(sEditorState.rootGraphResource);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] =
sEditorState.hierarchyStack.back();
sEditorState.hierarchyStackIndex = 0;
}
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) {
ax::NodeEditor::SetCurrentEditor(context);
//
// Menu bar
//
ImGui::BeginMenuBar();
if (ImGui::Button("Save")) {
sGraphGresource.saveToFile("editor_graph.json");
sEditorState.rootGraphResource->SaveToFile("editor_graph.json");
}
if (ImGui::Button("Load")) {
sGraphGresource.loadFromFile("editor_graph.json");
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
const AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::SetNodeGridSpacePos(
i,
ImVec2(node_resource.m_position[0], node_resource.m_position[1]));
}
AnimGraphEditorClear();
sEditorState.rootGraphResource->LoadFromFile("editor_graph.json");
sEditorState.isGraphLoadedThisFrame = true;
}
if (ImGui::Button("Clear")) {
sGraphGresource.clear();
AnimGraphEditorClear();
}
if (ImGui::Button("Content")) {
ax::NodeEditor::NavigateToContent();
}
char graph_name_buffer[256];
memset(graph_name_buffer, 0, sizeof(graph_name_buffer));
strncpy(
graph_name_buffer,
sGraphGresource.m_name.c_str(),
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_name.c_str(),
sizeof(graph_name_buffer));
if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) {
sGraphGresource.m_name = graph_name_buffer;
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]->m_name =
graph_name_buffer;
}
ImGui::EndMenuBar();
//
// Breadcrumb navigation
//
for (size_t i = 0, n = sEditorState.hierarchyStack.size(); i < n; i++) {
AnimGraphResource* graph_resource =
dynamic_cast<AnimGraphResource*>(sEditorState.hierarchyStack[i]);
ImGui::PushID(graph_resource);
bool highlight_button = i == sEditorState.hierarchyStackIndex;
if (highlight_button) {
ImGui::PushStyleColor(
ImGuiCol_Button,
(ImVec4)ImColor::HSV(1. / 7.0f, 0.6f, 0.6f));
}
if (ImGui::Button(graph_resource->m_name.c_str())) {
sEditorState.hierarchyStackIndex = i;
}
if (highlight_button) {
ImGui::PopStyleColor(1);
}
ImGui::PopID();
if (i < n - 1) {
ImGui::SameLine();
}
}
ImGui::Columns(2);
//
// Node editor canvas
//
ImNodes::BeginNodeEditor();
ax::NodeEditor::Begin("Graph Editor");
for (size_t node_index = 0,
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNumNodes();
node_index < n;
node_index++) {
AnimNodeResource* node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(node_index);
ax::NodeEditor::NodeId node_id(node_resource);
if (sEditorState.isGraphLoadedThisFrame) {
ax::NodeEditor::SetNodePosition(
node_id,
ImVec2(node_resource->m_position[0], node_resource->m_position[1]));
}
ax::NodeEditor::BeginNode(node_id);
ImGui::Text("%s", node_resource->m_node_type_name.c_str());
// Inputs
std::vector<Socket> node_inputs =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeInputSockets(node_resource);
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ax::NodeEditor::BeginPin(
NodeIndexAndSocketIndexToInputPinId(
static_cast<int>(node_index),
static_cast<int>(j)),
ax::NodeEditor::PinKind::Input);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
// Outputs
std::vector<Socket> node_outputs =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeOutputSockets(node_resource);
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
Socket& socket = node_outputs[j];
ax::NodeEditor::BeginPin(
NodeIndexAndSocketIndexToOutputPinId(
static_cast<int>(node_index),
static_cast<int>(j)),
ax::NodeEditor::PinKind::Output);
ImGui::Text("%s", socket.m_name.c_str());
ax::NodeEditor::EndPin();
}
ax::NodeEditor::EndNode();
ImVec2 node_position = ax::NodeEditor::GetNodePosition(node_id);
node_resource->m_position[0] = node_position.x;
node_resource->m_position[1] = node_position.y;
}
int link_id = 0;
for (size_t connection_id = 0,
n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNumConnections();
connection_id < n;
connection_id++) {
const BlendTreeConnectionResource* connection_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetConnection(connection_id);
const AnimNodeResource* source_node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(
connection_resource->source_node_index);
int source_socket_index =
source_node_resource->m_socket_accessor->GetOutputIndex(
connection_resource->source_socket_name.c_str());
const AnimNodeResource* target_node_resource =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(
connection_resource->target_node_index);
int target_socket_index =
target_node_resource->m_socket_accessor->GetInputIndex(
connection_resource->target_socket_name.c_str());
int source_socket_pin_id = NodeIndexAndSocketIndexToOutputPinId(
static_cast<int>(connection_resource->source_node_index),
source_socket_index);
int target_socket_pin_id = NodeIndexAndSocketIndexToInputPinId(
static_cast<int>(connection_resource->target_node_index),
target_socket_index);
ax::NodeEditor::Link(
ax::NodeEditor::LinkId(connection_resource),
source_socket_pin_id,
target_socket_pin_id);
}
// Create Connections
if (ax::NodeEditor::BeginCreate()) {
ax::NodeEditor::PinId input_pin_id, output_pin_id;
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
int source_node_index;
int source_node_socket_index;
const AnimNodeResource* source_node = nullptr;
const Socket* source_socket = nullptr;
if (input_pin_id) {
OutputPinIdToNodeIndexAndSocketIndex(
input_pin_id.Get(),
&source_node_index,
&source_node_socket_index);
source_node =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(source_node_index);
if (source_node->m_socket_accessor->m_outputs.size()
< source_node_socket_index) {
source_node_socket_index = -1;
} else {
source_socket =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeOutputSocketByIndex(
source_node,
source_node_socket_index);
}
}
int target_node_index;
int target_node_socket_index;
const AnimNodeResource* target_node = nullptr;
const Socket* target_socket = nullptr;
if (output_pin_id) {
InputPinIdToNodeIndexAndSocketIndex(
output_pin_id.Get(),
&target_node_index,
&target_node_socket_index);
target_node =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNode(target_node_index);
if (target_node->m_socket_accessor->m_inputs.size()
< target_node_socket_index) {
target_node_socket_index = -1;
} else {
target_socket =
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.GetNodeInputSocketByIndex(
target_node,
target_node_socket_index);
}
}
if (input_pin_id && output_pin_id) {
if (source_socket == nullptr || target_socket == nullptr
|| !sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.IsConnectionValid(
source_node,
source_socket->m_name,
target_node,
target_socket->m_name)) {
ax::NodeEditor::RejectNewItem();
} else if (ax::NodeEditor::AcceptNewItem()) {
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.ConnectSockets(
source_node,
source_socket->m_name,
target_node,
target_socket->m_name);
}
}
}
}
ax::NodeEditor::EndCreate();
// Popup menu
{
const bool open_popup =
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)
&& ImNodes::IsEditorHovered()
&& ImGui::IsMouseReleased(ImGuiMouseButton_Right);
const bool open_popup = ImGui::IsMouseReleased(ImGuiMouseButton_Right);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
if (!ImGui::IsAnyItemHovered() && open_popup) {
if (open_popup && ImGui::IsWindowHovered()) {
ax::NodeEditor::Suspend();
ImGui::OpenPopup("add node");
ax::NodeEditor::Resume();
sEditorState.mousePopupStart = ImGui::GetMousePos();
}
ax::NodeEditor::Suspend();
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
if (ImGui::BeginPopup("add node")) {
const ImVec2 click_pos = ImGui::GetMousePosOnOpeningCurrentPopup();
std::string node_type_name = "";
if (ImGui::MenuItem("AnimSampler")) {
node_type_name = "AnimSampler";
@@ -326,226 +607,108 @@ void AnimGraphEditorUpdate() {
node_type_name = "ConstScalarNode";
}
if (node_type_name != "") {
AnimNodeResource node_resource =
if (ImGui::MenuItem("BlendTree")) {
node_type_name = "BlendTree";
}
if (!node_type_name.empty()) {
AnimNodeResource* node_resource =
AnimNodeResourceFactory(node_type_name);
size_t node_id = sGraphGresource.m_nodes.size();
ImNodes::SetNodeScreenSpacePos(node_id, ImGui::GetMousePos());
sGraphGresource.m_nodes.push_back(node_resource);
ax::NodeEditor::SetNodePosition(
ax::NodeEditor::NodeId(node_resource),
sEditorState.mousePopupStart);
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource.AddNode(node_resource);
}
ImGui::EndPopup();
}
ImGui::PopStyleVar(ImGuiStyleVar_WindowPadding);
ImGui::PopStyleVar();
ax::NodeEditor::Resume();
}
for (size_t i = 0, n = sGraphGresource.m_nodes.size(); i < n; i++) {
AnimNodeResource& node_resource = sGraphGresource.m_nodes[i];
ImNodes::BeginNode(i);
ImGui::PushItemWidth(110.0f);
// Header
ImNodes::BeginNodeTitleBar();
if (&node_resource == &sGraphGresource.getGraphOutputNode()) {
ImGui::TextUnformatted("Graph Outputs");
} else if (&node_resource == &sGraphGresource.getGraphInputNode()) {
ImGui::TextUnformatted("Graph Inputs");
} else {
ImGui::TextUnformatted(node_resource.m_type_name.c_str());
}
ImNodes::EndNodeTitleBar();
// Inputs
std::vector<Socket>& node_inputs =
node_resource.m_socket_accessor->m_inputs;
for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) {
Socket& socket = node_inputs[j];
ImColor socket_color = ImColor(255, 255, 255, 255);
if (socket.m_flags & SocketFlagAffectsTime) {
socket_color = ImColor(255, 128, 128, 255);
}
ImNodes::BeginInputAttribute(
GenerateInputAttributeId(i, j),
sGetSocketShapeFromSocketType(socket.m_type),
socket_color);
ImGui::TextUnformatted(socket.m_name.c_str());
bool socket_connected =
sGraphGresource.isSocketConnected(node_resource, socket.m_name);
if (!socket_connected && (socket.m_type == SocketType::SocketTypeFloat)) {
ImGui::SameLine();
float socket_value = socket.m_value.float_value;
ImGui::PushItemWidth(
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
if (ImGui::DragFloat("##hidelabel", &socket_value, 0.01f)) {
socket.SetValue(socket_value);
}
ImGui::PopItemWidth();
}
if (!socket_connected && (socket.m_type == SocketType::SocketTypeInt)) {
ImGui::SameLine();
int socket_value = socket.m_value.int_value;
ImGui::PushItemWidth(
130.0f - ImGui::CalcTextSize(socket.m_name.c_str()).x);
if (ImGui::InputInt("##hidelabel", &socket_value, 1)) {
socket.SetValue(socket_value);
}
ImGui::PopItemWidth();
}
ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute();
}
// Outputs
const std::vector<Socket>& node_outputs =
node_resource.m_socket_accessor->m_outputs;
for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) {
const Socket& socket = node_outputs[j];
ImNodes::BeginOutputAttribute(
GenerateOutputAttributeId(i, j),
sGetSocketShapeFromSocketType(socket.m_type),
ImColor(255, 255, 255, 255));
ImGui::TextUnformatted(socket.m_name.c_str());
ImNodes::PushAttributeFlag(
ImNodesAttributeFlags_EnableLinkDetachWithDragClick);
ImNodes::EndInputAttribute();
}
// Graph output node
if (i == 0) {
if (ImGui::Button("+Output")) {
AnimNodeResource& graph_output_node =
sGraphGresource.getGraphOutputNode();
static float bla = 0.f;
std::string socket_name = "Output";
socket_name += std::to_string(
graph_output_node.m_socket_accessor->m_inputs.size());
graph_output_node.m_socket_accessor->RegisterInput<float>(
socket_name.c_str(),
nullptr);
}
} else if (i == 1) {
if (ImGui::Button("+Input")) {
AnimNodeResource& graph_input_node =
sGraphGresource.getGraphInputNode();
static float bla = 0.f;
std::string socket_name = "Input";
socket_name += std::to_string(
graph_input_node.m_socket_accessor->m_outputs.size());
graph_input_node.m_socket_accessor->RegisterOutput<float>(
socket_name.c_str(),
nullptr);
}
}
// Save state in node resource
ImVec2 node_pos = ImNodes::GetNodeGridSpacePos(i);
node_resource.m_position[0] = node_pos[0];
node_resource.m_position[1] = node_pos[1];
ImGui::PopItemWidth();
ImNodes::EndNode();
// Ensure flags such as SocketFlagAffectsTime are properly set.
node_resource.m_socket_accessor->UpdateFlags();
}
for (size_t i = 0, n = sGraphGresource.m_connections.size(); i < n; i++) {
const AnimGraphConnectionResource& connection =
sGraphGresource.m_connections[i];
int start_attr, end_attr;
const AnimNodeResource& source_node =
sGraphGresource.m_nodes[connection.source_node_index];
int source_socket_index = source_node.m_socket_accessor->GetOutputIndex(
connection.source_socket_name.c_str());
const AnimNodeResource& target_node =
sGraphGresource.m_nodes[connection.target_node_index];
int target_socket_index = target_node.m_socket_accessor->GetInputIndex(
connection.target_socket_name.c_str());
start_attr = GenerateOutputAttributeId(
connection.source_node_index,
source_socket_index);
end_attr = GenerateInputAttributeId(
connection.target_node_index,
target_socket_index);
ImNodes::Link(i, start_attr, end_attr);
}
ImNodes::EndNodeEditor();
// Handle newly created links.
int start_attr, end_attr;
if (ImNodes::IsLinkCreated(&start_attr, &end_attr)) {
int node_start_id;
int node_start_output_index;
SplitOutputAttributeId(
start_attr,
&node_start_id,
&node_start_output_index);
int node_end_id;
int node_end_input_index;
SplitInputAttributeId(end_attr, &node_end_id, &node_end_input_index);
AnimGraphConnectionResource connection;
connection.source_node_index = node_start_id;
const AnimNodeResource& source_node =
sGraphGresource.m_nodes[node_start_id];
connection.source_socket_name =
source_node.m_socket_accessor->m_outputs[node_start_output_index]
.m_name;
connection.target_node_index = node_end_id;
const AnimNodeResource& target_node = sGraphGresource.m_nodes[node_end_id];
connection.target_socket_name =
target_node.m_socket_accessor->m_inputs[node_end_input_index].m_name;
sGraphGresource.m_connections.push_back(connection);
}
if (ImGui::IsKeyPressed(ImGuiKey_Delete, false)) {
std::cerr << "Delete key!" << std::endl;
}
// Handle link detachements.
int link_id = 0;
if (ImNodes::IsLinkDestroyed(&link_id)) {
sGraphGresource.m_connections.erase(
sGraphGresource.m_connections.begin() + link_id);
}
int selected_nodes[ImNodes::NumSelectedNodes()];
ImNodes::GetSelectedNodes(selected_nodes);
ax::NodeEditor::End();
//
// Sidebar
//
ImGui::NextColumn();
if (ImNodes::NumSelectedNodes() == 1) {
if (selected_nodes[0] < sGraphGresource.m_nodes.size()) {
AnimNodeResource& selected_node =
sGraphGresource.m_nodes[selected_nodes[0]];
AnimGraphEditorRenderSidebar(sGraphGresource, selected_node);
if (ax::NodeEditor::GetSelectedObjectCount() > 0) {
ax::NodeEditor::NodeId selected_node_id = 0;
ax::NodeEditor::GetSelectedNodes(&selected_node_id, 1);
if (selected_node_id.Get() != 0) {
AnimGraphEditorRenderSidebar(
sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource,
selected_node_id.AsPointer<AnimNodeResource>());
}
}
ImGui::Columns(1);
// Clear flag, however it may be re-set further down when handling double
// clicking into subgraphs.
sEditorState.isGraphLoadedThisFrame = false;
//
// Handle double click into subgraphs
//
ax::NodeEditor::NodeId double_clicked_node_id =
ax::NodeEditor::GetDoubleClickedNode();
if (!double_clicked_node_id.Invalid) {
AnimNodeResource* clicked_node_resource =
double_clicked_node_id.AsPointer<AnimNodeResource>();
if (clicked_node_resource != nullptr
&& clicked_node_resource->m_node_type_name == "BlendTree") {
AnimGraphResource* clicked_graph_resource =
dynamic_cast<AnimGraphResource*>(clicked_node_resource);
assert(clicked_graph_resource != nullptr);
if (sEditorState.hierarchyStack.size()
> sEditorState.hierarchyStackIndex + 1
&& sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex + 1]
== clicked_graph_resource) {
sEditorState.hierarchyStackIndex++;
} else {
sEditorState.hierarchyStack.resize(
sEditorState.hierarchyStackIndex + 1);
sEditorState.hierarchyStack.push_back(clicked_graph_resource);
sEditorState.hierarchyStackIndex++;
}
sEditorState.isGraphLoadedThisFrame = true;
ax::NodeEditor::ClearSelection();
}
}
ax::NodeEditor::LinkId hovered_link = ax::NodeEditor::GetHoveredLink();
if (!hovered_link.Invalid) {
BlendTreeConnectionResource* connection_resource =
hovered_link.AsPointer<BlendTreeConnectionResource>();
if (connection_resource && ImGui::IsKeyPressed(ImGuiKey_Delete)) {
BlendTreeResource* blend_tree_resource =
&sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]
->m_blend_tree_resource;
blend_tree_resource->DisconnectSockets(
blend_tree_resource->GetNode(connection_resource->source_node_index),
connection_resource->source_socket_name,
blend_tree_resource->GetNode(connection_resource->target_node_index),
connection_resource->target_socket_name);
ax::NodeEditor::DeleteLink(hovered_link);
}
}
ax::NodeEditor::SetCurrentEditor(nullptr);
}
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph) {
sGraphGresource.createInstance(anim_graph);
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& blend_tree) {
sEditorState.rootGraphResource->CreateBlendTreeInstance(blend_tree);
}
+37 -3
View File
@@ -5,9 +5,13 @@
#ifndef ANIMTESTBED_ANIMGRAPHEDITOR_H
#define ANIMTESTBED_ANIMGRAPHEDITOR_H
#include "AnimGraph.h"
namespace ax::NodeEditor {
struct EditorContext;
} // namespace ax::NodeEditor
struct SkinnedMesh;
struct AnimGraphBlendTree;
struct SyncTrack;
inline int GenerateInputAttributeId(int node_id, int input_index) {
return ((input_index + 1) << 14) + node_id;
@@ -29,12 +33,42 @@ SplitOutputAttributeId(int attribute_id, int* node_id, int* output_index) {
*output_index = (attribute_id >> 23) - 1;
}
inline int NodeIndexAndSocketIndexToInputPinId(
int node_index,
int input_socket_index) {
return node_index * 1000 + input_socket_index;
}
inline int NodeIndexAndSocketIndexToOutputPinId(
int node_index,
int output_socket_index) {
return node_index * 1000 + 500 + output_socket_index;
}
inline void InputPinIdToNodeIndexAndSocketIndex(
unsigned long input_pin_id,
int* node_index,
int* socket_index) {
*socket_index = input_pin_id % 1000;
*node_index = (input_pin_id - *socket_index) / 1000;
}
inline void OutputPinIdToNodeIndexAndSocketIndex(
unsigned long output_pin_id,
int* node_index,
int* socket_index) {
*socket_index = ((output_pin_id - 500) % 1000);
*node_index = (output_pin_id - *socket_index) / 1000;
}
void SyncTrackEditor(SyncTrack* sync_track);
void SkinnedMeshWidget(SkinnedMesh* skinned_mesh);
void AnimGraphEditorUpdate();
void AnimGraphEditorClear();
void AnimGraphEditorGetRuntimeGraph(AnimGraph& anim_graph);
void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context);
void AnimGraphEditorGetRuntimeGraph(AnimGraphBlendTree& anim_graph);
#endif //ANIMTESTBED_ANIMGRAPHEDITOR_H
+70 -12
View File
@@ -4,17 +4,77 @@
#include "AnimGraphNodes.h"
#include "ozz/base/log.h"
#include "ozz/animation/runtime/blending_job.h"
#include "AnimGraphBlendTree.h"
#include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/blending_job.h"
#include "ozz/base/io/archive.h"
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result;
if (name == "Blend2") {
result = new Blend2Node;
} else if (name == "SpeedScale") {
result = new SpeedScaleNode;
} else if (name == "AnimSampler") {
result = new AnimSamplerNode;
} else if (name == "LockTranslationNode") {
result = new LockTranslationNode;
} else if (name == "BlendTree") {
result = new AnimGraphBlendTree;
} else if (name == "BlendTreeSockets") {
result = new BlendTreeSocketNode;
} else if (name == "MathAddNode") {
result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") {
result = new MathFloatToVec3Node;
} else if (name == "ConstScalarNode") {
result = new ConstScalarNode;
}
if (result != nullptr) {
result->m_node_type_name = name;
return result;
}
std::cerr << "Invalid node type: " << name << std::endl;
return nullptr;
}
NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name,
AnimNode* node) {
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "BlendTreeSockets") {
return CreateNodeDescriptor<BlendTreeSocketNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
void Blend2Node::Evaluate(AnimGraphContext& context) {
assert (i_input0 != nullptr);
assert (i_input1 != nullptr);
assert (i_blend_weight != nullptr);
assert (o_output != nullptr);
assert(i_input0 != nullptr);
assert(i_input1 != nullptr);
assert(i_blend_weight != nullptr);
assert(o_output != nullptr);
// perform blend
ozz::animation::BlendingJob::Layer layers[2];
@@ -38,12 +98,10 @@ void Blend2Node::Evaluate(AnimGraphContext& context) {
//
// AnimSamplerNode
//
AnimSamplerNode::~AnimSamplerNode() noexcept {
m_animation = nullptr;
}
AnimSamplerNode::~AnimSamplerNode() noexcept { m_animation = nullptr; }
bool AnimSamplerNode::Init(AnimGraphContext& context) {
assert (m_animation == nullptr);
assert(m_animation == nullptr);
assert(!m_filename.empty());
AnimGraphContext::AnimationFileMap::const_iterator animation_map_iter;
@@ -70,14 +128,14 @@ bool AnimSamplerNode::Init(AnimGraphContext& context) {
context.m_animation_map[m_filename] = m_animation;
}
assert (context.m_skeleton != nullptr);
assert(context.m_skeleton != nullptr);
m_sampling_context.Resize(context.m_skeleton->num_joints());
return true;
}
void AnimSamplerNode::Evaluate(AnimGraphContext& context) {
assert (o_output != nullptr);
assert(o_output != nullptr);
ozz::animation::SamplingJob sampling_job;
sampling_job.animation = m_animation;
+20 -144
View File
@@ -8,81 +8,12 @@
#include <vector>
#include "AnimGraphData.h"
#include "AnimNode.h"
#include "SyncTrack.h"
#include "ozz/animation/runtime/sampling_job.h"
struct AnimNode;
enum class AnimNodeEvalState {
Undefined,
Deactivated,
Activated,
SyncTrackUpdated,
TimeUpdated,
Evaluated
};
struct AnimGraphConnection {
AnimNode* m_source_node = nullptr;
Socket m_source_socket;
AnimNode* m_target_node = nullptr;
Socket m_target_socket;
};
struct AnimNode {
std::string m_name;
std::string m_node_type_name;
float m_time_now = 0.f;
float m_time_last = 0.f;
size_t m_index = -1;
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
SyncTrack m_sync_track;
virtual ~AnimNode() = default;
virtual bool Init(AnimGraphContext& context) { return true; };
virtual void MarkActiveInputs(const std::vector<AnimGraphConnection>& inputs) {
for (const auto & input : inputs) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr) {
input_node->m_state = AnimNodeEvalState::Activated;
}
}
}
virtual void CalcSyncTrack(const std::vector<AnimGraphConnection>& inputs) {
for (const auto & input : inputs) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr
&& input.m_source_socket.m_type == SocketType::SocketTypeAnimation
&& input_node->m_state != AnimNodeEvalState::Deactivated) {
m_sync_track = input_node->m_sync_track;
return;
}
}
}
virtual void UpdateTime(float time_last, float time_now) {
m_time_last = time_last;
m_time_now = time_now;
m_state = AnimNodeEvalState::TimeUpdated;
}
virtual void Evaluate(AnimGraphContext& context){};
};
//
// BlendTreeNode
//
struct BlendTreeNode : public AnimNode {};
template <>
struct NodeDescriptor<BlendTreeNode> : public NodeDescriptorBase {
NodeDescriptor(BlendTreeNode* node_) {}
};
//
// Blend2Node
//
@@ -93,26 +24,27 @@ struct Blend2Node : public AnimNode {
float* i_blend_weight = nullptr;
bool m_sync_blend = false;
virtual void MarkActiveInputs(const std::vector<AnimGraphConnection>& inputs) override {
for (const auto & input : inputs) {
void MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) override {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node;
if (input_node == nullptr) {
continue;
}
if (input.m_target_socket.m_name == "Input0" && *i_blend_weight < 0.999) {
if (input.m_target_socket_name == "Input0" && *i_blend_weight < 0.999) {
input_node->m_state = AnimNodeEvalState::Activated;
continue;
}
if (input.m_target_socket.m_name == "Input1" && *i_blend_weight > 0.001) {
if (input.m_target_socket_name == "Input1" && *i_blend_weight > 0.001) {
input_node->m_state = AnimNodeEvalState::Activated;
continue;
}
}
}
virtual void Evaluate(AnimGraphContext& context) override;
void Evaluate(AnimGraphContext& context) override;
};
template <>
@@ -139,7 +71,6 @@ struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
}
};
//
// SpeedScaleNode
//
@@ -155,8 +86,8 @@ struct SpeedScaleNode : public AnimNode {
}
void Evaluate(AnimGraphContext& context) override {
assert (i_input != nullptr);
assert (o_output != nullptr);
assert(i_input != nullptr);
assert(o_output != nullptr);
o_output->m_local_matrices = i_input->m_local_matrices;
};
@@ -175,7 +106,6 @@ struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
}
};
//
// AnimSamplerNode
//
@@ -185,8 +115,8 @@ struct AnimSamplerNode : public AnimNode {
ozz::animation::SamplingJob::Context m_sampling_context;
ozz::animation::Animation* m_animation = nullptr;
virtual ~AnimSamplerNode();
virtual bool Init(AnimGraphContext& context) override;
~AnimSamplerNode() noexcept override;
bool Init(AnimGraphContext& context) override;
void UpdateTime(float time_last, float time_now) override {
m_time_last = time_last;
m_time_now = time_now;
@@ -231,7 +161,6 @@ struct NodeDescriptor<LockTranslationNode> : public NodeDescriptorBase {
}
};
//
// ConstScalarNode
//
@@ -239,9 +168,7 @@ struct ConstScalarNode : public AnimNode {
float* o_value = nullptr;
float value = 0.f;
virtual void Evaluate(AnimGraphContext& context){
*o_value = value;
};
virtual void Evaluate(AnimGraphContext& context) { *o_value = value; };
};
template <>
@@ -252,7 +179,6 @@ struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
}
};
//
// MathAddNode
//
@@ -262,8 +188,8 @@ struct MathAddNode : public AnimNode {
float* o_output = nullptr;
void Evaluate(AnimGraphContext& context) override {
assert (i_input0 != nullptr);
assert (i_input1 != nullptr);
assert(i_input0 != nullptr);
assert(i_input1 != nullptr);
*o_output = *i_input0 + *i_input1;
}
@@ -288,9 +214,9 @@ struct MathFloatToVec3Node : public AnimNode {
Vec3* o_output = nullptr;
void Evaluate(AnimGraphContext& context) override {
assert (i_input0 != nullptr);
assert (i_input1 != nullptr);
assert (i_input2 != nullptr);
assert(i_input0 != nullptr);
assert(i_input1 != nullptr);
assert(i_input2 != nullptr);
o_output->v[0] = *i_input0;
o_output->v[1] = *i_input1;
@@ -308,60 +234,10 @@ struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
}
};
AnimNode* AnimNodeFactory(const std::string& name);
static inline AnimNode* AnimNodeFactory(const std::string& name) {
AnimNode* result;
if (name == "Blend2") {
result = new Blend2Node;
} else if (name == "SpeedScale") {
result = new SpeedScaleNode;
} else if (name == "AnimSampler") {
result = new AnimSamplerNode;
} else if (name == "LockTranslationNode") {
result = new LockTranslationNode;
} else if (name == "BlendTree") {
result = new BlendTreeNode;
} else if (name == "MathAddNode") {
result = new MathAddNode;
} else if (name == "MathFloatToVec3Node") {
result = new MathFloatToVec3Node;
} else if (name == "ConstScalarNode") {
result = new ConstScalarNode;
}
if (result != nullptr) {
result->m_node_type_name = name;
return result;
}
std::cerr << "Invalid node type: " << name << std::endl;
return nullptr;
}
static inline NodeDescriptorBase* AnimNodeDescriptorFactory(
NodeDescriptorBase* AnimNodeDescriptorFactory(
const std::string& node_type_name,
AnimNode* node) {
if (node_type_name == "Blend2") {
return CreateNodeDescriptor<Blend2Node>(node);
} else if (node_type_name == "SpeedScale") {
return CreateNodeDescriptor<SpeedScaleNode>(node);
} else if (node_type_name == "AnimSampler") {
return CreateNodeDescriptor<AnimSamplerNode>(node);
} else if (node_type_name == "LockTranslationNode") {
return CreateNodeDescriptor<LockTranslationNode>(node);
} else if (node_type_name == "BlendTree") {
return CreateNodeDescriptor<BlendTreeNode>(node);
} else if (node_type_name == "MathAddNode") {
return CreateNodeDescriptor<MathAddNode>(node);
} else if (node_type_name == "MathFloatToVec3Node") {
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
} else if (node_type_name == "ConstScalarNode") {
return CreateNodeDescriptor<ConstScalarNode>(node);
} else {
std::cerr << "Invalid node type name " << node_type_name << "."
<< std::endl;
}
return nullptr;
}
AnimNode* node);
#endif //ANIMTESTBED_ANIMGRAPHNODES_H
File diff suppressed because it is too large Load Diff
+335 -91
View File
@@ -1,146 +1,390 @@
//
// Created by martin on 04.02.22.
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMGRAPHRESOURCE_H
#define ANIMTESTBED_ANIMGRAPHRESOURCE_H
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <type_traits>
#include <vector>
#include "AnimGraph.h"
#include "AnimGraphData.h"
#include "3rdparty/json/json.hpp"
#include "AnimGraphNodes.h"
#include "SyncTrack.h"
struct AnimNode;
struct AnimGraphBlendTree;
struct AnimGraphStateMachine;
struct AnimNodeResource {
virtual ~AnimNodeResource() = default;
std::string m_name;
std::string m_type_name;
std::string m_node_type_name;
AnimNode* m_anim_node = nullptr;
NodeDescriptorBase* m_socket_accessor = nullptr;
float m_position[2] = {0.f, 0.f};
};
static inline AnimNodeResource AnimNodeResourceFactory(
const std::string& node_type_name) {
AnimNodeResource result;
result.m_type_name = node_type_name;
result.m_anim_node = AnimNodeFactory(node_type_name);
result.m_socket_accessor =
AnimNodeDescriptorFactory(node_type_name, result.m_anim_node);
return result;
}
static inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name);
//
// AnimGraphResource
//
struct AnimGraphConnectionResource {
size_t source_node_index = -1;
struct BlendTreeConnectionResource {
int source_node_index = -1;
std::string source_socket_name;
size_t target_node_index = -1;
int target_node_index = -1;
std::string target_socket_name;
bool operator==(const BlendTreeConnectionResource& other) const {
return (
source_node_index == other.source_node_index
&& target_node_index == other.target_node_index
&& source_socket_name == other.source_socket_name
&& target_socket_name == other.target_socket_name);
}
};
struct AnimGraphResource {
std::string m_name;
std::vector<AnimNodeResource> m_nodes;
std::vector<AnimGraphConnectionResource> m_connections;
struct BlendTreeResource {
std::vector<std::vector<size_t> > m_node_input_connection_indices;
std::vector<std::vector<size_t> > m_node_inputs_subtree;
~AnimGraphResource() {
for (auto & m_node : m_nodes) {
delete m_node.m_anim_node;
delete m_node.m_socket_accessor;
}
~BlendTreeResource() { CleanupNodes(); }
void Reset() {
CleanupNodes();
m_connections.clear();
m_node_input_connection_indices.clear();
m_node_inputs_subtree.clear();
}
AnimGraphResource() { clear(); }
void CleanupNodes() {
for (AnimNodeResource* node_resource : m_nodes) {
delete node_resource->m_anim_node;
delete node_resource->m_socket_accessor;
delete node_resource;
}
void clear();
void clearNodes();
void initGraphConnectors();
bool saveToFile(const char* filename) const;
bool loadFromFile(const char* filename);
m_nodes.clear();
}
AnimNodeResource& getGraphOutputNode() { return m_nodes[0]; }
AnimNodeResource& getGraphInputNode() { return m_nodes[1]; }
void InitGraphConnectors() {
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* output_node = GetGraphOutputNode();
output_node->m_name = "Outputs";
size_t getNodeIndex(const AnimNodeResource& node_resource) const {
AddNode(AnimNodeResourceFactory("BlendTreeSockets"));
AnimNodeResource* input_node = GetGraphInputNode();
output_node->m_name = "Inputs";
}
[[nodiscard]] AnimNodeResource* GetGraphOutputNode() const {
return m_nodes[0];
}
[[nodiscard]] AnimNodeResource* GetGraphInputNode() const {
return m_nodes[1];
}
int GetNodeIndex(const AnimNodeResource* node_resource) const {
for (size_t i = 0, n = m_nodes.size(); i < n; i++) {
if (&m_nodes[i] == &node_resource) {
if (m_nodes[i] == node_resource) {
return i;
}
}
std::cerr << "Error: could not find node index for node resource "
<< node_resource << std::endl;
return -1;
}
size_t addNode(const AnimNodeResource &node_resource) {
[[maybe_unused]] size_t AddNode(AnimNodeResource* node_resource) {
m_nodes.push_back(node_resource);
m_node_input_connection_indices.emplace_back();
m_node_inputs_subtree.emplace_back();
return m_nodes.size() - 1;
}
bool connectSockets(
const AnimNodeResource& source_node,
[[nodiscard]] size_t GetNumNodes() const { return m_nodes.size(); }
[[nodiscard]] AnimNodeResource* GetNode(size_t i) { return m_nodes[i]; }
[[nodiscard]] const AnimNodeResource* GetNode(size_t i) const {
return m_nodes[i];
}
[[nodiscard]] const std::vector<AnimNodeResource*>& GetNodes() const {
return m_nodes;
}
[[nodiscard]] size_t GetNumConnections() const {
return m_connections.size();
}
[[nodiscard]] BlendTreeConnectionResource* GetConnection(size_t i) {
return &m_connections[i];
}
[[nodiscard]] const BlendTreeConnectionResource* GetConnection(
size_t i) const {
return &m_connections[i];
}
[[nodiscard]] const std::vector<BlendTreeConnectionResource>& GetConnections()
const {
return m_connections;
}
Socket* GetNodeOutputSocket(
const AnimNodeResource* node,
const std::string& output_socket_name) const;
const Socket* GetNodeOutputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_output_index) const;
Socket* GetNodeInputSocket(
const AnimNodeResource* node,
const std::string& input_socket_name) const;
const Socket* GetNodeInputSocketByIndex(
const AnimNodeResource* node,
const size_t socket_input_index) const;
std::vector<Socket> GetNodeOutputSockets(const AnimNodeResource* node) const;
std::vector<Socket> GetNodeInputSockets(const AnimNodeResource* node) const;
bool ConnectSockets(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource& target_node,
const std::string& target_socket_name) {
size_t source_node_index = getNodeIndex(source_node);
size_t target_node_index = getNodeIndex(target_node);
const AnimNodeResource* target_node,
const std::string& target_socket_name);
if (source_node_index >= m_nodes.size()
|| target_node_index >= m_nodes.size()) {
std::cerr << "Cannot connect nodes: could not find nodes." << std::endl;
bool DisconnectSockets(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name);
bool IsConnectionValid(
const AnimNodeResource* source_node,
const std::string& source_socket_name,
const AnimNodeResource* target_node,
const std::string& target_socket_name) const;
const BlendTreeConnectionResource* FindConnectionForSocket(
const AnimNodeResource* node,
const std::string& socket_name) const;
bool IsSocketConnected(
const AnimNodeResource* node,
const std::string& socket_name) const {
const BlendTreeConnectionResource* connection =
FindConnectionForSocket(node, socket_name);
return connection != nullptr;
}
std::vector<Socket*> GetConstantNodeInputs(
std::vector<NodeDescriptorBase*>& instance_node_descriptors) const {
std::vector<Socket*> result;
for (size_t i = 0; i < m_nodes.size(); i++) {
for (size_t j = 0,
num_inputs = instance_node_descriptors[i]->m_inputs.size();
j < num_inputs;
j++) {
Socket& input = instance_node_descriptors[i]->m_inputs[j];
if (*input.m_reference.ptr_ptr == nullptr) {
memcpy(
&input.m_value,
&m_nodes[i]->m_socket_accessor->m_inputs[j].m_value,
sizeof(Socket::SocketValue));
result.push_back(&input);
}
}
}
return result;
}
size_t GetNodeIndexForOutputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.target_node_index == 0
&& connection.target_socket_name == socket_name) {
return connection.source_node_index;
}
}
std::cerr << "Error: could not find a node connected to output '"
<< socket_name << "'." << std::endl;
return -1;
}
size_t GetNodeIndexForInputSocket(const std::string& socket_name) const {
for (size_t i = 0; i < m_connections.size(); i++) {
const BlendTreeConnectionResource& connection = m_connections[i];
if (connection.source_node_index == 1
&& connection.source_socket_name == socket_name) {
return connection.target_node_index;
}
}
std::cerr << "Error: could not find a node connected to input '"
<< socket_name << "'." << std::endl;
return -1;
}
void UpdateTreeTopologyInfo();
[[nodiscard]] const std::vector<size_t>& GetNodeEvalOrder() const {
return m_node_eval_order;
}
private:
void UpdateNodeEvalOrder() {
m_node_eval_order.clear();
UpdateNodeEvalOrderRecursive(0);
}
void UpdateNodeEvalOrderRecursive(size_t node_index);
void UpdateNodeSubtrees();
std::vector<AnimNodeResource*> m_nodes;
std::vector<BlendTreeConnectionResource> m_connections;
std::vector<size_t> m_node_eval_order;
};
struct StateMachineTransitionResources {
size_t source_state_index = -1;
size_t target_state_index = -1;
float blend_time = 0.f;
bool sync_blend = false;
};
struct StateMachineResource {
std::vector<AnimNodeResource> m_states;
std::vector<StateMachineTransitionResources> m_transitions;
};
struct AnimGraphResource : AnimNodeResource {
std::string m_graph_type_name;
BlendTreeResource m_blend_tree_resource;
typedef std::pair<const AnimNodeResource*, std::string> NodeSocketPair;
typedef std::map<NodeSocketPair, int> NodeSocketDataOffsetMap;
StateMachineResource m_state_machine_resource;
void Clear() { m_blend_tree_resource.Reset(); }
bool SaveToFile(const char* filename) const;
bool LoadFromFile(const char* filename);
void CreateBlendTreeInstance(AnimGraphBlendTree& result) const;
template <typename T>
bool RegisterBlendTreeInputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return RegisterBlendTreeInputSocket(socket);
}
bool RegisterBlendTreeInputSocket(const Socket& socket) {
AnimNodeResource* input_node = m_blend_tree_resource.GetGraphInputNode();
std::vector<Socket> input_sockets =
input_node->m_socket_accessor->m_outputs;
std::vector<Socket>::const_iterator iter = std::find_if(
input_sockets.begin(),
input_sockets.end(),
[&socket](const Socket& input_socket) {
return socket.m_name == input_socket.m_name;
});
if (iter != input_sockets.end()) {
std::cerr << "Error: cannot register input socket as socket with name '"
<< socket.m_name << "' already exists!" << std::endl;
return false;
}
Socket* source_socket =
source_node.m_socket_accessor->GetOutputSocket(source_socket_name.c_str());
Socket* target_socket =
target_node.m_socket_accessor->GetInputSocket(target_socket_name.c_str());
if (source_socket == nullptr || target_socket == nullptr) {
std::cerr << "Cannot connect nodes: could not find sockets." << std::endl;
return false;
}
AnimGraphConnectionResource connection;
connection.source_node_index = source_node_index;
connection.source_socket_name = source_socket_name;
connection.target_node_index = target_node_index;
connection.target_socket_name = target_socket_name;
m_connections.push_back(connection);
input_node->m_socket_accessor->m_outputs.push_back(socket);
m_socket_accessor->m_inputs = input_node->m_socket_accessor->m_outputs;
return true;
}
bool isSocketConnected(
const AnimNodeResource& node,
const std::string& socket_name) {
size_t node_index = getNodeIndex(node);
for (const auto & connection : m_connections) {
if ((connection.source_node_index == node_index
&& connection.source_socket_name == socket_name)
|| ((connection.target_node_index == node_index)
&& connection.target_socket_name == socket_name)) {
return true;
}
}
template <typename T>
bool RegisterBlendTreeOutputSocket(const std::string& socket_name) {
Socket socket;
socket.m_name = socket_name;
socket.m_type = GetSocketType<T>();
socket.m_type_size = sizeof(T);
return false;
return RegisterBlendTreeOutputSocket(socket);
}
void createInstance(AnimGraph& result) const;
bool RegisterBlendTreeOutputSocket(const Socket& socket) {
AnimNodeResource* output_node = m_blend_tree_resource.GetGraphOutputNode();
void createRuntimeNodeInstances(AnimGraph& instance) const;
void prepareGraphIOData(AnimGraph& instance) const;
void setRuntimeNodeProperties(AnimGraph& instance) const;
std::vector<Socket*> getConstNodeInputs(std::vector<NodeDescriptorBase*>& instance_node_descriptors) const;
std::vector<Socket> output_sockets =
output_node->m_socket_accessor->m_inputs;
std::vector<Socket>::const_iterator iter = std::find_if(
output_sockets.begin(),
output_sockets.end(),
[&socket](const Socket& input_socket) {
return socket.m_name == input_socket.m_name;
});
if (iter != output_sockets.end()) {
return false;
}
output_node->m_socket_accessor->m_inputs.push_back(socket);
m_socket_accessor->m_outputs = output_node->m_socket_accessor->m_inputs;
return true;
}
void CreateStateMachineInstance(AnimGraphStateMachine& result) const;
private:
// BlendTree
bool SaveBlendTreeResourceToFile(const char* filename) const;
void CreateBlendTreeRuntimeNodeInstances(AnimGraphBlendTree& result) const;
void PrepareBlendTreeIOData(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void CreateBlendTreeConnectionInstances(
AnimGraphBlendTree& instance,
NodeSocketDataOffsetMap& node_offset_map) const;
void SetRuntimeNodeProperties(AnimGraphBlendTree& result) const;
bool SaveStateMachineResourceToFile(const char* filename) const;
bool LoadStateMachineResourceFromJson(nlohmann::json const& json_data);
};
static inline AnimNodeResource* AnimNodeResourceFactory(
const std::string& node_type_name) {
AnimNodeResource* result;
if (node_type_name == "BlendTree") {
AnimGraphResource* blend_tree_resource = new AnimGraphResource();
blend_tree_resource->m_blend_tree_resource.InitGraphConnectors();
result = blend_tree_resource;
} else {
result = new AnimNodeResource();
}
result->m_node_type_name = node_type_name;
if (node_type_name == "BlendTreeSockets") {
result->m_anim_node = AnimNodeFactory("BlendTree");
result->m_socket_accessor = new NodeDescriptorBase();
} else {
result->m_anim_node = AnimNodeFactory(node_type_name);
result->m_socket_accessor =
AnimNodeDescriptorFactory(node_type_name, result->m_anim_node);
}
return result;
}
#endif //ANIMTESTBED_ANIMGRAPHRESOURCE_H
+28
View File
@@ -0,0 +1,28 @@
//
// Created by martin on 17.03.24.
//
#include "AnimGraphStateMachine.h"
bool AnimGraphStateMachine::Init(AnimGraphContext& context) {
assert(false && !"Not yet implemented!");
return false;
}
void AnimGraphStateMachine::MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) {
assert(false && !"Not yet implemented!");
}
void AnimGraphStateMachine::CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) {
assert(false && !"Not yet implemented!");
}
void AnimGraphStateMachine::UpdateTime(float time_last, float time_now) {
assert(false && !"Not yet implemented!");
}
void AnimGraphStateMachine::Evaluate(AnimGraphContext& context) {
assert(false && !"Not yet implemented!");
}
+35
View File
@@ -0,0 +1,35 @@
//
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMGRAPHSTATEMACHINE_H
#define ANIMTESTBED_ANIMGRAPHSTATEMACHINE_H
#include "AnimGraphNodes.h"
struct Transition {
AnimNode* m_source_state = nullptr;
AnimNode* m_target_state = nullptr;
float m_blend_time = 0.f;
bool m_sync_blend = false;
};
struct AnimGraphStateMachine : public AnimNode {
std::vector<AnimNode> m_states;
std::vector<Transition> m_transitions;
std::vector<std::vector<Transition*> > m_state_out_transitions;
AnimNode* m_next_state = nullptr;
AnimNode* m_current_state = nullptr;
Transition* m_active_transition = nullptr;
bool Init(AnimGraphContext& context);
void MarkActiveInputs(const std::vector<AnimGraphConnection>& input_connections) override;
void CalcSyncTrack(const std::vector<AnimGraphConnection>& input_connections) override;
void UpdateTime(float time_last, float time_now) override;
void Evaluate(AnimGraphContext& context) override;
};
#endif //ANIMTESTBED_ANIMGRAPHSTATEMACHINE_H
+5
View File
@@ -0,0 +1,5 @@
//
// Created by martin on 17.03.24.
//
#include "AnimNode.h"
+76
View File
@@ -0,0 +1,76 @@
//
// Created by martin on 17.03.24.
//
#ifndef ANIMTESTBED_ANIMNODE_H
#define ANIMTESTBED_ANIMNODE_H
#include <string>
#include <vector>
#include "AnimGraphData.h"
#include "SyncTrack.h"
struct AnimNode;
enum class AnimNodeEvalState {
Undefined,
Deactivated,
Activated,
SyncTrackUpdated,
TimeUpdated,
Evaluated
};
struct AnimNode {
std::string m_name;
std::string m_node_type_name;
int m_tick_number = 0;
AnimNodeEvalState m_state = AnimNodeEvalState::Undefined;
float m_time_now = 0.f;
float m_time_last = 0.f;
SyncTrack m_sync_track;
virtual ~AnimNode() = default;
virtual bool Init(AnimGraphContext& context) {
m_time_now = 0.f;
m_time_last = 0.f;
return true;
};
virtual void MarkActiveInputs(
const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr) {
input_node->m_tick_number = m_tick_number;
input_node->m_state = AnimNodeEvalState::Activated;
}
}
}
virtual void CalcSyncTrack(
const std::vector<AnimGraphConnection>& input_connections) {
for (const auto& input : input_connections) {
AnimNode* input_node = input.m_source_node;
if (input_node != nullptr
&& input.m_socket.m_type == SocketType::SocketTypeAnimation
&& input_node->m_state != AnimNodeEvalState::Deactivated) {
m_sync_track = input_node->m_sync_track;
return;
}
}
}
virtual void UpdateTime(float time_last, float time_now) {
m_time_last = time_last;
m_time_now = time_now;
m_state = AnimNodeEvalState::TimeUpdated;
}
virtual void Evaluate(AnimGraphContext& context){};
};
#endif //ANIMTESTBED_ANIMNODE_H
+10441
View File
File diff suppressed because it is too large Load Diff
+101 -185
View File
@@ -16,13 +16,17 @@
#define GLFW_INCLUDE_NONE
#include <iostream>
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
#include "3rdparty/json/json.hpp"
#include "AnimGraph/AnimGraphBlendTree.h"
#include "AnimGraph/AnimGraphData.h"
#include "Camera.h"
#include "GLFW/glfw3.h"
#include "SkinnedMesh.h"
#include "SkinnedMeshRenderer.h"
#include "SkinnedMeshResource.h"
#include "embedded_fonts.h"
#include "src/AnimGraph/AnimGraphEditor.h"
#include "3rdparty/imgui-node-editor/imgui_node_editor.h"
const int Width = 1024;
const int Height = 768;
@@ -42,18 +46,14 @@ typedef struct {
static void draw_imgui(ImDrawData*);
#define HANDMADE_MATH_IMPLEMENTATION
#define HANDMADE_MATH_NO_SSE
#include "HandmadeMath.h"
// ozz-animation headers
#include <cmath> // fmodf
#include <fstream>
#include <memory> // std::unique_ptr, std::make_unique
#include "SkinnedMeshRenderer.h"
#include "ozz/animation/runtime/animation.h"
#include "ozz/animation/runtime/local_to_model_job.h"
#include "ozz/animation/runtime/sampling_job.h"
#include "ozz/animation/runtime/skeleton.h"
#include "ozz/base/containers/vector.h"
@@ -61,7 +61,6 @@ static void draw_imgui(ImDrawData*);
#include "ozz/base/io/stream.h"
#include "ozz/base/log.h"
#include "ozz/base/maths/soa_transform.h"
#include "ozz/base/maths/vec_float.h"
static struct {
struct {
@@ -69,7 +68,7 @@ static struct {
ozz::animation::SamplingJob sampling_job;
ozz::vector<ozz::math::SoaTransform> local_matrices;
} ozz;
sg_pass_action pass_action;
sg_pass_action pass_action = {};
Camera camera;
struct {
bool skeleton;
@@ -87,7 +86,7 @@ static struct {
bool paused;
bool use_graph = false;
} time;
} state;
} state = {};
typedef struct {
int32_t mousedX;
@@ -160,12 +159,10 @@ struct Viewport {
this->pass = sg_make_pass(&offscreen_pass_desc);
sg_pipeline_desc gl_pipeline_desc = {
.depth = {
.compare = SG_COMPAREFUNC_LESS_EQUAL,
.write_enabled = true
},
.cull_mode = SG_CULLMODE_BACK
};
.depth = {.compare = SG_COMPAREFUNC_LESS_EQUAL, .write_enabled = true},
.cull_mode = SG_CULLMODE_BACK,
.sample_count = cMSAASampleCount};
// this->pip = sg_make_pipeline(gl_pipeline_desc);
}
};
@@ -173,13 +170,6 @@ struct ApplicationConfig {
int window_position[2] = {100, 30};
int window_size[2] = {1000, 600};
struct LegacyGraphEditor {
bool visible = false;
int position[2] = {20, 20};
int size[2] = {800, 500};
};
LegacyGraphEditor legacy_graph_editor;
struct GraphEditor {
bool visible = false;
int position[2] = {20, 20};
@@ -188,7 +178,7 @@ struct ApplicationConfig {
ax::NodeEditor::EditorContext* context = nullptr;
};
GraphEditor graph_editor;
struct SkinnedMeshWidget {
bool visible = false;
int position[2] = {20, 20};
@@ -225,18 +215,12 @@ void to_json(nlohmann::json& j, const ApplicationConfig& config) {
j["main_window"]["size"][0] = config.window_size[0];
j["main_window"]["size"][1] = config.window_size[1];
j["legacy_graph_editor"]["visible"] = config.legacy_graph_editor.visible;
j["legacy_graph_editor"]["position"][0] = config.legacy_graph_editor.position[0];
j["legacy_graph_editor"]["position"][1] = config.legacy_graph_editor.position[1];
j["legacy_graph_editor"]["size"][0] = config.legacy_graph_editor.size[0];
j["legacy_graph_editor"]["size"][1] = config.legacy_graph_editor.size[1];
j["graph_editor"]["visible"] = config.graph_editor.visible;
j["graph_editor"]["position"][0] = config.graph_editor.position[0];
j["graph_editor"]["position"][1] = config.graph_editor.position[1];
j["graph_editor"]["size"][0] = config.graph_editor.size[0];
j["graph_editor"]["size"][1] = config.graph_editor.size[1];
j["skinned_mesh_widget"]["visible"] = config.skinned_mesh_widget.visible;
j["skinned_mesh_widget"]["position"][0] =
config.skinned_mesh_widget.position[0];
@@ -282,29 +266,11 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) {
}
}
if (j.contains("legacy_graph_editor")) {
if (j["legacy_graph_editor"].contains("visible")) {
config.legacy_graph_editor.visible = j["legacy_graph_editor"]["visible"];
}
if (j["legacy_graph_editor"].contains("position")
and j["legacy_graph_editor"]["position"].size() == 2) {
config.legacy_graph_editor.position[0] = j["legacy_graph_editor"]["position"].at(0);
config.legacy_graph_editor.position[1] = j["legacy_graph_editor"]["position"].at(1);
}
if (j["legacy_graph_editor"].contains("size")
and j["legacy_graph_editor"]["size"].size() == 2) {
config.legacy_graph_editor.size[0] = j["legacy_graph_editor"]["size"].at(0);
config.legacy_graph_editor.size[1] = j["legacy_graph_editor"]["size"].at(1);
}
}
if (j.contains("graph_editor")) {
if (j["graph_editor"].contains("visible")) {
config.graph_editor.visible = j["graph_editor"]["visible"];
}
if (j["graph_editor"].contains("position")
and j["graph_editor"]["position"].size() == 2) {
config.graph_editor.position[0] = j["graph_editor"]["position"].at(0);
@@ -317,7 +283,7 @@ void from_json(const nlohmann::json& j, ApplicationConfig& config) {
config.graph_editor.size[1] = j["graph_editor"]["size"].at(1);
}
}
if (j.contains("skinned_mesh_widget")) {
if (j["skinned_mesh_widget"].contains("visible")) {
config.skinned_mesh_widget.visible = j["skinned_mesh_widget"]["visible"];
@@ -476,7 +442,7 @@ int main() {
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
glfwWindowHint(GLFW_SAMPLES, 16);
glfwWindowHint(GLFW_SAMPLES, cMSAASampleCount);
GLFWwindow* w =
glfwCreateWindow(Width, Height, "ATP Editor", nullptr, nullptr);
glfwMakeContextCurrent(w);
@@ -531,7 +497,9 @@ int main() {
// setup sokol_gfx and sokol_time
stm_setup();
sg_desc desc = {.logger = {.func = slog_func}};
sg_desc desc = {
.logger = {.func = slog_func},
.context{.sample_count = cMSAASampleCount}};
sg_setup(&desc);
assert(sg_isvalid());
@@ -552,11 +520,12 @@ int main() {
skinned_mesh_resource.createInstance(skinned_mesh);
skinned_mesh.SetCurrentAnimation(0);
AnimGraph anim_graph;
AnimGraphBlendTree anim_graph;
AnimGraphContext anim_graph_context;
AnimData anim_graph_output;
anim_graph_output.m_local_matrices.resize(
skinned_mesh.m_skeleton.num_soa_joints());
AnimGraphEditorClear();
state.time.factor = 1.0f;
@@ -568,7 +537,20 @@ int main() {
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.IniFilename = "ATPImgui.ini";
io.Fonts->AddFontDefault();
//io.Fonts->AddFontDefault();
ImFontConfig font_config;
font_config.OversampleH = 4;
font_config.OversampleV = 4;
font_config.GlyphExtraSpacing.x = 1.0f;
io.Fonts->AddFontFromMemoryCompressedTTF(
// roboto_medium_ttf_compressed_data,
// roboto_medium_ttf_compressed_size,
droid_sans_ttf_compressed_data,
droid_sans_ttf_compressed_size,
14,
&font_config);
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB;
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
@@ -662,6 +644,7 @@ int main() {
pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
pip_desc.colors[0].write_mask = SG_COLORMASK_RGB;
pip_desc.sample_count = cMSAASampleCount;
pip_desc.label = "imgui-rendering";
pip = sg_make_pipeline(&pip_desc);
@@ -673,7 +656,9 @@ int main() {
// Graph Editor
gApplicationConfig.graph_editor.config.SettingsFile = "graph_editor.json";
gApplicationConfig.graph_editor.context = ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config);
gApplicationConfig.graph_editor.config.NavigateButtonIndex = 2;
gApplicationConfig.graph_editor.context =
ax::NodeEditor::CreateEditor(&gApplicationConfig.graph_editor.config);
// draw loop
while (!glfwWindowShouldClose(w)) {
@@ -715,13 +700,7 @@ int main() {
// handle input
handle_mouse(w, &gGuiInputState);
if (glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_RIGHT)) {
if (gControlMode == ControlMode::ControlModeNone) {
gControlMode = ControlMode::ControlModeFPS;
Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]);
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
}
} else {
if (!glfwGetMouseButton(w, GLFW_MOUSE_BUTTON_RIGHT)) {
gControlMode = ControlMode::ControlModeNone;
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
Camera_Update(
@@ -765,11 +744,11 @@ int main() {
Camera_Update(
&state.camera,
cur_width,
cur_height,
offscreen_viewport.size[0],
offscreen_viewport.size[1],
static_cast<float>(state.time.frame),
gGuiInputState.mousedX,
gGuiInputState.mousedY,
static_cast<float>(gGuiInputState.mousedX),
static_cast<float>(gGuiInputState.mousedY),
camera_accel);
}
@@ -801,9 +780,6 @@ int main() {
&gApplicationConfig.animation_player_widget.visible);
ImGui::Separator();
ImGui::Checkbox(
"Legacy Graph Editor",
&gApplicationConfig.legacy_graph_editor.visible);
ImGui::Checkbox(
"ImGui Demo",
@@ -817,13 +793,12 @@ int main() {
AnimGraphEditorGetRuntimeGraph(anim_graph);
anim_graph_context.m_skeleton = &skinned_mesh.m_skeleton;
anim_graph.init(anim_graph_context);
anim_graph.Init(anim_graph_context);
// For simplicity use first animation data output
const std::vector<Socket>& graph_output_sockets =
anim_graph.getGraphOutputs();
for (int i = 0; i < graph_output_sockets.size(); i++) {
const Socket& output = graph_output_sockets[i];
anim_graph.GetGraphOutputs();
for (const auto& output : graph_output_sockets) {
if (output.m_type == SocketType::SocketTypeAnimation) {
anim_graph.SetOutput(output.m_name.c_str(), &anim_graph_output);
}
@@ -852,9 +827,20 @@ int main() {
ImGui::Begin("Viewport", &gApplicationConfig.viewport_widget.visible);
if (ImGui::IsWindowHovered()
&& ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
if (gControlMode == ControlMode::ControlModeNone) {
gControlMode = ControlMode::ControlModeFPS;
Camera_CalcFromMatrix(&state.camera, &state.camera.mtxView[0]);
glfwSetInputMode(w, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
}
}
ImVec2 viewport_widget_size = ImGui::GetWindowSize();
gApplicationConfig.viewport_widget.size[0] = viewport_widget_size.x;
gApplicationConfig.viewport_widget.size[1] = viewport_widget_size.y;
gApplicationConfig.viewport_widget.size[0] =
static_cast<int>(viewport_widget_size.x);
gApplicationConfig.viewport_widget.size[1] =
static_cast<int>(viewport_widget_size.y);
ImGui::Text(
"Viewport size: %d, %d",
@@ -865,15 +851,19 @@ int main() {
int* current_size = offscreen_viewport.size;
if (current_size[0] != content_size[0]
|| current_size[1] != content_size[1]
if (static_cast<float>(current_size[0]) != content_size[0]
|| static_cast<float>(current_size[1]) != content_size[1]
|| offscreen_viewport.pass.id == 0) {
offscreen_viewport.Resize(content_size[0], content_size[1]);
offscreen_viewport.Resize(
static_cast<int>(content_size[0]),
static_cast<int>(content_size[1]));
}
ImGui::Image(
(ImTextureID)(uintptr_t)offscreen_viewport.color_image.id,
ImVec2(offscreen_viewport.size[0], offscreen_viewport.size[1]),
ImVec2(
static_cast<float>(offscreen_viewport.size[0]),
static_cast<float>(offscreen_viewport.size[1])),
ImVec2(0.0f, 1.0f),
ImVec2(1.0f, 0.0f));
@@ -896,13 +886,17 @@ int main() {
if (gApplicationConfig.skinned_mesh_widget.visible) {
ImGui::SetNextWindowPos(
ImVec2(
gApplicationConfig.skinned_mesh_widget.position[0],
gApplicationConfig.skinned_mesh_widget.position[1]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.position[0]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.position[1])),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(
gApplicationConfig.skinned_mesh_widget.size[0],
gApplicationConfig.skinned_mesh_widget.size[1]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.size[0]),
static_cast<float>(
gApplicationConfig.skinned_mesh_widget.size[1])),
ImGuiCond_FirstUseEver);
ImGui::Begin(
@@ -911,15 +905,15 @@ int main() {
ImVec2 skinned_mesh_widget_position = ImGui::GetWindowPos();
gApplicationConfig.skinned_mesh_widget.position[0] =
skinned_mesh_widget_position.x;
static_cast<int>(skinned_mesh_widget_position.x);
gApplicationConfig.skinned_mesh_widget.position[1] =
skinned_mesh_widget_position.y;
static_cast<int>(skinned_mesh_widget_position.y);
ImVec2 skinned_mesh_widget_size = ImGui::GetWindowSize();
gApplicationConfig.skinned_mesh_widget.size[0] =
skinned_mesh_widget_size.x;
static_cast<int>(skinned_mesh_widget_size.x);
gApplicationConfig.skinned_mesh_widget.size[1] =
skinned_mesh_widget_size.y;
static_cast<int>(skinned_mesh_widget_size.y);
SkinnedMeshWidget(&skinned_mesh);
@@ -929,13 +923,17 @@ int main() {
if (gApplicationConfig.animation_player_widget.visible) {
ImGui::SetNextWindowPos(
ImVec2(
gApplicationConfig.animation_player_widget.position[0],
gApplicationConfig.animation_player_widget.position[1]),
static_cast<float>(
gApplicationConfig.animation_player_widget.position[0]),
static_cast<float>(
gApplicationConfig.animation_player_widget.position[1])),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(
gApplicationConfig.animation_player_widget.size[0],
gApplicationConfig.animation_player_widget.size[1]),
static_cast<float>(
gApplicationConfig.animation_player_widget.size[0]),
static_cast<float>(
gApplicationConfig.animation_player_widget.size[1])),
ImGuiCond_FirstUseEver);
ImGui::Begin(
@@ -944,15 +942,15 @@ int main() {
ImVec2 animation_player_widget_position = ImGui::GetWindowPos();
gApplicationConfig.animation_player_widget.position[0] =
animation_player_widget_position.x;
static_cast<int>(animation_player_widget_position.x);
gApplicationConfig.animation_player_widget.position[1] =
animation_player_widget_position.y;
static_cast<int>(animation_player_widget_position.y);
ImVec2 animation_player_widget_size = ImGui::GetWindowSize();
gApplicationConfig.animation_player_widget.size[0] =
animation_player_widget_size.x;
static_cast<int>(animation_player_widget_size.x);
gApplicationConfig.animation_player_widget.size[1] =
animation_player_widget_size.y;
static_cast<int>(animation_player_widget_size.y);
if (anim_graph.m_nodes.size() > 0) {
ImGui::Checkbox("Use Graph", &state.time.use_graph);
@@ -973,7 +971,7 @@ int main() {
"Animation",
&selected,
items,
skinned_mesh.m_animations.size())) {
static_cast<int>(skinned_mesh.m_animations.size()))) {
state.ozz.animation = skinned_mesh.m_animations[selected];
}
}
@@ -1014,8 +1012,8 @@ int main() {
if (state.ozz.animation != nullptr) {
state.ozz.sampling_job.animation = state.ozz.animation;
state.ozz.sampling_job.ratio =
state.time.absolute / state.ozz.animation->duration();
state.ozz.sampling_job.ratio = static_cast<float>(state.time.absolute)
/ state.ozz.animation->duration();
state.ozz.sampling_job.context = &skinned_mesh.m_sampling_context;
state.ozz.sampling_job.output =
ozz::make_span(skinned_mesh.m_local_matrices);
@@ -1028,11 +1026,12 @@ int main() {
skinned_mesh.CalcModelMatrices();
}
if (state.time.use_graph && anim_graph.m_nodes.size() > 0
if (state.time.use_graph && !anim_graph.m_nodes.empty()
&& state.time.anim_update_time > 0.) {
anim_graph.markActiveNodes();
anim_graph.updateTime(state.time.anim_update_time);
anim_graph.evaluate(anim_graph_context);
// TODO: update for new API after embedding refactor
// anim_graph.MarkActiveNodes();
// anim_graph.UpdateTime()pdateTime(state.time.anim_update_time);
// anim_graph.evaluate(anim_graph_context);
skinned_mesh.m_local_matrices = anim_graph_output.m_local_matrices;
skinned_mesh.CalcModelMatrices();
@@ -1074,87 +1073,7 @@ int main() {
gApplicationConfig.graph_editor.size[0] = graph_editor_size.x;
gApplicationConfig.graph_editor.size[1] = graph_editor_size.y;
ax::NodeEditor::SetCurrentEditor(gApplicationConfig.graph_editor.context);
ax::NodeEditor::Begin("Graph Editor");
int unique_id = 1;
ax::NodeEditor::BeginNode(unique_id++);
// Node A
ImGui::Text("Node A");
ax::NodeEditor::BeginPin(unique_id++, ax::NodeEditor::PinKind::Input);
ImGui::Text("In");
ax::NodeEditor::EndPin();
ImGui::SameLine();
ax::NodeEditor::BeginPin(unique_id++, ax::NodeEditor::PinKind::Output);
ImGui::Text("Out");
ax::NodeEditor::EndPin();
ax::NodeEditor::EndNode();
// Node B
ax::NodeEditor::BeginNode(unique_id++);
ImGui::Text("Node B");
ax::NodeEditor::BeginPin(unique_id++, ax::NodeEditor::PinKind::Input);
ImGui::Text("In");
ax::NodeEditor::EndPin();
ImGui::SameLine();
ax::NodeEditor::BeginPin(unique_id++, ax::NodeEditor::PinKind::Output);
ImGui::Text("Out");
ax::NodeEditor::EndPin();
ax::NodeEditor::EndNode();
// Create Connections
if (ax::NodeEditor::BeginCreate()) {
ax::NodeEditor::PinId input_pin_id, output_pin_id;
if (ax::NodeEditor::QueryNewLink(&input_pin_id, &output_pin_id)) {
if (input_pin_id && output_pin_id) {
if (ax::NodeEditor::AcceptNewItem()) {
}
}
}
}
ax::NodeEditor::EndCreate();
ax::NodeEditor::End();
ax::NodeEditor::SetCurrentEditor(nullptr);
ImGui::End();
}
// Legacy Animation Graph Editor
if (gApplicationConfig.legacy_graph_editor.visible) {
ImGui::SetNextWindowPos(
ImVec2(
gApplicationConfig.legacy_graph_editor.position[0],
gApplicationConfig.legacy_graph_editor.position[1]),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(
ImVec2(
gApplicationConfig.legacy_graph_editor.size[0],
gApplicationConfig.legacy_graph_editor.size[1]),
ImGuiCond_FirstUseEver);
ImGui::Begin(
"Legacy Graph Editor",
&gApplicationConfig.legacy_graph_editor.visible,
ImGuiWindowFlags_MenuBar);
ImVec2 graph_editor_position = ImGui::GetWindowPos();
gApplicationConfig.legacy_graph_editor.position[0] = graph_editor_position.x;
gApplicationConfig.legacy_graph_editor.position[1] = graph_editor_position.y;
ImVec2 graph_editor_size = ImGui::GetWindowSize();
gApplicationConfig.legacy_graph_editor.size[0] = graph_editor_size.x;
gApplicationConfig.legacy_graph_editor.size[1] = graph_editor_size.y;
AnimGraphEditorUpdate();
AnimGraphEditorUpdate(gApplicationConfig.graph_editor.context);
ImGui::End();
}
@@ -1176,9 +1095,6 @@ int main() {
draw_imgui(ImGui::GetDrawData());
sg_end_pass();
sg_commit();
glfwSwapBuffers(w);
glfwPollEvents();
+40
View File
@@ -0,0 +1,40 @@
#include "AnimGraph/AnimGraphEditor.h"
#include "catch.hpp"
TEST_CASE("Node Socket To InputPin Conversion", "[animGraphEditor]") {
int node_index = 321;
int socket_index = 221;
long socket_id;
socket_id = NodeIndexAndSocketIndexToInputPinId(node_index, socket_index);
int node_index_resolved;
int socket_index_resolved;
InputPinIdToNodeIndexAndSocketIndex(
socket_id,
&node_index_resolved,
&socket_index_resolved);
CHECK(node_index == node_index_resolved);
CHECK(socket_index == socket_index_resolved);
}
TEST_CASE("Node Socket To OutputPin Conversion", "[animGraphEditor]") {
int node_index = 321;
int socket_index = 221;
long socket_id;
socket_id = NodeIndexAndSocketIndexToOutputPinId(node_index, socket_index);
int node_index_resolved;
int socket_index_resolved;
OutputPinIdToNodeIndexAndSocketIndex(
socket_id,
&node_index_resolved,
&socket_index_resolved);
CHECK(node_index == node_index_resolved);
CHECK(socket_index == socket_index_resolved);
}
+2 -2
View File
@@ -3,8 +3,8 @@
//
#include "AnimGraph/AnimGraph.h"
#include "AnimGraph/AnimGraphBlendTreeResource.h"
#include "AnimGraph/AnimGraphEditor.h"
#include "AnimGraph/AnimGraphResource.h"
#include "catch.hpp"
#include "ozz/animation/offline/animation_builder.h"
#include "ozz/animation/offline/raw_animation.h"
@@ -141,7 +141,7 @@ TEST_CASE_METHOD(
SimpleAnimFixture,
"AnimGraphSimpleEval",
"[AnimGraphEvalTests]") {
AnimGraphResource graph_resource;
AnimGraphBlendTreeResource graph_resource;
// Add nodes
size_t trans_x_node_index =
File diff suppressed because it is too large Load Diff