Skip to content

Commit 4ab90ae

Browse files
Add support for zoom relative depth planes
1 parent 20bfe42 commit 4ab90ae

File tree

1 file changed

+85
-26
lines changed

1 file changed

+85
-26
lines changed

src/camera.rs

Lines changed: 85 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ pub struct Camera {
240240
projection_type: ProjectionType,
241241
z_near: f32,
242242
z_far: f32,
243+
zoom_relative_depth: bool,
243244
position: Vec3,
244245
target: Vec3,
245246
up: Vec3,
@@ -259,10 +260,11 @@ impl Camera {
259260
height: f32,
260261
z_near: f32,
261262
z_far: f32,
263+
zoom_relative_depth: bool,
262264
) -> Self {
263265
let mut camera = Camera::new(viewport);
264266
camera.set_view(position, target, up);
265-
camera.set_orthographic_projection(height, z_near, z_far);
267+
camera.set_orthographic_projection(height, z_near, z_far, zoom_relative_depth);
266268
camera
267269
}
268270

@@ -277,15 +279,17 @@ impl Camera {
277279
field_of_view_y: impl Into<Radians>,
278280
z_near: f32,
279281
z_far: f32,
282+
zoom_relative_depth: bool,
280283
) -> Self {
281284
let mut camera = Camera::new(viewport);
282285
camera.set_view(position, target, up);
283-
camera.set_perspective_projection(field_of_view_y, z_near, z_far);
286+
camera.set_perspective_projection(field_of_view_y, z_near, z_far, zoom_relative_depth);
284287
camera
285288
}
286289

287290
///
288291
/// New camera which projects the world with a general planar projection.
292+
/// This is best used with the relative depth unit if zooming is allowed.
289293
///
290294
pub fn new_planar(
291295
viewport: Viewport,
@@ -295,10 +299,11 @@ impl Camera {
295299
field_of_view_y: impl Into<Radians>,
296300
z_near: f32,
297301
z_far: f32,
302+
zoom_relative_depth: bool,
298303
) -> Self {
299304
let mut camera = Camera::new(viewport);
300305
camera.set_view(position, target, up);
301-
camera.set_planar_projection(field_of_view_y, z_near, z_far);
306+
camera.set_planar_projection(field_of_view_y, z_near, z_far, zoom_relative_depth);
302307
camera
303308
}
304309

@@ -308,13 +313,20 @@ impl Camera {
308313
pub fn set_perspective_projection(
309314
&mut self,
310315
field_of_view_y: impl Into<Radians>,
311-
z_near: f32,
312-
z_far: f32,
316+
mut z_near: f32,
317+
mut z_far: f32,
318+
zoom_relative_depth: bool,
313319
) {
314320
self.z_near = z_near;
315321
self.z_far = z_far;
322+
self.zoom_relative_depth = zoom_relative_depth;
316323
let field_of_view_y = field_of_view_y.into();
317324
self.projection_type = ProjectionType::Perspective { field_of_view_y };
325+
if zoom_relative_depth {
326+
let zoom = self.position.distance(self.target);
327+
z_near *= zoom;
328+
z_far *= zoom;
329+
}
318330
self.projection =
319331
cgmath::perspective(field_of_view_y, self.viewport.aspect(), z_near, z_far);
320332
}
@@ -326,13 +338,24 @@ impl Camera {
326338
/// The view frustum depth is `z_near` to `z_far`.
327339
/// All of the above values are scaled by the zoom factor which is one over the distance between the camera position and target.
328340
///
329-
pub fn set_orthographic_projection(&mut self, height: f32, z_near: f32, z_far: f32) {
341+
pub fn set_orthographic_projection(
342+
&mut self,
343+
height: f32,
344+
mut z_near: f32,
345+
mut z_far: f32,
346+
zoom_relative_depth: bool,
347+
) {
330348
self.projection_type = ProjectionType::Orthographic { height };
331349
self.z_near = z_near;
332350
self.z_far = z_far;
351+
self.zoom_relative_depth = zoom_relative_depth;
333352
let zoom = self.position.distance(self.target);
334353
let height = zoom * height;
335354
let width = height * self.viewport.aspect();
355+
if zoom_relative_depth {
356+
z_near *= zoom;
357+
z_far *= zoom;
358+
}
336359
self.projection = cgmath::ortho(
337360
-0.5 * width,
338361
0.5 * width,
@@ -345,35 +368,33 @@ impl Camera {
345368

346369
///
347370
/// Specify the camera to use planar projection with the given field of view in the y-direction and near and far plane.
348-
/// This can be either a planar or perspective projection depending on the field of view provided, which is permitted to be zero or negative.
371+
/// This can be either an orthographic or perspective projection depending on the field of view provided, which is permitted to be zero or negative.
372+
/// This is best used with the relative depth unit if zooming is allowed.
349373
///
350374
pub fn set_planar_projection(
351375
&mut self,
352376
field_of_view_y: impl Into<Radians>,
353377
mut z_near: f32,
354378
mut z_far: f32,
379+
zoom_relative_depth: bool,
355380
) {
356381
self.z_near = z_near;
357382
self.z_far = z_far;
383+
self.zoom_relative_depth = zoom_relative_depth;
358384
let field_of_view_y = field_of_view_y.into();
359385
self.projection_type = ProjectionType::Planar { field_of_view_y };
360386
let depth = self.position.distance(self.target);
361387
let height = 2.0 * depth;
362-
let focal = -Rad::cot(field_of_view_y / 2.0) * depth;
363-
z_near -= depth;
364-
z_far -= depth;
365-
// Required to ensure near/far plane does not cross focal point when at close zoom levels
366-
if focal < 0.0 && z_near < focal {
367-
z_near = focal + 0.001;
368-
} else if focal > 0.0 && z_far > focal {
369-
z_far = focal - 0.001;
388+
if zoom_relative_depth {
389+
z_near *= depth;
390+
z_far *= depth;
370391
}
371392
self.projection = planar(
372393
field_of_view_y,
373394
self.viewport.aspect(),
374395
height,
375-
z_near,
376-
z_far,
396+
z_near - depth,
397+
z_far - depth,
377398
) * Mat4::from_translation(vec3(0.0, 0.0, depth));
378399
}
379400

@@ -386,13 +407,28 @@ impl Camera {
386407
self.viewport = viewport;
387408
match self.projection_type {
388409
ProjectionType::Orthographic { height } => {
389-
self.set_orthographic_projection(height, self.z_near, self.z_far);
410+
self.set_orthographic_projection(
411+
height,
412+
self.z_near,
413+
self.z_far,
414+
self.zoom_relative_depth,
415+
);
390416
}
391417
ProjectionType::Perspective { field_of_view_y } => {
392-
self.set_perspective_projection(field_of_view_y, self.z_near, self.z_far);
418+
self.set_perspective_projection(
419+
field_of_view_y,
420+
self.z_near,
421+
self.z_far,
422+
self.zoom_relative_depth,
423+
);
393424
}
394425
ProjectionType::Planar { field_of_view_y } => {
395-
self.set_planar_projection(field_of_view_y, self.z_near, self.z_far);
426+
self.set_planar_projection(
427+
field_of_view_y,
428+
self.z_near,
429+
self.z_far,
430+
self.zoom_relative_depth,
431+
);
396432
}
397433
}
398434
true
@@ -415,13 +451,28 @@ impl Camera {
415451
self.up,
416452
);
417453
match self.projection_type {
418-
ProjectionType::Orthographic { height } => {
419-
self.set_orthographic_projection(height, self.z_near, self.z_far)
420-
}
421-
ProjectionType::Planar { field_of_view_y } => {
422-
self.set_planar_projection(field_of_view_y, self.z_near, self.z_far)
454+
ProjectionType::Perspective { field_of_view_y } => {
455+
if self.zoom_relative_depth {
456+
self.set_perspective_projection(
457+
field_of_view_y,
458+
self.z_near,
459+
self.z_far,
460+
self.zoom_relative_depth,
461+
);
462+
}
423463
}
424-
_ => {}
464+
ProjectionType::Orthographic { height } => self.set_orthographic_projection(
465+
height,
466+
self.z_near,
467+
self.z_far,
468+
self.zoom_relative_depth,
469+
),
470+
ProjectionType::Planar { field_of_view_y } => self.set_planar_projection(
471+
field_of_view_y,
472+
self.z_near,
473+
self.z_far,
474+
self.zoom_relative_depth,
475+
),
425476
};
426477
}
427478

@@ -578,6 +629,13 @@ impl Camera {
578629
self.z_far
579630
}
580631

632+
///
633+
/// Returns if the near and far planes are calculated relative to the current zoom level.
634+
///
635+
pub fn zoom_relative_depth(&self) -> bool {
636+
self.zoom_relative_depth
637+
}
638+
581639
///
582640
/// Returns the position of this camera.
583641
///
@@ -627,6 +685,7 @@ impl Camera {
627685
projection_type: ProjectionType::Orthographic { height: 1.0 },
628686
z_near: 0.0,
629687
z_far: 0.0,
688+
zoom_relative_depth: false,
630689
position: vec3(0.0, 0.0, 5.0),
631690
target: vec3(0.0, 0.0, 0.0),
632691
up: vec3(0.0, 1.0, 0.0),

0 commit comments

Comments
 (0)