You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

193 lines
5.3 KiB

#include <GL/gl3w.h>
#include "camera.h"
// TODO: add these props to scene json
#define MOVE_SPEED 5.f
#define ROTATE_SPEED 0.005f
#define CAMERA_Z_CLAMP_ANGLE 85.f
#define FOV 60.f
#define ASPECT_RATIO 16.f/9.f
#define NEAR_CLIP_PLANE 20.f
// forward declarations
inline glm::vec3 convertv3f(v3f v);
// interface
void
cameraInitPerspective(camera& cam, glm::vec3 position, glm::vec3 target, glm::vec3 world_up)
{
cam.position = position;
cam.target = target;
cam.world_up = world_up;
cam.projection = glm::infinitePerspective(glm::radians(FOV), ASPECT_RATIO, NEAR_CLIP_PLANE);
cam.forward = glm::normalize(target - position);
cam.left = glm::normalize(glm::cross(cam.world_up, cam.forward));
cam.up = glm::normalize(glm::cross(cam.forward, cam.left));
cam.hAngle = glm::atan(cam.forward.x, cam.forward.y);
// NOTE: using pythagoras' to get absolute value of relative axis for vAngle component
real32 len = glm::sqrt(glm::pow(cam.forward.y, 2) + glm::pow(cam.forward.x, 2));
cam.vAngle = glm::atan(cam.forward.z, len);
cam.view = glm::lookAt(cam.position, cam.position + cam.forward, cam.up);
cam.model = glm::mat4(1.0f);
cam.MVP = cam.projection * cam.view * cam.model;
}
void
cameraInitOrthographic(/*camera& cam, */)
{
#if 0
// left, right, bottom, top, zNear, zFar
cam.projection = glm::ortho(0.f, 1280.0f, 0.f, 720.0f, 0.1f, 100.0f);
cam.view = glm::lookAt(
glm::vec3(0.0f, 0.0f, 1.0f), // camera position
glm::vec3(0.0f, 0.0f, 0.0f), // look at position
glm::vec3(0,1,0) // "up" vector
);
cam.model = glm::mat4(1.0f);
cam.MVP = cam.projection * cam.view * cam.model;
#endif
}
v2f
cameraUnproject(camera& cam, int x, int y, int vp_width, int vp_height)
{
// NOTE: using depth buffer may not be as accurate as doing ray-cast
GLfloat depth;
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
glm::vec4 viewport = glm::vec4(0, 0, vp_width, vp_height);
glm::vec3 wincoord = glm::vec3(x, y, depth);
glm::vec3 vU = glm::unProject(wincoord, cam.view, cam.projection, viewport);
v2f v(vU.x, vU.y);
return v;
}
v3f
cameraCreateRay(camera& cam, v2i vp_coords, v2i vp_dims)
{
// NOTE: http://antongerdelan.net/opengl/raycasting.html
float x = 2.f * vp_coords.x / vp_dims.x - 1.f;
float y = 2.f * vp_coords.y / vp_dims.y - 1.f;
glm::vec4 ray_clip = glm::vec4(x, y, -1.f, 1.f);
glm::vec4 ray_eye = glm::inverse(cam.projection) * ray_clip;
ray_eye = glm::vec4(ray_eye.x, ray_eye.y, -1.f, 0); // NOTE: reset as ray
glm::vec4 ray_world = glm::normalize(glm::inverse(cam.view) * ray_eye);
return v3f(ray_world.x, ray_world.y, ray_world.z);
}
bool
cameraIntersectPlane(camera& cam, v3f ray, v3f plane_origin, v3f plane_normal, v3f& intersection)
{
// NOTE: https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection
glm::vec3 c_o = cam.position;
glm::vec3 r = convertv3f(ray);
glm::vec3 p_o = convertv3f(plane_origin);
glm::vec3 p_n = convertv3f(plane_normal);
float divisor = glm::dot(r, p_n);
if (divisor <= 0.000001f && divisor >= -0.000001f) // NOTE: ray and plane are co-planar
return false;
float distance = glm::dot((p_o - c_o), p_n) / divisor;
glm::vec3 xsect = c_o + (r * distance);
intersection = v3f(xsect.x, xsect.y, xsect.z);
return true;
}
void
cameraMove(camera& cam, bool up, bool left, bool down, bool right, bool forward, bool backward)
{
if (!up && !left && !down && !right && !forward && !backward)
return;
glm::vec3 f = cam.forward;
glm::vec3 u = cam.up;
glm::vec3 old = cam.position;
glm::vec3 &p = cam.position;
glm::vec3 v(0.f); // normalized direction
// TODO: still seems like we're adding magnitude when moving in 2 directions
#if 0
if (forward) v = glm::normalize(v + f);
if (backward) v = glm::normalize(v - f);
if (up) v = glm::normalize(v + u);
if (down) v = glm::normalize(v - u);
if (left) v -= glm::normalize(glm::cross(f, u));
if (right) v -= glm::normalize(glm::cross(u, f));
#else
if (forward) v += f;
if (backward) v -= f;
if (up) v += u;
if (down) v -= u;
if (left) v -= glm::cross(f, u);
if (right) v -= glm::cross(u, f);
#endif
p += (v * MOVE_SPEED);
glm::vec3 diff = old - p;
cam.view = glm::translate(cam.view, diff);
cam.MVP = cam.projection * cam.view * cam.model;
}
void
cameraRotate(camera& cam, int32 xrel, int32 yrel)
{
float &h = cam.hAngle;
float &v = cam.vAngle;
h += ROTATE_SPEED * xrel;
v -= ROTATE_SPEED * yrel;
// clamp vAngle to prevent gimbal lock
float a = glm::radians(CAMERA_Z_CLAMP_ANGLE);
if (v < (-1 * a)) v = (-1 * a);
if (v > a) v = a;
cam.forward = glm::vec3(
glm::cos(v) * glm::sin(h),
glm::cos(v) * glm::cos(h),
glm::sin(v)
);
glm::normalize(cam.forward);
cam.left = glm::normalize(glm::cross(cam.forward, cam.world_up));
cam.up = glm::normalize(glm::cross(cam.left, cam.forward));
cam.view = glm::lookAt(cam.position, cam.position + cam.forward, cam.up);
cam.MVP = cam.projection * cam.view * cam.model;
}
void
cameraRoll(camera& cam, bool CW, bool CCW)
{
if ((!CW && !CCW) || (CW && CCW))
return;
float a = 0.005f;
if (CW) a *= 1;
if (CCW) a *= -1;
glm::mat4 m = glm::rotate(glm::mat4(1.f), a, cam.forward);
glm::vec4 v(cam.up.x, cam.up.y, cam.up.z, 0);
v = v * m;
cam.up = glm::vec3(v.x, v.y, v.z);
cam.view *= m;
cam.MVP = cam.projection * cam.view * cam.model;
}
// internal
inline glm::vec3
convertv3f(v3f v)
{
return glm::vec3(v.x, v.y, v.z);
}