* chore(ci): add clang-format gate, CODEOWNERS and agent collaboration rules
* refactor(modules): clear remaining back-edges and split the largest TUs
Three known dependency back-edges are gone (module_depgraph.py KNOWN_BACK_EDGES
is now empty; --check passes with 0 violations, 0 cycles):
- window -> graphics: window queries the new IWindowSurfaceHost capability
(common/WindowSurfaceHost.h) instead of holding a Graphics pointer;
Window::setGraphics was removed and all call sites updated.
- window -> image: Window::setIcon was replaced by setIconRGBA(const uint8_t*,
w, h); the window no longer decodes ImageData.
- scene -> graphics: pickScreenAt / collectFrustumIdsAt implementations moved
to graphics/ScenePicking.cpp (graphics -> scene is a legal downward edge);
scene now depends only on spatial.
TU splits (pure moves, verified identical method sets and brace balance):
- vulkan/Graphics.cpp (5118 lines) -> Graphics.cpp (lifecycle), GraphicsPipeline,
Graphics2D, Graphics3D, GraphicsMesh + shared GraphicsInternal.h.
- scene/Scene.cpp -> Scene.cpp (script layer / host lifecycle / serialization)
+ SceneNodes.cpp + shared SceneInternal.h.
Verified with a real build (MSVC 19.51, prebuilt third-party): full build green;
window.* and Scene.* suites pass, including scene picking/culling and GPU cases.
* refactor(graphics): narrow Graphics interfaces and clear public-header leaks
Step 4 — interface segregation:
- New lean interfaces: IGraphics2D, IGraphics3D, IResourceFactory, IPostFX
(pure virtuals, forward-declared types only; graphics/Color.h extracted from
Canvas.h so modules that only need the color type stop pulling Canvas.h).
- Graphics now implements all four facets; consumers can depend on the narrow
interface they use instead of the 129-method god class.
- Migrated voxel (VoxelWorld::drawVisible -> IGraphics3D*) and model3d
(ModelRenderer::buildRenderable(s) -> IResourceFactory&).
- Graphics.h slims from 24 sub-header includes to a handful of small type
headers + forward declarations; the destructor moved out-of-line so
unique_ptr effect members are destroyed where types are complete.
Step 5 — header leaks (module_depgraph.py '*' marks: 14 -> 7):
- map/TileLayer.h, particles/ParticleEmitter.h, avatar/AvatarInstance.h,
animation/SpineAnim.h, map/TileSystem.h, model3d/ModelData.h,
model3d/ModelRenderer.h, audio/Audio.h + Source.h, image/ImageData.h +
Image.h, dialogue/Dialogue.h, procgen Palette/OutputSpec/TextureRecipe/
PbrMaterial -> forward declarations, includes moved to .cpp.
- voxel/VoxelWorld.h: terrain sampler Pimpl'd (unique_ptr + out-of-line
destructor), removing the procgen header dependency.
- gpgpu/vulkan backend headers stop pulling graphics/vulkan/Graphics.h.
- Remaining '*' marks are deliberate: tiny POD/enum headers (GridConfig,
MapObject, BlendMode, Color), an internal backend header
(GraphicsInternal.h -> filesystem), and lean interface references.
Verified with a full clean build (MSVC) and tests: window/Scene/voxel/
RenderSystem/Quad/shader/TextureSampler/resourceLifetime pass; broad suite
only shows the pre-existing PHYSFS-init filesystem-environment failures.
* feat(resources): revive ResourceManager as unified IAssetReloader-backed cache
* fix(resources): leak ResourceManager singleton to avoid exit-time teardown crashes
Cached CPU resources may own third-party handles (FreeType faces, Assimp
scenes, image decode handlers) whose libraries are torn down at process
exit in an unspecified TU order; destroying cached entries from the
singleton destructor can segfault at exit (seen on CI Windows in
model3d.materialApi.objMtl). Keep the singleton alive until OS reclaim;
explicit unload()/clear() still release entries during the run.
* refactor(devtools): consume business modules through capability interfaces
DevTools / cmdline no longer include business-module headers, so trimming
scene / physics / procgen / particles / audio / ui / graphics no longer
requires disabling devtools, and devtools can be switched off with
-DEVENGINE_MODULE_DEVTOOLS=OFF.
New capability interfaces in common/:
- ISceneQuery (scene) - host/node inspection + mutation + transform sync
- IRenderCapture (graphics) - render status, PNG capture, camera control,
visible-entity snapshot, entity-ID mask, G-buffer PNGs
- IPhysicsQuery (physics) - 2D world lifecycle + ray casts
- IProcgenQuery (procgen) - recipe listing, map/mesh generation
- IParticlesQuery (particles) - emitter status + one-shot emit
- IAudioQuery (audio) - volume / stop-all
- IEditorHost (ui) - declarative editor host (used by cmdline too)
Providers register in their module constructors. DevTools rewires McpServer
(render/scene/procgen/physics/particles/audio/host tools), SceneInspect
(now delegates to ISceneQuery + IRenderCapture) and RenderVision (uses
IRenderCapture::capturePngDataUrl); cmdline/Mcp.cpp uses IEditorHost.
manifest: devtools DEPS graphics scene physics procgen particles audio ui
image event filesystem -> event; cmdline DEPS filesystem ui -> filesystem.
Verified: full build green; devtools.mcp.* tests pass; broad suite shows
only the pre-existing PHYSFS-init filesystem-environment failures.
* style: apply clang-format to changed lines after dev merge
* fix(webgpu): include RenderControl/TextureSampler explicitly after Graphics.h slimming
* fix(webgpu): provide newFont/print stubs to satisfy IGraphics2D/IResourceFactory vtables
* fix(common): make ProcgenQuery.h self-sufficient (cstdint/utility for GCC)
- Mesh gains a model-space bounding sphere; RenderSystem3D now walks the ECS
once per frame and culls draw units against the camera frustum (forward /
G-buffer) and per-cascade light frustums (CSM) instead of re-walking the
whole scene five times with no culling.
- Camera matrices, lighting packs and the clustered light table are resolved
once per camera per frame; per-draw lighting becomes a cheap state toggle
via the new Graphics::setMesh3DClusteredActive (Vulkan + WebGPU).
- The pseudo-GPU particle path (per-emitter upload -> dispatch -> synchronous
readback every frame) is disabled; particles integrate on the CPU until a
GPU-resident render path exists.
Add releaseTexture/releaseMesh/releaseShader to both backends and fix the contradictory ownership comments: handles returned by newTexture/newMesh/newShader are borrowed and Graphics owns the GPU resources until shutdown or an explicit release. Move the Color typedef into eve::graphics and migrate all module/test users. Turn the silently no-op drawSolidRectRotated / drawTexturedRectShaderUVRotated / drawTexturedRectShaderDepth and the validation-scope hooks into pure virtuals so a backend can never silently skip a draw.
Add begin3DFrameToCanvas / end3DFrameToCanvas on Graphics: open a 3D render
pass targeting an offscreen Canvas (color + D32 depth) at the canvas size,
using setMesh3DViewProj/View/CameraPos/Env as the camera. Rendered on a
dedicated command buffer and submitted directly to the graphics queue, so it
does not touch the swapchain / present flow. Enables render targets such as a
mirror-camera planar reflection (reflection captured, though compositing it
into a live scene is a follow-up).
Vulkan: offscreen3D render pass + mesh3d pipeline + pool/cb/fence, canvas
gets a depth attachment + color+depth framebuffer; currentPresentCb/frameToken
route to the offscreen cb during the pass. WebGPU: unsupported (throws).
Test: graphics.render3d.toCanvas renders a red box into a canvas and reads it
back to confirm the scene lands in the offscreen target.
Show only the part of a marked character that is hidden behind a building,
as a filled highlight silhouette (Overwatch-style). Visible parts render
normally.
- Shader: xray_mesh.frag samples G-buffer hardware depth (binding 7) and
compares it against its own fragment depth: occluded (behind) pixels emit
the highlight, visible pixels discard. Drawn as a second pass over the
scene with depth test/write off + alpha blend.
- RenderSystem3D: MeshRenderer gains xrayShader + xrayHighlight. X-ray
targets are skipped in the G-buffer (their pixels record occluder depth),
then redrawn with the X-ray shader; scene depth is bound via
Graphics::setMesh3DSceneDepth.
- Graphics: new setMesh3DSceneDepth + Shader::setXray. Vulkan adds binding 7
to the mesh3d set layout and a dedicated X-ray pipeline variant.
- WebGPU: new newMeshShaderFromWgsl (WGSL custom mesh shaders), scene-depth
binding 9, X-ray pipeline variant, xray params packed into the Frame UBO,
and the G-buffer hardware depth is exposed/sampleable (getHwDepthTexture).
AO (SPIR-V) is skipped on WebGPU via supportsGBufferPost.
- stylize: 'xray' style registered (createMeshShader dispatches SPIR-V on
Vulkan / WGSL on WebGPU) with push-constant params.
- Tests: test/Xray.cpp (shader/flags + occluded-silhouette render smoke).
- Introduced `drawScene3DRGBA` and `drawCanvasRGBA` methods for improved 3D scene rendering and canvas drawing.
- Added `prepareSceneColorResolveShader` to handle FXAA settings for scene color resolution.
- Updated `Graphics` class to manage overlays and batch rendering more effectively.
- Enhanced exposure of new methods to scripting for better integration and usability.
Add procedural cloud generation and dynamic cloud shadows that darken the
ground as clouds drift overhead.
Procgen (CPU, fully testable):
- CloudField: deterministic, seamlessly-tiling, time-animated 2D cloud
coverage over the XZ plane (domain-warped value-noise fBm, wind drift,
wisp detail). coverageAt(x,z,t), sample(...).
- CloudShadow: projects clouds along the sun onto the ground plane
(coverageAt / shadowFactorAt / bake), accounting for sun direction and
cloud altitude.
- New recipes: tex.cloud (cloud cover), tex.cloud_shadow (projected
ground shadow coverage).
- Procgen module + script API: newCloudField/newCloudShadow,
cloudCoverageAt/cloudShadowFactor, sampleCloud/sampleCloudShadow.
Graphics (real-time):
- setCloudShadows() packs {strength, worldCell, time, wind, coverage,
detail} into the Mesh3D UBO (default strength 0 = no change).
- mesh3d PBR shaders (Vulkan GLSL + regenerated SPIR-V, and WebGPU WGSL)
sample an inline seamless cloud field in world XZ and modulate the
primary directional light.
Tests: CloudField reproducibility/animation/seamless tiling, wind drift,
shadow projection & factor bounds, recipe generation, module API, and a GPU
render test proving clouds darken the ground and the effect resets when
disabled. Verified no regression on the default PBR path (ClassicScenes
pbrChart + hexplanet render tests).
The browser build ran without GPU errors but drew nothing for 2D/3D content. Four independent root causes made every non-clear fragment invisible:
- Zero-initialized WGPUMultisampleState left sampleMask=0, which discards every fragment (the swapchain clear still shows because it is unaffected by the mask). Set mask=0xFFFFFFFF on all 9 pipelines and writeMask=All on every color target.
- Render passes attached Depth32Float views (no stencil aspect) with stencilLoadOp=Clear / stencilStoreOp=Discard, a validation error that invalidated the whole command buffer. Use Undefined for the stencil ops on shadow/scene/gbuffer attachments.
- begin3DFrame() left frameHad3D=false, so RenderSystem3D::render bailed out before any mesh was drawn. Mirror the Vulkan backend.
- The mesh3D fragment shader returned alpha=linear depth, which the swapchain compositor blended as near-transparency. Output opaque.
Also sync clearColor from setBackgroundColor in present() (matching the desktop flow) and call instance.ProcessEvents() after submit so queued draws are executed by emdawnwebgpu's JS-driven GPU.
Add push/popValidationScope hooks so render3D() reports WebGPU validation errors (async + silent otherwise) instead of a black frame.
- Added support for the WebGPU backend in the CMake configuration, enabling builds for Emscripten and native Dawn.
- Implemented conditional compilation for WebGPU-specific features, including shader management and file system handling.
- Updated the build process to exclude unnecessary modules and dependencies for WebGPU, optimizing the engine for browser environments.
- Enhanced the installation script to include WebGPU-related files and directories in the output.
- Refactored existing modules to accommodate WebGPU, ensuring compatibility and performance improvements across the engine.
- Introduced new compile options and configurations tailored for WebGPU builds, including handling of third-party libraries and assets.