Skip to content

Latest commit

 

History

History

README.md

ESP 88 — a demo by CubeCoders

ESP 88: ESP32-style screenshot, performance overlay hidden

A 116-second, twelve-scene Jet cinematic, followed by a one-second black hold and an intentional ESP32 restart. The completed film was reviewed on S3; demanding shots can drop below the 60-field/s target. See the validation record for measured performance.

The original low wedge coupe, compact streets and framing follow the car design and one concept reference per shot. These generated references guide composition and proportions; they are not screenshots or promises of ESP32 image quality. The former NASCAR model is not used or bundled in this project.

Time Shot Reference
0–12 River skyline, fade-in, searching sky beams, floating holograms 01
12–22 Descending camera in a rainy narrow street; mirrored shopfronts 02
22–29 Oblique overhead courier and tyre spray 03
29–38 Close three-quarter orbit around the front of the car 04
38–47 Angled driver-height cockpit, bonnet and red scanner 05
47–53 Oblique instrument close-up: 70 to 88 MPH 06
53–64 Hard braking, faster sideways skid, close pass-by, then tracking in traffic 07
64–78 Police close from established distant positions, overtakes and camera cuts 08
78–88 Wheels slow and hinge downward; cyan hub glow builds 09
88–93 Pitched climb, forward flight and frozen police 10
93–101 Lower fly-by over the city, fade to black 11
101–116 Waterfront pan, closing credits and shared fade to black 01

Camera translation is linear within each tracking shot; the front orbit uses constant angular progression. Smooth lane changes describe vehicle steering, not camera easing. The wheels provide the entire hover conversion: no wings or deployed rear engine. The launch has no flash: the car retains its forward speed of 88 MPH while climbing at 650 world units/s. Its nose follows the flight direction, including the hinged wheels, window reflections and glow sprites. The final camera waits ahead of its flight path, letting the coupe pass close to the lens before tilting down over the city. After the fly-by fades to black, the original waterfront composition returns with its linear pan. It fades in over two seconds; the credits appear from 103–105 seconds, remain fully visible for eight seconds, then fade with the scene from 113–116 seconds. Road sections recycle around the tracking camera. The framing uses offset subjects and diagonal street lines, with lenses chosen per cut. Linear field-of-view changes tighten the 70–88 MPH gauge insert from 62° to 46°, widen the launch from 54° to 70°, and pull the final approach from 34° to 68° before the car passes the lens. These projection changes add no blur pass or depth-of-field buffer.

The 570-unit coupe represents a 4.5-metre car: 70 MPH is 6.95 car lengths per second and 88 MPH is 8.74. Road parallax and wheel rotation use the same conversion. The tight right turn slows to roughly 25 MPH, then cuts back to the faster boulevard tracking rig. The speedometer keeps its 70–88 MPH climb.

Street draw distance is 9,000 world units. Recycled streets provide sixteen blocks and put their longer extent in the viewing direction, including behind the car for front tracking. The fixed rain street uses simpler distant facades; the boulevard exit recycles sixteen blocks along its eastbound axis. Painter sorting uses 128 depth buckets to preserve nearby surface ordering at this range: still one byte per triangle, plus 512 bytes of sorting stack versus the default 64 buckets. Jet's default configuration remains unchanged.

At 3,200 units, grouped storefronts switch to six-triangle alternatives that retain their shop artwork and facade planes. Four box-filtered 32×32 facade mipmaps occupy 8 KiB of internal DRAM; the cache is initialized once and remains immutable during rendering. Distant lamp meshes are culled separately.

The colour grade increases contrast by 25% around an 80/255 pivot, deepening shadows and lifting highlights. It is applied to authored material colours and baked texture RGB565 values, with no per-pixel postprocess. Additive glow textures retain their original falloff.

Rendering and memory

  • Shared ESP32 runtime: 480×320 output, half-width RGB565, alternating fields, overlapped DMA scanout and two raster workers on the S3. At 60 fields/s an individual physical LCD row refreshes at 30 Hz.
  • Painter sorting without a depth allocation, with exact mean-depth refinement inside buckets touched by vehicle meshes. Opaque Phong body, unlit tyres and scenery, nearest environment mapping on the separately modelled canopy.
  • Previous-field river reflection; inverted mesh reflections under the rainy street; translucent additive beams and full-resolution sprite halos.
  • Bevelled/sloping skyline, shop textures, sloped awnings, projecting signs, spinning wheels, flickering lamps and fast rain streaks in every exterior shot. The narrow street also has short, independently scattered road splashes; interiors remain dry.
  • Scene-local geometry/materials are owned by a bank and released at cuts. Persistent immutable textures remain valid for concurrent scanout snapshots. Large scene allocations prefer PSRAM; internal RAM is reserved for live transforms and runtime work. Adjacent shop details share transform work.
  • Neon tubes widen to maintain at least two projected pixels in the slow skyline pan. The same ribbon treatment covers shop trims and city ground lines; far shop LOD uses broader trim bands. Searchlights have brighter core beams and wider, faster sweeps. Fine texture details can still alias.
  • Closed traffic wheel wells and a recessed chassis floor prevent the body from intersecting the wheels. Suspension pitches/rolls the hero body while its road wheels stay planted.
  • Closed hero body, shared canopy/glass boundaries, inner wheel-well walls, a continuous chassis floor and capped wheels that remain sealed in hover mode.
  • Enclosed cockpit with sloping windscreen pillars, roof header and door trim. One low dashboard holds two flush displays and a slim red scanner; its open centre makes the autonomous layout clear. The driver camera stays at road height. The speed display updates a scene-local 16 KiB RGB565 texture only when its integer value changes; the allocation prefers PSRAM on the S3.
  • Only the rounded FPS number appears at the absolute top-right on hardware. Full timing and triangle information remains in serial diagnostics.

Assets are original procedural geometry and artwork in main/Vehicle.hpp, main/City.hpp and tools/prepare_assets.py. Bitmap concept art was made with the built-in image generation tool; the exact prompts are in references/prompts.json. The small firmware artwork is generated with Pillow; rebuilding does not need image generation services.

Lossless indexed textures

The 17 static 3D textures use byte indices into small RGB565 palettes in internal RAM. Their source artwork retains every original RGB565 colour and resolution; indices remain in flash. The four filtered facade mipmaps stay RGB565 in 8 KiB of DRAM. Glow sprites, credits and the dynamic speed display also remain RGB565.

On the reference S3 and 80 MHz ST7796 display, repeated 36-view comparisons reduced mean render time from 23.550 to 21.578 ms (8.37%) on top of the earlier engine optimisations. A control using identical renderer code confirmed 8.55% less render time and 12.48% less raster time, with identical field hashes and triangle counts. These are fixed-view timings, not moving-film average FPS.

Texture storage falls from 323,584 to 164,448 bytes including palettes. Linked benchmark firmware is 155,864 bytes smaller, at a cost of 5,800 bytes of static internal memory: 2,984 bytes of IRAM instructions and 2,816 bytes of palette data including alignment. This saves flash/cache footprint rather than PSRAM. See Jet's measurement and compatibility notes.

The generator grades and converts to RGB565 before indexing, checks the 256-colour limit and verifies a lossless round trip. Indexed artwork is sampled with nearest filtering, including in desktop-quality builds; Sprite2D requires RGB565 textures. Normal builds use the checked-in arrays and do not regenerate the font-dependent artwork.

Build and review

Initialize the shared submodules from the repository root, then use an ESP-IDF 6.0.1 environment in this directory:

idf.py -B build-s3 -DIDF_TARGET=esp32s3 -DSDKCONFIG=sdkconfig.s3 build
idf.py -B build-s3 -DIDF_TARGET=esp32s3 -DSDKCONFIG=sdkconfig.s3 -p COM6 flash monitor

P4 configuration is included but this film has only been run on S3 hardware. The complete sequence must be watched before judging resets: restarting after the final black hold is intentional; an earlier restart is a fault.

Native validation and video export (C++17, CMake, Python, ffmpeg):

cmake -S tests -B build-preview -DCMAKE_BUILD_TYPE=Release
cmake --build build-preview --config Release
ctest --test-dir build-preview -C Release --output-on-failure
python tools/render_video.py preview.mp4 --renderer build-preview/film_video.exe

Use the executable path generated by your build system; multi-configuration builds may put it in build-preview/Release. --ffmpeg accepts a custom encoder path; --start and --seconds export an excerpt. The exporter streams RGB24 directly into ffmpeg and writes no individual frame files. Its 60 fps video reconstructs the same alternating packed fields, including previous-field water and scanline sprite compositing. It omits the hardware FPS overlay: native export speed is not an S3 measurement. Silent video is for visual review.

A separate desktop quality target renders full 2880×1920 RGB565 colour and depth buffers at every frame, with perspective mapping and full-detail meshes. RGB565 textures can use bilinear filtering; the lossless indexed artwork remains nearest-sampled. The export downsamples to 1920×1280 at 60 fps using Lanczos filtering for spatial anti-aliasing. It has no packed fields, scanline reconstruction or hardware counters. Geometry, artwork and camera paths are shared with the S3 build; it remains an actual Jet render, not a ray-traced remake.

cmake -S tests/quality -B build-quality -DCMAKE_BUILD_TYPE=Release
cmake --build build-quality --config Release
ctest --test-dir build-quality -C Release --output-on-failure
python tools/render_video.py quality.mp4 --quality

For multi-configuration generators, also pass --renderer with the executable under build-quality/Release. Both export modes write render and encoding logs next to the MP4. The quality build enables Jet's optional JET_HIGH_PRECISION_UVS: large projected triangles use full-width edge weights and double reciprocal depths to prevent texture overflow. The quality build also enables JET_PERSPECTIVE_DEPTH, interpolating reciprocal Z so large sloping panels occlude correctly. Both options remain disabled on S3 and add no firmware pixel cost.

The checked-in main/CreditMask.hpp contains the closing typography at native and desktop resolution. It needs no runtime font library. To replace its typeface, run python tools/prepare_credits.py --font regular.ttf --bold-font bold.ttf. The embedded mask occupies 7,560 flash bytes and expands into approximately 118 KiB of PSRAM only in the final scene.

See VALIDATION.md for checks and known limitations.