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

3.9 KiB
Raw Blame History

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 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-41e-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 is empty when any min component exceeds the corresponding max; Aabb::EMPTY is the identity for union.
  • A Ray always stores a normalized direction, so its parameter t is a true distance.
  • A 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 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.