Skip to content

Commit 366efd8

Browse files
committed
Add a spline-following camera transform
1 parent 50fd6e9 commit 366efd8

File tree

5 files changed

+152
-47
lines changed

5 files changed

+152
-47
lines changed

core/src/math/mat.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub trait Compose<Inner: LinearMap>: LinearMap<Source = Inner::Dest> {
4242

4343
/// A change of basis in real vector space of dimension `DIM`.
4444
#[derive(Copy, Clone, Default, Eq, PartialEq)]
45-
pub struct RealToReal<const DIM: usize, SrcBasis = (), DstBasis = ()>(
45+
pub struct RealToReal<const DIM: usize, SrcBasis = (), DstBasis = SrcBasis>(
4646
Pd<(SrcBasis, DstBasis)>,
4747
);
4848

@@ -555,6 +555,12 @@ pub fn orient_z(new_z: Vec3, x: Vec3) -> Mat4x4<RealToReal<3>> {
555555
orient(new_z.cross(&x).normalize(), new_z)
556556
}
557557

558+
pub fn orient_z_y(new_z: Vec3, y: Vec3) -> Mat4x4<RealToReal<3>> {
559+
let new_x = y.cross(&new_z);
560+
let new_y = new_z.cross(&new_x);
561+
Mat4x4::from_basis(new_x, new_y, new_z)
562+
}
563+
558564
/// Constructs a change-of-basis matrix given y and z basis vectors.
559565
///
560566
/// The third basis vector is the cross product of `new_y` and `new_z`.
@@ -565,7 +571,7 @@ pub fn orient_z(new_z: Vec3, x: Vec3) -> Mat4x4<RealToReal<3>> {
565571
/// If `new_y` is approximately parallel to `new_z` and the basis would
566572
/// be degenerate.
567573
#[cfg(feature = "fp")]
568-
fn orient(new_y: Vec3, new_z: Vec3) -> Mat4x4<RealToReal<3>> {
574+
pub fn orient(new_y: Vec3, new_z: Vec3) -> Mat4x4<RealToReal<3>> {
569575
let new_x = new_y.cross(&new_z);
570576
assert!(
571577
!new_x.len_sqr().approx_eq(&0.0),

core/src/math/spline.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ where
112112
co2.mul(t).add(&co1).mul(t).add(&co0).mul(3.0)
113113
}
114114

115+
/// Returns the acceleration vector of `self`
116+
pub fn acceleration(&self, t: f32) -> T::Diff {
117+
let [p0, p1, p2, p3] = &self.0;
118+
let t = t.clamp(0.0, 1.0);
119+
120+
// 6 (3 (p1 - p2) + (p3 - p0)) * t
121+
// + 6 ((p0 - p1 + p2 - p1)
122+
123+
let co1: T::Diff = p1.sub(p2).mul(3.0).add(&p3.sub(p0)).mul(3.0);
124+
let co0: T::Diff = p0.sub(p1).add(&p2.sub(p1));
125+
126+
co1.mul(t).add(&co0).mul(6.0)
127+
}
128+
115129
/// Returns the coefficients used to evaluate the spline.
116130
///
117131
/// These are constant as long as the control points do not change,
@@ -224,6 +238,11 @@ where
224238
CubicBezier(seg).tangent(t)
225239
}
226240

241+
pub fn acceleration(&self, t: f32) -> T::Diff {
242+
let (t, seg) = self.segment(t);
243+
CubicBezier(seg).acceleration(t)
244+
}
245+
227246
fn segment(&self, t: f32) -> (f32, [T; 4]) {
228247
let segs = ((self.0.len() - 1) / 3) as f32;
229248
// TODO use floor and make the code cleaner

core/src/render/cam.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use core::ops::Range;
44

55
use crate::geom::{Tri, Vertex};
66
use crate::math::{
7-
mat::RealToReal, orthographic, perspective, pt2, viewport, Lerp, Mat4x4,
8-
Point3, SphericalVec, Vary,
7+
mat::RealToReal, orient_y, orthographic, perspective, pt2, scale3,
8+
viewport, ApproxEq, BezierSpline, Lerp, Mat4x4, Point3, SphericalVec, Vary,
99
};
1010
use crate::util::{rect::Rect, Dims};
1111

12+
use crate::math::mat::orient_z_y;
1213
#[cfg(feature = "fp")]
1314
use crate::math::{
1415
orient_z, pt3, rotate_x, rotate_y, spherical, translate, turns, Angle, Vec3,
@@ -69,16 +70,21 @@ pub struct Orbit {
6970
pub dir: SphericalVec,
7071
}
7172

73+
pub struct Spline {
74+
pub spline: BezierSpline<Point3<World>>,
75+
pub t: f32,
76+
}
77+
7278
//
7379
// Inherent impls
7480
//
7581

7682
impl Camera<()> {
7783
/// Creates a camera with the given resolution.
78-
pub fn new(dims: Dims) -> Self {
84+
pub fn new(dims @ (w, h): Dims) -> Self {
7985
Self {
8086
dims,
81-
viewport: viewport(pt2(0, 0)..pt2(dims.0, dims.1)),
87+
viewport: viewport(pt2(0, 0)..pt2(w, h)),
8288
..Default::default()
8389
}
8490
}
@@ -297,6 +303,18 @@ impl Transform for Orbit {
297303
}
298304
}
299305

306+
impl Transform for Spline {
307+
fn world_to_view(&self) -> Mat4x4<WorldToView> {
308+
let pos = self.spline.eval(self.t);
309+
let fwd = self.spline.tangent(self.t).normalize();
310+
311+
// Inverse of the view-to-world: (OT)^-1 = T^-1 O^-1
312+
translate(-pos.to_vec().to())
313+
.then(&orient_z_y(fwd.to(), Vec3::Y).transpose())
314+
.to()
315+
}
316+
}
317+
300318
impl Transform for Mat4x4<WorldToView> {
301319
fn world_to_view(&self) -> Mat4x4<WorldToView> {
302320
*self

demos/src/bin/bezier.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn main() {
4141
let vel = VectorsOnUnitDisk;
4242

4343
let mut pos_vels: Vec<(Point2, Vec2)> =
44-
(pos, vel).samples(rng).take(32).collect();
44+
(pos, vel).samples(rng).take(8).collect();
4545

4646
win.run(|Frame { dt, buf, .. }| {
4747
let rays: Vec<Ray<_, _>> = pos_vels
@@ -53,6 +53,19 @@ fn main() {
5353
// Stop once error is less than one pixel
5454
let apx = b.approximate(|err| err.len_sqr() < 1.0);
5555

56+
for t in 0.0.vary_to(1.0, 10) {
57+
let o = b.eval(t);
58+
let d = b.tangent(t) * 0.005;
59+
let n = b.acceleration(t) * 0.0001;
60+
61+
for pt in line([o, o + 50.0 * d]) {
62+
buf.color_buf[pt] = 0xFF_00_FF;
63+
}
64+
for pt in line([o, o + 50.0 * n]) {
65+
buf.color_buf[pt] = 0xFF_FF_00;
66+
}
67+
}
68+
5669
for seg in apx.windows(2) {
5770
for pt in line([seg[0], seg[1]]) {
5871
// The curve can't go out of bounds if the control points don't

demos/src/bin/crates.rs

Lines changed: 89 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
use core::ops::ControlFlow::*;
2+
use re::geom::Ray;
23

34
use re::prelude::*;
45

56
use re::math::color::gray;
7+
use re::math::mat::{orient, orient_z_y, RealToReal};
8+
use re::render::cam::Spline;
69
use re::render::{
710
cam::FirstPerson, shader::Shader, Batch, Camera, ModelToProj,
811
};
912

1013
use re_front::sdl2::Window;
11-
use re_geom::solids::Box;
14+
use re_geom::solids::{Box, Cone};
1215

1316
fn main() {
1417
let mut win = Window::builder()
@@ -32,55 +35,101 @@ fn main() {
3235
},
3336
);
3437

38+
let spline: BezierSpline<Point3<World>> = BezierSpline::from_rays([
39+
Ray(pt3(-12.0, 0.0, 0.0), Vec3::Z.to() * 50.0),
40+
Ray(pt3(0.0, -6.0, 10.0), Vec3::X.to() * 50.0),
41+
Ray(pt3(12.0, -16.0, 0.0), -Vec3::Z.to() * 50.0),
42+
Ray(pt3(10.0, -3.0, -10.0), -Vec3::X.to() * 50.0),
43+
Ray(pt3(-12.0, 0.0, 0.0), Vec3::Z.to() * 50.0),
44+
]);
45+
3546
let (w, h) = win.dims;
3647
let mut cam = Camera::new(win.dims)
37-
.transform(FirstPerson::default())
38-
.viewport((10..w - 10, 10..h - 10))
48+
/*.transform(FirstPerson {
49+
pos: pt3(-20.0, -2.0, 0.0),
50+
heading: Default::default(),
51+
})*/
52+
.transform(Spline { spline: spline.clone(), t: 0.0 })
3953
.perspective(1.0, 0.1..1000.0);
4054

4155
let floor = floor();
4256
let crat = Box::cube(2.0).build();
4357

4458
win.run(|frame| {
45-
// Camera
46-
47-
let mut cam_vel = Vec3::zero();
48-
4959
let ep = &frame.win.ev_pump;
5060

51-
for key in ep.keyboard_state().pressed_scancodes() {
52-
use sdl2::keyboard::Scancode as Sc;
53-
match key {
54-
Sc::W => cam_vel[2] += 4.0,
55-
Sc::S => cam_vel[2] -= 2.0,
56-
Sc::D => cam_vel[0] += 3.0,
57-
Sc::A => cam_vel[0] -= 3.0,
58-
_ => {}
59-
}
60-
}
61-
62-
let ms = ep.relative_mouse_state();
63-
let d_az = turns(ms.x() as f32) * -0.001;
64-
let d_alt = turns(ms.y() as f32) * 0.001;
65-
66-
cam.transform.rotate(d_az, d_alt);
67-
cam.transform
68-
.translate(cam_vel.mul(frame.dt.as_secs_f32()));
61+
// eprint!(
62+
// "\r pos={:.2?} sdir={:.2?} cdir={:.2?}",
63+
// cam.transform.pos,
64+
// cam.transform.heading,
65+
// cam.transform.heading.to_cart::<()>()
66+
// );
6967

70-
let flip = scale3(1.0, -1.0, -1.0).to();
68+
// Camera
69+
// let mut cam_vel = Vec3::zero();
70+
//
71+
// for key in ep.keyboard_state().pressed_scancodes() {
72+
// use sdl2::keyboard::Scancode as Sc;
73+
// match key {
74+
// Sc::W => cam_vel[2] += 4.0,
75+
// Sc::S => cam_vel[2] -= 2.0,
76+
// Sc::D => cam_vel[0] += 3.0,
77+
// Sc::A => cam_vel[0] -= 3.0,
78+
// _ => {}
79+
// }
80+
// }
81+
//
82+
// let ms = ep.relative_mouse_state();
83+
//
84+
// let d_az = turns(ms.x() as f32) * -0.001;
85+
//
86+
// let d_alt = turns(ms.y() as f32) * 0.001;
87+
//
88+
// cam.transform.rotate(d_az, d_alt);
89+
// cam.transform
90+
// .translate(cam_vel.mul(frame.dt.as_secs_f32()));
91+
92+
cam.transform.t = (frame.t.as_secs_f32() * 0.1) % 1.0;
7193

7294
// Render
7395

74-
let world_to_project = flip.then(&cam.world_to_project());
96+
let world_to_project = &cam.world_to_project();
7597

7698
let batch = Batch::new()
7799
.viewport(cam.viewport)
78100
.context(&frame.ctx);
79101

102+
let arrow = Cone {
103+
sectors: 20,
104+
segments: 1,
105+
capped: true,
106+
base_radius: 0.4,
107+
apex_radius: 0.0,
108+
}
109+
.build();
110+
111+
let t = (frame.t.as_secs_f32() * 0.1 + 0.1) % 1.0;
112+
let pos = spline.eval(t);
113+
114+
let fwd = spline.tangent(t).normalize();
115+
116+
let arrow_tf = orient_y(fwd.to(), Vec3::Y)
117+
.then(&translate(pos.to_vec().to()))
118+
.to()
119+
.then(&world_to_project);
120+
121+
batch
122+
.clone()
123+
.mesh(&arrow)
124+
.uniform(&arrow_tf)
125+
.shader(crate_shader)
126+
.target(&mut frame.buf)
127+
.render();
128+
80129
batch
81130
.clone()
82131
.mesh(&floor)
83-
.uniform(&world_to_project)
132+
.uniform(&world_to_project.to())
84133
.shader(floor_shader)
85134
.target(&mut frame.buf)
86135
.render();
@@ -113,19 +162,19 @@ fn main() {
113162
fn floor() -> Mesh<Color3f> {
114163
let mut bld = Mesh::builder();
115164

116-
let size = 50;
117-
for j in -size..=size {
118-
for i in -size..=size {
165+
let (min, max) = (-40, 40);
166+
for j in min..=max {
167+
for i in min..=max {
119168
let even_odd = ((i & 1) ^ (j & 1)) == 1;
120169

121-
let pos = pt3(i as f32, -1.0, j as f32);
170+
let pos = pt3(i as f32, 1.0, j as f32);
122171
let col = if even_odd { gray(0.2) } else { gray(0.9) };
123172
bld.push_vert(pos, col);
124173

125-
if j > -size && i > -size {
126-
let w = size * 2 + 1;
127-
let j = size + j;
128-
let i = size + i;
174+
if j > min && i > min {
175+
let w = (max - min) + 1;
176+
let j = -min + j;
177+
let i = -min + i;
129178
let [a, b, c, d] = [
130179
w * (j - 1) + (i - 1),
131180
w * (j - 1) + i,
@@ -135,11 +184,11 @@ fn floor() -> Mesh<Color3f> {
135184
.map(|i| i as usize);
136185

137186
if even_odd {
138-
bld.push_face(a, c, d);
139-
bld.push_face(a, d, b);
187+
bld.push_face(a, d, c);
188+
bld.push_face(a, b, d);
140189
} else {
141-
bld.push_face(b, c, d);
142-
bld.push_face(b, a, c)
190+
bld.push_face(b, d, c);
191+
bld.push_face(b, c, a)
143192
}
144193
}
145194
}

0 commit comments

Comments
 (0)