 fcd7c0fc3d
			
		
	
	
		fcd7c0fc3d
		
			
		
	
	
	
	
		
			
			Rebased and finished version of https://github.com/bevyengine/bevy/pull/8407. Huge thanks to @GitGhillie for adjusting all the examples, and the many other people who helped write this PR (@superdump , @coreh , among others) :) Fixes https://github.com/bevyengine/bevy/issues/8369 --- ## Changelog - Added a `brightness` control to `Skybox`. - Added an `intensity` control to `EnvironmentMapLight`. - Added `ExposureSettings` and `PhysicalCameraParameters` for controlling exposure of 3D cameras. - Removed the baked-in `DirectionalLight` exposure Bevy previously hardcoded internally. ## Migration Guide - If using a `Skybox` or `EnvironmentMapLight`, use the new `brightness` and `intensity` controls to adjust their strength. - All 3D scene will now have different apparent brightnesses due to Bevy implementing proper exposure controls. You will have to adjust the intensity of your lights and/or your camera exposure via the new `ExposureSettings` component to compensate. --------- Co-authored-by: Robert Swain <robert.swain@gmail.com> Co-authored-by: GitGhillie <jillisnoordhoek@gmail.com> Co-authored-by: Marco Buono <thecoreh@gmail.com> Co-authored-by: vero <email@atlasdostal.com> Co-authored-by: atlas dostal <rodol@rivalrebels.com>
		
			
				
	
	
		
			263 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| // ! This example demonstrates how to create a custom mesh,
 | |
| // ! assign a custom UV mapping for a custom texture,
 | |
| // ! and how to change the UV mapping at run-time.
 | |
| use bevy::prelude::*;
 | |
| use bevy::render::{
 | |
|     mesh::{Indices, VertexAttributeValues},
 | |
|     render_asset::RenderAssetPersistencePolicy,
 | |
|     render_resource::PrimitiveTopology,
 | |
| };
 | |
| 
 | |
| // Define a "marker" component to mark the custom mesh. Marker components are often used in Bevy for
 | |
| // filtering entities in queries with With, they're usually not queried directly since they don't contain information within them.
 | |
| #[derive(Component)]
 | |
| struct CustomUV;
 | |
| 
 | |
| fn main() {
 | |
|     App::new()
 | |
|         .add_plugins(DefaultPlugins)
 | |
|         .add_systems(Startup, setup)
 | |
|         .add_systems(Update, input_handler)
 | |
|         .run();
 | |
| }
 | |
| 
 | |
| fn setup(
 | |
|     mut commands: Commands,
 | |
|     asset_server: Res<AssetServer>,
 | |
|     mut materials: ResMut<Assets<StandardMaterial>>,
 | |
|     mut meshes: ResMut<Assets<Mesh>>,
 | |
| ) {
 | |
|     // Import the custom texture.
 | |
|     let custom_texture_handle: Handle<Image> = asset_server.load("textures/array_texture.png");
 | |
|     // Create and save a handle to the mesh.
 | |
|     let cube_mesh_handle: Handle<Mesh> = meshes.add(create_cube_mesh());
 | |
| 
 | |
|     // Render the mesh with the custom texture using a PbrBundle, add the marker.
 | |
|     commands.spawn((
 | |
|         PbrBundle {
 | |
|             mesh: cube_mesh_handle,
 | |
|             material: materials.add(StandardMaterial {
 | |
|                 base_color_texture: Some(custom_texture_handle),
 | |
|                 ..default()
 | |
|             }),
 | |
|             ..default()
 | |
|         },
 | |
|         CustomUV,
 | |
|     ));
 | |
| 
 | |
|     // Transform for the camera and lighting, looking at (0,0,0) (the position of the mesh).
 | |
|     let camera_and_light_transform =
 | |
|         Transform::from_xyz(1.8, 1.8, 1.8).looking_at(Vec3::ZERO, Vec3::Y);
 | |
| 
 | |
|     // Camera in 3D space.
 | |
|     commands.spawn(Camera3dBundle {
 | |
|         transform: camera_and_light_transform,
 | |
|         ..default()
 | |
|     });
 | |
| 
 | |
|     // Light up the scene.
 | |
|     commands.spawn(PointLightBundle {
 | |
|         point_light: PointLight {
 | |
|             intensity: 100_000.0,
 | |
|             range: 100.0,
 | |
|             ..default()
 | |
|         },
 | |
|         transform: camera_and_light_transform,
 | |
|         ..default()
 | |
|     });
 | |
| 
 | |
|     // Text to describe the controls.
 | |
|     commands.spawn(
 | |
|         TextBundle::from_section(
 | |
|             "Controls:\nSpace: Change UVs\nX/Y/Z: Rotate\nR: Reset orientation",
 | |
|             TextStyle {
 | |
|                 font_size: 20.0,
 | |
|                 ..default()
 | |
|             },
 | |
|         )
 | |
|         .with_style(Style {
 | |
|             position_type: PositionType::Absolute,
 | |
|             top: Val::Px(12.0),
 | |
|             left: Val::Px(12.0),
 | |
|             ..default()
 | |
|         }),
 | |
|     );
 | |
| }
 | |
| 
 | |
| // System to receive input from the user,
 | |
| // check out examples/input/ for more examples about user input.
 | |
| fn input_handler(
 | |
|     keyboard_input: Res<ButtonInput<KeyCode>>,
 | |
|     mesh_query: Query<&Handle<Mesh>, With<CustomUV>>,
 | |
|     mut meshes: ResMut<Assets<Mesh>>,
 | |
|     mut query: Query<&mut Transform, With<CustomUV>>,
 | |
|     time: Res<Time>,
 | |
| ) {
 | |
|     if keyboard_input.just_pressed(KeyCode::Space) {
 | |
|         let mesh_handle = mesh_query.get_single().expect("Query not successful");
 | |
|         let mesh = meshes.get_mut(mesh_handle).unwrap();
 | |
|         toggle_texture(mesh);
 | |
|     }
 | |
|     if keyboard_input.pressed(KeyCode::KeyX) {
 | |
|         for mut transform in &mut query {
 | |
|             transform.rotate_x(time.delta_seconds() / 1.2);
 | |
|         }
 | |
|     }
 | |
|     if keyboard_input.pressed(KeyCode::KeyY) {
 | |
|         for mut transform in &mut query {
 | |
|             transform.rotate_y(time.delta_seconds() / 1.2);
 | |
|         }
 | |
|     }
 | |
|     if keyboard_input.pressed(KeyCode::KeyZ) {
 | |
|         for mut transform in &mut query {
 | |
|             transform.rotate_z(time.delta_seconds() / 1.2);
 | |
|         }
 | |
|     }
 | |
|     if keyboard_input.pressed(KeyCode::KeyR) {
 | |
|         for mut transform in &mut query {
 | |
|             transform.look_to(Vec3::NEG_Z, Vec3::Y);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[rustfmt::skip]
 | |
| fn create_cube_mesh() -> Mesh {
 | |
|     // Keep the mesh data accessible in future frames to be able to mutate it in toggle_texture.
 | |
|     Mesh::new(PrimitiveTopology::TriangleList, RenderAssetPersistencePolicy::Keep)
 | |
|     .with_inserted_attribute(
 | |
|         Mesh::ATTRIBUTE_POSITION,
 | |
|         // Each array is an [x, y, z] coordinate in local space.
 | |
|         // Meshes always rotate around their local [0, 0, 0] when a rotation is applied to their Transform.
 | |
|         // By centering our mesh around the origin, rotating the mesh preserves its center of mass.
 | |
|         vec![
 | |
|             // top (facing towards +y)
 | |
|             [-0.5, 0.5, -0.5], // vertex with index 0
 | |
|             [0.5, 0.5, -0.5], // vertex with index 1
 | |
|             [0.5, 0.5, 0.5], // etc. until 23
 | |
|             [-0.5, 0.5, 0.5],
 | |
|             // bottom   (-y)
 | |
|             [-0.5, -0.5, -0.5],
 | |
|             [0.5, -0.5, -0.5],
 | |
|             [0.5, -0.5, 0.5],
 | |
|             [-0.5, -0.5, 0.5],
 | |
|             // right    (+x)
 | |
|             [0.5, -0.5, -0.5],
 | |
|             [0.5, -0.5, 0.5],
 | |
|             [0.5, 0.5, 0.5], // This vertex is at the same position as vertex with index 2, but they'll have different UV and normal
 | |
|             [0.5, 0.5, -0.5],
 | |
|             // left     (-x)
 | |
|             [-0.5, -0.5, -0.5],
 | |
|             [-0.5, -0.5, 0.5],
 | |
|             [-0.5, 0.5, 0.5],
 | |
|             [-0.5, 0.5, -0.5],
 | |
|             // back     (+z)
 | |
|             [-0.5, -0.5, 0.5],
 | |
|             [-0.5, 0.5, 0.5],
 | |
|             [0.5, 0.5, 0.5],
 | |
|             [0.5, -0.5, 0.5],
 | |
|             // forward  (-z)
 | |
|             [-0.5, -0.5, -0.5],
 | |
|             [-0.5, 0.5, -0.5],
 | |
|             [0.5, 0.5, -0.5],
 | |
|             [0.5, -0.5, -0.5],
 | |
|         ],
 | |
|     )
 | |
|     // Set-up UV coordinates to point to the upper (V < 0.5), "dirt+grass" part of the texture.
 | |
|     // Take a look at the custom image (assets/textures/array_texture.png)
 | |
|     // so the UV coords will make more sense
 | |
|     // Note: (0.0, 0.0) = Top-Left in UV mapping, (1.0, 1.0) = Bottom-Right in UV mapping
 | |
|     .with_inserted_attribute(
 | |
|         Mesh::ATTRIBUTE_UV_0,
 | |
|         vec![
 | |
|             // Assigning the UV coords for the top side.
 | |
|             [0.0, 0.2], [0.0, 0.0], [1.0, 0.0], [1.0, 0.25],
 | |
|             // Assigning the UV coords for the bottom side.
 | |
|             [0.0, 0.45], [0.0, 0.25], [1.0, 0.25], [1.0, 0.45],
 | |
|             // Assigning the UV coords for the right side.
 | |
|             [1.0, 0.45], [0.0, 0.45], [0.0, 0.2], [1.0, 0.2],
 | |
|             // Assigning the UV coords for the left side.
 | |
|             [1.0, 0.45], [0.0, 0.45], [0.0, 0.2], [1.0, 0.2],
 | |
|             // Assigning the UV coords for the back side.
 | |
|             [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], [1.0, 0.45],
 | |
|             // Assigning the UV coords for the forward side.
 | |
|             [0.0, 0.45], [0.0, 0.2], [1.0, 0.2], [1.0, 0.45],
 | |
|         ],
 | |
|     )
 | |
|     // For meshes with flat shading, normals are orthogonal (pointing out) from the direction of
 | |
|     // the surface.
 | |
|     // Normals are required for correct lighting calculations.
 | |
|     // Each array represents a normalized vector, which length should be equal to 1.0.
 | |
|     .with_inserted_attribute(
 | |
|         Mesh::ATTRIBUTE_NORMAL,
 | |
|         vec![
 | |
|             // Normals for the top side (towards +y)
 | |
|             [0.0, 1.0, 0.0],
 | |
|             [0.0, 1.0, 0.0],
 | |
|             [0.0, 1.0, 0.0],
 | |
|             [0.0, 1.0, 0.0],
 | |
|             // Normals for the bottom side (towards -y)
 | |
|             [0.0, -1.0, 0.0],
 | |
|             [0.0, -1.0, 0.0],
 | |
|             [0.0, -1.0, 0.0],
 | |
|             [0.0, -1.0, 0.0],
 | |
|             // Normals for the right side (towards +x)
 | |
|             [1.0, 0.0, 0.0],
 | |
|             [1.0, 0.0, 0.0],
 | |
|             [1.0, 0.0, 0.0],
 | |
|             [1.0, 0.0, 0.0],
 | |
|             // Normals for the left side (towards -x)
 | |
|             [-1.0, 0.0, 0.0],
 | |
|             [-1.0, 0.0, 0.0],
 | |
|             [-1.0, 0.0, 0.0],
 | |
|             [-1.0, 0.0, 0.0],
 | |
|             // Normals for the back side (towards +z)
 | |
|             [0.0, 0.0, 1.0],
 | |
|             [0.0, 0.0, 1.0],
 | |
|             [0.0, 0.0, 1.0],
 | |
|             [0.0, 0.0, 1.0],
 | |
|             // Normals for the forward side (towards -z)
 | |
|             [0.0, 0.0, -1.0],
 | |
|             [0.0, 0.0, -1.0],
 | |
|             [0.0, 0.0, -1.0],
 | |
|             [0.0, 0.0, -1.0],
 | |
|         ],
 | |
|     )
 | |
|     // Create the triangles out of the 24 vertices we created.
 | |
|     // To construct a square, we need 2 triangles, therefore 12 triangles in total.
 | |
|     // To construct a triangle, we need the indices of its 3 defined vertices, adding them one
 | |
|     // by one, in a counter-clockwise order (relative to the position of the viewer, the order
 | |
|     // should appear counter-clockwise from the front of the triangle, in this case from outside the cube).
 | |
|     // Read more about how to correctly build a mesh manually in the Bevy documentation of a Mesh,
 | |
|     // further examples and the implementation of the built-in shapes.
 | |
|     .with_indices(Some(Indices::U32(vec![
 | |
|         0,3,1 , 1,3,2, // triangles making up the top (+y) facing side.
 | |
|         4,5,7 , 5,6,7, // bottom (-y)
 | |
|         8,11,9 , 9,11,10, // right (+x)
 | |
|         12,13,15 , 13,14,15, // left (-x)
 | |
|         16,19,17 , 17,19,18, // back (+z)
 | |
|         20,21,23 , 21,22,23, // forward (-z)
 | |
|     ])))
 | |
| }
 | |
| 
 | |
| // Function that changes the UV mapping of the mesh, to apply the other texture.
 | |
| fn toggle_texture(mesh_to_change: &mut Mesh) {
 | |
|     // Get a mutable reference to the values of the UV attribute, so we can iterate over it.
 | |
|     let uv_attribute = mesh_to_change.attribute_mut(Mesh::ATTRIBUTE_UV_0).unwrap();
 | |
|     // The format of the UV coordinates should be Float32x2.
 | |
|     let VertexAttributeValues::Float32x2(uv_attribute) = uv_attribute else {
 | |
|         panic!("Unexpected vertex format, expected Float32x2.");
 | |
|     };
 | |
| 
 | |
|     // Iterate over the UV coordinates, and change them as we want.
 | |
|     for uv_coord in uv_attribute.iter_mut() {
 | |
|         // If the UV coordinate points to the upper, "dirt+grass" part of the texture...
 | |
|         if (uv_coord[1] + 0.5) < 1.0 {
 | |
|             // ... point to the equivalent lower, "sand+water" part instead,
 | |
|             uv_coord[1] += 0.5;
 | |
|         } else {
 | |
|             // else, point back to the upper, "dirt+grass" part.
 | |
|             uv_coord[1] -= 0.5;
 | |
|         }
 | |
|     }
 | |
| }
 |