Skip to content

Crate Refactoring, Scene Graph improvements, added deferred lighting, removed old preprocessor effects/shaders (added some core new ones), and more. - #90

Merged
trentpolack merged 11 commits into
developfrom
feature/rust-refactor-reorg-as-crates
Oct 20, 2026
Merged

trentpolack merged 11 commits into
developfrom
feature/rust-refactor-reorg-as-crates

Conversation

@trentpolack

Copy link
Copy Markdown
Owner

This crate reorganization grew in scope a bit but refactoring the engine into crates proved to be a useful time to also revisit functionality and ensure holistic architectural changes were building towards a more coherent core.

Lighting and Sky

  • Breaking: ambient light is dynamic. A lighting probe (an irradiance cube and a GGX-prefiltered specular cube) lights every surface, and by default (IndirectLighting::CapturedSky) the renderer fills it from the live sky whenever the sun, moon, skylight, wind, or sky settings change, so ambient light and reflections follow the time of day, scattering, and clouds instead of a fixed environment map. IndirectLighting::Skylight keeps the flat two-color skylight, and IndirectLighting::Image lights a frame with a prepared environment image. FrameInputs::indirect_lighting replaces image_lighting; SkyCaptureOptions (RendererOptions::sky_capture and Renderer::set_sky_capture) sets the probe size, samples, and refresh interval, and RendererDiagnostics::sky_captures counts captures. See ambient light.
  • Environment images and the sky share one GPU filtering path, and diffuse light is now a cosine convolution on the GPU in place of CPU spherical harmonics, so constant environments light every orientation evenly. The lit model and terrain read the probe's diffuse light like standard surfaces.
  • Breaking: shading models call ambientDiffuse, ambientSpecular, and environmentBrdf (in lighting-probe.wgsl) in place of ambientIrradiance, environmentDiffuse, and environmentSpecular; registration reports models that still use the old names.
  • Breaking: EnvironmentLighting is a named record (environment_seconds, sun, moon, key_light, skylight, wind_velocity, and sky: SkyOptions) in place of packed arrays, and SkyOptions keeps only settings the sky renders: atmosphere (rayleigh, mie, and horizon_warmth, formerly ozone), clouds (coverage and density), and sun_halo (intensity and cloud_occlusion). Removed the fog density, cloud altitude and thickness, light-shaft decay and exposure, and ambient occlusion settings, which nothing rendered; the volumetric fog scene effect is the engine's fog.
  • Breaking: removed ACES 2.0 tone mapping (ToneMapper::Aces) and its OpenColorIO notice; filmic, AgX, and none remain.
  • Fixed accepted filmic settings producing non-finite values, small constant environments gaining directional diffuse light, orthographic cameras using perspective rays for fog and the sky, lights near tiny near planes dropping out of light clusters, r.ScreenPercentage losing fractional values, and profiling reporting an older frame than the newest finished one.
  • Bloom thresholds compare the brightest color channel, so saturated highlights bloom as brightly as white ones; the threshold contract and migration note are in postprocessing.
  • Motion blur resolves each pixel's blur once in a velocity pass, takes separable tile maxima, skips depth weighting where nearby motion is uniform, and adapts its sample count to the blur length.

Meshes, Materials, and Animation

  • Breaking: the standard shading model (StandardSurface: base color, metallic, roughness, F0 and F90, emissive, occlusion strength, and normal scale) replaces layered shading, with base color, normal, emissive, and packed occlusion-roughness-metallic maps (StandardMaps); coat, sheen, haze, and thin film were removed until a game needs them as their own models. FORM palettes and FORM LAB use the packed map.
  • Breaking: Mesh carries optional channels: tangents with handedness, any number of texture coordinate sets (uv_sets), joint influences, and morph targets. The glTF loader reads them (normals are optional), and normal mapping uses authored tangents.
  • Added the joy-animation crate (joy_engine::animation): skeletons, skins, poses, and sampled animation clips, GPU-free.
  • Shading-model registration validates every pass a model draws with, so a broken model fails registration instead of its first draw. Vertex alpha now only scales coverage; a material's alpha mode decides its queue.

Shared Code

  • Consolidated duplicate implementations: one TextureCache for embedded images (replacing ParticleTextureSet and FormTextureCache), data URL and base64 handling in joy-core, the GLB container in joy_engine::dcc::glb_container (shared by the loader and FORM LAB export), FORM documents in joy_engine::form::document (FORM LAB, Joy Editor, levels, and examples read .formlab files the same way), OrbitControls for every orbit viewport, and shared full-screen, color, texture, and sampler helpers in the renderer.
  • Breaking: the renderer's geometry builders became shapes (fans, circles, rings, and segments written through a ShapeSink), which particles also use; single triangles, squares, polygons, and baked-light ShadedShapes moved into the games that draw them.

Naming

  • Breaking: renamed ambiguous fields so units and roles read at the call site: OrbitCamera (content_radius, yaw_radians, and pitch_radians), FrameInputs::key_light, EnvironmentFrame::key_light, RendererOptions (draw_sky and prefer_deferred), RendererDiagnostics (adapter_name, visible_instances, culled_instances, point_lights_drawn, deferred_active, deferred_lighting_passes, and unregistered_model_submeshes), GpuCapabilities (supports_compute, supports_timestamps, and supports_deferred), ShadowOptions (max_point_face_size, max_shadowed_point_lights, and directional_shadow_distance), PointLight::radiance, ImageLighting::rotation_radians, and VolumetricOptions::key_light_intensity.

Engine Organization

  • Moved unit tests from tests/unit/ to sit beside the module they test as <file>.test.rs (mesh.rs and mesh.test.rs), declared with a same-folder #[path], so tests sort and move with their module and tests/ holds only integration tests, fixtures, and tooling tests. See the Rust coding standards.

Rendering

  • Fixed display output: the resolve pass now encodes sRGB after tone mapping (grading, antialiasing, and film grain work on the encoded values), so colors decoded on input display as authored, through sRGB and non-sRGB presentation formats alike.
  • Breaking: removed FrameInputs::time_seconds and TriangleFrame::time_seconds. The shell sets one presentation clock with Renderer::set_presentation_time (PresentationTime, with fixed_rate for tests and offline captures), which temporal antialiasing, exposure adaptation, film grain, and volumetric noise all read.
  • Transparency now draws after the lighting scene effects and before temporal antialiasing, motion blur, exposure, and post-processing, and every transparent element (blended meshes, transient triangles, and transparent_draws such as particles) sorts back to front in one queue, so a particle behind glass draws behind it. Breaking: removed sort_transparent_triangles.
  • Opaque geometry rasterizes once per view: a depth pre-pass (terrain first, then batches front to back; RendererOptions::depth_prepass) fills depth, the scene pass writes the scene-effect normal, roughness, and motion attachments beside its color instead of in two extra passes, and the sky draws last on the far plane.
  • Each shadow view draws only the casters inside its cascade or point-light face, and a point light renders shadow faces only when a caster reaches it.
  • Material maps and sprite and particle textures get GPU-generated mip chains, sampled trilinearly with anisotropic filtering.
  • Added HDR output: on displays that can show HDR, the shell presents an extended-range surface (scRGB natively, extended sRGB in browsers with HDR canvases), follows the display's headroom, and the display resolve extends tone-mapped highlights into it while midtones and the UI stay at SDR white (DisplayOutput, Renderer::set_display_output, and ShellOptions::high_dynamic_range, on by default). Breaking: EguiLayer::new takes the display output.
  • Added a main-scene render scale (RendererOptions::render_scale, Renderer::set_render_scale, and Renderer::render_size): the scene, its effects, and bloom render below the drawing-buffer resolution and the display resolve upscales them with a bicubic filter, while antialiasing, grain, and UI stay at full resolution.

Meshes and Materials

  • Breaking: added the joy-render-types crate (joy_engine::render_types) for GPU-free render data, and moved DirectionalLight, PointLight, Skylight, meshes, and materials there from joy-core. See meshes and materials.
  • Breaking: Mesh replaces IndexedMesh3D, MeshAssetData, PbrMesh, and MeshChannels with typed attributes (DVec3 positions, Vec3 normals, Vec2 UVs, and LinearColor colors), per-triangle material slots, and named MaterialSlots. MeshDocument (a mesh plus one material per slot) is the saved format; dcc::parse_glb_mesh, create_chunk_mesh_data, and FORM's GeometryData::to_mesh produce meshes, and the form-building cottage was re-baked as a document.
  • Breaking: meshes no longer choose their shading. Each slot's Material names a shading model through Shading (Layered, Lit, Unlit, or Custom), mesh assets carry each slot's default MaterialAsset, and instances override slots (with_material and with_slot_material). Removed Shading::Pbr, MeshInstance::with_shading, and MeshInstance::scene_roughness.
  • Added layered shading: SurfaceLayers with F0/F90 and metallic reflectance, haze, Charlie sheen, coat, and thin-film iridescence lobes, composed in Surface trees (coat, blend, add, and weight) that collapse to one evaluated layer.
  • Added a shading-model registry: built-in lit, layered, and unlit models and game models registered with Renderer::register_shading_model implement one WGSL contract, and the engine composes each into the lit, depth pre-pass, and masked shadow-caster passes, so a new model needs no renderer change. Every built-in model is validated for WebGPU and WebGL 2.
  • Added surface debug views (DebugView, Renderer::set_debug_view): base color, diffuse albedo, specular color, roughness, metallic, normals, emissive, occlusion, lighting only, shading model, shadow cascades, and texture coordinates.
  • Breaking: ShadedShape, ShadedTriangle, and add_shaded_shape replace ShadedMesh3D, ShadedTriangle3D, and add_mesh for arcade geometry, and compile_form_mesh, form_mesh_asset, and FormAssetLibrary::resolve take a ShadingModel.

Profiling and Console

  • Added frame profiling (Renderer::set_profiling and Renderer::frame_profile): CPU recording time per frame phase, and GPU time where the device has timestamp queries (WebGPU and native; GpuCapabilities::timestamps), read back without stalling. The STATS panel shows it in place of "Not collected".
  • Added an Unreal-style development console on the backquote key with typed console variables (ConsoleVariables) games can register, help, find, reset, and stat unit / stat gpu overlays, history, and completion. Engine r. variables drive render scale, debug views, profiling, the sky, shadows, and each scene effect, mirroring the renderer's actual state. Breaking: backquote opens the console instead of the STATS panel, which its dock launcher still opens. See console and profiling.
  • The STATS panel gained a Rendering section with a debug view selector and the profiling switch.

…standards (and improve iterative build speeds).

- Grouped similarly-type engine source as crates (`joy-core`, `joy-input`, `joy-renderer`, etc.).
- Updated rules, documentation, and standards.
- Fixed an issue with `dist-rust` build artifacts getting checked-in (they now go to `dist` subfolders as intended).
- Still in flight:
  - Editor and tools refactor/reorganization as-needed.
  - Examples as crates (potentially).
… `.test.rs` rust test file as a separate binary to generate.
…ly made updates to some manually-authored files that go against rust formatting rules that have no nice overrides.
…nified vehicle physics with interpolated presentation.

- Corrected sRGB output and centralized presentation timing for temporal effects.
  - NOTE: This requires some per-project color tuning as well as ensuring SDR/HDR output are both still being handled properly.
- Unified rendering pipeline to fix transparenct render ordering and effect layering; as part of this work also:
  - Consolidated scene-effect attachments (various framebuffers)
  - Added depth prepasses and shadow culling.
  - Properly support gpu texture mipmaps.
- Lighting refactoring and unification:
  - Shared light definitions across simulation and rendering.
  - Added ground-bounce logic to Skylights.
  - Exposed shadow-casting on particle lights as an option (instead of always being off, just off by default now).
  - Added logic to rank point lights per view instead of rejecting oversized light lists (leading to a hard crash).
- Consolidated terrain into the scene renderer and migrated the terrain example and statistics away from the standalone rendering path.
- Refactored vehicles as a child of the more general physics bodies struct; it was previously a discrete entity type and extra sim loops.
- Added interpolated body and wheel poses for smooth presentation between fixed steps.
…den-file testing.

- Added native and browser HDR presentation with display headroom tracking, SDR fallback, and consistent UI brightness.
- Added adjustable scene resolution with bicubic upscaling while keeping UI and final display processing at full resolution.
- Centralized golden-file comparisons and fixture updates, migrated recorded suites, and exposed test support to consuming games.
- Updated the UI initialization contract for display output and removed retired TypeScript reference assumptions.
… deferred rendering and more explicit renderer typing (and less duplication). Updated post-processing effects and removed legacy ones. Also added engine profiling, g-buffer visualization, and a barebones debug console.

- Separated GPU-free mesh, material, and light contracts into joy-render-types, added material slots and layered/custom shading, and migrated engine consumers.
- Added deferred lighting and support for up to 1024 clustered point lights, and rebuilt shadow mapping and image-based lighting.
- Rebuilt bloom, tone mapping, and scene effects with filmic and ACES 2.0 transforms, temporal antialiasing, motion blur, adaptive exposure, and volumetric fog, while retiring legacy grading and screen-space effects.
- Added a development console, rendering debug views, and CPU/GPU frame profiling.
@trentpolack trentpolack self-assigned this Oct 20, 2026
@trentpolack trentpolack added joy-engine Label for issues for Joy Engine. tech-debt Tech debt issues. feature Feature work. labels Oct 20, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in joy-engine Oct 20, 2026
@trentpolack
trentpolack merged commit 471f33f into develop Oct 20, 2026
3 of 4 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Resolved in joy-engine Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Feature work. joy-engine Label for issues for Joy Engine. tech-debt Tech debt issues.

Projects

Status: Resolved

Development

Successfully merging this pull request may close these issues.

1 participant