Custom Domain Example
Overview
This example demonstrates how to create a custom "Audio" graph domain with its own output node. The domain registers shared Math/Value nodes plus domain-specific audio nodes.
Files
AudioDomain.h
#pragma once
#include "NodeGraph/GraphDomain.h"
class AudioDomain : public GraphDomain
{
public:
virtual const char* GetDomainName() const override { return "Audio"; }
virtual void RegisterNodeTypes() override;
virtual void OnGraphEvaluated(NodeGraph* graph) override;
virtual TypeId GetDefaultOutputNodeType() const override;
};
AudioDomain.cpp
#include "NodeGraph/Domains/AudioDomain.h"
#include "NodeGraph/GraphNode.h"
#include "NodeGraph/NodeGraph.h"
#include "NodeGraph/Nodes/MathNodes.h"
#include "NodeGraph/Nodes/ValueNodes.h"
#include "NodeGraph/Nodes/AudioNodes.h"
void AudioDomain::RegisterNodeTypes()
{
// Shared math nodes
AddNodeType(AddNode::GetStaticType(), "Add", "Math", glm::vec4(0.4f, 0.6f, 0.2f, 1.0f));
AddNodeType(MultiplyNode::GetStaticType(), "Multiply", "Math", glm::vec4(0.4f, 0.6f, 0.2f, 1.0f));
AddNodeType(LerpNode::GetStaticType(), "Lerp", "Math", glm::vec4(0.4f, 0.6f, 0.2f, 1.0f));
AddNodeType(ClampNode::GetStaticType(), "Clamp", "Math", glm::vec4(0.4f, 0.6f, 0.2f, 1.0f));
// Shared value nodes
AddNodeType(FloatConstantNode::GetStaticType(), "Float", "Value", glm::vec4(0.2f, 0.5f, 0.7f, 1.0f));
AddNodeType(TimeNode::GetStaticType(), "Time", "Value", glm::vec4(0.2f, 0.5f, 0.7f, 1.0f));
AddNodeType(ViewerNode::GetStaticType(), "Viewer", "Utility", glm::vec4(0.2f, 0.6f, 0.3f, 1.0f));
// Audio-specific nodes
AddNodeType(AudioOutputNode::GetStaticType(), "Audio Output", "Audio", glm::vec4(0.9f, 0.6f, 0.1f, 1.0f));
AddNodeType(OscillatorNode::GetStaticType(), "Oscillator", "Audio", glm::vec4(0.9f, 0.6f, 0.1f, 1.0f));
AddNodeType(AudioMixerNode::GetStaticType(), "Mixer", "Audio", glm::vec4(0.9f, 0.6f, 0.1f, 1.0f));
}
TypeId AudioDomain::GetDefaultOutputNodeType() const
{
return AudioOutputNode::GetStaticType();
}
void AudioDomain::OnGraphEvaluated(NodeGraph* graph)
{
// Find AudioOutputNode and read its input values
for (uint32_t i = 0; i < graph->GetNumNodes(); ++i)
{
GraphNode* node = graph->GetNodes()[i];
if (node->GetType() == AudioOutputNode::GetStaticType())
{
float volume = node->GetInputValue(0).GetFloat();
float pitch = node->GetInputValue(1).GetFloat();
// Apply to the audio system...
break;
}
}
}
AudioNodes.h
#pragma once
#include "NodeGraph/GraphNode.h"
class AudioOutputNode : public GraphNode
{
public:
DECLARE_GRAPH_NODE(AudioOutputNode, GraphNode);
virtual void SetupPins() override;
virtual void Evaluate() override;
virtual const char* GetNodeTypeName() const override { return "Audio Output"; }
virtual const char* GetNodeCategory() const override { return "Audio"; }
virtual glm::vec4 GetNodeColor() const override;
};
class OscillatorNode : public GraphNode
{
public:
DECLARE_GRAPH_NODE(OscillatorNode, GraphNode);
virtual void SetupPins() override;
virtual void Evaluate() override;
virtual const char* GetNodeTypeName() const override { return "Oscillator"; }
virtual const char* GetNodeCategory() const override { return "Audio"; }
virtual glm::vec4 GetNodeColor() const override;
};
class AudioMixerNode : public GraphNode
{
public:
DECLARE_GRAPH_NODE(AudioMixerNode, GraphNode);
virtual void SetupPins() override;
virtual void Evaluate() override;
virtual const char* GetNodeTypeName() const override { return "Mixer"; }
virtual const char* GetNodeCategory() const override { return "Audio"; }
virtual glm::vec4 GetNodeColor() const override;
};
AudioNodes.cpp
#include "NodeGraph/Nodes/AudioNodes.h"
#include "Utilities.h"
FORCE_LINK_DEF(AudioNodes);
static const glm::vec4 kAudioNodeColor = glm::vec4(0.9f, 0.6f, 0.1f, 1.0f);
// AudioOutputNode
DEFINE_GRAPH_NODE(AudioOutputNode);
void AudioOutputNode::SetupPins()
{
AddInputPin("Volume", DatumType::Float, Datum(1.0f));
AddInputPin("Pitch", DatumType::Float, Datum(1.0f));
AddInputPin("Pan", DatumType::Float, Datum(0.0f));
}
void AudioOutputNode::Evaluate() {}
glm::vec4 AudioOutputNode::GetNodeColor() const { return kAudioNodeColor; }
// OscillatorNode
DEFINE_GRAPH_NODE(OscillatorNode);
void OscillatorNode::SetupPins()
{
AddInputPin("Frequency", DatumType::Float, Datum(440.0f));
AddInputPin("Amplitude", DatumType::Float, Datum(1.0f));
AddInputPin("Time", DatumType::Float, Datum(0.0f));
AddOutputPin("Signal", DatumType::Float);
}
void OscillatorNode::Evaluate()
{
float freq = GetInputValue(0).GetFloat();
float amp = GetInputValue(1).GetFloat();
float time = GetInputValue(2).GetFloat();
float signal = amp * glm::sin(freq * time * 6.28318f);
SetOutputValue(0, Datum(signal));
}
glm::vec4 OscillatorNode::GetNodeColor() const { return kAudioNodeColor; }
// AudioMixerNode
DEFINE_GRAPH_NODE(AudioMixerNode);
void AudioMixerNode::SetupPins()
{
AddInputPin("Signal A", DatumType::Float, Datum(0.0f));
AddInputPin("Signal B", DatumType::Float, Datum(0.0f));
AddInputPin("Mix", DatumType::Float, Datum(0.5f));
AddOutputPin("Output", DatumType::Float);
}
void AudioMixerNode::Evaluate()
{
float a = GetInputValue(0).GetFloat();
float b = GetInputValue(1).GetFloat();
float mix = glm::clamp(GetInputValue(2).GetFloat(), 0.0f, 1.0f);
SetOutputValue(0, Datum(glm::mix(a, b, mix)));
}
glm::vec4 AudioMixerNode::GetNodeColor() const { return kAudioNodeColor; }
Engine.cpp Integration
#include "NodeGraph/Domains/AudioDomain.h"
// In ForceLinkage():
FORCE_LINK_CALL(AudioNodes);
// In engine initialization (before ProcessExternalRegistrations):
GraphDomainManager::Get()->RegisterDomain(new AudioDomain());
API Reference
GraphDomain Virtual Methods
| Method | Description |
|---|---|
GetDomainName() |
Returns the domain's unique name string |
RegisterNodeTypes() |
Registers all node types available in this domain |
GetDefaultOutputNodeType() |
Returns the TypeId of the default output node |
OnGraphEvaluated(NodeGraph*) |
Called after graph evaluation completes |
GraphDomain::AddNodeType(typeId, typeName, category, color)
Registers a node type with the domain. Duplicates are automatically skipped.
Best Practices
- Register the domain before calling
ProcessExternalRegistrations(). - Always override
GetDefaultOutputNodeType()so new graphs auto-create an output node. - Include shared Math/Value nodes that make sense for your domain.
- Use
OnGraphEvaluated()to bridge node graph results to your engine systems.