Skip to content

Commit

Permalink
coord: Use correct calculations for raycasting in ortho projection.
Browse files Browse the repository at this point in the history
  • Loading branch information
heinezen committed Apr 24, 2023
1 parent 13d7293 commit 11432df
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 45 deletions.
47 changes: 9 additions & 38 deletions libopenage/coord/pixel.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2016-2018 the openage authors. See copying.md for legal info.
// Copyright 2016-2023 the openage authors. See copying.md for legal info.

#include "pixel.h"

Expand Down Expand Up @@ -140,47 +140,18 @@ phys3 input::to_phys3(const std::shared_ptr<renderer::camera::Camera> &camera) c
}

scene3 input::to_scene3(const std::shared_ptr<renderer::camera::Camera> &camera) const {
// reverse transformations from world space to screen space
auto view_m = camera->get_view_matrix();
auto proj_m = camera->get_projection_matrix();

int32_t viewport_size_x = camera->get_viewport_size()[0];
int32_t viewport_size_y = camera->get_viewport_size()[1];

// Adjust projection matrix; otherwise the ray direction is wrong (too wide)
// TODO: why? (probably because of orthographic projection)
// this has (presumably) nothing to do with zoom/viewport size
proj_m(0, 0) = proj_m(0, 0) * 20;
proj_m(1, 1) = proj_m(1, 1) * 20;

// screen space -> normalized device coordinates
float mouse_ndc_x = (2.0f * this->x) / viewport_size_x - 1.0f;
float mouse_ndc_y = 1.0f - (2.0f * this->y) / viewport_size_y;
Eigen::Vector4f mouse_ndc = Eigen::Vector4f(
mouse_ndc_x,
mouse_ndc_y,
-1.0f,
1.0f);

// normalized device coordinates -> eye coordinates
Eigen::Vector4f eye = proj_m.inverse() * mouse_ndc;
eye = Eigen::Vector4f(eye.x(), eye.y(), -1.0f, 0.0f);

// eye coordinates -> world coordinates
Eigen::Vector4f world_ray = view_m.inverse() * eye;
Eigen::Vector3f input_ray = Eigen::Vector3f(world_ray.x(), world_ray.y(), world_ray.z());

// direction ray for the input coordinates from camera position
input_ray.normalize();

// calculate intersection with the xz plane
// Use raycasting to find the position
// Direction and origin point are fetched from the camera
auto cam_dir = renderer::camera::cam_direction;
auto ray_origin = camera->get_input_pos(*this);

// xz plane that we want to intersect with
// TODO: Account for different terrain heights (move code somewhere else?)
auto plane_pos = Eigen::Vector3f(0.0f, 0.0f, 0.0f);
auto plane_dir = Eigen::Vector3f(0.0f, 1.0f, 0.0f);

auto cam_pos = camera->get_scene_pos();

// calculate intersection point
Eigen::Vector3f p_intersect = cam_pos + input_ray * ((plane_pos - cam_pos).dot(plane_dir) / input_ray.dot(plane_dir));
Eigen::Vector3f p_intersect = ray_origin + cam_dir * ((plane_pos - ray_origin).dot(plane_dir) / cam_dir.dot(plane_dir));

return scene3(-p_intersect.z(), p_intersect.x(), 0.0f);
}
Expand Down
36 changes: 32 additions & 4 deletions libopenage/renderer/camera/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ const Eigen::Matrix4f &Camera::get_projection_matrix() {
}

// get center of viewport as the focus point
float halfwidth = viewport_size[0] / 2;
float halfheight = viewport_size[1] / 2;
float halfwidth = this->viewport_size[0] / 2;
float halfheight = this->viewport_size[1] / 2;

// get zoom level
float real_zoom = 0.5f * this->default_zoom_ratio * this->zoom;
Expand Down Expand Up @@ -200,8 +200,36 @@ const util::Vector2s &Camera::get_viewport_size() const {
return this->viewport_size;
}

const Eigen::Vector3f &Camera::get_scene_pos() const {
return this->scene_pos;
Eigen::Vector3f Camera::get_input_pos(const coord::input &coord) const {
// calculate up (u) and right (s) vectors for camera
// these define the camera plane in 3D space that the input
// coord exists on
auto direction = cam_direction.normalized();
Eigen::Vector3f eye = this->scene_pos;
Eigen::Vector3f center = this->scene_pos + direction;
Eigen::Vector3f up = Eigen::Vector3f(0.0f, 1.0f, 0.0f);

Eigen::Vector3f f = center - eye;
f.normalize();
Eigen::Vector3f s = f.cross(up);
s.normalize();
Eigen::Vector3f u = s.cross(f);
u.normalize();

// offsets are adjusted by zoom
// this is the same calculation as for the projection matrix
float halfwidth = this->viewport_size[0] / 2;
float halfheight = this->viewport_size[1] / 2;
float real_zoom = 0.5f * this->default_zoom_ratio * this->zoom;

// calculate x and y offset on the camera plane relative to the camera position
float x = +(2.0f * coord.x / this->viewport_size[0] - 1) * (halfwidth * real_zoom);
float y = -(2.0f * coord.y / this->viewport_size[1] - 1) * (halfheight * real_zoom);

// calculate the absolutive position of the input coordinates on the camera plane
Eigen::Vector3f input_pos = this->scene_pos + s * x + u * y;

return input_pos;
}

} // namespace openage::renderer::camera
11 changes: 8 additions & 3 deletions libopenage/renderer/camera/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <eigen3/Eigen/Dense>

#include "coord/pixel.h"
#include "coord/scene.h"
#include "util/vector.h"

Expand Down Expand Up @@ -156,11 +157,15 @@ class Camera {
const util::Vector2s &get_viewport_size() const;

/**
* Get the position of the camera in the 3D scene.
* Get the corresponding 3D position of a 2D input coordinate in the viewport.
* The position is on the plane created by the camera's orthographic projection.
*
* @return Position of the camera in the 3D scene.
* This may be used to get the 3D position of a mouse click and subsequent
* ray casting calculations.
*
* @return Position of the input in the 3D scene.
*/
const Eigen::Vector3f &get_scene_pos() const;
Eigen::Vector3f get_input_pos(const coord::input &coord) const;

private:
/**
Expand Down

0 comments on commit 11432df

Please sign in to comment.