Files
Oxide/docs/conventions.md
T
Homer Simpson 9eead719b0 Import Oxide engine (Stages 0–10) under MIT license
Full project snapshot migrated to new Gitea remote without history:
engine, editor, physics, script, examples, tests, docs, and assets.
Relicensed from GPLv3 to MIT and updated repo URLs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 20:41:02 +02:00

91 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Conventions
Cross-cutting conventions every Oxide system follows. These are decided once,
here, so individual systems don't each invent their own.
## Coordinate system
Oxide uses a **right-handed** coordinate system, consistent with `glam`'s
`*_rh` matrix constructors and the glTF asset format the engine will load.
In a default (identity) orientation:
| Axis | Direction | Local accessor on `Transform` |
|------|-----------|-------------------------------|
| `+X` | right | `Transform::right()` |
| `+Y` | up | `Transform::up()` |
| `-Z` | forward (the direction a camera/object looks) | `Transform::forward()` |
So **forward is `-Z`**. This matches the convention used by glTF, OpenGL, and
`glam`'s view-matrix helpers, which keeps asset import and camera math
consistent.
`Transform::looking_at(eye, target, up)` produces an orientation whose
`forward()` points from `eye` toward `target`.
## Rotations
- Rotations are stored as **unit quaternions** (`glam::Quat`), not Euler angles
or matrices, to avoid gimbal lock and accumulate cleanly under composition.
- Euler-angle helpers (`Quat::from_euler`) are available for authoring, but the
canonical stored form is always a quaternion.
- Angles are in **radians**. Convert from degrees explicitly at the boundary
(`90_f32.to_radians()`).
## Transform composition
- Transforms compose **parent-first**: `parent.mul_transform(&child)` yields the
child resolved in the parent's space, matching `parent_matrix * child_matrix`.
- The effective matrix order is `T * R * S` — scale is applied first, then
rotation, then translation.
- Composition is **exact** for uniform scale. With non-uniform scale plus
rotation the true product is not representable as a single translation /
rotation / scale triple, so the result is the closest TRS approximation
(re-decomposed from the matrix). Prefer uniform scale in deep hierarchies.
See [math.md](math.md#transform) for details.
## Units
- **Length:** meters. Physics (`rapier3d`, Stage 6) tunes its solver for
meter-scale geometry, so the whole engine adopts meters to avoid conversions.
- **Time:** seconds (`f32` for per-frame deltas; a fixed timestep drives physics
from Stage 6).
- **Angles:** radians (see above).
- **Mass:** kilograms (Stage 6+).
## Numeric type
- The engine is **`f32`-first**. `glam`'s `f32` types are the default throughout;
`f64` is used only where a specific algorithm demands it.
- Comparisons use explicit epsilons rather than `==` on floats. Helpers and tests
use a small tolerance (commonly `1e-4``1e-6`) appropriate to the operation.
## Color and color space
- `Color` stores **linear** RGBA as `f32`. Lighting and blending math is correct
only in linear space, so that is the engine's working space.
- Values are nominally in `[0, 1]` but are **not clamped** — values above `1.0`
represent HDR / emissive intensity.
- Conversions to and from 8-bit **sRGB** (the space of color pickers, image
files, and `#RRGGBB` hex) are explicit: `Color::from_srgb_u8`,
`Color::from_hex`, `Color::to_srgb_u8`. Never treat raw 8-bit values as linear.
## Geometry primitives
- An [`Aabb`](math.md#aabb) is *empty* when any `min` component exceeds the
corresponding `max`; `Aabb::EMPTY` is the identity for `union`.
- A [`Ray`](math.md#ray) always stores a **normalized** direction, so its
parameter `t` is a true distance.
- A [`Plane`](math.md#plane) is stored in **Hessian normal form** (`normal·p + d
= 0`) with a unit normal; the positive half-space is the side the normal points
toward.
- A [`Frustum`](math.md#frustum) stores six planes with **inward-facing**
normals; a point is inside when it is in the positive half-space of all six.
## Determinism
Procedural systems (Stage 11+) must be **deterministic**: the same seed always
produces the same output. Tests that need randomness use a small, explicit,
seeded PRNG rather than a system RNG, so failures are reproducible.