symmetry
changeset 0:a90a71a74f0b
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Tue, 25 Feb 2014 19:53:34 +0200 |
parents | |
children | 46fe847bba08 |
files | .hgignore Makefile src/camera.cc src/camera.h src/main.cc src/opengl.cc src/opengl.h src/vr.h src/vr/vr.cc src/vr/vr_impl.h src/vr/vr_sdr.h |
diffstat | 11 files changed, 1179 insertions(+), 0 deletions(-) [+] |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/.hgignore Tue Feb 25 19:53:34 2014 +0200 1.3 @@ -0,0 +1,10 @@ 1.4 +\.o$ 1.5 +\.d$ 1.6 +\.swp$ 1.7 +\.suo$ 1.8 +\.obj$ 1.9 +\.exe$ 1.10 +Debug/ 1.11 +Release/ 1.12 +\.user$ 1.13 +symmetry$
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/Makefile Tue Feb 25 19:53:34 2014 +0200 2.3 @@ -0,0 +1,31 @@ 2.4 +src = $(wildcard src/*.cc) $(wildcard src/vr/*.cc) 2.5 +csrc = $(wildcard src/*.c) $(wildcard src/vr/*.c) 2.6 +obj = $(src:.cc=.o) $(csrc:.c=.o) 2.7 +dep = $(obj:.o=.d) 2.8 +bin = symmetry 2.9 + 2.10 +CFLAGS = -pedantic -Wall -g -Isrc 2.11 +CXXFLAGS = $(CFLAGS) 2.12 +LDFLAGS = $(libgl) $(libovr) -lvmath -limago -lm 2.13 + 2.14 +ifeq ($(shell uname -s), Darwin) 2.15 + libgl = -framework OpenGL -framework GLUT -lGLEW 2.16 +else 2.17 + libgl = -lX11 -lGL -lGLU -lglut -lGLEW 2.18 + libovr = -lXinerama -lovr -lpthread -ludev 2.19 +endif 2.20 + 2.21 +$(bin): $(obj) 2.22 + $(CXX) -o $@ $(obj) $(LDFLAGS) 2.23 + 2.24 +-include $(dep) 2.25 + 2.26 +%.d: %.c 2.27 + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ 2.28 + 2.29 +%.d: %.cc 2.30 + @$(CPP) $(CXXFLAGS) $< -MM -MT $(@:.d=.o) >$@ 2.31 + 2.32 +.PHONY: clean 2.33 +clean: 2.34 + rm -f $(obj) $(bin) $(dep)
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/src/camera.cc Tue Feb 25 19:53:34 2014 +0200 3.3 @@ -0,0 +1,240 @@ 3.4 +#include "opengl.h" 3.5 +#include "camera.h" 3.6 +//#include "unistate.h" 3.7 +#include "vr.h" 3.8 + 3.9 +using namespace goatgfx; 3.10 + 3.11 +Camera::Camera() 3.12 +{ 3.13 + roll = 0.0; 3.14 + inval_cache(); 3.15 +} 3.16 + 3.17 +Camera::~Camera() 3.18 +{ 3.19 +} 3.20 + 3.21 +void Camera::calc_inv_matrix(Matrix4x4 *mat) const 3.22 +{ 3.23 + *mat = matrix().inverse(); 3.24 +} 3.25 + 3.26 +const Matrix4x4 &Camera::matrix() const 3.27 +{ 3.28 + if(!mcache.valid) { 3.29 + calc_matrix(&mcache.mat); 3.30 + mcache.valid = true; 3.31 + } 3.32 + return mcache.mat; 3.33 +} 3.34 + 3.35 +const Matrix4x4 &Camera::inv_matrix() const 3.36 +{ 3.37 + if(!mcache_inv.valid) { 3.38 + calc_inv_matrix(&mcache_inv.mat); 3.39 + mcache_inv.valid = true; 3.40 + } 3.41 + return mcache_inv.mat; 3.42 +} 3.43 + 3.44 +void Camera::use() const 3.45 +{ 3.46 + //set_view_matrix(matrix()); 3.47 + mult_matrix(matrix()); 3.48 +} 3.49 + 3.50 +void Camera::use_inverse() const 3.51 +{ 3.52 + //set_view_matrix(inv_matrix()); 3.53 + mult_matrix(inv_matrix()); 3.54 +} 3.55 + 3.56 +void Camera::input_move(float x, float y, float z) 3.57 +{ 3.58 +} 3.59 + 3.60 +void Camera::input_rotate(float x, float y, float z) 3.61 +{ 3.62 +} 3.63 + 3.64 +void Camera::input_zoom(float factor) 3.65 +{ 3.66 +} 3.67 + 3.68 + 3.69 +// ---- orbit camera ---- 3.70 + 3.71 +OrbitCamera::OrbitCamera() 3.72 +{ 3.73 + theta = 0.0; 3.74 + phi = 0.0; 3.75 + rad = 10.0; 3.76 +} 3.77 + 3.78 +OrbitCamera::~OrbitCamera() 3.79 +{ 3.80 +} 3.81 + 3.82 +void OrbitCamera::calc_matrix(Matrix4x4 *mat) const 3.83 +{ 3.84 + mat->reset_identity(); 3.85 + mat->translate(Vector3(0, 0, -rad)); 3.86 + mat->rotate(Vector3(phi, 0, 0)); 3.87 + mat->rotate(Vector3(0, theta, 0)); 3.88 + mat->rotate(Vector3(0, 0, roll)); 3.89 +} 3.90 + 3.91 +void OrbitCamera::calc_inv_matrix(Matrix4x4 *mat) const 3.92 +{ 3.93 + mat->reset_identity(); 3.94 + mat->rotate(Vector3(0, 0, roll)); 3.95 + mat->rotate(Vector3(0, theta, 0)); 3.96 + mat->rotate(Vector3(phi, 0, 0)); 3.97 + mat->translate(Vector3(0, 0, -rad)); 3.98 +} 3.99 + 3.100 +void OrbitCamera::input_rotate(float x, float y, float z) 3.101 +{ 3.102 + theta += y; 3.103 + phi += x; 3.104 + roll += z; 3.105 + 3.106 + if(phi < -M_PI / 2) 3.107 + phi = -M_PI / 2; 3.108 + if(phi > M_PI) 3.109 + phi = M_PI; 3.110 + 3.111 + inval_cache(); 3.112 +} 3.113 + 3.114 +void OrbitCamera::input_zoom(float factor) 3.115 +{ 3.116 + rad += factor; 3.117 + if(rad < 0.0) 3.118 + rad = 0.0; 3.119 + 3.120 + inval_cache(); 3.121 +} 3.122 + 3.123 +void FpsCamera::calc_matrix(Matrix4x4 *mat) const 3.124 +{ 3.125 + mat->reset_identity(); 3.126 + mat->translate(Vector3(pos.x, pos.y, pos.z)); 3.127 + mat->rotate(Vector3(0, theta, 0)); 3.128 + mat->rotate(Vector3(phi, 0, 0)); 3.129 + mat->rotate(Vector3(0, 0, roll)); 3.130 +} 3.131 + 3.132 +void FpsCamera::calc_inv_matrix(Matrix4x4 *mat) const 3.133 +{ 3.134 + mat->reset_identity(); 3.135 + mat->rotate(Vector3(0, 0, roll)); 3.136 + mat->rotate(Vector3(phi, 0, 0)); 3.137 + mat->rotate(Vector3(0, theta, 0)); 3.138 + mat->translate(Vector3(-pos.x, -pos.y, -pos.z)); 3.139 +} 3.140 + 3.141 +void FpsCamera::input_move(float x, float y, float z) 3.142 +{ 3.143 + pos.x += x * cos(theta) - z * sin(theta); 3.144 + pos.z += x * sin(theta) + z * cos(theta); 3.145 + pos.y += y; 3.146 + inval_cache(); 3.147 +} 3.148 + 3.149 +const Vector3 &FpsCamera::get_position() const 3.150 +{ 3.151 + return pos; 3.152 +} 3.153 + 3.154 + 3.155 +FlyCamera::FlyCamera() 3.156 +{ 3.157 + pos.z = 10.0f; 3.158 +} 3.159 + 3.160 +void FlyCamera::calc_matrix(Matrix4x4 *mat) const 3.161 +{ 3.162 + Matrix3x3 rmat = rot.get_rotation_matrix().transposed(); 3.163 + Matrix4x4 tmat; 3.164 + tmat.set_translation(pos); 3.165 + *mat = tmat * Matrix4x4(rmat); 3.166 +} 3.167 + 3.168 +/*void FlyCamera::calc_inv_matrix(Matrix4x4 *mat) const 3.169 +{ 3.170 +}*/ 3.171 + 3.172 +const Vector3 &FlyCamera::get_position() const 3.173 +{ 3.174 + return pos; 3.175 +} 3.176 + 3.177 +const Quaternion &FlyCamera::get_rotation() const 3.178 +{ 3.179 + return rot; 3.180 +} 3.181 + 3.182 +void FlyCamera::input_move(float x, float y, float z) 3.183 +{ 3.184 + static const Vector3 vfwd(0, 0, 1), vright(1, 0, 0); 3.185 + 3.186 + Vector3 k = vfwd.transformed(rot); 3.187 + Vector3 i = vright.transformed(rot); 3.188 + Vector3 j = cross_product(k, i); 3.189 + 3.190 + pos += i * x + j * y + k * z; 3.191 + inval_cache(); 3.192 +} 3.193 + 3.194 +void FlyCamera::input_rotate(float x, float y, float z) 3.195 +{ 3.196 + Vector3 axis(x, y, z); 3.197 + float axis_len = axis.length(); 3.198 + rot.rotate(axis / axis_len, axis_len); 3.199 + rot.normalize(); 3.200 + 3.201 + inval_cache(); 3.202 +} 3.203 + 3.204 + 3.205 +// --- VR additions --- 3.206 +VRFpsCamera::VRFpsCamera() 3.207 +{ 3.208 + neck_eye_dist = 0.14; // default neck->eye distance 14cm 3.209 +} 3.210 + 3.211 +void VRFpsCamera::calc_matrix(Matrix4x4 *mat) const 3.212 +{ 3.213 + mat->reset_identity(); 3.214 + mat->translate(Vector3(pos.x, pos.y, pos.z)); 3.215 + mat->rotate(Vector3(0, theta, 0)); 3.216 + mat->rotate(Vector3(phi, 0, 0)); 3.217 + mat->rotate(Vector3(0, 0, roll)); 3.218 + mat->translate(Vector3(0, neck_eye_dist, 0)); 3.219 +} 3.220 + 3.221 +void VRFpsCamera::calc_inv_matrix(Matrix4x4 *mat) const 3.222 +{ 3.223 + mat->reset_identity(); 3.224 + mat->translate(Vector3(0, -neck_eye_dist, 0)); 3.225 + mat->rotate(Vector3(0, 0, roll)); 3.226 + mat->rotate(Vector3(phi, 0, 0)); 3.227 + mat->rotate(Vector3(0, theta, 0)); 3.228 + mat->translate(Vector3(-pos.x, -pos.y, -pos.z)); 3.229 +} 3.230 + 3.231 +void VRFpsCamera::track_vr() 3.232 +{ 3.233 + float euler[3]; 3.234 + vr_get_rotation_euler(euler); 3.235 + 3.236 + // input_rotate invalidates cache 3.237 + input_rotate(prev_angles[0] - euler[0], prev_angles[1] - euler[1], prev_angles[2] - euler[2]); 3.238 + 3.239 + prev_angles[0] = euler[0]; 3.240 + prev_angles[1] = euler[1]; 3.241 + prev_angles[2] = euler[2]; 3.242 +} 3.243 +
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/camera.h Tue Feb 25 19:53:34 2014 +0200 4.3 @@ -0,0 +1,102 @@ 4.4 +#ifndef CAMERA_H_ 4.5 +#define CAMERA_H_ 4.6 + 4.7 +#include <vmath/vmath.h> 4.8 + 4.9 +namespace goatgfx { 4.10 + 4.11 +class Camera { 4.12 +protected: 4.13 + float roll; 4.14 + 4.15 + mutable struct { 4.16 + bool valid; 4.17 + Matrix4x4 mat; 4.18 + } mcache, mcache_inv; 4.19 + 4.20 + virtual void calc_matrix(Matrix4x4 *mat) const = 0; 4.21 + virtual void calc_inv_matrix(Matrix4x4 *mat) const; 4.22 + 4.23 + void inval_cache() { mcache.valid = mcache_inv.valid = false; } 4.24 + void set_glmat(const Matrix4x4 &m) const; 4.25 + 4.26 +public: 4.27 + Camera(); 4.28 + virtual ~Camera(); 4.29 + 4.30 + const Matrix4x4 &matrix() const; 4.31 + const Matrix4x4 &inv_matrix() const; 4.32 + 4.33 + void use() const; 4.34 + void use_inverse() const; 4.35 + 4.36 + // these do nothing, override to provide input handling 4.37 + virtual void input_move(float x, float y, float z); 4.38 + virtual void input_rotate(float x, float y, float z); 4.39 + virtual void input_zoom(float factor); 4.40 +}; 4.41 + 4.42 +class OrbitCamera : public Camera { 4.43 +protected: 4.44 + float theta, phi, rad; 4.45 + 4.46 + void calc_matrix(Matrix4x4 *mat) const; 4.47 + void calc_inv_matrix(Matrix4x4 *mat) const; 4.48 + 4.49 +public: 4.50 + OrbitCamera(); 4.51 + virtual ~OrbitCamera(); 4.52 + 4.53 + void input_rotate(float x, float y, float z); 4.54 + void input_zoom(float factor); 4.55 +}; 4.56 + 4.57 +class FpsCamera : public OrbitCamera { 4.58 +protected: 4.59 + Vector3 pos; 4.60 + 4.61 + void calc_matrix(Matrix4x4 *mat) const; 4.62 + void calc_inv_matrix(Matrix4x4 *mat) const; 4.63 + 4.64 +public: 4.65 + void input_move(float x, float y, float z); 4.66 + 4.67 + const Vector3 &get_position() const; 4.68 +}; 4.69 + 4.70 +class FlyCamera : public Camera { 4.71 +private: 4.72 + Vector3 pos; 4.73 + Quaternion rot; 4.74 + 4.75 + void calc_matrix(Matrix4x4 *mat) const; 4.76 + //void calc_inv_matrix(Matrix4x4 *mat) const; 4.77 + 4.78 +public: 4.79 + FlyCamera(); 4.80 + 4.81 + const Vector3 &get_position() const; 4.82 + const Quaternion &get_rotation() const; 4.83 + 4.84 + void input_move(float x, float y, float z); 4.85 + void input_rotate(float x, float y, float z); 4.86 +}; 4.87 + 4.88 + 4.89 +class VRFpsCamera : public FpsCamera { 4.90 +private: 4.91 + float neck_eye_dist; 4.92 + float prev_angles[3]; 4.93 + 4.94 + void calc_matrix(Matrix4x4 *mat) const; 4.95 + void calc_inv_matrix(Matrix4x4 *mat) const; 4.96 + 4.97 +public: 4.98 + VRFpsCamera(); 4.99 + 4.100 + void track_vr(); 4.101 +}; 4.102 + 4.103 +} // namespace goatgfx 4.104 + 4.105 +#endif // CAMERA_H_
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/main.cc Tue Feb 25 19:53:34 2014 +0200 5.3 @@ -0,0 +1,174 @@ 5.4 +#include <stdio.h> 5.5 +#include <stdlib.h> 5.6 +#include <assert.h> 5.7 +#include <vector> 5.8 +#include "opengl.h" 5.9 +#include "camera.h" 5.10 + 5.11 +static bool init(); 5.12 +static void cleanup(); 5.13 +static void handle_input(float dt); 5.14 +static void disp(); 5.15 +static void idle(); 5.16 +static void reshape(int x, int y); 5.17 +static void keypress(unsigned char key, int x, int y); 5.18 +static void keyrelease(unsigned char key, int x, int y); 5.19 +static void mouse(int bn, int st, int x, int y); 5.20 +static void motion(int x, int y); 5.21 + 5.22 +static goatgfx::VRFpsCamera cam; 5.23 +static std::vector<bool> keystate(256); 5.24 + 5.25 +int main(int argc, char **argv) 5.26 +{ 5.27 + glutInitWindowSize(800, 600); 5.28 + glutInit(&argc, argv); 5.29 + glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); 5.30 + glutCreateWindow("symmetry"); 5.31 + 5.32 + glutDisplayFunc(disp); 5.33 + glutIdleFunc(idle); 5.34 + glutReshapeFunc(reshape); 5.35 + glutKeyboardFunc(keypress); 5.36 + glutKeyboardUpFunc(keyrelease); 5.37 + glutMouseFunc(mouse); 5.38 + glutMotionFunc(motion); 5.39 + 5.40 + if(!init()) { 5.41 + return 1; 5.42 + } 5.43 + atexit(cleanup); 5.44 + 5.45 + glutMainLoop(); 5.46 + return 0; 5.47 +} 5.48 + 5.49 + 5.50 +static bool init() 5.51 +{ 5.52 + glewInit(); 5.53 + 5.54 + glEnable(GL_DEPTH_TEST); 5.55 + glEnable(GL_CULL_FACE); 5.56 + 5.57 + glEnable(GL_LIGHTING); 5.58 + glEnable(GL_LIGHT0); 5.59 + 5.60 + cam.input_move(0, 1.65, 0); 5.61 + 5.62 + return true; 5.63 +} 5.64 + 5.65 +static void cleanup() 5.66 +{ 5.67 +} 5.68 + 5.69 +static void disp() 5.70 +{ 5.71 + unsigned int msec = glutGet(GLUT_ELAPSED_TIME); 5.72 + static unsigned int prev_msec; 5.73 + float dt = (float)(msec - prev_msec) / 1000.0f; 5.74 + 5.75 + handle_input(dt); 5.76 + 5.77 + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 5.78 + 5.79 + glMatrixMode(GL_MODELVIEW); 5.80 + glLoadIdentity(); 5.81 + cam.use_inverse(); 5.82 + 5.83 + glBegin(GL_QUADS); 5.84 + glNormal3f(0, 1, 0); 5.85 + glVertex3f(-5, 0, 5); 5.86 + glVertex3f(5, 0, 5); 5.87 + glVertex3f(5, 0, -5); 5.88 + glVertex3f(-5, 0, -5); 5.89 + glEnd(); 5.90 + 5.91 + glFrontFace(GL_CW); 5.92 + glutSolidTeapot(1.0); 5.93 + glFrontFace(GL_CCW); 5.94 + 5.95 + glutSwapBuffers(); 5.96 + assert(glGetError() == GL_NO_ERROR); 5.97 +} 5.98 + 5.99 + 5.100 +static void handle_input(float dt) 5.101 +{ 5.102 + Vector3 inpv; 5.103 + float offs = dt * 2.0; 5.104 + 5.105 + if(keystate['w'] || keystate['W']) { 5.106 + inpv.z -= offs; 5.107 + } 5.108 + if(keystate['s'] || keystate['S']) { 5.109 + inpv.z += offs; 5.110 + } 5.111 + if(keystate['d'] || keystate['D']) { 5.112 + inpv.x += offs; 5.113 + } 5.114 + if(keystate['a'] || keystate['A']) { 5.115 + inpv.x -= offs; 5.116 + } 5.117 + 5.118 + cam.input_move(inpv.x, inpv.y, inpv.z); 5.119 +} 5.120 + 5.121 +static void idle() 5.122 +{ 5.123 + glutPostRedisplay(); 5.124 +} 5.125 + 5.126 +static void reshape(int x, int y) 5.127 +{ 5.128 + glViewport(0, 0, x, y); 5.129 + 5.130 + glMatrixMode(GL_PROJECTION); 5.131 + glLoadIdentity(); 5.132 + gluPerspective(45.0, (float)x / (float)y, 0.5, 500.0); 5.133 +} 5.134 + 5.135 +static void keypress(unsigned char key, int x, int y) 5.136 +{ 5.137 + keystate[key] = true; 5.138 + 5.139 + switch(key) { 5.140 + case 27: 5.141 + exit(0); 5.142 + } 5.143 +} 5.144 + 5.145 +static void keyrelease(unsigned char key, int x, int y) 5.146 +{ 5.147 + keystate[key] = false; 5.148 +} 5.149 + 5.150 +static int prev_x, prev_y; 5.151 +static bool bnstate[32]; 5.152 + 5.153 +static void mouse(int bn, int st, int x, int y) 5.154 +{ 5.155 + prev_x = x; 5.156 + prev_y = y; 5.157 + bnstate[bn - GLUT_LEFT_BUTTON] = st == GLUT_DOWN; 5.158 +} 5.159 + 5.160 +static void motion(int x, int y) 5.161 +{ 5.162 + int dx = x - prev_x; 5.163 + int dy = y - prev_y; 5.164 + prev_x = x; 5.165 + prev_y = y; 5.166 + 5.167 + if(!dx && !dy) { 5.168 + return; 5.169 + } 5.170 + 5.171 + if(bnstate[0]) { 5.172 + float dtheta_deg = dy * 0.5; 5.173 + float dphi_deg = dx * 0.5; 5.174 + 5.175 + cam.input_rotate(DEG_TO_RAD(dtheta_deg), DEG_TO_RAD(dphi_deg), 0); 5.176 + } 5.177 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/src/opengl.cc Tue Feb 25 19:53:34 2014 +0200 6.3 @@ -0,0 +1,61 @@ 6.4 +#include "opengl.h" 6.5 +#include <vmath/vmath.h> 6.6 + 6.7 +void load_matrix(const Matrix4x4 &m) 6.8 +{ 6.9 +#ifdef SINGLE_PRECISION_MATH 6.10 + if(glLoadTransposeMatrixfARB) { 6.11 + glLoadTransposeMatrixfARB((float*)&m); 6.12 + } else { 6.13 + Matrix4x4 tmat = m.transposed(); 6.14 + glLoadMatrixf((float*)&tmat); 6.15 + } 6.16 +#else 6.17 + if(glLoadTransposeMatrixdARB) { 6.18 + glLoadTransposeMatrixdARB((double*)&m); 6.19 + } else { 6.20 + Matrix4x4 tmat = m.transposed(); 6.21 + glLoadMatrixd((double*)&tmat); 6.22 + } 6.23 +#endif 6.24 +} 6.25 + 6.26 +void mult_matrix(const Matrix4x4 &m) 6.27 +{ 6.28 +#ifdef SINGLE_PRECISION_MATH 6.29 + if(glMultTransposeMatrixfARB) { 6.30 + glMultTransposeMatrixfARB((float*)&m); 6.31 + } else { 6.32 + Matrix4x4 tmat = m.transposed(); 6.33 + glMultMatrixf((float*)&tmat); 6.34 + } 6.35 +#else 6.36 + if(glMultTransposeMatrixdARB) { 6.37 + glMultTransposeMatrixdARB((double*)&m); 6.38 + } else { 6.39 + Matrix4x4 tmat = m.transposed(); 6.40 + glMultMatrixd((double*)&tmat); 6.41 + } 6.42 +#endif 6.43 +} 6.44 + 6.45 +const char *strglerr(int err) 6.46 +{ 6.47 + static const char *errnames[] = { 6.48 + "GL_INVALID_ENUM", 6.49 + "GL_INVALID_VALUE", 6.50 + "GL_INVALID_OPERATION", 6.51 + "GL_STACK_OVERFLOW", 6.52 + "GL_STACK_UNDERFLOW", 6.53 + "GL_OUT_OF_MEMORY", 6.54 + "GL_INVALID_FRAMEBUFFER_OPERATION" 6.55 + }; 6.56 + 6.57 + if(!err) { 6.58 + return "GL_NO_ERROR"; 6.59 + } 6.60 + if(err < GL_INVALID_ENUM || err > GL_OUT_OF_MEMORY) { 6.61 + return "<invalid gl error>"; 6.62 + } 6.63 + return errnames[err - GL_INVALID_ENUM]; 6.64 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/opengl.h Tue Feb 25 19:53:34 2014 +0200 7.3 @@ -0,0 +1,29 @@ 7.4 +#ifndef OPENGL_H_ 7.5 +#define OPENGL_H_ 7.6 + 7.7 +#include <GL/glew.h> 7.8 + 7.9 +#ifndef __APPLE__ 7.10 +#include <GL/glut.h> 7.11 +#else 7.12 +#include <GLUT/glut.h> 7.13 +#endif 7.14 + 7.15 +#define CHECKGLERR \ 7.16 + do { \ 7.17 + int err = glGetError(); \ 7.18 + if(err) { \ 7.19 + fprintf(stderr, "%s:%d: OpenGL error 0x%x: %s\n", __FILE__, __LINE__, err, strglerr(err)); \ 7.20 + abort(); \ 7.21 + } \ 7.22 + } while(0) 7.23 + 7.24 + 7.25 +class Matrix4x4; 7.26 + 7.27 +void load_matrix(const Matrix4x4 &m); 7.28 +void mult_matrix(const Matrix4x4 &m); 7.29 + 7.30 +const char *strglerr(int err); 7.31 + 7.32 +#endif /* OPENGL_H_ */
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/src/vr.h Tue Feb 25 19:53:34 2014 +0200 8.3 @@ -0,0 +1,63 @@ 8.4 +#ifndef GOAT3DGFX_VR_H_ 8.5 +#define GOAT3DGFX_VR_H_ 8.6 + 8.7 +/* VR mode init options */ 8.8 +enum vr_init_mode { 8.9 + VR_INIT_NONE, 8.10 + VR_INIT_OCULUS, 8.11 + VR_INIT_STEREO 8.12 +}; 8.13 + 8.14 +/* possible eye values */ 8.15 +enum { 8.16 + VR_EYE_CENTER, 8.17 + VR_EYE_LEFT, 8.18 + VR_EYE_RIGHT 8.19 +}; 8.20 + 8.21 +#ifdef __cplusplus 8.22 +extern "C" { 8.23 +#endif 8.24 + 8.25 +int vr_init(enum vr_init_mode mode); 8.26 +void vr_shutdown(void); 8.27 + 8.28 +const char *vr_get_display_name(void); 8.29 +void vr_get_display_pos(int *xptr, int *yptr); 8.30 + 8.31 +int vr_get_width(void); 8.32 +int vr_get_height(void); 8.33 + 8.34 +float vr_get_fov(void); 8.35 +float vr_get_aspect(void); 8.36 + 8.37 +void vr_set_eyedist(float ipd); 8.38 +float vr_get_eyedist(void); 8.39 + 8.40 +/* expects an array of 4 barrel distortion coefficients: 8.41 + * polar scale: k_0 + k_1 r^2 + k_2 r^4 + k_3 r^6 8.42 + */ 8.43 +void vr_set_distort(const float *coef); 8.44 +void vr_get_distort(float *coef); 8.45 + 8.46 +void vr_set_prediction_sec(float dt); 8.47 +float vr_get_prediction_sec(void); 8.48 + 8.49 +void vr_get_view_matrix(float *res, int eye); 8.50 +void vr_get_proj_matrix(float *res, int eye); 8.51 + 8.52 +/* expects an array of at least 3 floats (x, y, z, offset). */ 8.53 +void vr_get_translation(float *offs); 8.54 +/* expects an array of at least 4 floats (x, y, z, w, quaternion). */ 8.55 +void vr_get_rotation(float *quat); 8.56 +/* expects an array of at least 3 floats (pitch, yaw, roll, angles). */ 8.57 +void vr_get_rotation_euler(float *euler); 8.58 + 8.59 +/* OpenGL stuff */ 8.60 +void vr_draw_eye(int eye, unsigned int tex, float tex_scale_x, float tex_scale_y); 8.61 + 8.62 +#ifdef __cplusplus 8.63 +} 8.64 +#endif 8.65 + 8.66 +#endif /* GOAT3DGFX_VR_H_ */
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/src/vr/vr.cc Tue Feb 25 19:53:34 2014 +0200 9.3 @@ -0,0 +1,388 @@ 9.4 +#include <stdio.h> 9.5 +#include <GL/glew.h> 9.6 +#include "vr.h" 9.7 +#include "vr_impl.h" 9.8 +#include "vr_sdr.h" 9.9 + 9.10 +#ifndef WIN32 9.11 +#include <alloca.h> 9.12 +#else 9.13 +#include <malloc.h> 9.14 +#endif 9.15 + 9.16 +static void init_ctx(); 9.17 +static bool init_ovr(); 9.18 +static bool init_sdr(); 9.19 + 9.20 +VRContext vr_ctx; 9.21 +static unsigned int sdrprog; 9.22 + 9.23 +extern "C" int vr_init(enum vr_init_mode mode) 9.24 +{ 9.25 + init_ctx(); 9.26 + 9.27 + if(!init_ovr()) { 9.28 + return -1; 9.29 + } 9.30 + 9.31 + if(!init_sdr()) { 9.32 + return -1; 9.33 + } 9.34 + 9.35 + return 0; 9.36 +} 9.37 + 9.38 +extern "C" void vr_shutdown(void) 9.39 +{ 9.40 + delete [] vr_ctx.info.display; 9.41 + delete vr_ctx.ovr_sfusion; 9.42 + //System::Destroy(); 9.43 + 9.44 + memset(&vr_ctx, 0, sizeof vr_ctx); 9.45 +} 9.46 + 9.47 +static void init_ctx() 9.48 +{ 9.49 + vr_ctx.info.width = 1280; 9.50 + vr_ctx.info.height = 800; 9.51 + vr_ctx.info.fov = M_PI / 2.0; 9.52 + vr_ctx.info.aspect = (float)vr_ctx.info.width / (float)vr_ctx.info.height; 9.53 + vr_ctx.info.ipd = 0.035; 9.54 + vr_ctx.info.scale = 1.0; 9.55 +} 9.56 + 9.57 +static bool init_ovr() 9.58 +{ 9.59 + LogMaskConstants log_level = LogMask_All; 9.60 + // initialize Oculus SDK 9.61 + const char *logenv = getenv("VR_LOGLEVEL"); 9.62 + if(logenv) { 9.63 + switch(atoi(logenv)) { 9.64 + case 0: 9.65 + log_level = LogMask_None; 9.66 + break; 9.67 + case 1: 9.68 + log_level = LogMask_Regular; 9.69 + break; 9.70 + case 2: 9.71 + default: 9.72 + log_level = LogMask_All; 9.73 + break; 9.74 + } 9.75 + } 9.76 + 9.77 + System::Init(Log::ConfigureDefaultLog(log_level)); 9.78 + if(!(vr_ctx.ovr_devman = DeviceManager::Create())) { 9.79 + fprintf(stderr, "failed to create OVR device manager\n"); 9.80 + return false; 9.81 + } 9.82 + 9.83 + // create the display device 9.84 + HMDInfo info; 9.85 + if(!(vr_ctx.ovr_hmd_dev = vr_ctx.ovr_devman->EnumerateDevices<HMDDevice>().CreateDevice())) { 9.86 + fprintf(stderr, "no oculus rift devices found\n"); 9.87 + } else { 9.88 + if(vr_ctx.ovr_hmd_dev->GetDeviceInfo(&info)) { 9.89 + printf("oculus device info:\n"); 9.90 + printf(" name: %s\n", info.DisplayDeviceName); 9.91 + printf(" ipd: %f\n", info.InterpupillaryDistance); 9.92 + printf(" distortion: %f %f %f %f\n", info.DistortionK[0], 9.93 + info.DistortionK[1], info.DistortionK[2], info.DistortionK[3]); 9.94 + } 9.95 + 9.96 + // calculate and store viewing parameters 9.97 + vr_ctx.info.width = info.HResolution; 9.98 + vr_ctx.info.height = info.VResolution; 9.99 + vr_ctx.info.aspect = (float)vr_ctx.info.width / (float)vr_ctx.info.height; 9.100 + 9.101 + vr_ctx.info.ipd = info.InterpupillaryDistance; 9.102 + for(int i=0; i<4; i++) { 9.103 + vr_ctx.info.distort[i] = info.DistortionK[i]; 9.104 + } 9.105 + 9.106 + Util::Render::StereoConfig stereohelp; 9.107 + stereohelp.SetFullViewport(Util::Render::Viewport(0, 0, vr_ctx.info.width, vr_ctx.info.height)); 9.108 + stereohelp.SetStereoMode(Util::Render::Stereo_LeftRight_Multipass); 9.109 + stereohelp.SetHMDInfo(info); 9.110 + stereohelp.SetDistortionFitPointVP(-1.0, 0.0); 9.111 + 9.112 + vr_ctx.info.scale = stereohelp.GetDistortionScale(); 9.113 + 9.114 + float vhalfsz = vr_ctx.info.scale * info.VScreenSize * 0.5; 9.115 + vr_ctx.info.fov = 2.0 * atan(vhalfsz / info.EyeToScreenDistance); 9.116 + 9.117 + vr_ctx.info.lens_center_offset = 0.5 - info.LensSeparationDistance / info.HScreenSize; 9.118 + 9.119 + // calculate center of projection shift to match the lens positions 9.120 + float center_dist_meters = info.HScreenSize * 0.25; 9.121 + float proj_shift = center_dist_meters - info.LensSeparationDistance * 0.5; 9.122 + vr_ctx.info.proj_center_offset = 4.0 * proj_shift / info.HScreenSize; 9.123 + 9.124 + // grab the display info 9.125 + vr_ctx.info.display = new char[strlen(info.DisplayDeviceName) + 1]; 9.126 + strcpy(vr_ctx.info.display, info.DisplayDeviceName); 9.127 + 9.128 + vr_ctx.info.display_xoffs = info.DesktopX; 9.129 + vr_ctx.info.display_yoffs = info.DesktopY; 9.130 + 9.131 + printf("display: \"%s\" offset: %+d %+d\n", vr_ctx.info.display, 9.132 + vr_ctx.info.display_xoffs, vr_ctx.info.display_yoffs); 9.133 + } 9.134 + 9.135 + // get the sensor device 9.136 + if(vr_ctx.ovr_hmd_dev) { 9.137 + if(!(vr_ctx.ovr_sensor_dev = vr_ctx.ovr_hmd_dev->GetSensor())) { 9.138 + fprintf(stderr, "failed to get oculus sensor device\n"); 9.139 + } 9.140 + } else { 9.141 + if(!(vr_ctx.ovr_sensor_dev = vr_ctx.ovr_devman->EnumerateDevices<SensorDevice>().CreateDevice())) { 9.142 + fprintf(stderr, "failed to get oculus sensor device\n"); 9.143 + } 9.144 + } 9.145 + 9.146 + if(vr_ctx.ovr_sensor_dev) { 9.147 + SensorInfo sinfo; 9.148 + if(vr_ctx.ovr_sensor_dev->GetDeviceInfo(&sinfo)) { 9.149 + printf("oculus sensor device info:\n"); 9.150 + printf(" name: %s\n", sinfo.ProductName); 9.151 + } 9.152 + 9.153 + vr_ctx.ovr_sfusion = new SensorFusion; 9.154 + vr_ctx.ovr_sfusion->AttachToSensor(vr_ctx.ovr_sensor_dev); 9.155 + } 9.156 + return true; 9.157 +} 9.158 + 9.159 +#undef EXTERNAL_SHADER 9.160 + 9.161 +static bool init_sdr() 9.162 +{ 9.163 + int status; 9.164 + 9.165 +#ifdef EXTERNAL_SHADER 9.166 + FILE *fp = fopen("sdr/sdr.glsl", "rb"); 9.167 + if(!fp) { 9.168 + perror("failed to load sdr.glsl"); 9.169 + return false; 9.170 + } 9.171 + fseek(fp, 0, SEEK_END); 9.172 + long sz = ftell(fp); 9.173 + rewind(fp); 9.174 + 9.175 + char *buf = (char*)alloca(sz + 1); 9.176 + fread(buf, 1, sz, fp); 9.177 + buf[sz] = 0; 9.178 + sdr_src = buf; 9.179 + 9.180 + fclose(fp); 9.181 +#endif 9.182 + 9.183 + unsigned int sdr = glCreateShader(GL_FRAGMENT_SHADER); 9.184 + glShaderSource(sdr, 1, &sdr_src, 0); 9.185 + glCompileShader(sdr); 9.186 + glGetShaderiv(sdr, GL_COMPILE_STATUS, &status); 9.187 + if(!status) { 9.188 + fprintf(stderr, "failed to compile distortion shader\n"); 9.189 + 9.190 + int loglen; 9.191 + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &loglen); 9.192 + 9.193 + if(loglen > 0) { 9.194 + char *log = (char*)alloca(loglen); 9.195 + glGetShaderInfoLog(sdr, loglen, &loglen, log); 9.196 + fprintf(stderr, "%s\n", log); 9.197 + } 9.198 + 9.199 + return false; 9.200 + } 9.201 + 9.202 + sdrprog = glCreateProgram(); 9.203 + glAttachShader(sdrprog, sdr); 9.204 + glLinkProgram(sdrprog); 9.205 + if(!status) { 9.206 + fprintf(stderr, "failed to link distortion shader program\n"); 9.207 + glDeleteShader(sdr); 9.208 + return false; 9.209 + } 9.210 + 9.211 + int loc; 9.212 + 9.213 + glUseProgram(sdrprog); 9.214 + 9.215 + if((loc = glGetUniformLocation(sdrprog, "tex")) != -1) { 9.216 + glUniform1i(loc, 0); 9.217 + } 9.218 + if((loc = glGetUniformLocation(sdrprog, "lens_center_offset")) != -1) { 9.219 + glUniform1f(loc, 0); 9.220 + } 9.221 + if((loc = glGetUniformLocation(sdrprog, "scr_center")) != -1) { 9.222 + glUniform2f(loc, 0, 0); 9.223 + } 9.224 + if((loc = glGetUniformLocation(sdrprog, "scale")) != -1) { 9.225 + glUniform1f(loc, vr_ctx.info.scale); 9.226 + } 9.227 + if((loc = glGetUniformLocation(sdrprog, "aspect")) != -1) { 9.228 + printf("setting aspect: %f\n", vr_ctx.info.aspect / 2.0); 9.229 + glUniform1f(loc, vr_ctx.info.aspect / 2.0); 9.230 + } 9.231 + if((loc = glGetUniformLocation(sdrprog, "scale_in")) != -1) { 9.232 + glUniform2f(loc, 1, 1); 9.233 + } 9.234 + if((loc = glGetUniformLocation(sdrprog, "dist_factors")) != -1) { 9.235 + glUniform4f(loc, vr_ctx.info.distort[0], vr_ctx.info.distort[1], 9.236 + vr_ctx.info.distort[2], vr_ctx.info.distort[3]); 9.237 + } 9.238 + 9.239 + return true; 9.240 +} 9.241 + 9.242 +extern "C" const char *vr_get_display_name(void) 9.243 +{ 9.244 + return vr_ctx.info.display; 9.245 +} 9.246 + 9.247 +extern "C" void vr_get_display_pos(int *xptr, int *yptr) 9.248 +{ 9.249 + *xptr = vr_ctx.info.display_xoffs; 9.250 + *yptr = vr_ctx.info.display_yoffs; 9.251 +} 9.252 + 9.253 +extern "C" int vr_get_width(void) 9.254 +{ 9.255 + return vr_ctx.info.width; 9.256 +} 9.257 + 9.258 +extern "C" int vr_get_height(void) 9.259 +{ 9.260 + return vr_ctx.info.height; 9.261 +} 9.262 + 9.263 +extern "C" float vr_get_fov(void) 9.264 +{ 9.265 + return vr_ctx.info.fov; 9.266 +} 9.267 + 9.268 +extern "C" float vr_get_aspect(void) 9.269 +{ 9.270 + return vr_ctx.info.aspect; 9.271 +} 9.272 + 9.273 +extern "C" void vr_set_eyedist(float ipd) 9.274 +{ 9.275 + vr_ctx.info.ipd = ipd; 9.276 +} 9.277 + 9.278 +extern "C" float vr_get_eyedist(void) 9.279 +{ 9.280 + return vr_ctx.info.ipd; 9.281 +} 9.282 + 9.283 +extern "C" void vr_set_distort(const float *coef) 9.284 +{ 9.285 + memcpy(vr_ctx.info.distort, coef, sizeof vr_ctx.info.distort); 9.286 +} 9.287 + 9.288 +extern "C" void vr_get_distort(float *coef) 9.289 +{ 9.290 + memcpy(coef, vr_ctx.info.distort, sizeof vr_ctx.info.distort); 9.291 +} 9.292 + 9.293 +extern "C" void vr_set_prediction_sec(float dt) 9.294 +{ 9.295 + vr_ctx.ovr_sfusion->SetPrediction(dt); 9.296 +} 9.297 + 9.298 +extern "C" float vr_get_prediction_sec(void) 9.299 +{ 9.300 + return vr_ctx.ovr_sfusion->GetPredictionDelta(); 9.301 +} 9.302 + 9.303 +extern "C" void vr_get_view_matrix(float *res, int eye) 9.304 +{ 9.305 + // TODO 9.306 +} 9.307 + 9.308 +extern "C" void vr_get_proj_matrix(float *res, int eye) 9.309 +{ 9.310 + static float eye_scale[] = {0.0, 1.0, -1.0}; 9.311 + 9.312 + Matrix4f proj = Matrix4f::PerspectiveRH(vr_ctx.info.fov, vr_ctx.info.aspect / 2.0, 0.3, 1000.0); 9.313 + proj = Matrix4f::Translation(vr_ctx.info.proj_center_offset * eye_scale[eye], 0, 0) * proj; 9.314 + 9.315 + memcpy(res, proj.M[0], 16 * sizeof(float)); 9.316 +} 9.317 + 9.318 +extern "C" void vr_get_translation(float *offs) 9.319 +{ 9.320 + // current oculus devkit doesn't do translation 9.321 + offs[0] = offs[1] = offs[2] = 0.0f; 9.322 +} 9.323 + 9.324 +extern "C" void vr_get_rotation(float *quat) 9.325 +{ 9.326 + Quatf oq = vr_ctx.ovr_sfusion->GetPredictedOrientation(); 9.327 + quat[0] = oq.x; 9.328 + quat[1] = oq.y; 9.329 + quat[2] = oq.z; 9.330 + quat[3] = oq.w; 9.331 +} 9.332 + 9.333 +extern "C" void vr_get_rotation_euler(float *euler) 9.334 +{ 9.335 + Quatf oq = vr_ctx.ovr_sfusion->GetPredictedOrientation(); 9.336 + oq.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(euler + 1, euler, euler + 2); 9.337 +} 9.338 + 9.339 +extern "C" void vr_draw_eye(int eye, unsigned int tex, float tex_scale_x, float tex_scale_y) 9.340 +{ 9.341 + static const float rects[3][4] = { 9.342 + {-1, -1, 1, 1}, 9.343 + {-1, -1, 0, 1}, 9.344 + {0, -1, 1, 1} 9.345 + }; 9.346 + static const float offs_scale[3] = {0.0, -1.0, 1.0}; 9.347 + 9.348 + glPushAttrib(GL_ENABLE_BIT); 9.349 + glDisable(GL_DEPTH_TEST); 9.350 + glDisable(GL_LIGHTING); 9.351 + glEnable(GL_TEXTURE_2D); 9.352 + 9.353 + glMatrixMode(GL_MODELVIEW); 9.354 + glPushMatrix(); 9.355 + glLoadIdentity(); 9.356 + 9.357 + glMatrixMode(GL_PROJECTION); 9.358 + glPushMatrix(); 9.359 + glLoadIdentity(); 9.360 + 9.361 + glUseProgram(sdrprog); 9.362 + 9.363 + if(sdrprog) { 9.364 + int loc; 9.365 + if((loc = glGetUniformLocation(sdrprog, "lens_center_offset")) != -1) { 9.366 + float offset = vr_ctx.info.lens_center_offset * offs_scale[eye]; 9.367 + glUniform1f(loc, offset); 9.368 + } 9.369 + 9.370 + if((loc = glGetUniformLocation(sdrprog, "tex_scale")) != -1) { 9.371 + glUniform2f(loc, tex_scale_x, tex_scale_y); 9.372 + } 9.373 + } 9.374 + 9.375 + glBindTexture(GL_TEXTURE_2D, tex); 9.376 + glBegin(GL_QUADS); 9.377 + glColor4f(1, 1, 1, 1); 9.378 + glTexCoord2f(0, 0); glVertex2f(rects[eye][0], rects[eye][1]); 9.379 + glTexCoord2f(1, 0); glVertex2f(rects[eye][2], rects[eye][1]); 9.380 + glTexCoord2f(1, 1); glVertex2f(rects[eye][2], rects[eye][3]); 9.381 + glTexCoord2f(0, 1); glVertex2f(rects[eye][0], rects[eye][3]); 9.382 + glEnd(); 9.383 + 9.384 + glUseProgram(0); 9.385 + 9.386 + glPopMatrix(); 9.387 + glMatrixMode(GL_MODELVIEW); 9.388 + glPopMatrix(); 9.389 + 9.390 + glPopAttrib(); 9.391 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/src/vr/vr_impl.h Tue Feb 25 19:53:34 2014 +0200 10.3 @@ -0,0 +1,36 @@ 10.4 +#ifndef VR_IMPL_H_ 10.5 +#define VR_IMPL_H_ 10.6 + 10.7 +#include <OVR.h> 10.8 + 10.9 +using namespace OVR; 10.10 + 10.11 +struct VRContext { 10.12 + DeviceManager *ovr_devman; 10.13 + HMDDevice *ovr_hmd_dev; 10.14 + SensorDevice *ovr_sensor_dev; 10.15 + SensorFusion *ovr_sfusion; 10.16 + 10.17 + struct { 10.18 + char *display; 10.19 + int display_xoffs, display_yoffs; 10.20 + 10.21 + // the full width and height of the display (both eyes) 10.22 + int width, height; 10.23 + float fov; 10.24 + // the full aspect ratio of the display (both eyes) 10.25 + float aspect; 10.26 + float ipd; 10.27 + float distort[4]; 10.28 + // the right lens center offset (negate for left) 10.29 + float lens_center_offset; 10.30 + float proj_center_offset; 10.31 + float scale; // scaling to be applied to the two views to fill the screen 10.32 + } info; 10.33 +}; 10.34 + 10.35 +extern VRContext vr_ctx; 10.36 + 10.37 +bool vr_gl_init(); 10.38 + 10.39 +#endif // VR_IMPL_H_
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/src/vr/vr_sdr.h Tue Feb 25 19:53:34 2014 +0200 11.3 @@ -0,0 +1,45 @@ 11.4 +static const char *sdr_src = 11.5 + "uniform sampler2D tex;\n" 11.6 + "uniform float aspect, scale;\n" 11.7 + "uniform float lens_center_offset;\n" 11.8 + "uniform vec4 dist_factors;\n" 11.9 + "\n" 11.10 + "vec2 distort_texcoords(in vec2 tc);\n" 11.11 + "float barrel_scale(float x, in vec4 k);\n" 11.12 + "\n" 11.13 + "void main()\n" 11.14 + "{\n" 11.15 + " vec2 tc = distort_texcoords(gl_TexCoord[0].xy);\n" 11.16 + "\n" 11.17 + " float vis = any(greaterThan(tc, vec2(1.0)) || lessThan(tc, vec2(0.0))) ? 0.0 : 1.0;\n" 11.18 + "\n" 11.19 + " gl_FragColor.rgb = texture2D(tex, tc).rgb * vis;\n" 11.20 + " gl_FragColor.a = 1.0;\n" 11.21 + "}\n" 11.22 + "\n" 11.23 + "vec2 distort_texcoords(in vec2 tc)\n" 11.24 + "{\n" 11.25 + " // map tc [0, 1] -> [-1, 1]\n" 11.26 + " vec2 pt = tc * 2.0 - 1.0;\n" 11.27 + "\n" 11.28 + " pt.x += lens_center_offset * 2.0;\n" 11.29 + " pt.y /= aspect; // correct for aspect ratio\n" 11.30 + "\n" 11.31 + " float rad = barrel_scale(dot(pt, pt), dist_factors);\n" 11.32 + " pt *= rad; // scale the point by the computer distortion radius\n" 11.33 + "\n" 11.34 + " pt /= scale;\n" 11.35 + " pt.y *= aspect;\n" 11.36 + " pt.x -= lens_center_offset * 2.0;\n" 11.37 + "\n" 11.38 + " // map back to range [0, 1]\n" 11.39 + " return pt * 0.5 + 0.5;\n" 11.40 + "}\n" 11.41 + "\n" 11.42 + "float barrel_scale(float rad, in vec4 k)\n" 11.43 + "{\n" 11.44 + " float radsq = rad * rad;\n" 11.45 + " float radquad = radsq * radsq;\n" 11.46 + " return k.x + k.y * radsq + k.z * radquad + k.w * radquad * radsq;\n" 11.47 + "}\n"; 11.48 +