[{"t":"Cinder documentation","s":"Start","h":"Cinder documentation","u":"/docs/","x":"Cinder 0.1.0 is a 3D game engine written in Mojo 1.2.0 over one C11 native shim. These pages are imported verbatim from the engine repository; the status report is the source of truth for what has been verified. Every page is also available as raw Markdown at its path plus .md , and the whole set is indexed for AI agents in /llms.txt ."},{"t":"Cinder documentation","s":"Start","h":"Start","u":"/docs/#start","x":"Build your first game : This tutorial builds a small game on Cinder from nothing: a red ball you push around a lit ledge to knock six crates off the edge. It takes about an hour. The finished code is in examples/first_game/ and is compiled and run headless by tests/examples/test_first_game.mojo, so… First game: complete source : The finished code of the tutorial, copied verbatim from examples/first_game/ in the engine repository. The engine's test tests/examples/test_first_game.mojo compiles it and runs it headless, so it builds under the pinned toolchain (Mojo 1.2.0). Each # Step N comment marks… Sample game entry point : sample/main.mojo from the engine repository, verbatim: the entry poin"},{"t":"Cinder documentation","s":"Start","h":"Guides","u":"/docs/#guides","x":"Subsystem guides : One guide per engine module: key types and ownership, setup, per-frame use, threading rules and pitfalls. Each links to its generated API reference. Animation guide (cinder/anim) : cinder/anim is skeletal animation written in Mojo: skeletons built from glTF skins or read from cooked .cskel files, clip sampling into local poses, blend trees evaluated on a shared phase, animators with states, crossfades, events and root motion, and an AnimationSystem that… Application runtime (cinder/app) : cinder/app is the top of the engine: App owns every subsystem, runs the fixed-step game loop and renders, either into an SDL window or, with --headless, into an offscreen texture that can"},{"t":"Cinder documentation","s":"Start","h":"Engine design","u":"/docs/#engine-design","x":"Cinder architecture : Dependencies point downward only. A module may import from the layers below it, never from the same layer except where noted, and never from above. Threading model : Fixed by section 4 of the brief; this page records how it is realised and which shared state exists. Mojo 1.2.0 notes for Cinder : Source of truth: https://mojolang.org/docs (manual and std reference for 1.2.0) and the release notes for 1.0.0, 1.1.0 and 1.2.0. Everything below was read from those pages on 2026-11-08 and confirmed by compiling spikes/hello/ under the pinned toolchain. When the compiler… Toolchain : Every version below is pinned. Changing one requires an ADR and a green CI run. Render validat"},{"t":"Cinder documentation","s":"Start","h":"Decisions","u":"/docs/#decisions","x":"Architecture decision records : Every significant build-versus-reuse, layering and deviation decision, with context and consequences. Deviations from the original brief each point to one of these. ADR-0001: Build-versus-reuse decisions for the dependency stack : Status: Accepted, 2026-11-08 ADR-0002: No MAX / max.gpu for rendering; GPU compute is WGSL : Status: Accepted, 2026-11-08 ADR-0003: One C11 shim owning every callback; Mojo pulls events : Status: Accepted, 2026-11-08 (callback-in-Mojo question deferred to spike 4) ADR-0004: Building wgpu-native from source through CMake : Status: Accepted, 2026-11-08 ADR-0005: Generating the Mojo bindings : Status: Accepted, 2026-11-08 (decided by sp"},{"t":"Cinder documentation","s":"Start","h":"API reference","u":"/docs/#api-reference","x":"API reference : Generated by tools/gen_api_docs.py from the docstrings in cinder/; do not edit by hand. cinder.anim API : Source: cinder/anim/ init .mojo cinder.app API : Source: cinder/app/ init .mojo cinder.assets API : Source: cinder/assets/ init .mojo cinder.audio API : Source: cinder/audio/ init .mojo cinder.core API : Source: cinder/core/ init .mojo cinder.ecs API : Source: cinder/ecs/ init .mojo cinder.gfx API : Source: cinder/gfx/ init .mojo cinder.input API : Source: cinder/input/ init .mojo cinder.physics API : Source: cinder/physics/ init .mojo cinder.platform API : Source: cinder/platform/ init .mojo cinder.render API : Source: cinder/render/ init .mojo cinder.scene API : Source:"},{"t":"Cinder documentation","s":"Start","h":"Project","u":"/docs/#project","x":"Deliverable report : Living document. Every claim below was verified in this repository under the pinned toolchain on the date given; nothing is reported from memory or intention. Phases are those of the mission brief. Status : Last updated: 2026-11-09. One row per phase; \"verified\" means executed in this repository under the pinned toolchain, with the command named. Nothing here is claimed from memory. Changelog : All notable changes to Cinder. The format follows Keep a Changelog; versions follow semantic versioning. Dates are the day the phase gate passed on Linux x86-64 (REPORT.md has the runs). Engine README : A 3D game engine written in Mojo 1.2 over a single C11 native shim (wgpu-nativ"},{"t":"Cinder documentation","s":"Start","h":"Using Cinder with AI agents","u":"/docs/#using-cinder-with-ai-agents","x":"Using Cinder with AI coding agents : a ready-to-use agent context file, prompts, the llms.txt index and the MCP endpoint."},{"t":"Build your first game","s":"Start","h":"Build your first game","u":"/docs/tutorial/","x":"This tutorial builds a small game on Cinder from nothing: a red ball you push around a lit ledge to knock six crates off the edge. It takes about an hour. The finished code is in examples/first_game/ and is compiled and run headless by tests/examples/test_first_game.mojo , so every snippet below is code that builds under the pinned toolchain. Each # Step N comment in examples/first_game/game.mojo marks where that step's code lives. What you will use: the App runtime and Game trait ( cinder/app ), the ECS world and its built-in components ( cinder/ecs , cinder/scene ), the physics world ( cinder/physics ), the renderer's meshes, materials and lights ( cinder/render ), input actions ( cinder/i"},{"t":"Build your first game","s":"Start","h":"0. Prerequisites","u":"/docs/tutorial/#0-prerequisites","x":"Follow the README quick start once: the toolchain container, pixi run native , and pixi run test green. Every command below runs inside the container ( tools/in_container.sh ... ), on Linux x86-64. bash tools/in_container.sh pixi run example # the finished game, windowed tools/in_container.sh pixi run example -- --headless --frames 120 --screenshot .cache/first.png"},{"t":"Build your first game","s":"Start","h":"1. An App and a Game","u":"/docs/tutorial/#1-an-app-and-a-game","x":"A Cinder program is an App that owns every subsystem (window or offscreen target, GPU device, renderer, ECS world, physics, animation, audio, input, debug UI) and a Game , a struct you write that implements four methods. App.run_frame calls them in a fixed order every frame: Method When Use it for start(mut self, mut app) once, on the first frame loading content, spawning entities, binding input fixed_update(mut self, mut app, dt) zero or more times per frame, at AppConfig.fixed_hz (60 Hz) anything that reads or drives physics update(mut self, mut app, dt) once per frame, variable dt timers, camera, UI state draw_ui(mut self, mut app) once per frame, inside an ImGui frame HUD, menus examples"},{"t":"Build your first game","s":"Start","h":"2. A ledge, crates and a ball","u":"/docs/tutorial/#2-a-ledge-crates-and-a-ball","x":"Content is entities in app.world . Visible things carry a LocalTransform (or a RigidBody , which writes the transform for you) and a MeshRenderer that names a mesh and a material registered with the renderer. Physics objects live in app.physics[] and are linked to entities by the RigidBody component. Register the meshes and materials once. cube and plane are built-in primitives; the second add_mesh argument says whether to build LODs with meshoptimizer: mojo var floor_mesh = app.renderer[].add_mesh(String( \"ledge\" ), plane( LEDGE_HALF , 1 ), False ) var box_mesh = app.renderer[].add_mesh(String( \"box\" ), cube( 0.5 ), True ) var grey = app.renderer[].add_material(Material(String( \"grey\" ))) v"},{"t":"Build your first game","s":"Start","h":"3. A sun and a camera","u":"/docs/tutorial/#3-a-sun-and-a-camera","x":"Lights and cameras are entities too. A LightSource shines down its entity's -Z axis, so a directional light is placed with a look-at matrix; the last argument asks for cascaded shadow maps. The camera is the entity with an active CameraComponent ; its transform is the camera's world matrix (the inverse of a view matrix): mojo var sun = app.world.spawn() app.world.add[LocalTransform](sun, LocalTransform( Mat4.look_at(Vec3( 6.0 , 10.0 , 4.0 ), Vec3.zero(), Vec3.unit_y()).inverse())) app.world.add[LightSource](sun, LightSource( LightKind.directional(), Vec3( 1.0 , 0.95 , 0.9 ), 3.0 , 0.0 , 0.0 , 0.0 , True )) var camera = app.world.spawn() app.world.add[LocalTransform](camera, LocalTransform( M"},{"t":"Build your first game","s":"Start","h":"4. Input as actions and axes","u":"/docs/tutorial/#4-input-as-actions-and-axes","x":"Gameplay never reads keys directly. It binds named axes and actions in start , and reads them by name; the same names can be rebound at runtime ( InputMap.listen_action ) and saved as text ( InputMap.save ). Keys are SDL scancodes: mojo app.input.bind_axis(String( \"move_x\" ), Binding.key( KEY_D , 1.0 )) app.input.bind_axis(String( \"move_x\" ), Binding.key( KEY_A , - 1.0 )) app.input.bind_axis(String( \"move_z\" ), Binding.key( KEY_S , 1.0 )) app.input.bind_axis(String( \"move_z\" ), Binding.key( KEY_W , - 1.0 )) app.input.bind_axis(String( \"move_x\" ), Binding.gamepad_axis( 0 , 1.0 )) app.input.bind_action(String( \"jump\" ), Binding.key( KEY_SPACE )) app.input.bind_action(String( \"restart\" ), Bindi"},{"t":"Build your first game","s":"Start","h":"5. Pushing the ball: the fixed step","u":"/docs/tutorial/#5-pushing-the-ball-the-fixed-step","x":"Everything that drives physics goes in fixed_update , which runs at exactly 60 Hz however fast the screen refreshes, so the game behaves the same on every machine (and the headless test can replay it). Forces are per step; impulses are instantaneous: mojo def fixed_update ( mut self, mut app: App, dt: Float32) raises : if self .won: return var push = Vec3(app.input.axis(String( \"move_x\" )), 0.0 , app.input.axis(String( \"move_z\" ))) if push.length() > 1.0 : push = push.normalized() app.physics[].add_force( self .ball, push * PUSH_FORCE ) if app.input.just_pressed(String( \"jump\" )) and self .ball_grounded(app): app.physics[].add_impulse( self .ball, Vec3( 0.0 , 80.0 , 0.0 )) self .fallen = 0 f"},{"t":"Build your first game","s":"Start","h":"6. Per-frame logic","u":"/docs/tutorial/#6-per-frame-logic","x":"update runs once per rendered frame with the real elapsed time. It is the place for timers, camera smoothing and anything cosmetic: mojo def update ( mut self, mut app: App, dt: Float32) raises : if not self .won: self .elapsed += dt if app.input.just_pressed(String( \"restart\" )): self .restart(app)"},{"t":"Build your first game","s":"Start","h":"7. A HUD","u":"/docs/tutorial/#7-a-hud","x":"draw_ui is called inside an ImGui frame. The bindings are the raw dcimgui C API on app.lib.ptr() , and ImGui wants NUL-terminated C strings that stay alive for the frame, which is what CStrings does (clear it at the start of each draw_ui ): mojo def draw_ui ( mut self, mut app: App) raises : self .ui.clear() var lib = app.lib.ptr() lib[].ImGui_SetNextWindowPos(ImVec2( 12.0 , 12.0 ), Int32(ImGuiCond_Always)) if lib[].ImGui_Begin( self .ui.c(String( \"hud\" )), Optional[Pointer[Bool, MutUntrackedOrigin]](), Int32(ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoInputs)): lib[].ImGui_TextUnformatted( self .ui.c( String(t \"crates knocked off {self.fallen} / {C"},{"t":"Build your first game","s":"Start","h":"8. Cleaning up","u":"/docs/tutorial/#8-cleaning-up","x":"Shapes and bodies are owned by whoever created them. shutdown destroys the bodies and then the shapes, and main calls it before the game and the app go out of scope. The headless test checks app.physics[].body_count() == 0 afterwards, and App itself asserts that no GPU resource leaked when it is destroyed. mojo def shutdown ( mut self, mut app: App): for b in self .bodies: app.physics[].destroy_body(b) for s in self .shapes: app.physics[].destroy_shape(s)"},{"t":"Build your first game","s":"Start","h":"9. Running it headless, and testing it","u":"/docs/tutorial/#9-running-it-headless-and-testing-it","x":"--headless renders to an offscreen texture and reads it back; --frames N stops after N frames; --screenshot PATH writes the last frame. That is how the test drives the game without a keyboard: it lines the ball up behind each crate and applies the same force the move axis would, checks that every crate drops off and the game reports won , restarts, reads a frame and checks that the GPU reported no validation errors. bash tools/in_container.sh pixi run mojo run -I . -I examples tests/examples/test_first_game.mojo"},{"t":"Build your first game","s":"Start","h":"Where to go next","u":"/docs/tutorial/#where-to-go-next","x":"Load real content: cinder/assets imports glTF ( load_gltf , upload_gltf ) and cinder/scene.spawn_gltf turns a file into entities; docs/guides/assets.md . A walking character instead of a ball: CharacterController in cinder/physics and an Animator with a blend tree in cinder/anim ; the sample game ( sample/ ) does both. Sound: AudioEngine buses, one-shots and ECS AudioSource s; docs/guides/audio.md . Saving: make your components SceneComponent s and write the world with SceneRegistry ( docs/guides/scene.md ); the sample's F5/F9 save and load are forty lines. Performance: docs/BENCHMARKS.md , and tests/perf/test_zero_alloc.mojo for the rule that a steady-state frame allocates nothing."},{"t":"First game: complete source","s":"Start","h":"First game: complete source","u":"/docs/examples/first-game/","x":"The finished code of the tutorial , copied verbatim from examples/first_game/ in the engine repository. The engine's test tests/examples/test_first_game.mojo compiles it and runs it headless, so it builds under the pinned toolchain (Mojo 1.2.0). Each # Step N comment marks where that tutorial step's code lives. bash tools/in_container.sh pixi run example # windowed tools/in_container.sh pixi run example -- --headless --frames 120 --screenshot .cache/first.png"},{"t":"First game: complete source","s":"Start","h":"examples/first_game/main.mojo","u":"/docs/examples/first-game/#examplesfirst_gamemainmojo","x":"mojo \"\"\"Entry point of the tutorial game (docs/TUTORIAL.md, step 1). pixi run example [--headless] [--frames N] [--screenshot PATH] \"\"\" from std.sys import argv from cinder.app import App, AppConfig, parse_app_args from cinder.core.image import save_png from first_game import FirstGame def main () raises : var args = List[String]() var raw = argv() for i in range ( 1 , len (raw)): args.append(String(raw[i])) var config = AppConfig(String( \"First game\" ), 1280 , 720 , False ) config.audio = False var rest = parse_app_args(args, config) var max_frames = - 1 var screenshot = String() var i = 0 while i &#x3C; len (rest): if rest[i] == \"--frames\" and i + 1 &#x3C; len (rest): max_frames = Int(rest"},{"t":"First game: complete source","s":"Start","h":"examples/first_game/game.mojo","u":"/docs/examples/first-game/#examplesfirst_gamegamemojo","x":"mojo \"\"\"The game built in docs/TUTORIAL.md: a ball you push around a lit floor to knock crates off a ledge. Every numbered step of the tutorial is a `# Step N` comment in this file.\"\"\" from std.memory import Pointer from cinder.app import App, Game from cinder.core.math.mat4 import Mat4 from cinder.core.math.quat import Quat from cinder.core.math.vec import Vec3, Vec4 from cinder.ecs import Entity, LocalTransform from cinder.ffi.cinder_native import ( ImGuiCond_Always, ImGuiWindowFlags_AlwaysAutoResize, ImGuiWindowFlags_NoDecoration, ImGuiWindowFlags_NoInputs, ImVec2, ) from cinder.input import Binding from cinder.physics import BodyDesc, MotionType, RigidBody, Shape from cinder.render impor"},{"t":"First game: complete source","s":"Start","h":"examples/first_game/__init__.mojo","u":"/docs/examples/first-game/#examplesfirst_game__init__mojo","x":"mojo \"\"\"The tutorial game from docs/TUTORIAL.md.\"\"\" from .game import CRATE_COUNT , PUSH_FORCE , FirstGame"},{"t":"Sample game entry point","s":"Start","h":"Sample game entry point","u":"/docs/examples/sample-game/","x":"sample/main.mojo from the engine repository, verbatim: the entry point of the third-person sample game ( ADR-0014 ). The game itself lives in sample/game/ (components, levels, settings, gameplay) and is built only on public engine APIs. bash tools/in_container.sh pixi run sample # windowed: arena level, WASD/Space, Esc for the menu tools/in_container.sh pixi run sample -- --level sponza # the Khronos atrium with a mesh collider tools/in_container.sh pixi run sample-headless # offscreen autopilot run"},{"t":"Sample game entry point","s":"Start","h":"sample/main.mojo","u":"/docs/examples/sample-game/#samplemainmojo","x":"mojo \"\"\"Cinder sample game entry point. pixi run sample [--headless] [--level arena|sponza] [--autopilot] [--frames N] [--profile] [--screenshot PATH] `--headless` renders offscreen (tests, benchmarks); `--frames N` stops after N frames; `--screenshot PATH` writes the last headless frame as a PNG. \"\"\" from std.sys import argv from cinder.app import App, AppConfig, parse_app_args from cinder.core.image import save_png from game import SampleGame def main () raises : var args = List[String]() var raw = argv() for i in range ( 1 , len (raw)): args.append(String(raw[i])) var config = AppConfig(String( \"Cinder sample\" ), 1280 , 720 , False ) var rest = parse_app_args(args, config) var level = Str"},{"t":"Using Cinder with AI coding agents","s":"Start","h":"Using Cinder with AI coding agents","u":"/agents/","x":"Cinder's documentation is built to be read by agents as well as people. Everything on this site is available as plain Markdown or JSON, there is a ready-made context file for coding agents, and a read-only MCP server answers documentation queries. Everything here is generated from the engine repository at commit 47c9352 (Cinder 0.1.0)."},{"t":"Using Cinder with AI coding agents","s":"Start","h":"1. Drop the agent brief into your repository","u":"/agents/#1-drop-the-agent-brief-into-your-repository","x":"The brief covers what Cinder is and its honest status, the container build workflow and every pixi task you need, the layering rules, the zero-allocation rule and the Mojo 1.2.0 pitfalls that the engine's own notes record. Claude Code reads CLAUDE.md ; Codex, Cursor, Aider and most other agents read AGENTS.md . The content is identical. bash curl -fsSL https://cinder.ash10.dev/agents/AGENTS.md -o AGENTS.md curl -fsSL https://cinder.ash10.dev/agents/CLAUDE.md -o CLAUDE.md Download: AGENTS.md · CLAUDE.md . The full text is at the end of this page ."},{"t":"Using Cinder with AI coding agents","s":"Start","h":"2. Point the agent at the docs","u":"/agents/#2-point-the-agent-at-the-docs","x":"What URL Use it for llms.txt index https://cinder.ash10.dev/llms.txt A map of every page with one-line summaries, per the llms.txt convention. Full text https://cinder.ash10.dev/llms-full.txt Every page plus the brief in one file, for long-context models. Any page as Markdown page URL without the trailing slash plus .md , e.g. /docs/guides/ecs.md Fetching exactly the guide or API page the task needs. JSON index https://cinder.ash10.dev/docs/index.json Pages, headings and 1306 API symbols with signatures, summaries and anchors. Search index https://cinder.ash10.dev/search-index.json Section-level plain text, for local retrieval. MCP https://cinder.ash10.dev/mcp Search and page tools over Stre"},{"t":"Using Cinder with AI coding agents","s":"Start","h":"3. Connect the MCP server","u":"/agents/#3-connect-the-mcp-server","x":"The site runs a small, stateless, read-only MCP server next to the static pages. It needs no key and stores nothing. Tools: search_docs (ranked hits across pages, headings and API symbols), get_page (a page or one section as Markdown), get_symbol (signature and summary of an API symbol, e.g. CommandBuffer or cinder.physics.character.CharacterController ) and list_pages . Every page is also exposed as an MCP resource. Claude Code: bash claude mcp add --transport http cinder-docs https://cinder.ash10.dev/mcp Clients configured with JSON (Cursor, Claude Desktop through a remote connector, and others): json { \"mcpServers\" : { \"cinder-docs\" : { \"type\" : \"http\" , \"url\" : \"https://cinder.ash10.dev/"},{"t":"Using Cinder with AI coding agents","s":"Start","h":"4. Prompts that work","u":"/agents/#4-prompts-that-work","x":"Each prompt assumes the brief is in the repository root. , then fill in the brackets. Start a new game on Cinder: Read AGENTS.md, then https://cinder.ash10.dev/docs/tutorial.md and https://cinder.ash10.dev/docs/examples/first-game.md. Create a new game in games/[name]/ modelled on examples/first_game: a struct implementing the Game trait (start, fixed_update, update, draw_ui) run by cinder.app.App. The game is: [describe it]. Use only public engine APIs, keep physics in fixed_update, keep every per-frame path allocation-free, add a headless test like tests/examples/test_first_game.mojo, and run it with tools/in_container.sh. Report exactly which commands you ran and their results. Add a feat"},{"t":"Using Cinder with AI coding agents","s":"Start","h":"5. Ground rules to give any agent","u":"/agents/#5-ground-rules-to-give-any-agent","x":"Cinder is pinned to Mojo 1.2.0. Much of what models remember about Mojo ( fn , let , alias , inout , UnsafePointer , @parameter if ) is removed or banned here. Linux x86-64 is the only verified platform. Do not let an agent claim macOS support or performance budgets: the status report is the source of truth. Mojo has no Windows build; on Windows everything runs through tools/in_container.sh . The generated bindings in cinder/ffi and the generated API reference in docs/api are never edited by hand."},{"t":"Using Cinder with AI coding agents","s":"Start","h":"The brief","u":"/agents/#the-brief","x":"This is the exact content of /agents/AGENTS.md . markdown # AGENTS.md: working on or with the Cinder engine Context file for AI coding agents (Claude Code reads it as `CLAUDE.md` ; Codex, Cursor and others read `AGENTS.md` ). Drop it at the root of a Cinder checkout or of a game built on Cinder. Generated by https://cinder.ash10.dev from the engine documentation at commit 47c9352 (Cinder 0.1.0, imported 2026-11-09). When this file and the engine repository disagree, the repository wins; re-download this file. ## What Cinder is A 3D game engine written in **Mojo 1.2.0** (pinned; nightlies are never used) over one C11 shim, `libcinder_native` , wrapping wgpu-native (WGSL), SDL3, Jolt (through "},{"t":"Subsystem guides","s":"Guides","h":"Subsystem guides","u":"/docs/guides/","x":"One guide per engine module: key types and ownership, setup, per-frame use, threading rules and pitfalls. Each links to its generated API reference. Animation guide (cinder/anim) : cinder/anim is skeletal animation written in Mojo: skeletons built from glTF skins or read from cooked .cskel files, clip sampling into local poses, blend trees evaluated on a shared phase, animators with states, crossfades, events and root motion, and an AnimationSystem that… Application runtime (cinder/app) : cinder/app is the top of the engine: App owns every subsystem, runs the fixed-step game loop and renders, either into an SDL window or, with --headless, into an offscreen texture that can be read back. Game"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"Animation guide ( cinder/anim )","u":"/docs/guides/anim/","x":"cinder/anim is skeletal animation written in Mojo: skeletons built from glTF skins or read from cooked .cskel files, clip sampling into local poses, blend trees evaluated on a shared phase, animators with states, crossfades, events and root motion, and an AnimationSystem that advances every animator as its own job and publishes JointPalette components for the render bridge. There is no native code in the path; the only C involved is the glTF importer that produces the clips. In the module map ( docs/ARCHITECTURE.md ) the package sits beside physics and render . It imports cinder/core (math, jobs), cinder/assets ( Clip , ClipChannel , GltfAsset , the cooked-file Cursor ), cinder/ecs and cinde"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"Units and conventions","u":"/docs/guides/anim/#units-and-conventions","x":"Clip times are seconds. A blend tree and an animator run on a normalised phase in [0, 1) ; leaf clips sample at phase * duration . Clips keep glTF node indices in their channels and Skeleton.node_to_bone maps them to bones, so clips from the same glTF file work with the skeleton of any of its skins. Bones are stored parents-first; joints lists the skin's joints in palette order. Matrices are column-major Mat4 ; rest transforms are Transform (translation, rotation, scale) in parent space. The palette is expressed in the mesh node's rest space: inverse(mesh node world) * bone world * inverse bind , the same convention the importer uses at the bind pose, so the render instance's transform is th"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"Skeletons","u":"/docs/guides/anim/#skeletons","x":"mojo from cinder.anim import Skeleton, read_skeleton, skeleton_from_gltf, write_skeleton from cinder.assets.gltf import load_gltf var asset = load_gltf(lib.ptr(), String( \"assets/samples/Fox/Fox.glb\" )) var skeleton = skeleton_from_gltf(asset, 0 ) # skin 0 print (skeleton.bone_count(), skeleton.joint_count()) # >= 24 bones, 24 joints for the Fox var spine = skeleton.find_bone(String( \"b_Spine01_02\" )) var bytes = write_skeleton(skeleton) # CSKL v1 var back = read_skeleton( bytes .copy()) # raises on bad magic, version or trailing bytes skeleton_from_gltf(asset, skin) includes every joint of the skin plus the ancestors needed to reach the outermost ancestor shared by all joints, so an animate"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"Poses and clip sampling","u":"/docs/guides/anim/#poses-and-clip-sampling","x":"A Pose is the skeleton's local transforms plus morph weights. Pose(skeleton) starts at the rest pose with zero weights; reset(skeleton) returns to it, copy_from(other) copies without allocating and blend_with(b, t) lerps in place (translation and scale linearly, rotation with Quat.nlerp along the shorter arc, weights linearly). mojo from cinder.anim import Pose, sample_clip, wrap_time from cinder.assets.cooked import Clip, ClipChannel var clip = Clip(String( \"synthetic\" )) clip.duration = 2.0 # node 0, translation (path 0), linear (interpolation 0), 3 components: (0,0,0) -> (2,0,0) over 2 s clip.channels.append(ClipChannel( 0 , 0 , 0 , 3 , _floats( 0.0 , 2.0 ), _floats( 0.0 , 0.0 , 0.0 , 2.0"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"Blend trees","u":"/docs/guides/anim/#blend-trees","x":"A BlendTree is a flat list of BlendNode s with a root , plus named parameters. Leaves are clips ( add_clip(clip_index, speed=1.0) ); inner nodes are add_blend_1d(parameter) or add_blend_2d(parameter_x, parameter_y) . add_child(node, child, at: Vec2) attaches a child with full weight at parameter value at (1D nodes use at.x() ; their children must be added in increasing order). A node takes at most MAX_CHILDREN = 8 children. A tree must be acyclic: add_child raises when the edge would close a cycle ( add_child(n, n) , or attaching an ancestor of n under n ); a node may still be shared by several parents. Finish with set_root ; validate(clip_count) runs when the tree becomes an animator state "},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"The animator","u":"/docs/guides/anim/#the-animator","x":"Animator(skeleton^, clips^) owns a skeleton, its clips, the current pose and the scratch it needs. States are AnimationState(name, tree^, looping=True) ; add_state validates the tree against the clip count and returns the state index, and add_clip_state(name, clip, looping, speed) is the one-leaf shortcut. find_state(name) looks an index up; set_parameter(name, value) sets the parameter on every state that declares it and raises if none does. mojo var animator = Animator(skeleton.copy(), clips.copy()) var survey = animator.add_clip_state(String( \"survey\" ), survey_index) var locomotion = animator.add_state(AnimationState(String( \"locomotion\" ), tree ^ )) animator.add_event(locomotion, 0.5 , "},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"The animation system and the render bridge","u":"/docs/guides/anim/#the-animation-system-and-the-render-bridge","x":"Animators live in AnimationSystem.animators ; add(animator^) returns the index and an entity refers to it with AnimatorRef(index) (component id 8). publish(world) writes a JointPalette (component id 9, cinder/scene/render_sync.mojo ) for every entity with an AnimatorRef : joints through palette_into and the pose's morph weights, in place when the component already exists, otherwise added. build_render_scene then copies JointPalette.joints into RenderScene.joints and gives the instance its joint_offset , so the skinned draw sees the matrices. __len__ is the animator count and clear() drops them all (a level restart), after which every AnimatorRef is stale and publish raises on it. mojo from c"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"Threading: one job per animator","u":"/docs/guides/anim/#threading-one-job-per-animator","x":"AnimationSystem.update(dt, jobs) builds one _AnimatorJob (animator pointer, dt , failure flag) per animator, keeps them in a persistent list that outlives the wait, and submits each to the engine job system with a shared Counter ; the caller then wait s on the counter, executing jobs itself while it waits. Each job touches only its own animator, so no synchronisation is needed beyond the counter, and the results are bit-identical to serial evaluation (the test runs 16 animators both ways and compares every palette matrix). With no job system, or with a single animator, the same jobs run inline one after the other. Both paths behave the same: an animator whose update raised (one that was neve"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"Pitfalls","u":"/docs/guides/anim/#pitfalls","x":"A Pose is tied to the skeleton it was built for; sample_clip , evaluate and blend_with index by bone and do no resizing. Children of a 1D node must be added with increasing thresholds or add_child raises; a clip node cannot take children. Animator.set_parameter raises when no state declares the parameter, so declare it on every tree that should react, or set states[i].tree.parameters[p] directly. update before play raises; AnimationSystem.update turns that into a failure after all other animators have run, with or without jobs, so play an animator before (or right after) adding it to the system. play of the current state does not restart it, even once a non-looping state has finished; use pl"},{"t":"Animation guide (cinder/anim)","s":"Guides","h":"See also","u":"/docs/guides/anim/#see-also","x":"docs/adr/0012-physics-animation-audio-input.md , animation decisions 1 to 6. docs/adr/0014-sample-game-and-performance.md , the zero-allocation frame and the _into convention. docs/THREADING.md , job data ownership rules; docs/ARCHITECTURE.md , the frame loop. docs/api/anim.md , the generated reference; docs/api/assets.md for Clip and the cooker. tests/anim/test_animation.mojo : sampling modes and loops; the Fox skeleton, .cskel round trip, 1D and 2D blend trees, phase sync and crossfades; events and root motion; the system on jobs versus serial, and a skinned render through the ECS bridge. docs/guides/physics.md for the character controller the sample drives its animator from."},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Application runtime ( cinder/app )","u":"/docs/guides/app/","x":"cinder/app is the top of the engine: App owns every subsystem, runs the fixed-step game loop and renders, either into an SDL window or, with --headless , into an offscreen texture that can be read back. Gameplay implements the Game trait and is called from App.run_frame . The sample game ( sample/ ), the tests under tests/app and tests/sample , the zero-allocation test and the benchmarks in bench/ all run this one loop. Source: cinder/app/app.mojo ( AppConfig , Game , App ), cinder/app/cli.mojo ( parse_app_args ). API reference: docs/api/app.md . Design: docs/adr/0013-tools-and-app-runtime.md (decisions 1 to 3), docs/adr/0014-sample-game-and-performance.md (zero allocations, benchmarks)."},{"t":"Application runtime (cinder/app)","s":"Guides","h":"AppConfig","u":"/docs/guides/app/#appconfig","x":"AppConfig(title=\"Cinder\", width=1280, height=720, headless=False) plus fields set afterwards: Field Default Meaning title , width , height \"Cinder\" , 1280, 720 Window title and size. In windowed mode App.width / height are replaced by the window's pixel size. headless False No window, no swapchain, no overlay, device-less audio; frames go to an offscreen RGBA8 target. fixed_hz 60.0 Simulation rate of the fixed step ( FixedStep(fixed_hz) , at most 8 steps per frame). render RenderSettings() Renderer settings (MSAA, shadows, AO, bloom, FXAA, ...). Tests set config.render.msaa = 1 . layers LayerConfig.default() Physics object layers and collision pairs. audio True Create the AudioEngine . debug"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"The Game trait","u":"/docs/guides/app/#the-game-trait","x":"mojo trait Game : def start ( mut self, mut app: App) raises : ... def fixed_update ( mut self, mut app: App, dt: Float32) raises : ... def update ( mut self, mut app: App, dt: Float32) raises : ... def draw_ui ( mut self, mut app: App) raises : ... start runs once, on the first run_frame . Create meshes, materials, bodies, buses, register scene components ( app.registry.register[T]() ) and load the input map here. fixed_update(dt) runs once per simulation step with dt = 1 / fixed_hz , after the physics step. update(dt) runs once per frame with the real (or overridden) frame time. draw_ui runs only in windowed mode with the overlay enabled, between DebugUi.begin_frame and DebugUi.draw ; ImGu"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"App ownership and destruction order","u":"/docs/guides/app/#app-ownership-and-destruction-order","x":"App is a Movable value whose fields are declared in destruction order, because Mojo destroys fields in declaration order ( docs/MOJO_NOTES.md ). Subsystems that hand out pointers to themselves are held in Boxed[T] cells ( cinder/core/boxed.mojo ): a heap allocation with a stable address, so the App value can move while Resources keeps pointing at the same Gfx . Access a boxed field with app.physics[] , app.renderer[] , app.gfx[] , app.res[] , app.jobs[] ; .ptr() gives the raw Pointer the subsystems themselves use. Declaration (and destruction) order, top to bottom: debug_ui: Optional[DebugUi] , assets: Optional[Boxed[AssetServer]] , anim , audio: Optional[AudioEngine] , physics_sync , bridge"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Construction","u":"/docs/guides/app/#construction","x":"mojo from cinder.app import App, AppConfig, Game var config = AppConfig(String( \"cinder headless test\" ), 160 , 120 , True ) config.render.msaa = 1 var app = App(config ^ ) App.__init__ loads the native library ( NativeLibrary() , path from CINDER_NATIVE_LIB or the default per OS), creates the profiler (started if config.profile ), clock, FixedStep , job system, log sink, registry, ECS world, input map, animation system, physics world (with config.layers ), physics sync, render bridge and the audio engine (headless when config.headless ). Then, windowed: Platform , Window , Gfx on the window's surface, Swapchain (whose format the renderer uses); headless: Gfx without a surface and RGBA8 as t"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"run_frame and the loop order","u":"/docs/guides/app/#run_frame-and-the-loop-order","x":"run_frame[G: Game](mut game: G, frame_seconds: Float64 = -1.0) -> Bool runs one frame and returns False once a quit was requested (window close, EventKind.quit , or request_quit() ). With frame_seconds >= 0 that value replaces the clock's measured delta, which is how tests and benchmarks get deterministic step counts ( 1.0 / 60.0 gives exactly one fixed step per frame). In order: First call only: game.start(app) , then the clock is reset so loading time does not become dt. dt = frame_seconds or clock.tick() . _poll : windowed apps pump SDL events; quit/close set quit_requested ; resize updates width / height , the swapchain and the camera aspect; events go to input.process unless ui_consumes"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Windowed vs headless; read_frame","u":"/docs/guides/app/#windowed-vs-headless-read_frame","x":"Both modes run the same code; headless differs only in what step 3 and step 11 touch. The offscreen target is an RGBA8 texture with RenderAttachment | CopySrc | TextureBinding usage and a map-readable buffer whose row stride is rounded up to READBACK_ALIGN = 256 bytes (a wgpu requirement; an unaligned copy aborts the process, docs/MOJO_NOTES.md ). read_frame() (headless only, raises otherwise) copies the target into the readback buffer, waits for the queue, maps it and returns an Image ( width , height , pixel(x, y) ): mojo var frame = app.read_frame() var lit = 0 for y in range (frame.height): for x in range (frame.width): var p = frame.pixel(x, y) if Int(p[ 0 ]) + Int(p[ 1 ]) + Int(p[ 2 ])"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Command line: parse_app_args","u":"/docs/guides/app/#command-line-parse_app_args","x":"parse_app_args(args, config) -> List[String] applies the engine options to config and returns what it did not consume, so every executable shares the same flags and keeps its own: Option Effect --headless config.headless = True --profile config.profile = True (start Tracy at construction) --no-audio config.audio = False --no-debug-ui config.debug_ui = False --width N , --height N positive integers, else raises --title TEXT window title A flag missing its value raises ( \"--width needs a value\" ). sample/main.mojo parses the rest itself ( --level , --autopilot , --frames N , --screenshot PATH , --verbose ) and raises on anything unknown: mojo var config = AppConfig(String( \"Cinder sample\" ), 1"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Timing and allocation accounting","u":"/docs/guides/app/#timing-and-allocation-accounting","x":"frame_ms : CPU time of the last run_frame , from after start to the end of the frame. physics_ms : time inside physics[].step summed over the frame's fixed steps. render_ms : time inside _render (scene build, submission, overlay, present call), measured on the CPU; it does not wait for the GPU. bench/stress.mojo and bench/sample_bench.mojo sample these three per frame into their JSON results ( docs/BENCHMARKS.md ). count_allocations turns on per-phase Mojo heap allocation counts, read through the shim's cn_alloc_counter_from_executable at phase boundaries. It needs libcinder_alloccount.so preloaded ( tools/run_tests.sh does this for tests/perf/test_zero_alloc.mojo ; without it every counter "},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Logging","u":"/docs/guides/app/#logging","x":"App.logs is a MemorySink ; everything the app learns about is appended there as LogRecord (level, category, message): SDL log lines ( Platform.take_diagnostics ), GPU diagnostics ( Gfx.take_diagnostics ), and your own app.log(LogLevel.error(), String(\"app\"), message) calls. The overlay's Log tab shows the last 200 records. Tests scan app.logs.records for LogLevel.error() . Audio diagnostics stay in AudioEngine.diagnostics and are not forwarded."},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Threading","u":"/docs/guides/app/#threading","x":"run_frame runs on the main thread: the SDL pump, presentation, ImGui and the subsystem calls all happen there ( docs/THREADING.md ). Work fans out from inside the subsystems: animation submits one job per animator to App.jobs , Jolt uses its own thread pool inside physics[].step , the asset server decodes on the job system, and the miniaudio device thread (windowed) mixes on its own and only posts events. Nothing in cinder/app runs Mojo on a foreign thread."},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Pitfalls","u":"/docs/guides/app/#pitfalls","x":"Field order in App is load-bearing. Adding a field that needs lib or gfx after them breaks shutdown; add new subsystems in the first block. App is Movable but not Copyable ; functions return it by value ( def _app() raises -> App ). Pointers into it ( app.renderer.ptr() ) are stable because of Boxed , pointers to the App value itself are not. run_frame calls start lazily; app.world is empty before the first frame. With frame_seconds left at the default, the first frame's dt is the time since start finished, not since construction. FixedStep caps steps at 8 per frame and drops the excess accumulator, so a frame longer than 133 ms at 60 Hz loses simulation time instead of spiralling. read_fram"},{"t":"Application runtime (cinder/app)","s":"Guides","h":"Tests and references","u":"/docs/guides/app/#tests-and-references","x":"tests/app/test_app_headless.mojo : a cube falls onto a lit floor for 45 frames; checks step counts, physics-driven transforms, a visible readback, no GPU errors, no error logs, quit, and live_objects() == 0 ; plus parse_app_args coverage and ui_consumes_event (releases reach the input map while the overlay has focus). tests/tools/test_debug_ui.mojo : the windowed path with the overlay. tests/sample/test_sample_game.mojo , tests/perf/test_zero_alloc.mojo , bench/ . docs/adr/0013-tools-and-app-runtime.md , docs/adr/0014-sample-game-and-performance.md , docs/THREADING.md , docs/MOJO_NOTES.md (Phase 7 and Phase 8 sections)."},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"Assets guide ( cinder/assets )","u":"/docs/guides/assets/","x":"cinder/assets gets content from disk into the renderer. It has two paths: Direct import : load_gltf parses a .gltf / .glb through cgltf into a CPU-side GltfAsset ; upload_gltf / instantiate push it to the GPU. Simple, synchronous, and what the sample's Sponza level and the golden tests use. Cooked assets : cook_gltf (and the cinder-cook CLI) turn a glTF source into versioned binary files ( .cmesh , .cmat , .cclip , .cskel , .ktx2 ) named by stable ids and listed in a manifest; the AssetServer loads those asynchronously on the job system with reference counting and hot reload. The package sits beside render , physics , anim , audio and scene in the module map; it imports cinder/core (bytes, f"},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"glTF import","u":"/docs/guides/assets/#gltf-import","x":"mojo var lib = NativeLibrary() var asset = load_gltf(lib.ptr(), String( \"tests/fixtures/skinned_quad.gltf\" )) ref prim = asset.meshes[ 0 ].primitives[ 0 ] assert_true(prim.mesh.has_skin()) assert_equal( len (prim.mesh.tangents), 4 , \"tangents generated when absent\" ) var b = asset.world_bounds() # SceneBounds: min, max, centre(), radius() GltfAsset is a plain tree of owned lists: images ( GltfImage : name + decoded RGBA8 Image ), textures ( GltfTexture : image index, linear_filter , wgpu address_mode ), materials ( GltfMaterial : a render Material plus five texture indices, -1 when absent, in slot order base colour, metallic-roughness, normal, occlusion, emissive), meshes ( GltfMesh -> GltfP"},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"Cooked formats ( cooked.mojo )","u":"/docs/guides/assets/#cooked-formats-cookedmojo","x":"All cooked files are little-endian, start with a four-byte magic and a u32 version, and are fully bounds-checked on read: bad magic, a different version, truncation, trailing bytes, out of range indices and inconsistent counts all raise cooked ... errors. Strings are u32 length + bytes. Readers take var data: List[UInt8] and consume it. extension magic version written by read by .cmesh CMSH 1 write_mesh(cook_mesh(data, lods, material)) read_mesh .cclip CCLP 1 write_clip(Clip) read_clip .cmat CMAT 1 write_material(CookedMaterial) read_material .cskel CSKL 1 cinder.anim.write_skeleton cinder.anim.read_skeleton .ktx2 KTX 2.0 - cook_texture_ktx2 (libktx) load_ktx2 .cmesh header after the version"},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"The cooker ( cook.mojo , tools/cinder_cook.mojo )","u":"/docs/guides/assets/#the-cooker-cookmojo-toolscinder_cookmojo","x":"mojo var manifest = CookManifest.load(String( OUT ) + \"/manifest.txt\" ) var report = cook_gltf(lib.ptr(), String( \"assets/samples/Fox/Fox.glb\" ), String( \"Fox\" ), String( OUT ), manifest) manifest.save(String( OUT ) + \"/manifest.txt\" ) print (report.cooked, \"cooked,\" , report.skipped, \"up to date\" ) # report.outputs: logical paths cook_gltf(lib, source_path, prefix, out_dir, manifest) produces, for a source with logical prefix : logical path file content <prefix>#mesh<i>/prim<p> .cmesh primitive with LODs ( build_lods when more than 64 triangles, else one LOD) and its material's logical path <prefix>#material<m> .cmat material with <prefix>#image<n> per used slot, and one sampler (filter and"},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"KTX2 textures ( textures.mojo )","u":"/docs/guides/assets/#ktx2-textures-texturesmojo","x":"mojo cook_texture_ktx2(lib.ptr(), img, path, srgb = True , normal_map = False , uastc = False ) # quality=128 var loaded = load_ktx2(lib.ptr(), path, prefer_bc7 = gfx.bc_supported) assert_equal( len (loaded.levels), 7 ) # full chain for 64x32 var handles = upload_loaded(res, String( \"label\" ), loaded) # (TextureHandle, TextureViewHandle) Cooking builds the full mip chain on the CPU ( downsample ), writes it into a ktxTexture2 and compresses with BasisU: UASTC (higher quality, normal-map mode available) or ETC1S at quality 1..255. sRGB images record the transfer function in the data format descriptor. Loading transcodes to BC7 when prefer_bc7 (the device advertises BC compression) or RGBA8 ot"},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"The asset server ( runtime.mojo )","u":"/docs/guides/assets/#the-asset-server-runtimemojo","x":"AssetServer(lib, jobs, renderer, root) reads root/manifest.txt and resolves logical paths through it. The App creates one with App.mount_assets(root) and calls assets.update() once per frame. mojo var jobs = JobSystem(lib.ptr(), 2 , job_capacity = 256 ) var server = AssetServer(lib.ptr(), _ptr(jobs), _ptr(renderer), String( OUT )) var fox = server.load_mesh(String( \"Fox#mesh0/prim0\" )) # asset index; a job starts now var fox_again = server.load_mesh(String( \"Fox#mesh0/prim0\" )) assert_equal(fox, fox_again) # one record, refcount 2 var clip = server.load_clip(String( \"Fox#anim0\" )) var mat = server.load_material(String( \"Box#material0\" )) # pulls in its KTX2 textures server.wait_all() # or: s"},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"Threading rules","u":"/docs/guides/assets/#threading-rules","x":"load_* , update , wait_all , release , check_for_changes and the accessors are main-thread calls: AssetServer has no locks and its record list is owned by the thread that owns the renderer. Job threads only run _load_job : file I/O, cooked-format parsing and KTX2 transcoding. They never touch Renderer , Resources or wgpu; GPU uploads happen in update() . Slots are stored as single-element lists ( slots: List[List[_LoadSlot]] ) so their heap addresses stay put while the outer list grows; a job may hold a pointer to its slot until its done flag is set. Do not move or shrink that list. JobSystem.submit returns False when the job table is full; the server then runs the load inline on the main th"},{"t":"Assets guide (cinder/assets)","s":"Guides","h":"Pitfalls","u":"/docs/guides/assets/#pitfalls","x":"Cooked formats have no migrations: a version mismatch raises cooked mesh version N (this engine reads M) and the fix is to re-cook ( docs/STATUS.md ). Bump the *_VERSION constants in cooked.mojo when a layout changes. The cooker decides incrementality from the source hash alone. Changing cooker code or the cooked format does not re-cook an unchanged source; delete the output or the manifest. cinder-cook derives prefixes from base names, so a/Fox.glb and b/Fox.glb overwrite each other's entries. Call cook_gltf with explicit prefixes when that matters. A texture reload gets a new TextureView . Before the old texture is freed, every ready material that binds the old view is rebuilt in place ( R"},{"t":"Audio (cinder/audio)","s":"Guides","h":"Audio ( cinder/audio )","u":"/docs/guides/audio/","x":"cinder/audio is the engine's sound layer over miniaudio. It provides mixing buses, decoded and streamed sounds, one-shots that clean up after themselves, 3D spatialisation with distance attenuation, a single listener, and an ECS bridge that moves sounds with entities and the listener with the camera. It sits beside render , physics , anim and input in the module map ( docs/ARCHITECTURE.md ): it depends on core , ecs , platform and the generated ffi bindings, and is owned and driven by cinder/app . Source: cinder/audio/engine.mojo (the engine), cinder/audio/sync.mojo (ECS components and sync_audio ), native/src/audio.c (the shim side). API reference: docs/api/audio.md . Design decisions: docs"},{"t":"Audio (cinder/audio)","s":"Guides","h":"Key types and ownership","u":"/docs/guides/audio/#key-types-and-ownership","x":"Type Role AudioEngine Owns the miniaudio engine, the buses and every sound. One per App ( App.audio , an Optional[AudioEngine] ). SoundDesc How to load a sound: path, bus, streamed , looping , spatial , volume, pitch, attenuation model and distances, one_shot . SoundHandle Generational handle (index, generation) to a loaded sound. SoundHandle.null() is the invalid handle; Bool(handle) is false for it. Attenuation Distance model: none() , inverse() (default), linear() , exponential() . AudioSource ECS component: a SoundHandle plus a velocity (Doppler). AudioListener ECS component marking the entity whose WorldTransform drives the listener. Everything miniaudio-shaped lives in the shim. ma_eng"},{"t":"Audio (cinder/audio)","s":"Guides","h":"Creating an engine","u":"/docs/guides/audio/#creating-an-engine","x":"Through App , AppConfig.audio (default True ) creates the engine; headless apps get a device-less engine automatically. Standalone, as the tests do: mojo from cinder.audio import AudioEngine, SoundDesc from cinder.platform import NativeLibrary var lib = NativeLibrary() var audio = AudioEngine(lib.ptr(), True ) # headless=True: no device, output is pulled var sfx = audio.create_bus(String( \"sfx\" )) # parent defaults to 0 (master) var music = audio.create_bus(String( \"music\" )) The constructor arguments are (lib, headless=False, sample_rate=48000, channels=2) . It raises \"audio: miniaudio engine creation failed (no device?)\" when a device is requested on a machine without one; pass headless=Tr"},{"t":"Audio (cinder/audio)","s":"Guides","h":"Loading and playing","u":"/docs/guides/audio/#loading-and-playing","x":"load creates the sound stopped; play starts it. SoundDesc takes the common options positionally: SoundDesc(path, bus=0, streamed=False, looping=False, spatial=True) . The remaining fields ( volume , pitch , attenuation , min_distance , max_distance , rolloff , one_shot ) are set on the value after construction. Supported file types are WAV, FLAC, MP3 and Vorbis. mojo # Streamed, looping, non-spatial music on its own bus (from sample/game/game.mojo). var song = audio.load( SoundDesc(String( \"assets/game/audio/music_level.wav\" ), music, True , True , False ) ) audio.play(song) audio.set_bus_volume(music, 0.6 ) # Decoded, looping, spatial effect at a position. var tone = audio.load(SoundDesc(St"},{"t":"Audio (cinder/audio)","s":"Guides","h":"One-shots","u":"/docs/guides/audio/#one-shots","x":"play_one_shot(desc, position) sets one_shot=True and looping=False on the description, loads, positions the sound if it is spatial, starts it and returns the handle. When the sound ends, the next update() releases it. The sample fires impact sounds this way: mojo var desc = SoundDesc(String( \"assets/game/audio/impact.wav\" ), self .sfx_bus, False , False , True ) desc.min_distance = 2.0 desc.max_distance = 60.0 _ = app.audio.value().play_one_shot(desc ^ , position) Do not keep one-shot handles around for long: once released, is_valid is false and play raises."},{"t":"Audio (cinder/audio)","s":"Guides","h":"3D position, attenuation and the listener","u":"/docs/guides/audio/#3d-position-attenuation-and-the-listener","x":"Spatial sounds take set_position(handle, Vec3) and set_velocity(handle, Vec3) ; set_spatial toggles spatialisation on an existing sound. The attenuation model and its distances are applied at load time from the SoundDesc (defaults: Attenuation.inverse() , min_distance=1.0 , max_distance=100.0 , rolloff=1.0 ) and can be changed with set_attenuation(handle, model, min_distance, max_distance, rolloff) . The listener is miniaudio's listener 0. set_listener(position, forward, up=Vec3.unit_y(), velocity=Vec3.zero()) sets its position, direction, world up and velocity. The engine convention is right-handed, Y up, -Z forward, so a listener looking down -Z hears a source at negative X on the left; te"},{"t":"Audio (cinder/audio)","s":"Guides","h":"The ECS bridge","u":"/docs/guides/audio/#the-ecs-bridge","x":"cinder/audio/sync.mojo connects sounds to entities. AudioSource(sound, velocity) on an entity with a WorldTransform makes sync_audio set that sound's position to the transform's translation and its velocity to the component's velocity . AudioListener(velocity) marks the listener entity: its WorldTransform gives the position, -Z transformed by the matrix gives forward , +Y gives up . When several entities carry AudioListener , the first chunk found wins. mojo from cinder.audio import AudioListener, AudioSource from cinder.ecs import LocalTransform, World, propagate_transforms var camera = world.spawn() world.add[LocalTransform](camera, LocalTransform(Mat4.identity())) world.add[AudioListener]"},{"t":"Audio (cinder/audio)","s":"Guides","h":"Per-frame work: update , take_ended , read_frames","u":"/docs/guides/audio/#per-frame-work-update-take_ended-read_frames","x":"update() drains the shim's event queue. Log callbacks from miniaudio become LogRecord s in AudioEngine.diagnostics (category \"audio\" ; error_count() counts those at error level). Sound-end events are matched to a live handle, recorded in AudioEngine.ended , and one-shots are released. update() returns how many sounds ended. take_ended() moves the ended list out so gameplay can react once: mojo _ = audio.update() for h in audio.take_ended(): if h == self .footstep: self .footstep = SoundHandle.null() In headless mode there is no device pulling the mixer, so nothing progresses unless you pull it. read_frames(frames) mixes frames frames and returns frames * channels interleaved Float32 samples;"},{"t":"Audio (cinder/audio)","s":"Guides","h":"Threading","u":"/docs/guides/audio/#threading","x":"The miniaudio device thread never runs Mojo. The shim registers only two callbacks ( on_log and on_sound_end in native/src/audio.c ), and both post fixed-size events to a CnEventQueue that update() drains on the main thread ( ADR-0003 , docs/THREADING.md ). Setters such as set_position and set_bus_volume are direct ma_* calls from the calling thread; miniaudio makes them safe against its own mixing thread. Call them from the main thread. With a headless engine there is no audio thread at all: mixing happens inside read_frames , so the end callbacks fire on the caller's thread during that call and still only post events."},{"t":"Audio (cinder/audio)","s":"Guides","h":"Pitfalls","u":"/docs/guides/audio/#pitfalls","x":"The sound-end callback fires on the processing step after the one that reached the end, and the sound is already stopped inside it ( docs/MOJO_NOTES.md , Phase 6). A headless test therefore reads one more buffer with read_frames before calling update() and expecting the end event. update() releases one-shots, so a handle returned by play_one_shot becomes stale without any call of yours. Check is_valid before reusing it. Sound paths are passed to C as a borrowed C string; load keeps the String alive across the call with keep . If you add a shim call that takes a path, do the same ( MOJO_NOTES.md , untracked pointers do not keep their pointee alive). set_master_volume is the engine's own volum"},{"t":"Audio (cinder/audio)","s":"Guides","h":"Tests and references","u":"/docs/guides/audio/#tests-and-references","x":"tests/audio/test_audio_engine.mojo : writes WAV files, then checks buses, left/right panning, distance attenuation, bus muting, streamed looping music, one-shot release through update , failed loads, and the ECS bridge (sources follow entities, listener follows the camera, stale handles are skipped). Run with pixi run test-audio-engine . tests/sample/test_sample_game.mojo : after the level is cleared, 240 headless frames let the one-shots end so only the music loop remains ( live_sounds() == 1 , live_buses() == 2 ). docs/adr/0012-physics-animation-audio-input.md : miniaudio used as is, headless engines for tests, callbacks post events, listener follows an entity. docs/adr/0013-tools-and-app-"},{"t":"cinder/core guide","s":"Guides","h":"cinder/core guide","u":"/docs/guides/core/","x":"cinder/core is the lowest Mojo layer of the engine: logging, assertions, a bump arena, generational handles and pools, a bounded ring, byte packing and hashing, file helpers, a monotonic clock with a fixed-step accumulator, CPU images, the job system, Tracy zones, Boxed heap cells and the SIMD math package. It performs no GPU, window or audio I/O. Everything above it ( platform , gfx , ecs , render , ...) depends on it; it depends only on the generated bindings in cinder/ffi and, for two modules that call into the shim ( jobs , image , profile ), on libcinder_native being loaded. Note on layering: core/profile.mojo and core/image.mojo import NativePtr from cinder.platform.native so they can "},{"t":"cinder/core guide","s":"Guides","h":"Key types","u":"/docs/guides/core/#key-types","x":"Module Types Ownership and lifetime log LogLevel , LogField , LogRecord , trait LogSink , PrintSink , MemorySink , Logger[S] A Logger owns its sink by value ( Logger[MemorySink](MemorySink(), LogLevel.info()) ). Records are copied into MemorySink.records . assertions cinder_assert , cinder_check , ASSERTS_ENABLED Free functions; both abort, never raise. arena LinearArena Owns one heap block for its lifetime; pointers it hands out are untracked and die at reset() . handles Handle[Tag] , Pool[T, Tag] A Pool owns its values; a Handle is an 8-byte copyable name (index + generation). Zero generation is the null handle. ring RingBuffer[T] Single-threaded bounded FIFO, owns its slots. bytes , hash "},{"t":"cinder/core guide","s":"Guides","h":"Logging","u":"/docs/guides/core/#logging","x":"Logger[S] filters on min_level and forwards a LogRecord to its sink. Sinks implement the LogSink trait ( emit(mut self, record: LogRecord) ) and must not raise. Records print as one line, [LEVEL] category: message key=value ... . The convenience methods trace , debug , info , warn , error take a category and a message; the full log overload also takes a List[LogField] , which it consumes. mojo from cinder.core.log import LogField, LogLevel, Logger, MemorySink var logger = Logger[MemorySink](MemorySink(), LogLevel.info()) logger.debug( \"gfx\" , \"dropped\" ) # below min_level, filtered logger.info( \"gfx\" , \"device created\" ) var fields = List[LogField]() fields.append(LogField( \"adapter\" , \"lava"},{"t":"cinder/core guide","s":"Guides","h":"Assertions","u":"/docs/guides/core/#assertions","x":"cinder_assert(condition, message) is compiled out when the build defines CINDER_ASSERTS=0 ( mojo build -D CINDER_ASSERTS=0 ); cinder_check is never compiled out and guards memory safety (handle validation, FFI preconditions). Both call std.os.abort with cinder assertion failed: ... or cinder check failed: ... . Recoverable conditions raise instead; a failed assertion is a bug. Tests verify the abort in a child process with std.testing.assert_aborts._assert_aborts (the module name shadows the function, so the underscore form is called directly)."},{"t":"cinder/core guide","s":"Guides","h":"Arena","u":"/docs/guides/core/#arena","x":"LinearArena(capacity) allocates one block (plus one cache line so any alignment up to 64 can be served) and bumps an offset. alloc_bytes(size, alignment=16) returns a Pointer[UInt8, MutUntrackedOrigin] ; alloc[T](count) returns uninitialised, T -aligned storage. reset() makes everything available again and high_water() is the number to size the arena by. mojo from cinder.core.arena import LinearArena var arena = LinearArena( 1024 ) var p16 = arena.alloc_bytes( 10 , 16 ) # 16-byte aligned var f = arena.alloc[Float64]( 4 ) # aligned for Float64 f[] = 1.5 f[unsafe_offset= 3 ] = 2.5 arena.reset() # every earlier pointer now dangles var again = arena.alloc_bytes( 3 , 1 ) # same address as the fir"},{"t":"cinder/core guide","s":"Guides","h":"Handles and pools","u":"/docs/guides/core/#handles-and-pools","x":"Handle[Tag] is a 64-bit generational handle (32-bit slot, 32-bit generation) whose Tag is a phantom type: a Handle[TextureTag] cannot be passed where a Handle[BufferTag] is expected. packed() and from_packed() round-trip through one UInt64 for serialisation. Pool[T, Tag](capacity) is a slot map with O(1) insert , get , update , remove ; generations start at 1 and skip 0 on wrap, so the zero handle is never valid. Slots are never reallocated: the pool holds at most capacity values and insert raises Pool is full instead of growing, which keeps steady-state frames allocation-free. mojo from cinder.core.handles import Handle, Pool struct TextureTag : var _unused: UInt8 var pool = Pool[String, Te"},{"t":"cinder/core guide","s":"Guides","h":"Ring buffer, bytes, hashing","u":"/docs/guides/core/#ring-buffer-bytes-hashing","x":"RingBuffer[T](capacity) requires a power-of-two capacity (>= 2). push returns False when full without dropping anything queued, pop returns None when empty, peek raises when empty. It is single-threaded by design: cross-thread queues live in the shim ( CnEventQueue ), see ADR-0003 . ByteWriter appends little-endian u8 , u32 , i32 , f32 , vec2 , vec3 , vec3_padded , vec4 and column-major mat4 values for GPU uploads; pad_to(16) aligns for std140 rules (the layout itself is the caller's job). pad_to needs a positive alignment: 0 or a negative value is a bug that cinder_assert aborts on (with assertions compiled out it pads nothing). reset() keeps capacity for frame scratch; take() consumes the "},{"t":"cinder/core guide","s":"Guides","h":"Filesystem and time","u":"/docs/guides/core/#filesystem-and-time","x":"read_bytes , read_text , write_bytes , write_text wrap open and raise FileError(path, message) , declared as raises FileError , so a caller sees the path in the error text ( FileError(path: message) ). file_exists never raises. Clock is a monotonic nanosecond clock; tick() returns seconds since the previous tick and is never negative. FixedStep(hz=60.0, max_steps_per_frame=8) is the accumulator that drives the simulation: mojo from cinder.core.time import FixedStep var step = FixedStep( hz = 60.0 , max_steps_per_frame = 4 ) var n = step.advance(frame_seconds) # simulation steps to run this frame, capped at 4 var alpha = step.alpha() # [0, 1): interpolation between the last two states When th"},{"t":"cinder/core guide","s":"Guides","h":"Images","u":"/docs/guides/core/#images","x":"Image(width, height) is a tightly packed RGBA8 buffer. load_image(lib, path) and decode_image(lib, bytes) go through the shim's stb wrappers and raise with cn_image_failure_reason() in the message; save_png raises when the file cannot be written. Image.from_rows(width, height, rows, stride) repacks a GPU readback whose rows are padded to the 256-byte copy alignment; swap_rb() converts BGRA swapchain readbacks. pixel(x, y) and set_pixel(x, y, v) abort through cinder_check (in every build) when x is outside [0, width) or y outside [0, height) . compare(a, b, tolerance=2, window=8) returns an ImageDiff with SSIM on luma over non-overlapping windows plus max_channel_delta and differing_pixels ; "},{"t":"cinder/core guide","s":"Guides","h":"Job system","u":"/docs/guides/core/#job-system","x":"JobSystem(lib, worker_count, job_capacity=1 << 14) starts worker_count threads ( cinder-worker , created through cn_thread_create ) each owning a Chase-Lev deque of job indices; deque 0 belongs to the main thread. A job is a thin function pointer JobFn = def(Pointer[NoneType, MutUntrackedOrigin]) thin -> None plus an untyped data pointer and an optional Counter . submit queues from the main thread and returns False when the job table or the main deque is full: the caller must then run the job inline. wait(counter) runs jobs on the calling thread until the counter reaches zero, so waiting never idles a core. reset() recycles the job table and is legal only when nothing is queued or running (f"},{"t":"cinder/core guide","s":"Guides","h":"Profiling","u":"/docs/guides/core/#profiling","x":"Profiler(lib) wraps the shim's Tracy client. Nothing listens on the network until start() , which is idempotent and returns False when the shim was built without Tracy ( available() ). While stopped, zone , plot , message and frame_mark are no-ops and Zone.id is 0. A zone is one line: mojo from cinder.core.profile import COLOR_RENDER , Profiler var profiler = Profiler(lib.ptr()) _ = profiler.start() var z = profiler.zone(String( \"frame\" ), COLOR_RENDER ) z.text(String( \"frame body\" )) _ = z ^ # ends the zone; otherwise it ends at the last use of `z` profiler.frame_mark() Tracy copies zone names, zone text and messages during the call, but keys plots by the name pointer and reads it until the"},{"t":"cinder/core guide","s":"Guides","h":"C strings","u":"/docs/guides/core/#c-strings","x":"string_from_c(ptr, max_len=-1) turns text returned by C (SDL errors, driver names, SDL log lines, stb failure reasons) into a String . It decodes the bytes as UTF-8, replacing invalid sequences with U+FFFD; building a String one chr(byte) at a time would double-encode every non-ASCII character. max_len caps the scan for fixed-size C buffers without a guaranteed terminator."},{"t":"cinder/core guide","s":"Guides","h":"Boxed","u":"/docs/guides/core/#boxed","x":"Boxed[T](value^) owns T at a stable heap address. Subsystems that hand out pointers to themselves ( Gfx , Resources , JobSystem , the renderer) must not move after those pointers exist, so the App runtime keeps them in Boxed cells and passes box.ptr() ; box[] is a mutable reference."},{"t":"cinder/core guide","s":"Guides","h":"Math","u":"/docs/guides/core/#math","x":"Fixed conventions ( docs/ARCHITECTURE.md ): right-handed, Y up, -Z forward, metres, column-major matrices, reverse-Z depth with an infinite far plane. Vec3 keeps its fourth SIMD lane at zero. Mat4 stores columns c0..c3 ; m[row, col] indexes an element, column(c) and row(r) return Vec4 s. Indices outside 0..3 are bugs: cinder_assert aborts (with assertions compiled out the index wraps to i & 3 ). Constructors: identity , zero , from_rows , translation , scaling , rotation(q) , trs , look_at , perspective_reverse_z_infinite(fov_y, aspect, near) , orthographic_reverse_z(l, r, b, t, near, far) . Point and vector transforms are transform_point , transform_vector , project_point (perspective divid"},{"t":"cinder/core guide","s":"Guides","h":"Threading and error handling","u":"/docs/guides/core/#threading-and-error-handling","x":"Everything in core except JobSystem is single-threaded and owned by one thread. RingBuffer , LinearArena , Pool and the loggers have no internal synchronisation. JobSystem is the only place Mojo code runs on a thread Cinder did not inherit from the OS loader: _worker_entry is an abi(\"C\") function invoked by the shim's thread ( ADR-0008 , spike 4). Job data must stay valid until the counter it was submitted with reaches zero; std.benchmark.keep the descriptors and the counter after wait . submit and reset are main-thread calls. Raises (recoverable, Error unless noted): LinearArena construction and allocation, Pool construction, insert , get , update , remove , RingBuffer construction and peek"},{"t":"cinder/core guide","s":"Guides","h":"Pitfalls","u":"/docs/guides/core/#pitfalls","x":"Untracked pointers do not keep their pointee alive ( docs/MOJO_NOTES.md ). Everything handed to C or to a job by pointer ( Image.pixels , a String turned into a C string, job descriptors) must be followed by keep(value) after the last call that can read it. profile.mojo and image.mojo do this internally for their own temporaries; callers do it for job data. Pointer(to=x) cast to MutUntrackedOrigin ends the compiler's view of x ; _ = x is not a read. A struct that stores pointers to its own fields breaks when moved. Box it ( Boxed ) and hand out box.ptr() . String has inline small-string storage; a C pointer from as_c_string_span() dies when the owning List[String] reallocates. Hand long-live"},{"t":"cinder/core guide","s":"Guides","h":"Related documents","u":"/docs/guides/core/#related-documents","x":"docs/ARCHITECTURE.md (module map, conventions), docs/THREADING.md (threads and shared state). docs/MOJO_NOTES.md (lifetime rules that every unsafe_ call here relies on). ADR-0003 docs/adr/0003-native-shim-pull-model.md : why cross-thread queues are in C and RingBuffer is single-threaded. ADR-0008 docs/adr/0008-job-system-threads.md : job system design. Tests: tests/core/test_core.mojo (handles, arena, ring, time, fs, logging, assertions, ByteWriter.pad_to bounds, UTF-8 C strings), tests/core/test_math.mojo , tests/core/test_jobs.mojo (contention, saturation, zero workers), tests/core/test_image.mojo (PNG round trip, SSIM), tests/core/test_profile.mojo ."},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"ECS guide ( cinder/ecs )","u":"/docs/guides/ecs/","x":"cinder/ecs is the archetype entity component system every gameplay-facing module builds on. It sits directly above cinder/core (it uses core/math for Mat4 and core/jobs for the scheduler) and below render , physics , anim , audio , assets and scene , which define their own components on top of it. The App runtime owns one World ; the sample game and every bridge ( PhysicsSync , the animation system, RenderBridge ) read and write that world. Source: cinder/ecs/world.mojo , commands.mojo , hierarchy.mojo , schedule.mojo . API reference: docs/api/ecs.md . Tests: tests/ecs/test_ecs.mojo . Decision record: ADR-0011 (why this ECS and not flecs, and the benchmark that keeps it honest)."},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Model","u":"/docs/guides/ecs/#model","x":"An Entity is a generational id ( index , generation ). Entity.null() is never live; World.is_alive(e) is false for despawned entities even after their slot is reused. A component is any struct conforming to Component . The trait is Copyable, Deinitable, Movable plus a static component_id() -> Int that must be unique per type and lie in 0 .. MAX_COMPONENTS - 1 (256). Copyable is required (so World.get can hand out a copy); ImplicitlyCopyable is not. The sample marks its components ImplicitlyCopyable for convenience, the engine's own ( Parent , MeshRenderer , ...) do not. Entities with the same component set share an Archetype : one Column per component, each a contiguous, 16-byte-aligned byte"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Component ids in use","u":"/docs/guides/ecs/#component-ids-in-use","x":"id component module 1, 2, 3 Parent , LocalTransform , WorldTransform cinder/ecs/hierarchy.mojo 4, 5, 6 MeshRenderer , LightSource , CameraComponent cinder/scene/render_sync.mojo 7 RigidBody cinder/physics/sync.mojo 8 AnimatorRef cinder/anim/system.mojo 9 JointPalette cinder/scene/render_sync.mojo 10, 11 AudioSource , AudioListener cinder/audio/sync.mojo 100 .. 105 sample game components sample/game/components.mojo Two types sharing an id is caught only when their sizes differ ( register raises); pick an unused id and keep game components well above the engine's range."},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Entities and components","u":"/docs/guides/ecs/#entities-and-components","x":"From tests/ecs/test_ecs.mojo : mojo from cinder.ecs import Component, Entity, World @fieldwise_init struct Position ( Component ): var x: Float32 var y: Float32 @ staticmethod def component_id () -> Int: return 10 var world = World() var a = world.spawn() world.add[Position](a, Position( 1.0 , 2.0 )) world.add[Velocity](a, Velocity( 0.5 , 0.0 )) assert_true(world.has[Position](a) and not world.has[Label](a)) assert_equal(world.get[Position](a).x, 1.0 ) # a copy world.add[Position](a, Position( 3.0 , 3.0 )) # replace: same archetype, marked changed assert_true(world.remove[Velocity](a)) # False when absent world.despawn(a) assert_false(world.is_alive(a)) Access paths and what they do to chang"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Queries and chunks","u":"/docs/guides/ecs/#queries-and-chunks","x":"world.query() builds a filter; .chunks() returns one Chunk per non-empty archetype whose signature has every with_ component and none of the without ones. A chunk gives typed access to that archetype's rows: mojo _ = world.advance_tick() for chunk in world.query().with_[Position]().with_[Velocity]().chunks(): var pos = chunk.column[Position]() # tight typed column base var vel = chunk.column[Velocity]() for row in range (chunk.count): pos[unsafe_offset=row].x += vel[unsafe_offset=row].dx pos[unsafe_offset=row].y += vel[unsafe_offset=row].dy chunk.mark_changed[Position](row) (The loop body is the one bench/ecs_bench.mojo times; the test does the same through chunk.get[T](row)[] .) chunk.get[T"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Change detection","u":"/docs/guides/ecs/#change-detection","x":"Every column keeps a UInt32 tick per row. World.tick starts at 1 and advance_tick() increments it; the App calls it once per fixed step. A row's tick is set to the current tick by add , set , get_mut , Chunk.mark_changed and by CommandBuffer.apply . Readers compare: mojo var start_tick = world.tick _ = world.advance_tick() # ... systems run ... for chunk in world.query().with_[Position]().chunks(): for row in range (chunk.count): if chunk.changed_since[Position](row, start_tick): ... changed_since is a strict > against the stored tick, so record the tick before advancing and writing. world.changed_tick[T](e) reads one entity's tick. A newly spawned row carries the tick of its spawn; a compon"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Command buffers","u":"/docs/guides/ecs/#command-buffers","x":"Structural changes are immediate on the World , which makes them illegal while iterating chunks (the row you are on may be swap-removed) and from jobs. CommandBuffer records them instead: mojo _ = world.register[Position]() # the world must know every type a buffer will add _ = world.register[Label]() var cmds = CommandBuffer() var p0 = cmds.spawn() # placeholder index, not an Entity cmds.add_to_spawned[Position](p0, Position( 1.0 , 1.0 )) cmds.add_to_spawned[Label](p0, Label(String( \"deferred\" ))) cmds.add[Label](existing, Label(String( \"late label\" ))) cmds.remove[Position](existing) cmds.despawn(existing) var applied = cmds.apply(world) # in order; clears the buffer var e0 = cmds.resolved"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Hierarchy and transforms","u":"/docs/guides/ecs/#hierarchy-and-transforms","x":"hierarchy.mojo defines Parent(entity) , LocalTransform(matrix) and WorldTransform(matrix) (all Mat4 , column-major, right-handed Y-up as everywhere in Cinder) and propagate_transforms(world) -> Int : mojo world.add[LocalTransform](root, LocalTransform(Mat4.translation(Vec3( 1.0 , 0.0 , 0.0 )))) world.add[LocalTransform](child, LocalTransform(Mat4.translation(Vec3( 0.0 , 2.0 , 0.0 )))) world.add[Parent](child, Parent(root)) assert_equal(propagate_transforms(world), 2 ) # both WorldTransforms written assert_equal(propagate_transforms(world), 0 ) # unchanged values are not rewritten Rules, all from the implementation: Every entity with a LocalTransform gets WorldTransform = parent.world * local"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Schedule","u":"/docs/guides/ecs/#schedule","x":"Schedule runs systems whose component access is declared up front. A system is a thin function def(Pointer[World, MutUntrackedOrigin]) -> None ( SystemFn ); SystemDesc carries its name, the components it reads and writes, and an exclusive flag for systems that do structural changes: mojo def _integrate (world: Pointer[World, MutUntrackedOrigin]): for chunk in world[].query().with_[Position]().with_[Velocity]().chunks(): for row in range (chunk.count): var p = chunk.get[Position](row) var v = chunk.get[Velocity](row) p[].x += v[].dx p[].y += v[].dy var integrate: def (Pointer[World, MutUntrackedOrigin]) thin -> None = _integrate var schedule = Schedule() schedule.add(SystemDesc(String( \"integ"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Threading rules","u":"/docs/guides/ecs/#threading-rules","x":"The World has no locks and is owned by the main thread. Spawn, despawn, add, remove, register , advance_tick , propagate_transforms , CommandBuffer.apply and anything that can create an archetype are main-thread operations. Systems inside a Schedule.run stage run concurrently on job workers. They may read and write component data through chunks, restricted to what they declared; they must not perform structural changes unless marked exclusive (which serialises them into their own stage). Record structural changes into a CommandBuffer and apply it on the main thread after run . Anything else that touches the world from a job (the animation system's pose jobs, for instance) follows the same ru"},{"t":"ECS guide (cinder/ecs)","s":"Guides","h":"Pitfalls","u":"/docs/guides/ecs/#pitfalls","x":"A pointer from get_ptr , get_mut or a Chunk dangles after the next structural change. Re-query after add / remove / spawn / despawn / apply . remove[T] on the last component of an entity moves it back to the empty archetype; the entity stays alive. Components are relocated bytewise ( unsafe_memcpy ): never store a pointer to a component's own field inside it, and never keep raw pointers to components across frames. Entity indices are reused after despawn with a bumped generation; compare whole entities (or packed() ), never bare indices, and check is_alive before trusting a stored handle. World.entities() allocates a fresh list; it is for tests and tooling, not per-frame code. Query.chunks()"},{"t":"cinder/gfx guide","s":"Guides","h":"cinder/gfx guide","u":"/docs/guides/gfx/","x":"cinder/gfx is the safe, typed layer over wgpu-native. It owns the wgpu instance, adapter, device and queue ( Gfx ), keeps every GPU object the engine creates in generational pools behind typed handles ( Resources ), compiles WGSL with editor-friendly diagnostics and hot reload ( ShaderManager ), translates engine-side descriptors into wgpu descriptor graphs ( pipeline ), records and submits command buffers ( commands ), configures and presents a window surface ( Swapchain ) and draws debug lines ( DebugDraw ). It sits above cinder/core and cinder/platform ; the renderer, tools and app layers build on it and never call wgpu directly. Source: cinder/gfx/*.mojo . Reference: docs/api/gfx.md . De"},{"t":"cinder/gfx guide","s":"Guides","h":"Key types and ownership","u":"/docs/guides/gfx/#key-types-and-ownership","x":"Type Owns Lifetime rules Gfx instance , adapter , device , queue ; the shim event queue ( events , capacity 4096); the window surface when constructed from a Window . One per device. Other gfx objects hold Pointer[Gfx, MutUntrackedOrigin] , so it must outlive them and must not move (the app keeps it in a Boxed ). __deinit__ releases everything in reverse order. GfxError A recoverable failure: adapter or device request, await_event timeout, surface problems. Raised by Gfx , Swapchain , wait_for_queue . Resources One Pool per kind: buffers, textures, texture views, samplers, shaders, bind group layouts, pipeline layouts, bind groups, render pipelines, compute pipelines. Holds a pointer to Gfx "},{"t":"cinder/gfx guide","s":"Guides","h":"Creating a device","u":"/docs/guides/gfx/#creating-a-device","x":"mojo from cinder.ffi.cinder_native import WGPUSurface from cinder.gfx import Gfx from cinder.platform import NativeLibrary var lib = NativeLibrary() var gfx = Gfx(lib.ptr(), WGPUSurface()) # headless; or Gfx(lib.ptr(), window) assert_true(Bool(gfx.device) and Bool(gfx.queue)) gfx.pump( False ) assert_equal(gfx.error_count, 0 ) keep(gfx) keep(lib) Construction creates the instance, requests an adapter (compatible with the surface when one is given), requests a device with the optional features the adapter has ( wireframe_supported for wgpu-native's PolygonModeLine , bc_supported for BC texture compression) and reads adapter_name and backend . Each async request is awaited through the shim que"},{"t":"cinder/gfx guide","s":"Guides","h":"Pulling callbacks","u":"/docs/guides/gfx/#pulling-callbacks","x":"No wgpu callback ever runs Mojo code ( ADR-0003 ). The shim registers every callback and writes a CnEvent into Gfx.events . Gfx.pump(wait) calls cn_wgpu_pump ( wgpuInstanceProcessEvents plus wgpuDevicePoll ) and drains the queue: CN_EVENT_WGPU_LOG becomes a LogRecord in diagnostics with category wgpu (error, warn or info by wgpu level; the shim only forwards Warn and above). CN_EVENT_WGPU_UNCAPTURED_ERROR increments error_count and adds an error record with category wgpu.validation and a type field. CN_EVENT_WGPU_DEVICE_LOST sets lost = True and adds an error record with category wgpu.device . Everything else (adapter, device, error scope, buffer mapped, work done) is held in _pending for aw"},{"t":"cinder/gfx guide","s":"Guides","h":"Resources","u":"/docs/guides/gfx/#resources","x":"mojo from cinder.gfx.resources import BufferDesc, Resources, SamplerDesc, TextureDesc var res = Resources(_gfx_ptr(gfx), 64 ) # pool capacity for buffers/textures/views/bind groups var buf = res.create_buffer(BufferDesc(String( \"vertices\" ), 1024 , WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst | WGPUBufferUsage_CopySrc, False )) var tex = res.create_texture(TextureDesc(String( \"albedo\" ), 64 , 32 , 1 , 1 , 1 , WGPUTextureFormat_RGBA8Unorm, WGPUTextureUsage_TextureBinding | WGPUTextureUsage_RenderAttachment)) var view = res.create_texture_view(tex) var sampler = res.create_sampler(SamplerDesc(String( \"linear\" ), True , WGPUAddressMode_Repeat, 1 )) res.write_buffer(buf, 0 , Span(data)) # r"},{"t":"cinder/gfx guide","s":"Guides","h":"Shaders","u":"/docs/guides/gfx/#shaders","x":"mojo from cinder.gfx.shaders import ShaderManager var shaders = ShaderManager(_ptr(gfx), _ptr(res)) var handle = shaders.load(String( \"shaders/lit.wgsl\" )) # compiles, adopts, registers for reload ... for result in shaders.check_for_changes(): # once per frame or on a timer if result.success: print ( \"reloaded\" , result.path) # same handle, new module behind it: # rebuild pipelines that use it for record in shaders.take_diagnostics(): logger.log(record.level, record.category, record.message) compile_source(path, source) -> WGPUShaderModule is the primitive: wgpuShaderModuleGetCompilationInfo is unimplemented in wgpu-native 29 (it aborts the process), so the module is created inside a Validat"},{"t":"cinder/gfx guide","s":"Guides","h":"Pipelines, commands, readback","u":"/docs/guides/gfx/#pipelines-commands-readback","x":"The render test draws a triangle offscreen; abridged: mojo var module = shaders.compile_source(String( \"test_render.wgsl\" ), String( SHADER )) var shader = res.adopt_shader(module, String( \"test_render.wgsl\" )) var uniform_layout = create_bind_group_layout(res, String( \"tint uniform\" ), [LayoutEntry( 0 , UInt64(WGPUShaderStage_Vertex | WGPUShaderStage_Fragment), BindingKind.uniform_buffer(), False , 16 )]) var render_layout = create_pipeline_layout(res, String( \"render\" ), [uniform_layout]) var uniform_group = create_bind_group(res, String( \"tint uniform\" ), uniform_layout, [BindingResource.for_buffer( 0 , tint, 0 , 16 )]) var pipeline = create_render_pipeline(res, RenderPipelineDesc( String"},{"t":"cinder/gfx guide","s":"Guides","h":"Swapchain","u":"/docs/guides/gfx/#swapchain","x":"mojo var gfx = Gfx(lib.ptr(), window) var swapchain = Swapchain(_ptr(gfx), gfx.surface, size[ 0 ], size[ 1 ], True , True ) # prefer sRGB, readback var frame_opt = swapchain.acquire() if frame_opt: # None while minimised (frames_skipped counts them) var frame = frame_opt.take() var encoder = CommandEncoder(_ptr(gfx), _ptr(res), String( \"clear\" )) var pass_ = encoder.begin_render_pass( [ColorAttachment.cleared(frame.view, r, g, b, 1.0 )], DepthAttachment.none()) pass_.end() encoder ^ .finish_and_submit() swapchain.present(frame ^ ) Construction queries wgpuSurfaceGetCapabilities , scores the 8-bit BGRA/RGBA formats (sRGB first when prefer_srgb ), requires RenderAttachment (plus CopySrc when r"},{"t":"cinder/gfx guide","s":"Guides","h":"Debug draw","u":"/docs/guides/gfx/#debug-draw","x":"mojo var draw = DebugDraw(_ptr(gfx), _ptr(res), shaders, WGPUTextureFormat_RGBA8Unorm) # depth_format optional draw.line(Vec3( - 1.0 , 0.0 , 0.0 ), Vec3( 1.0 , 0.0 , 0.0 ), Color8.red()) draw.aabb(Vec3( - 0.5 , - 0.5 , - 0.5 ), Vec3( 0.5 , 0.5 , 0.5 ), Color8.white()) draw.sphere(Vec3.zero(), 0.25 , Color8.yellow(), 12 ) draw.axes(Vec3.zero(), 1.0 ) draw.grid( 2 , 0.1 , Color8.blue()) draw.upload(view_proj) # before the render pass opens: camera uniform + vertex buffer ... draw.flush(pass_) # inside an open RenderPass; clears the frame's lines Vertices ( Float32x3 position + Unorm8x4 colour, 16 bytes) accumulate on the CPU. upload grows the GPU vertex buffer by doubling when needed ( grows c"},{"t":"cinder/gfx guide","s":"Guides","h":"Threading and error handling","u":"/docs/guides/gfx/#threading-and-error-handling","x":"Every call in this module is main-thread only. wgpu-native's internal threads post events to the shim queue; Mojo never runs on them ( ADR-0003 , docs/THREADING.md ). GfxError (typed, raises GfxError ): Gfx construction, await_event timeout, Swapchain construction and acquire , wait_for_queue . Plain Error : Resources.create_* on NULL, write_buffer / write_texture bounds, every Pool lookup with a stale handle ( invalid handle ), create_* in pipeline , _creation_error failures, adopt_* on a null object, destroy_texture with live views, destroy_texture_view / forget_texture_view on the wrong kind of view, set_vertex_buffer / set_index_buffer offsets past the end, CommandEncoder construction an"},{"t":"cinder/gfx guide","s":"Guides","h":"Pitfalls","u":"/docs/guides/gfx/#pitfalls","x":"Gfx , Resources , ShaderManager , DebugDraw , RenderPass and CommandEncoder hold untracked pointers to Gfx and Resources . Keep both in engine-lifetime storage ( Boxed in the app), never move them after handing out _ptr(gfx) , and keep them at the end of a test. Destruction order: Resources before Gfx , Gfx before the Window it created a surface for, everything before the NativeLibrary . In a struct, declare the library last. Every raw descriptor passed to wgpu is a stack value or a List whose pointer wgpu reads during the call; the keep(...) calls after each C call are the lifetime fence ( ADR-0009 , docs/MOJO_NOTES.md ). Do not remove them, and add one for any new pointee. A pass must be e"},{"t":"cinder/gfx guide","s":"Guides","h":"Related documents","u":"/docs/guides/gfx/#related-documents","x":"ADR-0009 docs/adr/0009-gfx-layer.md : pooled handles, error scopes for shader diagnostics, keep -fenced descriptor building, lazy swapchain reconfiguration, debug draw, depth convention. ADR-0003 docs/adr/0003-native-shim-pull-model.md : the pull model Gfx.pump implements, and the cn_wgpu_pump amendment (no wgpuInstanceWaitAny in wgpu-native 29). ADR-0007 docs/adr/0007-dev-environment-container.md : lavapipe is the CI device. docs/MOJO_NOTES.md : untracked pointers, keep , fatal submit-time validation errors. docs/THREADING.md , docs/ARCHITECTURE.md (conventions: reverse-Z, sRGB targets, column-major). Tests: tests/gfx/test_device.mojo (device, validation capture), test_resources.mojo (handl"},{"t":"Input (cinder/input)","s":"Guides","h":"Input ( cinder/input )","u":"/docs/guides/input/","x":"cinder/input maps raw platform events to named gameplay controls. A game asks pressed(\"jump\") or axis(\"move_x\") instead of testing scancodes, players can rebind controls at runtime, and the whole map saves to a small text file. The package sits beside audio , physics , anim and render in the module map ( docs/ARCHITECTURE.md ); it depends on cinder/platform for the Event type, on cinder/core/fs for file I/O and on cinder/scene/format for its exact float formatter. cinder/app owns one InputMap ( App.input ) and feeds it every frame. Source: cinder/input/map.mojo . API reference: docs/api/input.md . Design decisions: docs/adr/0012-physics-animation-audio-input.md , \"Input\" section."},{"t":"Input (cinder/input)","s":"Guides","h":"Model","u":"/docs/guides/input/#model","x":"An action is a named button: held, just pressed, just released. It is held while any of its bindings is down. An axis is a named float. Its value is the sum of its bindings' contributions: a digital source contributes scale while held; a gamepad axis contributes its value after a rescaled dead zone, times scale ; a mouse axis contributes the frame's accumulated delta times scale . A Binding is one source: device , code , scale , dead_zone . Devices are the module constants DEVICE_KEY , DEVICE_MOUSE_BUTTON , DEVICE_MOUSE_AXIS , DEVICE_GAMEPAD_BUTTON , DEVICE_GAMEPAD_AXIS . Codes are the platform's: SDL scancodes for keys (W is 26, Space is 44; sample/game/game.mojo lists the ones the sample u"},{"t":"Input (cinder/input)","s":"Guides","h":"Defining a map","u":"/docs/guides/input/#defining-a-map","x":"bind_action and bind_axis create the action or axis on first use and append the binding. add_action / add_axis create an empty one (they raise if the name exists); clear_*_bindings and rebind_action (clear, then bind one) edit existing ones; action_bindings / axis_bindings return copies; find_action / find_axis return an index or -1 . mojo from cinder.input import Binding, InputMap def default_bindings () raises -> InputMap: var m = InputMap() m.bind_axis(String( \"move_x\" ), Binding.key( KEY_D , 1.0 )) m.bind_axis(String( \"move_x\" ), Binding.key( KEY_A , - 1.0 )) m.bind_axis(String( \"move_x\" ), Binding.gamepad_axis( 0 , 1.0 )) m.bind_axis(String( \"move_y\" ), Binding.key( KEY_W , 1.0 )) m.bin"},{"t":"Input (cinder/input)","s":"Guides","h":"Feeding events and reading state","u":"/docs/guides/input/#feeding-events-and-reading-state","x":"Call process(ev) for every Event from Platform.poll , read the map during the frame, and call end_frame() once after reading. App.run_frame does all three: _poll forwards events to App.input (skipping mouse and key presses, motion and wheel while the ImGui overlay wants them; releases always get through, see ui_consumes_event ) and end_frame runs at the end of the frame, after Game.update and rendering. A Game only reads: mojo def update ( mut self, mut app: App, dt: Float32) raises : var mx = app.input.axis(String( \"move_x\" )) var my = app.input.axis(String( \"move_y\" )) if app.input.just_pressed(String( \"pause\" )): self .paused = not self .paused if app.input.pressed(String( \"walk\" )): spee"},{"t":"Input (cinder/input)","s":"Guides","h":"Dead zones","u":"/docs/guides/input/#dead-zones","x":"A gamepad axis binding with dead_zone = d ignores |v| <= d and rescales the rest so the dead zone edge maps to 0 and full deflection to 1: sign(v) * min((|v| - d) / (1 - d), 1) * scale . With Binding.gamepad_axis(1, -1.0, 0.2) , a value of 0.1 reads 0, -1.0 reads 1.0 and 0.6 reads -0.5. The default dead zone is 0.15; digital bindings carry dead_zone = 0.0 ."},{"t":"Input (cinder/input)","s":"Guides","h":"Runtime rebinding by listening","u":"/docs/guides/input/#runtime-rebinding-by-listening","x":"A settings menu rebinds a control by arming a capture and letting the player press something: listen_action(name) : the next key, mouse button or gamepad button press replaces all of the action's bindings. The capturing press itself does not fire the action, and an action that was held through its old bindings is released at that moment. listen_axis(name, scale=1.0) : the next key, button or gamepad axis event (an axis must exceed 0.5 so stick noise does not bind) is appended to the axis with the given scale ; the captured gamepad axis keeps the default dead zone. is_listening() reports whether a capture is armed; cancel_listening() disarms it. Mouse motion and wheel never bind anything whil"},{"t":"Input (cinder/input)","s":"Guides","h":"File format","u":"/docs/guides/input/#file-format","x":"to_text() produces the file, from_text(text) parses it, save(path) and InputMap.load(path) wrap them with write_text / read_text . The format is line based and diffable: cinder-input 1 action jump key 44 1 0 action jump gamepad_button 0 1 0 action fire mouse_button 1 1 0 action unbound axis move_y key 26 1 0 axis move_y key 22 -1 0 axis move_y gamepad_axis 1 -1 0.2 axis look_x mouse_axis 0 0.01 0 Line 1 is the header cinder-input <version> ; INPUT_MAP_VERSION is 1 and any other version is rejected ( \"input map version 7 (this engine reads 1)\" ). Each later line is action|axis <name> followed either by nothing (an entry without bindings, which still exists after loading) or by <device> <code>"},{"t":"Input (cinder/input)","s":"Guides","h":"Threading","u":"/docs/guides/input/#threading","x":"InputMap is main-thread state. Events arrive from Platform.poll on the main thread (the SDL event pump lives there, docs/THREADING.md ), process and the queries run on the main thread inside App.run_frame , and no other thread touches the map. Nothing in the package calls into the native library."},{"t":"Input (cinder/input)","s":"Guides","h":"Pitfalls","u":"/docs/guides/input/#pitfalls","x":"Call end_frame exactly once per frame, after every reader has run. Calling it before Game.update loses every just_pressed edge; App orders this correctly. While the debug overlay has the mouse or keyboard ( DebugUi.wants_mouse / wants_keyboard ), App does not forward presses, motion or wheel from them to the map, so a key pressed while typing in an ImGui field does not move the player. Releases are always forwarded, so a key or button held when the overlay takes focus is released in the map when the player lets go. Scancodes outside 0..511 , mouse buttons outside 0..7 , gamepad buttons outside 0..31 and gamepad axes outside 0..7 are ignored by process and read as not pressed / zero; bindings"},{"t":"Input (cinder/input)","s":"Guides","h":"Tests and references","u":"/docs/guides/input/#tests-and-references","x":"tests/input/test_input_map.mojo ( pixi run test-input ): edges per frame, key repeats, multiple bindings per action, digital axis clamping, dead zone rescaling, mouse accumulation and reset, listening for actions and axes, cancel_listening , rebind_action , and the exact save/load round trip including rejected versions, device names and headers, negative codes from a file, and the release of an action whose bindings are cleared or captured while it is held. The test builds events with Event.empty(EventKind.key()) and sets scancode , down , repeat ; use the same approach to drive a map without a window. sample/game/game.mojo : default_bindings() , the per-frame reads in update , and the pause"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Physics guide ( cinder/physics )","u":"/docs/guides/physics/","x":"cinder/physics is the safe Mojo layer over Jolt. Jolt is reached through joltc inside libcinder_native ; the parts of the shim that need C callbacks live in native/src/physics_shim.c (query filters, closest-hit collectors, CharacterVirtual extended update defaults) and native/src/physics_events.c (contact and activation listeners). Mojo never runs inside a Jolt callback ( ADR-0003 ): listeners write fixed-size events into the shim's lock-free queue and PhysicsWorld.poll_events() drains it on the calling thread. The package sits beside render , anim and audio in the module map ( docs/ARCHITECTURE.md ): it imports cinder/core (math), cinder/ecs (for PhysicsSync ), cinder/ffi and cinder/platfor"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Units and conventions","u":"/docs/guides/physics/#units-and-conventions","x":"Metres, seconds, kilograms, newtons; impulses in N·s. Right-handed, Y up, −Z forward, as everywhere in the engine. Quaternions are Quat(x, y, z, w) ; Quat.from_axis_angle takes radians, CharacterDesc.max_slope_degrees takes degrees. Body ids are UInt32 ( INVALID_BODY = 0xFFFFFFFF ). Capsules and cylinders stand along Y. A dynamic body with mass == 0 gets its mass from the shape volume at Jolt's default density, 1000 kg/m³."},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Creating a world","u":"/docs/guides/physics/#creating-a-world","x":"PhysicsWorld(lib, layers, max_bodies=4096, worker_threads=2, event_capacity=1 << 14) creates one Jolt PhysicsSystem , one JobSystemThreadPool with worker_threads workers, one event queue of event_capacity entries, the two listeners and one reusable body filter. lib is a NativePtr ( NativeLibrary.ptr() ). JPH_Init is idempotent in joltc, so every world calls it and the process-wide Jolt state is never shut down while a world exists. mojo from cinder.physics import BodyDesc, LayerConfig, MotionType, PhysicsWorld from cinder.platform import NativeLibrary comptime LAYER_STATIC = 0 comptime LAYER_MOVING = 1 comptime LAYER_GHOST = 2 def _layers () -> LayerConfig: \"\"\"0 static, 1 moving (collides wi"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Layers are bits","u":"/docs/guides/physics/#layers-are-bits","x":"LayerConfig(count, static_mask=1) declares up to 32 object layers; layer i is bit i in every query mask, so \"static only\" is UInt32(1) << LAYER_STATIC and the default mask 0xFFFFFFFF is everything. static_mask marks the layers that go to the static broad-phase layer, so static geometry is never tested against itself; everything else shares the moving broad-phase layer. collide(a, b) enables a pair (symmetric); pairs are checked against count when the world is built and create_body rejects layers outside [0, count) . LayerConfig.default() is two layers, 0 static and 1 moving, with moving colliding with both."},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Ownership and lifetimes","u":"/docs/guides/physics/#ownership-and-lifetimes","x":"The world owns Jolt. Its destructor destroys the filters, the system (which destroys any body still registered), the listeners, the job system and the queue, in that order. Keep the NativeLibrary alive past the world ( keep(lib) at the end of a test; the App declares the library field last so it is destroyed last, see docs/MOJO_NOTES.md ). Shapes are handles. Shape is ImplicitlyCopyable ; copy it freely into as many BodyDesc s as you like and call destroy_shape exactly once. Jolt reference-counts the shape, so a body keeps its shape alive and you may destroy the handle right after create_body (the tests' _floor helper does this). scaled(shape, ...) is a new shape that also needs destroying. "},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Shapes and bodies","u":"/docs/guides/physics/#shapes-and-bodies","x":"mojo var ball = physics.sphere( 0.5 ) var cube = physics.box(Vec3( 0.5 , 0.5 , 0.5 )) # half extents, convex radius 0.05 var pill = physics.capsule( 0.6 , 0.3 ) # half cylinder height, radius var wide = physics.scaled(cube, Vec3( 2.0 , 1.0 , 1.0 )) var pts = List[Vec3]() pts.append(Vec3( 0.0 , 0.0 , 0.0 )) pts.append(Vec3( 1.0 , 0.0 , 0.0 )) pts.append(Vec3( 0.0 , 1.0 , 0.0 )) pts.append(Vec3( 0.0 , 0.0 , 1.0 )) var hull = physics.convex_hull(pts) # at least 4 points var verts = List[Vec3]() # a triangle plateau at y = 2 verts.append(Vec3( 15.0 , 2.0 , - 5.0 )) verts.append(Vec3( 25.0 , 2.0 , - 5.0 )) verts.append(Vec3( 25.0 , 2.0 , 5.0 )) verts.append(Vec3( 15.0 , 2.0 , 5.0 )) var indices ="},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Stepping and events","u":"/docs/guides/physics/#stepping-and-events","x":"step(dt, collision_steps=1) runs JPH_PhysicsSystem_Update on the world's own thread pool and raises if Jolt reports an error; steps counts successful calls. collision_steps is Jolt's sub-step count for fast scenes. Call poll_events() once after each step; it returns every queued PhysicsEvent and refreshes dropped_events , the number of events the fixed-capacity queue had to drop (asserted zero in the tests; raise event_capacity if it is not). mojo physics.step( DT ) for ev in physics.poll_events(): if ev.kind == PhysicsEventKind.contact_added() and ev.pair(floor, moving): print ( \"hit the floor, normal\" , ev.normal, \"at\" , ev.point, ev.contact_count, \"points\" ) if ev.kind == PhysicsEventKind"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Queries","u":"/docs/guides/physics/#queries","x":"mojo var hit = physics.raycast(Vec3( 0.0 , 5.0 , 0.0 ), Vec3( 0.0 , - 10.0 , 0.0 )) if hit: print (hit.value().body, hit.value().fraction, hit.value().point, hit.value().normal) var floor_only = physics.raycast( Vec3( 0.0 , 5.0 , 0.0 ), Vec3( 0.0 , - 10.0 , 0.0 ), UInt32( 1 ) &#x3C;&#x3C; LAYER_STATIC ) var not_me = physics.raycast(Vec3( 0.0 , 5.0 , 0.0 ), Vec3( 0.0 , - 10.0 , 0.0 ), 0x FFFFFFFF , box) var probe = physics.sphere( 0.25 ) var sweep = physics.shape_cast( probe, Mat4.translation(Vec3( 0.0 , 5.0 , 0.0 )), Vec3( 0.0 , - 10.0 , 0.0 ), UInt32( 1 ) &#x3C;&#x3C; LAYER_STATIC ) if sweep and sweep.value().penetration_depth &#x3C;= 0.0 and sweep.value().normal.y() > 0.7 : var contact = V"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"The character controller","u":"/docs/guides/physics/#the-character-controller","x":"CharacterController wraps Jolt's CharacterVirtual . It is not a body: it has no id, it never appears in events or query results, and it moves itself by sweeping its capsule against the layers that desc.layer collides with. Its position is at its feet (the capsule is offset through shapeOffset ); only the lower hemisphere supports it, so standing on an edge counts as not_supported rather than ground. mojo var hero = CharacterController( _ptr(physics), # app.physics.ptr() in a game CharacterDesc( 0.3 , 1.8 , LAYER_MOVING ), # radius, total height, layer Vec3( 0.0 , 0.0 , 0.0 ), ) for _ in range ( 120 ): hero.update( DT , Vec3( 2.0 , 0.0 , 0.0 )) # walk +X at 2 m/s physics.step( DT ) if hero.gr"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"ECS synchronisation and the fixed-step loop","u":"/docs/guides/physics/#ecs-synchronisation-and-the-fixed-step-loop","x":"RigidBody (component id 7) links an entity to a body and keeps the two most recent simulated poses: RigidBody(body, motion, position, rotation, scale=Vec3.one(), interpolate=True) . Jolt carries no scale, so the component's scale is reapplied whenever it writes a transform. mojo var falling = world.spawn() var falling_body = physics.create_body( BodyDesc(cube, Vec3( 0.0 , 5.0 , 0.0 ), MotionType.dynamic(), LAYER_MOVING ) ) world.add[RigidBody]( falling, RigidBody(falling_body, MotionType.dynamic(), Vec3( 0.0 , 5.0 , 0.0 ), Quat.identity(), Vec3( 2.0 , 2.0 , 2.0 )), ) var lift = world.spawn() var lift_body = physics.create_body( BodyDesc(cube, Vec3( 5.0 , 0.5 , 0.0 ), MotionType.kinematic(), "},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Threading","u":"/docs/guides/physics/#threading","x":"step hands the islands to Jolt's own JPH_JobSystemThreadPool ( worker_threads , 2 by default). The engine's job system is not involved: joltc's job system cannot be driven by an external scheduler without calling back into Mojo ( ADR-0012 §1). Jolt's pool is idle outside the step so the two pools do not contend. Contact and activation callbacks run on Jolt's workers in C only; they push into the world's MPMC CnEventQueue . poll_events pops on the thread that calls it. Overflow is counted in dropped_events , never silent ( docs/THREADING.md ). Every PhysicsWorld and CharacterController method is called from one thread, the one running the fixed step (the main thread in App ). Nothing in the M"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Determinism","u":"/docs/guides/physics/#determinism","x":"Jolt's deterministic mode is on by default, so a world fed the same sequence of calls with the same inputs produces bit-identical results. test_deterministic_replay_of_recorded_inputs builds two fresh worlds, creates 72 bodies in the same order, replays the same impulses at the same steps and asserts every final position is exactly equal, then shows a different stream diverges. For replay in a game this means: same LayerConfig , same creation order, same dt and collision_steps , same inputs applied at the same step. Floating-point positions read back between steps may be compared exactly, as the test does."},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"Pitfalls","u":"/docs/guides/physics/#pitfalls","x":"Values passed to C by pointer must be followed by keep(value) after the last C call; every wrapper in this package does so (see docs/MOJO_NOTES.md , \"Untracked pointers do not keep their pointee alive\"). Follow the same pattern if you add a binding. Call update on a controller before the step, not after; call poll_events after the step. move_kinematic wants the next target, not a delta; call it every step while the platform moves. set_position_rotation teleports and does not carry riders. desc.mass is only applied to dynamic bodies; desc.linear_velocity only to non-static ones. A convex hull needs at least four points; a mesh needs indices in multiples of three. Destroy controllers before th"},{"t":"Physics guide (cinder/physics)","s":"Guides","h":"See also","u":"/docs/guides/physics/#see-also","x":"docs/adr/0012-physics-animation-audio-input.md , physics decisions 1 to 7. docs/adr/0003-native-shim-pull-model.md , why callbacks stay in C and events are pulled. docs/adr/0014-sample-game-and-performance.md , mesh colliders from glTF, query-based placement, the zero-allocation frame. docs/THREADING.md , docs/ARCHITECTURE.md (frame loop and conventions). docs/api/physics.md , the generated reference. tests/physics/test_physics_world.mojo : bodies, layers, sensors, kinematics and events; queries and mesh/hull/scaled colliders; the character controller; ECS sync with interpolation; deterministic replay. docs/guides/anim.md for driving a skinned character from the controller."},{"t":"cinder/platform guide","s":"Guides","h":"cinder/platform guide","u":"/docs/guides/platform/","x":"cinder/platform is the thin Mojo layer over SDL3 and the native shim's surface helpers. It loads libcinder_native exactly once, initialises SDL, creates windows, drains SDL's event queue into engine-level Event s, opens and closes gamepads as they appear, creates the wgpu surface for a window and, when the debug UI asks for it, forwards every raw SDL event to the ImGui backend. It sits directly above cinder/core and below cinder/gfx ; it is the only engine module with per-operating-system branches (library file names, surface creation inside the shim). Everything above it is platform-neutral. Source: cinder/platform/native.mojo , events.mojo , window.mojo . Reference: docs/api/platform.md . "},{"t":"cinder/platform guide","s":"Guides","h":"Key types","u":"/docs/guides/platform/#key-types","x":"Type Role Ownership and lifetime NativeLibrary Owns the one loaded copy of libcinder_native (the generated CinderNative struct: library handle plus every resolved symbol) at a stable heap address. Construct first, destroy last. __deinit__ destroys the CinderNative and frees the allocation, which closes the library. NativePtr Pointer[CinderNative, MutUntrackedOrigin] , from NativeLibrary.ptr() . How every subsystem refers to the library. Borrowed; valid while the NativeLibrary lives. default_library_path() CINDER_NATIVE_LIB if set, otherwise native/build/release/lib/libcinder_native.{so,dylib} relative to the repository root. Free function. Platform SDL initialised for video, events and gamep"},{"t":"cinder/platform guide","s":"Guides","h":"Loading the library","u":"/docs/guides/platform/#loading-the-library","x":"mojo from cinder.platform import NativeLibrary, Platform, Window var lib = NativeLibrary() # default_library_path(), raises if missing or ABI mismatch var platform = Platform(lib.ptr()) var window = Window(platform, String( \"cinder platform test\" ), 640 , 480 ) ... keep(window) keep(platform) keep(lib) NativeLibrary(path) resolves every bound symbol and compares cn_native_abi_version() with the bindings' CN_NATIVE_ABI_VERSION ; a mismatch raises libcinder_native ABI version N does not match the bindings' M . The library is loaded and checked before the heap cell that holds it is allocated, so a raise (missing file or symbol, ABI mismatch) unloads it and leaks nothing. The keep calls at the e"},{"t":"cinder/platform guide","s":"Guides","h":"Windows and events","u":"/docs/guides/platform/#windows-and-events","x":"Platform.poll(mut events) clears events (capacity is retained, so a warmed-up list does not allocate), pumps SDL with SDL_PollEvent , translates each event, opens or closes gamepads on gamepad_added / gamepad_removed , appends every event whose kind is not none() , and then drains the shim's SDL log queue into diagnostics . mojo var events = List[Event]() platform.poll(events) for ev in events: if ev.kind == EventKind.window_resized() and ev.window_id == window.id: swapchain.resize(Int(ev.a), Int(ev.b)) # a, b: new pixel width and height elif ev.kind == EventKind.key(): # ev.scancode, ev.keycode, ev.mods, ev.down, ev.repeat ... elif ev.kind == EventKind.quit() or ev.kind == EventKind.window_"},{"t":"cinder/platform guide","s":"Guides","h":"Injecting events","u":"/docs/guides/platform/#injecting-events","x":"Platform.push_event(ev: SDL_Event) -> Bool pushes a raw SDL event (tests, input replay). The test packs a variant into the 128-byte union and reads it back translated: mojo var key = SDL_KeyboardEvent( SDL_EVENT_KEY_DOWN , 0 , 0 , window.id, 1 , SDL_SCANCODE_W , 119 , 0 , 0 , True , False ) assert_true(platform.push_event(_blob(key))) # _blob copies the variant into an SDL_Event platform.poll(events) for ev in events: if ev.kind == EventKind.key(): assert_equal(Int(ev.scancode), Int( SDL_SCANCODE_W )) assert_true(ev.down and not ev.repeat) Creation events (focus, shown, resized) arrive on the first poll after Window construction; the test discards them with one poll before injecting."},{"t":"cinder/platform guide","s":"Guides","h":"Surfaces","u":"/docs/guides/platform/#surfaces","x":"Window.create_surface(instance: WGPUInstance) raises -> WGPUSurface calls cn_platform_create_surface , which picks X11, Wayland or Metal inside the shim from SDL's window properties and fills a 256-byte error string on failure (raised as cn_platform_create_surface failed: ... ). Window.destroy_surface(surface) releases it. There are two ways to pair a window with a device: mojo # 1. Gfx owns the surface: it is created with the device and destroyed in Gfx.__deinit__. var gfx = Gfx(lib.ptr(), window) var swapchain = Swapchain(_ptr(gfx), gfx.surface, size[ 0 ], size[ 1 ], True , True ) # 2. Headless device, surface managed by hand (tests of the platform layer). var gfx = Gfx(lib.ptr(), WGPUSurf"},{"t":"cinder/platform guide","s":"Guides","h":"ImGui event forwarding","u":"/docs/guides/platform/#imgui-event-forwarding","x":"Platform.forward_to_imgui is False until the debug UI ( cinder/tools ) sets it. While it is set, poll hands every raw SDL_Event to cn_imgui_process_event before translating it, so ImGui sees the same events the engine does (there is no capture test: both receive everything). The ImGui context itself is owned by the tools layer; the platform only forwards."},{"t":"cinder/platform guide","s":"Guides","h":"Threading and error handling","u":"/docs/guides/platform/#threading-and-error-handling","x":"Everything in this module runs on the main thread: SDL requires its event pump and window calls there ( docs/THREADING.md ). Platform.poll is called once per frame. SDL's log callback runs in C and writes into a CnEventQueue of capacity 256; poll drains it. No Mojo code runs inside the callback ( ADR-0003 ). Text from SDL (log lines, last_error , video_driver , error messages) is UTF-8 and is decoded with cinder.core.cstring.string_from_c ; invalid sequences become U+FFFD. Raises ( Error with a message that includes SDL_GetError() where SDL has one): NativeLibrary construction, Platform construction ( cn_event_queue_create failed , SDL_Init failed: ... ), Window construction ( SDL_CreateWind"},{"t":"cinder/platform guide","s":"Guides","h":"Pitfalls","u":"/docs/guides/platform/#pitfalls","x":"Declare the NativeLibrary last in any struct that also holds objects whose destructors call into it; struct fields are destroyed in declaration order ( docs/MOJO_NOTES.md ). In a function, keep the library, the platform and every window after their last use, on every return path. Window copies platform.lib at construction, not a reference to the Platform ; destroying the Platform (which calls SDL_Quit ) before the Window leaves SDL_DestroyWindow running after SDL has shut down. poll clears the caller's list. Keep one List[Event] field for the frame loop rather than building a new list each frame (allocation-free after warm-up). window_resized reports pixel size; a minimised window reports wi"},{"t":"cinder/platform guide","s":"Guides","h":"Related documents","u":"/docs/guides/platform/#related-documents","x":"docs/ARCHITECTURE.md (module map; platform is the only per-OS module), docs/THREADING.md . ADR-0003 docs/adr/0003-native-shim-pull-model.md : callbacks (SDL log included) are pulled from shim queues. ADR-0007 docs/adr/0007-dev-environment-container.md : Linux container, Xvfb/Weston for windowed tests, no macOS host. ADR-0009 docs/adr/0009-gfx-layer.md : swapchain over the surface created here. docs/MOJO_NOTES.md : OwnedDLHandle lifetime, keep , early-return hazards. Tests: tests/platform/test_platform.mojo (window lifecycle, resize event, surface, injected events, non-ASCII SDL log lines and window titles), tests/platform/test_swapchain.mojo (window + Gfx(lib, window) + Swapchain )."},{"t":"cinder/render: the renderer","s":"Guides","h":"cinder/render : the renderer","u":"/docs/guides/render/","x":"cinder/render turns a RenderScene (camera, lights, instances, joint palettes, environment) into one submitted command buffer per frame. It is a clustered forward+ renderer with cascaded and atlas shadow maps, GPU-baked image-based lighting, GTAO, physically based bloom, AgX or ACES tone mapping, optional MSAA and FXAA, meshoptimizer LODs, GPU skinning and morph targets, and a frame graph that orders passes and aliases transient textures. The design decisions are recorded in ADR-0010 ; the allocation-free steady state in ADR-0014 . In the layering of docs/ARCHITECTURE.md the module sits above cinder/gfx (typed wgpu handles, ShaderManager ), cinder/core (math, ByteWriter , logging) and cinder/"},{"t":"cinder/render: the renderer","s":"Guides","h":"Key types and ownership","u":"/docs/guides/render/#key-types-and-ownership","x":"Type Role Owned by Renderer Pipelines, layouts, persistent buffers and textures, mesh and material registries, the FrameGraph , ShaderLibrary , ShadowAtlas , frame scratch. The application ( App boxes it). Holds raw pointers to Gfx and Resources , which must outlive it. RenderSettings Quality knobs; Renderer.settings is read at the start of every render . Copied into the renderer; mutate renderer.settings between frames. RenderScene camera: Camera , lights: List[Light] , instances: List[RenderInstance] , joints: List[Mat4] , environment: Environment . The caller. The renderer only reads it. RenderInstance mesh and material indices into the renderer's registries, transform , joint_offset (-1 "},{"t":"cinder/render: the renderer","s":"Guides","h":"Setting up a renderer","u":"/docs/guides/render/#setting-up-a-renderer","x":"Adapted from tests/render/test_renderer.mojo . The native library, Gfx and Resources must be declared so that they are destroyed after the renderer (struct fields die in declaration order). mojo var lib = NativeLibrary() var gfx = Gfx(lib.ptr(), WGPUSurface()) # headless: no surface var res = Resources(_ptr(gfx), 4096 ) var settings = RenderSettings() var renderer = Renderer(_ptr(gfx), _ptr(res), lib.ptr(), settings, WGPUTextureFormat_RGBA8Unorm) var ground = renderer.add_mesh(String( \"ground\" ), plane( 8.0 , 4 ), False ) # no LODs var ball = renderer.add_mesh(String( \"sphere\" ), uv_sphere( 0.5 , 24 , 48 ), True ) var red = Material(String( \"red\" )) red.base_color = Vec4( 0.8 , 0.1 , 0.1 , 1"},{"t":"cinder/render: the renderer","s":"Guides","h":"The frame, in order","u":"/docs/guides/render/#the-frame-in-order","x":"Renderer.render(scene, target, width, height) performs the CPU work below, then records every pass of the compiled graph into one CommandEncoder and submits it. Environment bake (only when changed). If scene.environment (zenith, ground, sun_intensity , equirect ) or the sun direction differs from the last bake, or shaders were reloaded, _bake_environment runs the ibl.wgsl compute passes: the 256 px environment cube with a full mip chain ( cs_environment , procedural sky or equirect), 6 GGX-prefiltered mips at 128 px with 64 filtered importance samples each ( cs_prefilter ), a 32 px irradiance cube ( cs_irradiance ) and the 256 px split-sum BRDF LUT ( cs_brdf_lut ). These are submitted separa"},{"t":"cinder/render: the renderer","s":"Guides","h":"Render settings and defaults","u":"/docs/guides/render/#render-settings-and-defaults","x":"Field Default Effect msaa 1 1 or 4 samples for depth, HDR and the sky; GTAO switches to gtao_msaa.wgsl . Changing it rebuilds pipelines lazily (cached per sample count). bloom , bloom_strength , bloom_threshold True, 0.04, 1.0 Enables the 6-mip chain; strength is the mix factor in fs_tonemap ; threshold is the soft knee on the first downsample. tonemapper Tonemapper.agx() agx , aces or none (clamp). exposure 1.0 Linear multiplier before the tone curve. fxaa False Adds the ldr transient and the fxaa pass. ao True GTAO; when false the AO passes are culled from the graph and the forward pass binds the white default. shadows True Cascades and atlas. When false no shadow views are allocated and n"},{"t":"cinder/render: the renderer","s":"Guides","h":"Conventions","u":"/docs/guides/render/#conventions","x":"Right-handed, Y up, the camera looks down -Z ( Camera.forward() is Vec3.forward() rotated); plane faces +Y, cube winds counter-clockwise seen from outside, back faces are culled unless the material is double_sided . Metres; light intensity in lux for the sun and watts for point and spot lights ( color * intensity , radiometric / 4 pi in the shader); angles in radians. Reverse-Z with an infinite far plane everywhere: Mat4.perspective_reverse_z_infinite , depth cleared to 0 ( DepthAttachment.cleared_reverse_z ), DepthState.reverse_z compares greater-or-equal, the frustum has five planes and the shadow pipelines use a negative bias. Linear lighting; textures are uploaded as RGBA8UnormSrgb when "},{"t":"cinder/render: the renderer","s":"Guides","h":"Camera and culling","u":"/docs/guides/render/#camera-and-culling","x":"Camera.look_at builds the rotation quaternion from Mat4.look_at ; view , projection , view_projection and frustum derive from the fields, and set_viewport fixes the aspect from the target size ( render does this every frame). From tests/render/test_render_core.mojo : mojo var cam = Camera.look_at(Vec3( 0.0 , 2.0 , 5.0 ), Vec3.zero(), Vec3.unit_y(), 1.0 , 1.0 , 0.1 ) var f = cam.frustum() assert_true(f.contains_sphere(Vec3( 0.0 , 0.0 , - 50.0 ), 1.0 ), \"infinite far plane keeps distant objects\" ) assert_false(f.contains_point(Vec3( 0.0 , 2.0 , 5.05 )), \"closer than near\" ) var visible = f.cull_spheres(spheres) # List[Vec4]: xyz centre, w radius"},{"t":"cinder/render: the renderer","s":"Guides","h":"Meshes, LODs, skinning, morph targets","u":"/docs/guides/render/#meshes-lods-skinning-morph-targets","x":"MeshData holds parallel per-vertex lists; tangents , uv1 , joints and weights may be empty (defaults are packed at upload), but joints and weights are a pair: both empty or both one per vertex ( has_skin requires both). validate checks counts (including that pairing) and index ranges, compute_normals accumulates area-weighted normals, compute_tangents(lib) runs MikkTSpace through the shim. Primitives: cube(half) , uv_sphere(radius, rings, segments) , plane(half, subdivisions) . Renderer.add_mesh(label, data, lods=True) builds LODs with MeshData.build_lods when the mesh has more than 64 triangles: level 0 is vertex-cache optimised, each further level targets half the triangles with a 2% error"},{"t":"cinder/render: the renderer","s":"Guides","h":"Materials and textures","u":"/docs/guides/render/#materials-and-textures","x":"Material mirrors glTF 2.0 metallic-roughness plus KHR_texture_transform ( uv_scale , uv_offset , uv_rotation , applied to every map) and emissive_strength . textures holds five optional views ( TEX_BASE_COLOR , TEX_METALLIC_ROUGHNESS , TEX_NORMAL , TEX_OCCLUSION , TEX_EMISSIVE ), texcoords picks uv0 or uv1 per map, and sampler defaults to DefaultTextures.sampler_linear_repeat . pack_uniform produces the 96-byte layout documented at the top of material.mojo and in material.wgsl . mojo var tex = upload_image(res, String( \"checker\" ), img, True , True ) # sRGB, with mips var mat = Material(String( \"test\" )) mat.base_color = Vec4( 0.5 , 0.25 , 1.0 , 1.0 ) mat.textures[ 0 ] = tex.view mat.texcoor"},{"t":"cinder/render: the renderer","s":"Guides","h":"Lights and clustered culling","u":"/docs/guides/render/#lights-and-clustered-culling","x":"Light.sun(direction, color, lux, cast_shadows) , Light.point_light(position, color, watts, range, cast_shadows) and Light.spot_light(position, direction, color, watts, range, inner, outer, cast_shadows) construct lights; direction is the direction light travels. ClusterParams.compute (near, far) gives the logarithmic slice scale and bias shared with cluster_z_slice in common.wgsl ; the renderer uses camera.near to 200 m. cs_cull tests each local light as a sphere against each cluster's view-space AABB and writes up to CLUSTER_MAX_LIGHTS indices per cluster; the fragment shader walks that list. DebugMode.clusters() shows the per-cluster count as a heat map."},{"t":"cinder/render: the renderer","s":"Guides","h":"Shadows","u":"/docs/guides/render/#shadows","x":"cascade_splits_into(out, near, shadow_distance) fills a stack array of four end distances; fit_cascade fits an orthographic projection to the bounding sphere of a frustum slice (radius quantised to 1/16) and snaps its centre to whole shadow texels in a fixed light-space basis, so the footprint is rotation invariant and edges do not shimmer; fit_cascades_into does all four. The shader ( sample_cascade_shadow ) selects the cascade by view depth, applies a normal offset scaled by Cascade.texel_world and takes 3x3 PCF taps through a comparison sampler. ShadowAtlas(size, tile_size) hands out tiles in order: allocate(light) returns the first view index or -1 when full; a spot light gets a perspect"},{"t":"cinder/render: the renderer","s":"Guides","h":"The frame graph","u":"/docs/guides/render/#the-frame-graph","x":"The graph is declared from scratch every frame and compiled by FrameGraph.compile(res) : create(name, desc) declares a transient texture from a TransientDesc (size, format, usage, sample count, mips). import_view(name, view, desc) declares a view the caller owns (swapchain, the shadow maps): never aliased, never freed. create_external(name) declares a buffer-backed resource (the cluster lists) that only carries ordering and liveness. export(resource) marks what must survive the frame; the renderer exports target . add_pass(name) , read(pass, resource) , write(pass, resource) record the data flow. A reader depends on the most recent earlier writer (or the first writer if the producer is decla"},{"t":"cinder/render: the renderer","s":"Guides","h":"Adding a pass","u":"/docs/guides/render/#adding-a-pass","x":"Declare its resources in render next to the existing ones: self.graph.create for a new transient (give its name a persistent String field, not a per-frame literal), or reuse an existing one. var p_mine = self.graph.add_pass(String(\"my pass\")) followed by read and write calls. The pass is culled unless something it writes reaches target . In the recording loop add an elif p == p_mine: branch: fetch views with self.graph.view_of , begin a render or compute pass on encoder , bind groups, draw, end. Bind groups that reference transient views must be cached by view (index, generation) like _post_group and _gtao_group , because views change on resize. Create the pipeline once in __init__ (or lazil"},{"t":"cinder/render: the renderer","s":"Guides","h":"Shaders: assembly, errors, hot reload","u":"/docs/guides/render/#shaders-assembly-errors-hot-reload","x":"WGSL has no preprocessor. assemble(directory, entry) reads entry and splices every #include \"file.wgsl\" line (each file once per program, depth limit 16) into one AssembledShader whose chunks remember which file and line each run of text came from. ShaderLibrary.load(entry) assembles, compiles through ShaderManager.compile_source , adopts the module as a ShaderHandle and records the modification times of every file involved. The renderer loads eight programs: forward.wgsl , depth.wgsl , clusters.wgsl , sky.wgsl , ibl.wgsl , post.wgsl , gtao.wgsl , gtao_msaa.wgsl (the last two include gtao_body.wgsl after declaring depth_tex , load_depth and ao_out ). Compile errors raise ShaderError (from ci"},{"t":"cinder/render: the renderer","s":"Guides","h":"Adding a shader include","u":"/docs/guides/render/#adding-a-shader-include","x":"Put the file in cinder/render/shaders/ , reference it with #include \"name.wgsl\" from the program that needs it (includes resolve relative to SHADER_DIR ), and keep the declaration order in mind: an include sees everything spliced before it. If the include declares bindings, mirror the layout in Renderer.__init__ ( create_bind_group_layout ) and any CPU packing in Mojo with a byte-count assertion like the frame uniform's. tests/render/test_shader_programs.mojo:: test_all_programs_compile must still compile every program and assemble must report the expected file count."},{"t":"cinder/render: the renderer","s":"Guides","h":"How validation errors surface","u":"/docs/guides/render/#how-validation-errors-surface","x":"wgpu validation errors are uncaptured errors delivered through the shim's event queue; Gfx.pump increments Gfx.error_count and appends a wgpu.validation LogRecord to Gfx.diagnostics . Every render test asserts gfx.error_count == 0 after pumping. A validation error inside wgpuQueueSubmit (for example a readback stride that is not 256-aligned) aborts the process instead (see docs/MOJO_NOTES.md ). Shader compile errors raise ShaderError with mapped file:line:column ; reload failures become diagnostics instead of exceptions. Device loss sets Gfx.lost ; render raises GfxError(\"device lost: ...\") on the next call. Engine-side invariants raise Error : frame graph cycles, view_of on an external or u"},{"t":"cinder/render: the renderer","s":"Guides","h":"Golden tests","u":"/docs/guides/render/#golden-tests","x":"tests/golden/test_golden_gltf.mojo renders 16 Khronos glTF-Sample-Assets models headless at 512 x 512 with RenderSettings() plus msaa = 4 , a sun from (-0.5, -1.0, -0.4) at 3 lux, the procedural sky, and shadow_distance scaled to the model; the camera is framed from the model's bounds (Sponza is viewed from inside). The result is compared with tests/golden/<platform>/<Model>.png by cinder.core.image.compare : luma SSIM over 8x8 windows must be at least 0.985 and at most 0.5% of pixels may differ by more than 8/255 in any channel. Both thresholds, and the review of every reference image, live in docs/RENDER_VALIDATION.md . Running: pixi run test-golden (also part of tools/run_tests.sh ; model"},{"t":"cinder/render: the renderer","s":"Guides","h":"Render stats","u":"/docs/guides/render/#render-stats","x":"Renderer.stats ( FrameStats ) after each frame: instances_submitted , instances_drawn , draw_calls (forward opaque and blend), prepass_draw_calls , shadow_draw_calls , lights , shadow_views , passes (executed) and transient_textures (pooled physical textures)."},{"t":"cinder/render: the renderer","s":"Guides","h":"Zero-allocation rules","u":"/docs/guides/render/#zero-allocation-rules","x":"tests/perf/test_zero_alloc.mojo ( pixi run test-zero-alloc , libcinder_alloccount.so preloaded) renders a representative scene for 90 warm-up frames and then requires exactly zero Mojo heap allocations over 60 frames. Any change to the frame path must keep that true: Per-frame data lives in persistent scratch fields of Renderer ( _lights , _cascades , _spheres , _visible , _order , _keys , batches , instance_bytes , the ByteWriter s, ...) that are cleared, never recreated. List.clear and ByteWriter.reset keep capacity; resize only allocates when growing. Use the _into variants: cull_spheres_into , fit_cascades_into , cascade_splits_into , pack_lights_into , pack_views_into . The allocating f"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Scene guide ( cinder/scene )","u":"/docs/guides/scene/","x":"cinder/scene is the layer between the ECS and the rest of the engine's content pipeline. It has three independent parts: format.mojo : versioned scene serialisation, a diffable text format and a binary format over the same intermediate document ( SceneDoc ), with SceneComponent / SceneRegistry deciding which component types are saved and how old documents migrate. render_sync.mojo : the render components ( MeshRenderer , LightSource , CameraComponent , JointPalette ) and RenderBridge , which turns the world into the renderer's RenderScene . gltf_spawn.mojo : spawn_gltf , which turns a loaded GltfAsset into an entity hierarchy, and collision_triangles for static mesh colliders. It imports cin"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Scene components and the registry","u":"/docs/guides/scene/#scene-components-and-the-registry","x":"A component becomes saveable by conforming to SceneComponent , which extends Component with a stable name and a field writer/reader pair: mojo from cinder.scene import FieldReader, FieldWriter, SceneComponent @fieldwise_init struct Position ( SceneComponent ): var x: Float32 var y: Float32 var z: Float32 @ staticmethod def component_id () -> Int: return 20 @ staticmethod def scene_name () -> String: return String( \"Position\" ) def write_scene (self, mut w: FieldWriter): w.float(String( \"x\" ), self .x) w.float(String( \"y\" ), self .y) w.float(String( \"z\" ), self .z) @ staticmethod def read_scene ( mut r: FieldReader) -> Position: return Position(r.float(String( \"x\" ), 0.0 ), r.float(String( \"y"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Documents, text and binary","u":"/docs/guides/scene/#documents-text-and-binary","x":"SceneDoc(version, entities: List[EntityRecord]) ; an EntityRecord is a list of ComponentRecord(name, fields) ; a Field has a name , a kind ( KIND_FLOAT , KIND_INT , KIND_BOOL , KIND_STRING , KIND_ENTITY , KIND_FLOATS ) and the value in number / floats , integer , or text . The document is the unit that is read, migrated and written, in either format, independently of a world: mojo var doc = reg.to_doc(world) # every live entity, archetype order var text = write_text(doc) # \"cinder-scene 2\\nentity 0\\n Position x=1.5 ...\" var back = read_text(text) var world2 = World() var spawned = reg.instantiate(world2, back) # entities in file order assert_equal(write_text(reg.to_doc(world2)), text) # byte"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Text format (version 2)","u":"/docs/guides/scene/#text-format-version-2","x":"cinder-scene 2 entity 0 Position x=1.5 y=-2.0 z=0.1 Label text=\"quote \\\" backslash \\\\ newline\\nend\" visible=false layer=7 entity 1 Link target=#0 transform=[1.0,0.0,0.0,0.0, ... ,3.0,1.0] Health health=42 Grammar as implemented by read_text : The first line is cinder-scene <version> ; anything else is missing header . Blank lines and lines whose first non-blank character is # are skipped. entity <n> opens entity n ; indices must be consecutive from 0 ( out of order ). Every other line is <ComponentName> key=value ... ; leading whitespace is ignored. A component line before the first entity is an error. Values: \"...\" string with \\\" , \\\\ and \\n escapes; #n entity reference; true / false ; [a,b"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Binary format","u":"/docs/guides/scene/#binary-format","x":"Little-endian, magic CSCN , then u32 version , u32 entity count , and per entity u32 component count , per component string name ( u32 length + bytes), u32 field count , per field string name , u8 kind , then the payload: f32 for floats; two u32 (low, high) of an i64 for ints, bools and entity indices; a string for strings; u32 count + f32 s for float lists. String bytes are UTF-8; a string that is not valid UTF-8 is rejected ( not valid UTF-8 ). read_binary bounds-checks every read and rejects implausible counts (more than ten million entities, ten thousand components or fields, a million floats), bad magic and truncation ( unexpected end of data ). Neither format is compressed."},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Versioning and migrations","u":"/docs/guides/scene/#versioning-and-migrations","x":"SCENE_VERSION is 2. A document's version is whatever its header says; SceneRegistry.migrate applies registered (from_version, MigrationFn) pairs one step at a time until the document is at SCENE_VERSION , raising no migration to N when a step is missing and newer than this engine for a document from the future. instantiate refuses a document that is not at the current version, so the call order is read, migrate, instantiate: mojo def _migrate_1_to_2 (doc: Pointer[SceneDoc, MutUntrackedOrigin]): for i in range ( len (doc[].entities)): for c in range ( len (doc[].entities[i].components)): if doc[].entities[i].components[c].name == \"Health\" : _ = doc[].entities[i].components[c].rename_field(Str"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Render bridge","u":"/docs/guides/scene/#render-bridge","x":"render_sync.mojo defines the components the renderer understands: component id fields MeshRenderer 4 mesh , material (renderer indices), cast_shadows LightSource 5 kind: LightKind , color , intensity , range , inner_angle , outer_angle , cast_shadows ; position and direction (local -Z) come from WorldTransform CameraComponent 6 fov_y , near , active ; placed by WorldTransform (-Z forward, +Y up), aspect kept from the scene's current camera JointPalette 9 joints: List[Mat4] , morph_weights: List[Float32] , written each frame by the animation system for entities with an AnimatorRef RenderBridge.build(world, scene) clears scene.instances , scene.lights and scene.joints and refills them from thr"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Spawning glTF","u":"/docs/guides/scene/#spawning-gltf","x":"spawn_gltf(renderer, res, asset, world, root_transform, lods, cast_shadows, skinned_meshes) uploads the asset ( upload_gltf , see the assets guide ) and builds entities: one root entity carrying LocalTransform(root_transform) ; one entity per glTF node reachable from the asset's roots, with LocalTransform(node.local) and Parent pointing at the node's parent entity (or the root). A node the file reaches more than once (listed as a root and as a child, shared by two parents, or inside a cycle) is spawned once, the first time it is reached; when its glTF parent has not been spawned by then it hangs under the root, so the Parent links never form a cycle; for a node with a mesh: when the mesh has"},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Threading rules","u":"/docs/guides/scene/#threading-rules","x":"to_doc , instantiate , migrate , spawn_gltf and RenderBridge.build all touch the World (instantiate and spawn structurally) and are main-thread calls. spawn_gltf and upload_gltf create GPU resources through Renderer / Resources ; GPU work is main-thread only in Cinder. read_text , read_binary , write_text , write_binary and migrations only touch a SceneDoc and could run anywhere, but nothing in the engine currently runs them off the main thread. JointPalette is written by AnimationSystem.publish on the main thread after its pose jobs complete; the bridge reads it afterwards in the same frame."},{"t":"Scene guide (cinder/scene)","s":"Guides","h":"Pitfalls","u":"/docs/guides/scene/#pitfalls","x":"SceneRegistry has no duplicate-name check: registering two types with the same scene_name() means the first wins on load and both are written on save. FieldReader.float accepts an integer field (text x=1 parses as an int), but int does not accept a float, bool accepts only true / false (not 0 / 1 ), and string does not accept a bare token, so write strings quoted. to_doc writes Parent references only if Parent is registered as a SceneComponent ; the engine's Parent , LocalTransform and render components are plain Component s and are not saved unless you wrap them. The sample saves its own gameplay components and rebuilds transforms, bodies and renderables in a \"realise\" pass after instantiat"},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Tools: debug overlay, Tracy profiling, headless mode","u":"/docs/guides/tools/","x":"Cinder's developer tooling is three things: the Dear ImGui debug overlay in cinder/tools , the Tracy client wrapper in cinder/core/profile.mojo , and the --headless mode of cinder/app that lets the same executables run without a display for tests, benchmarks and captures. Sources: cinder/tools/debug_ui.mojo , cinder/tools/cstrings.mojo , cinder/core/profile.mojo , native/src/profile.c , native/src/imgui_shim.cpp . API reference: docs/api/tools.md , docs/api/core.md . Design: docs/adr/0013-tools-and-app-runtime.md (decisions 2, 4, 5, 7)."},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"The debug overlay ( DebugUi )","u":"/docs/guides/tools/#the-debug-overlay-debugui","x":"DebugUi draws one ImGui window titled \"Cinder\" with five tabs. ImGui itself runs in the shim: cn_imgui_init creates the context and the SDL3 + WebGPU backends, cn_imgui_process_event feeds raw SDL events, cn_imgui_new_frame , cn_imgui_render and cn_imgui_shutdown wrap the frame and teardown. Widgets are called through the generated dcimgui bindings ( lib[].ImGui_Begin , ImGui_TextUnformatted , ImGui_InputFloat , ...), so a Game.draw_ui can draw its own windows with the same functions; the sample's HUD and pause menu do."},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Ownership and lifetime","u":"/docs/guides/tools/#ownership-and-lifetime","x":"App creates the overlay when it has a window and AppConfig.debug_ui is true ( --no-debug-ui disables it; headless apps never have one). It is App.debug_ui: Optional[DebugUi] , the first field of App , so it is destroyed first; its __deinit__ calls cn_imgui_shutdown . The constructor takes the native library pointer, the SDL_Window , the WGPUDevice and the swapchain format, and raises if the backend fails to initialise. Setting Platform.forward_to_imgui = True (done by App ) makes Platform.poll hand every raw SDL event to ImGui before translating it. Per frame, App._draw_overlay calls begin_frame(dt) (records the frame time, clears the label buffers, ImGui::NewFrame ), then game.draw_ui(app) "},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Tabs","u":"/docs/guides/tools/#tabs","x":"Tab Index Contents Frame 0 ImGui_PlotLinesEx of the last HISTORY = 240 frame times (ms, 0 to 33.3 scale) with the average overlaid; renderer stats ( draw_calls , prepass_draw_calls , shadow_draw_calls , instances_drawn / instances_submitted , lights , shadow_views , passes , transient_textures ); live physics objects, live sounds; total live GPU resources ( res[].live_counts().total() ). Entities 1 Entity count and archetype count; a list of entity <index> selectables; the inspector for the selected entity. Assets 2 \"no asset server mounted\", or live_count() / in_flight , then one line per referenced asset record: logical name, refcount, state ( ready , loading , failed: <error> ), file. Ren"},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"What the inspector edits, and through what","u":"/docs/guides/tools/#what-the-inspector-edits-and-through-what","x":"The inspector is data-driven ( ADR-0013 , decision 5). Each frame _entities_tab calls registry.to_doc(world) , which serialises every component type registered on the SceneRegistry ( app.registry.register[T]() for a SceneComponent ) into a SceneDoc , and snapshots world.entities() . The selected entity's ComponentRecord s are drawn as tree nodes, one widget per field by kind: KIND_FLOAT : ImGui_InputFloat ; KIND_INT : ImGui_InputInt . KIND_FLOATS with up to 4 values: one ImGui_InputFloat per element (dcimgui passes float[3] by value in the generated bindings, so DragFloat3 cannot write back). Longer arrays are shown as a count only. Everything else ( KIND_BOOL , KIND_STRING , KIND_ENTITY ) i"},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Input capture","u":"/docs/guides/tools/#input-capture","x":"wants_mouse() and wants_keyboard() return ImGui's WantCaptureMouse / WantCaptureKeyboard (false when there is no ImGui context). App._poll reads them before processing the frame's events and does not forward mouse presses, motion and wheel, or key presses, to App.input while they are set, so typing into an overlay field does not move the player ( ADR-0013 , decision 7). Key and mouse button releases are always forwarded ( cinder.app.app.ui_consumes_event ), so a key held when the overlay takes focus does not stay held in the input map. They answer from the previous frame's ImGui state, which is the usual immediate-mode one-frame lag."},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"CStrings","u":"/docs/guides/tools/#cstrings","x":"ImGui keeps label pointers until the frame is rendered, and a Mojo String has inline small-string storage that moves when the list holding it grows ( docs/MOJO_NOTES.md , Phase 7). CStrings copies each label into its own List[UInt8] with a NUL terminator; the outer list may reallocate, but each buffer's heap data stays put, so the pointer stays valid until clear() . DebugUi owns one ( _labels ) and clears it in begin_frame . A game drawing its own ImGui uses the same pattern: mojo from cinder.tools import CStrings var ui = CStrings() # a field of the game ui.clear() # once per frame, at the top of draw_ui if lib[].ImGui_Begin(ui.c(String( \"hud\" )), Optional[Pointer[Bool, MutUntrackedOrigin]]"},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Tracy profiling ( cinder/core/profile.mojo )","u":"/docs/guides/tools/#tracy-profiling-cindercoreprofilemojo","x":"The shim is built with the Tracy client ( CINDER_TRACY , default ON in native/CMakeLists.txt , Tracy v0.15.0 vendored under third_party/tracy ) compiled with TRACY_MANUAL_LIFETIME , TRACY_DELAYED_INIT and TRACY_ON_DEMAND . Nothing runs, no socket is opened and no thread is started until cn_profile_start , and every emit function is a no-op while the profiler is stopped ( native/src/profile.c ). Tests and headless runs therefore never touch the network."},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Profiler and Zone","u":"/docs/guides/tools/#profiler-and-zone","x":"Profiler(lib) is an ImplicitlyCopyable handle; copies share the process-wide client. App holds one as app.profiler . Call Effect available() Whether the shim was built with Tracy. start() Starts the client (idempotent). Returns False without Tracy. --profile / AppConfig.profile calls this at app construction. stop() Shuts the client down (idempotent). is_started() , is_connected() Client state; is_connected is whether a Tracy server is attached. zone(name, color=0) -> Zone Opens a zone. COLOR_RENDER , COLOR_PHYSICS , COLOR_ANIM , COLOR_AUDIO , COLOR_ASSETS are the engine's colours. frame_mark() Ends a frame ( App.run_frame calls it last). plot(name, value) A plot sample. Tracy keeps the name"},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Attaching the Tracy server","u":"/docs/guides/tools/#attaching-the-tracy-server","x":"Run the executable with --profile (or set AppConfig.profile , or call app.profiler.start() later, for example from a menu), then connect the Tracy profiler GUI of the same major version (0.15) to the machine running the game; with TRACY_ON_DEMAND the client waits for a connection and starts streaming when one arrives. is_connected() becomes true at that point. Data captured: the app's zones, Renderer.render and AssetServer.update zones, frame marks, and any plots and messages you emit. tests/core/test_profile.mojo runs the whole lifecycle with no server attached and asserts that everything is a no-op while stopped. The server is not part of the repository; install it separately."},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"--headless as a tooling feature","u":"/docs/guides/tools/#--headless-as-a-tooling-feature","x":"parse_app_args gives every Cinder executable --headless , and AppConfig.headless = True makes App run the full frame without a window: no SDL platform, a Gfx without a surface, an RGBA8 offscreen render target, a device-less miniaudio engine that run_frame pumps with read_frames , and no overlay. App.read_frame() returns the last frame as an Image . This is what makes the following work in CI and in containers with only a software Vulkan driver ( REPORT.md ): tests/app/test_app_headless.mojo and tests/sample/test_sample_game.mojo run gameplay, read back pixels and assert gpu_error_count() == 0 and live_objects() == 0 . tests/perf/test_zero_alloc.mojo runs 90 warm-up and 60 measured frames an"},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Threading","u":"/docs/guides/tools/#threading","x":"The overlay and the profiler wrapper are main-thread code. ImGui is called only from the main thread inside run_frame ; Tracy's own worker thread is started by cn_profile_start inside the client library and never runs Mojo (the client reads the zone and frame data the main thread emits). Jolt, the job workers and the audio thread are described in docs/THREADING.md ."},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Pitfalls","u":"/docs/guides/tools/#pitfalls","x":"A Zone must be consumed ( _ = z^ ) where you want it to end; see above. Profiler.start() after stop() works, but zones opened before a stop() and ended after it are dropped silently ( cn_profile_zone_end checks g_started ). ImGui enum constants have their own types in the bindings; wrap them ( Int32(ImGuiCond_FirstUseEver) ). Passing self.entities[i] into a mut self method is an aliasing error; copy into a local first ( debug_ui.mojo does this for the inspector). DebugUi.draw takes renderer: Renderer immutably and calls renderer.graph.to_graphviz() every frame while the tab is open; that allocates the DOT text, so keep the Render graph tab closed when measuring allocations (the zero-allocati"},{"t":"Tools: debug overlay, Tracy profiling, headless mode","s":"Guides","h":"Tests and references","u":"/docs/guides/tools/#tests-and-references","x":"tests/tools/test_debug_ui.mojo : windowed app, six frames with the overlay, inspector snapshot of two entities, select_tab(1) , wants_mouse / wants_keyboard , no GPU errors or error logs. tests/core/test_profile.mojo : no-ops while stopped, idempotent start, zones with text, nested zones, plots, messages, frame marks, clean stop; plot names interned once per name and still valid after the caller's String and later table growth. tests/app/test_app_headless.mojo , tests/perf/test_zero_alloc.mojo , bench/ . docs/adr/0013-tools-and-app-runtime.md ; docs/MOJO_NOTES.md (Phase 7 pitfalls); docs/STATUS.md (Phase 7 row); docs/guides/app.md ."},{"t":"Cinder architecture","s":"Engine design","h":"Cinder architecture","u":"/docs/architecture/","x":""},{"t":"Cinder architecture","s":"Engine design","h":"Module map","u":"/docs/architecture/#module-map","x":"Dependencies point downward only. A module may import from the layers below it, never from the same layer except where noted, and never from above. sample/ third-person sample game (Mojo), a `Game` run by cinder/app └── cinder/app App runtime: owns every subsystem, fixed-step loop, headless mode, CLI └── cinder/tools ImGui debug overlay (inspector, asset browser, render graph, log) └── cinder/{render,physics,anim,audio,input,assets,scene} └── cinder/ecs archetype ECS, queries, command buffers, hierarchy, scheduler └── cinder/gfx safe typed wgpu layer, shader manager, hot reload, debug draw └── cinder/platform SDL3 window/input/gamepad, surfaces, ImGui event forwarding └── cinder/core log, as"},{"t":"Cinder architecture","s":"Engine design","h":"The C boundary","u":"/docs/architecture/#the-c-boundary","x":"One shim, one shared library, flat C ABI ( cinder_native.h , C11). Pull model. C callbacks (wgpu async requests, device lost, uncaptured errors, Jolt contacts, miniaudio data, SDL log) write fixed-size events into fixed-capacity SPSC/MPSC lock-free rings inside the shim. Mojo drains them once per frame on the main thread. Mojo code never executes on a C thread unless spike 4 proves it safe and an ADR records that ( ADR-0003 ). Generated raw bindings live in cinder/ffi and are never edited by hand. A C program emits sizeof / alignof / offsetof for every bound struct and every enum value; a generated Mojo test asserts identical values. Mismatch fails CI. Safe wrappers: one distinct handle stru"},{"t":"Cinder architecture","s":"Engine design","h":"Threading model","u":"/docs/architecture/#threading-model","x":"Main thread: SDL event pump, window, presentation ( wgpuSurfacePresent ), ImGui. Job system ( cinder/core/jobs ): one worker per physical core minus one, work-stealing deques, a per-frame dependency graph. Everything else (culling, animation, ECS systems, asset decode, physics step through Jolt's own job system adapter) runs there. Shared state crosses threads only through jobs, atomics or documented locks named in docs/THREADING.md . Audio: miniaudio's device thread runs in C only; it reads a lock-free command ring and never calls Mojo."},{"t":"Cinder architecture","s":"Engine design","h":"Frame loop","u":"/docs/architecture/#frame-loop","x":"Fixed 60 Hz simulation with accumulator ( App.run_frame : poll events, fixed steps with physics and Game.fixed_update , Game.update , animation jobs, transform propagation, audio sync, render-scene bridge, render, ImGui, present or offscreen readback). Per-frame memory lives in persistent scratch fields that are cleared, never freed, between frames; steady state performs zero heap allocations from Mojo code (asserted by tests/perf/test_zero_alloc.mojo , ADR-0014 )."},{"t":"Cinder architecture","s":"Engine design","h":"Conventions (fixed)","u":"/docs/architecture/#conventions-fixed","x":"Right-handed, Y up, −Z forward, metres, column-major matrices, reverse-Z with infinite far plane, linear lighting, sRGB render targets, RGBA16F intermediates. Recoverable failures raise typed errors; broken invariants abort with a message."},{"t":"Threading model","s":"Engine design","h":"Threading model","u":"/docs/threading/","x":"Fixed by section 4 of the brief; this page records how it is realised and which shared state exists."},{"t":"Threading model","s":"Engine design","h":"Threads","u":"/docs/threading/#threads","x":"Thread Owner Runs Main process SDL event pump, window and input, surface acquire and wgpuSurfacePresent , ImGui, frame orchestration; also executes jobs while waiting on a counter. Job workers ( cinder-worker ), one per logical core minus one JobSystem ( cinder/core/jobs.mojo ), threads created by cn_thread_create Everything else: culling, animation, ECS systems, asset decode, physics step orchestration. Jolt workers Jolt's own job system ( JPH_JobSystemThreadPool ) Physics islands; contact callbacks are posted to the MPMC event queue from these threads. miniaudio device thread miniaudio Mixing; only the shim's log and end-of-sound callbacks run there and they post events. wgpu-native intern"},{"t":"Threading model","s":"Engine design","h":"Shared state and how it is protected","u":"/docs/threading/#shared-state-and-how-it-is-protected","x":"State Mechanism Job deques, job table, counters Chase-Lev deque protocol with Atomic and explicit orderings; counters are atomic. Worker parking cn_semaphore_* (counting semaphore) plus an atomic idle count; submit posts only when a worker is parked. Native callback results (wgpu, Jolt, miniaudio, SDL log) CnEventQueue , a bounded lock-free MPMC ring in the shim; drained by the main thread each frame. Overflow is counted, never silent. Everything else Owned by one thread. Cross-thread hand-over happens only by submitting a job whose data the submitter keeps alive until the counter reaches zero."},{"t":"Threading model","s":"Engine design","h":"Rules","u":"/docs/threading/#rules","x":"Mojo code never runs inside a C callback ( ADR-0003 ). The only Mojo functions that run on native threads are job-system worker entry points, which Cinder itself created. A job's data pointer must stay valid until the counter it was submitted with reaches zero; the submitter owns that memory (arena, pool or stack with keep ). JobSystem.reset() is called at frame start only, when no job is queued or running. No locks exist in Mojo code. If one ever becomes necessary it must be listed here with its owner and the invariant it protects."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Mojo 1.2.0 notes for Cinder","u":"/docs/mojo-notes/","x":"Source of truth: https://mojolang.org/docs (manual and std reference for 1.2.0) and the release notes for 1.0.0, 1.1.0 and 1.2.0. Everything below was read from those pages on 2026-11-08 and confirmed by compiling spikes/hello/ under the pinned toolchain. When the compiler disagrees with this file, the compiler and the current docs win; update this file."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Forms we use","u":"/docs/mojo-notes/#forms-we-use","x":""},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Functions and conventions","u":"/docs/mojo-notes/#functions-and-conventions","x":"def is the only function keyword. fn was removed in 1.1.0 . Functions are non-raising by default. Add raises after the parameter list; typed errors are written def f() raises MyError -> Int: . Argument conventions: default (immutable borrow, spelled imm when explicit), mut , var (take ownership, caller passes x^ ), ref (mutability follows the referenced value), out (uninitialised, must be initialised; used for self in __init__ and for named results), deinit (consumed; used for self in __deinit__ ). read as a convention is a hard error since 1.1.0; use imm . C ABI: def f(a: Int32) abi(\"C\") -> Int32: . Function pointer type: def(Int32) thin abi(\"C\") -> Int32 . thin means a function pointer, no"},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Compile time","u":"/docs/mojo-notes/#compile-time","x":"comptime declares compile-time values: comptime pi = 3.14159 . alias was removed in 1.1.0. comptime if / comptime for replace @parameter if / @parameter for (removed in 1.1.0). comptime match evaluates patterns at compile time (1.2.0). Parameters: def repeat[count: Int](msg: String): , struct FixedBuffer[length: Int]: . Constraints: trailing where <cond> else \"<message>\" after the signature (preferred form in 1.2.0)."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Variables","u":"/docs/mojo-notes/#variables","x":"Every variable is declared with var . Implicit declaration is an error since 1.1.0. let does not exist. := only assigns to existing variables."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Structs, traits, lifecycle","u":"/docs/mojo-notes/#structs-traits-lifecycle","x":"struct Name(TraitA, TraitB): ; conformance is always explicit. @fieldwise_init generates the field-wise initialiser. Structs are Movable by default and not copyable. Declare Copyable for a generated copy initialiser ( __init__(out self, *, copy: Self) ), ImplicitlyCopyable to allow implicit copies. Prefer x.copy() . Destructor: def __deinit__(deinit self): . __del__ still compiles but warns; we treat it as banned. Values are destroyed ASAP after their last use. C-layout structs: struct DivT(RegisterPassable): with C-compatible fields in C order; @align(n) for alignment. @register_passable as a decorator is not the current form. @export marks a function for export from a built library. @inlin"},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Strings and formatting","u":"/docs/mojo-notes/#strings-and-formatting","x":"String (mutable, UTF-8), StringSpan (immutable view; StringSlice is a deprecated alias), StringLiteral , CStringSpan (C strings; as_c_string_span() ). Formatting uses template strings: t\"value={x}\" ( TString , lazy). String.format exists but we use t-strings. MutStringSpan / MutStringSlice were removed in 1.2.0. Iterating a String yields grapheme clusters; use .bytes() or .codepoints() explicitly."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Pointers and memory (std.memory)","u":"/docs/mojo-notes/#pointers-and-memory-stdmemory","x":"Pointer[T, origin] is the raw pointer. UnsafePointer is a deprecated alias (1.0.0). MutPointer , ImmPointer , OptionalPointer , OpaquePointer (= Pointer[NoneType] ). Every unsafe operation is prefixed: ptr[unsafe_offset=i] , unsafe_load() , unsafe_store() , unsafe_offset(n) , unsafe_bitcast[T]() , unsafe_write(value^) / unsafe_write(copy=v) , unsafe_take_pointee() , unsafe_deinit_pointee() , unsafe_free() . Unprefixed load/store/free are deprecated and banned here. Pointer(to=value) borrows an existing value. Pointer[T, MutUntrackedOrigin](unsafe_from_address=addr) . Allocation: alloc[T]({count = n}) or alloc(Layout[T](count=n)) returns an Allocation ; allocation.unsafe_ptr() , into_thin() f"},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Collections","u":"/docs/mojo-notes/#collections","x":"List[T] ( capacity() is a method; resize(new_length=, fill=) ), Array[T, length] (replaces InlineArray ; .length ), Span ( Span(unsafe_ptr=, length=) ; ImmSpan , MutSpan ), Dict , Set , Optional (iterable), Variant ( unwrap[T]() ), Tuple ( Tuple[] is the empty tuple type). A list literal defaults to Array , not List ; annotate var l: List[Int] = [...] when a List is wanted. Negative slice indices and out-of-range insert abort; always spell indices out."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"SIMD and numerics","u":"/docs/mojo-notes/#simd-and-numerics","x":"SIMD[dtype, length] ; Scalar[dtype] ; Float32 , Int32 , etc. The parameter is length , not size . SIMD.from_bytes()/as_bytes() take endian: Endian ; compare against Endian.native() . std.simd holds SIMD (moved out of std.builtin in 1.1.0)."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"FFI (std.ffi)","u":"/docs/mojo-notes/#ffi-stdffi","x":"external_call[\"symbol\", ReturnType](args...) for symbols linked into the binary; variadics need num_fixed_args= . OwnedDLHandle(path) for runtime loading; get_function[ReturnType](\"name\") , check_symbol(\"name\") . Argument types are not checked against C, which is why Cinder has layout and signature tests. c_int , c_uint , c_char (target signedness since 1.2.0), c_uchar , c_short , c_long , c_long_long , c_size_t , c_ssize_t , c_float , c_double . Large by-value struct returns are not directly callable; the C side takes an out-pointer. This is exactly the subject of spike 3. mojo build has no -l / -L ; use -Xlinker and MODULAR_MOJO_MAX_SYSTEM_LIBS , or OwnedDLHandle ."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Testing (std.testing)","u":"/docs/mojo-notes/#testing-stdtesting","x":"Tests are module-level def test_*() raises: functions. Each test file ends with def main() raises: TestSuite.discover_tests[__functions_in_module()]().run() and is run with mojo run -I <src> <file> . There is no mojo test subcommand in 1.2.0. Assertions: assert_true , assert_false , assert_equal , assert_not_equal , assert_almost_equal , assert_is , assert_is_not , assert_raises (context manager), assert_aborts module, testing.prop for property tests. Filtering: --only , --skip , --skip-all ; programmatic suite.skip[test]() . Cinder never skips a test to make a run pass (section 10 of the mission)."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"GPU","u":"/docs/mojo-notes/#gpu","x":"std.gpu is private; GPU compute lives in max.gpu . Cinder does not use it ( ADR-0002 ). All GPU work is WGSL through wgpu-native."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Build","u":"/docs/mojo-notes/#build","x":"mojo build --Werror -O3 -g1 -o out file.mojo ; --sanitize address|thread is experimental; --emit for other outputs; -D KEY=VALUE read through std.defines . .mojopkg is gone; precompiled packages are .mojoc ."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Facts established by compiling under 1.2.0 (spikes/api_probe, 2026-11-08)","u":"/docs/mojo-notes/#facts-established-by-compiling-under-120-spikesapi_probe-2026-11-08","x":"ASAP destruction applies to OwnedDLHandle . The handle is destroyed, and dlclose runs, right after the handle's last syntactic use. A function pointer obtained from it then dangles and the next call segfaults with no diagnostic. This cost half a day. Rule: function pointers live only as fields of the struct that also owns the handle ( cinder/ffi/* generates exactly that), they are never copied out into locals, and the owning struct is held by a long-lived engine object. In a main that uses a handle directly, reference it after the last call ( print(Bool(lib)) ). Untracked pointers do not keep their pointee alive. Once Pointer(to=x) is cast to MutUntrackedOrigin (which every raw C binding req"},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Compiler limits met so far","u":"/docs/mojo-notes/#compiler-limits-met-so-far","x":"Large comptime Array[StaticString, N] literals exhaust the compiler (N ≈ 2,000 already fails under a 13 GB cap; N = 370 compiles but costs ~90 s per program). Reproducer and measurements: bugs/comptime_array_literal_oom/ . Workaround in the generated bindings: one newline-joined StringLiteral , split at load time. Keep compile-time collections small; put bulk data in a single literal or in a file."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Legacy forms that are banned in this repository","u":"/docs/mojo-notes/#legacy-forms-that-are-banned-in-this-repository","x":"A CI grep ( tools/lint_legacy_mojo.sh ) fails the build on any of these in *.mojo files: fn (as a keyword), alias , inout , borrowed , owned , let , @parameter , __del__ , InlineArray , StringSlice , UnsafePointer , MutUnsafePointer , ImmUnsafePointer , std.gpu , .load( / .store( / .free( on pointers, memcpy( , memset( , read as a convention, @parameter if , @parameter for , @register_passable , __comptime_assert , steal_data , .size on SIMD/Array."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Open questions to verify by compiling","u":"/docs/mojo-notes/#open-questions-to-verify-by-compiling","x":"Whether abi(\"C\") Mojo functions can be passed to C and invoked from a foreign thread (spike 4). How @export interacts with mojo build --emit shared-lib (needed only if Mojo ever exports to C; the design keeps all callbacks in C, so this is not on the critical path). The exact std.runtime / threading primitives available for the job system ( Atomic[T] , std.runtime.parallelism_level() ); verified in Phase 2."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"wgpu-native behaviour worth knowing (verified 2026-11-08)","u":"/docs/mojo-notes/#wgpu-native-behaviour-worth-knowing-verified-2026-11-08","x":"A validation error raised inside wgpuQueueSubmit (for example a texture-to-buffer copy whose bytesPerRow is not a multiple of 256) is fatal : wgpu-native panics and aborts the process instead of reporting through the uncaptured-error callback. Resource creation errors do go through the callback. Tests therefore keep copy strides 256-aligned and the engine's copy helpers take the stride explicitly so the caller can see it. Line rasterisation follows the diamond-exit rule: a line placed exactly on a pixel boundary lands on one of the two rows depending on the backend. Pixel tests put lines through pixel centres."},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"More lifetime pitfalls seen in Phase 4 (verified 2026-11-08)","u":"/docs/mojo-notes/#more-lifetime-pitfalls-seen-in-phase-4-verified-2026-11-08","x":"Early returns skip the keep . A function that creates a NativeLibrary , calls lib.ptr()[]... and returns from one branch destroys lib (dlclose) right after .ptr() on that branch, so the C call jumps into unmapped memory (SIGSEGV with a ?? () frame under gdb). Structure such functions as if/else with one keep(lib) at the end, or keep on every path. _uptr(list) -style helpers that return an untracked pointer end the list's lifetime at the call site. Every list whose pointer is passed to C must be fenced with keep(list) after the call ( MeshData.build_lods fences optimised , current , dest and cache ). SIMD[DType.bool, 4](True) is rejected (\"must be a scalar; use the fill keyword\"); write SIMD["},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Pitfalls seen in Phase 6 (verified 2026-11-09)","u":"/docs/mojo-notes/#pitfalls-seen-in-phase-6-verified-2026-11-09","x":"A method taking mut self cannot also take mut field_of_self (passing self.current to a mut layer: _Layer parameter is an exclusivity violation). Return the new value and assign it, or branch on which field to touch. pose.blend(pose, other, t) with mut self and a: Pose aliasing the same object is rejected; write in-place variants ( self = lerp(self, other, t) ). T: Copyable & Movable is flagged as a redundant composition: Copyable implies Movable . Single-field enum-style structs ( struct Kind: var value: UInt8 ) need @fieldwise_init for Kind(0) to resolve; a struct with an explicit __init__ must not also carry it. Int(phase) truncates towards zero: counting loop wraps from an unwrapped phase"},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Pitfalls seen in Phase 7 (verified 2026-11-09)","u":"/docs/mojo-notes/#pitfalls-seen-in-phase-7-verified-2026-11-09","x":"Inside a parametric struct, the parameter must be written Self.T in field and method signatures ( var _storage: Allocation[Self.T] ). unsafe_deinit_pointee() needs the pointee type bound to Deinitable ( T: Deinitable & Movable ). A struct whose constructor stores pointers to its own fields breaks when the struct moves; box such subsystems ( cinder/core/boxed.mojo ) and keep raw pointers to the box. String has inline small-string storage: a pointer from as_c_string_span() dies when the owning list reallocates. Hand C APIs List[UInt8] buffers (heap data stays put) when the string must outlive the call. Passing a field of self as an immutable argument to a mut self method is an aliasing error ("},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Allocation facts measured in Phase 8 (verified 2026-11-09 with libcinder_alloccount)","u":"/docs/mojo-notes/#allocation-facts-measured-in-phase-8-verified-2026-11-09-with-libcinder_alloccount","x":"Mojo heap allocations go through KGEN_CompilerRT_AlignedAlloc in libKGENCompilerRTShared , served by the runtime's own TCMalloc over mmap : glibc malloc hooks never see them. Count them by interposing that entry point (LD_PRELOAD, dlsym(RTLD_NEXT) ). String(\"literal\") , t-string formatting of short strings, small-String copies, Optional[Int] assignment and as_c_string_span() on short strings do not allocate; List growth does (and List[...] = [x] literals allocate every time). List.clear() keeps capacity, so clear-and-refill scratch fields are allocation-free after warm-up; resize(n, fill) only allocates when growing. A List moved out of a struct and back ( var p = passes.pop(); ...; spares.a"},{"t":"Mojo 1.2.0 notes for Cinder","s":"Engine design","h":"Phase 9 (defect sweep)","u":"/docs/mojo-notes/#phase-9-defect-sweep","x":"Stale compile cache. The compiler's module cache under .pixi/envs/default/share/max/cache occasionally served an earlier version of an edited module (seen after reverting and restoring a file, and once as \"value has no attribute\" for a method that was present in the source). tools/run_tests.sh deletes the cache before compiling; delete it by hand when a build result contradicts the source. Self-aliasing with handles. self.method(self.handle_field) where method takes mut self is the same exclusivity error as with lists: copy the handle into a local first (handles are ImplicitlyCopyable ). String(from_utf8_lossy=...) / String(from_utf8=...) build Strings from byte spans; never rebuild text one"},{"t":"Toolchain","s":"Engine design","h":"Toolchain","u":"/docs/toolchain/","x":"Every version below is pinned. Changing one requires an ADR and a green CI run."},{"t":"Toolchain","s":"Engine design","h":"Mojo","u":"/docs/toolchain/#mojo","x":"Item Value Verified Mojo 1.2.0 (compiler build 2016d0cd ), released 2026-10-22 pixi run mojo --version in the toolchain container, 2026-11-08 Channel https://conda.modular.com/max/ then conda-forge pixi.toml Pin mojo = \"==1.2.0\" in pixi.toml pixi.lock Nightlies are never used. The newest stable on 2026-11-08 was 1.2.0; the nightly channel offered 1.3.0.dev2026110805 , which is excluded."},{"t":"Toolchain","s":"Engine design","h":"Environment and native build","u":"/docs/toolchain/#environment-and-native-build","x":"Tool Version Source pixi v0.63.2 (pinned through PIXI_VERSION in docker/Dockerfile.toolchain ) pixi.sh install script CMake >=3.28,<4 from conda-forge pixi.toml Ninja >=1.11 from conda-forge pixi.toml C/C++ compiler (Linux) clang from Ubuntu 24.04 ( clang package) docker image C/C++ compiler (macOS) Apple clang from Xcode command line tools CI runner Rust (wgpu-native only) pinned by third_party/wgpu-native/rust-toolchain.toml at the vendored tag, see ADR-0004"},{"t":"Toolchain","s":"Engine design","h":"Development host","u":"/docs/toolchain/#development-host","x":"Mojo has no Windows build. Development on the Windows host used for this project runs inside the Linux container defined in docker/Dockerfile.toolchain (Ubuntu 24.04, x86-64, Mesa lavapipe for headless Vulkan). See ADR-0007 . Native Linux and macOS hosts run the same pixi tasks directly. bash docker build -t cinder-toolchain:0 -f docker/Dockerfile.toolchain docker docker run --rm -it -v \" $PWD :/work\" -w /work cinder-toolchain:0 bash pixi install"},{"t":"Toolchain","s":"Engine design","h":"Third-party sources","u":"/docs/toolchain/#third-party-sources","x":"All third-party code is vendored as git submodules under third_party/ , pinned to the exact tags or commits listed in THIRD_PARTY_NOTICES.md ."},{"t":"Render validation","s":"Engine design","h":"Render validation","u":"/docs/render-validation/","x":"Golden-image tests ( tests/golden/test_golden_gltf.mojo ) render Khronos glTF-Sample-Assets models headless and compare them with committed references per platform. This file records how the references were produced and reviewed, and the tolerances."},{"t":"Render validation","s":"Engine design","h":"Setup","u":"/docs/render-validation/#setup","x":"Models: tools/fetch_sample_assets.py , pinned to glTF-Sample-Assets commit 8f492390ddc7a2ae221a1369ef560589cf8c70fb (recorded in assets/samples/COMMIT ). Frame: 512 x 512, 4x MSAA, AgX, bloom, GTAO, cascaded sun shadows (sun from upper left front, 3 lux), procedural sky IBL, camera auto-framed on the model's bounds from the front right above. Reference location: tests/golden/<platform>/<Model>.png . Regenerate with pixi run update-golden ; compare with pixi run test-golden (also part of tools/run_tests.sh )."},{"t":"Render validation","s":"Engine design","h":"Tolerances","u":"/docs/render-validation/#tolerances","x":"Metric Threshold Why SSIM (luma, 8x8 windows) >= 0.985 Catches structural changes (missing geometry, shifted shadows, broken textures) while ignoring sub-texel rasterisation noise. Pixels differing by more than 8/255 in any channel <= 0.5% of the frame Bounds local colour drift (lighting or material regressions) that SSIM on luma can miss. Both must hold. The thresholds were chosen from the observed run-to-run difference on lavapipe (zero: frames are bit-identical between runs) with headroom for driver updates; a change that fails them is either a regression or an intentional change that must update the reference and this review."},{"t":"Render validation","s":"Engine design","h":"Review of the Linux (lavapipe) references","u":"/docs/render-validation/#review-of-the-linux-lavapipe-references","x":"Each image was inspected against the Khronos reference render of the model (the screenshot/screenshot.png or .jpg in the model's folder of glTF-Sample-Assets) for geometry, orientation, material response and alpha handling. The lighting setup differs from Khronos's, so only structure, texture mapping and material plausibility are compared. Model Review DamagedHelmet Visor, metal trim and the emissive HUD glyphs (cyan) match the Khronos render; normal map detail on the shell reads correctly; roughness variation on the metal is plausible. Passed. Sponza Framed from inside the atrium at eye height looking down the hall (the auto-framing camera would sit outside the closed building). Columns, ar"},{"t":"Render validation","s":"Engine design","h":"macOS (Metal)","u":"/docs/render-validation/#macos-metal","x":"No references yet: no macOS machine or runner was available (see REPORT.md , Blockers). The test selects the platform directory by PLATFORM ; Metal references must be generated on a Mac with pixi run update-golden and reviewed the same way."},{"t":"Benchmarks","s":"Engine design","h":"Benchmarks","u":"/docs/benchmarks/","x":"How Cinder measures performance, what the numbers mean, and how regressions are caught. The measured results for each run live in REPORT.md (section 4); this page is the method."},{"t":"Benchmarks","s":"Engine design","h":"Programs","u":"/docs/benchmarks/#programs","x":"Program Scene Default run Output bench/stress.mojo Section 8 stress scene: 50,000 static instances in a grid, 2,000 dynamic boxes falling onto a floor, 256 clustered point lights plus a shadowed sun, headless at 1920x1080, high preset 120 frames after one start-up frame and 10 warm-up frames (pipelines, environment bake) .cache/bench/stress.json bench/sample_bench.mojo The sample game ( sample/ ) on autopilot, headless at 1920x1080, high preset, arena level by default 300 frames after one start-up frame .cache/bench/sample.json bench/ecs_bench.mojo 100,000 entities, two archetypes, position += velocity * dt through a two-component query, against flecs through the shim 100 frames stdout ( ADR"},{"t":"Benchmarks","s":"Engine design","h":"What is measured","u":"/docs/benchmarks/#what-is-measured","x":"Each frame records three durations with the engine's own clock, from inside App.run_frame : frame_ms : the whole frame, from the start of input polling to the end of the offscreen readback (headless) or present (windowed). This is what the p99 budget is about. physics_ms : the Jolt step calls of that frame's fixed steps, summed (zero on frames with no step). Kinematic push, body pull and Game.fixed_update are in frame_ms but not here, so a level whose bodies are asleep reports microseconds. render_ms : Renderer.render , from the bridge's render scene to command submission. The JSON carries median, p99, mean, max and count of each, the adapter name reported by wgpu, the time from App construc"},{"t":"Benchmarks","s":"Engine design","h":"Budgets (section 8 of the brief)","u":"/docs/benchmarks/#budgets-section-8-of-the-brief","x":"Budget Where it is checked Sample p99 frame time <= 16.6 ms at 1080p, high preset, on the reference hardware bench/sample_bench p99 frame_ms bench/stress >= 60 fps (50k instances, 2,000 bodies, 256 lights) bench/stress median frame_ms <= 16.6 Physics step <= 4 ms for 2,000 bodies bench/stress p99 physics_ms Cold start <= 2 s to the first frame of the sample cold_start_ms in both results Zero allocations per frame in steady state tests/perf/test_zero_alloc.mojo ( pixi run test-zero-alloc ), a hard test rather than a benchmark The budgets name hardware (Linux with an RTX 3060-class GPU, Apple M2). Numbers from any other adapter are regression baselines for that adapter only; REPORT.md says whi"},{"t":"Benchmarks","s":"Engine design","h":"Regression checking","u":"/docs/benchmarks/#regression-checking","x":"tools/bench_check.py RESULT.json... [--baselines bench/baselines] [--tolerance 0.10] compares each result with the baseline recorded for the same benchmark name and adapter and fails when any tracked metric (median and p99 of frame, physics and render time; cold start) is more than 10% slower. A result from an adapter with no baseline is printed and skipped, never compared against a different GPU. --update records the current numbers as the new baseline; a baseline change is a reviewable diff in bench/baselines/ ."},{"t":"Benchmarks","s":"Engine design","h":"Counting allocations","u":"/docs/benchmarks/#counting-allocations","x":"native/src/alloc_counter.c builds to libcinder_alloccount.so . When LD_PRELOADed it interposes malloc , calloc , realloc , posix_memalign , aligned_alloc and the Mojo runtime's KGEN_CompilerRT_AlignedAlloc , and attributes each call to the main executable, the Mojo runtime or a shared library by its return address. The shim exposes the counters as cn_alloc_counter_* (no-ops when the library is not loaded). tests/perf/test_zero_alloc.mojo runs a representative scene, warms up for 90 frames, then asserts that 60 frames make zero allocations from the executable. App.count_allocations prints per-phase deltas and Renderer.alloc_marks prints per-render-stage deltas to locate a regression; CN_ALLOC"},{"t":"Benchmarks","s":"Engine design","h":"Interpreting a run on lavapipe","u":"/docs/benchmarks/#interpreting-a-run-on-lavapipe","x":"Inside the toolchain container the only Vulkan device is Mesa's lavapipe, a CPU rasteriser. Frame times there are dominated by shading on the CPU and say nothing about the budgets; they are still stable enough (the same container, the same CPU) to catch CPU-side regressions in the engine, which is how they are used in CI."},{"t":"Architecture decision records","s":"Decisions","h":"Architecture decision records","u":"/docs/adr/","x":"Every significant build-versus-reuse, layering and deviation decision, with context and consequences. Deviations from the original brief each point to one of these. ADR-0001: Build-versus-reuse decisions for the dependency stack : Status: Accepted, 2026-11-08 ADR-0002: No MAX / max.gpu for rendering; GPU compute is WGSL : Status: Accepted, 2026-11-08 ADR-0003: One C11 shim owning every callback; Mojo pulls events : Status: Accepted, 2026-11-08 (callback-in-Mojo question deferred to spike 4) ADR-0004: Building wgpu-native from source through CMake : Status: Accepted, 2026-11-08 ADR-0005: Generating the Mojo bindings : Status: Accepted, 2026-11-08 (decided by spike 9; supersedes the \"Proposed\""},{"t":"ADR-0001: Build-versus-reuse decisions for the dependency stack","s":"Decisions","h":"ADR-0001: Build-versus-reuse decisions for the dependency stack","u":"/docs/adr/0001-dependency-stack/","x":"Status: Accepted, 2026-11-08"},{"t":"ADR-0001: Build-versus-reuse decisions for the dependency stack","s":"Decisions","h":"Context","u":"/docs/adr/0001-dependency-stack/#context","x":"The mission (section 2) lists a default library for every solved problem and allows replacement only with evidence. Each row below records the decision, the pinned version, and the alternatives considered. All libraries are vendored as git submodules under third_party/ and built from source by native/ ."},{"t":"ADR-0001: Build-versus-reuse decisions for the dependency stack","s":"Decisions","h":"Decisions","u":"/docs/adr/0001-dependency-stack/#decisions","x":"Concern Decision Pin Alternatives considered and why rejected Graphics Reuse wgpu-native (webgpu.h), WGSL shaders v29.0.1.1 Raw Vulkan + Metal: two backends to write and validate, months of work, no gain for this scope. SDL3 GPU API: no compute shader stage guarantees on all backends at the pinned release and a smaller validation layer than wgpu's. Window/input Reuse SDL3 release-3.4.20 GLFW: no gamepad rumble/haptics API parity, no file dialogs, weaker Wayland story. Physics Reuse Jolt via joltc joltc 2bbcf73 (vendors Jolt 5.x; see THIRD_PARTY_NOTICES) Bullet: C++ only, no maintained C API, weaker character controller. PhysX: C++ only, large. Writing our own: not the engine's job. glTF Reus"},{"t":"ADR-0001: Build-versus-reuse decisions for the dependency stack","s":"Decisions","h":"Consequences","u":"/docs/adr/0001-dependency-stack/#consequences","x":"Every pinned library's licence is permissive (MIT, zlib, BSD, Apache-2.0); recorded in THIRD_PARTY_NOTICES.md . No copyleft code is linked. wgpu-native is Rust. CMake drives cargo for it; see ADR-0004 . The imgui docking branch is a release-tagged branch ( v1.93.0-docking ), pinned by tag like the rest."},{"t":"ADR-0002: No MAX / max.gpu for rendering; GPU compute is WGSL","s":"Decisions","h":"ADR-0002: No MAX / max.gpu for rendering; GPU compute is WGSL","u":"/docs/adr/0002-no-max-gpu/","x":"Status: Accepted, 2026-11-08"},{"t":"ADR-0002: No MAX / max.gpu for rendering; GPU compute is WGSL","s":"Decisions","h":"Context","u":"/docs/adr/0002-no-max-gpu/#context","x":"Mojo 1.x moved GPU programming out of the standard library into the max package ( max.gpu.* ), per the 1.0.0 and 1.1.0 release notes. MAX device buffers are allocated and owned by MAX's runtime and cannot be imported into a wgpu WGPUBuffer or WGPUTexture ; there is no documented interop for sharing memory with a wgpu surface or swap chain."},{"t":"ADR-0002: No MAX / max.gpu for rendering; GPU compute is WGSL","s":"Decisions","h":"Decision","u":"/docs/adr/0002-no-max-gpu/#decision","x":"Cinder does not depend on max at all. All GPU compute work (clustered light culling, IBL prefiltering and irradiance, BRDF LUT generation, GPU culling, particles) is written as WGSL compute shaders and dispatched through wgpu-native, in the same device and queue as rendering."},{"t":"ADR-0002: No MAX / max.gpu for rendering; GPU compute is WGSL","s":"Decisions","h":"Consequences","u":"/docs/adr/0002-no-max-gpu/#consequences","x":"One GPU runtime, one validation layer, one set of synchronisation rules. Mojo's SIMD is used on the CPU side (culling, animation, math); GPU parallelism lives in WGSL. pixi.toml depends on mojo only, never on max . A CI grep fails on import max or from max ."},{"t":"ADR-0003: One C11 shim owning every callback; Mojo pulls events","s":"Decisions","h":"ADR-0003: One C11 shim owning every callback; Mojo pulls events","u":"/docs/adr/0003-native-shim-pull-model/","x":"Status: Accepted, 2026-11-08 (callback-in-Mojo question deferred to spike 4)"},{"t":"ADR-0003: One C11 shim owning every callback; Mojo pulls events","s":"Decisions","h":"Context","u":"/docs/adr/0003-native-shim-pull-model/#context","x":"The pinned libraries deliver results through C callbacks: wgpu async requests (adapter, device, buffer map, queue work done), device-lost and uncaptured-error handlers, Jolt contact listeners (called from Jolt's worker threads), miniaudio data callbacks (called from the audio device thread), SDL log hooks. Mojo 1.2.0 can declare abi(\"C\") thin functions, but whether they are safe to invoke from a foreign thread with no Mojo runtime context is undocumented."},{"t":"ADR-0003: One C11 shim owning every callback; Mojo pulls events","s":"Decisions","h":"Decision","u":"/docs/adr/0003-native-shim-pull-model/#decision","x":"native/ builds a single shared library, libcinder_native , with a flat C11 ABI declared in native/include/cinder_native.h . It wraps the C++ dependencies (Jolt through joltc, the ImGui backends) and registers every callback itself. Each callback writes a fixed-size, plain-data event into a fixed-capacity lock-free ring owned by the shim (SPSC where the producer is a single known thread, MPSC for Jolt contacts). The rings never allocate after creation and report overflow through a counter that Mojo checks each frame. Mojo drains the rings once per frame on the main thread ( cinder/platform/events , cinder/gfx/async , cinder/physics/contacts , cinder/audio/commands ). Mojo code is never execut"},{"t":"ADR-0003: One C11 shim owning every callback; Mojo pulls events","s":"Decisions","h":"Consequences","u":"/docs/adr/0003-native-shim-pull-model/#consequences","x":"Latency of one frame on async GPU results; acceptable for resource creation and readback. The shim is the only place that needs C++; everything above it is C ABI, which keeps bindings generation simple and the layout test complete. ASan/UBSan runs cover the shim and all callback paths in CI (CTest)."},{"t":"ADR-0003: One C11 shim owning every callback; Mojo pulls events","s":"Decisions","h":"Amendments (2026-11-08, after spikes 1, 2 and 4)","u":"/docs/adr/0003-native-shim-pull-model/#amendments-2026-11-08-after-spikes-1-2-and-4","x":"Waiting. wgpu-native v29.0.1.1 leaves wgpuInstanceWaitAny in src/unimplemented.rs , so the shim exposes cn_wgpu_pump(instance, device, wait) ( wgpuInstanceProcessEvents plus wgpuDevicePoll ) and Mojo awaits a result by pumping and draining its event queue with a deadline ( spikes/common/ffi_util.mojo: await_event ). This is the pull model with no blocking primitive at all and is what the engine's frame loop will do once per frame. Spike 4 result. A Mojo def ... abi(\"C\") function is invoked correctly by C both on the calling thread and on a freshly created native thread ( spikes/ffi/test_ffi_spikes.mojo ). The pull model is nevertheless kept: it keeps all threading inside C where ASan/TSan co"},{"t":"ADR-0004: Building wgpu-native from source through CMake","s":"Decisions","h":"ADR-0004: Building wgpu-native from source through CMake","u":"/docs/adr/0004-wgpu-native-build/","x":"Status: Accepted, 2026-11-08"},{"t":"ADR-0004: Building wgpu-native from source through CMake","s":"Decisions","h":"Context","u":"/docs/adr/0004-wgpu-native-build/#context","x":"The mission requires every dependency to be vendored as a submodule and built from source with CMake in native/ . wgpu-native is a Rust crate ( cargo build) that exposes webgpu.h and wgpu.h . It cannot be compiled by CMake directly."},{"t":"ADR-0004: Building wgpu-native from source through CMake","s":"Decisions","h":"Decision","u":"/docs/adr/0004-wgpu-native-build/#decision","x":"third_party/wgpu-native is pinned to tag v29.0.1.1 . native/CMakeLists.txt adds an ExternalProject step that invokes cargo build --release --locked in the submodule using the Rust toolchain pinned by the submodule's own rust-toolchain.toml (or, when absent at that tag, the version recorded in docs/TOOLCHAIN.md ), with --locked so the vendored Cargo.lock is authoritative. The produced libwgpu_native.{so,dylib} and the two headers are installed into the native build tree and linked by libcinder_native . Prebuilt release binaries from GitHub are not used, so that the exact source is auditable and sanitizer or debug builds can be produced when needed."},{"t":"ADR-0004: Building wgpu-native from source through CMake","s":"Decisions","h":"Consequences","u":"/docs/adr/0004-wgpu-native-build/#consequences","x":"The Linux CI image and the macOS runner need rustup ; the docker toolchain image installs it. Build time for wgpu-native (several minutes cold) is cached in CI by target/ hash of Cargo.lock . This is the only non-CMake build step in the tree, and it is driven by CMake, which satisfies the intent of the rule: one cmake --build builds all native code."},{"t":"ADR-0005: Generating the Mojo bindings","s":"Decisions","h":"ADR-0005: Generating the Mojo bindings","u":"/docs/adr/0005-bindings-generation/","x":"Status: Accepted, 2026-11-08 (decided by spike 9; supersedes the \"Proposed\" version of the same day)"},{"t":"ADR-0005: Generating the Mojo bindings","s":"Decisions","h":"Context","u":"/docs/adr/0005-bindings-generation/#context","x":"The mission names mojo-bindgen (libclang based) as the bindings generator and requires the output to be reviewed and committed, with a C program and a generated Mojo test cross-checking every struct layout and enum value. It also forbids Mojo nightlies."},{"t":"ADR-0005: Generating the Mojo bindings","s":"Decisions","h":"Evidence gathered in spike 9 (2026-11-08, pinned toolchain)","u":"/docs/adr/0005-bindings-generation/#evidence-gathered-in-spike-9-2026-11-08-pinned-toolchain","x":"mojo-bindgen 0.3.4 from PyPI (MIT) was installed in an isolated pixi environment ( pixi run -e bindgen ). Findings: It requires Python 3.14 and has no Mojo dependency at all, so the \"generator pins a nightly\" concern from the README's unreleased 0.4.0 notes does not apply to the released package. Run on native/include/cinder_native.h (which includes webgpu.h and wgpu.h ) it produced 10,616 lines that do not parse under Mojo 1.2.0 : a macro body ( } ) ) is emitted as code, and the output targets nightly idioms that 1.2.0 rejects or has removed: InlineArray (removed in 1.1.0), UnsafePointer (372 uses), StringSlice (618 uses), ImmutOpaquePointer , private stdlib internals ( _DLHandle , _Global "},{"t":"ADR-0005: Generating the Mojo bindings","s":"Decisions","h":"Decision","u":"/docs/adr/0005-bindings-generation/#decision","x":"Cinder uses its own generator, tools/bindgen/cinder_bindgen.py (about 600 lines of Python over libclang, run from the isolated bindgen pixi environment), which emits from one parse : cinder/ffi/<lib>.mojo : Mojo 1.2 idioms only ( Pointer , Array , comptime , __deinit__ -free plain-data structs); one struct <Lib> that owns the OwnedDLHandle and holds every resolved function pointer as a field, so pointers cannot outlive the library (see the ASAP-destruction note in docs/MOJO_NOTES.md ); every C pointer as Optional[Pointer[T, MutUntrackedOrigin]] (8 bytes, verified); nullable callback fields as Optional[Pointer[NoneType, ...]] because Optional[def(...) thin abi(\"C\")] is 16 bytes; unions, bitfi"},{"t":"ADR-0005: Generating the Mojo bindings","s":"Decisions","h":"Consequences","u":"/docs/adr/0005-bindings-generation/#consequences","x":"A generator we control, small enough to review, that fails loudly on anything it cannot bind. The layout test is platform-correct by construction: Linux and macOS each compile and run their own printer. Constness of C pointers is not expressed in the raw layer; the safe layer ( cinder/gfx and friends) owns it. This is deliberate: Optional[Pointer] has no implicit mutable-to-immutable conversion. Deviation from the mission's named tool, recorded here with the evidence above."},{"t":"ADR-0005: Generating the Mojo bindings","s":"Decisions","h":"Amendment (2026-11-08): compile-time cost","u":"/docs/adr/0005-bindings-generation/#amendment-2026-11-08-compile-time-cost","x":"The first generator output kept the symbol names in a comptime Array[StaticString, N] . With the umbrella header (4,463 functions) the Mojo 1.2.0 compiler exhausted 13 GB on any program that instantiated the library struct; a 2,200-entry array failed the same way. The names are now one newline-joined StringLiteral split at load time, after which the full module compiles in about 7 s with 0.3 GB peak. Reproducer and numbers: bugs/comptime_array_literal_oom/ ."},{"t":"ADR-0006: Start from scratch rather than fork stargine","s":"Decisions","h":"ADR-0006: Start from scratch rather than fork stargine","u":"/docs/adr/0006-no-fork-of-stargine/","x":"Status: Accepted, 2026-11-08"},{"t":"ADR-0006: Start from scratch rather than fork stargine","s":"Decisions","h":"Context","u":"/docs/adr/0006-no-fork-of-stargine/#context","x":"The operator asked whether to fork https://github.com/ssslakter/stargine (Apache-2.0, 17 stars, 68 commits, last commit 2026-08-28, ~3,100 lines of Mojo) and build Cinder from it. Inspected on 2026-11-08 by shallow clone. Findings: Rendering is OpenGL 4.5 through opengl-mojo ; windowing through sdl-mojo (both from MojoGameDevs, hand-written bindings, the SDL one unpinned). The mission mandates wgpu-native with Vulkan and Metal, generated bindings, and exact pins. The GPU layer, renderer, shader loader, cubemap and texture code, the majority of the repository, would be discarded. pixi.toml depends on mojo = \">=1.1.0.dev2026082605\" from the nightly channel and is linux-64 only. Cinder pins sta"},{"t":"ADR-0006: Start from scratch rather than fork stargine","s":"Decisions","h":"Decision","u":"/docs/adr/0006-no-fork-of-stargine/#decision","x":"Cinder starts from an empty repository. stargine is kept as a reference (not a dependency and not vendored) for one thing: a working, compiled example of SDL3 FFI from Mojo on the 1.x line, useful when writing the platform layer and when debugging bindings. Any code copied from it, if ever, carries the Apache-2.0 notice in THIRD_PARTY_NOTICES.md ."},{"t":"ADR-0006: Start from scratch rather than fork stargine","s":"Decisions","h":"Consequences","u":"/docs/adr/0006-no-fork-of-stargine/#consequences","x":"No inherited design debt around OpenGL state or hand-written bindings. Zero immediate cost; the reference is consulted during Phase 1 spike 1 and Phase 3."},{"t":"ADR-0007: Development host is a Linux container; Windows host is a file server","s":"Decisions","h":"ADR-0007: Development host is a Linux container; Windows host is a file server","u":"/docs/adr/0007-dev-environment-container/","x":"Status: Accepted, 2026-11-08"},{"t":"ADR-0007: Development host is a Linux container; Windows host is a file server","s":"Decisions","h":"Context","u":"/docs/adr/0007-dev-environment-container/#context","x":"Work started on a Windows 11 machine. Mojo 1.2.0 has no Windows build; the mission excludes Windows as a target. The machine has Docker Desktop (WSL2 backend, Linux x86-64, 12 logical cores, 15 GB RAM) and network access. No GPU is exposed to containers, so Vulkan is Mesa lavapipe (software), which is exactly the Linux CI configuration the mission prescribes for golden-image tests. Two Windows-specific hazards were hit on day one and are recorded here so they are not re-discovered: The app's session workspace path is ~200 characters. Git submodule clones failed with \"Filename too long\" and \"'$GIT_DIR' too big\" because Git's remote helper and loose-object paths exceeded MAX_PATH . Fix: map th"},{"t":"ADR-0007: Development host is a Linux container; Windows host is a file server","s":"Decisions","h":"Decision","u":"/docs/adr/0007-dev-environment-container/#decision","x":"All compilation, tests and benchmarks run inside cinder-toolchain:0 ( docker/Dockerfile.toolchain , Ubuntu 24.04 + pixi v0.63.2 + clang/cmake/ninja + Mesa Vulkan + X11/Wayland dev headers). The project folder is bind-mounted at /work . pixi inside the container installs Mojo 1.2.0 from https://conda.modular.com/max/ . The same pixi.toml tasks run unchanged on native Linux and macOS, so nothing in the repository depends on the container. macOS (Metal) verification is not possible on this host . It is an open CI requirement, see docs/STATUS.md and the Blockers section of REPORT.md ."},{"t":"ADR-0007: Development host is a Linux container; Windows host is a file server","s":"Decisions","h":"Consequences","u":"/docs/adr/0007-dev-environment-container/#consequences","x":"Benchmarks measured here are software-rasteriser numbers and do not satisfy section 8's reference hardware; they are reported as such, never as budget results. Build caches ( .pixi/ , native/build/ ) live on the bind mount; this is slower than a native volume but keeps everything visible on the host. If it becomes a bottleneck, a named Docker volume for native/build is the first thing to try."},{"t":"ADR-0008: Job system scheduling in Mojo, threads and parking from the shim","s":"Decisions","h":"ADR-0008: Job system scheduling in Mojo, threads and parking from the shim","u":"/docs/adr/0008-job-system-threads/","x":"Status: Accepted, 2026-11-08"},{"t":"ADR-0008: Job system scheduling in Mojo, threads and parking from the shim","s":"Decisions","h":"Context","u":"/docs/adr/0008-job-system-threads/#context","x":"The brief requires a work-stealing job system written in Mojo that runs everything except SDL and presentation. Mojo 1.2.0's standard library exposes parallelism_level() and parallelize() but no public API to create OS threads or to block and wake a thread; its async runtime is documented as private and unfinished ( std.runtime ). Spike 4 showed that Mojo abi(\"C\") functions run correctly when a native thread calls them."},{"t":"ADR-0008: Job system scheduling in Mojo, threads and parking from the shim","s":"Decisions","h":"Decision","u":"/docs/adr/0008-job-system-threads/#decision","x":"Scheduling is Mojo ( cinder/core/jobs.mojo ): a fixed-capacity job table, one Chase-Lev deque of job indices per thread (deque 0 is the main thread's), work stealing by idle workers, completion counters ( Counter , an Atomic[Int32] ), and wait that helps execute jobs instead of blocking. All synchronisation uses std.atomic.Atomic with explicit Ordering arguments. Threads and parking are C ( native/src/threads.c ): cn_thread_create/join/yield , cn_cpu_count , and a counting semaphore ( sem_t on Linux, dispatch_semaphore_t on macOS). Each worker thread's entry point is the Mojo function _worker_entry declared abi(\"C\") , given a pointer to a heap-allocated WorkerContext that outlives the thread"},{"t":"ADR-0008: Job system scheduling in Mojo, threads and parking from the shim","s":"Decisions","h":"Consequences","u":"/docs/adr/0008-job-system-threads/#consequences","x":"The threading model is documented in docs/THREADING.md ; the shim's thread code is covered by the ASan/UBSan CTest build and the Mojo tests run against both shim builds. Memory orderings are conservative ( SEQUENTIAL on the deque's pop / steal races, acquire/release elsewhere). Relaxing them is a measured optimisation for Phase 8, not a correctness change. Job functions are thin function pointers with an untyped data pointer; typed, closure-like submission helpers are layered on top in the ECS scheduler (Phase 5) where the job graph is known."},{"t":"ADR-0009: Graphics layer shape (typed handles, error scopes for shader diagnostics)","s":"Decisions","h":"ADR-0009: Graphics layer shape (typed handles, error scopes for shader diagnostics)","u":"/docs/adr/0009-gfx-layer/","x":"Status: accepted, 2026-11-08."},{"t":"ADR-0009: Graphics layer shape (typed handles, error scopes for shader diagnostics)","s":"Decisions","h":"Context","u":"/docs/adr/0009-gfx-layer/#context","x":"Phase 3 adds the Mojo-side graphics layer over wgpu-native. Three decisions needed recording: how GPU objects are owned and referenced, how shader compile errors are obtained given the gaps in wgpu-native 29.0.1.1, and how the typed pipeline layer talks to the raw bindings."},{"t":"ADR-0009: Graphics layer shape (typed handles, error scopes for shader diagnostics)","s":"Decisions","h":"Decisions","u":"/docs/adr/0009-gfx-layer/#decisions","x":"Pooled ownership behind generational handles. Every wgpu object the engine creates lives in a Pool inside cinder.gfx.Resources and is referenced by a phantom-tagged Handle[Tag] ( BufferHandle , TextureHandle , ... ComputePipelineHandle ). Stale handles raise invalid handle ; shutdown releases whatever is left alive and reports the count as leaked_at_shutdown . Swapchain views are the only raw WGPUTextureView s outside the pool, because their lifetime is one frame and owned by the surface. Amended 2026-11-09 (Phase 9 defect sweep): the leaked_at_shutdown field could never be read (it was written in the destructor), so it was removed. Shutdown now appends one gfx.resources warning to Gfx.diagn"},{"t":"ADR-0009: Graphics layer shape (typed handles, error scopes for shader diagnostics)","s":"Decisions","h":"Consequences","u":"/docs/adr/0009-gfx-layer/#consequences","x":"Hot reload of shaders and pipelines is possible without invalidating user-held handles. Shader error reporting is exact in location and text but is one message per report rather than per naga diagnostic span; naga already concatenates spans into one report. The command layer ( cinder.gfx.commands ) exposes wait_for_queue and read_buffer for tests and tools only; the frame loop never blocks on them. Verified by tests/gfx/test_resources.mojo , test_shaders.mojo and test_render.mojo (offscreen triangle with compute-updated uniform, pixel readback)."},{"t":"ADR-0010: Renderer architecture (forward+, frame graph, shader assembly)","s":"Decisions","h":"ADR-0010: Renderer architecture (forward+, frame graph, shader assembly)","u":"/docs/adr/0010-renderer/","x":"Status: accepted, 2026-11-08."},{"t":"ADR-0010: Renderer architecture (forward+, frame graph, shader assembly)","s":"Decisions","h":"Context","u":"/docs/adr/0010-renderer/#context","x":"Section 5 of the brief asks for a clustered forward+ renderer with cascaded and atlas shadows, image-based lighting, HDR post-processing, MSAA, GTAO, FXAA, instancing, LODs, skinning, morph targets, debug views and golden-image proof. This ADR records the shape chosen and the places where the implementation deliberately differs from the most common textbook choices."},{"t":"ADR-0010: Renderer architecture (forward+, frame graph, shader assembly)","s":"Decisions","h":"Decisions","u":"/docs/adr/0010-renderer/#decisions","x":"Forward+ with a fixed 16 x 9 x 24 froxel grid and 32 lights per cluster. One compute dispatch per frame tests every point and spot light as a sphere against each cluster's view-space AABB (logarithmic depth slices between the near plane and 200 m). The fragment shader walks the cluster's list. The grid is small enough that the whole list fits in 442 KB and the dispatch is a single workgroup column (24 groups), which suits lavapipe and small scenes while scaling to the 256 lights the stress benchmark needs. Directional lights are not clustered. Shadows: 4 cascades in a 2D array plus one depth atlas. Cascades use the practical split scheme (lambda 0.75), a bounding-sphere fit per slice so the "},{"t":"ADR-0010: Renderer architecture (forward+, frame graph, shader assembly)","s":"Decisions","h":"Consequences","u":"/docs/adr/0010-renderer/#consequences","x":"Every pass is validation clean on lavapipe (the tests fail on any uncaptured error). The renderer never blocks on the GPU in a frame; wait_for_queue / read_buffer are test tools. Golden images ( tests/golden ) are the correctness proof; thresholds live in docs/RENDER_VALIDATION.md . Known gaps tracked for later phases: GPU-driven culling, text in debug draw (needs ImGui, Phase 7), KTX2 textures (Phase 5 cooker), animation playback (Phase 6)."},{"t":"ADR-0011: Keep the Mojo archetype ECS (benchmarked against flecs)","s":"Decisions","h":"ADR-0011: Keep the Mojo archetype ECS (benchmarked against flecs)","u":"/docs/adr/0011-ecs-mojo-vs-flecs/","x":"Status: accepted, 2026-11-08."},{"t":"ADR-0011: Keep the Mojo archetype ECS (benchmarked against flecs)","s":"Decisions","h":"Context","u":"/docs/adr/0011-ecs-mojo-vs-flecs/#context","x":"The brief requires an archetype ECS with typed queries, change detection, command buffers, hierarchy and access-scheduled systems, and asks for a benchmark against flecs through its iterator API: keep the Mojo ECS only if it is within 10% of flecs or faster."},{"t":"ADR-0011: Keep the Mojo archetype ECS (benchmarked against flecs)","s":"Decisions","h":"Benchmark","u":"/docs/adr/0011-ecs-mojo-vs-flecs/#benchmark","x":"bench/ecs_bench.mojo (run with pixi run mojo build -I . bench/ecs_bench.mojo , then ecs_bench 100000 100 ): 100,000 entities with Position and Velocity (8 bytes each), half of them with a third component so both worlds hold two archetypes/tables; 100 frames of position += velocity * dt over a two-component query. flecs 4.2.0 is driven through ecs_query_iter / ecs_query_next / ecs_field_w_size (no callbacks), both sides from the same Mojo process on the same core. Container on the Windows host (lavapipe machine, Ubuntu 24.04, Mojo 1.2.0), three runs: Run Mojo ns / entity-iteration flecs ns / entity-iteration ratio 1 0.758 0.829 0.91 2 0.746 0.750 1.00 3 0.728 0.744 0.98 Two changes made the d"},{"t":"ADR-0011: Keep the Mojo archetype ECS (benchmarked against flecs)","s":"Decisions","h":"Decision","u":"/docs/adr/0011-ecs-mojo-vs-flecs/#decision","x":"Keep the Mojo ECS ( cinder/ecs ). It is within the 10% band (and slightly faster in every run), it is the only implementation whose queries are typed at compile time with Mojo parameters, and its scheduler integrates directly with the Mojo job system. flecs stays vendored and linked for the benchmark so the comparison can be rerun whenever the ECS changes."},{"t":"ADR-0011: Keep the Mojo archetype ECS (benchmarked against flecs)","s":"Decisions","h":"Consequences","u":"/docs/adr/0011-ecs-mojo-vs-flecs/#consequences","x":"bench/ecs_bench.mojo is part of the benchmark suite; a regression above the band reopens this decision. Components are plain Mojo structs with a unique component_id() ; heap-owning components are supported through the type-erased move/destroy functions recorded at registration. flecs' features the Mojo ECS does not have (relationships, reflection, the explorer) are not needed by the brief; the debug UI (Phase 7) inspects entities through the Mojo world directly."},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","u":"/docs/adr/0012-physics-animation-audio-input/","x":"Status: accepted, 2026-11-09."},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"Context","u":"/docs/adr/0012-physics-animation-audio-input/#context","x":"Phase 6 adds the gameplay subsystems over the pinned dependencies: Jolt (joltc) for physics, miniaudio for audio, SDL3 events for input, and the engine's own animation evaluation (the brief asks for SIMD Mojo on the job system). Every decision below was taken against the rules of ADR-0003 (Mojo never runs inside a C callback) and section 10 (reuse a pinned dependency before writing a subsystem)."},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"Decisions","u":"/docs/adr/0012-physics-animation-audio-input/#decisions","x":""},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"Physics ( cinder/physics )","u":"/docs/adr/0012-physics-animation-audio-input/#physics-cinderphysics","x":"Jolt keeps its own thread pool. JPH_PhysicsSystem_Update runs on a JPH_JobSystemThreadPool created per world (2 workers by default) instead of the engine's job system. joltc's job system cannot be driven by an external scheduler without callbacks into Mojo, which ADR-0003 forbids; Jolt's pool is idle outside the step so the two systems do not contend. Revisit if profiling (Phase 8) shows the step starving render jobs. Object layers are bits. Up to 32 layers; query masks are 32-bit; two broad-phase layers (static, moving) keep static geometry from being tested against itself. Collision pairs are declared once in LayerConfig . All callbacks are C. Query filters ( cn_physics_*_filter_* ), the c"},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"Animation ( cinder/anim )","u":"/docs/adr/0012-physics-animation-audio-input/#animation-cinderanim","x":"Skeleton = joints plus ancestors. skeleton_from_gltf includes every skin joint and the ancestors needed to reach a common root, in parent-before-child order, so animated parents move their children. The palette is inverse(mesh node) * bone world * inverse bind , the same convention the importer uses at the bind pose (tested against it), so the render instance keeps the mesh node's transform. Clips stay glTF-shaped. Clip / ClipChannel (node, path, interpolation, components, times, values) are the cooked .cclip format; sampling implements step, linear (slerp for rotations) and cubic spline exactly as the glTF specification describes. A cooked .cskel (CSKL v1) carries the skeleton; the cooker e"},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"Audio ( cinder/audio )","u":"/docs/adr/0012-physics-animation-audio-input/#audio-cinderaudio","x":"miniaudio's engine, node graph and resource manager are used as they are. Buses are ma_sound_group s (allocated in the shim because the struct is opaque to Mojo); streamed sounds use MA_SOUND_FLAG_STREAM , everything else is decoded up front. Headless engines for tests. noDevice engines are pulled with ma_engine_read_pcm_frames , which makes spatialisation, bus volumes and sound-end callbacks testable deterministically. Sound-end and log callbacks post events. update() drains them; one-shots release themselves on their end event. (The shim posted the sound id in the wrong event field before this phase; the audio test now covers the path.) The listener follows an entity. AudioListener on the "},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"Input ( cinder/input )","u":"/docs/adr/0012-physics-animation-audio-input/#input-cinderinput","x":"Actions and axes over device codes. Bindings are (device, code, scale, dead zone); digital sources contribute scale while held, analog ones their value after a rescaled dead zone. Mouse deltas accumulate within a frame. Rebinding by listening. The next matching event replaces an action's bindings or adds an axis binding; the capturing press does not fire the action. Text file, exact floats. The map saves as cinder-input 1 lines using the scene format's exact float formatter, so a save/load round trip is byte-identical."},{"t":"ADR-0012: Phase 6 subsystems: physics, animation, audio and input","s":"Decisions","h":"Consequences","u":"/docs/adr/0012-physics-animation-audio-input/#consequences","x":"Phase 7 tools read PhysicsWorld.live_objects() , AudioEngine.live_sounds() and the renderer's resource counts for the shutdown leak report. The sample game (Phase 8) builds its character on CharacterController + Animator root motion disabled (controller-driven) and a locomotion 1D blend tree. JPH_Init is process-wide and never shut down while a world exists; worlds call it (it is idempotent in joltc)."},{"t":"ADR-0013: Tools and the application runtime (Phase 7)","s":"Decisions","h":"ADR-0013: Tools and the application runtime (Phase 7)","u":"/docs/adr/0013-tools-and-app-runtime/","x":"Status: accepted, 2026-11-09."},{"t":"ADR-0013: Tools and the application runtime (Phase 7)","s":"Decisions","h":"Context","u":"/docs/adr/0013-tools-and-app-runtime/#context","x":"Phase 7 asks for an ImGui debug overlay (entity inspector with editing, asset browser, render graph view, frame-time graph, log console), Tracy integration throughout, and a --headless mode for tests and benchmarks. The engine had no single owner of its subsystems: every test assembled Gfx , Resources , Renderer , the ECS and the rest by hand."},{"t":"ADR-0013: Tools and the application runtime (Phase 7)","s":"Decisions","h":"Decisions","u":"/docs/adr/0013-tools-and-app-runtime/#decisions","x":"App owns the subsystems and the loop ( cinder/app ). It builds the job system, the platform/window/swapchain (or nothing in headless mode), Gfx / Resources / Renderer , the physics world, the audio engine (device-less when headless), the animation system, the input map, the ECS world, a scene registry and an in-memory log. run_frame is the canonical frame: poll, fixed steps (ECS tick, kinematics in, physics step, dynamics out, Game.fixed_update ), interpolated transforms, Game.update , animation, transform propagation, audio sync, asset streaming, render, overlay, present. Gameplay implements the Game trait, so the sample game, tests and benchmarks run the same code. Headless is the same loo"},{"t":"ADR-0013: Tools and the application runtime (Phase 7)","s":"Decisions","h":"Consequences","u":"/docs/adr/0013-tools-and-app-runtime/#consequences","x":"Phase 8's sample game is a Game over App ; its benchmarks run with --headless and read App.read_frame / renderer stats. Engine components become inspectable as they gain SceneComponent conformance (Phase 8 registers the sample's components; built-in components follow when the scene format grows past v1). dcimgui passes float v[3] arrays by value in the generated bindings, so the overlay uses one InputFloat per component instead of DragFloat3 ."},{"t":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","s":"Decisions","h":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","u":"/docs/adr/0014-sample-game-and-performance/","x":"Status: accepted, 2026-11-09."},{"t":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","s":"Decisions","h":"Context","u":"/docs/adr/0014-sample-game-and-performance/#context","x":"Section 7 asks for a short third-person game built only on the engine (skeletal character on the character controller, lit level with 32+ dynamic lights, 500+ rigid bodies, collectables, win and restart, 3D audio and music, a pause menu with saved settings, save/load). Section 8 sets performance budgets, demands benchmarks in bench/ with JSON results and regression checks, and requires \"zero allocations per frame in steady state, asserted by a test\"."},{"t":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","s":"Decisions","h":"Decisions","u":"/docs/adr/0014-sample-game-and-performance/#decisions","x":""},{"t":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","s":"Decisions","h":"Sample game ( sample/ )","u":"/docs/adr/0014-sample-game-and-performance/#sample-game-sample","x":"The game is a Game over the App runtime , nothing else. Everything it does goes through public engine APIs, so it doubles as the integration test of the whole stack. Content is CC0, pinned and fetched, never committed. tools/fetch_game_assets.py downloads the KayKit Character Animations pack (character, 30 clips) and two Juhani Junkala audio packs (effects, music) with SHA-256 verification into assets/game/ and writes ATTRIBUTION.md ; the level, crates and collectables are Khronos sample models already fetched for the golden tests (Sponza, BoxTextured, Avocado). Two levels share the same gameplay. arena is procedural (floor and walls, fast to load: the headless playthrough test and benchmark"},{"t":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","s":"Decisions","h":"Benchmarks ( bench/ )","u":"/docs/adr/0014-sample-game-and-performance/#benchmarks-bench","x":"bench/stress builds the section 8 stress scene (50,000 instances, 2,000 dynamic bodies, 256 clustered lights) headless at 1920x1080 and bench/sample_bench runs the sample game on the high preset. Both record per-frame CPU time, physics step time and render submission time (median, p99, mean, max), the adapter name and startup time, as JSON. tools/bench_check.py compares results with bench/baselines/<name>.json and fails on a regression above 10% of any tracked metric; results from a different adapter are reported but not compared. pixi run bench-all runs everything; CI runs it on a schedule once the reference hardware exists. Budgets are reported, not met, on this host. The only GPU availabl"},{"t":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","s":"Decisions","h":"Zero allocations per frame","u":"/docs/adr/0014-sample-game-and-performance/#zero-allocations-per-frame","x":"Counting. libcinder_alloccount.so (LD_PRELOADed by tools/run_tests.sh for tests/perf/test_zero_alloc.mojo ) interposes the glibc allocation entry points and, since the Mojo runtime allocates through its own TCMalloc over mmap, also KGEN_CompilerRT_AlignedAlloc . It attributes calls to the executable, the Mojo runtime or a shared library by return address, and the shim forwards the counters ( cn_alloc_counter_* ) so a plain run is a no-op. What the test asserts. A representative headless scene (300 static instances, 100 falling bodies, 32 point lights and a sun, an animated skinned character) runs 90 warm-up frames, then 60 frames during which Mojo allocations must be exactly zero. Third-part"},{"t":"ADR-0014: Sample game, benchmarks and the zero-allocation frame (Phase 8)","s":"Decisions","h":"Consequences","u":"/docs/adr/0014-sample-game-and-performance/#consequences","x":"The steady-state budget is enforced by CI: a new per-frame allocation anywhere in the frame path fails test_zero_alloc . pack_lights , fit_cascades , cull_spheres , palette , leaf_weights keep allocating convenience forms for tests; the engine uses the _into variants. The sample's Sponza level takes several seconds to start on this CPU because the importer decodes PNG/JPEG textures in process; the cooked KTX2 path exists but the sample does not use it yet, so the cold-start budget is unmeasured as well as unmet here."},{"t":"API reference","s":"API reference","h":"API reference","u":"/docs/api/","x":"Generated by tools/gen_api_docs.py from the docstrings in cinder/ ; do not edit by hand."},{"t":"API reference","s":"API reference","h":"cinder.anim","u":"/docs/api/#cinderanim","x":"cinder.anim : Skeletal animation: skeletons, clip sampling, blend trees, animators with crossfades, root motion and events, and a job-parallel system publishing joint palettes to the ECS. cinder.anim.animator : Animator: states (blend trees) with crossfades, animation events and root motion. cinder.anim.blend : Blend trees: clips, 1D and 2D blend nodes, evaluated phase-synchronised. cinder.anim.sampling : Clip sampling: step, linear and cubic-spline keyframes into a local pose. cinder.anim.skeleton : Skeletons: a bone hierarchy with rest pose, the skin's joint list and inverse bind matrices. cinder.anim.system : The animation system: animators stored in a pool, advanced in parallel on the jo"},{"t":"API reference","s":"API reference","h":"cinder.app","u":"/docs/api/#cinderapp","x":"cinder.app : Application runtime: subsystems, the fixed-step game loop, windowed and headless modes. cinder.app.app : The application runtime: owns every subsystem, runs the fixed-step game loop and renders. cinder.app.cli : Command-line options shared by every Cinder executable ( --headless for tests and benchmarks)."},{"t":"API reference","s":"API reference","h":"cinder.assets","u":"/docs/api/#cinderassets","x":"cinder.assets : Assets: glTF import into engine meshes, materials, textures, skins and scenes. cinder.assets.cook : The asset cooker: glTF sources become cooked meshes, materials and animation clips. cinder.assets.cooked : Cooked asset formats (little-endian binary, versioned, bounds-checked on read). cinder.assets.gltf : glTF 2.0 import through cgltf: meshes (with tangents, second UV set, skinning, morph targets), metallic-roughness materials (KHR_texture_transform, KHR_materials_emissive_strength), images (file, data URI or GLB buffer view), nodes, skins and scenes. cinder.assets.runtime : Runtime asset server: asynchronous loading of cooked assets on the job system, reference counting, an"},{"t":"API reference","s":"API reference","h":"cinder.audio","u":"/docs/api/#cinderaudio","x":"cinder.audio : Audio over miniaudio: buses, decoded and streamed sounds, one-shots, 3D spatialisation, a listener, and the ECS bridge that follows entities and the camera. cinder.audio.engine : Audio over miniaudio: buses, decoded and streamed sounds, one-shots, 3D spatialisation and a listener. cinder.audio.sync : ECS <-> audio: AudioSource entities position their sounds, AudioListener places the listener (attach it to the camera entity so the listener follows the camera)."},{"t":"API reference","s":"API reference","h":"cinder.core","u":"/docs/api/#cindercore","x":"cinder.core : Core: math, logging, assertions, allocators, containers, handles, time, file system. cinder.core.arena : LinearArena: a bump allocator for per-frame scratch memory. cinder.core.assertions : Engine assertions: broken invariants abort with an explicit message. cinder.core.base64 : Base64 decoding (RFC 4648, with or without padding) for data URIs in glTF files. cinder.core.boxed : Boxed: a value at a stable heap address. cinder.core.bytes : Little-endian byte packing for GPU uploads (std140/std430-style layouts are the caller's job). cinder.core.cstring : C strings to Mojo String s, decoding the bytes as UTF-8. cinder.core.fs : Filesystem helpers with typed errors. cinder.core.han"},{"t":"API reference","s":"API reference","h":"cinder.ecs","u":"/docs/api/#cinderecs","x":"cinder.ecs : Entity component system: archetype world, typed queries, change detection, command buffers, parent-child transforms and a scheduler that runs non-conflicting systems in parallel. cinder.ecs.commands : Deferred structural changes: record while iterating, apply when it is safe. cinder.ecs.hierarchy : Parent-child transforms: Parent , LocalTransform , WorldTransform and their propagation. cinder.ecs.schedule : System scheduling by declared component access. cinder.ecs.world : Archetype ECS: entities, type-erased component columns, typed queries, change detection."},{"t":"API reference","s":"API reference","h":"cinder.gfx","u":"/docs/api/#cindergfx","x":"cinder.gfx : Safe, typed layer over wgpu-native. cinder.gfx.commands : Command recording: encoders, render and compute passes, copies, submission and readback. cinder.gfx.debug_draw : Immediate-mode debug lines: segments, boxes, spheres, axes and grids drawn in one line-list pass. cinder.gfx.device : Gfx: instance, adapter, device and queue, with every wgpu callback pulled from the shim queue. cinder.gfx.pipeline : Typed descriptors for bind group layouts, bind groups, pipeline layouts and pipelines. cinder.gfx.resources : Typed GPU resources: one handle type per kind, pooled records, ownership and leak tracking. cinder.gfx.shaders : ShaderManager: WGSL modules from files, compile diagnostic"},{"t":"API reference","s":"API reference","h":"cinder.input","u":"/docs/api/#cinderinput","x":"cinder.input : Input: action and axis mapping over keyboard, mouse and gamepad, rebindable and saved to disk. cinder.input.map : Input mapping: named actions (buttons) and axes over keyboard, mouse and gamepad, rebindable at runtime and saved as a diffable text file."},{"t":"API reference","s":"API reference","h":"cinder.physics","u":"/docs/api/#cinderphysics","x":"cinder.physics : Physics over Jolt: rigid-body world, queries, character controller and ECS synchronisation. cinder.physics.character : Character controller over Jolt's CharacterVirtual: slopes, steps and moving platforms. cinder.physics.sync : ECS <-> physics synchronisation with interpolation. cinder.physics.world : Rigid-body world over Jolt (joltc): layers, shapes, bodies, queries and queued events."},{"t":"API reference","s":"API reference","h":"cinder.platform","u":"/docs/api/#cinderplatform","x":"cinder.platform : Platform layer: the native library, SDL3 windows, input events and wgpu surfaces. cinder.platform.events : Platform events in engine terms, translated from SDL3's event union. cinder.platform.native : Loading libcinder_native exactly once and sharing it by pointer. cinder.platform.window : SDL3 platform: initialisation, windows, event polling, gamepads and wgpu surface creation."},{"t":"API reference","s":"API reference","h":"cinder.render","u":"/docs/api/#cinderrender","x":"cinder.render : Renderer: cameras, meshes, materials, textures, the frame graph and the forward+ pipeline. cinder.render.camera : Cameras and frustum culling. cinder.render.frame_graph : Frame graph: declared passes, transient textures with lifetimes and aliasing, automatic ordering. cinder.render.lights : Lights and the clustered-culling configuration shared with shaders/common.wgsl . cinder.render.material : glTF 2.0 metallic-roughness materials and their GPU bind groups. cinder.render.mesh : Mesh data on the CPU, GPU meshes, the engine vertex format and procedural primitives. cinder.render.renderer : The forward+ renderer: shadows, depth prepass, GTAO, clustered lights, PBR, sky, bloom, t"},{"t":"API reference","s":"API reference","h":"cinder.scene","u":"/docs/api/#cinderscene","x":"cinder.scene : Scene files: versioned text and binary formats with migrations, over the ECS world. cinder.scene.format : Scene serialisation: a human-diffable text format and a binary format, versioned, with migrations. cinder.scene.gltf_spawn : Spawning a glTF asset into the ECS: one entity per node (transform hierarchy) and one child entity per mesh primitive ( MeshRenderer ), plus static collision triangles for level geometry. cinder.scene.render_sync : Bridge from the ECS world to a RenderScene : render components and their collection."},{"t":"API reference","s":"API reference","h":"cinder.tools","u":"/docs/api/#cindertools","x":"cinder.tools : Developer tools: the ImGui debug overlay (frame graph, inspector, assets, render graph, log). cinder.tools.cstrings : Frame-scoped C strings for immediate-mode UI calls. cinder.tools.debug_ui : The ImGui debug overlay: frame-time graph and render stats, entity inspector (view and edit components through the scene registry), asset browser, render graph view and log console."},{"t":"cinder.anim API","s":"API reference","h":"cinder.anim","u":"/docs/api/anim/","x":""},{"t":"cinder.anim API","s":"API reference","h":"cinder.anim","u":"/docs/api/anim/#cinderanim-1","x":"Source: cinder/anim/__init__.mojo Skeletal animation: skeletons, clip sampling, blend trees, animators with crossfades, root motion and events, and a job-parallel system publishing joint palettes to the ECS."},{"t":"cinder.anim API","s":"API reference","h":"cinder.anim.animator","u":"/docs/api/anim/#cinderanimanimator","x":"Source: cinder/anim/animator.mojo Animator: states (blend trees) with crossfades, animation events and root motion. update(dt) advances the active state (and the one fading out), blends them, extracts root motion and collects events; pose then holds the local pose and palette() the skinning matrices. Events are authored per state at a normalised phase in [0, 1) and fire once each time the phase passes them, including across loop wraps."},{"t":"cinder.anim API","s":"API reference","h":"struct AnimationEvent","u":"/docs/api/anim/#struct-animationevent","x":"mojo struct AnimationEvent ( Copyable , Movable ) An event that fired during Animator.update : the index of the state that owns it and the event's name. Fields: state : Int name : String"},{"t":"cinder.anim API","s":"API reference","h":"struct AnimationState","u":"/docs/api/anim/#struct-animationstate","x":"mojo struct AnimationState ( Copyable , Movable ) A named animator state: a blend tree, whether its phase loops, and its events. Build it with AnimationState(name, tree^, looping) and register it with Animator.add_state , which validates the tree; add events afterwards with Animator.add_event . Fields: name : String tree : BlendTree looping : Bool events : List[_StateEvent] __init__ __init__( out self, name: String, var tree: BlendTree, looping: Bool = True )"},{"t":"cinder.anim API","s":"API reference","h":"struct Animator","u":"/docs/api/anim/#struct-animator","x":"mojo struct Animator ( Movable ) Plays the states of one skeleton with crossfades, events and root motion. Owns its skeleton, clips and poses. Author states with add_clip_state / add_state , start one with play ( play(state, restart=True) replays a finished non-looping state), then call update(dt) once per frame (it raises until a state has been played). Read the result from pose , palette() / palette_into or bone_matrices() . Nothing is synchronised: AnimationSystem updates each animator from a single job at a time. Fields: skeleton : Skeleton clips : List[Clip] states : List[AnimationState] pose : Pose current : _Layer previous : _Layer fade_remaining : Float32 fade_duration : Float32 spee"},{"t":"cinder.anim API","s":"API reference","h":"cinder.anim.blend","u":"/docs/api/anim/#cinderanimblend","x":"Source: cinder/anim/blend.mojo Blend trees: clips, 1D and 2D blend nodes, evaluated phase-synchronised. A tree runs on a normalised phase in [0, 1). Every leaf clip plays at phase * duration , so blended cycles (walk and run) stay in step; the phase advances at the weighted average of the leaves' cycle rates. Leaf weights come from the blend parameters: 1D nodes interpolate between the two nearest thresholds, 2D nodes use gradient-band interpolation (Johansen 2009), which is smooth, sums to one and reproduces each child exactly at its own point. comptime NODE_CLIP = 0 — BlendNode.kind of a leaf that samples one clip. comptime NODE_BLEND_1D = 1 — BlendNode.kind of a node that blends its child"},{"t":"cinder.anim API","s":"API reference","h":"struct BlendNode","u":"/docs/api/anim/#struct-blendnode","x":"mojo struct BlendNode ( Copyable , Movable ) One node of a BlendTree : a clip leaf or a 1D/2D blend over child node indices. Created by BlendTree.add_clip , add_blend_1d and add_blend_2d ; indices a kind does not use stay -1 and its lists stay empty. Fields: kind : Int clip : Int — Clip index (leaf). speed : Float32 — Playback rate multiplier (leaf). parameter : Int — Blend parameter index (1D: one scalar; 2D: parameter is x and parameter2 is y). parameter2 : Int children : List[Int] points : List[Vec2] — Per child: the parameter value (1D uses x) where the child has full weight. __init__ __init__(out self, kind: Int)"},{"t":"cinder.anim API","s":"API reference","h":"struct BlendTree","u":"/docs/api/anim/#struct-blendtree","x":"mojo struct BlendTree ( Copyable , Movable ) Nodes are stored flat; root names the entry node. Build with add_clip , add_blend_1d and add_blend_2d , then set_root . Fields: nodes : List[BlendNode] root : Int parameter_names : List[String] parameters : List[Float32] __init__ __init__(out self) add_parameter add_parameter(mut self, name: String, initial: Float32 = 0.0) -> Int Declares a blend parameter with an initial value and returns its index, which add_blend_1d / add_blend_2d take. Duplicate names are not rejected; set_parameter updates the first match. set_parameter set_parameter(mut self, name: String, value: Float32) raises Sets the first parameter called name to value . Raises when the"},{"t":"cinder.anim API","s":"API reference","h":"cinder.anim.sampling","u":"/docs/api/anim/#cinderanimsampling","x":"Source: cinder/anim/sampling.mojo Clip sampling: step, linear and cubic-spline keyframes into a local pose. Pose is the skeleton's local transforms plus morph weights. Sampling starts from the rest pose so channels that are missing from a clip keep the skeleton's values (glTF semantics). comptime PATH_TRANSLATION = 0 — ClipChannel.path of a translation channel (3 components). comptime PATH_ROTATION = 1 — ClipChannel.path of a rotation channel (quaternion x, y, z, w). comptime PATH_SCALE = 2 — ClipChannel.path of a scale channel (3 components). comptime PATH_WEIGHTS = 3 — ClipChannel.path of a morph target weights channel (one component per target). comptime INTERP_LINEAR = 0 — ClipChannel.in"},{"t":"cinder.anim API","s":"API reference","h":"struct Pose","u":"/docs/api/anim/#struct-pose","x":"mojo struct Pose ( Copyable , Movable ) A local pose: one parent-space transform per bone plus the morph target weights. Pose(skeleton) starts at the rest pose with zero weights. A pose is tied to the skeleton it was built for: sampling, blending and reset index by bone and never resize. Fields: local : List[Transform] morph_weights : List[Float32] __init__ __init__(out self, skeleton: Skeleton) __init__ __init__( out self, var local: List[Transform], var morph_weights: List[Float32] ) reset reset(mut self, skeleton: Skeleton) Restores the rest pose of skeleton and zeroes the morph weights in place (no allocation). skeleton must be the one the pose was built for. copy_from copy_from(mut self"},{"t":"cinder.anim API","s":"API reference","h":"def sample_component","u":"/docs/api/anim/#def-sample_component","x":"mojo def sample_component ( ch: ClipChannel, pair: _KeyPair, component: Int ) -> Float32 One output component at the located time."},{"t":"cinder.anim API","s":"API reference","h":"def sample_rotation","u":"/docs/api/anim/#def-sample_rotation","x":"mojo def sample_rotation (ch: ClipChannel, pair: _KeyPair) -> Quat Rotations interpolate along the shorter arc (slerp for linear, normalised spline for cubic, as the glTF specification describes)."},{"t":"cinder.anim API","s":"API reference","h":"def sample_clip","u":"/docs/api/anim/#def-sample_clip","x":"mojo def sample_clip (clip: Clip, skeleton: Skeleton, time: Float32, mut pose: Pose) Writes every channel of clip at time (seconds, unclamped: before the first key or after the last key the end values hold) into pose . Channels whose node is not in the skeleton are ignored; pose must have been created for skeleton ."},{"t":"cinder.anim API","s":"API reference","h":"def wrap_time","u":"/docs/api/anim/#def-wrap_time","x":"mojo def wrap_time (time: Float32, duration: Float32) -> Float32 Loops time into [0, duration)."},{"t":"cinder.anim API","s":"API reference","h":"cinder.anim.skeleton","u":"/docs/api/anim/#cinderanimskeleton","x":"Source: cinder/anim/skeleton.mojo Skeletons: a bone hierarchy with rest pose, the skin's joint list and inverse bind matrices. A skeleton is built from a glTF skin (every joint plus the ancestors needed to reach a common root, so animated parents move their children) or read from a cooked .cskel file. Clips keep glTF node indices, which node_to_bone maps to bones. comptime SKELETON_MAGIC = \"CSKL\" — Four-byte tag at the start of a cooked .cskel file. comptime SKELETON_VERSION = 1 — Cooked skeleton format version; read_skeleton rejects any other. comptime MAX_BONES = 256 — Upper bound for a palette (the renderer's joints buffer grows in 1024-matrix chunks)."},{"t":"cinder.anim API","s":"API reference","h":"struct Skeleton","u":"/docs/api/anim/#struct-skeleton","x":"mojo struct Skeleton ( Copyable , Movable ) Bone hierarchy, rest pose and skin data of one glTF skin. Bones are ordered parents first. Build one with skeleton_from_gltf or read_skeleton (both validate it); palette and world_transforms turn a local pose into matrices. Fields: names : List[String] parents : List[Int] — Parent bone per bone (-1 for roots); parents always precede children. rest : List[Transform] — Rest pose in parent space. node_to_bone : List[Int] — glTF node index -> bone index (-1 when the node is not part of the skeleton). joints : List[Int] — Skin joint i -> bone index (palette order). inverse_bind : List[Mat4] mesh_node : Int — glTF node that carries the skinned mesh (-1 w"},{"t":"cinder.anim API","s":"API reference","h":"def skeleton_from_gltf","u":"/docs/api/anim/#def-skeleton_from_gltf","x":"mojo def skeleton_from_gltf (asset: GltfAsset, skin: Int) raises -> Skeleton Builds the skeleton of asset.skins[skin] : the joints and every ancestor up to the outermost ancestor shared by all joints (so animated parents are evaluated), in parent-before-child order. The mesh node is the first node using the skin."},{"t":"cinder.anim API","s":"API reference","h":"def write_skeleton","u":"/docs/api/anim/#def-write_skeleton","x":"mojo def write_skeleton (skeleton: Skeleton) -> List[UInt8] Serialises skeleton to the cooked .cskel format: magic, version, bones (name, parent, rest transform), node map, joints with inverse bind matrices, mesh node and its inverse, morph count. Indices are stored plus one so -1 round-trips as 0. Does not validate."},{"t":"cinder.anim API","s":"API reference","h":"def read_skeleton","u":"/docs/api/anim/#def-read_skeleton","x":"mojo def read_skeleton ( var data: List[UInt8]) raises -> Skeleton Parses a cooked .cskel buffer (consumed) into a validated skeleton. Raises on a wrong magic or version, implausible bone, node or joint counts, data that ends early, trailing bytes, or a result that fails Skeleton.validate ."},{"t":"cinder.anim API","s":"API reference","h":"cinder.anim.system","u":"/docs/api/anim/#cinderanimsystem","x":"Source: cinder/anim/system.mojo The animation system: animators stored in a pool, advanced in parallel on the job system, and published to the ECS as JointPalette components for the render bridge. Animators live in the system (they own clips and poses); entities refer to them with AnimatorRef . update(dt, jobs) runs one job per animator: each job touches only its own animator, so no synchronisation is needed beyond the completion counter. comptime ANIMATOR_REF_COMPONENT_ID = 8 — ECS component id of AnimatorRef ."},{"t":"cinder.anim API","s":"API reference","h":"struct AnimatorRef","u":"/docs/api/anim/#struct-animatorref","x":"mojo struct AnimatorRef ( Component ) Links an entity to AnimationSystem.animators[index] . Fields: index : Int component_id component_id() -> Int"},{"t":"cinder.anim API","s":"API reference","h":"struct AnimationSystem","u":"/docs/api/anim/#struct-animationsystem","x":"mojo struct AnimationSystem ( Movable ) Owns every animator, updates them in parallel and publishes their palettes to the ECS. Entities refer to animators by index through AnimatorRef ; updates counts completed update calls. Jobs receive raw pointers into animators , so the system must stay in place and the list must not change while update runs. Fields: animators : List[Animator] updates : Int __init__ __init__(out self) add add(mut self, var animator: Animator) -> Int Takes ownership of animator and returns its index for AnimatorRef . Call between frames, never while update is running. __len__ __len__(self) -> Int clear clear(mut self) Drops every animator (a level restart); AnimatorRef s "},{"t":"cinder.app API","s":"API reference","h":"cinder.app","u":"/docs/api/app/","x":""},{"t":"cinder.app API","s":"API reference","h":"cinder.app","u":"/docs/api/app/#cinderapp-1","x":"Source: cinder/app/__init__.mojo Application runtime: subsystems, the fixed-step game loop, windowed and headless modes."},{"t":"cinder.app API","s":"API reference","h":"cinder.app.app","u":"/docs/api/app/#cinderappapp","x":"Source: cinder/app/app.mojo The application runtime: owns every subsystem, runs the fixed-step game loop and renders. App works in two modes. Windowed: an SDL window, a swapchain and the ImGui debug overlay. Headless ( AppConfig.headless , the --headless flag of the sample): no window, no audio device, frames render into an offscreen texture that read_frame reads back, so tests and benchmarks drive exactly the same loop. A Game supplies the per-step and per-frame logic. Subsystems that hand out pointers to themselves live in Boxed heap cells so the App value can move. Fields are declared in destruction order: the overlay and gameplay systems first, GPU objects next, the native library last. "},{"t":"cinder.app API","s":"API reference","h":"def ui_consumes_event","u":"/docs/api/app/#def-ui_consumes_event","x":"mojo def ui_consumes_event (ev: Event, ui_wants_mouse: Bool, ui_wants_keyboard: Bool) -> Bool Whether the debug overlay swallows ev instead of the game's InputMap . While ImGui wants the mouse, mouse presses, motion and wheel go only to the overlay; while it wants the keyboard, key presses do. Key and mouse button releases always reach the input map: a key or button held when the overlay takes focus would otherwise stay held there."},{"t":"cinder.app API","s":"API reference","h":"struct AppConfig","u":"/docs/api/app/#struct-appconfig","x":"mojo struct AppConfig ( Copyable , Movable ) Settings read once by App.__init__ ; parse_app_args fills them from the command line. Defaults: 1280x720 windowed, 60 Hz fixed step, audio and debug UI on, profiler off, 3 worker threads. Fields: title : String width : Int height : Int headless : Bool fixed_hz : Float64 render : RenderSettings layers : LayerConfig audio : Bool — Create the audio engine (headless apps use miniaudio's device-less engine). debug_ui : Bool — Create the ImGui overlay (windowed only). profile : Bool — Start the Tracy client at startup. worker_threads : Int __init__ __init__( out self, title: String = String(\"Cinder\"), width: Int = 1280, height: Int = 720, headless: Bool"},{"t":"cinder.app API","s":"API reference","h":"trait Game","u":"/docs/api/app/#trait-game","x":"mojo trait Game Gameplay hooks called by App.run_frame . start start(mut self, mut app: App) raises Called once, at the start of the first run_frame and before its events are polled. fixed_update fixed_update(mut self, mut app: App, dt: Float32) raises Called once per fixed step with the step length in seconds, after physics has stepped and body transforms were pulled into the world. update update(mut self, mut app: App, dt: Float32) raises Called once per frame with the frame time in seconds, after the fixed steps and before animation, transform propagation, audio and rendering. draw_ui draw_ui(mut self, mut app: App) raises Called while the debug overlay is built (windowed with debug_ui on"},{"t":"cinder.app API","s":"API reference","h":"struct App","u":"/docs/api/app/#struct-app","x":"mojo struct App ( Movable ) The engine runtime: owns the native library, the platform and window (windowed), the GPU device, renderer, ECS world, physics, audio, animation, input map, scene registry and assets, and drives them from run_frame . Construction raises when a subsystem fails to start. Fields are destroyed in declaration order, the overlay first and lib last. Use it from the main thread, where Platform must live. Fields: debug_ui : Optional[DebugUi] assets : Optional[Boxed[AssetServer]] anim : AnimationSystem audio : Optional[AudioEngine] physics_sync : PhysicsSync bridge : RenderBridge physics : Boxed[PhysicsWorld] input : InputMap world : World scene : RenderScene registry : Scen"},{"t":"cinder.app API","s":"API reference","h":"cinder.app.cli","u":"/docs/api/app/#cinderappcli","x":"Source: cinder/app/cli.mojo Command-line options shared by every Cinder executable ( --headless for tests and benchmarks)."},{"t":"cinder.app API","s":"API reference","h":"def parse_app_args","u":"/docs/api/app/#def-parse_app_args","x":"mojo def parse_app_args ( args: List[String], mut config: AppConfig ) raises -> List[String] Applies engine options to config and returns the arguments it did not consume. Options: --headless , --profile , --no-audio , --no-debug-ui , --width N , --height N , --title TEXT . Everything else (positional arguments and the program's own options) is returned for the caller."},{"t":"cinder.assets API","s":"API reference","h":"cinder.assets","u":"/docs/api/assets/","x":""},{"t":"cinder.assets API","s":"API reference","h":"cinder.assets","u":"/docs/api/assets/#cinderassets-1","x":"Source: cinder/assets/__init__.mojo Assets: glTF import into engine meshes, materials, textures, skins and scenes."},{"t":"cinder.assets API","s":"API reference","h":"cinder.assets.cook","u":"/docs/api/assets/#cinderassetscook","x":"Source: cinder/assets/cook.mojo The asset cooker: glTF sources become cooked meshes, materials and animation clips. Cooking is incremental and keyed on content: the manifest ( manifest.txt in the output directory) records, per logical asset, the FNV-1a hash of the source file and the output file name. A source whose hash is unchanged and whose outputs still exist is skipped. Output files are named by the asset id of their logical path, so ids are stable across cooks and machines. Textures are cooked by cook_textures (KTX2) in the native-backed path; this module records the image references materials need ( <prefix>#image<N> )."},{"t":"cinder.assets API","s":"API reference","h":"struct ManifestEntry","u":"/docs/api/assets/#struct-manifestentry","x":"mojo struct ManifestEntry ( Copyable , Movable ) One manifest line: logical asset path, FNV-1a source hash ( hex64 ) and output file name. file is relative to the output directory and named by asset_file_name(logical, ...) . Fields: logical : String source_hash : String file : String"},{"t":"cinder.assets API","s":"API reference","h":"struct CookManifest","u":"/docs/api/assets/#struct-cookmanifest","x":"mojo struct CookManifest ( Movable ) The cook manifest ( <out_dir>/manifest.txt ), shared by the cooker and AssetServer . One entry per logical asset; lookups are linear scans. Re-cooking a source drops ( prune ) the entries of outputs it no longer produces. Fields: entries : List[ManifestEntry] __init__ __init__(out self) load load(path: String) raises -> CookManifest Reads a manifest; a missing file yields an empty one. Lines without exactly three space-separated fields ( <logical> <hash> <file> ) are skipped, so logical paths must not contain spaces. Raises when the file exists but cannot be read. save save(self, path: String) raises Writes a <logical> <hash> <file> line per entry, replac"},{"t":"cinder.assets API","s":"API reference","h":"struct CookReport","u":"/docs/api/assets/#struct-cookreport","x":"mojo struct CookReport ( Movable ) What one cook_gltf call did. cooked counts outputs written; skipped counts outputs found up to date when the whole source was skipped (the two never mix in one report). Fields: cooked : Int skipped : Int outputs : List[String] — Logical paths produced (or found up to date), in cook order. __init__ __init__(out self)"},{"t":"cinder.assets API","s":"API reference","h":"def cook_gltf","u":"/docs/api/assets/#def-cook_gltf","x":"mojo def cook_gltf ( lib: NativePtr, source_path: String, prefix: String, out_dir: String, mut manifest: CookManifest, ) raises -> CookReport Cooks every mesh primitive, material and animation of a glTF file. prefix is the logical name of the source (e.g. models/Fox ); outputs are <prefix>#mesh<i>/prim<p> , <prefix>#material<m> and <prefix>#anim<a> ."},{"t":"cinder.assets API","s":"API reference","h":"cinder.assets.cooked","u":"/docs/api/assets/#cinderassetscooked","x":"Source: cinder/assets/cooked.mojo Cooked asset formats (little-endian binary, versioned, bounds-checked on read). - .cmesh : engine vertex stream (56-byte vertices), optional skin stream, u32 indices with LOD ranges, bounds, morph deltas. Written by the cooker from glTF primitives (tangents generated, LODs built with meshoptimizer), loaded by AssetServer without any further processing. - .cclip : an animation clip (channels of keyframes targeting node indices). Asset ids are the FNV-1a hash of the logical asset path ( models/Fox.glb#mesh0/prim1 ), so they are stable across cooks and machines. comptime MESH_MAGIC = \"CMSH\" — First four bytes of a .cmesh file. comptime MESH_VERSION = 1 — .cmesh"},{"t":"cinder.assets API","s":"API reference","h":"def asset_id","u":"/docs/api/assets/#def-asset_id","x":"mojo def asset_id (logical_path: String) -> UInt64 Stable 64-bit id of a logical asset path: its FNV-1a hash."},{"t":"cinder.assets API","s":"API reference","h":"def asset_file_name","u":"/docs/api/assets/#def-asset_file_name","x":"mojo def asset_file_name (logical_path: String, extension: String) -> String <16 hex digits>.<extension> : content-addressed by logical path, not by contents."},{"t":"cinder.assets API","s":"API reference","h":"struct CookedMesh","u":"/docs/api/assets/#struct-cookedmesh","x":"mojo struct CookedMesh ( Movable ) What .cmesh holds. Vertex and index bytes are already in GPU layout. Fields: vertex_count : UInt32 vertex_bytes : List[UInt8] skin_bytes : List[UInt8] index_bytes : List[UInt8] lods : List[MeshLod] bounds_min : Vec3 bounds_max : Vec3 morph_target_count : UInt32 morph_bytes : List[UInt8] material : String — Logical path of the cooked material, or empty. __init__ __init__(out self)"},{"t":"cinder.assets API","s":"API reference","h":"def cook_mesh","u":"/docs/api/assets/#def-cook_mesh","x":"mojo def cook_mesh ( data: MeshData, lod_indices: List[List[UInt32]], material: String ) -> CookedMesh Packs a validated MeshData and its LOD index lists."},{"t":"cinder.assets API","s":"API reference","h":"def write_mesh","u":"/docs/api/assets/#def-write_mesh","x":"mojo def write_mesh (mesh: CookedMesh) -> List[UInt8] Serialises a CookedMesh to .cmesh bytes (version MESH_VERSION, little-endian). Layout: magic, version, vertex count, byte lengths of the vertex, skin and index streams, LOD count, bounds min and max, morph target count and byte length, material path (u32 length + bytes), the LOD table (first index, index count, error), then the vertex, skin, index and morph streams."},{"t":"cinder.assets API","s":"API reference","h":"struct Cursor","u":"/docs/api/assets/#struct-cursor","x":"mojo struct Cursor ( Movable ) Bounds-checked little-endian reader over an owned byte buffer (the cooked-format readers). Every read raises when it would run past the end of the data. Fields: data : List[UInt8] pos : Int __init__ __init__(out self, var data: List[UInt8]) remaining remaining(self) -> Int Bytes left after the read position. u8 u8(mut self) raises -> UInt8 Reads one byte; raises at the end of the data. u32 u32(mut self) raises -> UInt32 Reads a little-endian u32; raises when fewer than 4 bytes remain. f32 f32(mut self) raises -> Float32 Reads a float32 from the bit pattern of a little-endian u32. bytes bytes(mut self, count: Int) raises -> List[UInt8] Copies the next count byte"},{"t":"cinder.assets API","s":"API reference","h":"def read_mesh","u":"/docs/api/assets/#def-read_mesh","x":"mojo def read_mesh ( var data: List[UInt8]) raises -> CookedMesh Parses .cmesh bytes into a CookedMesh , taking ownership of data . Raises on a wrong magic or version; stream lengths that disagree with the vertex count (56 bytes per vertex, 24 per skin vertex, 32 per vertex and morph target); index bytes not a multiple of 4; a LOD count outside 1..16; a LOD range outside the index buffer or not a multiple of 3; any index >= vertex count; truncated data or trailing bytes."},{"t":"cinder.assets API","s":"API reference","h":"struct ClipChannel","u":"/docs/api/assets/#struct-clipchannel","x":"mojo struct ClipChannel ( Copyable , Movable ) Keyframes for one node property. path : 0 translation, 1 rotation, 2 scale, 3 weights. interpolation : 0 linear, 1 step, 2 cubic spline. values holds components floats per key (3 per cubic-spline key: in-tangent, value, out-tangent). Fields: node : Int path : Int interpolation : Int components : Int times : List[Float32] values : List[Float32]"},{"t":"cinder.assets API","s":"API reference","h":"struct Clip","u":"/docs/api/assets/#struct-clip","x":"mojo struct Clip ( Copyable , Movable ) A cooked animation clip: name, duration (the latest keyframe time) and its channels. Fields: name : String duration : Float32 channels : List[ClipChannel] __init__ __init__(out self, name: String)"},{"t":"cinder.assets API","s":"API reference","h":"def write_clip","u":"/docs/api/assets/#def-write_clip","x":"mojo def write_clip (clip: Clip) -> List[UInt8] Serialises a clip to .cclip bytes (version CLIP_VERSION, little-endian). Layout: magic, version, name, duration, channel count, then per channel its node, path, interpolation, components, key count and value count followed by the times and values."},{"t":"cinder.assets API","s":"API reference","h":"def read_clip","u":"/docs/api/assets/#def-read_clip","x":"mojo def read_clip ( var data: List[UInt8]) raises -> Clip Parses .cclip bytes into a Clip , taking ownership of data . Raises on a wrong magic or version, more than 100000 channels, a path above 3, an interpolation above 2, components outside 1..64, a value count other than keys * components (* 3 for cubic spline), decreasing keyframe times, truncated data or trailing bytes. Node indices are not checked. comptime MATERIAL_MAGIC = \"CMAT\" — First four bytes of a .cmat file. comptime MATERIAL_VERSION = 1 — .cmat layout version; read_material rejects any other."},{"t":"cinder.assets API","s":"API reference","h":"struct CookedMaterial","u":"/docs/api/assets/#struct-cookedmaterial","x":"mojo struct CookedMaterial ( Copyable , Movable ) Material parameters plus the logical image path per map slot (empty when absent). Fields: material : Material images : Array[String, 5] linear_filter : Bool address_mode : UInt32 __init__ __init__(out self, var material: Material)"},{"t":"cinder.assets API","s":"API reference","h":"def write_material","u":"/docs/api/assets/#def-write_material","x":"mojo def write_material (cm: CookedMaterial) -> List[UInt8] Serialises a CookedMaterial to .cmat bytes (version MATERIAL_VERSION, little-endian). Layout: magic, version, name, factors, alpha mode and cutoff, double-sided flag, UV transform, then per map slot (5) the texcoord set and image path, then the sampler's linear-filter flag and address mode."},{"t":"cinder.assets API","s":"API reference","h":"def read_material","u":"/docs/api/assets/#def-read_material","x":"mojo def read_material ( var data: List[UInt8]) raises -> CookedMaterial Parses .cmat bytes into a CookedMaterial , taking ownership of data . Raises on a wrong magic or version, an alpha mode above 2, truncated data or trailing bytes. Only the parameters and image paths are read; AssetServer resolves the textures and creates the sampler."},{"t":"cinder.assets API","s":"API reference","h":"cinder.assets.gltf","u":"/docs/api/assets/#cinderassetsgltf","x":"Source: cinder/assets/gltf.mojo glTF 2.0 import through cgltf: meshes (with tangents, second UV set, skinning, morph targets), metallic-roughness materials (KHR_texture_transform, KHR_materials_emissive_strength), images (file, data URI or GLB buffer view), nodes, skins and scenes. load_gltf produces a CPU-side GltfAsset ; instantiate uploads it through a Renderer and fills a RenderScene with one instance per (node, primitive), joints at the bind pose and the default morph weights. Malformed files raise; they never crash (cgltf validates the structure and every accessor read is bounds-checked by cgltf)."},{"t":"cinder.assets API","s":"API reference","h":"struct GltfImage","u":"/docs/api/assets/#struct-gltfimage","x":"mojo struct GltfImage ( Copyable , Movable ) An image decoded on the CPU by load_gltf , with its glTF name (empty when unnamed). Fields: name : String image : Image"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfTexture","u":"/docs/api/assets/#struct-gltftexture","x":"mojo struct GltfTexture ( ImplicitlyCopyable , Movable ) A glTF texture: image index (-1 without one) plus sampler settings. linear_filter is True for a linear or unspecified magnification filter; address_mode is the WGPU address mode from the sampler's wrap_s (repeat when there is no sampler). Fields: image : Int linear_filter : Bool address_mode : UInt32"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfMaterial","u":"/docs/api/assets/#struct-gltfmaterial","x":"mojo struct GltfMaterial ( Copyable , Movable ) Engine material parameters plus the asset texture index per map (-1 when absent). Fields: material : Material textures : Array[Int, 5] __init__ __init__(out self, var material: Material)"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfPrimitive","u":"/docs/api/assets/#struct-gltfprimitive","x":"mojo struct GltfPrimitive ( Copyable , Movable ) A triangle primitive as validated MeshData and its material index (-1 when none). Point, line and strip primitives are dropped on import. Fields: mesh : MeshData material : Int"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfMesh","u":"/docs/api/assets/#struct-gltfmesh","x":"mojo struct GltfMesh ( Copyable , Movable ) A glTF mesh: name, its triangle primitives and the mesh's default morph weights. Fields: name : String primitives : List[GltfPrimitive] weights : List[Float32]"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfNode","u":"/docs/api/assets/#struct-gltfnode","x":"mojo struct GltfNode ( Copyable , Movable ) A scene node; every reference is an index into GltfAsset , -1 when absent. local and world are computed by cgltf at import (the world matrix walks the parents); weights are the node's morph weight overrides (empty when it has none). Fields: name : String parent : Int children : List[Int] mesh : Int skin : Int local : Mat4 world : Mat4 weights : List[Float32]"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfSkin","u":"/docs/api/assets/#struct-gltfskin","x":"mojo struct GltfSkin ( Copyable , Movable ) Joint node indices and an inverse bind matrix each (identity where the file has none). Fields: joints : List[Int] inverse_bind : List[Mat4]"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfChannel","u":"/docs/api/assets/#struct-gltfchannel","x":"mojo struct GltfChannel ( Copyable , Movable ) One animated node property. path : 0 translation, 1 rotation, 2 scale, 3 weights; interpolation : 0 linear, 1 step, 2 cubic spline (values hold in-tangent, value, out-tangent). Fields: node : Int path : Int interpolation : Int components : Int times : List[Float32] values : List[Float32]"},{"t":"cinder.assets API","s":"API reference","h":"struct GltfAnimation","u":"/docs/api/assets/#struct-gltfanimation","x":"mojo struct GltfAnimation ( Copyable , Movable ) An animation: name, duration (latest keyframe time over its channels) and channels. Channels without a sampler, target node or supported target path are skipped on import. Fields: name : String duration : Float32 channels : List[GltfChannel] __init__ __init__(out self, name: String)"},{"t":"cinder.assets API","s":"API reference","h":"struct SceneBounds","u":"/docs/api/assets/#struct-scenebounds","x":"mojo struct SceneBounds ( ImplicitlyCopyable , Movable ) Axis-aligned box from GltfAsset.world_bounds ; all zero when the asset has no meshes. Fields: min : Vec3 max : Vec3 centre centre(self) -> Vec3 Midpoint of the box. radius radius(self) -> Float32 Half the diagonal: the radius of the sphere around centre that encloses the box."},{"t":"cinder.assets API","s":"API reference","h":"struct GltfAsset","u":"/docs/api/assets/#struct-gltfasset","x":"mojo struct GltfAsset ( Movable ) Everything load_gltf imported from one file, CPU side; cross references are list indices. roots holds the default scene's root nodes, or every parentless node when the file has no default scene. Fields: path : String images : List[GltfImage] textures : List[GltfTexture] materials : List[GltfMaterial] meshes : List[GltfMesh] nodes : List[GltfNode] skins : List[GltfSkin] animations : List[GltfAnimation] roots : List[Int] __init__ __init__(out self, path: String) world_bounds world_bounds(self) -> SceneBounds Axis-aligned bounds of every mesh instance in world space. Skinned meshes are posed on the CPU with their bind-pose joint matrices (what the GPU draws at "},{"t":"cinder.assets API","s":"API reference","h":"def load_gltf","u":"/docs/api/assets/#def-load_gltf","x":"mojo def load_gltf (lib: NativePtr, path: String) raises -> GltfAsset Parses a .gltf or .glb file (and its buffers and images) into a GltfAsset ."},{"t":"cinder.assets API","s":"API reference","h":"struct UploadedGltf","u":"/docs/api/assets/#struct-uploadedgltf","x":"mojo struct UploadedGltf ( Movable ) Renderer ids for an uploaded asset: mesh_ids[mesh][primitive] , material_ids[material] and a default material for primitives without one. Fields: mesh_ids : List[List[Int]] material_ids : List[Int] default_material : Int __init__ __init__(out self)"},{"t":"cinder.assets API","s":"API reference","h":"def upload_gltf","u":"/docs/api/assets/#def-upload_gltf","x":"mojo def upload_gltf ( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, lods: Bool = True , ) raises -> UploadedGltf Uploads every image, material and primitive of asset to the renderer. Base colour and emissive images are sRGB; the rest are linear. Materials without textures share the renderer's default textures."},{"t":"cinder.assets API","s":"API reference","h":"def instantiate","u":"/docs/api/assets/#def-instantiate","x":"mojo def instantiate ( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, mut scene: RenderScene, lods: Bool = True , ) raises -> Int Uploads the asset and appends its instances, joints and morph weights to scene . Returns the number of instances added. Base colour and emissive images are sRGB; the rest are linear. Materials without textures share the renderer's default textures."},{"t":"cinder.assets API","s":"API reference","h":"cinder.assets.runtime","u":"/docs/api/assets/#cinderassetsruntime","x":"Source: cinder/assets/runtime.mojo Runtime asset server: asynchronous loading of cooked assets on the job system, reference counting, and hot reload. Loading a cooked mesh has two halves: a job reads and parses the file (CPU only, any thread) into a heap slot; update() on the main thread uploads finished slots to the GPU through the renderer and flips the handle to ready. Nothing GPU-related happens off the main thread. Each logical asset is loaded once and shared; release drops a reference and frees the GPU mesh when the last one goes. check_for_changes reloads assets whose cooked file changed on disk and swaps the GPU mesh (or material) under the same renderer index, so instances keep work"},{"t":"cinder.assets API","s":"API reference","h":"struct AssetState","u":"/docs/api/assets/#struct-assetstate","x":"mojo struct AssetState ( Equatable , ImplicitlyCopyable , RegisterPassable ) Load state of an AssetServer record: loading, ready or failed. Fields: value : Int loading loading() -> AssetState Load started and not finished; a material also stays here while its textures load. ready ready() -> AssetState Loaded; the accessors return data. failed failed() -> AssetState The load failed (see AssetServer.error ) or the record was released."},{"t":"cinder.assets API","s":"API reference","h":"struct AssetRecord","u":"/docs/api/assets/#struct-assetrecord","x":"mojo struct AssetRecord ( Movable ) One logical asset known to an AssetServer and its loaded result. kind is 0 mesh, 1 clip, 2 texture, 3 material. Records are never removed: releasing the last reference leaves the record with state failed and error released (and an empty logical , so loading the path again makes a new record). Fields: logical : String file : String kind : Int state : AssetState refcount : Int mesh_index : Int — Renderer mesh index once ready (meshes). clip : Optional[Clip] texture_view : TextureViewHandle texture : TextureHandle material_index : Int — Renderer material index once built (materials); kept across hot reloads. sampler : SamplerHandle — Material: the sampler the"},{"t":"cinder.assets API","s":"API reference","h":"struct AssetServer","u":"/docs/api/assets/#struct-assetserver","x":"mojo struct AssetServer ( Movable ) Loads cooked assets listed in <root>/manifest.txt , shared by logical path and refcounted. Jobs only read, parse and transcode files; update does every GPU upload. All methods are main-thread calls (there are no locks). Indices from load_* stay valid for the server's lifetime. Dropping the server waits for all jobs but does not free the GPU resources of assets that were never released. Fields: lib : NativePtr jobs : Pointer[JobSystem, MutUntrackedOrigin] renderer : Pointer[Renderer, MutUntrackedOrigin] root : String manifest : CookManifest records : List[AssetRecord] counter : Counter in_flight : Int slots : List[List[_LoadSlot]] — Load slots as single-ele"},{"t":"cinder.assets API","s":"API reference","h":"cinder.assets.textures","u":"/docs/api/assets/#cinderassetstextures","x":"Source: cinder/assets/textures.mojo KTX2 textures: cooking (BasisU ETC1S or UASTC with full mip chains) and runtime loading (transcode to BC7 when the device has block compression, otherwise to RGBA8), through libktx. comptime VK_FORMAT_R8G8B8A8_UNORM: UInt32 = 37 — Vulkan format id for linear RGBA8: written for non-sRGB images, accepted when uncompressed. comptime VK_FORMAT_R8G8B8A8_SRGB: UInt32 = 43 — Vulkan format id for sRGB RGBA8: written for sRGB images and read back as \"is sRGB\". comptime VK_FORMAT_BC7_SRGB_BLOCK: UInt32 = 146 — Vulkan format id libktx reports after transcoding an sRGB texture to BC7."},{"t":"cinder.assets API","s":"API reference","h":"def cook_texture_ktx2","u":"/docs/api/assets/#def-cook_texture_ktx2","x":"mojo def cook_texture_ktx2 ( lib: NativePtr, img: Image, path: String, srgb: Bool, normal_map: Bool, uastc: Bool, quality: Int = 128 , ) raises Writes img with a full mip chain as a BasisU-compressed KTX2 file. uastc picks the high-quality UASTC codec (normal maps, detail textures); otherwise ETC1S at quality (1..255). sRGB images carry the sRGB transfer function in the data format descriptor so loaders choose an sRGB GPU format."},{"t":"cinder.assets API","s":"API reference","h":"struct LoadedTexture","u":"/docs/api/assets/#struct-loadedtexture","x":"mojo struct LoadedTexture ( Movable ) A KTX2 file transcoded on the CPU: one byte list per mip level, ready for upload_loaded . Fields: width : Int height : Int levels : List[List[UInt8]] bc7 : Bool srgb : Bool __init__ __init__(out self, width: Int, height: Int, bc7: Bool, srgb: Bool)"},{"t":"cinder.assets API","s":"API reference","h":"def load_ktx2","u":"/docs/api/assets/#def-load_ktx2","x":"mojo def load_ktx2 ( lib: NativePtr, path: String, prefer_bc7: Bool ) raises -> LoadedTexture Reads and transcodes a KTX2 file (CPU only; safe on a job thread)."},{"t":"cinder.assets API","s":"API reference","h":"def upload_loaded","u":"/docs/api/assets/#def-upload_loaded","x":"mojo def upload_loaded ( mut res: Resources, label: String, loaded: LoadedTexture ) raises -> Tuple[TextureHandle, TextureViewHandle] Creates the GPU texture (BC7 or RGBA8, sRGB variants when flagged) and uploads every level."},{"t":"cinder.audio API","s":"API reference","h":"cinder.audio","u":"/docs/api/audio/","x":""},{"t":"cinder.audio API","s":"API reference","h":"cinder.audio","u":"/docs/api/audio/#cinderaudio-1","x":"Source: cinder/audio/__init__.mojo Audio over miniaudio: buses, decoded and streamed sounds, one-shots, 3D spatialisation, a listener, and the ECS bridge that follows entities and the camera."},{"t":"cinder.audio API","s":"API reference","h":"cinder.audio.engine","u":"/docs/api/audio/#cinderaudioengine","x":"Source: cinder/audio/engine.mojo Audio over miniaudio: buses, decoded and streamed sounds, one-shots, 3D spatialisation and a listener. The engine can run headless (no device) for tests and servers; frames are then pulled with read_frames . Sounds live in the shim (miniaudio structs are opaque to Mojo) and are tracked here with generational handles. Release sounds with release and destroy the buses you created with destroy_bus ; live_sounds() and live_buses() must be zero before the engine is destroyed (the destructor still frees whatever is left). Sound-end and log callbacks post to the shim queue and are drained by update ."},{"t":"cinder.audio API","s":"API reference","h":"struct SoundHandle","u":"/docs/api/audio/#struct-soundhandle","x":"mojo struct SoundHandle ( Equatable , ImplicitlyCopyable , Movable ) Generational handle to a sound owned by AudioEngine : a slot index and that slot's generation . It goes stale once the sound is released (one-shots are released by AudioEngine.update without any call of yours); AudioEngine.is_valid tells, and every method that takes a stale handle raises. Fields: index : Int generation : Int null null() -> SoundHandle The invalid handle (index -1); it converts to False."},{"t":"cinder.audio API","s":"API reference","h":"struct Attenuation","u":"/docs/api/audio/#struct-attenuation","x":"mojo struct Attenuation ( Equatable , ImplicitlyCopyable , Movable ) Distance attenuation model of a spatial sound, mapped to miniaudio's models by AudioEngine.set_attenuation . Fields: value : UInt8 none none() -> Attenuation No distance attenuation. inverse inverse() -> Attenuation miniaudio's inverse-distance model; the SoundDesc default. linear linear() -> Attenuation miniaudio's linear model. exponential exponential() -> Attenuation miniaudio's exponential model."},{"t":"cinder.audio API","s":"API reference","h":"struct SoundDesc","u":"/docs/api/audio/#struct-sounddesc","x":"mojo struct SoundDesc ( Copyable , Movable ) How AudioEngine.load creates a sound. The constructor takes the file path, bus index, streamed , looping and spatial ; the other fields keep their defaults until set on the value: volume 1, pitch 1, inverse attenuation with min_distance 1 m, max_distance 100 m and rolloff 1, not a one-shot. The attenuation settings apply only when spatial is set. Fields: path : String bus : Int streamed : Bool — Decode while playing (music); otherwise the whole file is decoded up front. looping : Bool spatial : Bool volume : Float32 pitch : Float32 attenuation : Attenuation min_distance : Float32 max_distance : Float32 rolloff : Float32 one_shot : Bool — Released "},{"t":"cinder.audio API","s":"API reference","h":"struct AudioEngine","u":"/docs/api/audio/#struct-audioengine","x":"mojo struct AudioEngine ( Movable ) Owns the miniaudio engine, its mixing buses and every loaded sound. Bus 0 is the master bus and lives as long as the engine; buses made with create_bus are destroyed with destroy_bus . Sounds are addressed by SoundHandle and freed with release (one-shots by update ); the destructor frees whatever is left, but live_sounds() and live_buses() should be zero first. The NativePtr library must outlive the engine. Call every method from the main thread: miniaudio's threads only post events, which update drains. The constructor raises when the event queue or the miniaudio engine cannot be created (for example no audio device without headless ). Fields: lib : Nativ"},{"t":"cinder.audio API","s":"API reference","h":"cinder.audio.sync","u":"/docs/api/audio/#cinderaudiosync","x":"Source: cinder/audio/sync.mojo ECS <-> audio: AudioSource entities position their sounds, AudioListener places the listener (attach it to the camera entity so the listener follows the camera). comptime AUDIO_SOURCE_COMPONENT_ID = 10 — ECS component id of AudioSource . comptime AUDIO_LISTENER_COMPONENT_ID = 11 — ECS component id of AudioListener ."},{"t":"cinder.audio API","s":"API reference","h":"struct AudioSource","u":"/docs/api/audio/#struct-audiosource","x":"mojo struct AudioSource ( Component ) A spatial sound that follows its entity. velocity feeds the Doppler effect. Fields: sound : SoundHandle velocity : Vec3 component_id component_id() -> Int"},{"t":"cinder.audio API","s":"API reference","h":"struct AudioListener","u":"/docs/api/audio/#struct-audiolistener","x":"mojo struct AudioListener ( Component ) Marks the entity whose world transform drives the listener (position, -Z forward, +Y up). With several, the first one found wins. Fields: velocity : Vec3 component_id component_id() -> Int"},{"t":"cinder.audio API","s":"API reference","h":"def sync_audio","u":"/docs/api/audio/#def-sync_audio","x":"mojo def sync_audio (world: World, audio: AudioEngine) raises -> Int Pushes entity transforms to the sounds and the listener. Returns the number of sources positioned; stale handles are skipped."},{"t":"cinder.core API","s":"API reference","h":"cinder.core","u":"/docs/api/core/","x":""},{"t":"cinder.core API","s":"API reference","h":"cinder.core","u":"/docs/api/core/#cindercore-1","x":"Source: cinder/core/__init__.mojo Core: math, logging, assertions, allocators, containers, handles, time, file system. No I/O to GPU, audio or window systems lives here."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.arena","u":"/docs/api/core/#cindercorearena","x":"Source: cinder/core/arena.mojo LinearArena: a bump allocator for per-frame scratch memory. One block is allocated up front; alloc_bytes hands out aligned sub-ranges in O(1) and reset returns everything at once. There is no per-allocation free. After warm-up the engine's frame loop allocates only from arenas and pools, which is what makes the zero-allocation steady-state test possible."},{"t":"cinder.core API","s":"API reference","h":"struct LinearArena","u":"/docs/api/core/#struct-lineararena","x":"mojo struct LinearArena ( Movable ) Bump allocator over one fixed block. Every address returned is aligned as requested. Fields: __init__ __init__(out self, capacity: Int) raises Allocates capacity bytes once. Raises on a non-positive capacity. capacity capacity(self) -> Int Usable bytes requested at construction; the 64-byte alignment slack is not counted. used used(self) -> Int Bytes handed out since the last reset, including alignment padding. high_water high_water(self) -> Int Largest used() ever observed; the number to size the arena by. alloc_bytes alloc_bytes( mut self, size: Int, alignment: Int = 16 ) raises -> Pointer[UInt8, MutUntrackedOrigin] Returns size bytes aligned to alignmen"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.assertions","u":"/docs/api/core/#cindercoreassertions","x":"Source: cinder/core/assertions.mojo Engine assertions: broken invariants abort with an explicit message. cinder_assert is compiled in unless the build defines CINDER_ASSERTS=0 ( mojo build -D CINDER_ASSERTS=0 ), which release builds of the shipped game set and nothing else does. cinder_check is never compiled out and is used where the cost is negligible and the invariant guards memory safety (handle validation, FFI preconditions). Both abort rather than raise: an invariant violation is a bug, not a recoverable condition (section 4 of the brief). comptime ASSERTS_ENABLED: Bool = get_defined_bool[\"CINDER_ASSERTS\", True]() — Whether cinder_assert is compiled in. Read from -D CINDER_ASSERTS at b"},{"t":"cinder.core API","s":"API reference","h":"def cinder_assert","u":"/docs/api/core/#def-cinder_assert","x":"mojo def cinder_assert (condition: Bool, message: StaticString) Aborts with message when condition is false, unless asserts are compiled out."},{"t":"cinder.core API","s":"API reference","h":"def cinder_check","u":"/docs/api/core/#def-cinder_check","x":"mojo def cinder_check (condition: Bool, message: StaticString) Aborts with message when condition is false, in every build."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.base64","u":"/docs/api/core/#cindercorebase64","x":"Source: cinder/core/base64.mojo Base64 decoding (RFC 4648, with or without padding) for data URIs in glTF files."},{"t":"cinder.core API","s":"API reference","h":"def decode_base64","u":"/docs/api/core/#def-decode_base64","x":"mojo def decode_base64 (text: StringSpan) raises -> List[UInt8] Decodes base64 text; whitespace is skipped and '=' padding is optional."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.boxed","u":"/docs/api/core/#cindercoreboxed","x":"Source: cinder/core/boxed.mojo Boxed: a value at a stable heap address. Subsystems that hand out Pointer s to themselves (Gfx, Resources, Renderer, JobSystem, ...) must never move once those pointers exist. Boxed[T] owns one T on the heap so the owner can move freely while the pointee stays put."},{"t":"cinder.core API","s":"API reference","h":"struct Boxed","u":"/docs/api/core/#struct-boxed","x":"mojo struct Boxed[T: Deinitable &#x26; Movable](Movable) Owns one T on the heap so pointers to it stay valid while the box itself moves. The value is moved in at construction and destroyed, then freed, when the box is destroyed. ptr() and [] give untracked mutable access; neither may outlive the box. Fields: __init__ __init__(out self, var value: Self.T) ptr ptr(self) -> Pointer[Self.T, MutUntrackedOrigin] A raw pointer valid for the box's lifetime. __getitem__ __getitem__(ref self) -> ref[MutUntrackedOrigin] Self.T"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.bytes","u":"/docs/api/core/#cindercorebytes","x":"Source: cinder/core/bytes.mojo Little-endian byte packing for GPU uploads (std140/std430-style layouts are the caller's job)."},{"t":"cinder.core API","s":"API reference","h":"struct ByteWriter","u":"/docs/api/core/#struct-bytewriter","x":"mojo struct ByteWriter ( Movable , Sized ) Appends plain values to a byte buffer. pad_to aligns the length for struct rules. Fields: bytes : List[UInt8] __init__ __init__(out self, capacity: Int = 256) __len__ __len__(self) -> Int clear clear(mut self) Empties the buffer, keeping its capacity (same as reset ). u8 u8(mut self, v: UInt8) Appends one byte. u32 u32(mut self, v: UInt32) Appends v as four bytes, little-endian. i32 i32(mut self, v: Int32) Appends v as four bytes, little-endian two's complement. f32 f32(mut self, v: Float32) Appends the IEEE-754 bits of v as four bytes, little-endian. vec2 vec2(mut self, v: Vec2) Appends x then y as two f32 (8 bytes, no padding). vec3 vec3(mut self,"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.cstring","u":"/docs/api/core/#cindercorecstring","x":"Source: cinder/core/cstring.mojo C strings to Mojo String s, decoding the bytes as UTF-8. C APIs (SDL, the shim, stb) hand back NUL-terminated UTF-8. Converting byte by byte with chr treats every byte as a code point and double-encodes anything outside ASCII, so every conversion goes through string_from_c instead."},{"t":"cinder.core API","s":"API reference","h":"def string_from_c","u":"/docs/api/core/#def-string_from_c","x":"mojo def string_from_c ( p: Optional[Pointer[c_char, MutUntrackedOrigin]], max_len: Int = - 1 ) -> String The UTF-8 text at p up to its NUL terminator, or up to max_len bytes when max_len is not negative (fixed-size C buffers). A null pointer gives the empty string. Invalid UTF-8 sequences become U+FFFD instead of failing: C text is diagnostic data here and must never make the caller raise. The caller guarantees p points at a terminated string or at least max_len readable bytes."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.fs","u":"/docs/api/core/#cindercorefs","x":"Source: cinder/core/fs.mojo Filesystem helpers with typed errors. Thin on purpose: the asset database layers on top."},{"t":"cinder.core API","s":"API reference","h":"struct FileError","u":"/docs/api/core/#struct-fileerror","x":"mojo struct FileError ( Copyable , Movable , Writable ) A failed file operation, with the path and the underlying message. Fields: path : String message : String write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core API","s":"API reference","h":"def file_exists","u":"/docs/api/core/#def-file_exists","x":"mojo def file_exists (path: String) -> Bool True when something exists at path (a file or a directory). Never raises."},{"t":"cinder.core API","s":"API reference","h":"def read_bytes","u":"/docs/api/core/#def-read_bytes","x":"mojo def read_bytes (path: String) raises FileError -> List[UInt8] Reads a whole file into memory."},{"t":"cinder.core API","s":"API reference","h":"def read_text","u":"/docs/api/core/#def-read_text","x":"mojo def read_text (path: String) raises FileError -> String Reads a whole UTF-8 text file."},{"t":"cinder.core API","s":"API reference","h":"def write_bytes","u":"/docs/api/core/#def-write_bytes","x":"mojo def write_bytes (path: String, data: Span[UInt8, _]) raises FileError Writes (truncating) data to path ."},{"t":"cinder.core API","s":"API reference","h":"def write_text","u":"/docs/api/core/#def-write_text","x":"mojo def write_text (path: String, text: String) raises FileError Writes (truncating) text to path . Raises FileError carrying the path on failure."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.handles","u":"/docs/api/core/#cindercorehandles","x":"Source: cinder/core/handles.mojo Generational handles and the slot pool that issues them. A Handle[Tag] names a slot in a Pool[T, Tag] . The generation counter makes a stale handle (one whose slot was freed and reused) fail validation instead of aliasing a new object. Tag is a phantom type so that, for example, a texture handle cannot be passed where a buffer handle is expected."},{"t":"cinder.core API","s":"API reference","h":"struct Handle","u":"/docs/api/core/#struct-handle","x":"mojo struct Handle[Tag: AnyType]( Boolable, Equatable, Hashable, ImplicitlyCopyable, RegisterPassable, Writable, ) A 64-bit generational handle: 32-bit slot index, 32-bit generation. Zero is the null handle. Fields: index : UInt32 generation : UInt32 null null() -> Handle[Self.Tag] The null handle (index 0, generation 0); never valid in any Pool . is_null is_null(self) -> Bool True when the generation is 0, whatever the index; pools never issue generation 0. packed packed(self) -> UInt64 The handle as one integer, for serialization and GPU-side ids. from_packed from_packed(v: UInt64) -> Handle[Self.Tag] Inverse of packed() : low 32 bits are the index, high 32 bits the generation. write_to wr"},{"t":"cinder.core API","s":"API reference","h":"struct Pool","u":"/docs/api/core/#struct-pool","x":"mojo struct Pool[T: Copyable &#x26; Deinitable, Tag: AnyType](Movable) A slot map: O(1) insert, remove and lookup by Handle[Self.Tag] , with generation checking. Slots are never reallocated once created, so a Pool holds at most capacity live values; insert raises when full, which keeps steady-state frames free of allocation (section 4 of the brief). Freed slots are recycled through an intrusive free list. Generations start at 1 and skip 0 so the zero handle is never valid. Fields: __init__ __init__(out self, capacity: Int) raises Creates a pool with room for capacity values (1 <= capacity < 2^32). capacity capacity(self) -> Int Number of slots, fixed at construction; the pool never grows pas"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.hash","u":"/docs/api/core/#cindercorehash","x":"Source: cinder/core/hash.mojo Content hashing: 64-bit FNV-1a over bytes and strings (stable across runs and platforms). comptime FNV_OFFSET: UInt64 = 14695981039346656037 — FNV-1a 64-bit offset basis; also the hash of empty input. comptime FNV_PRIME: UInt64 = 1099511628211 — FNV-1a 64-bit prime."},{"t":"cinder.core API","s":"API reference","h":"def fnv1a64","u":"/docs/api/core/#def-fnv1a64","x":"mojo def fnv1a64 (data: Span[UInt8, _]) -> UInt64 64-bit FNV-1a of data . Stable across runs and platforms, which is why the asset cooker uses it as a content hash. Not cryptographic: do not use it where collisions could be engineered."},{"t":"cinder.core API","s":"API reference","h":"def fnv1a64_string","u":"/docs/api/core/#def-fnv1a64_string","x":"mojo def fnv1a64_string (text: String) -> UInt64 fnv1a64 of the string's UTF-8 bytes, without a terminator."},{"t":"cinder.core API","s":"API reference","h":"def hex64","u":"/docs/api/core/#def-hex64","x":"mojo def hex64 (v: UInt64) -> String Sixteen lowercase hex digits."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.image","u":"/docs/api/core/#cindercoreimage","x":"Source: cinder/core/image.mojo CPU images: RGBA8 load/decode/save through the shim (stb) and perceptual comparison (SSIM). Image owns its pixels in a List[UInt8] (tightly packed RGBA8). ssim is the structural similarity index on luma with an 8x8 box window; golden-image tests compare against it."},{"t":"cinder.core API","s":"API reference","h":"struct Image","u":"/docs/api/core/#struct-image","x":"mojo struct Image ( Copyable , Movable ) A tightly packed RGBA8 image. Fields: width : Int height : Int pixels : List[UInt8] __init__ __init__(out self, width: Int, height: Int) __init__ __init__( out self, width: Int, height: Int, var pixels: List[UInt8] ) raises from_rows from_rows( width: Int, height: Int, rows: List[UInt8], stride: Int ) raises -> Image Repacks rows of stride bytes (GPU readback with 256-byte alignment) into a tight image. pixel pixel(self, x: Int, y: Int) -> SIMD[DType.uint8, 4] The RGBA8 value at column x , row y ; (0, 0) is the first pixel in memory. x must be in [0, width) and y in [0, height). Anything else is a bug and aborts through cinder_check in every build (an"},{"t":"cinder.core API","s":"API reference","h":"def load_image","u":"/docs/api/core/#def-load_image","x":"mojo def load_image (lib: NativePtr, path: String) raises -> Image Loads the image file at path through the shim's stb_image and returns it as RGBA8. Any channel count in the file is expanded to four. lib must point at the loaded native shim. Raises with stb's failure reason when the file cannot be read or decoded. The stb buffer is copied into the Image and freed before returning."},{"t":"cinder.core API","s":"API reference","h":"def decode_image","u":"/docs/api/core/#def-decode_image","x":"mojo def decode_image (lib: NativePtr, bytes: Span[UInt8, _]) raises -> Image Decodes an encoded image held in memory (stb_image) to RGBA8. Raises with stb's failure reason on undecodable data; the shim also rejects inputs larger than 2^31 - 1 bytes. The stb buffer is copied into the Image and freed before returning."},{"t":"cinder.core API","s":"API reference","h":"def save_png","u":"/docs/api/core/#def-save_png","x":"mojo def save_png (lib: NativePtr, path: String, img: Image) raises Writes img to path as an RGBA8 PNG through the shim's stb_image_write. Raises when the file cannot be written; the shim also refuses an image with zero width or height."},{"t":"cinder.core API","s":"API reference","h":"struct ImageDiff","u":"/docs/api/core/#struct-imagediff","x":"mojo struct ImageDiff ( ImplicitlyCopyable , Movable , Writable ) Result of compare : SSIM on luma, plus the raw pixel statistics tests may also bound. Fields: ssim : Float64 max_channel_delta : Int differing_pixels : Int — Pixels where any channel differs by more than tolerance passed to compare . write_to write_to(self, mut w: Some[Writer])"},{"t":"cinder.core API","s":"API reference","h":"def compare","u":"/docs/api/core/#def-compare","x":"mojo def compare ( a: Image, b: Image, tolerance: Int = 2 , window: Int = 8 ) raises -> ImageDiff SSIM (luma, non-overlapping window boxes, K1 = 0.01, K2 = 0.03, L = 255) and pixel deltas."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.jobs","u":"/docs/api/core/#cindercorejobs","x":"Source: cinder/core/jobs.mojo The job system: a work-stealing thread pool with dependency counters. Design ( ADR-0008 , docs/THREADING.md): - One worker per logical core minus one (the main thread is the remaining core), each with a fixed-capacity Chase-Lev deque of job indices. A worker pushes and pops at the bottom of its own deque; idle workers steal from the top of others'. - Jobs are JobFn thin function pointers plus an untyped data pointer; the caller owns the data for the job's lifetime. Each job decrements a Counter when it finishes; wait on a counter helps run jobs until it reaches zero, so waiting never idles a core. - OS threads and semaphores come from the native shim ( cn_thread"},{"t":"cinder.core API","s":"API reference","h":"struct Counter","u":"/docs/api/core/#struct-counter","x":"mojo struct Counter ( Movable ) Completion counter: incremented per submitted job, decremented when each finishes. Fields: remaining : Atomic[Int32] __init__ __init__(out self) load load(self) -> Int32 Jobs still outstanding on this counter (acquire load); 0 when all have finished."},{"t":"cinder.core API","s":"API reference","h":"struct Job","u":"/docs/api/core/#struct-job","x":"mojo struct Job ( ImplicitlyCopyable , Movable ) One unit of work in the job table. Fields: run : JobFn data : Pointer[NoneType, MutUntrackedOrigin] counter : Optional[Pointer[Counter, MutUntrackedOrigin]] __init__ __init__( out self, run: JobFn, data: Pointer[NoneType, MutUntrackedOrigin], counter: Optional[Pointer[Counter, MutUntrackedOrigin]], )"},{"t":"cinder.core API","s":"API reference","h":"struct Deque","u":"/docs/api/core/#struct-deque","x":"mojo struct Deque ( Movable ) Chase-Lev work-stealing deque of job indices with a fixed power-of-two capacity. The owner thread calls push and pop ; any thread calls steal . Indices wrap through the mask; push fails when the deque is full instead of growing. Fields: __init__ __init__(out self, capacity: Int) push push(mut self, job: Int32) -> Bool Owner only. Returns False when full. pop pop(mut self) -> Int32 Owner only. Returns INVALID_JOB when empty. steal steal(mut self) -> Int32 Any thread. Returns INVALID_JOB when empty or when it lost a race (caller retries elsewhere)."},{"t":"cinder.core API","s":"API reference","h":"struct Shared","u":"/docs/api/core/#struct-shared","x":"mojo struct Shared ( Movable ) State shared by all workers; lives at a stable heap address for the system's lifetime. Fields: jobs : Allocation[Job] job_capacity : Int next_job : Atomic[Int64] deques : List[Deque] worker_count : Int stop : Atomic[Int32] idle : Atomic[Int32] semaphore : Optional[Pointer[CnSemaphore, MutUntrackedOrigin]] executed : Atomic[Int64] lib : Pointer[CinderNative, MutUntrackedOrigin] __init__ __init__( out self, worker_count: Int, job_capacity: Int, semaphore: Optional[Pointer[CnSemaphore, MutUntrackedOrigin]], lib: Pointer[CinderNative, MutUntrackedOrigin], )"},{"t":"cinder.core API","s":"API reference","h":"struct WorkerContext","u":"/docs/api/core/#struct-workercontext","x":"mojo struct WorkerContext ( Movable ) Passed to each worker thread entry: which deque is mine and where the shared state lives. Fields: shared : Pointer[Shared, MutUntrackedOrigin] index : Int __init__ __init__( out self, shared: Pointer[Shared, MutUntrackedOrigin], index: Int )"},{"t":"cinder.core API","s":"API reference","h":"struct JobSystem","u":"/docs/api/core/#struct-jobsystem","x":"mojo struct JobSystem ( Movable ) Owns the worker threads, the shared state and the job table. Deque 0 belongs to the main thread. Hold one instance for the life of the program (section 4: the job system runs everything that is not SDL or presentation). __deinit__ stops and joins every worker. Fields: __init__ __init__( out self, lib: Pointer[CinderNative, MutUntrackedOrigin], worker_count: Int, job_capacity: Int = 1 << 14, ) raises Starts worker_count workers (0 runs everything on the calling thread). job_capacity must be a power of two; it bounds the number of jobs in flight between reset calls. worker_count worker_count(self) -> Int Number of worker threads started, not counting the main "},{"t":"cinder.core API","s":"API reference","h":"cinder.core.log","u":"/docs/api/core/#cindercorelog","x":"Source: cinder/core/log.mojo Structured, levelled logging. A Logger owns a minimum level and a sink. Records carry a level, a category and a message plus optional key=value fields, and are formatted as one line: [LEVEL] category: message key=value ... . Sinks are a trait so tests capture records in memory and the engine routes them to stderr, a file or the debug console. Logging never raises; a failing sink drops the record."},{"t":"cinder.core API","s":"API reference","h":"struct LogLevel","u":"/docs/api/core/#struct-loglevel","x":"mojo struct LogLevel ( Comparable , Equatable , ImplicitlyCopyable , RegisterPassable , Writable ) Severity, ordered. Compare with the usual operators. Fields: value : UInt8 trace trace() -> LogLevel Severity 0, the lowest: a logger at this level passes everything. debug debug() -> LogLevel Severity 1, between trace and info . info info() -> LogLevel Severity 2; the default Logger minimum level. warn warn() -> LogLevel Severity 3, between info and error . error error() -> LogLevel Severity 4, the highest named level. name name(self) -> StaticString Label printed in records: TRACE, DEBUG, INFO, WARN, or ERROR for 4 and above. write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core API","s":"API reference","h":"struct LogField","u":"/docs/api/core/#struct-logfield","x":"mojo struct LogField ( Copyable , Movable , Writable ) One key=value pair attached to a record. Fields: key : String value : String write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core API","s":"API reference","h":"struct LogRecord","u":"/docs/api/core/#struct-logrecord","x":"mojo struct LogRecord ( Copyable , Movable , Writable ) A formatted log entry. Fields: level : LogLevel category : String message : String fields : List[LogField] __init__ __init__( out self, level: LogLevel, category: String, message: String, var fields: List[LogField], ) write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core API","s":"API reference","h":"trait LogSink","u":"/docs/api/core/#trait-logsink","x":"mojo trait LogSink Receives formatted records. Implementations must not raise. emit emit(mut self, record: LogRecord) Handles one record that passed the filter. Must not raise: drop the record instead."},{"t":"cinder.core API","s":"API reference","h":"struct PrintSink","u":"/docs/api/core/#struct-printsink","x":"mojo struct PrintSink ( LogSink , Movable ) Writes each record as one line to standard output. Fields: emitted : Int — Number of records written so far. __init__ __init__(out self) emit emit(mut self, record: LogRecord) Prints the record as one line to standard output and increments emitted ."},{"t":"cinder.core API","s":"API reference","h":"struct MemorySink","u":"/docs/api/core/#struct-memorysink","x":"mojo struct MemorySink ( LogSink , Movable ) Keeps every record; used by tests and the in-engine console. Fields: records : List[LogRecord] __init__ __init__(out self) emit emit(mut self, record: LogRecord) Appends a copy of the record to records , which grows without bound."},{"t":"cinder.core API","s":"API reference","h":"struct Logger","u":"/docs/api/core/#struct-logger","x":"mojo struct Logger[S: LogSink &#x26; Movable &#x26; Deinitable](Movable) Filters by minimum level and forwards to its sink. Fields: min_level : LogLevel sink : Self.S __init__ __init__( out self, var sink: Self.S, min_level: LogLevel = LogLevel.info() ) enabled enabled(self, level: LogLevel) -> Bool True when level >= min_level ; check it before building costly messages. log log( mut self, level: LogLevel, category: String, message: String, var fields: List[LogField], ) Emits a record when level passes the filter. Field lists are consumed. log log(mut self, level: LogLevel, category: String, message: String) Emits a record without fields when level passes the filter. trace trace(mut self, ca"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.math","u":"/docs/api/core/#cindercoremath","x":"Source: cinder/core/math/__init__.mojo SIMD math for the engine. Conventions (docs/ARCHITECTURE.md, fixed): right-handed, Y up, -Z forward, metres, column-major matrices, reverse-Z depth with an infinite far plane. All types are plain 32-bit float values backed by SIMD[DType.float32, 4] ; a Vec3 keeps its fourth lane at zero."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.math.mat4","u":"/docs/api/core/#cindercoremathmat4","x":"Source: cinder/core/math/mat4.mojo Mat4: a column-major 4x4 float matrix, as WGSL and glTF store it."},{"t":"cinder.core API","s":"API reference","h":"struct Mat4","u":"/docs/api/core/#struct-mat4","x":"mojo struct Mat4 ( Equatable , ImplicitlyCopyable , RegisterPassable , Writable ) Column-major 4x4 matrix. c0..c3 are the columns; element (row r, column c) is cC[r] . Fields: c0 : F4 c1 : F4 c2 : F4 c3 : F4 identity identity() -> Mat4 The identity matrix. zero zero() -> Mat4 All sixteen elements zero; also what inverse returns for a singular matrix. from_rows from_rows(r0: Vec4, r1: Vec4, r2: Vec4, r3: Vec4) -> Mat4 Builds a matrix from its rows (handy for literals written the way maths books print them). translation translation(t: Vec3) -> Mat4 Translation by t (metres): the identity with t in the last column. scaling scaling(s: Vec3) -> Mat4 Scale by s.x() , s.y() , s.z() along the x, y a"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.math.quat","u":"/docs/api/core/#cindercoremathquat","x":"Source: cinder/core/math/quat.mojo Quat: a unit quaternion (x, y, z, w) representing a rotation."},{"t":"cinder.core API","s":"API reference","h":"struct Quat","u":"/docs/api/core/#struct-quat","x":"mojo struct Quat ( Equatable , ImplicitlyCopyable , RegisterPassable , Writable ) Rotation quaternion. Lanes are (x, y, z, w); w is the scalar part. Fields: data : F4 __init__ __init__(out self, x: Float32, y: Float32, z: Float32, w: Float32) identity identity() -> Quat The no-rotation quaternion (0, 0, 0, 1). from_axis_angle from_axis_angle(axis: Vec3, angle: Float32) -> Quat Rotation of angle radians about the unit vector axis (right-handed). from_euler from_euler(yaw: Float32, pitch: Float32, roll: Float32) -> Quat Yaw about +Y, then pitch about +X, then roll about +Z (applied in that order to a body). x x(self) -> Float32 Lane 0, the x term of the vector part. y y(self) -> Float32 Lane 1"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.math.scalar","u":"/docs/api/core/#cindercoremathscalar","x":"Source: cinder/core/math/scalar.mojo Scalar helpers shared by the vector, quaternion and matrix types. comptime EPSILON: Float32 = 1e-6 — Default tolerance for approximate comparisons of unit-scale quantities."},{"t":"cinder.core API","s":"API reference","h":"def radians","u":"/docs/api/core/#def-radians","x":"mojo def radians (deg: Float32) -> Float32 Converts degrees to radians."},{"t":"cinder.core API","s":"API reference","h":"def degrees","u":"/docs/api/core/#def-degrees","x":"mojo def degrees (rad: Float32) -> Float32 Converts radians to degrees."},{"t":"cinder.core API","s":"API reference","h":"def clamp","u":"/docs/api/core/#def-clamp","x":"mojo def clamp (x: Float32, lo: Float32, hi: Float32) -> Float32 Clamps x to [lo, hi]."},{"t":"cinder.core API","s":"API reference","h":"def saturate","u":"/docs/api/core/#def-saturate","x":"mojo def saturate (x: Float32) -> Float32 Clamps x to [0, 1]."},{"t":"cinder.core API","s":"API reference","h":"def lerp","u":"/docs/api/core/#def-lerp","x":"mojo def lerp (a: Float32, b: Float32, t: Float32) -> Float32 Linear interpolation; exact at t = 0 and t = 1."},{"t":"cinder.core API","s":"API reference","h":"def approx_eq","u":"/docs/api/core/#def-approx_eq","x":"mojo def approx_eq (a: Float32, b: Float32, eps: Float32 = EPSILON ) -> Bool True when |a - b| <= eps * max(1, |a|, |b|): absolute near zero, relative elsewhere."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.math.transform","u":"/docs/api/core/#cindercoremathtransform","x":"Source: cinder/core/math/transform.mojo Transform: translation, rotation and scale as separate terms (poses, animation keys)."},{"t":"cinder.core API","s":"API reference","h":"struct Transform","u":"/docs/api/core/#struct-transform","x":"mojo struct Transform ( Equatable , ImplicitlyCopyable , Movable , Writable ) A TRS transform. Composition order is scale, then rotation, then translation. Fields: translation : Vec3 rotation : Quat scale : Vec3 identity identity() -> Transform Zero translation, identity rotation and unit scale. from_mat4 from_mat4(m: Mat4) -> Transform Decomposes a matrix without shear: translation from the last column, scale from the column lengths, rotation from the normalised columns. A mirrored matrix (negative determinant) has no proper rotation; the reflection is folded into a negative x scale and the rotation is taken from the matrix with its first column negated, so to_mat4() rebuilds the input. to_"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.math.vec","u":"/docs/api/core/#cindercoremathvec","x":"Source: cinder/core/math/vec.mojo Vec2, Vec3 and Vec4: 32-bit float vectors backed by SIMD registers. comptime F2 = SIMD[DType.float32, 2] — Storage of Vec2 : two float32 lanes. comptime F4 = SIMD[DType.float32, 4] — Storage of Vec3 , Vec4 , Quat and each Mat4 column: four float32 lanes."},{"t":"cinder.core API","s":"API reference","h":"struct Vec2","u":"/docs/api/core/#struct-vec2","x":"mojo struct Vec2 ( Equatable , ImplicitlyCopyable , RegisterPassable , Writable ) A 2D vector. Fields: data : F2 __init__ __init__(out self, x: Float32, y: Float32) zero zero() -> Vec2 (0, 0). x x(self) -> Float32 Lane 0. y y(self) -> Float32 Lane 1. dot dot(self, o: Vec2) -> Float32 Sum of the component-wise products. length_squared length_squared(self) -> Float32 Squared Euclidean length; avoids the square root when only comparing lengths. length length(self) -> Float32 Euclidean length. approx_equal approx_equal(self, o: Vec2, eps: Float32 = EPSILON) -> Bool True when x and y each match within eps under approx_eq . write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core API","s":"API reference","h":"struct Vec3","u":"/docs/api/core/#struct-vec3","x":"mojo struct Vec3 ( Equatable , ImplicitlyCopyable , RegisterPassable , Writable ) A 3D vector. The fourth SIMD lane is always zero so 4-wide operations stay exact. Fields: data : F4 __init__ __init__(out self, x: Float32, y: Float32, z: Float32) zero zero() -> Vec3 (0, 0, 0). one one() -> Vec3 (1, 1, 1), the unit scale. unit_x unit_x() -> Vec3 +X, the engine's right axis. unit_y unit_y() -> Vec3 +Y, the engine's up axis (right-handed, Y-up). unit_z unit_z() -> Vec3 +Z, pointing backwards: the engine's forward is -Z. forward forward() -> Vec3 The engine's forward direction, -Z. splat splat(v: Float32) -> Vec3 All three components set to v (the fourth lane stays zero). x x(self) -> Float32 Lan"},{"t":"cinder.core API","s":"API reference","h":"struct Vec4","u":"/docs/api/core/#struct-vec4","x":"mojo struct Vec4 ( Equatable , ImplicitlyCopyable , RegisterPassable , Writable ) A 4D vector, also used for homogeneous points and colours. Fields: data : F4 __init__ __init__(out self, x: Float32, y: Float32, z: Float32, w: Float32) zero zero() -> Vec4 (0, 0, 0, 0). x x(self) -> Float32 Lane 0. y y(self) -> Float32 Lane 1. z z(self) -> Float32 Lane 2. w w(self) -> Float32 Lane 3: 1 for a point, 0 for a direction, alpha for a colour. __getitem__ __getitem__(self, i: Int) -> Float32 xyz xyz(self) -> Vec3 (x, y, z) as a Vec3, dropping w without a perspective divide. dot dot(self, o: Vec4) -> Float32 Sum of the component-wise products over all four lanes. length length(self) -> Float32 Euclide"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.profile","u":"/docs/api/core/#cindercoreprofile","x":"Source: cinder/core/profile.mojo Tracy profiling for the engine: zones, frame marks, plots and messages. Profiling is off until Profiler.start() (nothing listens on the network before that) and every call is a cheap no-op while it is off. Zone opens on construction and closes when it is destroyed. Mojo destroys a value right after its last use, so end the zone explicitly where it should close: var z = profiler.zone(\"render\") ... # the instrumented work _ = z^ String lifetimes: Tracy copies zone names, zone text and messages during the call, but keys plots by the name pointer and reads it for the rest of the program. Profiler.plot therefore interns each plot name once in a table that is never"},{"t":"cinder.core API","s":"API reference","h":"struct Profiler","u":"/docs/api/core/#struct-profiler","x":"mojo struct Profiler ( ImplicitlyCopyable , Movable ) A handle to the shim's Tracy client; copies share the process-wide profiler and its plot name table. Fields: lib : NativePtr __init__ __init__(out self, lib: NativePtr) available available(self) -> Bool Whether the shim was built with Tracy. start start(self) -> Bool Starts the Tracy client (idempotent). Returns False when Tracy is not built in. stop stop(self) Shuts the Tracy client down if it is running; a no-op otherwise or without Tracy. is_started is_started(self) -> Bool Whether the Tracy client is running ( start without stop ). is_connected is_connected(self) -> Bool Whether a Tracy viewer is connected; always False while stopped "},{"t":"cinder.core API","s":"API reference","h":"struct Zone","u":"/docs/api/core/#struct-zone","x":"mojo struct Zone ( Movable ) An open profiling zone; ends when destroyed. id is 0 while profiling is off. Fields: lib : NativePtr id : UInt64 __init__ __init__(out self, lib: NativePtr, name: String, color: UInt32) text text(self, note: String) Attaches text to the open zone."},{"t":"cinder.core API","s":"API reference","h":"cinder.core.ring","u":"/docs/api/core/#cindercorering","x":"Source: cinder/core/ring.mojo RingBuffer: a fixed-capacity FIFO queue with no allocation after construction."},{"t":"cinder.core API","s":"API reference","h":"struct RingBuffer","u":"/docs/api/core/#struct-ringbuffer","x":"mojo struct RingBuffer[T: Copyable &#x26; Deinitable](Movable, Sized) Single-threaded bounded FIFO. push returns False when full; pop returns None when empty. Capacity must be a power of two so index wrapping is a mask. Cross-thread queues live in the native shim (CnEventQueue); this type is for in-engine command and event buffering. Fields: __init__ __init__(out self, capacity: Int) raises capacity capacity(self) -> Int Number of slots, a power of two fixed at construction. __len__ __len__(self) -> Int is_empty is_empty(self) -> Bool True when nothing is queued ( pop would return None). is_full is_full(self) -> Bool True when every slot is taken ( push would return False). push push(mut sel"},{"t":"cinder.core API","s":"API reference","h":"cinder.core.time","u":"/docs/api/core/#cindercoretime","x":"Source: cinder/core/time.mojo Time: a monotonic clock and the fixed-timestep accumulator that drives the simulation. The frame loop (section 4 of the brief) simulates at a fixed 60 Hz and renders at whatever rate the display allows, interpolating between the last two simulation states with alpha . comptime NS_PER_SECOND: Int = 1_000_000_000 — Nanoseconds per second."},{"t":"cinder.core API","s":"API reference","h":"struct Clock","u":"/docs/api/core/#struct-clock","x":"mojo struct Clock ( Movable ) Monotonic wall clock in nanoseconds, with per-frame deltas. Fields: __init__ __init__(out self) now_ns now_ns(self) -> Int Current perf_counter_ns() reading. Monotonic, but its origin is arbitrary (not the clock's creation), so only differences between readings are meaningful. elapsed_seconds elapsed_seconds(self) -> Float64 Seconds since the clock was created. tick tick(mut self) -> Float64 Seconds since the previous tick (or creation). Never negative."},{"t":"cinder.core API","s":"API reference","h":"struct FixedStep","u":"/docs/api/core/#struct-fixedstep","x":"mojo struct FixedStep ( ImplicitlyCopyable , Movable ) Fixed-timestep accumulator. Feed real frame times with advance ; it returns how many simulation steps of step_seconds to run (capped at max_steps_per_frame to avoid the spiral of death) and leaves alpha() as the interpolation factor for rendering between the previous and current simulation state. Fields: step_seconds : Float64 max_steps_per_frame : Int __init__ __init__(out self, hz: Float64 = 60.0, max_steps_per_frame: Int = 8) advance advance(mut self, frame_seconds: Float64) -> Int Accounts for frame_seconds of real time and returns the number of steps to simulate. alpha alpha(self) -> Float64 Fraction of a step accumulated but not ye"},{"t":"cinder.ecs API","s":"API reference","h":"cinder.ecs","u":"/docs/api/ecs/","x":""},{"t":"cinder.ecs API","s":"API reference","h":"cinder.ecs","u":"/docs/api/ecs/#cinderecs-1","x":"Source: cinder/ecs/__init__.mojo Entity component system: archetype world, typed queries, change detection, command buffers, parent-child transforms and a scheduler that runs non-conflicting systems in parallel."},{"t":"cinder.ecs API","s":"API reference","h":"cinder.ecs.commands","u":"/docs/api/ecs/#cinderecscommands","x":"Source: cinder/ecs/commands.mojo Deferred structural changes: record while iterating, apply when it is safe. A CommandBuffer stores spawns (returning placeholder entities resolved on apply), despawns, and typed adds/removes as type-erased byte payloads with the component's move/destroy functions, so it can be filled from jobs that must not mutate the world's archetypes."},{"t":"cinder.ecs API","s":"API reference","h":"struct CommandBuffer","u":"/docs/api/ecs/#struct-commandbuffer","x":"mojo struct CommandBuffer ( Movable , Sized ) Records spawns, despawns, adds and removes for a later apply on the main thread. Recording never touches the world, so a job can fill a buffer while systems iterate; one buffer is not safe to share between threads (no locks). Added values are moved into a payload the buffer owns; every add command still in the buffer owns its value, so dropping the buffer (or clear ) destroys the values of commands that were never applied. Fields: commands : List[_Command] payload : List[Block] payload_used : Int placeholders : Int resolved : List[Entity] — After apply : the real entity for each placeholder spawn (null while not applied). resuming : Bool — True a"},{"t":"cinder.ecs API","s":"API reference","h":"cinder.ecs.hierarchy","u":"/docs/api/ecs/#cinderecshierarchy","x":"Source: cinder/ecs/hierarchy.mojo Parent-child transforms: Parent , LocalTransform , WorldTransform and their propagation."},{"t":"cinder.ecs API","s":"API reference","h":"struct Parent","u":"/docs/api/ecs/#struct-parent","x":"mojo struct Parent ( Component ) Parent link for propagate_transforms (id 1); a dead parent or a self link is ignored. Fields: entity : Entity component_id component_id() -> Int"},{"t":"cinder.ecs API","s":"API reference","h":"struct LocalTransform","u":"/docs/api/ecs/#struct-localtransform","x":"mojo struct LocalTransform ( Component ) Transform relative to the Parent (to world space without one); component id 2. Fields: matrix : Mat4 component_id component_id() -> Int"},{"t":"cinder.ecs API","s":"API reference","h":"struct WorldTransform","u":"/docs/api/ecs/#struct-worldtransform","x":"mojo struct WorldTransform ( Component ) World-space transform, parent world * local, written by propagate_transforms ; id 3. Fields: matrix : Mat4 component_id component_id() -> Int"},{"t":"cinder.ecs API","s":"API reference","h":"def propagate_transforms","u":"/docs/api/ecs/#def-propagate_transforms","x":"mojo def propagate_transforms ( mut world: World) raises -> Int Writes WorldTransform = parent world * LocalTransform for every entity with a local transform (adding the component when missing). Parents are resolved recursively with memoisation, so the order of entities does not matter and each is computed once. Only transforms whose value changed are marked changed. Scratch lists live in the world, so a steady-state call allocates nothing. Returns the number of updated entities."},{"t":"cinder.ecs API","s":"API reference","h":"cinder.ecs.schedule","u":"/docs/api/ecs/#cinderecsschedule","x":"Source: cinder/ecs/schedule.mojo System scheduling by declared component access. Each system names the components it reads and writes. Schedule.build groups systems into stages in declaration order: a system joins the current stage unless it conflicts with one already there (write/write or read/write on the same component, or a declared exclusive world access), in which case a new stage starts. Systems within a stage run in parallel on the job system; stages run one after another, so a system always sees the effects of every earlier conflicting system. comptime SystemFn = def(Pointer[World, MutUntrackedOrigin]) thin -> None — A system body, called with an untracked world pointer, possibly on"},{"t":"cinder.ecs API","s":"API reference","h":"struct SystemDesc","u":"/docs/api/ecs/#struct-systemdesc","x":"mojo struct SystemDesc ( Copyable , Movable ) A named system plus the component ids it reads and writes, used by Schedule for staging. The declarations are trusted: nothing checks what run actually accesses. Fields: name : String run : SystemFn reads : List[Int] writes : List[Int] exclusive : Bool — Structural changes (spawn/add/remove): conflicts with every other system. __init__ __init__( out self, name: String, run: SystemFn, exclusive: Bool = False ) reads_ reads_[T: Component](var self) -> SystemDesc Declares a read of T ; returns the description for chaining. writes_ writes_[T: Component](var self) -> SystemDesc Declares a write of T ; returns the description for chaining. conflicts_wi"},{"t":"cinder.ecs API","s":"API reference","h":"struct Schedule","u":"/docs/api/ecs/#struct-schedule","x":"mojo struct Schedule ( Movable ) Systems grouped into stages by declared access (see the module docstring). Fields: systems : List[SystemDesc] stages : List[List[Int]] — System indices per stage, filled by build . built : Bool contexts : List[_JobContext] __init__ __init__(out self) add add(mut self, var system: SystemDesc) Appends a system after those already added and marks the stages for rebuilding. build build(mut self) Assigns systems to stages in declaration order. A system joins the last stage unless it conflicts with any system there, otherwise it opens a new stage; earlier stages are never revisited, so the order affects packing. stage_count stage_count(mut self) -> Int Number of st"},{"t":"cinder.ecs API","s":"API reference","h":"cinder.ecs.world","u":"/docs/api/ecs/#cinderecsworld","x":"Source: cinder/ecs/world.mojo Archetype ECS: entities, type-erased component columns, typed queries, change detection. Components are structs conforming to Component (a unique component_id() ). The world registers a component type on first use, recording its size, alignment, and type-erased move/destroy functions, so columns can hold any Movable type. Entities with the same component set live in one archetype, one contiguous column per component (16-byte aligned blocks), so a query iterates plain arrays. Change detection: every column keeps a per-row tick written by set / get_mut / add ; the world's tick advances with advance_tick() (once per frame or per system) and queries filter rows with"},{"t":"cinder.ecs API","s":"API reference","h":"trait Component","u":"/docs/api/ecs/#trait-component","x":"mojo trait Component ( Copyable , Deinitable , Movable ) A component type. component_id() must be unique per type (0 .. MAX_COMPONENTS - 1). component_id component_id() -> Int The type's id, unique among component types and in 0 .. MAX_COMPONENTS - 1."},{"t":"cinder.ecs API","s":"API reference","h":"struct Entity","u":"/docs/api/ecs/#struct-entity","x":"mojo struct Entity ( Equatable , Hashable , ImplicitlyCopyable , RegisterPassable , Writable ) Generational entity id: index + generation. Entity.null() is never live. Fields: index : UInt32 generation : UInt32 null null() -> Entity The null entity (index 0xFFFFFFFF, generation 0): never alive, False as a Bool. is_null is_null(self) -> Bool Whether the index is the null index 0xFFFFFFFF (the generation is not compared). packed packed(self) -> UInt64 Index in the low 32 bits, generation in the high 32; from_packed inverts it. from_packed from_packed(v: UInt64) -> Entity Inverse of packed() : index from the low 32 bits, generation from the high 32. write_to write_to(self, mut w: Some[Writer])"},{"t":"cinder.ecs API","s":"API reference","h":"def component_stride","u":"/docs/api/ecs/#def-component_stride","x":"mojo def component_stride [T: Component]() -> Int Element stride: the size rounded up to the type's alignment, so arrays stay tight (an 8-byte component takes 8 bytes per row) while every element is correctly aligned."},{"t":"cinder.ecs API","s":"API reference","h":"struct ComponentInfo","u":"/docs/api/ecs/#struct-componentinfo","x":"mojo struct ComponentInfo ( ImplicitlyCopyable , Movable ) Registration record per component id: size, alignment, row stride, type name and type-erased destroy/move. Every id has one; ids that were never registered hold no-op functions, size 0, an empty type name and registered False. Fields: id : Int size : Int align : Int stride : Int — component_stride[T]() : the size rounded up to the type's own alignment (not to 16). Column storage starts on a 16-byte boundary and register rejects alignments above 16, so every element is correctly aligned. type_name : StaticString — reflect[T].name() of the registered type; tells apart same-size types sharing an id. destroy : DestroyFn move : MoveFn reg"},{"t":"cinder.ecs API","s":"API reference","h":"struct Column","u":"/docs/api/ecs/#struct-column","x":"mojo struct Column ( Movable ) One component's storage inside an archetype: count elements of stride bytes. Fields: component : Int stride : Int blocks : List[Block] ticks : List[UInt32] __init__ __init__(out self, component: Int, stride: Int, capacity: Int) capacity capacity(self) -> Int Rows that fit in the allocated blocks before the column must grow. base base(self) -> ErasedPtr Untyped pointer to row 0; dangles once the column grows. element element(self, row: Int) -> ErasedPtr Untyped pointer to row ( row * stride bytes past base ); no bounds check. grow grow(mut self, needed: Int) Ensures room for needed rows, at least doubling the capacity. Existing rows are relocated bytewise (no mo"},{"t":"cinder.ecs API","s":"API reference","h":"struct Archetype","u":"/docs/api/ecs/#struct-archetype","x":"mojo struct Archetype ( Movable ) Entities sharing one component set. Columns are parallel to entities . Fields: signature : List[Int] — Sorted component ids. entities : List[Entity] columns : List[Column] column_of : List[Int] — Component id -> column index, or -1. __init__ __init__( out self, var signature: List[Int], infos: List[ComponentInfo] ) count count(self) -> Int Number of entities (rows) stored in this archetype. has has(self, component: Int) -> Bool Whether component id component is in this archetype's signature. matches matches(self, with_: List[Int], without: List[Int]) -> Bool Whether the archetype has every id in with_ and none of the ids in without . reserve_row reserve_row("},{"t":"cinder.ecs API","s":"API reference","h":"struct World","u":"/docs/api/ecs/#struct-world","x":"mojo struct World ( Movable ) The entity store: component registry, archetypes, entity locations and the change tick. New entities start in archetype 0 (no components); archetypes are created on demand and never removed. The world has no locks and belongs to the main thread: structural changes, register and advance_tick must not overlap any other access. Dropping the world destroys every stored component. Fields: infos : List[ComponentInfo] archetypes : List[Archetype] locations : List[_Location] free_indices : List[UInt32] tick : UInt32 live_count : Int prop_computed : List[Bool] — Scratch for propagate_transforms (kept between frames). prop_results : List[Mat4] prop_todo : List[Entity] pro"},{"t":"cinder.ecs API","s":"API reference","h":"def pass_through","u":"/docs/api/ecs/#def-pass_through","x":"mojo def pass_through (p: ErasedPtr) Hands p to keep ; the body of the no-op destroy/move used for unregistered ids."},{"t":"cinder.ecs API","s":"API reference","h":"struct Chunk","u":"/docs/api/ecs/#struct-chunk","x":"mojo struct Chunk ( ImplicitlyCopyable , Movable ) One archetype's rows matched by a query: typed column access by row index. Fields: world : Pointer[World, MutUntrackedOrigin] archetype : Int count : Int __init__ __init__( out self, world: Pointer[World, MutUntrackedOrigin], archetype: Int ) has has[T: Component](self) -> Bool Whether every entity in this chunk has T . entity entity(self, row: Int) -> Entity The entity at row of this chunk's archetype. column column[T: Component](self) -> Pointer[T, MutUntrackedOrigin] Base pointer of the component column (element row is column[T]()[unsafe_offset=row] ). Aborts when the chunk has no T (use has for optional components). stride stride[T: Comp"},{"t":"cinder.ecs API","s":"API reference","h":"struct QuerySpec","u":"/docs/api/ecs/#struct-queryspec","x":"mojo struct QuerySpec ( Copyable , Movable ) The filters of a query without a world pointer: safe to keep in a system struct. Fields: with_ids : List[Int] without_ids : List[Int] __init__ __init__(out self) with_ with_[T: Component](var self) -> QuerySpec Requires component T ; returns the spec for chaining. without without[T: Component](var self) -> QuerySpec Excludes archetypes that have T ; returns the spec for chaining."},{"t":"cinder.ecs API","s":"API reference","h":"struct Query","u":"/docs/api/ecs/#struct-query","x":"mojo struct Query ( Movable ) Builder: world.query().with_[A]().with_[B]().without[C]().chunks() lists matching chunks. Component access inside a chunk is typed ( chunk.column[A]() ); the filters decide which archetypes take part. Fields: world : Pointer[World, MutUntrackedOrigin] with_ids : List[Int] without_ids : List[Int] __init__ __init__(out self, world: Pointer[World, MutUntrackedOrigin]) with_ with_[T: Component](var self) -> Query Requires component T ; returns the query for chaining. without without[T: Component](var self) -> Query Excludes archetypes that have T ; returns the query for chaining. chunks chunks(self) -> List[Chunk] A new list with one Chunk per non-empty matching arc"},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx","u":"/docs/api/gfx/","x":""},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx","u":"/docs/api/gfx/#cindergfx-1","x":"Source: cinder/gfx/__init__.mojo Safe, typed layer over wgpu-native. Gfx owns the instance, adapter, device and queue and drains the shim's event queue into diagnostics (validation errors, device loss, wgpu log). Resource modules build on it with one distinct handle type per resource kind and ownership through __deinit__ ."},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.commands","u":"/docs/api/gfx/#cindergfxcommands","x":"Source: cinder/gfx/commands.mojo Command recording: encoders, render and compute passes, copies, submission and readback. comptime NoChain = Optional[Pointer[WGPUChainedStruct, MutUntrackedOrigin]] — Null nextInChain for wgpu descriptors that carry no chained extension struct."},{"t":"cinder.gfx API","s":"API reference","h":"struct ColorAttachment","u":"/docs/api/gfx/#struct-colorattachment","x":"mojo struct ColorAttachment ( Copyable , Movable ) A colour target of a render pass. view may be a swapchain view obtained outside Resources; resolve is the single-sample target when view is multisampled (null otherwise). Fields: view : WGPUTextureView clear : Bool clear_color : WGPUColor store : Bool resolve : WGPUTextureView __init__ __init__( out self, view: WGPUTextureView, clear: Bool, var clear_color: WGPUColor, store: Bool, resolve: WGPUTextureView = WGPUTextureView(), ) cleared cleared( view: WGPUTextureView, r: Float64, g: Float64, b: Float64, a: Float64 ) -> ColorAttachment Clears view to (r, g, b, a) at load and stores the result; no resolve target. cleared_resolving cleared_resol"},{"t":"cinder.gfx API","s":"API reference","h":"struct DepthAttachment","u":"/docs/api/gfx/#struct-depthattachment","x":"mojo struct DepthAttachment ( ImplicitlyCopyable , Movable ) Depth target. Reverse-Z clears to 0.0 (far). Fields: view : WGPUTextureView clear : Bool clear_depth : Float32 store : Bool none none() -> DepthAttachment No depth target: begin_render_pass then passes no depth-stencil attachment. cleared_reverse_z cleared_reverse_z(view: WGPUTextureView) -> DepthAttachment Clears view to 0.0 (the far plane under reverse-Z) and stores the result."},{"t":"cinder.gfx API","s":"API reference","h":"struct RenderPass","u":"/docs/api/gfx/#struct-renderpass","x":"mojo struct RenderPass ( Movable ) An open render pass. end must be called exactly once. Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] res : Pointer[Resources, MutUntrackedOrigin] raw : WGPURenderPassEncoder ended : Bool __init__ __init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], raw: WGPURenderPassEncoder, ) set_pipeline set_pipeline(self, h: RenderPipelineHandle) raises Binds the render pipeline; raises if the handle is not live in Resources . set_bind_group set_bind_group(self, index: UInt32, h: BindGroupHandle) raises Binds a bind group at group index with no dynamic offsets; raises on an invalid handle. set_bind_group_dynamic set_bin"},{"t":"cinder.gfx API","s":"API reference","h":"struct ComputePass","u":"/docs/api/gfx/#struct-computepass","x":"mojo struct ComputePass ( Movable ) An open compute pass from CommandEncoder.begin_compute_pass . end must be called exactly once. Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] res : Pointer[Resources, MutUntrackedOrigin] raw : WGPUComputePassEncoder ended : Bool __init__ __init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], raw: WGPUComputePassEncoder, ) set_pipeline set_pipeline(self, h: ComputePipelineHandle) raises Binds the compute pipeline; raises if the handle is not live in Resources . set_bind_group set_bind_group(self, index: UInt32, h: BindGroupHandle) raises Binds a bind group at group index with no dynamic offsets; raises on an "},{"t":"cinder.gfx API","s":"API reference","h":"def command_encoder_descriptor","u":"/docs/api/gfx/#def-command_encoder_descriptor","x":"mojo def command_encoder_descriptor ( ref label: String) -> WGPUCommandEncoderDescriptor The descriptor CommandEncoder creates its encoder with: no chain, label as the label. The view borrows label , which must outlive the wgpu call ( keep it)."},{"t":"cinder.gfx API","s":"API reference","h":"struct CommandEncoder","u":"/docs/api/gfx/#struct-commandencoder","x":"mojo struct CommandEncoder ( Movable ) Records one command buffer. finish_and_submit consumes it; an encoder dropped without it is released by its destructor and its commands are discarded. Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] res : Pointer[Resources, MutUntrackedOrigin] raw : WGPUCommandEncoder __init__ __init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], label: String, ) raises begin_render_pass begin_render_pass( self, colors: List[ColorAttachment], depth: DepthAttachment ) raises -> RenderPass Begins a render pass on the given colour targets and optional depth target. Raises when there are more than MAX_COLOR_ATTACHMENTS colour"},{"t":"cinder.gfx API","s":"API reference","h":"def wait_for_queue","u":"/docs/api/gfx/#def-wait_for_queue","x":"mojo def wait_for_queue ( mut gfx: Gfx) raises GfxError Blocks until all submitted work has completed (tests and shutdown; never in the frame loop)."},{"t":"cinder.gfx API","s":"API reference","h":"def read_buffer","u":"/docs/api/gfx/#def-read_buffer","x":"mojo def read_buffer ( mut gfx: Gfx, res: Resources, buffer: BufferHandle, size: Int ) raises -> List[UInt8] Maps a MapRead buffer, copies size bytes out and unmaps it (tests and tools only)."},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.debug_draw","u":"/docs/api/gfx/#cindergfxdebug_draw","x":"Source: cinder/gfx/debug_draw.mojo Immediate-mode debug lines: segments, boxes, spheres, axes and grids drawn in one line-list pass. Vertices accumulate on the CPU each frame ( line , aabb , ...) and are uploaded once in flush , which records the draw into an open RenderPass . The vertex buffer grows by doubling and is never shrunk, so the steady state allocates nothing. Depth testing is reverse-Z when a depth format is given; depth_format == Undefined draws on top of everything. comptime VERTEX_BYTES = 16 — Float32x3 position + Unorm8x4 colour. comptime SHADER = \"\"\" — WGSL for debug lines: transforms positions by the camera uniform and passes the vertex colour through unlit."},{"t":"cinder.gfx API","s":"API reference","h":"struct Color8","u":"/docs/api/gfx/#struct-color8","x":"mojo struct Color8 ( Equatable , ImplicitlyCopyable , RegisterPassable ) 8-bit RGBA colour. Fields: r : UInt8 g : UInt8 b : UInt8 a : UInt8 red red() -> Color8 Opaque red (255, 0, 0, 255). green green() -> Color8 Opaque green (0, 255, 0, 255). blue blue() -> Color8 Opaque blue (0, 0, 255, 255). white white() -> Color8 Opaque white (255, 255, 255, 255). yellow yellow() -> Color8 Opaque yellow (255, 255, 0, 255)."},{"t":"cinder.gfx API","s":"API reference","h":"struct DebugDraw","u":"/docs/api/gfx/#struct-debugdraw","x":"mojo struct DebugDraw ( Movable ) Per-frame line accumulator plus the GPU objects that draw it. Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] res : Pointer[Resources, MutUntrackedOrigin] shader : ShaderHandle pipeline : RenderPipelineHandle camera_buffer : BufferHandle camera_group : BindGroupHandle vertex_buffer : BufferHandle vertex_capacity : Int — Vertices the GPU buffer can hold. vertices : List[UInt8] vertex_count : Int grows : Int — Times the vertex buffer was reallocated (tests check this stays flat in steady state). __init__ __init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], mut shaders: ShaderManager, color_format: WGPUTextureFo"},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.device","u":"/docs/api/gfx/#cindergfxdevice","x":"Source: cinder/gfx/device.mojo Gfx: instance, adapter, device and queue, with every wgpu callback pulled from the shim queue. comptime EventQueuePtr = Optional[Pointer[CnEventQueue, MutUntrackedOrigin]] — Nullable pointer to the shim's CnEventQueue that carries every wgpu callback as an event. comptime NoChain = Optional[Pointer[WGPUChainedStruct, MutUntrackedOrigin]] — Null nextInChain for wgpu descriptors that carry no chained extension struct. comptime DEFAULT_TIMEOUT_NS = 10_000_000_000 — How long Gfx.await_event pumps before raising GfxError: 10 seconds."},{"t":"cinder.gfx API","s":"API reference","h":"struct GfxError","u":"/docs/api/gfx/#struct-gfxerror","x":"mojo struct GfxError ( Copyable , Movable , Writable ) A recoverable graphics failure (adapter/device request, timeout, lost device). Fields: message : String write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx API","s":"API reference","h":"struct Gfx","u":"/docs/api/gfx/#struct-gfx","x":"mojo struct Gfx ( Movable ) Owns one wgpu instance, adapter, device and queue. Callbacks never run in Mojo: pump drives wgpu and drains the shim's event queue, turning log lines, uncaptured validation errors and device loss into diagnostics (LogRecords for the engine logger) while returning every other event to the caller that awaits it. Fields: lib : NativePtr events : EventQueuePtr instance : WGPUInstance adapter : WGPUAdapter device : WGPUDevice queue : WGPUQueue adapter_name : String backend : UInt32 wireframe_supported : Bool — True when the device was created with wgpu-native's PolygonModeLine feature. bc_supported : Bool — True when the device was created with BC (DXT/BC7) texture com"},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.pipeline","u":"/docs/api/gfx/#cindergfxpipeline","x":"Source: cinder/gfx/pipeline.mojo Typed descriptors for bind group layouts, bind groups, pipeline layouts and pipelines. Each create_* builds the wgpu descriptor from engine-side structs, calls wgpu with every pointee kept alive for the call, and adopts the result into Resources under a typed handle. comptime NoChain = Optional[Pointer[WGPUChainedStruct, MutUntrackedOrigin]] — Null nextInChain for wgpu descriptors that carry no chained extension struct."},{"t":"cinder.gfx API","s":"API reference","h":"struct BindingKind","u":"/docs/api/gfx/#struct-bindingkind","x":"mojo struct BindingKind ( Equatable , ImplicitlyCopyable , RegisterPassable ) What a bind group layout slot holds. Fields: value : UInt8 uniform_buffer uniform_buffer() -> BindingKind Uniform buffer; LayoutEntry.dynamic_offset and min_binding_size apply. storage_buffer storage_buffer() -> BindingKind Read-write storage buffer; dynamic_offset and min_binding_size apply. readonly_storage_buffer readonly_storage_buffer() -> BindingKind Read-only storage buffer; dynamic_offset and min_binding_size apply. texture_2d texture_2d() -> BindingKind Filterable float texture with a 2D view dimension. sampler sampler() -> BindingKind Filtering sampler. comparison_sampler comparison_sampler() -> BindingKi"},{"t":"cinder.gfx API","s":"API reference","h":"struct LayoutEntry","u":"/docs/api/gfx/#struct-layoutentry","x":"mojo struct LayoutEntry ( ImplicitlyCopyable , Movable ) One slot of a bind group layout. format matters for storage textures only. Fields: binding : UInt32 visibility : UInt64 kind : BindingKind dynamic_offset : Bool min_binding_size : UInt64 format : WGPUTextureFormat __init__ __init__( out self, binding: UInt32, visibility: UInt64, kind: BindingKind, dynamic_offset: Bool = False, min_binding_size: UInt64 = 0, format: WGPUTextureFormat = WGPUTextureFormat_Undefined, )"},{"t":"cinder.gfx API","s":"API reference","h":"struct BindingResource","u":"/docs/api/gfx/#struct-bindingresource","x":"mojo struct BindingResource ( ImplicitlyCopyable , Movable ) What to bind in a slot: exactly one of buffer, texture view or sampler is non-null. Fields: binding : UInt32 buffer : BufferHandle buffer_offset : UInt64 buffer_size : UInt64 texture_view : TextureViewHandle sampler : SamplerHandle for_buffer for_buffer( binding: UInt32, buffer: BufferHandle, offset: UInt64, size: UInt64 ) -> BindingResource Binds size bytes of buffer from offset ; a size of 0 binds from offset to the end of the buffer (resolved in create_bind_group ). for_texture for_texture( binding: UInt32, view: TextureViewHandle ) -> BindingResource Binds a texture view from Resources (owned or imported). for_sampler for_sampl"},{"t":"cinder.gfx API","s":"API reference","h":"struct VertexAttribute","u":"/docs/api/gfx/#struct-vertexattribute","x":"mojo struct VertexAttribute ( ImplicitlyCopyable , Movable ) One vertex attribute: its format, byte offset within the vertex and shader location. Fields: format : WGPUVertexFormat offset : UInt64 location : UInt32"},{"t":"cinder.gfx API","s":"API reference","h":"struct VertexLayout","u":"/docs/api/gfx/#struct-vertexlayout","x":"mojo struct VertexLayout ( Copyable , Movable ) One vertex buffer slot: stride, step mode and attributes. Fields: stride : UInt64 step_mode : WGPUVertexStepMode attributes : List[VertexAttribute] __init__ __init__( out self, stride: UInt64, var attributes: List[VertexAttribute], step_mode: WGPUVertexStepMode = WGPUVertexStepMode_Vertex, )"},{"t":"cinder.gfx API","s":"API reference","h":"struct DepthState","u":"/docs/api/gfx/#struct-depthstate","x":"mojo struct DepthState ( ImplicitlyCopyable , Movable ) Depth testing. Reverse-Z means GreaterEqual is the engine's default compare. Fields: format : WGPUTextureFormat write : Bool compare : WGPUCompareFunction bias : Int32 bias_slope : Float32 bias_clamp : Float32 __init__ __init__( out self, format: WGPUTextureFormat, write: Bool, compare: WGPUCompareFunction, bias: Int32 = 0, bias_slope: Float32 = 0.0, bias_clamp: Float32 = 0.0, ) none none() -> DepthState No depth-stencil state: build_render_pipeline omits it because the format is Undefined. reverse_z reverse_z(format: WGPUTextureFormat, write: Bool = True) -> DepthState Reverse-Z depth test (GreaterEqual) on format , writing depth unles"},{"t":"cinder.gfx API","s":"API reference","h":"struct BlendMode","u":"/docs/api/gfx/#struct-blendmode","x":"mojo struct BlendMode ( Equatable , ImplicitlyCopyable , RegisterPassable ) Colour blending preset of a ColorTarget , expanded to a WGPUBlendState when a pipeline is built. Any value other than alpha or additive builds as opaque. Fields: value : UInt8 opaque opaque() -> BlendMode No blending: the source replaces the destination (One, Zero). alpha alpha() -> BlendMode Premultiplied-free source-over: src * a + dst * (1 - a). additive additive() -> BlendMode Adds source to destination for colour and alpha (One, One)."},{"t":"cinder.gfx API","s":"API reference","h":"struct ColorTarget","u":"/docs/api/gfx/#struct-colortarget","x":"mojo struct ColorTarget ( ImplicitlyCopyable , Movable ) One colour output of a render pipeline: its texture format and blend mode, written with all channels enabled. Fields: format : WGPUTextureFormat blend : BlendMode"},{"t":"cinder.gfx API","s":"API reference","h":"struct RenderPipelineDesc","u":"/docs/api/gfx/#struct-renderpipelinedesc","x":"mojo struct RenderPipelineDesc ( Copyable , Movable ) Everything needed for a render pipeline. Entry points name functions in shader . Fields: label : String layout : PipelineLayoutHandle shader : ShaderHandle vertex_entry : String fragment_entry : String vertex_layouts : List[VertexLayout] color_targets : List[ColorTarget] depth : DepthState topology : WGPUPrimitiveTopology cull_mode : WGPUCullMode sample_count : UInt32 wireframe : Bool — Line polygon mode (needs the device feature; see Gfx.wireframe_supported ). front_face_cw : Bool __init__ __init__( out self, label: String, layout: PipelineLayoutHandle, shader: ShaderHandle, vertex_entry: String, fragment_entry: String, var vertex_layout"},{"t":"cinder.gfx API","s":"API reference","h":"def create_bind_group_layout","u":"/docs/api/gfx/#def-create_bind_group_layout","x":"mojo def create_bind_group_layout ( mut res: Resources, label: String, entries: List[LayoutEntry] ) raises -> BindGroupLayoutHandle Creates a WGPUBindGroupLayout from entries and adopts it into res . Each entry becomes a buffer, sampler, texture or storage-texture layout according to its BindingKind ; a kind not handled here leaves every binding type unused. Raises if wgpu returns NULL; other validation errors surface through Gfx.pump diagnostics. The caller destroys the handle with Resources.destroy_bind_group_layout ."},{"t":"cinder.gfx API","s":"API reference","h":"def create_pipeline_layout","u":"/docs/api/gfx/#def-create_pipeline_layout","x":"mojo def create_pipeline_layout ( mut res: Resources, label: String, layouts: List[BindGroupLayoutHandle] ) raises -> PipelineLayoutHandle Creates a WGPUPipelineLayout whose bind group layouts are in group order and adopts it. Raises on an invalid layout handle or a NULL result. The caller destroys the handle with Resources.destroy_pipeline_layout ."},{"t":"cinder.gfx API","s":"API reference","h":"def create_bind_group","u":"/docs/api/gfx/#def-create_bind_group","x":"mojo def create_bind_group ( mut res: Resources, label: String, layout: BindGroupLayoutHandle, bindings: List[BindingResource], ) raises -> BindGroupHandle Creates a WGPUBindGroup for layout from bindings and adopts it into res . The raw wgpu objects behind the handles are captured at creation time; replacing them behind the same handle later does not update this bind group. Raises on any invalid handle or a NULL result; mismatches with the layout surface through Gfx.pump diagnostics. The caller destroys the handle with Resources.destroy_bind_group ."},{"t":"cinder.gfx API","s":"API reference","h":"def build_render_pipeline","u":"/docs/api/gfx/#def-build_render_pipeline","x":"mojo def build_render_pipeline ( res: Resources, desc: RenderPipelineDesc ) raises -> WGPURenderPipeline Creates the raw pipeline object (used by create_render_pipeline and by hot reload). Raises on an invalid handle, a NULL result, or a validation error drained right after creation; the invalid object is then released and the error carries wgpu's validation message. The caller owns the returned object."},{"t":"cinder.gfx API","s":"API reference","h":"def create_render_pipeline","u":"/docs/api/gfx/#def-create_render_pipeline","x":"mojo def create_render_pipeline ( mut res: Resources, desc: RenderPipelineDesc ) raises -> RenderPipelineHandle Builds a render pipeline with build_render_pipeline and adopts it into res . Raises if creation fails, including validation errors drained right after creation. The caller destroys the handle with Resources.destroy_render_pipeline ."},{"t":"cinder.gfx API","s":"API reference","h":"def build_compute_pipeline","u":"/docs/api/gfx/#def-build_compute_pipeline","x":"mojo def build_compute_pipeline ( res: Resources, label: String, layout: PipelineLayoutHandle, shader: ShaderHandle, entry: String, ) raises -> WGPUComputePipeline Creates the raw compute pipeline object, adopted by create_compute_pipeline or passed to Resources.replace_compute_pipeline to swap it behind an existing handle. A null layout handle passes a null pipeline layout to wgpu. Raises on an invalid handle, a NULL result, or a validation error drained right after creation; the invalid object is then released and the error carries wgpu's validation message. The caller owns the returned object."},{"t":"cinder.gfx API","s":"API reference","h":"def create_compute_pipeline","u":"/docs/api/gfx/#def-create_compute_pipeline","x":"mojo def create_compute_pipeline ( mut res: Resources, label: String, layout: PipelineLayoutHandle, shader: ShaderHandle, entry: String, ) raises -> ComputePipelineHandle Builds a compute pipeline with build_compute_pipeline and adopts it into res . The caller destroys the handle with Resources.destroy_compute_pipeline ."},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.resources","u":"/docs/api/gfx/#cindergfxresources","x":"Source: cinder/gfx/resources.mojo Typed GPU resources: one handle type per kind, pooled records, ownership and leak tracking. Every resource created through Resources is tracked in a generational pool for its kind, so a BufferHandle cannot be passed where a TextureHandle is expected (compile error), stale handles fail validation (raise), and live_counts() reports exactly what is still alive. Check live_counts().total() == 0 before teardown to assert a clean shutdown. __deinit__ releases everything left and, when anything was left, appends one gfx.resources warning LogRecord with those counts to Gfx.diagnostics (read it with Gfx.take_diagnostics ). Ownership rules: a pool owns every object it"},{"t":"cinder.gfx API","s":"API reference","h":"struct BufferTag","u":"/docs/api/gfx/#struct-buffertag","x":"mojo struct BufferTag Phantom tag that makes BufferHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct TextureTag","u":"/docs/api/gfx/#struct-texturetag","x":"mojo struct TextureTag Phantom tag that makes TextureHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct TextureViewTag","u":"/docs/api/gfx/#struct-textureviewtag","x":"mojo struct TextureViewTag Phantom tag that makes TextureViewHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct SamplerTag","u":"/docs/api/gfx/#struct-samplertag","x":"mojo struct SamplerTag Phantom tag that makes SamplerHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct ShaderTag","u":"/docs/api/gfx/#struct-shadertag","x":"mojo struct ShaderTag Phantom tag that makes ShaderHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct BindGroupLayoutTag","u":"/docs/api/gfx/#struct-bindgrouplayouttag","x":"mojo struct BindGroupLayoutTag Phantom tag that makes BindGroupLayoutHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct PipelineLayoutTag","u":"/docs/api/gfx/#struct-pipelinelayouttag","x":"mojo struct PipelineLayoutTag Phantom tag that makes PipelineLayoutHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct BindGroupTag","u":"/docs/api/gfx/#struct-bindgrouptag","x":"mojo struct BindGroupTag Phantom tag that makes BindGroupHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct RenderPipelineTag","u":"/docs/api/gfx/#struct-renderpipelinetag","x":"mojo struct RenderPipelineTag Phantom tag that makes RenderPipelineHandle a distinct type. Fields:"},{"t":"cinder.gfx API","s":"API reference","h":"struct ComputePipelineTag","u":"/docs/api/gfx/#struct-computepipelinetag","x":"mojo struct ComputePipelineTag Phantom tag that makes ComputePipelineHandle a distinct type. Fields: comptime BufferHandle = Handle[BufferTag] — Generational handle to a WGPUBuffer owned by Resources.buffers . comptime TextureHandle = Handle[TextureTag] — Generational handle to a WGPUTexture owned by Resources.textures . comptime TextureViewHandle = Handle[TextureViewTag] — Generational handle to a WGPUTextureView in Resources.texture_views (owned or imported). comptime SamplerHandle = Handle[SamplerTag] — Generational handle to a WGPUSampler owned by Resources.samplers . comptime ShaderHandle = Handle[ShaderTag] — Generational handle to a WGPUShaderModule owned by Resources.shaders . compti"},{"t":"cinder.gfx API","s":"API reference","h":"struct BufferDesc","u":"/docs/api/gfx/#struct-bufferdesc","x":"mojo struct BufferDesc ( Copyable , Movable ) What to create a buffer with. usage is a WGPUBufferUsage bit set. Fields: label : String size : UInt64 usage : UInt64 mapped_at_creation : Bool"},{"t":"cinder.gfx API","s":"API reference","h":"struct TextureDesc","u":"/docs/api/gfx/#struct-texturedesc","x":"mojo struct TextureDesc ( Copyable , Movable ) A 2D texture (array layers and 3D arrive with the renderer). usage is WGPUTextureUsage bits. Fields: label : String width : UInt32 height : UInt32 depth_or_layers : UInt32 mip_levels : UInt32 sample_count : UInt32 format : WGPUTextureFormat usage : UInt64"},{"t":"cinder.gfx API","s":"API reference","h":"struct SamplerDesc","u":"/docs/api/gfx/#struct-samplerdesc","x":"mojo struct SamplerDesc ( Copyable , Movable ) Filtering and addressing. linear selects trilinear filtering; otherwise nearest. Fields: label : String linear : Bool address_mode : UInt32 max_anisotropy : UInt16"},{"t":"cinder.gfx API","s":"API reference","h":"struct BufferRecord","u":"/docs/api/gfx/#struct-bufferrecord","x":"mojo struct BufferRecord ( Copyable , Movable ) Pool entry for a buffer: the WGPUBuffer plus the size and usage it was created with. Copies handed out by Resources.buffer share the raw object; only the pool releases it. Fields: raw : WGPUBuffer size : UInt64 usage : UInt64"},{"t":"cinder.gfx API","s":"API reference","h":"struct TextureRecord","u":"/docs/api/gfx/#struct-texturerecord","x":"mojo struct TextureRecord ( Copyable , Movable ) Pool entry for a texture: the WGPUTexture plus the extent, mip count, format and usage it was created with. Copies share the raw object; only the pool releases it. Fields: raw : WGPUTexture width : UInt32 height : UInt32 depth_or_layers : UInt32 mip_levels : UInt32 format : WGPUTextureFormat usage : UInt64"},{"t":"cinder.gfx API","s":"API reference","h":"struct TextureViewRecord","u":"/docs/api/gfx/#struct-textureviewrecord","x":"mojo struct TextureViewRecord ( Copyable , Movable ) Pool entry for a view: the WGPUTextureView and the texture it views. texture is null for views wrapped by import_texture_view , whose raw object the caller owns ( is_imported ). Fields: raw : WGPUTextureView texture : TextureHandle is_imported is_imported(self) -> Bool True for a view wrapped by import_texture_view : Resources never releases it."},{"t":"cinder.gfx API","s":"API reference","h":"struct SamplerRecord","u":"/docs/api/gfx/#struct-samplerrecord","x":"mojo struct SamplerRecord ( Copyable , Movable ) Pool entry for a sampler: the WGPUSampler, released by Resources.destroy_sampler . Fields: raw : WGPUSampler"},{"t":"cinder.gfx API","s":"API reference","h":"struct ShaderRecord","u":"/docs/api/gfx/#struct-shaderrecord","x":"mojo struct ShaderRecord ( Copyable , Movable ) Pool entry for a shader: the WGPUShaderModule and the WGSL path it was compiled from. ShaderManager swaps raw in place on hot reload, keeping the handle stable. Fields: raw : WGPUShaderModule source_path : String"},{"t":"cinder.gfx API","s":"API reference","h":"struct BindGroupLayoutRecord","u":"/docs/api/gfx/#struct-bindgrouplayoutrecord","x":"mojo struct BindGroupLayoutRecord ( Copyable , Movable ) Pool entry for a bind group layout: the WGPUBindGroupLayout the pool owns. Fields: raw : WGPUBindGroupLayout"},{"t":"cinder.gfx API","s":"API reference","h":"struct PipelineLayoutRecord","u":"/docs/api/gfx/#struct-pipelinelayoutrecord","x":"mojo struct PipelineLayoutRecord ( Copyable , Movable ) Pool entry for a pipeline layout: the WGPUPipelineLayout the pool owns. Fields: raw : WGPUPipelineLayout"},{"t":"cinder.gfx API","s":"API reference","h":"struct BindGroupRecord","u":"/docs/api/gfx/#struct-bindgrouprecord","x":"mojo struct BindGroupRecord ( Copyable , Movable ) Pool entry for a bind group: the WGPUBindGroup the pool owns. Fields: raw : WGPUBindGroup"},{"t":"cinder.gfx API","s":"API reference","h":"struct RenderPipelineRecord","u":"/docs/api/gfx/#struct-renderpipelinerecord","x":"mojo struct RenderPipelineRecord ( Copyable , Movable ) Pool entry for a render pipeline: the WGPURenderPipeline the pool owns. Fields: raw : WGPURenderPipeline"},{"t":"cinder.gfx API","s":"API reference","h":"struct ComputePipelineRecord","u":"/docs/api/gfx/#struct-computepipelinerecord","x":"mojo struct ComputePipelineRecord ( Copyable , Movable ) Pool entry for a compute pipeline: the WGPUComputePipeline the pool owns. Fields: raw : WGPUComputePipeline"},{"t":"cinder.gfx API","s":"API reference","h":"struct LiveCounts","u":"/docs/api/gfx/#struct-livecounts","x":"mojo struct LiveCounts ( ImplicitlyCopyable , Movable , Writable ) Resources alive per kind. Fields: buffers : Int textures : Int texture_views : Int samplers : Int shaders : Int bind_group_layouts : Int pipeline_layouts : Int bind_groups : Int render_pipelines : Int compute_pipelines : Int total total(self) -> Int Sum over every kind; zero means nothing is left to release. write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx API","s":"API reference","h":"struct Resources","u":"/docs/api/gfx/#struct-resources","x":"mojo struct Resources ( Movable ) Pools of every GPU resource kind, owning the wgpu objects behind typed handles. Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] buffers : Pool[BufferRecord, BufferTag] textures : Pool[TextureRecord, TextureTag] texture_views : Pool[TextureViewRecord, TextureViewTag] samplers : Pool[SamplerRecord, SamplerTag] shaders : Pool[ShaderRecord, ShaderTag] bind_group_layouts : Pool[BindGroupLayoutRecord, BindGroupLayoutTag] pipeline_layouts : Pool[PipelineLayoutRecord, PipelineLayoutTag] bind_groups : Pool[BindGroupRecord, BindGroupTag] render_pipelines : Pool[RenderPipelineRecord, RenderPipelineTag] compute_pipelines : Pool[ComputePipelineRecord, ComputePipelineTag] "},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.shaders","u":"/docs/api/gfx/#cindergfxshaders","x":"Source: cinder/gfx/shaders.mojo ShaderManager: WGSL modules from files, compile diagnostics with file:line:column, hot reload. Every loaded shader remembers its path and modification time. check_for_changes recompiles files that changed on disk; a successful recompile swaps the module behind the same ShaderHandle and reports the handle so pipeline owners rebuild, a failed one keeps the last good module and reports the error. Compile messages come from wgpu's async compilation-info callback through the shim queue. comptime NoChain = Optional[Pointer[WGPUChainedStruct, MutUntrackedOrigin]] — Null nextInChain for wgpu descriptors that carry no chained extension struct."},{"t":"cinder.gfx API","s":"API reference","h":"struct ShaderMessage","u":"/docs/api/gfx/#struct-shadermessage","x":"mojo struct ShaderMessage ( Copyable , Movable , Writable ) One compiler diagnostic, already formatted with its source location. Fields: path : String line : Int column : Int is_error : Bool text : String write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx API","s":"API reference","h":"struct ShaderError","u":"/docs/api/gfx/#struct-shadererror","x":"mojo struct ShaderError ( Copyable , Movable , Writable ) Raised when a WGSL file fails to compile. messages holds every diagnostic. Fields: path : String messages : List[ShaderMessage] write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx API","s":"API reference","h":"struct ShaderSource","u":"/docs/api/gfx/#struct-shadersource","x":"mojo struct ShaderSource ( Copyable , Movable ) A loaded shader file and its on-disk state. Fields: handle : ShaderHandle path : String mtime_ns : Int reload_failures : Int"},{"t":"cinder.gfx API","s":"API reference","h":"struct ReloadResult","u":"/docs/api/gfx/#struct-reloadresult","x":"mojo struct ReloadResult ( Copyable , Movable ) Outcome of check_for_changes for one changed file. Fields: handle : ShaderHandle path : String success : Bool"},{"t":"cinder.gfx API","s":"API reference","h":"def parse_wgsl_report","u":"/docs/api/gfx/#def-parse_wgsl_report","x":"mojo def parse_wgsl_report (path: String, report: String) -> List[ShaderMessage] Turns a naga validation report into messages with locations. naga marks each location as wgsl:LINE:COLUMN ; every such mark becomes one error message carrying the whole report text. A report with no mark yields one message at line 0."},{"t":"cinder.gfx API","s":"API reference","h":"struct ShaderManager","u":"/docs/api/gfx/#struct-shadermanager","x":"mojo struct ShaderManager ( Movable ) Owns the list of shader files; the modules themselves live in Resources . Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] resources : Pointer[Resources, MutUntrackedOrigin] sources : List[ShaderSource] diagnostics : List[LogRecord] __init__ __init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], resources: Pointer[Resources, MutUntrackedOrigin], ) compile_source compile_source( mut self, path: String, source: String ) raises -> WGPUShaderModule Compiles WGSL text and returns the module, or raises ShaderError with every diagnostic. wgpu-native 29 does not implement the compilation-info callback, so the module is created inside a validation error scope; "},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.swapchain","u":"/docs/api/gfx/#cindergfxswapchain","x":"Source: cinder/gfx/swapchain.mojo Swapchain: surface configuration, per-frame texture acquisition and presentation. Handles the three real-world cases a frame loop meets: resize (reconfigure on the next acquire), minimise (zero-sized window; frames are skipped until it is restored) and surface loss/outdated (reconfigure and retry once, then report). Device loss is Gfx.lost and is the caller's decision. comptime NoChain = Optional[Pointer[WGPUChainedStruct, MutUntrackedOrigin]] — Null nextInChain for wgpu descriptors that carry no chained extension struct."},{"t":"cinder.gfx API","s":"API reference","h":"struct SurfaceFrame","u":"/docs/api/gfx/#struct-surfaceframe","x":"mojo struct SurfaceFrame ( Movable ) The acquired swapchain texture for one frame. present consumes it. Fields: texture : WGPUTexture view : WGPUTextureView suboptimal : Bool width : UInt32 height : UInt32"},{"t":"cinder.gfx API","s":"API reference","h":"struct Swapchain","u":"/docs/api/gfx/#struct-swapchain","x":"mojo struct Swapchain ( Movable ) Owns the configuration of a wgpu surface created by Window.create_surface . Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] surface : WGPUSurface format : WGPUTextureFormat usage : WGPUTextureUsage width : UInt32 height : UInt32 present_mode : WGPUPresentMode configured : Bool needs_reconfigure : Bool frames_skipped : Int — Frames skipped because the window had no area (minimised). reconfigures : Int — Reconfigurations performed (resize, outdated or lost surfaces). __init__ __init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], surface: WGPUSurface, width: Int, height: Int, prefer_srgb: Bool = True, readback: Bool = False, ) raises GfxError Picks a surfac"},{"t":"cinder.gfx API","s":"API reference","h":"cinder.gfx.util","u":"/docs/api/gfx/#cindergfxutil","x":"Source: cinder/gfx/util.mojo Small conversions shared by the gfx modules."},{"t":"cinder.gfx API","s":"API reference","h":"def string_view","u":"/docs/api/gfx/#def-string_view","x":"mojo def string_view ( ref text: String) -> WGPUStringView A non-owning WGPUStringView over text . text must outlive every use of the view."},{"t":"cinder.gfx API","s":"API reference","h":"def empty_string_view","u":"/docs/api/gfx/#def-empty_string_view","x":"mojo def empty_string_view () -> WGPUStringView A WGPUStringView with a null pointer and zero length, for absent labels and strings."},{"t":"cinder.gfx API","s":"API reference","h":"def c_message","u":"/docs/api/gfx/#def-c_message","x":"mojo def c_message (ev: CnEvent) -> String The NUL-terminated message of a shim event as a String, decoded as UTF-8 (invalid sequences become U+FFFD). Reads at most the 1000-byte message buffer."},{"t":"cinder.gfx API","s":"API reference","h":"def zero_event","u":"/docs/api/gfx/#def-zero_event","x":"mojo def zero_event () -> CnEvent An all-zero CnEvent, used as the out-parameter that cn_event_queue_pop fills."},{"t":"cinder.gfx API","s":"API reference","h":"def ptr_to","u":"/docs/api/gfx/#def-ptr_to","x":"mojo def ptr_to [ T: AnyType ]( ref value: T) -> Optional[Pointer[T, MutUntrackedOrigin]] Raw pointer for a C call. The pointee must stay alive past the call ( keep it)."},{"t":"cinder.gfx API","s":"API reference","h":"def opaque_to","u":"/docs/api/gfx/#def-opaque_to","x":"mojo def opaque_to [ T: AnyType ]( ref value: T) -> Optional[OpaquePointer[MutUntrackedOrigin]] ptr_to for void* parameters."},{"t":"cinder.gfx API","s":"API reference","h":"def handle_from","u":"/docs/api/gfx/#def-handle_from","x":"mojo def handle_from [ T: AnyType ](address: UInt64) -> Optional[Pointer[T, MutUntrackedOrigin]] Rebuilds a wgpu object handle delivered through a CnEvent."},{"t":"cinder.input API","s":"API reference","h":"cinder.input","u":"/docs/api/input/","x":""},{"t":"cinder.input API","s":"API reference","h":"cinder.input","u":"/docs/api/input/#cinderinput-1","x":"Source: cinder/input/__init__.mojo Input: action and axis mapping over keyboard, mouse and gamepad, rebindable and saved to disk."},{"t":"cinder.input API","s":"API reference","h":"cinder.input.map","u":"/docs/api/input/#cinderinputmap","x":"Source: cinder/input/map.mojo Input mapping: named actions (buttons) and axes over keyboard, mouse and gamepad, rebindable at runtime and saved as a diffable text file. Feed every platform Event to process , call end_frame once per frame (it clears the just-pressed and just-released edges and the per-frame mouse deltas), and query by name. An axis sums its bindings: a key contributes scale while held, an analog source its value times scale after the dead zone. listen arms a capture: the next key, button or axis event becomes the binding, which is how a settings menu rebinds a control. comptime DEVICE_KEY = 0 — Keyboard source; code is an SDL scancode (0-511 are tracked). comptime DEVICE_MOUS"},{"t":"cinder.input API","s":"API reference","h":"struct Binding","u":"/docs/api/input/#struct-binding","x":"mojo struct Binding ( Equatable , ImplicitlyCopyable , Movable , Writable ) One source: device and its code (scancode, button or axis index). scale multiplies the contribution to an axis (digital sources contribute scale while held), dead_zone applies to analog sources. Fields: device : Int code : Int scale : Float32 dead_zone : Float32 key key(scancode: Int, scale: Float32 = 1.0) -> Binding A keyboard binding for SDL scancode scancode . mouse_button mouse_button(button: Int, scale: Float32 = 1.0) -> Binding A mouse button binding (1 = left, 2 = middle, 3 = right). mouse_axis mouse_axis(axis: Int, scale: Float32 = 1.0) -> Binding A mouse axis binding: 0 = x delta, 1 = y delta, any other code"},{"t":"cinder.input API","s":"API reference","h":"struct InputMap","u":"/docs/api/input/#struct-inputmap","x":"mojo struct InputMap ( Movable ) Named actions and axes with their bindings, plus the device state they are evaluated against. Text format ( to_text , from_text ): a cinder-input 1 header, then one action NAME DEVICE CODE SCALE DEAD_ZONE or axis NAME ... line per binding, where DEVICE is key , mouse_button , mouse_axis , gamepad_button or gamepad_axis ; an entry without bindings is a bare action NAME or axis NAME line. Fields are separated by single spaces, so names must not contain spaces. Fields: actions : List[_Action] axes : List[_Axis] keys_down : List[Bool] mouse_buttons_down : List[Bool] gamepad_buttons_down : List[Bool] gamepad_axes : List[Float32] mouse_delta_x : Float32 mouse_delta"},{"t":"cinder.physics API","s":"API reference","h":"cinder.physics","u":"/docs/api/physics/","x":""},{"t":"cinder.physics API","s":"API reference","h":"cinder.physics","u":"/docs/api/physics/#cinderphysics-1","x":"Source: cinder/physics/__init__.mojo Physics over Jolt: rigid-body world, queries, character controller and ECS synchronisation."},{"t":"cinder.physics API","s":"API reference","h":"cinder.physics.character","u":"/docs/api/physics/#cinderphysicscharacter","x":"Source: cinder/physics/character.mojo Character controller over Jolt's CharacterVirtual: slopes, steps and moving platforms. The character is a capsule whose position is at its feet. update applies the engine's standard velocity rules (stick to the ground and inherit its velocity, otherwise fall under gravity), then runs Jolt's extended update which walks stairs up to step_height and sticks to the floor when walking down. Contacts with dynamic bodies push them with the character's mass. comptime PI_F32: Float32 = 3.14159265358979 — Pi as Float32 , for converting max_slope_degrees to radians."},{"t":"cinder.physics API","s":"API reference","h":"struct GroundState","u":"/docs/api/physics/#struct-groundstate","x":"mojo struct GroundState ( Equatable , ImplicitlyCopyable , Movable , Writable ) Ground contact state of a CharacterController , mirrored from Jolt's ground state. Fields: value : UInt8 on_ground on_ground() -> GroundState Standing on a surface no steeper than max_slope_degrees . on_steep_ground on_steep_ground() -> GroundState Touching a surface steeper than max_slope_degrees ; update treats it like the air and applies gravity. not_supported not_supported() -> GroundState Touching something that cannot hold the character (its edge, a falling body). in_air in_air() -> GroundState Touching nothing; update applies gravity. write_to write_to(self, mut w: Some[Writer])"},{"t":"cinder.physics API","s":"API reference","h":"struct CharacterDesc","u":"/docs/api/physics/#struct-characterdesc","x":"mojo struct CharacterDesc ( Copyable , Movable ) Parameters of a CharacterController in metres, kilograms and degrees. radius and height size the capsule ( height must exceed 2 * radius + 0.01 ); max_slope_degrees is the steepest walkable slope and step_height the tallest step walked up; mass and max_strength set how hard dynamic bodies are pushed; layer is the object layer the character collides as; gravity (m/s^2, default -9.81 on Y) applies while it is not standing on ground. Fields: radius : Float32 height : Float32 — Total standing height including both hemispheres. max_slope_degrees : Float32 step_height : Float32 mass : Float32 layer : Int gravity : Vec3 max_strength : Float32 — Maxim"},{"t":"cinder.physics API","s":"API reference","h":"struct CharacterController","u":"/docs/api/physics/#struct-charactercontroller","x":"mojo struct CharacterController ( Movable ) A capsule character over Jolt's CharacterVirtual, positioned at its feet. It is not a rigid body: it has no body id. It creates and owns its capsule shape and counts itself in world.live_characters ; its destructor destroys the character and the shape and decrements the count. It keeps a raw pointer to the PhysicsWorld , so the world must not move and must outlive the controller; use it from the thread that steps the world. The constructor raises when height does not exceed 2 * radius + 0.01 or Jolt cannot create the character. Fields: world : Pointer[PhysicsWorld, MutUntrackedOrigin] desc : CharacterDesc shape : Shape character : Optional[Pointer["},{"t":"cinder.physics API","s":"API reference","h":"cinder.physics.sync","u":"/docs/api/physics/#cinderphysicssync","x":"Source: cinder/physics/sync.mojo ECS <-> physics synchronisation with interpolation. RigidBody links an entity to a Jolt body and stores the two most recent simulated poses. The fixed-step loop is: push_kinematics(world, physics, dt) # moved LocalTransforms drive kinematic bodies physics.step(dt) pull_bodies(world, physics) # dynamic bodies: previous <- current <- Jolt ... write_transforms(world, alpha) # every frame: LocalTransform at alpha between the poses Kinematic bodies follow the entity; dynamic bodies drive it; static bodies are left alone. comptime RIGID_BODY_COMPONENT_ID = 7 — ECS component id of RigidBody ."},{"t":"cinder.physics API","s":"API reference","h":"struct RigidBody","u":"/docs/api/physics/#struct-rigidbody","x":"mojo struct RigidBody ( Component ) The physics side of an entity. scale is preserved when the body writes the transform (Jolt bodies carry no scale). Fields: body : UInt32 motion : MotionType interpolate : Bool previous_position : Vec3 previous_rotation : Quat position : Vec3 rotation : Quat scale : Vec3 last_pushed_tick : UInt32 — Change tick of the LocalTransform last pushed to a kinematic body. __init__ __init__( out self, body: UInt32, motion: MotionType, position: Vec3, rotation: Quat, scale: Vec3 = Vec3.one(), interpolate: Bool = True, ) component_id component_id() -> Int"},{"t":"cinder.physics API","s":"API reference","h":"struct PhysicsSync","u":"/docs/api/physics/#struct-physicssync","x":"mojo struct PhysicsSync ( Movable ) Per-frame physics <-> ECS synchronisation with persistent query specs and scratch buffers, so steady-state frames allocate nothing. The free functions below are convenience wrappers that build a temporary instance. Fields: __init__ __init__(out self) pull_bodies pull_bodies( mut self, mut world: World, physics: PhysicsWorld ) raises -> Int After a physics step: copies each dynamic body's pose into its RigidBody , keeping the previous pose for interpolation. Returns the number of bodies read. push_kinematics push_kinematics( mut self, mut world: World, physics: PhysicsWorld, dt: Float32 ) raises -> Int Before a physics step: kinematic bodies whose LocalTran"},{"t":"cinder.physics API","s":"API reference","h":"def pull_bodies","u":"/docs/api/physics/#def-pull_bodies","x":"mojo def pull_bodies ( mut world: World, physics: PhysicsWorld) raises -> Int PhysicsSync.pull_bodies through a temporary PhysicsSync , so it allocates query scratch on every call. Returns the number of dynamic bodies read."},{"t":"cinder.physics API","s":"API reference","h":"def push_kinematics","u":"/docs/api/physics/#def-push_kinematics","x":"mojo def push_kinematics ( mut world: World, physics: PhysicsWorld, dt: Float32 ) raises -> Int PhysicsSync.push_kinematics through a temporary PhysicsSync , so it allocates query scratch on every call. Returns the number of kinematic bodies moved."},{"t":"cinder.physics API","s":"API reference","h":"def write_transforms","u":"/docs/api/physics/#def-write_transforms","x":"mojo def write_transforms ( mut world: World, alpha: Float32) raises -> Int PhysicsSync.write_transforms through a temporary PhysicsSync , so it allocates scratch on every call. Returns the number of transforms written."},{"t":"cinder.physics API","s":"API reference","h":"cinder.physics.world","u":"/docs/api/physics/#cinderphysicsworld","x":"Source: cinder/physics/world.mojo Rigid-body world over Jolt (joltc): layers, shapes, bodies, queries and queued events. Every Jolt object created here is counted; live_objects() must be zero before the world is destroyed. The destructor does not abort on leaks (an abort would hide the real failure of a test that raised): it prints an ERROR record naming the leaked counts ( leak_report() ), and tests and App.live_objects() assert zero explicitly. Destroying a body or shape the world does not know (twice, or an invalid id) leaves the counts alone and records an ERROR in diagnostics . Jolt runs its own thread pool for the simulation step; its listeners only post to the shim queue ( ADR-0003 ) "},{"t":"cinder.physics API","s":"API reference","h":"def to_jolt","u":"/docs/api/physics/#def-to_jolt","x":"mojo def to_jolt (v: Vec3) -> JPH_Vec3 Converts an engine Vec3 to a joltc JPH_Vec3 ."},{"t":"cinder.physics API","s":"API reference","h":"def from_jolt","u":"/docs/api/physics/#def-from_jolt","x":"mojo def from_jolt (v: JPH_Vec3 ) -> Vec3 Converts a joltc JPH_Vec3 to an engine Vec3 ."},{"t":"cinder.physics API","s":"API reference","h":"def quat_to_jolt","u":"/docs/api/physics/#def-quat_to_jolt","x":"mojo def quat_to_jolt (q: Quat) -> JPH_Quat Converts an engine Quat to a joltc JPH_Quat (x, y, z, w)."},{"t":"cinder.physics API","s":"API reference","h":"def quat_from_jolt","u":"/docs/api/physics/#def-quat_from_jolt","x":"mojo def quat_from_jolt (q: JPH_Quat ) -> Quat Converts a joltc JPH_Quat to an engine Quat ."},{"t":"cinder.physics API","s":"API reference","h":"def mat_to_jolt","u":"/docs/api/physics/#def-mat_to_jolt","x":"mojo def mat_to_jolt (m: Mat4) -> JPH_Mat4 Column-major on both sides."},{"t":"cinder.physics API","s":"API reference","h":"struct LayerConfig","u":"/docs/api/physics/#struct-layerconfig","x":"mojo struct LayerConfig ( Copyable , Movable ) Object layers and which pairs collide. Layer i is bit i in query masks. Layers flagged static go to a separate broad-phase layer so static geometry is never tested against itself. Fields: count : Int static_mask : UInt32 pairs : List[Tuple[Int, Int]] __init__ __init__(out self, count: Int, static_mask: UInt32 = 1) collide collide(mut self, a: Int, b: Int) Enables collision between layers a and b (symmetric). default default() -> LayerConfig Two layers: 0 static, 1 moving; moving collides with both."},{"t":"cinder.physics API","s":"API reference","h":"struct MotionType","u":"/docs/api/physics/#struct-motiontype","x":"mojo struct MotionType ( Equatable , ImplicitlyCopyable , Movable ) How a body moves: static, kinematic (driven by the caller) or dynamic (simulated). Fields: value : UInt8 static static() -> MotionType A body that never moves. kinematic kinematic() -> MotionType A body moved by the caller ( move_kinematic , set_position_rotation ), not by forces or contacts. dynamic dynamic() -> MotionType A body simulated by Jolt: gravity, forces, impulses and contacts move it. to_jolt to_jolt(self) -> JPH_MotionType The matching joltc JPH_MotionType ; any value other than static or kinematic maps to dynamic."},{"t":"cinder.physics API","s":"API reference","h":"struct Shape","u":"/docs/api/physics/#struct-shape","x":"mojo struct Shape ( ImplicitlyCopyable , Movable ) A reference-counted Jolt shape that the world tracks; copy freely, destroy once. Fields: ptr : Optional[Pointer[JPH_Shape, MutUntrackedOrigin]]"},{"t":"cinder.physics API","s":"API reference","h":"struct BodyDesc","u":"/docs/api/physics/#struct-bodydesc","x":"mojo struct BodyDesc ( Copyable , Movable ) Creation parameters for PhysicsWorld.create_body . The constructor takes the shape, position (metres), motion type, object layer and rotation; the other fields keep their defaults until set: friction 0.5, restitution 0, linear and angular damping 0.05, gravity factor 1, not a sensor, discrete motion quality, sleeping allowed, user data 0, zero initial linear velocity (ignored for static bodies) and mass 0. The shape is borrowed: the caller still destroys it with destroy_shape . Fields: shape : Shape position : Vec3 rotation : Quat motion : MotionType layer : Int friction : Float32 restitution : Float32 linear_damping : Float32 angular_damping : Flo"},{"t":"cinder.physics API","s":"API reference","h":"struct PhysicsEventKind","u":"/docs/api/physics/#struct-physicseventkind","x":"mojo struct PhysicsEventKind ( Equatable , ImplicitlyCopyable , Movable , Writable ) Kind of a PhysicsEvent ; writes as its snake_case name. Fields: value : UInt8 contact_added contact_added() -> PhysicsEventKind Two bodies started touching. contact_persisted contact_persisted() -> PhysicsEventKind Two bodies that were touching are still touching. contact_removed contact_removed() -> PhysicsEventKind Two bodies stopped touching; the event carries only the body ids. body_activated body_activated() -> PhysicsEventKind A body woke up; the event carries the body and its user_data . body_deactivated body_deactivated() -> PhysicsEventKind A body went to sleep; the event carries the body and its us"},{"t":"cinder.physics API","s":"API reference","h":"struct PhysicsEvent","u":"/docs/api/physics/#struct-physicsevent","x":"mojo struct PhysicsEvent ( ImplicitlyCopyable , Movable ) One queued Jolt event. Contacts: body1 / body2 , world normal (from body 1 to body 2), first contact point on body 1 and the manifold contact_count . Activation: body1 and the body's user_data . Fields: kind : PhysicsEventKind body1 : UInt32 body2 : UInt32 normal : Vec3 point : Vec3 contact_count : Int user_data : UInt64 involves involves(self, body: UInt32) -> Bool Whether body is body1 or body2 . pair pair(self, a: UInt32, b: UInt32) -> Bool Whether the event is between bodies a and b , in either order."},{"t":"cinder.physics API","s":"API reference","h":"struct RayHit","u":"/docs/api/physics/#struct-rayhit","x":"mojo struct RayHit ( ImplicitlyCopyable , Movable ) Closest hit of PhysicsWorld.raycast : the body id, fraction of the ray (the hit is at origin + fraction * direction ), the world point and the hit surface normal . Fields: body : UInt32 fraction : Float32 point : Vec3 normal : Vec3"},{"t":"cinder.physics API","s":"API reference","h":"struct ShapeHit","u":"/docs/api/physics/#struct-shapehit","x":"mojo struct ShapeHit ( ImplicitlyCopyable , Movable ) Closest hit of PhysicsWorld.shape_cast : the body id, fraction of the sweep, the contact point on the hit body, that body's surface normal , and penetration_depth , which is greater than zero when the shape already overlaps at the start. Fields: body : UInt32 fraction : Float32 point : Vec3 normal : Vec3 penetration_depth : Float32"},{"t":"cinder.physics API","s":"API reference","h":"struct PhysicsWorld","u":"/docs/api/physics/#struct-physicsworld","x":"mojo struct PhysicsWorld ( Movable ) One Jolt physics system with its thread pool, layer filters and event queue. Shapes are Shape handles that the caller destroys once with destroy_shape ; bodies are UInt32 ids that the caller destroys with destroy_body . Shapes, bodies and characters are counted in live_objects() , which should be zero before the world is destroyed; the destructor prints leak_report() when it is not. Destroying an unknown shape or body is ignored and recorded as an ERROR in diagnostics . The world must not move while a CharacterController points at it, and the NativePtr library must outlive it. Call every method from one thread (the fixed-step thread); Jolt's workers run o"},{"t":"cinder.platform API","s":"API reference","h":"cinder.platform","u":"/docs/api/platform/","x":""},{"t":"cinder.platform API","s":"API reference","h":"cinder.platform","u":"/docs/api/platform/#cinderplatform-1","x":"Source: cinder/platform/__init__.mojo Platform layer: the native library, SDL3 windows, input events and wgpu surfaces. This is the only engine module with per-operating-system branches (library file names, surface creation inside the shim). Everything above it is platform-neutral."},{"t":"cinder.platform API","s":"API reference","h":"cinder.platform.events","u":"/docs/api/platform/#cinderplatformevents","x":"Source: cinder/platform/events.mojo Platform events in engine terms, translated from SDL3's event union."},{"t":"cinder.platform API","s":"API reference","h":"struct EventKind","u":"/docs/api/platform/#struct-eventkind","x":"mojo struct EventKind ( Equatable , ImplicitlyCopyable , RegisterPassable , Writable ) What an Event describes. Fields: value : UInt8 none none() -> EventKind No event: an SDL event this layer does not translate ( Platform.poll drops it). quit quit() -> EventKind The application was asked to quit (SDL_EVENT_QUIT). window_close window_close() -> EventKind The user asked to close the window window_id . window_resized window_resized() -> EventKind Pixel size changed; a / b carry the new width and height. window_minimized window_minimized() -> EventKind Window window_id was minimised. window_restored window_restored() -> EventKind Window window_id was restored. window_focus_gained window_focus_g"},{"t":"cinder.platform API","s":"API reference","h":"struct Event","u":"/docs/api/platform/#struct-event","x":"mojo struct Event ( ImplicitlyCopyable , RegisterPassable , Writable ) One translated platform event. Fields are interpreted per kind (see EventKind docstrings). Fields: kind : EventKind window_id : UInt32 device : UInt32 a : Int32 b : Int32 scancode : UInt32 keycode : UInt32 mods : UInt16 button : UInt8 axis : UInt8 down : Bool repeat : Bool x : Float32 y : Float32 dx : Float32 dy : Float32 value : Float32 empty empty(kind: EventKind) -> Event An event of kind with every other field zero or False. write_to write_to(self, mut writer: Some[Writer])"},{"t":"cinder.platform API","s":"API reference","h":"def translate_event","u":"/docs/api/platform/#def-translate_event","x":"mojo def translate_event (ev: SDL_Event ) -> Event Translates an SDL event into an Event ; unknown SDL events become EventKind.none() ."},{"t":"cinder.platform API","s":"API reference","h":"cinder.platform.native","u":"/docs/api/platform/#cinderplatformnative","x":"Source: cinder/platform/native.mojo Loading libcinder_native exactly once and sharing it by pointer. NativeLibrary keeps the generated CinderNative struct (library handle plus every resolved symbol) at a stable heap address for the life of the program. Subsystems hold a NativePtr to it and never copy function pointers out (docs/MOJO_NOTES.md: the handle must outlive every call). comptime NativePtr = Pointer[CinderNative, MutUntrackedOrigin] — How subsystems refer to the loaded native library. comptime LIBRARY_PATH_ENV = \"CINDER_NATIVE_LIB\" — Environment variable that, when set and non-empty, replaces the library path default_library_path returns."},{"t":"cinder.platform API","s":"API reference","h":"def default_library_path","u":"/docs/api/platform/#def-default_library_path","x":"mojo def default_library_path () -> String The library path for this OS relative to the repository root, unless CINDER_NATIVE_LIB is set."},{"t":"cinder.platform API","s":"API reference","h":"struct NativeLibrary","u":"/docs/api/platform/#struct-nativelibrary","x":"mojo struct NativeLibrary ( Movable ) Owns the one loaded copy of libcinder_native. Fields: __init__ __init__(out self, path: String) raises Loads the library at path , resolving every bound symbol. Raises when the library is missing, a symbol is missing, or the shim's ABI version differs from the bindings'. __init__ __init__(out self) raises Loads the library from default_library_path() . ptr ptr(self) -> NativePtr Pointer to the loaded bindings, valid only while this NativeLibrary lives: it must be destroyed after every subsystem holding the pointer."},{"t":"cinder.platform API","s":"API reference","h":"cinder.platform.window","u":"/docs/api/platform/#cinderplatformwindow","x":"Source: cinder/platform/window.mojo SDL3 platform: initialisation, windows, event polling, gamepads and wgpu surface creation. comptime SdlWindowPtr = Optional[Pointer[SDL_Window, MutUntrackedOrigin]] — Nullable pointer to an SDL window. comptime EventQueuePtr = Optional[Pointer[CnEventQueue, MutUntrackedOrigin]] — Nullable pointer to a shim event queue (the platform's SDL log queue)."},{"t":"cinder.platform API","s":"API reference","h":"struct GamepadSlot","u":"/docs/api/platform/#struct-gamepadslot","x":"mojo struct GamepadSlot ( ImplicitlyCopyable , Movable ) An open gamepad: SDL joystick id and the SDL_Gamepad handle. Fields: id : UInt32 handle : Optional[Pointer[SDL_Gamepad, MutUntrackedOrigin]]"},{"t":"cinder.platform API","s":"API reference","h":"struct Platform","u":"/docs/api/platform/#struct-platform","x":"mojo struct Platform ( Movable ) SDL initialised for video, events and gamepads. Create one, before any Window, on the main thread. SDL's log goes to a shim queue and is drained into diagnostics by poll . Fields: lib : NativePtr gamepads : List[GamepadSlot] diagnostics : List[LogRecord] — SDL log lines collected since the last take_diagnostics . forward_to_imgui : Bool — When set (by the debug UI), every raw SDL event is also handed to the ImGui backend. __init__ __init__(out self, lib: NativePtr) raises video_driver video_driver(self) -> String Name of the current SDL video driver; empty when SDL reports none. last_error last_error(self) -> String SDL's most recent error message ( SDL_GetEr"},{"t":"cinder.platform API","s":"API reference","h":"struct Window","u":"/docs/api/platform/#struct-window","x":"mojo struct Window ( Movable ) An SDL window. Destroyed with the struct; create the wgpu surface through create_surface . Fields: lib : NativePtr handle : SdlWindowPtr id : UInt32 __init__ __init__( out self, platform: Platform, title: String, width: Int, height: Int, resizable: Bool = True, hidden: Bool = False, ) raises pixel_size pixel_size(self) raises -> Tuple[Int, Int] Drawable size in pixels as (width, height); with high pixel density it can differ from the size the window was created with. Raises when SDL fails. set_size set_size(self, width: Int, height: Int) raises Requests a new window size and waits for it to apply ( SDL_SyncWindow , whose result is ignored); raises when SDL reje"},{"t":"cinder.render API","s":"API reference","h":"cinder.render","u":"/docs/api/render/","x":""},{"t":"cinder.render API","s":"API reference","h":"cinder.render","u":"/docs/api/render/#cinderrender-1","x":"Source: cinder/render/__init__.mojo Renderer: cameras, meshes, materials, textures, the frame graph and the forward+ pipeline. Everything here talks to the GPU only through cinder.gfx ."},{"t":"cinder.render API","s":"API reference","h":"cinder.render.camera","u":"/docs/api/render/#cinderrendercamera","x":"Source: cinder/render/camera.mojo Cameras and frustum culling. Right-handed, Y up, camera looks down -Z. Projection is reverse-Z with an infinite far plane, so the frustum has five planes. cull_spheres tests four spheres at a time with SIMD. comptime F4 = SIMD[DType.float32, 4] — Four float32 lanes: one coordinate of four spheres in Frustum.cull_spheres_into ."},{"t":"cinder.render API","s":"API reference","h":"struct Plane","u":"/docs/api/render/#struct-plane","x":"mojo struct Plane ( ImplicitlyCopyable , RegisterPassable ) normal . p + d >= 0 is the inside half-space. Fields: normal : Vec3 d : Float32 from_row from_row(v: Vec4) -> Plane Plane from a clip-space row combination, scaled so normal has unit length. distance distance(self, p: Vec3) -> Float32 Signed distance from p to the plane (positive inside), in the units of p ."},{"t":"cinder.render API","s":"API reference","h":"struct Frustum","u":"/docs/api/render/#struct-frustum","x":"mojo struct Frustum ( Copyable , Movable ) Left, right, bottom, top, near planes extracted from a view-projection matrix (Gribb/Hartmann). Reverse-Z with infinite far: the near plane is row3 - row2 (clip z <= w); no far plane. Fields: planes : Array[Plane, 5] __init__ __init__(out self, view_proj: Mat4) contains_sphere contains_sphere(self, centre: Vec3, radius: Float32) -> Bool True unless the sphere lies entirely behind one of the five planes. Conservative: a sphere near a frustum edge or corner may pass without intersecting it. contains_point contains_point(self, p: Vec3) -> Bool True when p is on the inside of all five planes (near, left, right, bottom, top). cull_spheres_into cull_spher"},{"t":"cinder.render API","s":"API reference","h":"struct Camera","u":"/docs/api/render/#struct-camera","x":"mojo struct Camera ( ImplicitlyCopyable , Movable ) A perspective camera. near is the only clip distance (infinite far). Fields: position : Vec3 rotation : Quat fov_y : Float32 aspect : Float32 near : Float32 __init__ __init__( out self, position: Vec3, rotation: Quat, fov_y: Float32 = 1.0471976, aspect: Float32 = 16.0 / 9.0, near: Float32 = 0.05, ) look_at look_at( eye: Vec3, target: Vec3, up: Vec3 = Vec3.unit_y(), fov_y: Float32 = 1.0471976, aspect: Float32 = 16.0 / 9.0, near: Float32 = 0.05, ) -> Camera A camera at eye looking at target , with its rotation taken from Mat4.look_at . up only orients the camera (the stored up() is re-orthogonalised); fov_y is the vertical field of view in ra"},{"t":"cinder.render API","s":"API reference","h":"cinder.render.frame_graph","u":"/docs/api/render/#cinderrenderframe_graph","x":"Source: cinder/render/frame_graph.mojo Frame graph: declared passes, transient textures with lifetimes and aliasing, automatic ordering. The graph is rebuilt every frame (cheap: a few passes). compile orders passes by their read/write dependencies, culls passes that contribute nothing to an exported resource, computes each transient's lifetime as a pass-index interval and assigns physical textures so that transients with compatible descriptions and disjoint lifetimes share one allocation. Physical textures persist in a pool across frames and are dropped when their description stops being requested (resize). The renderer executes the compiled order itself ( order ) and looks up each transient"},{"t":"cinder.render API","s":"API reference","h":"struct TransientDesc","u":"/docs/api/render/#struct-transientdesc","x":"mojo struct TransientDesc ( Equatable , ImplicitlyCopyable , Movable ) Size in pixels, format, WGPUTextureUsage bits, sample and mip count of a transient. Transients share one pooled physical texture only when their descriptions are equal. Fields: width : UInt32 height : UInt32 format : WGPUTextureFormat usage : UInt64 sample_count : UInt32 mip_levels : UInt32"},{"t":"cinder.render API","s":"API reference","h":"struct FrameGraph","u":"/docs/api/render/#struct-framegraph","x":"mojo struct FrameGraph ( Movable ) One frame's passes and resources, compiled into an execution order and pooled textures. Per frame: reset , declare resources ( create , import_view , create_external ) and passes ( add_pass , read , write ), export what must survive, compile , then walk order and look up views with view_of . Physical textures are owned by the graph and freed by release (or when a compile no longer requests them); declaration and compile do not allocate in steady state. Fields: resources : List[_Resource] passes : List[_Pass] order : List[Int] — Pass indices in execution order (set by compile ). physicals : List[_Physical] compiled : Bool culled_count : Int __init__ __init__"},{"t":"cinder.render API","s":"API reference","h":"cinder.render.lights","u":"/docs/api/render/#cinderrenderlights","x":"Source: cinder/render/lights.mojo Lights and the clustered-culling configuration shared with shaders/common.wgsl . comptime CLUSTER_X = 16 — Cluster grid columns across the screen; mirrors CLUSTER_X in common.wgsl. comptime CLUSTER_Y = 9 — Cluster grid rows across the screen; mirrors CLUSTER_Y in common.wgsl. comptime CLUSTER_Z = 24 — Logarithmic depth slices of the cluster grid; mirrors CLUSTER_Z in common.wgsl. comptime CLUSTER_COUNT = CLUSTER_X * CLUSTER_Y * CLUSTER_Z — Total clusters (16 x 9 x 24); sizes the cluster_grid count buffer (one u32 each). comptime CLUSTER_MAX_LIGHTS = 32 — Light indices stored per cluster; lights beyond this are dropped by clusters.wgsl . comptime LIGHT_BYTES "},{"t":"cinder.render API","s":"API reference","h":"struct LightKind","u":"/docs/api/render/#struct-lightkind","x":"mojo struct LightKind ( Equatable , ImplicitlyCopyable , RegisterPassable ) Light type tag; values mirror LIGHT_* in common.wgsl (0 directional, 1 point, 2 spot). Fields: value : UInt32 directional directional() -> LightKind Sun-like light at infinity; only its direction matters. point point() -> LightKind Omnidirectional light at a position, attenuated to zero at range . spot spot() -> LightKind Cone light at a position along direction , attenuated to zero at range ."},{"t":"cinder.render API","s":"API reference","h":"struct Light","u":"/docs/api/render/#struct-light","x":"mojo struct Light ( ImplicitlyCopyable , Movable ) A light. Directional: direction (travel direction), color * intensity in lux. Point and spot: position, range (metres), intensity in watts, spot cone angles in radians. Fields: kind : LightKind position : Vec3 direction : Vec3 color : Vec3 intensity : Float32 range : Float32 inner_angle : Float32 outer_angle : Float32 cast_shadows : Bool __init__ __init__( out self, kind: LightKind, position: Vec3 = Vec3.zero(), direction: Vec3 = Vec3(0.0, -1.0, 0.0), color: Vec3 = Vec3.one(), intensity: Float32 = 1.0, range: Float32 = 10.0, inner_angle: Float32 = 0.4, outer_angle: Float32 = 0.6, cast_shadows: Bool = False, ) sun sun( direction: Vec3, color:"},{"t":"cinder.render API","s":"API reference","h":"def pack_lights_into","u":"/docs/api/render/#def-pack_lights_into","x":"mojo def pack_lights_into ( mut w: ByteWriter, lights: List[Light], shadow_views: List[Int] ) Appends the GPU light array (at least one zeroed entry) to w ."},{"t":"cinder.render API","s":"API reference","h":"def pack_lights","u":"/docs/api/render/#def-pack_lights","x":"mojo def pack_lights (lights: List[Light], shadow_views: List[Int]) -> List[UInt8] pack_lights_into into a new byte list (allocates; at least one zeroed 64-byte entry). shadow_views[i] is light i's first shadow-view index (-1 or missing: no shadow)."},{"t":"cinder.render API","s":"API reference","h":"struct ClusterParams","u":"/docs/api/render/#struct-clusterparams","x":"mojo struct ClusterParams ( ImplicitlyCopyable , Movable ) Logarithmic z slicing: slice = log2(z) * scale + bias between near and far . Fields: scale : Float32 bias : Float32 far : Float32 compute compute(near: Float32, far: Float32) -> ClusterParams Slice parameters mapping near to slice 0 and far to CLUSTER_Z (metres). slice_of slice_of(self, view_z: Float32) -> Int Z slice for a positive view-space distance in metres, clamped to [0, CLUSTER_Z - 1]. Mirrors cluster_z_slice in common.wgsl."},{"t":"cinder.render API","s":"API reference","h":"cinder.render.material","u":"/docs/api/render/#cinderrendermaterial","x":"Source: cinder/render/material.mojo glTF 2.0 metallic-roughness materials and their GPU bind groups. Uniform layout (96 bytes, 16-byte aligned fields, mirrored by shaders/material.wgsl ): 0 base_color: vec4 16 emissive.rgb, emissive_strength 32 metallic, roughness, normal_scale, occlusion_strength 48 alpha_cutoff, alpha_mode (0 opaque, 1 mask, 2 blend), double_sided, texture_flags 64 uv_scale.xy, uv_offset.xy (KHR_texture_transform, applied to every map) 80 uv_rotation, texcoord_bits (3 bits per map: base, mr, normal, occlusion, emissive), pad, pad comptime MATERIAL_UNIFORM_SIZE = 96 — Bytes in the material uniform (group 2, binding 0); see the module docstring for the layout."},{"t":"cinder.render API","s":"API reference","h":"struct AlphaMode","u":"/docs/api/render/#struct-alphamode","x":"mojo struct AlphaMode ( Equatable , ImplicitlyCopyable , RegisterPassable ) How alpha is used; packed into the material uniform and mirrored in WGSL. Fields: value : UInt32 opaque opaque() -> AlphaMode Alpha is ignored; the surface is fully opaque. mask mask() -> AlphaMode Alpha-tested: fragments with alpha below alpha_cutoff are discarded. blend blend() -> AlphaMode Alpha-blended; drawn after opaque batches, sorted back to front. comptime TEX_BASE_COLOR = 0 — Slot of the base colour map in Material.textures , texcoords and texture_flags . comptime TEX_METALLIC_ROUGHNESS = 1 — Slot of the metallic-roughness map in Material.textures , texcoords and texture_flags . comptime TEX_NORMAL = 2 — Sl"},{"t":"cinder.render API","s":"API reference","h":"struct Material","u":"/docs/api/render/#struct-material","x":"mojo struct Material ( Copyable , Movable ) CPU description. Texture views are null when a map is absent (defaults are bound instead). Fields: name : String base_color : Vec4 metallic : Float32 roughness : Float32 emissive : Vec3 emissive_strength : Float32 normal_scale : Float32 occlusion_strength : Float32 alpha_mode : AlphaMode alpha_cutoff : Float32 double_sided : Bool uv_scale : Vec2 uv_offset : Vec2 uv_rotation : Float32 textures : Array[TextureViewHandle, 5] texcoords : Array[UInt8, 5] sampler : SamplerHandle __init__ __init__(out self, name: String = String(\"material\")) texture_flags texture_flags(self) -> UInt32 Bit i set when map slot i ( TEX_* ) has a texture view. texcoord_bits t"},{"t":"cinder.render API","s":"API reference","h":"def material_bind_group_layout","u":"/docs/api/render/#def-material_bind_group_layout","x":"mojo def material_bind_group_layout ( mut res: Resources, ) raises -> BindGroupLayoutHandle Creates the group-2 material layout matching shaders/material.wgsl ; raises on a GPU error. Binding 0 is the uniform (vertex and fragment), 1 the sampler, 2..6 the maps in TEX_* order."},{"t":"cinder.render API","s":"API reference","h":"struct GpuMaterial","u":"/docs/api/render/#struct-gpumaterial","x":"mojo struct GpuMaterial ( ImplicitlyCopyable , Movable ) Uniform buffer and bind group of an uploaded material. alpha_mode and double_sided are copied from the Material to select its pipeline. Fields: uniform : BufferHandle bind_group : BindGroupHandle alpha_mode : AlphaMode double_sided : Bool"},{"t":"cinder.render API","s":"API reference","h":"def upload_material","u":"/docs/api/render/#def-upload_material","x":"mojo def upload_material ( mut res: Resources, layout: BindGroupLayoutHandle, defaults: DefaultTextures, material: Material, ) raises -> GpuMaterial Creates the material's uniform buffer and group-2 bind group; raises on a GPU error. Absent maps bind the defaults (white base colour, metallic-roughness and occlusion, flat normal, black emissive) and a null sampler binds sampler_linear_repeat . The texture views stay owned by the caller; the returned buffer and bind group are new allocations."},{"t":"cinder.render API","s":"API reference","h":"def update_material","u":"/docs/api/render/#def-update_material","x":"mojo def update_material ( mut res: Resources, gpu: GpuMaterial, material: Material ) raises Re-uploads the parameters (textures are fixed per bind group)."},{"t":"cinder.render API","s":"API reference","h":"cinder.render.mesh","u":"/docs/api/render/#cinderrendermesh","x":"Source: cinder/render/mesh.mojo Mesh data on the CPU, GPU meshes, the engine vertex format and procedural primitives. Vertex stream (56 bytes): position f32x3 @0, normal f32x3 @12, tangent f32x4 @24 (w = handedness), uv0 f32x2 @40, uv1 f32x2 @48. Skinning attributes live in a second stream ( SkinVertex , 24 bytes): joints u16x4 @0, weights f32x4 @8. Indices are u32. comptime VERTEX_STRIDE = 56 — Bytes per vertex in the static stream (slot 0); layout in the module docstring. comptime SKIN_VERTEX_STRIDE = 24 — Bytes per vertex in the skinning stream (slot 1): u16x4 joints, f32x4 weights. comptime PI = Float32(3.14159265358979) — Pi as Float32, for the procedural primitives."},{"t":"cinder.render API","s":"API reference","h":"def vertex_layout","u":"/docs/api/render/#def-vertex_layout","x":"mojo def vertex_layout () -> VertexLayout Slot 0: the static vertex stream. Locations 0..4."},{"t":"cinder.render API","s":"API reference","h":"def skin_vertex_layout","u":"/docs/api/render/#def-skin_vertex_layout","x":"mojo def skin_vertex_layout () -> VertexLayout Slot 1: joints and weights. Locations 5 and 6."},{"t":"cinder.render API","s":"API reference","h":"struct Bounds","u":"/docs/api/render/#struct-bounds","x":"mojo struct Bounds ( ImplicitlyCopyable , Movable ) Axis-aligned bounding box in mesh space (metres). Fields: min : Vec3 max : Vec3 centre centre(self) -> Vec3 Midpoint of the box. radius radius(self) -> Float32 Half the box diagonal: the radius of the bounding sphere around centre() . sphere sphere(self) -> Vec4 Bounding sphere: xyz centre, w radius (the input of Frustum.cull_spheres_into ). empty empty() -> Bounds Inverted box (min +3.4e38, max -3.4e38); the first include collapses it to a point. include include(mut self, p: Vec3) Grows the box to contain p ."},{"t":"cinder.render API","s":"API reference","h":"struct MorphTarget","u":"/docs/api/render/#struct-morphtarget","x":"mojo struct MorphTarget ( Copyable , Movable ) Per-vertex position and normal deltas of one morph target (normals may be empty). Fields: positions : List[Vec3] normals : List[Vec3]"},{"t":"cinder.render API","s":"API reference","h":"struct MeshLod","u":"/docs/api/render/#struct-meshlod","x":"mojo struct MeshLod ( ImplicitlyCopyable , Movable ) An index range of the combined index buffer and the simplification error it carries. Fields: first_index : UInt32 index_count : UInt32 error : Float32"},{"t":"cinder.render API","s":"API reference","h":"struct MeshData","u":"/docs/api/render/#struct-meshdata","x":"mojo struct MeshData ( Copyable , Movable ) CPU-side triangle mesh. Parallel per-vertex arrays; tangents , uv1 , joints and weights may be empty and are filled with defaults at upload. Fields: positions : List[Vec3] normals : List[Vec3] tangents : List[Vec4] uv0 : List[Vec2] uv1 : List[Vec2] joints : List[SIMD[DType.uint16, 4]] weights : List[Vec4] indices : List[UInt32] morph_targets : List[MorphTarget] __init__ __init__(out self) vertex_count vertex_count(self) -> Int Number of vertices ( len(positions) ). triangle_count triangle_count(self) -> Int Number of triangles ( len(indices) // 3 ). has_skin has_skin(self) -> Bool True when every vertex has joint indices and weights ( joints and we"},{"t":"cinder.render API","s":"API reference","h":"struct GpuMesh","u":"/docs/api/render/#struct-gpumesh","x":"mojo struct GpuMesh ( Copyable , Movable ) Buffers for one mesh. skin_vertices is null when the mesh is not skinned, morph_buffer when it has no morph targets. lods[0] is the full-detail index range. index_count is the full-detail index count ( lods[0].index_count , 0 without LODs), the same for upload_mesh_lods and upload_mesh_bytes ; the index buffer holds every LOD range, so its total length is the sum over lods . Fields: vertices : BufferHandle skin_vertices : BufferHandle indices : BufferHandle index_count : UInt32 vertex_count : UInt32 bounds : Bounds lods : List[MeshLod] morph_buffer : BufferHandle morph_target_count : UInt32 __init__ __init__( out self, vertices: BufferHandle, skin_v"},{"t":"cinder.render API","s":"API reference","h":"def upload_mesh","u":"/docs/api/render/#def-upload_mesh","x":"mojo def upload_mesh ( mut res: Resources, label: String, data: MeshData ) raises -> GpuMesh Uploads with a single LOD."},{"t":"cinder.render API","s":"API reference","h":"def upload_mesh_lods","u":"/docs/api/render/#def-upload_mesh_lods","x":"mojo def upload_mesh_lods ( mut res: Resources, label: String, data: MeshData, lod_indices: List[List[UInt32]], ) raises -> GpuMesh Uploads vertex streams plus one combined index buffer holding every LOD range."},{"t":"cinder.render API","s":"API reference","h":"def upload_mesh_bytes","u":"/docs/api/render/#def-upload_mesh_bytes","x":"mojo def upload_mesh_bytes ( mut res: Resources, label: String, vertex_count: UInt32, vertex_bytes: List[UInt8], skin_bytes: List[UInt8], index_bytes: List[UInt8], var lods: List[MeshLod], bounds: Bounds, morph_target_count: UInt32, morph_bytes: List[UInt8], ) raises -> GpuMesh Uploads streams that are already in GPU layout (cooked meshes)."},{"t":"cinder.render API","s":"API reference","h":"def destroy_mesh","u":"/docs/api/render/#def-destroy_mesh","x":"mojo def destroy_mesh ( mut res: Resources, mesh: GpuMesh) raises Destroys the mesh's vertex and index buffers, and its skin and morph buffers when present. The handles in mesh are left as they are; raises on a Resources error."},{"t":"cinder.render API","s":"API reference","h":"def cube","u":"/docs/api/render/#def-cube","x":"mojo def cube (half: Float32 = 0.5 ) -> MeshData 24-vertex cube with per-face normals, CCW winding seen from outside."},{"t":"cinder.render API","s":"API reference","h":"def uv_sphere","u":"/docs/api/render/#def-uv_sphere","x":"mojo def uv_sphere ( radius: Float32 = 0.5 , rings: Int = 16 , segments: Int = 32 ) -> MeshData UV sphere centred at the origin, poles on +Y and -Y; radius in metres. (rings + 1) * (segments + 1) vertices (the seam is duplicated), counter-clockwise seen from outside. u runs around the sphere from +X towards +Z, v from the top pole (0) to the bottom (1); tangents point along increasing u."},{"t":"cinder.render API","s":"API reference","h":"def plane","u":"/docs/api/render/#def-plane","x":"mojo def plane (half: Float32 = 0.5 , subdivisions: Int = 1 ) -> MeshData An XZ plane facing +Y."},{"t":"cinder.render API","s":"API reference","h":"cinder.render.renderer","u":"/docs/api/render/#cinderrenderrenderer","x":"Source: cinder/render/renderer.mojo The forward+ renderer: shadows, depth prepass, GTAO, clustered lights, PBR, sky, bloom, tonemap. Renderer.render(scene, target) builds the frame graph for the current settings, compiles it (ordering, transient aliasing), fills the GPU buffers from the scene and records every pass into one command buffer. CPU work per frame: frustum culling (SIMD), LOD selection, batching by (mesh, material, skinning), cascade fitting, light packing. Nothing here waits on the GPU. comptime FRAME_UNIFORM_SIZE = 720 — Bytes in the frame uniform: the size of Frame in common.wgsl (checked each frame). comptime PASS_UNIFORM_STRIDE = 256 — Bytes per view-projection slot in pass_u"},{"t":"cinder.render API","s":"API reference","h":"struct Tonemapper","u":"/docs/api/render/#struct-tonemapper","x":"mojo struct Tonemapper ( Equatable , ImplicitlyCopyable , RegisterPassable ) Tone curve of the tonemap pass; values mirror post.settings.z in post.wgsl. Fields: value : UInt32 agx agx() -> Tonemapper AgX (the default). aces aces() -> Tonemapper ACES filmic curve. none none() -> Tonemapper No curve: the exposed colour is clamped to [0, 1]; also used for debug views."},{"t":"cinder.render API","s":"API reference","h":"struct DebugMode","u":"/docs/api/render/#struct-debugmode","x":"mojo struct DebugMode ( Equatable , ImplicitlyCopyable , RegisterPassable ) Mirrors DEBUG_* in common.wgsl. Fields: value : UInt32 none none() -> DebugMode Normal lit output. normals normals() -> DebugMode World-space normals mapped to colour ( n * 0.5 + 0.5 ). cascades cascades() -> DebugMode Colour per sun shadow cascade, shaded by the sun angle. clusters clusters() -> DebugMode Heat map of the light count in each fragment's cluster (full scale at 16 lights). overdraw overdraw() -> DebugMode Overdraw count: forward pipelines blend additively with the depth test off, so every rasterised fragment (hidden or not, opaque or blended) adds 0.1 grey; brightness is the fragment count / 10 (white a"},{"t":"cinder.render API","s":"API reference","h":"struct RenderSettings","u":"/docs/api/render/#struct-rendersettings","x":"mojo struct RenderSettings ( ImplicitlyCopyable , Movable ) Quality knobs, read at the start of every render (change them between frames). cascade_resolution , atlas_size and atlas_tile are read at construction only. Fields: msaa : UInt32 — 1 or 4. bloom : Bool bloom_strength : Float32 bloom_threshold : Float32 tonemapper : Tonemapper exposure : Float32 fxaa : Bool ao : Bool shadows : Bool shadow_distance : Float32 cascade_resolution : Int atlas_size : Int atlas_tile : Int ibl_intensity : Float32 debug : DebugMode wireframe : Bool cull : Bool __init__ __init__(out self)"},{"t":"cinder.render API","s":"API reference","h":"struct Environment","u":"/docs/api/render/#struct-environment","x":"mojo struct Environment ( ImplicitlyCopyable , Movable ) Procedural sky by default; equirect (an RGBA16F 2D texture view) replaces it when set. Fields: zenith : Vec3 ground : Vec3 sun_intensity : Float32 equirect : TextureViewHandle __init__ __init__(out self)"},{"t":"cinder.render API","s":"API reference","h":"struct RenderInstance","u":"/docs/api/render/#struct-renderinstance","x":"mojo struct RenderInstance ( Copyable , Movable ) One drawn object: mesh and material indices into the renderer's registries. Fields: mesh : Int material : Int transform : Mat4 joint_offset : Int — Index of the first joint matrix in RenderScene.joints (skinned meshes), else -1. morph_weights : List[Float32] cast_shadows : Bool __init__ __init__( out self, mesh: Int, material: Int, transform: Mat4, joint_offset: Int = -1, cast_shadows: Bool = True, )"},{"t":"cinder.render API","s":"API reference","h":"struct RenderScene","u":"/docs/api/render/#struct-renderscene","x":"mojo struct RenderScene ( Movable ) Everything one render call draws; owned by the caller and only read by the renderer. Fields: camera : Camera lights : List[Light] instances : List[RenderInstance] joints : List[Mat4] — Skinning matrices (joint world * inverse bind), referenced by instances. environment : Environment __init__ __init__(out self, camera: Camera)"},{"t":"cinder.render API","s":"API reference","h":"struct FrameStats","u":"/docs/api/render/#struct-framestats","x":"mojo struct FrameStats ( ImplicitlyCopyable , Movable ) Counters of the last render , in Renderer.stats . Fields: instances_submitted : Int instances_drawn : Int draw_calls : Int — Forward (lit) draws, opaque and blended. prepass_draw_calls : Int shadow_draw_calls : Int lights : Int shadow_views : Int passes : Int transient_textures : Int"},{"t":"cinder.render API","s":"API reference","h":"struct Renderer","u":"/docs/api/render/#struct-renderer","x":"mojo struct Renderer ( Movable ) The forward+ renderer; render draws a RenderScene into a target view. Owns the pipelines, persistent GPU buffers and textures, the mesh and material registries, the frame graph, the shader library and per-frame scratch (steady-state frames do not allocate). Holds raw pointers to Gfx and Resources , which must outlive it. Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] res : Pointer[Resources, MutUntrackedOrigin] lib : NativePtr shaders : ShaderManager library : ShaderLibrary settings : RenderSettings defaults : DefaultTextures graph : FrameGraph stats : FrameStats frame_index : Int graphviz : String — DOT text of the last compiled frame graph; refreshed by gra"},{"t":"cinder.render API","s":"API reference","h":"cinder.render.shader_library","u":"/docs/api/render/#cinderrendershader_library","x":"Source: cinder/render/shader_library.mojo Shader programs assembled from WGSL files with #include \"file.wgsl\" lines, with hot reload. WGSL has no preprocessor, so includes are resolved here (relative to the shader directory, once per program, in order of first appearance). Error locations reported by naga refer to the assembled text; ShaderLibrary maps them back to the file and line they came from before raising."},{"t":"cinder.render API","s":"API reference","h":"struct AssembledShader","u":"/docs/api/render/#struct-assembledshader","x":"mojo struct AssembledShader ( Copyable , Movable ) WGSL text with every include spliced in, mapped back to its source files. chunks locate each assembled line in its file; files lists every file read ( directory/name ), in order of first inclusion. Fields: text : String chunks : List[_Chunk] files : List[String] __init__ __init__(out self) locate locate(self, assembled_line: Int) -> Tuple[String, Int] File and file line for a line of the assembled text."},{"t":"cinder.render API","s":"API reference","h":"def assemble","u":"/docs/api/render/#def-assemble","x":"mojo def assemble (directory: String, entry: String) raises -> AssembledShader Reads entry and splices every #include \"x\" (each file at most once) into one text."},{"t":"cinder.render API","s":"API reference","h":"struct ShaderLibrary","u":"/docs/api/render/#struct-shaderlibrary","x":"mojo struct ShaderLibrary ( Movable ) Loads directory/<entry>.wgsl programs with includes; check_for_changes hot reloads. Fields: gfx : Pointer[Gfx, MutUntrackedOrigin] res : Pointer[Resources, MutUntrackedOrigin] shaders : Pointer[ShaderManager, MutUntrackedOrigin] directory : String programs : List[_Program] diagnostics : List[LogRecord] reloaded : List[ShaderHandle] — Handles whose module changed in the last check_for_changes (pipelines must be rebuilt). __init__ __init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], shaders: Pointer[ShaderManager, MutUntrackedOrigin], directory: String, ) compile compile(mut self, entry: String) raises -> "},{"t":"cinder.render API","s":"API reference","h":"cinder.render.shadows","u":"/docs/api/render/#cinderrendershadows","x":"Source: cinder/render/shadows.mojo Cascaded shadow maps for the sun and an atlas for spot and point lights (CPU-side maths). Cascade fitting: each cascade covers a slice of the camera frustum (practical split scheme, lambda 0.75). The orthographic projection is fitted to the slice's bounding sphere so it has a constant size while the camera rotates, and its origin is snapped to whole shadow texels in light space so edges do not shimmer when the camera moves. comptime CASCADE_COUNT = 4 — Sun shadow cascades; matches the four-entry cascade arrays of Frame in common.wgsl. comptime SHADOW_VIEW_BYTES = 96 — Bytes per packed ShadowView : the size of ShadowView in common.wgsl."},{"t":"cinder.render API","s":"API reference","h":"struct Cascade","u":"/docs/api/render/#struct-cascade","x":"mojo struct Cascade ( ImplicitlyCopyable , Movable ) One fitted sun cascade: an orthographic reverse-Z light view-projection (world to clip). Fields: view_proj : Mat4 split_end : Float32 — View-space distance where this cascade ends. texel_world : Float32 — World-space size of one shadow texel in this cascade."},{"t":"cinder.render API","s":"API reference","h":"def cascade_splits_into","u":"/docs/api/render/#def-cascade_splits_into","x":"mojo def cascade_splits_into ( mut out : Array[Float32, CASCADE_COUNT], near: Float32, shadow_distance: Float32, lambda_: Float32 = 0.75 , ) End distances of the four cascades (practical split: blend of log and uniform) into a stack array (no allocation)."},{"t":"cinder.render API","s":"API reference","h":"def cascade_splits","u":"/docs/api/render/#def-cascade_splits","x":"mojo def cascade_splits ( near: Float32, shadow_distance: Float32, lambda_: Float32 = 0.75 ) -> List[Float32] End distances of the four cascades (practical split: blend of log and uniform)."},{"t":"cinder.render API","s":"API reference","h":"def fit_cascade","u":"/docs/api/render/#def-fit_cascade","x":"mojo def fit_cascade ( camera: Camera, light_dir: Vec3, z0: Float32, z1: Float32, resolution: Int ) -> Cascade Orthographic light view-projection for one slice with texel snapping. light_dir is the direction light travels. Depth range is the sphere diameter plus a generous margin behind the slice so casters outside the camera frustum still cast."},{"t":"cinder.render API","s":"API reference","h":"def fit_cascades_into","u":"/docs/api/render/#def-fit_cascades_into","x":"mojo def fit_cascades_into ( mut out : List[Cascade], camera: Camera, light_dir: Vec3, shadow_distance: Float32, resolution: Int, ) fit_cascades into a list supplied by the caller (cleared first, capacity kept)."},{"t":"cinder.render API","s":"API reference","h":"def fit_cascades","u":"/docs/api/render/#def-fit_cascades","x":"mojo def fit_cascades ( camera: Camera, light_dir: Vec3, shadow_distance: Float32, resolution: Int ) -> List[Cascade] fit_cascades_into into a new list (allocates); per-frame code uses the _into variant."},{"t":"cinder.render API","s":"API reference","h":"struct ShadowView","u":"/docs/api/render/#struct-shadowview","x":"mojo struct ShadowView ( ImplicitlyCopyable , Movable ) One atlas tile: a light-space view-projection and its uv rectangle (matches common.wgsl). Fields: view_proj : Mat4 uv_origin_x : Float32 uv_origin_y : Float32 uv_size : Float32 texel_uv : Float32 near : Float32 far : Float32 pack pack(self, mut w: ByteWriter) Appends the 96-byte GPU form ( ShadowView in common.wgsl). view_proj , then the uv rectangle (origin x, origin y, size, size), then the texel size in uv, near, far and a zero pad."},{"t":"cinder.render API","s":"API reference","h":"struct ShadowAtlas","u":"/docs/api/render/#struct-shadowatlas","x":"mojo struct ShadowAtlas ( Movable ) Fixed grid of square tiles in one depth texture. Spot lights take one tile, point lights six. Fields: size : Int tile_size : Int tiles_per_row : Int next_tile : Int views : List[ShadowView] __init__ __init__(out self, size: Int = 4096, tile_size: Int = 512) capacity capacity(self) -> Int Number of tiles in the atlas ( tiles_per_row squared). reset reset(mut self) Frees every tile and clears views (capacity kept); called at the start of each frame. tile_pixel_origin tile_pixel_origin(self, view_index: Int) -> Tuple[Int, Int] Top-left pixel of the tile used by shadow view view_index (views fill tiles in order). allocate allocate(mut self, light: Light) -> In"},{"t":"cinder.render API","s":"API reference","h":"cinder.render.textures","u":"/docs/api/render/#cinderrendertextures","x":"Source: cinder/render/textures.mojo GPU textures from CPU images: CPU box-filter mip chains, sRGB or linear formats, defaults."},{"t":"cinder.render API","s":"API reference","h":"struct GpuTexture","u":"/docs/api/render/#struct-gputexture","x":"mojo struct GpuTexture ( ImplicitlyCopyable , Movable ) An uploaded 2D texture and a view of all its mips; width and height are mip 0 in pixels. The texture and view are owned by whoever uploaded them and freed through Resources . Fields: texture : TextureHandle view : TextureViewHandle width : UInt32 height : UInt32 mip_levels : UInt32"},{"t":"cinder.render API","s":"API reference","h":"def mip_count","u":"/docs/api/render/#def-mip_count","x":"mojo def mip_count (width: Int, height: Int) -> Int Levels in a full mip chain down to 1x1 ( floor(log2(max(width, height))) + 1 )."},{"t":"cinder.render API","s":"API reference","h":"def downsample","u":"/docs/api/render/#def-downsample","x":"mojo def downsample (img: Image) -> Image 2x2 box filter (edge pixels of odd sizes are clamped), in the image's own encoding."},{"t":"cinder.render API","s":"API reference","h":"def upload_image","u":"/docs/api/render/#def-upload_image","x":"mojo def upload_image ( mut res: Resources, label: String, img: Image, srgb: Bool, mips: Bool = True ) raises -> GpuTexture Uploads an RGBA8 image as a 2D texture with a CPU box-filtered mip chain. srgb selects RGBA8UnormSrgb (colour data) over RGBA8Unorm (linear data); mips = False uploads level 0 only. The caller owns the result; raises on a GPU error."},{"t":"cinder.render API","s":"API reference","h":"def solid_image","u":"/docs/api/render/#def-solid_image","x":"mojo def solid_image (r: UInt8, g: UInt8, b: UInt8, a: UInt8) -> Image 1x1 RGBA8 image of one colour (used for the default textures)."},{"t":"cinder.render API","s":"API reference","h":"struct DefaultTextures","u":"/docs/api/render/#struct-defaulttextures","x":"mojo struct DefaultTextures ( ImplicitlyCopyable , Movable ) 1x1 fallbacks: white (base colour, occlusion, metallic-roughness), flat normal, black emissive. Fields: white : GpuTexture normal : GpuTexture black : GpuTexture sampler_linear_repeat : SamplerHandle sampler_nearest_clamp : SamplerHandle __init__ __init__(out self, mut res: Resources) raises"},{"t":"cinder.scene API","s":"API reference","h":"cinder.scene","u":"/docs/api/scene/","x":""},{"t":"cinder.scene API","s":"API reference","h":"cinder.scene","u":"/docs/api/scene/#cinderscene-1","x":"Source: cinder/scene/__init__.mojo Scene files: versioned text and binary formats with migrations, over the ECS world."},{"t":"cinder.scene API","s":"API reference","h":"cinder.scene.format","u":"/docs/api/scene/#cindersceneformat","x":"Source: cinder/scene/format.mojo Scene serialisation: a human-diffable text format and a binary format, versioned, with migrations. Both formats carry the same intermediate representation ( SceneDoc : entities with component records of typed fields), so a document can be read in either format, migrated version by version, and written in either. Component types take part by implementing SceneComponent and being registered in a SceneRegistry , which stores type-erased read/write functions. Text format (version 2): cinder-scene 2 entity 0 Position x=1.5 y=-2 Label text=\"hello \\\"world\\\"\" entity 1 Parent entity=#0 Floats are written as the shortest decimal that round-trips, or as 0x... IEEE bits "},{"t":"cinder.scene API","s":"API reference","h":"struct Field","u":"/docs/api/scene/#struct-field","x":"mojo struct Field ( Copyable , Movable ) One named, typed value of a component record. kind is a KIND_* constant and selects which of number , integer , text or floats holds the value; the others keep their zero defaults. Fields: name : String kind : Int number : Float64 integer : Int64 text : String floats : List[Float32] __init__ __init__(out self, name: String, kind: Int)"},{"t":"cinder.scene API","s":"API reference","h":"struct ComponentRecord","u":"/docs/api/scene/#struct-componentrecord","x":"mojo struct ComponentRecord ( Copyable , Movable ) A component as stored in a scene: its scene name and its fields in write order. Fields: name : String fields : List[Field] __init__ __init__(out self, name: String) find find(self, name: String) -> Int Index of the field called name , or -1. rename_field rename_field(mut self, old: String, new: String) -> Bool Renames field old to new (for migrations); returns False when there is no field old . An existing field called new is not checked for."},{"t":"cinder.scene API","s":"API reference","h":"struct EntityRecord","u":"/docs/api/scene/#struct-entityrecord","x":"mojo struct EntityRecord ( Copyable , Movable ) One entity of a scene: the records of the components it carries. Fields: components : List[ComponentRecord] __init__ __init__(out self) find find(self, name: String) -> Int Index of the component record called name , or -1."},{"t":"cinder.scene API","s":"API reference","h":"struct SceneDoc","u":"/docs/api/scene/#struct-scenedoc","x":"mojo struct SceneDoc ( Movable ) The format-independent scene: a version and the entities in file order. Entity fields hold indices into entities . Produced by SceneRegistry.to_doc , read_text and read_binary ; consumed by SceneRegistry.migrate , SceneRegistry.instantiate , write_text and write_binary . Fields: version : Int entities : List[EntityRecord] __init__ __init__(out self, version: Int = SCENE_VERSION)"},{"t":"cinder.scene API","s":"API reference","h":"struct FieldWriter","u":"/docs/api/scene/#struct-fieldwriter","x":"mojo struct FieldWriter ( Movable ) Collects a component's fields. Entities are written as file-local indices. Fields: record : ComponentRecord index_of : Pointer[List[Int], MutUntrackedOrigin] — Entity slot index -> file-local index (-1 when not in the file). __init__ __init__( out self, name: String, index_of: Pointer[List[Int], MutUntrackedOrigin] ) float float(mut self, name: String, v: Float32) Appends a KIND_FLOAT field. int int(mut self, name: String, v: Int) Appends a KIND_INT field. bool bool(mut self, name: String, v: Bool) Appends a KIND_BOOL field (stored as 0 or 1). string string(mut self, name: String, v: String) Appends a KIND_STRING field. entity entity(mut self, name: String"},{"t":"cinder.scene API","s":"API reference","h":"struct FieldReader","u":"/docs/api/scene/#struct-fieldreader","x":"mojo struct FieldReader ( Movable ) Reads a component's fields by name; missing fields take the given default. Fields: record : ComponentRecord entities : Pointer[List[Entity], MutUntrackedOrigin] — File-local index -> spawned entity. error : String __init__ __init__( out self, var record: ComponentRecord, entities: Pointer[List[Entity], MutUntrackedOrigin], ) float float(mut self, name: String, default: Float32) -> Float32 Field name as a float, or default when absent. An integer field is converted; any other kind sets error and returns default . int int(mut self, name: String, default: Int) -> Int Field name as an integer, or default when absent; another kind sets error and returns default"},{"t":"cinder.scene API","s":"API reference","h":"trait SceneComponent","u":"/docs/api/scene/#trait-scenecomponent","x":"mojo trait SceneComponent ( Component ) A component that can be saved: name, field writer and reader. scene_name scene_name() -> String The component's name in scene files, matched against component records on load. write_scene write_scene(self, mut w: FieldWriter) Writes the component's fields into w . read_scene read_scene(mut r: FieldReader) -> Self Builds the component from r , with defaults for missing fields. When r.error is set afterwards (the typed getters set it on a kind mismatch) the component is not added and SceneRegistry.instantiate raises. comptime WriteFn = def( Pointer[World, MutUntrackedOrigin], Entity, Pointer[FieldWriter, MutUntrackedOrigin], ) thin -> Bool — Type-erased "},{"t":"cinder.scene API","s":"API reference","h":"struct SceneRegistry","u":"/docs/api/scene/#struct-sceneregistry","x":"mojo struct SceneRegistry ( Movable ) Known component types and version migrations. Fields: types : List[_Registered] migrations : List[Tuple[Int, MigrationFn]] — (from_version, migration) pairs; each converts a document from from_version to +1. __init__ __init__(out self) register register[T: SceneComponent](mut self) Makes T saveable under T.scene_name() . Duplicate names are not rejected; lookups on load use the first registration. add_migration add_migration(mut self, from_version: Int, migration: MigrationFn) Registers migration to upgrade documents at from_version to from_version + 1 . to_doc to_doc(self, mut world: World) raises -> SceneDoc Snapshots every live entity (in archetype or"},{"t":"cinder.scene API","s":"API reference","h":"def format_float","u":"/docs/api/scene/#def-format_float","x":"mojo def format_float (v: Float32) -> String Shortest decimal that round-trips, else the IEEE bits as 0x...."},{"t":"cinder.scene API","s":"API reference","h":"def parse_float","u":"/docs/api/scene/#def-parse_float","x":"mojo def parse_float (text: String) raises -> Float32 Parses a float written by format_float : a decimal, or 0x followed by up to 8 hex digits of IEEE bits. Raises on malformed text."},{"t":"cinder.scene API","s":"API reference","h":"def bitcast_u32_to_f32","u":"/docs/api/scene/#def-bitcast_u32_to_f32","x":"mojo def bitcast_u32_to_f32 (bits: UInt32) -> Float32 Reinterprets IEEE-754 single-precision bits as a Float32."},{"t":"cinder.scene API","s":"API reference","h":"def write_text","u":"/docs/api/scene/#def-write_text","x":"mojo def write_text (doc: SceneDoc) -> String Serialises doc in the text format described in the module docstring: a cinder-scene N header, one entity i line per entity and one indented Name key=value ... line per component."},{"t":"cinder.scene API","s":"API reference","h":"def read_text","u":"/docs/api/scene/#def-read_text","x":"mojo def read_text (text: String) raises -> SceneDoc Parses the text format into a SceneDoc at the file's version (not migrated). Blank lines and lines starting with # are skipped. Raises on a missing header, entities out of order, a component line before any entity, an unterminated quote or bracket, a token without = , or a value that does not parse."},{"t":"cinder.scene API","s":"API reference","h":"def write_binary","u":"/docs/api/scene/#def-write_binary","x":"mojo def write_binary (doc: SceneDoc) -> List[UInt8] Serialises doc in the binary format: MAGIC , then little-endian u32 version and entity count; per entity a component count, per component its name and field count, per field its name, a u8 kind and the value. Strings are a u32 byte length plus bytes; integers, bools and entities are 64-bit (low word first); float lists carry a u32 count."},{"t":"cinder.scene API","s":"API reference","h":"def read_binary","u":"/docs/api/scene/#def-read_binary","x":"mojo def read_binary ( var data: List[UInt8]) raises -> SceneDoc Parses the binary format written by write_binary into a SceneDoc at the file's version (not migrated). Raises on a bad magic, truncated data, an unknown field kind, or implausible counts (over 10 million entities, 10 000 components per entity or fields per component, or 1 million floats per field). Trailing bytes are ignored."},{"t":"cinder.scene API","s":"API reference","h":"cinder.scene.gltf_spawn","u":"/docs/api/scene/#cinderscenegltf_spawn","x":"Source: cinder/scene/gltf_spawn.mojo Spawning a glTF asset into the ECS: one entity per node (transform hierarchy) and one child entity per mesh primitive ( MeshRenderer ), plus static collision triangles for level geometry."},{"t":"cinder.scene API","s":"API reference","h":"struct SpawnedGltf","u":"/docs/api/scene/#struct-spawnedgltf","x":"mojo struct SpawnedGltf ( Movable ) Result of spawn_gltf : the root entity, the entity of each glTF node and of each mesh primitive, and the renderer ids of the upload. The entities stay in the world after this value is dropped. Fields: root : Entity — Carries root_transform ; every glTF root node is its child. node_entities : List[Entity] — Entity per glTF node (nodes outside the scene graph get the null entity). primitive_entities : List[Entity] — Every primitive entity, in node then primitive order. uploaded : UploadedGltf __init__ __init__(out self, root: Entity, var uploaded: UploadedGltf) entity_of_node entity_of_node(self, node: Int) -> Entity Entity spawned for glTF node node ; the n"},{"t":"cinder.scene API","s":"API reference","h":"def spawn_gltf","u":"/docs/api/scene/#def-spawn_gltf","x":"mojo def spawn_gltf ( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, mut world: World, root_transform: Mat4 = Mat4.identity(), lods: Bool = True , cast_shadows: Bool = True , skinned_meshes: Bool = True , ) raises -> SpawnedGltf Uploads the asset and spawns its node hierarchy. Skinned mesh nodes get a MeshRenderer too (the caller attaches an AnimatorRef so the animation system publishes their palette) unless skinned_meshes is False. Every node is spawned at most once, even when the file reaches it twice (a root that is also a child, a node shared by two parents, or a cycle); a node whose glTF parent has not been spawned yet hangs under root , so the Parent links never form a c"},{"t":"cinder.scene API","s":"API reference","h":"def collision_triangles","u":"/docs/api/scene/#def-collision_triangles","x":"mojo def collision_triangles ( asset: GltfAsset, root_transform: Mat4 = Mat4.identity() ) -> Tuple[List[Vec3], List[UInt32]] World-space triangles of every non-skinned mesh node, for a static mesh collider."},{"t":"cinder.scene API","s":"API reference","h":"cinder.scene.render_sync","u":"/docs/api/scene/#cinderscenerender_sync","x":"Source: cinder/scene/render_sync.mojo Bridge from the ECS world to a RenderScene : render components and their collection. Components: MeshRenderer (renderer mesh and material indices, shadow flag), LightSource , CameraComponent . build_render_scene reads WorldTransform (propagated beforehand) and fills the renderer's scene lists in one pass; it allocates nothing in steady state because the scene's lists are cleared and refilled in place."},{"t":"cinder.scene API","s":"API reference","h":"struct MeshRenderer","u":"/docs/api/scene/#struct-meshrenderer","x":"mojo struct MeshRenderer ( Component ) Draws renderer mesh mesh with material material at the entity's WorldTransform ; cast_shadows is passed to the render instance. Fields: mesh : Int material : Int cast_shadows : Bool component_id component_id() -> Int"},{"t":"cinder.scene API","s":"API reference","h":"struct LightSource","u":"/docs/api/scene/#struct-lightsource","x":"mojo struct LightSource ( Component ) A light positioned by the entity's world transform (direction = transform's -Z). Fields: kind : LightKind color : Vec3 intensity : Float32 range : Float32 inner_angle : Float32 outer_angle : Float32 cast_shadows : Bool component_id component_id() -> Int"},{"t":"cinder.scene API","s":"API reference","h":"struct CameraComponent","u":"/docs/api/scene/#struct-cameracomponent","x":"mojo struct CameraComponent ( Component ) A camera at the entity's world transform, looking down its -Z with its +Y up. RenderBridge.build uses the first active one it finds, keeping the scene camera's current aspect ratio. Fields: fov_y : Float32 near : Float32 active : Bool component_id component_id() -> Int"},{"t":"cinder.scene API","s":"API reference","h":"struct JointPalette","u":"/docs/api/scene/#struct-jointpalette","x":"mojo struct JointPalette ( Component ) Skinning matrices and morph weights for an entity's mesh, written by the animation system each frame and consumed by build_render_scene . Fields: joints : List[Mat4] morph_weights : List[Float32] component_id component_id() -> Int"},{"t":"cinder.scene API","s":"API reference","h":"struct RenderBridge","u":"/docs/api/scene/#struct-renderbridge","x":"mojo struct RenderBridge ( Movable ) ECS -> RenderScene with persistent query specs and chunk scratch (no steady-state allocations). build_render_scene is a convenience wrapper around a temporary instance. Fields: __init__ __init__(out self) build build(mut self, world: World, mut scene: RenderScene) raises -> Int Fills scene.instances , scene.joints and scene.lights from the world and sets the camera from the first active CameraComponent . Returns the number of instances."},{"t":"cinder.scene API","s":"API reference","h":"def build_render_scene","u":"/docs/api/scene/#def-build_render_scene","x":"mojo def build_render_scene (world: World, mut scene: RenderScene) raises -> Int One-off bridge build (tests); the App keeps a RenderBridge ."},{"t":"cinder.tools API","s":"API reference","h":"cinder.tools","u":"/docs/api/tools/","x":""},{"t":"cinder.tools API","s":"API reference","h":"cinder.tools","u":"/docs/api/tools/#cindertools-1","x":"Source: cinder/tools/__init__.mojo Developer tools: the ImGui debug overlay (frame graph, inspector, assets, render graph, log)."},{"t":"cinder.tools API","s":"API reference","h":"cinder.tools.cstrings","u":"/docs/api/tools/#cindertoolscstrings","x":"Source: cinder/tools/cstrings.mojo Frame-scoped C strings for immediate-mode UI calls. ImGui keeps label pointers only until the frame is rendered, and Mojo String s move when a list grows (inline small-string storage), so labels are copied into List[UInt8] buffers whose heap data stays put. Call clear once per frame after rendering."},{"t":"cinder.tools API","s":"API reference","h":"struct CStrings","u":"/docs/api/tools/#struct-cstrings","x":"mojo struct CStrings ( Movable ) Owns NUL-terminated byte copies of strings handed to C; pointers from c stay valid until clear . Fields: __init__ __init__(out self) clear clear(mut self) Frees every buffer; pointers returned by c become dangling. __len__ __len__(self) -> Int c c( mut self, text: String ) -> Optional[Pointer[c_char, MutUntrackedOrigin]] A NUL-terminated copy of text , valid until clear ."},{"t":"cinder.tools API","s":"API reference","h":"cinder.tools.debug_ui","u":"/docs/api/tools/#cindertoolsdebug_ui","x":"Source: cinder/tools/debug_ui.mojo The ImGui debug overlay: frame-time graph and render stats, entity inspector (view and edit components through the scene registry), asset browser, render graph view and log console. ImGui runs through the shim's SDL3 + WebGPU backends ( cn_imgui_* ); widgets are the generated dcimgui bindings. The overlay draws into the frame's final render pass after the scene. comptime HISTORY = 240 — Frames kept in the frame-time graph."},{"t":"cinder.tools API","s":"API reference","h":"struct DebugUi","u":"/docs/api/tools/#struct-debugui","x":"mojo struct DebugUi ( Movable ) The ImGui debug overlay. Construction initialises the shim's ImGui backend for the window and device (raising when that fails); destruction shuts it down. App creates it for windowed apps with debug_ui set and declares it first, so it is destroyed before the window and GPU device. Per frame: begin_frame , draw , then render into the final pass. Fields: lib : NativePtr frame_ms : List[Float32] — Ring of the last HISTORY frame times in milliseconds. frame_cursor : Int frames : Int selected : Int — Index into the last entity snapshot (-1 = none). entities : List[Entity] edits_applied : Int visible : Bool requested_tab : Int — Tab to force-select on the next frame"},{"t":"Deliverable report","s":"Project","h":"Cinder: deliverable report","u":"/docs/report/","x":"Living document. Every claim below was verified in this repository under the pinned toolchain on the date given; nothing is reported from memory or intention. Phases are those of the mission brief."},{"t":"Deliverable report","s":"Project","h":"1. What was built (and what was not)","u":"/docs/report/#1-what-was-built-and-what-was-not","x":"Area State Where Toolchain pins, container, CI skeleton Done pixi.toml , docker/ , .github/workflows/ci.yml , docs/TOOLCHAIN.md Mojo 1.2.0 notes and banned legacy forms, with CI lint Done docs/MOJO_NOTES.md , tools/lint_legacy_mojo.sh Architecture and ADRs 0001 to 0007 Done docs/ARCHITECTURE.md , docs/adr/ Vendored dependencies (14 submodules, exact pins, licences) Done third_party/ , THIRD_PARTY_NOTICES.md Native shim libcinder_native (C11): lock-free MPMC event queue, wgpu async/log callbacks, SDL surface + log capture, Jolt contact and activation listeners, miniaudio engine/sound ownership and callbacks, ImGui backends behind a C ABI Done for Phase 1 scope native/ Native build (CMake + Ni"},{"t":"Deliverable report","s":"Project","h":"2. Pinned version matrix","u":"/docs/report/#2-pinned-version-matrix","x":"Component Version Mojo 1.2.0 (build 2016d0cd) pixi v0.63.2 Rust (wgpu-native build only) 1.93.0 CMake / Ninja >=3.28,<4 / >=1.11 (conda-forge) wgpu-native v29.0.1.1 (webgpu-headers 673658b) SDL3 release-3.4.20 joltc / Jolt Physics 2bbcf73 / v5.6.0 cgltf v1.15 miniaudio 0.11.26 Dear ImGui / Dear Bindings v1.93.0-docking / v0.27 stb, KTX-Software, meshoptimizer, MikkTSpace, Tracy, flecs 4e9b368, v4.4.2, v1.3, 3e895b4, v0.15.0, v4.2.0 (all built into the shim) Linux toolchain image Ubuntu 24.04, clang 18, Mesa lavapipe, Xvfb, Weston"},{"t":"Deliverable report","s":"Project","h":"3. Test results (Linux x86-64, lavapipe, 2026-11-09)","u":"/docs/report/#3-test-results-linux-x86-64-lavapipe-2026-11-09","x":"Three consecutive full runs of tools/run_tests.sh and spikes/window/run_window_spike.sh , identical results each time (no flakes): Program Tests Result tests/ffi/test_layout_cinder_native.mojo 892 (size, alignment, every field offset of every record incl. flecs and libktx; every enum and flag constant) 892 pass spikes/ffi/test_ffi_spikes.mojo (spikes 3, 4, queue contract) 5 5 pass spikes/wgpu_compute/test_compute.mojo (spike 2) 1 pass spikes/physics/test_physics.mojo (spike 5) 1 pass spikes/audio/test_audio.mojo (spike 6) 1 pass spikes/gltf/test_gltf.mojo (spike 7) 2 2 pass spikes/window/test_window.mojo (spike 1) 1 pass on X11 (Xvfb) and on Wayland (Weston headless) spikes/imgui/test_imgui."},{"t":"Deliverable report","s":"Project","h":"4. Benchmark results","u":"/docs/report/#4-benchmark-results","x":"The reference hardware (Linux RTX 3060-class, Apple M2) is not available; the only GPU here is Mesa lavapipe (llvmpipe, LLVM 20.1.2) inside Docker on the Windows host's CPU. Every number below is from that configuration and is a regression baseline for it ( bench/baselines/*.json , checked by tools/bench_check.py with a 10% tolerance), not a statement about the section 8 budgets. Method: docs/BENCHMARKS.md ."},{"t":"Deliverable report","s":"Project","h":"Section 8 budgets","u":"/docs/report/#section-8-budgets","x":"Budget Measured here (lavapipe) Status Sample p99 <= 16.6 ms at 1080p high see sample_arena below Unmeasured on reference hardware ; far above on a CPU rasteriser bench/stress >= 60 fps (50k instances, 2,000 bodies, 256 lights) see stress below Unmeasured on reference hardware Physics step <= 4 ms, 2,000 bodies 0.73 ms median, 2.34 ms p99 over 120 frames (Jolt, 2 worker threads, bodies falling and settling) Met on this CPU Cold start <= 2 s stress 0.97 s; sample arena 1.55 s (includes the KayKit character and the environment bake); sample Sponza above 2 s (source glTF textures decoded in process) Met for arena and stress on this host; Sponza not met Zero allocations per steady-state frame te"},{"t":"Deliverable report","s":"Project","h":"Results ( pixi run bench-all , 1920x1080, high preset, headless)","u":"/docs/report/#results-pixi-run-bench-all-1920x1080-high-preset-headless","x":"Benchmark Frames Frame ms (median / p99) Physics ms (median / p99) Render submit ms (median / p99) Start-up ms Notes stress (50,000 instances, 2,000 bodies, 256 lights) 120 721.3 / 785.0 0.728 / 2.343 715.0 / 778.5 972 24257 instances drawn after culling in 17 draw calls; 0 GPU errors sample_arena (high preset) 300 294.6 / 356.9 0.007 / 0.011 294.2 / 356.4 1548 autopilot collected 4; 0 GPU errors Adapter for both: llvmpipe (LLVM 20.1.2, 256 bits) . Render time is almost all of frame time in both runs, and on this adapter the GPU work is software rasterisation of 1080p on the same CPU, so these numbers say nothing about the GPU budgets. The engine's CPU-only share of a frame was not isolated "},{"t":"Deliverable report","s":"Project","h":"5. Known limitations and Mojo workarounds","u":"/docs/report/#5-known-limitations-and-mojo-workarounds","x":"ASan alloc_dealloc_mismatch disabled in tools/run_tests_asan.sh . KTX-Software at the pinned commit allocates Basis supercompression global data with new[] ( lib/basis_encode.cpp , ktxTexture2_CompressBasisEx ) and frees it with free ( lib/texture2.c , ktxTexture2_destruct ), an alloc-dealloc mismatch inside the vendored library that only the in-process encoder path (the cooker) hits. ASan cannot scope that check with a suppression file, so that single check is off for the sanitizer runs; heap overflow, use-after-free, LeakSanitizer and UBSan stay on for every library and all Mojo code. The submodule is not patched; revisit when the pin moves. Item Reproducer Workaround A comptime Array[Stat"},{"t":"Deliverable report","s":"Project","h":"Defect sweep before v0.1.0 (Phase 9)","u":"/docs/report/#defect-sweep-before-v010-phase-9","x":"Writing a docstring for every public symbol meant reading every public function, and that review found real defects. Each was fixed at its root with a test that exercises it. Where the agent or operator ran the new test against the old code, it failed there. The fixes, by area: Area Fixed gfx Encoder labels reached no descriptor; passes and encoders dropped without end /submit were never released; rejected pipelines leaked and reported the wrong diagnostic; adopt_* / create_* leaked on a full pool and accepted null; imported views could be released by Resources (also at shutdown); unchecked buffer offsets; read_buffer left a buffer mapped on error; the shutdown leak count was unreadable (now"},{"t":"Deliverable report","s":"Project","h":"6. Deviations from the brief, each with its ADR","u":"/docs/report/#6-deviations-from-the-brief-each-with-its-adr","x":"Deviation ADR Own bindings generator instead of mojo-bindgen (its 0.3.4 output does not parse under 1.2.0; it needs Python 3.14 but no nightly Mojo, so that part of the concern was unfounded) ADR-0005 wgpu-native is built by cargo driven from CMake, not by CMake itself (it is Rust) ADR-0004 Development host is a Linux container on Windows; macOS verification deferred ADR-0007 stargine not forked ADR-0006 Mojo ECS kept over flecs after the mandated benchmark (parity, within the band) ADR-0011 Jolt steps on its own 2-thread pool rather than the engine job system (joltc cannot be driven without callbacks into Mojo) ADR-0012 Tracy is started on demand ( --profile ) rather than at process start, "},{"t":"Deliverable report","s":"Project","h":"7. Blockers escalated","u":"/docs/report/#7-blockers-escalated","x":"No macOS runner or machine. Exact problem: every gate requires both platforms; no Apple Silicon host was reachable from this session. Tried: none possible (Docker runs Linux only). Options: (a) enable the macos-14 job in .github/workflows/ci.yml by pushing to GitHub, (b) a local Apple Silicon machine running the same pixi tasks, (c) accept Linux-only gates for now and batch macOS verification later. Recommendation: (a), it is already written. No reference GPU hardware for section 8. Same options; benchmarks will run on CI hardware for regression tracking, but budget compliance cannot be claimed from lavapipe."},{"t":"Deliverable report","s":"Project","h":"8. Exact commands","u":"/docs/report/#8-exact-commands","x":"bash docker build -t cinder-toolchain:1 -f docker/Dockerfile.toolchain docker tools/in_container.sh pixi install tools/in_container.sh pixi run gen-dcimgui # Dear Bindings C API (committed) tools/in_container.sh pixi run -e bindgen bindgen # cinder/ffi, layout printer, layout test (committed) tools/in_container.sh pixi run native # Release shim + CTest + layout facts tools/in_container.sh pixi run native-sanitize # Debug shim with ASan+UBSan + CTest tools/in_container.sh pixi run test # lint, every Mojo test program, X11 + Wayland spikes tools/in_container.sh pixi run format-check Cooking assets and running the sample game: not available yet (Phases 5 and 8)."},{"t":"Status","s":"Project","h":"Status","u":"/docs/status/","x":"Last updated: 2026-11-09. One row per phase; \"verified\" means executed in this repository under the pinned toolchain, with the command named. Nothing here is claimed from memory. Phase State Verified Open risks 0 Reconnaissance Done on Linux ; macOS gate not runnable on this host pixi run mojo --version = 1.2.0; spikes/hello runs and its TestSuite passes (2/2); all 14 third-party submodules checked out at the pinned refs via tools/vendor_submodules.sh No macOS runner available (see Blockers). mojo-bindgen's generator pins a nightly ( ADR-0005 ). 1 Risk spikes 9 of 9 pass on Linux (last full run recorded in REPORT.md); macOS not run tools/run_tests.sh and spikes/window/run_window_spike.sh in "},{"t":"Status","s":"Project","h":"Blockers escalated to the operator","u":"/docs/status/#blockers-escalated-to-the-operator","x":"See REPORT.md → Blockers. Summary: No macOS/Metal machine or CI runner. Every phase gate requires both platforms. Linux work proceeds; macOS verification is pending a runner (GitHub Actions macos-14 or a local Apple Silicon machine). No reference GPU hardware. Section 8 budgets cannot be measured on lavapipe. Benchmarks are built and run for correctness and regression tracking; budget compliance is unreported until hardware exists."},{"t":"Status","s":"Project","h":"Decisions log","u":"/docs/status/#decisions-log","x":"2026-11-08 ADR-0001 ..0007 accepted (0005 proposed, decided by spike 9). 2026-11-08 ADR-0011 accepted: Mojo ECS kept after the flecs benchmark (ratios 0.91 / 1.00 / 0.98). 2026-11-09 ADR-0014 accepted: sample game as a Game over scene components, JSON benchmarks with adapter-scoped baselines, zero-allocation frame enforced by an interposed counter. 2026-11-09 ADR-0013 accepted: App runtime, headless mode, Tracy manual lifetime, data-driven ImGui overlay. 2026-11-09 ADR-0012 accepted: Phase 6 subsystem designs (Jolt thread pool, phase-synced blend trees, miniaudio buses, text input maps). 2026-11-08 Operator asked about forking stargine; declined, ADR-0006 . 2026-11-08 ADR-0005 decided: own g"},{"t":"Changelog","s":"Project","h":"Changelog","u":"/docs/changelog/","x":"All notable changes to Cinder. The format follows Keep a Changelog; versions follow semantic versioning. Dates are the day the phase gate passed on Linux x86-64 ( REPORT.md has the runs)."},{"t":"Changelog","s":"Project","h":"0.1.0 - 2026-11-09","u":"/docs/changelog/#010---2026-11-09","x":"First release: a complete Linux x86-64 engine with a playable sample. macOS arm64 is a declared target but has not been built or run (no machine or runner was available; see REPORT.md , Blockers)."},{"t":"Changelog","s":"Project","h":"Added","u":"/docs/changelog/#added","x":"Build and toolchain. pixi environment pinning Mojo 1.2.0, CMake/Ninja and Python; one C11 shim libcinder_native over pinned submodules (wgpu-native v29.0.1.1, SDL release-3.4.20, Jolt Physics v5.6.0 via joltc, cgltf v1.15, stb, KTX-Software v4.4.2, meshoptimizer v1.3, MikkTSpace, miniaudio 0.11.26, Dear ImGui v1.93.0-docking via Dear Bindings v0.27, Tracy v0.15.0, flecs v4.2.0 for the ECS benchmark; exact commits in REPORT.md ); generated Mojo bindings with a layout test covering every struct offset and enum constant; ASan/UBSan shim build and test runner; Docker toolchain container. Core ( cinder/core ): logging with sinks, assertions, arena and generational handle pools, rings, SIMD vector"},{"t":"Changelog","s":"Project","h":"Quality gates","u":"/docs/changelog/#quality-gates","x":"Every public symbol has a docstring ( pixi run api-docs-check , part of pixi run test ) and every code name quoted in the guides exists ( pixi run docs-check ). A pre-release review of every public function fixed about 60 defects across all subsystems, each with a test ( REPORT.md , section 5)."},{"t":"Changelog","s":"Project","h":"Known limitations","u":"/docs/changelog/#known-limitations","x":"Only the first directional light is used. Not built or run on macOS; golden images exist for lavapipe only; the section 8 budgets are recorded on lavapipe, not met on reference hardware (none available). The sample's Sponza level loads source glTF textures and starts in more than two seconds. KTX-Software's vendored allocator mismatch is tolerated under ASan ( alloc_dealloc_mismatch=0 ); see REPORT.md . Built-in components are not SceneComponent s, so the inspector edits game-registered types only."},{"t":"Engine README","s":"Project","h":"Cinder","u":"/docs/readme/","x":"A 3D game engine written in Mojo 1.2 over a single C11 native shim (wgpu-native, SDL3, Jolt, miniaudio, cgltf, Dear ImGui). Targets Linux x86-64 (Vulkan) and macOS Apple Silicon (Metal). Project state: Phase 0 (reconnaissance, pins, architecture) and Phase 1 (nine risk spikes) are complete and verified on Linux. The macOS half of every gate has not been run: no Apple hardware or runner was available. Phases 2 to 9 (engine proper) have not started. See docs/STATUS.md for exactly what has been verified and REPORT.md for the deliverable report."},{"t":"Engine README","s":"Project","h":"Quick start (Linux, or any host with Docker)","u":"/docs/readme/#quick-start-linux-or-any-host-with-docker","x":"bash git clone --recursive &#x3C; this repositor y > cinder &#x26;&#x26; cd cinder docker build -t cinder-toolchain:1 -f docker/Dockerfile.toolchain docker tools/in_container.sh pixi install tools/in_container.sh pixi run -e bindgen bindgen # regenerate cinder/ffi from the C headers tools/in_container.sh pixi run native # build the native shim, run its CTest suite tools/in_container.sh pixi run test # lint, layout test, every spike (incl. X11 + Wayland) On a native Linux or macOS host with pixi installed, drop the tools/in_container.sh prefix (the windowed spikes then need a display, or Xvfb/Weston as the script starts them). The Windows host used for development runs everything through the "},{"t":"Engine README","s":"Project","h":"The sample game","u":"/docs/readme/#the-sample-game","x":"bash tools/in_container.sh pixi run sample # windowed: arena level, WASD/Space, Esc for the menu tools/in_container.sh pixi run sample -- --level sponza # the Khronos atrium with a mesh collider tools/in_container.sh pixi run sample-headless # offscreen autopilot run (tests, CI, benchmarks) sample/main.mojo accepts --headless , --level arena|sponza , --autopilot , --frames N , --screenshot PATH , --verbose and the engine options from cinder.app.parse_app_args . Assets are CC0 and fetched by tools/fetch_game_assets.py (attribution in assets/game/ATTRIBUTION.md ). Settings, key bindings and saves land in .cache/sample/ ."},{"t":"Engine README","s":"Project","h":"Benchmarks","u":"/docs/readme/#benchmarks","x":"bash tools/in_container.sh pixi run bench-all # stress scene + sample game headless, JSON in .cache/bench, baseline check tools/in_container.sh pixi run test-zero-alloc # asserts zero Mojo allocations per steady-state frame tools/bench_check.py --update records new baselines in bench/baselines/ . Numbers are only comparable on the same adapter; see REPORT.md for the measured results and the budgets."},{"t":"Engine README","s":"Project","h":"Layout","u":"/docs/readme/#layout","x":"Path Contents cinder/ffi/ Generated raw bindings (one module, one library struct). Never edited by hand. native/ libcinder_native C11 shim, the ImGui C++ shim, CMake build of all vendored libraries, layout printer. third_party/ Vendored dependencies as pinned git submodules. Licences in THIRD_PARTY_NOTICES.md . spikes/ Phase 1 risk spikes, each a TestSuite program; spikes/common holds their shared helpers. tests/ Every test suite (core, gfx, render, golden images, ECS, scene, assets, physics, anim, audio, input, app, tools, sample, perf). tools/ Container runner, vendoring, bindings generator, lint, test drivers, asset fetchers, benchmark checker. sample/ The sample game (a Game over cinder."},{"t":"Engine README","s":"Project","h":"Documentation","u":"/docs/readme/#documentation","x":"docs/TOOLCHAIN.md : exact pinned versions. docs/MOJO_NOTES.md : Mojo 1.2.0 idioms used, lifetime rules learned the hard way, banned legacy forms. docs/ARCHITECTURE.md : module map, C boundary, threading model. docs/adr/ : architecture decision records."},{"t":"Engine README","s":"Project","h":"Licence","u":"/docs/readme/#licence","x":"Apache-2.0. See LICENSE ."},{"t":"Third-party notices","s":"Project","h":"Third-party notices","u":"/docs/third-party-notices/","x":"Every dependency is vendored as a git submodule under third_party/ at the exact ref below and built from source by native/ . Licence texts are in each submodule at the path given. All licences are permissive; no copyleft code is linked into Cinder. Component Path Pinned ref Commit Licence Licence file wgpu-native third_party/wgpu-native v29.0.1.1 589e6ca MIT OR Apache-2.0 LICENSE.MIT , LICENSE.APACHE webgpu-headers (nested in wgpu-native) third_party/wgpu-native/ffi/webgpu-headers as pinned by wgpu-native 673658b BSD-3-Clause LICENSE SDL3 third_party/SDL release-3.4.20 f00dc91 zlib LICENSE.txt joltc (Jolt Physics C API) third_party/joltc commit 2bbcf73b1bec 2bbcf73 MIT LICENSE Jolt Physics t"},{"t":"Third-party notices","s":"Project","h":"Tools (not linked into the engine)","u":"/docs/third-party-notices/#tools-not-linked-into-the-engine","x":"Tool Version Licence Mojo compiler and stdlib 1.2.0 Modular's Mojo licence (compiler), Apache-2.0 with LLVM exceptions (stdlib) pixi v0.63.2 BSD-3-Clause Rust toolchain (builds wgpu-native) 1.93.0 MIT OR Apache-2.0 mojo-bindgen see ADR-0005 MIT ply (dear_bindings dependency) 3.11 BSD-3-Clause"},{"t":"Third-party notices","s":"Project","h":"Assets","u":"/docs/third-party-notices/#assets","x":"Sample and test assets are CC0 or CC-BY and are listed with attribution in assets/ATTRIBUTION.md once added (Phase 4 onwards). Khronos glTF-Sample-Assets are used under their per-model licences."},{"t":"cinder.anim","s":"API symbol","h":"module","u":"/docs/api/anim/#cinderanim-1","x":"Skeletal animation: skeletons, clip sampling, blend trees, animators with crossfades, root motion and events, and a job-parallel system publishing joint palettes to the ECS."},{"t":"cinder.anim.animator","s":"API symbol","h":"module","u":"/docs/api/anim/#cinderanimanimator","x":"Animator: states (blend trees) with crossfades, animation events and root motion. update(dt) advances the active state (and the one fading out), blends them, extracts root motion and collects events; pose then holds the local pose and palette() the skinning matrices. Events are authored per state at"},{"t":"cinder.anim.animator.AnimationEvent","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-animationevent","x":"struct AnimationEvent(Copyable, Movable) An event that fired during Animator.update: the index of the state that owns it and the event's name."},{"t":"cinder.anim.animator.AnimationState","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-animationstate","x":"struct AnimationState(Copyable, Movable) A named animator state: a blend tree, whether its phase loops, and its events. Build it with AnimationState(name, tree^, looping) and register it with Animator.add_state, which validates the tree; add events afterwards with Animator.add_event."},{"t":"cinder.anim.animator.AnimationState.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#animationstate-__init__","x":"__init__( out self, name: String, var tree: BlendTree, looping: Bool = True )"},{"t":"cinder.anim.animator.Animator","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-animator","x":"struct Animator(Movable) Plays the states of one skeleton with crossfades, events and root motion. Owns its skeleton, clips and poses. Author states with add_clip_state / add_state, start one with play (play(state, restart=True) replays a finished non-looping state), then call update(dt) once per fr"},{"t":"cinder.anim.animator.Animator.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-__init__","x":"__init__(out self, var skeleton: Skeleton, var clips: List[Clip])"},{"t":"cinder.anim.animator.Animator.add_clip_state","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-add_clip_state","x":"add_clip_state( mut self, name: String, clip: Int, looping: Bool = True, speed: Float32 = 1.0, ) raises -> Int A state that plays one clip."},{"t":"cinder.anim.animator.Animator.add_state","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-add_state","x":"add_state(mut self, var state: AnimationState) raises -> Int Validates state.tree against the animator's clips, appends the state and returns its index. Raises when the tree has no root, names a clip that does not exist, or has a blend node without children or with a parameter out of range (see Blen"},{"t":"cinder.anim.animator.Animator.add_event","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-add_event","x":"add_event(mut self, state: Int, phase: Float32, name: String) raises Adds an event name to state that fires each time the state's phase passes phase. phase is normalised to the state's cycle and must be in [0, 1). Raises for an unknown state or a phase outside that range."},{"t":"cinder.anim.animator.Animator.find_state","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-find_state","x":"find_state(self, name: String) -> Int Index of the first state called name, or -1 when there is none."},{"t":"cinder.anim.animator.Animator.set_parameter","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-set_parameter","x":"set_parameter(mut self, name: String, value: Float32) raises Sets a blend parameter on every state that declares it."},{"t":"cinder.anim.animator.Animator.play","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-play","x":"play( mut self, state: Int, fade_seconds: Float32 = 0.0, start_phase: Float32 = 0.0, restart: Bool = False, ) raises Switches to state at start_phase, crossfading from the current one over fade_seconds, and clears finished. Playing the state that is already current is a no-op (so calling play every "},{"t":"cinder.anim.animator.Animator.current_state","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-current_state","x":"current_state(self) -> Int Index of the playing state (the incoming one during a crossfade), or -1 before the first play."},{"t":"cinder.anim.animator.Animator.phase","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-phase","x":"phase(self) -> Float32 Normalised phase of the current state: in [0, 1) while looping; a non-looping state holds 1 once it has finished."},{"t":"cinder.anim.animator.Animator.fade_weight","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-fade_weight","x":"fade_weight(self) -> Float32 Weight of the incoming state during a crossfade (1 when none is running)."},{"t":"cinder.anim.animator.Animator.update","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-update","x":"update(mut self, dt: Float32) raises Advances time, evaluates and blends the layers, then applies root motion."},{"t":"cinder.anim.animator.Animator.enable_root_motion","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-enable_root_motion","x":"enable_root_motion(mut self, bone: Int = 0) raises Starts extracting the horizontal motion of bone (the hierarchy root by default)."},{"t":"cinder.anim.animator.Animator.take_root_motion","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-take_root_motion","x":"take_root_motion(mut self) -> Vec3 Horizontal root displacement accumulated since the last call (model space)."},{"t":"cinder.anim.animator.Animator.take_events","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-take_events","x":"take_events(mut self) -> List[AnimationEvent] Returns the events fired since the last call and clears the list. Only the current state fires events; a state that is fading out fires none."},{"t":"cinder.anim.animator.Animator.palette","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-palette","x":"palette(self) -> List[Mat4] Skinning matrices for the current pose in palette (skin joint) order. Allocates a new list; palette_into is the allocation-free form."},{"t":"cinder.anim.animator.Animator.palette_into","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-palette_into","x":"palette_into(mut self, mut out: List[Mat4]) Skinning matrices for the current pose into out (no allocation once sized)."},{"t":"cinder.anim.animator.Animator.bone_matrices","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-bone_matrices","x":"bone_matrices(self) -> List[Mat4] Every bone's matrix in model space for the current pose (attachments, IK, debug)."},{"t":"cinder.anim.animator.Animator.bone_matrices_into","s":"API symbol","h":"method","u":"/docs/api/anim/#animator-bone_matrices_into","x":"bone_matrices_into(self, mut out: List[Mat4]) Every bone's model-space matrix for the current pose into out (resized; no allocation once large enough)."},{"t":"cinder.anim.blend","s":"API symbol","h":"module","u":"/docs/api/anim/#cinderanimblend","x":"Blend trees: clips, 1D and 2D blend nodes, evaluated phase-synchronised. A tree runs on a normalised phase in [0, 1). Every leaf clip plays at phase * duration, so blended cycles (walk and run) stay in step; the phase advances at the weighted average of the leaves' cycle rates. Leaf weights come fro"},{"t":"cinder.anim.blend.BlendNode","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-blendnode","x":"struct BlendNode(Copyable, Movable) One node of a BlendTree: a clip leaf or a 1D/2D blend over child node indices. Created by BlendTree.add_clip, add_blend_1d and add_blend_2d; indices a kind does not use stay -1 and its lists stay empty."},{"t":"cinder.anim.blend.BlendNode.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#blendnode-__init__","x":"__init__(out self, kind: Int)"},{"t":"cinder.anim.blend.BlendTree","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-blendtree","x":"struct BlendTree(Copyable, Movable) Nodes are stored flat; root names the entry node. Build with add_clip, add_blend_1d and add_blend_2d, then set_root."},{"t":"cinder.anim.blend.BlendTree.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-__init__","x":"__init__(out self)"},{"t":"cinder.anim.blend.BlendTree.add_parameter","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-add_parameter","x":"add_parameter(mut self, name: String, initial: Float32 = 0.0) -> Int Declares a blend parameter with an initial value and returns its index, which add_blend_1d / add_blend_2d take. Duplicate names are not rejected; set_parameter updates the first match."},{"t":"cinder.anim.blend.BlendTree.set_parameter","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-set_parameter","x":"set_parameter(mut self, name: String, value: Float32) raises Sets the first parameter called name to value. Raises when the tree has no such parameter."},{"t":"cinder.anim.blend.BlendTree.add_clip","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-add_clip","x":"add_clip(mut self, clip: Int, speed: Float32 = 1.0) -> Int Adds a leaf that plays clip index clip at speed times its natural rate and returns the node index. The clip index is checked by validate, not here."},{"t":"cinder.anim.blend.BlendTree.add_blend_1d","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-add_blend_1d","x":"add_blend_1d(mut self, parameter: Int) -> Int Adds a 1D blend node driven by parameter index parameter and returns the node index. Attach children with add_child; the parameter index is checked by validate."},{"t":"cinder.anim.blend.BlendTree.add_blend_2d","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-add_blend_2d","x":"add_blend_2d(mut self, parameter_x: Int, parameter_y: Int) -> Int Adds a 2D blend node driven by parameters parameter_x and parameter_y and returns the node index. Attach children with add_child; the parameter indices are checked by validate."},{"t":"cinder.anim.blend.BlendTree.add_child","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-add_child","x":"add_child(mut self, node: Int, child: Int, at: Vec2) raises Attaches child to a blend node with full weight at parameter value at (1D nodes use at.x()). Children of a 1D node must be added in increasing order. Raises when an index is out of range, node is a clip, the node is full, a 1D threshold doe"},{"t":"cinder.anim.blend.BlendTree.set_root","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-set_root","x":"set_root(mut self, node: Int) raises Makes node the entry node of the tree. Raises when the index is out of range."},{"t":"cinder.anim.blend.BlendTree.validate","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-validate","x":"validate(self, clip_count: Int) raises Checks that the tree can be evaluated with clip_count clips and raises on the first problem: no root (or one out of range), a clip leaf naming a missing clip, a blend node without children or with a child index out of range, a blend parameter index out of range"},{"t":"cinder.anim.blend.BlendTree.leaf_weights_into","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-leaf_weights_into","x":"leaf_weights_into(self, mut weights: List[Float32]) Weight per node (leaves carry the blended weight, inner nodes their subtree's), summing to one over the leaves, written into weights."},{"t":"cinder.anim.blend.BlendTree.leaf_weights","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-leaf_weights","x":"leaf_weights(self) -> List[Float32] Per-node weights for the current parameters (see leaf_weights_into) in a new list; allocates."},{"t":"cinder.anim.blend.BlendTree.cycle_rate","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-cycle_rate","x":"cycle_rate(self, clips: List[Clip]) -> Float32 Phase advance per second: the weighted average of each leaf's speed / duration."},{"t":"cinder.anim.blend.BlendTree.cycle_rate_with","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-cycle_rate_with","x":"cycle_rate_with( self, clips: List[Clip], mut weights: List[Float32] ) -> Float32 cycle_rate using the caller's weight scratch (no allocation once sized)."},{"t":"cinder.anim.blend.BlendTree.evaluate","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-evaluate","x":"evaluate( self, clips: List[Clip], skeleton: Skeleton, phase: Float32, mut pose: Pose, mut scratch: Pose, ) Blends every weighted leaf at phase into pose (allocates a weight list; the animator uses evaluate_with)."},{"t":"cinder.anim.blend.BlendTree.evaluate_with","s":"API symbol","h":"method","u":"/docs/api/anim/#blendtree-evaluate_with","x":"evaluate_with( self, clips: List[Clip], skeleton: Skeleton, phase: Float32, mut pose: Pose, mut scratch: Pose, mut weights: List[Float32], ) Blends every weighted leaf at phase into pose. scratch is a pose of the same skeleton for the per-leaf samples and weights the per-node weight scratch; callers"},{"t":"cinder.anim.sampling","s":"API symbol","h":"module","u":"/docs/api/anim/#cinderanimsampling","x":"Clip sampling: step, linear and cubic-spline keyframes into a local pose. Pose is the skeleton's local transforms plus morph weights. Sampling starts from the rest pose so channels that are missing from a clip keep the skeleton's values (glTF semantics)."},{"t":"cinder.anim.sampling.Pose","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-pose","x":"struct Pose(Copyable, Movable) A local pose: one parent-space transform per bone plus the morph target weights. Pose(skeleton) starts at the rest pose with zero weights. A pose is tied to the skeleton it was built for: sampling, blending and reset index by bone and never resize."},{"t":"cinder.anim.sampling.Pose.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#pose-__init__","x":"__init__(out self, skeleton: Skeleton)"},{"t":"cinder.anim.sampling.Pose.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#pose-__init__-1","x":"__init__( out self, var local: List[Transform], var morph_weights: List[Float32] )"},{"t":"cinder.anim.sampling.Pose.reset","s":"API symbol","h":"method","u":"/docs/api/anim/#pose-reset","x":"reset(mut self, skeleton: Skeleton) Restores the rest pose of skeleton and zeroes the morph weights in place (no allocation). skeleton must be the one the pose was built for."},{"t":"cinder.anim.sampling.Pose.copy_from","s":"API symbol","h":"method","u":"/docs/api/anim/#pose-copy_from","x":"copy_from(mut self, other: Pose) Element-wise copy (same skeleton), no allocation."},{"t":"cinder.anim.sampling.Pose.blend_with","s":"API symbol","h":"method","u":"/docs/api/anim/#pose-blend_with","x":"blend_with(mut self, b: Pose, t: Float32) self = lerp(self, b, t) component-wise."},{"t":"cinder.anim.sampling.sample_component","s":"API symbol","h":"def","u":"/docs/api/anim/#def-sample_component","x":"def sample_component( ch: ClipChannel, pair: _KeyPair, component: Int ) -> Float32 One output component at the located time."},{"t":"cinder.anim.sampling.sample_rotation","s":"API symbol","h":"def","u":"/docs/api/anim/#def-sample_rotation","x":"def sample_rotation(ch: ClipChannel, pair: _KeyPair) -> Quat Rotations interpolate along the shorter arc (slerp for linear, normalised spline for cubic, as the glTF specification describes)."},{"t":"cinder.anim.sampling.sample_clip","s":"API symbol","h":"def","u":"/docs/api/anim/#def-sample_clip","x":"def sample_clip(clip: Clip, skeleton: Skeleton, time: Float32, mut pose: Pose) Writes every channel of clip at time (seconds, unclamped: before the first key or after the last key the end values hold) into pose. Channels whose node is not in the skeleton are ignored; pose must have been created for "},{"t":"cinder.anim.sampling.wrap_time","s":"API symbol","h":"def","u":"/docs/api/anim/#def-wrap_time","x":"def wrap_time(time: Float32, duration: Float32) -> Float32 Loops time into [0, duration)."},{"t":"cinder.anim.skeleton","s":"API symbol","h":"module","u":"/docs/api/anim/#cinderanimskeleton","x":"Skeletons: a bone hierarchy with rest pose, the skin's joint list and inverse bind matrices. A skeleton is built from a glTF skin (every joint plus the ancestors needed to reach a common root, so animated parents move their children) or read from a cooked .cskel file. Clips keep glTF node indices, w"},{"t":"cinder.anim.skeleton.Skeleton","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-skeleton","x":"struct Skeleton(Copyable, Movable) Bone hierarchy, rest pose and skin data of one glTF skin. Bones are ordered parents first. Build one with skeleton_from_gltf or read_skeleton (both validate it); palette and world_transforms turn a local pose into matrices."},{"t":"cinder.anim.skeleton.Skeleton.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-__init__","x":"__init__(out self)"},{"t":"cinder.anim.skeleton.Skeleton.bone_count","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-bone_count","x":"bone_count(self) -> Int Number of bones (skin joints plus the ancestors needed to reach a common root)."},{"t":"cinder.anim.skeleton.Skeleton.joint_count","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-joint_count","x":"joint_count(self) -> Int Number of skin joints, which is the length of the skinning palette."},{"t":"cinder.anim.skeleton.Skeleton.find_bone","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-find_bone","x":"find_bone(self, name: String) -> Int Index of the first bone called name, or -1 when there is none."},{"t":"cinder.anim.skeleton.Skeleton.bone_of_node","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-bone_of_node","x":"bone_of_node(self, node: Int) -> Int Bone index of glTF node node, or -1 when the node is out of range or not part of the skeleton."},{"t":"cinder.anim.skeleton.Skeleton.world_transforms_into","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-world_transforms_into","x":"world_transforms_into( self, local: List[Transform], mut out: List[Mat4] ) Bone-space matrices from a local pose (parents first), in model space, written into out (resized; no allocation once large enough)."},{"t":"cinder.anim.skeleton.Skeleton.world_transforms","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-world_transforms","x":"world_transforms(self, local: List[Transform]) -> List[Mat4] Model-space bone matrices for the local pose local in a new list; allocates (see world_transforms_into)."},{"t":"cinder.anim.skeleton.Skeleton.palette_into","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-palette_into","x":"palette_into( self, local: List[Transform], mut worlds: List[Mat4], mut out: List[Mat4], ) Skinning matrices in palette order into out; worlds is scratch for the bone matrices."},{"t":"cinder.anim.skeleton.Skeleton.palette","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-palette","x":"palette(self, local: List[Transform]) -> List[Mat4] Skinning matrices in palette order: inverse(mesh node) * bone world * inverse bind."},{"t":"cinder.anim.skeleton.Skeleton.validate","s":"API symbol","h":"method","u":"/docs/api/anim/#skeleton-validate","x":"validate(self) raises Checks the invariants and raises on the first violation: 1 to MAX_BONES bones, one name and rest transform per bone, every parent before its child (or -1), node map and joints inside the bone range (or -1), and one inverse bind matrix per joint."},{"t":"cinder.anim.skeleton.skeleton_from_gltf","s":"API symbol","h":"def","u":"/docs/api/anim/#def-skeleton_from_gltf","x":"def skeleton_from_gltf(asset: GltfAsset, skin: Int) raises -> Skeleton Builds the skeleton of asset.skins[skin]: the joints and every ancestor up to the outermost ancestor shared by all joints (so animated parents are evaluated), in parent-before-child order. The mesh node is the first node using th"},{"t":"cinder.anim.skeleton.write_skeleton","s":"API symbol","h":"def","u":"/docs/api/anim/#def-write_skeleton","x":"def write_skeleton(skeleton: Skeleton) -> List[UInt8] Serialises skeleton to the cooked .cskel format: magic, version, bones (name, parent, rest transform), node map, joints with inverse bind matrices, mesh node and its inverse, morph count. Indices are stored plus one so -1 round-trips as 0. Does n"},{"t":"cinder.anim.skeleton.read_skeleton","s":"API symbol","h":"def","u":"/docs/api/anim/#def-read_skeleton","x":"def read_skeleton(var data: List[UInt8]) raises -> Skeleton Parses a cooked .cskel buffer (consumed) into a validated skeleton. Raises on a wrong magic or version, implausible bone, node or joint counts, data that ends early, trailing bytes, or a result that fails Skeleton.validate."},{"t":"cinder.anim.system","s":"API symbol","h":"module","u":"/docs/api/anim/#cinderanimsystem","x":"The animation system: animators stored in a pool, advanced in parallel on the job system, and published to the ECS as JointPalette components for the render bridge. Animators live in the system (they own clips and poses); entities refer to them with AnimatorRef. update(dt, jobs) runs one job per ani"},{"t":"cinder.anim.system.AnimatorRef","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-animatorref","x":"struct AnimatorRef(Component) Links an entity to AnimationSystem.animators[index]."},{"t":"cinder.anim.system.AnimatorRef.component_id","s":"API symbol","h":"method","u":"/docs/api/anim/#animatorref-component_id","x":"component_id() -> Int"},{"t":"cinder.anim.system.AnimationSystem","s":"API symbol","h":"struct","u":"/docs/api/anim/#struct-animationsystem","x":"struct AnimationSystem(Movable) Owns every animator, updates them in parallel and publishes their palettes to the ECS. Entities refer to animators by index through AnimatorRef; updates counts completed update calls. Jobs receive raw pointers into animators, so the system must stay in place and the l"},{"t":"cinder.anim.system.AnimationSystem.__init__","s":"API symbol","h":"method","u":"/docs/api/anim/#animationsystem-__init__","x":"__init__(out self)"},{"t":"cinder.anim.system.AnimationSystem.add","s":"API symbol","h":"method","u":"/docs/api/anim/#animationsystem-add","x":"add(mut self, var animator: Animator) -> Int Takes ownership of animator and returns its index for AnimatorRef. Call between frames, never while update is running."},{"t":"cinder.anim.system.AnimationSystem.__len__","s":"API symbol","h":"method","u":"/docs/api/anim/#animationsystem-__len__","x":"__len__(self) -> Int"},{"t":"cinder.anim.system.AnimationSystem.clear","s":"API symbol","h":"method","u":"/docs/api/anim/#animationsystem-clear","x":"clear(mut self) Drops every animator (a level restart); AnimatorRefs become stale."},{"t":"cinder.anim.system.AnimationSystem.update","s":"API symbol","h":"method","u":"/docs/api/anim/#animationsystem-update","x":"update( mut self, dt: Float32, jobs: Optional[Pointer[JobSystem, MutUntrackedOrigin]], ) raises Advances every animator by dt. With a job system (and more than one animator) each animator is its own job; otherwise they run inline, one after the other. Either way every animator is updated, and when a"},{"t":"cinder.anim.system.AnimationSystem.publish","s":"API symbol","h":"method","u":"/docs/api/anim/#animationsystem-publish","x":"publish(mut self, mut world: World) raises -> Int Writes a JointPalette for every entity with an AnimatorRef, in place when the component exists (no steady-state allocation). Returns the count."},{"t":"cinder.app","s":"API symbol","h":"module","u":"/docs/api/app/#cinderapp-1","x":"Application runtime: subsystems, the fixed-step game loop, windowed and headless modes."},{"t":"cinder.app.app","s":"API symbol","h":"module","u":"/docs/api/app/#cinderappapp","x":"The application runtime: owns every subsystem, runs the fixed-step game loop and renders. App works in two modes. Windowed: an SDL window, a swapchain and the ImGui debug overlay. Headless (AppConfig.headless, the --headless flag of the sample): no window, no audio device, frames render into an offs"},{"t":"cinder.app.app.ui_consumes_event","s":"API symbol","h":"def","u":"/docs/api/app/#def-ui_consumes_event","x":"def ui_consumes_event(ev: Event, ui_wants_mouse: Bool, ui_wants_keyboard: Bool) -> Bool Whether the debug overlay swallows ev instead of the game's InputMap. While ImGui wants the mouse, mouse presses, motion and wheel go only to the overlay; while it wants the keyboard, key presses do. Key and mous"},{"t":"cinder.app.app.AppConfig","s":"API symbol","h":"struct","u":"/docs/api/app/#struct-appconfig","x":"struct AppConfig(Copyable, Movable) Settings read once by App.__init__; parse_app_args fills them from the command line. Defaults: 1280x720 windowed, 60 Hz fixed step, audio and debug UI on, profiler off, 3 worker threads."},{"t":"cinder.app.app.AppConfig.__init__","s":"API symbol","h":"method","u":"/docs/api/app/#appconfig-__init__","x":"__init__( out self, title: String = String(\"Cinder\"), width: Int = 1280, height: Int = 720, headless: Bool = False, )"},{"t":"cinder.app.app.Game","s":"API symbol","h":"trait","u":"/docs/api/app/#trait-game","x":"trait Game Gameplay hooks called by App.run_frame."},{"t":"cinder.app.app.Game.start","s":"API symbol","h":"method","u":"/docs/api/app/#game-start","x":"start(mut self, mut app: App) raises Called once, at the start of the first run_frame and before its events are polled."},{"t":"cinder.app.app.Game.fixed_update","s":"API symbol","h":"method","u":"/docs/api/app/#game-fixed_update","x":"fixed_update(mut self, mut app: App, dt: Float32) raises Called once per fixed step with the step length in seconds, after physics has stepped and body transforms were pulled into the world."},{"t":"cinder.app.app.Game.update","s":"API symbol","h":"method","u":"/docs/api/app/#game-update","x":"update(mut self, mut app: App, dt: Float32) raises Called once per frame with the frame time in seconds, after the fixed steps and before animation, transform propagation, audio and rendering."},{"t":"cinder.app.app.Game.draw_ui","s":"API symbol","h":"method","u":"/docs/api/app/#game-draw_ui","x":"draw_ui(mut self, mut app: App) raises Called while the debug overlay is built (windowed with debug_ui only, on frames that acquired a swapchain image), before the engine's own windows; make ImGui calls here."},{"t":"cinder.app.app.App","s":"API symbol","h":"struct","u":"/docs/api/app/#struct-app","x":"struct App(Movable) The engine runtime: owns the native library, the platform and window (windowed), the GPU device, renderer, ECS world, physics, audio, animation, input map, scene registry and assets, and drives them from run_frame. Construction raises when a subsystem fails to start. Fields are d"},{"t":"cinder.app.app.App.__init__","s":"API symbol","h":"method","u":"/docs/api/app/#app-__init__","x":"__init__(out self, var config: AppConfig) raises"},{"t":"cinder.app.app.App.mount_assets","s":"API symbol","h":"method","u":"/docs/api/app/#app-mount_assets","x":"mount_assets(mut self, root: String) raises Attaches a cooked-asset directory (its manifest.txt)."},{"t":"cinder.app.app.App.log","s":"API symbol","h":"method","u":"/docs/api/app/#app-log","x":"log(mut self, level: LogLevel, category: String, message: String) Records a message in logs, the in-memory sink shown in the debug UI's Log tab."},{"t":"cinder.app.app.App.run_frame","s":"API symbol","h":"method","u":"/docs/api/app/#app-run_frame","x":"run_frame[ G: Game ](mut self, mut game: G, frame_seconds: Float64 = -1.0) raises -> Bool One frame: events, fixed steps, per-frame update, render, present. frame_seconds overrides the measured frame time (tests and benchmarks run deterministically). Returns False once a quit was requested."},{"t":"cinder.app.app.App.read_frame","s":"API symbol","h":"method","u":"/docs/api/app/#app-read_frame","x":"read_frame(mut self) raises -> Image Headless only: the last rendered frame as an image."},{"t":"cinder.app.app.App.request_quit","s":"API symbol","h":"method","u":"/docs/api/app/#app-request_quit","x":"request_quit(mut self) Makes run_frame return False. Requested from fixed_update, update or draw_ui, the current frame still completes; later calls return right after polling events."},{"t":"cinder.app.app.App.gpu_error_count","s":"API symbol","h":"method","u":"/docs/api/app/#app-gpu_error_count","x":"gpu_error_count(self) -> Int Uncaptured wgpu validation errors seen since the device was created."},{"t":"cinder.app.app.App.live_objects","s":"API symbol","h":"method","u":"/docs/api/app/#app-live_objects","x":"live_objects(self) -> Int Physics objects plus audio sounds and buses alive: the game-owned objects that must be zero before shutdown (the leak report). GPU objects are not counted: the engine's own render targets, pipelines and renderer meshes stay alive for the App's whole life, so a total includi"},{"t":"cinder.app.cli","s":"API symbol","h":"module","u":"/docs/api/app/#cinderappcli","x":"Command-line options shared by every Cinder executable (--headless for tests and benchmarks)."},{"t":"cinder.app.cli.parse_app_args","s":"API symbol","h":"def","u":"/docs/api/app/#def-parse_app_args","x":"def parse_app_args( args: List[String], mut config: AppConfig ) raises -> List[String] Applies engine options to config and returns the arguments it did not consume. Options: --headless, --profile, --no-audio, --no-debug-ui, --width N, --height N, --title TEXT. Everything else (positional arguments "},{"t":"cinder.assets","s":"API symbol","h":"module","u":"/docs/api/assets/#cinderassets-1","x":"Assets: glTF import into engine meshes, materials, textures, skins and scenes."},{"t":"cinder.assets.cook","s":"API symbol","h":"module","u":"/docs/api/assets/#cinderassetscook","x":"The asset cooker: glTF sources become cooked meshes, materials and animation clips. Cooking is incremental and keyed on content: the manifest (manifest.txt in the output directory) records, per logical asset, the FNV-1a hash of the source file and the output file name. A source whose hash is unchang"},{"t":"cinder.assets.cook.ManifestEntry","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-manifestentry","x":"struct ManifestEntry(Copyable, Movable) One manifest line: logical asset path, FNV-1a source hash (hex64) and output file name. file is relative to the output directory and named by asset_file_name(logical, ...)."},{"t":"cinder.assets.cook.CookManifest","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-cookmanifest","x":"struct CookManifest(Movable) The cook manifest (<out_dir>/manifest.txt), shared by the cooker and AssetServer. One entry per logical asset; lookups are linear scans. Re-cooking a source drops (prune) the entries of outputs it no longer produces."},{"t":"cinder.assets.cook.CookManifest.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#cookmanifest-__init__","x":"__init__(out self)"},{"t":"cinder.assets.cook.CookManifest.load","s":"API symbol","h":"method","u":"/docs/api/assets/#cookmanifest-load","x":"load(path: String) raises -> CookManifest Reads a manifest; a missing file yields an empty one. Lines without exactly three space-separated fields (<logical> <hash> <file>) are skipped, so logical paths must not contain spaces. Raises when the file exists but cannot be read."},{"t":"cinder.assets.cook.CookManifest.save","s":"API symbol","h":"method","u":"/docs/api/assets/#cookmanifest-save","x":"save(self, path: String) raises Writes a <logical> <hash> <file> line per entry, replacing path; raises on error."},{"t":"cinder.assets.cook.CookManifest.find","s":"API symbol","h":"method","u":"/docs/api/assets/#cookmanifest-find","x":"find(self, logical: String) -> Int Index of the entry for logical, or -1."},{"t":"cinder.assets.cook.CookManifest.record","s":"API symbol","h":"method","u":"/docs/api/assets/#cookmanifest-record","x":"record(mut self, logical: String, source_hash: String, file: String) Replaces the entry for logical, or appends one when there is none."},{"t":"cinder.assets.cook.CookManifest.prune","s":"API symbol","h":"method","u":"/docs/api/assets/#cookmanifest-prune","x":"prune( mut self, root: String, produced: List[String] ) -> List[ManifestEntry] Removes the entries under root (logical paths starting with it, root itself excepted) that are not in produced, and returns them in manifest order."},{"t":"cinder.assets.cook.CookManifest.up_to_date","s":"API symbol","h":"method","u":"/docs/api/assets/#cookmanifest-up_to_date","x":"up_to_date( self, logical: String, source_hash: String, out_dir: String ) -> Bool Whether logical is recorded with source_hash and its file exists in out_dir."},{"t":"cinder.assets.cook.CookReport","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-cookreport","x":"struct CookReport(Movable) What one cook_gltf call did. cooked counts outputs written; skipped counts outputs found up to date when the whole source was skipped (the two never mix in one report)."},{"t":"cinder.assets.cook.CookReport.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#cookreport-__init__","x":"__init__(out self)"},{"t":"cinder.assets.cook.cook_gltf","s":"API symbol","h":"def","u":"/docs/api/assets/#def-cook_gltf","x":"def cook_gltf( lib: NativePtr, source_path: String, prefix: String, out_dir: String, mut manifest: CookManifest, ) raises -> CookReport Cooks every mesh primitive, material and animation of a glTF file. prefix is the logical name of the source (e.g. models/Fox); outputs are <prefix>#mesh<i>/prim<p>,"},{"t":"cinder.assets.cooked","s":"API symbol","h":"module","u":"/docs/api/assets/#cinderassetscooked","x":"Cooked asset formats (little-endian binary, versioned, bounds-checked on read). - .cmesh: engine vertex stream (56-byte vertices), optional skin stream, u32 indices with LOD ranges, bounds, morph deltas. Written by the cooker from glTF primitives (tangents generated, LODs built with meshoptimizer), "},{"t":"cinder.assets.cooked.asset_id","s":"API symbol","h":"def","u":"/docs/api/assets/#def-asset_id","x":"def asset_id(logical_path: String) -> UInt64 Stable 64-bit id of a logical asset path: its FNV-1a hash."},{"t":"cinder.assets.cooked.asset_file_name","s":"API symbol","h":"def","u":"/docs/api/assets/#def-asset_file_name","x":"def asset_file_name(logical_path: String, extension: String) -> String <16 hex digits>.<extension>: content-addressed by logical path, not by contents."},{"t":"cinder.assets.cooked.CookedMesh","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-cookedmesh","x":"struct CookedMesh(Movable) What .cmesh holds. Vertex and index bytes are already in GPU layout."},{"t":"cinder.assets.cooked.CookedMesh.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#cookedmesh-__init__","x":"__init__(out self)"},{"t":"cinder.assets.cooked.cook_mesh","s":"API symbol","h":"def","u":"/docs/api/assets/#def-cook_mesh","x":"def cook_mesh( data: MeshData, lod_indices: List[List[UInt32]], material: String ) -> CookedMesh Packs a validated MeshData and its LOD index lists."},{"t":"cinder.assets.cooked.write_mesh","s":"API symbol","h":"def","u":"/docs/api/assets/#def-write_mesh","x":"def write_mesh(mesh: CookedMesh) -> List[UInt8] Serialises a CookedMesh to .cmesh bytes (version MESH_VERSION, little-endian). Layout: magic, version, vertex count, byte lengths of the vertex, skin and index streams, LOD count, bounds min and max, morph target count and byte length, material path (u"},{"t":"cinder.assets.cooked.Cursor","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-cursor","x":"struct Cursor(Movable) Bounds-checked little-endian reader over an owned byte buffer (the cooked-format readers). Every read raises when it would run past the end of the data."},{"t":"cinder.assets.cooked.Cursor.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-__init__","x":"__init__(out self, var data: List[UInt8])"},{"t":"cinder.assets.cooked.Cursor.remaining","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-remaining","x":"remaining(self) -> Int Bytes left after the read position."},{"t":"cinder.assets.cooked.Cursor.u8","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-u8","x":"u8(mut self) raises -> UInt8 Reads one byte; raises at the end of the data."},{"t":"cinder.assets.cooked.Cursor.u32","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-u32","x":"u32(mut self) raises -> UInt32 Reads a little-endian u32; raises when fewer than 4 bytes remain."},{"t":"cinder.assets.cooked.Cursor.f32","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-f32","x":"f32(mut self) raises -> Float32 Reads a float32 from the bit pattern of a little-endian u32."},{"t":"cinder.assets.cooked.Cursor.bytes","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-bytes","x":"bytes(mut self, count: Int) raises -> List[UInt8] Copies the next count bytes; raises if count is negative or above remaining()."},{"t":"cinder.assets.cooked.Cursor.string","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-string","x":"string(mut self) raises -> String Reads a u32 byte length and that many bytes of UTF-8 text; raises when the length exceeds the data or the bytes are not valid UTF-8."},{"t":"cinder.assets.cooked.Cursor.magic","s":"API symbol","h":"method","u":"/docs/api/assets/#cursor-magic","x":"magic(mut self, expected: String) raises Reads the bytes of expected; raises bad magic at the first mismatch."},{"t":"cinder.assets.cooked.read_mesh","s":"API symbol","h":"def","u":"/docs/api/assets/#def-read_mesh","x":"def read_mesh(var data: List[UInt8]) raises -> CookedMesh Parses .cmesh bytes into a CookedMesh, taking ownership of data. Raises on a wrong magic or version; stream lengths that disagree with the vertex count (56 bytes per vertex, 24 per skin vertex, 32 per vertex and morph target); index bytes not"},{"t":"cinder.assets.cooked.ClipChannel","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-clipchannel","x":"struct ClipChannel(Copyable, Movable) Keyframes for one node property. path: 0 translation, 1 rotation, 2 scale, 3 weights. interpolation: 0 linear, 1 step, 2 cubic spline. values holds components floats per key (3 per cubic-spline key: in-tangent, value, out-tangent)."},{"t":"cinder.assets.cooked.Clip","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-clip","x":"struct Clip(Copyable, Movable) A cooked animation clip: name, duration (the latest keyframe time) and its channels."},{"t":"cinder.assets.cooked.Clip.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#clip-__init__","x":"__init__(out self, name: String)"},{"t":"cinder.assets.cooked.write_clip","s":"API symbol","h":"def","u":"/docs/api/assets/#def-write_clip","x":"def write_clip(clip: Clip) -> List[UInt8] Serialises a clip to .cclip bytes (version CLIP_VERSION, little-endian). Layout: magic, version, name, duration, channel count, then per channel its node, path, interpolation, components, key count and value count followed by the times and values."},{"t":"cinder.assets.cooked.read_clip","s":"API symbol","h":"def","u":"/docs/api/assets/#def-read_clip","x":"def read_clip(var data: List[UInt8]) raises -> Clip Parses .cclip bytes into a Clip, taking ownership of data. Raises on a wrong magic or version, more than 100000 channels, a path above 3, an interpolation above 2, components outside 1..64, a value count other than keys * components (* 3 for cubic "},{"t":"cinder.assets.cooked.CookedMaterial","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-cookedmaterial","x":"struct CookedMaterial(Copyable, Movable) Material parameters plus the logical image path per map slot (empty when absent)."},{"t":"cinder.assets.cooked.CookedMaterial.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#cookedmaterial-__init__","x":"__init__(out self, var material: Material)"},{"t":"cinder.assets.cooked.write_material","s":"API symbol","h":"def","u":"/docs/api/assets/#def-write_material","x":"def write_material(cm: CookedMaterial) -> List[UInt8] Serialises a CookedMaterial to .cmat bytes (version MATERIAL_VERSION, little-endian). Layout: magic, version, name, factors, alpha mode and cutoff, double-sided flag, UV transform, then per map slot (5) the texcoord set and image path, then the s"},{"t":"cinder.assets.cooked.read_material","s":"API symbol","h":"def","u":"/docs/api/assets/#def-read_material","x":"def read_material(var data: List[UInt8]) raises -> CookedMaterial Parses .cmat bytes into a CookedMaterial, taking ownership of data. Raises on a wrong magic or version, an alpha mode above 2, truncated data or trailing bytes. Only the parameters and image paths are read; AssetServer resolves the te"},{"t":"cinder.assets.gltf","s":"API symbol","h":"module","u":"/docs/api/assets/#cinderassetsgltf","x":"glTF 2.0 import through cgltf: meshes (with tangents, second UV set, skinning, morph targets), metallic-roughness materials (KHR_texture_transform, KHR_materials_emissive_strength), images (file, data URI or GLB buffer view), nodes, skins and scenes. load_gltf produces a CPU-side GltfAsset; instanti"},{"t":"cinder.assets.gltf.GltfImage","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfimage","x":"struct GltfImage(Copyable, Movable) An image decoded on the CPU by load_gltf, with its glTF name (empty when unnamed)."},{"t":"cinder.assets.gltf.GltfTexture","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltftexture","x":"struct GltfTexture(ImplicitlyCopyable, Movable) A glTF texture: image index (-1 without one) plus sampler settings. linear_filter is True for a linear or unspecified magnification filter; address_mode is the WGPU address mode from the sampler's wrap_s (repeat when there is no sampler)."},{"t":"cinder.assets.gltf.GltfMaterial","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfmaterial","x":"struct GltfMaterial(Copyable, Movable) Engine material parameters plus the asset texture index per map (-1 when absent)."},{"t":"cinder.assets.gltf.GltfMaterial.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#gltfmaterial-__init__","x":"__init__(out self, var material: Material)"},{"t":"cinder.assets.gltf.GltfPrimitive","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfprimitive","x":"struct GltfPrimitive(Copyable, Movable) A triangle primitive as validated MeshData and its material index (-1 when none). Point, line and strip primitives are dropped on import."},{"t":"cinder.assets.gltf.GltfMesh","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfmesh","x":"struct GltfMesh(Copyable, Movable) A glTF mesh: name, its triangle primitives and the mesh's default morph weights."},{"t":"cinder.assets.gltf.GltfNode","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfnode","x":"struct GltfNode(Copyable, Movable) A scene node; every reference is an index into GltfAsset, -1 when absent. local and world are computed by cgltf at import (the world matrix walks the parents); weights are the node's morph weight overrides (empty when it has none)."},{"t":"cinder.assets.gltf.GltfSkin","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfskin","x":"struct GltfSkin(Copyable, Movable) Joint node indices and an inverse bind matrix each (identity where the file has none)."},{"t":"cinder.assets.gltf.GltfChannel","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfchannel","x":"struct GltfChannel(Copyable, Movable) One animated node property. path: 0 translation, 1 rotation, 2 scale, 3 weights; interpolation: 0 linear, 1 step, 2 cubic spline (values hold in-tangent, value, out-tangent)."},{"t":"cinder.assets.gltf.GltfAnimation","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfanimation","x":"struct GltfAnimation(Copyable, Movable) An animation: name, duration (latest keyframe time over its channels) and channels. Channels without a sampler, target node or supported target path are skipped on import."},{"t":"cinder.assets.gltf.GltfAnimation.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#gltfanimation-__init__","x":"__init__(out self, name: String)"},{"t":"cinder.assets.gltf.SceneBounds","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-scenebounds","x":"struct SceneBounds(ImplicitlyCopyable, Movable) Axis-aligned box from GltfAsset.world_bounds; all zero when the asset has no meshes."},{"t":"cinder.assets.gltf.SceneBounds.centre","s":"API symbol","h":"method","u":"/docs/api/assets/#scenebounds-centre","x":"centre(self) -> Vec3 Midpoint of the box."},{"t":"cinder.assets.gltf.SceneBounds.radius","s":"API symbol","h":"method","u":"/docs/api/assets/#scenebounds-radius","x":"radius(self) -> Float32 Half the diagonal: the radius of the sphere around centre that encloses the box."},{"t":"cinder.assets.gltf.GltfAsset","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-gltfasset","x":"struct GltfAsset(Movable) Everything load_gltf imported from one file, CPU side; cross references are list indices. roots holds the default scene's root nodes, or every parentless node when the file has no default scene."},{"t":"cinder.assets.gltf.GltfAsset.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#gltfasset-__init__","x":"__init__(out self, path: String)"},{"t":"cinder.assets.gltf.GltfAsset.world_bounds","s":"API symbol","h":"method","u":"/docs/api/assets/#gltfasset-world_bounds","x":"world_bounds(self) -> SceneBounds Axis-aligned bounds of every mesh instance in world space. Skinned meshes are posed on the CPU with their bind-pose joint matrices (what the GPU draws at rest)."},{"t":"cinder.assets.gltf.load_gltf","s":"API symbol","h":"def","u":"/docs/api/assets/#def-load_gltf","x":"def load_gltf(lib: NativePtr, path: String) raises -> GltfAsset Parses a .gltf or .glb file (and its buffers and images) into a GltfAsset."},{"t":"cinder.assets.gltf.UploadedGltf","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-uploadedgltf","x":"struct UploadedGltf(Movable) Renderer ids for an uploaded asset: mesh_ids[mesh][primitive], material_ids[material] and a default material for primitives without one."},{"t":"cinder.assets.gltf.UploadedGltf.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#uploadedgltf-__init__","x":"__init__(out self)"},{"t":"cinder.assets.gltf.upload_gltf","s":"API symbol","h":"def","u":"/docs/api/assets/#def-upload_gltf","x":"def upload_gltf( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, lods: Bool = True, ) raises -> UploadedGltf Uploads every image, material and primitive of asset to the renderer. Base colour and emissive images are sRGB; the rest are linear. Materials without textures share the rendere"},{"t":"cinder.assets.gltf.instantiate","s":"API symbol","h":"def","u":"/docs/api/assets/#def-instantiate","x":"def instantiate( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, mut scene: RenderScene, lods: Bool = True, ) raises -> Int Uploads the asset and appends its instances, joints and morph weights to scene. Returns the number of instances added. Base colour and emissive images are sRGB; t"},{"t":"cinder.assets.runtime","s":"API symbol","h":"module","u":"/docs/api/assets/#cinderassetsruntime","x":"Runtime asset server: asynchronous loading of cooked assets on the job system, reference counting, and hot reload. Loading a cooked mesh has two halves: a job reads and parses the file (CPU only, any thread) into a heap slot; update() on the main thread uploads finished slots to the GPU through the "},{"t":"cinder.assets.runtime.AssetState","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-assetstate","x":"struct AssetState(Equatable, ImplicitlyCopyable, RegisterPassable) Load state of an AssetServer record: loading, ready or failed."},{"t":"cinder.assets.runtime.AssetState.loading","s":"API symbol","h":"method","u":"/docs/api/assets/#assetstate-loading","x":"loading() -> AssetState Load started and not finished; a material also stays here while its textures load."},{"t":"cinder.assets.runtime.AssetState.ready","s":"API symbol","h":"method","u":"/docs/api/assets/#assetstate-ready","x":"ready() -> AssetState Loaded; the accessors return data."},{"t":"cinder.assets.runtime.AssetState.failed","s":"API symbol","h":"method","u":"/docs/api/assets/#assetstate-failed","x":"failed() -> AssetState The load failed (see AssetServer.error) or the record was released."},{"t":"cinder.assets.runtime.AssetRecord","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-assetrecord","x":"struct AssetRecord(Movable) One logical asset known to an AssetServer and its loaded result. kind is 0 mesh, 1 clip, 2 texture, 3 material. Records are never removed: releasing the last reference leaves the record with state failed and error released (and an empty logical, so loading the path again "},{"t":"cinder.assets.runtime.AssetRecord.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#assetrecord-__init__","x":"__init__(out self, logical: String, file: String, kind: Int)"},{"t":"cinder.assets.runtime.AssetServer","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-assetserver","x":"struct AssetServer(Movable) Loads cooked assets listed in <root>/manifest.txt, shared by logical path and refcounted. Jobs only read, parse and transcode files; update does every GPU upload. All methods are main-thread calls (there are no locks). Indices from load_* stay valid for the server's lifet"},{"t":"cinder.assets.runtime.AssetServer.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-__init__","x":"__init__( out self, lib: NativePtr, jobs: Pointer[JobSystem, MutUntrackedOrigin], renderer: Pointer[Renderer, MutUntrackedOrigin], root: String, ) raises"},{"t":"cinder.assets.runtime.AssetServer.load_mesh","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-load_mesh","x":"load_mesh(mut self, logical: String) raises -> Int Returns the asset index (also a reference). Loading starts immediately on a job. Raises when logical is not in the manifest or is known as another kind of asset. A known path returns its existing record (refcount + 1); one whose load failed is loade"},{"t":"cinder.assets.runtime.AssetServer.load_clip","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-load_clip","x":"load_clip(mut self, logical: String) raises -> Int Returns the asset index of a cooked clip (also a reference); a job parses the file. Same sharing, kind check and retry of failed loads as load_mesh."},{"t":"cinder.assets.runtime.AssetServer.load_texture","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-load_texture","x":"load_texture(mut self, logical: String) raises -> Int Returns the asset index of a cooked KTX2 texture (also a reference). The job reads and transcodes it (BC7 when the device supports block compression, else RGBA8); update uploads it. Same sharing, kind check and retry of failed loads as load_mesh."},{"t":"cinder.assets.runtime.AssetServer.load_material","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-load_material","x":"load_material(mut self, logical: String) raises -> Int Loads a cooked material and every texture it references; ready when all of them are. Same sharing, kind check and retry of failed loads as load_mesh."},{"t":"cinder.assets.runtime.AssetServer.material_index","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-material_index","x":"material_index(self, index: Int) raises -> Int Renderer material index of a ready material; raises otherwise."},{"t":"cinder.assets.runtime.AssetServer.texture_view","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-texture_view","x":"texture_view(self, index: Int) raises -> TextureViewHandle View of a ready texture; raises otherwise. A hot reload destroys it for a new one."},{"t":"cinder.assets.runtime.AssetServer.update","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-update","x":"update(mut self) raises -> Int Main thread: finishes loads whose job completed (GPU upload for meshes). Returns how many assets became ready or failed this call."},{"t":"cinder.assets.runtime.AssetServer.wait_all","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-wait_all","x":"wait_all(mut self) raises Blocks until every pending load is applied (tests, loading screens)."},{"t":"cinder.assets.runtime.AssetServer.state","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-state","x":"state(self, index: Int) -> AssetState State of the record at index (failed once released)."},{"t":"cinder.assets.runtime.AssetServer.mesh_index","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-mesh_index","x":"mesh_index(self, index: Int) raises -> Int Renderer mesh index of a ready asset, stable across hot reloads; raises when not ready. The kind is not checked: a ready asset that is not a mesh returns -1."},{"t":"cinder.assets.runtime.AssetServer.clip","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-clip","x":"clip(self, index: Int) raises -> Clip A copy of a ready clip with all its keyframes; raises when not a ready clip."},{"t":"cinder.assets.runtime.AssetServer.error","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-error","x":"error(self, index: Int) -> String Message of a failed load (released after the last release), empty otherwise."},{"t":"cinder.assets.runtime.AssetServer.refcount","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-refcount","x":"refcount(self, index: Int) -> Int Outstanding references to the record at index; 0 once released."},{"t":"cinder.assets.runtime.AssetServer.release","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-release","x":"release(mut self, index: Int) raises Drops one reference; the last one frees what the asset holds on the GPU (mesh, texture, renderer material and its sampler), releases a material's textures and forgets the asset."},{"t":"cinder.assets.runtime.AssetServer.check_for_changes","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-check_for_changes","x":"check_for_changes(mut self) -> Int Starts a reload for every ready asset whose cooked file changed. Returns how many."},{"t":"cinder.assets.runtime.AssetServer.live_count","s":"API symbol","h":"method","u":"/docs/api/assets/#assetserver-live_count","x":"live_count(self) -> Int Number of records with a positive refcount."},{"t":"cinder.assets.textures","s":"API symbol","h":"module","u":"/docs/api/assets/#cinderassetstextures","x":"KTX2 textures: cooking (BasisU ETC1S or UASTC with full mip chains) and runtime loading (transcode to BC7 when the device has block compression, otherwise to RGBA8), through libktx."},{"t":"cinder.assets.textures.cook_texture_ktx2","s":"API symbol","h":"def","u":"/docs/api/assets/#def-cook_texture_ktx2","x":"def cook_texture_ktx2( lib: NativePtr, img: Image, path: String, srgb: Bool, normal_map: Bool, uastc: Bool, quality: Int = 128, ) raises Writes img with a full mip chain as a BasisU-compressed KTX2 file. uastc picks the high-quality UASTC codec (normal maps, detail textures); otherwise ETC1S at qual"},{"t":"cinder.assets.textures.LoadedTexture","s":"API symbol","h":"struct","u":"/docs/api/assets/#struct-loadedtexture","x":"struct LoadedTexture(Movable) A KTX2 file transcoded on the CPU: one byte list per mip level, ready for upload_loaded."},{"t":"cinder.assets.textures.LoadedTexture.__init__","s":"API symbol","h":"method","u":"/docs/api/assets/#loadedtexture-__init__","x":"__init__(out self, width: Int, height: Int, bc7: Bool, srgb: Bool)"},{"t":"cinder.assets.textures.load_ktx2","s":"API symbol","h":"def","u":"/docs/api/assets/#def-load_ktx2","x":"def load_ktx2( lib: NativePtr, path: String, prefer_bc7: Bool ) raises -> LoadedTexture Reads and transcodes a KTX2 file (CPU only; safe on a job thread)."},{"t":"cinder.assets.textures.upload_loaded","s":"API symbol","h":"def","u":"/docs/api/assets/#def-upload_loaded","x":"def upload_loaded( mut res: Resources, label: String, loaded: LoadedTexture ) raises -> Tuple[TextureHandle, TextureViewHandle] Creates the GPU texture (BC7 or RGBA8, sRGB variants when flagged) and uploads every level."},{"t":"cinder.audio","s":"API symbol","h":"module","u":"/docs/api/audio/#cinderaudio-1","x":"Audio over miniaudio: buses, decoded and streamed sounds, one-shots, 3D spatialisation, a listener, and the ECS bridge that follows entities and the camera."},{"t":"cinder.audio.engine","s":"API symbol","h":"module","u":"/docs/api/audio/#cinderaudioengine","x":"Audio over miniaudio: buses, decoded and streamed sounds, one-shots, 3D spatialisation and a listener. The engine can run headless (no device) for tests and servers; frames are then pulled with read_frames. Sounds live in the shim (miniaudio structs are opaque to Mojo) and are tracked here with gene"},{"t":"cinder.audio.engine.SoundHandle","s":"API symbol","h":"struct","u":"/docs/api/audio/#struct-soundhandle","x":"struct SoundHandle(Equatable, ImplicitlyCopyable, Movable) Generational handle to a sound owned by AudioEngine: a slot index and that slot's generation. It goes stale once the sound is released (one-shots are released by AudioEngine.update without any call of yours); AudioEngine.is_valid tells, and "},{"t":"cinder.audio.engine.SoundHandle.null","s":"API symbol","h":"method","u":"/docs/api/audio/#soundhandle-null","x":"null() -> SoundHandle The invalid handle (index -1); it converts to False."},{"t":"cinder.audio.engine.Attenuation","s":"API symbol","h":"struct","u":"/docs/api/audio/#struct-attenuation","x":"struct Attenuation(Equatable, ImplicitlyCopyable, Movable) Distance attenuation model of a spatial sound, mapped to miniaudio's models by AudioEngine.set_attenuation."},{"t":"cinder.audio.engine.Attenuation.none","s":"API symbol","h":"method","u":"/docs/api/audio/#attenuation-none","x":"none() -> Attenuation No distance attenuation."},{"t":"cinder.audio.engine.Attenuation.inverse","s":"API symbol","h":"method","u":"/docs/api/audio/#attenuation-inverse","x":"inverse() -> Attenuation miniaudio's inverse-distance model; the SoundDesc default."},{"t":"cinder.audio.engine.Attenuation.linear","s":"API symbol","h":"method","u":"/docs/api/audio/#attenuation-linear","x":"linear() -> Attenuation miniaudio's linear model."},{"t":"cinder.audio.engine.Attenuation.exponential","s":"API symbol","h":"method","u":"/docs/api/audio/#attenuation-exponential","x":"exponential() -> Attenuation miniaudio's exponential model."},{"t":"cinder.audio.engine.SoundDesc","s":"API symbol","h":"struct","u":"/docs/api/audio/#struct-sounddesc","x":"struct SoundDesc(Copyable, Movable) How AudioEngine.load creates a sound. The constructor takes the file path, bus index, streamed, looping and spatial; the other fields keep their defaults until set on the value: volume 1, pitch 1, inverse attenuation with min_distance 1 m, max_distance 100 m and r"},{"t":"cinder.audio.engine.SoundDesc.__init__","s":"API symbol","h":"method","u":"/docs/api/audio/#sounddesc-__init__","x":"__init__( out self, path: String, bus: Int = 0, streamed: Bool = False, looping: Bool = False, spatial: Bool = True, )"},{"t":"cinder.audio.engine.AudioEngine","s":"API symbol","h":"struct","u":"/docs/api/audio/#struct-audioengine","x":"struct AudioEngine(Movable) Owns the miniaudio engine, its mixing buses and every loaded sound. Bus 0 is the master bus and lives as long as the engine; buses made with create_bus are destroyed with destroy_bus. Sounds are addressed by SoundHandle and freed with release (one-shots by update); the de"},{"t":"cinder.audio.engine.AudioEngine.__init__","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-__init__","x":"__init__( out self, lib: NativePtr, headless: Bool = False, sample_rate: Int = 48000, channels: Int = 2, ) raises"},{"t":"cinder.audio.engine.AudioEngine.create_bus","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-create_bus","x":"create_bus(mut self, name: String, parent: Int = 0) raises -> Int A mixing bus under parent (0 = master, -1 = the engine endpoint). Returns its index; destroy it with destroy_bus. Raises when parent does not exist (or was destroyed) or miniaudio fails to create the group."},{"t":"cinder.audio.engine.AudioEngine.destroy_bus","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-destroy_bus","x":"destroy_bus(mut self, bus: Int) raises Destroys a bus made with create_bus; its index stays invalid (indices are not reused) and live_buses() drops by one. Raises for the master bus (0, owned by the engine), a bus that does not exist or was already destroyed, and a bus that still has sounds or live "},{"t":"cinder.audio.engine.AudioEngine.find_bus","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-find_bus","x":"find_bus(self, name: String) -> Int Index of the first live bus called name (the master bus is 0), or -1 when there is none."},{"t":"cinder.audio.engine.AudioEngine.set_bus_volume","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_bus_volume","x":"set_bus_volume(self, bus: Int, volume: Float32) raises Sets the linear volume of bus bus (1 is unity), which scales every sound and bus routed through it. Raises when the bus does not exist."},{"t":"cinder.audio.engine.AudioEngine.bus_volume","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-bus_volume","x":"bus_volume(self, bus: Int) raises -> Float32 Linear volume of bus bus. Raises when the bus does not exist."},{"t":"cinder.audio.engine.AudioEngine.set_master_volume","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_master_volume","x":"set_master_volume(self, volume: Float32) Sets the engine's own output volume (linear), separate from the master bus volume; miniaudio's result code is ignored."},{"t":"cinder.audio.engine.AudioEngine.live_buses","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-live_buses","x":"live_buses(self) -> Int Buses created with create_bus and not yet destroyed with destroy_bus (the master bus is the engine's own); should be zero before the engine is destroyed."},{"t":"cinder.audio.engine.AudioEngine.load","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-load","x":"load(mut self, desc: SoundDesc) raises -> SoundHandle Creates a sound from a file (WAV, FLAC, MP3, Vorbis). It starts stopped."},{"t":"cinder.audio.engine.AudioEngine.play_one_shot","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-play_one_shot","x":"play_one_shot( mut self, var desc: SoundDesc, position: Vec3 ) raises -> SoundHandle Loads, positions and starts a sound that is released when it ends."},{"t":"cinder.audio.engine.AudioEngine.release","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-release","x":"release(mut self, handle: SoundHandle) raises Destroys the sound and invalidates handle and its copies; the slot is reused with a new generation. Raises on a stale handle, so do not release a one-shot that update already released."},{"t":"cinder.audio.engine.AudioEngine.is_valid","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-is_valid","x":"is_valid(self, handle: SoundHandle) -> Bool Whether handle refers to a live sound (in range, same generation, not released). Never raises."},{"t":"cinder.audio.engine.AudioEngine.live_sounds","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-live_sounds","x":"live_sounds(self) -> Int Number of sounds not yet released, one-shots included; should be zero before the engine is destroyed."},{"t":"cinder.audio.engine.AudioEngine.play","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-play","x":"play(self, handle: SoundHandle) raises Starts the sound. Raises on a stale handle or when miniaudio fails to start it."},{"t":"cinder.audio.engine.AudioEngine.stop","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-stop","x":"stop(self, handle: SoundHandle) raises Stops the sound without releasing it. Raises on a stale handle; miniaudio's result code is ignored."},{"t":"cinder.audio.engine.AudioEngine.is_playing","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-is_playing","x":"is_playing(self, handle: SoundHandle) raises -> Bool Whether the sound is playing. Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.at_end","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-at_end","x":"at_end(self, handle: SoundHandle) raises -> Bool Whether the sound has reached the end of its data. Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.seek","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-seek","x":"seek(self, handle: SoundHandle, frame: Int) raises Moves the playback cursor to PCM frame frame. Raises on a stale handle; miniaudio's result code is ignored."},{"t":"cinder.audio.engine.AudioEngine.set_volume","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_volume","x":"set_volume(self, handle: SoundHandle, volume: Float32) raises Sets the sound's linear volume (1 is unity). Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.set_pitch","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_pitch","x":"set_pitch(self, handle: SoundHandle, pitch: Float32) raises Sets the sound's pitch multiplier (1 is the original pitch). Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.set_pan","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_pan","x":"set_pan(self, handle: SoundHandle, pan: Float32) raises Sets the sound's stereo pan (-1 left, 0 centre, 1 right); the only positioning control of a non-spatial sound. Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.set_looping","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_looping","x":"set_looping(self, handle: SoundHandle, looping: Bool) raises Turns looping on or off. Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.fade","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-fade","x":"fade( self, handle: SoundHandle, from_volume: Float32, to_volume: Float32, milliseconds: Int, ) raises Fades the sound's volume from from_volume to to_volume over milliseconds. Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.set_position","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_position","x":"set_position(self, handle: SoundHandle, p: Vec3) raises Sets the sound's world position in metres (used while spatialisation is on). Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.set_velocity","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_velocity","x":"set_velocity(self, handle: SoundHandle, v: Vec3) raises Sets the sound's velocity in metres per second, used for the Doppler effect. Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.set_spatial","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_spatial","x":"set_spatial(self, handle: SoundHandle, enabled: Bool) raises Turns 3D spatialisation of the sound on or off. Raises on a stale handle."},{"t":"cinder.audio.engine.AudioEngine.set_attenuation","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_attenuation","x":"set_attenuation( self, handle: SoundHandle, model: Attenuation, min_distance: Float32, max_distance: Float32, rolloff: Float32, ) raises Sets the distance attenuation: the model, min_distance and max_distance in metres and the rolloff factor. An unrecognised model value falls back to inverse. Raises"},{"t":"cinder.audio.engine.AudioEngine.set_listener","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-set_listener","x":"set_listener( self, position: Vec3, forward: Vec3, up: Vec3 = Vec3.unit_y(), velocity: Vec3 = Vec3.zero(), ) Places miniaudio's listener 0: position in metres, the forward direction it faces, the world up and its velocity in metres per second (Doppler). The engine convention is -Z forward and +Y up;"},{"t":"cinder.audio.engine.AudioEngine.update","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-update","x":"update(mut self) raises -> Int Drains the shim queue: log messages become diagnostics, finished sounds are recorded (and one-shots released). Returns the number of sounds that ended."},{"t":"cinder.audio.engine.AudioEngine.take_ended","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-take_ended","x":"take_ended(mut self) -> List[SoundHandle] Returns the handles of sounds that ended since the last call and clears the list. Handles of one-shots are already stale when they are returned."},{"t":"cinder.audio.engine.AudioEngine.error_count","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-error_count","x":"error_count(self) -> Int Number of error-level records in diagnostics (miniaudio log messages collected by update)."},{"t":"cinder.audio.engine.AudioEngine.read_frames","s":"API symbol","h":"method","u":"/docs/api/audio/#audioengine-read_frames","x":"read_frames(self, frames: Int) raises -> List[Float32] Headless engines: mixes frames interleaved float frames (tests, offline rendering)."},{"t":"cinder.audio.sync","s":"API symbol","h":"module","u":"/docs/api/audio/#cinderaudiosync","x":"ECS <-> audio: AudioSource entities position their sounds, AudioListener places the listener (attach it to the camera entity so the listener follows the camera)."},{"t":"cinder.audio.sync.AudioSource","s":"API symbol","h":"struct","u":"/docs/api/audio/#struct-audiosource","x":"struct AudioSource(Component) A spatial sound that follows its entity. velocity feeds the Doppler effect."},{"t":"cinder.audio.sync.AudioSource.component_id","s":"API symbol","h":"method","u":"/docs/api/audio/#audiosource-component_id","x":"component_id() -> Int"},{"t":"cinder.audio.sync.AudioListener","s":"API symbol","h":"struct","u":"/docs/api/audio/#struct-audiolistener","x":"struct AudioListener(Component) Marks the entity whose world transform drives the listener (position, -Z forward, +Y up). With several, the first one found wins."},{"t":"cinder.audio.sync.AudioListener.component_id","s":"API symbol","h":"method","u":"/docs/api/audio/#audiolistener-component_id","x":"component_id() -> Int"},{"t":"cinder.audio.sync.sync_audio","s":"API symbol","h":"def","u":"/docs/api/audio/#def-sync_audio","x":"def sync_audio(world: World, audio: AudioEngine) raises -> Int Pushes entity transforms to the sounds and the listener. Returns the number of sources positioned; stale handles are skipped."},{"t":"cinder.core","s":"API symbol","h":"module","u":"/docs/api/core/#cindercore-1","x":"Core: math, logging, assertions, allocators, containers, handles, time, file system. No I/O to GPU, audio or window systems lives here."},{"t":"cinder.core.arena","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorearena","x":"LinearArena: a bump allocator for per-frame scratch memory. One block is allocated up front; alloc_bytes hands out aligned sub-ranges in O(1) and reset returns everything at once. There is no per-allocation free. After warm-up the engine's frame loop allocates only from arenas and pools, which is wh"},{"t":"cinder.core.arena.LinearArena","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-lineararena","x":"struct LinearArena(Movable) Bump allocator over one fixed block. Every address returned is aligned as requested."},{"t":"cinder.core.arena.LinearArena.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#lineararena-__init__","x":"__init__(out self, capacity: Int) raises Allocates capacity bytes once. Raises on a non-positive capacity."},{"t":"cinder.core.arena.LinearArena.capacity","s":"API symbol","h":"method","u":"/docs/api/core/#lineararena-capacity","x":"capacity(self) -> Int Usable bytes requested at construction; the 64-byte alignment slack is not counted."},{"t":"cinder.core.arena.LinearArena.used","s":"API symbol","h":"method","u":"/docs/api/core/#lineararena-used","x":"used(self) -> Int Bytes handed out since the last reset, including alignment padding."},{"t":"cinder.core.arena.LinearArena.high_water","s":"API symbol","h":"method","u":"/docs/api/core/#lineararena-high_water","x":"high_water(self) -> Int Largest used() ever observed; the number to size the arena by."},{"t":"cinder.core.arena.LinearArena.alloc_bytes","s":"API symbol","h":"method","u":"/docs/api/core/#lineararena-alloc_bytes","x":"alloc_bytes( mut self, size: Int, alignment: Int = 16 ) raises -> Pointer[UInt8, MutUntrackedOrigin] Returns size bytes aligned to alignment (a power of two <= 64). Raises when exhausted."},{"t":"cinder.core.arena.LinearArena.alloc","s":"API symbol","h":"method","u":"/docs/api/core/#lineararena-alloc","x":"alloc[ T: AnyType ](mut self, count: Int = 1) raises -> Pointer[T, MutUntrackedOrigin] Uninitialised storage for count values of T, aligned for T. The caller initialises it."},{"t":"cinder.core.arena.LinearArena.reset","s":"API symbol","h":"method","u":"/docs/api/core/#lineararena-reset","x":"reset(mut self) Makes the whole block available again. Previously returned pointers become dangling."},{"t":"cinder.core.assertions","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoreassertions","x":"Engine assertions: broken invariants abort with an explicit message. cinder_assert is compiled in unless the build defines CINDER_ASSERTS=0 (mojo build -D CINDER_ASSERTS=0), which release builds of the shipped game set and nothing else does. cinder_check is never compiled out and is used where the c"},{"t":"cinder.core.assertions.cinder_assert","s":"API symbol","h":"def","u":"/docs/api/core/#def-cinder_assert","x":"def cinder_assert(condition: Bool, message: StaticString) Aborts with message when condition is false, unless asserts are compiled out."},{"t":"cinder.core.assertions.cinder_check","s":"API symbol","h":"def","u":"/docs/api/core/#def-cinder_check","x":"def cinder_check(condition: Bool, message: StaticString) Aborts with message when condition is false, in every build."},{"t":"cinder.core.base64","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorebase64","x":"Base64 decoding (RFC 4648, with or without padding) for data URIs in glTF files."},{"t":"cinder.core.base64.decode_base64","s":"API symbol","h":"def","u":"/docs/api/core/#def-decode_base64","x":"def decode_base64(text: StringSpan) raises -> List[UInt8] Decodes base64 text; whitespace is skipped and '=' padding is optional."},{"t":"cinder.core.boxed","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoreboxed","x":"Boxed: a value at a stable heap address. Subsystems that hand out Pointers to themselves (Gfx, Resources, Renderer, JobSystem, ...) must never move once those pointers exist. Boxed[T] owns one T on the heap so the owner can move freely while the pointee stays put."},{"t":"cinder.core.boxed.Boxed","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-boxed","x":"struct Boxed[T: Deinitable & Movable](Movable) Owns one T on the heap so pointers to it stay valid while the box itself moves. The value is moved in at construction and destroyed, then freed, when the box is destroyed. ptr() and [] give untracked mutable access; neither may outlive the box."},{"t":"cinder.core.boxed.Boxed.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#boxed-__init__","x":"__init__(out self, var value: Self.T)"},{"t":"cinder.core.boxed.Boxed.ptr","s":"API symbol","h":"method","u":"/docs/api/core/#boxed-ptr","x":"ptr(self) -> Pointer[Self.T, MutUntrackedOrigin] A raw pointer valid for the box's lifetime."},{"t":"cinder.core.boxed.Boxed.__getitem__","s":"API symbol","h":"method","u":"/docs/api/core/#boxed-__getitem__","x":"__getitem__(ref self) -> ref[MutUntrackedOrigin] Self.T"},{"t":"cinder.core.bytes","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorebytes","x":"Little-endian byte packing for GPU uploads (std140/std430-style layouts are the caller's job)."},{"t":"cinder.core.bytes.ByteWriter","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-bytewriter","x":"struct ByteWriter(Movable, Sized) Appends plain values to a byte buffer. pad_to aligns the length for struct rules."},{"t":"cinder.core.bytes.ByteWriter.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-__init__","x":"__init__(out self, capacity: Int = 256)"},{"t":"cinder.core.bytes.ByteWriter.__len__","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-__len__","x":"__len__(self) -> Int"},{"t":"cinder.core.bytes.ByteWriter.clear","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-clear","x":"clear(mut self) Empties the buffer, keeping its capacity (same as reset)."},{"t":"cinder.core.bytes.ByteWriter.u8","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-u8","x":"u8(mut self, v: UInt8) Appends one byte."},{"t":"cinder.core.bytes.ByteWriter.u32","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-u32","x":"u32(mut self, v: UInt32) Appends v as four bytes, little-endian."},{"t":"cinder.core.bytes.ByteWriter.i32","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-i32","x":"i32(mut self, v: Int32) Appends v as four bytes, little-endian two's complement."},{"t":"cinder.core.bytes.ByteWriter.f32","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-f32","x":"f32(mut self, v: Float32) Appends the IEEE-754 bits of v as four bytes, little-endian."},{"t":"cinder.core.bytes.ByteWriter.vec2","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-vec2","x":"vec2(mut self, v: Vec2) Appends x then y as two f32 (8 bytes, no padding)."},{"t":"cinder.core.bytes.ByteWriter.vec3","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-vec3","x":"vec3(mut self, v: Vec3) Three floats, no padding (callers add f32 or pad_to(16) for vec4 alignment)."},{"t":"cinder.core.bytes.ByteWriter.vec3_padded","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-vec3_padded","x":"vec3_padded(mut self, v: Vec3, w: Float32 = 0.0) Appends x, y, z, then w (default 0) as four f32: a vec3 in a 16-byte vec4 slot."},{"t":"cinder.core.bytes.ByteWriter.vec4","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-vec4","x":"vec4(mut self, v: Vec4) Appends x, y, z, w as four f32 (16 bytes)."},{"t":"cinder.core.bytes.ByteWriter.mat4","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-mat4","x":"mat4(mut self, m: Mat4) Column-major, matching WGSL mat4x4<f32>."},{"t":"cinder.core.bytes.ByteWriter.pad_to","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-pad_to","x":"pad_to(mut self, alignment: Int) Zero-fills to the next multiple of alignment; no-op when already aligned. alignment must be positive: 0 or a negative value is a bug and cinder_assert aborts. With assertions compiled out it leaves the bytes unchanged."},{"t":"cinder.core.bytes.ByteWriter.span","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-span","x":"span(self) -> Span[UInt8, origin_of(self.bytes)] A view of the bytes written so far, borrowed from the writer; invalid after it changes."},{"t":"cinder.core.bytes.ByteWriter.reset","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-reset","x":"reset(mut self) Empties the writer, keeping its capacity (frame scratch buffers)."},{"t":"cinder.core.bytes.ByteWriter.take","s":"API symbol","h":"method","u":"/docs/api/core/#bytewriter-take","x":"take(deinit self) -> List[UInt8] Consumes the writer and returns its bytes."},{"t":"cinder.core.cstring","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorecstring","x":"C strings to Mojo Strings, decoding the bytes as UTF-8. C APIs (SDL, the shim, stb) hand back NUL-terminated UTF-8. Converting byte by byte with chr treats every byte as a code point and double-encodes anything outside ASCII, so every conversion goes through string_from_c instead."},{"t":"cinder.core.cstring.string_from_c","s":"API symbol","h":"def","u":"/docs/api/core/#def-string_from_c","x":"def string_from_c( p: Optional[Pointer[c_char, MutUntrackedOrigin]], max_len: Int = -1 ) -> String The UTF-8 text at p up to its NUL terminator, or up to max_len bytes when max_len is not negative (fixed-size C buffers). A null pointer gives the empty string. Invalid UTF-8 sequences become U+FFFD in"},{"t":"cinder.core.fs","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorefs","x":"Filesystem helpers with typed errors. Thin on purpose: the asset database layers on top."},{"t":"cinder.core.fs.FileError","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-fileerror","x":"struct FileError(Copyable, Movable, Writable) A failed file operation, with the path and the underlying message."},{"t":"cinder.core.fs.FileError.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#fileerror-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.fs.file_exists","s":"API symbol","h":"def","u":"/docs/api/core/#def-file_exists","x":"def file_exists(path: String) -> Bool True when something exists at path (a file or a directory). Never raises."},{"t":"cinder.core.fs.read_bytes","s":"API symbol","h":"def","u":"/docs/api/core/#def-read_bytes","x":"def read_bytes(path: String) raises FileError -> List[UInt8] Reads a whole file into memory."},{"t":"cinder.core.fs.read_text","s":"API symbol","h":"def","u":"/docs/api/core/#def-read_text","x":"def read_text(path: String) raises FileError -> String Reads a whole UTF-8 text file."},{"t":"cinder.core.fs.write_bytes","s":"API symbol","h":"def","u":"/docs/api/core/#def-write_bytes","x":"def write_bytes(path: String, data: Span[UInt8, _]) raises FileError Writes (truncating) data to path."},{"t":"cinder.core.fs.write_text","s":"API symbol","h":"def","u":"/docs/api/core/#def-write_text","x":"def write_text(path: String, text: String) raises FileError Writes (truncating) text to path. Raises FileError carrying the path on failure."},{"t":"cinder.core.handles","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorehandles","x":"Generational handles and the slot pool that issues them. A Handle[Tag] names a slot in a Pool[T, Tag]. The generation counter makes a stale handle (one whose slot was freed and reused) fail validation instead of aliasing a new object. Tag is a phantom type so that, for example, a texture handle cann"},{"t":"cinder.core.handles.Handle","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-handle","x":"struct Handle[Tag: AnyType]( Boolable, Equatable, Hashable, ImplicitlyCopyable, RegisterPassable, Writable, ) A 64-bit generational handle: 32-bit slot index, 32-bit generation. Zero is the null handle."},{"t":"cinder.core.handles.Handle.null","s":"API symbol","h":"method","u":"/docs/api/core/#handle-null","x":"null() -> Handle[Self.Tag] The null handle (index 0, generation 0); never valid in any Pool."},{"t":"cinder.core.handles.Handle.is_null","s":"API symbol","h":"method","u":"/docs/api/core/#handle-is_null","x":"is_null(self) -> Bool True when the generation is 0, whatever the index; pools never issue generation 0."},{"t":"cinder.core.handles.Handle.packed","s":"API symbol","h":"method","u":"/docs/api/core/#handle-packed","x":"packed(self) -> UInt64 The handle as one integer, for serialization and GPU-side ids."},{"t":"cinder.core.handles.Handle.from_packed","s":"API symbol","h":"method","u":"/docs/api/core/#handle-from_packed","x":"from_packed(v: UInt64) -> Handle[Self.Tag] Inverse of packed(): low 32 bits are the index, high 32 bits the generation."},{"t":"cinder.core.handles.Handle.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#handle-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.handles.Pool","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-pool","x":"struct Pool[T: Copyable & Deinitable, Tag: AnyType](Movable) A slot map: O(1) insert, remove and lookup by Handle[Self.Tag], with generation checking. Slots are never reallocated once created, so a Pool holds at most capacity live values; insert raises when full, which keeps steady-state frames free"},{"t":"cinder.core.handles.Pool.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#pool-__init__","x":"__init__(out self, capacity: Int) raises Creates a pool with room for capacity values (1 <= capacity < 2^32)."},{"t":"cinder.core.handles.Pool.capacity","s":"API symbol","h":"method","u":"/docs/api/core/#pool-capacity","x":"capacity(self) -> Int Number of slots, fixed at construction; the pool never grows past it."},{"t":"cinder.core.handles.Pool.live","s":"API symbol","h":"method","u":"/docs/api/core/#pool-live","x":"live(self) -> Int Number of values currently stored."},{"t":"cinder.core.handles.Pool.insert","s":"API symbol","h":"method","u":"/docs/api/core/#pool-insert","x":"insert(mut self, var value: Self.T) raises -> Handle[Self.Tag] Stores value and returns its handle. Raises when the pool is full."},{"t":"cinder.core.handles.Pool.is_valid","s":"API symbol","h":"method","u":"/docs/api/core/#pool-is_valid","x":"is_valid(self, h: Handle[Self.Tag]) -> Bool True when h names a live value (right slot, current generation)."},{"t":"cinder.core.handles.Pool.get","s":"API symbol","h":"method","u":"/docs/api/core/#pool-get","x":"get(self, h: Handle[Self.Tag]) raises -> Self.T A copy of the value behind h; raises on a null, stale or foreign handle. Pooled values are small records (handles, descriptors), so copying is the intended access pattern; update writes a value back."},{"t":"cinder.core.handles.Pool.update","s":"API symbol","h":"method","u":"/docs/api/core/#pool-update","x":"update(mut self, h: Handle[Self.Tag], var value: Self.T) raises Replaces the value behind h; raises on an invalid handle."},{"t":"cinder.core.handles.Pool.remove","s":"API symbol","h":"method","u":"/docs/api/core/#pool-remove","x":"remove(mut self, h: Handle[Self.Tag]) raises -> Self.T Removes and returns the value behind h, retiring the handle's generation."},{"t":"cinder.core.handles.Pool.handles","s":"API symbol","h":"method","u":"/docs/api/core/#pool-handles","x":"handles(self) -> List[Handle[Self.Tag]] Handles of every live value, in slot order (allocates; for tools and tests)."},{"t":"cinder.core.hash","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorehash","x":"Content hashing: 64-bit FNV-1a over bytes and strings (stable across runs and platforms)."},{"t":"cinder.core.hash.fnv1a64","s":"API symbol","h":"def","u":"/docs/api/core/#def-fnv1a64","x":"def fnv1a64(data: Span[UInt8, _]) -> UInt64 64-bit FNV-1a of data. Stable across runs and platforms, which is why the asset cooker uses it as a content hash. Not cryptographic: do not use it where collisions could be engineered."},{"t":"cinder.core.hash.fnv1a64_string","s":"API symbol","h":"def","u":"/docs/api/core/#def-fnv1a64_string","x":"def fnv1a64_string(text: String) -> UInt64 fnv1a64 of the string's UTF-8 bytes, without a terminator."},{"t":"cinder.core.hash.hex64","s":"API symbol","h":"def","u":"/docs/api/core/#def-hex64","x":"def hex64(v: UInt64) -> String Sixteen lowercase hex digits."},{"t":"cinder.core.image","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoreimage","x":"CPU images: RGBA8 load/decode/save through the shim (stb) and perceptual comparison (SSIM). Image owns its pixels in a List[UInt8] (tightly packed RGBA8). ssim is the structural similarity index on luma with an 8x8 box window; golden-image tests compare against it."},{"t":"cinder.core.image.Image","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-image","x":"struct Image(Copyable, Movable) A tightly packed RGBA8 image."},{"t":"cinder.core.image.Image.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#image-__init__","x":"__init__(out self, width: Int, height: Int)"},{"t":"cinder.core.image.Image.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#image-__init__-1","x":"__init__( out self, width: Int, height: Int, var pixels: List[UInt8] ) raises"},{"t":"cinder.core.image.Image.from_rows","s":"API symbol","h":"method","u":"/docs/api/core/#image-from_rows","x":"from_rows( width: Int, height: Int, rows: List[UInt8], stride: Int ) raises -> Image Repacks rows of stride bytes (GPU readback with 256-byte alignment) into a tight image."},{"t":"cinder.core.image.Image.pixel","s":"API symbol","h":"method","u":"/docs/api/core/#image-pixel","x":"pixel(self, x: Int, y: Int) -> SIMD[DType.uint8, 4] The RGBA8 value at column x, row y; (0, 0) is the first pixel in memory. x must be in [0, width) and y in [0, height). Anything else is a bug and aborts through cinder_check in every build (an unchecked index would read outside the pixel list, or s"},{"t":"cinder.core.image.Image.set_pixel","s":"API symbol","h":"method","u":"/docs/api/core/#image-set_pixel","x":"set_pixel(mut self, x: Int, y: Int, value: SIMD[DType.uint8, 4]) Writes RGBA8 value at column x, row y; addressed and range-checked like pixel."},{"t":"cinder.core.image.Image.swap_rb","s":"API symbol","h":"method","u":"/docs/api/core/#image-swap_rb","x":"swap_rb(mut self) BGRA <-> RGBA in place (swapchain readbacks are BGRA)."},{"t":"cinder.core.image.Image.luma","s":"API symbol","h":"method","u":"/docs/api/core/#image-luma","x":"luma(self, x: Int, y: Int) -> Float64 Rec. 709 luma of the pixel at (x, y) in [0, 255], from the stored (gamma-encoded) values; alpha is ignored. compare computes SSIM on this value."},{"t":"cinder.core.image.load_image","s":"API symbol","h":"def","u":"/docs/api/core/#def-load_image","x":"def load_image(lib: NativePtr, path: String) raises -> Image Loads the image file at path through the shim's stb_image and returns it as RGBA8. Any channel count in the file is expanded to four. lib must point at the loaded native shim. Raises with stb's failure reason when the file cannot be read o"},{"t":"cinder.core.image.decode_image","s":"API symbol","h":"def","u":"/docs/api/core/#def-decode_image","x":"def decode_image(lib: NativePtr, bytes: Span[UInt8, _]) raises -> Image Decodes an encoded image held in memory (stb_image) to RGBA8. Raises with stb's failure reason on undecodable data; the shim also rejects inputs larger than 2^31 - 1 bytes. The stb buffer is copied into the Image and freed befor"},{"t":"cinder.core.image.save_png","s":"API symbol","h":"def","u":"/docs/api/core/#def-save_png","x":"def save_png(lib: NativePtr, path: String, img: Image) raises Writes img to path as an RGBA8 PNG through the shim's stb_image_write. Raises when the file cannot be written; the shim also refuses an image with zero width or height."},{"t":"cinder.core.image.ImageDiff","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-imagediff","x":"struct ImageDiff(ImplicitlyCopyable, Movable, Writable) Result of compare: SSIM on luma, plus the raw pixel statistics tests may also bound."},{"t":"cinder.core.image.ImageDiff.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#imagediff-write_to","x":"write_to(self, mut w: Some[Writer])"},{"t":"cinder.core.image.compare","s":"API symbol","h":"def","u":"/docs/api/core/#def-compare","x":"def compare( a: Image, b: Image, tolerance: Int = 2, window: Int = 8 ) raises -> ImageDiff SSIM (luma, non-overlapping window boxes, K1 = 0.01, K2 = 0.03, L = 255) and pixel deltas."},{"t":"cinder.core.jobs","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorejobs","x":"The job system: a work-stealing thread pool with dependency counters. Design (ADR-0008, docs/THREADING.md): - One worker per logical core minus one (the main thread is the remaining core), each with a fixed-capacity Chase-Lev deque of job indices. A worker pushes and pops at the bottom of its own de"},{"t":"cinder.core.jobs.Counter","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-counter","x":"struct Counter(Movable) Completion counter: incremented per submitted job, decremented when each finishes."},{"t":"cinder.core.jobs.Counter.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#counter-__init__","x":"__init__(out self)"},{"t":"cinder.core.jobs.Counter.load","s":"API symbol","h":"method","u":"/docs/api/core/#counter-load","x":"load(self) -> Int32 Jobs still outstanding on this counter (acquire load); 0 when all have finished."},{"t":"cinder.core.jobs.Job","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-job","x":"struct Job(ImplicitlyCopyable, Movable) One unit of work in the job table."},{"t":"cinder.core.jobs.Job.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#job-__init__","x":"__init__( out self, run: JobFn, data: Pointer[NoneType, MutUntrackedOrigin], counter: Optional[Pointer[Counter, MutUntrackedOrigin]], )"},{"t":"cinder.core.jobs.Deque","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-deque","x":"struct Deque(Movable) Chase-Lev work-stealing deque of job indices with a fixed power-of-two capacity. The owner thread calls push and pop; any thread calls steal. Indices wrap through the mask; push fails when the deque is full instead of growing."},{"t":"cinder.core.jobs.Deque.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#deque-__init__","x":"__init__(out self, capacity: Int)"},{"t":"cinder.core.jobs.Deque.push","s":"API symbol","h":"method","u":"/docs/api/core/#deque-push","x":"push(mut self, job: Int32) -> Bool Owner only. Returns False when full."},{"t":"cinder.core.jobs.Deque.pop","s":"API symbol","h":"method","u":"/docs/api/core/#deque-pop","x":"pop(mut self) -> Int32 Owner only. Returns INVALID_JOB when empty."},{"t":"cinder.core.jobs.Deque.steal","s":"API symbol","h":"method","u":"/docs/api/core/#deque-steal","x":"steal(mut self) -> Int32 Any thread. Returns INVALID_JOB when empty or when it lost a race (caller retries elsewhere)."},{"t":"cinder.core.jobs.Shared","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-shared","x":"struct Shared(Movable) State shared by all workers; lives at a stable heap address for the system's lifetime."},{"t":"cinder.core.jobs.Shared.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#shared-__init__","x":"__init__( out self, worker_count: Int, job_capacity: Int, semaphore: Optional[Pointer[CnSemaphore, MutUntrackedOrigin]], lib: Pointer[CinderNative, MutUntrackedOrigin], )"},{"t":"cinder.core.jobs.WorkerContext","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-workercontext","x":"struct WorkerContext(Movable) Passed to each worker thread entry: which deque is mine and where the shared state lives."},{"t":"cinder.core.jobs.WorkerContext.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#workercontext-__init__","x":"__init__( out self, shared: Pointer[Shared, MutUntrackedOrigin], index: Int )"},{"t":"cinder.core.jobs.JobSystem","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-jobsystem","x":"struct JobSystem(Movable) Owns the worker threads, the shared state and the job table. Deque 0 belongs to the main thread. Hold one instance for the life of the program (section 4: the job system runs everything that is not SDL or presentation). __deinit__ stops and joins every worker."},{"t":"cinder.core.jobs.JobSystem.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#jobsystem-__init__","x":"__init__( out self, lib: Pointer[CinderNative, MutUntrackedOrigin], worker_count: Int, job_capacity: Int = 1 << 14, ) raises Starts worker_count workers (0 runs everything on the calling thread). job_capacity must be a power of two; it bounds the number of jobs in flight between reset calls."},{"t":"cinder.core.jobs.JobSystem.worker_count","s":"API symbol","h":"method","u":"/docs/api/core/#jobsystem-worker_count","x":"worker_count(self) -> Int Number of worker threads started, not counting the main thread (which owns deque 0)."},{"t":"cinder.core.jobs.JobSystem.executed","s":"API symbol","h":"method","u":"/docs/api/core/#jobsystem-executed","x":"executed(self) -> Int Jobs completed since construction (diagnostics)."},{"t":"cinder.core.jobs.JobSystem.submit","s":"API symbol","h":"method","u":"/docs/api/core/#jobsystem-submit","x":"submit( mut self, run: JobFn, data: Pointer[NoneType, MutUntrackedOrigin], counter: Optional[Pointer[Counter, MutUntrackedOrigin]], ) -> Bool Queues a job from the main thread. Increments counter before the job can run. Returns False when the job table or the main deque is full; the caller must then"},{"t":"cinder.core.jobs.JobSystem.wait","s":"API symbol","h":"method","u":"/docs/api/core/#jobsystem-wait","x":"wait(mut self, counter: Pointer[Counter, MutUntrackedOrigin]) Runs jobs on the calling thread until counter reaches zero."},{"t":"cinder.core.jobs.JobSystem.reset","s":"API symbol","h":"method","u":"/docs/api/core/#jobsystem-reset","x":"reset(mut self) Recycles the job table. Only valid when no job is queued or running (e.g. at frame start)."},{"t":"cinder.core.log","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorelog","x":"Structured, levelled logging. A Logger owns a minimum level and a sink. Records carry a level, a category and a message plus optional key=value fields, and are formatted as one line: [LEVEL] category: message key=value .... Sinks are a trait so tests capture records in memory and the engine routes t"},{"t":"cinder.core.log.LogLevel","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-loglevel","x":"struct LogLevel( Comparable, Equatable, ImplicitlyCopyable, RegisterPassable, Writable ) Severity, ordered. Compare with the usual operators."},{"t":"cinder.core.log.LogLevel.trace","s":"API symbol","h":"method","u":"/docs/api/core/#loglevel-trace","x":"trace() -> LogLevel Severity 0, the lowest: a logger at this level passes everything."},{"t":"cinder.core.log.LogLevel.debug","s":"API symbol","h":"method","u":"/docs/api/core/#loglevel-debug","x":"debug() -> LogLevel Severity 1, between trace and info."},{"t":"cinder.core.log.LogLevel.info","s":"API symbol","h":"method","u":"/docs/api/core/#loglevel-info","x":"info() -> LogLevel Severity 2; the default Logger minimum level."},{"t":"cinder.core.log.LogLevel.warn","s":"API symbol","h":"method","u":"/docs/api/core/#loglevel-warn","x":"warn() -> LogLevel Severity 3, between info and error."},{"t":"cinder.core.log.LogLevel.error","s":"API symbol","h":"method","u":"/docs/api/core/#loglevel-error","x":"error() -> LogLevel Severity 4, the highest named level."},{"t":"cinder.core.log.LogLevel.name","s":"API symbol","h":"method","u":"/docs/api/core/#loglevel-name","x":"name(self) -> StaticString Label printed in records: TRACE, DEBUG, INFO, WARN, or ERROR for 4 and above."},{"t":"cinder.core.log.LogLevel.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#loglevel-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.log.LogField","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-logfield","x":"struct LogField(Copyable, Movable, Writable) One key=value pair attached to a record."},{"t":"cinder.core.log.LogField.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#logfield-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.log.LogRecord","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-logrecord","x":"struct LogRecord(Copyable, Movable, Writable) A formatted log entry."},{"t":"cinder.core.log.LogRecord.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#logrecord-__init__","x":"__init__( out self, level: LogLevel, category: String, message: String, var fields: List[LogField], )"},{"t":"cinder.core.log.LogRecord.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#logrecord-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.log.LogSink","s":"API symbol","h":"trait","u":"/docs/api/core/#trait-logsink","x":"trait LogSink Receives formatted records. Implementations must not raise."},{"t":"cinder.core.log.LogSink.emit","s":"API symbol","h":"method","u":"/docs/api/core/#logsink-emit","x":"emit(mut self, record: LogRecord) Handles one record that passed the filter. Must not raise: drop the record instead."},{"t":"cinder.core.log.PrintSink","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-printsink","x":"struct PrintSink(LogSink, Movable) Writes each record as one line to standard output."},{"t":"cinder.core.log.PrintSink.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#printsink-__init__","x":"__init__(out self)"},{"t":"cinder.core.log.PrintSink.emit","s":"API symbol","h":"method","u":"/docs/api/core/#printsink-emit","x":"emit(mut self, record: LogRecord) Prints the record as one line to standard output and increments emitted."},{"t":"cinder.core.log.MemorySink","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-memorysink","x":"struct MemorySink(LogSink, Movable) Keeps every record; used by tests and the in-engine console."},{"t":"cinder.core.log.MemorySink.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#memorysink-__init__","x":"__init__(out self)"},{"t":"cinder.core.log.MemorySink.emit","s":"API symbol","h":"method","u":"/docs/api/core/#memorysink-emit","x":"emit(mut self, record: LogRecord) Appends a copy of the record to records, which grows without bound."},{"t":"cinder.core.log.Logger","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-logger","x":"struct Logger[S: LogSink & Movable & Deinitable](Movable) Filters by minimum level and forwards to its sink."},{"t":"cinder.core.log.Logger.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#logger-__init__","x":"__init__( out self, var sink: Self.S, min_level: LogLevel = LogLevel.info() )"},{"t":"cinder.core.log.Logger.enabled","s":"API symbol","h":"method","u":"/docs/api/core/#logger-enabled","x":"enabled(self, level: LogLevel) -> Bool True when level >= min_level; check it before building costly messages."},{"t":"cinder.core.log.Logger.log","s":"API symbol","h":"method","u":"/docs/api/core/#logger-log","x":"log( mut self, level: LogLevel, category: String, message: String, var fields: List[LogField], ) Emits a record when level passes the filter. Field lists are consumed."},{"t":"cinder.core.log.Logger.log","s":"API symbol","h":"method","u":"/docs/api/core/#logger-log-1","x":"log(mut self, level: LogLevel, category: String, message: String) Emits a record without fields when level passes the filter."},{"t":"cinder.core.log.Logger.trace","s":"API symbol","h":"method","u":"/docs/api/core/#logger-trace","x":"trace(mut self, category: String, message: String) Logs message under category at TRACE, subject to min_level."},{"t":"cinder.core.log.Logger.debug","s":"API symbol","h":"method","u":"/docs/api/core/#logger-debug","x":"debug(mut self, category: String, message: String) Logs message under category at DEBUG, subject to min_level."},{"t":"cinder.core.log.Logger.info","s":"API symbol","h":"method","u":"/docs/api/core/#logger-info","x":"info(mut self, category: String, message: String) Logs message under category at INFO, subject to min_level."},{"t":"cinder.core.log.Logger.warn","s":"API symbol","h":"method","u":"/docs/api/core/#logger-warn","x":"warn(mut self, category: String, message: String) Logs message under category at WARN, subject to min_level."},{"t":"cinder.core.log.Logger.error","s":"API symbol","h":"method","u":"/docs/api/core/#logger-error","x":"error(mut self, category: String, message: String) Logs message under category at ERROR, subject to min_level."},{"t":"cinder.core.math","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoremath","x":"SIMD math for the engine. Conventions (docs/ARCHITECTURE.md, fixed): right-handed, Y up, -Z forward, metres, column-major matrices, reverse-Z depth with an infinite far plane. All types are plain 32-bit float values backed by SIMD[DType.float32, 4]; a Vec3 keeps its fourth lane at zero."},{"t":"cinder.core.math.mat4","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoremathmat4","x":"Mat4: a column-major 4x4 float matrix, as WGSL and glTF store it."},{"t":"cinder.core.math.mat4.Mat4","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-mat4","x":"struct Mat4(Equatable, ImplicitlyCopyable, RegisterPassable, Writable) Column-major 4x4 matrix. c0..c3 are the columns; element (row r, column c) is cC[r]."},{"t":"cinder.core.math.mat4.Mat4.identity","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-identity","x":"identity() -> Mat4 The identity matrix."},{"t":"cinder.core.math.mat4.Mat4.zero","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-zero","x":"zero() -> Mat4 All sixteen elements zero; also what inverse returns for a singular matrix."},{"t":"cinder.core.math.mat4.Mat4.from_rows","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-from_rows","x":"from_rows(r0: Vec4, r1: Vec4, r2: Vec4, r3: Vec4) -> Mat4 Builds a matrix from its rows (handy for literals written the way maths books print them)."},{"t":"cinder.core.math.mat4.Mat4.translation","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-translation","x":"translation(t: Vec3) -> Mat4 Translation by t (metres): the identity with t in the last column."},{"t":"cinder.core.math.mat4.Mat4.scaling","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-scaling","x":"scaling(s: Vec3) -> Mat4 Scale by s.x(), s.y(), s.z() along the x, y and z axes."},{"t":"cinder.core.math.mat4.Mat4.rotation","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-rotation","x":"rotation(q: Quat) -> Mat4 Rotation matrix of a unit quaternion."},{"t":"cinder.core.math.mat4.Mat4.trs","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-trs","x":"trs(t: Vec3, r: Quat, s: Vec3) -> Mat4 Translation * Rotation * Scale, the glTF node transform order."},{"t":"cinder.core.math.mat4.Mat4.look_at","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-look_at","x":"look_at(eye: Vec3, target: Vec3, up: Vec3) -> Mat4 Right-handed view matrix: the camera looks down -Z, with +Y up and +X right."},{"t":"cinder.core.math.mat4.Mat4.perspective_reverse_z_infinite","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-perspective_reverse_z_infinite","x":"perspective_reverse_z_infinite( fov_y: Float32, aspect: Float32, near: Float32 ) -> Mat4 Right-handed perspective projection, reverse-Z (near maps to 1) with an infinite far plane, clip depth in [0, 1] as wgpu expects."},{"t":"cinder.core.math.mat4.Mat4.orthographic_reverse_z","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-orthographic_reverse_z","x":"orthographic_reverse_z( left: Float32, right: Float32, bottom: Float32, top: Float32, near: Float32, far: Float32, ) -> Mat4 Right-handed orthographic projection with reverse-Z (near maps to 1, far to 0)."},{"t":"cinder.core.math.mat4.Mat4.column","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-column","x":"column(self, c: Int) -> Vec4 Column c (0 to 3) as a Vec4. Any other index is a bug: cinder_assert aborts. With assertions compiled out (-D CINDER_ASSERTS=0) the index wraps to c & 3."},{"t":"cinder.core.math.mat4.Mat4.__getitem__","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-__getitem__","x":"__getitem__(self, row: Int, col: Int) -> Float32 Element (row, col); both indices are checked like row and column."},{"t":"cinder.core.math.mat4.Mat4.row","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-row","x":"row(self, r: Int) -> Vec4 Row r (0 to 3), gathered from the four columns. Any other index is a bug: cinder_assert aborts. With assertions compiled out the index wraps to r & 3."},{"t":"cinder.core.math.mat4.Mat4.to_quat","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-to_quat","x":"to_quat(self) -> Quat Rotation of an orthonormal (possibly scaled) matrix's upper 3x3 (Shepperd's method, choosing the largest diagonal term for stability). Scale is removed first."},{"t":"cinder.core.math.mat4.Mat4.transform_point","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-transform_point","x":"transform_point(self, p: Vec3) -> Vec3 Applies the affine part to a point (w = 1), ignoring any projective row."},{"t":"cinder.core.math.mat4.Mat4.transform_vector","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-transform_vector","x":"transform_vector(self, v: Vec3) -> Vec3 Applies the linear part to a direction (w = 0)."},{"t":"cinder.core.math.mat4.Mat4.project_point","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-project_point","x":"project_point(self, p: Vec3) -> Vec3 Full projective transform with perspective divide."},{"t":"cinder.core.math.mat4.Mat4.transpose","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-transpose","x":"transpose(self) -> Mat4 Rows and columns swapped; for a pure rotation this is also its inverse."},{"t":"cinder.core.math.mat4.Mat4.determinant","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-determinant","x":"determinant(self) -> Float32 Determinant of the full 4x4 matrix, by expansion in 2x2 minors. Exactly 0 is what makes inverse return the zero matrix; a negative value means the matrix mirrors space (Transform.from_mat4 relies on this)."},{"t":"cinder.core.math.mat4.Mat4.inverse","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-inverse","x":"inverse(self) -> Mat4 General inverse by cofactors. A singular matrix returns the zero matrix; callers that need to detect that check determinant() first."},{"t":"cinder.core.math.mat4.Mat4.approx_equal","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-approx_equal","x":"approx_equal(self, o: Mat4, eps: Float32 = EPSILON) -> Bool True when every element matches within eps under approx_eq."},{"t":"cinder.core.math.mat4.Mat4.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#mat4-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.math.quat","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoremathquat","x":"Quat: a unit quaternion (x, y, z, w) representing a rotation."},{"t":"cinder.core.math.quat.Quat","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-quat","x":"struct Quat(Equatable, ImplicitlyCopyable, RegisterPassable, Writable) Rotation quaternion. Lanes are (x, y, z, w); w is the scalar part."},{"t":"cinder.core.math.quat.Quat.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#quat-__init__","x":"__init__(out self, x: Float32, y: Float32, z: Float32, w: Float32)"},{"t":"cinder.core.math.quat.Quat.identity","s":"API symbol","h":"method","u":"/docs/api/core/#quat-identity","x":"identity() -> Quat The no-rotation quaternion (0, 0, 0, 1)."},{"t":"cinder.core.math.quat.Quat.from_axis_angle","s":"API symbol","h":"method","u":"/docs/api/core/#quat-from_axis_angle","x":"from_axis_angle(axis: Vec3, angle: Float32) -> Quat Rotation of angle radians about the unit vector axis (right-handed)."},{"t":"cinder.core.math.quat.Quat.from_euler","s":"API symbol","h":"method","u":"/docs/api/core/#quat-from_euler","x":"from_euler(yaw: Float32, pitch: Float32, roll: Float32) -> Quat Yaw about +Y, then pitch about +X, then roll about +Z (applied in that order to a body)."},{"t":"cinder.core.math.quat.Quat.x","s":"API symbol","h":"method","u":"/docs/api/core/#quat-x","x":"x(self) -> Float32 Lane 0, the x term of the vector part."},{"t":"cinder.core.math.quat.Quat.y","s":"API symbol","h":"method","u":"/docs/api/core/#quat-y","x":"y(self) -> Float32 Lane 1, the y term of the vector part."},{"t":"cinder.core.math.quat.Quat.z","s":"API symbol","h":"method","u":"/docs/api/core/#quat-z","x":"z(self) -> Float32 Lane 2, the z term of the vector part."},{"t":"cinder.core.math.quat.Quat.w","s":"API symbol","h":"method","u":"/docs/api/core/#quat-w","x":"w(self) -> Float32 Lane 3, the scalar part: cos(angle / 2) for a unit quaternion."},{"t":"cinder.core.math.quat.Quat.vector_part","s":"API symbol","h":"method","u":"/docs/api/core/#quat-vector_part","x":"vector_part(self) -> Vec3 (x, y, z) as a Vec3: the rotation axis times sin(angle / 2) for a unit quaternion."},{"t":"cinder.core.math.quat.Quat.dot","s":"API symbol","h":"method","u":"/docs/api/core/#quat-dot","x":"dot(self, o: Quat) -> Float32 4D dot product; negative when the two are on opposite hemispheres (see slerp)."},{"t":"cinder.core.math.quat.Quat.length","s":"API symbol","h":"method","u":"/docs/api/core/#quat-length","x":"length(self) -> Float32 Euclidean norm over all four lanes; 1 for a unit quaternion."},{"t":"cinder.core.math.quat.Quat.normalized","s":"API symbol","h":"method","u":"/docs/api/core/#quat-normalized","x":"normalized(self) -> Quat Unit quaternion; the zero quaternion becomes the identity."},{"t":"cinder.core.math.quat.Quat.conjugate","s":"API symbol","h":"method","u":"/docs/api/core/#quat-conjugate","x":"conjugate(self) -> Quat The vector part negated; for a unit quaternion this is the inverse rotation."},{"t":"cinder.core.math.quat.Quat.inverse","s":"API symbol","h":"method","u":"/docs/api/core/#quat-inverse","x":"inverse(self) -> Quat Inverse for any non-zero quaternion (equals the conjugate for unit quaternions). The zero quaternion has no inverse and returns the zero quaternion, as Mat4.inverse returns the zero matrix for a singular matrix."},{"t":"cinder.core.math.quat.Quat.rotate","s":"API symbol","h":"method","u":"/docs/api/core/#quat-rotate","x":"rotate(self, v: Vec3) -> Vec3 Rotates v by this unit quaternion: v' = v + 2w(u x v) + 2u x (u x v)."},{"t":"cinder.core.math.quat.Quat.nlerp","s":"API symbol","h":"method","u":"/docs/api/core/#quat-nlerp","x":"nlerp(self, o: Quat, t: Float32) -> Quat Normalised linear interpolation along the shorter arc."},{"t":"cinder.core.math.quat.Quat.slerp","s":"API symbol","h":"method","u":"/docs/api/core/#quat-slerp","x":"slerp(self, o: Quat, t: Float32) -> Quat Spherical interpolation along the shorter arc; falls back to nlerp when nearly parallel."},{"t":"cinder.core.math.quat.Quat.approx_equal","s":"API symbol","h":"method","u":"/docs/api/core/#quat-approx_equal","x":"approx_equal(self, o: Quat, eps: Float32 = EPSILON) -> Bool True when both represent the same rotation (q and -q are equal rotations)."},{"t":"cinder.core.math.quat.Quat.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#quat-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.math.scalar","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoremathscalar","x":"Scalar helpers shared by the vector, quaternion and matrix types."},{"t":"cinder.core.math.scalar.radians","s":"API symbol","h":"def","u":"/docs/api/core/#def-radians","x":"def radians(deg: Float32) -> Float32 Converts degrees to radians."},{"t":"cinder.core.math.scalar.degrees","s":"API symbol","h":"def","u":"/docs/api/core/#def-degrees","x":"def degrees(rad: Float32) -> Float32 Converts radians to degrees."},{"t":"cinder.core.math.scalar.clamp","s":"API symbol","h":"def","u":"/docs/api/core/#def-clamp","x":"def clamp(x: Float32, lo: Float32, hi: Float32) -> Float32 Clamps x to [lo, hi]."},{"t":"cinder.core.math.scalar.saturate","s":"API symbol","h":"def","u":"/docs/api/core/#def-saturate","x":"def saturate(x: Float32) -> Float32 Clamps x to [0, 1]."},{"t":"cinder.core.math.scalar.lerp","s":"API symbol","h":"def","u":"/docs/api/core/#def-lerp","x":"def lerp(a: Float32, b: Float32, t: Float32) -> Float32 Linear interpolation; exact at t = 0 and t = 1."},{"t":"cinder.core.math.scalar.approx_eq","s":"API symbol","h":"def","u":"/docs/api/core/#def-approx_eq","x":"def approx_eq(a: Float32, b: Float32, eps: Float32 = EPSILON) -> Bool True when |a - b| <= eps * max(1, |a|, |b|): absolute near zero, relative elsewhere."},{"t":"cinder.core.math.transform","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoremathtransform","x":"Transform: translation, rotation and scale as separate terms (poses, animation keys)."},{"t":"cinder.core.math.transform.Transform","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-transform","x":"struct Transform(Equatable, ImplicitlyCopyable, Movable, Writable) A TRS transform. Composition order is scale, then rotation, then translation."},{"t":"cinder.core.math.transform.Transform.identity","s":"API symbol","h":"method","u":"/docs/api/core/#transform-identity","x":"identity() -> Transform Zero translation, identity rotation and unit scale."},{"t":"cinder.core.math.transform.Transform.from_mat4","s":"API symbol","h":"method","u":"/docs/api/core/#transform-from_mat4","x":"from_mat4(m: Mat4) -> Transform Decomposes a matrix without shear: translation from the last column, scale from the column lengths, rotation from the normalised columns. A mirrored matrix (negative determinant) has no proper rotation; the reflection is folded into a negative x scale and the rotation"},{"t":"cinder.core.math.transform.Transform.to_mat4","s":"API symbol","h":"method","u":"/docs/api/core/#transform-to_mat4","x":"to_mat4(self) -> Mat4 The equivalent matrix, Mat4.trs: scale, then rotation, then translation."},{"t":"cinder.core.math.transform.Transform.lerp","s":"API symbol","h":"method","u":"/docs/api/core/#transform-lerp","x":"lerp(self, o: Transform, t: Float32) -> Transform Linear translation and scale, normalised-lerp rotation along the shorter arc."},{"t":"cinder.core.math.transform.Transform.approx_equal","s":"API symbol","h":"method","u":"/docs/api/core/#transform-approx_equal","x":"approx_equal(self, o: Transform, eps: Float32 = EPSILON) -> Bool True when translation, rotation and scale match within eps (q and -q are equal)."},{"t":"cinder.core.math.transform.Transform.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#transform-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.math.vec","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoremathvec","x":"Vec2, Vec3 and Vec4: 32-bit float vectors backed by SIMD registers."},{"t":"cinder.core.math.vec.Vec2","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-vec2","x":"struct Vec2(Equatable, ImplicitlyCopyable, RegisterPassable, Writable) A 2D vector."},{"t":"cinder.core.math.vec.Vec2.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-__init__","x":"__init__(out self, x: Float32, y: Float32)"},{"t":"cinder.core.math.vec.Vec2.zero","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-zero","x":"zero() -> Vec2 (0, 0)."},{"t":"cinder.core.math.vec.Vec2.x","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-x","x":"x(self) -> Float32 Lane 0."},{"t":"cinder.core.math.vec.Vec2.y","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-y","x":"y(self) -> Float32 Lane 1."},{"t":"cinder.core.math.vec.Vec2.dot","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-dot","x":"dot(self, o: Vec2) -> Float32 Sum of the component-wise products."},{"t":"cinder.core.math.vec.Vec2.length_squared","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-length_squared","x":"length_squared(self) -> Float32 Squared Euclidean length; avoids the square root when only comparing lengths."},{"t":"cinder.core.math.vec.Vec2.length","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-length","x":"length(self) -> Float32 Euclidean length."},{"t":"cinder.core.math.vec.Vec2.approx_equal","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-approx_equal","x":"approx_equal(self, o: Vec2, eps: Float32 = EPSILON) -> Bool True when x and y each match within eps under approx_eq."},{"t":"cinder.core.math.vec.Vec2.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#vec2-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.math.vec.Vec3","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-vec3","x":"struct Vec3(Equatable, ImplicitlyCopyable, RegisterPassable, Writable) A 3D vector. The fourth SIMD lane is always zero so 4-wide operations stay exact."},{"t":"cinder.core.math.vec.Vec3.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-__init__","x":"__init__(out self, x: Float32, y: Float32, z: Float32)"},{"t":"cinder.core.math.vec.Vec3.zero","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-zero","x":"zero() -> Vec3 (0, 0, 0)."},{"t":"cinder.core.math.vec.Vec3.one","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-one","x":"one() -> Vec3 (1, 1, 1), the unit scale."},{"t":"cinder.core.math.vec.Vec3.unit_x","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-unit_x","x":"unit_x() -> Vec3 +X, the engine's right axis."},{"t":"cinder.core.math.vec.Vec3.unit_y","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-unit_y","x":"unit_y() -> Vec3 +Y, the engine's up axis (right-handed, Y-up)."},{"t":"cinder.core.math.vec.Vec3.unit_z","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-unit_z","x":"unit_z() -> Vec3 +Z, pointing backwards: the engine's forward is -Z."},{"t":"cinder.core.math.vec.Vec3.forward","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-forward","x":"forward() -> Vec3 The engine's forward direction, -Z."},{"t":"cinder.core.math.vec.Vec3.splat","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-splat","x":"splat(v: Float32) -> Vec3 All three components set to v (the fourth lane stays zero)."},{"t":"cinder.core.math.vec.Vec3.x","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-x","x":"x(self) -> Float32 Lane 0."},{"t":"cinder.core.math.vec.Vec3.y","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-y","x":"y(self) -> Float32 Lane 1."},{"t":"cinder.core.math.vec.Vec3.z","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-z","x":"z(self) -> Float32 Lane 2."},{"t":"cinder.core.math.vec.Vec3.__getitem__","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-__getitem__","x":"__getitem__(self, i: Int) -> Float32"},{"t":"cinder.core.math.vec.Vec3.with_x","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-with_x","x":"with_x(self, v: Float32) -> Vec3 A copy with x replaced by v."},{"t":"cinder.core.math.vec.Vec3.with_y","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-with_y","x":"with_y(self, v: Float32) -> Vec3 A copy with y replaced by v."},{"t":"cinder.core.math.vec.Vec3.with_z","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-with_z","x":"with_z(self, v: Float32) -> Vec3 A copy with z replaced by v."},{"t":"cinder.core.math.vec.Vec3.dot","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-dot","x":"dot(self, o: Vec3) -> Float32 Sum of the component-wise products of x, y and z."},{"t":"cinder.core.math.vec.Vec3.cross","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-cross","x":"cross(self, o: Vec3) -> Vec3 Right-handed cross product self x o (unit_x cross unit_y is unit_z)."},{"t":"cinder.core.math.vec.Vec3.length_squared","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-length_squared","x":"length_squared(self) -> Float32 Squared Euclidean length; avoids the square root when only comparing lengths."},{"t":"cinder.core.math.vec.Vec3.length","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-length","x":"length(self) -> Float32 Euclidean length."},{"t":"cinder.core.math.vec.Vec3.distance","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-distance","x":"distance(self, o: Vec3) -> Float32 Euclidean distance to o."},{"t":"cinder.core.math.vec.Vec3.normalized","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-normalized","x":"normalized(self) -> Vec3 Unit vector in the same direction; zero stays zero."},{"t":"cinder.core.math.vec.Vec3.lerp","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-lerp","x":"lerp(self, o: Vec3, t: Float32) -> Vec3 self + (o - self) * t per component; t is unclamped, so it extrapolates."},{"t":"cinder.core.math.vec.Vec3.min","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-min","x":"min(self, o: Vec3) -> Vec3 Component-wise minimum."},{"t":"cinder.core.math.vec.Vec3.max","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-max","x":"max(self, o: Vec3) -> Vec3 Component-wise maximum."},{"t":"cinder.core.math.vec.Vec3.abs","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-abs","x":"abs(self) -> Vec3 Component-wise absolute value."},{"t":"cinder.core.math.vec.Vec3.max_component","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-max_component","x":"max_component(self) -> Float32 Largest of x, y and z by signed value (not by magnitude; use abs() first for that)."},{"t":"cinder.core.math.vec.Vec3.approx_equal","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-approx_equal","x":"approx_equal(self, o: Vec3, eps: Float32 = EPSILON) -> Bool True when x, y and z each match within eps under approx_eq."},{"t":"cinder.core.math.vec.Vec3.to_vec4","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-to_vec4","x":"to_vec4(self, w: Float32) -> Vec4 (x, y, z, w): pass 1 for a point and 0 for a direction."},{"t":"cinder.core.math.vec.Vec3.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#vec3-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.math.vec.Vec4","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-vec4","x":"struct Vec4(Equatable, ImplicitlyCopyable, RegisterPassable, Writable) A 4D vector, also used for homogeneous points and colours."},{"t":"cinder.core.math.vec.Vec4.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-__init__","x":"__init__(out self, x: Float32, y: Float32, z: Float32, w: Float32)"},{"t":"cinder.core.math.vec.Vec4.zero","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-zero","x":"zero() -> Vec4 (0, 0, 0, 0)."},{"t":"cinder.core.math.vec.Vec4.x","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-x","x":"x(self) -> Float32 Lane 0."},{"t":"cinder.core.math.vec.Vec4.y","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-y","x":"y(self) -> Float32 Lane 1."},{"t":"cinder.core.math.vec.Vec4.z","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-z","x":"z(self) -> Float32 Lane 2."},{"t":"cinder.core.math.vec.Vec4.w","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-w","x":"w(self) -> Float32 Lane 3: 1 for a point, 0 for a direction, alpha for a colour."},{"t":"cinder.core.math.vec.Vec4.__getitem__","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-__getitem__","x":"__getitem__(self, i: Int) -> Float32"},{"t":"cinder.core.math.vec.Vec4.xyz","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-xyz","x":"xyz(self) -> Vec3 (x, y, z) as a Vec3, dropping w without a perspective divide."},{"t":"cinder.core.math.vec.Vec4.dot","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-dot","x":"dot(self, o: Vec4) -> Float32 Sum of the component-wise products over all four lanes."},{"t":"cinder.core.math.vec.Vec4.length","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-length","x":"length(self) -> Float32 Euclidean length over all four lanes."},{"t":"cinder.core.math.vec.Vec4.approx_equal","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-approx_equal","x":"approx_equal(self, o: Vec4, eps: Float32 = EPSILON) -> Bool True when all four components match within eps under approx_eq."},{"t":"cinder.core.math.vec.Vec4.write_to","s":"API symbol","h":"method","u":"/docs/api/core/#vec4-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.core.profile","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoreprofile","x":"Tracy profiling for the engine: zones, frame marks, plots and messages. Profiling is off until Profiler.start() (nothing listens on the network before that) and every call is a cheap no-op while it is off. Zone opens on construction and closes when it is destroyed. Mojo destroys a value right after "},{"t":"cinder.core.profile.Profiler","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-profiler","x":"struct Profiler(ImplicitlyCopyable, Movable) A handle to the shim's Tracy client; copies share the process-wide profiler and its plot name table."},{"t":"cinder.core.profile.Profiler.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-__init__","x":"__init__(out self, lib: NativePtr)"},{"t":"cinder.core.profile.Profiler.available","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-available","x":"available(self) -> Bool Whether the shim was built with Tracy."},{"t":"cinder.core.profile.Profiler.start","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-start","x":"start(self) -> Bool Starts the Tracy client (idempotent). Returns False when Tracy is not built in."},{"t":"cinder.core.profile.Profiler.stop","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-stop","x":"stop(self) Shuts the Tracy client down if it is running; a no-op otherwise or without Tracy."},{"t":"cinder.core.profile.Profiler.is_started","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-is_started","x":"is_started(self) -> Bool Whether the Tracy client is running (start without stop)."},{"t":"cinder.core.profile.Profiler.is_connected","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-is_connected","x":"is_connected(self) -> Bool Whether a Tracy viewer is connected; always False while stopped or without Tracy."},{"t":"cinder.core.profile.Profiler.zone","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-zone","x":"zone(self, name: String, color: UInt32 = 0) -> Zone Opens a zone named name that ends when the returned Zone is destroyed. color is 0xRRGGBB (0 keeps Tracy's default; see the COLOR_* constants). Mojo destroys a value after its last use, so end the zone explicitly with _ = z^ where it should close. W"},{"t":"cinder.core.profile.Profiler.frame_mark","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-frame_mark","x":"frame_mark(self) Marks the end of a frame."},{"t":"cinder.core.profile.Profiler.plot","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-plot","x":"plot(self, name: String, value: Float64) Adds value to the Tracy plot called name. Does nothing while profiling is off. Tracy keeps the name pointer for the program's life, so the name is interned (copied once into a never-freed table); later calls with the same name reuse that copy and do not alloc"},{"t":"cinder.core.profile.Profiler.plot_name_count","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-plot_name_count","x":"plot_name_count(self) -> Int How many distinct plot names have been interned (shared by every copy)."},{"t":"cinder.core.profile.Profiler.message","s":"API symbol","h":"method","u":"/docs/api/core/#profiler-message","x":"message(self, text: String) Sends text to Tracy's message log (info severity); a no-op while profiling is off."},{"t":"cinder.core.profile.Zone","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-zone","x":"struct Zone(Movable) An open profiling zone; ends when destroyed. id is 0 while profiling is off."},{"t":"cinder.core.profile.Zone.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#zone-__init__","x":"__init__(out self, lib: NativePtr, name: String, color: UInt32)"},{"t":"cinder.core.profile.Zone.text","s":"API symbol","h":"method","u":"/docs/api/core/#zone-text","x":"text(self, note: String) Attaches text to the open zone."},{"t":"cinder.core.ring","s":"API symbol","h":"module","u":"/docs/api/core/#cindercorering","x":"RingBuffer: a fixed-capacity FIFO queue with no allocation after construction."},{"t":"cinder.core.ring.RingBuffer","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-ringbuffer","x":"struct RingBuffer[T: Copyable & Deinitable](Movable, Sized) Single-threaded bounded FIFO. push returns False when full; pop returns None when empty. Capacity must be a power of two so index wrapping is a mask. Cross-thread queues live in the native shim (CnEventQueue); this type is for in-engine com"},{"t":"cinder.core.ring.RingBuffer.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-__init__","x":"__init__(out self, capacity: Int) raises"},{"t":"cinder.core.ring.RingBuffer.capacity","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-capacity","x":"capacity(self) -> Int Number of slots, a power of two fixed at construction."},{"t":"cinder.core.ring.RingBuffer.__len__","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-__len__","x":"__len__(self) -> Int"},{"t":"cinder.core.ring.RingBuffer.is_empty","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-is_empty","x":"is_empty(self) -> Bool True when nothing is queued (pop would return None)."},{"t":"cinder.core.ring.RingBuffer.is_full","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-is_full","x":"is_full(self) -> Bool True when every slot is taken (push would return False)."},{"t":"cinder.core.ring.RingBuffer.push","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-push","x":"push(mut self, var value: Self.T) -> Bool Appends value; returns False (and drops nothing already queued) when full."},{"t":"cinder.core.ring.RingBuffer.pop","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-pop","x":"pop(mut self) -> Optional[Self.T] Removes and returns the oldest value, or None when empty."},{"t":"cinder.core.ring.RingBuffer.peek","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-peek","x":"peek(self) raises -> Self.T A copy of the oldest value; raises when empty."},{"t":"cinder.core.ring.RingBuffer.clear","s":"API symbol","h":"method","u":"/docs/api/core/#ringbuffer-clear","x":"clear(mut self) Pops and destroys every queued value, oldest first."},{"t":"cinder.core.time","s":"API symbol","h":"module","u":"/docs/api/core/#cindercoretime","x":"Time: a monotonic clock and the fixed-timestep accumulator that drives the simulation. The frame loop (section 4 of the brief) simulates at a fixed 60 Hz and renders at whatever rate the display allows, interpolating between the last two simulation states with alpha."},{"t":"cinder.core.time.Clock","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-clock","x":"struct Clock(Movable) Monotonic wall clock in nanoseconds, with per-frame deltas."},{"t":"cinder.core.time.Clock.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#clock-__init__","x":"__init__(out self)"},{"t":"cinder.core.time.Clock.now_ns","s":"API symbol","h":"method","u":"/docs/api/core/#clock-now_ns","x":"now_ns(self) -> Int Current perf_counter_ns() reading. Monotonic, but its origin is arbitrary (not the clock's creation), so only differences between readings are meaningful."},{"t":"cinder.core.time.Clock.elapsed_seconds","s":"API symbol","h":"method","u":"/docs/api/core/#clock-elapsed_seconds","x":"elapsed_seconds(self) -> Float64 Seconds since the clock was created."},{"t":"cinder.core.time.Clock.tick","s":"API symbol","h":"method","u":"/docs/api/core/#clock-tick","x":"tick(mut self) -> Float64 Seconds since the previous tick (or creation). Never negative."},{"t":"cinder.core.time.FixedStep","s":"API symbol","h":"struct","u":"/docs/api/core/#struct-fixedstep","x":"struct FixedStep(ImplicitlyCopyable, Movable) Fixed-timestep accumulator. Feed real frame times with advance; it returns how many simulation steps of step_seconds to run (capped at max_steps_per_frame to avoid the spiral of death) and leaves alpha() as the interpolation factor for rendering between "},{"t":"cinder.core.time.FixedStep.__init__","s":"API symbol","h":"method","u":"/docs/api/core/#fixedstep-__init__","x":"__init__(out self, hz: Float64 = 60.0, max_steps_per_frame: Int = 8)"},{"t":"cinder.core.time.FixedStep.advance","s":"API symbol","h":"method","u":"/docs/api/core/#fixedstep-advance","x":"advance(mut self, frame_seconds: Float64) -> Int Accounts for frame_seconds of real time and returns the number of steps to simulate."},{"t":"cinder.core.time.FixedStep.alpha","s":"API symbol","h":"method","u":"/docs/api/core/#fixedstep-alpha","x":"alpha(self) -> Float64 Fraction of a step accumulated but not yet simulated, in [0, 1)."},{"t":"cinder.core.time.FixedStep.total_steps","s":"API symbol","h":"method","u":"/docs/api/core/#fixedstep-total_steps","x":"total_steps(self) -> Int Sum of every step count advance has returned."},{"t":"cinder.ecs","s":"API symbol","h":"module","u":"/docs/api/ecs/#cinderecs-1","x":"Entity component system: archetype world, typed queries, change detection, command buffers, parent-child transforms and a scheduler that runs non-conflicting systems in parallel."},{"t":"cinder.ecs.commands","s":"API symbol","h":"module","u":"/docs/api/ecs/#cinderecscommands","x":"Deferred structural changes: record while iterating, apply when it is safe. A CommandBuffer stores spawns (returning placeholder entities resolved on apply), despawns, and typed adds/removes as type-erased byte payloads with the component's move/destroy functions, so it can be filled from jobs that "},{"t":"cinder.ecs.commands.CommandBuffer","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-commandbuffer","x":"struct CommandBuffer(Movable, Sized) Records spawns, despawns, adds and removes for a later apply on the main thread. Recording never touches the world, so a job can fill a buffer while systems iterate; one buffer is not safe to share between threads (no locks). Added values are moved into a payload"},{"t":"cinder.ecs.commands.CommandBuffer.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-__init__","x":"__init__(out self)"},{"t":"cinder.ecs.commands.CommandBuffer.clear","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-clear","x":"clear(mut self) Drops every recorded command, destroying the values of unapplied adds. Use it to discard what is left after an apply that raised."},{"t":"cinder.ecs.commands.CommandBuffer.spawn","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-spawn","x":"spawn(mut self) -> Int Returns a placeholder index usable with add_to_spawned; see resolved after apply."},{"t":"cinder.ecs.commands.CommandBuffer.despawn","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-despawn","x":"despawn(mut self, e: Entity) Records a despawn of e; apply skips it when e is no longer alive by then."},{"t":"cinder.ecs.commands.CommandBuffer.add","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-add","x":"add[T: Component](mut self, e: Entity, var value: T) Records adding (or replacing) T on e, moving value into the buffer. apply raises when T was never registered with the world or when e is not alive."},{"t":"cinder.ecs.commands.CommandBuffer.add_to_spawned","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-add_to_spawned","x":"add_to_spawned[T: Component](mut self, placeholder: Int, var value: T) Like add, for the entity created by the spawn() call that returned placeholder."},{"t":"cinder.ecs.commands.CommandBuffer.remove","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-remove","x":"remove[T: Component](mut self, e: Entity) Records removing T from e; apply skips a missing T, raises if e is dead."},{"t":"cinder.ecs.commands.CommandBuffer.__len__","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-__len__","x":"__len__(self) -> Int"},{"t":"cinder.ecs.commands.CommandBuffer.apply","s":"API symbol","h":"method","u":"/docs/api/ecs/#commandbuffer-apply","x":"apply(mut self, mut world: World) raises -> Int Applies every command in order and clears the buffer. Returns how many were applied. Payload values are moved into the world through the world's registered move function, so the world must already know the component type (register it or add it once dir"},{"t":"cinder.ecs.hierarchy","s":"API symbol","h":"module","u":"/docs/api/ecs/#cinderecshierarchy","x":"Parent-child transforms: Parent, LocalTransform, WorldTransform and their propagation."},{"t":"cinder.ecs.hierarchy.Parent","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-parent","x":"struct Parent(Component) Parent link for propagate_transforms (id 1); a dead parent or a self link is ignored."},{"t":"cinder.ecs.hierarchy.Parent.component_id","s":"API symbol","h":"method","u":"/docs/api/ecs/#parent-component_id","x":"component_id() -> Int"},{"t":"cinder.ecs.hierarchy.LocalTransform","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-localtransform","x":"struct LocalTransform(Component) Transform relative to the Parent (to world space without one); component id 2."},{"t":"cinder.ecs.hierarchy.LocalTransform.component_id","s":"API symbol","h":"method","u":"/docs/api/ecs/#localtransform-component_id","x":"component_id() -> Int"},{"t":"cinder.ecs.hierarchy.WorldTransform","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-worldtransform","x":"struct WorldTransform(Component) World-space transform, parent world * local, written by propagate_transforms; id 3."},{"t":"cinder.ecs.hierarchy.WorldTransform.component_id","s":"API symbol","h":"method","u":"/docs/api/ecs/#worldtransform-component_id","x":"component_id() -> Int"},{"t":"cinder.ecs.hierarchy.propagate_transforms","s":"API symbol","h":"def","u":"/docs/api/ecs/#def-propagate_transforms","x":"def propagate_transforms(mut world: World) raises -> Int Writes WorldTransform = parent world * LocalTransform for every entity with a local transform (adding the component when missing). Parents are resolved recursively with memoisation, so the order of entities does not matter and each is computed"},{"t":"cinder.ecs.schedule","s":"API symbol","h":"module","u":"/docs/api/ecs/#cinderecsschedule","x":"System scheduling by declared component access. Each system names the components it reads and writes. Schedule.build groups systems into stages in declaration order: a system joins the current stage unless it conflicts with one already there (write/write or read/write on the same component, or a dec"},{"t":"cinder.ecs.schedule.SystemDesc","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-systemdesc","x":"struct SystemDesc(Copyable, Movable) A named system plus the component ids it reads and writes, used by Schedule for staging. The declarations are trusted: nothing checks what run actually accesses."},{"t":"cinder.ecs.schedule.SystemDesc.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#systemdesc-__init__","x":"__init__( out self, name: String, run: SystemFn, exclusive: Bool = False )"},{"t":"cinder.ecs.schedule.SystemDesc.reads_","s":"API symbol","h":"method","u":"/docs/api/ecs/#systemdesc-reads_","x":"reads_[T: Component](var self) -> SystemDesc Declares a read of T; returns the description for chaining."},{"t":"cinder.ecs.schedule.SystemDesc.writes_","s":"API symbol","h":"method","u":"/docs/api/ecs/#systemdesc-writes_","x":"writes_[T: Component](var self) -> SystemDesc Declares a write of T; returns the description for chaining."},{"t":"cinder.ecs.schedule.SystemDesc.conflicts_with","s":"API symbol","h":"method","u":"/docs/api/ecs/#systemdesc-conflicts_with","x":"conflicts_with(self, other: SystemDesc) -> Bool Whether the two systems may not share a stage. True when either is exclusive or one writes a component id the other reads or writes; two readers of the same component never conflict."},{"t":"cinder.ecs.schedule.Schedule","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-schedule","x":"struct Schedule(Movable) Systems grouped into stages by declared access (see the module docstring)."},{"t":"cinder.ecs.schedule.Schedule.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#schedule-__init__","x":"__init__(out self)"},{"t":"cinder.ecs.schedule.Schedule.add","s":"API symbol","h":"method","u":"/docs/api/ecs/#schedule-add","x":"add(mut self, var system: SystemDesc) Appends a system after those already added and marks the stages for rebuilding."},{"t":"cinder.ecs.schedule.Schedule.build","s":"API symbol","h":"method","u":"/docs/api/ecs/#schedule-build","x":"build(mut self) Assigns systems to stages in declaration order. A system joins the last stage unless it conflicts with any system there, otherwise it opens a new stage; earlier stages are never revisited, so the order affects packing."},{"t":"cinder.ecs.schedule.Schedule.stage_count","s":"API symbol","h":"method","u":"/docs/api/ecs/#schedule-stage_count","x":"stage_count(mut self) -> Int Number of stages, building them first when systems changed since the last build."},{"t":"cinder.ecs.schedule.Schedule.run","s":"API symbol","h":"method","u":"/docs/api/ecs/#schedule-run","x":"run( mut self, mut world: World, jobs: Optional[Pointer[JobSystem, MutUntrackedOrigin]], ) Runs every stage. With a job system, systems in one stage run as parallel jobs; without one (or for single-system stages) they run inline on the calling thread."},{"t":"cinder.ecs.world","s":"API symbol","h":"module","u":"/docs/api/ecs/#cinderecsworld","x":"Archetype ECS: entities, type-erased component columns, typed queries, change detection. Components are structs conforming to Component (a unique component_id()). The world registers a component type on first use, recording its size, alignment, and type-erased move/destroy functions, so columns can "},{"t":"cinder.ecs.world.Component","s":"API symbol","h":"trait","u":"/docs/api/ecs/#trait-component","x":"trait Component(Copyable, Deinitable, Movable) A component type. component_id() must be unique per type (0 .. MAX_COMPONENTS - 1)."},{"t":"cinder.ecs.world.Component.component_id","s":"API symbol","h":"method","u":"/docs/api/ecs/#component-component_id","x":"component_id() -> Int The type's id, unique among component types and in 0 .. MAX_COMPONENTS - 1."},{"t":"cinder.ecs.world.Entity","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-entity","x":"struct Entity( Equatable, Hashable, ImplicitlyCopyable, RegisterPassable, Writable ) Generational entity id: index + generation. Entity.null() is never live."},{"t":"cinder.ecs.world.Entity.null","s":"API symbol","h":"method","u":"/docs/api/ecs/#entity-null","x":"null() -> Entity The null entity (index 0xFFFFFFFF, generation 0): never alive, False as a Bool."},{"t":"cinder.ecs.world.Entity.is_null","s":"API symbol","h":"method","u":"/docs/api/ecs/#entity-is_null","x":"is_null(self) -> Bool Whether the index is the null index 0xFFFFFFFF (the generation is not compared)."},{"t":"cinder.ecs.world.Entity.packed","s":"API symbol","h":"method","u":"/docs/api/ecs/#entity-packed","x":"packed(self) -> UInt64 Index in the low 32 bits, generation in the high 32; from_packed inverts it."},{"t":"cinder.ecs.world.Entity.from_packed","s":"API symbol","h":"method","u":"/docs/api/ecs/#entity-from_packed","x":"from_packed(v: UInt64) -> Entity Inverse of packed(): index from the low 32 bits, generation from the high 32."},{"t":"cinder.ecs.world.Entity.write_to","s":"API symbol","h":"method","u":"/docs/api/ecs/#entity-write_to","x":"write_to(self, mut w: Some[Writer])"},{"t":"cinder.ecs.world.component_stride","s":"API symbol","h":"def","u":"/docs/api/ecs/#def-component_stride","x":"def component_stride[T: Component]() -> Int Element stride: the size rounded up to the type's alignment, so arrays stay tight (an 8-byte component takes 8 bytes per row) while every element is correctly aligned."},{"t":"cinder.ecs.world.ComponentInfo","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-componentinfo","x":"struct ComponentInfo(ImplicitlyCopyable, Movable) Registration record per component id: size, alignment, row stride, type name and type-erased destroy/move. Every id has one; ids that were never registered hold no-op functions, size 0, an empty type name and registered False."},{"t":"cinder.ecs.world.Column","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-column","x":"struct Column(Movable) One component's storage inside an archetype: count elements of stride bytes."},{"t":"cinder.ecs.world.Column.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#column-__init__","x":"__init__(out self, component: Int, stride: Int, capacity: Int)"},{"t":"cinder.ecs.world.Column.capacity","s":"API symbol","h":"method","u":"/docs/api/ecs/#column-capacity","x":"capacity(self) -> Int Rows that fit in the allocated blocks before the column must grow."},{"t":"cinder.ecs.world.Column.base","s":"API symbol","h":"method","u":"/docs/api/ecs/#column-base","x":"base(self) -> ErasedPtr Untyped pointer to row 0; dangles once the column grows."},{"t":"cinder.ecs.world.Column.element","s":"API symbol","h":"method","u":"/docs/api/ecs/#column-element","x":"element(self, row: Int) -> ErasedPtr Untyped pointer to row (row * stride bytes past base); no bounds check."},{"t":"cinder.ecs.world.Column.grow","s":"API symbol","h":"method","u":"/docs/api/ecs/#column-grow","x":"grow(mut self, needed: Int) Ensures room for needed rows, at least doubling the capacity. Existing rows are relocated bytewise (no move constructor runs), so stored values must not point into themselves, and pointers from base/element dangle afterwards. ticks is left to the caller."},{"t":"cinder.ecs.world.Archetype","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-archetype","x":"struct Archetype(Movable) Entities sharing one component set. Columns are parallel to entities."},{"t":"cinder.ecs.world.Archetype.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#archetype-__init__","x":"__init__( out self, var signature: List[Int], infos: List[ComponentInfo] )"},{"t":"cinder.ecs.world.Archetype.count","s":"API symbol","h":"method","u":"/docs/api/ecs/#archetype-count","x":"count(self) -> Int Number of entities (rows) stored in this archetype."},{"t":"cinder.ecs.world.Archetype.has","s":"API symbol","h":"method","u":"/docs/api/ecs/#archetype-has","x":"has(self, component: Int) -> Bool Whether component id component is in this archetype's signature."},{"t":"cinder.ecs.world.Archetype.matches","s":"API symbol","h":"method","u":"/docs/api/ecs/#archetype-matches","x":"matches(self, with_: List[Int], without: List[Int]) -> Bool Whether the archetype has every id in with_ and none of the ids in without."},{"t":"cinder.ecs.world.Archetype.reserve_row","s":"API symbol","h":"method","u":"/docs/api/ecs/#archetype-reserve_row","x":"reserve_row(mut self, entity: Entity, tick: UInt32) -> Int Appends entity with a new row stamped tick in every column; returns the row index. Component values in the new row are uninitialised: the caller must write every column."},{"t":"cinder.ecs.world.Archetype.swap_remove","s":"API symbol","h":"method","u":"/docs/api/ecs/#archetype-swap_remove","x":"swap_remove( mut self, row: Int, infos: List[ComponentInfo], destroy: Bool ) -> Entity Removes row, moving the last row into it. Returns the entity that moved (or null)."},{"t":"cinder.ecs.world.World","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-world","x":"struct World(Movable) The entity store: component registry, archetypes, entity locations and the change tick. New entities start in archetype 0 (no components); archetypes are created on demand and never removed. The world has no locks and belongs to the main thread: structural changes, register and"},{"t":"cinder.ecs.world.World.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-__init__","x":"__init__(out self)"},{"t":"cinder.ecs.world.World.register","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-register","x":"register[T: Component](mut self) raises -> Int Registers T under its component_id() and returns the id; repeat calls are no-ops. Raises when the id is outside 0 .. MAX_COMPONENTS - 1, when the id is already registered by another type (different type name, size or alignment), or when T needs alignmen"},{"t":"cinder.ecs.world.World.advance_tick","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-advance_tick","x":"advance_tick(mut self) -> UInt32 Increments the change tick and returns it; later writes are stamped with it."},{"t":"cinder.ecs.world.World.spawn","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-spawn","x":"spawn(mut self) -> Entity Creates an entity without components and returns it. A freed index is reused with its generation incremented, so handles to the despawned entity stay dead; otherwise a new index starts at generation 1."},{"t":"cinder.ecs.world.World.is_alive","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-is_alive","x":"is_alive(self, e: Entity) -> Bool Whether e is not null, in range, alive, and of the index's current generation."},{"t":"cinder.ecs.world.World.despawn","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-despawn","x":"despawn(mut self, e: Entity) raises Destroys the entity's components and frees its index for reuse. Raises when e is not alive. The row is swap-removed, so the archetype's last entity moves into it (pointers into that archetype dangle)."},{"t":"cinder.ecs.world.World.count","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-count","x":"count(self) -> Int Number of live entities."},{"t":"cinder.ecs.world.World.add","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-add","x":"add[T: Component](mut self, e: Entity, var value: T) raises Adds (or replaces) a component. Replacing marks it changed."},{"t":"cinder.ecs.world.World.remove","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-remove","x":"remove[T: Component](mut self, e: Entity) raises -> Bool Removes a component; False when the entity did not have it."},{"t":"cinder.ecs.world.World.has","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-has","x":"has[T: Component](self, e: Entity) -> Bool Whether e is alive and has a T; False (no raise) for dead or null entities."},{"t":"cinder.ecs.world.World.get","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-get","x":"get[T: Component](self, e: Entity) raises -> T A copy of the component."},{"t":"cinder.ecs.world.World.get_ptr","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-get_ptr","x":"get_ptr[ T: Component ](self, e: Entity) raises -> Pointer[T, MutUntrackedOrigin] Direct pointer, valid until the next structural change. Does not mark the row changed."},{"t":"cinder.ecs.world.World.get_mut","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-get_mut","x":"get_mut[ T: Component ](mut self, e: Entity) raises -> Pointer[T, MutUntrackedOrigin] Like get_ptr and marks the component changed at the current tick."},{"t":"cinder.ecs.world.World.set","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-set","x":"set[T: Component](mut self, e: Entity, var value: T) raises Overwrites an existing T and marks it changed (via get_mut). Raises when e is not alive or has no T; use add to attach a new component."},{"t":"cinder.ecs.world.World.changed_tick","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-changed_tick","x":"changed_tick[T: Component](self, e: Entity) raises -> UInt32 The tick at which e's T was last written. Raises when e is dead or has no T."},{"t":"cinder.ecs.world.World.entities","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-entities","x":"entities(self) -> List[Entity] A new list of every live entity, grouped by archetype (not in spawn order)."},{"t":"cinder.ecs.world.World.archetype_count","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-archetype_count","x":"archetype_count(self) -> Int Archetypes created so far, including the empty one (they are never removed)."},{"t":"cinder.ecs.world.World.query","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-query","x":"query(self) -> Query Starts a Query builder over this world. The query keeps an untracked pointer to the world and must not outlive it. Systems that run every frame should keep a QuerySpec and use collect_chunks, which reuses its list."},{"t":"cinder.ecs.world.World.collect_prop_chunks","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-collect_prop_chunks","x":"collect_prop_chunks(mut self) collect_chunks of prop_spec into the world's own prop_chunks scratch (transform propagation)."},{"t":"cinder.ecs.world.World.collect_chunks","s":"API symbol","h":"method","u":"/docs/api/ecs/#world-collect_chunks","x":"collect_chunks(self, spec: QuerySpec, mut out: List[Chunk]) Fills out (cleared first, capacity kept) with the chunks matching spec. Systems keep their QuerySpec and chunk list across frames so steady-state frames allocate nothing."},{"t":"cinder.ecs.world.pass_through","s":"API symbol","h":"def","u":"/docs/api/ecs/#def-pass_through","x":"def pass_through(p: ErasedPtr) Hands p to keep; the body of the no-op destroy/move used for unregistered ids."},{"t":"cinder.ecs.world.Chunk","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-chunk","x":"struct Chunk(ImplicitlyCopyable, Movable) One archetype's rows matched by a query: typed column access by row index."},{"t":"cinder.ecs.world.Chunk.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-__init__","x":"__init__( out self, world: Pointer[World, MutUntrackedOrigin], archetype: Int )"},{"t":"cinder.ecs.world.Chunk.has","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-has","x":"has[T: Component](self) -> Bool Whether every entity in this chunk has T."},{"t":"cinder.ecs.world.Chunk.entity","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-entity","x":"entity(self, row: Int) -> Entity The entity at row of this chunk's archetype."},{"t":"cinder.ecs.world.Chunk.column","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-column","x":"column[T: Component](self) -> Pointer[T, MutUntrackedOrigin] Base pointer of the component column (element row is column[T]()[unsafe_offset=row]). Aborts when the chunk has no T (use has for optional components)."},{"t":"cinder.ecs.world.Chunk.stride","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-stride","x":"stride[T: Component]() -> Int Bytes between consecutive T elements of a column (component_stride[T]())."},{"t":"cinder.ecs.world.Chunk.get","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-get","x":"get[T: Component](self, row: Int) -> Pointer[T, MutUntrackedOrigin] Pointer to the T at row; does not mark it changed (see mark_changed). Aborts when the chunk has no T (use has); row is not range-checked. The pointer dangles after the next structural change."},{"t":"cinder.ecs.world.Chunk.mark_changed","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-mark_changed","x":"mark_changed[T: Component](self, row: Int) Stamps the T at row with the world's current tick; aborts when the chunk has no T."},{"t":"cinder.ecs.world.Chunk.changed_since","s":"API symbol","h":"method","u":"/docs/api/ecs/#chunk-changed_since","x":"changed_since[T: Component](self, row: Int, tick: UInt32) -> Bool Whether the T at row was written after tick (a write at exactly tick is not). Aborts when the chunk has no T."},{"t":"cinder.ecs.world.QuerySpec","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-queryspec","x":"struct QuerySpec(Copyable, Movable) The filters of a query without a world pointer: safe to keep in a system struct."},{"t":"cinder.ecs.world.QuerySpec.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#queryspec-__init__","x":"__init__(out self)"},{"t":"cinder.ecs.world.QuerySpec.with_","s":"API symbol","h":"method","u":"/docs/api/ecs/#queryspec-with_","x":"with_[T: Component](var self) -> QuerySpec Requires component T; returns the spec for chaining."},{"t":"cinder.ecs.world.QuerySpec.without","s":"API symbol","h":"method","u":"/docs/api/ecs/#queryspec-without","x":"without[T: Component](var self) -> QuerySpec Excludes archetypes that have T; returns the spec for chaining."},{"t":"cinder.ecs.world.Query","s":"API symbol","h":"struct","u":"/docs/api/ecs/#struct-query","x":"struct Query(Movable) Builder: world.query().with_[A]().with_[B]().without[C]().chunks() lists matching chunks. Component access inside a chunk is typed (chunk.column[A]()); the filters decide which archetypes take part."},{"t":"cinder.ecs.world.Query.__init__","s":"API symbol","h":"method","u":"/docs/api/ecs/#query-__init__","x":"__init__(out self, world: Pointer[World, MutUntrackedOrigin])"},{"t":"cinder.ecs.world.Query.with_","s":"API symbol","h":"method","u":"/docs/api/ecs/#query-with_","x":"with_[T: Component](var self) -> Query Requires component T; returns the query for chaining."},{"t":"cinder.ecs.world.Query.without","s":"API symbol","h":"method","u":"/docs/api/ecs/#query-without","x":"without[T: Component](var self) -> Query Excludes archetypes that have T; returns the query for chaining."},{"t":"cinder.ecs.world.Query.chunks","s":"API symbol","h":"method","u":"/docs/api/ecs/#query-chunks","x":"chunks(self) -> List[Chunk] A new list with one Chunk per non-empty matching archetype, in archetype order. Each chunk's count is captured here and goes stale after a structural change."},{"t":"cinder.ecs.world.Query.count","s":"API symbol","h":"method","u":"/docs/api/ecs/#query-count","x":"count(self) -> Int Number of matching entities (builds the chunk list to sum it)."},{"t":"cinder.gfx","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfx-1","x":"Safe, typed layer over wgpu-native. Gfx owns the instance, adapter, device and queue and drains the shim's event queue into diagnostics (validation errors, device loss, wgpu log). Resource modules build on it with one distinct handle type per resource kind and ownership through __deinit__."},{"t":"cinder.gfx.commands","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxcommands","x":"Command recording: encoders, render and compute passes, copies, submission and readback."},{"t":"cinder.gfx.commands.ColorAttachment","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-colorattachment","x":"struct ColorAttachment(Copyable, Movable) A colour target of a render pass. view may be a swapchain view obtained outside Resources; resolve is the single-sample target when view is multisampled (null otherwise)."},{"t":"cinder.gfx.commands.ColorAttachment.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#colorattachment-__init__","x":"__init__( out self, view: WGPUTextureView, clear: Bool, var clear_color: WGPUColor, store: Bool, resolve: WGPUTextureView = WGPUTextureView(), )"},{"t":"cinder.gfx.commands.ColorAttachment.cleared","s":"API symbol","h":"method","u":"/docs/api/gfx/#colorattachment-cleared","x":"cleared( view: WGPUTextureView, r: Float64, g: Float64, b: Float64, a: Float64 ) -> ColorAttachment Clears view to (r, g, b, a) at load and stores the result; no resolve target."},{"t":"cinder.gfx.commands.ColorAttachment.cleared_resolving","s":"API symbol","h":"method","u":"/docs/api/gfx/#colorattachment-cleared_resolving","x":"cleared_resolving( view: WGPUTextureView, resolve: WGPUTextureView, r: Float64, g: Float64, b: Float64, a: Float64, ) -> ColorAttachment Clears the multisampled view to (r, g, b, a), resolves into resolve and discards the multisampled contents (store op Discard)."},{"t":"cinder.gfx.commands.ColorAttachment.loaded","s":"API symbol","h":"method","u":"/docs/api/gfx/#colorattachment-loaded","x":"loaded(view: WGPUTextureView) -> ColorAttachment Keeps the existing contents of view (load op Load) and stores the result."},{"t":"cinder.gfx.commands.DepthAttachment","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-depthattachment","x":"struct DepthAttachment(ImplicitlyCopyable, Movable) Depth target. Reverse-Z clears to 0.0 (far)."},{"t":"cinder.gfx.commands.DepthAttachment.none","s":"API symbol","h":"method","u":"/docs/api/gfx/#depthattachment-none","x":"none() -> DepthAttachment No depth target: begin_render_pass then passes no depth-stencil attachment."},{"t":"cinder.gfx.commands.DepthAttachment.cleared_reverse_z","s":"API symbol","h":"method","u":"/docs/api/gfx/#depthattachment-cleared_reverse_z","x":"cleared_reverse_z(view: WGPUTextureView) -> DepthAttachment Clears view to 0.0 (the far plane under reverse-Z) and stores the result."},{"t":"cinder.gfx.commands.RenderPass","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-renderpass","x":"struct RenderPass(Movable) An open render pass. end must be called exactly once."},{"t":"cinder.gfx.commands.RenderPass.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], raw: WGPURenderPassEncoder, )"},{"t":"cinder.gfx.commands.RenderPass.set_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-set_pipeline","x":"set_pipeline(self, h: RenderPipelineHandle) raises Binds the render pipeline; raises if the handle is not live in Resources."},{"t":"cinder.gfx.commands.RenderPass.set_bind_group","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-set_bind_group","x":"set_bind_group(self, index: UInt32, h: BindGroupHandle) raises Binds a bind group at group index with no dynamic offsets; raises on an invalid handle."},{"t":"cinder.gfx.commands.RenderPass.set_bind_group_dynamic","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-set_bind_group_dynamic","x":"set_bind_group_dynamic( self, index: UInt32, h: BindGroupHandle, mut offsets: List[UInt32] ) raises Binds a bind group at group index with one dynamic offset per dynamic binding. offsets is read during the call only. Raises on an invalid handle; a wrong offset count or alignment surfaces as a valida"},{"t":"cinder.gfx.commands.RenderPass.set_vertex_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-set_vertex_buffer","x":"set_vertex_buffer( self, slot: UInt32, h: BufferHandle, offset: UInt64 = 0 ) raises Binds h to vertex slot slot from offset to the end of the buffer. Raises on an invalid handle or when offset is not below the buffer size (nothing is bound then)."},{"t":"cinder.gfx.commands.RenderPass.set_index_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-set_index_buffer","x":"set_index_buffer( self, h: BufferHandle, format: WGPUIndexFormat, offset: UInt64 = 0 ) raises Binds h as the index buffer with format from offset to the end of the buffer. Raises on an invalid handle or when offset is not below the buffer size (nothing is bound then)."},{"t":"cinder.gfx.commands.RenderPass.set_viewport","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-set_viewport","x":"set_viewport( self, x: Float32, y: Float32, width: Float32, height: Float32 ) Sets the viewport rectangle in pixels with depth range 0..1."},{"t":"cinder.gfx.commands.RenderPass.set_scissor","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-set_scissor","x":"set_scissor(self, x: UInt32, y: UInt32, width: UInt32, height: UInt32) Sets the scissor rectangle in pixels."},{"t":"cinder.gfx.commands.RenderPass.draw","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-draw","x":"draw( self, vertex_count: UInt32, instance_count: UInt32 = 1, first_vertex: UInt32 = 0, first_instance: UInt32 = 0, ) Draws non-indexed primitives with the bound pipeline and vertex buffers."},{"t":"cinder.gfx.commands.RenderPass.draw_indexed","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-draw_indexed","x":"draw_indexed( self, index_count: UInt32, instance_count: UInt32 = 1, first_index: UInt32 = 0, base_vertex: Int32 = 0, first_instance: UInt32 = 0, ) Draws indexed primitives from the bound index buffer; base_vertex is added to each index."},{"t":"cinder.gfx.commands.RenderPass.end","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpass-end","x":"end(mut self) Ends and releases the pass encoder. Later calls do nothing. A pass dropped without end is ended and released by its destructor, which runs right after the pass's last use, so the encoder is never left with an open pass."},{"t":"cinder.gfx.commands.ComputePass","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-computepass","x":"struct ComputePass(Movable) An open compute pass from CommandEncoder.begin_compute_pass. end must be called exactly once."},{"t":"cinder.gfx.commands.ComputePass.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#computepass-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], raw: WGPUComputePassEncoder, )"},{"t":"cinder.gfx.commands.ComputePass.set_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#computepass-set_pipeline","x":"set_pipeline(self, h: ComputePipelineHandle) raises Binds the compute pipeline; raises if the handle is not live in Resources."},{"t":"cinder.gfx.commands.ComputePass.set_bind_group","s":"API symbol","h":"method","u":"/docs/api/gfx/#computepass-set_bind_group","x":"set_bind_group(self, index: UInt32, h: BindGroupHandle) raises Binds a bind group at group index with no dynamic offsets; raises on an invalid handle."},{"t":"cinder.gfx.commands.ComputePass.set_bind_group_dynamic","s":"API symbol","h":"method","u":"/docs/api/gfx/#computepass-set_bind_group_dynamic","x":"set_bind_group_dynamic( self, index: UInt32, h: BindGroupHandle, mut offsets: List[UInt32] ) raises Binds a bind group at group index with one dynamic offset per dynamic binding. offsets is read during the call only. Raises on an invalid handle; a wrong offset count or alignment surfaces as a valida"},{"t":"cinder.gfx.commands.ComputePass.dispatch","s":"API symbol","h":"method","u":"/docs/api/gfx/#computepass-dispatch","x":"dispatch(self, x: UInt32, y: UInt32 = 1, z: UInt32 = 1) Dispatches an x by y by z grid of workgroups with the bound pipeline."},{"t":"cinder.gfx.commands.ComputePass.end","s":"API symbol","h":"method","u":"/docs/api/gfx/#computepass-end","x":"end(mut self) Ends and releases the pass encoder. Later calls do nothing. A pass dropped without end is ended and released by its destructor, which runs right after the pass's last use, so the encoder is never left with an open pass."},{"t":"cinder.gfx.commands.command_encoder_descriptor","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-command_encoder_descriptor","x":"def command_encoder_descriptor(ref label: String) -> WGPUCommandEncoderDescriptor The descriptor CommandEncoder creates its encoder with: no chain, label as the label. The view borrows label, which must outlive the wgpu call (keep it)."},{"t":"cinder.gfx.commands.CommandEncoder","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-commandencoder","x":"struct CommandEncoder(Movable) Records one command buffer. finish_and_submit consumes it; an encoder dropped without it is released by its destructor and its commands are discarded."},{"t":"cinder.gfx.commands.CommandEncoder.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#commandencoder-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], label: String, ) raises"},{"t":"cinder.gfx.commands.CommandEncoder.begin_render_pass","s":"API symbol","h":"method","u":"/docs/api/gfx/#commandencoder-begin_render_pass","x":"begin_render_pass( self, colors: List[ColorAttachment], depth: DepthAttachment ) raises -> RenderPass Begins a render pass on the given colour targets and optional depth target. Raises when there are more than MAX_COLOR_ATTACHMENTS colours or wgpu returns NULL. The depth attachment is used only when"},{"t":"cinder.gfx.commands.CommandEncoder.begin_compute_pass","s":"API symbol","h":"method","u":"/docs/api/gfx/#commandencoder-begin_compute_pass","x":"begin_compute_pass(self) raises -> ComputePass Begins a compute pass without timestamp writes; raises if wgpu returns NULL. Call ComputePass.end before recording anything else on this encoder."},{"t":"cinder.gfx.commands.CommandEncoder.copy_buffer_to_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#commandencoder-copy_buffer_to_buffer","x":"copy_buffer_to_buffer( self, src: BufferHandle, src_offset: UInt64, dst: BufferHandle, dst_offset: UInt64, size: UInt64, ) raises Records a copy of size bytes between two buffers. Raises on an invalid handle; range and usage errors surface as validation diagnostics through Gfx.pump."},{"t":"cinder.gfx.commands.CommandEncoder.copy_texture_to_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#commandencoder-copy_texture_to_buffer","x":"copy_texture_to_buffer( self, texture: TextureHandle, dst: BufferHandle, bytes_per_row: UInt32, width: UInt32, height: UInt32, ) raises Copies mip 0, layer 0 of a Resources texture into dst at offset 0, with bytes_per_row stride and height rows per image. Raises on an invalid handle."},{"t":"cinder.gfx.commands.CommandEncoder.copy_raw_texture_to_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#commandencoder-copy_raw_texture_to_buffer","x":"copy_raw_texture_to_buffer( self, texture: WGPUTexture, dst: BufferHandle, bytes_per_row: UInt32, width: UInt32, height: UInt32, ) raises Copies mip 0 of a texture outside Resources (a swapchain texture) into dst."},{"t":"cinder.gfx.commands.CommandEncoder.finish_and_submit","s":"API symbol","h":"method","u":"/docs/api/gfx/#commandencoder-finish_and_submit","x":"finish_and_submit(deinit self) raises Finishes the encoder, submits the command buffer to the queue and releases both. Raises if wgpuCommandEncoderFinish returns NULL (the encoder is still released). Does not wait for the GPU; use wait_for_queue for that."},{"t":"cinder.gfx.commands.wait_for_queue","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-wait_for_queue","x":"def wait_for_queue(mut gfx: Gfx) raises GfxError Blocks until all submitted work has completed (tests and shutdown; never in the frame loop)."},{"t":"cinder.gfx.commands.read_buffer","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-read_buffer","x":"def read_buffer( mut gfx: Gfx, res: Resources, buffer: BufferHandle, size: Int ) raises -> List[UInt8] Maps a MapRead buffer, copies size bytes out and unmaps it (tests and tools only)."},{"t":"cinder.gfx.debug_draw","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxdebug_draw","x":"Immediate-mode debug lines: segments, boxes, spheres, axes and grids drawn in one line-list pass. Vertices accumulate on the CPU each frame (line, aabb, ...) and are uploaded once in flush, which records the draw into an open RenderPass. The vertex buffer grows by doubling and is never shrunk, so th"},{"t":"cinder.gfx.debug_draw.Color8","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-color8","x":"struct Color8(Equatable, ImplicitlyCopyable, RegisterPassable) 8-bit RGBA colour."},{"t":"cinder.gfx.debug_draw.Color8.red","s":"API symbol","h":"method","u":"/docs/api/gfx/#color8-red","x":"red() -> Color8 Opaque red (255, 0, 0, 255)."},{"t":"cinder.gfx.debug_draw.Color8.green","s":"API symbol","h":"method","u":"/docs/api/gfx/#color8-green","x":"green() -> Color8 Opaque green (0, 255, 0, 255)."},{"t":"cinder.gfx.debug_draw.Color8.blue","s":"API symbol","h":"method","u":"/docs/api/gfx/#color8-blue","x":"blue() -> Color8 Opaque blue (0, 0, 255, 255)."},{"t":"cinder.gfx.debug_draw.Color8.white","s":"API symbol","h":"method","u":"/docs/api/gfx/#color8-white","x":"white() -> Color8 Opaque white (255, 255, 255, 255)."},{"t":"cinder.gfx.debug_draw.Color8.yellow","s":"API symbol","h":"method","u":"/docs/api/gfx/#color8-yellow","x":"yellow() -> Color8 Opaque yellow (255, 255, 0, 255)."},{"t":"cinder.gfx.debug_draw.DebugDraw","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-debugdraw","x":"struct DebugDraw(Movable) Per-frame line accumulator plus the GPU objects that draw it."},{"t":"cinder.gfx.debug_draw.DebugDraw.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], mut shaders: ShaderManager, color_format: WGPUTextureFormat, depth_format: WGPUTextureFormat = WGPUTextureFormat_Undefined, initial_capacity: Int = 4096, ) raises"},{"t":"cinder.gfx.debug_draw.DebugDraw.clear","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-clear","x":"clear(mut self) Drops this frame's lines (called by flush)."},{"t":"cinder.gfx.debug_draw.DebugDraw.line","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-line","x":"line(mut self, a: Vec3, b: Vec3, color: Color8) Queues one world-space segment from a to b for this frame (two vertices). Only CPU memory grows here; upload enlarges the GPU buffer if needed, flush draws and clears."},{"t":"cinder.gfx.debug_draw.DebugDraw.aabb","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-aabb","x":"aabb(mut self, min: Vec3, max: Vec3, color: Color8) The 12 edges of an axis-aligned box."},{"t":"cinder.gfx.debug_draw.DebugDraw.sphere","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-sphere","x":"sphere( mut self, centre: Vec3, radius: Float32, color: Color8, segments: Int = 24, ) Three great circles (XY, XZ, YZ)."},{"t":"cinder.gfx.debug_draw.DebugDraw.axes","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-axes","x":"axes(mut self, origin: Vec3, length: Float32) X red, Y green, Z blue."},{"t":"cinder.gfx.debug_draw.DebugDraw.grid","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-grid","x":"grid(mut self, half_extent: Int, spacing: Float32, color: Color8) An XZ ground grid centred on the origin with 2 * half_extent + 1 lines per axis."},{"t":"cinder.gfx.debug_draw.DebugDraw.upload","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-upload","x":"upload(mut self, view_proj: Mat4) raises Writes the camera and this frame's vertices to the GPU (before the render pass opens)."},{"t":"cinder.gfx.debug_draw.DebugDraw.flush","s":"API symbol","h":"method","u":"/docs/api/gfx/#debugdraw-flush","x":"flush(mut self, pass_: RenderPass) raises Records the draw for the vertices uploaded by upload, then clears them."},{"t":"cinder.gfx.device","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxdevice","x":"Gfx: instance, adapter, device and queue, with every wgpu callback pulled from the shim queue."},{"t":"cinder.gfx.device.GfxError","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-gfxerror","x":"struct GfxError(Copyable, Movable, Writable) A recoverable graphics failure (adapter/device request, timeout, lost device)."},{"t":"cinder.gfx.device.GfxError.write_to","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfxerror-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx.device.Gfx","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-gfx","x":"struct Gfx(Movable) Owns one wgpu instance, adapter, device and queue. Callbacks never run in Mojo: pump drives wgpu and drains the shim's event queue, turning log lines, uncaptured validation errors and device loss into diagnostics (LogRecords for the engine logger) while returning every other even"},{"t":"cinder.gfx.device.Gfx.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-__init__","x":"__init__( out self, lib: NativePtr, surface: WGPUSurface ) raises GfxError Creates instance, adapter (compatible with surface when given) and device."},{"t":"cinder.gfx.device.Gfx.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-__init__-1","x":"__init__(out self, lib: NativePtr, ref window: Window) raises GfxError Creates instance, a surface for window, a compatible adapter and device. Gfx keeps the surface (surface field) and destroys it at teardown."},{"t":"cinder.gfx.device.Gfx.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-__init__-2","x":"__init__( out self, lib: NativePtr, surface: WGPUSurface, window: Optional[Pointer[Window, MutUntrackedOrigin]], ) raises GfxError"},{"t":"cinder.gfx.device.Gfx.next_request_id","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-next_request_id","x":"next_request_id(mut self) -> UInt64 Returns a fresh id (1, 2, ...) that tags an async shim request so its reply event can be matched by await_event / take_pending."},{"t":"cinder.gfx.device.Gfx.pump","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-pump","x":"pump(mut self, wait: Bool) Drives wgpu callbacks and drains the shim queue. Log, error and device-lost events become diagnostics; everything else is held for await_event / take_pending."},{"t":"cinder.gfx.device.Gfx.await_event","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-await_event","x":"await_event( mut self, kind: UInt32, request_id: UInt64, timeout_ns: Int = DEFAULT_TIMEOUT_NS, ) raises GfxError -> CnEvent Pumps until the event (kind, request id) arrives; raises GfxError on timeout."},{"t":"cinder.gfx.device.Gfx.take_pending","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-take_pending","x":"take_pending( mut self, kind: UInt32, request_id: UInt64, mut out: List[CnEvent] ) Moves every pending event matching (kind, request id) into out, in arrival order."},{"t":"cinder.gfx.device.Gfx.take_diagnostics","s":"API symbol","h":"method","u":"/docs/api/gfx/#gfx-take_diagnostics","x":"take_diagnostics(mut self) -> List[LogRecord] Returns and clears the LogRecords collected by pump: wgpu log lines (wgpu), uncaptured validation errors (wgpu.validation) and device loss (wgpu.device). error_count and lost are not reset."},{"t":"cinder.gfx.pipeline","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxpipeline","x":"Typed descriptors for bind group layouts, bind groups, pipeline layouts and pipelines. Each create_* builds the wgpu descriptor from engine-side structs, calls wgpu with every pointee kept alive for the call, and adopts the result into Resources under a typed handle."},{"t":"cinder.gfx.pipeline.BindingKind","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bindingkind","x":"struct BindingKind(Equatable, ImplicitlyCopyable, RegisterPassable) What a bind group layout slot holds."},{"t":"cinder.gfx.pipeline.BindingKind.uniform_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-uniform_buffer","x":"uniform_buffer() -> BindingKind Uniform buffer; LayoutEntry.dynamic_offset and min_binding_size apply."},{"t":"cinder.gfx.pipeline.BindingKind.storage_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-storage_buffer","x":"storage_buffer() -> BindingKind Read-write storage buffer; dynamic_offset and min_binding_size apply."},{"t":"cinder.gfx.pipeline.BindingKind.readonly_storage_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-readonly_storage_buffer","x":"readonly_storage_buffer() -> BindingKind Read-only storage buffer; dynamic_offset and min_binding_size apply."},{"t":"cinder.gfx.pipeline.BindingKind.texture_2d","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-texture_2d","x":"texture_2d() -> BindingKind Filterable float texture with a 2D view dimension."},{"t":"cinder.gfx.pipeline.BindingKind.sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-sampler","x":"sampler() -> BindingKind Filtering sampler."},{"t":"cinder.gfx.pipeline.BindingKind.comparison_sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-comparison_sampler","x":"comparison_sampler() -> BindingKind Comparison sampler, as made by Resources.create_comparison_sampler."},{"t":"cinder.gfx.pipeline.BindingKind.depth_texture_2d","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-depth_texture_2d","x":"depth_texture_2d() -> BindingKind Depth texture with a 2D view dimension."},{"t":"cinder.gfx.pipeline.BindingKind.depth_texture_2d_array","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-depth_texture_2d_array","x":"depth_texture_2d_array() -> BindingKind Depth texture with a 2D-array view dimension."},{"t":"cinder.gfx.pipeline.BindingKind.texture_cube","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-texture_cube","x":"texture_cube() -> BindingKind Filterable float texture with a cube view dimension."},{"t":"cinder.gfx.pipeline.BindingKind.texture_2d_array","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-texture_2d_array","x":"texture_2d_array() -> BindingKind Filterable float texture with a 2D-array view dimension."},{"t":"cinder.gfx.pipeline.BindingKind.storage_texture_2d_write","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-storage_texture_2d_write","x":"storage_texture_2d_write() -> BindingKind Write-only storage texture; LayoutEntry.format names the texel format."},{"t":"cinder.gfx.pipeline.BindingKind.storage_texture_2d_array_write","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-storage_texture_2d_array_write","x":"storage_texture_2d_array_write() -> BindingKind Write-only storage texture with a 2D-array view; LayoutEntry.format names the texel format."},{"t":"cinder.gfx.pipeline.BindingKind.depth_texture_multisampled_2d","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-depth_texture_multisampled_2d","x":"depth_texture_multisampled_2d() -> BindingKind Multisampled depth texture with a 2D view dimension."},{"t":"cinder.gfx.pipeline.BindingKind.non_filtering_sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-non_filtering_sampler","x":"non_filtering_sampler() -> BindingKind Non-filtering sampler, for use with texture_2d_unfilterable textures."},{"t":"cinder.gfx.pipeline.BindingKind.texture_2d_unfilterable","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingkind-texture_2d_unfilterable","x":"texture_2d_unfilterable() -> BindingKind Float texture read with textureLoad or a non-filtering sampler (RGBA32Float)."},{"t":"cinder.gfx.pipeline.LayoutEntry","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-layoutentry","x":"struct LayoutEntry(ImplicitlyCopyable, Movable) One slot of a bind group layout. format matters for storage textures only."},{"t":"cinder.gfx.pipeline.LayoutEntry.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#layoutentry-__init__","x":"__init__( out self, binding: UInt32, visibility: UInt64, kind: BindingKind, dynamic_offset: Bool = False, min_binding_size: UInt64 = 0, format: WGPUTextureFormat = WGPUTextureFormat_Undefined, )"},{"t":"cinder.gfx.pipeline.BindingResource","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bindingresource","x":"struct BindingResource(ImplicitlyCopyable, Movable) What to bind in a slot: exactly one of buffer, texture view or sampler is non-null."},{"t":"cinder.gfx.pipeline.BindingResource.for_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingresource-for_buffer","x":"for_buffer( binding: UInt32, buffer: BufferHandle, offset: UInt64, size: UInt64 ) -> BindingResource Binds size bytes of buffer from offset; a size of 0 binds from offset to the end of the buffer (resolved in create_bind_group)."},{"t":"cinder.gfx.pipeline.BindingResource.for_texture","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingresource-for_texture","x":"for_texture( binding: UInt32, view: TextureViewHandle ) -> BindingResource Binds a texture view from Resources (owned or imported)."},{"t":"cinder.gfx.pipeline.BindingResource.for_sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#bindingresource-for_sampler","x":"for_sampler(binding: UInt32, sampler: SamplerHandle) -> BindingResource Binds a sampler from Resources."},{"t":"cinder.gfx.pipeline.VertexAttribute","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-vertexattribute","x":"struct VertexAttribute(ImplicitlyCopyable, Movable) One vertex attribute: its format, byte offset within the vertex and shader location."},{"t":"cinder.gfx.pipeline.VertexLayout","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-vertexlayout","x":"struct VertexLayout(Copyable, Movable) One vertex buffer slot: stride, step mode and attributes."},{"t":"cinder.gfx.pipeline.VertexLayout.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#vertexlayout-__init__","x":"__init__( out self, stride: UInt64, var attributes: List[VertexAttribute], step_mode: WGPUVertexStepMode = WGPUVertexStepMode_Vertex, )"},{"t":"cinder.gfx.pipeline.DepthState","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-depthstate","x":"struct DepthState(ImplicitlyCopyable, Movable) Depth testing. Reverse-Z means GreaterEqual is the engine's default compare."},{"t":"cinder.gfx.pipeline.DepthState.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#depthstate-__init__","x":"__init__( out self, format: WGPUTextureFormat, write: Bool, compare: WGPUCompareFunction, bias: Int32 = 0, bias_slope: Float32 = 0.0, bias_clamp: Float32 = 0.0, )"},{"t":"cinder.gfx.pipeline.DepthState.none","s":"API symbol","h":"method","u":"/docs/api/gfx/#depthstate-none","x":"none() -> DepthState No depth-stencil state: build_render_pipeline omits it because the format is Undefined."},{"t":"cinder.gfx.pipeline.DepthState.reverse_z","s":"API symbol","h":"method","u":"/docs/api/gfx/#depthstate-reverse_z","x":"reverse_z(format: WGPUTextureFormat, write: Bool = True) -> DepthState Reverse-Z depth test (GreaterEqual) on format, writing depth unless write is False, with no bias."},{"t":"cinder.gfx.pipeline.DepthState.reverse_z_biased","s":"API symbol","h":"method","u":"/docs/api/gfx/#depthstate-reverse_z_biased","x":"reverse_z_biased( format: WGPUTextureFormat, bias: Int32, bias_slope: Float32 ) -> DepthState Shadow-map depth: reverse-Z, written, with constant and slope bias (negative values push depth away from the light in reverse-Z)."},{"t":"cinder.gfx.pipeline.BlendMode","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-blendmode","x":"struct BlendMode(Equatable, ImplicitlyCopyable, RegisterPassable) Colour blending preset of a ColorTarget, expanded to a WGPUBlendState when a pipeline is built. Any value other than alpha or additive builds as opaque."},{"t":"cinder.gfx.pipeline.BlendMode.opaque","s":"API symbol","h":"method","u":"/docs/api/gfx/#blendmode-opaque","x":"opaque() -> BlendMode No blending: the source replaces the destination (One, Zero)."},{"t":"cinder.gfx.pipeline.BlendMode.alpha","s":"API symbol","h":"method","u":"/docs/api/gfx/#blendmode-alpha","x":"alpha() -> BlendMode Premultiplied-free source-over: src * a + dst * (1 - a)."},{"t":"cinder.gfx.pipeline.BlendMode.additive","s":"API symbol","h":"method","u":"/docs/api/gfx/#blendmode-additive","x":"additive() -> BlendMode Adds source to destination for colour and alpha (One, One)."},{"t":"cinder.gfx.pipeline.ColorTarget","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-colortarget","x":"struct ColorTarget(ImplicitlyCopyable, Movable) One colour output of a render pipeline: its texture format and blend mode, written with all channels enabled."},{"t":"cinder.gfx.pipeline.RenderPipelineDesc","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-renderpipelinedesc","x":"struct RenderPipelineDesc(Copyable, Movable) Everything needed for a render pipeline. Entry points name functions in shader."},{"t":"cinder.gfx.pipeline.RenderPipelineDesc.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#renderpipelinedesc-__init__","x":"__init__( out self, label: String, layout: PipelineLayoutHandle, shader: ShaderHandle, vertex_entry: String, fragment_entry: String, var vertex_layouts: List[VertexLayout], var color_targets: List[ColorTarget], depth: DepthState = DepthState.none(), topology: WGPUPrimitiveTopology = WGPUPrimitiveTop"},{"t":"cinder.gfx.pipeline.create_bind_group_layout","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-create_bind_group_layout","x":"def create_bind_group_layout( mut res: Resources, label: String, entries: List[LayoutEntry] ) raises -> BindGroupLayoutHandle Creates a WGPUBindGroupLayout from entries and adopts it into res. Each entry becomes a buffer, sampler, texture or storage-texture layout according to its BindingKind; a kin"},{"t":"cinder.gfx.pipeline.create_pipeline_layout","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-create_pipeline_layout","x":"def create_pipeline_layout( mut res: Resources, label: String, layouts: List[BindGroupLayoutHandle] ) raises -> PipelineLayoutHandle Creates a WGPUPipelineLayout whose bind group layouts are in group order and adopts it. Raises on an invalid layout handle or a NULL result. The caller destroys the ha"},{"t":"cinder.gfx.pipeline.create_bind_group","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-create_bind_group","x":"def create_bind_group( mut res: Resources, label: String, layout: BindGroupLayoutHandle, bindings: List[BindingResource], ) raises -> BindGroupHandle Creates a WGPUBindGroup for layout from bindings and adopts it into res. The raw wgpu objects behind the handles are captured at creation time; replac"},{"t":"cinder.gfx.pipeline.build_render_pipeline","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-build_render_pipeline","x":"def build_render_pipeline( res: Resources, desc: RenderPipelineDesc ) raises -> WGPURenderPipeline Creates the raw pipeline object (used by create_render_pipeline and by hot reload). Raises on an invalid handle, a NULL result, or a validation error drained right after creation; the invalid object is"},{"t":"cinder.gfx.pipeline.create_render_pipeline","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-create_render_pipeline","x":"def create_render_pipeline( mut res: Resources, desc: RenderPipelineDesc ) raises -> RenderPipelineHandle Builds a render pipeline with build_render_pipeline and adopts it into res. Raises if creation fails, including validation errors drained right after creation. The caller destroys the handle wit"},{"t":"cinder.gfx.pipeline.build_compute_pipeline","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-build_compute_pipeline","x":"def build_compute_pipeline( res: Resources, label: String, layout: PipelineLayoutHandle, shader: ShaderHandle, entry: String, ) raises -> WGPUComputePipeline Creates the raw compute pipeline object, adopted by create_compute_pipeline or passed to Resources.replace_compute_pipeline to swap it behind "},{"t":"cinder.gfx.pipeline.create_compute_pipeline","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-create_compute_pipeline","x":"def create_compute_pipeline( mut res: Resources, label: String, layout: PipelineLayoutHandle, shader: ShaderHandle, entry: String, ) raises -> ComputePipelineHandle Builds a compute pipeline with build_compute_pipeline and adopts it into res. The caller destroys the handle with Resources.destroy_com"},{"t":"cinder.gfx.resources","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxresources","x":"Typed GPU resources: one handle type per kind, pooled records, ownership and leak tracking. Every resource created through Resources is tracked in a generational pool for its kind, so a BufferHandle cannot be passed where a TextureHandle is expected (compile error), stale handles fail validation (ra"},{"t":"cinder.gfx.resources.BufferTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-buffertag","x":"struct BufferTag Phantom tag that makes BufferHandle a distinct type."},{"t":"cinder.gfx.resources.TextureTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-texturetag","x":"struct TextureTag Phantom tag that makes TextureHandle a distinct type."},{"t":"cinder.gfx.resources.TextureViewTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-textureviewtag","x":"struct TextureViewTag Phantom tag that makes TextureViewHandle a distinct type."},{"t":"cinder.gfx.resources.SamplerTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-samplertag","x":"struct SamplerTag Phantom tag that makes SamplerHandle a distinct type."},{"t":"cinder.gfx.resources.ShaderTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-shadertag","x":"struct ShaderTag Phantom tag that makes ShaderHandle a distinct type."},{"t":"cinder.gfx.resources.BindGroupLayoutTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bindgrouplayouttag","x":"struct BindGroupLayoutTag Phantom tag that makes BindGroupLayoutHandle a distinct type."},{"t":"cinder.gfx.resources.PipelineLayoutTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-pipelinelayouttag","x":"struct PipelineLayoutTag Phantom tag that makes PipelineLayoutHandle a distinct type."},{"t":"cinder.gfx.resources.BindGroupTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bindgrouptag","x":"struct BindGroupTag Phantom tag that makes BindGroupHandle a distinct type."},{"t":"cinder.gfx.resources.RenderPipelineTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-renderpipelinetag","x":"struct RenderPipelineTag Phantom tag that makes RenderPipelineHandle a distinct type."},{"t":"cinder.gfx.resources.ComputePipelineTag","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-computepipelinetag","x":"struct ComputePipelineTag Phantom tag that makes ComputePipelineHandle a distinct type."},{"t":"cinder.gfx.resources.BufferDesc","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bufferdesc","x":"struct BufferDesc(Copyable, Movable) What to create a buffer with. usage is a WGPUBufferUsage bit set."},{"t":"cinder.gfx.resources.TextureDesc","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-texturedesc","x":"struct TextureDesc(Copyable, Movable) A 2D texture (array layers and 3D arrive with the renderer). usage is WGPUTextureUsage bits."},{"t":"cinder.gfx.resources.SamplerDesc","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-samplerdesc","x":"struct SamplerDesc(Copyable, Movable) Filtering and addressing. linear selects trilinear filtering; otherwise nearest."},{"t":"cinder.gfx.resources.BufferRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bufferrecord","x":"struct BufferRecord(Copyable, Movable) Pool entry for a buffer: the WGPUBuffer plus the size and usage it was created with. Copies handed out by Resources.buffer share the raw object; only the pool releases it."},{"t":"cinder.gfx.resources.TextureRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-texturerecord","x":"struct TextureRecord(Copyable, Movable) Pool entry for a texture: the WGPUTexture plus the extent, mip count, format and usage it was created with. Copies share the raw object; only the pool releases it."},{"t":"cinder.gfx.resources.TextureViewRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-textureviewrecord","x":"struct TextureViewRecord(Copyable, Movable) Pool entry for a view: the WGPUTextureView and the texture it views. texture is null for views wrapped by import_texture_view, whose raw object the caller owns (is_imported)."},{"t":"cinder.gfx.resources.TextureViewRecord.is_imported","s":"API symbol","h":"method","u":"/docs/api/gfx/#textureviewrecord-is_imported","x":"is_imported(self) -> Bool True for a view wrapped by import_texture_view: Resources never releases it."},{"t":"cinder.gfx.resources.SamplerRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-samplerrecord","x":"struct SamplerRecord(Copyable, Movable) Pool entry for a sampler: the WGPUSampler, released by Resources.destroy_sampler."},{"t":"cinder.gfx.resources.ShaderRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-shaderrecord","x":"struct ShaderRecord(Copyable, Movable) Pool entry for a shader: the WGPUShaderModule and the WGSL path it was compiled from. ShaderManager swaps raw in place on hot reload, keeping the handle stable."},{"t":"cinder.gfx.resources.BindGroupLayoutRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bindgrouplayoutrecord","x":"struct BindGroupLayoutRecord(Copyable, Movable) Pool entry for a bind group layout: the WGPUBindGroupLayout the pool owns."},{"t":"cinder.gfx.resources.PipelineLayoutRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-pipelinelayoutrecord","x":"struct PipelineLayoutRecord(Copyable, Movable) Pool entry for a pipeline layout: the WGPUPipelineLayout the pool owns."},{"t":"cinder.gfx.resources.BindGroupRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-bindgrouprecord","x":"struct BindGroupRecord(Copyable, Movable) Pool entry for a bind group: the WGPUBindGroup the pool owns."},{"t":"cinder.gfx.resources.RenderPipelineRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-renderpipelinerecord","x":"struct RenderPipelineRecord(Copyable, Movable) Pool entry for a render pipeline: the WGPURenderPipeline the pool owns."},{"t":"cinder.gfx.resources.ComputePipelineRecord","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-computepipelinerecord","x":"struct ComputePipelineRecord(Copyable, Movable) Pool entry for a compute pipeline: the WGPUComputePipeline the pool owns."},{"t":"cinder.gfx.resources.LiveCounts","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-livecounts","x":"struct LiveCounts(ImplicitlyCopyable, Movable, Writable) Resources alive per kind."},{"t":"cinder.gfx.resources.LiveCounts.total","s":"API symbol","h":"method","u":"/docs/api/gfx/#livecounts-total","x":"total(self) -> Int Sum over every kind; zero means nothing is left to release."},{"t":"cinder.gfx.resources.LiveCounts.write_to","s":"API symbol","h":"method","u":"/docs/api/gfx/#livecounts-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx.resources.Resources","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-resources","x":"struct Resources(Movable) Pools of every GPU resource kind, owning the wgpu objects behind typed handles."},{"t":"cinder.gfx.resources.Resources.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], capacity: Int = 4096 ) raises"},{"t":"cinder.gfx.resources.Resources.live_counts","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-live_counts","x":"live_counts(self) -> LiveCounts Live handles per pool right now. Imported texture views count until they are forgotten. A non-zero total() just before teardown is what __deinit__ reports as left alive."},{"t":"cinder.gfx.resources.Resources.create_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-create_buffer","x":"create_buffer(mut self, desc: BufferDesc) raises -> BufferHandle Creates a GPU buffer. Validation failures surface through Gfx.pump diagnostics; a NULL object (out of memory) raises, as does a full buffer pool (the new buffer is then released)."},{"t":"cinder.gfx.resources.Resources.buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-buffer","x":"buffer(self, h: BufferHandle) raises -> BufferRecord A copy of the buffer's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.destroy_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_buffer","x":"destroy_buffer(mut self, h: BufferHandle) raises Removes the handle, then destroys and releases the WGPUBuffer. Raises on an invalid handle, in which case nothing is released."},{"t":"cinder.gfx.resources.Resources.write_buffer","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-write_buffer","x":"write_buffer( self, h: BufferHandle, offset: UInt64, data: Span[UInt8, _] ) raises Uploads data through the queue (copied immediately; no mapping)."},{"t":"cinder.gfx.resources.Resources.write_texture","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-write_texture","x":"write_texture( self, h: TextureHandle, mip_level: UInt32, width: UInt32, height: UInt32, layer: UInt32, data: Span[UInt8, _], bytes_per_row: UInt32, rows_per_image: UInt32 = 0, ) raises Uploads one mip level of one array layer through the queue (copied immediately). rows_per_image is the number of r"},{"t":"cinder.gfx.resources.Resources.create_texture","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-create_texture","x":"create_texture(mut self, desc: TextureDesc) raises -> TextureHandle Creates a 2D WGPUTexture (depth_or_layers array layers) and returns its handle. Validation failures surface through Gfx.pump diagnostics; a NULL object raises, as does a full texture pool (the new texture is then released)."},{"t":"cinder.gfx.resources.Resources.texture","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-texture","x":"texture(self, h: TextureHandle) raises -> TextureRecord A copy of the texture's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.texture_view_count","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-texture_view_count","x":"texture_view_count(self, h: TextureHandle) raises -> Int How many live views created through Resources view this texture; raises on an invalid handle."},{"t":"cinder.gfx.resources.Resources.destroy_texture","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_texture","x":"destroy_texture(mut self, h: TextureHandle) raises Removes the handle, then destroys and releases the WGPUTexture. Raises, releasing nothing, on an invalid handle or while views created from the texture are still alive: destroy those views first (destroy_texture_view), so no live view handle is left"},{"t":"cinder.gfx.resources.Resources.create_texture_view_ex","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-create_texture_view_ex","x":"create_texture_view_ex( mut self, texture: TextureHandle, dimension: WGPUTextureViewDimension, base_mip: UInt32, mip_count: UInt32, base_layer: UInt32, layer_count: UInt32, ) raises -> TextureViewHandle A view of a mip range and layer range with an explicit dimension (2D array, cube, one layer of an"},{"t":"cinder.gfx.resources.Resources.create_texture_view","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-create_texture_view","x":"create_texture_view( mut self, texture: TextureHandle ) raises -> TextureViewHandle A view of the whole texture with its own format and dimension."},{"t":"cinder.gfx.resources.Resources.import_texture_view","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-import_texture_view","x":"import_texture_view( mut self, raw: WGPUTextureView ) raises -> TextureViewHandle Wraps a view the caller owns (a swapchain frame) so renderers can target it. Drop it with forget_texture_view; the raw view is never released here, not even at teardown, and destroy_texture_view refuses it."},{"t":"cinder.gfx.resources.Resources.forget_texture_view","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-forget_texture_view","x":"forget_texture_view(mut self, h: TextureViewHandle) raises Drops an imported view without releasing the wgpu object. Raises, dropping nothing, on an invalid handle or on a view created by Resources (that one must be released with destroy_texture_view, or it would leak)."},{"t":"cinder.gfx.resources.Resources.texture_view","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-texture_view","x":"texture_view(self, h: TextureViewHandle) raises -> TextureViewRecord A copy of the view's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.destroy_texture_view","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_texture_view","x":"destroy_texture_view(mut self, h: TextureViewHandle) raises Removes the handle and releases the WGPUTextureView. Raises, releasing nothing, on an invalid handle or on an imported view (the caller owns that one; drop it with forget_texture_view)."},{"t":"cinder.gfx.resources.Resources.create_sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-create_sampler","x":"create_sampler(mut self, desc: SamplerDesc) raises -> SamplerHandle Creates a sampler with the same address mode on u, v and w and LOD clamp 0..32. linear picks linear min/mag/mip filtering, otherwise nearest. A NULL object raises; invalid settings surface as validation diagnostics through Gfx.pump."},{"t":"cinder.gfx.resources.Resources.create_comparison_sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-create_comparison_sampler","x":"create_comparison_sampler( mut self, label: String ) raises -> SamplerHandle Linear PCF sampler: clamp to edge, compare GreaterEqual (reverse-Z shadow maps)."},{"t":"cinder.gfx.resources.Resources.sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-sampler","x":"sampler(self, h: SamplerHandle) raises -> SamplerRecord A copy of the sampler's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.destroy_sampler","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_sampler","x":"destroy_sampler(mut self, h: SamplerHandle) raises Removes the handle and releases the WGPUSampler; raises on an invalid handle."},{"t":"cinder.gfx.resources.Resources.adopt_shader","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-adopt_shader","x":"adopt_shader( mut self, raw: WGPUShaderModule, source_path: String ) raises -> ShaderHandle Takes ownership of a compiled module (from ShaderManager) and returns its handle. Raises on a null module. Ownership passes even when this raises: if the pool is full the module is released before the error p"},{"t":"cinder.gfx.resources.Resources.shader","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-shader","x":"shader(self, h: ShaderHandle) raises -> ShaderRecord A copy of the shader's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.destroy_shader","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_shader","x":"destroy_shader(mut self, h: ShaderHandle) raises Removes the handle and releases the WGPUShaderModule. A ShaderManager that loaded it keeps watching the file; a later change then fails to reload and logs an error."},{"t":"cinder.gfx.resources.Resources.adopt_bind_group_layout","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-adopt_bind_group_layout","x":"adopt_bind_group_layout( mut self, raw: WGPUBindGroupLayout ) raises -> BindGroupLayoutHandle Takes ownership of a WGPUBindGroupLayout built in pipeline.mojo. Raises on a null raw; a full pool releases raw and raises."},{"t":"cinder.gfx.resources.Resources.bind_group_layout","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-bind_group_layout","x":"bind_group_layout( self, h: BindGroupLayoutHandle ) raises -> BindGroupLayoutRecord A copy of the layout's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.destroy_bind_group_layout","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_bind_group_layout","x":"destroy_bind_group_layout(mut self, h: BindGroupLayoutHandle) raises Removes the handle and releases the WGPUBindGroupLayout; raises on an invalid handle."},{"t":"cinder.gfx.resources.Resources.adopt_pipeline_layout","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-adopt_pipeline_layout","x":"adopt_pipeline_layout( mut self, raw: WGPUPipelineLayout ) raises -> PipelineLayoutHandle Takes ownership of a WGPUPipelineLayout built in pipeline.mojo. Raises on a null raw; a full pool releases raw and raises."},{"t":"cinder.gfx.resources.Resources.pipeline_layout","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-pipeline_layout","x":"pipeline_layout( self, h: PipelineLayoutHandle ) raises -> PipelineLayoutRecord A copy of the layout's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.destroy_pipeline_layout","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_pipeline_layout","x":"destroy_pipeline_layout(mut self, h: PipelineLayoutHandle) raises Removes the handle and releases the WGPUPipelineLayout; raises on an invalid handle."},{"t":"cinder.gfx.resources.Resources.adopt_bind_group","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-adopt_bind_group","x":"adopt_bind_group( mut self, raw: WGPUBindGroup ) raises -> BindGroupHandle Takes ownership of a WGPUBindGroup built in pipeline.mojo. Raises on a null raw; a full pool releases raw and raises."},{"t":"cinder.gfx.resources.Resources.bind_group","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-bind_group","x":"bind_group(self, h: BindGroupHandle) raises -> BindGroupRecord A copy of the bind group's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.destroy_bind_group","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_bind_group","x":"destroy_bind_group(mut self, h: BindGroupHandle) raises Removes the handle and releases the WGPUBindGroup; raises on an invalid handle."},{"t":"cinder.gfx.resources.Resources.adopt_render_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-adopt_render_pipeline","x":"adopt_render_pipeline( mut self, raw: WGPURenderPipeline ) raises -> RenderPipelineHandle Takes ownership of a WGPURenderPipeline built in pipeline.mojo. Raises on a null raw; a full pool releases raw and raises."},{"t":"cinder.gfx.resources.Resources.render_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-render_pipeline","x":"render_pipeline( self, h: RenderPipelineHandle ) raises -> RenderPipelineRecord A copy of the pipeline's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.replace_render_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-replace_render_pipeline","x":"replace_render_pipeline( mut self, h: RenderPipelineHandle, raw: WGPURenderPipeline ) raises Swaps the object behind an existing handle (hot reload), releasing the old one. Takes ownership of raw in every case: on a null raw or an invalid handle it raises, and a non-null raw is released first, so a "},{"t":"cinder.gfx.resources.Resources.destroy_render_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_render_pipeline","x":"destroy_render_pipeline(mut self, h: RenderPipelineHandle) raises Removes the handle and releases the WGPURenderPipeline; raises on an invalid handle."},{"t":"cinder.gfx.resources.Resources.adopt_compute_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-adopt_compute_pipeline","x":"adopt_compute_pipeline( mut self, raw: WGPUComputePipeline ) raises -> ComputePipelineHandle Takes ownership of a WGPUComputePipeline built in pipeline.mojo. Raises on a null raw; a full pool releases raw and raises."},{"t":"cinder.gfx.resources.Resources.compute_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-compute_pipeline","x":"compute_pipeline( self, h: ComputePipelineHandle ) raises -> ComputePipelineRecord A copy of the pipeline's record; raises on a null, stale or foreign handle."},{"t":"cinder.gfx.resources.Resources.replace_compute_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-replace_compute_pipeline","x":"replace_compute_pipeline( mut self, h: ComputePipelineHandle, raw: WGPUComputePipeline ) raises Swaps the object behind an existing handle (hot reload), releasing the old one. Takes ownership of raw in every case: on a null raw or an invalid handle it raises, and a non-null raw is released first, so"},{"t":"cinder.gfx.resources.Resources.destroy_compute_pipeline","s":"API symbol","h":"method","u":"/docs/api/gfx/#resources-destroy_compute_pipeline","x":"destroy_compute_pipeline(mut self, h: ComputePipelineHandle) raises Removes the handle and releases the WGPUComputePipeline; raises on an invalid handle."},{"t":"cinder.gfx.shaders","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxshaders","x":"ShaderManager: WGSL modules from files, compile diagnostics with file:line:column, hot reload. Every loaded shader remembers its path and modification time. check_for_changes recompiles files that changed on disk; a successful recompile swaps the module behind the same ShaderHandle and reports the h"},{"t":"cinder.gfx.shaders.ShaderMessage","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-shadermessage","x":"struct ShaderMessage(Copyable, Movable, Writable) One compiler diagnostic, already formatted with its source location."},{"t":"cinder.gfx.shaders.ShaderMessage.write_to","s":"API symbol","h":"method","u":"/docs/api/gfx/#shadermessage-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx.shaders.ShaderError","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-shadererror","x":"struct ShaderError(Copyable, Movable, Writable) Raised when a WGSL file fails to compile. messages holds every diagnostic."},{"t":"cinder.gfx.shaders.ShaderError.write_to","s":"API symbol","h":"method","u":"/docs/api/gfx/#shadererror-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.gfx.shaders.ShaderSource","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-shadersource","x":"struct ShaderSource(Copyable, Movable) A loaded shader file and its on-disk state."},{"t":"cinder.gfx.shaders.ReloadResult","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-reloadresult","x":"struct ReloadResult(Copyable, Movable) Outcome of check_for_changes for one changed file."},{"t":"cinder.gfx.shaders.parse_wgsl_report","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-parse_wgsl_report","x":"def parse_wgsl_report(path: String, report: String) -> List[ShaderMessage] Turns a naga validation report into messages with locations. naga marks each location as wgsl:LINE:COLUMN; every such mark becomes one error message carrying the whole report text. A report with no mark yields one message at "},{"t":"cinder.gfx.shaders.ShaderManager","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-shadermanager","x":"struct ShaderManager(Movable) Owns the list of shader files; the modules themselves live in Resources."},{"t":"cinder.gfx.shaders.ShaderManager.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#shadermanager-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], resources: Pointer[Resources, MutUntrackedOrigin], )"},{"t":"cinder.gfx.shaders.ShaderManager.compile_source","s":"API symbol","h":"method","u":"/docs/api/gfx/#shadermanager-compile_source","x":"compile_source( mut self, path: String, source: String ) raises -> WGPUShaderModule Compiles WGSL text and returns the module, or raises ShaderError with every diagnostic. wgpu-native 29 does not implement the compilation-info callback, so the module is created inside a validation error scope; naga'"},{"t":"cinder.gfx.shaders.ShaderManager.load","s":"API symbol","h":"method","u":"/docs/api/gfx/#shadermanager-load","x":"load(mut self, path: String) raises -> ShaderHandle Loads and compiles a WGSL file, registering it for hot reload."},{"t":"cinder.gfx.shaders.ShaderManager.check_for_changes","s":"API symbol","h":"method","u":"/docs/api/gfx/#shadermanager-check_for_changes","x":"check_for_changes(mut self) -> List[ReloadResult] Recompiles every registered file whose modification time changed. On success the module behind the handle is replaced (old one released); on failure the last good module stays and an error diagnostic with file:line:column is recorded."},{"t":"cinder.gfx.shaders.ShaderManager.take_diagnostics","s":"API symbol","h":"method","u":"/docs/api/gfx/#shadermanager-take_diagnostics","x":"take_diagnostics(mut self) -> List[LogRecord] Returns and clears the reload LogRecords from check_for_changes (info on a successful reload, error with the reason when the last good module was kept)."},{"t":"cinder.gfx.swapchain","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxswapchain","x":"Swapchain: surface configuration, per-frame texture acquisition and presentation. Handles the three real-world cases a frame loop meets: resize (reconfigure on the next acquire), minimise (zero-sized window; frames are skipped until it is restored) and surface loss/outdated (reconfigure and retry on"},{"t":"cinder.gfx.swapchain.SurfaceFrame","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-surfaceframe","x":"struct SurfaceFrame(Movable) The acquired swapchain texture for one frame. present consumes it."},{"t":"cinder.gfx.swapchain.Swapchain","s":"API symbol","h":"struct","u":"/docs/api/gfx/#struct-swapchain","x":"struct Swapchain(Movable) Owns the configuration of a wgpu surface created by Window.create_surface."},{"t":"cinder.gfx.swapchain.Swapchain.__init__","s":"API symbol","h":"method","u":"/docs/api/gfx/#swapchain-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], surface: WGPUSurface, width: Int, height: Int, prefer_srgb: Bool = True, readback: Bool = False, ) raises GfxError Picks a surface format (8-bit BGRA/RGBA, sRGB when prefer_srgb) and configures."},{"t":"cinder.gfx.swapchain.Swapchain.resize","s":"API symbol","h":"method","u":"/docs/api/gfx/#swapchain-resize","x":"resize(mut self, width: Int, height: Int) Records a new size; the surface is reconfigured on the next acquire."},{"t":"cinder.gfx.swapchain.Swapchain.acquire","s":"API symbol","h":"method","u":"/docs/api/gfx/#swapchain-acquire","x":"acquire(mut self) raises GfxError -> Optional[SurfaceFrame] Returns the frame's texture, or None when the window is minimised. Outdated and lost surfaces are reconfigured and retried once; a second failure raises."},{"t":"cinder.gfx.swapchain.Swapchain.present","s":"API symbol","h":"method","u":"/docs/api/gfx/#swapchain-present","x":"present(self, var frame: SurfaceFrame) Presents the acquired frame and releases its view and texture."},{"t":"cinder.gfx.swapchain.Swapchain.discard","s":"API symbol","h":"method","u":"/docs/api/gfx/#swapchain-discard","x":"discard(self, var frame: SurfaceFrame) Releases an acquired frame without presenting (used when a frame is abandoned)."},{"t":"cinder.gfx.util","s":"API symbol","h":"module","u":"/docs/api/gfx/#cindergfxutil","x":"Small conversions shared by the gfx modules."},{"t":"cinder.gfx.util.string_view","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-string_view","x":"def string_view(ref text: String) -> WGPUStringView A non-owning WGPUStringView over text. text must outlive every use of the view."},{"t":"cinder.gfx.util.empty_string_view","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-empty_string_view","x":"def empty_string_view() -> WGPUStringView A WGPUStringView with a null pointer and zero length, for absent labels and strings."},{"t":"cinder.gfx.util.c_message","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-c_message","x":"def c_message(ev: CnEvent) -> String The NUL-terminated message of a shim event as a String, decoded as UTF-8 (invalid sequences become U+FFFD). Reads at most the 1000-byte message buffer."},{"t":"cinder.gfx.util.zero_event","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-zero_event","x":"def zero_event() -> CnEvent An all-zero CnEvent, used as the out-parameter that cn_event_queue_pop fills."},{"t":"cinder.gfx.util.ptr_to","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-ptr_to","x":"def ptr_to[ T: AnyType ](ref value: T) -> Optional[Pointer[T, MutUntrackedOrigin]] Raw pointer for a C call. The pointee must stay alive past the call (keep it)."},{"t":"cinder.gfx.util.opaque_to","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-opaque_to","x":"def opaque_to[ T: AnyType ](ref value: T) -> Optional[OpaquePointer[MutUntrackedOrigin]] ptr_to for void* parameters."},{"t":"cinder.gfx.util.handle_from","s":"API symbol","h":"def","u":"/docs/api/gfx/#def-handle_from","x":"def handle_from[ T: AnyType ](address: UInt64) -> Optional[Pointer[T, MutUntrackedOrigin]] Rebuilds a wgpu object handle delivered through a CnEvent."},{"t":"cinder.input","s":"API symbol","h":"module","u":"/docs/api/input/#cinderinput-1","x":"Input: action and axis mapping over keyboard, mouse and gamepad, rebindable and saved to disk."},{"t":"cinder.input.map","s":"API symbol","h":"module","u":"/docs/api/input/#cinderinputmap","x":"Input mapping: named actions (buttons) and axes over keyboard, mouse and gamepad, rebindable at runtime and saved as a diffable text file. Feed every platform Event to process, call end_frame once per frame (it clears the just-pressed and just-released edges and the per-frame mouse deltas), and quer"},{"t":"cinder.input.map.Binding","s":"API symbol","h":"struct","u":"/docs/api/input/#struct-binding","x":"struct Binding(Equatable, ImplicitlyCopyable, Movable, Writable) One source: device and its code (scancode, button or axis index). scale multiplies the contribution to an axis (digital sources contribute scale while held), dead_zone applies to analog sources."},{"t":"cinder.input.map.Binding.key","s":"API symbol","h":"method","u":"/docs/api/input/#binding-key","x":"key(scancode: Int, scale: Float32 = 1.0) -> Binding A keyboard binding for SDL scancode scancode."},{"t":"cinder.input.map.Binding.mouse_button","s":"API symbol","h":"method","u":"/docs/api/input/#binding-mouse_button","x":"mouse_button(button: Int, scale: Float32 = 1.0) -> Binding A mouse button binding (1 = left, 2 = middle, 3 = right)."},{"t":"cinder.input.map.Binding.mouse_axis","s":"API symbol","h":"method","u":"/docs/api/input/#binding-mouse_axis","x":"mouse_axis(axis: Int, scale: Float32 = 1.0) -> Binding A mouse axis binding: 0 = x delta, 1 = y delta, any other code = wheel."},{"t":"cinder.input.map.Binding.gamepad_button","s":"API symbol","h":"method","u":"/docs/api/input/#binding-gamepad_button","x":"gamepad_button(button: Int, scale: Float32 = 1.0) -> Binding A gamepad button binding (SDL_GamepadButton index)."},{"t":"cinder.input.map.Binding.gamepad_axis","s":"API symbol","h":"method","u":"/docs/api/input/#binding-gamepad_axis","x":"gamepad_axis( axis: Int, scale: Float32 = 1.0, dead_zone: Float32 = 0.15 ) -> Binding An analog gamepad axis binding (SDL_GamepadAxis index) with a dead zone."},{"t":"cinder.input.map.Binding.same_source","s":"API symbol","h":"method","u":"/docs/api/input/#binding-same_source","x":"same_source(self, o: Binding) -> Bool True when both bindings read the same device and code, whatever their scale and dead zone."},{"t":"cinder.input.map.Binding.write_to","s":"API symbol","h":"method","u":"/docs/api/input/#binding-write_to","x":"write_to(self, mut w: Some[Writer])"},{"t":"cinder.input.map.InputMap","s":"API symbol","h":"struct","u":"/docs/api/input/#struct-inputmap","x":"struct InputMap(Movable) Named actions and axes with their bindings, plus the device state they are evaluated against. Text format (to_text, from_text): a cinder-input 1 header, then one action NAME DEVICE CODE SCALE DEAD_ZONE or axis NAME ... line per binding, where DEVICE is key, mouse_button, mou"},{"t":"cinder.input.map.InputMap.__init__","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-__init__","x":"__init__(out self)"},{"t":"cinder.input.map.InputMap.add_action","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-add_action","x":"add_action(mut self, name: String) raises -> Int Adds an action without bindings and returns its index; raises when the name is taken."},{"t":"cinder.input.map.InputMap.add_axis","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-add_axis","x":"add_axis(mut self, name: String) raises -> Int Adds an axis without bindings and returns its index; raises when the name is taken."},{"t":"cinder.input.map.InputMap.find_action","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-find_action","x":"find_action(self, name: String) -> Int Index of the action called name, or -1."},{"t":"cinder.input.map.InputMap.find_axis","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-find_axis","x":"find_axis(self, name: String) -> Int Index of the axis called name, or -1."},{"t":"cinder.input.map.InputMap.bind_action","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-bind_action","x":"bind_action(mut self, name: String, binding: Binding) raises Appends binding to the action, creating the action when it does not exist. When the new source is already down the action becomes held (with a just-pressed edge)."},{"t":"cinder.input.map.InputMap.bind_axis","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-bind_axis","x":"bind_axis(mut self, name: String, binding: Binding) raises Appends binding to the axis, creating the axis when it does not exist."},{"t":"cinder.input.map.InputMap.clear_action_bindings","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-clear_action_bindings","x":"clear_action_bindings(mut self, name: String) raises Removes every binding of the action; raises for an unknown action. An action held at that moment is released (pressed turns False and just_released fires), so it cannot stay stuck with no binding to release it."},{"t":"cinder.input.map.InputMap.clear_axis_bindings","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-clear_axis_bindings","x":"clear_axis_bindings(mut self, name: String) raises Removes every binding of the axis; raises for an unknown axis."},{"t":"cinder.input.map.InputMap.rebind_action","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-rebind_action","x":"rebind_action(mut self, name: String, binding: Binding) raises Replaces every binding of the action with binding."},{"t":"cinder.input.map.InputMap.action_bindings","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-action_bindings","x":"action_bindings(self, name: String) raises -> List[Binding] A copy of the action's bindings; raises for an unknown action."},{"t":"cinder.input.map.InputMap.axis_bindings","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-axis_bindings","x":"axis_bindings(self, name: String) raises -> List[Binding] A copy of the axis' bindings; raises for an unknown axis."},{"t":"cinder.input.map.InputMap.listen_action","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-listen_action","x":"listen_action(mut self, name: String) raises The next key, mouse button or gamepad button event replaces the action's bindings. An action held through its old bindings is released at that point (see clear_action_bindings)."},{"t":"cinder.input.map.InputMap.listen_axis","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-listen_axis","x":"listen_axis(mut self, name: String, scale: Float32 = 1.0) raises The next key, button or analog event is added to the axis with scale."},{"t":"cinder.input.map.InputMap.is_listening","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-is_listening","x":"is_listening(self) -> Bool True while listen_action or listen_axis waits for an event."},{"t":"cinder.input.map.InputMap.cancel_listening","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-cancel_listening","x":"cancel_listening(mut self) Disarms a pending listen_action or listen_axis without changing any binding."},{"t":"cinder.input.map.InputMap.process","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-process","x":"process(mut self, ev: Event) Updates device state from one platform event (and captures it while listening)."},{"t":"cinder.input.map.InputMap.end_frame","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-end_frame","x":"end_frame(mut self) Clears edges and per-frame deltas; call once per frame after reading input."},{"t":"cinder.input.map.InputMap.pressed","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-pressed","x":"pressed(self, name: String) raises -> Bool True while any key or button bound to the action is held; mouse and gamepad axis bindings never press an action. Raises for an unknown action."},{"t":"cinder.input.map.InputMap.just_pressed","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-just_pressed","x":"just_pressed(self, name: String) raises -> Bool True when the action went from released to held since the last end_frame; raises for an unknown action."},{"t":"cinder.input.map.InputMap.just_released","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-just_released","x":"just_released(self, name: String) raises -> Bool True when the action went from held to released since the last end_frame; raises for an unknown action."},{"t":"cinder.input.map.InputMap.axis","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-axis","x":"axis(self, name: String) raises -> Float32 Sum of the axis' bindings, clamped to [-1, 1] for digital-only axes; analog and mouse contributions are not clamped (mouse deltas are in pixels)."},{"t":"cinder.input.map.InputMap.to_text","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-to_text","x":"to_text(self) -> String Diffable text: one action/axis line per binding, sorted as defined."},{"t":"cinder.input.map.InputMap.from_text","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-from_text","x":"from_text(text: String) raises -> InputMap Parses the text format written by to_text; blank lines are skipped. Raises on a missing header, another version, a line that is not 2 or 6 fields, an unknown entry or device, or a malformed number."},{"t":"cinder.input.map.InputMap.save","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-save","x":"save(self, path: String) raises Writes to_text() to path; raises when the file cannot be written."},{"t":"cinder.input.map.InputMap.load","s":"API symbol","h":"method","u":"/docs/api/input/#inputmap-load","x":"load(path: String) raises -> InputMap Reads path and parses it with from_text; raises when the file cannot be read or does not parse."},{"t":"cinder.physics","s":"API symbol","h":"module","u":"/docs/api/physics/#cinderphysics-1","x":"Physics over Jolt: rigid-body world, queries, character controller and ECS synchronisation."},{"t":"cinder.physics.character","s":"API symbol","h":"module","u":"/docs/api/physics/#cinderphysicscharacter","x":"Character controller over Jolt's CharacterVirtual: slopes, steps and moving platforms. The character is a capsule whose position is at its feet. update applies the engine's standard velocity rules (stick to the ground and inherit its velocity, otherwise fall under gravity), then runs Jolt's extended"},{"t":"cinder.physics.character.GroundState","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-groundstate","x":"struct GroundState(Equatable, ImplicitlyCopyable, Movable, Writable) Ground contact state of a CharacterController, mirrored from Jolt's ground state."},{"t":"cinder.physics.character.GroundState.on_ground","s":"API symbol","h":"method","u":"/docs/api/physics/#groundstate-on_ground","x":"on_ground() -> GroundState Standing on a surface no steeper than max_slope_degrees."},{"t":"cinder.physics.character.GroundState.on_steep_ground","s":"API symbol","h":"method","u":"/docs/api/physics/#groundstate-on_steep_ground","x":"on_steep_ground() -> GroundState Touching a surface steeper than max_slope_degrees; update treats it like the air and applies gravity."},{"t":"cinder.physics.character.GroundState.not_supported","s":"API symbol","h":"method","u":"/docs/api/physics/#groundstate-not_supported","x":"not_supported() -> GroundState Touching something that cannot hold the character (its edge, a falling body)."},{"t":"cinder.physics.character.GroundState.in_air","s":"API symbol","h":"method","u":"/docs/api/physics/#groundstate-in_air","x":"in_air() -> GroundState Touching nothing; update applies gravity."},{"t":"cinder.physics.character.GroundState.write_to","s":"API symbol","h":"method","u":"/docs/api/physics/#groundstate-write_to","x":"write_to(self, mut w: Some[Writer])"},{"t":"cinder.physics.character.CharacterDesc","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-characterdesc","x":"struct CharacterDesc(Copyable, Movable) Parameters of a CharacterController in metres, kilograms and degrees. radius and height size the capsule (height must exceed 2 * radius + 0.01); max_slope_degrees is the steepest walkable slope and step_height the tallest step walked up; mass and max_strength "},{"t":"cinder.physics.character.CharacterDesc.__init__","s":"API symbol","h":"method","u":"/docs/api/physics/#characterdesc-__init__","x":"__init__( out self, radius: Float32 = 0.3, height: Float32 = 1.8, layer: Int = 1, max_slope_degrees: Float32 = 50.0, step_height: Float32 = 0.4, mass: Float32 = 70.0, )"},{"t":"cinder.physics.character.CharacterController","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-charactercontroller","x":"struct CharacterController(Movable) A capsule character over Jolt's CharacterVirtual, positioned at its feet. It is not a rigid body: it has no body id. It creates and owns its capsule shape and counts itself in world.live_characters; its destructor destroys the character and the shape and decrement"},{"t":"cinder.physics.character.CharacterController.__init__","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-__init__","x":"__init__( out self, world: Pointer[PhysicsWorld, MutUntrackedOrigin], desc: CharacterDesc, position: Vec3, ) raises"},{"t":"cinder.physics.character.CharacterController.position","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-position","x":"position(self) -> Vec3 World position of the character's feet, in metres."},{"t":"cinder.physics.character.CharacterController.set_position","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-set_position","x":"set_position(self, position: Vec3) Teleports the character so its feet are at position (metres)."},{"t":"cinder.physics.character.CharacterController.rotation","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-rotation","x":"rotation(self) -> Quat World rotation of the character."},{"t":"cinder.physics.character.CharacterController.set_rotation","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-set_rotation","x":"set_rotation(self, rotation: Quat) Sets the world rotation of the character."},{"t":"cinder.physics.character.CharacterController.velocity","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-velocity","x":"velocity(self) -> Vec3 Linear velocity of the character in metres per second."},{"t":"cinder.physics.character.CharacterController.ground_state","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-ground_state","x":"ground_state(self) -> GroundState Ground contact state from Jolt's last character update (see GroundState)."},{"t":"cinder.physics.character.CharacterController.is_supported","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-is_supported","x":"is_supported(self) -> Bool Whether Jolt considers the character supported by the ground it touches."},{"t":"cinder.physics.character.CharacterController.ground_normal","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-ground_normal","x":"ground_normal(self) -> Vec3 World-space surface normal of the ground contact."},{"t":"cinder.physics.character.CharacterController.ground_velocity","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-ground_velocity","x":"ground_velocity(self) -> Vec3 Velocity of the ground under the character in metres per second (a moving platform's), refreshed at the start of update."},{"t":"cinder.physics.character.CharacterController.ground_body","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-ground_body","x":"ground_body(self) -> UInt32 Id of the body under the character's feet; INVALID_BODY when Jolt reports none."},{"t":"cinder.physics.character.CharacterController.update","s":"API symbol","h":"method","u":"/docs/api/physics/#charactercontroller-update","x":"update( mut self, dt: Float32, desired_velocity: Vec3, jump_speed: Float32 = 0.0 ) Moves the character for one step. desired_velocity is the horizontal velocity the player asks for (its Y is ignored); a positive jump_speed launches the character when it stands on the ground. Call before PhysicsWorld"},{"t":"cinder.physics.sync","s":"API symbol","h":"module","u":"/docs/api/physics/#cinderphysicssync","x":"ECS <-> physics synchronisation with interpolation. RigidBody links an entity to a Jolt body and stores the two most recent simulated poses. The fixed-step loop is: push_kinematics(world, physics, dt)   # moved LocalTransforms drive kinematic bodies physics.step(dt) pull_bodies(world, physics)      "},{"t":"cinder.physics.sync.RigidBody","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-rigidbody","x":"struct RigidBody(Component) The physics side of an entity. scale is preserved when the body writes the transform (Jolt bodies carry no scale)."},{"t":"cinder.physics.sync.RigidBody.__init__","s":"API symbol","h":"method","u":"/docs/api/physics/#rigidbody-__init__","x":"__init__( out self, body: UInt32, motion: MotionType, position: Vec3, rotation: Quat, scale: Vec3 = Vec3.one(), interpolate: Bool = True, )"},{"t":"cinder.physics.sync.RigidBody.component_id","s":"API symbol","h":"method","u":"/docs/api/physics/#rigidbody-component_id","x":"component_id() -> Int"},{"t":"cinder.physics.sync.PhysicsSync","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-physicssync","x":"struct PhysicsSync(Movable) Per-frame physics <-> ECS synchronisation with persistent query specs and scratch buffers, so steady-state frames allocate nothing. The free functions below are convenience wrappers that build a temporary instance."},{"t":"cinder.physics.sync.PhysicsSync.__init__","s":"API symbol","h":"method","u":"/docs/api/physics/#physicssync-__init__","x":"__init__(out self)"},{"t":"cinder.physics.sync.PhysicsSync.pull_bodies","s":"API symbol","h":"method","u":"/docs/api/physics/#physicssync-pull_bodies","x":"pull_bodies( mut self, mut world: World, physics: PhysicsWorld ) raises -> Int After a physics step: copies each dynamic body's pose into its RigidBody, keeping the previous pose for interpolation. Returns the number of bodies read."},{"t":"cinder.physics.sync.PhysicsSync.push_kinematics","s":"API symbol","h":"method","u":"/docs/api/physics/#physicssync-push_kinematics","x":"push_kinematics( mut self, mut world: World, physics: PhysicsWorld, dt: Float32 ) raises -> Int Before a physics step: kinematic bodies whose LocalTransform changed since the last push are moved so they reach the new pose during this step. Returns the count."},{"t":"cinder.physics.sync.PhysicsSync.write_transforms","s":"API symbol","h":"method","u":"/docs/api/physics/#physicssync-write_transforms","x":"write_transforms( mut self, mut world: World, alpha: Float32 ) raises -> Int Writes LocalTransform for dynamic bodies at alpha in [0, 1] between the previous and current poses. Bodies whose id is INVALID_BODY are skipped, as in pull_bodies. Only changed transforms are marked. Returns the count updat"},{"t":"cinder.physics.sync.pull_bodies","s":"API symbol","h":"def","u":"/docs/api/physics/#def-pull_bodies","x":"def pull_bodies(mut world: World, physics: PhysicsWorld) raises -> Int PhysicsSync.pull_bodies through a temporary PhysicsSync, so it allocates query scratch on every call. Returns the number of dynamic bodies read."},{"t":"cinder.physics.sync.push_kinematics","s":"API symbol","h":"def","u":"/docs/api/physics/#def-push_kinematics","x":"def push_kinematics( mut world: World, physics: PhysicsWorld, dt: Float32 ) raises -> Int PhysicsSync.push_kinematics through a temporary PhysicsSync, so it allocates query scratch on every call. Returns the number of kinematic bodies moved."},{"t":"cinder.physics.sync.write_transforms","s":"API symbol","h":"def","u":"/docs/api/physics/#def-write_transforms","x":"def write_transforms(mut world: World, alpha: Float32) raises -> Int PhysicsSync.write_transforms through a temporary PhysicsSync, so it allocates scratch on every call. Returns the number of transforms written."},{"t":"cinder.physics.world","s":"API symbol","h":"module","u":"/docs/api/physics/#cinderphysicsworld","x":"Rigid-body world over Jolt (joltc): layers, shapes, bodies, queries and queued events. Every Jolt object created here is counted; live_objects() must be zero before the world is destroyed. The destructor does not abort on leaks (an abort would hide the real failure of a test that raised): it prints "},{"t":"cinder.physics.world.to_jolt","s":"API symbol","h":"def","u":"/docs/api/physics/#def-to_jolt","x":"def to_jolt(v: Vec3) -> JPH_Vec3 Converts an engine Vec3 to a joltc JPH_Vec3."},{"t":"cinder.physics.world.from_jolt","s":"API symbol","h":"def","u":"/docs/api/physics/#def-from_jolt","x":"def from_jolt(v: JPH_Vec3) -> Vec3 Converts a joltc JPH_Vec3 to an engine Vec3."},{"t":"cinder.physics.world.quat_to_jolt","s":"API symbol","h":"def","u":"/docs/api/physics/#def-quat_to_jolt","x":"def quat_to_jolt(q: Quat) -> JPH_Quat Converts an engine Quat to a joltc JPH_Quat (x, y, z, w)."},{"t":"cinder.physics.world.quat_from_jolt","s":"API symbol","h":"def","u":"/docs/api/physics/#def-quat_from_jolt","x":"def quat_from_jolt(q: JPH_Quat) -> Quat Converts a joltc JPH_Quat to an engine Quat."},{"t":"cinder.physics.world.mat_to_jolt","s":"API symbol","h":"def","u":"/docs/api/physics/#def-mat_to_jolt","x":"def mat_to_jolt(m: Mat4) -> JPH_Mat4 Column-major on both sides."},{"t":"cinder.physics.world.LayerConfig","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-layerconfig","x":"struct LayerConfig(Copyable, Movable) Object layers and which pairs collide. Layer i is bit i in query masks. Layers flagged static go to a separate broad-phase layer so static geometry is never tested against itself."},{"t":"cinder.physics.world.LayerConfig.__init__","s":"API symbol","h":"method","u":"/docs/api/physics/#layerconfig-__init__","x":"__init__(out self, count: Int, static_mask: UInt32 = 1)"},{"t":"cinder.physics.world.LayerConfig.collide","s":"API symbol","h":"method","u":"/docs/api/physics/#layerconfig-collide","x":"collide(mut self, a: Int, b: Int) Enables collision between layers a and b (symmetric)."},{"t":"cinder.physics.world.LayerConfig.default","s":"API symbol","h":"method","u":"/docs/api/physics/#layerconfig-default","x":"default() -> LayerConfig Two layers: 0 static, 1 moving; moving collides with both."},{"t":"cinder.physics.world.MotionType","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-motiontype","x":"struct MotionType(Equatable, ImplicitlyCopyable, Movable) How a body moves: static, kinematic (driven by the caller) or dynamic (simulated)."},{"t":"cinder.physics.world.MotionType.static","s":"API symbol","h":"method","u":"/docs/api/physics/#motiontype-static","x":"static() -> MotionType A body that never moves."},{"t":"cinder.physics.world.MotionType.kinematic","s":"API symbol","h":"method","u":"/docs/api/physics/#motiontype-kinematic","x":"kinematic() -> MotionType A body moved by the caller (move_kinematic, set_position_rotation), not by forces or contacts."},{"t":"cinder.physics.world.MotionType.dynamic","s":"API symbol","h":"method","u":"/docs/api/physics/#motiontype-dynamic","x":"dynamic() -> MotionType A body simulated by Jolt: gravity, forces, impulses and contacts move it."},{"t":"cinder.physics.world.MotionType.to_jolt","s":"API symbol","h":"method","u":"/docs/api/physics/#motiontype-to_jolt","x":"to_jolt(self) -> JPH_MotionType The matching joltc JPH_MotionType; any value other than static or kinematic maps to dynamic."},{"t":"cinder.physics.world.Shape","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-shape","x":"struct Shape(ImplicitlyCopyable, Movable) A reference-counted Jolt shape that the world tracks; copy freely, destroy once."},{"t":"cinder.physics.world.BodyDesc","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-bodydesc","x":"struct BodyDesc(Copyable, Movable) Creation parameters for PhysicsWorld.create_body. The constructor takes the shape, position (metres), motion type, object layer and rotation; the other fields keep their defaults until set: friction 0.5, restitution 0, linear and angular damping 0.05, gravity facto"},{"t":"cinder.physics.world.BodyDesc.__init__","s":"API symbol","h":"method","u":"/docs/api/physics/#bodydesc-__init__","x":"__init__( out self, shape: Shape, position: Vec3, motion: MotionType, layer: Int, rotation: Quat = Quat.identity(), )"},{"t":"cinder.physics.world.PhysicsEventKind","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-physicseventkind","x":"struct PhysicsEventKind(Equatable, ImplicitlyCopyable, Movable, Writable) Kind of a PhysicsEvent; writes as its snake_case name."},{"t":"cinder.physics.world.PhysicsEventKind.contact_added","s":"API symbol","h":"method","u":"/docs/api/physics/#physicseventkind-contact_added","x":"contact_added() -> PhysicsEventKind Two bodies started touching."},{"t":"cinder.physics.world.PhysicsEventKind.contact_persisted","s":"API symbol","h":"method","u":"/docs/api/physics/#physicseventkind-contact_persisted","x":"contact_persisted() -> PhysicsEventKind Two bodies that were touching are still touching."},{"t":"cinder.physics.world.PhysicsEventKind.contact_removed","s":"API symbol","h":"method","u":"/docs/api/physics/#physicseventkind-contact_removed","x":"contact_removed() -> PhysicsEventKind Two bodies stopped touching; the event carries only the body ids."},{"t":"cinder.physics.world.PhysicsEventKind.body_activated","s":"API symbol","h":"method","u":"/docs/api/physics/#physicseventkind-body_activated","x":"body_activated() -> PhysicsEventKind A body woke up; the event carries the body and its user_data."},{"t":"cinder.physics.world.PhysicsEventKind.body_deactivated","s":"API symbol","h":"method","u":"/docs/api/physics/#physicseventkind-body_deactivated","x":"body_deactivated() -> PhysicsEventKind A body went to sleep; the event carries the body and its user_data."},{"t":"cinder.physics.world.PhysicsEventKind.write_to","s":"API symbol","h":"method","u":"/docs/api/physics/#physicseventkind-write_to","x":"write_to(self, mut w: Some[Writer])"},{"t":"cinder.physics.world.PhysicsEvent","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-physicsevent","x":"struct PhysicsEvent(ImplicitlyCopyable, Movable) One queued Jolt event. Contacts: body1/body2, world normal (from body 1 to body 2), first contact point on body 1 and the manifold contact_count. Activation: body1 and the body's user_data."},{"t":"cinder.physics.world.PhysicsEvent.involves","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsevent-involves","x":"involves(self, body: UInt32) -> Bool Whether body is body1 or body2."},{"t":"cinder.physics.world.PhysicsEvent.pair","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsevent-pair","x":"pair(self, a: UInt32, b: UInt32) -> Bool Whether the event is between bodies a and b, in either order."},{"t":"cinder.physics.world.RayHit","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-rayhit","x":"struct RayHit(ImplicitlyCopyable, Movable) Closest hit of PhysicsWorld.raycast: the body id, fraction of the ray (the hit is at origin + fraction * direction), the world point and the hit surface normal."},{"t":"cinder.physics.world.ShapeHit","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-shapehit","x":"struct ShapeHit(ImplicitlyCopyable, Movable) Closest hit of PhysicsWorld.shape_cast: the body id, fraction of the sweep, the contact point on the hit body, that body's surface normal, and penetration_depth, which is greater than zero when the shape already overlaps at the start."},{"t":"cinder.physics.world.PhysicsWorld","s":"API symbol","h":"struct","u":"/docs/api/physics/#struct-physicsworld","x":"struct PhysicsWorld(Movable) One Jolt physics system with its thread pool, layer filters and event queue. Shapes are Shape handles that the caller destroys once with destroy_shape; bodies are UInt32 ids that the caller destroys with destroy_body. Shapes, bodies and characters are counted in live_obj"},{"t":"cinder.physics.world.PhysicsWorld.__init__","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-__init__","x":"__init__( out self, lib: NativePtr, layers: LayerConfig = LayerConfig.default(), max_bodies: Int = 4096, worker_threads: Int = 2, event_capacity: Int = 1 << 14, ) raises Creates the Jolt system. JPH_Init is idempotent in joltc, so every world calls it; the process-wide Jolt state is never shut down "},{"t":"cinder.physics.world.PhysicsWorld.set_gravity","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-set_gravity","x":"set_gravity(self, g: Vec3) Sets the gravity acceleration in m/s^2 (scaled per body by BodyDesc.gravity_factor)."},{"t":"cinder.physics.world.PhysicsWorld.optimize_broad_phase","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-optimize_broad_phase","x":"optimize_broad_phase(self) Call after creating many static bodies and before the first step."},{"t":"cinder.physics.world.PhysicsWorld.live_objects","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-live_objects","x":"live_objects(self) -> Int Shapes, bodies and characters still alive. Should be zero before the world is destroyed; the destructor prints leak_report() when it is not, but does not abort."},{"t":"cinder.physics.world.PhysicsWorld.leak_report","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-leak_report","x":"leak_report(self) -> Optional[LogRecord] The ERROR record the destructor prints when live_objects() is not zero (category physics, with shapes, bodies and characters fields), or None without leaks."},{"t":"cinder.physics.world.PhysicsWorld.error_count","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-error_count","x":"error_count(self) -> Int Number of ERROR records in diagnostics."},{"t":"cinder.physics.world.PhysicsWorld.box","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-box","x":"box( mut self, half_extents: Vec3, convex_radius: Float32 = 0.05 ) raises -> Shape Creates a box shape with half_extents in metres; convex_radius (metres) rounds its edges. The caller destroys it with destroy_shape. Raises when Jolt fails to create it."},{"t":"cinder.physics.world.PhysicsWorld.sphere","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-sphere","x":"sphere(mut self, radius: Float32) raises -> Shape Creates a sphere shape of radius metres. The caller destroys it with destroy_shape. Raises when Jolt fails to create it."},{"t":"cinder.physics.world.PhysicsWorld.capsule","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-capsule","x":"capsule( mut self, half_height: Float32, radius: Float32 ) raises -> Shape Capsule along Y: a cylinder of 2 * half_height capped by hemispheres of radius."},{"t":"cinder.physics.world.PhysicsWorld.cylinder","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-cylinder","x":"cylinder( mut self, half_height: Float32, radius: Float32 ) raises -> Shape Creates a cylinder along Y, 2 * half_height tall with radius (metres). The caller destroys it with destroy_shape. Raises when Jolt fails to create it."},{"t":"cinder.physics.world.PhysicsWorld.convex_hull","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-convex_hull","x":"convex_hull( mut self, points: List[Vec3], convex_radius: Float32 = 0.05 ) raises -> Shape Creates the convex hull of points (metres, shape space); convex_radius rounds it. The caller destroys it with destroy_shape. Raises with fewer than 4 points or when Jolt cannot build the hull."},{"t":"cinder.physics.world.PhysicsWorld.mesh","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-mesh","x":"mesh( mut self, vertices: List[Vec3], indices: List[UInt32] ) raises -> Shape Static triangle mesh collider (Jolt mesh shapes only work on static or kinematic bodies)."},{"t":"cinder.physics.world.PhysicsWorld.scaled","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-scaled","x":"scaled(mut self, shape: Shape, scale: Vec3) raises -> Shape Creates a shape that is shape scaled per axis by scale. It is a new tracked shape: destroy it with destroy_shape in addition to the original. Raises when Jolt fails to create it."},{"t":"cinder.physics.world.PhysicsWorld.destroy_shape","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-destroy_shape","x":"destroy_shape(mut self, shape: Shape) Releases the caller's reference to shape and decrements the live count; bodies created from it keep their own reference. Call once per created shape and do not use its copies afterwards; a null shape is ignored. A shape this world did not create, or one already "},{"t":"cinder.physics.world.PhysicsWorld.create_body","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-create_body","x":"create_body( mut self, desc: BodyDesc, activate: Bool = True ) raises -> UInt32 Creates a body from desc, adds it to the world (awake when activate) and returns its id; destroy it with destroy_body. The body keeps its own reference to the shape, so the shape handle may be destroyed right after. A po"},{"t":"cinder.physics.world.PhysicsWorld.destroy_body","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-destroy_body","x":"destroy_body(mut self, id: UInt32) Removes body id from the world and destroys it. Jolt may reuse the index, but with a new sequence number, so a stale id never names the new body. An id that is not a live body of this world (INVALID_BODY, never created, already destroyed) is ignored: the live count"},{"t":"cinder.physics.world.PhysicsWorld.position","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-position","x":"position(self, id: UInt32) -> Vec3 World position of body id, in metres."},{"t":"cinder.physics.world.PhysicsWorld.rotation","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-rotation","x":"rotation(self, id: UInt32) -> Quat World rotation of body id."},{"t":"cinder.physics.world.PhysicsWorld.transform","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-transform","x":"transform(self, id: UInt32) -> Mat4 World matrix of body id, with unit scale."},{"t":"cinder.physics.world.PhysicsWorld.set_position_rotation","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-set_position_rotation","x":"set_position_rotation( self, id: UInt32, position: Vec3, rotation: Quat, activate: Bool = True ) Teleports body id to position (metres) and rotation, waking it when activate. Kinematic bodies that should carry riders are moved with move_kinematic instead."},{"t":"cinder.physics.world.PhysicsWorld.move_kinematic","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-move_kinematic","x":"move_kinematic( self, id: UInt32, target_position: Vec3, target_rotation: Quat, dt: Float32, ) Sets a kinematic body's velocity so it reaches the target over dt (next step)."},{"t":"cinder.physics.world.PhysicsWorld.linear_velocity","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-linear_velocity","x":"linear_velocity(self, id: UInt32) -> Vec3 Linear velocity of body id, in metres per second."},{"t":"cinder.physics.world.PhysicsWorld.set_linear_velocity","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-set_linear_velocity","x":"set_linear_velocity(self, id: UInt32, velocity: Vec3) Sets the linear velocity of body id, in metres per second."},{"t":"cinder.physics.world.PhysicsWorld.angular_velocity","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-angular_velocity","x":"angular_velocity(self, id: UInt32) -> Vec3 Angular velocity of body id, in radians per second."},{"t":"cinder.physics.world.PhysicsWorld.set_angular_velocity","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-set_angular_velocity","x":"set_angular_velocity(self, id: UInt32, velocity: Vec3) Sets the angular velocity of body id, in radians per second."},{"t":"cinder.physics.world.PhysicsWorld.add_force","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-add_force","x":"add_force(self, id: UInt32, force: Vec3) Adds force (newtons) to body id at its centre of mass."},{"t":"cinder.physics.world.PhysicsWorld.add_impulse","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-add_impulse","x":"add_impulse(self, id: UInt32, impulse: Vec3) Applies impulse (newton seconds) to body id at its centre of mass; wakes a sleeping body."},{"t":"cinder.physics.world.PhysicsWorld.add_impulse_at","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-add_impulse_at","x":"add_impulse_at(self, id: UInt32, impulse: Vec3, point: Vec3) Applies impulse (newton seconds) to body id at the world-space point (metres), which can also spin it."},{"t":"cinder.physics.world.PhysicsWorld.is_active","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-is_active","x":"is_active(self, id: UInt32) -> Bool Whether body id is awake."},{"t":"cinder.physics.world.PhysicsWorld.activate","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-activate","x":"activate(self, id: UInt32) Wakes body id."},{"t":"cinder.physics.world.PhysicsWorld.set_motion_type","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-set_motion_type","x":"set_motion_type(self, id: UInt32, motion: MotionType) Changes the motion type of body id and wakes it. Mesh shapes work only on static or kinematic bodies."},{"t":"cinder.physics.world.PhysicsWorld.motion_type","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-motion_type","x":"motion_type(self, id: UInt32) -> MotionType Current motion type of body id."},{"t":"cinder.physics.world.PhysicsWorld.user_data","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-user_data","x":"user_data(self, id: UInt32) -> UInt64 The 64-bit user value stored on body id (BodyDesc.user_data or set_user_data)."},{"t":"cinder.physics.world.PhysicsWorld.set_user_data","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-set_user_data","x":"set_user_data(self, id: UInt32, value: UInt64) Stores a 64-bit user value on body id; activation events report it."},{"t":"cinder.physics.world.PhysicsWorld.body_count","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-body_count","x":"body_count(self) -> Int Number of bodies in the Jolt system."},{"t":"cinder.physics.world.PhysicsWorld.step","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-step","x":"step(mut self, dt: Float32, collision_steps: Int = 1) raises Advances the simulation by dt. Deterministic for a given sequence of calls and inputs (Jolt's deterministic mode is on by default)."},{"t":"cinder.physics.world.PhysicsWorld.poll_events","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-poll_events","x":"poll_events(mut self) -> List[PhysicsEvent] Drains the listener queue (call once per step after step)."},{"t":"cinder.physics.world.PhysicsWorld.raycast","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-raycast","x":"raycast( mut self, origin: Vec3, direction: Vec3, layer_mask: UInt32 = 0xFFFFFFFF, ignore_body: UInt32 = INVALID_BODY, ) -> Optional[RayHit] Closest hit along origin + t * direction, t in [0, 1] (direction carries the range). layer_mask selects object layers; ignore_body is skipped."},{"t":"cinder.physics.world.PhysicsWorld.shape_cast","s":"API symbol","h":"method","u":"/docs/api/physics/#physicsworld-shape_cast","x":"shape_cast( mut self, shape: Shape, transform: Mat4, direction: Vec3, layer_mask: UInt32 = 0xFFFFFFFF, ignore_body: UInt32 = INVALID_BODY, ) -> Optional[ShapeHit] Closest hit of shape at transform swept by direction (t in [0, 1])."},{"t":"cinder.platform","s":"API symbol","h":"module","u":"/docs/api/platform/#cinderplatform-1","x":"Platform layer: the native library, SDL3 windows, input events and wgpu surfaces. This is the only engine module with per-operating-system branches (library file names, surface creation inside the shim). Everything above it is platform-neutral."},{"t":"cinder.platform.events","s":"API symbol","h":"module","u":"/docs/api/platform/#cinderplatformevents","x":"Platform events in engine terms, translated from SDL3's event union."},{"t":"cinder.platform.events.EventKind","s":"API symbol","h":"struct","u":"/docs/api/platform/#struct-eventkind","x":"struct EventKind(Equatable, ImplicitlyCopyable, RegisterPassable, Writable) What an Event describes."},{"t":"cinder.platform.events.EventKind.none","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-none","x":"none() -> EventKind No event: an SDL event this layer does not translate (Platform.poll drops it)."},{"t":"cinder.platform.events.EventKind.quit","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-quit","x":"quit() -> EventKind The application was asked to quit (SDL_EVENT_QUIT)."},{"t":"cinder.platform.events.EventKind.window_close","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-window_close","x":"window_close() -> EventKind The user asked to close the window window_id."},{"t":"cinder.platform.events.EventKind.window_resized","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-window_resized","x":"window_resized() -> EventKind Pixel size changed; a/b carry the new width and height."},{"t":"cinder.platform.events.EventKind.window_minimized","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-window_minimized","x":"window_minimized() -> EventKind Window window_id was minimised."},{"t":"cinder.platform.events.EventKind.window_restored","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-window_restored","x":"window_restored() -> EventKind Window window_id was restored."},{"t":"cinder.platform.events.EventKind.window_focus_gained","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-window_focus_gained","x":"window_focus_gained() -> EventKind Window window_id gained keyboard focus."},{"t":"cinder.platform.events.EventKind.window_focus_lost","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-window_focus_lost","x":"window_focus_lost() -> EventKind Window window_id lost keyboard focus."},{"t":"cinder.platform.events.EventKind.key","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-key","x":"key() -> EventKind scancode, keycode, mods, down, repeat."},{"t":"cinder.platform.events.EventKind.mouse_motion","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-mouse_motion","x":"mouse_motion() -> EventKind x, y absolute; dx, dy relative."},{"t":"cinder.platform.events.EventKind.mouse_button","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-mouse_button","x":"mouse_button() -> EventKind button (1 = left, 2 = middle, 3 = right), down, x, y."},{"t":"cinder.platform.events.EventKind.mouse_wheel","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-mouse_wheel","x":"mouse_wheel() -> EventKind dx, dy scroll amounts."},{"t":"cinder.platform.events.EventKind.gamepad_added","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-gamepad_added","x":"gamepad_added() -> EventKind A gamepad was connected; device is its SDL joystick id (Platform.poll opens it)."},{"t":"cinder.platform.events.EventKind.gamepad_removed","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-gamepad_removed","x":"gamepad_removed() -> EventKind A gamepad was disconnected; device is its SDL joystick id (Platform.poll closes it)."},{"t":"cinder.platform.events.EventKind.gamepad_button","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-gamepad_button","x":"gamepad_button() -> EventKind device, button (SDL_GamepadButton), down."},{"t":"cinder.platform.events.EventKind.gamepad_axis","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-gamepad_axis","x":"gamepad_axis() -> EventKind device, axis (SDL_GamepadAxis), value normalised to [-1, 1]."},{"t":"cinder.platform.events.EventKind.write_to","s":"API symbol","h":"method","u":"/docs/api/platform/#eventkind-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.platform.events.Event","s":"API symbol","h":"struct","u":"/docs/api/platform/#struct-event","x":"struct Event(ImplicitlyCopyable, RegisterPassable, Writable) One translated platform event. Fields are interpreted per kind (see EventKind docstrings)."},{"t":"cinder.platform.events.Event.empty","s":"API symbol","h":"method","u":"/docs/api/platform/#event-empty","x":"empty(kind: EventKind) -> Event An event of kind with every other field zero or False."},{"t":"cinder.platform.events.Event.write_to","s":"API symbol","h":"method","u":"/docs/api/platform/#event-write_to","x":"write_to(self, mut writer: Some[Writer])"},{"t":"cinder.platform.events.translate_event","s":"API symbol","h":"def","u":"/docs/api/platform/#def-translate_event","x":"def translate_event(ev: SDL_Event) -> Event Translates an SDL event into an Event; unknown SDL events become EventKind.none()."},{"t":"cinder.platform.native","s":"API symbol","h":"module","u":"/docs/api/platform/#cinderplatformnative","x":"Loading libcinder_native exactly once and sharing it by pointer. NativeLibrary keeps the generated CinderNative struct (library handle plus every resolved symbol) at a stable heap address for the life of the program. Subsystems hold a NativePtr to it and never copy function pointers out (docs/MOJO_N"},{"t":"cinder.platform.native.default_library_path","s":"API symbol","h":"def","u":"/docs/api/platform/#def-default_library_path","x":"def default_library_path() -> String The library path for this OS relative to the repository root, unless CINDER_NATIVE_LIB is set."},{"t":"cinder.platform.native.NativeLibrary","s":"API symbol","h":"struct","u":"/docs/api/platform/#struct-nativelibrary","x":"struct NativeLibrary(Movable) Owns the one loaded copy of libcinder_native."},{"t":"cinder.platform.native.NativeLibrary.__init__","s":"API symbol","h":"method","u":"/docs/api/platform/#nativelibrary-__init__","x":"__init__(out self, path: String) raises Loads the library at path, resolving every bound symbol. Raises when the library is missing, a symbol is missing, or the shim's ABI version differs from the bindings'."},{"t":"cinder.platform.native.NativeLibrary.__init__","s":"API symbol","h":"method","u":"/docs/api/platform/#nativelibrary-__init__-1","x":"__init__(out self) raises Loads the library from default_library_path()."},{"t":"cinder.platform.native.NativeLibrary.ptr","s":"API symbol","h":"method","u":"/docs/api/platform/#nativelibrary-ptr","x":"ptr(self) -> NativePtr Pointer to the loaded bindings, valid only while this NativeLibrary lives: it must be destroyed after every subsystem holding the pointer."},{"t":"cinder.platform.window","s":"API symbol","h":"module","u":"/docs/api/platform/#cinderplatformwindow","x":"SDL3 platform: initialisation, windows, event polling, gamepads and wgpu surface creation."},{"t":"cinder.platform.window.GamepadSlot","s":"API symbol","h":"struct","u":"/docs/api/platform/#struct-gamepadslot","x":"struct GamepadSlot(ImplicitlyCopyable, Movable) An open gamepad: SDL joystick id and the SDL_Gamepad handle."},{"t":"cinder.platform.window.Platform","s":"API symbol","h":"struct","u":"/docs/api/platform/#struct-platform","x":"struct Platform(Movable) SDL initialised for video, events and gamepads. Create one, before any Window, on the main thread. SDL's log goes to a shim queue and is drained into diagnostics by poll."},{"t":"cinder.platform.window.Platform.__init__","s":"API symbol","h":"method","u":"/docs/api/platform/#platform-__init__","x":"__init__(out self, lib: NativePtr) raises"},{"t":"cinder.platform.window.Platform.video_driver","s":"API symbol","h":"method","u":"/docs/api/platform/#platform-video_driver","x":"video_driver(self) -> String Name of the current SDL video driver; empty when SDL reports none."},{"t":"cinder.platform.window.Platform.last_error","s":"API symbol","h":"method","u":"/docs/api/platform/#platform-last_error","x":"last_error(self) -> String SDL's most recent error message (SDL_GetError)."},{"t":"cinder.platform.window.Platform.poll","s":"API symbol","h":"method","u":"/docs/api/platform/#platform-poll","x":"poll(mut self, mut events: List[Event]) Drains SDL's event queue into events (cleared first; capacity is retained so a warmed-up list does not allocate), opening and closing gamepads as they come and go, and moves SDL log lines into diagnostics."},{"t":"cinder.platform.window.Platform.take_diagnostics","s":"API symbol","h":"method","u":"/docs/api/platform/#platform-take_diagnostics","x":"take_diagnostics(mut self) -> List[LogRecord] Moves out the SDL log records collected by poll, leaving diagnostics empty."},{"t":"cinder.platform.window.Platform.push_event","s":"API symbol","h":"method","u":"/docs/api/platform/#platform-push_event","x":"push_event(self, ev: SDL_Event) -> Bool Injects an SDL event (tests and replay). Returns False when SDL refused it."},{"t":"cinder.platform.window.Window","s":"API symbol","h":"struct","u":"/docs/api/platform/#struct-window","x":"struct Window(Movable) An SDL window. Destroyed with the struct; create the wgpu surface through create_surface."},{"t":"cinder.platform.window.Window.__init__","s":"API symbol","h":"method","u":"/docs/api/platform/#window-__init__","x":"__init__( out self, platform: Platform, title: String, width: Int, height: Int, resizable: Bool = True, hidden: Bool = False, ) raises"},{"t":"cinder.platform.window.Window.pixel_size","s":"API symbol","h":"method","u":"/docs/api/platform/#window-pixel_size","x":"pixel_size(self) raises -> Tuple[Int, Int] Drawable size in pixels as (width, height); with high pixel density it can differ from the size the window was created with. Raises when SDL fails."},{"t":"cinder.platform.window.Window.set_size","s":"API symbol","h":"method","u":"/docs/api/platform/#window-set_size","x":"set_size(self, width: Int, height: Int) raises Requests a new window size and waits for it to apply (SDL_SyncWindow, whose result is ignored); raises when SDL rejects the request."},{"t":"cinder.platform.window.Window.set_title","s":"API symbol","h":"method","u":"/docs/api/platform/#window-set_title","x":"set_title(mut self, title: String) Sets the window title, keeping the text alive in the window; SDL failures are ignored."},{"t":"cinder.platform.window.Window.show","s":"API symbol","h":"method","u":"/docs/api/platform/#window-show","x":"show(self) Shows the window (one created with hidden); SDL failures are ignored."},{"t":"cinder.platform.window.Window.set_relative_mouse","s":"API symbol","h":"method","u":"/docs/api/platform/#window-set_relative_mouse","x":"set_relative_mouse(self, enabled: Bool) -> Bool Turns SDL relative mouse mode on or off for this window; returns False when SDL refused."},{"t":"cinder.platform.window.Window.create_surface","s":"API symbol","h":"method","u":"/docs/api/platform/#window-create_surface","x":"create_surface(self, instance: WGPUInstance) raises -> WGPUSurface Creates the wgpu surface for this window through the shim (X11, Wayland or Metal)."},{"t":"cinder.platform.window.Window.destroy_surface","s":"API symbol","h":"method","u":"/docs/api/platform/#window-destroy_surface","x":"destroy_surface(self, surface: WGPUSurface) Destroys a surface returned by create_surface, through the shim with this window's handle."},{"t":"cinder.render","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrender-1","x":"Renderer: cameras, meshes, materials, textures, the frame graph and the forward+ pipeline. Everything here talks to the GPU only through cinder.gfx."},{"t":"cinder.render.camera","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrendercamera","x":"Cameras and frustum culling. Right-handed, Y up, camera looks down -Z. Projection is reverse-Z with an infinite far plane, so the frustum has five planes. cull_spheres tests four spheres at a time with SIMD."},{"t":"cinder.render.camera.Plane","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-plane","x":"struct Plane(ImplicitlyCopyable, RegisterPassable) normal . p + d >= 0 is the inside half-space."},{"t":"cinder.render.camera.Plane.from_row","s":"API symbol","h":"method","u":"/docs/api/render/#plane-from_row","x":"from_row(v: Vec4) -> Plane Plane from a clip-space row combination, scaled so normal has unit length."},{"t":"cinder.render.camera.Plane.distance","s":"API symbol","h":"method","u":"/docs/api/render/#plane-distance","x":"distance(self, p: Vec3) -> Float32 Signed distance from p to the plane (positive inside), in the units of p."},{"t":"cinder.render.camera.Frustum","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-frustum","x":"struct Frustum(Copyable, Movable) Left, right, bottom, top, near planes extracted from a view-projection matrix (Gribb/Hartmann). Reverse-Z with infinite far: the near plane is row3 - row2 (clip z <= w); no far plane."},{"t":"cinder.render.camera.Frustum.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#frustum-__init__","x":"__init__(out self, view_proj: Mat4)"},{"t":"cinder.render.camera.Frustum.contains_sphere","s":"API symbol","h":"method","u":"/docs/api/render/#frustum-contains_sphere","x":"contains_sphere(self, centre: Vec3, radius: Float32) -> Bool True unless the sphere lies entirely behind one of the five planes. Conservative: a sphere near a frustum edge or corner may pass without intersecting it."},{"t":"cinder.render.camera.Frustum.contains_point","s":"API symbol","h":"method","u":"/docs/api/render/#frustum-contains_point","x":"contains_point(self, p: Vec3) -> Bool True when p is on the inside of all five planes (near, left, right, bottom, top)."},{"t":"cinder.render.camera.Frustum.cull_spheres_into","s":"API symbol","h":"method","u":"/docs/api/render/#frustum-cull_spheres_into","x":"cull_spheres_into(self, spheres: List[Vec4], mut out: List[Bool]) Visibility per sphere (xyz centre, w radius), four spheres per SIMD step, written into a list supplied by the caller (cleared first, capacity kept)."},{"t":"cinder.render.camera.Frustum.cull_spheres","s":"API symbol","h":"method","u":"/docs/api/render/#frustum-cull_spheres","x":"cull_spheres(self, spheres: List[Vec4]) -> List[Bool] Visibility per sphere (xyz centre, w radius); allocates the result list. Use cull_spheres_into in per-frame code to reuse a scratch list."},{"t":"cinder.render.camera.Camera","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-camera","x":"struct Camera(ImplicitlyCopyable, Movable) A perspective camera. near is the only clip distance (infinite far)."},{"t":"cinder.render.camera.Camera.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#camera-__init__","x":"__init__( out self, position: Vec3, rotation: Quat, fov_y: Float32 = 1.0471976, aspect: Float32 = 16.0 / 9.0, near: Float32 = 0.05, )"},{"t":"cinder.render.camera.Camera.look_at","s":"API symbol","h":"method","u":"/docs/api/render/#camera-look_at","x":"look_at( eye: Vec3, target: Vec3, up: Vec3 = Vec3.unit_y(), fov_y: Float32 = 1.0471976, aspect: Float32 = 16.0 / 9.0, near: Float32 = 0.05, ) -> Camera A camera at eye looking at target, with its rotation taken from Mat4.look_at. up only orients the camera (the stored up() is re-orthogonalised); fov"},{"t":"cinder.render.camera.Camera.forward","s":"API symbol","h":"method","u":"/docs/api/render/#camera-forward","x":"forward(self) -> Vec3 World-space view direction: Vec3.forward() (-Z) rotated by rotation, unit length."},{"t":"cinder.render.camera.Camera.right","s":"API symbol","h":"method","u":"/docs/api/render/#camera-right","x":"right(self) -> Vec3 World-space right axis: +X rotated by rotation."},{"t":"cinder.render.camera.Camera.up","s":"API symbol","h":"method","u":"/docs/api/render/#camera-up","x":"up(self) -> Vec3 World-space up axis: +Y rotated by rotation."},{"t":"cinder.render.camera.Camera.view","s":"API symbol","h":"method","u":"/docs/api/render/#camera-view","x":"view(self) -> Mat4 World-to-view matrix (right-handed, looking down -Z), column-major."},{"t":"cinder.render.camera.Camera.projection","s":"API symbol","h":"method","u":"/docs/api/render/#camera-projection","x":"projection(self) -> Mat4 Reverse-Z perspective with an infinite far plane: depth 1 at near, 0 at infinity."},{"t":"cinder.render.camera.Camera.view_projection","s":"API symbol","h":"method","u":"/docs/api/render/#camera-view_projection","x":"view_projection(self) -> Mat4 projection() * view(): world space to clip space."},{"t":"cinder.render.camera.Camera.frustum","s":"API symbol","h":"method","u":"/docs/api/render/#camera-frustum","x":"frustum(self) -> Frustum The five-plane culling frustum of view_projection() (no heap allocation)."},{"t":"cinder.render.camera.Camera.set_viewport","s":"API symbol","h":"method","u":"/docs/api/render/#camera-set_viewport","x":"set_viewport(mut self, width: Int, height: Int) Sets aspect to width / height in pixels; ignored when height is not positive."},{"t":"cinder.render.frame_graph","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrenderframe_graph","x":"Frame graph: declared passes, transient textures with lifetimes and aliasing, automatic ordering. The graph is rebuilt every frame (cheap: a few passes). compile orders passes by their read/write dependencies, culls passes that contribute nothing to an exported resource, computes each transient's li"},{"t":"cinder.render.frame_graph.TransientDesc","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-transientdesc","x":"struct TransientDesc(Equatable, ImplicitlyCopyable, Movable) Size in pixels, format, WGPUTextureUsage bits, sample and mip count of a transient. Transients share one pooled physical texture only when their descriptions are equal."},{"t":"cinder.render.frame_graph.FrameGraph","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-framegraph","x":"struct FrameGraph(Movable) One frame's passes and resources, compiled into an execution order and pooled textures. Per frame: reset, declare resources (create, import_view, create_external) and passes (add_pass, read, write), export what must survive, compile, then walk order and look up views with "},{"t":"cinder.render.frame_graph.FrameGraph.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-__init__","x":"__init__(out self)"},{"t":"cinder.render.frame_graph.FrameGraph.reset","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-reset","x":"reset(mut self) Starts a new frame's declaration. Physical textures stay pooled; pass records keep their read/write list capacity for reuse."},{"t":"cinder.render.frame_graph.FrameGraph.create","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-create","x":"create(mut self, name: String, desc: TransientDesc) -> Int Declares a transient texture and returns its resource index (valid until reset). The physical texture is assigned by compile, possibly shared with other transients."},{"t":"cinder.render.frame_graph.FrameGraph.import_view","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-import_view","x":"import_view( mut self, name: String, view: TextureViewHandle, desc: TransientDesc ) -> Int A view the caller keeps alive (swapchain, persistent target). Never aliased."},{"t":"cinder.render.frame_graph.FrameGraph.create_external","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-create_external","x":"create_external(mut self, name: String) -> Int A buffer-backed resource (no texture): passes writing it are ordered before readers and kept alive by them, but no physical texture is allocated."},{"t":"cinder.render.frame_graph.FrameGraph.export","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-export","x":"export(mut self, resource: Int) Marks a resource as needed outside the graph; passes feeding it are kept."},{"t":"cinder.render.frame_graph.FrameGraph.add_pass","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-add_pass","x":"add_pass(mut self, name: String) -> Int Declares a pass and returns its index; reuses a spare pass record when there is one."},{"t":"cinder.render.frame_graph.FrameGraph.read","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-read","x":"read(mut self, pass_: Int, resource: Int) Declares that pass_ reads resource (ordered after the resource's producer)."},{"t":"cinder.render.frame_graph.FrameGraph.write","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-write","x":"write(mut self, pass_: Int, resource: Int) Declares that pass_ writes resource; writers of a resource keep declaration order."},{"t":"cinder.render.frame_graph.FrameGraph.compile","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-compile","x":"compile(mut self, mut res: Resources) raises Orders, culls, computes lifetimes and assigns physical textures."},{"t":"cinder.render.frame_graph.FrameGraph.release","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-release","x":"release(mut self, mut res: Resources) raises Destroys every pooled physical texture and its view; raises on a Resources error."},{"t":"cinder.render.frame_graph.FrameGraph.view_of","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-view_of","x":"view_of(self, resource: Int) raises -> TextureViewHandle View of resource after compile: the imported view or its physical texture's view. Raises for an external (buffer-backed) resource and for one with no physical texture (unused, culled or not compiled)."},{"t":"cinder.render.frame_graph.FrameGraph.texture_of","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-texture_of","x":"texture_of(self, resource: Int) raises -> TextureHandle Physical texture behind a transient after compile. Raises for imported and external resources and for transients without a physical texture."},{"t":"cinder.render.frame_graph.FrameGraph.find","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-find","x":"find(self, name: String) -> Int Index of the resource with this name, or -1."},{"t":"cinder.render.frame_graph.FrameGraph.physical_count","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-physical_count","x":"physical_count(self) -> Int Pooled physical textures after the last compile (FrameStats.transient_textures)."},{"t":"cinder.render.frame_graph.FrameGraph.is_culled","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-is_culled","x":"is_culled(self, pass_: Int) -> Bool True when the last compile culled the pass (it feeds no exported resource)."},{"t":"cinder.render.frame_graph.FrameGraph.lifetime","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-lifetime","x":"lifetime(self, resource: Int) -> Tuple[Int, Int] (first, last) execution-order indices using the resource; (-1, -1) if no kept pass does."},{"t":"cinder.render.frame_graph.FrameGraph.to_graphviz","s":"API symbol","h":"method","u":"/docs/api/render/#framegraph-to_graphviz","x":"to_graphviz(self) -> String DOT text: passes as boxes in execution order, resources as ellipses, edges for reads/writes."},{"t":"cinder.render.lights","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrenderlights","x":"Lights and the clustered-culling configuration shared with shaders/common.wgsl."},{"t":"cinder.render.lights.LightKind","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-lightkind","x":"struct LightKind(Equatable, ImplicitlyCopyable, RegisterPassable) Light type tag; values mirror LIGHT_* in common.wgsl (0 directional, 1 point, 2 spot)."},{"t":"cinder.render.lights.LightKind.directional","s":"API symbol","h":"method","u":"/docs/api/render/#lightkind-directional","x":"directional() -> LightKind Sun-like light at infinity; only its direction matters."},{"t":"cinder.render.lights.LightKind.point","s":"API symbol","h":"method","u":"/docs/api/render/#lightkind-point","x":"point() -> LightKind Omnidirectional light at a position, attenuated to zero at range."},{"t":"cinder.render.lights.LightKind.spot","s":"API symbol","h":"method","u":"/docs/api/render/#lightkind-spot","x":"spot() -> LightKind Cone light at a position along direction, attenuated to zero at range."},{"t":"cinder.render.lights.Light","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-light","x":"struct Light(ImplicitlyCopyable, Movable) A light. Directional: direction (travel direction), color * intensity in lux. Point and spot: position, range (metres), intensity in watts, spot cone angles in radians."},{"t":"cinder.render.lights.Light.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#light-__init__","x":"__init__( out self, kind: LightKind, position: Vec3 = Vec3.zero(), direction: Vec3 = Vec3(0.0, -1.0, 0.0), color: Vec3 = Vec3.one(), intensity: Float32 = 1.0, range: Float32 = 10.0, inner_angle: Float32 = 0.4, outer_angle: Float32 = 0.6, cast_shadows: Bool = False, )"},{"t":"cinder.render.lights.Light.sun","s":"API symbol","h":"method","u":"/docs/api/render/#light-sun","x":"sun( direction: Vec3, color: Vec3 = Vec3.one(), lux: Float32 = 3.0, cast_shadows: Bool = True, ) -> Light Directional light travelling along direction (normalised), with lux illuminance. Position, range and cone angles are zero; shadows default to on (cascaded shadow maps)."},{"t":"cinder.render.lights.Light.point_light","s":"API symbol","h":"method","u":"/docs/api/render/#light-point_light","x":"point_light( position: Vec3, color: Vec3, watts: Float32, range: Float32, cast_shadows: Bool = False, ) -> Light Point light at position (metres) emitting watts, reaching zero at range metres."},{"t":"cinder.render.lights.Light.spot_light","s":"API symbol","h":"method","u":"/docs/api/render/#light-spot_light","x":"spot_light( position: Vec3, direction: Vec3, color: Vec3, watts: Float32, range: Float32, inner: Float32, outer: Float32, cast_shadows: Bool = False, ) -> Light Spot light along direction (normalised) emitting watts with a range in metres. inner and outer are cone half-angles in radians from the axi"},{"t":"cinder.render.lights.Light.bounding_sphere","s":"API symbol","h":"method","u":"/docs/api/render/#light-bounding_sphere","x":"bounding_sphere(self) -> Vec4 Influence sphere: xyz position, w range in metres (meaningless for directional lights)."},{"t":"cinder.render.lights.Light.pack","s":"API symbol","h":"method","u":"/docs/api/render/#light-pack","x":"pack(self, mut w: ByteWriter, shadow_view: Int) 64 bytes matching Light in common.wgsl."},{"t":"cinder.render.lights.pack_lights_into","s":"API symbol","h":"def","u":"/docs/api/render/#def-pack_lights_into","x":"def pack_lights_into( mut w: ByteWriter, lights: List[Light], shadow_views: List[Int] ) Appends the GPU light array (at least one zeroed entry) to w."},{"t":"cinder.render.lights.pack_lights","s":"API symbol","h":"def","u":"/docs/api/render/#def-pack_lights","x":"def pack_lights(lights: List[Light], shadow_views: List[Int]) -> List[UInt8] pack_lights_into into a new byte list (allocates; at least one zeroed 64-byte entry). shadow_views[i] is light i's first shadow-view index (-1 or missing: no shadow)."},{"t":"cinder.render.lights.ClusterParams","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-clusterparams","x":"struct ClusterParams(ImplicitlyCopyable, Movable) Logarithmic z slicing: slice = log2(z) * scale + bias between near and far."},{"t":"cinder.render.lights.ClusterParams.compute","s":"API symbol","h":"method","u":"/docs/api/render/#clusterparams-compute","x":"compute(near: Float32, far: Float32) -> ClusterParams Slice parameters mapping near to slice 0 and far to CLUSTER_Z (metres)."},{"t":"cinder.render.lights.ClusterParams.slice_of","s":"API symbol","h":"method","u":"/docs/api/render/#clusterparams-slice_of","x":"slice_of(self, view_z: Float32) -> Int Z slice for a positive view-space distance in metres, clamped to [0, CLUSTER_Z - 1]. Mirrors cluster_z_slice in common.wgsl."},{"t":"cinder.render.material","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrendermaterial","x":"glTF 2.0 metallic-roughness materials and their GPU bind groups. Uniform layout (96 bytes, 16-byte aligned fields, mirrored by shaders/material.wgsl): 0  base_color: vec4 16 emissive.rgb, emissive_strength 32 metallic, roughness, normal_scale, occlusion_strength 48 alpha_cutoff, alpha_mode (0 opaque"},{"t":"cinder.render.material.AlphaMode","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-alphamode","x":"struct AlphaMode(Equatable, ImplicitlyCopyable, RegisterPassable) How alpha is used; packed into the material uniform and mirrored in WGSL."},{"t":"cinder.render.material.AlphaMode.opaque","s":"API symbol","h":"method","u":"/docs/api/render/#alphamode-opaque","x":"opaque() -> AlphaMode Alpha is ignored; the surface is fully opaque."},{"t":"cinder.render.material.AlphaMode.mask","s":"API symbol","h":"method","u":"/docs/api/render/#alphamode-mask","x":"mask() -> AlphaMode Alpha-tested: fragments with alpha below alpha_cutoff are discarded."},{"t":"cinder.render.material.AlphaMode.blend","s":"API symbol","h":"method","u":"/docs/api/render/#alphamode-blend","x":"blend() -> AlphaMode Alpha-blended; drawn after opaque batches, sorted back to front."},{"t":"cinder.render.material.Material","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-material","x":"struct Material(Copyable, Movable) CPU description. Texture views are null when a map is absent (defaults are bound instead)."},{"t":"cinder.render.material.Material.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#material-__init__","x":"__init__(out self, name: String = String(\"material\"))"},{"t":"cinder.render.material.Material.texture_flags","s":"API symbol","h":"method","u":"/docs/api/render/#material-texture_flags","x":"texture_flags(self) -> UInt32 Bit i set when map slot i (TEX_*) has a texture view."},{"t":"cinder.render.material.Material.texcoord_bits","s":"API symbol","h":"method","u":"/docs/api/render/#material-texcoord_bits","x":"texcoord_bits(self) -> UInt32 Texture coordinate set per map, 3 bits per TEX_* slot (the shader takes uv1 for 1)."},{"t":"cinder.render.material.Material.pack_uniform","s":"API symbol","h":"method","u":"/docs/api/render/#material-pack_uniform","x":"pack_uniform(self) -> List[UInt8] The 96-byte uniform laid out as in the module docstring, in a new list (allocates)."},{"t":"cinder.render.material.material_bind_group_layout","s":"API symbol","h":"def","u":"/docs/api/render/#def-material_bind_group_layout","x":"def material_bind_group_layout( mut res: Resources, ) raises -> BindGroupLayoutHandle Creates the group-2 material layout matching shaders/material.wgsl; raises on a GPU error. Binding 0 is the uniform (vertex and fragment), 1 the sampler, 2..6 the maps in TEX_* order."},{"t":"cinder.render.material.GpuMaterial","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-gpumaterial","x":"struct GpuMaterial(ImplicitlyCopyable, Movable) Uniform buffer and bind group of an uploaded material. alpha_mode and double_sided are copied from the Material to select its pipeline."},{"t":"cinder.render.material.upload_material","s":"API symbol","h":"def","u":"/docs/api/render/#def-upload_material","x":"def upload_material( mut res: Resources, layout: BindGroupLayoutHandle, defaults: DefaultTextures, material: Material, ) raises -> GpuMaterial Creates the material's uniform buffer and group-2 bind group; raises on a GPU error. Absent maps bind the defaults (white base colour, metallic-roughness and"},{"t":"cinder.render.material.update_material","s":"API symbol","h":"def","u":"/docs/api/render/#def-update_material","x":"def update_material( mut res: Resources, gpu: GpuMaterial, material: Material ) raises Re-uploads the parameters (textures are fixed per bind group)."},{"t":"cinder.render.mesh","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrendermesh","x":"Mesh data on the CPU, GPU meshes, the engine vertex format and procedural primitives. Vertex stream (56 bytes): position f32x3 @0, normal f32x3 @12, tangent f32x4 @24 (w = handedness), uv0 f32x2 @40, uv1 f32x2 @48. Skinning attributes live in a second stream (SkinVertex, 24 bytes): joints u16x4 @0, "},{"t":"cinder.render.mesh.vertex_layout","s":"API symbol","h":"def","u":"/docs/api/render/#def-vertex_layout","x":"def vertex_layout() -> VertexLayout Slot 0: the static vertex stream. Locations 0..4."},{"t":"cinder.render.mesh.skin_vertex_layout","s":"API symbol","h":"def","u":"/docs/api/render/#def-skin_vertex_layout","x":"def skin_vertex_layout() -> VertexLayout Slot 1: joints and weights. Locations 5 and 6."},{"t":"cinder.render.mesh.Bounds","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-bounds","x":"struct Bounds(ImplicitlyCopyable, Movable) Axis-aligned bounding box in mesh space (metres)."},{"t":"cinder.render.mesh.Bounds.centre","s":"API symbol","h":"method","u":"/docs/api/render/#bounds-centre","x":"centre(self) -> Vec3 Midpoint of the box."},{"t":"cinder.render.mesh.Bounds.radius","s":"API symbol","h":"method","u":"/docs/api/render/#bounds-radius","x":"radius(self) -> Float32 Half the box diagonal: the radius of the bounding sphere around centre()."},{"t":"cinder.render.mesh.Bounds.sphere","s":"API symbol","h":"method","u":"/docs/api/render/#bounds-sphere","x":"sphere(self) -> Vec4 Bounding sphere: xyz centre, w radius (the input of Frustum.cull_spheres_into)."},{"t":"cinder.render.mesh.Bounds.empty","s":"API symbol","h":"method","u":"/docs/api/render/#bounds-empty","x":"empty() -> Bounds Inverted box (min +3.4e38, max -3.4e38); the first include collapses it to a point."},{"t":"cinder.render.mesh.Bounds.include","s":"API symbol","h":"method","u":"/docs/api/render/#bounds-include","x":"include(mut self, p: Vec3) Grows the box to contain p."},{"t":"cinder.render.mesh.MorphTarget","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-morphtarget","x":"struct MorphTarget(Copyable, Movable) Per-vertex position and normal deltas of one morph target (normals may be empty)."},{"t":"cinder.render.mesh.MeshLod","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-meshlod","x":"struct MeshLod(ImplicitlyCopyable, Movable) An index range of the combined index buffer and the simplification error it carries."},{"t":"cinder.render.mesh.MeshData","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-meshdata","x":"struct MeshData(Copyable, Movable) CPU-side triangle mesh. Parallel per-vertex arrays; tangents, uv1, joints and weights may be empty and are filled with defaults at upload."},{"t":"cinder.render.mesh.MeshData.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-__init__","x":"__init__(out self)"},{"t":"cinder.render.mesh.MeshData.vertex_count","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-vertex_count","x":"vertex_count(self) -> Int Number of vertices (len(positions))."},{"t":"cinder.render.mesh.MeshData.triangle_count","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-triangle_count","x":"triangle_count(self) -> Int Number of triangles (len(indices) // 3)."},{"t":"cinder.render.mesh.MeshData.has_skin","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-has_skin","x":"has_skin(self) -> Bool True when every vertex has joint indices and weights (joints and weights both match positions, non-empty), which is what pack_skin_vertices reads."},{"t":"cinder.render.mesh.MeshData.bounds","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-bounds","x":"bounds(self) -> Bounds Bounds of positions; a zero box at the origin when the mesh has no vertices."},{"t":"cinder.render.mesh.MeshData.validate","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-validate","x":"validate(self) raises Raises when the arrays are inconsistent. Checks that normals and uv0 match the vertex count, that tangents and uv1 are empty or match it, that joints and weights are either both empty or both match it (a skin needs one of each per vertex), that the index count is a multiple of "},{"t":"cinder.render.mesh.MeshData.compute_normals","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-compute_normals","x":"compute_normals(mut self) Area-weighted face normals accumulated per vertex."},{"t":"cinder.render.mesh.MeshData.compute_tangents","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-compute_tangents","x":"compute_tangents(mut self, lib: NativePtr) raises MikkTSpace tangents through the shim (requires normals and uv0)."},{"t":"cinder.render.mesh.MeshData.pack_vertices","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-pack_vertices","x":"pack_vertices(self) -> List[UInt8] The 56-byte-per-vertex static stream as a new byte list (allocates). Missing tangents become (1, 0, 0, 1) and missing uv1 repeats uv0. Expects normals and uv0 for every vertex (see validate)."},{"t":"cinder.render.mesh.MeshData.pack_skin_vertices","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-pack_skin_vertices","x":"pack_skin_vertices(self) -> List[UInt8] The 24-byte-per-vertex skin stream: little-endian u16x4 joints, then f32x4 weights. Allocates; expects joints and weights for every vertex."},{"t":"cinder.render.mesh.MeshData.pack_indices","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-pack_indices","x":"pack_indices(self) -> List[UInt8] indices as little-endian u32 bytes in a new list (allocates)."},{"t":"cinder.render.mesh.MeshData.pack_morph_deltas","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-pack_morph_deltas","x":"pack_morph_deltas(self) -> List[UInt8] Per target, per vertex: position delta (vec4) then normal delta (vec4), as common.wgsl reads."},{"t":"cinder.render.mesh.MeshData.build_lods","s":"API symbol","h":"method","u":"/docs/api/render/#meshdata-build_lods","x":"build_lods( self, lib: NativePtr, levels: Int = 4 ) raises -> List[List[UInt32]] LOD index lists (level 0 is the original, vertex-cache optimised) via meshoptimizer. Each level targets half the triangles of the previous one with a relative error bound of 2%; a level that cannot be simplified further"},{"t":"cinder.render.mesh.GpuMesh","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-gpumesh","x":"struct GpuMesh(Copyable, Movable) Buffers for one mesh. skin_vertices is null when the mesh is not skinned, morph_buffer when it has no morph targets. lods[0] is the full-detail index range. index_count is the full-detail index count (lods[0].index_count, 0 without LODs), the same for upload_mesh_lo"},{"t":"cinder.render.mesh.GpuMesh.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#gpumesh-__init__","x":"__init__( out self, vertices: BufferHandle, skin_vertices: BufferHandle, indices: BufferHandle, index_count: UInt32, vertex_count: UInt32, bounds: Bounds, var lods: List[MeshLod], morph_buffer: BufferHandle, morph_target_count: UInt32, )"},{"t":"cinder.render.mesh.GpuMesh.select_lod","s":"API symbol","h":"method","u":"/docs/api/render/#gpumesh-select_lod","x":"select_lod( self, projected_diameter_px: Float32, screen_height_px: Float32 ) -> Int Smaller on screen -> coarser LOD. Level 1 below 1/4 of the screen height, level 2 below 1/8, level 3 below 1/16 (resolution independent)."},{"t":"cinder.render.mesh.upload_mesh","s":"API symbol","h":"def","u":"/docs/api/render/#def-upload_mesh","x":"def upload_mesh( mut res: Resources, label: String, data: MeshData ) raises -> GpuMesh Uploads with a single LOD."},{"t":"cinder.render.mesh.upload_mesh_lods","s":"API symbol","h":"def","u":"/docs/api/render/#def-upload_mesh_lods","x":"def upload_mesh_lods( mut res: Resources, label: String, data: MeshData, lod_indices: List[List[UInt32]], ) raises -> GpuMesh Uploads vertex streams plus one combined index buffer holding every LOD range."},{"t":"cinder.render.mesh.upload_mesh_bytes","s":"API symbol","h":"def","u":"/docs/api/render/#def-upload_mesh_bytes","x":"def upload_mesh_bytes( mut res: Resources, label: String, vertex_count: UInt32, vertex_bytes: List[UInt8], skin_bytes: List[UInt8], index_bytes: List[UInt8], var lods: List[MeshLod], bounds: Bounds, morph_target_count: UInt32, morph_bytes: List[UInt8], ) raises -> GpuMesh Uploads streams that are al"},{"t":"cinder.render.mesh.destroy_mesh","s":"API symbol","h":"def","u":"/docs/api/render/#def-destroy_mesh","x":"def destroy_mesh(mut res: Resources, mesh: GpuMesh) raises Destroys the mesh's vertex and index buffers, and its skin and morph buffers when present. The handles in mesh are left as they are; raises on a Resources error."},{"t":"cinder.render.mesh.cube","s":"API symbol","h":"def","u":"/docs/api/render/#def-cube","x":"def cube(half: Float32 = 0.5) -> MeshData 24-vertex cube with per-face normals, CCW winding seen from outside."},{"t":"cinder.render.mesh.uv_sphere","s":"API symbol","h":"def","u":"/docs/api/render/#def-uv_sphere","x":"def uv_sphere( radius: Float32 = 0.5, rings: Int = 16, segments: Int = 32 ) -> MeshData UV sphere centred at the origin, poles on +Y and -Y; radius in metres. (rings + 1) * (segments + 1) vertices (the seam is duplicated), counter-clockwise seen from outside. u runs around the sphere from +X towards"},{"t":"cinder.render.mesh.plane","s":"API symbol","h":"def","u":"/docs/api/render/#def-plane","x":"def plane(half: Float32 = 0.5, subdivisions: Int = 1) -> MeshData An XZ plane facing +Y."},{"t":"cinder.render.renderer","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrenderrenderer","x":"The forward+ renderer: shadows, depth prepass, GTAO, clustered lights, PBR, sky, bloom, tonemap. Renderer.render(scene, target) builds the frame graph for the current settings, compiles it (ordering, transient aliasing), fills the GPU buffers from the scene and records every pass into one command bu"},{"t":"cinder.render.renderer.Tonemapper","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-tonemapper","x":"struct Tonemapper(Equatable, ImplicitlyCopyable, RegisterPassable) Tone curve of the tonemap pass; values mirror post.settings.z in post.wgsl."},{"t":"cinder.render.renderer.Tonemapper.agx","s":"API symbol","h":"method","u":"/docs/api/render/#tonemapper-agx","x":"agx() -> Tonemapper AgX (the default)."},{"t":"cinder.render.renderer.Tonemapper.aces","s":"API symbol","h":"method","u":"/docs/api/render/#tonemapper-aces","x":"aces() -> Tonemapper ACES filmic curve."},{"t":"cinder.render.renderer.Tonemapper.none","s":"API symbol","h":"method","u":"/docs/api/render/#tonemapper-none","x":"none() -> Tonemapper No curve: the exposed colour is clamped to [0, 1]; also used for debug views."},{"t":"cinder.render.renderer.DebugMode","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-debugmode","x":"struct DebugMode(Equatable, ImplicitlyCopyable, RegisterPassable) Mirrors DEBUG_* in common.wgsl."},{"t":"cinder.render.renderer.DebugMode.none","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-none","x":"none() -> DebugMode Normal lit output."},{"t":"cinder.render.renderer.DebugMode.normals","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-normals","x":"normals() -> DebugMode World-space normals mapped to colour (n * 0.5 + 0.5)."},{"t":"cinder.render.renderer.DebugMode.cascades","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-cascades","x":"cascades() -> DebugMode Colour per sun shadow cascade, shaded by the sun angle."},{"t":"cinder.render.renderer.DebugMode.clusters","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-clusters","x":"clusters() -> DebugMode Heat map of the light count in each fragment's cluster (full scale at 16 lights)."},{"t":"cinder.render.renderer.DebugMode.overdraw","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-overdraw","x":"overdraw() -> DebugMode Overdraw count: forward pipelines blend additively with the depth test off, so every rasterised fragment (hidden or not, opaque or blended) adds 0.1 grey; brightness is the fragment count / 10 (white at 10 or more) and empty sky is black."},{"t":"cinder.render.renderer.DebugMode.lod","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-lod","x":"lod() -> DebugMode Colour per instance from its LOD index (bits 8..15 of the instance flags), skinned and morphed instances included."},{"t":"cinder.render.renderer.DebugMode.albedo","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-albedo","x":"albedo() -> DebugMode Unlit base colour (material factor times the base colour map)."},{"t":"cinder.render.renderer.DebugMode.ao","s":"API symbol","h":"method","u":"/docs/api/render/#debugmode-ao","x":"ao() -> DebugMode The ambient occlusion term as greyscale."},{"t":"cinder.render.renderer.RenderSettings","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-rendersettings","x":"struct RenderSettings(ImplicitlyCopyable, Movable) Quality knobs, read at the start of every render (change them between frames). cascade_resolution, atlas_size and atlas_tile are read at construction only."},{"t":"cinder.render.renderer.RenderSettings.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#rendersettings-__init__","x":"__init__(out self)"},{"t":"cinder.render.renderer.Environment","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-environment","x":"struct Environment(ImplicitlyCopyable, Movable) Procedural sky by default; equirect (an RGBA16F 2D texture view) replaces it when set."},{"t":"cinder.render.renderer.Environment.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#environment-__init__","x":"__init__(out self)"},{"t":"cinder.render.renderer.RenderInstance","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-renderinstance","x":"struct RenderInstance(Copyable, Movable) One drawn object: mesh and material indices into the renderer's registries."},{"t":"cinder.render.renderer.RenderInstance.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#renderinstance-__init__","x":"__init__( out self, mesh: Int, material: Int, transform: Mat4, joint_offset: Int = -1, cast_shadows: Bool = True, )"},{"t":"cinder.render.renderer.RenderScene","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-renderscene","x":"struct RenderScene(Movable) Everything one render call draws; owned by the caller and only read by the renderer."},{"t":"cinder.render.renderer.RenderScene.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#renderscene-__init__","x":"__init__(out self, camera: Camera)"},{"t":"cinder.render.renderer.FrameStats","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-framestats","x":"struct FrameStats(ImplicitlyCopyable, Movable) Counters of the last render, in Renderer.stats."},{"t":"cinder.render.renderer.Renderer","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-renderer","x":"struct Renderer(Movable) The forward+ renderer; render draws a RenderScene into a target view. Owns the pipelines, persistent GPU buffers and textures, the mesh and material registries, the frame graph, the shader library and per-frame scratch (steady-state frames do not allocate). Holds raw pointer"},{"t":"cinder.render.renderer.Renderer.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], lib: NativePtr, settings: RenderSettings, target_format: WGPUTextureFormat, ) raises"},{"t":"cinder.render.renderer.Renderer.add_mesh","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-add_mesh","x":"add_mesh( mut self, label: String, data: MeshData, lods: Bool = True ) raises -> Int Uploads data and returns its index in meshes; raises if validation or upload fails. With lods and more than 64 triangles, up to four LODs are built with meshoptimizer; otherwise the mesh has one. Always appends (rem"},{"t":"cinder.render.renderer.Renderer.add_cooked_mesh","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-add_cooked_mesh","x":"add_cooked_mesh( mut self, label: String, cooked: CookedMesh ) raises -> Int Registers a cooked mesh (streams already in GPU layout). Reuses a removed slot if any."},{"t":"cinder.render.renderer.Renderer.replace_cooked_mesh","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-replace_cooked_mesh","x":"replace_cooked_mesh( mut self, index: Int, label: String, cooked: CookedMesh ) raises Hot reload: swaps the GPU buffers behind index; instances keep their mesh index."},{"t":"cinder.render.renderer.Renderer.remove_mesh","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-remove_mesh","x":"remove_mesh(mut self, index: Int) raises Frees the GPU buffers; the slot is reused by the next add_cooked_mesh. Instances that still reference it are the caller's error (rendering them raises on the invalid handle)."},{"t":"cinder.render.renderer.Renderer.add_material","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-add_material","x":"add_material(mut self, material: Material) raises -> Int Uploads material and returns its index in materials; raises on a GPU error. A copy of the parameters is kept in material_params; texture views stay the caller's."},{"t":"cinder.render.renderer.Renderer.replace_material","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-replace_material","x":"replace_material(mut self, index: Int, material: Material) raises Hot reload: uploads material into slot index (instances keep their material index) and frees the uniform buffer and bind group of the version it replaces. The new version is uploaded before anything is freed, so a failed upload raises"},{"t":"cinder.render.renderer.Renderer.remove_material","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-remove_material","x":"remove_material(mut self, index: Int) raises Frees the uniform buffer and bind group of slot index and leaves the slot empty (null handles); removing an empty slot does nothing. Indices stay stable: add_material always appends, so a removed slot is never handed out again (only replace_material on th"},{"t":"cinder.render.renderer.Renderer.material_is_live","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-material_is_live","x":"material_is_live(self, index: Int) -> Bool True when slot index exists and has not been removed."},{"t":"cinder.render.renderer.Renderer.mesh_lod_count","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-mesh_lod_count","x":"mesh_lod_count(self, mesh: Int) -> Int Number of LOD index ranges of mesh mesh; level 0 is full detail."},{"t":"cinder.render.renderer.Renderer.reload_shaders","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-reload_shaders","x":"reload_shaders(mut self) raises -> Int Hot reload: recompiles changed programs and rebuilds every pipeline on success. Pipelines keep their handles; a failed compile keeps the last good module and pipelines."},{"t":"cinder.render.renderer.Renderer.render","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-render","x":"render( mut self, scene: RenderScene, target: TextureViewHandle, width: Int, height: Int, ) raises Renders scene into target (format given at construction). Raises GfxError on device loss."},{"t":"cinder.render.renderer.Renderer.graph_dot","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-graph_dot","x":"graph_dot(mut self) -> String DOT text of the last frame's graph (built on demand: debug UI, docs)."},{"t":"cinder.render.renderer.Renderer.release","s":"API symbol","h":"method","u":"/docs/api/render/#renderer-release","x":"release(mut self) raises Frees the frame graph's pooled textures (everything else dies with Resources)."},{"t":"cinder.render.shader_library","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrendershader_library","x":"Shader programs assembled from WGSL files with #include \"file.wgsl\" lines, with hot reload. WGSL has no preprocessor, so includes are resolved here (relative to the shader directory, once per program, in order of first appearance). Error locations reported by naga refer to the assembled text; Shader"},{"t":"cinder.render.shader_library.AssembledShader","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-assembledshader","x":"struct AssembledShader(Copyable, Movable) WGSL text with every include spliced in, mapped back to its source files. chunks locate each assembled line in its file; files lists every file read (directory/name), in order of first inclusion."},{"t":"cinder.render.shader_library.AssembledShader.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#assembledshader-__init__","x":"__init__(out self)"},{"t":"cinder.render.shader_library.AssembledShader.locate","s":"API symbol","h":"method","u":"/docs/api/render/#assembledshader-locate","x":"locate(self, assembled_line: Int) -> Tuple[String, Int] File and file line for a line of the assembled text."},{"t":"cinder.render.shader_library.assemble","s":"API symbol","h":"def","u":"/docs/api/render/#def-assemble","x":"def assemble(directory: String, entry: String) raises -> AssembledShader Reads entry and splices every #include \"x\" (each file at most once) into one text."},{"t":"cinder.render.shader_library.ShaderLibrary","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-shaderlibrary","x":"struct ShaderLibrary(Movable) Loads directory/<entry>.wgsl programs with includes; check_for_changes hot reloads."},{"t":"cinder.render.shader_library.ShaderLibrary.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#shaderlibrary-__init__","x":"__init__( out self, gfx: Pointer[Gfx, MutUntrackedOrigin], res: Pointer[Resources, MutUntrackedOrigin], shaders: Pointer[ShaderManager, MutUntrackedOrigin], directory: String, )"},{"t":"cinder.render.shader_library.ShaderLibrary.compile","s":"API symbol","h":"method","u":"/docs/api/render/#shaderlibrary-compile","x":"compile(mut self, entry: String) raises -> WGPUShaderModule Assembles and compiles without registering (used for one-off modules and by load)."},{"t":"cinder.render.shader_library.ShaderLibrary.load","s":"API symbol","h":"method","u":"/docs/api/render/#shaderlibrary-load","x":"load(mut self, entry: String) raises -> ShaderHandle Assembles, compiles and registers entry (a file in directory) for hot reload. Returns the handle Resources holds the module under. Raises on a missing include or a compile error, with locations mapped to the source files."},{"t":"cinder.render.shader_library.ShaderLibrary.check_for_changes","s":"API symbol","h":"method","u":"/docs/api/render/#shaderlibrary-check_for_changes","x":"check_for_changes(mut self) -> Int Recompiles programs whose files changed. Returns how many modules were replaced; failures keep the last good module and add an error diagnostic naming file:line:column."},{"t":"cinder.render.shader_library.ShaderLibrary.take_diagnostics","s":"API symbol","h":"method","u":"/docs/api/render/#shaderlibrary-take_diagnostics","x":"take_diagnostics(mut self) -> List[LogRecord] Returns and clears the reload records from check_for_changes (info or error)."},{"t":"cinder.render.shadows","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrendershadows","x":"Cascaded shadow maps for the sun and an atlas for spot and point lights (CPU-side maths). Cascade fitting: each cascade covers a slice of the camera frustum (practical split scheme, lambda 0.75). The orthographic projection is fitted to the slice's bounding sphere so it has a constant size while the"},{"t":"cinder.render.shadows.Cascade","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-cascade","x":"struct Cascade(ImplicitlyCopyable, Movable) One fitted sun cascade: an orthographic reverse-Z light view-projection (world to clip)."},{"t":"cinder.render.shadows.cascade_splits_into","s":"API symbol","h":"def","u":"/docs/api/render/#def-cascade_splits_into","x":"def cascade_splits_into( mut out: Array[Float32, CASCADE_COUNT], near: Float32, shadow_distance: Float32, lambda_: Float32 = 0.75, ) End distances of the four cascades (practical split: blend of log and uniform) into a stack array (no allocation)."},{"t":"cinder.render.shadows.cascade_splits","s":"API symbol","h":"def","u":"/docs/api/render/#def-cascade_splits","x":"def cascade_splits( near: Float32, shadow_distance: Float32, lambda_: Float32 = 0.75 ) -> List[Float32] End distances of the four cascades (practical split: blend of log and uniform)."},{"t":"cinder.render.shadows.fit_cascade","s":"API symbol","h":"def","u":"/docs/api/render/#def-fit_cascade","x":"def fit_cascade( camera: Camera, light_dir: Vec3, z0: Float32, z1: Float32, resolution: Int ) -> Cascade Orthographic light view-projection for one slice with texel snapping. light_dir is the direction light travels. Depth range is the sphere diameter plus a generous margin behind the slice so caste"},{"t":"cinder.render.shadows.fit_cascades_into","s":"API symbol","h":"def","u":"/docs/api/render/#def-fit_cascades_into","x":"def fit_cascades_into( mut out: List[Cascade], camera: Camera, light_dir: Vec3, shadow_distance: Float32, resolution: Int, ) fit_cascades into a list supplied by the caller (cleared first, capacity kept)."},{"t":"cinder.render.shadows.fit_cascades","s":"API symbol","h":"def","u":"/docs/api/render/#def-fit_cascades","x":"def fit_cascades( camera: Camera, light_dir: Vec3, shadow_distance: Float32, resolution: Int ) -> List[Cascade] fit_cascades_into into a new list (allocates); per-frame code uses the _into variant."},{"t":"cinder.render.shadows.ShadowView","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-shadowview","x":"struct ShadowView(ImplicitlyCopyable, Movable) One atlas tile: a light-space view-projection and its uv rectangle (matches common.wgsl)."},{"t":"cinder.render.shadows.ShadowView.pack","s":"API symbol","h":"method","u":"/docs/api/render/#shadowview-pack","x":"pack(self, mut w: ByteWriter) Appends the 96-byte GPU form (ShadowView in common.wgsl). view_proj, then the uv rectangle (origin x, origin y, size, size), then the texel size in uv, near, far and a zero pad."},{"t":"cinder.render.shadows.ShadowAtlas","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-shadowatlas","x":"struct ShadowAtlas(Movable) Fixed grid of square tiles in one depth texture. Spot lights take one tile, point lights six."},{"t":"cinder.render.shadows.ShadowAtlas.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#shadowatlas-__init__","x":"__init__(out self, size: Int = 4096, tile_size: Int = 512)"},{"t":"cinder.render.shadows.ShadowAtlas.capacity","s":"API symbol","h":"method","u":"/docs/api/render/#shadowatlas-capacity","x":"capacity(self) -> Int Number of tiles in the atlas (tiles_per_row squared)."},{"t":"cinder.render.shadows.ShadowAtlas.reset","s":"API symbol","h":"method","u":"/docs/api/render/#shadowatlas-reset","x":"reset(mut self) Frees every tile and clears views (capacity kept); called at the start of each frame."},{"t":"cinder.render.shadows.ShadowAtlas.tile_pixel_origin","s":"API symbol","h":"method","u":"/docs/api/render/#shadowatlas-tile_pixel_origin","x":"tile_pixel_origin(self, view_index: Int) -> Tuple[Int, Int] Top-left pixel of the tile used by shadow view view_index (views fill tiles in order)."},{"t":"cinder.render.shadows.ShadowAtlas.allocate","s":"API symbol","h":"method","u":"/docs/api/render/#shadowatlas-allocate","x":"allocate(mut self, light: Light) -> Int Adds the light's shadow views; returns the first view index or -1 when the atlas is full."},{"t":"cinder.render.shadows.ShadowAtlas.pack_views_into","s":"API symbol","h":"method","u":"/docs/api/render/#shadowatlas-pack_views_into","x":"pack_views_into(self, mut w: ByteWriter) Appends the GPU shadow-view array (at least one zeroed entry) to w."},{"t":"cinder.render.shadows.ShadowAtlas.pack_views","s":"API symbol","h":"method","u":"/docs/api/render/#shadowatlas-pack_views","x":"pack_views(self) -> List[UInt8] pack_views_into into a new byte list (allocates; at least one zeroed entry)."},{"t":"cinder.render.textures","s":"API symbol","h":"module","u":"/docs/api/render/#cinderrendertextures","x":"GPU textures from CPU images: CPU box-filter mip chains, sRGB or linear formats, defaults."},{"t":"cinder.render.textures.GpuTexture","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-gputexture","x":"struct GpuTexture(ImplicitlyCopyable, Movable) An uploaded 2D texture and a view of all its mips; width and height are mip 0 in pixels. The texture and view are owned by whoever uploaded them and freed through Resources."},{"t":"cinder.render.textures.mip_count","s":"API symbol","h":"def","u":"/docs/api/render/#def-mip_count","x":"def mip_count(width: Int, height: Int) -> Int Levels in a full mip chain down to 1x1 (floor(log2(max(width, height))) + 1)."},{"t":"cinder.render.textures.downsample","s":"API symbol","h":"def","u":"/docs/api/render/#def-downsample","x":"def downsample(img: Image) -> Image 2x2 box filter (edge pixels of odd sizes are clamped), in the image's own encoding."},{"t":"cinder.render.textures.upload_image","s":"API symbol","h":"def","u":"/docs/api/render/#def-upload_image","x":"def upload_image( mut res: Resources, label: String, img: Image, srgb: Bool, mips: Bool = True ) raises -> GpuTexture Uploads an RGBA8 image as a 2D texture with a CPU box-filtered mip chain. srgb selects RGBA8UnormSrgb (colour data) over RGBA8Unorm (linear data); mips = False uploads level 0 only. "},{"t":"cinder.render.textures.solid_image","s":"API symbol","h":"def","u":"/docs/api/render/#def-solid_image","x":"def solid_image(r: UInt8, g: UInt8, b: UInt8, a: UInt8) -> Image 1x1 RGBA8 image of one colour (used for the default textures)."},{"t":"cinder.render.textures.DefaultTextures","s":"API symbol","h":"struct","u":"/docs/api/render/#struct-defaulttextures","x":"struct DefaultTextures(ImplicitlyCopyable, Movable) 1x1 fallbacks: white (base colour, occlusion, metallic-roughness), flat normal, black emissive."},{"t":"cinder.render.textures.DefaultTextures.__init__","s":"API symbol","h":"method","u":"/docs/api/render/#defaulttextures-__init__","x":"__init__(out self, mut res: Resources) raises"},{"t":"cinder.scene","s":"API symbol","h":"module","u":"/docs/api/scene/#cinderscene-1","x":"Scene files: versioned text and binary formats with migrations, over the ECS world."},{"t":"cinder.scene.format","s":"API symbol","h":"module","u":"/docs/api/scene/#cindersceneformat","x":"Scene serialisation: a human-diffable text format and a binary format, versioned, with migrations. Both formats carry the same intermediate representation (SceneDoc: entities with component records of typed fields), so a document can be read in either format, migrated version by version, and written"},{"t":"cinder.scene.format.Field","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-field","x":"struct Field(Copyable, Movable) One named, typed value of a component record. kind is a KIND_* constant and selects which of number, integer, text or floats holds the value; the others keep their zero defaults."},{"t":"cinder.scene.format.Field.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#field-__init__","x":"__init__(out self, name: String, kind: Int)"},{"t":"cinder.scene.format.ComponentRecord","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-componentrecord","x":"struct ComponentRecord(Copyable, Movable) A component as stored in a scene: its scene name and its fields in write order."},{"t":"cinder.scene.format.ComponentRecord.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#componentrecord-__init__","x":"__init__(out self, name: String)"},{"t":"cinder.scene.format.ComponentRecord.find","s":"API symbol","h":"method","u":"/docs/api/scene/#componentrecord-find","x":"find(self, name: String) -> Int Index of the field called name, or -1."},{"t":"cinder.scene.format.ComponentRecord.rename_field","s":"API symbol","h":"method","u":"/docs/api/scene/#componentrecord-rename_field","x":"rename_field(mut self, old: String, new: String) -> Bool Renames field old to new (for migrations); returns False when there is no field old. An existing field called new is not checked for."},{"t":"cinder.scene.format.EntityRecord","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-entityrecord","x":"struct EntityRecord(Copyable, Movable) One entity of a scene: the records of the components it carries."},{"t":"cinder.scene.format.EntityRecord.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#entityrecord-__init__","x":"__init__(out self)"},{"t":"cinder.scene.format.EntityRecord.find","s":"API symbol","h":"method","u":"/docs/api/scene/#entityrecord-find","x":"find(self, name: String) -> Int Index of the component record called name, or -1."},{"t":"cinder.scene.format.SceneDoc","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-scenedoc","x":"struct SceneDoc(Movable) The format-independent scene: a version and the entities in file order. Entity fields hold indices into entities. Produced by SceneRegistry.to_doc, read_text and read_binary; consumed by SceneRegistry.migrate, SceneRegistry.instantiate, write_text and write_binary."},{"t":"cinder.scene.format.SceneDoc.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#scenedoc-__init__","x":"__init__(out self, version: Int = SCENE_VERSION)"},{"t":"cinder.scene.format.FieldWriter","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-fieldwriter","x":"struct FieldWriter(Movable) Collects a component's fields. Entities are written as file-local indices."},{"t":"cinder.scene.format.FieldWriter.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-__init__","x":"__init__( out self, name: String, index_of: Pointer[List[Int], MutUntrackedOrigin] )"},{"t":"cinder.scene.format.FieldWriter.float","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-float","x":"float(mut self, name: String, v: Float32) Appends a KIND_FLOAT field."},{"t":"cinder.scene.format.FieldWriter.int","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-int","x":"int(mut self, name: String, v: Int) Appends a KIND_INT field."},{"t":"cinder.scene.format.FieldWriter.bool","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-bool","x":"bool(mut self, name: String, v: Bool) Appends a KIND_BOOL field (stored as 0 or 1)."},{"t":"cinder.scene.format.FieldWriter.string","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-string","x":"string(mut self, name: String, v: String) Appends a KIND_STRING field."},{"t":"cinder.scene.format.FieldWriter.entity","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-entity","x":"entity(mut self, name: String, e: Entity) Appends a KIND_ENTITY field with e's file-local index: -1 when e is null, beyond the index table or not part of the snapshot. Only the slot index is looked up; the generation is not checked."},{"t":"cinder.scene.format.FieldWriter.floats","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-floats","x":"floats(mut self, name: String, values: List[Float32]) Appends a KIND_FLOATS field holding a copy of values."},{"t":"cinder.scene.format.FieldWriter.vec3","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-vec3","x":"vec3(mut self, name: String, v: Vec3) Appends v as a three-float KIND_FLOATS field."},{"t":"cinder.scene.format.FieldWriter.mat4","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldwriter-mat4","x":"mat4(mut self, name: String, m: Mat4) Appends m as a 16-float KIND_FLOATS field in column-major order."},{"t":"cinder.scene.format.FieldReader","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-fieldreader","x":"struct FieldReader(Movable) Reads a component's fields by name; missing fields take the given default."},{"t":"cinder.scene.format.FieldReader.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-__init__","x":"__init__( out self, var record: ComponentRecord, entities: Pointer[List[Entity], MutUntrackedOrigin], )"},{"t":"cinder.scene.format.FieldReader.float","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-float","x":"float(mut self, name: String, default: Float32) -> Float32 Field name as a float, or default when absent. An integer field is converted; any other kind sets error and returns default."},{"t":"cinder.scene.format.FieldReader.int","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-int","x":"int(mut self, name: String, default: Int) -> Int Field name as an integer, or default when absent; another kind sets error and returns default."},{"t":"cinder.scene.format.FieldReader.bool","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-bool","x":"bool(mut self, name: String, default: Bool) -> Bool Field name as a boolean, or default when absent; another kind sets error and returns default."},{"t":"cinder.scene.format.FieldReader.string","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-string","x":"string(mut self, name: String, default: String) -> String Field name as a string, or default when absent; another kind sets error and returns default."},{"t":"cinder.scene.format.FieldReader.entity","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-entity","x":"entity(mut self, name: String) -> Entity Field name remapped to the spawned entity. Returns the null entity when the field is absent, not an entity field, -1 or out of range; never sets error."},{"t":"cinder.scene.format.FieldReader.floats","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-floats","x":"floats(mut self, name: String, count: Int) -> List[Float32] The count floats of field name. Returns count zeros when the field is absent, and also when it is not a KIND_FLOATS field of exactly count values, in which case error is set."},{"t":"cinder.scene.format.FieldReader.vec3","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-vec3","x":"vec3(mut self, name: String, default: Vec3) -> Vec3 Field name as a Vec3, or default when absent; a field of another shape sets error and yields zero."},{"t":"cinder.scene.format.FieldReader.mat4","s":"API symbol","h":"method","u":"/docs/api/scene/#fieldreader-mat4","x":"mat4(mut self, name: String) -> Mat4 Field name as a column-major Mat4, or identity when absent; a field of another shape sets error and yields an all-zero matrix."},{"t":"cinder.scene.format.SceneComponent","s":"API symbol","h":"trait","u":"/docs/api/scene/#trait-scenecomponent","x":"trait SceneComponent(Component) A component that can be saved: name, field writer and reader."},{"t":"cinder.scene.format.SceneComponent.scene_name","s":"API symbol","h":"method","u":"/docs/api/scene/#scenecomponent-scene_name","x":"scene_name() -> String The component's name in scene files, matched against component records on load."},{"t":"cinder.scene.format.SceneComponent.write_scene","s":"API symbol","h":"method","u":"/docs/api/scene/#scenecomponent-write_scene","x":"write_scene(self, mut w: FieldWriter) Writes the component's fields into w."},{"t":"cinder.scene.format.SceneComponent.read_scene","s":"API symbol","h":"method","u":"/docs/api/scene/#scenecomponent-read_scene","x":"read_scene(mut r: FieldReader) -> Self Builds the component from r, with defaults for missing fields. When r.error is set afterwards (the typed getters set it on a kind mismatch) the component is not added and SceneRegistry.instantiate raises."},{"t":"cinder.scene.format.SceneRegistry","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-sceneregistry","x":"struct SceneRegistry(Movable) Known component types and version migrations."},{"t":"cinder.scene.format.SceneRegistry.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#sceneregistry-__init__","x":"__init__(out self)"},{"t":"cinder.scene.format.SceneRegistry.register","s":"API symbol","h":"method","u":"/docs/api/scene/#sceneregistry-register","x":"register[T: SceneComponent](mut self) Makes T saveable under T.scene_name(). Duplicate names are not rejected; lookups on load use the first registration."},{"t":"cinder.scene.format.SceneRegistry.add_migration","s":"API symbol","h":"method","u":"/docs/api/scene/#sceneregistry-add_migration","x":"add_migration(mut self, from_version: Int, migration: MigrationFn) Registers migration to upgrade documents at from_version to from_version + 1."},{"t":"cinder.scene.format.SceneRegistry.to_doc","s":"API symbol","h":"method","u":"/docs/api/scene/#sceneregistry-to_doc","x":"to_doc(self, mut world: World) raises -> SceneDoc Snapshots every live entity (in archetype order) with its registered components."},{"t":"cinder.scene.format.SceneRegistry.migrate","s":"API symbol","h":"method","u":"/docs/api/scene/#sceneregistry-migrate","x":"migrate(self, mut doc: SceneDoc) raises Applies registered migrations until the document is at SCENE_VERSION."},{"t":"cinder.scene.format.SceneRegistry.instantiate","s":"API symbol","h":"method","u":"/docs/api/scene/#sceneregistry-instantiate","x":"instantiate( self, mut world: World, doc: SceneDoc ) raises -> List[Entity] Spawns the document's entities (after migration) and returns them in file order."},{"t":"cinder.scene.format.format_float","s":"API symbol","h":"def","u":"/docs/api/scene/#def-format_float","x":"def format_float(v: Float32) -> String Shortest decimal that round-trips, else the IEEE bits as 0x...."},{"t":"cinder.scene.format.parse_float","s":"API symbol","h":"def","u":"/docs/api/scene/#def-parse_float","x":"def parse_float(text: String) raises -> Float32 Parses a float written by format_float: a decimal, or 0x followed by up to 8 hex digits of IEEE bits. Raises on malformed text."},{"t":"cinder.scene.format.bitcast_u32_to_f32","s":"API symbol","h":"def","u":"/docs/api/scene/#def-bitcast_u32_to_f32","x":"def bitcast_u32_to_f32(bits: UInt32) -> Float32 Reinterprets IEEE-754 single-precision bits as a Float32."},{"t":"cinder.scene.format.write_text","s":"API symbol","h":"def","u":"/docs/api/scene/#def-write_text","x":"def write_text(doc: SceneDoc) -> String Serialises doc in the text format described in the module docstring: a cinder-scene N header, one entity i line per entity and one indented Name key=value ... line per component."},{"t":"cinder.scene.format.read_text","s":"API symbol","h":"def","u":"/docs/api/scene/#def-read_text","x":"def read_text(text: String) raises -> SceneDoc Parses the text format into a SceneDoc at the file's version (not migrated). Blank lines and lines starting with # are skipped. Raises on a missing header, entities out of order, a component line before any entity, an unterminated quote or bracket, a to"},{"t":"cinder.scene.format.write_binary","s":"API symbol","h":"def","u":"/docs/api/scene/#def-write_binary","x":"def write_binary(doc: SceneDoc) -> List[UInt8] Serialises doc in the binary format: MAGIC, then little-endian u32 version and entity count; per entity a component count, per component its name and field count, per field its name, a u8 kind and the value. Strings are a u32 byte length plus bytes; int"},{"t":"cinder.scene.format.read_binary","s":"API symbol","h":"def","u":"/docs/api/scene/#def-read_binary","x":"def read_binary(var data: List[UInt8]) raises -> SceneDoc Parses the binary format written by write_binary into a SceneDoc at the file's version (not migrated). Raises on a bad magic, truncated data, an unknown field kind, or implausible counts (over 10 million entities, 10 000 components per entity"},{"t":"cinder.scene.gltf_spawn","s":"API symbol","h":"module","u":"/docs/api/scene/#cinderscenegltf_spawn","x":"Spawning a glTF asset into the ECS: one entity per node (transform hierarchy) and one child entity per mesh primitive (MeshRenderer), plus static collision triangles for level geometry."},{"t":"cinder.scene.gltf_spawn.SpawnedGltf","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-spawnedgltf","x":"struct SpawnedGltf(Movable) Result of spawn_gltf: the root entity, the entity of each glTF node and of each mesh primitive, and the renderer ids of the upload. The entities stay in the world after this value is dropped."},{"t":"cinder.scene.gltf_spawn.SpawnedGltf.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#spawnedgltf-__init__","x":"__init__(out self, root: Entity, var uploaded: UploadedGltf)"},{"t":"cinder.scene.gltf_spawn.SpawnedGltf.entity_of_node","s":"API symbol","h":"method","u":"/docs/api/scene/#spawnedgltf-entity_of_node","x":"entity_of_node(self, node: Int) -> Entity Entity spawned for glTF node node; the null entity for an out-of-range index or a node outside the scene graph."},{"t":"cinder.scene.gltf_spawn.spawn_gltf","s":"API symbol","h":"def","u":"/docs/api/scene/#def-spawn_gltf","x":"def spawn_gltf( mut renderer: Renderer, mut res: Resources, asset: GltfAsset, mut world: World, root_transform: Mat4 = Mat4.identity(), lods: Bool = True, cast_shadows: Bool = True, skinned_meshes: Bool = True, ) raises -> SpawnedGltf Uploads the asset and spawns its node hierarchy. Skinned mesh nod"},{"t":"cinder.scene.gltf_spawn.collision_triangles","s":"API symbol","h":"def","u":"/docs/api/scene/#def-collision_triangles","x":"def collision_triangles( asset: GltfAsset, root_transform: Mat4 = Mat4.identity() ) -> Tuple[List[Vec3], List[UInt32]] World-space triangles of every non-skinned mesh node, for a static mesh collider."},{"t":"cinder.scene.render_sync","s":"API symbol","h":"module","u":"/docs/api/scene/#cinderscenerender_sync","x":"Bridge from the ECS world to a RenderScene: render components and their collection. Components: MeshRenderer (renderer mesh and material indices, shadow flag), LightSource, CameraComponent. build_render_scene reads WorldTransform (propagated beforehand) and fills the renderer's scene lists in one pa"},{"t":"cinder.scene.render_sync.MeshRenderer","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-meshrenderer","x":"struct MeshRenderer(Component) Draws renderer mesh mesh with material material at the entity's WorldTransform; cast_shadows is passed to the render instance."},{"t":"cinder.scene.render_sync.MeshRenderer.component_id","s":"API symbol","h":"method","u":"/docs/api/scene/#meshrenderer-component_id","x":"component_id() -> Int"},{"t":"cinder.scene.render_sync.LightSource","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-lightsource","x":"struct LightSource(Component) A light positioned by the entity's world transform (direction = transform's -Z)."},{"t":"cinder.scene.render_sync.LightSource.component_id","s":"API symbol","h":"method","u":"/docs/api/scene/#lightsource-component_id","x":"component_id() -> Int"},{"t":"cinder.scene.render_sync.CameraComponent","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-cameracomponent","x":"struct CameraComponent(Component) A camera at the entity's world transform, looking down its -Z with its +Y up. RenderBridge.build uses the first active one it finds, keeping the scene camera's current aspect ratio."},{"t":"cinder.scene.render_sync.CameraComponent.component_id","s":"API symbol","h":"method","u":"/docs/api/scene/#cameracomponent-component_id","x":"component_id() -> Int"},{"t":"cinder.scene.render_sync.JointPalette","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-jointpalette","x":"struct JointPalette(Component) Skinning matrices and morph weights for an entity's mesh, written by the animation system each frame and consumed by build_render_scene."},{"t":"cinder.scene.render_sync.JointPalette.component_id","s":"API symbol","h":"method","u":"/docs/api/scene/#jointpalette-component_id","x":"component_id() -> Int"},{"t":"cinder.scene.render_sync.RenderBridge","s":"API symbol","h":"struct","u":"/docs/api/scene/#struct-renderbridge","x":"struct RenderBridge(Movable) ECS -> RenderScene with persistent query specs and chunk scratch (no steady-state allocations). build_render_scene is a convenience wrapper around a temporary instance."},{"t":"cinder.scene.render_sync.RenderBridge.__init__","s":"API symbol","h":"method","u":"/docs/api/scene/#renderbridge-__init__","x":"__init__(out self)"},{"t":"cinder.scene.render_sync.RenderBridge.build","s":"API symbol","h":"method","u":"/docs/api/scene/#renderbridge-build","x":"build(mut self, world: World, mut scene: RenderScene) raises -> Int Fills scene.instances, scene.joints and scene.lights from the world and sets the camera from the first active CameraComponent. Returns the number of instances."},{"t":"cinder.scene.render_sync.build_render_scene","s":"API symbol","h":"def","u":"/docs/api/scene/#def-build_render_scene","x":"def build_render_scene(world: World, mut scene: RenderScene) raises -> Int One-off bridge build (tests); the App keeps a RenderBridge."},{"t":"cinder.tools","s":"API symbol","h":"module","u":"/docs/api/tools/#cindertools-1","x":"Developer tools: the ImGui debug overlay (frame graph, inspector, assets, render graph, log)."},{"t":"cinder.tools.cstrings","s":"API symbol","h":"module","u":"/docs/api/tools/#cindertoolscstrings","x":"Frame-scoped C strings for immediate-mode UI calls. ImGui keeps label pointers only until the frame is rendered, and Mojo Strings move when a list grows (inline small-string storage), so labels are copied into List[UInt8] buffers whose heap data stays put. Call clear once per frame after rendering."},{"t":"cinder.tools.cstrings.CStrings","s":"API symbol","h":"struct","u":"/docs/api/tools/#struct-cstrings","x":"struct CStrings(Movable) Owns NUL-terminated byte copies of strings handed to C; pointers from c stay valid until clear."},{"t":"cinder.tools.cstrings.CStrings.__init__","s":"API symbol","h":"method","u":"/docs/api/tools/#cstrings-__init__","x":"__init__(out self)"},{"t":"cinder.tools.cstrings.CStrings.clear","s":"API symbol","h":"method","u":"/docs/api/tools/#cstrings-clear","x":"clear(mut self) Frees every buffer; pointers returned by c become dangling."},{"t":"cinder.tools.cstrings.CStrings.__len__","s":"API symbol","h":"method","u":"/docs/api/tools/#cstrings-__len__","x":"__len__(self) -> Int"},{"t":"cinder.tools.cstrings.CStrings.c","s":"API symbol","h":"method","u":"/docs/api/tools/#cstrings-c","x":"c( mut self, text: String ) -> Optional[Pointer[c_char, MutUntrackedOrigin]] A NUL-terminated copy of text, valid until clear."},{"t":"cinder.tools.debug_ui","s":"API symbol","h":"module","u":"/docs/api/tools/#cindertoolsdebug_ui","x":"The ImGui debug overlay: frame-time graph and render stats, entity inspector (view and edit components through the scene registry), asset browser, render graph view and log console. ImGui runs through the shim's SDL3 + WebGPU backends (cn_imgui_*); widgets are the generated dcimgui bindings. The ove"},{"t":"cinder.tools.debug_ui.DebugUi","s":"API symbol","h":"struct","u":"/docs/api/tools/#struct-debugui","x":"struct DebugUi(Movable) The ImGui debug overlay. Construction initialises the shim's ImGui backend for the window and device (raising when that fails); destruction shuts it down. App creates it for windowed apps with debug_ui set and declares it first, so it is destroyed before the window and GPU de"},{"t":"cinder.tools.debug_ui.DebugUi.__init__","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-__init__","x":"__init__( out self, lib: NativePtr, window: Optional[Pointer[SDL_Window, MutUntrackedOrigin]], device: WGPUDevice, format: WGPUTextureFormat, ) raises"},{"t":"cinder.tools.debug_ui.DebugUi.begin_frame","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-begin_frame","x":"begin_frame(mut self, dt_seconds: Float64) Records dt_seconds in the frame-time ring, frees the previous frame's labels and starts a new ImGui frame."},{"t":"cinder.tools.debug_ui.DebugUi.render","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-render","x":"render(self, pass_: RenderPass) Draws the accumulated ImGui data into an open render pass."},{"t":"cinder.tools.debug_ui.DebugUi.wants_mouse","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-wants_mouse","x":"wants_mouse(self) -> Bool True when ImGui wants the mouse (App then keeps mouse events from the input map); False when ImGui has no IO."},{"t":"cinder.tools.debug_ui.DebugUi.wants_keyboard","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-wants_keyboard","x":"wants_keyboard(self) -> Bool True when ImGui wants the keyboard (App then keeps key events from the input map); False when ImGui has no IO."},{"t":"cinder.tools.debug_ui.DebugUi.average_frame_ms","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-average_frame_ms","x":"average_frame_ms(self) -> Float32 Mean frame time in ms over the recorded frames (at most HISTORY); 0 before the first frame."},{"t":"cinder.tools.debug_ui.DebugUi.draw","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-draw","x":"draw( mut self, mut world: World, registry: SceneRegistry, renderer: Renderer, assets: Optional[Pointer[AssetServer, MutUntrackedOrigin]], logs: List[LogRecord], physics_live: Int, audio_live: Int, ) raises Builds the overlay for this frame (between begin_frame and render)."},{"t":"cinder.tools.debug_ui.DebugUi.select_tab","s":"API symbol","h":"method","u":"/docs/api/tools/#debugui-select_tab","x":"select_tab(mut self, index: Int) Makes tab index the active one on the next draw."}]