Skill v1.0.0
currentAutomated scan100/100metadata: 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:
| Doc | When to read | |
|---|---|---|
.llm/Architecture.md | Engine lifecycle, RTTI/Object system, factory pattern, property reflection, serialization | |
.llm/NodeSystem.md | Node class hierarchy, Node3D vs Widget, scene tree, World management | |
.llm/NodeGraph.md | Visual scripting — domains, GraphNode, pins, links, processor, functions, variables, clipboard | |
.llm/Editor.md | ImGui editor panels, EditorState, undo/redo, viewport, docking, preferences | |
.llm/Graphics.md | Vulkan pipeline, GX/C3D backends, materials, renderer | |
.llm/Scripting.md | Lua bindings, Script component, binding macros, stub generator | |
.llm/AssetSystem.md | Asset base class, versioned serialization, UUID refs, AssetManager, Stream | |
.llm/Timeline.md | Keyframe animation — tracks, clips, interpolation, TimelinePlayer | |
.llm/Addons.md | Runtime plugins, native addons, EditorUIHooks, PolyphaseEngineAPI | |
.llm/Platforms.md | Platform abstraction, SYS_* functions, platform-specific backends | |
.llm/NavMesh.md | Navigation mesh system, pathfinding API | |
.llm/ControlServer.md | REST 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 lifecycleEngine/Assets/— Asset type implementations (Texture, Material, StaticMesh, Scene, etc.)Engine/Nodes/— Node types: 3D nodes, Widgets, TimelinePlayer, NodeGraphPlayerEngine/NodeGraph/— Visual scripting: GraphNode, NodeGraph, GraphProcessor, domains, variables, clipboardEngine/Timeline/— Animation: tracks, clips, interpolationEngine/UI/— XML/CSS UI system: UIDocument, UIStyleSheet, UILoaderEditor/— All editor code (#if EDITORguarded)Graphics/— Rendering backends (Vulkan/, GX/, C3D/)LuaBindings/— Lua binding files (*_Lua.h/.cpp)Plugins/— Runtime plugin APISystem/— Platform abstraction layerAudio/,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
| Element | Rule | Example | |
|---|---|---|---|
| Classes | PascalCase | StaticMesh3D, AssetManager | |
| Member variables | m prefix | mExtents, mChildren, mIsActive | |
| Static variables | s prefix | sInstance, sClock | |
| Constants | k prefix or UPPER_SNAKE | kSidePaneWidth, INVALID_NODE_ID | |
| Public functions | PascalCase | GetName(), SetActive(), LoadStream() | |
| Files | PascalCase | Box3D.h, EditorImgui.cpp | |
| Lua bindings | *_Lua.h/.cpp suffix | Box3d_Lua.cpp, Gizmos_Lua.cpp | |
| Macros | UPPER_CASE | DECLARE_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 oncethroughout the codebase. - Forward declarations over includes in headers when possible.
4. Core Patterns
RTTI and Factory Registration
Every node type:
// Headerclass MyNode : public Node3D{DECLARE_NODE(MyNode, Node3D);// ...};// SourceDEFINE_NODE(MyNode, Node3D);
Every asset type:
// Headerclass MyAsset : public Asset{DECLARE_ASSET(MyAsset, Asset);// ...};// SourceDEFINE_ASSET(MyAsset);
Every graph node type:
// Headerclass MyGraphNode : public GraphNode{DECLARE_GRAPH_NODE(MyGraphNode, GraphNode);// ...};// SourceDEFINE_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
Renderer::Get() // Renderer*AssetManager::Get() // AssetManager*PreferencesManager::Get() // PreferencesManager*GetEditorState() // EditorState* (EDITOR only)GetEngineState() // EngineState&GetEngineConfig() // EngineConfig&GetAppClock()->GetTime() // float seconds
Property Reflection
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
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
#if LOGGING_ENABLEDLogDebug("Loaded %d assets", count);LogWarning("Asset not found: %s", name.c_str());LogError("Failed to initialize renderer");#endif
Editor Guards
#if EDITOR// Editor-only code: panels, gizmo callbacks, inspector UIEditorImguiDraw();#endif
5. Adding New Things — Checklists
New Node Type
- Create
MyNode.h/MyNode.cppin the appropriateNodes/subdirectory. - Use
DECLARE_NODE(MyNode, ParentNode)/DEFINE_NODE(MyNode, ParentNode). - Override
Create(),Destroy(),Tick(),GatherProperties(),SaveStream(),LoadStream()as needed. - Add
FORCE_LINK_CALL(MyNode)toEngine.cpp. - Add files to
Engine.vcxprojandEngine.vcxproj.filters. - If Lua-exposed, create
MyNode_Lua.h/.cppinLuaBindings/, addBind()call inLuaBindings.cpp.
New Asset Type
- Create
MyAsset.h/MyAsset.cppinEngine/Source/Engine/Assets/. - Use
DECLARE_ASSET(MyAsset, Asset)/DEFINE_ASSET(MyAsset). - Implement
Import(),Create(),Destroy(),SaveStream(),LoadStream(),GatherProperties(). - Register file extension in
AssetManagerdiscovery. - Add
FORCE_LINK_CALL(MyAsset)toEngine.cpp. - Add files to
Engine.vcxprojandEngine.vcxproj.filters. - Bump
ASSET_VERSION_CURRENTif new serialized fields are added.
New Graph Node
- Create
MyNodes.h/MyNodes.cppinEngine/Source/Engine/NodeGraph/or a relevant subdirectory. - Use
DECLARE_GRAPH_NODE/DEFINE_GRAPH_NODEfor each node. - Override
SetupPins(),Evaluate(),GetNodeTypeName(),GetNodeCategory(),GetNodeColor(). - Register in the appropriate
GraphDomainsubclass. - Add
FORCE_LINK_CALLtoEngine.cpp. - Add files to
Engine.vcxprojandEngine.vcxproj.filters.
New Lua Binding
- Create
Thing_Lua.h/Thing_Lua.cppinEngine/Source/LuaBindings/. - Implement a static
Bind(lua_State* L)function. - Call
Thing_Lua::Bind(L)fromLuaBindings.cpp. - Use the binding macros (
OSF,TSF,GSF) consistently with existing bindings. - Add files to
Engine.vcxprojandEngine.vcxproj.filters. - Regenerate stubs:
python Tools/generate_lua_stubs.py.
New Editor Panel
- Create panel files in
Engine/Source/Editor/YourPanel/. - Guard everything with
#if EDITOR. - Follow the ImGui panel pattern:
ImGui::Begin("PanelName", ...)/ImGui::End(). - Call your panel's draw function from
EditorImguiDraw()inEditorImgui.cpp. - Add files to
Engine.vcxprojandEngine.vcxproj.filters.
New Controller Server REST Endpoint
- Add route in
Engine/Source/Editor/ControllerServer/ControllerServerRoutes.cpp. - Use
CROW_ROUTE(app, "/api/your/path").methods("GET"_method)([server](const crow::request& req) { ... }); - Wrap all engine API calls in
server->QueueCommand(lambda)for thread safety. - Use
crow::json::wvaluefor building JSON responses,crow::json::load(body)for parsing. - Call
LogRequest(server, "METHOD", "/api/your/path")for request logging. - Return
crow::response(200, "application/json", result). - See
.llm/ControlServer.mdfor architecture details.
New Controller Server Addon Route (Native Addon)
- Add callback typedef if needed in
Engine/Source/Plugins/EditorUIHooks.h. - Call
api->editorUI->RegisterControllerRoute(hookId, method, path, callback, userData)in addon OnLoad. - Implement
ControllerRouteCallback— write JSON intoresponseBuffer. - Routes are matched via
CROW_CATCHALL_ROUTEin ControllerServerRoutes.cpp.
Visual Studio Project Integration
When adding any new source file:
`Engine.vcxproj`:
<ClCompile Include="Source\Path\To\File.cpp" /><ClInclude Include="Source\Path\To\File.h" />
`Engine.vcxproj.filters`:
<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:
| Macro | Platform | |
|---|---|---|
PLATFORM_WINDOWS | Windows (primary dev) | |
PLATFORM_LINUX | Linux | |
PLATFORM_ANDROID | Android | |
PLATFORM_DOLPHIN | GameCube / Wii | |
PLATFORM_3DS | Nintendo 3DS | |
API_VULKAN | Vulkan rendering backend | |
API_GX | GameCube/Wii rendering | |
API_C3D | 3DS 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:
- Read the relevant source files. Don't guess at interfaces — read the headers.
- Trace the call chain. Use Grep to find callers and callees.
- Check the `.llm/` docs for architectural context and gotchas.
- Look at similar existing implementations. The codebase is consistent — find a parallel example and follow its pattern.
- 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_CURRENTis 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
sMutexviaLockLog()/UnlockLog(). Don't hold the log lock while doing heavy work.
9. Workflow
- Understand the request. Read the relevant
.llm/docs and source files. - Find existing patterns. Search for similar implementations in the codebase.
- Implement. Write code that matches the existing style exactly.
- Integrate. Update
Engine.vcxproj,.filters,Engine.cpp(FORCE_LINK_CALL),LuaBindings.cpp(if Lua), etc. - 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.