Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# CLAUDE.md

Guidance for coding agents (and new developers) working in this repository.

## What this is

Carbon Trinity: Fenris Creations' rendering engine for the Carbon game engine. C++, built per graphics
API as Python extension modules named `_trinity_<api>[_<flavor>]` (for example
`_trinity_dx12_trinitydev`; on Windows the extension suffix is `.pyd`, while on macOS/Linux it's typically `.so`). The engine is scripted from Python through the "Blue" exposure

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good to differentiate between blue and blue-exposure and point Claude at the relevant component, e.g. something along the lines of:
"The engine's functionality is exposed to Python using blue-exposure".
The motivation here is that blue potentially adds a lot of stuff to the agent's context window that is not truly relevant to the exposure part.

layer (see below). The primary consumer is Fenris's internal game codebase, which imports it as
`import trinity`; the repo can also be built standalone (public headers in `trinity/Include/`).

## Repository structure

- `trinity/` - main engine library: meshes, models, effects, particles, lights, post-process,
raytracing, resources. `Eve/` holds game-layer classes, `Include/` the public headers.
- `trinityal/` - graphics abstraction layer (RHI) with `dx11/`, `dx12/`, `metal/`, `stub/`
backends and a GoogleTest suite in `tests/`.
- `shadercompiler/` - HLSL-to-backend effect compiler, with its own `tests/` and `python/`
bindings.
- `cmake/` - CCP build helper modules (`Ccp*.cmake`).
- `vendor/` - git submodules only: `microsoft/vcpkg` and `carbonengine/vcpkg-registry`.
- `python/upgrade_scripts/` - content-migration helper scripts.

Naming conventions: `Tr2*` core engine classes, `Tri*` low-level math/device primitives, `Eve*`
game-layer classes, `*AL` / `*ALDx11` / `*ALDx12` in trinityal, `I*` interfaces.

No shader source or game content lives in this repo; the only shaders are test assets under
`trinityal/tests/`.

## Building

vcpkg manifest mode using the vendored submodules as toolchain. Before the first configure run
`git submodule update --init --recursive`.

Blessed Windows dev recipe:

```
cmake --preset x64-windows-trinitydev -DBUILD_DX11=ON -DBUILD_DX12=ON
cmake --build .cmake-build-x64-windows-trinitydev --config TrinityDev --target trinity_dx11 trinity_dx12 trinity_stub
```

Presets follow `<arch>-<os>-<flavor>` (see `CMakePresets.json`); flavors are Debug / TrinityDev /
Internal / Release. macOS presets build the metal backend. Personal presets belong in
`CMakeUserPresets.json`, which is gitignored. Binary dirs are `.cmake-build-<preset>`.

Tripwires:

- `BUILD_DX11` / `BUILD_DX12` / `BUILD_METAL` default OFF. A bare configure builds only the stub
backend; if your dx targets are missing, this is why.
- The Windows generator is Visual Studio (multi-config). `--config` is mandatory on every build
and must match the preset's flavor, or you silently build the wrong configuration.
- CMake writes status messages and warnings to stderr. PowerShell 5.1 with

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CMake writes most message categories to stderr, but STATUS messages are not part of those. As per documentation:

The CMake command-line tool displays STATUS to TRACE messages on stdout with the message preceded by two hyphens and a space. All other message types are sent to stderr and are not prefixed with hyphens.

`$ErrorActionPreference = 'Stop'` turns that into a spurious NativeCommandError even on a
successful run; judge success by `$LASTEXITCODE`, not by the presence of stderr output.

## The Blue exposure layer

Most engine classes `Foo.{h,cpp}` have a sibling `Foo_Blue.cpp` (about 500 in the repo) defining
Foo's Python surface. Only members mapped there (`MAP_ATTRIBUTE`, `MAP_METHOD_AND_WRAP`,
`MAP_INTERFACE`) are reachable from Python. When editing any class that has a `_Blue.cpp` sibling:

1. A new public member intended for Python use MUST be mapped in the `_Blue.cpp`, or it is
invisible from Python. The symptom is "my change has no effect" despite a clean build and
deploy. Do not reflexively expose every new member either: exposure is a public-API
commitment, so confirm the member is actually meant for Python before mapping it.
2. Renaming or removing a C++ member leaves a stale `_Blue.cpp` entry that breaks the build.
Update the `_Blue.cpp` in the same change.
3. Exposed names AND their semantics are a public API with consumers outside this repo. Before
renaming, removing, or reducing an exposed attribute to a no-op, check the consuming python
codebases (grep the consuming game workspace if you have one); if you cannot check, flag the
change as potentially breaking in the PR. A silently no-op'd attribute passes every build here
and breaks consumers you cannot see.

Match the style of nearby `_Blue.cpp` files when adding entries.

## Reaching the game

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I'm aware that there's still a lot of reliance on any game branch, I would refrain from tying the component even closer to a branch. That's only going to increase the work when the eventual decoupling has to happen. After all, people outside Fenris Creations don't have access to these branches, but they may well use Claude. 🙂


Built modules carry a flavor postfix (for example `_trinity_dx12_trinitydev.pyd`). A host
application importing the unsuffixed module name fails with `ModuleNotFoundError` unless it is
launched in the matching build flavor.

With `INSTALL_TO_MONOLITH=ON` the build installs into a vendor-style layout consumed by the game
workspace. For a local build to actually load there, the workspace's `carbon.json` must pin
carbon-trinity to the local drop (not a tagged release), and the workspace's `updateBinaries.py`
step must be re-run after every build to copy the DLLs into the runtime location.

Stale deployed DLLs are the most common cause of "my change isn't showing in-game". Verify the
deployed binaries are fresh (and the `carbon.json` pin is `local`) before debugging the code.

## Verification

Before declaring a change complete:

1. Build all three Windows targets: `trinity_dx11 trinity_dx12 trinity_stub`. A single-target
build misses stale `_Blue.cpp` entries and per-backend breaks.
2. When touching `trinityal/` or `shadercompiler/`, build and run the matching GoogleTest targets
(for example `TrinityALTest_dx11`, `TrinityALTest_dx12`).
3. clang-format the diff before pushing; CI (`.github/workflows/cpp_format.yml`) hard-fails PRs
on formatting.
4. `trinity/` itself has no unit tests. Rendering changes are verified visually in a host
application; a successful compile is not verification. Report explicitly what was and was not
verified.

## Gotchas

- An effect's resolved path may end in a suffix like `.sm_depth`. This is internal permutation
naming, not technique semantics: `sm` does not mean shadow-map or depth-only.
- An effect with empty `parameters` / `resources` / `options` lists is not necessarily broken;
minimal vertex-driven shaders legitimately expose nothing. Cross-check `effectResource.isGood`
and whether the object actually renders before concluding a shader is missing.