240 lines
6.6 KiB
C++
240 lines
6.6 KiB
C++
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
#if COMPILE_WITH_MATERIAL_GRAPH
|
|
|
|
#include "MaterialLayer.h"
|
|
#include "MaterialGenerator.h"
|
|
|
|
MaterialLayer::MaterialLayer(const Guid& id)
|
|
: ID(id)
|
|
, Root(nullptr)
|
|
, FeaturesFlags(MaterialFeaturesFlags::None)
|
|
, UsageFlags(MaterialUsageFlags::None)
|
|
, Domain(MaterialDomain::Surface)
|
|
, BlendMode(MaterialBlendMode::Opaque)
|
|
, ShadingModel(MaterialShadingModel::Lit)
|
|
, MaskThreshold(0.3f)
|
|
, OpacityThreshold(0.12f)
|
|
, ParamIdsMappings(8)
|
|
{
|
|
ASSERT(ID.IsValid());
|
|
}
|
|
|
|
void MaterialLayer::ClearCache()
|
|
{
|
|
for (int32 i = 0; i < Graph.Nodes.Count(); i++)
|
|
{
|
|
auto& node = Graph.Nodes[i];
|
|
for (int32 j = 0; j < node.Boxes.Count(); j++)
|
|
{
|
|
node.Boxes[j].Cache.Clear();
|
|
}
|
|
}
|
|
for (int32 i = 0; i < ARRAY_COUNT(Usage); i++)
|
|
{
|
|
Usage[i].VarName.Clear();
|
|
Usage[i].Hint = nullptr;
|
|
}
|
|
}
|
|
|
|
void MaterialLayer::Prepare()
|
|
{
|
|
// Clear cached data
|
|
ClearCache();
|
|
|
|
// Ensure to have root node set
|
|
if (Root == nullptr)
|
|
{
|
|
for (int32 i = 0; i < Graph.Nodes.Count(); i++)
|
|
{
|
|
const auto node = &Graph.Nodes[i];
|
|
if (node->Type == ROOT_NODE_TYPE)
|
|
{
|
|
Root = node;
|
|
break;
|
|
}
|
|
}
|
|
if (Root == nullptr)
|
|
{
|
|
// Ensure to have root node
|
|
createRootNode();
|
|
}
|
|
}
|
|
}
|
|
|
|
Guid MaterialLayer::GetMappedParamId(const Guid& id)
|
|
{
|
|
// TODO: test ParamIdsMappings using Dictionary. will performance change? maybe we don't wont to allocate too much memory
|
|
|
|
for (int32 i = 0; i < ParamIdsMappings.Count(); i++)
|
|
{
|
|
if (ParamIdsMappings[i].SrcId == id)
|
|
return ParamIdsMappings[i].DstId;
|
|
}
|
|
|
|
return Guid::Empty;
|
|
}
|
|
|
|
String& MaterialLayer::GetVariableName(void* hint)
|
|
{
|
|
if (hint == nullptr)
|
|
{
|
|
return Usage[0].VarName;
|
|
}
|
|
|
|
for (int32 i = 1; i < ARRAY_COUNT(Usage); i++)
|
|
{
|
|
if (Usage[i].Hint == nullptr)
|
|
{
|
|
Usage[i].Hint = hint;
|
|
}
|
|
|
|
if (Usage[i].Hint == hint)
|
|
{
|
|
return Usage[i].VarName;
|
|
}
|
|
}
|
|
|
|
LOG(Error, "Too many layer samples per material! Layer {0}", ID);
|
|
return Usage[0].VarName;
|
|
}
|
|
|
|
bool MaterialLayer::HasAnyVariableName()
|
|
{
|
|
for (int32 i = 1; i < ARRAY_COUNT(Usage); i++)
|
|
{
|
|
if (Usage[i].Hint || Usage[i].VarName.HasChars())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void MaterialLayer::UpdateFeaturesFlags()
|
|
{
|
|
ASSERT(Root);
|
|
|
|
// Gather the material features usage
|
|
UsageFlags = MaterialUsageFlags::None;
|
|
#define CHECK_BOX_AS_FEATURE(box, feature) \
|
|
if (Root->GetBox(static_cast<int32>(MaterialGraphBoxes::box))->HasConnection()) \
|
|
UsageFlags |= MaterialUsageFlags::feature; \
|
|
else \
|
|
UsageFlags &= ~MaterialUsageFlags::feature;
|
|
CHECK_BOX_AS_FEATURE(Emissive, UseEmissive);
|
|
CHECK_BOX_AS_FEATURE(Normal, UseNormal);
|
|
CHECK_BOX_AS_FEATURE(Mask, UseMask);
|
|
CHECK_BOX_AS_FEATURE(PositionOffset, UsePositionOffset);
|
|
CHECK_BOX_AS_FEATURE(WorldDisplacement, UseDisplacement);
|
|
CHECK_BOX_AS_FEATURE(Refraction, UseRefraction);
|
|
#undef CHECK_BOX_AS_FEATURE
|
|
}
|
|
|
|
MaterialLayer* MaterialLayer::CreateDefault(const Guid& id)
|
|
{
|
|
// Create new layer object
|
|
auto layer = New<MaterialLayer>(id);
|
|
|
|
// Create default root node
|
|
layer->createRootNode();
|
|
|
|
return layer;
|
|
}
|
|
|
|
MaterialLayer* MaterialLayer::Load(const Guid& id, ReadStream* graphData, const MaterialInfo& info, const String& caller)
|
|
{
|
|
// Create new layer object
|
|
auto layer = New<MaterialLayer>(id);
|
|
|
|
// Load graph
|
|
if (layer->Graph.Load(graphData, false))
|
|
{
|
|
LOG(Warning, "Cannot load graph '{0}'.", caller);
|
|
}
|
|
|
|
// Find root node
|
|
for (int32 i = 0; i < layer->Graph.Nodes.Count(); i++)
|
|
{
|
|
if (layer->Graph.Nodes[i].Type == ROOT_NODE_TYPE)
|
|
{
|
|
layer->Root = &layer->Graph.Nodes[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Ensure to have root node
|
|
if (layer->Root == nullptr)
|
|
{
|
|
LOG(Warning, "Missing root node in '{0}'.", caller);
|
|
layer->createRootNode();
|
|
}
|
|
// Ensure to have valid root node
|
|
else if (layer->Root->Boxes.Count() < static_cast<int32>(MaterialGraphBoxes::MAX))
|
|
{
|
|
#define ADD_BOX(type, valueType) \
|
|
if (layer->Root->Boxes.Count() <= static_cast<int32>(MaterialGraphBoxes::type)) \
|
|
layer->Root->Boxes.Add(MaterialGraphBox(layer->Root, static_cast<int32>(MaterialGraphBoxes::type), VariantType::valueType))
|
|
ADD_BOX(TessellationMultiplier, Float);
|
|
ADD_BOX(WorldDisplacement, Float3);
|
|
ADD_BOX(SubsurfaceColor, Float3);
|
|
static_assert(static_cast<int32>(MaterialGraphBoxes::MAX) == 15, "Invalid amount of boxes added for root node. Please update the code above");
|
|
ASSERT(layer->Root->Boxes.Count() == static_cast<int32>(MaterialGraphBoxes::MAX));
|
|
#if BUILD_DEBUG
|
|
// Test for valid pointers after node upgrade
|
|
for (int32 i = 0; i < layer->Root->Boxes.Count(); i++)
|
|
{
|
|
if (layer->Root->Boxes[i].Parent != layer->Root)
|
|
{
|
|
CRASH;
|
|
}
|
|
}
|
|
#endif
|
|
#undef ADD_BOX
|
|
}
|
|
|
|
// Setup
|
|
layer->FeaturesFlags = info.FeaturesFlags;
|
|
layer->UsageFlags = info.UsageFlags;
|
|
layer->Domain = info.Domain;
|
|
layer->BlendMode = info.BlendMode;
|
|
layer->ShadingModel = info.ShadingModel;
|
|
layer->MaskThreshold = info.MaskThreshold;
|
|
layer->OpacityThreshold = info.OpacityThreshold;
|
|
|
|
return layer;
|
|
}
|
|
|
|
void MaterialLayer::createRootNode()
|
|
{
|
|
// Create node
|
|
auto& rootNode = Graph.Nodes.AddOne();
|
|
rootNode.ID = 1;
|
|
rootNode.Type = ROOT_NODE_TYPE;
|
|
rootNode.Boxes.Resize(static_cast<int32>(MaterialGraphBoxes::MAX));
|
|
#define INIT_BOX(type, valueType) rootNode.Boxes[static_cast<int32>(MaterialGraphBoxes::type)] = MaterialGraphBox(&rootNode, static_cast<int32>(MaterialGraphBoxes::type), VariantType::valueType)
|
|
INIT_BOX(Layer, Void);
|
|
INIT_BOX(Color, Float3);
|
|
INIT_BOX(Mask, Float);
|
|
INIT_BOX(Emissive, Float3);
|
|
INIT_BOX(Metalness, Float);
|
|
INIT_BOX(Specular, Float);
|
|
INIT_BOX(Roughness, Float);
|
|
INIT_BOX(AmbientOcclusion, Float);
|
|
INIT_BOX(Normal, Float3);
|
|
INIT_BOX(Opacity, Float);
|
|
INIT_BOX(Refraction, Float);
|
|
INIT_BOX(PositionOffset, Float3);
|
|
INIT_BOX(TessellationMultiplier, Float);
|
|
INIT_BOX(WorldDisplacement, Float3);
|
|
INIT_BOX(SubsurfaceColor, Float3);
|
|
static_assert(static_cast<int32>(MaterialGraphBoxes::MAX) == 15, "Invalid amount of boxes created for root node. Please update the code above");
|
|
#undef INIT_BOX
|
|
|
|
// Mark as root
|
|
Root = &rootNode;
|
|
}
|
|
|
|
#endif
|