Initial step for connectivity refactor.
Instead of wiring up pointers with prepareNodeEval() and finishNodeEval() use for each connection a single memory block where outputs and inputs point to.AnimGraphEditor
parent
411aa5ef20
commit
91607baa9d
|
@ -15,16 +15,9 @@ bool AnimGraph::init(AnimGraphContext& context) {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<AnimGraphConnection>& graph_outputs = m_node_input_connections[0];
|
||||
for (size_t i = 0, n = graph_outputs.size(); i < n; i++) {
|
||||
AnimGraphConnection& connection = graph_outputs[i];
|
||||
if (connection.m_target_socket.m_type == SocketType::SocketTypeAnimation) {
|
||||
AnimData* graph_anim_output =
|
||||
static_cast<AnimData*>(connection.m_target_socket.m_reference.ptr);
|
||||
assert(graph_anim_output != nullptr);
|
||||
graph_anim_output->m_local_matrices.resize(
|
||||
context.m_skeleton->num_soa_joints());
|
||||
}
|
||||
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;
|
||||
|
@ -37,11 +30,11 @@ void AnimGraph::updateOrderedNodes() {
|
|||
|
||||
void AnimGraph::updateOrderedNodesRecursive(int node_index) {
|
||||
AnimNode* node = m_nodes[node_index];
|
||||
const std::vector<AnimGraphConnection> node_input_connections =
|
||||
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[i].m_source_node);
|
||||
getAnimNodeIndex(node_input_connections.at(i).m_source_node);
|
||||
|
||||
if (input_node_index == 1) {
|
||||
continue;
|
||||
|
@ -104,10 +97,7 @@ void AnimGraph::prepareNodeEval(
|
|||
continue;
|
||||
}
|
||||
|
||||
assert (*output_connection.m_source_socket.m_reference.ptr_ptr == nullptr);
|
||||
|
||||
(*output_connection.m_source_socket.m_reference.ptr_ptr) =
|
||||
m_anim_data_allocator.allocate(graph_context.m_skeleton);
|
||||
// TODO: only allocate local matrices for active nodes
|
||||
}
|
||||
|
||||
for (size_t i = 0, n = m_node_input_connections[node_index].size(); i < n;
|
||||
|
@ -118,9 +108,6 @@ void AnimGraph::prepareNodeEval(
|
|||
!= SocketType::SocketTypeAnimation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
(*input_connection.m_target_socket.m_reference.ptr_ptr) =
|
||||
(*input_connection.m_source_socket.m_reference.ptr_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,9 +121,7 @@ void AnimGraph::finishNodeEval(size_t node_index) {
|
|||
continue;
|
||||
}
|
||||
|
||||
m_anim_data_allocator.free(static_cast<AnimData*>(
|
||||
*input_connection.m_source_socket.m_reference.ptr_ptr));
|
||||
(*input_connection.m_source_socket.m_reference.ptr_ptr) = nullptr;
|
||||
// TODO: free local matrices for inactive nodes
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,20 +147,6 @@ void AnimGraph::evalOutputNode() {
|
|||
for (size_t i = 0, n = m_node_input_connections[0].size(); i < n; i++) {
|
||||
AnimGraphConnection& graph_output_connection =
|
||||
m_node_input_connections[0][i];
|
||||
|
||||
if (graph_output_connection.m_source_socket.m_type
|
||||
!= SocketType::SocketTypeAnimation) {
|
||||
memcpy(
|
||||
graph_output_connection.m_target_socket.m_reference.ptr,
|
||||
graph_output_connection.m_source_socket.m_reference.ptr,
|
||||
graph_output_connection.m_target_socket.m_type_size);
|
||||
} else {
|
||||
AnimData* source_data = static_cast<AnimData*>(
|
||||
*graph_output_connection.m_source_socket.m_reference.ptr_ptr);
|
||||
AnimData* target_data = static_cast<AnimData*>(
|
||||
graph_output_connection.m_target_socket.m_reference.ptr);
|
||||
target_data->m_local_matrices = source_data->m_local_matrices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,11 @@ struct AnimGraph {
|
|||
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;
|
||||
NodeSocketAccessorBase* m_socket_accessor;
|
||||
std::vector<AnimData*> m_animdata_blocks;
|
||||
NodeDescriptorBase* m_socket_accessor;
|
||||
char* m_input_buffer = nullptr;
|
||||
char* m_output_buffer = nullptr;
|
||||
char* m_connection_data_storage = nullptr;
|
||||
|
||||
std::vector<Socket>& getGraphOutputs() { return m_socket_accessor->m_inputs; }
|
||||
std::vector<Socket>& getGraphInputs() { return m_socket_accessor->m_outputs; }
|
||||
|
@ -31,48 +33,17 @@ struct AnimGraph {
|
|||
|
||||
bool init(AnimGraphContext& context);
|
||||
void dealloc() {
|
||||
if (m_node_input_connections.size() > 0) {
|
||||
std::vector<AnimGraphConnection>& graph_outputs =
|
||||
m_node_input_connections[0];
|
||||
|
||||
for (size_t i = 0, n = graph_outputs.size(); i < n; i++) {
|
||||
AnimGraphConnection& connection = graph_outputs[i];
|
||||
if (connection.m_target_socket.m_type
|
||||
== SocketType::SocketTypeAnimation) {
|
||||
AnimData* graph_anim_output = static_cast<AnimData*>(
|
||||
connection.m_target_socket.m_reference.ptr);
|
||||
assert(graph_anim_output != nullptr);
|
||||
|
||||
// we have to explicitly call the destructor as the AnimData* was
|
||||
// initialized using a placement new operator.
|
||||
graph_anim_output->m_local_matrices.vector::~vector();
|
||||
}
|
||||
}
|
||||
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();
|
||||
|
||||
if (m_node_output_connections.size() > 0) {
|
||||
std::vector<AnimGraphConnection>& graph_inputs =
|
||||
m_node_output_connections[0];
|
||||
|
||||
for (size_t i = 0, n = graph_inputs.size(); i < n; i++) {
|
||||
AnimGraphConnection& connection = graph_inputs[i];
|
||||
if (connection.m_target_socket.m_type
|
||||
== SocketType::SocketTypeAnimation) {
|
||||
AnimData* graph_anim_output = static_cast<AnimData*>(
|
||||
connection.m_target_socket.m_reference.ptr);
|
||||
assert(graph_anim_output != nullptr);
|
||||
|
||||
// we have to explicitly call the destructor as the AnimData* was
|
||||
// initialized using a placement new operator.
|
||||
graph_anim_output->m_local_matrices.vector::~vector();
|
||||
}
|
||||
}
|
||||
}
|
||||
m_node_output_connections.clear();
|
||||
|
||||
delete[] m_input_buffer;
|
||||
delete[] m_output_buffer;
|
||||
delete[] m_connection_data_storage;
|
||||
|
||||
for (int i = 0; i < m_nodes.size(); i++) {
|
||||
delete m_nodes[i];
|
||||
|
|
|
@ -29,6 +29,10 @@ struct AnimData {
|
|||
ozz::vector<ozz::math::SoaTransform> m_local_matrices;
|
||||
};
|
||||
|
||||
struct AnimDataRef {
|
||||
AnimData* ptr = nullptr;
|
||||
};
|
||||
|
||||
struct AnimDataAllocator {
|
||||
struct AnimDataList {
|
||||
AnimData* m_anim_data = nullptr;
|
||||
|
@ -104,8 +108,26 @@ struct AnimGraphContext {
|
|||
}
|
||||
};
|
||||
|
||||
typedef float Vec3[3];
|
||||
typedef float Quat[4];
|
||||
struct Vec3 {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
float v[3] = { 0 };
|
||||
};
|
||||
|
||||
struct Quat {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
|
||||
float v[4] = { 0 };
|
||||
};
|
||||
|
||||
enum class SocketType {
|
||||
SocketTypeUndefined = 0,
|
||||
|
@ -131,11 +153,11 @@ struct Socket {
|
|||
union SocketValue {
|
||||
bool flag;
|
||||
float float_value;
|
||||
float vec3[3];
|
||||
float quat[4];
|
||||
std::string* string_ptr;
|
||||
Vec3 vec3;
|
||||
Quat quat;
|
||||
};
|
||||
SocketValue m_value = {0};
|
||||
std::string m_value_string;
|
||||
union SocketReference {
|
||||
void* ptr;
|
||||
void** ptr_ptr;
|
||||
|
@ -177,9 +199,9 @@ SocketType GetSocketType() {
|
|||
}
|
||||
|
||||
struct NodeDescriptorBase {
|
||||
std::vector<Socket> m_properties;
|
||||
std::vector<Socket> m_inputs;
|
||||
std::vector<Socket> m_outputs;
|
||||
std::vector<Socket> m_properties;
|
||||
|
||||
template <typename T>
|
||||
bool RegisterInput(
|
||||
|
@ -234,10 +256,39 @@ struct NodeDescriptorBase {
|
|||
*socket->m_reference.ptr_ptr = value_ptr;
|
||||
}
|
||||
|
||||
void SetInputUnchecked(const char* name, void* value_ptr) {
|
||||
Socket* socket = FindSocket(name, m_inputs);
|
||||
*socket->m_reference.ptr_ptr = value_ptr;
|
||||
}
|
||||
|
||||
Socket* GetInputSocket(const char* name) {
|
||||
return FindSocket(name, m_inputs);
|
||||
}
|
||||
|
||||
int GetInputIndex(const char* name) {
|
||||
return FindSocketIndex(name, m_inputs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetOutput(const char* name, T* value_ptr) {
|
||||
Socket* socket = FindSocket(name, m_outputs);
|
||||
assert(GetSocketType<T>() == socket->m_type);
|
||||
*socket->m_reference.ptr_ptr = value_ptr;
|
||||
}
|
||||
|
||||
void SetOutputUnchecked(const char* name, void* value_ptr) {
|
||||
Socket* socket = FindSocket(name, m_outputs);
|
||||
*socket->m_reference.ptr_ptr = value_ptr;
|
||||
}
|
||||
|
||||
Socket* GetOutputSocket(const char* name) {
|
||||
return FindSocket(name, m_outputs);
|
||||
}
|
||||
|
||||
int GetOutputIndex(const char* name) {
|
||||
return FindSocketIndex(name, m_outputs);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetProperty(const char* name, const T& value) {
|
||||
Socket* socket = FindSocket(name, m_properties);
|
||||
|
@ -263,6 +314,16 @@ struct NodeDescriptorBase {
|
|||
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) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
virtual void UpdateFlags(){};
|
||||
|
||||
template <typename T>
|
||||
|
@ -295,328 +356,11 @@ struct NodeDescriptor : public NodeDescriptorBase {
|
|||
virtual ~NodeDescriptor() {}
|
||||
};
|
||||
|
||||
struct NodeSocketAccessorBase {
|
||||
std::vector<Socket> m_properties;
|
||||
std::vector<Socket> m_inputs;
|
||||
std::vector<Socket> m_outputs;
|
||||
|
||||
NodeSocketAccessorBase() {}
|
||||
virtual ~NodeSocketAccessorBase() {}
|
||||
|
||||
virtual void UpdateFlags(){};
|
||||
|
||||
Socket* FindSocket(std::vector<Socket>& sockets, const std::string& name) {
|
||||
Socket* result = nullptr;
|
||||
for (size_t i = 0, n = sockets.size(); i < n; i++) {
|
||||
if (sockets[i].m_name == name) {
|
||||
result = &sockets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const Socket* FindSocket(
|
||||
const std::vector<Socket>& sockets,
|
||||
const std::string& name) const {
|
||||
const Socket* result = nullptr;
|
||||
for (size_t i = 0, n = sockets.size(); i < n; i++) {
|
||||
if (sockets[i].m_name == name) {
|
||||
result = &sockets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SocketType GetSocketType(
|
||||
const std::vector<Socket>& sockets,
|
||||
const std::string& name) {
|
||||
const Socket* socket = FindSocket(sockets, name);
|
||||
if (socket == nullptr) {
|
||||
return SocketType::SocketTypeUndefined;
|
||||
}
|
||||
return socket->m_type;
|
||||
}
|
||||
|
||||
size_t GetSocketIndex(
|
||||
const std::vector<Socket>& sockets,
|
||||
const std::string& name) const {
|
||||
for (size_t i = 0, n = sockets.size(); i < n; i++) {
|
||||
if (sockets[i].m_name == name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetSocketValue(
|
||||
const std::vector<Socket>& sockets,
|
||||
const std::string& name,
|
||||
T default_value) {
|
||||
const Socket* socket = FindSocket(sockets, name);
|
||||
if (socket == nullptr) {
|
||||
return default_value;
|
||||
}
|
||||
|
||||
return *static_cast<T*>(socket->m_reference.ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetSocketReferenceValue(Socket* socket, T value) {
|
||||
std::cerr << "Could not find template specialization for socket type "
|
||||
<< static_cast<int>(socket->m_type) << " ("
|
||||
<< SocketTypeNames[static_cast<int>(socket->m_type)] << ")."
|
||||
<< std::endl;
|
||||
// *static_cast<T*>(socket->m_value.ptr) = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void SetSocketValue(Socket* socket, T value) {
|
||||
std::cerr << "Could not find template specialization for socket type "
|
||||
<< static_cast<int>(socket->m_type) << " ("
|
||||
<< SocketTypeNames[static_cast<int>(socket->m_type)] << ")."
|
||||
<< std::endl;
|
||||
// *static_cast<T*>(socket->m_value.ptr) = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool RegisterSocket(
|
||||
std::vector<Socket>& sockets,
|
||||
const std::string& name,
|
||||
T* value_ptr,
|
||||
SocketFlags flags = SocketFlagNone) {
|
||||
Socket* socket = FindSocket(sockets, name);
|
||||
if (socket != nullptr) {
|
||||
std::cerr << "Socket " << name << " already registered." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
sockets.push_back(Socket());
|
||||
socket = &sockets[sockets.size() - 1];
|
||||
socket->m_name = name;
|
||||
socket->m_type_size = sizeof(T);
|
||||
socket->m_flags = flags;
|
||||
|
||||
if constexpr (std::is_same<T, float>::value) {
|
||||
socket->m_type = SocketType::SocketTypeFloat;
|
||||
} else if constexpr (std::is_same<T, bool>::value) {
|
||||
socket->m_type = SocketType::SocketTypeBool;
|
||||
} else if constexpr (std::is_same<T, Vec3>::value) {
|
||||
socket->m_type = SocketType::SocketTypeVec3;
|
||||
} else if constexpr (std::is_same<T, Quat>::value) {
|
||||
socket->m_type = SocketType::SocketTypeQuat;
|
||||
} else if constexpr (std::is_same<T, AnimData>::value) {
|
||||
socket->m_type = SocketType::SocketTypeAnimation;
|
||||
} else if constexpr (std::is_same<T, std::string>::value) {
|
||||
socket->m_type = SocketType::SocketTypeString;
|
||||
socket->m_value.string_ptr = value_ptr;
|
||||
socket->m_reference.ptr = value_ptr;
|
||||
return true;
|
||||
} else if constexpr (std::is_same<T, float*>::value) {
|
||||
socket->m_type = SocketType::SocketTypeFloat;
|
||||
} else if constexpr (std::is_same<T, bool*>::value) {
|
||||
socket->m_type = SocketType::SocketTypeBool;
|
||||
} else if constexpr (std::is_same<T, Vec3*>::value) {
|
||||
socket->m_type = SocketType::SocketTypeVec3;
|
||||
} else if constexpr (std::is_same<T, Quat*>::value) {
|
||||
socket->m_type = SocketType::SocketTypeQuat;
|
||||
} else if constexpr (std::is_same<T, AnimData*>::value) {
|
||||
socket->m_type = SocketType::SocketTypeAnimation;
|
||||
} else if constexpr (std::is_same<T, std::string*>::value) {
|
||||
socket->m_type = SocketType::SocketTypeString;
|
||||
} else {
|
||||
std::cerr << "Cannot register socket, invalid type." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
socket->m_reference.ptr = value_ptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool RegisterProperty(const std::string& name, T* value) {
|
||||
return RegisterSocket(m_properties, name, value);
|
||||
}
|
||||
template <typename T>
|
||||
void SetPropertyReferenceValue(const std::string& name, T value) {
|
||||
Socket* socket = FindSocket(m_properties, name);
|
||||
SetSocketReferenceValue<T>(socket, value);
|
||||
}
|
||||
template <typename T>
|
||||
void SetPropertyValue(const std::string& name, T value) {
|
||||
Socket* socket = FindSocket(m_properties, name);
|
||||
SetSocketValue<T>(socket, value);
|
||||
}
|
||||
template <typename T>
|
||||
T GetProperty(const std::string& name, T default_value) {
|
||||
return GetSocketValue(m_properties, name, default_value);
|
||||
}
|
||||
SocketType GetPropertyType(const std::string& name) {
|
||||
return GetSocketType(m_properties, name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool RegisterInput(
|
||||
const std::string& name,
|
||||
T* value,
|
||||
SocketFlags flags = SocketFlagNone) {
|
||||
return RegisterSocket(m_inputs, name, value, flags);
|
||||
}
|
||||
template <typename T>
|
||||
T* GetInput(const std::string& name, T* value) {
|
||||
return GetSocketValue(m_inputs, name, value);
|
||||
}
|
||||
Socket* FindInputSocket(const std::string& name) {
|
||||
return FindSocket(m_inputs, name);
|
||||
}
|
||||
SocketType GetInputType(const std::string& name) {
|
||||
return GetSocketType(m_inputs, name);
|
||||
}
|
||||
size_t GetInputIndex(const std::string& name) {
|
||||
return GetSocketIndex(m_inputs, name);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool RegisterOutput(
|
||||
const std::string& name,
|
||||
T* value,
|
||||
SocketFlags flags = SocketFlagNone) {
|
||||
return RegisterSocket(m_outputs, name, value, flags);
|
||||
}
|
||||
template <typename T>
|
||||
bool RegisterOutput(
|
||||
const std::string& name,
|
||||
T** value,
|
||||
SocketFlags flags = SocketFlagNone) {
|
||||
return RegisterSocket(m_outputs, name, value, flags);
|
||||
}
|
||||
SocketType GetOutputType(const std::string& name) {
|
||||
return GetSocketType(m_outputs, name);
|
||||
}
|
||||
Socket* FindOutputSocket(const std::string& name) {
|
||||
return FindSocket(m_outputs, name);
|
||||
}
|
||||
size_t GetOutputIndex(const std::string& name) {
|
||||
return GetSocketIndex(m_outputs, name);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// SetSocketReferenceValue<> specializations
|
||||
//
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const bool&>(
|
||||
Socket* socket,
|
||||
const bool& value) {
|
||||
*static_cast<bool*>(socket->m_reference.ptr) = value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const float&>(
|
||||
Socket* socket,
|
||||
const float& value) {
|
||||
*static_cast<float*>(socket->m_reference.ptr) = value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const Vec3&>(
|
||||
Socket* socket,
|
||||
const Vec3& value) {
|
||||
static_cast<float*>(socket->m_reference.ptr)[0] = value[0];
|
||||
static_cast<float*>(socket->m_reference.ptr)[1] = value[1];
|
||||
static_cast<float*>(socket->m_reference.ptr)[2] = value[2];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const Quat&>(
|
||||
Socket* socket,
|
||||
const Quat& value) {
|
||||
static_cast<float*>(socket->m_reference.ptr)[0] = value[0];
|
||||
static_cast<float*>(socket->m_reference.ptr)[1] = value[1];
|
||||
static_cast<float*>(socket->m_reference.ptr)[2] = value[2];
|
||||
static_cast<float*>(socket->m_reference.ptr)[3] = value[3];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const std::string*>(
|
||||
Socket* socket,
|
||||
const std::string* value) {
|
||||
socket->m_value.string_ptr = const_cast<std::string*>(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const std::string&>(
|
||||
Socket* socket,
|
||||
const std::string& value) {
|
||||
*static_cast<std::string*>(socket->m_reference.ptr) = value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketReferenceValue<const char*>(
|
||||
Socket* socket,
|
||||
const char* value) {
|
||||
std::string value_string(value);
|
||||
SetSocketReferenceValue<const std::string&>(socket, value_string);
|
||||
}
|
||||
|
||||
//
|
||||
// SetSocketValue<> specializations
|
||||
//
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketValue<const bool&>(
|
||||
Socket* socket,
|
||||
const bool& value) {
|
||||
socket->m_value.flag = value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketValue<float>(
|
||||
Socket* socket,
|
||||
float value) {
|
||||
*static_cast<float*>(socket->m_reference.ptr) = value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketValue<const Vec3&>(
|
||||
Socket* socket,
|
||||
const Vec3& value) {
|
||||
socket->m_value.vec3[0] = value[0];
|
||||
socket->m_value.vec3[1] = value[1];
|
||||
socket->m_value.vec3[2] = value[2];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketValue<const Quat&>(
|
||||
Socket* socket,
|
||||
const Quat& value) {
|
||||
socket->m_value.quat[0] = value[0];
|
||||
socket->m_value.quat[1] = value[1];
|
||||
socket->m_value.quat[2] = value[2];
|
||||
socket->m_value.quat[3] = value[3];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketValue<std::string>(
|
||||
Socket* socket,
|
||||
std::string value) {
|
||||
*socket->m_value.string_ptr = value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void NodeSocketAccessorBase::SetSocketValue<const char*>(
|
||||
Socket* socket,
|
||||
const char* value) {
|
||||
SetSocketValue<std::string>(socket, value);
|
||||
}
|
||||
struct AnimNode;
|
||||
|
||||
template <typename T>
|
||||
struct NodeSocketAccessor : public NodeSocketAccessorBase {
|
||||
virtual ~NodeSocketAccessor() {}
|
||||
};
|
||||
NodeDescriptorBase* CreateNodeDescriptor(AnimNode* node) {
|
||||
return new NodeDescriptor<T>(dynamic_cast<T*>(node));
|
||||
}
|
||||
|
||||
#endif //ANIMTESTBED_ANIMGRAPHDATA_H
|
||||
|
|
|
@ -86,8 +86,8 @@ struct AnimNode {
|
|||
struct BlendTreeNode : public AnimNode {};
|
||||
|
||||
template <>
|
||||
struct NodeSocketAccessor<BlendTreeNode> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* node_) {}
|
||||
struct NodeDescriptor<BlendTreeNode> : public NodeDescriptorBase {
|
||||
NodeDescriptor(BlendTreeNode* node_) {}
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -122,34 +122,6 @@ struct Blend2Node : public AnimNode {
|
|||
virtual void Evaluate(AnimGraphContext& context) override;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct NodeSocketAccessor<Blend2Node> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* node_) {
|
||||
Blend2Node* node = dynamic_cast<Blend2Node*>(node_);
|
||||
RegisterInput("Input0", &node->i_input0);
|
||||
RegisterInput("Input1", &node->i_input1);
|
||||
RegisterInput(
|
||||
"Weight",
|
||||
&node->i_blend_weight,
|
||||
SocketFlags::SocketFlagAffectsTime);
|
||||
|
||||
RegisterOutput("Output", &node->o_output);
|
||||
|
||||
RegisterProperty("Sync", &node->m_sync_blend);
|
||||
}
|
||||
|
||||
virtual void UpdateFlags() override {
|
||||
Socket* weight_input_socket = FindSocket(m_inputs, "Weight");
|
||||
assert(weight_input_socket != nullptr);
|
||||
|
||||
if (GetProperty<bool>("Sync", false) == true) {
|
||||
weight_input_socket->m_flags = SocketFlags::SocketFlagAffectsTime;
|
||||
} else {
|
||||
weight_input_socket->m_flags = SocketFlags::SocketFlagNone;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct NodeDescriptor<Blend2Node> : public NodeDescriptorBase {
|
||||
NodeDescriptor(Blend2Node* node) {
|
||||
|
@ -198,9 +170,8 @@ struct SpeedScaleNode : public AnimNode {
|
|||
};
|
||||
|
||||
template <>
|
||||
struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* node_) {
|
||||
SpeedScaleNode* node = dynamic_cast<SpeedScaleNode*>(node_);
|
||||
struct NodeDescriptor<SpeedScaleNode> : public NodeDescriptorBase {
|
||||
NodeDescriptor(SpeedScaleNode* node) {
|
||||
RegisterInput(
|
||||
"SpeedScale",
|
||||
&node->i_speed_scale,
|
||||
|
@ -211,6 +182,7 @@ struct NodeSocketAccessor<SpeedScaleNode> : public NodeSocketAccessorBase {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// AnimSamplerNode
|
||||
//
|
||||
|
@ -231,9 +203,8 @@ struct AnimSamplerNode : public AnimNode {
|
|||
};
|
||||
|
||||
template <>
|
||||
struct NodeSocketAccessor<AnimSamplerNode> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* node_) {
|
||||
AnimSamplerNode* node = dynamic_cast<AnimSamplerNode*>(node_);
|
||||
struct NodeDescriptor<AnimSamplerNode> : public NodeDescriptorBase {
|
||||
NodeDescriptor(AnimSamplerNode* node) {
|
||||
RegisterOutput("Output", &node->o_output);
|
||||
|
||||
RegisterProperty("Filename", &node->m_filename);
|
||||
|
@ -253,34 +224,33 @@ struct ConstScalarNode : public AnimNode {
|
|||
};
|
||||
|
||||
template <>
|
||||
struct NodeSocketAccessor<ConstScalarNode> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* node_) {
|
||||
ConstScalarNode* node = dynamic_cast<ConstScalarNode*>(node_);
|
||||
struct NodeDescriptor<ConstScalarNode> : public NodeDescriptorBase {
|
||||
NodeDescriptor(ConstScalarNode* node) {
|
||||
RegisterOutput("ScalarOutput", &node->o_value);
|
||||
RegisterProperty("ScalarValue", &node->value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// MathAddNode
|
||||
//
|
||||
struct MathAddNode : public AnimNode {
|
||||
float* i_input0 = nullptr;
|
||||
float* i_input1 = nullptr;
|
||||
float o_output = 0.f;
|
||||
float* o_output = nullptr;
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input0 != nullptr);
|
||||
assert (i_input1 != nullptr);
|
||||
|
||||
o_output = *i_input0 + *i_input1;
|
||||
*o_output = *i_input0 + *i_input1;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct NodeSocketAccessor<MathAddNode> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* node_) {
|
||||
MathAddNode* node = dynamic_cast<MathAddNode*>(node_);
|
||||
struct NodeDescriptor<MathAddNode> : public NodeDescriptorBase {
|
||||
NodeDescriptor(MathAddNode* node) {
|
||||
RegisterInput("Input0", &node->i_input0);
|
||||
RegisterInput("Input1", &node->i_input1);
|
||||
RegisterOutput("Output", &node->o_output);
|
||||
|
@ -294,23 +264,22 @@ struct MathFloatToVec3Node : public AnimNode {
|
|||
float* i_input0 = nullptr;
|
||||
float* i_input1 = nullptr;
|
||||
float* i_input2 = nullptr;
|
||||
Vec3 o_output = {0.f, 0.f, 0.f};
|
||||
Vec3* o_output = nullptr;
|
||||
|
||||
void Evaluate(AnimGraphContext& context) override {
|
||||
assert (i_input0 != nullptr);
|
||||
assert (i_input1 != nullptr);
|
||||
assert (i_input2 != nullptr);
|
||||
|
||||
o_output[0] = *i_input0;
|
||||
o_output[1] = *i_input1;
|
||||
o_output[2] = *i_input2;
|
||||
o_output->v[0] = *i_input0;
|
||||
o_output->v[1] = *i_input1;
|
||||
o_output->v[2] = *i_input2;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct NodeSocketAccessor<MathFloatToVec3Node> : public NodeSocketAccessorBase {
|
||||
NodeSocketAccessor(AnimNode* node_) {
|
||||
MathFloatToVec3Node* node = dynamic_cast<MathFloatToVec3Node*>(node_);
|
||||
struct NodeDescriptor<MathFloatToVec3Node> : public NodeDescriptorBase {
|
||||
NodeDescriptor(MathFloatToVec3Node* node) {
|
||||
RegisterInput("Input0", &node->i_input0);
|
||||
RegisterInput("Input1", &node->i_input1);
|
||||
RegisterInput("Input2", &node->i_input2);
|
||||
|
@ -318,6 +287,7 @@ struct NodeSocketAccessor<MathFloatToVec3Node> : public NodeSocketAccessorBase {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
static inline AnimNode* AnimNodeFactory(const std::string& name) {
|
||||
AnimNode* result;
|
||||
if (name == "Blend2") {
|
||||
|
@ -345,23 +315,23 @@ static inline AnimNode* AnimNodeFactory(const std::string& name) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static inline NodeSocketAccessorBase* AnimNodeAccessorFactory(
|
||||
static inline NodeDescriptorBase* AnimNodeDescriptorFactory(
|
||||
const std::string& node_type_name,
|
||||
AnimNode* node) {
|
||||
if (node_type_name == "Blend2") {
|
||||
return new NodeSocketAccessor<Blend2Node>(node);
|
||||
return CreateNodeDescriptor<Blend2Node>(node);
|
||||
} else if (node_type_name == "SpeedScale") {
|
||||
return new NodeSocketAccessor<SpeedScaleNode>(node);
|
||||
return CreateNodeDescriptor<SpeedScaleNode>(node);
|
||||
} else if (node_type_name == "AnimSampler") {
|
||||
return new NodeSocketAccessor<AnimSamplerNode>(node);
|
||||
return CreateNodeDescriptor<AnimSamplerNode>(node);
|
||||
} else if (node_type_name == "BlendTree") {
|
||||
return new NodeSocketAccessor<BlendTreeNode>(node);
|
||||
return CreateNodeDescriptor<BlendTreeNode>(node);
|
||||
} else if (node_type_name == "MathAddNode") {
|
||||
return new NodeSocketAccessor<MathAddNode>(node);
|
||||
return CreateNodeDescriptor<MathAddNode>(node);
|
||||
} else if (node_type_name == "MathFloatToVec3Node") {
|
||||
return new NodeSocketAccessor<MathFloatToVec3Node>(node);
|
||||
return CreateNodeDescriptor<MathFloatToVec3Node>(node);
|
||||
} else if (node_type_name == "ConstScalarNode") {
|
||||
return new NodeSocketAccessor<ConstScalarNode>(node);
|
||||
return CreateNodeDescriptor<ConstScalarNode>(node);
|
||||
} else {
|
||||
std::cerr << "Invalid node type name " << node_type_name << "."
|
||||
<< std::endl;
|
||||
|
|
|
@ -34,16 +34,16 @@ json sSocketToJson(const Socket& socket) {
|
|||
} else if (socket.m_type == SocketType::SocketTypeFloat) {
|
||||
result["value"] = socket.m_value.float_value;
|
||||
} else if (socket.m_type == SocketType::SocketTypeVec3) {
|
||||
result["value"][0] = socket.m_value.vec3[0];
|
||||
result["value"][1] = socket.m_value.vec3[1];
|
||||
result["value"][2] = socket.m_value.vec3[2];
|
||||
result["value"][0] = socket.m_value.vec3.v[0];
|
||||
result["value"][1] = socket.m_value.vec3.v[1];
|
||||
result["value"][2] = socket.m_value.vec3.v[2];
|
||||
} else if (socket.m_type == SocketType::SocketTypeQuat) {
|
||||
result["value"][0] = socket.m_value.quat[0];
|
||||
result["value"][1] = socket.m_value.quat[1];
|
||||
result["value"][2] = socket.m_value.quat[2];
|
||||
result["value"][3] = socket.m_value.quat[3];
|
||||
result["value"][0] = socket.m_value.quat.v[0];
|
||||
result["value"][1] = socket.m_value.quat.v[1];
|
||||
result["value"][2] = socket.m_value.quat.v[2];
|
||||
result["value"][3] = socket.m_value.quat.v[3];
|
||||
} else if (socket.m_type == SocketType::SocketTypeString) {
|
||||
result["value"] = *socket.m_value.string_ptr;
|
||||
result["value"] = *static_cast<std::string*>(socket.m_reference.ptr);
|
||||
} else {
|
||||
std::cerr << "Invalid socket type '" << static_cast<int>(socket.m_type)
|
||||
<< "'." << std::endl;
|
||||
|
@ -118,7 +118,7 @@ AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
|
|||
|
||||
result.m_anim_node = AnimNodeFactory(result.m_type_name);
|
||||
result.m_socket_accessor =
|
||||
AnimNodeAccessorFactory(result.m_type_name, result.m_anim_node);
|
||||
AnimNodeDescriptorFactory(result.m_type_name, result.m_anim_node);
|
||||
|
||||
for (size_t j = 0, n = result.m_socket_accessor->m_properties.size(); j < n;
|
||||
j++) {
|
||||
|
@ -132,17 +132,17 @@ AnimNodeResource sAnimGraphNodeFromJson(const json& json_node) {
|
|||
} else if (property.m_type == SocketType::SocketTypeFloat) {
|
||||
property.m_value.float_value = json_property["value"];
|
||||
} else if (property.m_type == SocketType::SocketTypeVec3) {
|
||||
property.m_value.vec3[0] = json_property["value"][0];
|
||||
property.m_value.vec3[1] = json_property["value"][1];
|
||||
property.m_value.vec3[2] = json_property["value"][2];
|
||||
property.m_value.vec3.v[0] = json_property["value"][0];
|
||||
property.m_value.vec3.v[1] = json_property["value"][1];
|
||||
property.m_value.vec3.v[2] = json_property["value"][2];
|
||||
} else if (property.m_type == SocketType::SocketTypeQuat) {
|
||||
Quat* property_quat = reinterpret_cast<Quat*>(property.m_reference.ptr);
|
||||
property.m_value.quat[0] = json_property["value"][0];
|
||||
property.m_value.quat[1] = json_property["value"][1];
|
||||
property.m_value.quat[2] = json_property["value"][2];
|
||||
property.m_value.quat[3] = json_property["value"][3];
|
||||
property.m_value.quat.v[0] = json_property["value"][0];
|
||||
property.m_value.quat.v[1] = json_property["value"][1];
|
||||
property.m_value.quat.v[2] = json_property["value"][2];
|
||||
property.m_value.quat.v[3] = json_property["value"][3];
|
||||
} else if (property.m_type == SocketType::SocketTypeString) {
|
||||
*(property.m_value.string_ptr) = json_property["value"].get<std::string>();
|
||||
property.m_value_string = json_property["value"].get<std::string>();
|
||||
} else {
|
||||
std::cerr << "Invalid type for property '" << property.m_name
|
||||
<< "'. Cannot parse json to type '"
|
||||
|
@ -333,7 +333,6 @@ bool AnimGraphResource::loadFromFile(const char* filename) {
|
|||
void AnimGraphResource::createInstance(AnimGraph& result) const {
|
||||
createRuntimeNodeInstances(result);
|
||||
prepareGraphIOData(result);
|
||||
connectRuntimeNodes(result);
|
||||
setRuntimeNodeProperties(result);
|
||||
|
||||
result.updateOrderedNodes();
|
||||
|
@ -359,10 +358,12 @@ void AnimGraphResource::createRuntimeNodeInstances(AnimGraph& instance) const {
|
|||
|
||||
void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
|
||||
instance.m_socket_accessor =
|
||||
AnimNodeAccessorFactory("BlendTree", instance.m_nodes[0]);
|
||||
AnimNodeDescriptorFactory("BlendTree", instance.m_nodes[0]);
|
||||
instance.m_socket_accessor->m_outputs =
|
||||
m_nodes[1].m_socket_accessor->m_outputs;
|
||||
instance.m_socket_accessor->m_inputs = m_nodes[0].m_socket_accessor->m_inputs;
|
||||
instance.m_socket_accessor->m_outputs =
|
||||
m_nodes[1].m_socket_accessor->m_outputs;
|
||||
|
||||
// inputs
|
||||
int input_block_size = 0;
|
||||
|
@ -401,8 +402,87 @@ void AnimGraphResource::prepareGraphIOData(AnimGraph& instance) const {
|
|||
&instance.m_output_buffer[output_block_offset];
|
||||
output_block_offset += graph_outputs[i].m_type_size;
|
||||
}
|
||||
|
||||
// connections: make source and target sockets point to the same address in the connection data storage.
|
||||
int connection_data_storage_size = 0;
|
||||
for (int i = 0; i < m_connections.size(); i++) {
|
||||
const AnimGraphConnectionResource& connection = m_connections[i];
|
||||
const AnimNodeResource& source_node = m_nodes[connection.source_node_index];
|
||||
Socket* source_socket = source_node.m_socket_accessor->GetOutputSocket(
|
||||
connection.source_socket_name.c_str());
|
||||
connection_data_storage_size += source_socket->m_type_size;
|
||||
}
|
||||
|
||||
if (connection_data_storage_size > 0) {
|
||||
instance.m_connection_data_storage = new char[connection_data_storage_size];
|
||||
memset(instance.m_connection_data_storage, 0, connection_data_storage_size);
|
||||
}
|
||||
|
||||
std::vector<NodeDescriptorBase*> instance_node_descriptors(
|
||||
m_nodes.size(),
|
||||
nullptr);
|
||||
for (int i = 0; i < m_nodes.size(); i++) {
|
||||
instance_node_descriptors[i] = AnimNodeDescriptorFactory(
|
||||
m_nodes[i].m_type_name.c_str(),
|
||||
instance.m_nodes[i]);
|
||||
}
|
||||
|
||||
instance_node_descriptors[0]->m_inputs = instance.getGraphOutputs();
|
||||
instance_node_descriptors[1]->m_outputs = instance.getGraphInputs();
|
||||
|
||||
int connection_data_offset = 0;
|
||||
for (int i = 0; i < m_connections.size(); i++) {
|
||||
const AnimGraphConnectionResource& connection = m_connections[i];
|
||||
const AnimNodeResource& source_node_resource =
|
||||
m_nodes[connection.source_node_index];
|
||||
const AnimNodeResource& target_node_resource =
|
||||
m_nodes[connection.target_node_index];
|
||||
|
||||
NodeDescriptorBase* source_node_descriptor =
|
||||
instance_node_descriptors[connection.source_node_index];
|
||||
NodeDescriptorBase* target_node_descriptor =
|
||||
instance_node_descriptors[connection.target_node_index];
|
||||
|
||||
AnimNode* source_node = instance.m_nodes[connection.source_node_index];
|
||||
AnimNode* target_node = instance.m_nodes[connection.target_node_index];
|
||||
|
||||
Socket* source_socket = source_node_descriptor->GetOutputSocket(
|
||||
connection.source_socket_name.c_str());
|
||||
Socket* target_socket = target_node_descriptor->GetInputSocket(
|
||||
connection.target_socket_name.c_str());
|
||||
|
||||
AnimGraphConnection instance_connection;
|
||||
instance_connection.m_source_node = source_node;
|
||||
instance_connection.m_source_socket = *source_socket;
|
||||
instance_connection.m_target_node = target_node;
|
||||
instance_connection.m_target_socket = *target_socket;
|
||||
instance.m_node_input_connections[connection.target_node_index].push_back(
|
||||
instance_connection);
|
||||
instance.m_node_output_connections[connection.source_node_index].push_back(
|
||||
instance_connection);
|
||||
|
||||
source_node_descriptor->SetOutputUnchecked(
|
||||
connection.source_socket_name.c_str(),
|
||||
&instance.m_connection_data_storage[connection_data_offset]);
|
||||
|
||||
target_node_descriptor->SetInputUnchecked(
|
||||
connection.target_socket_name.c_str(),
|
||||
&instance.m_connection_data_storage[connection_data_offset]);
|
||||
|
||||
if (source_socket->m_type == SocketType::SocketTypeAnimation) {
|
||||
instance.m_animdata_blocks.push_back((AnimData*)(
|
||||
&instance.m_connection_data_storage[connection_data_offset]));
|
||||
}
|
||||
|
||||
connection_data_offset += source_socket->m_type_size;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_nodes.size(); i++) {
|
||||
delete instance_node_descriptors[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void AnimGraphResource::connectRuntimeNodes(AnimGraph& instance) const {
|
||||
for (int i = 0; i < m_connections.size(); i++) {
|
||||
const AnimGraphConnectionResource& connection = m_connections[i];
|
||||
|
@ -513,13 +593,15 @@ void AnimGraphResource::connectRuntimeNodes(AnimGraph& instance) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
|
||||
for (int i = 2; i < m_nodes.size(); i++) {
|
||||
const AnimNodeResource& node_resource = m_nodes[i];
|
||||
|
||||
NodeSocketAccessorBase* node_instance_accessor =
|
||||
AnimNodeAccessorFactory(node_resource.m_type_name, instance.m_nodes[i]);
|
||||
NodeDescriptorBase* node_instance_accessor = AnimNodeDescriptorFactory(
|
||||
node_resource.m_type_name,
|
||||
instance.m_nodes[i]);
|
||||
|
||||
std::vector<Socket>& resource_properties =
|
||||
node_resource.m_socket_accessor->m_properties;
|
||||
|
@ -529,29 +611,29 @@ void AnimGraphResource::setRuntimeNodeProperties(AnimGraph& instance) const {
|
|||
|
||||
switch (property.m_type) {
|
||||
case SocketType::SocketTypeBool:
|
||||
node_instance_accessor->SetPropertyReferenceValue<const bool&>(
|
||||
name,
|
||||
node_instance_accessor->SetProperty(
|
||||
name.c_str(),
|
||||
property.m_value.flag);
|
||||
break;
|
||||
case SocketType::SocketTypeFloat:
|
||||
node_instance_accessor->SetPropertyValue(
|
||||
name,
|
||||
node_instance_accessor->SetProperty(
|
||||
name.c_str(),
|
||||
property.m_value.float_value);
|
||||
break;
|
||||
case SocketType::SocketTypeVec3:
|
||||
node_instance_accessor->SetPropertyReferenceValue(
|
||||
name,
|
||||
node_instance_accessor->SetProperty<Vec3>(
|
||||
name.c_str(),
|
||||
property.m_value.vec3);
|
||||
break;
|
||||
case SocketType::SocketTypeQuat:
|
||||
node_instance_accessor->SetPropertyReferenceValue(
|
||||
name,
|
||||
node_instance_accessor->SetProperty(
|
||||
name.c_str(),
|
||||
property.m_value.quat);
|
||||
break;
|
||||
case SocketType::SocketTypeString:
|
||||
node_instance_accessor->SetPropertyValue(
|
||||
name,
|
||||
*property.m_value.string_ptr);
|
||||
node_instance_accessor->SetProperty(
|
||||
name.c_str(),
|
||||
property.m_value_string);
|
||||
break;
|
||||
default:
|
||||
std::cerr << "Invalid socket type "
|
||||
|
|
|
@ -24,7 +24,7 @@ struct AnimNodeResource {
|
|||
std::string m_name;
|
||||
std::string m_type_name;
|
||||
AnimNode* m_anim_node = nullptr;
|
||||
NodeSocketAccessorBase* m_socket_accessor = nullptr;
|
||||
NodeDescriptorBase* m_socket_accessor = nullptr;
|
||||
float m_position[2] = {0.f, 0.f};
|
||||
};
|
||||
|
||||
|
@ -34,7 +34,7 @@ static inline AnimNodeResource AnimNodeResourceFactory(
|
|||
result.m_type_name = node_type_name;
|
||||
result.m_anim_node = AnimNodeFactory(node_type_name);
|
||||
result.m_socket_accessor =
|
||||
AnimNodeAccessorFactory(node_type_name, result.m_anim_node);
|
||||
AnimNodeDescriptorFactory(node_type_name.c_str(), result.m_anim_node);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -104,9 +104,9 @@ struct AnimGraphResource {
|
|||
}
|
||||
|
||||
Socket* source_socket =
|
||||
source_node.m_socket_accessor->FindOutputSocket(source_socket_name);
|
||||
source_node.m_socket_accessor->GetOutputSocket(source_socket_name.c_str());
|
||||
Socket* target_socket =
|
||||
target_node.m_socket_accessor->FindInputSocket(target_socket_name);
|
||||
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;
|
||||
|
|
|
@ -153,11 +153,11 @@ TEST_CASE_METHOD(
|
|||
|
||||
// Setup nodes
|
||||
AnimNodeResource& trans_x_node = graph_resource.m_nodes[trans_x_node_index];
|
||||
trans_x_node.m_socket_accessor->SetPropertyValue("Filename", "trans_x");
|
||||
trans_x_node.m_socket_accessor->SetProperty("Filename", std::string("trans_x"));
|
||||
trans_x_node.m_name = "trans_x";
|
||||
|
||||
AnimNodeResource& trans_y_node = graph_resource.m_nodes[trans_y_node_index];
|
||||
trans_y_node.m_socket_accessor->SetPropertyValue("Filename", "trans_y");
|
||||
trans_y_node.m_socket_accessor->SetProperty("Filename", std::string("trans_y"));
|
||||
trans_y_node.m_name = "trans_y";
|
||||
|
||||
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
|
||||
|
|
|
@ -48,9 +48,9 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
|||
|
||||
AnimNodeResource& walk_node = graph_resource.m_nodes[walk_node_index];
|
||||
walk_node.m_name = "WalkAnim";
|
||||
walk_node.m_socket_accessor->SetPropertyValue("Filename", "data/walk.anim.ozz");
|
||||
walk_node.m_socket_accessor->SetProperty("Filename", std::string("data/walk.anim.ozz"));
|
||||
AnimNodeResource& run_node = graph_resource.m_nodes[run_node_index];
|
||||
run_node.m_socket_accessor->SetPropertyValue("Filename", "data/run.anim.ozz");
|
||||
run_node.m_socket_accessor->SetProperty("Filename", std::string("data/run.anim.ozz"));
|
||||
run_node.m_name = "RunAnim";
|
||||
AnimNodeResource& blend_node = graph_resource.m_nodes[blend_node_index];
|
||||
blend_node.m_name = "BlendWalkRun";
|
||||
|
@ -132,46 +132,33 @@ TEST_CASE("BasicGraph", "[AnimGraphResource]") {
|
|||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
||||
graph.prepareNodeEval(graph_context, walk_node_index);
|
||||
graph.finishNodeEval(walk_node_index);
|
||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 1);
|
||||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
||||
|
||||
graph.prepareNodeEval(graph_context, run_node_index);
|
||||
graph.finishNodeEval(run_node_index);
|
||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 2);
|
||||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
||||
|
||||
graph.prepareNodeEval(graph_context, blend_node_index);
|
||||
CHECK(blend2_instance->i_input0 == anim_sampler_walk->o_output);
|
||||
CHECK(blend2_instance->i_input1 == anim_sampler_run->o_output);
|
||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 3);
|
||||
CHECK(graph.m_anim_data_allocator.size() == 0);
|
||||
|
||||
graph.finishNodeEval(blend_node_index);
|
||||
CHECK(anim_sampler_walk->o_output == nullptr);
|
||||
CHECK(anim_sampler_run->o_output == nullptr);
|
||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 3);
|
||||
CHECK(graph.m_anim_data_allocator.size() == 2);
|
||||
|
||||
// Evaluate output node.
|
||||
graph.evalOutputNode();
|
||||
graph.finishNodeEval(0);
|
||||
|
||||
|
||||
const Socket* graph_output_socket = graph.getOutputSocket("GraphOutput");
|
||||
AnimData* graph_output =
|
||||
static_cast<AnimData*>(graph_output_socket->m_reference.ptr);
|
||||
static_cast<AnimData*>(*graph_output_socket->m_reference.ptr_ptr);
|
||||
|
||||
CHECK(graph_output->m_local_matrices.size() == graph_context.m_skeleton->num_soa_joints());
|
||||
|
||||
CHECK(graph.m_anim_data_allocator.m_num_allocations == 3);
|
||||
CHECK(graph.m_anim_data_allocator.size() == 3);
|
||||
|
||||
CHECK(blend2_instance->o_output == nullptr);
|
||||
CHECK(blend2_instance->o_output == *graph_output_socket->m_reference.ptr_ptr);
|
||||
}
|
||||
|
||||
graph_context.freeAnimations();
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
|
||||
int node_id = 3321;
|
||||
int input_index = 221;
|
||||
|
@ -191,6 +178,7 @@ TEST_CASE("InputAttributeConversion", "[AnimGraphResource]") {
|
|||
CHECK(output_index == parsed_output_index);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
||||
AnimGraphResource graph_resource_origin;
|
||||
|
||||
|
@ -269,16 +257,16 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||
== graph_loaded_input_node.m_socket_accessor->m_outputs.size());
|
||||
|
||||
REQUIRE(
|
||||
graph_loaded_input_node.m_socket_accessor->FindOutputSocket(
|
||||
graph_loaded_input_node.m_socket_accessor->GetOutputSocket(
|
||||
"GraphFloatInput")
|
||||
!= nullptr);
|
||||
|
||||
REQUIRE(
|
||||
graph_loaded_output_node.m_socket_accessor->FindInputSocket(
|
||||
graph_loaded_output_node.m_socket_accessor->GetInputSocket(
|
||||
"GraphFloatOutput")
|
||||
!= nullptr);
|
||||
REQUIRE(
|
||||
graph_loaded_output_node.m_socket_accessor->FindInputSocket(
|
||||
graph_loaded_output_node.m_socket_accessor->GetInputSocket(
|
||||
"GraphVec3Output")
|
||||
!= nullptr);
|
||||
|
||||
|
@ -312,9 +300,9 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||
*static_cast<Vec3*>(vec3_output_socket->m_reference.ptr);
|
||||
|
||||
THEN("output vector components equal the graph input vaulues") {
|
||||
CHECK(vec3_output[0] == *graph_float_input);
|
||||
CHECK(vec3_output[1] == *graph_float_input);
|
||||
CHECK(vec3_output[2] == *graph_float_input);
|
||||
CHECK(vec3_output.v[0] == *graph_float_input);
|
||||
CHECK(vec3_output.v[1] == *graph_float_input);
|
||||
CHECK(vec3_output.v[2] == *graph_float_input);
|
||||
}
|
||||
|
||||
context.freeAnimations();
|
||||
|
@ -324,6 +312,7 @@ TEST_CASE("ResourceSaveLoadMathGraphInputs", "[AnimGraphResource]") {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
||||
AnimGraphResource graph_resource_origin;
|
||||
|
||||
|
@ -470,337 +459,4 @@ TEST_CASE("SimpleMathEvaluations", "[AnimGraphResource]") {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
WHEN("Connecting input to output and instantiating the graph") {
|
||||
AnimNodeResource& graph_output_node = graph_resource_origin.m_nodes[0];
|
||||
AnimNodeResource& graph_input_node = graph_resource_origin.m_nodes[1];
|
||||
|
||||
REQUIRE(graph_resource_origin.connectSockets(
|
||||
graph_input_node,
|
||||
"GraphAnimInput",
|
||||
graph_output_node,
|
||||
"GraphOutput"));
|
||||
|
||||
AnimGraph anim_graph = graph_resource_origin.createInstance();
|
||||
|
||||
void* graph_anim_input_ptr = anim_graph.getInput("GraphAnimInput");
|
||||
void* graph_output_ptr = anim_graph.getOutput("GraphOutput");
|
||||
|
||||
REQUIRE(graph_anim_input_ptr == graph_output_ptr);
|
||||
REQUIRE(graph_output_ptr == anim_graph.m_output_buffer);
|
||||
|
||||
REQUIRE(
|
||||
anim_graph.getInput("GraphAnimInput")
|
||||
== anim_graph.getOutput("GraphOutput"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("GraphInputOutputConnectivity", "[AnimGraphResource]") {
|
||||
AnimGraphResource sGraphGresource;
|
||||
|
||||
sGraphGresource.clear();
|
||||
sGraphGresource.m_name = "TestGraphInputOutputConnectivity";
|
||||
|
||||
AnimNodeResource& graph_output_node = sGraphGresource.m_nodes[0];
|
||||
graph_output_node.m_socket_accessor->RegisterInput<float>(
|
||||
"GraphFloatOutput",
|
||||
nullptr);
|
||||
graph_output_node.m_socket_accessor->RegisterInput<AnimData>(
|
||||
"GraphAnimOutput",
|
||||
nullptr);
|
||||
|
||||
AnimNodeResource& graph_input_node = sGraphGresource.m_nodes[1];
|
||||
graph_input_node.m_socket_accessor->RegisterOutput<float>(
|
||||
"GraphFloatInput",
|
||||
nullptr);
|
||||
graph_input_node.m_socket_accessor->RegisterOutput<float>(
|
||||
"SpeedScaleInput",
|
||||
nullptr);
|
||||
graph_input_node.m_socket_accessor->RegisterOutput<AnimData>(
|
||||
"GraphAnimInput0",
|
||||
nullptr);
|
||||
graph_input_node.m_socket_accessor->RegisterOutput<AnimData>(
|
||||
"GraphAnimInput1",
|
||||
nullptr);
|
||||
|
||||
WHEN("Connecting float input with float output") {
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sGraphGresource.getGraphInputNode(),
|
||||
"GraphFloatInput",
|
||||
sGraphGresource.getGraphOutputNode(),
|
||||
"GraphFloatOutput"));
|
||||
|
||||
AnimGraph anim_graph = sGraphGresource.createInstance();
|
||||
|
||||
THEN("Writing to the input pointer changes the value of the output.") {
|
||||
float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput");
|
||||
REQUIRE(float_input_ptr != nullptr);
|
||||
*float_input_ptr = 23.123f;
|
||||
|
||||
float* float_output_ptr =
|
||||
(float*)anim_graph.getOutput("GraphFloatOutput");
|
||||
REQUIRE(float_output_ptr != nullptr);
|
||||
CHECK(*float_output_ptr == Approx(23.123f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Connecting adding a Blend2 node") {
|
||||
size_t blend2_node_index =
|
||||
sGraphGresource.addNode(AnimNodeResourceFactory("Blend2"));
|
||||
AnimNodeResource& blend2_node_resource =
|
||||
sGraphGresource.m_nodes[blend2_node_index];
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sGraphGresource.getGraphInputNode(),
|
||||
"GraphFloatInput",
|
||||
blend2_node_resource,
|
||||
"Weight"));
|
||||
|
||||
THEN("Connected float input points to the blend weight.") {
|
||||
AnimGraph anim_graph = sGraphGresource.createInstance();
|
||||
|
||||
Blend2Node* blend2_node =
|
||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
||||
|
||||
REQUIRE(
|
||||
*anim_graph.m_socket_accessor->m_outputs[0].m_reference.ptr_ptr
|
||||
== blend2_node->i_blend_weight);
|
||||
float* float_input_ptr = (float*)anim_graph.getInput("GraphFloatInput");
|
||||
REQUIRE(float_input_ptr == blend2_node->i_blend_weight);
|
||||
}
|
||||
|
||||
WHEN(
|
||||
"Connecting AnimData inputs to blend2 node and blend2 output to graph "
|
||||
"output.") {
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sGraphGresource.getGraphInputNode(),
|
||||
"GraphAnimInput0",
|
||||
blend2_node_resource,
|
||||
"Input0"));
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sGraphGresource.getGraphInputNode(),
|
||||
"GraphAnimInput1",
|
||||
blend2_node_resource,
|
||||
"Input1"));
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
blend2_node_resource,
|
||||
"Output",
|
||||
sGraphGresource.getGraphOutputNode(),
|
||||
"GraphAnimOutput"));
|
||||
|
||||
THEN(
|
||||
"AnimData from output gets blended and result is written to "
|
||||
"Output.") {
|
||||
AnimGraph anim_graph = sGraphGresource.createInstance();
|
||||
|
||||
Blend2Node* blend2_node =
|
||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
||||
|
||||
AnimData* graph_input0 =
|
||||
(AnimData*)anim_graph.getInput("GraphAnimInput0");
|
||||
REQUIRE(graph_input0 == blend2_node->i_input0);
|
||||
REQUIRE(
|
||||
anim_graph.m_nodes[1]
|
||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
|
||||
|
||||
AnimData* graph_input1 =
|
||||
(AnimData*)anim_graph.getInput("GraphAnimInput1");
|
||||
REQUIRE(graph_input1 == blend2_node->i_input1);
|
||||
REQUIRE(
|
||||
anim_graph.m_nodes[1]
|
||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
|
||||
|
||||
AnimData* graph_output =
|
||||
(AnimData*)anim_graph.getOutput("GraphAnimOutput");
|
||||
REQUIRE(graph_output == blend2_node->o_output);
|
||||
REQUIRE(
|
||||
anim_graph.m_nodes[blend2_node_index]
|
||||
== anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Adding AnimSampler Nodes") {
|
||||
size_t blend2_node_index =
|
||||
sGraphGresource.addNode(AnimNodeResourceFactory("Blend2"));
|
||||
size_t sampler_node_index =
|
||||
sGraphGresource.addNode(AnimNodeResourceFactory("AnimSampler"));
|
||||
size_t speed_scale_node_index =
|
||||
sGraphGresource.addNode(AnimNodeResourceFactory("SpeedScale"));
|
||||
|
||||
AnimNodeResource& blend2_node_resource =
|
||||
sGraphGresource.m_nodes[blend2_node_index];
|
||||
AnimNodeResource& sampler_node_resource =
|
||||
sGraphGresource.m_nodes[sampler_node_index];
|
||||
AnimNodeResource& speed_scale_node_resource =
|
||||
sGraphGresource.m_nodes[speed_scale_node_index];
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sGraphGresource.getGraphInputNode(),
|
||||
"GraphFloatInput",
|
||||
blend2_node_resource,
|
||||
"Weight"));
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sGraphGresource.getGraphInputNode(),
|
||||
"SpeedScaleInput",
|
||||
speed_scale_node_resource,
|
||||
"SpeedScale"));
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sGraphGresource.getGraphInputNode(),
|
||||
"GraphAnimInput0",
|
||||
blend2_node_resource,
|
||||
"Input0"));
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
sampler_node_resource,
|
||||
"Output",
|
||||
speed_scale_node_resource,
|
||||
"Input"));
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
speed_scale_node_resource,
|
||||
"Output",
|
||||
blend2_node_resource,
|
||||
"Input1"));
|
||||
|
||||
REQUIRE(sGraphGresource.connectSockets(
|
||||
blend2_node_resource,
|
||||
"Output",
|
||||
sGraphGresource.getGraphOutputNode(),
|
||||
"GraphAnimOutput"));
|
||||
|
||||
THEN("Data flow and node ordering must be correct.") {
|
||||
AnimGraph anim_graph = sGraphGresource.createInstance();
|
||||
Blend2Node* blend2_node =
|
||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
||||
SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
|
||||
anim_graph.m_nodes[speed_scale_node_index]);
|
||||
AnimSamplerNode* sampler_node = dynamic_cast<AnimSamplerNode*>(
|
||||
anim_graph.m_nodes[sampler_node_index]);
|
||||
|
||||
//
|
||||
// check connectivity
|
||||
//
|
||||
AnimData* graph_input0 =
|
||||
(AnimData*)anim_graph.getInput("GraphAnimInput0");
|
||||
REQUIRE(graph_input0 == blend2_node->i_input0);
|
||||
REQUIRE(
|
||||
anim_graph.m_nodes[1]
|
||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input0"));
|
||||
|
||||
AnimData* graph_input1 =
|
||||
(AnimData*)anim_graph.getInput("GraphAnimInput1");
|
||||
REQUIRE(graph_input1 == nullptr);
|
||||
|
||||
REQUIRE(sampler_node->o_output == speed_scale_node->i_input);
|
||||
REQUIRE(
|
||||
sampler_node
|
||||
== anim_graph.getAnimNodeForInput(speed_scale_node_index, "Input"));
|
||||
|
||||
REQUIRE(speed_scale_node->o_output == blend2_node->i_input1);
|
||||
REQUIRE(
|
||||
speed_scale_node
|
||||
== anim_graph.getAnimNodeForInput(blend2_node_index, "Input1"));
|
||||
|
||||
AnimData* graph_output =
|
||||
(AnimData*)anim_graph.getOutput("GraphAnimOutput");
|
||||
REQUIRE(graph_output == blend2_node->o_output);
|
||||
REQUIRE(
|
||||
anim_graph.m_nodes[blend2_node_index]
|
||||
== anim_graph.getAnimNodeForInput(0, "GraphAnimOutput"));
|
||||
|
||||
//
|
||||
// check ordering
|
||||
//
|
||||
REQUIRE(
|
||||
anim_graph.getNodeEvalOrderIndex(blend2_node)
|
||||
< anim_graph.getNodeEvalOrderIndex(sampler_node));
|
||||
REQUIRE(
|
||||
anim_graph.getNodeEvalOrderIndex(blend2_node)
|
||||
< anim_graph.getNodeEvalOrderIndex(speed_scale_node));
|
||||
REQUIRE(
|
||||
anim_graph.getNodeEvalOrderIndex(speed_scale_node)
|
||||
< anim_graph.getNodeEvalOrderIndex(sampler_node));
|
||||
}
|
||||
|
||||
WHEN("Instantiating graph") {
|
||||
AnimGraph anim_graph = sGraphGresource.createInstance();
|
||||
float* blend_weight_input =
|
||||
reinterpret_cast<float*>(anim_graph.getInput("GraphFloatInput"));
|
||||
|
||||
Blend2Node* blend2_node =
|
||||
dynamic_cast<Blend2Node*>(anim_graph.m_nodes[blend2_node_index]);
|
||||
SpeedScaleNode* speed_scale_node = dynamic_cast<SpeedScaleNode*>(
|
||||
anim_graph.m_nodes[speed_scale_node_index]);
|
||||
AnimSamplerNode* sampler_node = dynamic_cast<AnimSamplerNode*>(
|
||||
anim_graph.m_nodes[sampler_node_index]);
|
||||
|
||||
WHEN("Setting weight to 0. and marking nodes active.") {
|
||||
*blend_weight_input = 0.;
|
||||
anim_graph.markActiveNodes();
|
||||
|
||||
THEN("Speed scale and sampler node are inactive") {
|
||||
REQUIRE(anim_graph.checkIsNodeActive(speed_scale_node) == false);
|
||||
REQUIRE(anim_graph.checkIsNodeActive(sampler_node) == false);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Setting weight to 0. and marking nodes active") {
|
||||
*blend_weight_input = 0.1;
|
||||
anim_graph.markActiveNodes();
|
||||
|
||||
THEN("Speed scale and sampler nodes are active") {
|
||||
REQUIRE(anim_graph.checkIsNodeActive(speed_scale_node) == true);
|
||||
REQUIRE(anim_graph.checkIsNodeActive(sampler_node) == true);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Setting weight to 1. and marking nodes active") {
|
||||
*blend_weight_input = 1.0;
|
||||
anim_graph.markActiveNodes();
|
||||
|
||||
THEN("Speed scale and sampler nodes are active") {
|
||||
REQUIRE(anim_graph.checkIsNodeActive(speed_scale_node) == true);
|
||||
REQUIRE(anim_graph.checkIsNodeActive(sampler_node) == true);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Updating time with dt = 0.3f and speed scale = 1.0f") {
|
||||
float* speed_scale_input =
|
||||
reinterpret_cast<float*>(anim_graph.getInput("SpeedScaleInput"));
|
||||
|
||||
*blend_weight_input = 0.1;
|
||||
*speed_scale_input = 1.0f;
|
||||
|
||||
anim_graph.markActiveNodes();
|
||||
anim_graph.updateTime(0.3f);
|
||||
|
||||
THEN ("Anim sampler node time now must be 0.3f") {
|
||||
REQUIRE(sampler_node->m_time_now == Approx(0.3f));
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Updating time with dt = 0.3f and speed scale = 1.3f") {
|
||||
float* speed_scale_input =
|
||||
reinterpret_cast<float*>(anim_graph.getInput("SpeedScaleInput"));
|
||||
|
||||
*blend_weight_input = 0.1;
|
||||
*speed_scale_input = 1.3f;
|
||||
|
||||
anim_graph.markActiveNodes();
|
||||
anim_graph.updateTime(0.3f);
|
||||
|
||||
THEN ("Anim sampler node time now must be 0.39f") {
|
||||
REQUIRE(sampler_node->m_time_now == Approx(0.39f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
*/
|
Loading…
Reference in New Issue