This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
R3D-cs is a .NET/C# binding library for r3d, an advanced 3D rendering library built on top of raylib. The project consists of:
- R3D-cs - The main bindings library (auto-generated + manual utilities)
- R3D-cs.GenerateBindings - Tool to generate C# bindings from r3d C headers
- Examples - Sample applications demonstrating r3d features
The bindings are partially auto-generated from the r3d C headers:
-
Auto-generated files (
.g.cssuffix):enums/**/*.g.cs- C enums mapped to C# enumstypes/**/*.g.cs- C structs mapped to C# structsinterop/**/*.g.cs- P/Invoke declarations for C functions- These files have "Do not edit manually" headers
-
Hand-written files (no
.g.cssuffix):R3D-cs/interop/R3D.Utilities.cs- Helper methods, constants, and convenience wrappers- Contains
MATERIAL_BASE,DECAL_BASE,PROCEDURAL_SKY_BASEconstants - Contains
MapInstances<T>(),SetEnvironmentEx(),CreateMeshData()utility methods
IMPORTANT: When r3d structs change (like Material or InstanceBuffer), you MUST update:
- Auto-generated files via bindings generator
R3D.Utilities.csconstants manually (they won't auto-update)- Native DLLs in
runtimes/win-x64/native/
The project uses [assembly: DisableRuntimeMarshalling] in R3D.core.g.cs for performance. This means:
- Structs must have
[StructLayout(LayoutKind.Sequential)] - Fixed buffers work correctly with
LibraryImport(preferred overDllImport) - Structs with complex layouts may need special handling
Known Issue: Functions returning structs by value with fixed buffers may fail if the native DLL is out of sync with C# struct definitions. Symptoms: fields appear to have wrong values (e.g., Capacity reads from wrong offset).
The C# bindings and native DLLs (r3d.dll, raylib.dll, assimp-vc145-mt.dll) MUST match:
C# Binding (*.g.cs) <---> C Header (*.h) <---> Compiled DLL (*.dll)
^ ^ ^
| | |
Generated from Source of truth Built from
Production: CI automatically builds matching DLLs for all platforms during release. Development: You may need to build DLLs locally to test binding changes before CI runs.
If they don't match, you'll see:
- Crashes with access violations (
0xC0000005) - Struct fields containing garbage data
- Wrong function signatures causing stack corruption
dotnet build R3D-cs.sln# Run specific example
cd Examples
dotnet run -- <example-name> # e.g., shader, lights, transparency
# Run all examples sequentially
dotnet runThe r3d source is pinned as a git submodule at External/r3d. To update:
cd External/r3d
git fetch origin
git checkout <new-tag-or-commit> # e.g. git checkout v0.9
cd ../..
git add External/r3dAfter updating you should regenerate bindings and rebuild native libraries (see below).
When to regenerate:
- r3d library updates its C API
- New functions/structs/enums are added
- Existing struct layouts change
Steps:
cd R3D-cs.GenerateBindings
# Uses External/r3d submodule by default (no -p needed)
dotnet run
# Or specify a different r3d repository path
dotnet run -- -p /path/to/r3d
# Override version detection
dotnet run -- -v 0.8.0After regeneration, you MUST:
- Update
R3D.Utilities.csifMaterial,Decal, or other constants changed - Rebuild r3d native library for local development/testing (see below)
Important: Building native libraries is only needed for local development and testing.
- DO NOT commit DLLs in
runtimes/folder to git - CI builds them automatically for all platforms during release
- Only build locally when you need to test bindings changes immediately
# Build from the submodule
cd External/r3d
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \
-DR3D_ASSIMP_VENDORED=ON -DR3D_RAYLIB_VENDORED=ON ..
cmake --build . --config Release -j8
# Copy DLLs to R3D-cs for local testing (DO NOT COMMIT)
cp bin/Release/*.dll ../../../R3D-cs/runtimes/win-x64/native/Note: Native DLLs in runtimes/*/native/ are already in .gitignore and will not be committed. CI automatically builds and packages them for all platforms during the release process.
Maps C types to C# equivalents:
- Primitives:
int→int,float→float,bool→bool - Pointers:
Type*→Type*(unsafe),const char*→string - Pointer-to-pointer:
Type**→Type**(NOTref Type) const char**/const char*[]→byte**- Function pointers (
void (*)()) →IntPtr - Arrays:
Type[N]→fixed Type Field[N] char[N]→internal fixed byte _field[N]+stringproperty (auto UTF-8)- Opaque types: Empty structs or unions with no fields (e.g.,
ScreenShader,AnimationTreeNode)
GetParameterModifier() logic:
Get*functions with primitive pointers →outparameters- Single pointers to structs →
refparameters - Pointer-to-pointer (
**) → kept as-is (raw pointer) - Opaque handles → kept as pointers
Extracts Doxygen-style comments from C headers and converts to C# XML doc comments:
@brief→<summary>@param→<param>@return→<returns>@warning→<remarks><b>Warning:</b>
Generates four file types:
- Enums (
enums/) - StripsR3D_prefix, converts to PascalCase, stripsMode/Type/Statussuffixes from values - Structs (
types/) - Addsunsafeif contains pointers/fixed buffers- Typed pointer + count/capacity pairs → internal pointer + public
Span<T>property (prefersCapacityoverCount) char[N]fixed buffers → internal backing field + publicstringproperty with UTF-8 get/set- Callback fields (ending in
Callback) →IntPtr
- Typed pointer + count/capacity pairs → internal pointer + public
- Misc (
types/) - Callback delegates ([UnmanagedFunctionPointer]), opaque handle types, macro-based enums - P/Invoke (
interop/) - Groups by C header file, usesLibraryImport
Symptom: Objects don't render or render incorrectly after r3d update
Cause: Material struct changed but MATERIAL_BASE in R3D.Utilities.cs wasn't updated
Fix: Compare Material.g.cs with MATERIAL_BASE and add missing fields with defaults
Symptom: Crash when calling functions like SetScreenShaderChain
Cause: Bindings generator converted Type** to ref Type
Fix: Should be auto-fixed by TypeMapper.cs check for EndsWith("**"), but verify binding
Symptom: Capacity shows 1 or garbage value instead of expected count
Cause: Native DLL built with different R3D_INSTANCE_ATTRIBUTE_COUNT than C# bindings
Fix: Rebuild r3d library and update DLLs
Symptom: Accessing positions array throws index out of bounds
Cause: Span constructor was given byte count instead of element count
Fix: new Span<T>(ptr, buffer.Capacity) NOT buffer.Capacity * sizeof(T)
External/
└── r3d/ # Git submodule — upstream r3d source (headers + build system)
R3D-cs/
├── enums/ # Auto-generated enums (*.g.cs)
├── types/ # Auto-generated structs (*.g.cs)
├── interop/ # Auto-generated P/Invoke + R3D.Utilities.cs
└── runtimes/ # Native DLLs per platform
└── win-x64/native/
├── r3d.dll
├── raylib.dll
└── assimp-vc145-mt.dll
R3D-cs.GenerateBindings/
├── Program.cs # CLI entry point
├── CodeGenerator.cs # Main generation logic
├── TypeMapper.cs # C to C# type mapping
├── CommentGenerator.cs # Doxygen to XML doc conversion
└── StringHelpers.cs # Naming conventions
Examples/
├── Program.cs # Example runner
└── *.cs # Individual example files
End-to-end checklist for updating bindings when a new r3d version is released:
cd External/r3d
git fetch origin --tags
git checkout <new-tag> # e.g. git checkout v0.9
cd ../..Compare headers between versions to understand API changes:
cd External/r3d
git diff <old-tag>..<new-tag> --stat -- 'include/*.h'Look for: new headers, removed headers, renamed types/functions, changed struct layouts.
Common issues when new C patterns appear:
- New opaque types: unions or empty structs auto-detected, but verify
- New callback typedefs: auto-generated as delegates; parameter names that are C# keywords need
EscapeIdentifier - New type patterns: e.g.,
char**, function pointer params — check TypeMapper handles them
cd R3D-cs.GenerateBindings
dotnet run -- -v <version> # e.g. -v 0.9.0R3D.Utilities.cs: Update constants (MATERIAL_BASE,PROCEDURAL_SKY_BASE,DECAL_BASE) if struct layouts changed. Add convenience overloads for new shader types (e.g.,SetSkyShaderUniform<T>).- Examples: Update for any renamed APIs. Port new upstream examples from
External/r3d/examples/. Copy any new resources (models, shaders) from upstream.
dotnet build R3D-cs.slnFix any compilation errors from API renames in examples or utilities.
git add -A
git commit -m "Update bindings to r3d <version>"
git tag v<version>
git push origin master --tagsAfter CI passes (builds native DLLs and publishes NuGet package):
gh release create v<version> --title "v<version>" --notes "<release notes>"Follow the format of previous releases (see gh release view v0.8.2 for reference). Include: what changed, breaking changes, install instructions, commit list, and full changelog link.
Update the binding version in the r3d README bindings table:
- Update
README.mdin your fork (graphnode/r3d) with the new version number - Open a PR to
Bigfoot71/r3d(see PR #193 as reference)
- Raylib-cs (7.0.2): C# bindings for raylib, required dependency
- CppAst (0.24.0): C/C++ parser for header files (bindings generator only)
- r3d native libraries: Included in
runtimes/, built from r3d source (pinned viaExternal/r3dsubmodule)