Function vec3

Source
pub const fn vec3(x: f32, y: f32, z: f32) -> Vec3
Expand description

Creates a 3-dimensional vector.

Examples found in repository?
examples/3d/visibility_range.rs (line 15)
15const CAMERA_FOCAL_POINT: Vec3 = vec3(0.0, 0.3, 0.0);
More examples
Hide additional examples
examples/3d/anisotropy.rs (line 14)
14const CAMERA_INITIAL_POSITION: Vec3 = vec3(-0.4, 0.0, 0.0);
15
16/// The current settings of the app, as chosen by the user.
17#[derive(Resource)]
18struct AppStatus {
19    /// Which type of light is in the scene.
20    light_mode: LightMode,
21    /// Whether anisotropy is enabled.
22    anisotropy_enabled: bool,
23    /// Which mesh is visible
24    visible_scene: Scene,
25}
26
27/// Which type of light we're using: a directional light, a point light, or an
28/// environment map.
29#[derive(Clone, Copy, PartialEq, Default)]
30enum LightMode {
31    /// A rotating directional light.
32    #[default]
33    Directional,
34    /// A rotating point light.
35    Point,
36    /// An environment map (image-based lighting, including skybox).
37    EnvironmentMap,
38}
39
40/// A component that stores the version of the material with anisotropy and the
41/// version of the material without it.
42///
43/// This is placed on each mesh with a material. It exists so that the
44/// appropriate system can replace the materials when the user presses Enter to
45/// turn anisotropy on and off.
46#[derive(Component)]
47struct MaterialVariants {
48    /// The version of the material in the glTF file, with anisotropy.
49    anisotropic: Handle<StandardMaterial>,
50    /// The version of the material with anisotropy removed.
51    isotropic: Handle<StandardMaterial>,
52}
53
54#[derive(Default, Clone, Copy, PartialEq, Eq, Component)]
55enum Scene {
56    #[default]
57    BarnLamp,
58    Sphere,
59}
60
61impl Scene {
62    fn next(&self) -> Self {
63        match self {
64            Self::BarnLamp => Self::Sphere,
65            Self::Sphere => Self::BarnLamp,
66        }
67    }
68}
69
70impl Display for Scene {
71    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72        let scene_name = match self {
73            Self::BarnLamp => "Barn Lamp",
74            Self::Sphere => "Sphere",
75        };
76        write!(f, "{scene_name}")
77    }
78}
79
80/// The application entry point.
81fn main() {
82    App::new()
83        .init_resource::<AppStatus>()
84        .add_plugins(DefaultPlugins.set(WindowPlugin {
85            primary_window: Some(Window {
86                title: "Bevy Anisotropy Example".into(),
87                ..default()
88            }),
89            ..default()
90        }))
91        .add_systems(Startup, setup)
92        .add_systems(Update, create_material_variants)
93        .add_systems(Update, animate_light)
94        .add_systems(Update, rotate_camera)
95        .add_systems(Update, (handle_input, update_help_text).chain())
96        .run();
97}
98
99/// Creates the initial scene.
100fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_status: Res<AppStatus>) {
101    commands.spawn((
102        Camera3d::default(),
103        Transform::from_translation(CAMERA_INITIAL_POSITION).looking_at(Vec3::ZERO, Vec3::Y),
104    ));
105
106    spawn_directional_light(&mut commands);
107
108    commands.spawn((
109        SceneRoot(asset_server.load("models/AnisotropyBarnLamp/AnisotropyBarnLamp.gltf#Scene0")),
110        Transform::from_xyz(0.0, 0.07, -0.13),
111        Scene::BarnLamp,
112    ));
113
114    commands.spawn((
115        Mesh3d(
116            asset_server.add(
117                Mesh::from(Sphere::new(0.1))
118                    .with_generated_tangents()
119                    .unwrap(),
120            ),
121        ),
122        MeshMaterial3d(asset_server.add(StandardMaterial {
123            base_color: palettes::tailwind::GRAY_300.into(),
124            anisotropy_rotation: 0.5,
125            anisotropy_strength: 1.,
126            ..default()
127        })),
128        Scene::Sphere,
129        Visibility::Hidden,
130    ));
131
132    spawn_text(&mut commands, &app_status);
133}
134
135/// Spawns the help text.
136fn spawn_text(commands: &mut Commands, app_status: &AppStatus) {
137    commands.spawn((
138        app_status.create_help_text(),
139        Node {
140            position_type: PositionType::Absolute,
141            bottom: Val::Px(12.0),
142            left: Val::Px(12.0),
143            ..default()
144        },
145    ));
146}
147
148/// For each material, creates a version with the anisotropy removed.
149///
150/// This allows the user to press Enter to toggle anisotropy on and off.
151fn create_material_variants(
152    mut commands: Commands,
153    mut materials: ResMut<Assets<StandardMaterial>>,
154    new_meshes: Query<
155        (Entity, &MeshMaterial3d<StandardMaterial>),
156        (
157            Added<MeshMaterial3d<StandardMaterial>>,
158            Without<MaterialVariants>,
159        ),
160    >,
161) {
162    for (entity, anisotropic_material_handle) in new_meshes.iter() {
163        let Some(anisotropic_material) = materials.get(anisotropic_material_handle).cloned() else {
164            continue;
165        };
166
167        commands.entity(entity).insert(MaterialVariants {
168            anisotropic: anisotropic_material_handle.0.clone(),
169            isotropic: materials.add(StandardMaterial {
170                anisotropy_texture: None,
171                anisotropy_strength: 0.0,
172                anisotropy_rotation: 0.0,
173                ..anisotropic_material
174            }),
175        });
176    }
177}
178
179/// A system that animates the light every frame, if there is one.
180fn animate_light(
181    mut lights: Query<&mut Transform, Or<(With<DirectionalLight>, With<PointLight>)>>,
182    time: Res<Time>,
183) {
184    let now = time.elapsed_secs();
185    for mut transform in lights.iter_mut() {
186        transform.translation = vec3(ops::cos(now), 1.0, ops::sin(now)) * vec3(3.0, 4.0, 3.0);
187        transform.look_at(Vec3::ZERO, Vec3::Y);
188    }
189}
examples/3d/mixed_lighting.rs (line 114)
114const INITIAL_SPHERE_POSITION: Vec3 = vec3(0.0, 0.5233223, 0.0);
115
116fn main() {
117    App::new()
118        .add_plugins(DefaultPlugins.set(WindowPlugin {
119            primary_window: Some(Window {
120                title: "Bevy Mixed Lighting Example".into(),
121                ..default()
122            }),
123            ..default()
124        }))
125        .add_plugins(MeshPickingPlugin)
126        .insert_resource(AmbientLight {
127            color: ClearColor::default().0,
128            brightness: 10000.0,
129            affects_lightmapped_meshes: true,
130        })
131        .init_resource::<AppStatus>()
132        .add_event::<WidgetClickEvent<LightingMode>>()
133        .add_event::<LightingModeChanged>()
134        .add_systems(Startup, setup)
135        .add_systems(Update, update_lightmaps)
136        .add_systems(Update, update_directional_light)
137        .add_systems(Update, make_sphere_nonpickable)
138        .add_systems(Update, update_radio_buttons)
139        .add_systems(Update, handle_lighting_mode_change)
140        .add_systems(Update, widgets::handle_ui_interactions::<LightingMode>)
141        .add_systems(Update, reset_sphere_position)
142        .add_systems(Update, move_sphere)
143        .add_systems(Update, adjust_help_text)
144        .run();
145}
146
147/// Creates the scene.
148fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_status: Res<AppStatus>) {
149    spawn_camera(&mut commands);
150    spawn_scene(&mut commands, &asset_server);
151    spawn_buttons(&mut commands);
152    spawn_help_text(&mut commands, &app_status);
153}
154
155/// Spawns the 3D camera.
156fn spawn_camera(commands: &mut Commands) {
157    commands
158        .spawn(Camera3d::default())
159        .insert(Transform::from_xyz(-0.7, 0.7, 1.0).looking_at(vec3(0.0, 0.3, 0.0), Vec3::Y));
160}
161
162/// Spawns the scene.
163///
164/// The scene is loaded from a glTF file.
165fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
166    commands
167        .spawn(SceneRoot(
168            asset_server.load(
169                GltfAssetLabel::Scene(0)
170                    .from_asset("models/MixedLightingExample/MixedLightingExample.gltf"),
171            ),
172        ))
173        .observe(
174            |_: Trigger<SceneInstanceReady>,
175             mut lighting_mode_change_event_writer: EventWriter<LightingModeChanged>| {
176                // When the scene loads, send a `LightingModeChanged` event so
177                // that we set up the lightmaps.
178                lighting_mode_change_event_writer.write(LightingModeChanged);
179            },
180        );
181}
182
183/// Spawns the buttons that allow the user to change the lighting mode.
184fn spawn_buttons(commands: &mut Commands) {
185    commands
186        .spawn(widgets::main_ui_node())
187        .with_children(|parent| {
188            widgets::spawn_option_buttons(
189                parent,
190                "Lighting",
191                &[
192                    (LightingMode::Baked, "Baked"),
193                    (LightingMode::MixedDirect, "Mixed (Direct)"),
194                    (LightingMode::MixedIndirect, "Mixed (Indirect)"),
195                    (LightingMode::RealTime, "Real-Time"),
196                ],
197            );
198        });
199}
200
201/// Spawns the help text at the top of the window.
202fn spawn_help_text(commands: &mut Commands, app_status: &AppStatus) {
203    commands.spawn((
204        create_help_text(app_status),
205        Node {
206            position_type: PositionType::Absolute,
207            top: Val::Px(12.0),
208            left: Val::Px(12.0),
209            ..default()
210        },
211        HelpText,
212    ));
213}
214
215/// Adds lightmaps to and/or removes lightmaps from objects in the scene when
216/// the lighting mode changes.
217///
218/// This is also called right after the scene loads in order to set up the
219/// lightmaps.
220fn update_lightmaps(
221    mut commands: Commands,
222    asset_server: Res<AssetServer>,
223    mut materials: ResMut<Assets<StandardMaterial>>,
224    meshes: Query<(Entity, &Name, &MeshMaterial3d<StandardMaterial>), With<Mesh3d>>,
225    mut lighting_mode_change_event_reader: EventReader<LightingModeChanged>,
226    app_status: Res<AppStatus>,
227) {
228    // Only run if the lighting mode changed. (Note that a change event is fired
229    // when the scene first loads.)
230    if lighting_mode_change_event_reader.read().next().is_none() {
231        return;
232    }
233
234    // Select the lightmap to use, based on the lighting mode.
235    let lightmap: Option<Handle<Image>> = match app_status.lighting_mode {
236        LightingMode::Baked => {
237            Some(asset_server.load("lightmaps/MixedLightingExample-Baked.zstd.ktx2"))
238        }
239        LightingMode::MixedDirect => {
240            Some(asset_server.load("lightmaps/MixedLightingExample-MixedDirect.zstd.ktx2"))
241        }
242        LightingMode::MixedIndirect => {
243            Some(asset_server.load("lightmaps/MixedLightingExample-MixedIndirect.zstd.ktx2"))
244        }
245        LightingMode::RealTime => None,
246    };
247
248    'outer: for (entity, name, material) in &meshes {
249        // Add lightmaps to or remove lightmaps from the scenery objects in the
250        // scene (all objects but the sphere).
251        //
252        // Note that doing a linear search through the `LIGHTMAPS` array is
253        // inefficient, but we do it anyway in this example to improve clarity.
254        for (lightmap_name, uv_rect) in LIGHTMAPS {
255            if &**name != lightmap_name {
256                continue;
257            }
258
259            // Lightmap exposure defaults to zero, so we need to set it.
260            if let Some(ref mut material) = materials.get_mut(material) {
261                material.lightmap_exposure = LIGHTMAP_EXPOSURE;
262            }
263
264            // Add or remove the lightmap.
265            match lightmap {
266                Some(ref lightmap) => {
267                    commands.entity(entity).insert(Lightmap {
268                        image: (*lightmap).clone(),
269                        uv_rect,
270                        bicubic_sampling: false,
271                    });
272                }
273                None => {
274                    commands.entity(entity).remove::<Lightmap>();
275                }
276            }
277            continue 'outer;
278        }
279
280        // Add lightmaps to or remove lightmaps from the sphere.
281        if &**name == "Sphere" {
282            // Lightmap exposure defaults to zero, so we need to set it.
283            if let Some(ref mut material) = materials.get_mut(material) {
284                material.lightmap_exposure = LIGHTMAP_EXPOSURE;
285            }
286
287            // Add or remove the lightmap from the sphere. We only apply the
288            // lightmap in fully-baked mode.
289            match (&lightmap, app_status.lighting_mode) {
290                (Some(lightmap), LightingMode::Baked) => {
291                    commands.entity(entity).insert(Lightmap {
292                        image: (*lightmap).clone(),
293                        uv_rect: SPHERE_UV_RECT,
294                        bicubic_sampling: false,
295                    });
296                }
297                _ => {
298                    commands.entity(entity).remove::<Lightmap>();
299                }
300            }
301        }
302    }
303}
304
305/// Converts a uv rectangle from the OpenGL coordinate system (origin in the
306/// lower left) to the Vulkan coordinate system (origin in the upper left) that
307/// Bevy uses.
308///
309/// For this particular example, the baking tool happened to use the OpenGL
310/// coordinate system, so it was more convenient to do the conversion at compile
311/// time than to pre-calculate and hard-code the values.
312const fn uv_rect_opengl(gl_min: Vec2, size: Vec2) -> Rect {
313    let min = vec2(gl_min.x, 1.0 - gl_min.y - size.y);
314    Rect {
315        min,
316        max: vec2(min.x + size.x, min.y + size.y),
317    }
318}
319
320/// Ensures that clicking on the scene to move the sphere doesn't result in a
321/// hit on the sphere itself.
322fn make_sphere_nonpickable(
323    mut commands: Commands,
324    mut query: Query<(Entity, &Name), (With<Mesh3d>, Without<Pickable>)>,
325) {
326    for (sphere, name) in &mut query {
327        if &**name == "Sphere" {
328            commands.entity(sphere).insert(Pickable::IGNORE);
329        }
330    }
331}
332
333/// Updates the directional light settings as necessary when the lighting mode
334/// changes.
335fn update_directional_light(
336    mut lights: Query<&mut DirectionalLight>,
337    mut lighting_mode_change_event_reader: EventReader<LightingModeChanged>,
338    app_status: Res<AppStatus>,
339) {
340    // Only run if the lighting mode changed. (Note that a change event is fired
341    // when the scene first loads.)
342    if lighting_mode_change_event_reader.read().next().is_none() {
343        return;
344    }
345
346    // Real-time direct light is used on the scenery if we're using mixed
347    // indirect or real-time mode.
348    let scenery_is_lit_in_real_time = matches!(
349        app_status.lighting_mode,
350        LightingMode::MixedIndirect | LightingMode::RealTime
351    );
352
353    for mut light in &mut lights {
354        light.affects_lightmapped_mesh_diffuse = scenery_is_lit_in_real_time;
355        // Don't bother enabling shadows if they won't show up on the scenery.
356        light.shadows_enabled = scenery_is_lit_in_real_time;
357    }
358}
359
360/// Updates the state of the selection widgets at the bottom of the window when
361/// the lighting mode changes.
362fn update_radio_buttons(
363    mut widgets: Query<
364        (
365            Entity,
366            Option<&mut BackgroundColor>,
367            Has<Text>,
368            &WidgetClickSender<LightingMode>,
369        ),
370        Or<(With<RadioButton>, With<RadioButtonText>)>,
371    >,
372    app_status: Res<AppStatus>,
373    mut writer: TextUiWriter,
374) {
375    for (entity, image, has_text, sender) in &mut widgets {
376        let selected = **sender == app_status.lighting_mode;
377
378        if let Some(mut bg_color) = image {
379            widgets::update_ui_radio_button(&mut bg_color, selected);
380        }
381        if has_text {
382            widgets::update_ui_radio_button_text(entity, &mut writer, selected);
383        }
384    }
385}
386
387/// Handles clicks on the widgets at the bottom of the screen and fires
388/// [`LightingModeChanged`] events.
389fn handle_lighting_mode_change(
390    mut widget_click_event_reader: EventReader<WidgetClickEvent<LightingMode>>,
391    mut lighting_mode_change_event_writer: EventWriter<LightingModeChanged>,
392    mut app_status: ResMut<AppStatus>,
393) {
394    for event in widget_click_event_reader.read() {
395        app_status.lighting_mode = **event;
396        lighting_mode_change_event_writer.write(LightingModeChanged);
397    }
398}
399
400/// Moves the sphere to its original position when the user selects the baked
401/// lighting mode.
402///
403/// As the light from the sphere is precomputed and depends on the sphere's
404/// original position, the sphere must be placed there in order for the lighting
405/// to be correct.
406fn reset_sphere_position(
407    mut objects: Query<(&Name, &mut Transform)>,
408    mut lighting_mode_change_event_reader: EventReader<LightingModeChanged>,
409    app_status: Res<AppStatus>,
410) {
411    // Only run if the lighting mode changed and if the lighting mode is
412    // `LightingMode::Baked`. (Note that a change event is fired when the scene
413    // first loads.)
414    if lighting_mode_change_event_reader.read().next().is_none()
415        || app_status.lighting_mode != LightingMode::Baked
416    {
417        return;
418    }
419
420    for (name, mut transform) in &mut objects {
421        if &**name == "Sphere" {
422            transform.translation = INITIAL_SPHERE_POSITION;
423            break;
424        }
425    }
426}
427
428/// Updates the position of the sphere when the user clicks on a spot in the
429/// scene.
430///
431/// Note that the position of the sphere is locked in baked lighting mode.
432fn move_sphere(
433    mouse_button_input: Res<ButtonInput<MouseButton>>,
434    pointers: Query<&PointerInteraction>,
435    mut meshes: Query<(&Name, &ChildOf), With<Mesh3d>>,
436    mut transforms: Query<&mut Transform>,
437    app_status: Res<AppStatus>,
438) {
439    // Only run when the left button is clicked and we're not in baked lighting
440    // mode.
441    if app_status.lighting_mode == LightingMode::Baked
442        || !mouse_button_input.pressed(MouseButton::Left)
443    {
444        return;
445    }
446
447    // Find the sphere.
448    let Some(child_of) = meshes
449        .iter_mut()
450        .filter_map(|(name, child_of)| {
451            if &**name == "Sphere" {
452                Some(child_of)
453            } else {
454                None
455            }
456        })
457        .next()
458    else {
459        return;
460    };
461
462    // Grab its transform.
463    let Ok(mut transform) = transforms.get_mut(child_of.parent()) else {
464        return;
465    };
466
467    // Set its transform to the appropriate position, as determined by the
468    // picking subsystem.
469    for interaction in pointers.iter() {
470        if let Some(&(
471            _,
472            HitData {
473                position: Some(position),
474                ..
475            },
476        )) = interaction.get_nearest_hit()
477        {
478            transform.translation = position + vec3(0.0, SPHERE_OFFSET, 0.0);
479        }
480    }
481}
examples/stress_tests/many_cubes.rs (line 498)
497fn fast_hue_to_rgb(hue: f32) -> Vec3 {
498    (hue * 6.0 - vec3(3.0, 2.0, 4.0)).abs() * vec3(1.0, -1.0, -1.0) + vec3(-1.0, 2.0, 2.0)
499}
examples/shader/custom_phase_item.rs (line 163)
162static VERTICES: [Vertex; 3] = [
163    Vertex::new(vec3(-0.866, -0.5, 0.5), vec3(1.0, 0.0, 0.0)),
164    Vertex::new(vec3(0.866, -0.5, 0.5), vec3(0.0, 1.0, 0.0)),
165    Vertex::new(vec3(0.0, 1.0, 0.5), vec3(0.0, 0.0, 1.0)),
166];
examples/3d/clearcoat.rs (lines 238-242)
232fn animate_light(
233    mut lights: Query<&mut Transform, Or<(With<PointLight>, With<DirectionalLight>)>>,
234    time: Res<Time>,
235) {
236    let now = time.elapsed_secs();
237    for mut transform in lights.iter_mut() {
238        transform.translation = vec3(
239            ops::sin(now * 1.4),
240            ops::cos(now * 1.0),
241            ops::cos(now * 0.6),
242        ) * vec3(3.0, 4.0, 3.0);
243        transform.look_at(Vec3::ZERO, Vec3::Y);
244    }
245}