You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -0,0 +1,600 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Log.h"
#include "Engine/Core/Collections/Array.h"
#include "VisjectMeta.h"
#include "GraphNode.h"
#include "GraphParameter.h"
#include "Engine/Serialization/ReadStream.h"
#include "Engine/Serialization/WriteStream.h"
// [Deprecated on 31.07.2020, expires on 31.07.2022]
extern FLAXENGINE_API void ReadOldGraphParamValue_Deprecated(byte graphParamType, ReadStream* stream, GraphParameter* param);
extern FLAXENGINE_API void ReadOldGraphNodeValue_Deprecated(ReadStream* stream, Variant& result);
extern FLAXENGINE_API void ReadOldGraphBoxType_Deprecated(uint32 connectionType, VariantType& type);
extern FLAXENGINE_API StringView GetGraphFunctionTypeName_Deprecated(const Variant& v);
/// <summary>
/// Visject graph
/// </summary>
template<class NodeType, class BoxType, class ParameterType>
class Graph
{
public:
typedef Graph<NodeType, BoxType, ParameterType> GraphType;
typedef NodeType Node;
typedef BoxType Box;
typedef ParameterType Parameter;
private:
struct TmpConnectionHint
{
Node* Node;
byte BoxID;
};
public:
NON_COPYABLE(Graph);
/// <summary>
/// All graph nodes
/// </summary>
Array<Node> Nodes;
/// <summary>
/// All graph parameters
/// </summary>
Array<Parameter> Parameters;
/// <summary>
/// Metadata for whole graph
/// </summary>
VisjectMeta Meta;
public:
/// <summary>
/// Initializes a new instance of the <see cref="Graph"/> class.
/// </summary>
Graph()
{
}
/// <summary>
/// Finalizes an instance of the <see cref="Graph"/> class.
/// </summary>
virtual ~Graph()
{
}
public:
/// <summary>
/// Save graph to the stream
/// </summary>
/// <param name="stream">Output stream</param>
/// <param name="saveMeta">True if save all loaded metadata</param>
/// <returns>True if cannot save data, otherwise false</returns>
virtual bool Save(WriteStream* stream, bool saveMeta) const
{
// Magic Code
stream->WriteInt32(1963542358);
// Version
stream->WriteInt32(7000);
// Nodes count
stream->WriteInt32(Nodes.Count());
// Parameters count
stream->WriteInt32(Parameters.Count());
// For each node
for (int32 i = 0; i < Nodes.Count(); i++)
{
auto node = &Nodes[i];
stream->WriteUint32(node->ID);
stream->WriteUint32(node->Type);
}
// For each param
for (int32 i = 0; i < Parameters.Count(); i++)
{
const Parameter* param = &Parameters[i];
stream->WriteVariantType(param->Type);
stream->Write(&param->Identifier);
stream->WriteString(param->Name, 97);
stream->WriteBool(param->IsPublic);
stream->WriteVariant(param->Value);
if (param->Meta.Save(stream, saveMeta))
return true;
}
// For each node
Array<const Box*, InlinedAllocation<32>> boxes;
for (int32 i = 0; i < Nodes.Count(); i++)
{
const Node* node = &Nodes[i];
// Values
stream->WriteInt32(node->Values.Count());
for (int32 j = 0; j < node->Values.Count(); j++)
stream->WriteVariant(node->Values[j]);
// Boxes
node->GetBoxes(boxes);
stream->WriteUint16(boxes.Count());
for (int32 j = 0; j < boxes.Count(); j++)
{
const Box* box = boxes[j];
stream->WriteByte(box->ID);
stream->WriteVariantType(box->Type);
stream->WriteUint16(box->Connections.Count());
for (int32 k = 0; k < box->Connections.Count(); k++)
{
auto targetBox = box->Connections[k];
if (targetBox == nullptr)
return true;
stream->WriteUint32(targetBox->template GetParent<Node>()->ID);
stream->WriteByte(targetBox->ID);
}
}
// Meta
if (node->Meta.Save(stream, saveMeta))
return true;
}
// Meta
if (Meta.Save(stream, saveMeta))
return true;
// Ending char
stream->WriteByte('\t');
return false;
}
/// <summary>
/// Load graph from the stream
/// </summary>
/// <param name="stream">Input stream</param>
/// <param name="loadMeta">True if load all saved metadata</param>
/// <returns>True if cannot load data, otherwise false</returns>
virtual bool Load(ReadStream* stream, bool loadMeta)
{
// Clear previous data
Clear();
// Magic Code
int32 tmp;
stream->ReadInt32(&tmp);
if (tmp != 1963542358)
{
LOG(Warning, "Invalid data.");
return true;
}
// Version
uint32 version;
stream->ReadUint32(&version);
Array<TmpConnectionHint> tmpHints;
if (version < 7000)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
// Time saved
int64 timeSaved;
stream->ReadInt64(&timeSaved);
// Nodes count
int32 nodesCount;
stream->ReadInt32(&nodesCount);
Nodes.Resize(nodesCount, false);
// Parameters count
int32 parametersCount;
stream->ReadInt32(&parametersCount);
Parameters.Resize(parametersCount, false);
// For each node
for (int32 i = 0; i < nodesCount; i++)
{
auto node = &Nodes[i];
// ID
stream->ReadUint32(&node->ID);
// Type
stream->ReadUint32(&node->Type);
if (onNodeCreated(node))
return true;
}
// For each param
for (int32 i = 0; i < parametersCount; i++)
{
// Create param
auto param = &Parameters[i];
// Properties
auto type = stream->ReadByte();
stream->Read(&param->Identifier);
stream->ReadString(&param->Name, 97);
param->IsPublic = stream->ReadBool();
bool isStatic = stream->ReadBool();
bool isUIVisible = stream->ReadBool();
bool isUIEditable = stream->ReadBool();
// References [Deprecated]
int32 refsCount;
stream->ReadInt32(&refsCount);
for (int32 j = 0; j < refsCount; j++)
{
uint32 refID;
stream->ReadUint32(&refID);
}
// Value
ReadOldGraphParamValue_Deprecated(type, stream, param);
// Meta
if (param->Meta.Load(stream, loadMeta))
return true;
if (onParamCreated(param))
return true;
}
// For each node
for (int32 i = 0; i < nodesCount; i++)
{
auto node = &Nodes[i];
// Values
int32 valuesCnt;
stream->ReadInt32(&valuesCnt);
node->Values.Resize(valuesCnt);
for (int32 j = 0; j < valuesCnt; j++)
ReadOldGraphNodeValue_Deprecated(stream, node->Values[j]);
// Boxes
uint16 boxesCount;
stream->ReadUint16(&boxesCount);
node->Boxes.Clear();
for (int32 j = 0; j < boxesCount; j++)
{
byte boxID = stream->ReadByte();
node->Boxes.Resize(node->Boxes.Count() > boxID + 1 ? node->Boxes.Count() : boxID + 1);
Box* box = &node->Boxes[boxID];
box->Parent = node;
box->ID = boxID;
uint32 connectionType;
stream->ReadUint32((uint32*)&connectionType);
ReadOldGraphBoxType_Deprecated(connectionType, box->Type);
uint16 connectionsCount;
stream->ReadUint16(&connectionsCount);
box->Connections.Resize(connectionsCount);
for (int32 k = 0; k < connectionsCount; k++)
{
uint32 targetNodeID;
stream->ReadUint32(&targetNodeID);
byte targetBoxID = stream->ReadByte();
TmpConnectionHint hint;
hint.Node = GetNode(targetNodeID);
if (hint.Node == nullptr)
return true;
hint.BoxID = targetBoxID;
box->Connections[k] = (Box*)(intptr)tmpHints.Count();
tmpHints.Add(hint);
}
}
// Meta
if (node->Meta.Load(stream, loadMeta))
return true;
if (onNodeLoaded(node))
return true;
}
// Visject Meta
if (Meta.Load(stream, loadMeta))
return true;
// Setup connections
for (int32 i = 0; i < Nodes.Count(); i++)
{
auto node = &Nodes[i];
for (int32 j = 0; j < node->Boxes.Count(); j++)
{
Box* box = &node->Boxes[j];
if (box->Parent == nullptr)
continue;
for (int32 k = 0; k < box->Connections.Count(); k++)
{
int32 hintIndex = (int32)(intptr)box->Connections[k];
TmpConnectionHint hint = tmpHints[hintIndex];
box->Connections[k] = hint.Node->GetBox(hint.BoxID);
}
}
}
// Ending char
byte end;
stream->ReadByte(&end);
if (end != '\t')
{
return true;
}
}
else if (version == 7000)
{
// Nodes count
int32 nodesCount;
stream->ReadInt32(&nodesCount);
Nodes.Resize(nodesCount, false);
// Parameters count
int32 parametersCount;
stream->ReadInt32(&parametersCount);
Parameters.Resize(parametersCount, false);
// For each node
for (int32 i = 0; i < nodesCount; i++)
{
auto node = &Nodes[i];
stream->ReadUint32(&node->ID);
stream->ReadUint32(&node->Type);
if (onNodeCreated(node))
return true;
}
// For each param
for (int32 i = 0; i < parametersCount; i++)
{
auto param = &Parameters[i];
stream->ReadVariantType(&param->Type);
stream->Read(&param->Identifier);
stream->ReadString(&param->Name, 97);
param->IsPublic = stream->ReadBool();
stream->ReadVariant(&param->Value);
if (param->Meta.Load(stream, loadMeta))
return true;
if (onParamCreated(param))
return true;
}
// For each node
for (int32 i = 0; i < nodesCount; i++)
{
auto node = &Nodes[i];
// Values
int32 valuesCnt;
stream->ReadInt32(&valuesCnt);
node->Values.Resize(valuesCnt);
for (int32 j = 0; j < valuesCnt; j++)
stream->ReadVariant(&node->Values[j]);
// Boxes
uint16 boxesCount;
stream->ReadUint16(&boxesCount);
node->Boxes.Clear();
for (int32 j = 0; j < boxesCount; j++)
{
byte boxID = stream->ReadByte();
node->Boxes.Resize(node->Boxes.Count() > boxID + 1 ? node->Boxes.Count() : boxID + 1);
Box* box = &node->Boxes[boxID];
box->Parent = node;
box->ID = boxID;
stream->ReadVariantType(&box->Type);
uint16 connectionsCount;
stream->ReadUint16(&connectionsCount);
box->Connections.Resize(connectionsCount);
for (int32 k = 0; k < connectionsCount; k++)
{
uint32 targetNodeID;
stream->ReadUint32(&targetNodeID);
byte targetBoxID = stream->ReadByte();
TmpConnectionHint hint;
hint.Node = GetNode(targetNodeID);
if (hint.Node == nullptr)
return true;
hint.BoxID = targetBoxID;
box->Connections[k] = (Box*)(intptr)tmpHints.Count();
tmpHints.Add(hint);
}
}
// Meta
if (node->Meta.Load(stream, loadMeta))
return true;
if (onNodeLoaded(node))
return true;
}
// Visject Meta
if (Meta.Load(stream, loadMeta))
return true;
// Setup connections
for (int32 i = 0; i < Nodes.Count(); i++)
{
auto node = &Nodes[i];
for (int32 j = 0; j < node->Boxes.Count(); j++)
{
Box* box = &node->Boxes[j];
if (box->Parent == nullptr)
continue;
for (int32 k = 0; k < box->Connections.Count(); k++)
{
int32 hintIndex = (int32)(intptr)box->Connections[k];
TmpConnectionHint hint = tmpHints[hintIndex];
box->Connections[k] = hint.Node->GetBox(hint.BoxID);
}
}
}
// Ending char
byte end;
stream->ReadByte(&end);
if (end != '\t')
return true;
}
return false;
}
public:
/// <summary>
/// Find node by ID
/// </summary>
/// <param name="id">Node ID</param>
/// <returns>Node or null if cannot find it</returns>
Node* GetNode(uint32 id)
{
Node* result = nullptr;
for (int32 i = 0; i < Nodes.Count(); i++)
{
if (Nodes[i].ID == id)
{
result = &Nodes[i];
break;
}
}
return result;
}
/// <summary>
/// Find parameter by ID
/// </summary>
/// <param name="id">Parameter ID</param>
/// <returns>Parameter or null if cannot find it</returns>
Parameter* GetParameter(const Guid& id)
{
Parameter* result = nullptr;
for (int32 i = 0; i < Parameters.Count(); i++)
{
if (Parameters[i].Identifier == id)
{
result = &Parameters[i];
break;
}
}
return result;
}
/// <summary>
/// Find parameter by ID
/// </summary>
/// <param name="id">Parameter ID</param>
/// <param name="outIndex">Parameter index</param>
/// <returns>Parameter or null if cannot find it</returns>
Parameter* GetParameter(const Guid& id, int32& outIndex)
{
Parameter* result = nullptr;
outIndex = INVALID_INDEX;
for (int32 i = 0; i < Parameters.Count(); i++)
{
if (Parameters[i].Identifier == id)
{
result = &Parameters[i];
outIndex = i;
break;
}
}
return result;
}
/// <summary>
/// Clear whole graph data
/// </summary>
virtual void Clear()
{
Nodes.Resize(0);
Parameters.Resize(0);
Meta.Release();
}
public:
#if USE_EDITOR
/// <summary>
/// Gets the asset references.
/// </summary>
/// </remarks>
/// <param name="output">The output collection of the asset ids referenced by this object.</param>
virtual void GetReferences(Array<Guid>& output) const
{
for (int32 i = 0; i < Parameters.Count(); i++)
{
const auto& p = Parameters[i];
const Guid id = (Guid)p.Value;
if (id.IsValid())
output.Add(id);
}
for (int32 i = 0; i < Nodes.Count(); i++)
{
const auto& n = Nodes[i];
for (int32 j = 0; j < n.Values.Count(); j++)
{
const Guid id = (Guid)n.Values[j];
if (id.IsValid())
output.Add(id);
}
}
}
#endif
protected:
virtual bool onNodeCreated(Node* n)
{
return false;
}
virtual bool onNodeLoaded(Node* n)
{
return false;
}
virtual bool onParamCreated(Parameter* p)
{
return false;
}
uint32 getFreeNodeID() const
{
uint32 result = 1;
while (true)
{
bool valid = true;
for (int32 i = 0; i < Nodes.Count(); i++)
{
if (Nodes[i].Identifier == result)
{
result++;
valid = false;
break;
}
}
if (valid)
break;
}
return result;
}
};

View File

@@ -0,0 +1,225 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/Variant.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Visject/VisjectMeta.h"
template<class BoxType>
class GraphNode;
#define GRAPH_NODE_MAKE_TYPE(groupID, typeID) ((groupID) << 16 | (typeID))
#define GRAPH_NODE_MAX_VALUES 32
/// <summary>
/// Represents single box of the graph node
/// </summary>
class GraphBox
{
public:
/// <summary>
/// The parent node
/// </summary>
void* Parent;
/// <summary>
/// Unique box ID within single node
/// </summary>
byte ID;
/// <summary>
/// The box type.
/// </summary>
VariantType Type;
/// <summary>
/// List with all connections to other boxes
/// </summary>
Array<GraphBox*, InlinedAllocation<4>> Connections;
public:
GraphBox()
: Parent(nullptr)
, ID(0)
{
}
/// <summary>
/// Init
/// </summary>
/// <param name="parent">Parent node</param>
/// <param name="id">Unique box id</param>
/// <param name="type">Connections type</param>
GraphBox(void* parent, byte id, const VariantType::Types type)
: Parent(parent)
, ID(id)
, Type(type)
{
}
/// <summary>
/// Init
/// </summary>
/// <param name="parent">Parent node</param>
/// <param name="id">Unique box id</param>
/// <param name="type">Connections type</param>
GraphBox(void* parent, byte id, const VariantType& type)
: Parent(parent)
, ID(id)
, Type(type)
{
}
public:
/// <summary>
/// Gets the parent node.
/// </summary>
template<class NodeType>
FORCE_INLINE NodeType* GetParent() const
{
return static_cast<NodeType*>(Parent);
}
/// <summary>
/// Returns true if box has one or more connections.
/// </summary>
FORCE_INLINE bool HasConnection() const
{
return Connections.HasItems();
}
};
/// <summary>
/// Visject graph node base
/// </summary>
template<class BoxType>
class GraphNode
{
public:
typedef BoxType Box;
typedef GraphNode<BoxType> Node;
public:
/// <summary>
/// Unique node ID (within a graph).
/// </summary>
uint32 ID;
union
{
struct
{
uint16 TypeID;
uint16 GroupID;
};
uint32 Type;
};
/// <summary>
/// List of all node values. Array size and value types are constant over time. Only value data can change.
/// </summary>
Array<Variant, FixedAllocation<GRAPH_NODE_MAX_VALUES>> Values;
/// <summary>
/// Node boxes cache. Array index matches the box ID (for fast O(1) lookups).
/// </summary>
Array<Box> Boxes;
/// <summary>
/// Additional metadata.
/// </summary>
VisjectMeta Meta;
public:
GraphNode()
: ID(0)
, Type(0)
{
}
/// <summary>
/// Destructor
/// </summary>
~GraphNode()
{
}
public:
/// <summary>
/// Gets all the valid boxes.
/// </summary>
/// <param name="result">The result container.</param>
template<typename AllocationType = HeapAllocation>
void GetBoxes(Array<Box*, AllocationType>& result)
{
result.Clear();
for (int32 i = 0; i < Boxes.Count(); i++)
{
if (Boxes[i].Parent == this)
{
result.Add(&Boxes[i]);
}
}
}
/// <summary>
/// Gets all the valid boxes.
/// </summary>
/// <param name="result">The result container.</param>
template<typename AllocationType = HeapAllocation>
void GetBoxes(Array<const Box*, AllocationType>& result) const
{
result.Clear();
for (int32 i = 0; i < Boxes.Count(); i++)
{
if (Boxes[i].Parent == this)
{
result.Add(&Boxes[i]);
}
}
}
/// <summary>
/// Get box by ID
/// </summary>
/// <param name="id">Box ID</param>
/// <returns>Box</returns>
Box* GetBox(int32 id)
{
ASSERT(Boxes.HasItems() && Boxes.Count() > id && Boxes[id].ID == id && Boxes[id].Parent == this);
return &Boxes[id];
}
/// <summary>
/// Get box by ID, returns null if missing.
/// </summary>
/// <param name="id">Box ID</param>
/// <returns>Box</returns>
Box* TryGetBox(int32 id)
{
if (id >= 0 && id < Boxes.Count() && Boxes[id].ID == id && Boxes[id].Parent == this)
return &Boxes[id];
return nullptr;
}
/// <summary>
/// Get box by ID
/// </summary>
/// <param name="id">Box ID</param>
/// <returns>Box</returns>
const Box* GetBox(int32 id) const
{
ASSERT(Boxes.HasItems() && Boxes.Count() > id && Boxes[id].ID == id && Boxes[id].Parent == this);
return &Boxes[id];
}
};

View File

@@ -0,0 +1,18 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "GraphParameter.h"
#include "Engine/Core/Types/DataContainer.h"
BytesContainer GraphParameter::GetMetaData(int32 typeID) const
{
BytesContainer result;
for (const auto& e : Meta.Entries)
{
if (e.TypeID == typeID)
{
result.Link(e.Data);
break;
}
}
return result;
}

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/Variant.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "VisjectMeta.h"
template<typename T>
class DataContainer;
typedef DataContainer<byte> BytesContainer;
/// <summary>
/// The channel mask modes.
/// </summary>
API_ENUM() enum class ChannelMask
{
/// <summary>
/// The red channel.
/// </summary>
Red = 0,
/// <summary>
/// The green channel.
/// </summary>
Green = 1,
/// <summary>
/// The blue channel.
/// </summary>
Blue = 2,
/// <summary>
/// The alpha channel.
/// </summary>
Alpha = 3,
};
/// <summary>
/// Represents a parameter in the Graph.
/// </summary>
API_CLASS() class FLAXENGINE_API GraphParameter : public PersistentScriptingObject
{
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(GraphParameter, PersistentScriptingObject);
public:
/// <summary>
/// Parameter type
/// </summary>
API_FIELD(ReadOnly) VariantType Type;
/// <summary>
/// Parameter unique ID
/// </summary>
API_FIELD(ReadOnly) Guid Identifier;
/// <summary>
/// Parameter name
/// </summary>
API_FIELD(ReadOnly) String Name;
/// <summary>
/// Parameter value
/// </summary>
API_FIELD() Variant Value;
/// <summary>
/// True if is exposed outside
/// </summary>
API_FIELD() bool IsPublic = true;
/// <summary>
/// Additional metadata
/// </summary>
VisjectMeta Meta;
public:
/// <summary>
/// Gets the typename of the parameter type (excluding in-build types).
/// </summary>
/// <returns>The typename of the parameter type.</returns>
API_PROPERTY() StringAnsiView GetTypeTypeName() const
{
return StringAnsiView(Type.TypeName);
}
/// <summary>
/// Gets the data of the Visject Meta entry assigned to this parameter.
/// </summary>
/// <param name="typeID">Entry type ID</param>
/// <returns>The entry data or empty if missing or not loaded.</returns>
API_FUNCTION() BytesContainer GetMetaData(int32 typeID) const;
};

View File

@@ -0,0 +1,704 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "GraphUtilities.h"
// [Deprecated on 31.07.2020, expires on 31.07.2022]
enum class GraphParamType_Deprecated
{
Bool = 0,
Integer = 1,
Float = 2,
Vector2 = 3,
Vector3 = 4,
Vector4 = 5,
Color = 6,
Texture = 7,
NormalMap = 8,
String = 9,
Box = 10,
Rotation = 11,
Transform = 12,
Asset = 13,
Actor = 14,
Rectangle = 15,
CubeTexture = 16,
SceneTexture = 17,
GPUTexture = 18,
Matrix = 19,
GPUTextureArray = 20,
GPUTextureVolume = 21,
GPUTextureCube = 22,
ChannelMask = 23,
};
// [Deprecated on 31.07.2020, expires on 31.07.2022]
enum class GraphConnectionType_Deprecated : uint32
{
Invalid = 0,
Impulse = 1 << 0,
Bool = 1 << 1,
Integer = 1 << 2,
Float = 1 << 3,
Vector2 = 1 << 4,
Vector3 = 1 << 5,
Vector4 = 1 << 6,
String = 1 << 7,
Object = 1 << 8,
Rotation = 1 << 9,
Transform = 1 << 10,
Box = 1 << 11,
ImpulseSecondary = 1 << 12,
UnsignedInteger = 1 << 13,
Scalar = Bool | Integer | Float | UnsignedInteger,
Vector = Vector2 | Vector3 | Vector4,
Variable = Scalar | Vector | String | Object | Rotation | Transform | Box,
All = Variable | Impulse,
};
// Required by deprecated graph data upgrade code
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Content/Assets/CubeTexture.h"
#include "Engine/Scripting/ScriptingObjectReference.h"
#include "Engine/Core/Types/CommonValue.h"
#include "Engine/Level/Actor.h"
FLAXENGINE_API void ReadOldGraphParamValue_Deprecated(byte graphParamType, ReadStream* stream, GraphParameter* param)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
CommonValue value;
stream->ReadCommonValue(&value);
switch ((GraphParamType_Deprecated)graphParamType)
{
case GraphParamType_Deprecated::Bool:
param->Type = VariantType(VariantType::Bool);
param->Value = value.GetBool();
break;
case GraphParamType_Deprecated::Integer:
param->Type = VariantType(VariantType::Int);
param->Value = value.GetInteger();
break;
case GraphParamType_Deprecated::Float:
param->Type = VariantType(VariantType::Float);
param->Value = value.GetFloat();
break;
case GraphParamType_Deprecated::Vector2:
param->Type = VariantType(VariantType::Vector2);
param->Value = value.GetVector2();
break;
case GraphParamType_Deprecated::Vector3:
param->Type = VariantType(VariantType::Vector3);
param->Value = value.GetVector3();
break;
case GraphParamType_Deprecated::Vector4:
param->Type = VariantType(VariantType::Vector4);
param->Value = value.GetVector4();
break;
case GraphParamType_Deprecated::Color:
param->Type = VariantType(VariantType::Color);
param->Value = value.GetColor();
break;
case GraphParamType_Deprecated::Texture:
case GraphParamType_Deprecated::NormalMap:
ASSERT(value.Type == CommonType::Guid);
param->Type = VariantType(VariantType::Asset, TEXT("FlaxEngine.Texture"));
param->Value.SetAsset(LoadAsset(value.AsGuid, Texture::TypeInitializer));
break;
case GraphParamType_Deprecated::String:
ASSERT(value.Type == CommonType::String);
param->Type = VariantType(VariantType::String);
param->Value.SetString(StringView(value.AsString));
break;
case GraphParamType_Deprecated::Box:
ASSERT(value.Type == CommonType::Box);
param->Type = VariantType(VariantType::BoundingBox);
param->Value = Variant(value.AsBox);
break;
case GraphParamType_Deprecated::Rotation:
ASSERT(value.Type == CommonType::Rotation);
param->Type = VariantType(VariantType::Quaternion);
param->Value = value.AsRotation;
break;
case GraphParamType_Deprecated::Transform:
ASSERT(value.Type == CommonType::Transform);
param->Type = VariantType(VariantType::Transform);
param->Value = Variant(value.AsTransform);
break;
case GraphParamType_Deprecated::Asset:
ASSERT(value.Type == CommonType::Guid);
param->Type = VariantType(VariantType::Asset);
param->Value.SetAsset(LoadAsset(value.AsGuid, Asset::TypeInitializer));
break;
case GraphParamType_Deprecated::Rectangle:
ASSERT(value.Type == CommonType::Rectangle);
param->Type = VariantType(VariantType::Rectangle);
param->Value = value.AsRectangle;
break;
case GraphParamType_Deprecated::Matrix:
ASSERT(value.Type == CommonType::Matrix);
param->Type = VariantType(VariantType::Matrix);
param->Value = Variant(value.AsMatrix);
break;
case GraphParamType_Deprecated::Actor:
ASSERT(value.Type == CommonType::Guid);
param->Type = VariantType(VariantType::Object, TEXT("FlaxEngine.Actor"));
param->Value.SetObject(FindObject(value.AsGuid, Actor::GetStaticClass()));
break;
case GraphParamType_Deprecated::CubeTexture:
ASSERT(value.Type == CommonType::Guid);
param->Type = VariantType(VariantType::Asset, TEXT("FlaxEngine.CubeTexture"));
param->Value.SetAsset(LoadAsset(value.AsGuid, CubeTexture::TypeInitializer));
break;
case GraphParamType_Deprecated::GPUTexture:
case GraphParamType_Deprecated::GPUTextureArray:
case GraphParamType_Deprecated::GPUTextureVolume:
case GraphParamType_Deprecated::GPUTextureCube:
param->Type = VariantType(VariantType::Object, TEXT("FlaxEngine.GPUTexture"));
param->Value.SetObject(nullptr);
break;
case GraphParamType_Deprecated::SceneTexture:
param->Type = VariantType(VariantType::Enum, TEXT("FlaxEngine.MaterialSceneTextures"));
param->Value.AsUint64 = (uint64)value.AsInteger;
break;
case GraphParamType_Deprecated::ChannelMask:
param->Type = VariantType(VariantType::Enum, TEXT("FlaxEngine.ChannelMask"));
param->Value.AsUint64 = (uint64)value.AsInteger;
break;
default:
CRASH;
}
}
FLAXENGINE_API void ReadOldGraphNodeValue_Deprecated(ReadStream* stream, Variant& result)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
CommonValue value;
stream->ReadCommonValue(&value);
result = Variant(value);
}
FLAXENGINE_API void ReadOldGraphBoxType_Deprecated(uint32 connectionType, VariantType& type)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
switch ((GraphConnectionType_Deprecated)connectionType)
{
case GraphConnectionType_Deprecated::Invalid:
type = VariantType(VariantType::Null);
break;
case GraphConnectionType_Deprecated::Impulse:
type = VariantType(VariantType::Void);
break;
case GraphConnectionType_Deprecated::Bool:
type = VariantType(VariantType::Bool);
break;
case GraphConnectionType_Deprecated::Integer:
type = VariantType(VariantType::Int);
break;
case GraphConnectionType_Deprecated::Float:
type = VariantType(VariantType::Float);
break;
case GraphConnectionType_Deprecated::Vector2:
type = VariantType(VariantType::Vector2);
break;
case GraphConnectionType_Deprecated::Vector3:
type = VariantType(VariantType::Vector3);
break;
case GraphConnectionType_Deprecated::Vector4:
type = VariantType(VariantType::Vector4);
break;
case GraphConnectionType_Deprecated::String:
type = VariantType(VariantType::String);
break;
case GraphConnectionType_Deprecated::Object:
type = VariantType(VariantType::Object);
break;
case GraphConnectionType_Deprecated::Rotation:
type = VariantType(VariantType::Quaternion);
break;
case GraphConnectionType_Deprecated::Transform:
type = VariantType(VariantType::Transform);
break;
case GraphConnectionType_Deprecated::Box:
type = VariantType(VariantType::BoundingBox);
break;
case GraphConnectionType_Deprecated::ImpulseSecondary:
type = VariantType(VariantType::Void);
break;
case GraphConnectionType_Deprecated::UnsignedInteger:
type = VariantType(VariantType::Uint);
break;
case GraphConnectionType_Deprecated::Scalar:
type = VariantType(VariantType::Null);
break;
case GraphConnectionType_Deprecated::Vector:
type = VariantType(VariantType::Null);
break;
case GraphConnectionType_Deprecated::Variable:
type = VariantType(VariantType::Null);
break;
case GraphConnectionType_Deprecated::All:
type = VariantType(VariantType::Null);
break;
default:
type = VariantType(VariantType::Null);
}
}
FLAXENGINE_API StringView GetGraphFunctionTypeName_Deprecated(const Variant& v)
{
// [Deprecated on 31.07.2020, expires on 31.07.2022]
if (v.Type.Type == VariantType::String)
return (StringView)v;
if (v.Type.Type == VariantType::Int)
{
switch ((GraphConnectionType_Deprecated)v.AsInt)
{
case GraphConnectionType_Deprecated::Impulse:
case GraphConnectionType_Deprecated::ImpulseSecondary:
return TEXT("System.Void");
case GraphConnectionType_Deprecated::Invalid:
case GraphConnectionType_Deprecated::Variable:
case GraphConnectionType_Deprecated::All:
return StringView::Empty;
case GraphConnectionType_Deprecated::Bool:
return TEXT("System.Boolean");
case GraphConnectionType_Deprecated::Integer:
return TEXT("System.Int32");
case GraphConnectionType_Deprecated::Float:
case GraphConnectionType_Deprecated::Scalar:
return TEXT("System.Single");
case GraphConnectionType_Deprecated::Vector2:
return TEXT("FlaxEngine.Vector2");
case GraphConnectionType_Deprecated::Vector3:
return TEXT("FlaxEngine.Vector3");
case GraphConnectionType_Deprecated::Vector4:
case GraphConnectionType_Deprecated::Vector:
return TEXT("FlaxEngine.Vector4");
case GraphConnectionType_Deprecated::String:
return TEXT("System.String");
case GraphConnectionType_Deprecated::Object:
return TEXT("FlaxEngine.Object");
case GraphConnectionType_Deprecated::Rotation:
return TEXT("System.Quaternion");
case GraphConnectionType_Deprecated::Transform:
return TEXT("System.Transform");
case GraphConnectionType_Deprecated::Box:
return TEXT("System.BoundingBox");
case GraphConnectionType_Deprecated::UnsignedInteger:
return TEXT("System.UInt32");
default: ;
}
}
return StringView::Empty;
}
void GraphUtilities::ApplySomeMathHere(Variant& v, Variant& a, MathOp1 op)
{
v.SetType(a.Type);
switch (a.Type.Type)
{
case VariantType::Bool:
v.AsBool = op(a.AsBool ? 1.0f : 0.0f) > ZeroTolerance;
break;
case VariantType::Int:
v.AsInt = (int32)op((float)a.AsInt);
break;
case VariantType::Uint:
v.AsUint = (uint32)op((float)a.AsUint);
break;
case VariantType::Float:
v.AsFloat = op(a.AsFloat);
break;
case VariantType::Vector2:
(*(Vector2*)v.AsData).X = op((*(Vector2*)a.AsData).X);
(*(Vector2*)v.AsData).Y = op((*(Vector2*)a.AsData).Y);
break;
case VariantType::Vector3:
(*(Vector3*)v.AsData).X = op((*(Vector3*)a.AsData).X);
(*(Vector3*)v.AsData).Y = op((*(Vector3*)a.AsData).Y);
(*(Vector3*)v.AsData).Z = op((*(Vector3*)a.AsData).Z);
break;
case VariantType::Vector4:
case VariantType::Color:
(*(Vector4*)v.AsData).X = op((*(Vector4*)a.AsData).X);
(*(Vector4*)v.AsData).Y = op((*(Vector4*)a.AsData).Y);
(*(Vector4*)v.AsData).Z = op((*(Vector4*)a.AsData).Z);
(*(Vector4*)v.AsData).W = op((*(Vector4*)a.AsData).W);
break;
case VariantType::Quaternion:
(*(Quaternion*)v.AsData).X = op((*(Quaternion*)a.AsData).X);
(*(Quaternion*)v.AsData).Y = op((*(Quaternion*)a.AsData).Y);
(*(Quaternion*)v.AsData).Z = op((*(Quaternion*)a.AsData).Z);
(*(Quaternion*)v.AsData).W = op((*(Quaternion*)a.AsData).W);
break;
case VariantType::Transform:
{
Transform& vTransform = *(Transform*)v.AsBlob.Data;
const Transform& aTransform = *(const Transform*)a.AsBlob.Data;
vTransform.Translation.X = op(aTransform.Translation.X);
vTransform.Translation.Y = op(aTransform.Translation.Y);
vTransform.Translation.Z = op(aTransform.Translation.Z);
vTransform.Orientation.X = op(aTransform.Orientation.X);
vTransform.Orientation.Y = op(aTransform.Orientation.Y);
vTransform.Orientation.Z = op(aTransform.Orientation.Z);
vTransform.Orientation.W = op(aTransform.Orientation.W);
vTransform.Scale.X = op(aTransform.Scale.X);
vTransform.Scale.Y = op(aTransform.Scale.Y);
vTransform.Scale.Z = op(aTransform.Scale.Z);
break;
}
default:
v = a;
break;
}
}
void GraphUtilities::ApplySomeMathHere(Variant& v, Variant& a, Variant& b, MathOp2 op)
{
v.SetType(a.Type);
switch (a.Type.Type)
{
case VariantType::Bool:
v.AsBool = op(a.AsBool ? 1.0f : 0.0f, b.AsBool ? 1.0f : 0.0f) > ZeroTolerance;
break;
case VariantType::Int:
v.AsInt = (int32)op((float)a.AsInt, (float)b.AsInt);
break;
case VariantType::Uint:
v.AsUint = (uint32)op((float)a.AsUint, (float)b.AsUint);
break;
case VariantType::Float:
v.AsFloat = op(a.AsFloat, b.AsFloat);
break;
case VariantType::Vector2:
(*(Vector2*)v.AsData).X = op((*(Vector2*)a.AsData).X, (*(Vector2*)b.AsData).X);
(*(Vector2*)v.AsData).Y = op((*(Vector2*)a.AsData).Y, (*(Vector2*)b.AsData).Y);
break;
case VariantType::Vector3:
(*(Vector3*)v.AsData).X = op((*(Vector3*)a.AsData).X, (*(Vector3*)b.AsData).X);
(*(Vector3*)v.AsData).Y = op((*(Vector3*)a.AsData).Y, (*(Vector3*)b.AsData).Y);
(*(Vector3*)v.AsData).Z = op((*(Vector3*)a.AsData).Z, (*(Vector3*)b.AsData).Z);
break;
case VariantType::Vector4:
case VariantType::Color:
(*(Vector4*)v.AsData).X = op((*(Vector4*)a.AsData).X, (*(Vector4*)b.AsData).X);
(*(Vector4*)v.AsData).Y = op((*(Vector4*)a.AsData).Y, (*(Vector4*)b.AsData).Y);
(*(Vector4*)v.AsData).Z = op((*(Vector4*)a.AsData).Z, (*(Vector4*)b.AsData).Z);
(*(Vector4*)v.AsData).W = op((*(Vector4*)a.AsData).W, (*(Vector4*)b.AsData).W);
case VariantType::Quaternion:
(*(Quaternion*)v.AsData).X = op((*(Quaternion*)a.AsData).X, (*(Quaternion*)b.AsData).X);
(*(Quaternion*)v.AsData).Y = op((*(Quaternion*)a.AsData).Y, (*(Quaternion*)b.AsData).Y);
(*(Quaternion*)v.AsData).Z = op((*(Quaternion*)a.AsData).Z, (*(Quaternion*)b.AsData).Z);
(*(Quaternion*)v.AsData).W = op((*(Quaternion*)a.AsData).W, (*(Quaternion*)b.AsData).W);
break;
case VariantType::Transform:
{
Transform& vTransform = *(Transform*)v.AsBlob.Data;
const Transform& aTransform = *(const Transform*)a.AsBlob.Data;
const Transform& bTransform = *(const Transform*)b.AsBlob.Data;
vTransform.Translation.X = op(aTransform.Translation.X, bTransform.Translation.X);
vTransform.Translation.Y = op(aTransform.Translation.Y, bTransform.Translation.Y);
vTransform.Translation.Z = op(aTransform.Translation.Z, bTransform.Translation.Z);
vTransform.Orientation.X = op(aTransform.Orientation.X, bTransform.Orientation.X);
vTransform.Orientation.Y = op(aTransform.Orientation.Y, bTransform.Orientation.Y);
vTransform.Orientation.Z = op(aTransform.Orientation.Z, bTransform.Orientation.Z);
vTransform.Orientation.W = op(aTransform.Orientation.W, bTransform.Orientation.W);
vTransform.Scale.X = op(aTransform.Scale.X, bTransform.Scale.X);
vTransform.Scale.Y = op(aTransform.Scale.Y, bTransform.Scale.Y);
vTransform.Scale.Z = op(aTransform.Scale.Z, bTransform.Scale.Z);
break;
}
default:
v = a;
break;
}
}
void GraphUtilities::ApplySomeMathHere(Variant& v, Variant& a, Variant& b, Variant& c, MathOp3 op)
{
v.SetType(a.Type);
switch (a.Type.Type)
{
case VariantType::Bool:
v.AsBool = op(a.AsBool ? 1.0f : 0.0f, b.AsBool ? 1.0f : 0.0f, c.AsBool ? 1.0f : 0.0f) > ZeroTolerance;
break;
case VariantType::Int:
v.AsInt = (int32)op((float)a.AsInt, (float)b.AsInt, (float)c.AsInt);
break;
case VariantType::Uint:
v.AsUint = (int32)op((float)a.AsUint, (float)b.AsUint, (float)c.AsUint);
break;
case VariantType::Float:
v.AsFloat = op(a.AsFloat, b.AsFloat, c.AsFloat);
break;
case VariantType::Vector2:
(*(Vector2*)v.AsData).X = op((*(Vector2*)a.AsData).X, (*(Vector2*)b.AsData).X, (*(Vector2*)c.AsData).X);
(*(Vector2*)v.AsData).Y = op((*(Vector2*)a.AsData).Y, (*(Vector2*)b.AsData).Y, (*(Vector2*)c.AsData).Y);
break;
case VariantType::Vector3:
(*(Vector3*)v.AsData).X = op((*(Vector3*)a.AsData).X, (*(Vector3*)b.AsData).X, (*(Vector3*)c.AsData).X);
(*(Vector3*)v.AsData).Y = op((*(Vector3*)a.AsData).Y, (*(Vector3*)b.AsData).Y, (*(Vector3*)c.AsData).Y);
(*(Vector3*)v.AsData).Z = op((*(Vector3*)a.AsData).Z, (*(Vector3*)b.AsData).Z, (*(Vector3*)c.AsData).Z);
break;
case VariantType::Vector4:
case VariantType::Color:
(*(Vector4*)v.AsData).X = op((*(Vector4*)a.AsData).X, (*(Vector4*)b.AsData).X, (*(Vector4*)c.AsData).X);
(*(Vector4*)v.AsData).Y = op((*(Vector4*)a.AsData).Y, (*(Vector4*)b.AsData).Y, (*(Vector4*)c.AsData).Y);
(*(Vector4*)v.AsData).Z = op((*(Vector4*)a.AsData).Z, (*(Vector4*)b.AsData).Z, (*(Vector4*)c.AsData).Z);
(*(Vector4*)v.AsData).W = op((*(Vector4*)a.AsData).W, (*(Vector4*)b.AsData).W, (*(Vector4*)c.AsData).W);
break;
case VariantType::Quaternion:
(*(Quaternion*)v.AsData).X = op((*(Quaternion*)a.AsData).X, (*(Quaternion*)b.AsData).X, (*(Quaternion*)c.AsData).X);
(*(Quaternion*)v.AsData).Y = op((*(Quaternion*)a.AsData).Y, (*(Quaternion*)b.AsData).Y, (*(Quaternion*)c.AsData).Y);
(*(Quaternion*)v.AsData).Z = op((*(Quaternion*)a.AsData).Z, (*(Quaternion*)b.AsData).Z, (*(Quaternion*)c.AsData).Z);
(*(Quaternion*)v.AsData).W = op((*(Quaternion*)a.AsData).W, (*(Quaternion*)b.AsData).W, (*(Quaternion*)c.AsData).W);
break;
case VariantType::Transform:
{
Transform& vTransform = *(Transform*)v.AsBlob.Data;
const Transform& aTransform = *(const Transform*)a.AsBlob.Data;
const Transform& bTransform = *(const Transform*)b.AsBlob.Data;
const Transform& cTransform = *(const Transform*)c.AsBlob.Data;
vTransform.Translation.X = op(aTransform.Translation.X, bTransform.Translation.X, cTransform.Translation.X);
vTransform.Translation.Y = op(aTransform.Translation.Y, bTransform.Translation.Y, cTransform.Translation.Y);
vTransform.Translation.Z = op(aTransform.Translation.Z, bTransform.Translation.Z, cTransform.Translation.Z);
vTransform.Orientation.X = op(aTransform.Orientation.X, bTransform.Orientation.X, cTransform.Orientation.X);
vTransform.Orientation.Y = op(aTransform.Orientation.Y, bTransform.Orientation.Y, cTransform.Orientation.Y);
vTransform.Orientation.Z = op(aTransform.Orientation.Z, bTransform.Orientation.Z, cTransform.Orientation.Z);
vTransform.Orientation.W = op(aTransform.Orientation.W, bTransform.Orientation.W, cTransform.Orientation.W);
vTransform.Scale.X = op(aTransform.Scale.X, bTransform.Scale.X, cTransform.Scale.X);
vTransform.Scale.Y = op(aTransform.Scale.Y, bTransform.Scale.Y, cTransform.Scale.Y);
vTransform.Scale.Z = op(aTransform.Scale.Z, bTransform.Scale.Z, cTransform.Scale.Z);
break;
}
default:
v = a;
break;
}
}
void GraphUtilities::ApplySomeMathHere(uint16 typeId, Variant& v, Variant& a)
{
// Select operation
MathOp1 op;
switch (typeId)
{
case 7:
op = [](float a)
{
return Math::Abs(a);
};
break;
case 8:
op = [](float a)
{
return Math::Ceil(a);
};
break;
case 9:
op = [](float a)
{
return Math::Cos(a);
};
break;
case 10:
op = [](float a)
{
return Math::Floor(a);
};
break;
case 13:
op = [](float a)
{
return Math::Round(a);
};
break;
case 14:
op = [](float a)
{
return Math::Saturate(a);
};
break;
case 15:
op = [](float a)
{
return Math::Sin(a);
};
break;
case 16:
op = [](float a)
{
return Math::Sqrt(a);
};
break;
case 17:
op = [](float a)
{
return Math::Tan(a);
};
break;
case 27:
op = [](float a)
{
return -a;
};
break;
case 28:
op = [](float a)
{
return 1 - a;
};
break;
case 33:
op = [](float a)
{
return Math::Asin(a);
};
break;
case 34:
op = [](float a)
{
return Math::Acos(a);
};
break;
case 35:
op = [](float a)
{
return Math::Atan(a);
};
break;
case 38:
op = [](float a)
{
return Math::Trunc(a);
};
break;
case 39:
op = [](float a)
{
float tmp;
return Math::ModF(a, &tmp);
};
break;
case 43:
{
op = [](float a)
{
return a * RadiansToDegrees;
};
break;
}
case 44:
{
op = [](float a)
{
return a * DegreesToRadians;
};
break;
}
default:
return;
}
// Perform operation
ApplySomeMathHere(v, a, op);
}
void GraphUtilities::ApplySomeMathHere(uint16 typeId, Variant& v, Variant& a, Variant& b)
{
// Select operation
MathOp2 op;
switch (typeId)
{
case 1:
op = [](float a, float b)
{
return a + b;
};
break;
case 2:
op = [](float a, float b)
{
return a - b;
};
break;
case 3:
op = [](float a, float b)
{
return a * b;
};
break;
case 4:
op = [](float a, float b)
{
return (float)((int)a % (int)b);
};
break;
case 5:
op = [](float a, float b)
{
return a / b;
};
break;
case 21:
op = [](float a, float b)
{
return Math::Max(a, b);
};
break;
case 22:
op = [](float a, float b)
{
return Math::Min(a, b);
};
break;
case 23:
op = [](float a, float b)
{
return Math::Pow(a, b);
};
break;
case 40:
op = [](float a, float b)
{
return Math::Mod(a, b);
};
break;
case 41:
op = [](float a, float b)
{
return Math::Atan2(a, b);
};
break;
default:
return;
}
// Perform operation
ApplySomeMathHere(v, a, b, op);
}
int32 GraphUtilities::CountComponents(VariantType::Types type)
{
switch (type)
{
case VariantType::Bool:
case VariantType::Int:
case VariantType::Int64:
case VariantType::Uint:
case VariantType::Uint64:
case VariantType::Float:
case VariantType::Double:
case VariantType::Pointer:
return 1;
case VariantType::Vector2:
return 2;
case VariantType::Vector3:
return 3;
case VariantType::Vector4:
case VariantType::Color:
return 4;
default:
return 0;
}
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/Variant.h"
#include "Engine/Visject/Graph.h"
namespace GraphUtilities
{
typedef float (*MathOp1)(float);
typedef float (*MathOp2)(float, float);
typedef float (*MathOp3)(float, float, float);
void ApplySomeMathHere(Variant& v, Variant& a, MathOp1 op);
void ApplySomeMathHere(Variant& v, Variant& a, Variant& b, MathOp2 op);
void ApplySomeMathHere(Variant& v, Variant& a, Variant& b, Variant& c, MathOp3 op);
void ApplySomeMathHere(uint16 typeId, Variant& v, Variant& a);
void ApplySomeMathHere(uint16 typeId, Variant& v, Variant& a, Variant& b);
int32 CountComponents(VariantType::Types type);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,292 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#if USE_EDITOR
#include "Graph.h"
#include "ShaderGraphValue.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Core/Collections/HashSet.h"
#include "Engine/Utilities/TextWriter.h"
#include "Engine/Graphics/Materials/MaterialParams.h"
#include "Engine/Content/Utilities/AssetsContainer.h"
#include "Engine/Animations/Curve.h"
#define SHADER_GRAPH_MAX_CALL_STACK 100
enum class MaterialSceneTextures;
template<class BoxType>
class ShaderGraphNode;
class ShaderGraphBox : public GraphBox
{
public:
/// <summary>
/// The cached value.
/// </summary>
ShaderGraphValue Cache;
public:
ShaderGraphBox()
: GraphBox()
{
}
ShaderGraphBox(void* parent, byte id, const VariantType::Types type)
: GraphBox(parent, id, type)
{
}
ShaderGraphBox(void* parent, byte id, const VariantType& type)
: GraphBox(parent, id, type)
{
}
public:
ShaderGraphBox* FirstConnection() const
{
return (ShaderGraphBox*)Connections[0];
}
};
template<class BoxType = ShaderGraphBox>
class ShaderGraphNode : public GraphNode<BoxType>
{
public:
struct CurveData
{
/// <summary>
/// The curve index.
/// </summary>
int32 CurveIndex;
};
/// <summary>
/// Custom cached data per node type. Compact to use as small amount of memory as possible.
/// </summary>
struct AdditionalData
{
union
{
CurveData Curve;
};
};
public:
ShaderGraphNode()
: GraphNode<ShaderGraphBox>()
{
}
public:
/// <summary>
/// The custom data (depends on node type). Used to cache data for faster usage at runtime.
/// </summary>
AdditionalData Data;
};
class ShaderGraphParameter : public GraphParameter
{
public:
ShaderGraphParameter()
: GraphParameter(SpawnParams(Guid::New(), TypeInitializer))
{
}
ShaderGraphParameter(const ShaderGraphParameter& other)
: ShaderGraphParameter()
{
#if !BUILD_RELEASE
CRASH; // Not used
#endif
}
ShaderGraphParameter& operator=(const ShaderGraphParameter& other)
{
#if !BUILD_RELEASE
CRASH; // Not used
#endif
return *this;
}
};
template<class NodeType = ShaderGraphNode<>, class BoxType = ShaderGraphBox, class ParameterType = ShaderGraphParameter>
class ShaderGraph : public Graph<NodeType, BoxType, ParameterType>
{
public:
typedef ShaderGraphValue Value;
typedef Graph<NodeType, BoxType, ParameterType> Base;
public:
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<float>> FloatCurves;
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<Vector2>> Vector2Curves;
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<Vector3>> Vector3Curves;
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<Vector4>> Vector4Curves;
public:
// [Graph]
bool onNodeLoaded(Node* n) override
{
// Check if this node needs a state or data cache
switch (n->GroupID)
{
// Tools
case 7:
switch (n->TypeID)
{
// Curves
#define SETUP_CURVE(id, curves, access) \
case id: \
{ \
n->Data.Curve.CurveIndex = curves.Count(); \
auto& curve = curves.AddOne(); \
const int32 keyframesCount = n->Values[0].AsInt; \
auto& keyframes = curve.GetKeyframes(); \
keyframes.Resize(keyframesCount); \
for (int32 i = 0; i < keyframesCount; i++) \
{ \
const int32 idx = i * 4; \
auto& keyframe = keyframes[i]; \
keyframe.Time = n->Values[idx + 1].AsFloat; \
keyframe.Value = n->Values[idx + 2].access; \
keyframe.TangentIn = n->Values[idx + 3].access; \
keyframe.TangentOut = n->Values[idx + 4].access; \
} \
break; \
}
SETUP_CURVE(12, FloatCurves, AsFloat)
SETUP_CURVE(13, Vector2Curves, AsVector2())
SETUP_CURVE(14, Vector3Curves, AsVector3())
SETUP_CURVE(15, Vector4Curves, AsVector4())
#undef SETUP_CURVE
}
break;
}
// Base
return Base::onNodeLoaded(n);
}
};
/// <summary>
/// Shaders generator from graphs.
/// </summary>
class ShaderGenerator
{
public:
typedef ShaderGraph<> Graph;
typedef ShaderGraph<>::Node Node;
typedef ShaderGraph<>::Box Box;
typedef ShaderGraph<>::Parameter Parameter;
typedef ShaderGraphValue Value;
typedef VariantType::Types ValueType;
typedef Delegate<Node*, Box*, const StringView&> ErrorHandler;
typedef Function<void(Box*, Node*, Value&)> ProcessBoxHandler;
protected:
int32 _localIndex;
Dictionary<Node*, Graph*> _functions;
Array<SerializedMaterialParam> _parameters;
TextWriterUnicode _writer;
HashSet<String> _includes;
Array<ProcessBoxHandler, FixedAllocation<17>> _perGroupProcessCall;
Array<Node*, FixedAllocation<SHADER_GRAPH_MAX_CALL_STACK>> _callStack;
Array<Graph*, FixedAllocation<32>> _graphStack;
public:
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGenerator"/> class.
/// </summary>
ShaderGenerator();
/// <summary>
/// Finalizes an instance of the <see cref="ShaderGenerator"/> class.
/// </summary>
~ShaderGenerator();
public:
ErrorHandler Error;
/// <summary>
/// The assets container for graph generation. Holds references to used assets. Can be used to gather assets referenced by graph (eg. nested graph functions).
/// </summary>
AssetsContainer Assets;
public:
void OnError(Node* node, Box* box, const StringView& message);
void ProcessGroupConstants(Box* box, Node* node, Value& value);
void ProcessGroupMath(Box* box, Node* node, Value& value);
void ProcessGroupPacking(Box* box, Node* node, Value& value);
void ProcessGroupTools(Box* box, Node* node, Value& value);
void ProcessGroupBoolean(Box* box, Node* node, Value& value);
void ProcessGroupBitwise(Box* box, Node* node, Value& value);
void ProcessGroupComparisons(Box* box, Node* node, Value& value);
protected:
static const Char* _mathFunctions[];
static const Char* _subs[];
protected:
Value eatBox(Node* caller, Box* box);
Value tryGetValue(Box* box, int32 defaultValueBoxIndex, const Value& defaultValue);
Value tryGetValue(Box* box, const Value& defaultValue);
Value tryGetValue(Box* box, const Variant& defaultValue);
Value writeLocal(ValueType type, Node* caller);
Value writeLocal(ValueType type, Node* caller, const String& name);
Value writeLocal(ValueType type, const Value& value, Node* caller);
Value writeLocal(const Value& value, Node* caller);
Value writeLocal(ValueType type, const String& value, Node* caller);
Value writeLocal(ValueType type, const String& value, Node* caller, const String& name);
Value writeOperation2(Node* caller, const Value& valueA, const Value& valueB, Char op1);
Value writeFunction1(Node* caller, const Value& valueA, const String& function);
Value writeFunction2(Node* caller, const Value& valueA, const Value& valueB, const String& function);
Value writeFunction2(Node* caller, const Value& valueA, const Value& valueB, const String& function, ValueType resultType);
Value writeFunction3(Node* caller, const Value& valueA, const Value& valueB, const Value& valueC, const String& function, ValueType resultType);
SerializedMaterialParam* findParam(const String& shaderName);
SerializedMaterialParam* findParam(const Guid& id);
SerializedMaterialParam findOrAddTexture(const Guid& id);
SerializedMaterialParam findOrAddNormalMap(const Guid& id);
SerializedMaterialParam findOrAddCubeTexture(const Guid& id);
SerializedMaterialParam findOrAddSceneTexture(MaterialSceneTextures type);
static String getLocalName(int32 index);
static String getParamName(int32 index);
};
#endif

View File

@@ -0,0 +1,377 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#if USE_EDITOR
#include "ShaderGraphUtilities.h"
#include "ShaderGraphValue.h"
#include "Engine/Core/Types/StringBuilder.h"
#include "Engine/Content/Content.h"
#include "Engine/Engine/GameplayGlobals.h"
void ShaderGraphUtilities::GenerateShaderConstantBuffer(TextWriterUnicode& writer, Array<SerializedMaterialParam>& parameters)
{
int32 constantsOffset = 0;
int32 paddingIndex = 0;
for (int32 i = 0; i < parameters.Count(); i++)
{
auto& param = parameters[i];
const Char* format = nullptr;
int32 size;
int32 alignment;
switch (param.Type)
{
case MaterialParameterType::Bool:
size = 4;
alignment = 4;
format = TEXT("bool {0};");
break;
case MaterialParameterType::Integer:
size = 4;
alignment = 4;
format = TEXT("int {0};");
break;
case MaterialParameterType::Float:
size = 4;
alignment = 4;
format = TEXT("float {0};");
break;
case MaterialParameterType::Vector2:
size = 8;
alignment = 8;
format = TEXT("float2 {0};");
break;
case MaterialParameterType::Vector3:
size = 12;
alignment = 16;
format = TEXT("float3 {0};");
break;
case MaterialParameterType::Vector4:
case MaterialParameterType::ChannelMask:
case MaterialParameterType::Color:
size = 16;
alignment = 16;
format = TEXT("float4 {0};");
break;
case MaterialParameterType::Matrix:
size = 16 * 4;
alignment = 16;
format = TEXT("float4x4 {0};");
break;
case MaterialParameterType::GameplayGlobal:
{
auto asset = Content::LoadAsync<GameplayGlobals>(param.AsGuid);
if (!asset || asset->WaitForLoaded())
break;
GameplayGlobals::Variable variable;
if (!asset->Variables.TryGet(param.Name, variable))
break;
switch (variable.DefaultValue.Type.Type)
{
case VariantType::Bool:
size = 4;
alignment = 4;
format = TEXT("bool {0};");
break;
case VariantType::Int:
size = 4;
alignment = 4;
format = TEXT("int {0};");
break;
case VariantType::Uint:
size = 4;
alignment = 4;
format = TEXT("uint {0};");
break;
case VariantType::Float:
size = 4;
alignment = 4;
format = TEXT("float {0};");
break;
case VariantType::Vector2:
size = 8;
alignment = 8;
format = TEXT("float2 {0};");
break;
case VariantType::Vector3:
size = 12;
alignment = 16;
format = TEXT("float3 {0};");
break;
case VariantType::Vector4:
case VariantType::Color:
size = 16;
alignment = 16;
format = TEXT("float4 {0};");
break;
default: ;
}
break;
}
default: ;
}
if (format)
{
int32 padding = Math::Abs((alignment - (constantsOffset % 16))) % alignment;
if (padding != 0)
{
constantsOffset += padding;
padding /= 4;
while (padding-- > 0)
{
writer.WriteLine(TEXT("uint PADDING_{0};"), paddingIndex++);
}
}
param.RegisterIndex = 0;
param.Offset = constantsOffset;
writer.WriteLine(format, param.ShaderName);
constantsOffset += size;
}
}
}
const Char* ShaderGraphUtilities::GenerateShaderResources(TextWriterUnicode& writer, Array<SerializedMaterialParam>& parameters, int32 startRegister)
{
int32 registerIndex = startRegister;
for (int32 i = 0; i < parameters.Count(); i++)
{
auto& param = parameters[i];
const Char* format;
switch (param.Type)
{
case MaterialParameterType::NormalMap:
case MaterialParameterType::GPUTexture:
case MaterialParameterType::SceneTexture:
case MaterialParameterType::Texture:
format = TEXT("Texture2D {0} : register(t{1});");
break;
case MaterialParameterType::GPUTextureCube:
case MaterialParameterType::CubeTexture:
format = TEXT("TextureCube {0} : register(t{1});");
break;
case MaterialParameterType::GPUTextureArray:
format = TEXT("Texture2DArray {0} : register(t{1});");
break;
case MaterialParameterType::GPUTextureVolume:
format = TEXT("Texture3D {0} : register(t{1});");
break;
default:
format = nullptr;
break;
}
if (format)
{
param.Offset = 0;
param.RegisterIndex = registerIndex;
writer.WriteLine(format, param.ShaderName, static_cast<int32>(registerIndex));
registerIndex++;
// Validate Shader Resource count limit
if (param.RegisterIndex >= GPU_MAX_SR_BINDED)
{
return TEXT("Too many textures used. The maximum supported amount is " MACRO_TO_STR(GPU_MAX_SR_BINDED) " (including lightmaps and utility textures for lighting).");
}
}
}
if (startRegister != registerIndex)
writer.WriteLine();
return nullptr;
}
template<typename T>
const Char* GetTypename()
{
return TEXT("");
}
template<>
const Char* GetTypename<float>()
{
return TEXT("float");
}
template<>
const Char* GetTypename<Vector2>()
{
return TEXT("float2");
}
template<>
const Char* GetTypename<Vector3>()
{
return TEXT("float3");
}
template<>
const Char* GetTypename<Vector4>()
{
return TEXT("float4");
}
template<typename T>
void ShaderGraphUtilities::SampleCurve(TextWriterUnicode& writer, const BezierCurve<T>& curve, const String& time, const String& value)
{
const auto& keyframes = curve.GetKeyframes();
if (keyframes.Count() == 0)
{
writer.Write(
TEXT(
" {{\n"
" // Curve ({1})\n"
" {0} = 0;\n"
" }}\n"
),
value, // {0}
GetTypename<T>() // {1}
);
}
else if (keyframes.Count() == 1)
{
writer.Write(
TEXT(
" {{\n"
" // Curve ({1})\n"
" {0} = {2};\n"
" }}\n"
),
value, // {0}
GetTypename<T>(), // {1}
ShaderGraphValue(keyframes[0].Value).Value // {2}
);
}
else if (keyframes.Count() == 2)
{
writer.Write(
TEXT(
" {{\n"
" // Curve ({4})\n"
" const float leftTime = {3};\n"
" const float rightTime = {5};\n"
" const float lengthTime = rightTime - leftTime;\n"
" float time = clamp({0}, leftTime, rightTime);\n"
" float alpha = lengthTime < 0.0000001 ? 0.0f : (time - leftTime) / lengthTime;\n"
" const {4} leftValue = {6};\n"
" const {4} rightValue = {7};\n"
" const float oneThird = 1.0f / 3.0f;\n"
" {4} leftTangent = leftValue + {8} * (lengthTime * oneThird);\n"
" {4} rightTangent = rightValue + {1} * (lengthTime * oneThird);\n"
" {4} p01 = lerp(leftValue, leftTangent, alpha);\n"
" {4} p12 = lerp(leftTangent, rightTangent, alpha);\n"
" {4} p23 = lerp(rightTangent, rightValue, alpha);\n"
" {4} p012 = lerp(p01, p12, alpha);\n"
" {4} p123 = lerp(p12, p23, alpha);\n"
" {2} = lerp(p012, p123, alpha);\n"
" }}\n"
),
time, // {0}
ShaderGraphValue(keyframes[1].TangentIn).Value, // {1}
value, // {2}
StringUtils::ToString(keyframes[0].Time), // {3}
GetTypename<T>(), // {4}
StringUtils::ToString(keyframes[1].Time), // {5}
ShaderGraphValue(keyframes[0].Value).Value, // {6}
ShaderGraphValue(keyframes[1].Value).Value, // {7}
ShaderGraphValue(keyframes[0].TangentOut).Value // {8}
);
}
else
{
StringBuilder keyframesTime, keyframesValue, keyframesTangentIn, keyframesTangentOut;
for (int32 i = 0; i < keyframes.Count(); i++)
{
const auto& keyframe = keyframes[i];
if (i != 0)
{
keyframesTime.Append(',');
keyframesValue.Append(',');
keyframesTangentIn.Append(',');
keyframesTangentOut.Append(',');
}
keyframesTime.Append(StringUtils::ToString(keyframe.Time));
keyframesValue.Append(ShaderGraphValue(keyframe.Value).Value);
keyframesTangentIn.Append(ShaderGraphValue(keyframe.TangentIn).Value);
keyframesTangentOut.Append(ShaderGraphValue(keyframe.TangentOut).Value);
}
keyframesTime.Append('\0');
keyframesValue.Append('\0');
keyframesTangentIn.Append('\0');
keyframesTangentOut.Append('\0');
writer.Write(
TEXT(
" {{\n"
" // Curve ({4})\n"
" int count = {0};\n"
" float time = clamp({1}, 0.0, {2});\n"
" static float keyframesTime[] = {{ {5} }};\n"
" static {4} keyframesValue[] = {{ {6} }};\n"
" static {4} keyframesTangentIn[] = {{ {7} }};\n"
" static {4} keyframesTangentOut[] = {{ {8} }};\n"
" int start = 0;\n"
" int searchLength = count;\n"
" while (searchLength > 0)\n"
" {{\n"
" int halfPos = searchLength >> 1;\n"
" int midPos = start + halfPos;\n"
" if (time < keyframesTime[midPos])\n"
" {{\n"
" searchLength = halfPos;\n"
" }}\n"
" else\n"
" {{\n"
" start = midPos + 1;\n"
" searchLength -= halfPos + 1;\n"
" }}\n"
" }}\n"
" int leftKey = max(0, start - 1);\n"
" int rightKey = min(start, count - 1);\n"
" const float leftTime = keyframesTime[leftKey];\n"
" const float rightTime = keyframesTime[rightKey];\n"
" const float lengthTime = rightTime - leftTime;\n"
" float alpha = lengthTime < 0.0000001 ? 0.0f : (time - leftTime) / lengthTime;\n"
" const {4} leftValue = keyframesValue[leftKey];\n"
" const {4} rightValue = keyframesValue[rightKey];\n"
" const float oneThird = 1.0f / 3.0f;\n"
" {4} leftTangent = leftValue + keyframesTangentOut[leftKey] * (lengthTime * oneThird);\n"
" {4} rightTangent = rightValue + keyframesTangentIn[rightKey] * (lengthTime * oneThird);\n"
" {4} p01 = lerp(leftValue, leftTangent, alpha);\n"
" {4} p12 = lerp(leftTangent, rightTangent, alpha);\n"
" {4} p23 = lerp(rightTangent, rightValue, alpha);\n"
" {4} p012 = lerp(p01, p12, alpha);\n"
" {4} p123 = lerp(p12, p23, alpha);\n"
" {3} = lerp(p012, p123, alpha);\n"
" }}\n"
),
keyframes.Count(), // {0}
time, // {1}
curve.GetLength(), // {2}
value, // {3}
GetTypename<T>(), // {4}
*keyframesTime, // {5}
*keyframesValue, // {6}
*keyframesTangentIn, // {7}
*keyframesTangentOut // {8}
);
}
}
template void ShaderGraphUtilities::SampleCurve(TextWriterUnicode& writer, const BezierCurve<float>& curve, const String& time, const String& value);
template void ShaderGraphUtilities::SampleCurve(TextWriterUnicode& writer, const BezierCurve<Vector2>& curve, const String& time, const String& value);
template void ShaderGraphUtilities::SampleCurve(TextWriterUnicode& writer, const BezierCurve<Vector3>& curve, const String& time, const String& value);
template void ShaderGraphUtilities::SampleCurve(TextWriterUnicode& writer, const BezierCurve<Vector4>& curve, const String& time, const String& value);
#endif

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#if USE_EDITOR
#include "Engine/Graphics/Materials/MaterialParams.h"
#include "Engine/Utilities/TextWriter.h"
#include "Engine/Animations/Curve.h"
namespace ShaderGraphUtilities
{
void GenerateShaderConstantBuffer(TextWriterUnicode& writer, Array<SerializedMaterialParam>& parameters);
const Char* GenerateShaderResources(TextWriterUnicode& writer, Array<SerializedMaterialParam>& parameters, int32 startRegister);
template<typename T>
void SampleCurve(TextWriterUnicode& writer, const BezierCurve<T>& curve, const String& time, const String& value);
}
#endif

View File

@@ -0,0 +1,328 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "ShaderGraphValue.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Types/StringView.h"
const Char* ShaderGraphValue::_subs[] =
{
TEXT(".x"),
TEXT(".y"),
TEXT(".z"),
TEXT(".w")
};
const ShaderGraphValue ShaderGraphValue::Zero(VariantType::Types::Float, TEXT("0.0"));
const ShaderGraphValue ShaderGraphValue::Half(VariantType::Types::Float, TEXT("0.5"));
const ShaderGraphValue ShaderGraphValue::One(VariantType::Types::Float, TEXT("1.0"));
const ShaderGraphValue ShaderGraphValue::True(VariantType::Types::Bool, TEXT("true"));
const ShaderGraphValue ShaderGraphValue::False(VariantType::Types::Bool, TEXT("false"));
ShaderGraphValue::ShaderGraphValue(const Variant& v)
{
switch (v.Type.Type)
{
case VariantType::Bool:
Type = VariantType::Types::Bool;
Value = v.AsBool ? TEXT('1') : TEXT('0');
break;
case VariantType::Int:
Type = VariantType::Types::Int;
Value = StringUtils::ToString(v.AsInt);
break;
case VariantType::Uint:
Type = VariantType::Types::Uint;
Value = StringUtils::ToString(v.AsUint);
break;
case VariantType::Float:
Type = VariantType::Types::Float;
Value = String::Format(TEXT("{0}"), v.AsFloat);
break;
case VariantType::Vector2:
Type = VariantType::Types::Vector2;
Value = String::Format(TEXT("float2({0}, {1})"), (*(Vector2*)v.AsData).X, (*(Vector2*)v.AsData).Y);
break;
case VariantType::Vector3:
Type = VariantType::Types::Vector3;
Value = String::Format(TEXT("float3({0}, {1}, {2})"), (*(Vector3*)v.AsData).X, (*(Vector3*)v.AsData).Y, (*(Vector3*)v.AsData).Z);
break;
case VariantType::Vector4:
case VariantType::Color:
Type = VariantType::Types::Vector4;
Value = String::Format(TEXT("float4({0}, {1}, {2}, {3})"), (*(Vector4*)v.AsData).X, (*(Vector4*)v.AsData).Y, (*(Vector4*)v.AsData).Z, (*(Vector4*)v.AsData).W);
break;
case VariantType::String:
Type = VariantType::Types::String;
Value = (StringView)v;
break;
default:
Type = VariantType::Types::Null;
break;
}
}
bool ShaderGraphValue::IsZero() const
{
switch (Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
case VariantType::Types::Float:
return Value == TEXT("0") || Value == TEXT("0.0");
default:
return false;
}
}
bool ShaderGraphValue::IsOne() const
{
switch (Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
case VariantType::Types::Float:
return Value == TEXT("1") || Value == TEXT("1.0");
default:
return false;
}
}
ShaderGraphValue ShaderGraphValue::InitForZero(VariantType::Types type)
{
const Char* v;
switch (type)
{
case VariantType::Types::Float:
v = TEXT("0.0");
break;
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
v = TEXT("0");
break;
case VariantType::Types::Vector2:
v = TEXT("float2(0, 0)");
break;
case VariantType::Types::Vector3:
v = TEXT("float3(0, 0, 0)");
break;
case VariantType::Types::Vector4:
case VariantType::Types::Color:
v = TEXT("float4(0, 0, 0, 0)");
break;
case VariantType::Types::Void:
v = TEXT("((Material)0)");
break;
default:
CRASH;
v = nullptr;
}
return ShaderGraphValue(type, String(v));
}
ShaderGraphValue ShaderGraphValue::InitForHalf(VariantType::Types type)
{
const Char* v;
switch (type)
{
case VariantType::Types::Float:
v = TEXT("0.5");
break;
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
v = TEXT("0");
break;
case VariantType::Types::Vector2:
v = TEXT("float2(0.5, 0.5)");
break;
case VariantType::Types::Vector3:
v = TEXT("float3(0.5, 0.5, 0.5)");
break;
case VariantType::Types::Vector4:
case VariantType::Types::Color:
v = TEXT("float4(0.5, 0.5, 0.5, 0.5)");
break;
default:
CRASH;
v = nullptr;
}
return ShaderGraphValue(type, String(v));
}
ShaderGraphValue ShaderGraphValue::InitForOne(VariantType::Types type)
{
const Char* v;
switch (type)
{
case VariantType::Types::Float:
v = TEXT("1.0");
break;
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
v = TEXT("1");
break;
case VariantType::Types::Vector2:
v = TEXT("float2(1, 1)");
break;
case VariantType::Types::Vector3:
v = TEXT("float3(1, 1, 1)");
break;
case VariantType::Types::Vector4:
case VariantType::Types::Color:
v = TEXT("float4(1, 1, 1, 1)");
break;
default:
CRASH;
v = nullptr;
}
return ShaderGraphValue(type, String(v));
}
ShaderGraphValue ShaderGraphValue::Cast(const ShaderGraphValue& v, VariantType::Types to)
{
// If they are the same types or input value is empty, then just return value
if (v.Type == to || v.Value.IsEmpty())
{
return v;
}
// Select format string
const Char* format = nullptr;
switch (to)
{
case VariantType::Types::Bool:
switch (v.Type)
{
case VariantType::Types::Int:
case VariantType::Types::Uint:
case VariantType::Types::Float:
format = TEXT("((bool){0})");
break;
case VariantType::Types::Vector2:
case VariantType::Types::Vector3:
case VariantType::Types::Vector4:
case VariantType::Types::Color:
format = TEXT("((bool){0}.x)");
break;
}
break;
case VariantType::Types::Int:
switch (v.Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Uint:
case VariantType::Types::Float:
format = TEXT("((int){0})");
break;
case VariantType::Types::Vector2:
case VariantType::Types::Vector3:
case VariantType::Types::Vector4:
case VariantType::Types::Color:
format = TEXT("((int){0}.x)");
break;
}
break;
case VariantType::Types::Uint:
switch (v.Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Float:
format = TEXT("((uint){0})");
break;
case VariantType::Types::Vector2:
case VariantType::Types::Vector3:
case VariantType::Types::Vector4:
case VariantType::Types::Color:
format = TEXT("((uint){0}.x)");
break;
}
break;
case VariantType::Types::Float:
switch (v.Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
format = TEXT("((float){0})");
break;
case VariantType::Types::Vector2:
case VariantType::Types::Vector3:
case VariantType::Types::Vector4:
case VariantType::Types::Color:
format = TEXT("((float){0}.x)");
break;
}
break;
case VariantType::Types::Vector2:
switch (v.Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
case VariantType::Types::Float:
format = TEXT("float2({0}, {0})");
break;
case VariantType::Types::Vector3:
case VariantType::Types::Vector4:
case VariantType::Types::Color:
format = TEXT("{0}.xy");
break;
}
break;
case VariantType::Types::Vector3:
switch (v.Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
case VariantType::Types::Float:
format = TEXT("float3({0}, {0}, {0})");
break;
case VariantType::Types::Vector2:
format = TEXT("float3({0}.xy, 0)");
break;
case VariantType::Types::Vector4:
case VariantType::Types::Color:
format = TEXT("{0}.xyz");
break;
}
break;
case VariantType::Types::Vector4:
case VariantType::Types::Color:
switch (v.Type)
{
case VariantType::Types::Bool:
case VariantType::Types::Int:
case VariantType::Types::Uint:
case VariantType::Types::Float:
format = TEXT("float4({0}, {0}, {0}, {0})");
break;
case VariantType::Types::Vector2:
format = TEXT("float4({0}.xy, 0, 0)");
break;
case VariantType::Types::Vector3:
format = TEXT("float4({0}.xyz, 0)");
break;
case VariantType::Types::Color:
case VariantType::Types::Vector4:
format = TEXT("{0}");
break;
}
break;
}
if (format == nullptr)
{
LOG(Error, "Failed to cast shader graph value of type {0} to {1}", VariantType(v.Type), VariantType(to));
return Zero;
}
return ShaderGraphValue(to, String::Format(format, v.Value));
}

View File

@@ -0,0 +1,420 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Object.h"
#include "GraphNode.h"
/// <summary>
/// Shader source generator value container. Caches the value type and the value variable name (shader local, global parameter or constant value). Supports value type casting and component swizzle.
/// </summary>
struct ShaderGraphValue : Object
{
private:
static const Char* _subs[];
public:
/// <summary>
/// The value type.
/// </summary>
VariantType::Types Type;
/// <summary>
/// The shader value.
/// </summary>
String Value;
public:
/// <summary>
/// Zero value (as float).
/// </summary>
static const ShaderGraphValue Zero;
/// <summary>
/// Half value (as float).
/// </summary>
static const ShaderGraphValue Half;
/// <summary>
/// One value (as float).
/// </summary>
static const ShaderGraphValue One;
/// <summary>
/// True value (as bool).
/// </summary>
static const ShaderGraphValue True;
/// <summary>
/// False value (as bool).
/// </summary>
static const ShaderGraphValue False;
public:
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGraphValue"/> struct.
/// </summary>
ShaderGraphValue()
: Type(VariantType::Types::Null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGraphValue"/> struct.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="value">The value.</param>
ShaderGraphValue(VariantType::Types type, const Char* value)
: Type(type)
, Value(value)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGraphValue"/> struct.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="value">The value.</param>
ShaderGraphValue(VariantType::Types type, const String& value)
: Type(type)
, Value(value)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGraphValue"/> struct.
/// </summary>
/// <param name="value">The value.</param>
explicit ShaderGraphValue(const bool value)
: Type(VariantType::Types::Bool)
, Value(value ? TEXT("true") : TEXT("false"))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGraphValue"/> struct.
/// </summary>
/// <param name="value">The value.</param>
explicit ShaderGraphValue(const float value)
: Type(VariantType::Types::Float)
, Value(StringUtils::ToString(value))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGraphValue"/> struct.
/// </summary>
/// <param name="value">The value.</param>
explicit ShaderGraphValue(const int32 value)
: Type(VariantType::Types::Int)
, Value(StringUtils::ToString(value))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="ShaderGraphValue"/> struct.
/// </summary>
/// <param name="v">The value.</param>
explicit ShaderGraphValue(const Variant& v);
public:
/// <summary>
/// Returns true if value is valid.
/// </summary>
/// <returns>True if is valid, otherwise false.</returns>
bool IsValid() const
{
return Type != VariantType::Types::Null;
}
/// <summary>
/// Returns true if value is invalid.
/// </summary>
/// <returns>True if is invalid, otherwise false.</returns>
bool IsInvalid() const
{
return Type == VariantType::Types::Null;
}
/// <summary>
/// Checks if value contains static part with zero.
/// </summary>
/// <returns>True if contains zero number.</returns>
bool IsZero() const;
/// <summary>
/// Checks if value contains static part with one.
/// </summary>
/// <returns>True if contains one number.</returns>
bool IsOne() const;
/// <summary>
/// Clears this instance.
/// </summary>
void Clear()
{
Type = VariantType::Types::Null;
Value.Clear();
}
public:
/// <summary>
/// Formats thw value.
/// </summary>
/// <param name="format">The format text.</param>
/// <param name="v1">The value.</param>
/// <returns>The formatted value.</returns>
static String Format(const Char* format, const ShaderGraphValue& v1)
{
return String::Format(format, v1.Value);
}
/// <summary>
/// Formats thw value.
/// </summary>
/// <param name="format">The format text.</param>
/// <param name="v1">The first value.</param>
/// <param name="v2">The second value.</param>
/// <returns>The formatted value.</returns>
static String Format(const Char* format, const ShaderGraphValue& v1, const ShaderGraphValue& v2)
{
return String::Format(format, v1.Value, v2.Value);
}
/// <summary>
/// Formats thw value.
/// </summary>
/// <param name="format">The format text.</param>
/// <param name="v1">The first value.</param>
/// <param name="v2">The second value.</param>
/// <param name="v3">The third value.</param>
/// <returns>The formatted value.</returns>
static String Format(const Char* format, const ShaderGraphValue& v1, const ShaderGraphValue& v2, const ShaderGraphValue& v3)
{
return String::Format(format, v1.Value, v2.Value, v3.Value);
}
/// <summary>
/// Formats thw value.
/// </summary>
/// <param name="format">The format text.</param>
/// <param name="v1">The first value.</param>
/// <param name="v2">The second value.</param>
/// <param name="v3">The third value.</param>
/// <param name="v4">The fourth value.</param>
/// <returns>The formatted value.</returns>
static String Format(const Char* format, const ShaderGraphValue& v1, const ShaderGraphValue& v2, const ShaderGraphValue& v3, const ShaderGraphValue& v4)
{
return String::Format(format, v1.Value, v2.Value, v3.Value, v4.Value);
}
public:
/// <summary>
/// Initializes the shader variable for given connection type Zero.
/// </summary>
/// <param name="type">The graph connection type.</param>
/// <returns>Initial value for given type.</returns>
static ShaderGraphValue InitForZero(VariantType::Types type);
/// <summary>
/// Initializes the shader variable for given connection type Half.
/// </summary>
/// <param name="type">The graph connection type.</param>
/// <returns>Initial value for given type.</returns>
static ShaderGraphValue InitForHalf(VariantType::Types type);
/// <summary>
/// Initializes the shader variable for given connection type One.
/// </summary>
/// <param name="type">The graph connection type.</param>
/// <returns>Initial value for given type.</returns>
static ShaderGraphValue InitForOne(VariantType::Types type);
/// <summary>
/// Create float2 from X and Y values.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <returns>float2</returns>
static ShaderGraphValue Float2(const ShaderGraphValue& x, const ShaderGraphValue& y)
{
return ShaderGraphValue(
VariantType::Types::Vector2,
String::Format(TEXT("float2({0}, {1})"),
Cast(x, VariantType::Types::Float).Value,
Cast(y, VariantType::Types::Float).Value));
}
/// <summary>
/// Create float3 from X, Y and Z values.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
/// <param name="z">The z.</param>
/// <returns>float3</returns>
static ShaderGraphValue Float3(const ShaderGraphValue& x, const ShaderGraphValue& y, const ShaderGraphValue& z)
{
return ShaderGraphValue(
VariantType::Types::Vector3,
String::Format(TEXT("float3({0}, {1}, {2})"),
Cast(x, VariantType::Types::Float).Value,
Cast(y, VariantType::Types::Float).Value,
Cast(z, VariantType::Types::Float).Value));
}
/// <summary>
/// Create float4 from X, Y, Z and W values.
/// </summary>
/// <param name="x">The X.</param>
/// <param name="y">The Y.</param>
/// <param name="z">The Z.</param>
/// <param name="w">The W.</param>
/// <returns>float4</returns>
static ShaderGraphValue Float4(const ShaderGraphValue& x, const ShaderGraphValue& y, const ShaderGraphValue& z, const ShaderGraphValue& w)
{
return ShaderGraphValue(
VariantType::Types::Vector4,
String::Format(TEXT("float4({0}, {1}, {2}, {3})"),
Cast(x, VariantType::Types::Float).Value,
Cast(y, VariantType::Types::Float).Value,
Cast(z, VariantType::Types::Float).Value,
Cast(w, VariantType::Types::Float).Value));
}
public:
/// <summary>
/// Gets the X component of the value. Valid only for single or vector types.
/// </summary>
/// <returns>The X component.</returns>
ShaderGraphValue GetX() const
{
return ShaderGraphValue(VariantType::Types::Float, Value + _subs[0]);
}
/// <summary>
/// Gets the Y component of the value. Valid only for vector types.
/// </summary>
/// <returns>The Y component.</returns>
ShaderGraphValue GetY() const
{
return ShaderGraphValue(VariantType::Types::Float, Value + _subs[1]);
}
/// <summary>
/// Gets the Z component of the value. Valid only for vector types.
/// </summary>
/// <returns>The Z component.</returns>
ShaderGraphValue GetZ() const
{
return ShaderGraphValue(VariantType::Types::Float, Value + _subs[2]);
}
/// <summary>
/// Gets the W component of the value. Valid only for vector types.
/// </summary>
/// <returns>The W component.</returns>
ShaderGraphValue GetW() const
{
return ShaderGraphValue(VariantType::Types::Float, Value + _subs[3]);
}
public:
/// <summary>
/// Casts the value to the bool type.
/// </summary>
/// <returns>Bool</returns>
ShaderGraphValue AsBool() const
{
return Cast(*this, VariantType::Types::Bool);
}
/// <summary>
/// Casts the value to the integer type.
/// </summary>
/// <returns>Integer</returns>
ShaderGraphValue AsInt() const
{
return Cast(*this, VariantType::Types::Int);
}
/// <summary>
/// Casts the value to the unsigned integer type.
/// </summary>
/// <returns>UnsignedInteger</returns>
ShaderGraphValue AsUint() const
{
return Cast(*this, VariantType::Types::Uint);
}
/// <summary>
/// Casts the value to the float type.
/// </summary>
/// <returns>Float</returns>
ShaderGraphValue AsFloat() const
{
return Cast(*this, VariantType::Types::Float);
}
/// <summary>
/// Casts the value to the Vector2 type.
/// </summary>
/// <returns>Vector2</returns>
ShaderGraphValue AsVector2() const
{
return Cast(*this, VariantType::Types::Vector2);
}
/// <summary>
/// Casts the value to the Vector3 type.
/// </summary>
/// <returns>Vector3</returns>
ShaderGraphValue AsVector3() const
{
return Cast(*this, VariantType::Types::Vector3);
}
/// <summary>
/// Casts the value to the Vector4 type.
/// </summary>
/// <returns>Vector4</returns>
ShaderGraphValue AsVector4() const
{
return Cast(*this, VariantType::Types::Vector4);
}
/// <summary>
/// Casts the value from its type to the another type.
/// </summary>
/// <param name="to">The result type.</param>
/// <returns>The result value.</returns>
ShaderGraphValue Cast(const VariantType::Types to) const
{
return Cast(*this, to);
}
/// <summary>
/// Casts the value from its type to the another type.
/// </summary>
/// <param name="v">The value to cast.</param>
/// <param name="to">The result type.</param>
/// <returns>The result value.</returns>
static ShaderGraphValue Cast(const ShaderGraphValue& v, VariantType::Types to);
public:
// [Object]
String ToString() const override
{
return Value;
}
};

View File

@@ -0,0 +1,10 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
using Flax.Build;
/// <summary>
/// Visject Surface module.
/// </summary>
public class Visject : EngineModule
{
}

View File

@@ -0,0 +1,957 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "VisjectGraph.h"
#include "GraphUtilities.h"
#include "Engine/Core/Random.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Core/Math/Transform.h"
#include "Engine/Engine/GameplayGlobals.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Level/Actor.h"
#define RAND Random::Rand()
VisjectExecutor::VisjectExecutor()
{
// Register per group type processing events
// Note: index must match group id
#if BUILD_DEBUG
Platform::MemoryClear(&_perGroupProcessCall, sizeof(_perGroupProcessCall));
#endif
_perGroupProcessCall[2] = &VisjectExecutor::ProcessGroupConstants;
_perGroupProcessCall[3] = &VisjectExecutor::ProcessGroupMath;
_perGroupProcessCall[4] = &VisjectExecutor::ProcessGroupPacking;
_perGroupProcessCall[7] = &VisjectExecutor::ProcessGroupTools;
_perGroupProcessCall[10] = &VisjectExecutor::ProcessGroupBoolean;
_perGroupProcessCall[11] = &VisjectExecutor::ProcessGroupBitwise;
_perGroupProcessCall[12] = &VisjectExecutor::ProcessGroupComparisons;
_perGroupProcessCall[14] = &VisjectExecutor::ProcessGroupParticles;
}
VisjectExecutor::~VisjectExecutor()
{
}
void VisjectExecutor::OnError(Node* node, Box* box, const StringView& message)
{
Error(node, box, message);
}
void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// Constant value
case 1:
case 2:
case 3:
case 12:
value = node->Values[0];
break;
case 4:
{
const Value& cv = node->Values[0];
if (box->ID == 0)
value = cv;
else if (box->ID == 1)
value = cv.AsVector2().X;
else if (box->ID == 2)
value = cv.AsVector2().Y;
break;
}
case 5:
{
const Value& cv = node->Values[0];
if (box->ID == 0)
value = cv;
else if (box->ID == 1)
value = cv.AsVector3().X;
else if (box->ID == 2)
value = cv.AsVector3().Y;
else if (box->ID == 3)
value = cv.AsVector3().Z;
break;
}
case 6:
case 7:
{
const Value& cv = node->Values[0];
if (box->ID == 0)
value = cv;
else if (box->ID == 1)
value = cv.AsVector4().X;
else if (box->ID == 2)
value = cv.AsVector4().Y;
else if (box->ID == 3)
value = cv.AsVector4().Z;
else if (box->ID == 4)
value = cv.AsVector4().W;
break;
}
case 8:
{
const float pitch = (float)node->Values[0];
const float yaw = (float)node->Values[1];
const float roll = (float)node->Values[2];
value = Quaternion::Euler(pitch, yaw, roll);
break;
}
case 9:
{
value = node->Values[0];
break;
}
// PI
case 10:
value = PI;
break;
default:
break;
}
}
void VisjectExecutor::ProcessGroupMath(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// Add, Subtract, Multiply, Divide, Modulo, Max, Min, Pow, Fmod, Atan2
case 1:
case 2:
case 3:
case 4:
case 5:
case 21:
case 22:
case 23:
case 40:
case 41:
{
Value v1 = tryGetValue(node->GetBox(0), 0, Value::Zero);
Value v2 = tryGetValue(node->GetBox(1), 1, Value::Zero).Cast(v1.Type);
GraphUtilities::ApplySomeMathHere(node->TypeID, value, v1, v2);
break;
}
// Absolute Value, Ceil, Cosine, Floor, Round, Saturate, Sine, Sqrt, Tangent, Negate, 1 - Value, Asine, Acosine, Atan, Trunc, Frac, Degrees, Radians
case 7:
case 8:
case 9:
case 10:
case 13:
case 14:
case 15:
case 16:
case 17:
case 27:
case 28:
case 33:
case 34:
case 35:
case 38:
case 39:
case 43:
case 44:
{
Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
GraphUtilities::ApplySomeMathHere(node->TypeID, value, v1);
break;
}
// Length, Normalize
case 11:
case 12:
{
Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
switch (node->TypeID)
{
case 11:
{
switch (v1.Type.Type)
{
case VariantType::Vector2:
value = v1.AsVector2().Length();
break;
case VariantType::Vector3:
value = v1.AsVector3().Length();
break;
case VariantType::Vector4:
value = Vector3(v1.AsVector4()).Length();
break;
default: CRASH;
break;
}
}
case 12:
switch (v1.Type.Type)
{
case VariantType::Int:
value = Math::Saturate(v1.AsInt);
break;
case VariantType::Uint:
value = Math::Saturate(v1.AsUint);
break;
case VariantType::Float:
value = Math::Saturate(v1.AsFloat);
break;
case VariantType::Vector2:
value = Vector2::Normalize(v1.AsVector2());
break;
case VariantType::Vector3:
value = Vector3::Normalize(v1.AsVector3());
break;
case VariantType::Vector4:
value = Vector4(Vector3::Normalize(Vector3(v1.AsVector4())), 0.0f);
break;
default: CRASH;
break;
}
}
break;
}
// Cross, Distance, Dot
case 18:
case 19:
case 20:
{
Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
Value v2 = tryGetValue(node->GetBox(1), Value::Zero).Cast(v1.Type);
switch (node->TypeID)
{
case 18:
switch (v1.Type.Type)
{
case VariantType::Vector3:
value = Vector3::Cross(v1.AsVector3(), v2.AsVector3());
break;
default: CRASH;
break;
}
break;
case 19:
switch (v1.Type.Type)
{
case VariantType::Vector2:
value = Vector2::Distance(v1.AsVector2(), v2.AsVector2());
break;
case VariantType::Vector3:
value = Vector3::Distance(v1.AsVector3(), v2.AsVector3());
break;
case VariantType::Vector4:
case VariantType::Color:
value = Vector3::Distance((Vector3)v1, (Vector3)v2);
break;
default: CRASH;
break;
}
break;
case 20:
switch (v1.Type.Type)
{
case VariantType::Vector2:
value = Vector2::Dot(v1.AsVector2(), v2.AsVector2());
break;
case VariantType::Vector3:
value = Vector3::Dot(v1.AsVector3(), v2.AsVector3());
break;
case VariantType::Vector4:
case VariantType::Color:
value = Vector3::Dot((Vector3)v1, (Vector3)v2);
break;
default: CRASH;
break;
}
break;
}
break;
}
// Clamp
case 24:
{
Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
Value v2 = tryGetValue(node->GetBox(1), 0, Value::Zero).Cast(v1.Type);
Value v3 = tryGetValue(node->GetBox(2), 1, Value::One).Cast(v1.Type);
GraphUtilities::ApplySomeMathHere(value, v1, v2, v3, [](float a, float b, float c)
{
return Math::Clamp(a, b, c);
});
break;
}
// Lerp
case 25:
{
Value a = tryGetValue(node->GetBox(0), 0, Value::Zero);
Value b = tryGetValue(node->GetBox(1), 1, Value::One).Cast(a.Type);
Value alpha = tryGetValue(node->GetBox(2), 2, Value::Zero).Cast(ValueType(ValueType::Float));
value = Value::Lerp(a, b, alpha.AsFloat);
break;
}
// Reflect
case 26:
{
Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
Value v2 = tryGetValue(node->GetBox(1), Value::Zero).Cast(v1.Type);
switch (v1.Type.Type)
{
case VariantType::Vector2:
value = v1.AsVector2() - 2 * v2.AsVector2() * Vector2::Dot(v1.AsVector2(), v2.AsVector2());
break;
case VariantType::Vector3:
value = v1.AsVector3() - 2 * v2.AsVector3() * Vector3::Dot(v1.AsVector3(), v2.AsVector3());
break;
case VariantType::Vector4:
value = Vector4(v1.AsVector4() - 2 * v2.AsVector4() * Vector3::Dot((Vector3)v1, (Vector3)v2));
break;
default: CRASH;
break;
}
break;
}
// Mad
case 31:
{
Value v1 = tryGetValue(node->GetBox(0), Value::Zero);
Value v2 = tryGetValue(node->GetBox(1), 0, Value::One).Cast(v1.Type);
Value v3 = tryGetValue(node->GetBox(2), 1, Value::Zero).Cast(v1.Type);
GraphUtilities::ApplySomeMathHere(value, v1, v2, v3, [](float a, float b, float c)
{
return (a * b) + c;
});
break;
}
// Extract Largest Component
case 32:
{
const auto v1 = (Vector3)tryGetValue(node->GetBox(0), Value::Zero);
value = Math::ExtractLargestComponent(v1);
break;
}
// Bias and Scale
case 36:
{
ASSERT(node->Values.Count() == 2 && node->Values[0].Type == VariantType::Float && node->Values[1].Type == VariantType::Float);
const auto bias = node->Values[0].AsFloat;
const auto scale = node->Values[1].AsFloat;
const auto input = (Vector3)tryGetValue(node->GetBox(0), Value::Zero);
value = (input + bias) * scale;
break;
}
// Rotate About Axis
case 37:
{
const auto normalizedRotationAxis = (Vector3)tryGetValue(node->GetBox(0), Value::Zero);
const auto rotationAngle = (float)tryGetValue(node->GetBox(1), Value::Zero);
const auto pivotPoint = (Vector3)tryGetValue(node->GetBox(2), Value::Zero);
const auto position = (Vector3)tryGetValue(node->GetBox(3), Value::Zero);
value = Math::RotateAboutAxis(normalizedRotationAxis, rotationAngle, pivotPoint, position);
break;
}
// Near Equal
case 42:
{
const Value a = tryGetValue(node->GetBox(0), node->Values[0]);
const Value b = tryGetValue(node->GetBox(1), node->Values[1]).Cast(a.Type);
const float epsilon = (float)tryGetValue(node->GetBox(2), node->Values[2]);
value = Value::NearEqual(a, b, epsilon);
break;
}
// Enum Value
case 45:
value = (uint64)tryGetValue(node->GetBox(0), Value::Zero);
break;
// Enum AND
case 46:
value = tryGetValue(node->GetBox(0), Value::Zero);
if (value.Type.Type == VariantType::Enum)
value.AsUint64 = value.AsUint64 & (uint64)tryGetValue(node->GetBox(1), Value::Zero);
break;
// Enum OR
case 47:
value = tryGetValue(node->GetBox(0), Value::Zero);
if (value.Type.Type == VariantType::Enum)
value.AsUint64 = value.AsUint64 | (uint64)tryGetValue(node->GetBox(1), Value::Zero);
break;
default:
break;
}
}
void VisjectExecutor::ProcessGroupPacking(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// Pack
case 20:
{
float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
value = Vector2(vX, vY);
break;
}
case 21:
{
float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
float vZ = (float)tryGetValue(node->GetBox(3), node->Values[2]);
value = Vector3(vX, vY, vZ);
break;
}
case 22:
{
float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
float vZ = (float)tryGetValue(node->GetBox(3), node->Values[2]);
float vW = (float)tryGetValue(node->GetBox(4), node->Values[3]);
value = Vector4(vX, vY, vZ, vW);
break;
}
case 23:
{
float vX = (float)tryGetValue(node->GetBox(1), node->Values[0]);
float vY = (float)tryGetValue(node->GetBox(2), node->Values[1]);
float vZ = (float)tryGetValue(node->GetBox(3), node->Values[2]);
value = Quaternion::Euler(vX, vY, vZ);
break;
}
case 24:
{
const Vector3 vX = (Vector3)tryGetValue(node->GetBox(1), Vector3::Zero);
const Quaternion vY = (Quaternion)tryGetValue(node->GetBox(2), Quaternion::Identity);
const Vector3 vZ = (Vector3)tryGetValue(node->GetBox(3), Vector3::One);
value = Variant(Transform(vX, vY, vZ));
break;
}
case 25:
{
const Vector3 vX = (Vector3)tryGetValue(node->GetBox(1), Vector3::Zero);
const Vector3 vY = (Vector3)tryGetValue(node->GetBox(2), Vector3::Zero);
value = Variant(BoundingBox(vX, vY));
break;
}
// Unpack
case 30:
{
Vector2 v = (Vector2)tryGetValue(node->GetBox(0), Vector2::Zero);
int32 subIndex = box->ID - 1;
ASSERT(subIndex >= 0 && subIndex < 2);
value = v.Raw[subIndex];
break;
}
case 31:
{
Vector3 v = (Vector3)tryGetValue(node->GetBox(0), Vector3::Zero);
int32 subIndex = box->ID - 1;
ASSERT(subIndex >= 0 && subIndex < 3);
value = v.Raw[subIndex];
break;
}
case 32:
{
Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
int32 subIndex = box->ID - 1;
ASSERT(subIndex >= 0 && subIndex < 4);
value = v.Raw[subIndex];
break;
}
case 33:
{
const Vector3 v = ((Quaternion)tryGetValue(node->GetBox(0), Quaternion::Identity)).GetEuler();
const int32 subIndex = box->ID - 1;
ASSERT(subIndex >= 0 && subIndex < 3);
value = v.Raw[subIndex];
break;
}
case 34:
{
const Transform v = (Transform)tryGetValue(node->GetBox(0), Variant::Zero);
switch (box->ID)
{
case 1:
value = v.Translation;
break;
case 2:
value = v.Orientation;
break;
case 3:
value = v.Scale;
break;
}
break;
}
case 35:
{
const BoundingBox v = (BoundingBox)tryGetValue(node->GetBox(0), Variant::Zero);
switch (box->ID)
{
case 1:
value = v.Minimum;
break;
case 2:
value = v.Maximum;
break;
}
break;
}
// Mask X, Y, Z, W
case 40:
case 41:
case 42:
case 43:
{
const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
value = v.Raw[node->TypeID - 40];
break;
}
// Mask XY, YZ, XZ,...
case 44:
{
value = (Vector2)tryGetValue(node->GetBox(0), Vector2::Zero);
break;
}
case 45:
{
const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
value = Vector2(v.X, v.Z);
break;
}
case 46:
{
const Vector4 v = (Vector4)tryGetValue(node->GetBox(0), Vector4::Zero);
value = Vector2(v.Y, v.Z);
break;
}
// Mask XYZ
case 70:
{
value = (Vector3)tryGetValue(node->GetBox(0), Vector3::Zero);
break;
}
// Append
case 100:
{
auto in0 = node->GetBox(0);
auto in1 = node->GetBox(1);
if (!in0->HasConnection() || !in1->HasConnection())
{
value = Value::Zero;
break;
}
auto value0 = eatBox(in0->GetParent<Node>(), in0->FirstConnection());
auto value1 = eatBox(in1->GetParent<Node>(), in1->FirstConnection());
auto count0 = GraphUtilities::CountComponents(value0.Type.Type);
auto count1 = GraphUtilities::CountComponents(value1.Type.Type);
auto count = count0 + count1;
switch (count)
{
case 1:
value = count0 ? value0 : value1;
break;
case 2:
value = Vector2((float)value0, (float)value1);
break;
case 3:
if (count0 == 1)
value = Vector3((float)value0, value1.AsVector2().X, value1.AsVector2().Y);
else
value = Vector3((Vector2)value0, (float)value1);
break;
case 4:
if (count0 == 1)
value = Vector4((float)value0, value1.AsVector3().X, value1.AsVector3().Y, value1.AsVector3().Z);
else if (count0 == 2)
value = Vector4(value0.AsVector2().X, value0.AsVector2().Y, value1.AsVector2().X, value1.AsVector2().Y);
else
value = Vector4((Vector3)value0, (float)value1);
break;
default:
value = Value::Zero;
break;
}
break;
}
default:
break;
}
}
void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// Color Gradient
case 10:
{
float time, prevTime, curTime;
Color prevColor, curColor;
const int32 count = (int32)node->Values[0];
switch (count)
{
case 0:
// No sample
value = Value::Zero;
break;
case 1:
// Single sample
value = (Color)node->Values[2];
break;
case 2:
// Linear blend between 2 samples
time = (float)tryGetValue(node->GetBox(0), Value::Zero);
prevTime = (float)node->Values[1];
prevColor = (Color)node->Values[2];
curTime = (float)node->Values[3];
curColor = (Color)node->Values[4];
value = Color::Lerp(prevColor, curColor, Math::Saturate((time - prevTime) / (curTime - prevTime)));
break;
default:
time = (float)tryGetValue(node->GetBox(0), Value::Zero);
if (time >= node->Values[1 + count * 2 - 2].AsFloat)
{
// Outside the range
value = (Color)node->Values[1 + count * 2 - 2 + 1];
}
else
{
// Find 2 samples to blend between them
prevTime = (float)node->Values[1];
prevColor = (Color)node->Values[2];
for (int32 i = 1; i < count; i++)
{
curTime = (float)node->Values[i * 2 + 1];
curColor = (Color)node->Values[i * 2 + 2];
if (time <= curTime)
{
value = Color::Lerp(prevColor, curColor, Math::Saturate((time - prevTime) / (curTime - prevTime)));
break;
}
prevTime = curTime;
prevColor = curColor;
}
}
break;
}
break;
}
// Curve
#define SAMPLE_CURVE(id, curves, type, graphType) \
case id: \
{ \
const auto& curve = GetCurrentGraph()->curves[node->Data.Curve.CurveIndex]; \
const float time = (float)tryGetValue(node->GetBox(0), Value::Zero); \
value.Type = VariantType(VariantType::graphType); \
curve.Evaluate(*(type*)value.AsData, time, false); \
break; \
}
SAMPLE_CURVE(12, FloatCurves, float, Float)
SAMPLE_CURVE(13, Vector2Curves, Vector2, Vector2)
SAMPLE_CURVE(14, Vector3Curves, Vector3, Vector3)
SAMPLE_CURVE(15, Vector4Curves, Vector4, Vector4)
#undef SETUP_CURVE
// Get Gameplay Global
case 16:
{
const auto asset = node->Assets[0].As<GameplayGlobals>();
if (asset)
{
const StringView& name = (StringView)node->Values[1];
const auto e = asset->Variables.Find(name);
value = e != asset->Variables.End() ? e->Value.Value : Value::Zero;
}
else
{
value = Value::Zero;
}
break;
}
// Platform Switch
case 17:
{
int32 boxId = 1;
switch (PLATFORM_TYPE)
{
case PlatformType::Windows:
boxId = 2;
break;
case PlatformType::XboxOne:
boxId = 3;
break;
case PlatformType::UWP:
boxId = 4;
break;
case PlatformType::Linux:
boxId = 5;
break;
case PlatformType::PS4:
boxId = 6;
break;
case PlatformType::XboxScarlett:
boxId = 7;
break;
default: ;
}
value = tryGetValue(node->GetBox(node->GetBox(boxId)->HasConnection() ? boxId : 1), Value::Zero);
break;
}
// Asset Reference
case 18:
{
value = Content::LoadAsync<Asset>((Guid)node->Values[0]);
break;
}
// To String
case 20:
{
value.SetString(tryGetValue(node->GetBox(1), Value(StringView::Empty)).ToString());
break;
}
// Actor Reference
case 21:
{
value = Scripting::FindObject<Actor>((Guid)node->Values[0]);
break;
}
default:
break;
}
}
void VisjectExecutor::ProcessGroupBoolean(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// NOT
case 1:
{
const bool a = (bool)tryGetValue(node->GetBox(0), Value::False);
value = !a;
break;
}
// AND, OR, XOR, NOR, NAND
case 2:
case 3:
case 4:
case 5:
case 6:
{
const bool a = (bool)tryGetValue(node->GetBox(0), 0, node->Values[0]);
const bool b = (bool)tryGetValue(node->GetBox(1), 1, node->Values[1]);
bool result = false;
switch (node->TypeID)
{
// AND
case 2:
result = a && b;
break;
// OR
case 3:
result = a || b;
break;
// XOR
case 4:
result = !a != !b;
break;
// NOR
case 5:
result = !(a || b);
break;
// NAND
case 6:
result = !(a && b);
break;
}
value = result;
break;
}
default:
break;
}
}
void VisjectExecutor::ProcessGroupBitwise(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// NOT
case 1:
{
const int32 a = (int32)tryGetValue(node->GetBox(0), Value(0));
value = !a;
break;
}
// AND, OR, XOR
case 2:
case 3:
case 4:
{
const int32 a = (int32)tryGetValue(node->GetBox(0), 0, node->Values[0]);
const int32 b = (int32)tryGetValue(node->GetBox(1), 1, node->Values[1]);
int32 result = 0;
switch (node->TypeID)
{
// AND
case 2:
result = a & b;
break;
// OR
case 3:
result = a | b;
break;
// XOR
case 4:
result = a ^ b;
break;
}
value = result;
break;
}
default:
break;
}
}
void VisjectExecutor::ProcessGroupComparisons(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// ==, !=, >, <=, >=
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
{
const Value a = tryGetValue(node->GetBox(0), 0, node->Values[0]);
const Value b = tryGetValue(node->GetBox(1), 1, node->Values[1]).Cast(a.Type);
bool result = false;
switch (node->TypeID)
{
case 1:
result = a == b;
break;
case 2:
result = a != b;
break;
case 3:
result = a > b;
break;
case 4:
result = a < b;
break;
case 5:
result = a <= b;
break;
case 6:
result = a >= b;
break;
}
value = result;
break;
}
// Switch On Bool
case 7:
{
const Value condition = tryGetValue(node->GetBox(0), Value::False);
if (condition)
value = tryGetValue(node->GetBox(2), 1, Value::Zero);
else
value = tryGetValue(node->GetBox(1), 0, Value::Zero);
break;
}
}
}
void VisjectExecutor::ProcessGroupParticles(Box* box, Node* node, Value& value)
{
switch (node->TypeID)
{
// Random Float
case 208:
{
value = RAND;
break;
}
// Random Vector2
case 209:
{
value = Vector2(RAND, RAND);
break;
}
// Random Vector3
case 210:
{
value = Vector3(RAND, RAND, RAND);
break;
}
// Random Vector4
case 211:
{
value = Vector4(RAND, RAND, RAND, RAND);
break;
}
// Random Float Range
case 213:
{
auto& a = node->Values[0].AsFloat;
auto& b = node->Values[1].AsFloat;
value = Math::Lerp(a, b, RAND);
break;
}
// Random Vector2 Range
case 214:
{
auto a = (Vector2)node->Values[0];
auto b = (Vector2)node->Values[1];
value = Vector2(
Math::Lerp(a.X, b.X, RAND),
Math::Lerp(a.Y, b.Y, RAND)
);
break;
}
// Random Vector3 Range
case 215:
{
auto a = (Vector3)node->Values[0];
auto b = (Vector3)node->Values[1];
value = Vector3(
Math::Lerp(a.X, b.X, RAND),
Math::Lerp(a.Y, b.Y, RAND),
Math::Lerp(a.Z, b.Z, RAND)
);
break;
}
// Random Vector4 Range
case 216:
{
auto a = (Vector4)node->Values[0];
auto b = (Vector4)node->Values[1];
value = Vector4(
Math::Lerp(a.X, b.X, RAND),
Math::Lerp(a.Y, b.Y, RAND),
Math::Lerp(a.Z, b.Z, RAND),
Math::Lerp(a.W, b.W, RAND)
);
break;
}
default:
break;
}
}
VisjectExecutor::Value VisjectExecutor::tryGetValue(Box* box, int32 defaultValueBoxIndex, const Value& defaultValue)
{
const auto parentNode = box->GetParent<Node>();
if (box->HasConnection())
return eatBox(parentNode, box->FirstConnection());
if (parentNode->Values.Count() > defaultValueBoxIndex)
return Value(parentNode->Values[defaultValueBoxIndex]);
return defaultValue;
}
VisjectExecutor::Value VisjectExecutor::tryGetValue(Box* box, const Value& defaultValue)
{
return box && box->HasConnection() ? eatBox(box->GetParent<Node>(), box->FirstConnection()) : defaultValue;
}

View File

@@ -0,0 +1,273 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Graph.h"
#include "Engine/Core/Math/Vector2.h"
#include "Engine/Core/Math/Vector3.h"
#include "Engine/Core/Math/Vector4.h"
#include "Engine/Content/Asset.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/Utilities/AssetsContainer.h"
#include "Engine/Animations/Curve.h"
#define VISJECT_GRAPH_NODE_MAX_ASSETS 14
template<class BoxType>
class VisjectGraphNode;
class VisjectGraphBox : public GraphBox
{
public:
/// <summary>
/// The cached value.
/// </summary>
Variant Cache;
public:
VisjectGraphBox()
: GraphBox()
{
}
VisjectGraphBox(void* parent, byte id, const VariantType::Types type)
: GraphBox(parent, id, type)
{
}
VisjectGraphBox(void* parent, byte id, const VariantType& type)
: GraphBox(parent, id, type)
{
}
FORCE_INLINE VisjectGraphBox* FirstConnection() const
{
return (VisjectGraphBox*)Connections[0];
}
};
template<class BoxType = VisjectGraphBox>
class VisjectGraphNode : public GraphNode<BoxType>
{
public:
struct CurveData
{
/// <summary>
/// The curve index.
/// </summary>
int32 CurveIndex;
};
/// <summary>
/// Custom cached data per node type. Compact to use as small amount of memory as possible.
/// </summary>
struct AdditionalData
{
union
{
CurveData Curve;
struct
{
void* Method;
BinaryModule* Module;
int32 ParamsCount;
uint32 OutParamsMask;
bool IsStatic;
} InvokeMethod;
struct
{
void* Field;
BinaryModule* Module;
bool IsStatic;
} GetSetField;
};
};
public:
VisjectGraphNode()
: GraphNode<BoxType>()
{
}
public:
/// <summary>
/// The custom data (depends on node type). Used to cache data for faster usage at runtime.
/// </summary>
AdditionalData Data;
/// <summary>
/// The asset references. Linked resources such as Animation assets are referenced in graph data as ID. We need to keep valid refs to them at runtime to keep data in memory.
/// </summary>
AssetReference<Asset> Assets[VISJECT_GRAPH_NODE_MAX_ASSETS];
};
/// <summary>
/// Visject graph parameter.
/// </summary>
/// <seealso cref="GraphParameter" />
API_CLASS() class VisjectGraphParameter : public GraphParameter
{
DECLARE_SCRIPTING_TYPE_WITH_CONSTRUCTOR_IMPL(VisjectGraphParameter, GraphParameter);
public:
VisjectGraphParameter(const VisjectGraphParameter& other)
: VisjectGraphParameter()
{
#if !BUILD_RELEASE
CRASH; // Not used
#endif
}
VisjectGraphParameter& operator=(const VisjectGraphParameter& other)
{
#if !BUILD_RELEASE
CRASH; // Not used
#endif
return *this;
}
};
template<class NodeType = VisjectGraphNode<>, class BoxType = VisjectGraphBox, class ParameterType = VisjectGraphParameter>
class VisjectGraph : public Graph<NodeType, BoxType, ParameterType>
{
public:
typedef Variant Value;
typedef VariantType::Types ValueType;
typedef Graph<NodeType, BoxType, ParameterType> Base;
public:
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<float>> FloatCurves;
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<Vector2>> Vector2Curves;
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<Vector3>> Vector3Curves;
/// <summary>
/// The float curves used by the graph.
/// </summary>
Array<BezierCurve<Vector4>> Vector4Curves;
public:
// [Graph]
bool onNodeLoaded(NodeType* n) override
{
switch (n->GroupID)
{
// Tools
case 7:
switch (n->TypeID)
{
// Curves
#define SETUP_CURVE(id, curves, access) \
case id: \
{ \
n->Data.Curve.CurveIndex = curves.Count(); \
auto& curve = curves.AddOne(); \
const int32 keyframesCount = n->Values[0].AsInt; \
auto& keyframes = curve.GetKeyframes(); \
keyframes.Resize(keyframesCount); \
for (int32 i = 0; i < keyframesCount; i++) \
{ \
const int32 idx = i * 4; \
auto& keyframe = keyframes[i]; \
keyframe.Time = n->Values[idx + 1].AsFloat; \
keyframe.Value = n->Values[idx + 2].access; \
keyframe.TangentIn = n->Values[idx + 3].access; \
keyframe.TangentOut = n->Values[idx + 4].access; \
} \
break; \
}
SETUP_CURVE(12, FloatCurves, AsFloat)
SETUP_CURVE(13, Vector2Curves, AsVector2())
SETUP_CURVE(14, Vector3Curves, AsVector3())
SETUP_CURVE(15, Vector4Curves, AsVector4())
#undef SETUP_CURVE
// Get Gameplay Global
case 16:
{
n->Assets[0] = Content::LoadAsync<Asset>((Guid)n->Values[0]);
break;
}
}
}
// Base
return Base::onNodeLoaded(n);
}
};
/// <summary>
/// Visject Surface graph executor at runtime.
/// </summary>
class VisjectExecutor
{
public:
typedef VisjectGraph<> Graph;
typedef VisjectGraph<>::Node Node;
typedef VisjectGraph<>::Box Box;
typedef VisjectGraph<>::Parameter Parameter;
typedef Variant Value;
typedef VariantType ValueType;
typedef Delegate<Node*, Box*, const StringView&> ErrorHandler;
typedef void (VisjectExecutor::*ProcessBoxHandler)(Box*, Node*, Value&);
protected:
ProcessBoxHandler _perGroupProcessCall[18];
public:
/// <summary>
/// Initializes a new instance of the <see cref="VisjectExecutor"/> class.
/// </summary>
VisjectExecutor();
/// <summary>
/// Finalizes an instance of the <see cref="VisjectExecutor"/> class.
/// </summary>
~VisjectExecutor();
public:
ErrorHandler Error;
public:
void OnError(Node* node, Box* box, const StringView& message);
void ProcessGroupConstants(Box* box, Node* node, Value& value);
void ProcessGroupMath(Box* box, Node* node, Value& value);
void ProcessGroupPacking(Box* box, Node* node, Value& value);
void ProcessGroupTools(Box* box, Node* node, Value& value);
void ProcessGroupBoolean(Box* box, Node* node, Value& value);
void ProcessGroupBitwise(Box* box, Node* node, Value& value);
void ProcessGroupComparisons(Box* box, Node* node, Value& value);
void ProcessGroupParticles(Box* box, Node* node, Value& value);
protected:
virtual Value eatBox(Node* caller, Box* box) = 0;
virtual Graph* GetCurrentGraph() const = 0;
Value tryGetValue(Box* box, int32 defaultValueBoxIndex, const Value& defaultValue);
Value tryGetValue(Box* box, const Value& defaultValue);
};

View File

@@ -0,0 +1,110 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "VisjectMeta.h"
#include "Engine/Core/Types/DateTime.h"
#include "Engine/Serialization/ReadStream.h"
#include "Engine/Serialization/WriteStream.h"
VisjectMeta::VisjectMeta()
{
}
bool VisjectMeta::Load(ReadStream* stream, bool loadData)
{
Release();
int32 entries;
stream->ReadInt32(&entries);
Entries.Resize(entries);
for (int32 i = 0; i < entries; i++)
{
Entry& e = Entries[i];
stream->ReadInt32(&e.TypeID);
DateTime creationTime;
stream->Read(&creationTime);
uint32 dataSize;
stream->ReadUint32(&dataSize);
e.IsLoaded = loadData;
if (loadData)
{
e.Data.Resize(dataSize, false);
if (dataSize > 0)
{
stream->ReadBytes(e.Data.Get(), dataSize);
}
}
else
{
e.Data.SetCapacity(0);
stream->SetPosition(stream->GetPosition() + dataSize);
}
}
return false;
}
bool VisjectMeta::Save(WriteStream* stream, bool saveData) const
{
stream->WriteInt32(Entries.Count());
for (int32 i = 0; i < Entries.Count(); i++)
{
const Entry& e = Entries[i];
stream->WriteInt32(e.TypeID);
stream->WriteInt64(0); // unused creation time
const uint32 dataSize = e.IsLoaded && saveData ? e.Data.Count() : 0;
stream->WriteUint32(dataSize);
if (dataSize > 0)
{
stream->WriteBytes(e.Data.Get(), dataSize);
}
}
return false;
}
void VisjectMeta::Release()
{
Entries.Clear();
}
const VisjectMeta::Entry* VisjectMeta::GetEntry(int32 typeID) const
{
const Entry* result = nullptr;
for (const Entry& e : Entries)
{
if (e.TypeID == typeID)
{
result = &e;
break;
}
}
return result;
}
VisjectMeta::Entry* VisjectMeta::GetEntry(int32 typeID)
{
Entry* result = nullptr;
for (Entry& e : Entries)
{
if (e.TypeID == typeID)
{
result = &e;
break;
}
}
return result;
}
void VisjectMeta::AddEntry(int32 typeID, byte* data, int32 size)
{
auto& e = Entries.AddOne();
e.IsLoaded = true;
e.TypeID = typeID;
e.Data.Set(data, size);
}

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Collections/Array.h"
#include "Engine/Serialization/Stream.h"
/// <summary>
/// Visject metadata container
/// </summary>
class VisjectMeta
{
public:
/// <summary>
/// Metadata entry
/// </summary>
struct Entry
{
int32 TypeID;
bool IsLoaded;
Array<byte> Data;
};
public:
/// <summary>
/// All meta entries
/// </summary>
Array<Entry, FixedAllocation<8>> Entries;
public:
/// <summary>
/// Initializes a new instance of the <see cref="VisjectMeta"/> class.
/// </summary>
VisjectMeta();
/// <summary>
/// Finalizes an instance of the <see cref="VisjectMeta"/> class.
/// </summary>
~VisjectMeta()
{
}
public:
/// <summary>
/// Load from the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="loadData">True if load meta data</param>
/// <returns>True if cannot load data</returns>
bool Load(ReadStream* stream, bool loadData);
/// <summary>
/// Save to the stream
/// </summary>
/// <param name="stream">Stream</param>
/// <param name="saveData">True if load meta data</param>
/// <returns>True if cannot save data</returns>
bool Save(WriteStream* stream, bool saveData) const;
/// <summary>
/// Release meta data
/// </summary>
void Release();
/// <summary>
/// Get entry
/// </summary>
/// <param name="typeID">Entry type ID</param>
/// <returns>Entry</returns>
const Entry* GetEntry(int32 typeID) const;
/// <summary>
/// Get entry
/// </summary>
/// <param name="typeID">Entry type ID</param>
/// <returns>Entry</returns>
Entry* GetEntry(int32 typeID);
/// <summary>
/// Add new entry
/// </summary>
/// <param name="typeID">Type ID</param>
/// <param name="data">Bytes to set</param>
/// <param name="size">Amount of bytes to assign</param>
void AddEntry(int32 typeID, byte* data, int32 size);
};