Commit Graph

257 Commits

Author SHA1 Message Date
Visse 6f270d4776 Expose pipeline constants to materials (#24502)
# Objective

Allow materials to use pipeline constants ([pipeline-overridable
constants](https://www.w3.org/TR/WGSL/#override-decls)).
They are already available in wgpu, but bevy didn't expose them.

## Solution

Expose constants in `RenderPipelineDescriptor` &
`ComputePipelineDescriptor`, allowing materials to specify them in their
specilize function.

**Note:** I had to remove the `Eq` derive from
`ComputePipelineDescriptor`, `VertexState` and `FragmentState`, due to
the new f64 field. It was already a bit inconsistent with
`RenderPipelineDescriptor` not having it.

## Testing
- Ran `cargo check`
- Created an example & ran it
- Couldn't run `cargo test` due to it taking looots of disk space to
run, but I have a hard time seeing it break something at runtime

## Showcase

See the added example, where pipeline constants are used to change the
`LEVELS` override in WGSL.
<img width="760" height="289" alt="Screenshot from 2026-05-31 09-46-05"
src="https://github.com/user-attachments/assets/6902757c-aea4-4b91-9ff0-e653ce4c3448"
/>
2026-06-09 00:06:01 +00:00
Luo Zhihao 7517c61ecd Add options to lower precision/compressed vertex buffer (#21926)
# Objective

Resolves #21902.

## Solution

This PR adopts a relatively transparent approach to reduce the GPU
vertex buffer size. On CPU-side mesh can still use uncompressed Float32
data, and users are not required to insert compressed vertex formats.
The vertex data is automatically processed into
lower-precision/octahedral encoded data when uploading to the GPU.

To enable vertex attribute compression, just set the
`attribute_compression` field of Mesh, or set
`mesh_attribute_compression` of GltfLoaderSettings. If enabled, normal
and tangent will be octahedral encoded Unorm16x2, uv0, uv1, joint weight
and color will be corresponding Unorm16 or Float16. I also provide
Unorm8x4 for vertex color if hdr isn't needed.

Update 2026-2-16

Removed previous approach that automatically compresses vertex buffer
according to flags when uploading to GPU. Instead, I added
`compressed_mesh` method to Mesh to construct compressed Mesh ahead of
time. GltfLoader can also opt-in mesh compressing when loading. I also
add an option to convert indices to u16, though I believe blender gltf
exporter already uses u16 indices when possible.


## Testing

Run `many_cubes`, `many_foxes`, `many_morph_targets` with
`--vertex-compression` to test 3d.
Run `bevymark` with `sprite_mesh` to test 2d, because `SpriteMesh` uses
compressed quad mesh now.

---------

Co-authored-by: Greeble <166992735+greeble-dev@users.noreply.github.com>
2026-06-02 03:22:56 +00:00
Christophe Dehais 5ec83e1185 Improve Order Independent Transparency example (#22781)
# Objectives

- Use a cleaner UI inspired by other examples
- Add a scene with custom material to fix #20297 

## Testing

Running the example
 ## Showcase 
<details>
<summary>Click to view showcase</summary>
<img width="872" height="732" alt="Screenshot From 2026-02-06 11-35-19"
src="https://github.com/user-attachments/assets/cc294c33-bb53-4ed4-9dce-7558f3bb8fee"
/>
<img width="872" height="732" alt="Screenshot From 2026-02-06 11-35-30"
src="https://github.com/user-attachments/assets/c2b79651-c99e-4bcc-8089-518d11631b9f"
/>
</details>

---------

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2026-05-21 15:13:25 +00:00
Duncan afd576f380 Alpha discard (#11895)
# Objective

I want to call the `pbr_functions::alpha_discard` shader function from
my own material shader, but it only takes a `StandardMaterial`
parameter.

## Solution

This PR replaces the `StandardMaterial` parameter with less restrictive
ones so my shader can call it.

---

## Changelog

- The signature of `pbr_functions::alpha_discard` changed by replacing
`StandardMaterial` with only the `flags` and `alpha_cutoff` fields.

## Migration Guide

Replace this:

```wgsl
pbr_functions::alpha_discard(material, ...);
```

with this:

```wgsl
pbr_functions::alpha_discard(material.flags, material.alpha_cutoff, ...);
```

---------

Co-authored-by: Duncan Fairbanks <duncanfairbanks6@gmail.com>
2026-05-21 08:49:32 +00:00
JMS55 489818930b Update ktx2 to v0.5.0 (#23900)
Updated ktx2 crate.

After doing so, Bevy ktx2 files failed to validate due to invalid
metadata. I fixed all their metadata, and made sure they passed ktx
validate.
2026-05-02 01:57:00 +00:00
Carter Anderson 535cf401cc Reframe old "scene" terminology as "world serialization" (#23630)
Part 2 of #23619 

In **Bevy 0.19** we are landing a subset of Bevy's Next Generation Scene
system (often known as BSN), which now lives in the `bevy_scene` /
`bevy::scene` crate. However the old `bevy_scene` system still needs to
stick around for a bit longer, as it provides some features that Bevy's
Next Generation Scene system doesn't (yet!):

1. It is not _yet_ possible to write a World _to_ BSN, so the old system
is still necessary for "round trip World serialization".
2. The GLTF scene loader has not yet been ported to BSN, so the old
system is still necessary to spawn GLTF scenes in Bevy.

For this reason, we have renamed the old `bevy_scene` crate to
`bevy_world_serialization`. If you were referencing `bevy_scene::*` or
`bevy::scene::*` types, rename those paths to
`bevy_world_serialization::*` and `bevy::world_serialization::*`
respectively.

Additionally, to avoid confusion / conflicts with the new scene system,
all "scene" terminology / types have been reframed as "world
serialization":

- `Scene` -> `WorldAsset` (as this was always just a World wrapper)
- `SceneRoot` -> `WorldAssetRoot`
- `DynamicScene` -> `DynamicWorld`
    - `DynamicScene::from_scene` -> `DynamicWorld::from_world_asset`
- `DynamicSceneBuilder` -> `DynamicWorldBuilder`
- `DynamicSceneRoot` -> `DynamicWorldRoot`
- `SceneInstanceReady` -> `WorldInstanceReady`
- `SceneLoader` -> `WorldAssetLoader`
- `ScenePlugin` -> `WorldSerializationPlugin`
- `SceneRootTemplate` -> `WorldAssetRootTemplate`
- `SceneSpawner` -> `WorldInstanceSpawner`
- `SceneFilter` -> `WorldFilter`
- `SceneLoaderError` -> `WorldAssetLoaderError`
- `SceneSpawnError` -> `WorldInstanceSpawnError`

Note that I went with `bevy_world_serialization` over
`bevy_ecs_serialization`, as that is what all of the internal features
described themselves as. I think it is both more specific and does a
better job of making itself decoupled from `bevy_ecs` proper.
2026-04-04 00:31:47 +00:00
Carter Anderson 45e454a83b Rename bevy_scene to bevy_ecs_serialization (#23619)
# Objective

The first part of #23606.

## Solution

Rename `bevy_scene` to `bevy_ecs_serialization`. No other changes were
made.

## Testing

The `scene` example still works as expected.
2026-04-02 02:22:58 +00:00
andriyDev 2e2b29eb33 Allow serializing and deserializing handles during reflection. (#23329)
# Objective

- Partly addresses #15518 - this is a big issue but the handle part is
all resolved!
- Make the existing scene system much more useful!

## Solution

- Create a `ReflectSerializerProcessor` and
`ReflectDeserializerProcessor` that "hijacks" the ser/de of handles to
just write out the path being loaded (or the UUID), and loads the assets
on deserialization.
- This was made possible by #15482! Very useful! This *should* also be
composable with other processors by just creating a wrapper that calls
both processors in series.
- Make the scene ser/de use this processor by default. Now handles just
work in scenes!
- Update the scene example to show a scene that includes a scene! Nested
scenes!!

I've left a TODO that the scene system could expose the
`EphemeralHandleBehavior` so users can choose to be more strict. It's
probably not a big deal to solve IMO.

Also I had to call it `AssetServer::load_erased` since we already have
an `AssetServer::load_untyped` which tries to guess the asset type based
on the path. `AssetServer::load_erased` in contrast knows what type it
wants, just at runtime. It feels like we should have a less adhoc API
here, one where we know we've covered the cross product of all features.
Something to think about!

## Testing

- Added a test for the processors that they can roundtrip a type with
handles.
- Added a test that the scene system can roundtrip components with
handles.
- Updated the scene example and it spawns with the handle correctly!
2026-03-17 00:39:38 +00:00
Máté Homolya acd1091c3e Add Martian atmosphere constant (#22884)
# Objective

- Generally hard to customize meter-based metrics for atmospheres
- Address user concerns described in issue #22882 for creating a mars
atmosphere

## Solution

- Provide a default for mars like atmospheres, a common use case for a
good base to start with.
- The values in this pull request are based on this paper:
https://www.researchgate.net/publication/380239530_Physically_Based_Real-Time_Rendering_of_Atmospheres_using_Mie_Theory
- I created a pre-computed lookup texture for the mie scattering and
enabled the use of such textures within the atmosphere's `medium`
struct. note that this is only 16kb and doesn't weigh much in the repo.
however it could be moved to an asset on the network.
- Full derivation of the texture and constants can be found in this repo
I created: https://github.com/mate-h/mars-atmo

## Testing

- Updated the atmosphere example

---

## Showcase

<img width="1280" height="723" alt="Screenshot 2026-02-14 at 1 20 35 AM"
src="https://github.com/user-attachments/assets/3487c67d-ce46-428e-a62b-88f6d9accbc6"
/>
<img width="1278" height="719" alt="Screenshot 2026-02-14 at 1 18 44 AM"
src="https://github.com/user-attachments/assets/2613182d-27e6-41d6-85fd-57cf40277909"
/>
<img width="1276" height="721" alt="Screenshot 2026-02-14 at 1 17 41 AM"
src="https://github.com/user-attachments/assets/cedb8d51-74eb-4c1c-9aaf-1994f6776b88"
/>
<img width="1278" height="721" alt="Screenshot 2026-02-14 at 1 13 53 AM"
src="https://github.com/user-attachments/assets/357a6907-1012-401b-857b-107f20c24fdd"
/>
<img width="1281" height="721" alt="Screenshot 2026-02-14 at 1 12 30 AM"
src="https://github.com/user-attachments/assets/aac10fed-d06f-4fad-9057-4ea5634d7a78"
/>
<img width="1279" height="722" alt="Screenshot 2026-02-14 at 1 09 26 AM"
src="https://github.com/user-attachments/assets/7ddb3663-e6dd-413f-a771-26ae620da480"
/>

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: atlv <email@atlasdostal.com>
2026-03-01 19:31:06 +00:00
Patrick Walton 1b0f112d90 Implement GPU clustering for lights, light probes, and decals. (#23036)
Currently, Bevy clusters lights on the CPU. This is generally not
considered a best practice any longer, and it can be a bottleneck in
workloads like `many_lights`. Moreover, it prevents GPU systems like
[Hanabi] from creating clusterable objects such as lights and decals
without a round trip to the CPU.

This PR introduces GPU light clustering when supported by the hardware.
The algorithm is the same as the existing GPU light clustering, but
parallelized over all clusters, and the resulting on-GPU format for
clusters is unchanged. GPU light clustering uses the hardware rasterizer
for compute purposes as a way to automatically distribute workloads
within 2D axis-aligned bounding boxes without actually rendering any
pixels, a first for Bevy. The algorithm is as follows, with each step
corresponding to a raster or compute command:

1. *Z slicing*: We have a 3D cluster froxel grid of size WxHxD and seek
to rasterize D axis-aligned quads, each of size WxH, representing the
range of each clusterable object. In this compute phase, we generate D
indirect instances for each clusterable object for the subsequent
indirect draws.

2. *Count rasterization*: We use instanced indirect drawing to rasterize
each quad generated in step 1 to a viewport of size WxH, with color
writes disabled. Each rasterized fragment represents a cluster-object
pair. In the fragment shader, we check to see if the object intersects
the cluster, and, if it does, we atomically bump a counter corresponding
to the number of objects of the given type intersecting the cluster in
question. We don't record the ID of the object in this phase; we simply
count the number of objects.

3. *Local allocation*: Now that we know the number of objects of each
type in each cluster, we can proceed to allocate space in the clustered
object buffer for each clustered object list. To do this, we need to
perform a [*prefix sum*] operation so that each list is tightly packed
with the others. For example, if adjacent clusters have 2, 5, and 3
objects, they'll be allocated at offsets 0, 2, and 7 respectively. This
*local* step uses a [Hillis-Steele scan] in shared memory to compute the
prefix sum of each chunk of 256 clusters. We can't go beyond 256
clusters in this local step because 256 is the maximum workgroup size in
`wgpu`.

4. *Global allocation*: To deal with the fact that we can't calculate
prefix sums beyond 256 clusters in step 3, we employ this second step
that does a sequential loop over every 256-cluster chunk, propagating
the prefix sum. At the end of this step, every list of clustered objects
is allocated.

5. *Populate rasterization*: Finally, we issue an instanced indirect
draw command using the same parameters as step (2). We test each
cluster-object pair for intersection, and, if the test passes, we record
the ID of each clustered object into the correct space in the list,
using a scratch pad buffer of atomics to store the position of the next
object in each list.

The buffer of clustered objects has a fixed size and can overflow. We
detect this condition via asynchronous CPU readback and automatically
grow the buffer for subsequent frames. In this case, we also log a
message so that the developer can choose a larger initial buffer size
and avoid any incorrect frames. Additionally, like #22874, the automatic
clustering heuristics are dynamically adjusted from frame to frame, by
recording statistics on the GPU and using CPU readback to download them
back to the CPU for processing.

As part of this PR, I refactored clustered visibility so that clustered
objects go through the same `ViewVisibility` system as other objects,
instead of using `VisibleClusterableObjects`. This was a nice
simplification.

On the `many_lights` benchmark, with about 8,000 lights visible out of
100,000, this process takes approximately 0.099 ms on my NVIDIA GeForce
RTX 4070 Laptop GPU. The AMD Ryzen 9 8945HS CPU, however, takes 2.12 ms
to do the same task. The GPU version is therefore a 21x speedup.

`main` `assign_objects_to_clusters` time, 2.12 ms:
<img width="2756" height="1800" alt="Screenshot 2026-02-17 222757"
src="https://github.com/user-attachments/assets/66341ad2-96f2-4e4a-87ee-fe3462bc05de"
/>

GPU clustering GPU time, 0.099 ms:
<img width="2756" height="1800" alt="Screenshot 2026-02-17 222458"
src="https://github.com/user-attachments/assets/18e2e0ae-a946-4b80-b38a-0543e76ebc02"
/>

`main`, 5.71 ms median frame time, 175 FPS:
<img width="2756" height="1800" alt="Screenshot 2026-02-17 222243"
src="https://github.com/user-attachments/assets/111c8e22-414f-4ee1-95fa-d7cfe422c2ab"
/>

GPU clustering, 4.88 ms median frame time, 205 FPS:
<img width="2756" height="1800" alt="Screenshot 2026-02-17 222256"
src="https://github.com/user-attachments/assets/0a662e88-a1b9-49c8-8bab-cc12b46cd079"
/>

[Hanabi]: https://github.com/djeedai/bevy_hanabi

[*prefix sum*]: https://en.wikipedia.org/wiki/Prefix_sum

[Hillis-Steele scan]:
https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel


## Alice's PM Note from @kfc35

Fixes https://github.com/bevyengine/bevy/issues/22957 and also fixes
https://github.com/bevyengine/bevy/issues/22904.
2026-02-28 17:07:33 +00:00
Jordan Halase 5e1630bfd8 Fix 16 byte alignment typo (WebGL 2: 16 bit -> 16 byte) (#23124)
# Objective

WebGL 2 requires 16 **byte** UBO alignment. Some comments incorrectly
state 16 **bits**.

## Solution

Fix comment typos.

## Testing

N/A

---

## Showcase

N/A
2026-02-24 00:51:54 +00:00
trepidacious 87c9a5a0ef Support Tile transforms in TilemapChunk (#22889)
# Objective

Fixes #22888 

## Solution

- Add an `orientation` field to `TileData`. This contains a
`TileOrientation` enum representing the tile being rotated and/or
mirrored. This defaults to the current orientation (tile is not rotated
or mirrored). The orientation is packed into the `flags` of
`PackedTileData` as 3 new bits alongside the existing `visible` boolean.
The bits represent mirroring horizontally (left and right swapped),
mirroring vertically (top and bottom swapped), and diagonally (top-right
and bottom-left corners are swapped). Together, the 8 combinations
represent all possible combinations of 90 degree rotations with
mirroring in the X or Y axis.
- Update `tilemap_chunk_material.wgsl` to unpack the bits, and use them
to modify the `local_uv` as needed to apply the transform to the tile.

`TileOrientation` also provides some convenience methods for
construction, and for treating the orientation as a transform that can
be inverted and combined, as well as applying that transform to an
`IVec2`.

There is a new `tilemap_chunk_orientation` example showing all
orientations, combined with tile indices, color, alpha and visibility to
check packing of data.

## Testing

There are some simple tests on the `TileOrientation` itself (e.g.
checking that combining an orientation with its inverse produces the
default orientation, applying each orientation as a transform to a test
`IVec2` to confirm the result is as expected based on manually worked
out expected results, and that inverses will map a point back to its
origin).

Rendering can be tested using the new example, I've tested this on a
Macbook M1 Max.

---

## Showcase

The new `tilemap_chunk_orientation` example uses one tileset entry for
each tile on each row, showing the 8 transforms along the row:

<img width="687" height="707" alt="Screenshot 2026-02-23 at 14 25 29"
src="https://github.com/user-attachments/assets/9cb20236-d180-490c-bd3f-f6e61c30c099"
/>

This also shows different colors and alpha values on each of 8 rows to
check they work alongside transforms, the top row is set to invisible.
The `tileset_index` alternates per row.

This uses a simple two-tile tileset, shown below scaled up by 4:

<img width="32" height="64" alt="arrow"
src="https://github.com/user-attachments/assets/5536beb9-7a3e-4ec0-a667-777ba8ad283a"
/>

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2026-02-24 00:17:21 +00:00
Chris Biscardi 71ce303ec2 compute-shader mesh generation example (#22296)
# Objective

People have been asking how to get a compute shader-built mesh into
bevy's "stuff".

Some people want to control the lifetime of the mesh via Handle, and
others don't don't how to set data in bind groups.

## Solution

a new example that shows how to initialize a mesh handle with a
render_world usage mesh, and then put the output of the compute shader
into the mesh_allocator slab for the mesh.

The demo creates a scene with a camera, light, a circular base mesh, and
an empty "cube to be" mesh that is shared by cloning the handle across
two entities. The compute shader then fills in the data directly into
the mesh_allocator slabs for the vertex/index buffers.

If the compute shader failed, there would be no cube meshes showing as
the data would be empty.

## Testing

```
cargo run --example compute_mesh
```

---

## Showcase


<img width="3392" height="2106" alt="screenshot-2025-12-29-at-16 06
48@2x"
src="https://github.com/user-attachments/assets/88d8fed4-e3c1-418e-bb04-6f08d673403a"
/>
2026-01-16 00:06:16 +00:00
Patrick Walton 4189ba072d Provide a mechanism for applications to invoke the single-pass downsampler. (#22286)
The [AMD FidelityFX single-pass downsampler] (SPD) is the fastest way to
generate mipmap levels of a texture. Bevy currently has two separate
ports of that algorithm to WGSL: one for use in the environment map
generation and one for use on the depth buffer for the purposes of
occlusion culling (though the latter isn't the best use of it). Absent
is any mechanism to use the single-pass downsampler to generate mipmap
levels of a color texture for typical use in rendering. This is a
standard feature in game engines: for example, Unity has
[`GenerateMips`] and Unreal has [`bAutoGenerateMips`].

This PR adds a mechanism by which applications can invoke SPD to
generate mipmap levels for any `Image`. Using this mechanism is a two
step process. First, the application adds the `Handle<Image>` to a
resource, `MipGenerationJobs` and associates it with a *phase*, which is
an arbitrary ID chosen by the application. Second, the application adds
a `MipGenerationNode` for that phase to the render graph. During
rendering, the `MipGenerationNode` invokes SPD to generate a full mipmap
chain for all textures in that phase.

The reason why mipmap generation jobs are associated with phases is that
the generation of mipmaps may need to occur at precise points in the
application rendering cycle. For example, consider the common situation
of a mipmapped portal texture. The mipmaps must be generated *after* the
portal is rendered, but *before* the object in the main world displaying
the portal texture is drawn. The phased approach taken in this PR allows
complex dependencies like this to be expressed using the node graph
feature that Bevy already possesses. (In the future, if render graphs
are removed in favor of systems, this approach can naturally be reframed
in terms of systems, so this patch contains no hazards in that regard.)

Note that this patch by itself doesn't automatically generate mipmaps
for imported textures that don't have them the way that
[`bevy_mod_mipmap_generator`] does, in order to keep this patch
relatively small and self-contained. However, it'd be straightforward to
either (a) extend `bevy_mod_mipmap_generator`, (b) write another plugin,
and/or (c) add a new feature to Bevy itself, all built on top of this
PR, to support automatic GPU mip generation for image assets that don't
have them.

A new example, `dynamic_mip_generation`, has been added. This is a 2D
example that produces a texture at runtime on the CPU and invokes the
new `MipGenerationNode` that this patch adds to generate mipmaps for
that texture at runtime. The colors of the texture are randomly
generated, and UI for the example allows the texture to be regenerated
and for the size to be adjusted; this proves that the mipmap levels for
the texture are indeed generated at runtime and not pre-calculated at
build time. Note that, although the example is 2D, the feature that this
patch adds can be equally used in 2D and 3D.

[AMD FidelityFX single-pass downsampler]:
https://gpuopen.com/fidelityfx-spd/

[`GenerateMips`]:
https://docs.unity3d.com/ScriptReference/Rendering.CommandBuffer.GenerateMips.html

[`bAutoGenerateMips`]:
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/DisplayClusterConfiguration/FDisplayClusterC-_33/bAutoGenerateMips

[`bevy_mod_mipmap_generator`]:
https://github.com/DGriffin91/bevy_mod_mipmap_generator
2025-12-31 23:05:54 +00:00
ickshonpe b4e74debc8 Font weight support (#22038)
# Objective

Add font weight support.

## Solution

* New `FontWeight` struct that newtypes a `u16`.
* New `font: FontWeight` field on `TextFont`.
* The weight attribute for the cosmic text buffer is set in
`TextPipeline` during text updates.
* Added a new font asset, `MonSans-VariableFont.ttf`. This needs a
variable font for testing.

#

Doesn't support `lighter` and `bolder` as that would require text style
inheritance, which we don't support yet.

I added stretch and slant as well, but split them off from this PR.
Swash only has limited variable-font support and there's no way to
demonstrate that they work without other changes.

## Testing

Added a basic example:
```cargo run --example font_weights```

## Showcase

<img width="671" height="845" alt="font_weights" src="https://github.com/user-attachments/assets/8a8686ac-faa8-442c-89d4-f56348a7e788" />

---------

Co-authored-by: Thierry Berger <contact@thierryberger.com>
2025-12-16 20:04:40 +00:00
IceSentry 681751647a Add FullscreenMaterial (#20414)
# Objective

- Users often want to run a fullscreen shader but the current solution
involves copying the custom_post_processing example which is a 350 line
file with a lot of low level wgpu complexity. Users shouldn't have to
deal with that just to make a fullscreen shader

## Solution

- Introduce a new FullscreenMaterial trait and FullscsreenMaterialPlugin
- This new material will run a fullscreen triangle with the specified
shader. It builds on top of the existing FullscreenShader infrastructure
- It lets user customize the node ordering. There's no defaults right
now becausae it's intended as a bit of a primitive plugin. Eventually we
could have some kind of default for custom post processing

## Testing

Made a new fullscreen_material example and made sure it works

## Follow up

Once this is merged there are various things that should be done to
improve it. Add the option to bind the depth texture, offer defaults for
post processing, use a full AsBindGroup, add a way to bind the gbuffer.

---------

Co-authored-by: JMS55 <47158642+JMS55@users.noreply.github.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-12-14 22:35:20 +00:00
Chris Biscardi e7b64b6b04 Add support for arbitrary/third party glTF Extension processing via GltfExtensionHandler (#22106)
# Objective

Currently Bevy doesn't support arbitrary glTF extensions. The ones it
does support are hardcoded.

We should support glTF extensions, as this is a primary mechanism for
sharing behavior via data exported from applications like Blender.

I personally have found usecases in exporting component data, lightmap
textures/information, and processing other kinds of data
(AnimationGraph, 3d meshes into 2d, etc).

## Solution

This PR introduces a new `GltfExtensionHandler` trait that users can
implement and add to the glTF loader processing via inserting into a
Resource.

There are two example processors currently added, with a third that I'd
like to add after this PR.

- `examples/gltf/gltf_extension_animation_graph.rs` duplicates the
functionality of `animation_mesh`, constructing AnimationGraphs via
extension processing and applying them to be played on the relevant
nodes.
- `examples/gltf/gltf_extension_mesh_2d.rs` duplicates the functionality
of the `custom_gltf_vertex_attribute` example, showing how the extension
processing could be used to convert 3d meshes to 2d meshes alongside
custom materials.

Both of these examples re-use existing assets and thus don't *actually
use* extension data, but show how one could access the relevant data to
say, only convert specifically labelled Mesh3ds to 2d, or process many
animations into multiple graphs based on extension-data based labelling
introduced in Blender.

A third example I want to introduce after this PR is the same core
functionality Skein requires: an example that uses reflected component
data stored in glTF extensions and inserts that data onto the relevant
entities, resulting in scenes that are "ready to go".

## Comparison to Extras

In comparison to extensions: data placed in glTF extras is well
supported through the `GltfExtras` category of components.

Extras only support adding an additional `extras` field to any object.

Data stored in extras is application-specific. It should be usable by
Bevy developers to implement their own, application-specific, data
transfer. This is supported by applications like Blender through the
application of Custom Properties.

Once data is used by more than one application, it belongs in a glTF
extension.

## What is a glTF Extension?

Extensions are named with a prefix like `KHR` or `EXT`. Bevy has already
reserved the `BEVY` namespace for this, which is listed in the official
[prefix
list](https://github.com/KhronosGroup/glTF/blob/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions/Prefixes.md).

For a glTF file, an extension must be listed in `extensionsUsed` and
optionally `extensionsRequired`.

```
{
    "extensionsRequired": [
        "KHR_texture_transform"
    ],
    "extensionsUsed": [
        "KHR_texture_transform"
    ]
}
```

Extension data is allowed in any place extras are also allowed, but also
allow much more flexibility.

Extensions are also allowed to define global data, add additional binary
chunks, and more.

For meshes, extensions can add additional attribute names, accessor
types, and/or component types

`KHR_lights_punctual` is a contained and understandable example of an
extension:
https://github.com/KhronosGroup/glTF/blob/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions/2.0/Khronos/KHR_lights_punctual/README.md
. This one happens to be already hardcoded into Bevy's handling, so it
doesn't benefit from arbitrary extension processing, but there are
additional
[ratified](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#ratified-khronos-extensions)
and
[in-progress](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#in-progress-khronos-and-multi-vendor-extensions-and-projects)
extensions, as well as
[vendor](https://github.com/KhronosGroup/glTF/tree/7bbd90978cad06389eee3a36882c5ef2f2039faf/extensions#vendor-extensions)
and other arbitrary extensions that would benefit from userland support.

## Implementation

This initial implementation is reasonably minimal: enabling extension
processing for objects/etc as they're loaded which may also define
extension data, including the scene world. This may leave out useful
functionality; as detailed in the next section: "What's not
implemented".

Extension handlers are defined by implementing a trait which can
optionally define hooks and data.
Extension handler data is cloned to start with a fresh slate for each
glTF load, which limits scope to "one glTF load".
So while state can be maintained across hooks during a single load,
users who want to combine or handle multiple glTF assets should do so in
the main app, not in an extension handler.
Following this, because the extensions are stored as `dyn GltfExtension`
*and* we want to clone them to isolate state to a single load,
`dyn_clone` must be included as a workaround to enable this cloning.

An extension handler has to be added to the list of handler by accessing
a `Resource` and pushing an instantiated handler into it.
This Resource keeps the list of extension handlers so that a new glTF
loader can bootstrap them.

The design of the hooks is such that:

- If no extensions handlers are registered, none are called for
processing
- If an extension handler is defined, it receives all "events"
- handlers are defined by a trait, and default implementations are
called if an override is not specified.
    - default implementations are no-ops

It is important that extensions receive all events because certain
information is not embedded in extension data.
For example, processing animation data into an animation graph could
require both processing animations with extension data, tracking the
animation roots through hooks like `on_node`, *and* applying those
graphs in the `on_scene_completed` hook.

- Extension data is passed to hooks as `Option<&serde_json::Value>`
which is only passing references around as the data has already been
converted to `Value` by the `gltf` crate.
- `LoadContext` is required for creating any new additional assets, like
`AnimationGraph`s.
- *scene* World access is provided in hooks like `on_scene_completed`,
which allows calculating data over the course of a glTF load and
applying it to a Scene.

### What's not implemented

This PR chooses to *not* implement some features that it could. Instead
the approach in this PR is to offer up the data that Bevy has already
processed to extensions to do more with that data.

- Overriding `load_image`/`process_loaded_texture`
- This could allow projects like bevy_web_codecs, [which currently forks
the entire gltf
loader](https://github.com/jf908/bevy_web_codecs/tree/373bbf29be6555c7603fd6867a01159ab0f20fed/bevy_web_codecs_gltf).
Associated [issue](https://github.com/bevyengine/bevy/issues/21185).
However I believe this needs some design work dedicated to what exactly
happens here to support that use case.
- This PR doesn't include any refactoring of the glTF loader, which I
feel is important for a first merge.
- ~~There is some benefit to passing in the relevant `gltf::*` object to
every hook. For example, I believe this is the only way to access
extension data for `KHR_lights_punctual`, and
[`KHR_materials_variants`](https://docs.rs/gltf/1.4.1/gltf/struct.Document.html#method.variants)
or other extensions with "built-in" support. I haven't done this in all
places.~~ (edit: after external implementation I decided this was a good
idea and added it to more places)

## Testing

```
cargo run --example gltf_extension_animation_graph
cargo run --example gltf_extension_mesh_2d
```

---

## Showcase

Both examples running:


https://github.com/user-attachments/assets/f9e7c3c9-cdad-4d33-ace7-7c2ca5469d5e



https://github.com/user-attachments/assets/baa9bc92-ca3b-46ad-a3f0-2f74bbc29b68


<details>
<summary>An example that showcases converting Mesh3d to Mesh2d</summary>

```rust
#[derive(Default, Clone)]
struct GltfExtensionProcessorToMesh2d;

impl GltfExtensionProcessor for GltfExtensionProcessorToMesh2d {
    fn extension_ids(&self) -> &'static [&'static str] {
        &[""]
    }

    fn dyn_clone(&self) -> Box<dyn GltfExtensionHandler> {
        Box::new((*self).clone())
    }

    fn on_spawn_mesh_and_material(
        &mut self,
        load_context: &mut LoadContext<'_>,
        _gltf_node: &gltf::Node,
        entity: &mut EntityWorldMut,
    ) {
        if let Some(mesh3d) = entity.get::<Mesh3d>()
            && let Some(_) = entity.get::<MeshMaterial3d<StandardMaterial>>()
        {
            let material_handle =
                load_context.add_loaded_labeled_asset("AColorMaterial", (CustomMaterial {}).into());
            let mesh_handle = mesh3d.0.clone();
            entity
                .remove::<(Mesh3d, MeshMaterial3d<StandardMaterial>)>()
                .insert((Mesh2d(mesh_handle), MeshMaterial2d(material_handle.clone())));
        }
    }
}
```

</details>

---------

Co-authored-by: Kristoffer Søholm <k.soeholm@gmail.com>
2025-12-14 21:25:07 +00:00
mgi388 d60a1b8166 Add MeshTag to array_texture example to demonstrate layer selection in shader (#21989)
## Objective

- When looking at the `array_texture` example, it wasn't clear to me how
I could send the "layer" to the GPU, but it turns out that [MeshTag is
one recommended
way](https://discord.com/channels/691052431525675048/866787577687310356/1444495450999754823)
to pass this.
- The example previously extracted a fake "layer" from the world
position, but IIUC this isn't the most realistic way to demonstrate
layer selection.

## Solution

- Update the `array_texture` example by using `MeshTag`.
- Add a system to the example that periodically changes the `MeshTag` on
entities to show that the mesh tag can also change dynamically at
runtime (and show how easy it is).

## Testing and showcase

Before, you can see each cube's texture is fixed.

<img width="1280" height="747" alt="image"
src="https://github.com/user-attachments/assets/1ffde7db-8110-4431-b4e8-3a5a4ba5c5db"
/>

After, you can see each cube's texture changes as time passes.


https://github.com/user-attachments/assets/b1227659-5886-4d2c-a401-84b80423c798

----

I'm hoping for a rendering dev to validate this approach is correct, and
useful. I think it is, but [I'm only just starting to
understand](https://discord.com/channels/691052431525675048/866787577687310356/1444888786478829668)
how to use this stuff and it's [possibly not the only
way](https://discord.com/channels/691052431525675048/866787577687310356/1444888304020488365)
so I don't want to submit this if it's the wrong approach to teach
future me's.
2025-12-09 23:38:23 +00:00
Patrick Walton 00f6eb7a1c Implement the infrastructure needed to support portals and mirrors. (#13797)
Implement the infrastructure needed to support portals and mirrors.

Bevy currently supports multiple cameras and rendering to off-screen
render targets, so one might naïvely think that the engine has support
for portals and mirrors already. However, Bevy is missing two key
features that enable portals and mirrors at present:

1. Bevy has support for neither custom clip planes nor oblique clip
planes. This prevents the construction of proper portals or mirrors, as
meshes that intersect the portal plane must be clipped to render
properly.

2. Bevy has no support for cameras that invert the culling mode, so
meshes that are reflected across a plane will render inside-out.

This PR addresses the two issues above:

1. This commit introduces a new field on `PerspectiveProjection`,
`near_plane`, which allows the application to specify a custom near
plane. That feature fully enables [Lengyel oblique clipping], which is
the most optimal way to achieve a custom near clipping plane. It allows
us to avoid having to support custom clip planes, which are often
implemented inefficiently in hardware.

2. This patch adds a new field on the `Camera` component,
`invert_culling`. This field causes the Bevy renderer to invert the
front face setting when rendering the objects visible from that camera.
When coupled with an appropriately-set [Householder matrix] on the
camera, this allows correct rendering of objects reflected across a
plane.

Additionally, this PR adds a new function to `bevy_math::mat3`,
`reflection_matrix`. This generates the matrix that reflects objects
across a plane, suitable for encoding into a `Transform`. It's fully
documented for ease of use.

Finally, a new example, `mirror`, has been added. This example is a
complete instance of a working mirror, combining a camera with a
Householder matrix, oblique projection, and inverted culling with a
custom material to render an animated mesh and its planar reflection.
The camera and mesh may be moved with the mouse, and the off-screen
render target that stores the rendered contents of the mirror world is
properly resized when the user resizes the window.

[Lengyel oblique clipping]:
https://terathon.com/lengyel/Lengyel-Oblique.pdf

[Householder matrix]:
https://en.wikipedia.org/wiki/Householder_transformation

<img width="2564" height="1500" alt="Screenshot 2025-12-05 212155"
src="https://github.com/user-attachments/assets/35652b58-a9a5-415a-bdff-367889a23b9f"
/>
2025-12-09 23:08:15 +00:00
Patrick Walton 185712fbef Add support for normal maps, metallic-roughness maps, and emissive maps to clustered decals. (#22039)
This commit expands the number of textures associated with each
clustered decal from 1 to 4. The additional 3 textures apply normal
maps, metallic-roughness maps, and emissive maps respectively to the
surfaces onto which decals are projected.

Normal maps are combined using the [*Whiteout* blending method] from
SIGGRAPH 2007. This approach was chosen because, subjectively, it
appeared better than the more complex [*reoriented normal mapping*
(RNM)] approach. Additionally, *Whiteout* normal map blending is
commutative and associative, unlike RNM, which is a useful property for
our decals, which are currently applied in an unspecified order. (The
fact that the order in which our decals are applied is unspecified is
unfortunate, but is a long-standing issue and should probably be fixed
in a followup.) In particular, commutativity is desirable because
otherwise one must specify which normal map is the *base* normal map and
which normal map is the *detail* normal map, but that's not a policy
decision that Bevy can unconditionally make, as decals aren't necessary
more detailed than the base normal map. (For instance, consider a bullet
hole decal embedded in a wall with a subtle rough texture; one might
reasonably argue that the base material's normal map is the detail map
and the bullet hole is the base map, even though the bullet hole's
normal map comes from a decal.)

Note that, with a custom material shader, it's possible for application
code to use the decal images for arbitrary other purposes. For example,
with a custom shader an application might use the metallic-roughness map
as a clearcoat map instead if it has no need for a metallic-roughness
map on a decal. And, of course, a custom material shader could adopt RNM
blending for decals if it wishes.

A new example, `clustered_decal_maps`, has been added. This example
demonstrates the new maps by spawning clustered decals with maps
randomly over time and projecting them onto a wall.

<img width="2564" height="1500" alt="Screenshot 2025-12-05 095953"
src="https://github.com/user-attachments/assets/255fca64-2b42-4794-a367-14336d023310"
/>
2025-12-09 18:14:55 +00:00
shunkie 0410482c13 Remove unused num_workgroups from game_of_life shader (#21944)
# Objective

Remove unused `num_workgroups`.

## Testing

```
cargo run --example compute_shader_game_of_life
```
2025-11-26 04:03:02 +00:00
John Hansler e8099c0ec1 Add support for OpenType features in text (e.g. ligatures, smallcaps) (#19020)
# Objective

OpenType features include things like smallcaps, lined vs old-style
numbers, ligatures, stylistic alternate characters, fractional numbers
(numerator placed above the denominator), forced monospacing for
numbers, and more. There are >100 possible OpenType feature tags; see
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
for the up-to-date list. This provides a way for Bevy users to use these
features when using .otf fonts that support them.

## Solution

OpenType features are now supported in cosmic-text, so this just
provides a way to pass them through. A few notes:

- I extended the existing "text" example to showcase a few different
OpenType features.
- OpenType features are only available for .otf fonts. Since there
weren't any existing .otf fonts in the asset/ folder, I've added an
SIL-licenced font so that we can showcase this in example code.
- I added a "FontFeatures" struct. cosmic-text does already include its
own FontFeatures struct, but 1) it does not implement Reflect, which is
required by TextFont, and 2) the one I added has a couple ergonomics
improvements for the builder methods compared to cosmic-text's.
- OpenType font features are four characters strings, e.g. "liga". I
considered representing these within an enum, but decided against this
since there are hundreds of possible features, and more get added
frequently, so this would require quite a bit of ongoing maintenance.
Since these features are typically referred to by their four-letter name
in documentation, I think the [u8; 4] representation is appropriate, and
this mirrors what cosmic-text does as well. I added some consts for
commonly used features.

## Testing

I extended the "text" example. Run:

`cargo run --example text`

---

## Showcase

Screenshot:

![opentype_features](https://github.com/user-attachments/assets/08167404-e7c1-4a9f-b21c-d3370d7e4924)

---------

Co-authored-by: John Hansler <john@hansler.net>
2025-11-06 18:23:19 +00:00
Máté Homolya e3814a944b Atmosphere occlusion and PBR shading (#21383)
# Objective

- Occlude the directional lights by the atmosphere
- More Physically accurate light values reaching the objects from
directional lights
- Support volumetric shadowing through the atmosphere with an additional
layer of FogVolume
- Strategic direction: use the FogVolume and expand on that
implementation for shadow sampling Since it already uses pipeline
specialization.
- For now keep the atmosphere pipeline as is, free from shadow sampling
code since these effects only matter at large scales. Shadow sampling
for the atmosphere can be implemented in a later PR for planetary rings,
objects in space, other large scale objects.

## Solution

- Bind the transmittance LUT to the core 3d mesh PBR shader
- tint the incoming light L to point P by the transmittance through the
atmospheric medium
- Apply the same effects to volumetric fog
- Ensured that new new Atmosphere pipeline key works with deferred
rendering
- Updated example to include water plane and screen space reflections.
Removed the water plane from the GLB terrain asset. (small asset churn!)

## Testing

```bash
cargo run --example atmosphere
# or
cargo run --example atmosphere --features=free_camera
```

---

## Showcase


<img width="1275" height="721" alt="Screenshot 2025-10-22 at 9 50 27 PM"
src="https://github.com/user-attachments/assets/f91ec8ba-a4ee-4bd2-b205-5158193466b9"
/>

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: atlv <email@atlasdostal.com>
2025-11-01 21:24:14 +00:00
Patrick Walton 89f9dcb431 Don't require cameras to have color render targets. (#20830)
It can occasionally be useful to have cameras that *only* render
prepasses such as depth. Other game engines such as Unity support this
feature by allowing a depth-only render target to be assigned to a
camera. Bevy, however, has no easy mechanism for this. (Creating an
`ShadowView` in the render app doesn't work, because various places in
rendering assume that shadow views are associated with lights.)

This patch fixes the problem by introducing a new type of
`RenderTarget`, `RenderTarget::None`. Cameras with no render target will
skip the main opaque and transparent render passes, but any prepasses on
such cameras will still occur. Adding a `DepthPrepass` to such a camera
enables depth-only cameras, with maximum efficiency as the fragment
shader won't exist and no color buffer will be bound.

Note that, when no render target is specified, the physical size of the
viewport must be explicitly specified, as Bevy has no other mechanism to
determine it.

A new example, `render_depth_to_texture`, has been added, containing a
rotating cube and a depth-only camera orbiting it. The depth texture
that the camera produces is rendered onto a plane using a custom shader.
(NB: In such scenarios, the depth texture must be copied from the camera
to a custom image due to (a) the `wgpu` limitation that a depth texture
can't be both a render target and bindable as a texture and (b) the fact
that Bevy depth textures are managed by Bevy itself and exposed only to
the render world. The example uses a custom render node to perform the
copy.) The depth-only camera can be moved using the WASD keys.

<img width="2564" height="1500" alt="Screenshot 2025-09-02 080508"
src="https://github.com/user-attachments/assets/415e7f4d-393d-4be3-b569-829c06901078"
/>
2025-09-03 03:18:39 +00:00
charlotte 🌸 b6922f98d1 Revert bevy_sprite_render rename in shaders (#20644)
Fixes #20643
2025-08-18 23:11:28 +00:00
Rob Parrett 3560b112f4 Fix imports in some 2d examples with custom shaders (#20639)
# Objective

Fixes #20615

## Solution

These shaders weren't updated when the import moved in #20587.

Fix the imports.

## Testing

```
cargo run --example custom_gltf_vertex_attribute
cargo run --example shader_material_2d
cargo run --example mesh2d_manual
```

Co-authored-by: François Mockers <francois.mockers@vleue.com>
2025-08-18 21:59:54 +00:00
dontgetfoundout 5bc5a1325a Update Game of Life compute example to include a uniform buffer variable (#20466)
# Objective
It is currently a little unclear how to use uniform buffers in compute
shaders. The other examples of uniform buffers in the Bevy examples and
codebase either are built on Materials or use `DynamicUniformBuffer`s
created from a `ViewNode`. Neither of these are a great fit for use in a
compute shader.

## Solution
Update the compute shader example to pass a uniform buffer to the shader
that determines the color for alive cells.

## Discussion Topics
- Is this the right way to pass this data to the shader?
- Should we be encouraging use of uniform buffers in compute shaders at
all? Some in the community prefer the ergonomics of storage buffers in
most (all?) compute shader cases. Do we want to push users to use
storage buffers instead?
- I took the idea to use color as the input from IceSentry on Discord,
but this did require me to change the texture format to support non-red
colors. Does this undermine the goals of the shader example? Is this the
wrong texture format?

## Testing

- Did you test these changes? If so, how?
- The changes were manually validated with a number of different
`LinearRgba` values for `alive_color`
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - ` cargo run --example compute_shader_game_of_life`
- Color can be set using `alive_color` property on `GameOfLifeUniforms`
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  -  Manually validated on Windows and WASM (WebGPU) targets
    - WASM WebGL2 doesn't appear to support textures in compute shaders

---

## Showcase
<img width="1602" height="939" alt="image"
src="https://github.com/user-attachments/assets/9a535617-a179-4f20-b686-596899f11d18"
/>

---------

Co-authored-by: dontgetfoundout <inflatedego@gmail.com>
2025-08-11 22:52:02 +00:00
charlotte 🌸 e6ec2c181d Material bind group shader def (#20069)
Use a shader def for the material bind group index to make it easier for
when we want to switch back to group 2 in the future without breaking
everyone again.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: atlas dostal <rodol@rivalrebels.com>
2025-08-06 05:09:12 +00:00
Gilles Henaux ca25a67d0d Fix the extended_material example on WebGL2 (#18812)
# Objective

- Fixes #13872 (also mentioned in #17167)

## Solution

- Added conditional padding fields to the shader uniform

## Alternatives

### 1- Use a UVec4

Replace the `u32` field in `MyExtension` by a `UVec4` and only use the
`x` coordinate.

(This was the original approach, but for consistency with the rest of
the codebase, separate padding fields seem to be preferred)

### 2- Don't fix it, unlist it

While the fix is quite simple, it does muddy the waters a tiny bit due
to `quantize_steps` now being a UVec4 instead of a simple u32. We could
simply remove this example from the examples that support WebGL2.

## Testing

- Ran the example locally on WebGL2 (and native Vulkan) successfully
2025-07-07 19:34:12 +00:00
Nicky Fahey 831073105f Add comment to custom vertex attribute example to make it easier to convert to 2D (#18603)
# Objective

- It's not clear what changes are needed to the shader to convert the
example to 2D.
- If you leave the shader unchanged you get a very confusing error (see
linked issue).
- Fixes #14077

## Solution

A separate example probably isn't needed as there is little difference
between 3D and 2D, but a note saying what changes are needed to the
shader would make it a lot easier.

Let me know if you think it is also worth adding some notes to the rust
file, but it is mostly trivial changes such as changing `Mesh3d` to
`Mesh2d`. I have left the original code in comments next to the changes
in the gist linked at the bottom if you wish to compare.

## Testing

- I just spent a long time working it out the hard way. This would have
made it a lot quicker.
- I have tested the 2D version of the shader with the changes explained
in the suggested comment and it works as expected.
- For testing purposes [here is a complete working 2D
example](https://gist.github.com/nickyfahey/647e2a2c45e695f24e288432b811dfc2).
(note that as per the original example the shader file needs to go in
'assets/shaders/')
2025-07-07 19:26:37 +00:00
andriyDev 2ea8f779c3 Prevent AnimationGraph from serializing AssetIds. (#19615)
# Objective

- A step towards #19024.
- `AnimationGraph` can serialize raw `AssetId`s. However for normal
handles, this is a runtime ID. This means it is unlikely that the
`AssetId` will correspond to the same asset after deserializing -
effectively breaking the graph.

## Solution

- Stop allowing `AssetId` to be serialized by `AnimationGraph`.
Serializing a handle with no path is now an error.
- Add `MigrationSerializedAnimationClip`. This is an untagged enum for
serde, meaning that it will take the first variant that deserializes. So
it will first try the "modern" version, then it will fallback to the
legacy version.
- Add some logging/error messages to explain what users should do.

Note: one limitation here is that this removes the ability to serialize
and deserialize UUIDs. In theory, someone could be using this to have a
"default" animation. If someone inserts an empty `AnimationClip` into
the `Handle::default()`, this **might** produce a T-pose. It might also
do nothing though. Unclear! I think this is worth the risk for
simplicity as it seems unlikely that people are sticking UUIDs in here
(or that you want a default animation in **any** AnimationGraph).

## Testing

- Ran `cargo r --example animation_graph -- --save` on main, then ran
`cargo r --example animation_graph` on this PR. The PR was able to load
the old data (after #19631).
2025-06-30 22:26:05 +00:00
robtfm a2992fcffd Light Textures (#18031)
# Objective

add support for light textures (also known as light cookies, light
functions, and light projectors)


![image](https://github.com/user-attachments/assets/afdb23e2-b35f-4bf0-bf92-f883cd7db771)

## Solution

- add components:

```rs
/// Add to a [`PointLight`] to add a light texture effect.
/// A texture mask is applied to the light source to modulate its intensity,  
/// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs.
pub struct PointLightTexture {
    /// The texture image. Only the R channel is read.
    pub image: Handle<Image>,
    /// The cubemap layout. The image should be a packed cubemap in one of the formats described by the [`CubemapLayout`] enum.
    pub cubemap_layout: CubemapLayout,
}

/// Add to a [`SpotLight`] to add a light texture effect.
/// A texture mask is applied to the light source to modulate its intensity,  
/// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs.
pub struct SpotLightTexture {
    /// The texture image. Only the R channel is read.
    /// Note the border of the image should be entirely black to avoid leaking light.
    pub image: Handle<Image>,
}

/// Add to a [`DirectionalLight`] to add a light texture effect.
/// A texture mask is applied to the light source to modulate its intensity,  
/// simulating patterns like window shadows, gobo/cookie effects, or soft falloffs.
pub struct DirectionalLightTexture {
    /// The texture image. Only the R channel is read.
    pub image: Handle<Image>,
    /// Whether to tile the image infinitely, or use only a single tile centered at the light's translation
    pub tiled: bool,
}
```

- store images to the `RenderClusteredDecals` buffer
- read the image and modulate the lights
- add `light_textures` example to showcase the new features

## Testing

see light_textures example
2025-06-30 21:56:17 +00:00
charlotte 🌸 e6ba9a6d18 Type erased materials (#19667)
# Objective

Closes #18075

In order to enable a number of patterns for dynamic materials in the
engine, it's necessary to decouple the renderer from the `Material`
trait.

This opens the possibility for:
- Materials that aren't coupled to `AsBindGroup`.
- 2d using the underlying 3d bindless infrastructure.
- Dynamic materials that can change their layout at runtime.
- Materials that aren't even backed by a Rust struct at all.

## Solution

In short, remove all trait bounds from render world material systems and
resources. This means moving a bunch of stuff onto `MaterialProperties`
and engaging in some hacks to make specialization work. Rather than
storing the bind group data in `MaterialBindGroupAllocator`, right now
we're storing it in a closure on `MaterialProperties`. TBD if this has
bad performance characteristics.

## Benchmarks

- `many_cubes`:
`cargo run --example many_cubes --release --features=bevy/trace_tracy --
--vary-material-data-per-instance`:
![Screenshot 2025-06-26
235426](https://github.com/user-attachments/assets/10a0ee29-9932-4f91-ab43-33518b117ac5)

- @DGriffin91's Caldera
`cargo run --release --features=bevy/trace_tracy -- --random-materials`

![image](https://github.com/user-attachments/assets/ef91ba6a-8e88-4922-a73f-acb0af5b0dbc)


- @DGriffin91's Caldera with 20 unique material types (i.e.
`MaterialPlugin<M>`) and random materials per mesh
`cargo run --release --features=bevy/trace_tracy -- --random-materials`
![Screenshot 2025-06-27
000425](https://github.com/user-attachments/assets/9561388b-881d-46cf-8c3d-b15b3e9aedc7)


### TODO

- We almost certainly lost some parallelization from removing the type
params that could be gained back from smarter iteration.
- Test all the things that could have broken.
- ~Fix meshlets~

## Showcase

See [the
example](https://github.com/bevyengine/bevy/pull/19667/files#diff-9d768cfe1c3aa81eff365d250d3cbe5a63e8df63e81dd85f64c3c3cd993f6d94)
for a custom material implemented without the use of the `Material`
trait and thus `AsBindGroup`.


![image](https://github.com/user-attachments/assets/e3fcca7c-e04e-4a4e-9d89-39d697a9e3b8)

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
Co-authored-by: IceSentry <c.giguere42@gmail.com>
2025-06-27 22:57:24 +00:00
charlotte 🌸 96dcbc5f8c Ugrade to wgpu version 25.0 (#19563)
# Objective

Upgrade to `wgpu` version `25.0`.

Depends on https://github.com/bevyengine/naga_oil/pull/121

## Solution

### Problem

The biggest issue we face upgrading is the following requirement:
> To facilitate this change, there was an additional validation rule put
in place: if there is a binding array in a bind group, you may not use
dynamic offset buffers or uniform buffers in that bind group. This
requirement comes from vulkan rules on UpdateAfterBind descriptors.

This is a major difficulty for us, as there are a number of binding
arrays that are used in the view bind group. Note, this requirement does
not affect merely uniform buffors that use dynamic offset but the use of
*any* uniform in a bind group that also has a binding array.

### Attempted fixes

The easiest fix would be to change uniforms to be storage buffers
whenever binding arrays are in use:
```wgsl
#ifdef BINDING_ARRAYS_ARE_USED
@group(0) @binding(0) var<uniform> view: View;
@group(0) @binding(1) var<uniform> lights: types::Lights;
#else
@group(0) @binding(0) var<storage> view: array<View>;
@group(0) @binding(1) var<storage> lights: array<types::Lights>;
#endif
```

This requires passing the view index to the shader so that we know where
to index into the buffer:

```wgsl
struct PushConstants {
    view_index: u32,
}

var<push_constant> push_constants: PushConstants;
```

Using push constants is no problem because binding arrays are only
usable on native anyway.

However, this greatly complicates the ability to access `view` in
shaders. For example:
```wgsl
#ifdef BINDING_ARRAYS_ARE_USED
mesh_view_bindings::view.view_from_world[0].z
#else
mesh_view_bindings::view[mesh_view_bindings::view_index].view_from_world[0].z
#endif
```

Using this approach would work but would have the effect of polluting
our shaders with ifdef spam basically *everywhere*.

Why not use a function? Unfortunately, the following is not valid wgsl
as it returns a binding directly from a function in the uniform path.

```wgsl
fn get_view() -> View {
#if BINDING_ARRAYS_ARE_USED
    let view_index = push_constants.view_index;
    let view = views[view_index];
#endif
    return view;
}
```

This also poses problems for things like lights where we want to return
a ptr to the light data. Returning ptrs from wgsl functions isn't
allowed even if both bindings were buffers.

The next attempt was to simply use indexed buffers everywhere, in both
the binding array and non binding array path. This would be viable if
push constants were available everywhere to pass the view index, but
unfortunately they are not available on webgpu. This means either
passing the view index in a storage buffer (not ideal for such a small
amount of state) or using push constants sometimes and uniform buffers
only on webgpu. However, this kind of conditional layout infects
absolutely everything.

Even if we were to accept just using storage buffer for the view index,
there's also the additional problem that some dynamic offsets aren't
actually per-view but per-use of a setting on a camera, which would
require passing that uniform data on *every* camera regardless of
whether that rendering feature is being used, which is also gross.

As such, although it's gross, the simplest solution just to bump binding
arrays into `@group(1)` and all other bindings up one bind group. This
should still bring us under the device limit of 4 for most users.

### Next steps / looking towards the future

I'd like to avoid needing split our view bind group into multiple parts.
In the future, if `wgpu` were to add `@builtin(draw_index)`, we could
build a list of draw state in gpu processing and avoid the need for any
kind of state change at all (see
https://github.com/gfx-rs/wgpu/issues/6823). This would also provide
significantly more flexibility to handle things like offsets into other
arrays that may not be per-view.

### Testing

Tested a number of examples, there are probably more that are still
broken.

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
Co-authored-by: Elabajaba <Elabajaba@users.noreply.github.com>
2025-06-26 19:41:47 +00:00
JMS55 bab31e3777 Initial raytraced lighting progress (bevy_solari) (#19058)
# Bevy Solari 
<img
src="https://github.com/user-attachments/assets/94061fc8-01cf-4208-b72a-8eecad610d76"
width="100" />

## Preface
- See release notes.
- Please talk to me in #rendering-dev on discord or open a github
discussion if you have questions about the long term plan, and keep
discussion in this PR limited to the contents of the PR :)

## Connections
- Works towards #639, #16408.
- Spawned https://github.com/bevyengine/bevy/issues/18993.
- Need to fix RT stuff in naga_oil first
https://github.com/bevyengine/naga_oil/pull/116.

## This PR

After nearly two years, I've revived the raytraced lighting effort I
first started in https://github.com/bevyengine/bevy/pull/10000.

Unlike that PR, which has realtime techniques, I've limited this PR to:
* `RaytracingScenePlugin` - BLAS and TLAS building, geometry and texture
binding, sampling functions.
* `PathtracingPlugin` - A non-realtime path tracer intended to serve as
a testbed and reference.

## What's implemented?

![image](https://github.com/user-attachments/assets/06522007-c205-46eb-8178-823f19917def)

* BLAS building on mesh load
* Emissive lights
* Directional lights with soft shadows
* Diffuse (lambert, not Bevy's diffuse BRDF) and emissive materials
* A reference path tracer with:
  * Antialiasing
  * Direct light sampling (next event estimation) with 0/1 MIS weights
  * Importance-sampled BRDF bounces
  * Russian roulette 

## What's _not_ implemented?
* Anything realtime, including a real-time denoiser
* Integration with Bevy's rasterized gbuffer
* Specular materials
* Non-opaque geometry
* Any sort of CPU or GPU optimizations
* BLAS compaction, proper bindless, and further RT APIs are things that
we need wgpu to add
* PointLights, SpotLights, or skyboxes / environment lighting 
* Support for materials other than StandardMaterial (and only a subset
of properties are supported)
* Skinned/morphed or otherwise animating/deformed meshes
* Mipmaps
* Adaptive self-intersection ray bias
* A good way for developers to detect whether the user's GPU supports RT
or not, and fallback to baked lighting.
* Documentation and actual finalized APIs (literally everything is
subject to change)

## End-user Usage
* Have a GPU that supports RT with inline ray queries
* Add `SolariPlugin` to your app
* Ensure any `Mesh` asset you want to use for raytracing has
`enable_raytracing: true` (defaults to true), and that it uses the
standard uncompressed position/normal/uv_0/tangent vertex attribute set,
triangle list topology, and 32-bit indices.
* If you don't want to build a BLAS and use the mesh for RT, set
enable_raytracing to false.
* Add the `RaytracingMesh3d` component to your entity (separate from
`Mesh3d` or `MeshletMesh3d`).

## Testing

- Did you test these changes? If so, how? 
  - Ran the solari example.
- Are there any parts that need more testing?
  - Other test scenes probably. Normal mapping would be good to test.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
  - See the solari.rs example for how to setup raytracing.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - Windows 11, NVIDIA RTX 3080.

---------

Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2025-06-12 21:26:10 +00:00
Niklas Eicker 923c2ad281 New cooldown example (#19234)
# Objective

We want to extend our examples with a new category "usage" to
demonstrate common use cases (see bevyengine/bevy-website#2131). This PR
adds an example of animated cooldowns on button clicks.

## Solution

- New example in "usage" directory
- Implement a cooldown with an animated child Node

## Testing

- I ran this on Linux
- [x] test web (with bevy CLI: `bevy run --example cooldown web --open`)

---------

Co-authored-by: Thierry Berger <contact@thierryberger.com>
Co-authored-by: Ida "Iyes" <40234599+inodentry@users.noreply.github.com>
2025-05-26 19:44:17 +00:00
Eagster 0b4858726c Make entity::index non max (#18704)
# Objective

There are two problems this aims to solve. 

First, `Entity::index` is currently a `u32`. That means there are
`u32::MAX + 1` possible entities. Not only is that awkward, but it also
make `Entity` allocation more difficult. I discovered this while working
on remote entity reservation, but even on main, `Entities` doesn't
handle the `u32::MAX + 1` entity very well. It can not be batch reserved
because that iterator uses exclusive ranges, which has a maximum upper
bound of `u32::MAX - 1`. In other words, having `u32::MAX` as a valid
index can be thought of as a bug right now. We either need to make that
invalid (this PR), which makes Entity allocation cleaner and makes
remote reservation easier (because the length only needs to be u32
instead of u64, which, in atomics is a big deal), or we need to take
another pass at `Entities` to make it handle the `u32::MAX` index
properly.

Second, `TableRow`, `ArchetypeRow` and `EntityIndex` (a type alias for
u32) all have `u32` as the underlying type. That means using these as
the index type in a `SparseSet` uses 64 bits for the sparse list because
it stores `Option<IndexType>`. By using `NonMaxU32` here, we cut the
memory of that list in half. To my knowledge, `EntityIndex` is the only
thing that would really benefit from this niche. `TableRow` and
`ArchetypeRow` I think are not stored in an `Option` in bulk. But if
they ever are, this would help. Additionally this ensures
`TableRow::INVALID` and `ArchetypeRow::INVALID` never conflict with an
actual row, which in a nice bonus.

As a related note, if we do components as entities where `ComponentId`
becomes `Entity`, the the `SparseSet<ComponentId>` will see a similar
memory improvement too.

## Solution

Create a new type `EntityRow` that wraps `NonMaxU32`, similar to
`TableRow` and `ArchetypeRow`.
Change `Entity::index` to this type.

## Downsides

`NonMax` is implemented as a `NonZero` with a binary inversion. That
means accessing and storing the value takes one more instruction. I
don't think that's a big deal, but it's worth mentioning.

As a consequence, `to_bits` uses `transmute` to skip the inversion which
keeps it a nop. But that also means that ordering has now flipped. In
other words, higher indices are considered less than lower indices. I
don't think that's a problem, but it's also worth mentioning.

## Alternatives

We could keep the index as a u32 type and just document that `u32::MAX`
is invalid, modifying `Entities` to ensure it never gets handed out.
(But that's not enforced by the type system.) We could still take
advantage of the niche here in `ComponentSparseSet`. We'd just need some
unsafe manual conversions, which is probably fine, but opens up the
possibility for correctness problems later.

We could change `Entities` to fully support the `u32::MAX` index. (But
that makes `Entities` more complex and potentially slightly slower.)

## Testing

- CI
- A few tests were changed because they depend on different ordering and
`to_bits` values.

## Future Work

- It might be worth removing the niche on `Entity::generation` since
there is now a different niche.
- We could move `Entity::generation` into it's own type too for clarity.
- We should change `ComponentSparseSet` to take advantage of the new
niche. (This PR doesn't change that yet.)
- Consider removing or updating `Identifier`. This is only used for
`Entity`, so it might be worth combining since `Entity` is now more
unique.

---------

Co-authored-by: atlv <email@atlasdostal.com>
Co-authored-by: Zachary Harrold <zac@harrold.com.au>
2025-05-07 18:20:30 +00:00
Mathis Brossier 119eb51f00 Fix game_of_life shader relying on Naga bug (#18951)
# Objective

The game of life example shader relies on a Naga bug
([6397](https://github.com/gfx-rs/wgpu/issues/6397) /
[4536](https://github.com/gfx-rs/wgpu/issues/4536)). In WGSL certain
arithmetic operations must be explicitly parenthesized
([reference](https://www.w3.org/TR/WGSL/#operator-precedence-associativity)).
Naga doesn't enforce that (and also the precedence order is [messed
up](https://github.com/gfx-rs/wgpu/issues/4536#issuecomment-1780113990)).

So this example may break soon. This is the only sample shader having
this issue.

## Solution

added parentheses

## Testing

ran the example before and after the fix with `cargo run --example
compute_shader_game_of_life`
2025-04-26 21:38:08 +00:00
Patrick Walton dc7c8f228f Add bindless support back to ExtendedMaterial. (#18025)
PR #17898 disabled bindless support for `ExtendedMaterial`. This commit
adds it back. It also adds a new example, `extended_material_bindless`,
showing how to use it.
2025-04-09 15:34:44 +00:00
François Mockers 3945a6de3b Fix wesl in wasm and webgl2 (#18591)
# Objective

- feature `shader_format_wesl` doesn't compile in Wasm
- once fixed, example `shader_material_wesl` doesn't work in WebGL2

## Solution

- remove special path handling when loading shaders. this seems like a
way to escape the asset folder which we don't want to allow, and can't
compile on android or wasm, and can't work on iOS (filesystem is rooted
there)
- pad material so that it's 16 bits. I couldn't get conditional
compilation to work in wesl for type declaration, it fails to parse
- the shader renders the color `(0.0, 0.0, 0.0, 0.0)` when it's not a
polka dot. this renders as black on WebGPU/metal/..., and white on
WebGL2. change it to `(0.0, 0.0, 0.0, 1.0)` so that it's black
everywhere
2025-03-28 21:45:02 +00:00
charlotte 35bf9753e8 Fixes for WESL on Windows (#18373)
# Objective

WESL was broken on windows.

## Solution

- Upgrade to `wesl_rs` 1.2.
- Fix path handling on windows.
- Improve example for khronos demo this week.
2025-03-17 22:29:29 +00:00
Benjamin Brienen c3ff6d4136 Fix non-crate typos (#18219)
# Objective

Correct spelling

## Solution

Fix typos, specifically ones that I found in folders other than /crates

## Testing

CI

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
2025-03-11 06:17:48 +00:00
Patrick Walton 913eb46324 Reimplement bindless storage buffers. (#17994)
Support for bindless storage buffers was temporarily removed with the
bindless revamp. This commit restores that support.
2025-03-10 21:32:19 +00:00
charlotte 181445c56b Add support for experimental WESL shader source (#17953)
# Objective

WESL's pre-MVP `0.1.0` has been
[released](https://docs.rs/wesl/latest/wesl/)!

Add support for WESL shader source so that we can begin playing and
testing WESL, as well as aiding in their development.

## Solution

Adds a `ShaderSource::WESL` that can be used to load `.wesl` shaders.

Right now, we don't support mixing `naga-oil`. Additionally, WESL
shaders currently need to pass through the naga frontend, which the WESL
team is aware isn't great for performance (they're working on compiling
into naga modules). Also, since our shaders are managed using the asset
system, we don't currently support using file based imports like `super`
or package scoped imports. Further work will be needed to asses how we
want to support this.

---

## Showcase

See the `shader_material_wesl` example. Be sure to press space to
activate party mode (trigger conditional compilation)!


https://github.com/user-attachments/assets/ec6ad19f-b6e4-4e9d-a00f-6f09336b08a4
2025-03-09 19:26:55 +00:00
Patrick Walton 28441337bb Use global binding arrays for bindless resources. (#17898)
Currently, Bevy's implementation of bindless resources is rather
unusual: every binding in an object that implements `AsBindGroup` (most
commonly, a material) becomes its own separate binding array in the
shader. This is inefficient for two reasons:

1. If multiple materials reference the same texture or other resource,
the reference to that resource will be duplicated many times. This
increases `wgpu` validation overhead.

2. It creates many unused binding array slots. This increases `wgpu` and
driver overhead and makes it easier to hit limits on APIs that `wgpu`
currently imposes tight resource limits on, like Metal.

This PR fixes these issues by switching Bevy to use the standard
approach in GPU-driven renderers, in which resources are de-duplicated
and passed as global arrays, one for each type of resource.

Along the way, this patch introduces per-platform resource limits and
bumps them from 16 resources per binding array to 64 resources per bind
group on Metal and 2048 resources per bind group on other platforms.
(Note that the number of resources per *binding array* isn't the same as
the number of resources per *bind group*; as it currently stands, if all
the PBR features are turned on, Bevy could pack as many as 496 resources
into a single slab.) The limits have been increased because `wgpu` now
has universal support for partially-bound binding arrays, which mean
that we no longer need to fill the binding arrays with fallback
resources on Direct3D 12. The `#[bindless(LIMIT)]` declaration when
deriving `AsBindGroup` can now simply be written `#[bindless]` in order
to have Bevy choose a default limit size for the current platform.
Custom limits are still available with the new
`#[bindless(limit(LIMIT))]` syntax: e.g. `#[bindless(limit(8))]`.

The material bind group allocator has been completely rewritten. Now
there are two allocators: one for bindless materials and one for
non-bindless materials. The new non-bindless material allocator simply
maintains a 1:1 mapping from material to bind group. The new bindless
material allocator maintains a list of slabs and allocates materials
into slabs on a first-fit basis. This unfortunately makes its
performance O(number of resources per object * number of slabs), but the
number of slabs is likely to be low, and it's planned to become even
lower in the future with `wgpu` improvements. Resources are
de-duplicated with in a slab and reference counted. So, for instance, if
multiple materials refer to the same texture, that texture will exist
only once in the appropriate binding array.

To support these new features, this patch adds the concept of a
*bindless descriptor* to the `AsBindGroup` trait. The bindless
descriptor allows the material bind group allocator to probe the layout
of the material, now that an array of `BindGroupLayoutEntry` records is
insufficient to describe the group. The `#[derive(AsBindGroup)]` has
been heavily modified to support the new features. The most important
user-facing change to that macro is that the struct-level `uniform`
attribute, `#[uniform(BINDING_NUMBER, StandardMaterial)]`, now reads
`#[uniform(BINDLESS_INDEX, MATERIAL_UNIFORM_TYPE,
binding_array(BINDING_NUMBER)]`, allowing the material to specify the
binding number for the binding array that holds the uniform data.

To make this patch simpler, I removed support for bindless
`ExtendedMaterial`s, as well as field-level bindless uniform and storage
buffers. I intend to add back support for these as a follow-up. Because
they aren't in any released Bevy version yet, I figured this was OK.

Finally, this patch updates `StandardMaterial` for the new bindless
changes. Generally, code throughout the PBR shaders that looked like
`base_color_texture[slot]` now looks like
`bindless_2d_textures[material_indices[slot].base_color_texture]`.

This patch fixes a system hang that I experienced on the [Caldera test]
when running with `caldera --random-materials --texture-count 100`. The
time per frame is around 19.75 ms, down from 154.2 ms in Bevy 0.14: a
7.8× speedup.

[Caldera test]: https://github.com/DGriffin91/bevy_caldera_scene
2025-02-21 05:55:36 +00:00
ickshonpe 02985c3d56 ui_material example webgl2 fix (#17852)
# Objective

Fixes #17851

## Solution

Align the `slider` uniform to 16 bytes by making it a `vec4`.

## Testing

Run the example using:
```
cargo run -p build-wasm-example -- --api webgl2 ui_material
basic-http-server examples/wasm/
```
2025-02-13 20:52:26 +00:00
Rob Parrett fe7a29e12b Fix error in scene example (#17799)
# Objective

After #16894, this example started logging errors:

```
ERROR bevy_asset::server: Failed to load asset 'scenes/load_scene_example.scn.ron' with asset loader 'bevy_scene::scene_loader::SceneLoader': Could not parse RON: 10:33: Expected string
```

Fixes #17798, this is the only actionable/unreported issue in there as
far as I can tell.

## Solution

Update the serialized scene with the expected format for `Name`

## Testing

`cargo run --example scene`

## Discussion

This example breaks very often and we don't always catch it. It might be
nice to have this scene either

1. produce visual output so that it can be checked
2. panic if the scene fails to load (check for LoadState::Failed)

Either of those would make the failures visible in [the example
report](https://thebevyflock.github.io/bevy-example-runner/). Not sure
which method would best suit the example.
2025-02-11 22:20:23 +00:00
Rob Parrett 0cb3eaef67 Fix validation errors in Fox.glb (#17801)
# Objective

Fix gltf validation errors in `Fox.glb`.

Inspired by #8099, but that issue doesn't appear to describe a real bug
to fix, as far as I can tell.

## Solution

Use the latest version of the Fox from
[glTF-Sample-Assets](https://github.com/KhronosGroup/glTF-Sample-Assets/blob/main/Models/Fox/glTF-Binary/Fox.glb).

## Testing

Dropped both versions in https://github.khronos.org/glTF-Validator/

`cargo run --example animated_mesh` seems to still look fine.

Before:

```
The asset contains errors.
"numErrors": 126,
"numWarnings": 4184,
```

After:

```
The asset is valid.
"numErrors": 0,
"numWarnings": 0,
```

## Discussion

The 3d testbed was panicking with
```
thread 'main' panicked at examples/testbed/3d.rs:288:60:
called `Result::unwrap()` on an `Err` value: QueryDoesNotMatch(35v1 with components Transform, GlobalTransform, Visibility, InheritedVisibility, ViewVisibility, ChildOf, Children, Name)
```
Which is bizarre. I think this might be related to #17720, or maybe the
structure of the gltf changed.

I fixed it by using updating the testbed to use a more robust method of
finding the correct entity as is done in `animated_mesh`.
2025-02-11 22:19:24 +00:00
charlotte a861452d68 Add user supplied mesh tag (#17648)
# Objective

Because of mesh preprocessing, users cannot rely on
`@builtin(instance_index)` in order to reference external data, as the
instance index is not stable, either from frame to frame or relative to
the total spawn order of mesh instances.

## Solution

Add a user supplied mesh index that can be used for referencing external
data when drawing instanced meshes.

Closes #13373

## Testing

Benchmarked `many_cubes` showing no difference in total frame time.

## Showcase



https://github.com/user-attachments/assets/80620147-aafc-4d9d-a8ee-e2149f7c8f3b

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
2025-02-10 22:38:13 +00:00
IceSentry 4ecbe001d5 Add a custom render phase example (#16916)
# Objective

- It's currently very hard for beginners and advanced users to get a
full understanding of a complete render phase.

## Solution

- Implement a full custom render phase
- The render phase in the example is intended to show a custom stencil
phase that renders the stencil in red directly on the screen

---

## Showcase

<img width="1277" alt="image"
src="https://github.com/user-attachments/assets/e9dc0105-4fb6-463f-ad53-0529b575fd28"
/>

## Notes

More docs to explain what is going on is still needed but the example
works and can already help some people.

We might want to consider using a batched phase and cold specialization
in the future, but the example is already complex enough as it is.

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2025-02-10 21:17:37 +00:00