Browse Source

re-add camera and input interfaces

remotes/bxxa/master
cinnaboot 4 years ago
parent
commit
35e61250e9
  1. 64
      include/camera.h
  2. 23
      include/input.h
  3. 4
      include/tangerine.h
  4. 207
      src/camera.cpp
  5. 42
      src/input.cpp

64
include/camera.h

@ -0,0 +1,64 @@
#pragma once
#include <glm/glm.hpp>
#include "types.h"
struct Camera
{
float hAngle;
float vAngle;
glm::vec3 position;
glm::vec3 forward;
glm::vec3 up;
glm::vec3 left;
glm::vec3 target;
glm::vec3 world_up;
glm::mat4 model;
glm::mat4 view;
glm::mat4 projection;
glm::mat4 MVP;
};
// FIXME: we should keep to our convention of passing pointers to structs in
// these interface functions
glm::vec2
cameraUnproject(Camera& cam, int x, int y, int vp_width, int vp_height);
glm::vec3
cameraCreateRay(Camera& cam, glm::ivec2 vp_coords, glm::ivec2 vp_dims);
bool
cameraIntersectPlane(Camera& cam,
glm::vec3 ray,
glm::vec3 plane_origin,
glm::vec3 plane_normal,
glm::vec3& intersection);
void
cameraInitPerspective(Camera* cam,
glm::vec3 position,
glm::vec3 target,
glm::vec3 world_up,
float aspect_ratio = 16.f / 9.f);
void
cameraMove(Camera& cam,
bool up,
bool left,
bool down,
bool right,
bool forward,
bool backward);
void
cameraRotate(Camera& cam, i32 xrel, i32 yrel);
void
cameraRoll(Camera& cam, bool CW, bool CCW);

23
include/input.h

@ -0,0 +1,23 @@
#pragma once
#include <SDL2/SDL.h>
struct InputState
{
bool window_closed;
bool escape;
bool left;
bool right;
bool up;
bool down;
};
void
inputProcessEvent(InputState* is, SDL_Event& e);
// NOTE: convenience function that provides a while(SDL_PollEvents()) loop
void
inputProcessEvents(InputState* is);

4
include/tangerine.h

@ -69,6 +69,7 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include "asset.h" #include "asset.h"
#include "camera.h"
#include "entity.h" #include "entity.h"
#include "dumbLog.h" #include "dumbLog.h"
#include "GLDebug.h" #include "GLDebug.h"
@ -160,9 +161,6 @@ struct LightsBuffer
void* buffer; void* buffer;
}; };
// FIXME: re-implement Camera interface
struct Camera;
struct RenderState struct RenderState
{ {
bool running; bool running;

207
src/camera.cpp

@ -0,0 +1,207 @@
#include <cassert>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#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 NEAR_CLIP_PLANE 20.f
// forward declarations
// interface
void
cameraInitPerspective(Camera* cam,
glm::vec3 position,
glm::vec3 target,
glm::vec3 world_up,
float aspect_ratio)
{
assert(aspect_ratio > 0);
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: get absolute value of relative axis for vAngle component
float 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;
}
// TODO: re-add orthographic camera
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
}
glm::vec2
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);
return glm::vec2(vU.x, vU.y);
}
glm::vec3
cameraCreateRay(Camera& cam, glm::ivec2 vp_coords, glm::ivec2 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 glm::vec3(ray_world.x, ray_world.y, ray_world.z);
}
bool
cameraIntersectPlane(Camera& cam,
glm::vec3 ray,
glm::vec3 plane_origin,
glm::vec3 plane_normal,
glm::vec3& intersection)
{
// NOTE: https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection
float divisor = glm::dot(ray, plane_normal);
if (divisor <= 0.000001f && divisor >= -0.000001f) // NOTE: ray and plane are co-planar
return false;
float distance =
glm::dot((plane_origin - cam.position), plane_normal) / divisor;
glm::vec3 xsect = cam.position + (ray * distance);
intersection = glm::vec3(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, i32 xrel, i32 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

42
src/input.cpp

@ -0,0 +1,42 @@
#include "input.h"
void
inputProcessEvent(InputState* is, SDL_Event& e)
{
switch (e.type) {
case SDL_QUIT:
is->window_closed = true;
break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym) {
case SDLK_ESCAPE: is->escape = true; break;
case SDLK_LEFT: is->left = true; break;
case SDLK_RIGHT: is->right = true; break;
case SDLK_UP: is->up = true; break;
case SDLK_DOWN: is->down = true; break;
}
break;
case SDL_KEYUP:
switch (e.key.keysym.sym) {
case SDLK_ESCAPE: is->escape = false; break;
case SDLK_LEFT: is->left = false; break;
case SDLK_RIGHT: is->right = false; break;
case SDLK_UP: is->up = false; break;
case SDLK_DOWN: is->down = false; break;
}
break;
default: break;
}
}
void
inputProcessEvents(InputState* is)
{
SDL_Event e;
while (SDL_PollEvent(&e))
inputProcessEvent(is, e);
}
Loading…
Cancel
Save