- graphics.water.render.planar (env-gated): render the scene from a mirror
camera into an offscreen canvas via begin3DFrameToCanvas, then have the
water plane sample it at screen UV. Confirms the mirror-camera capture
(red box in the reflection canvas) and a coherent final frame (sky above,
sky-reflecting water below).
- graphics.render3d.toCanvas: read the canvas once via newImageData instead of
per-pixel getPixel (each getPixel did a waitIdle + full image copy + staging
alloc, making the loop O(N) readbacks and looking like a hang).
The render-to-canvas path (already committed) is what makes this possible; the
offscreen render + subsequent present works once the readback is done once.
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.
The engine's 3D path cannot render into a user Canvas (begin3DFrame refuses
while a Canvas is active), and 3D always targets the fixed-size scene-color
target, so a mirror-camera planar reflection into an offscreen texture is not
achievable without a renderer-level render-to-texture-with-custom-camera
feature. Remove the fragile demo that relied on double-rendering to screen +
scene-color capture (final present came out black). SSR (committed) and env
cubemap reflection remain the water reflection options.
Add a screen-space reflection (SSR) fullscreen pass and hook it into the Water
shader as an optional, off-by-default reflection overlay.
- ScreenSpaceReflection: ray-marches the reflected ray over the lit scene color
+ hardware depth, reconstructing the world normal from depth (mirrors the
SSGI pass). Outputs reflected color with A = hit validity into a canvas.
Graphics::newScreenSpaceReflection() + script binding; quality knobs
(strength/steps/stepLen/thickness/distance) and enabled toggle.
- Water::setScreenSpaceReflection(enabled, strength) + setViewport: the water
shader samples the reflection texture (bound via the height slot, binding 6)
at screen UV and blends it over the env-cubemap reflection. Off by default.
- New ssr.frag + compile script; vulkan SPIR-V regenerated.
Note: SSR reflects only geometry that is on-screen. For a horizontal water plane
viewed from above the reflected geometry sits in the void below the plane (no
depth there), so SSR finds no hits there; it works for on-screen-reflective
surfaces (glossy floors/walls, grazing water). Env-cubemap reflection remains
the default; SSR is additive when enabled.
The middle-drop ripples were a hard-edged expanding ring (band ~0.055, radius
growing to cover half the plane), which read as an ugly big circle and did not
look like water. Replace with an expanding damped wavelet: a smooth gaussian
envelope times a crest+trough cosine, fading as it grows. Tune scene amplitudes
lower (wave 0.30, ripple 0.55) for calmer, more natural water.
Render a proper skybox: a huge sphere centered on the camera (raised far
plane so it isn't clipped) shaded purely by the env cubemap, so the sky is
clearly visible behind the water plane. Use a vertical-gradient cubemap
(pale horizon -> deep blue zenith) instead of a flat blue. The frame now
reads as bright sky (top) over a sky-reflecting water plane (bottom), with
no dark background.
Add a Waterfall helper (graphics/Waterfall.{h,cpp}) built on the custom
Mesh3D shader path that renders a vertical sheet as falling water:
- Downward flow: scrolling fractal noise + elongated vertical streaks
(streakScale) to convey falling velocity.
- White-water foam: turbulent crests in the body plus foam bands at the
top lip (topFoam) and the bottom splash pool (bottomFoam).
- Sky reflection: Fresnel-weighted reflection of the environment cubemap
plus a specular glint from the primary directional light.
- Bottom transparency fade so the sheet melts into the pool.
Push-constant params drive time, flow speed, turbulence, streak
count/scale, top/bottom foam, foam amount, water color, reflection
intensity and sun intensity. Exposed via Graphics::newWaterfall() and
the Squirrel binding (Waterfall class). Also adds Graphics::saveFramePng
to dump the presented frame to a PNG.
Includes a runnable demo (examples/waterfall-demo) and tests: params
round-trip plus a GPU render test proving the waterfall renders, changes
over time (flowing), and differs between calm and foamy configurations.
Replace the tiled-cube grid with one flat water plane carrying the water
shader (createPlane). A water surface is a plane; the cube tiling was
unnecessary. Renders brighter and cleaner (mean luma ~90, still strong
ripple/Fresnel variation) and fills the frame without block-gap gaps.
- Boost the water shader's sky-reflection blend baseline so the surface
clearly reflects the sky even at non-grazing angles (better first impression).
- Add an env-gated render test (EVENGINE_WATER_RENDER_PNG) that tiles a grid of
unit-cube meshes, each carrying the water shader, and saves a PNG so the water
scene can be previewed. The rendered frame reads as blue sky-reflected water
with visible ripple/block variation (grid std ~53, mean luma ~73).
Add a Water helper (graphics/Water.{h,cpp}) built on the custom Mesh3D
shader path that renders a plane as dynamic water:
- Sky reflection: Fresnel-weighted reflection of the environment cubemap,
blurred more at grazing angles; LOD clamped to available mip levels so a
non-mipmapped env cubemap stays safe.
- Edge ripples: animated shore waves strongest near the water boundary and
fading inward.
- Middle ripples: occasional expanding ring drops near the centre, staggered
by an interval, fading over time.
- Sun glint from the primary directional light + soft foam at the edge.
Push-constant params drive time, wave speed/amplitude/scale, ripple
amplitude/count/interval, edge falloff, water color, reflection tint/intensity
and sun intensity. Exposed via Graphics::newWater() and the Squirrel binding
(Water class). Water::draw() plus mesh+shader accessors let it be attached to a
Renderable3D for the scene graph.
Tests: params round-trip and a GPU render test proving the water renders,
changes over time (dynamic ripples), and differs between flat and rippled
configurations. Verified no regression on procgen + graphics render tests.
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).
Add 10 new albedo texture recipes (rock, wood, ripple, cloth, ornament,
spot, zebra, wall, cement, mud) alongside the existing soil/stone/marble/
water/sky_cloud, and introduce full metallic-roughness PBR generation.
A texture is now described by a single TextureRecipeDef (albedo ramp +
displacement field + PBR knobs), so both an albedo ImageData recipe
(tex.*) and a complete PBR set (pbr.*: albedo/normal/roughness/metallic/
height/ao) are derived from the same pixel-aligned height field, with
seamless tiling support.
Expose generatePbrMaterial + pbr recipe listing on the Procgen module and
script bindings. Add tests for expanded recipes, PBR reproducibility,
map ranges, and module/error paths.
Assert registration, per-kind validity (indices/normals/finite positions),
reproducibility, uniform scaling, linear vertex growth with segments, and
seam alignment (unit spans exactly [0, segLength]) for all six recipes.
Add mesh.skyscraper recipe producing a deterministic setback-tiered tower
with a window grid and optional spire. Window quads are raised a thin lip
above each facade so openings read under directional lighting without
coplanar z-fighting.
- Register recipe in MeshRecipeRegistry
- Add topology/height/reproducibility unit tests
- Add render smoke test outputting out/skyscraper.png
- Add interactive example examples/skyscraper-generator/
- Update procgen and module design docs
- Added new test cases for various graphics functionalities, including animation previews, particle effects, and inventory UI.
- Implemented rendering previews for Mixamo animations, Box2D physics, and procedural generation, allowing for real-time visualization during tests.
- Introduced helper functions for drawing graphics elements, improving the clarity and maintainability of test code.
- Enhanced existing tests to utilize the new graphics capabilities, ensuring comprehensive coverage of rendering features.
* docs(procgen): add procedural generation module design
Capture confirmed product direction (runtime map-first, textures
second, pixel/low-poly RPG) plus API sketch, algorithm registry,
configurable outputs, and Phase A checklist.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
* docs(procgen): list procgen in module summary table
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
* feat(procgen): Phase A runtime map generation with DTL
Add eve.Procgen module: semantic Grid2D, algorithm registry, palettes,
and TileLayer/JSON outputs. Built-ins use Dungeon Template Library
(BSP, cellular cave, maze, perlin terrain) plus local drunkard walk.
Includes tests and examples/procgen demo.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
* feat(procgen): Phase B runtime pixel texture recipes
Add seamless value-noise texture generation with banded color ramps
for soil/stone/marble/water/sky_cloud, ImageData/Texture/normal outputs,
and script bindings for gfx.newTexture / drawTexturedRect.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
* fix(test): qualify eve::image::ImageData in procgen tests
Bare image:: failed to resolve under `using namespace eve::procgen`,
breaking unit_test builds on Linux/Windows/macOS CI.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
* fix(procgen): link EVProcgen in tests and fix Apple Clang DTL build
unit_test was missing EVProcgen, causing undefined references on Linux/Windows.
Avoid the DTL umbrella header (broken Voronoi CRTP on Apple Clang) and provide
DrawJagged* compat shims that define the missing drawOperatorArray.
Verified locally: linux-debug build, unit_test link, and all 13 procgen ctests.
Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>