-
-
Notifications
You must be signed in to change notification settings - Fork 306
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Appification / Final Push #969
Merged
tychedelia
merged 133 commits into
nannou-org:bevy-refactor
from
tychedelia:955-abstract-the-nannoudraw-module-into-the-new-bevy_nannou_draw-crate-1-material
Jul 31, 2024
Merged
Appification / Final Push #969
tychedelia
merged 133 commits into
nannou-org:bevy-refactor
from
tychedelia:955-abstract-the-nannoudraw-module-into-the-new-bevy_nannou_draw-crate-1-material
Jul 31, 2024
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tychedelia
force-pushed
the
955-abstract-the-nannoudraw-module-into-the-new-bevy_nannou_draw-crate-1-material
branch
from
July 19, 2024 06:21
b47e959
to
bbe949e
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This code is probably going to be pretty impossible to review in the diff. Highly suggest checking it out and just playing around with it.
Changes
Draw materials
The
Draw
instance is now generic over abevy
material. By default, we expose aDefaultNannouMaterial
that is what most users will interact with. This material is a material extension over thebevy
StandardMaterial
, which is their default PBR material and contains a variety of useful properties that users can now use.Rendering materials
The rendering algorithm is significantly complicated by making
Draw
generic. Every time a user mutates their drawing in a way that requires a new material, this requires storing that material instance within our draw state. Further, we support changing that material's type (as described below) for any given draw instance or drawing, which transitions the type of the draw instance fromDraw<M>
toDraw<M2>
. Because these cloned / mutated draw instances still point to the same "logical" draw, we need a way to type erase materials within our draw state.This is accomplished in the following manner:
last_material
within state that tracks the id of the last known material, which works similarly to tracking context.None
slot is added todraw_commands
which can be used if the material is later changed for that drawing.last_material
has changed, we push aDrawCommand::Material(UntypedAssetId)
. Here,UntypedAssetId
is the mechanism through which we achieve type erasure. Inside the draw state, we store aHashMap<UntypedAssetId, Box<dyn Any + Send + Sync>>
, which contains all the materials that are used in our drawing and will be downcast later when processed for rendering.Drawing
, we now store a copy on write pointer typeDrawRef
that can either be a reference to a parent draw instance OR a owned clone of that draw instance with a different material parameter. The state of ref is used to determine on drop whether we mutated the material in our drawing, and thus whether a new material needs to be inserted intodraw_commands
at the index slot we created in advance when the drawing was created.bevy
system that runs after our drawing, we iterate through the materials stored inState
and try to downcast them into a given registered material type. We then add the material as an asset to thebevy
world so it can be used by our mesh rendering system.Custom materials
A variety of new possibilities are opened up by allowing users to create their own materials. The
bevy
Material
allows easy access to writing a vertex or fragment shader for a given drawing, which means user's can easily include any arbitrary data they want when writing a custom shader for a given drawing primitive. What this means in practice is that rather than writing manualwgpu
code, the user will write something like the following in order to render a fullscreen shader:Removal of
Texture
drawing primitivePreviously, a texture was rendered in
nannou
using a special primitive that instructed the fragment shader to sample a given texture. This has primitive has been removed. Users now should use the material methodtexture
that sets thebevy
material's texture to a givenHandle<Image>
, which represents an asset handle to awgpu
texture. Similarly to above, this means that you now will render a simple texture as a `rect:In the
model
function, we now usebevy
'sAssetLoader
exposed via the world in order to load images as an asset handle. These handles can then be used to load an image into cpu memory to do things like configure the given sampler for an image. In general, this provides a lot more flexibility and should allow users to do more powerful things with textures without having to drop into our "points" API.Mesh UVs
In order to render textures and do anything interesting with custom shaders, it is now important that every drawing primitive has texture coordinates, and these are a required field in our vertex layout. Drawing primitives have been changed to provide a set of default UVs where sensible. Additionally, a
points_vertex
method has been added to our "points" API that allows the user to provide explicit UVs in additional to position and color data. Our "full" vertex type is now always(Vec2, Color, Vec2)
.Additionally, a new
SetTexCoords
property trait has been added that allows users to change the UVs for a given drawing primitive. For example, ourrect
has a sensible[0.0, 0.0]
to[1.0, 1.0]
set by default, but users may want to sample from a smaller part of a texture:The
area
method (final name tbd) here ofSetTexCoords
allows providing ageom::Rect
that updates the UVs. In this way, while we make a best effort to provide sensible default values, users can override those values which may be particularly useful for things like triangles or circles, etc.There are still a few paths where we may fall back to a default
Vec2::ZERO
, but this can hopefully be eliminated over time. We also still provide thepoints_colored
API for instances where users truly don't care about their texture coordinates, but anyone working with textures or custom shaders will need to provide their own.App changes
Our
App
instance now wrapsbevy
's world as aRc<RefCell<UnsafeWorldCell<'w>>>
. This interior mutability allows us to access potentially mutating methods on thebevy
world without having to use&mut Self
on ourApp
instance.App
methodsAll of
App
's methods now borrow our internal reference to world and return that data to a user. Sometimes, this means returning an instance of abevy
resource directly (e.g.Time
orAssetLoader
). Due to the use of interior mutability, it's theoretically possible for the user to cause a panic, although I have not experienced that yet. We'll want to be careful and watch to ensure that this isn't easy to do.Creating
App
in internal systemsBecause we store our
nannou
bookkeeping state (i.e. windows, event handler function pointers, the user's model) in thebevy
world, there's a bit of unsafety and complexity around how to then provide an instance ofApp
to the user that wraps that world.Each system that runs a given event handler declares the dependencies it requires to be extracted from the world before creating the
App
instance to pass to the user's handler. For example:This
SystemState
is then used to unsafely extract the state and create anApp
instance:This is dangerous! The primary invariant we must uphold here is that we never expose methods on
App
that would allow mutable reference to the state we extract from world here. In many cases this is fine, because we are extracting internalnannou
state that user's don't need to know about, but is a potential source of soundness issues.TODO:
- [ ] Figure out how to interlace draw cmds where primitives may mutate their own material.* Actually this is okay. If a drawing changes it's material, we probably do want it to have a higher z-value as well as reset the mesh/material of its parents. I think this behavior makes more sense atm.
- [ ] Document / address send bounds?UpdateMode
examples.