Infinity Engine v0.6.20
C++ API Documentation
Loading...
Searching...
No Matches
Creating Your First Procedural Component

ProceduralComponents are the building blocks of generation systems in the Infinity Engine. This tutorial will guide you through creating a complete custom component from initial setup to integration with the Infinity Creator.

What We'll Build

We'll create a Deformable Sphere component that:

  • Generates a UV sphere mesh with configurable subdivisions
  • Applies noise-based deformation for organic variation
  • Demonstrates input/output handling, randomization, and mesh generation
  • Can be used in visual systems within the Creator

Prerequisites

Step 1: Generate Component Template

The Infinity SDK includes a powerful infinity_tool utility that automatically generates all the necessary files for a new component. This saves time and ensures proper structure.

Additionally, the Infinity Creator makes creating new components easy. It has a built-in UI that calls the infinity_tool to help create components easily.

Using the Infinity Creator to create Procedural Components

  1. Open the Infinity Creator
  2. Navigate to Component Menu
    • Click "Component" in the menu bar
    • Select "Create Procedural Component..."
Click 'Create Procedural Component...' in the Components menu

Fill out the relevant UI fields as follows:

  • Name: "Deformable Sphere"
  • Group: "Tutorial.Components"
  • Class Name: "DeformableSphere"
  • Path: "path/to/MyDeformableSphere" <– Set this to a folder you want the development files to be generated

Click Create.

Using infinity_tool to create Procedural Components

Open a terminal/command prompt and run:

# Create component in a new directory
infinity_tool component create -n "Deformable Sphere" -g "Tutorial.Components" -c "DeformableSphere" -p "path/to/MyDeformableSphere"
# On Windows:
infinity_tool.exe component create -n "Deformable Sphere" -g "Tutorial.Components" -c "DeformableSphere" -p "C:\MyProjects\MyDeformableSphere"

Set the path argument (-p) to a folder you want the development files to be generated.

Parameters:

  • -n : Component name (e.g., "Deformable Sphere", "Terrain Generator")
  • -g : Group namespace (e.g., "Tutorial.Components", "MyCompany.Tools")
  • -c : Component classname (e.g., "DeformableSphere", "TerrainGenerator"). Should have no spaces, or special symbols.
  • -p : Path to create the component (directory will be created if it doesn't exist)

Generated Project Structure

The tool creates this complete project structure:

MyDeformableSphere/
├── CMakeLists.txt # Main build configuration
├── DeformableSphere.yaml # Component metadata and interface
├── PluginManifest.yaml # Plugin manifest (for multi-component plugins)
├── DeformableSphere_icon.svg # Default component icon
├── README.md # Basic documentation
├── src/
│ ├── DeformableSphere.hpp # Component header
│ └── DeformableSphere.cpp # Component implementation
└── test/
├── CMakeLists.txt # Test build configuration
├── DeformableSphereTestCase.hpp # Test case header
├── DeformableSphereTestCase.cpp # Test case implementation
└── main.cpp # Test runner

All files are generated with proper boilerplate code, saving significant setup time!

Step 2: Understanding Generated Files

Generated CMakeLists.txt

The generated CMakeLists.txt provides a complete, professional build configuration:

cmake_minimum_required(VERSION 3.23 FATAL_ERROR)
project(ComponentTemplate
VERSION 1.0.0
LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
find_package(infinity REQUIRED)
include(GNUInstallDirs)
# Platform-specific optimizations
if(WIN32)
set(CMAKE_CXX_FLAGS "/DWIN32 /D_WINDOWS /EHsc /MP /Zc:__cplusplus")
# Debug symbols configuration for easier debugging...
endif(WIN32)
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -O3")
endif(APPLE)
# Auto-generate plugin registration code
find_package(infinity_tool REQUIRED)
get_target_property(infinity_tool_binary Infinity::infinity_tool LOCATION)
execute_process(
COMMAND ${infinity_tool_binary} plugin generate-code
${CMAKE_CURRENT_SOURCE_DIR}/PluginManifest.yaml
ComponentTemplate
${CMAKE_CURRENT_SOURCE_DIR}/src/plugin
)
# Component library with auto-generated plugin code
add_library(ComponentTemplate SHARED
src/ComponentTemplate.cpp
src/plugin/Plugin_generated.cpp # Auto-generated registration
)
target_link_libraries(ComponentTemplate
PRIVATE Infinity::infinity
)
# Complete installation setup for distribution
install(FILES ComponentTemplate.yaml PluginManifest.yaml
DESTINATION ComponentTemplate/${CMAKE_INSTALL_DATAROOTDIR})
install(TARGETS ComponentTemplate
RUNTIME DESTINATION ComponentTemplate/${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ComponentTemplate/${CMAKE_INSTALL_LIBDIR})

Key Features:

  • Auto-generated plugin code: Handles component registration automatically
  • Cross-platform optimization: Windows, macOS, and Linux specific settings
  • Professional installation: Proper CMake installation targets
  • Debug support: PDB files on Windows, optimized debug symbols

Understanding Auto-Generated Plugin Code

The infinity_tool automatically generates plugin registration code in src/plugin/Plugin_generated.cpp and src/plugin/Plugin_generated.hpp. This code handles:

  • Component registration with the Infinity Engine component registry
  • Plugin lifecycle management (load/unload)
  • Metadata integration from your YAML files
  • Cross-language bindings (when C# support is enabled)

You don't need to write any registration code manually - the tool handles everything based on your PluginManifest.yaml and component YAML files.

Generated Component Interface

The DeformableSphere.yaml file contains the basic component interface that you'll customize:

---
name: Deformable Sphere
group: Tutorial.Components
author:
email:
version: [1, 0, 0]
description: "Deformable Sphere component description"
plugin-name: Tutorial.Components.DeformableSphere
defaultExecutionPolicy: MultiThreaded
in:
- name: Input1
type: float
description: Example input parameter
out:
- name: Output1
type: float
description: Example output parameter

Step 3: Customize Component Metadata

Update the generated DeformableSphere.yaml with our specific interface:

---
name: Deformable Sphere
group: Tutorial.Components
author: Your Name
email: your.email@domain.com
version: [1, 0, 0]
description: "Generates a deformable sphere with noise-based surface variation"
plugin-name: Tutorial.Components.DeformableSphere
defaultExecutionPolicy: MultiThreaded
in:
- name: Subdivisions
type: int
default: 4
description: Number of subdivisions for sphere tessellation (higher = smoother)
- name: Scale
type: float
default: 1.0
description: Base scale of the sphere
- name: DeformStrength
type: float
default: 0.2
description: Intensity of noise-based deformation (0.0 = none, 1.0 = strong)
- name: NoiseFrequency
type: float
default: 2.0
description: Frequency of deformation noise pattern
out:
- name: Mesh
type: Mesh
description: Generated deformable sphere mesh
- name: VertexCount
type: int
description: Number of vertices in the generated mesh

Step 4: Implement Component Logic

Update the Generated Header

The generated src/DeformableSphere.hpp contains basic structure. Update it with our specific methods:

#pragma once
#include <Infinity/Procedural/ProceduralComponent.hpp>
#include <Infinity/Procedural/ProceduralComponentDescription.hpp>
#include <Infinity/Types/Rendering/Mesh.hpp>
#include <Infinity/Types/Math/Vector3.hpp>
#include <Infinity/Types/Containers/Array.hpp>
#include <Infinity/Engine/PRNGDistribution.hpp>
class INFINITY_API_PLUGIN DeformableSphere : public Infinity::Procedural::ProceduralComponent
{
public:
// Main component constructor - required signature
DeformableSphere(const Infinity::Procedural::ProceduralComponentDescription& description);
// Virtual destructor
~DeformableSphere();
// Main execution method - implement your logic here
void execute() override;
private:
// Helper methods for sphere generation
void generateBaseSphere(int subdivisions, float scale,
void applyNoiseDeformation(Infinity::Types::Rendering::Mesh& mesh,
float strength, float frequency);
void recalculateNormals(Infinity::Types::Rendering::Mesh& mesh);
};
Description and configuration data for a ProceduralComponent instance.
Definition ProceduralComponentDescription.hpp:48
Base class for procedural generation components.
Definition ProceduralComponent.hpp:73
virtual void execute()=0
Executes the component's procedural generation logic.
Complete 3D geometry definition with vertices, indices, and material partitioning.
Definition Mesh.hpp:135

Step 4: Component Implementation

src/DeformableSphere.cpp

#include "DeformableSphere.hpp"
#include <Infinity/Types/All.hpp>
#include <Infinity/Engine/PRNGDistribution.hpp>
#include <cmath>
#include <algorithm>
using namespace Infinity::Procedural;
using namespace Infinity::Types::Math;
using namespace Infinity::Engine;
DeformableSphere::DeformableSphere(const ProceduralComponentDescription& description)
: ProceduralComponent(description)
{
// Component initialization - runs once when component is created
logInfo("DeformableSphere component initialized");
}
DeformableSphere::~DeformableSphere()
{
// Cleanup code if needed
}
void DeformableSphere::execute()
{
// Read input parameters
int subdivisions = getIn<int>("Subdivisions");
float scale = getIn<float>("Scale");
float deformStrength = getIn<float>("DeformStrength");
float noiseFrequency = getIn<float>("NoiseFrequency");
// Validate inputs
subdivisions = std::max(2, subdivisions); // Ensure minimum subdivisions
scale = std::max(0.1f, scale); // Prevent zero/negative scale
deformStrength = std::clamp(deformStrength, 0.0f, 2.0f); // Reasonable range
logInfo("Generating sphere: subdivisions=" + std::to_string(subdivisions) +
", scale=" + std::to_string(scale));
// Generate base sphere mesh
Mesh mesh;
generateBaseSphere(subdivisions, scale, mesh);
// Apply deformation if requested
if (deformStrength > 0.001f) {
applyNoiseDeformation(mesh, deformStrength, noiseFrequency);
recalculateNormals(mesh);
}
// Set outputs
int vertexCount = static_cast<int>(mesh.vertexBuffer.positions.size());
setOut<Mesh>("Mesh", std::move(mesh));
setOut<int>("VertexCount", vertexCount);
logInfo("Generated deformable sphere with " + std::to_string(vertexCount) + " vertices");
}
void DeformableSphere::generateBaseSphere(int subdivisions, float scale, Mesh& mesh)
{
const float PI = 3.14159265358979323846f;
const size_t numLat = subdivisions;
const size_t numLon = subdivisions * 2;
const float halfScale = scale * 0.5f;
const size_t numVertices = (numLat - 1) * numLon + 2; // +2 for poles
const size_t numTriangles = numLon * 2 + (numLat - 2) * numLon * 2;
// Initialize mesh buffers
VertexBuffer vertexBuffer;
vertexBuffer.positions.resize(numVertices);
vertexBuffer.normals.resize(numVertices);
vertexBuffer.uvs.resize(numVertices);
IndexBuffer indexBuffer(0, IndexBuffer::IndexType::UInt32, IndexBuffer::Topology::Triangles);
indexBuffer.reserve(numTriangles * 3);
size_t vertexIndex = 0;
// Top pole
vertexBuffer.positions[vertexIndex] = Vector3(0.0f, halfScale, 0.0f);
vertexBuffer.normals[vertexIndex] = Vector3(0.0f, 1.0f, 0.0f);
vertexBuffer.uvs[vertexIndex] = Vector2(0.5f, 1.0f);
vertexIndex++;
// Generate latitude rings (excluding poles)
for (size_t lat = 1; lat < numLat; ++lat) {
float theta = lat * PI / numLat;
float sinTheta = std::sin(theta);
float cosTheta = std::cos(theta);
for (size_t lon = 0; lon < numLon; ++lon) {
float phi = lon * 2.0f * PI / numLon;
float sinPhi = std::sin(phi);
float cosPhi = std::cos(phi);
float x = sinTheta * cosPhi;
float y = cosTheta;
float z = sinTheta * sinPhi;
vertexBuffer.positions[vertexIndex] = Vector3(x * halfScale, y * halfScale, z * halfScale);
vertexBuffer.normals[vertexIndex] = Vector3(x, y, z);
vertexBuffer.uvs[vertexIndex] = Vector2(static_cast<float>(lon) / numLon,
1.0f - static_cast<float>(lat) / numLat);
vertexIndex++;
}
}
// Bottom pole
vertexBuffer.positions[vertexIndex] = Vector3(0.0f, -halfScale, 0.0f);
vertexBuffer.normals[vertexIndex] = Vector3(0.0f, -1.0f, 0.0f);
vertexBuffer.uvs[vertexIndex] = Vector2(0.5f, 0.0f);
// Generate triangles
// Top cap
for (size_t lon = 0; lon < numLon; ++lon) {
indexBuffer.pushTriangle(
0,
static_cast<uint32_t>((lon + 1) % numLon + 1),
static_cast<uint32_t>(lon + 1)
);
}
// Middle sections
for (size_t lat = 0; lat < numLat - 2; ++lat) {
size_t currentRow = lat * numLon + 1;
size_t nextRow = (lat + 1) * numLon + 1;
for (size_t lon = 0; lon < numLon; ++lon) {
size_t nextLon = (lon + 1) % numLon;
indexBuffer.pushTriangle(
static_cast<uint32_t>(currentRow + lon),
static_cast<uint32_t>(currentRow + nextLon),
static_cast<uint32_t>(nextRow + lon)
);
indexBuffer.pushTriangle(
static_cast<uint32_t>(nextRow + lon),
static_cast<uint32_t>(currentRow + nextLon),
static_cast<uint32_t>(nextRow + nextLon)
);
}
}
// Bottom cap
size_t southPoleIndex = numVertices - 1;
size_t lastRow = (numLat - 2) * numLon + 1;
for (size_t lon = 0; lon < numLon; ++lon) {
indexBuffer.pushTriangle(
static_cast<uint32_t>(southPoleIndex),
static_cast<uint32_t>(lastRow + lon),
static_cast<uint32_t>(lastRow + (lon + 1) % numLon)
);
}
mesh = Mesh(vertexBuffer, indexBuffer, {});
}
void DeformableSphere::applyNoiseDeformation(Mesh& mesh, float strength, float frequency)
{
auto& vertices = mesh.vertexBuffer.positions;
const auto& normals = mesh.vertexBuffer.normals;
// Use component's PRNG for consistent, reproducible noise
UniformRealDistribution<float> noiseDist(-1.0f, 1.0f);
for (size_t i = 0; i < vertices.size(); ++i) {
const Vector3& pos = vertices[i];
const Vector3& normal = normals[i];
// Generate noise based on position and frequency
// Simple procedural noise - in production, you might use FastNoise or similar
float noise = 0.0f;
float amplitude = 1.0f;
Vector3 samplePos = pos * frequency;
// Multi-octave noise
for (int octave = 0; octave < 3; ++octave) {
// Simple hash-based noise for demonstration
uint32_t hash = static_cast<uint32_t>(
std::abs(static_cast<int>(samplePos.x * 1000)) * 73856093 ^
std::abs(static_cast<int>(samplePos.y * 1000)) * 19349663 ^
std::abs(static_cast<int>(samplePos.z * 1000)) * 83492791
);
// Use component's PRNG seeded with position hash
prng.setSeed(hash + octave * 12345);
float octaveNoise = noiseDist(prng);
noise += octaveNoise * amplitude;
amplitude *= 0.5f;
samplePos = samplePos * 2.0f;
}
// Apply deformation along normal
float deformation = noise * strength;
vertices[i] = pos + normal * deformation;
}
}
void DeformableSphere::recalculateNormals(Mesh& mesh)
{
auto& vertices = mesh.vertexBuffer.positions;
auto& normals = mesh.vertexBuffer.normals;
const auto& indices = mesh.indexBuffer;
// Reset normals to zero
std::fill(normals.begin(), normals.end(), Vector3(0.0f, 0.0f, 0.0f));
// Accumulate face normals
for (size_t i = 0; i < indices.count(); i += 3) {
uint32_t indexA = indices[i];
uint32_t indexB = indices[i + 1];
uint32_t indexC = indices[i + 2];
const Vector3& a = vertices[indexA];
const Vector3& b = vertices[indexB];
const Vector3& c = vertices[indexC];
Vector3 ab = b - a;
Vector3 ac = c - a;
Vector3 faceNormal = ab.cross(ac);
normals[indexA] += faceNormal;
normals[indexB] += faceNormal;
normals[indexC] += faceNormal;
}
// Normalize accumulated normals
for (auto& normal : normals) {
normal = normal.normalized();
}
}
Platform-independent uniform real distribution.
Definition PRNGDistribution.hpp:55
Container for mesh index data defining vertex connectivity and topology.
Definition IndexBuffer.hpp:89
VertexBuffer vertexBuffer
Vertex attribute data for the mesh.
Definition Mesh.hpp:143
IndexBuffer indexBuffer
Index data defining primitive connectivity.
Definition Mesh.hpp:151
Container for mesh vertex attribute data.
Definition VertexBuffer.hpp:106
Containers::Array< Infinity::Types::Math::Vector3 > positions
Vertex positions in object/local space.
Definition VertexBuffer.hpp:117
Containers::Array< Infinity::Types::Math::Vector3 > normals
Vertex normal vectors.
Definition VertexBuffer.hpp:129
Definition BaseException.hpp:9
Definition ComponentException.hpp:7
Definition Array.hpp:25
Definition Math.hpp:9
constexpr float PI
Mathematical constants.
Definition Math.hpp:11
Definition IndexBuffer.hpp:9
Template structure representing a 2-component vector.
Definition Vector2.hpp:75
Template structure representing a 3-component vector.
Definition Vector3.hpp:82
T x
Definition Vector3.hpp:97
T z
Cartesian coordinate interpretation.
Definition Vector3.hpp:97
t_Vector3< T > cross(const t_Vector3< T > &other) const
Computes the cross product with another vector.
Definition Vector3.hpp:370
t_Vector3< T > normalized() const
Computes the normalized version of this vector.
Definition Vector3.hpp:344
T y
Definition Vector3.hpp:97

Step 5: Component Testing

tests/DeformableSphereTestCase.hpp

#pragma once
#include <InfinityTest/InfinityTest.hpp>
#include "../src/DeformableSphere.hpp"
class DeformableSphereTestCase : public Infinity::Testing::ComponentTestCase
{
public:
DeformableSphereTestCase();
Infinity::Procedural::ProceduralComponent* createComponent() override;
DeformableSphere* getComponent();
virtual void setUp() override;
virtual void tearDown() override;
// Test method declarations
void testBasicSphereGeneration();
void testDeformationEffects();
void testInputValidation();
void testOutputs();
};

Step 5: Component Testing

tests/DeformableSphereTestCase.hpp

#pragma once
#include <InfinityTest/InfinityTest.hpp>
#include "../src/DeformableSphere.hpp"
class DeformableSphereTestCase : public Infinity::Testing::ComponentTestCase
{
public:
DeformableSphereTestCase();
Infinity::Procedural::ProceduralComponent* createComponent() override;
DeformableSphere* getComponent();
virtual void setUp() override;
virtual void tearDown() override;
// Test method declarations
void testBasicSphereGeneration();
void testDeformationEffects();
void testInputValidation();
void testOutputs();
};

tests/DeformableSphereTestCase.cpp

#include "DeformableSphereTestCase.hpp"
#include <Infinity/Procedural/Registry.hpp>
#include <Infinity/Types/All.hpp>
#include <cassert>
using namespace Infinity::Procedural;
using namespace Infinity::Testing;
DeformableSphereTestCase::DeformableSphereTestCase()
{
// Register test methods
ADD_TEST(testBasicSphereGeneration);
ADD_TEST(testDeformationEffects);
ADD_TEST(testInputValidation);
ADD_TEST(testOutputs);
}
ProceduralComponent* DeformableSphereTestCase::createComponent()
{
return Registry::createComponent("Tutorial.Components", "Deformable Sphere");
}
DeformableSphere* DeformableSphereTestCase::getComponent()
{
return dynamic_cast<DeformableSphere*>(component);
}
void DeformableSphereTestCase::setUp()
{
ComponentTestCase::setUp();
// Additional setup if needed
}
void DeformableSphereTestCase::tearDown()
{
// Cleanup if needed
ComponentTestCase::tearDown();
}
void DeformableSphereTestCase::testBasicSphereGeneration()
{
// Set basic inputs
setIn<int>("Subdivisions", 3);
setIn<float>("Scale", 2.0f);
setIn<float>("DeformStrength", 0.0f); // No deformation
setIn<float>("NoiseFrequency", 1.0f);
// Execute component
execute();
// Verify outputs
const auto& mesh = getOut<Mesh>("Mesh");
int vertexCount = getOut<int>("VertexCount");
// Basic validation
assert(!mesh.vertexBuffer.positions.empty());
assert(mesh.vertexBuffer.positions.size() == mesh.vertexBuffer.normals.size());
assert(mesh.vertexBuffer.positions.size() == mesh.vertexBuffer.uvs.size());
assert(vertexCount > 0);
assert(static_cast<size_t>(vertexCount) == mesh.vertexBuffer.positions.size());
}
void DeformableSphereTestCase::testDeformationEffects()
{
// Generate two spheres: one normal, one deformed
setIn<int>("Subdivisions", 4);
setIn<float>("Scale", 1.0f);
setIn<float>("DeformStrength", 0.0f);
setIn<float>("NoiseFrequency", 2.0f);
execute();
const auto& normalMesh = getOut<Mesh>("Mesh");
// Reset and generate deformed version
setIn<float>("DeformStrength", 0.5f);
execute();
const auto& deformedMesh = getOut<Mesh>("Mesh");
// Verify they have same vertex count but different positions
assert(normalMesh.vertexBuffer.positions.size() == deformedMesh.vertexBuffer.positions.size());
// At least some vertices should be different
bool foundDifference = false;
for (size_t i = 0; i < normalMesh.vertexBuffer.positions.size(); ++i) {
if (normalMesh.vertexBuffer.positions[i] != deformedMesh.vertexBuffer.positions[i]) {
foundDifference = true;
break;
}
}
assert(foundDifference);
}
void DeformableSphereTestCase::testInputValidation()
{
// Test with extreme inputs
setIn<int>("Subdivisions", -5); // Should be clamped to minimum
setIn<float>("Scale", -1.0f); // Should be clamped to positive
setIn<float>("DeformStrength", 10.0f); // Should be clamped to reasonable range
setIn<float>("NoiseFrequency", 1.0f);
// Should not crash
execute();
// Should still produce valid output
const auto& mesh = getOut<Mesh>("Mesh");
assert(!mesh.vertexBuffer.positions.empty());
}
void DeformableSphereTestCase::testOutputs()
{
// Set specific inputs
setIn<int>("Subdivisions", 2);
setIn<float>("Scale", 1.0f);
setIn<float>("DeformStrength", 0.1f);
setIn<float>("NoiseFrequency", 1.0f);
execute();
// Verify all outputs are present and valid
const auto& mesh = getOut<Mesh>("Mesh");
int vertexCount = getOut<int>("VertexCount");
assert(mesh.vertexBuffer.positions.size() > 0);
assert(mesh.indexBuffer.count() > 0);
assert(vertexCount == static_cast<int>(mesh.vertexBuffer.positions.size()));
}
size_t count() const
Gets the number of indices in the buffer.
Containers::Array< Infinity::Types::Math::Vector2 > uvs
Texture coordinates (UVs).
Definition VertexBuffer.hpp:140

Step 5: Build and Test

Building the Component

mkdir build
cd build
cmake ..
cmake --build . --config Release

Running Tests

The generated test structure includes everything needed:

./test/DeformableSphereTests # Linux/macOS
.\test\Release\DeformableSphereTests.exe # Windows

Step 6: Component Icon (Already Generated!)

The infinity_tool automatically created DeformableSphere_icon.svg with a default icon. You can replace this with your own custom SVG or PNG icon if desired. The icon will appear in the Infinity Creator toolbox when your component is imported.

Step 7: Packaging for Import

To prepare the output folder with all the necessary files for importing into the Infinity Creator, create an install by running:

cd build
cmake --install . --prefix ../DeformableSphereInstall --config Release

This will create a folder structure similar to this:

DeformableSphereInstall/
bin/
lib/
share/

When importing into the Infinity Creator, select the folder **"DeformableSphereInstall"**.

Step 8: Import into Infinity Creator

Importing Your Component

  1. Open The Infinity Creator
  2. Navigate to Component Menu
    • Click "Component" in the menu bar
    • Select "Component Library"
Click 'Component Library...' in the Components menu
  1. Import Component
    • Click "Import Component..." button at the top of the Component Library dialog
    • Select your component's install directory (e.g. **"DeformableSphereInstall"**)
    • Click "Open" (the system will copy the component into your project)
Click the 'Import Component...' button
Choose your component's install folder
  1. Component Available in Toolbox
    • Your Deformable Sphere component will now appear in the Toolbox
    • It will be organized under the "Tutorial.Components" group
    • You can drag it into your system like any built-in component
The imported component visible in the component library

For detailed guidance on using imported components and building systems, refer to the Infinity Creator Manual.

Using Your Component in a System

  1. Drag the Deformable Sphere component from the Toolbox into the Procedural Graph view
  2. Connect inputs such as integer constants for Subdivisions, float constants for Scale, etc.
  3. Connect outputs to other components like mesh renderers or file exporters
  4. Test execution by running the system within the Creator

Step 9: Deployment

When you deploy a system using your custom component:

  1. Automatic Inclusion: The component's shared library will be automatically included in the deployment
  2. Distribution: All necessary component files are copied to the deployment directory
  3. Dependencies: The Infinity Engine runtime will automatically load your component when the system executes

Advanced Features

Using PRNG Distributions

For more sophisticated randomization, use the built-in distribution classes:

void DeformableSphere::advancedNoiseExample()
{
// Use normal distribution for smooth noise
NormalDistribution<float> normalDist(0.0f, 1.0f);
// Use gamma distribution for asymmetric variation
GammaDistribution<float> gammaDist(2.0f, 0.5f);
for (auto& vertex : mesh.vertexBuffer.positions) {
float normalNoise = normalDist(prng);
float gammaVariation = gammaDist(prng);
// Combine multiple noise sources
// ... apply to vertex
}
}
Platform-independent gamma distribution.
Definition PRNGDistribution.hpp:1098
Platform-independent normal (Gaussian) distribution.
Definition PRNGDistribution.hpp:524

Component Properties and Configuration

You can extend your component.yaml with more sophisticated input definitions:

in:
- name: NoiseType
type: string
default: "Perlin"
description: Type of noise to use for deformation
editor:
type: dropdown
options: ["Perlin", "Simplex", "Worley"]
- name: DeformationAxis
type: Vector3
default: [1.0, 1.0, 1.0]
description: Directional bias for deformation
- name: EnableUVUnwrapping
type: bool
default: true
description: Whether to generate UV coordinates

Troubleshooting

Build fails:

  • Verify the Infinity SDK is properly installed
  • Check CMakeLists.txt paths and library names
  • Ensure C++17 standard is supported by your compiler

Component not found in Creator:

  • Verify component.yaml syntax and plugin-name
  • Check that the shared library was built successfully
  • Ensure component import completed without errors

Runtime execution errors:

  • Check component logging output for error messages
  • Verify input types match component.yaml definitions
  • Use try-catch blocks around potentially failing operations

Inconsistent results:

  • Ensure you're using the component's PRNG for randomization
  • Verify seed handling is correct
  • Check that all random operations are deterministic

Best Practices

  1. Validate inputs thoroughly to prevent crashes
  2. Use logging to help debug component behavior
  3. Write comprehensive tests for different input scenarios
  4. Document your component clearly in the YAML description
  5. Follow consistent naming conventions for inputs/outputs
  6. Handle edge cases gracefully (empty inputs, extreme values)
  7. Use the component PRNG for all randomization
  8. Optimize for performance in frequently-used components
  9. Create meaningful icons to improve user experience
  10. Version your components appropriately

Next Steps

Now that you've created your first component:

  • Learn about Random Number Generation and Distributions for advanced randomization
  • Explore more complex mesh operations and data structures in the Types API
  • Study built-in components for patterns and advanced techniques
  • Create component libraries for related functionality
  • Build complete systems that showcase your custom components

For comprehensive guidance on component development and the Creator interface, visit the Infinity Creator Manual.