pub struct Quat(/* private fields */);Expand description
A quaternion representing an orientation.
This quaternion is intended to be of unit length but may denormalize due to floating point “error creep” which can occur when successive quaternion operations are applied.
SIMD vector types are used for storage on supported platforms.
This type is 16 byte aligned.
Implementations§
Source§impl Quat
impl Quat
Sourcepub const fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Quat
pub const fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Quat
Creates a new rotation quaternion.
This should generally not be called manually unless you know what you are doing.
Use one of the other constructors instead such as identity or from_axis_angle.
from_xyzw is mostly used by unit tests and serde deserialization.
§Preconditions
This function does not check if the input is normalized, it is up to the user to provide normalized input or to normalized the resulting quaternion.
Sourcepub const fn from_array(a: [f32; 4]) -> Quat
pub const fn from_array(a: [f32; 4]) -> Quat
Creates a rotation quaternion from an array.
§Preconditions
This function does not check if the input is normalized, it is up to the user to provide normalized input or to normalized the resulting quaternion.
Sourcepub const fn from_vec4(v: Vec4) -> Quat
pub const fn from_vec4(v: Vec4) -> Quat
Creates a new rotation quaternion from a 4D vector.
§Preconditions
This function does not check if the input is normalized, it is up to the user to provide normalized input or to normalized the resulting quaternion.
Sourcepub fn from_slice(slice: &[f32]) -> Quat
pub fn from_slice(slice: &[f32]) -> Quat
Sourcepub fn write_to_slice(self, slice: &mut [f32])
pub fn write_to_slice(self, slice: &mut [f32])
Sourcepub fn from_axis_angle(axis: Vec3, angle: f32) -> Quat
pub fn from_axis_angle(axis: Vec3, angle: f32) -> Quat
Create a quaternion for a normalized rotation axis and angle (in radians).
The axis must be a unit vector.
§Panics
Will panic if axis is not normalized when glam_assert is enabled.
Sourcepub fn from_scaled_axis(v: Vec3) -> Quat
pub fn from_scaled_axis(v: Vec3) -> Quat
Create a quaternion that rotates v.length() radians around v.normalize().
from_scaled_axis(Vec3::ZERO) results in the identity quaternion.
Sourcepub fn from_rotation_x(angle: f32) -> Quat
pub fn from_rotation_x(angle: f32) -> Quat
Creates a quaternion from the angle (in radians) around the x axis.
Sourcepub fn from_rotation_y(angle: f32) -> Quat
pub fn from_rotation_y(angle: f32) -> Quat
Creates a quaternion from the angle (in radians) around the y axis.
Examples found in repository?
1319 fn draw_crates(&self, ctx: &mut FrameContext<'_, Keep>) {
1320 for &(x, z, turn) in &CRATE_POSITIONS {
1321 ctx.draw(Crate.at(Transform::from_scale_rotation_translation(
1322 Vec3::splat(CRATE_SIZE),
1323 Quat::from_rotation_y(turn),
1324 Vec3::new(x, CRATE_SIZE * 0.5, z),
1325 )));
1326 }
1327 }
1328
1329 /// The well: its rim in grey masonry, and the mouth cell laid over the
1330 /// rim's top face.
1331 fn draw_well(&self, ctx: &mut FrameContext<'_, Keep>) {
1332 let cells = Sheet::new(UVec2::new(WELL_CELLS, 1));
1333 ctx.draw(
1334 Well.at(Transform::from_scale_rotation_translation(
1335 WELL_SIZE,
1336 Quat::IDENTITY,
1337 WELL_POSITION + Vec3::Y * (WELL_SIZE.y * 0.5),
1338 ))
1339 .frame(cells.cell(WELL_RIM_CELL)),
1340 );
1341 ctx.draw(
1342 WellMouth
1343 .at(Transform::from_scale_rotation_translation(
1344 Vec3::new(WELL_SIZE.x, 1.0, WELL_SIZE.z),
1345 Quat::IDENTITY,
1346 WELL_POSITION + Vec3::Y * (WELL_SIZE.y + WELL_MOUTH_LIFT),
1347 ))
1348 .frame(cells.cell(WELL_MOUTH_CELL)),
1349 );
1350 }
1351
1352 fn draw_flora(&self, ctx: &mut FrameContext<'_, Keep>) {
1353 for &(x, z, rock) in &FLORA {
1354 let (width, height) = if rock {
1355 (ROCK_WIDTH, ROCK_HEIGHT)
1356 } else {
1357 (BUSH_WIDTH, BUSH_HEIGHT)
1358 };
1359 let standing = Transform::from_scale_rotation_translation(
1360 Vec3::new(width, height, width),
1361 Quat::IDENTITY,
1362 Vec3::new(x, height * 0.5, z),
1363 );
1364 let flora: Instance<Shape, _> = if rock {
1365 Rock.at(standing).into_set()
1366 } else {
1367 Bush.at(standing).into_set()
1368 };
1369 ctx.draw(flora.upright());
1370 }
1371 }
1372
1373 /// One stone box drawn on the ground at `at`, `size` across, sampling
1374 /// the part of the sheet `frame` covers.
1375 fn draw_stone(ctx: &mut FrameContext<'_, Keep>, at: Vec3, size: Vec3, frame: Frame) {
1376 ctx.draw(
1377 Stone
1378 .at(Transform::from_scale_rotation_translation(
1379 size,
1380 Quat::IDENTITY,
1381 at + Vec3::Y * (size.y * 0.5),
1382 ))
1383 .frame(frame),
1384 );
1385 }
1386
1387 /// Two stone pillars drawn where `mouth` blocks the player, each a
1388 /// capital over its own course of masonry, and, on the one the camera
1389 /// looks into, the lintel across their tops and the dark filling
1390 /// the opening under it.
1391 fn draw_mouth(ctx: &mut FrameContext<'_, Keep>, mouth: Mouth) {
1392 for at in mouth.pillars() {
1393 Self::draw_stone(ctx, at, MOUTH_PILLAR_SIZE, Frame::default());
1394 }
1395 if !mouth.looked_into() {
1396 return;
1397 }
1398
1399 Self::draw_stone(
1400 ctx,
1401 mouth.at + Vec3::Y * MOUTH_PILLAR_SIZE.y,
1402 MOUTH_LINTEL_SIZE,
1403 masonry(MOUTH_LINTEL_TILES),
1404 );
1405 ctx.draw(
1406 Quad.at(Transform::from_scale_rotation_translation(
1407 Vec3::new(MOUTH_PILLAR_OFFSET * 2.0, MOUTH_DARK_HEIGHT, 1.0),
1408 Quat::IDENTITY,
1409 mouth.at + Vec3::Y * (MOUTH_DARK_HEIGHT * 0.5),
1410 ))
1411 .material(Material::color(Color::BLACK)),
1412 );
1413 }
1414
1415 fn draw_cave_floor(&self, ctx: &mut FrameContext<'_, Keep>) {
1416 let half = CAVE_HALF_WIDTH as i32;
1417 let near = CAVE_NEAR_Z as i32;
1418 let far = CAVE_FAR_Z as i32;
1419 for col in -half..=half {
1420 for row in far..=near {
1421 let variant = (col * 13 + row * 7).rem_euclid(CAVE_COLUMNS as i32) as u32;
1422 ctx.draw(
1423 CaveFloor
1424 .at(Vec3::new(
1425 col as f32 * TILE_SIZE,
1426 0.0,
1427 row as f32 * TILE_SIZE,
1428 ))
1429 .frame(
1430 Sheet::new(UVec2::new(CAVE_COLUMNS, CAVE_ROWS))
1431 .cell_at(UVec2::new(variant, CAVE_FLOOR_ROW)),
1432 ),
1433 );
1434 }
1435 }
1436 }
1437
1438 /// The wall drawn at `at` over the meters `standing`, in courses
1439 /// [`WALL_HEIGHT`] tall from the floor up, each cut to the part of it the
1440 /// span leaves; its faces are picked by `seed` and its stone faded to
1441 /// `fade`, which is `1.0` wherever it is solid.
1442 fn draw_wall(
1443 ctx: &mut FrameContext<'_, Keep>,
1444 at: Vec2,
1445 standing: Range<f32>,
1446 seed: i32,
1447 fade: f32,
1448 ) {
1449 for course in 0..WALL_COURSES {
1450 let base = course as f32 * WALL_HEIGHT;
1451 let low = (standing.start - base).max(0.0);
1452 let high = (standing.end - base).min(WALL_HEIGHT);
1453 if high <= low {
1454 continue;
1455 }
1456
1457 let variant = (seed + course).rem_euclid(CAVE_COLUMNS as i32) as u32;
1458 ctx.draw(
1459 CaveWall
1460 .at(Transform::from_scale_rotation_translation(
1461 Vec3::new(TILE_SIZE, high - low, TILE_SIZE),
1462 Quat::IDENTITY,
1463 Vec3::new(at.x, base + (low + high) * 0.5, at.y),
1464 ))
1465 .frame(cave_wall_face(variant, low..high))
1466 .faded(fade),
1467 );
1468 }
1469 }
1470
1471 /// The room's two side walls and its back wall, full height, and the low
1472 /// wall closing its near end between the side walls and the mouth. The
1473 /// back wall stops short of the corners the side walls already fill, and
1474 /// the near one leaves the mouth's own tile open.
1475 fn draw_cave_walls(&self, ctx: &mut FrameContext<'_, Keep>) {
1476 let half = CAVE_HALF_WIDTH as i32 + 1;
1477 let near = CAVE_NEAR_Z as i32;
1478 let far = CAVE_FAR_Z as i32;
1479
1480 for row in far..=near {
1481 let z = row as f32 * TILE_SIZE;
1482 let west = Vec2::new(-half as f32 * TILE_SIZE, z);
1483 let east = Vec2::new(half as f32 * TILE_SIZE, z);
1484 Self::draw_wall(ctx, west, 0.0..WALL_TOP, row * 5, SOLID);
1485 Self::draw_wall(ctx, east, 0.0..WALL_TOP, row * 5 + 1, SOLID);
1486 }
1487 for col in (-half + 1)..half {
1488 let x = col as f32 * TILE_SIZE;
1489 let back = Vec2::new(x, far as f32 * TILE_SIZE);
1490 Self::draw_wall(ctx, back, 0.0..WALL_TOP, col * 5 + 2, SOLID);
1491 if col != 0 {
1492 let lip = Vec2::new(x, CAVE_LIP_Z);
1493 Self::draw_wall(ctx, lip, 0.0..CAVE_LIP_HEIGHT, col * 5 + 4, SOLID);
1494 }
1495 }
1496 }
1497
1498 /// The wall the door hangs in, run across the room between the side walls
1499 /// with one tile left open on the room's axis for the doorway and stone
1500 /// filling the column over the door. A player behind the wall is drawn
1501 /// through the stacks between them and the camera, at `seen_through`,
1502 /// faded by `ghost`; the rest of it stays solid, and keeps casting.
1503 fn draw_door_wall(ctx: &mut FrameContext<'_, Keep>, seen_through: Option<f32>, ghost: f32) {
1504 let stone = |x: f32| match seen_through {
1505 Some(at) if (x - at).abs() < GHOST_CORRIDOR_HALF => ghost_alpha(ghost),
1506 _ => SOLID,
1507 };
1508 let half = CAVE_HALF_WIDTH as i32;
1509
1510 for col in (-half..=half).filter(|&col| col != 0) {
1511 let x = col as f32 * TILE_SIZE;
1512 Self::draw_wall(
1513 ctx,
1514 Vec2::new(x, DOOR_Z),
1515 0.0..WALL_TOP,
1516 col * 5 + 3,
1517 stone(x),
1518 );
1519 }
1520 Self::draw_wall(
1521 ctx,
1522 Vec2::new(0.0, DOOR_Z),
1523 DOOR_HEIGHT..WALL_TOP,
1524 3,
1525 stone(0.0),
1526 );
1527 }
1528
1529 /// The two torches: an upright cutout post apiece, the flame's loop
1530 /// burning over its binding, and the light that flame casts.
1531 fn draw_torches(&self, ctx: &mut FrameContext<'_, Keep>) {
1532 let elapsed = self.simulated.as_secs_f32();
1533 let loop_cells = Sheet::new(UVec2::new(FLAME_CELLS, 1));
1534
1535 for (index, &(x, z)) in TORCH_POSITIONS.iter().enumerate() {
1536 let base = Vec3::new(x, 0.0, z);
1537 ctx.draw(
1538 Torch
1539 .at(Transform::from_scale_rotation_translation(
1540 Vec3::new(TORCH_SPRITE_WIDTH, TORCH_STAND_HEIGHT, 1.0),
1541 Quat::IDENTITY,
1542 base + Vec3::Y * (TORCH_STAND_HEIGHT * 0.5),
1543 ))
1544 .upright(),
1545 );
1546
1547 let phase = index as f32 * 2.1;
1548 let flicker = (elapsed * FLAME_FLICKER_SPEED + phase).sin();
1549 let flame_pos =
1550 base + Vec3::Y * (TORCH_STAND_HEIGHT + FLAME_LIFT + flicker * FLAME_BOB);
1551
1552 let light_pos = flame_pos + Vec3::new(0.0, TORCH_LIGHT_LIFT, TORCH_LIGHT_STANDOFF);
1553 ctx.light(Light::point(light_pos, TORCH_LIGHT_COLOR, TORCH_LIGHT_RANGE).shadow());
1554 // The pair burn an even share of the loop apart.
1555 let offset = index as u32 * FLAME_CELLS / TORCH_POSITIONS.len() as u32;
1556 ctx.draw(
1557 Flame
1558 .at(Transform::from_scale_rotation_translation(
1559 Vec3::splat(FLAME_SIZE),
1560 Quat::IDENTITY,
1561 flame_pos,
1562 ))
1563 .billboard()
1564 .roll(flicker * FLAME_ROLL)
1565 .frame(loop_cells.cell((elapsed * FLAME_RATE) as u32 + offset)),
1566 );
1567 }
1568 }
1569
1570 /// The door at its hinge — swung back against the wall once opened —
1571 /// drawn through alongside its wall, faded by `ghost`.
1572 fn draw_door(&self, ctx: &mut FrameContext<'_, Keep>, ghost: f32) {
1573 let fade = ghost_alpha(ghost);
1574 let swung = if self.door_opening {
1575 Quat::from_rotation_y(core::f32::consts::FRAC_PI_2)
1576 } else {
1577 Quat::IDENTITY
1578 };
1579
1580 ctx.draw(
1581 Door.at(Transform::from_rotation_translation(swung, DOOR_HINGE))
1582 .material(Material::shaded(DOOR_COLOR, DOOR_LITNESS))
1583 .faded(fade),
1584 );
1585 }
1586
1587 /// The posts and lintel framing the doorway, in a color the stone never
1588 /// is, standing clear of the wall so the opening reads as a door from
1589 /// across the chamber. Glowing of their own while the door is closed and
1590 /// within [`INTERACT_RADIUS`], the cue that it opens.
1591 fn draw_door_frame(&self, ctx: &mut FrameContext<'_, Keep>, ghost: f32) {
1592 let reachable =
1593 !self.door_opening && self.position.distance(INTERACT_POINT) < INTERACT_RADIUS;
1594 let material =
1595 Material::shaded(DOOR_FRAME_COLOR, DOOR_FRAME_LITNESS).emissive(if reachable {
1596 DOOR_FRAME_GLOW
1597 } else {
1598 Color::BLACK
1599 });
1600 let fade = ghost_alpha(ghost);
1601 let z = DOOR_WALL_NEAR_Z + DOOR_FRAME_STANDOFF;
1602 let jamb_height = DOOR_HEIGHT + DOOR_FRAME_THICKNESS;
1603
1604 for side in SIDES {
1605 ctx.draw(
1606 Cube.at(Transform::from_scale_rotation_translation(
1607 Vec3::new(DOOR_FRAME_THICKNESS, jamb_height, DOOR_FRAME_THICKNESS),
1608 Quat::IDENTITY,
1609 Vec3::new(
1610 side * (DOORWAY_HALF + DOOR_FRAME_THICKNESS * 0.5),
1611 jamb_height * 0.5,
1612 z,
1613 ),
1614 ))
1615 .material(material)
1616 .faded(fade),
1617 );
1618 }
1619 ctx.draw(
1620 Cube.at(Transform::from_scale_rotation_translation(
1621 Vec3::new(
1622 DOOR_WIDTH + DOOR_FRAME_THICKNESS * 2.0,
1623 DOOR_FRAME_THICKNESS,
1624 DOOR_FRAME_THICKNESS,
1625 ),
1626 Quat::IDENTITY,
1627 Vec3::new(0.0, DOOR_HEIGHT + DOOR_FRAME_THICKNESS * 0.5, z),
1628 ))
1629 .material(material)
1630 .faded(fade),
1631 );
1632 }
1633
1634 /// A world prompt over the door: what opens it while the player is
1635 /// within [`INTERACT_RADIUS`] and it is closed, and that it swings while
1636 /// it does; gone once it has swung [`DOOR_SWING_TICKS`]. Laid out and
1637 /// placed like `examples/animation.rs`'s own prompt.
1638 fn draw_door_prompt(&self, ctx: &mut FrameContext<'_, Keep>, camera: Camera) {
1639 let near = self.position.distance(INTERACT_POINT) < INTERACT_RADIUS;
1640 let swinging = self.door_opening && self.swing_ticks < DOOR_SWING_TICKS;
1641 let text = if swinging {
1642 "opening"
1643 } else if near && !self.door_opening {
1644 "e opens the door"
1645 } else {
1646 return;
1647 };
1648
1649 let galley = ctx.text_layout(text, egui::FontId::proportional(DOOR_PROMPT_SIZE));
1650 let point = INTERACT_POINT + Vec3::Y * (DOOR_HEIGHT + DOOR_PROMPT_LIFT);
1651 let window_size = ctx.window_size();
1652 let pixels_per_point = ctx.pixels_per_point();
1653 let Some(pixel) = camera.pixel_of(point, window_size) else {
1654 return;
1655 };
1656
1657 ctx.ui(|ui| {
1658 let painter = ui.painter();
1659 let at = logical(pixel, pixels_per_point);
1660 let ink = galley.mesh_bounds;
1661 let pos = egui::pos2(at.x - ink.center().x, at.y - ink.center().y);
1662 let backdrop = egui::Rect::from_center_size(
1663 at,
1664 ink.size() + egui::Vec2::splat(DOOR_PROMPT_PADDING * 2.0),
1665 );
1666 painter.rect_filled(
1667 backdrop,
1668 DOOR_PROMPT_PADDING,
1669 egui::Color32::from_black_alpha(DOOR_PROMPT_BACKDROP),
1670 );
1671 painter.galley(pos, galley, DOOR_PROMPT_COLOR);
1672 });
1673 }
1674
1675 /// The gem, spinning and bobbing over the chamber's floor, and the light
1676 /// it casts over it.
1677 fn draw_gem(&self, ctx: &mut FrameContext<'_, Keep>) {
1678 let t = self.simulated.as_secs_f32();
1679 let bob = (t * 2.0).sin() * GEM_BOB_HEIGHT;
1680 ctx.light(
1681 Light::point(
1682 GEM_POSITION + Vec3::Y * (bob + GEM_LIGHT_LIFT),
1683 GEM_LIGHT_COLOR,
1684 GEM_LIGHT_RANGE,
1685 )
1686 .shadow(),
1687 );
1688 ctx.draw(
1689 Gem.at(Transform::from_scale_rotation_translation(
1690 Vec3::ONE,
1691 Quat::from_rotation_y(t * GEM_SPIN_SPEED),
1692 GEM_POSITION + Vec3::Y * bob,
1693 ))
1694 .material(Material::shaded(GEM_COLOR, 0.7).emissive(GEM_COLOR.dimmed(1.6))),
1695 );
1696 }More examples
527fn orbit_camera(target: Vec3, yaw: f32, pitch: f32) -> Camera {
528 let look_at = target + Vec3::Y * CAMERA_LOOK_HEIGHT;
529 let base = Vec3::new(0.0, CAMERA_UP, CAMERA_BACK);
530 let offset = Quat::from_rotation_y(yaw) * (Quat::from_rotation_x(pitch) * base);
531 Camera::new(
532 View::look_at(look_at + offset, look_at),
533 Projection::perspective(CAMERA_FOV),
534 )
535}
536
537/// The logical point egui paints the physical pixel `pixel` at.
538fn logical(pixel: Vec2, pixels_per_point: f32) -> egui::Pos2 {
539 let point = pixel / pixels_per_point;
540 egui::pos2(point.x, point.y)
541}
542
543#[derive(InputButtonAction, Clone, Copy)]
544enum Button {
545 Run,
546 Attack,
547 Jump,
548 Dance,
549 Interact,
550 Restart,
551 Hold,
552 Release,
553}
554
555impl InputButtonAction for Button {
556 fn bindings(&self) -> Vec<ButtonBinding> {
557 match self {
558 Button::Run => vec![Key::LeftShift.into()],
559 Button::Attack => vec![Key::F.into()],
560 Button::Jump => vec![Key::Space.into()],
561 Button::Dance => vec![Key::N.into()],
562 Button::Interact => vec![Key::E.into()],
563 Button::Restart => vec![Key::R.into()],
564 Button::Hold => vec![MouseButton::Left.into()],
565 Button::Release => vec![Key::Escape.into()],
566 }
567 }
568}
569
570/// The camera's own controls: turned and tilted by how far the pointer
571/// moves sideways and upward each tick.
572#[derive(InputAxisAction, Clone, Copy)]
573enum Axis {
574 CameraYaw,
575 CameraPitch,
576}
577
578impl InputAxisAction for Axis {
579 fn bindings(&self) -> Vec<AxisBinding> {
580 match self {
581 Axis::CameraYaw => {
582 vec![AxisBinding::pointer_delta(PointerDelta::Sideways).scale(CAMERA_YAW_SCALE)]
583 }
584 Axis::CameraPitch => {
585 vec![AxisBinding::pointer_delta(PointerDelta::Up).scale(CAMERA_PITCH_SCALE)]
586 }
587 }
588 }
589}
590
591#[derive(InputAxis2Action, Clone, Copy)]
592enum Move {
593 Walk,
594}
595
596impl InputAxis2Action for Move {
597 fn bindings(&self) -> Vec<Axis2Binding> {
598 match self {
599 Move::Walk => vec![
600 Axis2Binding::from(ButtonAxis2 {
601 left: Key::A,
602 right: Key::D,
603 down: Key::S,
604 up: Key::W,
605 }),
606 Axis2Binding::from(ButtonAxis2 {
607 left: Key::Left,
608 right: Key::Right,
609 down: Key::Down,
610 up: Key::Up,
611 }),
612 ],
613 }
614 }
615}
616
617struct Controls;
618
619impl InputActions for Controls {
620 type Button = Button;
621 type Axis = Axis;
622 type Axis2 = Move;
623}
624
625/// `text` in [`PANEL_TEXT_COLOR`].
626fn panel_text(text: impl Into<String>) -> egui::RichText {
627 egui::RichText::new(text.into()).color(PANEL_TEXT_COLOR)
628}
629
630struct Scene {
631 elf_pos: Vec3,
632 elf_prev: Vec3,
633 elf_yaw: f32,
634 /// Height the elf is lifted over the ground while off the ground,
635 /// integrated in [`Game::tick`] from [`Self::jump_speed`].
636 elf_height: f32,
637 elf_height_prev: f32,
638 /// The elf's own vertical speed while off the ground, in meters a
639 /// second, positive upward.
640 jump_speed: f32,
641 elf_input: ElfInput,
642 elf_animator: Animator<Elf, ElfState>,
643 scrubbed_animator: Animator<Elf, ScrubbedState>,
644 butterfly_animator: Animator<Butterfly, FlyingState>,
645 hits: u32,
646 /// True while a hurt patch already held the elf, so leaving and
647 /// returning to the same patch counts as a new hit.
648 in_patch: bool,
649 /// Whether the pointer is held; a click takes it, escape frees it.
650 holding: bool,
651 /// The camera's turn around the elf, and its tilt, both in radians.
652 camera_yaw: f32,
653 camera_pitch: f32,
654 /// The last state change the panel names.
655 last_event: &'static str,
656}
657
658impl Scene {
659 fn init(_ctx: &mut InitContext<'_, Scene>) -> Result<Self, Error> {
660 Ok(Self {
661 elf_pos: ELF_START,
662 elf_prev: ELF_START,
663 elf_yaw: 0.0,
664 elf_height: 0.0,
665 elf_height_prev: 0.0,
666 jump_speed: 0.0,
667 elf_input: ElfInput::default(),
668 elf_animator: Animator::new(),
669 scrubbed_animator: Animator::new(),
670 butterfly_animator: Animator::new(),
671 hits: 0,
672 in_patch: false,
673 holding: false,
674 camera_yaw: 0.0,
675 camera_pitch: 0.0,
676 last_event: "none yet",
677 })
678 }
679
680 /// Turns the camera by how far the pointer moves sideways and upward,
681 /// [`CAMERA_PITCH_RANGE`] holding how far it tilts.
682 fn steer_camera(&mut self, ctx: &mut FrameContext<'_, Scene>) {
683 self.camera_yaw -= ctx.axis(Axis::CameraYaw);
684 self.camera_pitch = (self.camera_pitch + ctx.axis(Axis::CameraPitch))
685 .clamp(CAMERA_PITCH_RANGE.start, CAMERA_PITCH_RANGE.end);
686 }
687
688 /// Turns `elf_yaw` toward the heading `ctx` reads, relative to the
689 /// camera's own turn, and moves `elf_pos` along it; the speed it moves
690 /// at, a fraction of [`ELF_SPEED`], held at [`WALK_CAP`] until
691 /// `Button::Run` is held.
692 fn advance(&mut self, ctx: &mut TickContext<'_, Scene>) -> f32 {
693 let control = ctx.axis2(Move::Walk).clamp_length_max(1.0);
694 let turn = Quat::from_rotation_y(self.camera_yaw);
695 let heading = turn * Vec3::X * control.x + turn * Vec3::NEG_Z * control.y;
696 let dt = ctx.dt().as_secs_f32();
697 if let Some(direction) = heading.try_normalize() {
698 let wanted = direction.x.atan2(direction.z);
699 let turn = (wanted - self.elf_yaw + core::f32::consts::PI).rem_euclid(TAU)
700 - core::f32::consts::PI;
701 self.elf_yaw += turn.clamp(-TURN_RATE * dt, TURN_RATE * dt);
702 }
703 let cap = if ctx.down(Button::Run) { 1.0 } else { WALK_CAP };
704 self.elf_pos += heading * cap * ELF_SPEED * dt;
705 heading.length() * cap
706 }
707
708 /// Integrates [`Self::elf_height`] under [`GRAVITY`] from
709 /// [`Self::jump_speed`], held at the ground; `true` the tick it
710 /// returns there from above it.
711 fn fall(&mut self, dt: f32) -> bool {
712 let off_ground = self.elf_height > 0.0;
713 self.jump_speed -= GRAVITY * dt;
714 self.elf_height = (self.elf_height + self.jump_speed * dt).max(0.0);
715 if self.elf_height == 0.0 {
716 self.jump_speed = 0.0;
717 }
718 off_ground && self.elf_height == 0.0
719 }
720
721 /// The hurt patch `elf_pos` stands inside, if any.
722 fn patch_underfoot(&self) -> Option<Vec3> {
723 HURT_PATCHES
724 .into_iter()
725 .find(|&patch| self.elf_pos.distance(patch) < HURT_RADIUS)
726 }
727
728 /// Reads the controls and moves the elf, filling [`Self::elf_input`]
729 /// for [`ElfState`] to read.
730 fn tick_elf(&mut self, ctx: &mut TickContext<'_, Scene>) {
731 let grounded = matches!(
732 self.elf_animator.state(),
733 ElfState::Idle | ElfState::Locomotion
734 );
735
736 self.elf_input.speed = self.advance(ctx);
737 self.elf_input.attack = ctx.pressed(Button::Attack);
738 self.elf_input.jump = ctx.pressed(Button::Jump);
739 self.elf_input.dance = ctx.pressed(Button::Dance);
740
741 self.elf_input.near_seat = self.elf_pos.distance(SEAT_POSITION) < SEAT_INTERACT_RADIUS;
742 self.elf_input.interact = ctx.pressed(Button::Interact);
743 if self.elf_input.interact && grounded && self.elf_input.near_seat {
744 self.elf_pos = SEAT_SPOT;
745 self.elf_yaw = SEAT_FACING;
746 }
747
748 let underfoot = self.patch_underfoot();
749 let entered_patch = underfoot.is_some() && !self.in_patch;
750 self.in_patch = underfoot.is_some();
751 self.hits += u32::from(entered_patch);
752 self.elf_input.hit = entered_patch && self.hits < FATAL_HITS;
753 self.elf_input.dying = entered_patch && self.hits >= FATAL_HITS;
754 if entered_patch {
755 self.last_event = match self.elf_input.dying {
756 true => "elf died",
757 false => "elf hit",
758 };
759 }
760 }
761
762 /// Starts a new [`Animator`] over the elf's own state, its position and
763 /// hit count reset with it.
764 fn restart_elf(&mut self) {
765 self.elf_animator = Animator::new();
766 self.elf_pos = ELF_START;
767 self.elf_prev = ELF_START;
768 self.elf_yaw = 0.0;
769 self.elf_height = 0.0;
770 self.elf_height_prev = 0.0;
771 self.jump_speed = 0.0;
772 self.elf_input = ElfInput::default();
773 self.hits = 0;
774 self.in_patch = false;
775 self.last_event = "new elf started";
776 }
777
778 fn panel(&self, ctx: &mut FrameContext<'_, Scene>) {
779 let state = match self.elf_animator.state() {
780 ElfState::Idle => "idle",
781 ElfState::Locomotion if self.elf_input.speed > WALK_CAP => "running",
782 ElfState::Locomotion => "walking",
783 ElfState::Attack => "attacking",
784 ElfState::Hit => "hit",
785 ElfState::Death => "dead",
786 ElfState::SitDown => "sitting down",
787 ElfState::Sit => "sitting",
788 ElfState::StandUp => "standing up",
789 ElfState::Jump => "jumping",
790 ElfState::Dance => "dancing",
791 };
792 ctx.ui(|ui| {
793 egui::Frame::new()
794 .fill(egui::Color32::from_black_alpha(PANEL_BACKDROP))
795 .inner_margin(PANEL_PADDING)
796 .corner_radius(f32::from(PANEL_PADDING))
797 .show(ui, |ui| {
798 ui.heading(panel_text(format!("elf is {state}")));
799 ui.label(panel_text(format!(
800 "hits taken {} of the {} red patches hurt for, {}",
801 self.hits, FATAL_HITS, self.last_event
802 )));
803 ui.label(panel_text(match self.elf_animator.transitioning() {
804 true => "fading between clips",
805 false => "one clip playing",
806 }));
807 ui.add_space(f32::from(PANEL_PADDING));
808 egui::Grid::new("controls").show(ui, |ui| {
809 for (key, does) in CONTROLS {
810 ui.label(panel_text(key));
811 ui.label(panel_text(does));
812 ui.end_row();
813 }
814 });
815 });
816 });
817 }
818
819 /// A prompt over the seat, each hurt patch, and the scrubbed elf,
820 /// naming what a player finds there; the seat's own prompt names the
821 /// live binding of `Button::Interact` by its own name, not one fixed
822 /// in the code, and is absent while the elf sits on it.
823 fn draw_prompts(&self, ctx: &mut FrameContext<'_, Scene>, camera: Camera) {
824 let sit_key = ctx
825 .bindings(Button::Interact)
826 .into_iter()
827 .next()
828 .map_or_else(|| "interact".to_owned(), |binding| binding.to_string());
829 let sit = ctx.text_layout(
830 &format!("{sit_key} sits"),
831 egui::FontId::proportional(PROMPT_SIZE),
832 );
833 let hurts = ctx.text_layout("hurts", egui::FontId::proportional(PROMPT_SIZE));
834 let walk_closer = ctx.text_layout("walk closer", egui::FontId::proportional(PROMPT_SIZE));
835
836 let mut prompts = vec![(
837 SCRUBBED_ELF_POSITION + Vec3::Y * (ELF_HEIGHT + PROMPT_LIFT),
838 walk_closer,
839 )];
840 if !self.elf_animator.state().seated() {
841 prompts.push((
842 SEAT_POSITION + Vec3::Y * (SEAT_HEAD_HEIGHT + PROMPT_LIFT),
843 sit,
844 ));
845 }
846 prompts.extend(HURT_PATCHES.map(|patch| (patch + Vec3::Y * PROMPT_LIFT, hurts.clone())));
847
848 let window_size = ctx.window_size();
849 let pixels_per_point = ctx.pixels_per_point();
850 ctx.ui(|ui| {
851 let painter = ui.painter();
852 for (point, galley) in prompts {
853 let Some(pixel) = camera.pixel_of(point, window_size) else {
854 continue;
855 };
856 let at = logical(pixel, pixels_per_point);
857 let ink = galley.mesh_bounds;
858 let pos = egui::pos2(at.x - ink.center().x, at.y - ink.center().y);
859 let backdrop = egui::Rect::from_center_size(
860 at,
861 ink.size() + egui::Vec2::splat(PROMPT_PADDING * 2.0),
862 );
863 painter.rect_filled(
864 backdrop,
865 PROMPT_PADDING,
866 egui::Color32::from_black_alpha(PANEL_BACKDROP),
867 );
868 painter.galley(pos, galley, PANEL_TEXT_COLOR);
869 }
870 });
871 }
872}
873
874impl Game for Scene {
875 type Meshes = Shape;
876 type Sounds = NoSounds;
877 type InputActions = Controls;
878 type Skyboxes = Sky;
879 type SurfaceStyles = NoSurfaceStyles;
880 type PostEffects = NoPostEffects;
881
882 fn tick(&mut self, ctx: &mut TickContext<'_, Scene>) {
883 self.elf_prev = self.elf_pos;
884 self.elf_height_prev = self.elf_height;
885
886 if ctx.pressed(Button::Restart) {
887 self.restart_elf();
888 }
889 if ctx.pressed(Button::Hold) {
890 self.holding = true;
891 }
892 if ctx.pressed(Button::Release) {
893 self.holding = false;
894 }
895
896 self.elf_input.landed = self.fall(ctx.dt().as_secs_f32());
897 self.tick_elf(ctx);
898 ctx.animate(Elf, &mut self.elf_animator, &self.elf_input);
899
900 if self.elf_animator.entered(ElfState::Jump) {
901 self.jump_speed = JUMP_LAUNCH_SPEED;
902 }
903 if self.elf_animator.left(ElfState::StandUp) {
904 self.last_event = "elf stood up";
905 }
906 if self.elf_animator.entered(ElfState::Sit) {
907 self.last_event = "elf sat down";
908 }
909 if self.elf_animator.entered(ElfState::Death) {
910 self.last_event = "elf died";
911 }
912
913 let scrubbed_input = ScrubbedInput {
914 settled: settled_at(self.elf_pos.distance(SCRUBBED_ELF_POSITION)),
915 };
916 ctx.animate(Elf, &mut self.scrubbed_animator, &scrubbed_input);
917 ctx.animate(Butterfly, &mut self.butterfly_animator, &());
918 }
919
920 fn frame(&mut self, ctx: &mut FrameContext<'_, Scene>) {
921 self.steer_camera(ctx);
922
923 let alpha = ctx.alpha();
924 let elf_pos = self.elf_prev.lerp(self.elf_pos, alpha);
925 let elf_height = self.elf_height_prev + (self.elf_height - self.elf_height_prev) * alpha;
926 let (butterfly_pos, butterfly_yaw) = butterfly_pose(ctx.elapsed().as_secs_f32());
927
928 let camera = orbit_camera(elf_pos, self.camera_yaw, self.camera_pitch);
929 ctx.set_camera(camera);
930 ctx.set_cursor(if self.holding {
931 Cursor::Held
932 } else {
933 Cursor::Arrow
934 });
935 ctx.set_skybox(Sky::Day);
936 ctx.set_exposure(3.0);
937 ctx.set_bloom(0.2);
938 ctx.light(Light::directional(SUN_DIRECTION, SUN_COLOR).shadow());
939 ctx.light(
940 Light::point(
941 LAMP_POST_POSITION + Vec3::Y * (LAMP_POST_HEIGHT + LAMP_HEAD_GAP * 0.5),
942 LAMP_LIGHT_COLOR,
943 LAMP_LIGHT_RANGE,
944 )
945 .shadow(),
946 );
947 ctx.light(
948 Light::spot(Spot {
949 position: SPOT_POSITION,
950 direction: SPOT_DIRECTION,
951 color: SPOT_COLOR,
952 range: SPOT_RANGE,
953 angle: SPOT_ANGLE,
954 })
955 .shadow(),
956 );
957 ctx.light(
958 Light::point(butterfly_pos, BUTTERFLY_LIGHT_COLOR, BUTTERFLY_LIGHT_RANGE).shadow(),
959 );
960
961 ctx.draw(
962 Plane
963 .at(Transform::from_scale(Vec3::new(
964 GROUND_SIZE,
965 1.0,
966 GROUND_SIZE,
967 )))
968 .material(Material::lit(GROUND_COLOR)),
969 );
970 for patch in HURT_PATCHES {
971 ctx.draw(
972 Plane
973 .at(Transform::from_scale_rotation_translation(
974 Vec3::splat(HURT_RADIUS * 2.0),
975 Quat::IDENTITY,
976 patch,
977 ))
978 .material(Material::lit(HURT_COLOR)),
979 );
980 }
981 ctx.draw(
982 Cube.at(Transform::from_scale_rotation_translation(
983 Vec3::new(SEAT_FOOTPRINT, SEAT_HEIGHT, SEAT_FOOTPRINT),
984 Quat::IDENTITY,
985 SEAT_POSITION + Vec3::Y * SEAT_HEIGHT * 0.5,
986 ))
987 .material(Material::lit(SEAT_COLOR)),
988 );
989 ctx.draw(
990 Cube.at(Transform::from_scale_rotation_translation(
991 Vec3::new(LAMP_POST_THICKNESS, LAMP_POST_HEIGHT, LAMP_POST_THICKNESS),
992 Quat::IDENTITY,
993 LAMP_POST_POSITION + Vec3::Y * LAMP_POST_HEIGHT * 0.5,
994 ))
995 .material(Material::lit(LAMP_POST_COLOR)),
996 );
997 ctx.draw(
998 Cube.at(Transform::from_scale_rotation_translation(
999 Vec3::splat(LAMP_HEAD_SIZE),
1000 Quat::IDENTITY,
1001 LAMP_POST_POSITION
1002 + Vec3::Y * (LAMP_POST_HEIGHT + LAMP_HEAD_GAP + LAMP_HEAD_SIZE * 0.5),
1003 ))
1004 .material(Material::color(Color::BLACK).emissive(LAMP_LIGHT_COLOR)),
1005 );
1006 ctx.draw(
1007 Cube.at(Transform::from_scale_rotation_translation(
1008 Vec3::splat(SPOT_FIXTURE_SIZE),
1009 Quat::IDENTITY,
1010 SPOT_POSITION + Vec3::Y * SPOT_FIXTURE_SIZE * 0.5,
1011 ))
1012 .material(Material::lit(SPOT_FIXTURE_COLOR)),
1013 );
1014
1015 ctx.draw(
1016 Elf.at(Transform::from_rotation_translation(
1017 Quat::from_rotation_y(self.elf_yaw),
1018 elf_pos + Vec3::Y * elf_height,
1019 ))
1020 .posed(&self.elf_animator),
1021 );
1022 ctx.draw(
1023 Elf.at(Transform::from_rotation_translation(
1024 Quat::from_rotation_y(core::f32::consts::PI),
1025 SCRUBBED_ELF_POSITION,
1026 ))
1027 .posed(&self.scrubbed_animator),
1028 );
1029 ctx.draw(
1030 Butterfly
1031 .at(Transform::from_rotation_translation(
1032 Quat::from_rotation_y(butterfly_yaw),
1033 butterfly_pos,
1034 ))
1035 .posed(&self.butterfly_animator)
1036 .material(Material::lit(Color::WHITE).emissive(BUTTERFLY_EMISSIVE)),
1037 );
1038
1039 self.draw_prompts(ctx, camera);
1040 self.panel(ctx);
1041 }568 fn draw_rocks(&self, ctx: &mut FrameContext<'_, Board>) {
569 for &(x, z, seed, scale) in &ROCKS {
570 let angle = hash_signed(seed, 99) * core::f32::consts::PI;
571 ctx.draw(
572 Rock { seed }
573 .at(Transform::from_scale_rotation_translation(
574 Vec3::splat(scale),
575 Quat::from_rotation_y(angle),
576 Vec3::new(x, 0.5 * scale, z),
577 ))
578 .material(Material::lit(ROCK_COLOR)),
579 );
580 }
581 }403 fn draw_field(&self, ctx: &mut FrameContext<'_, Self>, elapsed: f32) {
404 for entry in &self.field {
405 let yaw = if self.settings.moving && entry.moving {
406 entry.phase + elapsed * MOVING_SPEED
407 } else {
408 entry.phase
409 };
410 ctx.draw(
411 Rock { seed: entry.seed }.at(Transform::from_scale_rotation_translation(
412 Vec3::ONE,
413 Quat::from_rotation_y(yaw),
414 entry.position,
415 )),
416 );
417 }
418 }Sourcepub fn from_rotation_z(angle: f32) -> Quat
pub fn from_rotation_z(angle: f32) -> Quat
Creates a quaternion from the angle (in radians) around the z axis.
Sourcepub fn from_euler(euler: EulerRot, a: f32, b: f32, c: f32) -> Quat
pub fn from_euler(euler: EulerRot, a: f32, b: f32, c: f32) -> Quat
Creates a quaternion from the given Euler rotation sequence and the angles (in radians).
Sourcepub fn from_rotation_axes(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Quat
pub fn from_rotation_axes(x_axis: Vec3, y_axis: Vec3, z_axis: Vec3) -> Quat
From the columns of a 3x3 rotation matrix.
Note if the input axes contain scales, shears, or other non-rotation transformations then the output of this function is ill-defined.
§Panics
Will panic if any axis is not normalized when glam_assert is enabled.
Sourcepub fn from_mat3(mat: &Mat3) -> Quat
pub fn from_mat3(mat: &Mat3) -> Quat
Creates a quaternion from a 3x3 rotation matrix.
Note if the input matrix contain scales, shears, or other non-rotation transformations then the resulting quaternion will be ill-defined.
§Panics
Will panic if any input matrix column is not normalized when glam_assert is enabled.
Sourcepub fn from_mat3a(mat: &Mat3A) -> Quat
pub fn from_mat3a(mat: &Mat3A) -> Quat
Creates a quaternion from a 3x3 SIMD aligned rotation matrix.
Note if the input matrix contain scales, shears, or other non-rotation transformations then the resulting quaternion will be ill-defined.
§Panics
Will panic if any input matrix column is not normalized when glam_assert is enabled.
Sourcepub fn from_mat4(mat: &Mat4) -> Quat
pub fn from_mat4(mat: &Mat4) -> Quat
Creates a quaternion from the upper 3x3 rotation matrix inside a homogeneous 4x4 matrix.
Note if the upper 3x3 matrix contain scales, shears, or other non-rotation transformations then the resulting quaternion will be ill-defined.
§Panics
Will panic if any column of the upper 3x3 rotation matrix is not normalized when
glam_assert is enabled.
Sourcepub fn from_rotation_arc(from: Vec3, to: Vec3) -> Quat
pub fn from_rotation_arc(from: Vec3, to: Vec3) -> Quat
Gets the minimal rotation for transforming from to to. The rotation is in the
plane spanned by the two vectors. Will rotate at most 180 degrees.
The inputs must be unit vectors.
from_rotation_arc(from, to) * from ≈ to.
For near-singular cases (from≈to and from≈-to) the current implementation
is only accurate to about 0.001 (for f32).
§Panics
Will panic if from or to are not normalized when glam_assert is enabled.
Examples found in repository?
674 fn draw_butterflies(&self, ctx: &mut FrameContext<'_, Self>) {
675 for (position, velocity, kind) in self.butterflies.each() {
676 let rotation = Quat::from_rotation_arc(Vec3::Z, Vec3::from(velocity).normalize());
677 ctx.draw(
678 Butterfly
679 .at(Transform::from_scale_rotation_translation(
680 Vec3::splat(BUTTERFLY_SCALE),
681 rotation,
682 Vec3::from(position),
683 ))
684 .posed(&self.flaps[usize::from(kind.flap)])
685 .material(Material::lit(TINTS[usize::from(kind.tint)])),
686 );
687 }
688 }Sourcepub fn from_rotation_arc_colinear(from: Vec3, to: Vec3) -> Quat
pub fn from_rotation_arc_colinear(from: Vec3, to: Vec3) -> Quat
Gets the minimal rotation for transforming from to either to or -to. This means
that the resulting quaternion will rotate from so that it is colinear with to.
The rotation is in the plane spanned by the two vectors. Will rotate at most 90 degrees.
The inputs must be unit vectors.
to.dot(from_rotation_arc_colinear(from, to) * from).abs() ≈ 1.
§Panics
Will panic if from or to are not normalized when glam_assert is enabled.
Sourcepub fn from_rotation_arc_2d(from: Vec2, to: Vec2) -> Quat
pub fn from_rotation_arc_2d(from: Vec2, to: Vec2) -> Quat
Gets the minimal rotation for transforming from to to. The resulting rotation is
around the z axis. Will rotate at most 180 degrees.
The inputs must be unit vectors.
from_rotation_arc_2d(from, to) * from ≈ to.
For near-singular cases (from≈to and from≈-to) the current implementation
is only accurate to about 0.001 (for f32).
§Panics
Will panic if from or to are not normalized when glam_assert is enabled.
Sourcepub fn look_to_lh(dir: Vec3, up: Vec3) -> Quat
👎Deprecated since 0.33.1: use the glam::camera::lh::view::look_to_quat function instead
pub fn look_to_lh(dir: Vec3, up: Vec3) -> Quat
use the glam::camera::lh::view::look_to_quat function instead
Creates a quaterion rotation from a facing direction and an up direction.
For a left-handed view coordinate system with +X=right, +Y=up and +Z=forward.
§Panics
Will panic if up is not normalized when glam_assert is enabled.
Sourcepub fn look_to_rh(dir: Vec3, up: Vec3) -> Quat
👎Deprecated since 0.33.1: use the glam::camera::rh::view::look_to_quat function instead
pub fn look_to_rh(dir: Vec3, up: Vec3) -> Quat
use the glam::camera::rh::view::look_to_quat function instead
Creates a quaterion rotation from facing direction and an up direction.
For a right-handed view coordinate system with +X=right, +Y=up and +Z=back.
§Panics
Will panic if dir and up are not normalized when glam_assert is enabled.
Sourcepub fn look_at_lh(eye: Vec3, center: Vec3, up: Vec3) -> Quat
👎Deprecated since 0.33.1: use the glam::camera::lh::view::look_at_quat function instead
pub fn look_at_lh(eye: Vec3, center: Vec3, up: Vec3) -> Quat
use the glam::camera::lh::view::look_at_quat function instead
Creates a quaternion rotation from a camera position, a focal point, and an up direction.
For a left-handed view coordinate system with +X=right, +Y=up and +Z=forward.
§Panics
Will panic if up is not normalized when glam_assert is enabled.
Sourcepub fn look_at_rh(eye: Vec3, center: Vec3, up: Vec3) -> Quat
👎Deprecated since 0.33.1: use the glam::camera::rh::view::look_at_quat function instead
pub fn look_at_rh(eye: Vec3, center: Vec3, up: Vec3) -> Quat
use the glam::camera::rh::view::look_at_quat function instead
Creates a quaternion rotation using a camera position, an up direction, and a focal point.
For a right-handed view coordinate system with +X=right, +Y=up and +Z=back.
§Panics
Will panic if up is not normalized when glam_assert is enabled.
Sourcepub fn to_axis_angle(self) -> (Vec3, f32)
pub fn to_axis_angle(self) -> (Vec3, f32)
Returns the rotation axis (normalized) and angle (in radians) of self.
Sourcepub fn to_scaled_axis(self) -> Vec3
pub fn to_scaled_axis(self) -> Vec3
Returns the rotation axis scaled by the rotation in radians.
Sourcepub fn to_euler(self, order: EulerRot) -> (f32, f32, f32)
pub fn to_euler(self, order: EulerRot) -> (f32, f32, f32)
Returns the rotation angles for the given euler rotation sequence.
Sourcepub fn conjugate(self) -> Quat
pub fn conjugate(self) -> Quat
Returns the quaternion conjugate of self. For a unit quaternion the
conjugate is also the inverse.
Sourcepub fn inverse(self) -> Quat
pub fn inverse(self) -> Quat
Returns the inverse of a normalized quaternion.
Typically quaternion inverse returns the conjugate of a normalized quaternion.
Because self is assumed to already be unit length this method does not normalize
before returning the conjugate.
§Panics
Will panic if self is not normalized when glam_assert is enabled.
Sourcepub fn dot(self, rhs: Quat) -> f32
pub fn dot(self, rhs: Quat) -> f32
Computes the dot product of self and rhs. The dot product is
equal to the cosine of the angle between two quaternion rotations.
Sourcepub fn length_squared(self) -> f32
pub fn length_squared(self) -> f32
Computes the squared length of self.
This is generally faster than length() as it avoids a square
root operation.
Sourcepub fn length_recip(self) -> f32
pub fn length_recip(self) -> f32
Computes 1.0 / length().
For valid results, self must not be of length zero.
Sourcepub fn normalize(self) -> Quat
pub fn normalize(self) -> Quat
Returns self normalized to length 1.0.
For valid results, self must not be of length zero.
Panics
Will panic if self is zero length when glam_assert is enabled.
Sourcepub fn is_finite(self) -> bool
pub fn is_finite(self) -> bool
Returns true if, and only if, all elements are finite.
If any element is either NaN, positive or negative infinity, this will return false.
Sourcepub fn is_normalized(self) -> bool
pub fn is_normalized(self) -> bool
Returns whether self of length 1.0 or not.
Uses a precision threshold of 1e-6.
pub fn is_near_identity(self) -> bool
Sourcepub fn angle_between(self, rhs: Quat) -> f32
pub fn angle_between(self, rhs: Quat) -> f32
Returns the angle (in radians) for the minimal rotation between two quaternions
in the range [0, +π].
Both quaternions must be normalized.
§Panics
Will panic if self or rhs are not normalized when glam_assert is enabled.
Sourcepub fn rotate_towards(self, rhs: Quat, max_angle: f32) -> Quat
pub fn rotate_towards(self, rhs: Quat, max_angle: f32) -> Quat
Rotates towards rhs up to max_angle (in radians).
When max_angle is 0.0, the result will be equal to self. When max_angle is equal to
self.angle_between(rhs), the result will be equal to rhs. If max_angle is negative,
rotates towards the exact opposite of rhs. Will not go past the target.
Both quaternions must be normalized.
§Panics
Will panic if self or rhs are not normalized when glam_assert is enabled.
Sourcepub fn abs_diff_eq(self, rhs: Quat, max_abs_diff: f32) -> bool
pub fn abs_diff_eq(self, rhs: Quat, max_abs_diff: f32) -> bool
Returns true if the absolute difference of all elements between self and rhs
is less than or equal to max_abs_diff.
This can be used to compare if two quaternions contain similar elements. It works
best when comparing with a known value. The max_abs_diff that should be used used
depends on the values being compared against.
For more see comparing floating point numbers.
Sourcepub fn lerp(self, end: Quat, s: f32) -> Quat
pub fn lerp(self, end: Quat, s: f32) -> Quat
Performs a linear interpolation between self and rhs based on
the value s.
When s is 0.0, the result will be equal to self. When s
is 1.0, the result will be equal to rhs.
§Panics
Will panic if self or end are not normalized when glam_assert is enabled.
Sourcepub fn slerp(self, end: Quat, s: f32) -> Quat
pub fn slerp(self, end: Quat, s: f32) -> Quat
Performs a spherical linear interpolation between self and end
based on the value s.
When s is 0.0, the result will be equal to self. When s
is 1.0, the result will be equal to end.
§Panics
Will panic if self or end are not normalized when glam_assert is enabled.
Sourcepub fn slerp_long(self, end: Quat, s: f32) -> Quat
pub fn slerp_long(self, end: Quat, s: f32) -> Quat
Performs a spherical linear interpolation between self and end based on the value s,
preserving the rotation direction.
When s is 0.0, the result will be equal to self. When s is 1.0, the result will
be equal to end.
When the dot product of self and end is negative, the standard slerp
will flip the end quaternion to take the shortest path, while this method will take the
longer arc. This is useful when the intended rotation direction must be preserved.
§Panics
Will panic if self or end are not normalized when glam_assert is enabled.
Sourcepub fn mul_vec3(self, rhs: Vec3) -> Vec3
pub fn mul_vec3(self, rhs: Vec3) -> Vec3
Multiplies a quaternion and a 3D vector, returning the rotated vector.
§Panics
Will panic if self is not normalized when glam_assert is enabled.
Sourcepub fn mul_quat(self, rhs: Quat) -> Quat
pub fn mul_quat(self, rhs: Quat) -> Quat
Multiplies two quaternions. If they each represent a rotation, the result will represent the combined rotation.
Note that due to floating point rounding the result may not be perfectly normalized.
§Panics
Will panic if self or rhs are not normalized when glam_assert is enabled.
Sourcepub fn from_affine3(a: &Affine3) -> Quat
pub fn from_affine3(a: &Affine3) -> Quat
Creates a quaternion from a 3x3 rotation matrix inside a 3D affine transform.
Note if the input affine matrix contain scales, shears, or other non-rotation transformations then the resulting quaternion will be ill-defined.
§Panics
Will panic if any input affine matrix column is not normalized when glam_assert is
enabled.
Sourcepub fn from_affine3a(a: &Affine3A) -> Quat
pub fn from_affine3a(a: &Affine3A) -> Quat
Creates a quaternion from a 3x3 rotation matrix inside a 3D affine transform.
Note if the input affine matrix contain scales, shears, or other non-rotation transformations then the resulting quaternion will be ill-defined.
§Panics
Will panic if any input affine matrix column is not normalized when glam_assert is
enabled.
Sourcepub fn mul_vec3a(self, rhs: Vec3A) -> Vec3A
pub fn mul_vec3a(self, rhs: Vec3A) -> Vec3A
Multiplies a quaternion and a 3D vector, returning the rotated vector.
pub fn as_dquat(self) -> DQuat
Trait Implementations§
Source§impl Add for Quat
impl Add for Quat
Source§impl AddAssign for Quat
impl AddAssign for Quat
Source§fn add_assign(&mut self, rhs: Quat)
fn add_assign(&mut self, rhs: Quat)
+= operation. Read moreSource§impl AddAssign<&Quat> for Quat
impl AddAssign<&Quat> for Quat
Source§fn add_assign(&mut self, rhs: &Quat)
fn add_assign(&mut self, rhs: &Quat)
+= operation. Read moreimpl Copy for Quat
Source§impl DivAssign<&f32> for Quat
impl DivAssign<&f32> for Quat
Source§fn div_assign(&mut self, rhs: &f32)
fn div_assign(&mut self, rhs: &f32)
/= operation. Read moreSource§impl DivAssign<f32> for Quat
impl DivAssign<f32> for Quat
Source§fn div_assign(&mut self, rhs: f32)
fn div_assign(&mut self, rhs: f32)
/= operation. Read moreSource§impl Mul for Quat
impl Mul for Quat
Source§fn mul(self, rhs: Quat) -> Quat
fn mul(self, rhs: Quat) -> Quat
Multiplies two quaternions. If they each represent a rotation, the result will represent the combined rotation.
Note that due to floating point rounding the result may not be perfectly normalized.
§Panics
Will panic if self or rhs are not normalized when glam_assert is enabled.
Source§impl MulAssign for Quat
impl MulAssign for Quat
Source§fn mul_assign(&mut self, rhs: Quat)
fn mul_assign(&mut self, rhs: Quat)
*= operation. Read moreSource§impl MulAssign<&Quat> for Quat
impl MulAssign<&Quat> for Quat
Source§fn mul_assign(&mut self, rhs: &Quat)
fn mul_assign(&mut self, rhs: &Quat)
*= operation. Read moreSource§impl MulAssign<&f32> for Quat
impl MulAssign<&f32> for Quat
Source§fn mul_assign(&mut self, rhs: &f32)
fn mul_assign(&mut self, rhs: &f32)
*= operation. Read moreSource§impl MulAssign<f32> for Quat
impl MulAssign<f32> for Quat
Source§fn mul_assign(&mut self, rhs: f32)
fn mul_assign(&mut self, rhs: f32)
*= operation. Read moreimpl Pod for Quat
Source§impl SubAssign for Quat
impl SubAssign for Quat
Source§fn sub_assign(&mut self, rhs: Quat)
fn sub_assign(&mut self, rhs: Quat)
-= operation. Read moreSource§impl SubAssign<&Quat> for Quat
impl SubAssign<&Quat> for Quat
Source§fn sub_assign(&mut self, rhs: &Quat)
fn sub_assign(&mut self, rhs: &Quat)
-= operation. Read moreAuto Trait Implementations§
impl Freeze for Quat
impl RefUnwindSafe for Quat
impl Send for Quat
impl Sync for Quat
impl Unpin for Quat
impl UnsafeUnpin for Quat
impl UnwindSafe for Quat
Blanket Implementations§
impl<T> AnyBitPattern for Twhere
T: Pod,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
impl<T> CheckedBitPattern for Twhere
T: AnyBitPattern,
Source§type Bits = T
type Bits = T
Self must have the same layout as the specified Bits except for
the possible invalid bit patterns being checked during
is_valid_bit_pattern.Source§fn is_valid_bit_pattern(_bits: &T) -> bool
fn is_valid_bit_pattern(_bits: &T) -> bool
bits
as &Self.Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<S, T> Duplex<S> for Twhere
T: FromSample<S> + ToSample<S>,
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more