flutter_scene 0.24.0
flutter_scene: ^0.24.0 copied to clipboard
A 3D game engine for Flutter, built on Flutter GPU. Renders glTF models with PBR lighting, shadows, animation, physics, and audio, with a scene editor.
0.24.0 #
- Bloom weights an isolated extreme pixel down against its neighbors before blurring (
BloomSettings.fireflySuppression, on by default), so a mirror glint of a small intense light no longer floods the frame or strobes, while bright regions bloom in full. BloomSettings.highlight(default 1000) softly compresses extreme pixels before they bloom, so ordinary highlights pass nearly unchanged but no pixel floods the frame. 0 turns it off.PointLight.radiusandSpotLight.radiusgive a light a physical size, so glossy reflections widen to the emitter instead of peaking at a point, and the near-field falloff stops at its surface.- Opaque materials no longer
discard, so mobile GPUs keep early depth testing and hidden-surface removal on for every opaque draw (frames 2.5 to 6x faster on a Mali-G57). Alpha-masked and LOD cross-fading draws cut out through a depth pre-draw instead; translucent cross-fades fade by opacity. - An alpha-masked
PhysicalMaterialwithout a base color texture masks its shadows and depth by its vertex and factor alpha. Material.depthLayer(.fmatdepth_layer:) orders surfaces that share a plane. A higher layer draws over a lower one at any distance, so signs, screens, and road paint laid on a surface stop flickering (z-fighting).- Scenes store reversed float depth where the device supports it (Metal, and WebGL2 with
EXT_clip_control) and rasterize with a near plane fitted to visible content each frame, so distant surfaces centimetres apart stay distinct.Scene.reversedDepthandScene.fitNearPlaneturn each off; projections and picking keep the authored planes. PerspectiveProjectionandPerspectiveCameraaccept an infinite far plane (.fsceneinfiniteFar).Scene.probeDepthConflictsnames each pair of nodes that trades pixels as the camera moves, andDebugOverlay.depthConflictsmarks them live.Scene.findCoplanarOverlapslists faces that overlap in one plane, with the length that fixes a repeated piece; debug builds print it once the scene settles.SurfaceDebugChannel.depthGapshows the gap two surfaces need at each pixel to keep their order.Scene.coplanarTieBreak(experimental) gives unlayered exact overlaps a stable winner.- Custom
ShaderMaterialvertex shaders that write clip depth themselves (a sky atz = w) needScene.reversedDepth = false; ones that usecamera_transformare unaffected. loadSceneand the async.fscenebloaders parse on a background isolate;readFscenebAsyncis exported fromfscene.dart.- Generated mip chains upload in one submission instead of one per level.
Texture2D.fromImage(opaque: true)wraps the image's GPU texture instead of reading it back, when no mip chain is needed.Scene.warmUpwaits for the raster thread before drawing, so OpenGL ES pipeline compiles no longer stall the UI thread behind a swap.- Fixed shadows striping on half-precision GPUs (Mali, Adreno, software GL) since material shaders default to mediump; the shadow map and punctual light data samplers are declared highp again (#234).
- Fixed lit materials writing NaN at sharp specular highlights viewed at grazing angles on half-precision GPUs, which reflections, depth of field and bloom spread into screen-filling flashes.
- The bloom and depth of field prefilters map a NaN pixel to black instead of to the half-float maximum, so a stray NaN from any shader can no longer flood the screen with bloom.
- The shaders behind
PhysicallyBasedMaterialextensions andShadowCatcherMaterial, and the SMAA tables, load on first use instead of delaying every scene's first frame;Scene.preloadloads them up front. - Generated scenes, textures, and materials rebuild when the flutter_scene code that writes them changes, so an upgrade no longer reuses outputs from the old importer, encoder, or
.fmatcompiler. - Custom vertex attributes no longer have to match between mesh and material. A declared attribute the mesh lacks reads zero instead of crashing or reading garbage, and materials that do not declare a mesh's attribute draw it instead of skipping it (#440).
.fmatsamplers declared withhint: default_blackorhint: default_transparentsample a black or transparent placeholder until a texture is set; both used to sample white.- A draw the backend refuses is skipped instead of throwing out of
paint, and a pipeline that keeps failing (an OpenGL ES driver that cannot compile its shader) is dropped and reported once (#444). - Fixed a crash on M3 and newer Macs and recent iPhones (macOS and iOS 26+) when drawing a lit material with directional shadows; the lit shaders no longer inline their lighting twice (#436).
- Fixed bloom stamping a grid of square copies around bright highlights and popping as they moved. The first mip averages each texel's whole source footprint, the upsample tent no longer spreads its taps apart,
BloomSettings.scatterweights the wider mips instead, and the resolve magnifies bloom with a B-spline. - Fixed a native crash on Impeller Vulkan when cached static shadows re-rendered after a resize, a render-target release, or from a second view (#431).
- Runtime-imported glTF textures honor their sampler's per-axis wrap modes (
CLAMP_TO_EDGE,MIRRORED_REPEAT) andNEARESTfilters instead of always repeating. - ETC1S KTX2 textures (
KHR_texture_basisu) transcode to BC or ETC2 blocks on devices that sample them, instead of decoding to rgba8. buildTextures(encoding: TextureEncoding.etc1s(quality: ...))cooks textures to standard ETC1S KTX2 with a pure-Dart encoder, several times smaller than.fstex;encodingssets it per source.- Fixed morph targets on unskinned meshes not deforming on the GPU path; their weights animated but the mesh drew in its base pose (#428).
- Morphed mesh bounds grow to cover every weight a node draws with, including weights below 0 or above 1, so animated blend shapes are no longer culled while on screen.
- Setting
Node.highlightColorbetween a tick and the next draw no longer throws inside the selection outline pass; the outline draws white for that frame and takes the color on the next. - Punctual light data textures skip uploads that would not change them, saving a synchronous upload per texture per frame on drivers like Android Vulkan.
Scene.warmUp(sliceBudget:)compiles pipelines a slice at a time and yields between slices, so a slow device keeps answering input;SceneView(warmUp: true)now warms up this way.- Lit
.fmatmaterials can defineLight(),Ambient(), andComposite()hooks to shade each light with their own BRDF, adjust the image-based lighting inputs, and combine the lit lobes themselves;Light()also sees each light's table row and shadow slot. - Lit
.fmatmaterials can setenvironment_lighting: falseto compile out the engine's environment lighting and supply their own indirect light inComposite(). .fmatVertex()hooks readvertex.model_transform, the object-to-world transform, so a vertex stage can build geometry in object space and place it itself.- Fixed image-based lighting adding the specular multiscatter energy to the diffuse lobe instead of subtracting it, which brightened rough surfaces, partly metallic ones most.
.fmatalpha_to_coverage: trueturns an opaque material's alpha into MSAA coverage for crisp, sort-free cutouts such as foliage cards (a dithered cutout on OpenGL ES and WebGL2), and cuts its depth prepass and shadows by the same alpha.SpotLight.shadowFieldOfViewnarrows a spot's shadow frustum inside its cone, spending the tile's texels on the core of a wide, soft-edged cone.PointLight.falloffExponentandSpotLight.falloffExponentaccept 0, which turns off distance falloff and leaves only therangewindow; they used to clamp to 0.1.- Custom render passes read each casting spot's shadow matrix and atlas tile from
RenderPassContext.spotShadows(SpotShadowInfo). RenderTexture(linearColor: true)captures the view's linear HDR scene color with no post-processing or tone mapping, for mirrors and probes composited in HDR; MSAA still applies when the view asks for it.PlanarReflectionCamerais public, so an app can render its own mirror into aRenderTexturewith the reflected camera and an oblique near plane at the mirror.Node.renderOrdersorts a node's draws ahead of everything else in their pass, pinning the order of overlapping translucent surfaces that depth sorting gets wrong.MeshPrimitive.drawSelectorandInstancedMesh.drawSelectortake aMeshDrawSelectorthat picks how many leading instances and which index range each color, depth, or shadow draw uses, for detail that depends on the pass or camera.- An
InstancedMeshwhose instances stop changing keeps its instance data in a device buffer, so its draws stop copying every instance each frame. - Double-sided materials cast shadows from both faces, so cards turned away from the light no longer drop out of the shadow map.
- Fixed a Metal crash when a
.fmatnamed a declared sampler without reading it (in a comment or code the compiler removes); every declared sampler now stays in the compiled shader. buildScenesandbuildMaterialsrerun when a source is added or removed in a subdirectory, not only at the top of the discovery root.- Image and HDR sky backgrounds no longer draw a one-pixel line where the panorama wraps or a fan of wedges at the poles; the sky picks its mip level from the projection instead of screen derivatives.
.fscenedocuments keep data this build does not read through a load and save, and a property with an unknown tag loads as an inertUnknownValue(exported fromfscene.dart) instead of failing the document.- Render textures share the screen view's spot and point shadow tiles when no directional light casts, so mirrors no longer re-render them. Their casters draw from the screen view's camera.
- A
.fmatvertex stage that reads custom or per-instance attributes runs in depth and shadow passes too, so geometry it moves casts the shadow it draws. - After-scene custom passes that read depth see translucent surfaces that write depth, so a depth-based fog or composite no longer paints over them.
.fmateffects_depth: trueputs a translucent surface in the depth that depth of field and custom passes read, cut by its alpha, without writing depth in the color pass (fins, hair cards).Scene.debug.splitViewshows a second surface debug view left ofScene.debug.split, so a wipe can run between two channels instead of against the lit image.- Debug overlays follow
Scene.debug.split, drawn only on the view side, andScene.debug.splitOverlaysdraws overlays on the other side, so a wireframe can wipe in and out. - Depth of field spreads an out-of-focus foreground object's blur past its silhouette over its full blur radius, while the in-focus background beside it stays sharp.
SceneView.maxFrameRatecaps rendering at an even cadence, every other refresh for 60 on a 120 Hz display.- Transient GPU buffers are recycled by idle age, so a loaded scene stops allocating per frame.
Geometry.uploadVertexStreamsuploads vertex streams in a caller-defined format, andGeometry.setDepthOnlyVertexgives such a mesh a position-only shader for shadows and the depth prepass.- A geometry with a declared vertex layout draws depth through its own vertex shader instead of the engine's position-only one, which misread any first stream that was not a float position.
- Integer vertex formats work on the web backend.
MeshGeometryattributes left out at construction bind shared default streams instead of uploading per-mesh defaults.- A light that turns by a few degrees refreshes cached shadow cascades one per frame instead of re-rendering all of them in one frame, and keeps their textures.
- Encoding a frame allocates far less per draw and per node (uniform slots, pipeline lookups, sort depth, transient staging, uniform scratches).
- A view keeps render targets for its two most recent sizes, so a render scale toggling between two resolutions no longer reallocates.
- Lit
.fmatmaterials can setdirectional_light: falseto compile out the directional light. - Fixed unlit
.fmatmaterials drawing nothing on Impeller Vulkan. Their shader carried an unread radiance block that collided with the vertex stage's frame block in the pipeline layout, which some drivers reject; the block is now compiled out where nothing reads it. - Display-referred surfaces.
Material.displayReferred(settable onUnlitMaterial) marks a surface whose color is already final screen values, so it draws past the tone curve into its own layer and composites onto the resolved image with its colors unchanged.WidgetComponentturns it on for the material it owns, fixing captured widgets arriving dark and dark tones crushed (#382); passdisplayReferred: falsefor a screen that should read as a lit object. The layer is still occluded by opaque geometry, but takes no exposure, grading, tone mapping, fog, bloom or depth of field, casts no shadow, and does not order against translucent geometry. - Web decodes glTF,
.fscene, and asset textures in the browser and builds their mip chains on the GPU, so large images no longer render in pieces and no longer stall the main thread. Texture2D.fromEncodedBytesuploads an encoded image. It,Texture2D.fromAsset, and the glTF loaders take a size cap that scales larger images down as they decode.- Web wasm builds now enable anisotropic filtering, which a mistyped limit query had silently disabled.
- Web mesh uploads give vertex and index data a GL buffer each, written straight through, instead of one shared buffer mirrored in Dart and uploaded twice. Meshes outside a
GeometryBufferArenaupload about half the bytes, and far faster under wasm. Geometry.uploadVertexDatatakesTypedDatarather thanByteDatafor its vertices and indices, so an upload keeps the element type it was packed as. Subclasses overriding it must widen their parameters to match.OrthographicCameraandOrthographicProjectionadd parallel projection, sized byOrthographicSize(full world extents with height, width, contain, cover, or stretch fitting, or a fixedpixelsPerUnit) pluszoom,offset, and anearthat may be negative.CameraProjection.getProjectionMatrixForViewportlets a projection size its volume from the view's logical size, which rendering and picking both resolve against.- Shadows, AO, SSR, TAA, depth of field, god rays, GI, planar reflections, froxel lighting, LOD, and custom-pass depth/normals all work under orthographic and custom projections.
#include <view_projection.glsl>reconstructs view positions for any projection;PostCameraInfocarries its axis offset and orthographic flag in the formerly unusedwslots..fscenecameras serialize orthographic projections, the editor draws their view volume, andOrbitCameraControllerdollies an orthographic camera by zoom.- Gaussian splats sort back to front and evaluate view-dependent color under orthographic cameras.
- BREAKING:
LightingtakesprojectionScaleX/Y,projectionOffsetX/Y, andorthographicin place oftanHalfFovX/Y, which remain as deprecated getters. - The vertex stage writes the world normal and tangent varyings at unit length, so a model authored at a small scale no longer lights black on GPUs that flush its tiny normals to zero in mediump varyings.
- Screen-size LOD applies to every perspective camera, not only
PerspectiveCamera. - Apps ship each engine shader bundle once and only for their own platform; a shared pub cache used to ship every platform's bundles, twice with data assets enabled.
dart run flutter_scene:initlists a directory per platform in the app's pubspec so the app's own generated shaders ship only for their platform too; rerun it in an existing app.Scene.addTickListenerruns aSceneTickListenerat the start of every tick and before every fixed step, ahead of all components, for per-frame sampling such as input.FlyCameraController.setMoveInputdrives movement from a gamepad, touch controls, or an input system, summing with the keys and keeping analog magnitude.- Cascaded shadows skip casters that cannot shadow anything the camera shades, cutting shadow-pass draws with no change to the rendered image.
- Skinned vertex shaders normalize the four joint weights, so a mesh exported with weights summing to slightly under 1 no longer stretches toward the origin as it moves away from it.
releaseTransientRenderTargets()drops the render graph's pooled attachments (shadow atlas, scene color, depth, the post-process chain) and returns the bytes released; they reallocate on the next frame that needs them.- Pooled render targets are released automatically on platform memory pressure (
releaseRenderTargetsOnMemoryPressureturns that off), andtakeMemoryReport()reports them as arender targetscategory. - Surface debug views.
Scene.debug.viewshows a resolved material channel (base color, roughness, metallic, every physical field), a geometry attribute (normals, tangents, UV sets, vertex color, face orientation, UV checkers), an identity color per object or material, or a validation flag (NaN/Inf, albedo range, non-binary metallic, missing tangents, UV range) in place of the lit result, on every material including.fmatones, at runtime in any build.Scene.debug.splitcompares a view against the lit image,DebugViewcarries a range, gain, and out-of-range policy,Node.debugViewoverrides or excludes a subtree, andScene.debug.overlaysadds a wireframe drawn through each mesh's own vertex path.DebugViewRegistrylists the views by id for tools; a.fmatshows any value throughmaterial.debugand thecustomchannel. GeometryBufferArenafills the first block with room instead of abandoning a block's tail once an upload does not fit, and its docs now say it never reclaims space.DebugDraw.flushIntorebuilds one updatable line geometry in place each frame (DebugDraw.createGeometrymakes it) instead of allocating a geometry per flush; the Kit example uses it.Scene.renderStatsreports every frame's draw, instance, vertex, culling, batching, and pipeline counters per view and per pass with CPU times, plus a bounded history; the render graph also emits adart:developertimeline event per pass for DevTools.ShaderReflectionparses any loaded shader bundle into per-shader, per-backend reflection (inputs, uniform block layouts, texture bindings, entrypoint) and the compiled source for every backend it holds, maps shader objects back to their names, and decodes packed uniform bytes through the layout.- A render graph capture now records every draw call (
CapturedDraw, with node, material, shader pair, pipeline, counts, batch size, why an opaque run ended, and the uniform blocks bound for it) and every skipped item with its reason. RenderGraphCaptureResult.toJsonandfromJsonserialize a capture with PNG thumbnails and decoded uniforms, so a capture can be attached to a bug report and opened elsewhere..fmatcompile failures expose parsed diagnostics (FmatCompileException.diagnostics) with line numbers, andshaderSourceWindowrenders the marked source around one.- Projected box decals via
DecalNode, an oriented projection volume that paints a.fmatmaterial onto whatever opaque surfaces it intersects (scorch marks, splats), with no mesh work at the impact site. - Radial screen distortion pulses via
Scene.screenDistortion, expanding shockwave rings that warp the composed image with optional chromatic fringing. .fmatmaterials acceptblending: additivealongsideopaque/alpha, anddepth_write/depth_testconfigure the translucent depth state.- Barycentric wireframe helpers,
#include <wireframe.glsl>plusMeshData.unweldbarycentric attributes, for wireframe and scan-line overlays on any mesh. MaterialGrouppushes one parameter value to a heterogeneous set of materials, skipping names a member does not declare.- Unlit
.fmatmaterials may declareengine_inputs; the screen accessors gainGetSceneWorldPosition, materials that read scene depth get it at full resolution, andscene_color_reachbounds a reader so disjoint readers share one scene-color capture. - A draw whose render pipeline the backend rejects is skipped with a one-time console message naming the material and geometry, instead of failing the whole frame from inside paint.
- A render pipeline build that stalls a frame (8ms or more, always on its first draw) is reported in debug builds with the material and geometry that triggered it, so the fix (drawing it once behind a load screen) has a target.
Scene.shadowCasterOverflowCountandScene.isShadowCasterGrantedreport the lights whose authored shadow is not being drawn, since the shared shadow atlas caps shadow-casting spots and point lights (four each) and the lights past the cap previously went dark silently.Scene.globalIlluminationProbeGridreports the probe lattice the irradiance field resolved this frame (origin, spacing, and probe counts as anIrradianceProbeGrid), so an editor or debug overlay can draw where the probes actually are instead of re-deriving a placement that depends on the volume mode, camera, and scene bounds.Node.shadowCastingModeselects how a node's meshes cast:on,off(excluded from every shadow map while still drawing),doubleSided(casts from every face, closing light leaks through single-sided geometry), orshadowsOnly(casts while staying out of the color image, for proxy and off-screen occluders). ReplacesNode.castsShadows, which stays as a deprecated two-state view of it, and is carried by.fscenedocuments and the editor's node inspector.SpotLight.shadowCasterChannelMaskandPointLight.shadowCasterChannelMaskdrop casters from one light's shadow map by light channel, matchingDirectionalLight's, so a node can be lit by a light without casting into it.- Point lights cast shadows:
PointLight.castsShadowplus per-light resolution, near, bias, softness, and caster-face controls. Six 90-degree cube faces render into the shared shadow atlas (four quarter-resolution faces per tile, two tiles per light) and the lit shader picks the face by the dominant axis of the light-to-fragment vector, so point shadows cost no new samplers or shader variants. A limited number of point casters render per frame; the rest shade unshadowed. Scene.smaa(SmaaSettings) tunes SMAA's edge threshold, search steps, and corner rounding at runtime, previously compile-time constants..fscenestage effects now apply and serialize temporal anti-aliasing tuning and SMAA quality (the fields previously round-tripped through documents without reaching the scene).- The bundled agent skills cover this release (
flutter_scene-idiomsv14,flutter_scene-kitv7,flutter_scene-looksv5,flutter_scene-performancev3,flutter_scene-proceduralv4,flutter_scene-verification-loopv4), including game input throughflutter_scene_input. Rundart run flutter_scene:skillsto update installed copies. - Switching
Scene.antiAliasingModefrommsaato another mode on the OpenGL ES backend no longer renders without a depth test (far surfaces drew over near ones); pooled color targets now keep a separate texture per depth attachment setup. Scene.renderQuality(RenderQualitySettings) puts the automatic settings on a quality ladder:AntiAliasingMode.autoand the scene color capture budget follow the tier (web and Linux desktop start at medium, everything else at high), andadaptivelowers the render scale and then the tier from measured frame periods when frames overruntargetFrameRate, recovering when they keep it.Scene.effectiveRenderQualityTierandScene.adaptiveRenderScalereport what is in effect.Scene.sceneColorCaptureBatchescaps how many scene color captures a frame opens for overlapping transmissive readers (1 makes them all share one snapshot), the biggest lever on tiled and low-end GPUs; null follows the quality tier.- Smooth transmission (zero roughness, or an index of refraction of 1) no longer builds the rough-transmission filter pyramid every capture, which the shader never sampled.
- Material fragment shaders default to mediump with explicit highp on positions, coordinates, depth, and the HDR accumulators (see
shaders/PRECISION.md), so GPUs that run half precision faster (Mali, Adreno, the web) shade the standard and physical materials at their fp16 rate. Metal is unaffected. A.fmatbody inherits the default and should declarehighpon positions or coordinates it computes itself;noise.glslruns in highp regardless, since its parity with the Dart port is a float32 contract. Scene.maxGpuFramesInFlightpaces the GPU: once that many frames of GPU work are still running, a screen view presents its previous image instead of encoding a new frame, so a GPU-bound scene no longer stalls the UI thread on the Vulkan and Metal backends, which queue work from the calling thread. The default of 1 keeps the UI thread free for about a tenth of GPU throughput; 2 keeps the throughput but only halves the stall.Scene.pacedFrameCountcounts the frames it held, andScene.repaintRequestednotifies when the held frame can be painted, so a view that repaints only on demand still shows it (SceneViewlistens).ThirdPersonControllerComponent.rotatesToMovementkeeps the node's authored rotation while still moving it, andyawexposes the smoothed heading, now seeded from the node's rotation instead of snapping to zero on the first step.DebugDraw.colliderswireframes every physics collider in a node subtree, posed the way the simulation sees it, andDebugDraw.shapedraws oneShapeat a given transform. Triggers draw in their own color.SceneViewmeasuresonTickdeltas with a wall clock instead of the ticker's frame-begin timestamps, whose deltas alternate between tiny and double-length values under GPU load and stagger any motion integrated against them. The newSceneView.clockinjects a clock for tests and time-controlling drivers; the ambientpackage:clockclock (faked underflutter_test) is the default.Scene.punctualLightOverflowCountreports how many drawable items dropped punctual lights last frame because more lights reached them than the per-object budget shades.- A
-split<N>suffix on a glTF node name splits its mesh into grid-cell child meshes at import (whole triangles binned by world-space centroid intoN-unit cells, children namedGround_x0_z3), so a level-spanning mesh culls and receives punctual lights per cell; the split reapplies on every re-import. - Punctual lights shade through per-view froxel clustering (screen tiles crossed with exponential depth slices, each shading only the lights that reach it), removing the per-object light cap, so a large mesh reached by many lights shades them all and no draw carries light state. Perspective views cluster automatically; orthographic views and frames using light channel masks keep the per-object lists, and
Scene.punctualLightClusteringdisables clustering for comparison. - The punctual light loop is dynamically bounded (every compiled shader dialect is GLSL ES 3.00 or newer), so drivers cannot unroll it, and a froxel holds up to 255 lights.
- Normals transform by the inverse-transpose of the model matrix, so a node under non-uniform scale or shear lights the same as the shape modelled at that size.
- BREAKING for classes that
implementsrather than extends these types:GeometrygaineduploadVertexStreamsandsetDepthOnlyVertex,MaterialgaineddepthLayeranddisplayReferred,CameragainedprojectToScreenUv,CameraProjectiongainedgetProjectionMatrixForViewport, andComponentCodecgainedwriteLiveProperty, all with default implementations, alongside several internal engine members.IrradianceFieldBakeSteppertakes a requiredpointShadowFrame, and itsrenderViewcallback receives it. - BREAKING:
Scene.initializeStaticResources()completes with its error instead of normally, so a failed shader-bundle or material load now reaches the caller rather than onlydart:developerlog()(which web never showed). Awaiting it without atry/catchthrows where it previously continued; wrap the call, or use aSceneView, which reports the failure and stays on itsloadingBuilder.baseShaderLibrarynames that cause too, rather than telling you to await the call that already failed. - A rejected collider names the node it was on and the degenerate cases that cause the rejection.
- Batching comparators cache their identity keys,
sceneSortDepthallocates nothing, and instance batch objects are pooled, cutting per-frame work that multiplies across shadow, depth-prepass, and reflection views. - Allow
code_assets2.x. - Fix vertex-attribute traffic on the web backend growing quadratically across same-pipeline draws.
- On web, shader bundles and their generated JSON revalidate with the server on load, so a browser cache can no longer pair a previous build's bundle with new Dart code.
- Spatial audio follows a
SceneView's camera (orcameraBuilder) when no scene camera orAudioListeneris set, instead of a listener stuck at the origin. - Fix the mip-sampling probe reporting the base-mip clamp on hardware that samples mip chains correctly, about one cold run in three on a Mali-G57. It read its render target back without waiting for the draw, which the OpenGL ES backend runs on the raster thread, so an unrendered target answered with whatever its memory held. A false reading dropped every uploaded mip chain for the life of the process and moved image-based lighting to the radiance atlas, silently.
- The split-sum environment BRDF ships precomputed as
assets/dfg.bininstead of being integrated at every cold start, takingScene.initializeStaticResources()from 274ms to under 20ms on a Galaxy A16. The table is a constant, so the asset is bit-identical to the integration; an app that trims it still gets a correct table, built off the render isolate and with a message saying what it costs. - The CPU mip build is roughly twice as fast for color and normal textures: the content switch moved out of the per-texel loop, the normal filter's taps are unrolled, and the sRGB decode reads a 256-entry table instead of calling
powthree times per texel. A 2048-square color chain drops from 141ms to 55ms. - Radiance prefiltering fills one roughness band per frame instead of submitting the whole atlas as a single draw. That draw was ~850ms of GPU work on a Mali-G57, and because it lands inside a Flutter frame the display froze until the driver's buffer-queue timeout; a cold start that built two environments spent 1.7 seconds of 3.1 frozen. The texture is returned immediately and sharpens over the following frames, and
EnvironmentMap.radianceCompletewaits for the finished result. - The radiance prefilter takes its mirror band directly rather than integrating a delta lobe 256 times, and generates its samples from a rank-1 lattice instead of a float-emulated radical inverse whose loop cost more than the texture fetches it fed. Together about half the prefilter's GPU cost, with no change to the rendered result.
- Fixed animating glTF rigs with a joint named
root, whose channels drove the import root instead, dropping the Z flip and leaving the joint still. - On web,
EnvironmentMap.fromEquirectImageAssetandimageFromBytes(maxWidth:)no longer fail with "ImageDescriptor.width is not supported". - The Dart noise (
FastNoiseLite,noiseCurl3, particle turbulence) now matches native exactly on web; 3D OpenSimplex2 previously returned huge values there, driving particles to NaN. - On web, an int
.fmatparameter saved into an.fscenedocument keeps its int type instead of becoming a double.
0.23.0 #
- Build hooks no longer crash on a target OS
package:code_assetscannot name, which is how a third-party embedder announces tvOS or visionOS. - tvOS, visionOS, and watchOS select the iOS Metal shader variant, so an app on an Apple embedded platform finds its shader bundles and materials instead of looking for a target nothing builds.
- Added
package:flutter_scene/kit.dart, high-level gameplay, camera boom, character controller, day/night cycle, water surface, audio, pooling, and debug visualization components. - BREAKING: model-space front faces wind Counter-Clockwise (CCW) now, across primitives, procedural builders, materials, and
.fsceneversion 5, matching glTF and the rest of the ecosystem. Version 4 documents swap index pairs on read and version 4 containers migrate at realization, so assets need no action, but hand-authored index buffers and customGeometrysubclasses wind the other way and need their triangles swapped. - World-space global illumination via
Scene.globalIllumination, a grid of octahedral irradiance probes fed by scattering the visible surfaces' shaded radiance, so bounce light persists for geometry the camera is not looking at and colored bleed reads correctly. Off by default. - Probe visibility via
GlobalIlluminationSettings.visibility, per-direction depth moments and a Chebyshev bound so the field does not leak through walls. - Emissive surfaces light the probe field, scaled by
GlobalIlluminationSettings.emissiveGiBoostso a small bright emitter can light a room without brightening the direct image. - Offline and progressive irradiance field baking via
Scene.bakeIrradianceFieldandIrradianceFieldBakeStepper. IrradianceVolumeComponentplaces the irradiance volume on a node, for authored placement instead of fitting the scene or following the camera.Scene.invalidateGlobalIlluminationdiscards the accumulated field so it refills from scratch after a hard camera cut.- The lit shaders read the environment's diffuse coefficients with
texelFetchfrom a texture the probe atlas extends, so global illumination costs no additional fragment sampler. - Add the
flutter_scene-kitagent skill, covering the gameplay, camera rig, character controller, day/night, water, audio, pooling, and debug components inkit.dart. - Update
flutter_scene-idiomsskill (v4) with the reflection probes and lens flares the engine gained, and the traps that came with them. - Update
flutter_scene-performanceskill (v2) with SMAA's cost next to FXAA and the per-frame reflection-probe recapture trap. - Update
flutter_scene-proceduralskill (v2) with natural formation recipes (rock fractures, incised trails, pebble scatter, Gerstner waves, tree branching, and procedural island topographies). - Update
flutter_scene-looksskill (v4) with micro-surface and anti-waxiness tuning guidance. - Update
flutter_scene-verification-loopskill (v2) with empirical verification disciplines and blind-pairwise review rules. - Temporal anti-aliasing via
AntiAliasingMode.taaandScene.temporalAntiAliasing, reprojecting history with camera subpixel jittering, motion vectors for unskinned and skinned geometry, and variance-guided history clamping. - Geometric specular anti-aliasing adapts specular roughness based on screen-space normal derivatives to suppress high-frequency specular shimmer on subpixel geometry and grazing angles;
Material.specularAaThresholdconfigures the threshold. DirectionalShadowFilter.bilinearPcfcontinuous 2x2 bilinear percentage-closer filtering.CameraProjection.getProjectionMatrixadds an optional namedjitterparameter for temporal subpixel sampling.- Direct lighting modulates specular and clearcoat terms with
sun_visibility. - Added
ExternalTexture, aTextureSourcefed by a platform texture id (video, camera preview) with optionalcolorFilter, captured through the compositor on frames that sample it. - Generated scene registries are cached per asset bundle.
.fscenebpayload chunks compress with gzip, cutting file sizes by ~73% in exchange for higher load-time decompression CPU.- Static render items skip per-frame property re-assignment when their transform and bounds are unchanged.
- Instanced mesh static shadow updates track component visibility changes correctly.
- Widget-surface pointer forwarding dispatches events in the framework's global space (with the host's transform in the hit path), so widgets that convert
globalPositionthrough render objects (Slider, text selection) work when the scene view is not at the window origin. - The standard PBR shader ships a no-shadow variant, selected automatically for draws with no shadow atlas bound, roughly halving the compiled fragment program for shadow-less scenes on mobile GPUs; it also skips its five per-texture UV-transform evaluations when every transform is identity.
PhysicallyBasedMaterialkeeps a scalarior,specular, andspecularColoron the standard shader by folding them into its dielectric reflectance (theKHR_materials_ior/KHR_materials_specularformula the physical shader uses), so only specular textures and the layered features select the heavier physical variant.- The model scale is no longer interpolated per fragment (
v_model_scaleleft the vertex output interface, cutting varying bandwidth on tiler GPUs); lit materials read it fromFragInfo.model_scalethrough the unchangedGetModelScale()accessor, now carrying the node's scale rather than per-instance or per-joint scale, and a rawShaderMaterialpair that needs it passes its own varying computed from the model transform. - glTF import validates
extensionsRequiredand surfaces warnings through anonWarningcallback on every entry point. - Sparse glTF accessors apply, including the zero-filled base for an absent
bufferView. KHR_draco_mesh_compressiondecodes in pure Dart on every platform.EXT_meshopt_compressiondecodes in pure Dart;KHR_mesh_quantizationis recognized alongside it.- Standard KTX2 textures (
KHR_texture_basisu) load on both import paths, transcoded in pure Dart. - The runtime importer accepts multi-buffer
.gltfdocuments and percent-decodes resource URIs. MeshPrimitive.visibleandMeshPrimitive.castsShadowtoggle one primitive's color and shadow passes independently.MaterialParametersgains typed getters,hasParameter,isParameterAssigned, and parameter enumeration.- Light channels,
channelMaskon lights andNode.lightChannelMaskrestrict what a light illuminates;DirectionalLight.shadowCasterChannelMaskfilters shadow casters independently. DirectionalLight.firstCascadeFarBoundpins the first cascade split;cascadeOverlapcross-fades cascade hand-offs.ShadowCatcherMaterial, an invisible ground showing only received shadows and ambient occlusion, live by default with a baked mode.- Morph targets import on both paths, animate via
weightschannels, and blend on the CPU or a GPU morph texture;Node.morphWeightsdrives them. EnvironmentMap.fromKtx2Bytesloads a pre-baked radiance cubemap with SH-9 diffuse, skipping the load-time prefilter.EXT_lights_image_basedimports as anImageBasedLightComponenton the model root.- SH-9 diffuse helpers,
parseDiffuseShSidecar/encodeDiffuseShSidecar,evaluateDiffuseSphericalHarmonics, anddescribeDiffuseSphericalHarmonicsfor verifying imported coefficients. PhysicallyBasedMaterial.lightmapTextureadds a baked lightmap that replaces the SH diffuse ambient..fmatinstance_attributesdeclare typed per-instance data, set viaInstancedMesh.setInstanceAttribute.- A raw
ShaderMaterialdeclares the same per-instance data throughShaderInstanceAttribute, so a custom vertex shader reads theinstance_<name>inputs a.fmatwould have generated. PlanarReflectorComponentrenders a mirrored scene capture that.fmatmaterials sample via theplanar_reflectionengine input.ShaderMaterial.sceneInputsdeclares the engine's per-frame scene textures, so a raw shader can refract and measure its own thickness like a.fmatmaterial. Include<scene_inputs.glsl>for the samplers and their accessors;buildTargetShaderBundleJsonputs flutter_scene'sshaders/on the include path so it resolves with no setup.- Parallax-corrected reflection probes via
ReflectionProbeComponent, capturing the scene into a local environment whose reflections track the probe's box. Scene.captureEnvironmentrenders the scene's lighting at a point into a newEnvironmentMap.- SMAA anti-aliasing via
AntiAliasingMode.smaa, cleaner edges than FXAA with far less texture blurring. - Lens flares via
BloomSettings.lensFlare, ghost chains and a halo with chromatic dispersion off the bloom pyramid. - Bloom now spreads a consistent fraction of the screen across device pixel ratios, so bloom and lens flares no longer tighten on high-DPI displays; its passes also stop relying on reloaded attachments, matching every backend.
- Clearcoat now responds to rect area lights.
- Screen-space indirect light reprojects its radiance history, so the bounce stays put under camera motion.
0.22.2 #
- Four new agent skills alongside
flutter_scene-idioms:flutter_scene-verification-loop(the run-settle-capture-correct visual loop and the blind-pairwise judgment rule),flutter_scene-looks(copy-pasteEnvironmentSettingspresets for a deliberate look),flutter_scene-procedural(terrain, noise, and instancing from code), andflutter_scene-performance(frame-budget measurement and a fixed remediation order).dart run flutter_scene:initanddart run flutter_scene:skillsnow install every bundled skill.
0.22.1 #
- Camera controllers
OrbitCameraController,FlyCameraController, andFollowCameraControllerdrive a camera node from drag, scroll, and keyboard input with frame-rate-independent smoothing and clamped pitch. TheCameraControlswidget wires Flutter input to a controller; controllers also expose intent methods (orbitBy,dollyBy,look, ...) for driving them from any source. Node.lookAt,Node.lookAtFrom, and the staticNode.lookAtTransformorient a node's forward axis (local +Z) at a world-space target, aiming cameras, lights, and imported models without hand-building a view matrix.- Ship the
flutter_scene-idiomsagent skill, correct-usage guidance for coding agents that corrects the wrong assumptions carried from other 3D engines.dart run flutter_scene:initoffers to install it, asking first at a terminal and never installing unattended without--skills. - Add
dart run flutter_scene:skillsto install, update, or--checkthe agent skill on its own, without touching the build hook or pubspec.
0.22.0 #
Node.position,Node.rotation, andNode.scaleread and write the local transform one component at a time, and editing a returned copy in place throws in debug builds rather than silently doing nothing.Node.mutateLocalTransformedits the local matrix in place through a callback and marks the node dirty, the correct way to reach for the raw matrix.- A frame that draws nothing now prints once in debug builds naming the likely cause (not ready, empty region, no views composite to screen, no visible meshes, or a layer mask matching nothing).
- Degenerate cameras now assert in debug builds, catching a view direction of zero length, an
upparallel to it, and a field of view passed in degrees. - The web backend now throws on a bind to a shader uniform or texture name that does not exist, matching native, instead of silently sampling whatever was bound last.
SkinnedGeometry.uploadVertexDataandsetCustomAttributenow throw on a buffer whose length does not match the vertex count, instead of rendering garbage.- A
Meshwhose primitive geometry is replaced now recomputes its bounds on its own, rather than over-culling untilmarkLocalBoundsDirtyis called. - An
AnimationClipthat binds zero of its channels now asserts in debug builds, naming the wanted nodes, since it otherwise plays while nothing moves. - Fixed the second and every later
flutter runrendering nothing, from two hook invocations writing the same shader bundle filenames with different backend trims. - Fixed
Scene.initializeStaticResources()reporting ready when the shader bundle loaded but held nothing this engine can read. - Fixed
dart run flutter_scene:initthenflutter runfailing on a new app with "Flutter failed to list directory". - Fixed
Node.clone()sharing the original's transform matrix. - Fixed the skinning joints texture being too narrow for small joint counts, which duplicated matrix columns.
- Fixed
ParticleSystem.reset()not restarting its random stream. - Mutating
Node.localTransformin place withoutmarkTransformDirty()now throws in debug builds instead of silently doing nothing. SceneViewsBuilderis now exported.- Documentation fixes for names that no longer exist (
Environment,loadModel, the master channel requirement), plus a readme scene that needs no asset files and a list of the built-in geometry.
0.21.1 #
- Documentation only. The readme spells out how to turn Flutter GPU on for each platform, shows the build hook and the pubspec entry
dart run flutter_scene:initwrites, recommends converting assets ahead of time over importing glTF at runtime, and links the guides at fscene.dev.
0.21.0 #
-
Requires Flutter 3.47.0 or newer. That is the first stable carrying the Flutter GPU support this package needs, so the constraint is a real minimum rather than the loose value earlier releases carried. A master build numbered
3.47.0-1.0.pre.Nsorts below it and will not resolve. -
Assets are generated into
flutter_scene_generated/at the app root and loaded by source path, the same way on every Flutter channel.dart run flutter_scene:initinstalls the hook, creates the directory with a.gitignorefor its outputs, and adds the one pubspec assets entry it needs. -
flutter pub add flutter_sceneis the whole setup. flutter_scene's own build hook compiles the engine's shaders for the app being built, on every channel, so an app with no hook of its own renders a runtime-imported.glbeverywhere including web. -
Compiled shader bundles record the engine that built them and are rebuilt when it changes, so switching Flutter versions cannot leave a bundle the new engine refuses to read.
-
buildEngineAssetsis an optional build-hook call that puts the engine's shaders in the app's own generated tree instead of flutter_scene's. -
Breaking change,
legacyOnlyanddataAssetsIfAvailableare removed in favor ofgeneratedTree, now the default inSceneAssetMode,TextureAssetMode,MaterialAssetMode, andTargetShaderBundleAssetMode. A hook still passing a removed mode fails the build naming its replacement, since outputs moved out ofbuild/into the generated tree and an app loading by explicit asset key reads its new keys from the tree'smanifest.json. Re-rundart run flutter_scene:initto migrate a hook and add the pubspec entry. The Dart data assets modes remain as explicit opt-ins and prune the tree entries they replace. -
A build with no generated assets now fails with the exact pubspec lines to add instead of producing an app that dies at its first load.
-
Fixed a
.fmatreference written against the package root binding to a path beside its scene document, which left the material unresolved at load and the geometry on an opaque fallback. A reference only rebases onto the document when a file is actually there, matching how the source-root loader reads the same scene. -
resolveShaderBundleKeyresolves a shader bundle built bybuildTargetShaderBundleJson(orflutter_gpu_shaders) by name, forloadShaderLibraryAsync. -
Build stamps fingerprint a source over a megabyte by size and modification time instead of reading it, cutting about 5 seconds per gigabyte off every build. Set
flutter_scene_strict_hashing: trueunderhooks: user_defines:in the app pubspec to content-hash everything. -
Switching asset modes needs a clean build. Nothing prunes assets from a previous mode out of an app bundle.
-
Breaking change,
buildScenesandbuildTexturesno longer takeoutputDirectory. Outputs go to the app's generated tree, so a hook passing it fails to compile; drop the argument. -
Breaking change,
FmatMaterialBundleIndex.fromJsonrequiresassetKey. The bundle and its sidecar resolve from the index's own key, so a direct caller passes the key it parsed. -
Node.extractMeshDataflattens a subtree's geometry into oneMeshDatawith the descendant transforms baked in, andMeshData.toTriMeshShape/toConvexHullShapeturn that into a collider, so an imported model no longer needs a hand-written walk to collide. -
MeshData.transformedplaces a snapshot by a matrix, carrying normals by the inverse transpose and, through a mirror, flipping both tangent handedness and triangle winding. -
The lit shaders declare one prefiltered-radiance sampler instead of one per layout, freeing two fragment texture units so a skinned shadow-casting draw fits GLES drivers reporting the 16-unit minimum.
-
Breaking change for raw
ShaderMaterialshaders that sample the environment, they declareprefiltered_radianceasRadianceSamplerand supply aradianceCubeFragmentShadervariant built withFLUTTER_SCENE_RADIANCE_CUBE..fmatmaterials are unaffected. -
A material or sky that samples the environment without a cubemap-radiance variant now keeps its 2D sampler and is bound a placeholder instead of a mismatched cubemap, and says so in debug builds.
PreprocessedMaterialtakesradianceCubeFragmentShaderfor code that builds one directly rather than through a loader. -
Breaking change,
EnvironmentMap.prefilteredRadianceTextureandEnvironmentMap.prefilteredRadianceCubeare gone. The single prefiltered-radiance sampler replaces the layout pair, so code reading either field reads the one texture the map now carries. -
RenderItem.windingFlippedderives from its geometry instead of being assignable, so a mirrored transform reverses winding without callers tracking it. -
Breaking change, the fscene component codec API is declarative.
ComponentCodec.serializeis abstract,ComponentPropertyDefandComponentPropertyKindmoved to thesceneschema model (constraints replacemin/max/read), and flat codecs extendDeclarativeComponentCodec, deriving schema, realization, and delta serialization from oneComponentFieldlist. -
.fsceneversion 3. Component properties delta-serialize (values equal to their schema default are omitted) and audio attenuation settings nest; version 2 documents read unchanged. -
package:flutter_scene/annotations.dart,@SceneComponentand typed property annotations that lower to schema constraints for generated codecs. -
Unknown component types realize as lossless
ForeignComponentplaceholders and round-trip untouched. -
Physics components register in any mount order. A rigid body, collider, joint, or character controller added before its world (or its sibling dependency) registers when the dependency arrives instead of staying inert.
-
registerPhysicsBackendis public, so physics backend packages can register simulation factories the way audio backends do. -
FsceneComponentRegistry.unregisterremoves a registered codec; documents carrying the type realize it as a lossless unknown-type placeholder. -
nodeRefOfandresourceRefOfrecover document references from live nodes and realized resources, so custom codecs (generated ones included) round-trip them. -
Debug builds launched with
FLUTTER_SCENE_SOURCE_ROOTread scene sources straight from the project, andext.flutter_scene.reloadScenepatches loaded scenes in place. -
Component schemas carry declarative editor gizmos (
ComponentSchema.gizmo, theGizmoSpecprimitive model, the@SceneGizmoannotation), and the builtin lights, camera, colliders, audio, environment volumes, and particle emitters declare them. -
Builtin component schemas declare richer editing constraints, the camera projection is a fixed-option enum, its field of view carries hard degree bounds, and shadow, area-light, and volume fields gained slider soft ranges.
-
Scene.captureRenderGraphrecords one frame's render graph (passes, CPU timings, data flow, snapshot copies of written targets) behind theScene.debugAllowRenderGraphCaptureopt-in, andRenderPassContext.debugBlackboardTextureexposes raw pipeline textures to debug overlays. -
Screen-space contact shadows for the sun via
DirectionalLight.contactShadows, grounding small contacts that shadow-map resolution and bias miss. -
Screen-space indirect light via
AmbientOcclusionSettings.indirectLight, crediting one bounce of scene radiance to newly visible sectors of the occlusion bitmask. -
Rect area lights via
RectAreaLightandRectAreaLightComponent, shaded with linearly transformed cosines so panel highlights stretch and fall off physically. -
Film-look color grading via
ColorGradingSettings.lut, loading Adobe.cubetables withColorLut.fromCubeAssetand blending withlutBlend. -
Environments persist the grading LUT (
.cubereference and blend) in.fscenedocuments, andColorLut.sourceAssetreports where a table was loaded from. -
Percentage-closer soft shadows via
DirectionalShadowFilter.pcss, sharpening shadows at contact and widening them with caster distance scaled byDirectionalLight.angularRadius. -
Depth of field focuses on translucent depth-writing surfaces (fmat
depth_write: true, transmissivePhysicallyBasedMaterial) instead of the backdrop seen through them. -
Ground-truth ambient occlusion via
AmbientOcclusionSettings.method, integrating horizon-slice visibility withsliceCount/stepsPerSlicecontrols and an optional constant-thicknessvisibilityBitmaskmode. -
AmbientOcclusionSettings.multiBouncekeeps albedo-tinted bounce light in occluded creases instead of darkening them to gray. -
AmbientOcclusionSettings.bentNormalssteers indirect diffuse along the average unoccluded direction and enablesSpecularAmbientOcclusionMode.bentCone. -
Environment resources persist every ambient-occlusion control, including the new method, slice, thickness, bent-normal, and multi-bounce fields.
-
AmbientOcclusionSettings.intensitydefaults to1.0and both methods are calibrated there; the obscurance estimator folds its former 2x, so halve any explicitly set obscurance intensity to keep its look. -
Retuned enable-time defaults, bloom intensity
0.15, chromatic aberration0.2, and auto-exposure clamps of -4 to +4 EV. -
Breaking change,
.fsceneversion 2 uses one native coordinate system and offline glTF import bakes source coordinates into it. -
Removed
Handedness,UpAxis,StageMetadata.unitsPerMeter,NodeSpec.excludeFromWindingParity,SkyEnvironmentSpec.castShadows, and the directional-light component schema'sdirectionproperty. -
Runtime glTF import keeps source buffers unchanged and resolves coordinate winding in the renderer.
-
Directional-light nodes aim along native local +Z and support explicit primary-light priority.
-
Fscene directional lights preserve the complete cascaded-shadow configuration.
-
Text scenes can reference a binary payload sidecar with
payloadSource. -
Fixed world-transform assignment under transformed parents corrupting the parent's cached transform.
-
Fixed in-memory glTF import dropping punctual lights and material variants.
-
GPU memory can be inspected and released.
takeMemoryReport()reports what the engine's shared caches are pinning,releaseTexture/releaseScenegive a claim back, andclearTextureCache/clearSceneTemplateCachedrop everything. -
ResourceGroup.trackscopes a load to the group's lifetime, sodispose()releases the claims it took. Previouslydispose()only released the progress notifier despite the name. -
Fixed
ResourceGroupthrowing when it was disposed while a tracked load was still in flight, which is what tearing down a level mid-load does. -
Fixed
loadScenekeeping a scene template claim when it threw after the template loaded, so a caller that got noNodeno longer has to issue a compensatingreleaseScene. -
PhysicallyBasedMaterialand glTF import support every ratifiedKHR_materials_*extension with scene-native property names. -
Scene startup loads the shared physical-material shader bundle so every
PhysicallyBasedMaterialproperty remains synchronous. -
Shader build hooks keep only the target platform backends in generated bundles.
-
KHR_texture_transformapplies independently to every built-in and advanced material texture. -
Geometry and material textures support both UV0 and UV1, including
textureInfo.texCoordin both glTF import paths. -
ShaderMaterialowns the vertex stage too. Pass avertexShader(perMeshVariant, so skinned and shadow passes can differ) and set uniforms and textures on either stage withShaderStage, so a hand-written shader pair needs no subclassing. -
Geometry can declare its own pipeline vertex layout with
setVertexLayout, andVertexAttributeDescriptor/VertexBufferDescriptor/VertexLayoutDescriptorare public. -
Custom vertex attributes work on skinned meshes.
-
A
ShaderMaterialthat supplies a vertex shader for some mesh kinds but not others warns in debug builds, naming the missing variant, instead of failing opaquely at pipeline creation. -
Debug builds check a custom material's uniform blocks against the shader's reflection, so a misspelled or optimized-out block name and a buffer too small for its std140 layout raise named exceptions instead of failing at bind time or reading past the end.
-
Debug builds check a caller-declared vertex layout against the buffers actually bound, so a slot count mismatch raises instead of feeding the pipeline undefined vertex data.
-
IndexType,VertexFormat, andVertexStepModeare exported, so 32-bit indices and caller-declared layouts no longer needlib/srcimports. -
Made first-party shader/material hooks and examples DataAssets-only, with migration guidance for legacy assets.
-
buildMaterialsregisters generated outputs as DataAssets when available by default. -
Added
Node.castsShadowsfor excluding individual meshes from shadow passes. -
Added
GeometryBufferArenaandGeometryBuilder.build({bufferArena, retainCpuData})for lower-overhead immutable mesh batches. -
Added per-instance colors through
InstancedMesh.addInstance(color:)andsetInstanceColor. -
Breaking change, instanced vertex layouts now require
instance_colorand use an 80-byte instance stride instead of 64 bytes. -
Added
InstancedMesh.updateInstanceTransformsfor allocation-light bulk motion andInstancedMesh.cullInstancesfor opt-in per-instance culling. -
Added
InstancedMesh.sortTransparentInstancesto skip unnecessary particle sorting. -
Added
RenderView.cullingPlanesfor application-defined frustum planes andScene.warmUp(includeOffscreen:)for full-scene preparation. -
Added
SunLight.cacheStaticShadowsandDirectionalLight.cacheStaticShadowsfor continuously changing shadow lights. -
Added
SunLight.shadowFilterandDirectionalShadowFilter.fixedPcffor stable lower-ALU shadow filtering. -
Tightened stable cascade fitting to increase effective shadow resolution.
-
Reduced shadow-map color bandwidth with single-channel fp32 tiles.
-
Fixed cascade-edge striping from normal-biased receiver positions.
-
Switched material and resolve color conversion to exact sRGB transfer functions.
-
Cached instanced bounds and render data to keep moving-camera culling allocation-light.
-
Skipped per-instance bounds packing when instance culling is disabled.
-
Skipped per-instance culling when an entire batch is inside every active plane.
-
Reused large transient buffers across changing instance counts.
-
Translucent instanced meshes now sort as one batch, with instances ordered within it.
-
Translucent draws sort back to front by view-axis depth through their geometry bounds center.
-
Double-sided lit materials reverse geometric normals on back-facing fragments.
-
Fixed instanced meshes losing their lighting, shadow, and fog bindings on GLES, which drew them (and any mesh sharing their pipeline) black.
-
Fixed anisotropy strength being ignored by environment lighting and using an isotropic visibility term for analytic lights.
-
Fixed anisotropic materials exposing primitive triangles by importing and interpolating authored tangents.
-
Vertex payload layouts include UV1 and tangents under versioned names, growing unskinned payloads by 50% and skinned payloads by 30%; older
.fscenelayouts upgrade when loaded. -
Fixed runtime glTF normal maps building color-filtered mips, which skewed minified normals and washed out reflective materials.
-
Fixed uncompressed
.fscenebtextures uploading without mipmaps, so a cooked scene is mipmapped whether or notcompressTexturesis set. -
Fixed cooked
.fsceneband.fstextextures sampling without anisotropic filtering, which blurred them at grazing angles. -
Texture resources record what their pixels represent, so mip levels downsample by role (normal maps average as vectors, color in linear light).
-
buildSceneswarns when a texture is not block aligned and falls back to uncompressed, instead of dropping the compressed form silently. -
buildScenesandbuildTexturestakealignForCompressionto resample a misaligned source up to the next multiple of 4 rather than failing or storing it uncompressed. -
Cooked textures build their mip chain on a background isolate, so a large uncompressed scene no longer pays for it on the calling thread.
-
Devices measured to sample every texture at its base mip skip mip chains entirely, reclaiming the memory and transcode time they were spending on levels the sampler ignores, and say so once at startup.
-
Loose images cook to compressed, mipmapped
.fstexthroughbuildTextures, loaded by source path withloadTexture(shipped in 0.20.0, missing from its notes). -
Fixed a
RangeErrorwhen an updatable mesh rebuilds at a smaller vertex count. -
Fixed ambient-occlusion banding and over-darkening with normalized sampling, robust normals, and depth-aware filtering.
-
Ambient occlusion gains power, detail, horizon, direct-light, depth-mip, and specular controls. Intensity now scales obscurance, defaults to
2.0, and its former contrast role ispower. -
Baked and screen-space ambient occlusion now combine with
mininstead of multiplication. -
Added
ToneMappingMode.agxwith configurableScene.agxWhiteandScene.agxContrast, persisted by environment resources. -
Added
Material.depthBias, a world-space camera offset for coplanar surface details. -
Breaking change, custom
Geometry.bindoverrides must add the named parameterdouble depthBias = 0.0. -
EnvironmentSettingspreserves every ambient-occlusion control and can apply only its environment look withapplyLookTo. -
Added
EnvironmentMap.constantDiffuseand declarativeConstantEnvironmentambient lighting. -
Added declarative torus and icosphere geometry specs.
-
ParticleSystem.prewarmis now readable after construction. -
Nested prefab material overrides and linked material/scene references now compose and round-trip.
-
Custom asset bundles can load fmat shaders from bytes and provide their own fmat material loader.
-
Cooked glTF scenes import photometric punctual lights for use with physical camera exposure.
0.20.0 #
- The
.fscenedocument core (document model, stable ids, JSON/binary serialization, prefab composition, diffing) moved to the new pure-Dartscenepackage;flutter_scenere-exports it, so existing imports are unchanged. - Fixed the web backend dropping every uniform-block draw on drivers that pad uniform block sizes (Mesa on Linux).
- New declarative scene API.
SceneView.declarative,SceneNode,SceneMesh, andSceneModeldescribe scenes inbuild(), with bridges to and from the imperative API, which is unchanged. - Declarative animation control via
SceneModel.animations(SceneAnimationSpec), plus cached model templates so equal sources load once and share GPU resources. KHR_materials_variantssupport in both import paths viaMaterialsVariantsComponent, declaratively viaSceneModel.variant, with a new Configurator example.- Fixed animation blending collapsing rigs with mirrored bones.
- Fixed slerp taking the long arc when interpolating between quaternions in opposite hemispheres.
- Automatic exposure (eye adaptation) via
Scene.autoExposure, metered entirely on the GPU. - Imported materials keep their source names via the new
Material.name. - Fixed cloned skinned meshes all drawing with the last-updated skeleton.
- Physics backends now implement the pure
PhysicsSimulationcontract from thescenepackage and no longer depend on Flutter, so the same simulations run under plaindart run. - One generic physics component layer for every backend.
PhysicsWorld(RapierWorld()), plus concreteRigidBody,Collider, the joint components, andKinematicCharacterController; the backend-specific component classes are gone. - Backend-specific physics features stay first-class.
RigidBody.handle,Collider.handles, andJoint.handleare public, so downcastingPhysicsWorld.simulationto a backend reaches capabilities beyond the shared contract, and the components subclass for typed backend components. - Physics is now an optional sub-barrel. Import
package:flutter_scene/physics.dart(orpackage:scene/physics.dartfor the pure contract); the corepackage:flutter_scene/scene.dartno longer carries physics types. BasicSimulation(queries and triggers, pure Dart) replacesBasicPhysicsWorld/BasicCollider/BasicKinematicBody.- Colliders without a sibling
RigidBodynow attach as static geometry instead of throwing. - Spatial audio through an optional pluggable-backend contract.
AudioEngine(implemented byflutter_scene_soloudandflutter_scene_fmod), withAudioListener,AudioSource/ClipAudioSource,AudioBus, and distance attenuation. Importpackage:flutter_scene/audio.dart; it is not in the core barrel. - CPU particle systems are now public.
ParticleEmitterComponent(camera-facing billboards),MeshParticleEmitterComponent(instanced 3D debris), andTrailComponent(ribbon trails), all driven by aParticleSystembuilt from emitter shapes, spawners, over-life distributions, and a module stack (acceleration, drag, size/color-over-life, rotation, flipbook, curl-noise turbulence), with new Particles and Explosion examples. - Sprites and billboard particles depth-test against opaque geometry again, so closer objects occlude them.
0.19.0 #
- Steady-state frame allocations cut sharply. Fullscreen passes reuse cached pipelines and per-draw uniform packs reuse scratch buffers.
- Faster draw submission. Bindings clear only on pipeline change and the engine lighting set rebinds only when it actually changes.
- Cheaper
MeshGeometry.fromArraysfor streamed meshes. Bulk attribute copies, no repack for typed index lists, and an optional caller-suppliedbounds. - Depth of field with shaped bokeh via
scene.depthOfField, driven by a thin-lens camera model with three quality tiers. - Cached shadow tiles for nodes marked
shadowStatic, making static-world shadow rendering near free. - Alpha-masked materials are now alpha-tested in the shadow and depth passes, so cutouts cast and receive correctly.
- Materials can declare per-frame scene inputs (
scene_color,scene_depth) for refraction and depth-fade effects, andMaterialInputsgainsspecular. - Fixed
.fmatskies withrequires: [environment]sampling black on the roughness-mip layout, andShaderSkySource.sampledEnvironmentpins the sampled map. - The lit shader reads diffuse SH through one sampler, fixing skinned-mesh crashes on 16-unit GLES drivers (ANGLE on Windows).
- Fixed white speckles in compressed textures on devices preferring BC formats.
- Scene-graph conveniences.
Node.meshNodes,Node.combinedWorldBounds, andPerspectiveCamera.framing. - 3D Gaussian splatting. Load
.ply/.splatcaptures withGaussianSplatsand draw them withSplatComponent, with a new example. - Scene content can be exposed to assistive technology via
SemanticsComponent, including hosted widget-surface semantics. WidgetComponentgains opt-inocclusionHiding.- Added
Camera.worldToScreen. - A failed static-resource load no longer marks the engine ready to render; it retries instead of crashing mid-frame.
- Point and spot lights.
PointLightComponentandSpotLightComponent, unlimited BVH-culled lights, spot shadows,KHR_lights_punctualimport, and a Lights example. - Geometry readback and derivation.
Geometry.extractMeshData(),MeshDataops (unweld,extractEdges,merge), andLineSegmentsGeometryfor thick lines. - Per-frame transient GPU data rides a completion-aware arena allocator, fixing whole-frame aborts past ~1MB of transients and collapsing per-draw buffer writes.
- BREAKING: the custom-pass and material/geometry
bindsurfaces take aTransientWriterwhere they previously took agpu.HostBuffer. - Large speedup for many-draw scenes on the web backend. No more per-draw buffer ghosting, vertex state cached in VAOs, redundant sampler calls skipped.
- The web shim's
HostBufferis a faithful flutter_gpu port again. - On the web backend, an active but unbound uniform block reads zeros instead of rejecting the draw, fixing unlit
.fmatmaterials rendering nothing. - Fixed ambient occlusion and reflections sampling the wrong depth for double-sided materials.
- The
.fmatcompiler no longer lets declared but unread resources strip out of the generated shaders, which surfaced as build errors or draw-time crashes. - Built-in noise matched between CPU and GPU via
package:flutter_scene/noise.dartand#include <noise.glsl>(the Dart side is native-only for now). AmbientOcclusionSettings.depthMipChain(off by default) keeps occlusion accurate on grazing surfaces and large radii.- Distance fog via
scene.fog, with falloff modes, a height term, light in-scatter, and sky-color blending. - Custom render passes via
Scene.addRenderPass, powering the newNode.highlightColorselection outlines. - Custom passes can request scene buffers (
depth,normals,shadowMap) throughRenderInput. - Volumetric god rays via
scene.godRays. - Load gating and warm-up.
ResourceGroup,SceneView.loading/revealMinDuration,Scene.warmUp, and background-isolate GLB geometry packing. - Widget-texture captures stay on the GPU on texture-backed platforms, removing the per-capture CPU round trip.
.fmatmaterials gain a vertex stage (aVertex()hook,varyings, customattributes), applied consistently across the color, shadow, depth, and pick passes.- Built-in materials and geometries can now be constructed before
Scene.initializeStaticResources()completes. - Added
Scene.camerawithCameraComponentauto-promotion, and a default camera so a bareSceneView(scene)always renders. - Camera-facing billboards and sprites via
BillboardGeometry,SpriteMaterial, andSprite. - Directional-shadow controls.
shadowAmbientStrength,shadowCasterFaces, and cascades that extend toward the sun so long shadows stop dropping out. - Fixed the cascaded-shadow lookup misreading cascades on GLES and crashing the shader compiler on Windows.
- BREAKING: vertex layouts are described by
VertexLayoutDescriptorand unskinned geometry is structure of arrays (.fscenegainsunskinned_soa; old files still load). - New procedural primitives. Cylinder, capsule, torus, disc, ring, and icosphere, each with a
collisionShapephysics bridge. - Geometry level of detail via
LodComponent, with screen-size selection, hysteresis, and dithered cross-fade. - Image-based lighting prefilters into a mipped radiance cubemap, and
EnvironmentMap.fromEquirectHdrkeeps HDR intensity through the prefilter. - Spatial environment volumes with camera-driven blending via
EnvironmentSettings,Scene.baseEnvironment, andEnvironmentVolume. - Sky-driven sun light via
Scene.sunLight, aiming and coloring the directional light from the sky. - BREAKING:
.fscenestages reference an environment resource instead of inline stage fields, and prefab host-node additions areAttachments. - Added
Texture2D(generated mips, trilinear and anisotropic filtering) withTextureContent/TextureSampling; imported models are now mipmapped. - BREAKING: material texture slots hold a
TextureSourceinstead of a rawgpu.Texture. - Added
TextureAtlasfor uniform tile grids. - Added geometric specular antialiasing to
PhysicallyBasedMaterial. - Fixed the split-sum specular lookup sampling with its roughness axis flipped.
- Fixed image-based lighting taking the shadow-ambient gate and Fresnel term from the normal-mapped normal.
- Fixed
normalScalenot being applied to the perturbed normal. - Screen-space reflections via
Scene.screenSpaceReflections, with an example and live tuning panel. - Screen-space reflections fade out on rough surfaces.
- The environment BRDF lookup is generated at load as
RGBA16F, removing banding and the bundled PNG.
0.18.1 #
- No code changes. Reworded the package description and added the logo as a pub.dev screenshot.
0.18.0 #
- Offscreen render targets via
RenderTexture,Scene.views, and theRenderTextureViewwidget. - The prefiltered-radiance environment stores roughness bands as mips of one texture (smoother transitions, ~25% less memory).
- Fixed dim image-based specular lighting on the web backend by re-baking radiance after the first presented frame.
- The base shader bundle loads asynchronously on every backend; await
Scene.initializeStaticResources()before constructing geometry or materials. - The
flutterSDK constraint is now>=3.44.0; the real requirement is a 2026-06-09 or later master build (see the README). - Render targets serialize in
.fscenevia arenderTextureresource kind and a top-levelviewsarray. - Material texture slots accept a
RenderTexturefor live render-to-texture sampling. - Added render scaling (
Scene.renderScale) and composite filtering (Scene.filterQuality), with per-view overrides. - Added FXAA and an automatic anti-aliasing mode;
autois the new default, so backends without MSAA now get FXAA. - Adopted hardware instancing for
InstancedMesh. Breaking for directVertexLayoutconstruction, which now takes a list ofVertexBuffers. - BREAKING: package exports are explicit show lists; file an issue if a symbol you used disappeared.
- Added scene raycasting via
Scene.raycast/raycastAll, with layer masks andNode.raycastable. - Added
WidgetComponent, a live interactive widget subtree on a scene surface. - Added automatic widget-surface input, with
WidgetInput.manualopt-out andSceneView.debugWidgetInput. - Added
ScenePointerfor programmatic scene input. - Added
WidgetTextureandCamera.screenPointToRay. - Added
UnlitMaterial.alphaMode. - Added
Surface.lastSwapchainColorTexturefor one-frame feedback effects. - Fixed vertically flipped texturing on the cuboid and wedge primitives.
0.17.0 #
- Added
SceneView, a widget that renders aSceneand drives its frame loop. - Added debug-mode hot reload for
.fmatmaterials and re-exported.glbscenes, patched in place. - Added DataAssets-backed GLB import with auto-discovery, cached composed documents, and
dart run flutter_scene:initwiring. - Added
AnimationClip.rebind/AnimationPlayer.rebindandMesh.clone. - Skinned geometry from
buildScenescarries a pose-union bound for sound frustum culling. - BREAKING: removed the
.modelformat; convert withbuildScenesor load GLBs at runtime. - BREAKING:
.fmatmaterials are discovered underassets/and loaded by source path vialoadFmatMaterial. - Building
.fmatmaterials and models requiresflutter_gpu_shaders0.5.0. - Added the
.fscene/.fscenebserialized scene format with prefabs, hot reload, and deterministic composition. - Added scene serialization via
serializeSceneandrealizeStage/serializeStage, round-tripping byte-stably. - Added opt-in KTX2 texture compression for imported scenes, transcoded at load off the main isolate.
- BREAKING: fixed vertically inverted image-based lighting; loaded panoramas now light correctly.
- Added a skybox via
Scene.skyboxand the built-inEnvironmentSkySource. - Added custom sky shaders via
ShaderSkySourceand.fmatskyblocks. - Added sky-driven lighting via
EnvironmentMap.fromSkyandScene.skyEnvironment, with time-sliced re-bakes. - Added built-in procedural skies,
GradientSkySourceandPhysicalSkySource. - Diffuse SH coefficients are sampled from a texture, and
fromGpuTexturesaccepts a GPU-computeddiffuseShTexture. - A
.fmatcompile failure during hot reload keeps the last good shaders and reports the error. - Build-hook conversions are cached by input content; set
FLUTTER_SCENE_DISABLE_BUILD_CACHEto always reconvert. - Fixed progressive slowdown on the web backend from leaked framebuffers and per-frame program re-links.
0.16.0 #
- Added an abstract physics contract (bodies, colliders, joints, queries, events) with a built-in kinematic world; a full backend ships in
flutter_scene_rapier. - Added
WedgeGeometry, a triangular-prism ramp primitive. - Added optional screen-space ambient occlusion via
Scene.ambientOcclusion, working on every backend.
0.15.1 #
- Added a DataAssets-backed
.fmatmaterial workflow with auto-discovery. - Added
dart run flutter_scene:init. - Added
FmatMaterialRegistryandloadFmatMaterial. - Updated
flutter_gpu_shadersto^0.4.5and moved hook-time dependencies tohooks2.x/data_assets0.20.x.
0.15.0 #
- Added the
.fmatcustom-material format, compiled bybuildMaterialsand driven at runtime with typed parameters. SeeMATERIALS.md. - Added a post-processing suite via
Scene.postProcess(bloom, color grading, vignette, chromatic aberration, film grain), each off by default. - Added
PostEffectfor custom post-processing shaders. SeePOST_PROCESSING.md. - The tone-mapping pass is now the resolve pass (exposure, grading, tone map, EOTF, bloom composite).
- Fixed image-based lighting on the OpenGL ES backend.
- Building
.fmatcustom materials requiresflutter_gpu_shaders0.4.4 or newer.
0.14.2 #
- Fixed mirrored geometry rendering inside-out; cull winding now follows the world-transform determinant.
- Fixed
material.doubleSidedbeing ignored by the runtime glTF importer.
0.14.1 #
- Now WASM-compatible; build-hook helpers no longer pull
dart:ioonto the web graph. - Added a package example and a fuller description.
- Bumped
flat_buffersto^25.9.23. - Internal lint and formatting cleanup.
0.14.0 #
Renderer overhaul, with several breaking changes.
- Rendering is structured as a render graph (
ShadowPass?->ScenePass->TonemapPass) with a transient-texture pool. - HDR pipeline. The scene renders to a float target and a tone-map pass writes the swapchain; custom shaders now output linear HDR premultiplied by alpha (see
MATERIALS.md). - Tone mapping and exposure moved onto
Scene(exposure,toneMapping,physicalCameraExposure). - Added
DirectionalLightwith PCF shadow mapping, assignable asScene.directionalLight. - Image-based lighting rework. SH-9 diffuse, a GPU-prefiltered specular atlas, and the old brightness fudges removed.
- BREAKING:
Environmentremoved in favor ofScene.environment(anEnvironmentMap) andScene.environmentIntensity. - BREAKING:
EnvironmentMapalways carries a prefiltered atlas plus SH-9; the proceduralEnvironmentMap.studio()is the new default. ShaderMaterial.useEnvironmentnow bindsprefiltered_radianceandbrdf_lut.- Web support. flutter_scene runs on Flutter web through a built-in WebGL2 backend, under both CanvasKit and Skwasm.
- BREAKING:
flutter_scene_importerfolded intoflutter_scene(package:flutter_scene/build_hooks.dart), with a curatedgpu.dartfor custom shaders.
0.13.0 #
- Added
ShaderMaterial, custom fragment shaders with uniform and texture binding by name and render-state knobs. - Added
MATERIALS.md, the custom-shader contract guide. - The example app gains a toon-shader demo.
0.12.0 #
- Added bounding-volume and frustum culling, with offline pose-union bounds for skinned content.
- New bounds API on
Geometry,Mesh, andNode, plusCamera.getFrustum.
0.0.1-dev.1 #
- Initial render box.
0.1.0 #
- Rewrite for Flutter GPU.
- Physically based rendering.
- More conventional interface for scene construction.
0.1.1 #
- Rename PhysicallyBasedMaterial and UnlitMaterial.
- Fix environment lighting problems in PhysicallyBasedMaterial.
- Add default environment map.
0.2.0 #
- Skinned mesh import.
- Fix readme for pub.dev.
0.2.1-0 #
- Switch to pre-release versioning.
- Bump version of flutter_scene_importer.
0.2.1-1 #
- Bump flutter_scene_importer version.
0.3.0-0 #
- Add animation playback (Animation, AnimationPlayer, AnimationClip).
- Import animations from scene models.
- Add support for cloning nodes.
0.4.0-0 #
- Support node cloning for skins.
- Fix default/animation-less pose.
0.5.0-0 #
- Support non-embedded/URI-only image embeds.
0.6.0-0 #
- Fix memory leak in transients buffer.
- Optional MSAA support on iOS and Android (enabled by default).
- Cull backfaces by default.
- Fix animation blending bugs.
- Pin native_assets_cli to <0.9.0.
- Add car model and animation blending examples.
- Fancy readme and FAQ.
0.7.0-0 #
- Update to native_assets_cli 0.9.0.
- Update to flutter_gpu_shaders 0.2.0.
0.8.0-0 #
- Update to Flutter 3.29.0-1.0.pre.242.
0.9.0-0 #
- Update to native_assets_cli 0.13.0.
- Update to flutter_gpu_shaders 0.3.0.
0.9.1-0 #
- Fix invalid usage of textureLod on desktop platforms.
0.9.2-0 #
- Fix globalTransform calculation.
0.11.1 #
- Fixed the
Node.globalTransformsetter scaling by the parent's determinant instead of composing with its inverse.
0.11.0 #
- Added a runtime GLB importer,
Node.fromGlbBytesandNode.fromGlbAsset. (#12) - Bumped
flutter_scene_importerto^0.11.0(pure Dart, no CMake).
0.10.0 #
- Migrated from the discontinued
native_assets_clitohooks1.0; build hooks now importpackage:hooks/hooks.dart. (#82) - Dropped the obsolete
--enable-experiment=native-assetsflag that broke builds on Dart 3.10+. (#82) - Reorganized the repository as a pub workspace. (#36)
- Updated
flutter_gpu_shadersto^0.4.0.
