Changes
- Add distanceSq by @AlexAPPi in #9313
- Add useFog/useTonemap to ParticleSystemComponent, deprecate noFog by @mvaligursky in #9340
- Load audio assets regardless of the URL's file extension by @mvaligursky in #9365
- Poll gamepads every frame without allocating by @willeastcott in #9470
- Run the engine headless in Node.js without jsdom by @willeastcott in #9504
- Replace batch.origMeshInstances[0] with sourceMeshInstance variable by @AlexAPPi in #9454
Rendering
- Store mesh instance matrices in a persistent storage buffer on WebGPU by @mvaligursky in #9614
- Add a bloom threshold to CameraFrame by @mvaligursky in #9455
- Add per-attachment clear colors to the camera for multiple render targets by @mvaligursky in #9333
- Support stencil clears inside a render pass on WebGPU by @mvaligursky in #9612
- Allow camera viewports extending past the render target on WebGPU by @mvaligursky in #9516
- Add partial vertex and index buffer uploads by @mvaligursky in #9401
- Add screen_size to forward view uniforms and deprecate uScreenSize by @mvaligursky in #9331
- Pack forward view uniforms to reduce alignment padding by @mvaligursky in #9332
- Move per-pass uniforms into the view uniform buffer by @mvaligursky in #9582
- Move textureBias into the view uniform buffer by @mvaligursky in #9484
- Supply per-pass textures through the WebGPU view bind group by @mvaligursky in #9521
- Publish the scene environment textures under their own uniforms by @mvaligursky in #9364
- Add an opt-in std140 packing order to UniformBufferFormat by @mvaligursky in #9400
- Stop creating typed array views for every dynamic uniform buffer update by @mvaligursky in #9418
- Retain WebGPU render pipelines between draws by @mvaligursky in #9380
- Look the WebGPU render pipeline up only when its state changes by @mvaligursky in #9492
- Write WebGPU buffer data directly from its storage by @mvaligursky in #9379
- Pass WebGPU bind group dynamic offsets as a typed array by @mvaligursky in #9487
- Skip WebGPU vertex and index buffer binds the render pass already has by @mvaligursky in #9496
- Keep the backing store of the device vertex buffer list across draws by @mvaligursky in #9498
- Assign bind group slots by index during the update by @mvaligursky in #9425
- Split the bind group update into a scope path and an owner path by @mvaligursky in #9429
- Leave a texture out of the mesh bind group when a supplied group has it by @mvaligursky in #9436
- Let the graphics device own the built-in textures by @mvaligursky in #9438
- Rebuild layer mesh arrays lazily after removals by @mvaligursky in #9378
- Pack the mesh instance light mask into 8 bits, for 24 shader define flags by @mvaligursky in #9617
- Rename supportsUniformBuffers to usesMeshBindGroups by @mvaligursky in #9383
- Split WebGPU device creation from device initialization by @mvaligursky in #9483
- Ignore MeshInstance#setIndirect where indirect draw is not supported by @mvaligursky in #9462
- Remove obsolete immediate mesh rendering by @mvaligursky in #9385
- Remove the shader dump and precompile workflow by @mvaligursky in #9477
- Remove obsolete Mac Chrome blitFramebuffer workaround by @mvaligursky in #9318
- Remove obsolete Safari texture unit workaround by @mvaligursky in #9319
Lighting & Shadows
- Move light uniforms into the view uniform buffer, uploaded once per pass by @mvaligursky in #9482
- Give every light an absolute slot, shared by all light masks by @mvaligursky in #9475
- Dispatch the lights of a pass once, not per light mask by @mvaligursky in #9479
- Consolidate light slots into a LightList shared by renderer, shaders and lightmapper by @mvaligursky in #9481
- Make world-cluster allocation request/execute based by @mvaligursky in #9473
- Fade directional shadows at the far distance by @mvaligursky in #9417
- Skip shadow sampling beyond the far distance by @mvaligursky in #9419
- Request directional shadow culling from scheduled render passes by @mvaligursky in #9423
- Request local shadow culling from scheduled render passes by @mvaligursky in #9428
- Cull only requested directional shadow cascades by @mvaligursky in #9437
- Skip spot VSM blur when the shadow pass does not execute by @mvaligursky in #9440
- Set shadow caster material state once per material run by @mvaligursky in #9524
- Derive the normal matrix of shader materials in the shadow pass by @mvaligursky in #9585
- Give a mesh instance lightmap its own texture slot by @mvaligursky in #9415
- Bind only the lightmap slots a bake writes by @mvaligursky in #9420
- Fix shadow shader selection when changing cascade count by @mvaligursky in #9343
- Reset clustered shadow lights before preparing each frame by @mvaligursky in #9414
- Fix WebGPU shadow shader compile error for PCSS spot lights by @mvaligursky in #9545
- Pass an empty LightList, not 0, to the shadow pass shaders by @willeastcott in #9556
- Hidden mesh instances with culling disabled no longer cast shadows by @mvaligursky in #9606
Materials
- Add a material bind group slot and cached shader processing keys by @mvaligursky in #9370
- Hold the textures of a material in its own bind group by @mvaligursky in #9457
- Bind the material bind group at a material switch instead of at every draw by @mvaligursky in #9413
- Store StandardMaterial diffuse in a material-owned uniform buffer by @mvaligursky in #9381
- Store emissive color and intensity in the material uniform buffer by @mvaligursky in #9392
- Store the remaining StandardMaterial colors and numbers in the material uniform buffer by @mvaligursky in #9398
- Store the derived StandardMaterial uniforms in the material uniform buffer by @mvaligursky in #9402
- Store the texture map transforms in the material uniform buffer by @mvaligursky in #9405
- Store the cube map projection box in the material uniform buffer by @mvaligursky in #9406
- Copy the cube map projection box into the material instead of keeping a reference by @mvaligursky in #9411
- Support mesh instance overrides of material uniform buffer values by @mvaligursky in #9386
- Let a mesh instance override a texture of the material bind group by @mvaligursky in #9445
- Restore global scope values after mesh instance parameter overrides by @mvaligursky in #9588
- Warn in debug builds when a mesh instance override array changes in place by @mvaligursky in #9395
- Warn about material and mesh instance parameters of view uniforms by @mvaligursky in #9619
- Add support for all eight UV sets in StandardMaterial by @mvaligursky in #9376
- Make alphaTest an accessor of Material and retire the generic float property helper by @mvaligursky in #9408
- Separate sprite color from material uniforms by @mvaligursky in #9337
- Separate UI element colors from material uniforms by @mvaligursky in #9338
- Pass the model scale to dynamic refraction from the vertex shader by @mvaligursky in #9616
- Fix LitMaterial ambient by making the ambient tint a lit argument by @mvaligursky in #9538
- Fix StandardMaterial shader invalidation for environment textures and 0/1 number changes by @mvaligursky in #9359
- Fix StandardMaterial shader invalidation for refraction thresholds by @mvaligursky in #9363
- Make StandardMaterial texture map iteration ignore prototype properties by @mvaligursky in #9368
- Stop placeholder textures from forcing shader recompiles by @mvaligursky in #9592
Gaussian Splatting
- Make gsplat LOD distance-only, with target or limit splat budgets by @mvaligursky in #9584
- Only judge oversized gsplat LOD nodes by their middle by @mvaligursky in #9586
- Gsplat LOD: empty level for nodes without coarse data; distance default by @mvaligursky in #9326
- Load streamed gsplat LOD files nearest the camera first by @mvaligursky in #9520
- Update StreamedGSplat presets to use LOD falloff by @mvaligursky in #9369
- Stochastic alpha rendering for the GPU-sort gsplat renderer by @slimbuck in #9443
- Flat per-splat gsplat varyings and fix ComputeRadixSort reallocation on growth by @slimbuck in #9372
- Allow either provoking vertex for WGSL splat varyings by @mvaligursky in #9374
- Prefer first provoking vertex on WebGL by @mvaligursky in #9375
- Use a per-axis focal length for the gsplat footprint by @dfattal in #9486
- Skip compute work for cached Gaussian splat shadows by @mvaligursky in #9416
- Make Gaussian splatting GLB support tree-shakeable by @mvaligursky in #9327
- Make GSplat scene parameters tree-shakeable by @mvaligursky in #9328
- Avoid retaining GSplat work buffer shaders in container bundles by @mvaligursky in #9322
- Apply withCredentials to the gsplat loaders that stream with fetch by @mvaligursky in #9458
- Warn when gaussian splats render into a multisampled target by @mvaligursky in #9532
- Fix gsplat GPU sort flicker from sort key precision loss by @mvaligursky in #9493
- Fix mirrored gsplat footprints in flipped render targets by @mvaligursky in #9490
- Fix fisheye gsplats and sky in flipped render targets by @mvaligursky in #9495
- Correct gsplat spherical harmonics with orthographic cameras by @mvaligursky in #9531
- Fix NVIDIA GPU hangs during gsplat depth picking by @slimbuck in #9572
- Prevent gsplat crash after destroying a splat entity by @mvaligursky in #9522
- Prevent gsplat streaming crash after destroying a camera by @mvaligursky in #9533
- Render gsplats again after a camera re-adds their layer by @mvaligursky in #9535
- Keep gsplat placement changes made in frame:ready handlers by @mvaligursky in #9536
- Stop a gsplat from blanking for a frame when its asset is replaced by @willeastcott in #9610
Physics
- Add RigidBodyComponent#gravityScale by @willeastcott in #9465
- Add a hitBackFaces raycast option to skip mesh collider back faces by @willeastcott in #9506
- Tighten the public surface of the physics API by @willeastcott in #9519
- Harden physics raycasts and forward the Application physicsWorld option by @willeastcott in #9517
- Ship an Ammo build whose heap starts at 16MB and grows on demand by @willeastcott in #9488
- Route the Ammo internal tick callback through one dispatcher per module by @willeastcott in #9463
- Apply gravity to the physics backend on install and when it changes by @willeastcott in #9439
- Refresh Ammo broadphase bounds when a body is teleported by @willeastcott in #9489
- Mirror mesh colliders with negative scale factors by @willeastcott in #9499
- Keep the orientation of mirrored dynamic bodies across physics steps by @willeastcott in #9500
- Fix mesh colliders sharing a mesh at different scales and rebuild them on scale changes by @willeastcott in #9311
- Sync compound children from their own transforms, not the root's dirty flag by @willeastcott in #9456
- Rebuild a collision component as a trigger when its rigid body is removed by @willeastcott in #9464
- Give both entities of a contact the normal of the other surface by @willeastcott in #9450
XR
- Stop a double XR start from leaving a broken session by @willeastcott in #9599
- Stop the app freezing when an XR session ends by @willeastcott in #9600
- Stop XR culling from using the previous frame's head pose by @willeastcott in #9601
- Remove XR event listeners that outlive their owners by @willeastcott in #9623
- Apply camera clip plane changes to a running XR session by @willeastcott in #9624
- Correct XR input source rays and velocities by @willeastcott in #9625
- Start XrSession sessions facing the way the camera looks by @willeastcott in #9626
- Keep removed XR planes and meshes readable, and harden hit testing by @willeastcott in #9627
- Keep XrControllers models loaded while input sources share them by @willeastcott in #9631
- Hide XrControllers models while their pose is not tracked by @willeastcott in #9635
- Stop XrNavigation teleporting on system menu gestures by @willeastcott in #9636
- Document the XR select events of ElementComponent by @willeastcott in #9633
UI
- Warn when image materials have no opaque render pass by @mvaligursky in #9346
- Keep an element's position when it is added to an entity under a screen by @willeastcott in #9525
- Let ElementDragHelper drag an element that is a direct child of a screen by @willeastcott in #9551
- Update a scrollbar's handle when its track is resized by @willeastcott in #9573
- Fix layout group subscriptions when components change by @mvaligursky in #9399
- Build text meshes without a pass over empty arrays by @willeastcott in #9562
- Skip drawing text with an empty render range by @willeastcott in #9567
- Draw CanvasFont glyph cells fully transparent by @willeastcott in #9568
- Fix a markup parser crash when a tag name shadows hasOwnProperty by @willeastcott in #9468
- Correct UI component docs, the Spanish locale fallback and button examples by @willeastcott in #9552
Device Recovery
- Restore WebGPU rendering resources after device loss by @mvaligursky in #9355
- Restore WebGPU profiling after device loss by @mvaligursky in #9357
- Fix WebGPU recovery for compute, readback and generated data by @mvaligursky in #9358
- Cancel WebGPU recovery when the device is destroyed by @mvaligursky in #9397
- Fix WebGPU resource cleanup on device destruction by @mvaligursky in #9394
- Restore the WebGL backbuffer before recovery callbacks by @mvaligursky in #9347
- Recover SOG streaming across WebGL context loss by @mvaligursky in #9350
- Restore procedural sky lighting after device recovery by @mvaligursky in #9351
- Add cross-backend debug device loss testing by @mvaligursky in #9353
- Restore generated HDR environments in examples after device loss by @mvaligursky in #9345
- Redraw the on-demand splat example after device recovery by @mvaligursky in #9348
- Rebake example lightmaps after device recovery by @mvaligursky in #9352
Profiling & Debugging
- Redesign MiniStats with compact and grouped profiling views by @mvaligursky in #9307
- Add collapsible MiniStats sections by @mvaligursky in #9361
- Add tracked resource counts to MiniStats by @mvaligursky in #9366
- Expose application performance statistics through AppStats by @mvaligursky in #9308
- Add user counters to AppStats by @mvaligursky in #9341
- Correct MiniStats timing and build availability documentation by @mvaligursky in #9310
- Add TextureRenderer for debug texture previews by @mvaligursky in #9382
- Add per-camera culling and display fixes to TextureRenderer by @mvaligursky in #9387
- Add a debug-engine draw limit for stepping through a render pass by @mvaligursky in #9501
- Add descriptive GPU profiling names for quad passes by @mvaligursky in #9329
- Track shaders on every graphics device by @mvaligursky in #9466
- Add a bare WebGL 2 device type for testing on constrained devices by @mvaligursky in #9471
- Announce apps to a devtools hook by @mvaligursky in #9618
- Add AppOptions#devtools to opt out of the devtools hook by @mvaligursky in #9637
Fixes
- Add missing TEXTURE_2D_ARRAY entry to targetToSlot by @mvaligursky in #9321
- Fix sprite tint color space conversion by @mvaligursky in #9334
- Clear keyboard and mouse states on detach and focus loss by @mvaligursky in #9344
- Reset stale mouse movement deltas by @mvaligursky in #9349
- Fix WebGPU texture-formats-tier feature names and rename their flags by @mvaligursky in #9390
- Fix skydome depth of field with scene-pass depth by @mvaligursky in #9391
- Wait for container meshes before marking render assets ready by @mvaligursky in #9404
- Fix Picker readback rectangle in the top-left to bottom-left flip by @mvaligursky in #9441
- Reconstruct the picked world position at the pixel center by @mvaligursky in #9446
- Call super.frameUpdate() from RenderPassCompose by @mvaligursky in #9449
- Copy the render style onto a batched mesh instance by @mvaligursky in #9448
- Fix CameraComponent#setShaderPass() throwing when resetting the pass by @mvaligursky in #9451
- Resolve glb model resources against the directory of the glb by @mvaligursky in #9453
- Stop leaking a DrawCommands set per indirect draw per frame by @mvaligursky in #9459
- Fix bundle asset load retrying and error-response handling by @mvaligursky in #9460
- Fix the WebGL multi-draw fallback submitting no draws by @mvaligursky in #9461
- Treat every 2xx HTTP status as success by @willeastcott in #9507
- Accept status 0 responses from non-http schemes by @willeastcott in #9508
- Fix Curve.closest outside unit range by @humairaambreen in #9543
- Return the first key from Curve#closest for times before the curve by @willeastcott in #9544
- Accept mesh instances without a material in Layer#addMeshInstances by @mvaligursky in #9579
- Skip disabled entities in OutlineRenderer by @mvaligursky in #9452
- Fix OutlineRenderer shader errors when clustered lighting is disabled by @mvaligursky in #9590
- Fix OutlineRenderer material cleanup and frameUpdate listener leak by @mvaligursky in #9589
- Fix OutlineRenderer outlines missing with some CameraFrame settings by @mvaligursky in #9594
- Blur the TAA output in the camera frame's bloom and depth of field by @willeastcott in #9591
- Fix calculateNormals returning NaN for vertices with no triangle area by @mvaligursky in #9596
- Fix FloatPacking.float2Half corrupting values too large for a half float by @mvaligursky in #9595
- Clean up pole and tip triangles in sphere, capsule and cone geometry by @mvaligursky in #9597
- Keep screen-space particles square on any canvas aspect by @willeastcott in #9570
- Export skinned meshes to USDZ where their bones place them by @willeastcott in #9546
- Use the WGSL built-in saturate, fixing lightVertexColor on WebGPU by @mvaligursky in #9598
- Flip the vertex normal of back faces for two sided lighting by @willeastcott in #9559
- Initialize GraphicsDevice renderPassIndex to 0 by @mvaligursky in #9605
- Load cubemap faces given as URLs as sRGB color, not RGBP by @mvaligursky in #9607
- Fix Texture#lock on cubemap faces and mip levels by @mvaligursky in #9608
- Fix WebGPU stencil read mask, reuse pipelines across stencil refs by @mvaligursky in #9611
- Make createGraphicsDevice reject instead of hanging when no device can be created by @willeastcott in #9603
- Depth state fixes (depthTest, WebGPU depth bias, layer reset) by @mvaligursky in #9615
- Fix BlendState#setAttachment reviving overrides cleared by copy by @mvaligursky in #9620
Examples
- Restyle the examples browser by @mvaligursky in #9629
- Slim the collapsed code pane and hide the code minimap by @mvaligursky in #9632
- Open the PlayCanvas Inspector in the examples browser by @mvaligursky in #9621
- Serve the inspector loader as is in the dev server by @mvaligursky in #9638
- Link a whole examples category by URL by @mvaligursky in #9303
- Add category sharing and expand-all controls by @mvaligursky in #9356
- Reveal current example when clearing search by @mvaligursky in #9354
- Rebuild the UI button, panel, masking and custom shader examples by @willeastcott in #9565
- Rebuild the UI text examples by @willeastcott in #9566
- Rebuild the UI layout, image and world examples by @willeastcott in #9569
- Add the UI input, common widgets and drag and drop examples by @willeastcott in #9571
- Add a hidden test example for UI element states by @mvaligursky in #9580
- Add rendering without entities example by @mvaligursky in #9377
- Add environment sources test example by @mvaligursky in #9360
- Give the render to image example a knight of its own by @willeastcott in #9583
- Refresh the MiniStats example scene by @mvaligursky in #9309
- Migrate examples to TextureRenderer by @mvaligursky in #9384
- Load the VAT character data as a binary asset in the vat-characters example by @mvaligursky in #9518
- Render gaussian splat crop and depth-of-field examples without MSAA by @mvaligursky in #9634
- Restore the depth prepass in the gaussian splat depth-of-field example by @mvaligursky in #9639
- Apply area-picker example material changes via update() by @mvaligursky in #9497
- Remove no-op bloom.enabled writes from examples by @mvaligursky in #9476
- Replace the Arial and Courier fonts of the examples and tests with Roboto by @willeastcott in #9577
- Ship the Roboto ExtraLight license with the XR examples' font by @willeastcott in #9576
Documentation & Types
- Type Entity#addComponent options and result by component name by @willeastcott in #9320
- Type Asset#resource by asset type (Asset, AssetMap, AssetType) by @willeastcott in #9403
- Type the script lookup APIs against Script and return the class you pass in by @willeastcott in #9472
- Type element input events with the browser objects they carry by @willeastcott in #9527
- Add alpha option to createGraphicsDevice typings by @willeastcott in #9317
- Fix JSDoc types that disagree with runtime values, salvaged from #5817 by @willeastcott in #9469
- Fix Debug.assert and Texture levels JSDoc types by @willeastcott in #9491
- Clear all API reference build warnings and treat warnings as errors by @willeastcott in #9412
- Add a Framework category and home the uncategorised public classes by @willeastcott in #9421
- Rewrite Asset-related class overviews and document every resource handler by @willeastcott in #9409
- Rewrite the Math category class overviews by @willeastcott in #9410
- Rewrite the scene graph and render pipeline class overviews by @willeastcott in #9422
- Rewrite the material class overviews by @willeastcott in #9424
- Make the modern input system the Input category and describe its classes by @willeastcott in #9427
- Describe the modern animation classes and mark the legacy ones by @willeastcott in #9430
- Describe the sound classes and template the thin component system pages by @willeastcott in #9432
- Describe the Gizmo and TransformGizmo base classes by @willeastcott in #9433
- Use American spelling in the Material and LitShaderOptions overviews by @willeastcott in #9431
- Document which asset types fire the progress event by @willeastcott in #9474
- Correct RigidBodyComponent group/mask defaults and raycastAll sort order by @willeastcott in #9305
- Fix physics docs for impulse points, kinematic velocity and contact results by @willeastcott in #9515
- Improve OutlineRenderer API documentation by @mvaligursky in #9587
- Correct typos and British spellings across the engine source by @willeastcott in #9547
- Correct typos and British spellings in examples, scripts and tests by @willeastcott in #9550
- Add a context7.json so Context7 indexes usage docs and agent rules by @willeastcott in #9478
- Point the Context7 rules at the llms.txt indexes by @willeastcott in #9503
Engine Dependencies
- Update dependency sharp to v0.35.4 [SECURITY] in #9330
- Update all npm dependencies in #9434
- Update dependency mocha to v12 in #9435
Contributors
New Contributors
- @dfattal made their first contribution in #9486
- @humairaambreen made their first contribution in #9543
Full Changelog: v2.22.6...v2.23.0