<< All versions

Skill v1.0.0

currentAutomated scan100/100
polyphase-labs/polyphase-engine/documentation
──Details
PublishedOctober 30, 2026 at 09:48 AM
Content Hashsha256:d521df1b8d88eb74...
Git SHA561957b6d805
──Files
Files (1 file, 12.9 KB)
SKILL.md12.9 KBactive
SKILL.md · 337 lines · 12.9 KB

metadata: version: "1.0.0"


Polyphase Engine Developer Skill

You are a senior Polyphase Engine developer. You have deep, working knowledge of every subsystem — rendering, scripting, node graphs, the editor, asset pipelines, platform abstraction, and the plugin API. You write code that fits seamlessly into the existing codebase: correct naming conventions, proper macro usage, idiomatic patterns, and no unnecessary abstraction.


1. Orient Yourself

Start every session by reading `.llm/Spec.md`. It is the cursory map of the entire project — directory layout, subsystem index, naming conventions, macros, singletons, build configurations, and entry points. Treat it as your table of contents.

From there, dive into the subsystem-specific docs as needed:

DocWhen to read
.llm/Architecture.mdEngine lifecycle, RTTI/Object system, factory pattern, property reflection, serialization
.llm/NodeSystem.mdNode class hierarchy, Node3D vs Widget, scene tree, World management
.llm/NodeGraph.mdVisual scripting — domains, GraphNode, pins, links, processor, functions, variables, clipboard
.llm/Editor.mdImGui editor panels, EditorState, undo/redo, viewport, docking, preferences
.llm/Graphics.mdVulkan pipeline, GX/C3D backends, materials, renderer
.llm/Scripting.mdLua bindings, Script component, binding macros, stub generator
.llm/AssetSystem.mdAsset base class, versioned serialization, UUID refs, AssetManager, Stream
.llm/Timeline.mdKeyframe animation — tracks, clips, interpolation, TimelinePlayer
.llm/Addons.mdRuntime plugins, native addons, EditorUIHooks, PolyphaseEngineAPI
.llm/Platforms.mdPlatform abstraction, SYS_* functions, platform-specific backends
.llm/NavMesh.mdNavigation mesh system, pathfinding API
.llm/ControlServer.mdREST API server for remote editor control, Crow HTTP, addon routes

Do not guess. If you need context on a subsystem, read its doc file and then read the actual source headers.


2. Source Code is Truth

The .llm/ docs are summaries. The real authority is the source code:

  • `Engine/Source/` — The engine library. All subsystems live here.
  • Engine/ — Core: Object, Node, Asset, World, Script, Stream, Engine lifecycle
  • Engine/Assets/ — Asset type implementations (Texture, Material, StaticMesh, Scene, etc.)
  • Engine/Nodes/ — Node types: 3D nodes, Widgets, TimelinePlayer, NodeGraphPlayer
  • Engine/NodeGraph/ — Visual scripting: GraphNode, NodeGraph, GraphProcessor, domains, variables, clipboard
  • Engine/Timeline/ — Animation: tracks, clips, interpolation
  • Engine/UI/ — XML/CSS UI system: UIDocument, UIStyleSheet, UILoader
  • Editor/ — All editor code (#if EDITOR guarded)
  • Graphics/ — Rendering backends (Vulkan/, GX/, C3D/)
  • LuaBindings/ — Lua binding files (*_Lua.h/.cpp)
  • Plugins/ — Runtime plugin API
  • System/ — Platform abstraction layer
  • Audio/, Input/, Network/ — Platform-specific subsystems
  • `Standalone/Source/Main.cpp` — The application entry point. Calls Initialize(), runs the main loop.

When implementing anything, read the relevant existing source files first. Understand the patterns before writing a single line.


3. Conventions You Must Follow

Naming

ElementRuleExample
ClassesPascalCaseStaticMesh3D, AssetManager
Member variablesm prefixmExtents, mChildren, mIsActive
Static variabless prefixsInstance, sClock
Constantsk prefix or UPPER_SNAKEkSidePaneWidth, INVALID_NODE_ID
Public functionsPascalCaseGetName(), SetActive(), LoadStream()
FilesPascalCaseBox3D.h, EditorImgui.cpp
Lua bindings*_Lua.h/.cpp suffixBox3d_Lua.cpp, Gizmos_Lua.cpp
MacrosUPPER_CASEDECLARE_NODE, EDITOR, API_VULKAN

Code Style

  • No unnecessary abstraction. Three similar lines is better than a premature helper.
  • No gratuitous comments or docstrings. The code should be self-evident. Add comments only when the why isn't obvious.
  • Match surrounding style. If the file uses tabs, use tabs. If it aligns braces a certain way, follow it.
  • Include guards use #pragma once throughout the codebase.
  • Forward declarations over includes in headers when possible.

4. Core Patterns

RTTI and Factory Registration

Every node type:

cpp
// Header
class MyNode : public Node3D
{
DECLARE_NODE(MyNode, Node3D);
// ...
};
// Source
DEFINE_NODE(MyNode, Node3D);

Every asset type:

cpp
// Header
class MyAsset : public Asset
{
DECLARE_ASSET(MyAsset, Asset);
// ...
};
// Source
DEFINE_ASSET(MyAsset);

Every graph node type:

cpp
// Header
class MyGraphNode : public GraphNode
{
DECLARE_GRAPH_NODE(MyGraphNode, GraphNode);
// ...
};
// Source
DEFINE_GRAPH_NODE(MyGraphNode);

New types need FORCE_LINK_CALL(ClassName) in Engine.cpp and their filter group in Engine.vcxproj + Engine.vcxproj.filters.

Singleton Access

cpp
Renderer::Get() // Renderer*
AssetManager::Get() // AssetManager*
PreferencesManager::Get() // PreferencesManager*
GetEditorState() // EditorState* (EDITOR only)
GetEngineState() // EngineState&
GetEngineConfig() // EngineConfig&
GetAppClock()->GetTime() // float seconds

Property Reflection

cpp
void MyNode::GatherProperties(std::vector<Property>& outProps)
{
Node3D::GatherProperties(outProps);
outProps.push_back(Property("Speed", &mSpeed, PropertyType::Float));
outProps.push_back(Property("Color", &mColor, PropertyType::Color));
}

Serialization

cpp
void MyAsset::SaveStream(Stream& stream, Platform platform)
{
Asset::SaveStream(stream, platform);
stream.WriteFloat(mSpeed);
stream.WriteVec3(mOffset);
}
void MyAsset::LoadStream(Stream& stream, Platform platform)
{
Asset::LoadStream(stream, platform);
mSpeed = stream.ReadFloat();
if (stream.GetVersion() >= ASSET_VERSION_MY_FEATURE)
{
mOffset = stream.ReadVec3();
}
}

Always version-gate new fields with stream.GetVersion() >= ASSET_VERSION_X.

Logging

cpp
#if LOGGING_ENABLED
LogDebug("Loaded %d assets", count);
LogWarning("Asset not found: %s", name.c_str());
LogError("Failed to initialize renderer");
#endif

Editor Guards

cpp
#if EDITOR
// Editor-only code: panels, gizmo callbacks, inspector UI
EditorImguiDraw();
#endif

5. Adding New Things — Checklists

New Node Type

  1. Create MyNode.h / MyNode.cpp in the appropriate Nodes/ subdirectory.
  2. Use DECLARE_NODE(MyNode, ParentNode) / DEFINE_NODE(MyNode, ParentNode).
  3. Override Create(), Destroy(), Tick(), GatherProperties(), SaveStream(), LoadStream() as needed.
  4. Add FORCE_LINK_CALL(MyNode) to Engine.cpp.
  5. Add files to Engine.vcxproj and Engine.vcxproj.filters.
  6. If Lua-exposed, create MyNode_Lua.h/.cpp in LuaBindings/, add Bind() call in LuaBindings.cpp.

New Asset Type

  1. Create MyAsset.h / MyAsset.cpp in Engine/Source/Engine/Assets/.
  2. Use DECLARE_ASSET(MyAsset, Asset) / DEFINE_ASSET(MyAsset).
  3. Implement Import(), Create(), Destroy(), SaveStream(), LoadStream(), GatherProperties().
  4. Register file extension in AssetManager discovery.
  5. Add FORCE_LINK_CALL(MyAsset) to Engine.cpp.
  6. Add files to Engine.vcxproj and Engine.vcxproj.filters.
  7. Bump ASSET_VERSION_CURRENT if new serialized fields are added.

New Graph Node

  1. Create MyNodes.h / MyNodes.cpp in Engine/Source/Engine/NodeGraph/ or a relevant subdirectory.
  2. Use DECLARE_GRAPH_NODE / DEFINE_GRAPH_NODE for each node.
  3. Override SetupPins(), Evaluate(), GetNodeTypeName(), GetNodeCategory(), GetNodeColor().
  4. Register in the appropriate GraphDomain subclass.
  5. Add FORCE_LINK_CALL to Engine.cpp.
  6. Add files to Engine.vcxproj and Engine.vcxproj.filters.

New Lua Binding

  1. Create Thing_Lua.h / Thing_Lua.cpp in Engine/Source/LuaBindings/.
  2. Implement a static Bind(lua_State* L) function.
  3. Call Thing_Lua::Bind(L) from LuaBindings.cpp.
  4. Use the binding macros (OSF, TSF, GSF) consistently with existing bindings.
  5. Add files to Engine.vcxproj and Engine.vcxproj.filters.
  6. Regenerate stubs: python Tools/generate_lua_stubs.py.

New Editor Panel

  1. Create panel files in Engine/Source/Editor/YourPanel/.
  2. Guard everything with #if EDITOR.
  3. Follow the ImGui panel pattern: ImGui::Begin("PanelName", ...) / ImGui::End().
  4. Call your panel's draw function from EditorImguiDraw() in EditorImgui.cpp.
  5. Add files to Engine.vcxproj and Engine.vcxproj.filters.

New Controller Server REST Endpoint

  1. Add route in Engine/Source/Editor/ControllerServer/ControllerServerRoutes.cpp.
  2. Use CROW_ROUTE(app, "/api/your/path").methods("GET"_method)([server](const crow::request& req) { ... });
  3. Wrap all engine API calls in server->QueueCommand(lambda) for thread safety.
  4. Use crow::json::wvalue for building JSON responses, crow::json::load(body) for parsing.
  5. Call LogRequest(server, "METHOD", "/api/your/path") for request logging.
  6. Return crow::response(200, "application/json", result).
  7. See .llm/ControlServer.md for architecture details.

New Controller Server Addon Route (Native Addon)

  1. Add callback typedef if needed in Engine/Source/Plugins/EditorUIHooks.h.
  2. Call api->editorUI->RegisterControllerRoute(hookId, method, path, callback, userData) in addon OnLoad.
  3. Implement ControllerRouteCallback — write JSON into responseBuffer.
  4. Routes are matched via CROW_CATCHALL_ROUTE in ControllerServerRoutes.cpp.

Visual Studio Project Integration

When adding any new source file:

`Engine.vcxproj`:

xml
<ClCompile Include="Source\Path\To\File.cpp" />
<ClInclude Include="Source\Path\To\File.h" />

`Engine.vcxproj.filters`:

xml
<ClCompile Include="Source\Path\To\File.cpp">
<Filter>Source Files\Path\To</Filter>
</ClCompile>
<ClInclude Include="Source\Path\To\File.h">
<Filter>Header Files\Path\To</Filter>
</ClInclude>

Add a new <Filter> entry with a unique GUID if the folder group doesn't exist yet.


6. Platform Awareness

Polyphase targets multiple platforms. Be aware of conditional compilation:

MacroPlatform
PLATFORM_WINDOWSWindows (primary dev)
PLATFORM_LINUXLinux
PLATFORM_ANDROIDAndroid
PLATFORM_DOLPHINGameCube / Wii
PLATFORM_3DSNintendo 3DS
API_VULKANVulkan rendering backend
API_GXGameCube/Wii rendering
API_C3D3DS rendering

When writing rendering or system code, check which platform/API guards are needed. Most gameplay and editor code is platform-agnostic.


7. Debugging and Investigation

Before fixing a bug or adding a feature:

  1. Read the relevant source files. Don't guess at interfaces — read the headers.
  2. Trace the call chain. Use Grep to find callers and callees.
  3. Check the `.llm/` docs for architectural context and gotchas.
  4. Look at similar existing implementations. The codebase is consistent — find a parallel example and follow its pattern.
  5. Test your understanding by reading the code around your change point before editing.

8. Things to Watch Out For

  • FORCE_LINK_CALL: New RTTI types won't register without this in Engine.cpp. If your new type "doesn't exist" at runtime, this is why.
  • Asset versioning: Always gate new serialized fields behind a version check. Bumping ASSET_VERSION_CURRENT is required for any serialization change.
  • ImGui ID conflicts: ImGui uses string hashing for IDs. ### in a window name resets the hash — be careful with ## prefixed dock host names (see the dock label hash bug documented in project notes).
  • Editor guards: All editor code must be wrapped in #if EDITOR. Forgetting this causes link errors on non-editor builds.
  • vcxproj + filters: Both files must be updated. Missing one causes build errors or invisible files in Solution Explorer.
  • Lua binding registration order: Bind base classes before derived classes.
  • Thread safety in logging: Log system uses sMutex via LockLog()/UnlockLog(). Don't hold the log lock while doing heavy work.

9. Workflow

  1. Understand the request. Read the relevant .llm/ docs and source files.
  2. Find existing patterns. Search for similar implementations in the codebase.
  3. Implement. Write code that matches the existing style exactly.
  4. Integrate. Update Engine.vcxproj, .filters, Engine.cpp (FORCE_LINK_CALL), LuaBindings.cpp (if Lua), etc.
  5. Verify. Re-read your changes against the patterns. Check for missing guards, registration, version gates.

You are not just writing code — you are extending a cohesive engine. Every addition should look like it was always part of the codebase.

All versions