2024-05-27 16:44:52 +00:00
|
|
|
//! This example shows how to sample random points from primitive shapes.
|
|
|
|
|
|
|
|
use std::f32::consts::PI;
|
|
|
|
|
|
|
|
use bevy::{
|
Rename rendering components for improved consistency and clarity (#15035)
# Objective
The names of numerous rendering components in Bevy are inconsistent and
a bit confusing. Relevant names include:
- `AutoExposureSettings`
- `AutoExposureSettingsUniform`
- `BloomSettings`
- `BloomUniform` (no `Settings`)
- `BloomPrefilterSettings`
- `ChromaticAberration` (no `Settings`)
- `ContrastAdaptiveSharpeningSettings`
- `DepthOfFieldSettings`
- `DepthOfFieldUniform` (no `Settings`)
- `FogSettings`
- `SmaaSettings`, `Fxaa`, `TemporalAntiAliasSettings` (really
inconsistent??)
- `ScreenSpaceAmbientOcclusionSettings`
- `ScreenSpaceReflectionsSettings`
- `VolumetricFogSettings`
Firstly, there's a lot of inconsistency between `Foo`/`FooSettings` and
`FooUniform`/`FooSettingsUniform` and whether names are abbreviated or
not.
Secondly, the `Settings` post-fix seems unnecessary and a bit confusing
semantically, since it makes it seem like the component is mostly just
auxiliary configuration instead of the core *thing* that actually
enables the feature. This will be an even bigger problem once bundles
like `TemporalAntiAliasBundle` are deprecated in favor of required
components, as users will expect a component named `TemporalAntiAlias`
(or similar), not `TemporalAntiAliasSettings`.
## Solution
Drop the `Settings` post-fix from the component names, and change some
names to be more consistent.
- `AutoExposure`
- `AutoExposureUniform`
- `Bloom`
- `BloomUniform`
- `BloomPrefilter`
- `ChromaticAberration`
- `ContrastAdaptiveSharpening`
- `DepthOfField`
- `DepthOfFieldUniform`
- `DistanceFog`
- `Smaa`, `Fxaa`, `TemporalAntiAliasing` (note: we might want to change
to `Taa`, see "Discussion")
- `ScreenSpaceAmbientOcclusion`
- `ScreenSpaceReflections`
- `VolumetricFog`
I kept the old names as deprecated type aliases to make migration a bit
less painful for users. We should remove them after the next release.
(And let me know if I should just... not add them at all)
I also added some very basic docs for a few types where they were
missing, like on `Fxaa` and `DepthOfField`.
## Discussion
- `TemporalAntiAliasing` is still inconsistent with `Smaa` and `Fxaa`.
Consensus [on
Discord](https://discord.com/channels/691052431525675048/743663924229963868/1280601167209955431)
seemed to be that renaming to `Taa` would probably be fine, but I think
it's a bit more controversial, and it would've required renaming a lot
of related types like `TemporalAntiAliasNode`,
`TemporalAntiAliasBundle`, and `TemporalAntiAliasPlugin`, so I think
it's better to leave to a follow-up.
- I think `Fog` should probably have a more specific name like
`DistanceFog` considering it seems to be distinct from `VolumetricFog`.
~~This should probably be done in a follow-up though, so I just removed
the `Settings` post-fix for now.~~ (done)
---
## Migration Guide
Many rendering components have been renamed for improved consistency and
clarity.
- `AutoExposureSettings` → `AutoExposure`
- `BloomSettings` → `Bloom`
- `BloomPrefilterSettings` → `BloomPrefilter`
- `ContrastAdaptiveSharpeningSettings` → `ContrastAdaptiveSharpening`
- `DepthOfFieldSettings` → `DepthOfField`
- `FogSettings` → `DistanceFog`
- `SmaaSettings` → `Smaa`
- `TemporalAntiAliasSettings` → `TemporalAntiAliasing`
- `ScreenSpaceAmbientOcclusionSettings` → `ScreenSpaceAmbientOcclusion`
- `ScreenSpaceReflectionsSettings` → `ScreenSpaceReflections`
- `VolumetricFogSettings` → `VolumetricFog`
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-09-10 01:11:46 +00:00
|
|
|
core_pipeline::{bloom::Bloom, tonemapping::Tonemapping},
|
2024-07-29 23:38:59 +00:00
|
|
|
input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButtonInput},
|
2024-05-27 16:44:52 +00:00
|
|
|
math::prelude::*,
|
|
|
|
prelude::*,
|
|
|
|
};
|
2024-09-24 11:42:59 +00:00
|
|
|
use rand::{seq::SliceRandom, Rng, SeedableRng};
|
2024-05-27 16:44:52 +00:00
|
|
|
use rand_chacha::ChaCha8Rng;
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
App::new()
|
|
|
|
.add_plugins(DefaultPlugins)
|
|
|
|
.insert_resource(SampledShapes::new())
|
|
|
|
.add_systems(Startup, setup)
|
|
|
|
.add_systems(
|
|
|
|
Update,
|
|
|
|
(
|
|
|
|
handle_mouse,
|
|
|
|
handle_keypress,
|
|
|
|
spawn_points,
|
|
|
|
despawn_points,
|
|
|
|
animate_spawning,
|
|
|
|
animate_despawning,
|
|
|
|
update_camera,
|
|
|
|
update_lights,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constants
|
|
|
|
|
|
|
|
/// Maximum distance of the camera from its target. (meters)
|
|
|
|
/// Should be set such that it is possible to look at all objects
|
|
|
|
const MAX_CAMERA_DISTANCE: f32 = 12.0;
|
|
|
|
|
|
|
|
/// Minimum distance of the camera from its target. (meters)
|
|
|
|
/// Should be set such that it is not possible to clip into objects
|
|
|
|
const MIN_CAMERA_DISTANCE: f32 = 1.0;
|
|
|
|
|
|
|
|
/// Offset to be placed between the shapes
|
|
|
|
const DISTANCE_BETWEEN_SHAPES: Vec3 = Vec3::new(2.0, 0.0, 0.0);
|
|
|
|
|
|
|
|
/// Maximum amount of points allowed to be present.
|
|
|
|
/// Should be set such that it does not cause large amounts of lag when reached.
|
|
|
|
const MAX_POINTS: usize = 3000; // TODO: Test wasm and add a wasm-specific-bound
|
|
|
|
|
|
|
|
/// How many points should be spawned each frame
|
|
|
|
const POINTS_PER_FRAME: usize = 3;
|
|
|
|
|
|
|
|
/// Color used for the inside points
|
|
|
|
const INSIDE_POINT_COLOR: LinearRgba = LinearRgba::rgb(0.855, 1.1, 0.01);
|
|
|
|
/// Color used for the points on the boundary
|
|
|
|
const BOUNDARY_POINT_COLOR: LinearRgba = LinearRgba::rgb(0.08, 0.2, 0.90);
|
|
|
|
|
|
|
|
/// Time (in seconds) for the spawning/despawning animation
|
|
|
|
const ANIMATION_TIME: f32 = 1.0;
|
|
|
|
|
|
|
|
/// Color for the sky and the sky-light
|
|
|
|
const SKY_COLOR: Color = Color::srgb(0.02, 0.06, 0.15);
|
|
|
|
|
|
|
|
const SMALL_3D: f32 = 0.5;
|
|
|
|
const BIG_3D: f32 = 1.0;
|
|
|
|
|
|
|
|
// primitives
|
|
|
|
|
|
|
|
const CUBOID: Cuboid = Cuboid {
|
|
|
|
half_size: Vec3::new(SMALL_3D, BIG_3D, SMALL_3D),
|
|
|
|
};
|
|
|
|
|
|
|
|
const SPHERE: Sphere = Sphere {
|
|
|
|
radius: 1.5 * SMALL_3D,
|
|
|
|
};
|
|
|
|
|
|
|
|
const TRIANGLE_3D: Triangle3d = Triangle3d {
|
|
|
|
vertices: [
|
|
|
|
Vec3::new(BIG_3D, -BIG_3D * 0.5, 0.0),
|
|
|
|
Vec3::new(0.0, BIG_3D, 0.0),
|
|
|
|
Vec3::new(-BIG_3D, -BIG_3D * 0.5, 0.0),
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
const CAPSULE_3D: Capsule3d = Capsule3d {
|
|
|
|
radius: SMALL_3D,
|
|
|
|
half_length: SMALL_3D,
|
|
|
|
};
|
|
|
|
|
|
|
|
const CYLINDER: Cylinder = Cylinder {
|
|
|
|
radius: SMALL_3D,
|
|
|
|
half_height: SMALL_3D,
|
|
|
|
};
|
|
|
|
|
|
|
|
const TETRAHEDRON: Tetrahedron = Tetrahedron {
|
|
|
|
vertices: [
|
|
|
|
Vec3::new(-BIG_3D, -BIG_3D * 0.67, BIG_3D * 0.5),
|
|
|
|
Vec3::new(BIG_3D, -BIG_3D * 0.67, BIG_3D * 0.5),
|
|
|
|
Vec3::new(0.0, -BIG_3D * 0.67, -BIG_3D * 1.17),
|
|
|
|
Vec3::new(0.0, BIG_3D, 0.0),
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
// Components, Resources
|
|
|
|
|
|
|
|
/// Resource for the random sampling mode, telling whether to sample the interior or the boundary.
|
|
|
|
#[derive(Resource)]
|
|
|
|
enum SamplingMode {
|
|
|
|
Interior,
|
|
|
|
Boundary,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resource for storing whether points should spawn by themselves
|
|
|
|
#[derive(Resource)]
|
|
|
|
enum SpawningMode {
|
|
|
|
Manual,
|
|
|
|
Automatic,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resource for tracking how many points should be spawned
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct SpawnQueue(usize);
|
|
|
|
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct PointCounter(usize);
|
|
|
|
|
|
|
|
/// Resource storing the shapes being sampled and their translations.
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct SampledShapes(Vec<(Shape, Vec3)>);
|
|
|
|
|
|
|
|
impl SampledShapes {
|
|
|
|
fn new() -> Self {
|
|
|
|
let shapes = Shape::list_all_shapes();
|
|
|
|
|
|
|
|
let n_shapes = shapes.len();
|
|
|
|
|
|
|
|
let translations =
|
|
|
|
(0..n_shapes).map(|i| (i as f32 - n_shapes as f32 / 2.0) * DISTANCE_BETWEEN_SHAPES);
|
|
|
|
|
|
|
|
SampledShapes(shapes.into_iter().zip(translations).collect())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Enum listing the shapes that can be sampled
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
enum Shape {
|
|
|
|
Cuboid,
|
|
|
|
Sphere,
|
|
|
|
Capsule,
|
|
|
|
Cylinder,
|
|
|
|
Tetrahedron,
|
|
|
|
Triangle,
|
|
|
|
}
|
2024-06-04 17:27:32 +00:00
|
|
|
struct ShapeMeshBuilder {
|
|
|
|
shape: Shape,
|
|
|
|
}
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
impl Shape {
|
|
|
|
/// Return a vector containing all implemented shapes
|
|
|
|
fn list_all_shapes() -> Vec<Shape> {
|
|
|
|
vec![
|
|
|
|
Shape::Cuboid,
|
|
|
|
Shape::Sphere,
|
|
|
|
Shape::Capsule,
|
|
|
|
Shape::Cylinder,
|
|
|
|
Shape::Tetrahedron,
|
|
|
|
Shape::Triangle,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShapeSample for Shape {
|
|
|
|
type Output = Vec3;
|
2024-08-21 12:29:33 +00:00
|
|
|
fn sample_interior<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec3 {
|
2024-05-27 16:44:52 +00:00
|
|
|
match self {
|
|
|
|
Shape::Cuboid => CUBOID.sample_interior(rng),
|
|
|
|
Shape::Sphere => SPHERE.sample_interior(rng),
|
|
|
|
Shape::Capsule => CAPSULE_3D.sample_interior(rng),
|
|
|
|
Shape::Cylinder => CYLINDER.sample_interior(rng),
|
|
|
|
Shape::Tetrahedron => TETRAHEDRON.sample_interior(rng),
|
|
|
|
Shape::Triangle => TRIANGLE_3D.sample_interior(rng),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-21 12:29:33 +00:00
|
|
|
fn sample_boundary<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::Output {
|
2024-05-27 16:44:52 +00:00
|
|
|
match self {
|
|
|
|
Shape::Cuboid => CUBOID.sample_boundary(rng),
|
|
|
|
Shape::Sphere => SPHERE.sample_boundary(rng),
|
|
|
|
Shape::Capsule => CAPSULE_3D.sample_boundary(rng),
|
|
|
|
Shape::Cylinder => CYLINDER.sample_boundary(rng),
|
|
|
|
Shape::Tetrahedron => TETRAHEDRON.sample_boundary(rng),
|
|
|
|
Shape::Triangle => TRIANGLE_3D.sample_boundary(rng),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Meshable for Shape {
|
2024-06-04 17:27:32 +00:00
|
|
|
type Output = ShapeMeshBuilder;
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
fn mesh(&self) -> Self::Output {
|
2024-06-04 17:27:32 +00:00
|
|
|
ShapeMeshBuilder { shape: *self }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MeshBuilder for ShapeMeshBuilder {
|
|
|
|
fn build(&self) -> Mesh {
|
|
|
|
match self.shape {
|
|
|
|
Shape::Cuboid => CUBOID.mesh().into(),
|
2024-05-27 16:44:52 +00:00
|
|
|
Shape::Sphere => SPHERE.mesh().into(),
|
|
|
|
Shape::Capsule => CAPSULE_3D.mesh().into(),
|
|
|
|
Shape::Cylinder => CYLINDER.mesh().into(),
|
2024-06-04 17:27:32 +00:00
|
|
|
Shape::Tetrahedron => TETRAHEDRON.mesh().into(),
|
|
|
|
Shape::Triangle => TRIANGLE_3D.mesh().into(),
|
2024-05-27 16:44:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The source of randomness used by this example.
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct RandomSource(ChaCha8Rng);
|
|
|
|
|
|
|
|
/// A container for the handle storing the mesh used to display sampled points as spheres.
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct PointMesh(Handle<Mesh>);
|
|
|
|
|
|
|
|
/// A container for the handle storing the material used to display sampled points.
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct PointMaterial {
|
|
|
|
interior: Handle<StandardMaterial>,
|
|
|
|
boundary: Handle<StandardMaterial>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Marker component for sampled points.
|
|
|
|
#[derive(Component)]
|
|
|
|
struct SamplePoint;
|
|
|
|
|
|
|
|
/// Component for animating the spawn animation of lights.
|
|
|
|
#[derive(Component)]
|
|
|
|
struct SpawningPoint {
|
|
|
|
progress: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Marker component for lights which should change intensity.
|
|
|
|
#[derive(Component)]
|
|
|
|
struct DespawningPoint {
|
|
|
|
progress: f32,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Marker component for lights which should change intensity.
|
|
|
|
#[derive(Component)]
|
|
|
|
struct FireflyLights;
|
|
|
|
|
|
|
|
/// The pressed state of the mouse, used for camera motion.
|
|
|
|
#[derive(Resource)]
|
|
|
|
struct MousePressed(bool);
|
|
|
|
|
|
|
|
/// Camera movement component.
|
|
|
|
#[derive(Component)]
|
|
|
|
struct CameraRig {
|
|
|
|
/// Rotation around the vertical axis of the camera (radians).
|
|
|
|
/// Positive changes makes the camera look more from the right.
|
|
|
|
pub yaw: f32,
|
|
|
|
/// Rotation around the horizontal axis of the camera (radians) (-pi/2; pi/2).
|
|
|
|
/// Positive looks down from above.
|
|
|
|
pub pitch: f32,
|
|
|
|
/// Distance from the center, smaller distance causes more zoom.
|
|
|
|
pub distance: f32,
|
|
|
|
/// Location in 3D space at which the camera is looking and around which it is orbiting.
|
|
|
|
pub target: Vec3,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn setup(
|
|
|
|
mut commands: Commands,
|
|
|
|
mut meshes: ResMut<Assets<Mesh>>,
|
|
|
|
mut materials: ResMut<Assets<StandardMaterial>>,
|
|
|
|
shapes: Res<SampledShapes>,
|
|
|
|
) {
|
|
|
|
// Use seeded rng and store it in a resource; this makes the random output reproducible.
|
|
|
|
let seeded_rng = ChaCha8Rng::seed_from_u64(4); // Chosen by a fair die roll, guaranteed to be random.
|
|
|
|
commands.insert_resource(RandomSource(seeded_rng));
|
|
|
|
|
|
|
|
// Make a plane for establishing space.
|
Migrate meshes and materials to required components (#15524)
# Objective
A big step in the migration to required components: meshes and
materials!
## Solution
As per the [selected
proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ):
- Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle`.
- Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`.
- Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`,
which wrap a `Handle<M>`.
- Meshes *without* a mesh material should be rendered with a default
material. The existence of a material is determined by
`HasMaterial2d`/`HasMaterial3d`, which is required by
`MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the
generics.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, previously nothing was rendered. Now,
it renders a white default `ColorMaterial` in 2D and a
`StandardMaterial` in 3D (this can be overridden). Below, only every
other entity has a material:
![Näyttökuva 2024-09-29
181746](https://github.com/user-attachments/assets/5c8be029-d2fe-4b8c-ae89-17a72ff82c9a)
![Näyttökuva 2024-09-29
181918](https://github.com/user-attachments/assets/58adbc55-5a1e-4c7d-a2c7-ed456227b909)
Why white? This is still open for discussion, but I think white makes
sense for a *default* material, while *invalid* asset handles pointing
to nothing should have something like a pink material to indicate that
something is broken (I don't handle that in this PR yet). This is kind
of a mix of Godot and Unity: Godot just renders a white material for
non-existent materials, while Unity renders nothing when no materials
exist, but renders pink for invalid materials. I can also change the
default material to pink if that is preferable though.
## Testing
I ran some 2D and 3D examples to test if anything changed visually. I
have not tested all examples or features yet however. If anyone wants to
test more extensively, it would be appreciated!
## Implementation Notes
- The relationship between `bevy_render` and `bevy_pbr` is weird here.
`bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all
of the material logic, and `bevy_render` doesn't depend on it. I feel
like the two crates should be refactored in some way, but I think that's
out of scope for this PR.
- I didn't migrate meshlets to required components yet. That can
probably be done in a follow-up, as this is already a huge PR.
- It is becoming increasingly clear to me that we really, *really* want
to disallow raw asset handles as components. They caused me a *ton* of
headache here already, and it took me a long time to find every place
that queried for them or inserted them directly on entities, since there
were no compiler errors for it. If we don't remove the `Component`
derive, I expect raw asset handles to be a *huge* footgun for users as
we transition to wrapper components, especially as handles as components
have been the norm so far. I personally consider this to be a blocker
for 0.15: we need to migrate to wrapper components for asset handles
everywhere, and remove the `Component` derive. Also see
https://github.com/bevyengine/bevy/issues/14124.
---
## Migration Guide
Asset handles for meshes and mesh materials must now be wrapped in the
`Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d`
components for 2D and 3D respectively. Raw handles as components no
longer render meshes.
Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle` have been deprecated. Instead, use the mesh and material
components directly.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, a white default material is now used.
Previously, nothing was rendered if the material was missing.
The `WithMesh2d` and `WithMesh3d` query filter type aliases have also
been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`.
---------
Co-authored-by: Tim Blackbird <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-01 21:33:17 +00:00
|
|
|
commands.spawn((
|
|
|
|
Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))),
|
|
|
|
MeshMaterial3d(materials.add(StandardMaterial {
|
2024-05-27 16:44:52 +00:00
|
|
|
base_color: Color::srgb(0.3, 0.5, 0.3),
|
|
|
|
perceptual_roughness: 0.95,
|
|
|
|
metallic: 0.0,
|
|
|
|
..default()
|
Migrate meshes and materials to required components (#15524)
# Objective
A big step in the migration to required components: meshes and
materials!
## Solution
As per the [selected
proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ):
- Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle`.
- Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`.
- Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`,
which wrap a `Handle<M>`.
- Meshes *without* a mesh material should be rendered with a default
material. The existence of a material is determined by
`HasMaterial2d`/`HasMaterial3d`, which is required by
`MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the
generics.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, previously nothing was rendered. Now,
it renders a white default `ColorMaterial` in 2D and a
`StandardMaterial` in 3D (this can be overridden). Below, only every
other entity has a material:
![Näyttökuva 2024-09-29
181746](https://github.com/user-attachments/assets/5c8be029-d2fe-4b8c-ae89-17a72ff82c9a)
![Näyttökuva 2024-09-29
181918](https://github.com/user-attachments/assets/58adbc55-5a1e-4c7d-a2c7-ed456227b909)
Why white? This is still open for discussion, but I think white makes
sense for a *default* material, while *invalid* asset handles pointing
to nothing should have something like a pink material to indicate that
something is broken (I don't handle that in this PR yet). This is kind
of a mix of Godot and Unity: Godot just renders a white material for
non-existent materials, while Unity renders nothing when no materials
exist, but renders pink for invalid materials. I can also change the
default material to pink if that is preferable though.
## Testing
I ran some 2D and 3D examples to test if anything changed visually. I
have not tested all examples or features yet however. If anyone wants to
test more extensively, it would be appreciated!
## Implementation Notes
- The relationship between `bevy_render` and `bevy_pbr` is weird here.
`bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all
of the material logic, and `bevy_render` doesn't depend on it. I feel
like the two crates should be refactored in some way, but I think that's
out of scope for this PR.
- I didn't migrate meshlets to required components yet. That can
probably be done in a follow-up, as this is already a huge PR.
- It is becoming increasingly clear to me that we really, *really* want
to disallow raw asset handles as components. They caused me a *ton* of
headache here already, and it took me a long time to find every place
that queried for them or inserted them directly on entities, since there
were no compiler errors for it. If we don't remove the `Component`
derive, I expect raw asset handles to be a *huge* footgun for users as
we transition to wrapper components, especially as handles as components
have been the norm so far. I personally consider this to be a blocker
for 0.15: we need to migrate to wrapper components for asset handles
everywhere, and remove the `Component` derive. Also see
https://github.com/bevyengine/bevy/issues/14124.
---
## Migration Guide
Asset handles for meshes and mesh materials must now be wrapped in the
`Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d`
components for 2D and 3D respectively. Raw handles as components no
longer render meshes.
Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle` have been deprecated. Instead, use the mesh and material
components directly.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, a white default material is now used.
Previously, nothing was rendered if the material was missing.
The `WithMesh2d` and `WithMesh3d` query filter type aliases have also
been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`.
---------
Co-authored-by: Tim Blackbird <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-01 21:33:17 +00:00
|
|
|
})),
|
|
|
|
Transform::from_xyz(0.0, -2.5, 0.0),
|
|
|
|
));
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
let shape_material = materials.add(StandardMaterial {
|
|
|
|
base_color: Color::srgba(0.2, 0.1, 0.6, 0.3),
|
|
|
|
reflectance: 0.0,
|
|
|
|
alpha_mode: AlphaMode::Blend,
|
|
|
|
cull_mode: None,
|
|
|
|
..default()
|
|
|
|
});
|
|
|
|
|
|
|
|
// Spawn shapes to be sampled
|
|
|
|
for (shape, translation) in shapes.0.iter() {
|
|
|
|
// The sampled shape shown transparently:
|
Migrate meshes and materials to required components (#15524)
# Objective
A big step in the migration to required components: meshes and
materials!
## Solution
As per the [selected
proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ):
- Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle`.
- Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`.
- Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`,
which wrap a `Handle<M>`.
- Meshes *without* a mesh material should be rendered with a default
material. The existence of a material is determined by
`HasMaterial2d`/`HasMaterial3d`, which is required by
`MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the
generics.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, previously nothing was rendered. Now,
it renders a white default `ColorMaterial` in 2D and a
`StandardMaterial` in 3D (this can be overridden). Below, only every
other entity has a material:
![Näyttökuva 2024-09-29
181746](https://github.com/user-attachments/assets/5c8be029-d2fe-4b8c-ae89-17a72ff82c9a)
![Näyttökuva 2024-09-29
181918](https://github.com/user-attachments/assets/58adbc55-5a1e-4c7d-a2c7-ed456227b909)
Why white? This is still open for discussion, but I think white makes
sense for a *default* material, while *invalid* asset handles pointing
to nothing should have something like a pink material to indicate that
something is broken (I don't handle that in this PR yet). This is kind
of a mix of Godot and Unity: Godot just renders a white material for
non-existent materials, while Unity renders nothing when no materials
exist, but renders pink for invalid materials. I can also change the
default material to pink if that is preferable though.
## Testing
I ran some 2D and 3D examples to test if anything changed visually. I
have not tested all examples or features yet however. If anyone wants to
test more extensively, it would be appreciated!
## Implementation Notes
- The relationship between `bevy_render` and `bevy_pbr` is weird here.
`bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all
of the material logic, and `bevy_render` doesn't depend on it. I feel
like the two crates should be refactored in some way, but I think that's
out of scope for this PR.
- I didn't migrate meshlets to required components yet. That can
probably be done in a follow-up, as this is already a huge PR.
- It is becoming increasingly clear to me that we really, *really* want
to disallow raw asset handles as components. They caused me a *ton* of
headache here already, and it took me a long time to find every place
that queried for them or inserted them directly on entities, since there
were no compiler errors for it. If we don't remove the `Component`
derive, I expect raw asset handles to be a *huge* footgun for users as
we transition to wrapper components, especially as handles as components
have been the norm so far. I personally consider this to be a blocker
for 0.15: we need to migrate to wrapper components for asset handles
everywhere, and remove the `Component` derive. Also see
https://github.com/bevyengine/bevy/issues/14124.
---
## Migration Guide
Asset handles for meshes and mesh materials must now be wrapped in the
`Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d`
components for 2D and 3D respectively. Raw handles as components no
longer render meshes.
Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle` have been deprecated. Instead, use the mesh and material
components directly.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, a white default material is now used.
Previously, nothing was rendered if the material was missing.
The `WithMesh2d` and `WithMesh3d` query filter type aliases have also
been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`.
---------
Co-authored-by: Tim Blackbird <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-01 21:33:17 +00:00
|
|
|
commands.spawn((
|
|
|
|
Mesh3d(meshes.add(shape.mesh())),
|
|
|
|
MeshMaterial3d(shape_material.clone()),
|
|
|
|
Transform::from_translation(*translation),
|
|
|
|
));
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
// Lights which work as the bulk lighting of the fireflies:
|
|
|
|
commands.spawn((
|
2024-10-01 03:20:43 +00:00
|
|
|
PointLight {
|
|
|
|
range: 4.0,
|
|
|
|
radius: 0.6,
|
|
|
|
intensity: 1.0,
|
|
|
|
shadows_enabled: false,
|
|
|
|
color: Color::LinearRgba(INSIDE_POINT_COLOR),
|
2024-05-27 16:44:52 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-10-01 03:20:43 +00:00
|
|
|
Transform::from_translation(*translation),
|
2024-05-27 16:44:52 +00:00
|
|
|
FireflyLights,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Global light:
|
2024-10-01 03:20:43 +00:00
|
|
|
commands.spawn((
|
|
|
|
PointLight {
|
2024-05-27 16:44:52 +00:00
|
|
|
color: SKY_COLOR,
|
|
|
|
intensity: 2_000.0,
|
|
|
|
shadows_enabled: false,
|
|
|
|
..default()
|
|
|
|
},
|
2024-10-01 03:20:43 +00:00
|
|
|
Transform::from_xyz(4.0, 8.0, 4.0),
|
|
|
|
));
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
// A camera:
|
|
|
|
commands.spawn((
|
2024-10-05 01:59:52 +00:00
|
|
|
Camera3d::default(),
|
|
|
|
Camera {
|
|
|
|
hdr: true, // HDR is required for bloom
|
|
|
|
clear_color: ClearColorConfig::Custom(SKY_COLOR),
|
2024-05-27 16:44:52 +00:00
|
|
|
..default()
|
|
|
|
},
|
2024-10-05 01:59:52 +00:00
|
|
|
Tonemapping::TonyMcMapface,
|
|
|
|
Transform::from_xyz(-2.0, 3.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
Rename rendering components for improved consistency and clarity (#15035)
# Objective
The names of numerous rendering components in Bevy are inconsistent and
a bit confusing. Relevant names include:
- `AutoExposureSettings`
- `AutoExposureSettingsUniform`
- `BloomSettings`
- `BloomUniform` (no `Settings`)
- `BloomPrefilterSettings`
- `ChromaticAberration` (no `Settings`)
- `ContrastAdaptiveSharpeningSettings`
- `DepthOfFieldSettings`
- `DepthOfFieldUniform` (no `Settings`)
- `FogSettings`
- `SmaaSettings`, `Fxaa`, `TemporalAntiAliasSettings` (really
inconsistent??)
- `ScreenSpaceAmbientOcclusionSettings`
- `ScreenSpaceReflectionsSettings`
- `VolumetricFogSettings`
Firstly, there's a lot of inconsistency between `Foo`/`FooSettings` and
`FooUniform`/`FooSettingsUniform` and whether names are abbreviated or
not.
Secondly, the `Settings` post-fix seems unnecessary and a bit confusing
semantically, since it makes it seem like the component is mostly just
auxiliary configuration instead of the core *thing* that actually
enables the feature. This will be an even bigger problem once bundles
like `TemporalAntiAliasBundle` are deprecated in favor of required
components, as users will expect a component named `TemporalAntiAlias`
(or similar), not `TemporalAntiAliasSettings`.
## Solution
Drop the `Settings` post-fix from the component names, and change some
names to be more consistent.
- `AutoExposure`
- `AutoExposureUniform`
- `Bloom`
- `BloomUniform`
- `BloomPrefilter`
- `ChromaticAberration`
- `ContrastAdaptiveSharpening`
- `DepthOfField`
- `DepthOfFieldUniform`
- `DistanceFog`
- `Smaa`, `Fxaa`, `TemporalAntiAliasing` (note: we might want to change
to `Taa`, see "Discussion")
- `ScreenSpaceAmbientOcclusion`
- `ScreenSpaceReflections`
- `VolumetricFog`
I kept the old names as deprecated type aliases to make migration a bit
less painful for users. We should remove them after the next release.
(And let me know if I should just... not add them at all)
I also added some very basic docs for a few types where they were
missing, like on `Fxaa` and `DepthOfField`.
## Discussion
- `TemporalAntiAliasing` is still inconsistent with `Smaa` and `Fxaa`.
Consensus [on
Discord](https://discord.com/channels/691052431525675048/743663924229963868/1280601167209955431)
seemed to be that renaming to `Taa` would probably be fine, but I think
it's a bit more controversial, and it would've required renaming a lot
of related types like `TemporalAntiAliasNode`,
`TemporalAntiAliasBundle`, and `TemporalAntiAliasPlugin`, so I think
it's better to leave to a follow-up.
- I think `Fog` should probably have a more specific name like
`DistanceFog` considering it seems to be distinct from `VolumetricFog`.
~~This should probably be done in a follow-up though, so I just removed
the `Settings` post-fix for now.~~ (done)
---
## Migration Guide
Many rendering components have been renamed for improved consistency and
clarity.
- `AutoExposureSettings` → `AutoExposure`
- `BloomSettings` → `Bloom`
- `BloomPrefilterSettings` → `BloomPrefilter`
- `ContrastAdaptiveSharpeningSettings` → `ContrastAdaptiveSharpening`
- `DepthOfFieldSettings` → `DepthOfField`
- `FogSettings` → `DistanceFog`
- `SmaaSettings` → `Smaa`
- `TemporalAntiAliasSettings` → `TemporalAntiAliasing`
- `ScreenSpaceAmbientOcclusionSettings` → `ScreenSpaceAmbientOcclusion`
- `ScreenSpaceReflectionsSettings` → `ScreenSpaceReflections`
- `VolumetricFogSettings` → `VolumetricFog`
---------
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-09-10 01:11:46 +00:00
|
|
|
Bloom::NATURAL,
|
2024-05-27 16:44:52 +00:00
|
|
|
CameraRig {
|
|
|
|
yaw: 0.56,
|
|
|
|
pitch: 0.45,
|
|
|
|
distance: 8.0,
|
|
|
|
target: Vec3::ZERO,
|
|
|
|
},
|
|
|
|
));
|
|
|
|
|
|
|
|
// Store the mesh and material for sample points in resources:
|
|
|
|
commands.insert_resource(PointMesh(
|
|
|
|
meshes.add(Sphere::new(0.03).mesh().ico(1).unwrap()),
|
|
|
|
));
|
|
|
|
commands.insert_resource(PointMaterial {
|
|
|
|
interior: materials.add(StandardMaterial {
|
|
|
|
base_color: Color::BLACK,
|
|
|
|
reflectance: 0.05,
|
|
|
|
emissive: 2.5 * INSIDE_POINT_COLOR,
|
|
|
|
..default()
|
|
|
|
}),
|
|
|
|
boundary: materials.add(StandardMaterial {
|
|
|
|
base_color: Color::BLACK,
|
|
|
|
reflectance: 0.05,
|
|
|
|
emissive: 1.5 * BOUNDARY_POINT_COLOR,
|
|
|
|
..default()
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
|
|
|
|
// Instructions for the example:
|
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.**
# Objective
- Implement https://github.com/bevyengine/bevy/discussions/15014
## Solution
This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.
Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.
## Testing
- [x] Text examples all work.
---
## Showcase
TODO: showcase-worthy
## Migration Guide
TODO: very breaking
### Accessing text spans by index
Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.
Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
let text = query.single_mut();
text.sections[1].value = format_time(time.elapsed());
}
```
After:
```rust
fn refresh_text(
query: Query<Entity, With<TimeText>>,
mut writer: UiTextWriter,
time: Res<Time>
) {
let entity = query.single();
*writer.text(entity, 1) = format_time(time.elapsed());
}
```
### Iterating text spans
Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-09 18:35:36 +00:00
|
|
|
commands.spawn((
|
|
|
|
Text::new(
|
2024-05-27 16:44:52 +00:00
|
|
|
"Controls:\n\
|
|
|
|
M: Toggle between sampling boundary and interior.\n\
|
|
|
|
A: Toggle automatic spawning & despawning of points.\n\
|
|
|
|
R: Restart (erase all samples).\n\
|
|
|
|
S: Add one random sample.\n\
|
|
|
|
D: Add 100 random samples.\n\
|
2024-07-29 23:38:59 +00:00
|
|
|
Rotate camera by holding left mouse and panning.\n\
|
2024-05-27 16:44:52 +00:00
|
|
|
Zoom camera by scrolling via mouse or +/-.\n\
|
|
|
|
Move camera by L/R arrow keys.\n\
|
|
|
|
Tab: Toggle this text",
|
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.**
# Objective
- Implement https://github.com/bevyengine/bevy/discussions/15014
## Solution
This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.
Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.
## Testing
- [x] Text examples all work.
---
## Showcase
TODO: showcase-worthy
## Migration Guide
TODO: very breaking
### Accessing text spans by index
Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.
Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
let text = query.single_mut();
text.sections[1].value = format_time(time.elapsed());
}
```
After:
```rust
fn refresh_text(
query: Query<Entity, With<TimeText>>,
mut writer: UiTextWriter,
time: Res<Time>
) {
let entity = query.single();
*writer.text(entity, 1) = format_time(time.elapsed());
}
```
### Iterating text spans
Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-09 18:35:36 +00:00
|
|
|
),
|
|
|
|
Style {
|
2024-05-27 16:44:52 +00:00
|
|
|
position_type: PositionType::Absolute,
|
|
|
|
top: Val::Px(12.0),
|
|
|
|
left: Val::Px(12.0),
|
|
|
|
..default()
|
Text rework (#15591)
**Ready for review. Examples migration progress: 100%.**
# Objective
- Implement https://github.com/bevyengine/bevy/discussions/15014
## Solution
This implements [cart's
proposal](https://github.com/bevyengine/bevy/discussions/15014#discussioncomment-10574459)
faithfully except for one change. I separated `TextSpan` from
`TextSpan2d` because `TextSpan` needs to require the `GhostNode`
component, which is a `bevy_ui` component only usable by UI.
Extra changes:
- Added `EntityCommands::commands_mut` that returns a mutable reference.
This is a blocker for extension methods that return something other than
`self`. Note that `sickle_ui`'s `UiBuilder::commands` returns a mutable
reference for this reason.
## Testing
- [x] Text examples all work.
---
## Showcase
TODO: showcase-worthy
## Migration Guide
TODO: very breaking
### Accessing text spans by index
Text sections are now text sections on different entities in a
hierarchy, Use the new `TextReader` and `TextWriter` system parameters
to access spans by index.
Before:
```rust
fn refresh_text(mut query: Query<&mut Text, With<TimeText>>, time: Res<Time>) {
let text = query.single_mut();
text.sections[1].value = format_time(time.elapsed());
}
```
After:
```rust
fn refresh_text(
query: Query<Entity, With<TimeText>>,
mut writer: UiTextWriter,
time: Res<Time>
) {
let entity = query.single();
*writer.text(entity, 1) = format_time(time.elapsed());
}
```
### Iterating text spans
Text spans are now entities in a hierarchy, so the new `UiTextReader`
and `UiTextWriter` system parameters provide ways to iterate that
hierarchy. The `UiTextReader::iter` method will give you a normal
iterator over spans, and `UiTextWriter::for_each` lets you visit each of
the spans.
---------
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-09 18:35:36 +00:00
|
|
|
},
|
|
|
|
));
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
// No points are scheduled to spawn initially.
|
|
|
|
commands.insert_resource(SpawnQueue(0));
|
|
|
|
|
|
|
|
// No points have been spawned initially.
|
|
|
|
commands.insert_resource(PointCounter(0));
|
|
|
|
|
|
|
|
// The mode starts with interior points.
|
|
|
|
commands.insert_resource(SamplingMode::Interior);
|
|
|
|
|
|
|
|
// Points spawn automatically by default.
|
|
|
|
commands.insert_resource(SpawningMode::Automatic);
|
|
|
|
|
|
|
|
// Starting mouse-pressed state is false.
|
|
|
|
commands.insert_resource(MousePressed(false));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle user inputs from the keyboard:
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn handle_keypress(
|
|
|
|
mut commands: Commands,
|
|
|
|
keyboard: Res<ButtonInput<KeyCode>>,
|
|
|
|
mut mode: ResMut<SamplingMode>,
|
|
|
|
mut spawn_mode: ResMut<SpawningMode>,
|
|
|
|
samples: Query<Entity, With<SamplePoint>>,
|
|
|
|
shapes: Res<SampledShapes>,
|
|
|
|
mut spawn_queue: ResMut<SpawnQueue>,
|
|
|
|
mut counter: ResMut<PointCounter>,
|
|
|
|
mut text_menus: Query<&mut Visibility, With<Text>>,
|
2024-10-13 20:32:06 +00:00
|
|
|
mut camera_rig: Single<&mut CameraRig>,
|
2024-05-27 16:44:52 +00:00
|
|
|
) {
|
|
|
|
// R => restart, deleting all samples
|
|
|
|
if keyboard.just_pressed(KeyCode::KeyR) {
|
|
|
|
// Don't forget to zero out the counter!
|
|
|
|
counter.0 = 0;
|
|
|
|
for entity in &samples {
|
|
|
|
commands.entity(entity).despawn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// S => sample once
|
|
|
|
if keyboard.just_pressed(KeyCode::KeyS) {
|
|
|
|
spawn_queue.0 += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// D => sample a hundred
|
|
|
|
if keyboard.just_pressed(KeyCode::KeyD) {
|
|
|
|
spawn_queue.0 += 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
// M => toggle mode between interior and boundary.
|
|
|
|
if keyboard.just_pressed(KeyCode::KeyM) {
|
|
|
|
match *mode {
|
|
|
|
SamplingMode::Interior => *mode = SamplingMode::Boundary,
|
|
|
|
SamplingMode::Boundary => *mode = SamplingMode::Interior,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A => toggle spawning mode between automatic and manual.
|
|
|
|
if keyboard.just_pressed(KeyCode::KeyA) {
|
|
|
|
match *spawn_mode {
|
|
|
|
SpawningMode::Manual => *spawn_mode = SpawningMode::Automatic,
|
|
|
|
SpawningMode::Automatic => *spawn_mode = SpawningMode::Manual,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tab => toggle help menu.
|
|
|
|
if keyboard.just_pressed(KeyCode::Tab) {
|
|
|
|
for mut visibility in text_menus.iter_mut() {
|
|
|
|
*visibility = match *visibility {
|
|
|
|
Visibility::Hidden => Visibility::Visible,
|
|
|
|
_ => Visibility::Hidden,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// +/- => zoom camera.
|
|
|
|
if keyboard.just_pressed(KeyCode::NumpadSubtract) || keyboard.just_pressed(KeyCode::Minus) {
|
|
|
|
camera_rig.distance += MAX_CAMERA_DISTANCE / 15.0;
|
|
|
|
camera_rig.distance = camera_rig
|
|
|
|
.distance
|
|
|
|
.clamp(MIN_CAMERA_DISTANCE, MAX_CAMERA_DISTANCE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if keyboard.just_pressed(KeyCode::NumpadAdd) {
|
|
|
|
camera_rig.distance -= MAX_CAMERA_DISTANCE / 15.0;
|
|
|
|
camera_rig.distance = camera_rig
|
|
|
|
.distance
|
|
|
|
.clamp(MIN_CAMERA_DISTANCE, MAX_CAMERA_DISTANCE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Arrows => Move camera focus
|
|
|
|
let left = keyboard.just_pressed(KeyCode::ArrowLeft);
|
|
|
|
let right = keyboard.just_pressed(KeyCode::ArrowRight);
|
|
|
|
|
|
|
|
if left || right {
|
|
|
|
let mut closest = 0;
|
|
|
|
let mut closest_distance = f32::MAX;
|
|
|
|
for (i, (_, position)) in shapes.0.iter().enumerate() {
|
|
|
|
let distance = camera_rig.target.distance(*position);
|
|
|
|
if distance < closest_distance {
|
|
|
|
closest = i;
|
|
|
|
closest_distance = distance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if closest > 0 && left {
|
|
|
|
camera_rig.target = shapes.0[closest - 1].1;
|
|
|
|
}
|
|
|
|
if closest < shapes.0.len() - 1 && right {
|
|
|
|
camera_rig.target = shapes.0[closest + 1].1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle user mouse input for panning the camera around:
|
|
|
|
fn handle_mouse(
|
2024-07-29 23:38:59 +00:00
|
|
|
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
|
|
|
|
accumulated_mouse_scroll: Res<AccumulatedMouseScroll>,
|
2024-05-27 16:44:52 +00:00
|
|
|
mut button_events: EventReader<MouseButtonInput>,
|
2024-10-13 20:32:06 +00:00
|
|
|
mut camera_rig: Single<&mut CameraRig>,
|
2024-05-27 16:44:52 +00:00
|
|
|
mut mouse_pressed: ResMut<MousePressed>,
|
|
|
|
) {
|
|
|
|
// Store left-pressed state in the MousePressed resource
|
|
|
|
for button_event in button_events.read() {
|
|
|
|
if button_event.button != MouseButton::Left {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*mouse_pressed = MousePressed(button_event.state.is_pressed());
|
|
|
|
}
|
|
|
|
|
2024-07-29 23:38:59 +00:00
|
|
|
if accumulated_mouse_scroll.delta != Vec2::ZERO {
|
|
|
|
let mouse_scroll = accumulated_mouse_scroll.delta.y;
|
|
|
|
camera_rig.distance -= mouse_scroll / 15.0 * MAX_CAMERA_DISTANCE;
|
|
|
|
camera_rig.distance = camera_rig
|
|
|
|
.distance
|
|
|
|
.clamp(MIN_CAMERA_DISTANCE, MAX_CAMERA_DISTANCE);
|
|
|
|
}
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
// If the mouse is not pressed, just ignore motion events
|
|
|
|
if !mouse_pressed.0 {
|
|
|
|
return;
|
|
|
|
}
|
2024-07-29 23:38:59 +00:00
|
|
|
if accumulated_mouse_motion.delta != Vec2::ZERO {
|
|
|
|
let displacement = accumulated_mouse_motion.delta;
|
|
|
|
camera_rig.yaw += displacement.x / 90.;
|
|
|
|
camera_rig.pitch += displacement.y / 90.;
|
|
|
|
// The extra 0.01 is to disallow weird behaviour at the poles of the rotation
|
|
|
|
camera_rig.pitch = camera_rig.pitch.clamp(-PI / 2.01, PI / 2.01);
|
|
|
|
}
|
2024-05-27 16:44:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
|
|
fn spawn_points(
|
|
|
|
mut commands: Commands,
|
|
|
|
mode: ResMut<SamplingMode>,
|
|
|
|
shapes: Res<SampledShapes>,
|
|
|
|
mut random_source: ResMut<RandomSource>,
|
|
|
|
sample_mesh: Res<PointMesh>,
|
|
|
|
sample_material: Res<PointMaterial>,
|
|
|
|
mut spawn_queue: ResMut<SpawnQueue>,
|
|
|
|
mut counter: ResMut<PointCounter>,
|
|
|
|
spawn_mode: ResMut<SpawningMode>,
|
|
|
|
) {
|
|
|
|
if let SpawningMode::Automatic = *spawn_mode {
|
|
|
|
spawn_queue.0 += POINTS_PER_FRAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
if spawn_queue.0 == 0 {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let rng = &mut random_source.0;
|
|
|
|
|
|
|
|
// Don't go crazy
|
|
|
|
for _ in 0..1000 {
|
|
|
|
if spawn_queue.0 == 0 {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spawn_queue.0 -= 1;
|
|
|
|
counter.0 += 1;
|
|
|
|
|
|
|
|
let (shape, offset) = shapes.0.choose(rng).expect("There is at least one shape");
|
|
|
|
|
|
|
|
// Get a single random Vec3:
|
|
|
|
let sample: Vec3 = *offset
|
|
|
|
+ match *mode {
|
|
|
|
SamplingMode::Interior => shape.sample_interior(rng),
|
|
|
|
SamplingMode::Boundary => shape.sample_boundary(rng),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Spawn a sphere at the random location:
|
|
|
|
commands.spawn((
|
Migrate meshes and materials to required components (#15524)
# Objective
A big step in the migration to required components: meshes and
materials!
## Solution
As per the [selected
proposal](https://hackmd.io/@bevy/required_components/%2Fj9-PnF-2QKK0on1KQ29UWQ):
- Deprecate `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle`.
- Add `Mesh2d` and `Mesh3d` components, which wrap a `Handle<Mesh>`.
- Add `MeshMaterial2d<M: Material2d>` and `MeshMaterial3d<M: Material>`,
which wrap a `Handle<M>`.
- Meshes *without* a mesh material should be rendered with a default
material. The existence of a material is determined by
`HasMaterial2d`/`HasMaterial3d`, which is required by
`MeshMaterial2d`/`MeshMaterial3d`. This gets around problems with the
generics.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, previously nothing was rendered. Now,
it renders a white default `ColorMaterial` in 2D and a
`StandardMaterial` in 3D (this can be overridden). Below, only every
other entity has a material:
![Näyttökuva 2024-09-29
181746](https://github.com/user-attachments/assets/5c8be029-d2fe-4b8c-ae89-17a72ff82c9a)
![Näyttökuva 2024-09-29
181918](https://github.com/user-attachments/assets/58adbc55-5a1e-4c7d-a2c7-ed456227b909)
Why white? This is still open for discussion, but I think white makes
sense for a *default* material, while *invalid* asset handles pointing
to nothing should have something like a pink material to indicate that
something is broken (I don't handle that in this PR yet). This is kind
of a mix of Godot and Unity: Godot just renders a white material for
non-existent materials, while Unity renders nothing when no materials
exist, but renders pink for invalid materials. I can also change the
default material to pink if that is preferable though.
## Testing
I ran some 2D and 3D examples to test if anything changed visually. I
have not tested all examples or features yet however. If anyone wants to
test more extensively, it would be appreciated!
## Implementation Notes
- The relationship between `bevy_render` and `bevy_pbr` is weird here.
`bevy_render` needs `Mesh3d` for its own systems, but `bevy_pbr` has all
of the material logic, and `bevy_render` doesn't depend on it. I feel
like the two crates should be refactored in some way, but I think that's
out of scope for this PR.
- I didn't migrate meshlets to required components yet. That can
probably be done in a follow-up, as this is already a huge PR.
- It is becoming increasingly clear to me that we really, *really* want
to disallow raw asset handles as components. They caused me a *ton* of
headache here already, and it took me a long time to find every place
that queried for them or inserted them directly on entities, since there
were no compiler errors for it. If we don't remove the `Component`
derive, I expect raw asset handles to be a *huge* footgun for users as
we transition to wrapper components, especially as handles as components
have been the norm so far. I personally consider this to be a blocker
for 0.15: we need to migrate to wrapper components for asset handles
everywhere, and remove the `Component` derive. Also see
https://github.com/bevyengine/bevy/issues/14124.
---
## Migration Guide
Asset handles for meshes and mesh materials must now be wrapped in the
`Mesh2d` and `MeshMaterial2d` or `Mesh3d` and `MeshMaterial3d`
components for 2D and 3D respectively. Raw handles as components no
longer render meshes.
Additionally, `MaterialMesh2dBundle`, `MaterialMeshBundle`, and
`PbrBundle` have been deprecated. Instead, use the mesh and material
components directly.
Previously:
```rust
commands.spawn(MaterialMesh2dBundle {
mesh: meshes.add(Circle::new(100.0)).into(),
material: materials.add(Color::srgb(7.5, 0.0, 7.5)),
transform: Transform::from_translation(Vec3::new(-200., 0., 0.)),
..default()
});
```
Now:
```rust
commands.spawn((
Mesh2d(meshes.add(Circle::new(100.0))),
MeshMaterial2d(materials.add(Color::srgb(7.5, 0.0, 7.5))),
Transform::from_translation(Vec3::new(-200., 0., 0.)),
));
```
If the mesh material is missing, a white default material is now used.
Previously, nothing was rendered if the material was missing.
The `WithMesh2d` and `WithMesh3d` query filter type aliases have also
been removed. Simply use `With<Mesh2d>` or `With<Mesh3d>`.
---------
Co-authored-by: Tim Blackbird <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-10-01 21:33:17 +00:00
|
|
|
Mesh3d(sample_mesh.0.clone()),
|
|
|
|
MeshMaterial3d(match *mode {
|
|
|
|
SamplingMode::Interior => sample_material.interior.clone(),
|
|
|
|
SamplingMode::Boundary => sample_material.boundary.clone(),
|
|
|
|
}),
|
|
|
|
Transform::from_translation(sample).with_scale(Vec3::ZERO),
|
2024-05-27 16:44:52 +00:00
|
|
|
SamplePoint,
|
|
|
|
SpawningPoint { progress: 0.0 },
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn despawn_points(
|
|
|
|
mut commands: Commands,
|
|
|
|
samples: Query<Entity, With<SamplePoint>>,
|
|
|
|
spawn_mode: Res<SpawningMode>,
|
|
|
|
mut counter: ResMut<PointCounter>,
|
|
|
|
mut random_source: ResMut<RandomSource>,
|
|
|
|
) {
|
|
|
|
// Do not despawn automatically in manual mode
|
|
|
|
if let SpawningMode::Manual = *spawn_mode {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if counter.0 < MAX_POINTS {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let rng = &mut random_source.0;
|
|
|
|
// Skip a random amount of points to ensure random despawning
|
|
|
|
let skip = rng.gen_range(0..counter.0);
|
|
|
|
let despawn_amount = (counter.0 - MAX_POINTS).min(100);
|
|
|
|
counter.0 -= samples
|
|
|
|
.iter()
|
|
|
|
.skip(skip)
|
|
|
|
.take(despawn_amount)
|
|
|
|
.map(|entity| {
|
|
|
|
commands
|
|
|
|
.entity(entity)
|
|
|
|
.insert(DespawningPoint { progress: 0.0 })
|
|
|
|
.remove::<SpawningPoint>()
|
|
|
|
.remove::<SamplePoint>();
|
|
|
|
})
|
|
|
|
.count();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn animate_spawning(
|
|
|
|
mut commands: Commands,
|
|
|
|
time: Res<Time>,
|
|
|
|
mut samples: Query<(Entity, &mut Transform, &mut SpawningPoint)>,
|
|
|
|
) {
|
2024-10-16 21:09:32 +00:00
|
|
|
let dt = time.delta_secs();
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
for (entity, mut transform, mut point) in samples.iter_mut() {
|
|
|
|
point.progress += dt / ANIMATION_TIME;
|
|
|
|
transform.scale = Vec3::splat(point.progress.min(1.0));
|
|
|
|
if point.progress >= 1.0 {
|
|
|
|
commands.entity(entity).remove::<SpawningPoint>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn animate_despawning(
|
|
|
|
mut commands: Commands,
|
|
|
|
time: Res<Time>,
|
|
|
|
mut samples: Query<(Entity, &mut Transform, &mut DespawningPoint)>,
|
|
|
|
) {
|
2024-10-16 21:09:32 +00:00
|
|
|
let dt = time.delta_secs();
|
2024-05-27 16:44:52 +00:00
|
|
|
|
|
|
|
for (entity, mut transform, mut point) in samples.iter_mut() {
|
|
|
|
point.progress += dt / ANIMATION_TIME;
|
|
|
|
// If the point is already smaller than expected, jump ahead with the despawning progress to avoid sudden jumps in size
|
|
|
|
point.progress = f32::max(point.progress, 1.0 - transform.scale.x);
|
|
|
|
transform.scale = Vec3::splat((1.0 - point.progress).max(0.0));
|
|
|
|
if point.progress >= 1.0 {
|
|
|
|
commands.entity(entity).despawn();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_camera(mut camera: Query<(&mut Transform, &CameraRig), Changed<CameraRig>>) {
|
|
|
|
for (mut transform, rig) in camera.iter_mut() {
|
|
|
|
let looking_direction =
|
|
|
|
Quat::from_rotation_y(-rig.yaw) * Quat::from_rotation_x(rig.pitch) * Vec3::Z;
|
|
|
|
transform.translation = rig.target - rig.distance * looking_direction;
|
|
|
|
transform.look_at(rig.target, Dir3::Y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update_lights(
|
|
|
|
mut lights: Query<&mut PointLight, With<FireflyLights>>,
|
|
|
|
counter: Res<PointCounter>,
|
|
|
|
) {
|
|
|
|
let saturation = (counter.0 as f32 / MAX_POINTS as f32).min(2.0);
|
|
|
|
let intensity = 40_000.0 * saturation;
|
|
|
|
for mut light in lights.iter_mut() {
|
|
|
|
light.intensity = light.intensity.lerp(intensity, 0.04);
|
|
|
|
}
|
|
|
|
}
|