1 /* This file is part of Dilay 2 * Copyright © 2015-2018 Alexander Bau 3 * Use and redistribute under the terms of the GNU General Public License 4 */ 5 #include <glm/gtc/matrix_transform.hpp> 6 #include <glm/gtx/matrix_major_storage.hpp> 7 #include <glm/gtx/quaternion.hpp> 8 #include "camera.hpp" 9 #include "config.hpp" 10 #include "dimension.hpp" 11 #include "intersection.hpp" 12 #include "opengl.hpp" 13 #include "primitive/plane.hpp" 14 #include "primitive/ray.hpp" 15 #include "renderer.hpp" 16 #include "util.hpp" 17 18 struct Camera::Impl 19 { 20 Camera* self; 21 Renderer renderer; 22 glm::vec3 gazePoint; 23 glm::vec3 toEyePoint; 24 glm::vec3 right; 25 glm::mat4x4 projection; 26 glm::mat4x4 view; 27 glm::mat4x4 viewRotation; 28 glm::uvec2 resolution; 29 float nearClipping; 30 float farClipping; 31 float fieldOfView; 32 ImplCamera::Impl33 Impl (Camera* s, const Config& config) 34 : self (s) 35 , renderer (config) 36 , resolution (config.get<int> ("window/initial-width"), 37 config.get<int> ("window/initial-height")) 38 { 39 this->set (glm::vec3 (0.0f, 0.0f, 0.0f), glm::vec3 (0.0f, 0.0f, 6.0f)); 40 this->runFromConfig (config); 41 } 42 upCamera::Impl43 const glm::vec3& up () const 44 { 45 static const glm::vec3 up (0.0f, 1.0f, 0.0f); 46 return up; 47 } 48 realUpCamera::Impl49 glm::vec3 realUp () const { return glm::normalize (glm::cross (this->toEyePoint, this->right)); } 50 positionCamera::Impl51 glm::vec3 position () const { return this->gazePoint + this->toEyePoint; } 52 worldCamera::Impl53 glm::mat4x4 world () const 54 { 55 const glm::vec3 x = this->right; 56 const glm::vec3 z = glm::normalize (this->toEyePoint); 57 const glm::vec3 y = glm::cross (z, x); 58 const glm::vec3 p = this->position (); 59 60 return glm::colMajor4 (glm::vec4 (x, 0.0f), glm::vec4 (y, 0.0f), glm::vec4 (z, 0.0f), 61 glm::vec4 (p, 1.0f)); 62 } 63 updateResolutionCamera::Impl64 void updateResolution (const glm::uvec2& dimension) 65 { 66 this->resolution = dimension; 67 this->updateProjection (); 68 } 69 setModelViewProjectionCamera::Impl70 void setModelViewProjection (const glm::mat4x4& model, const glm::mat3x3& modelNormal, 71 bool onlyRotation) 72 { 73 this->renderer.setModel (&model[0][0], &modelNormal[0][0]); 74 this->renderer.setProjection (&this->projection[0][0]); 75 76 if (onlyRotation) 77 { 78 this->renderer.setView (&this->viewRotation[0][0]); 79 } 80 else 81 { 82 this->renderer.setView (&this->view[0][0]); 83 } 84 } 85 setCamera::Impl86 void set (const glm::vec3& g, const glm::vec3& e) 87 { 88 this->gazePoint = g; 89 this->toEyePoint = e; 90 91 if (Util::colinear (e, this->up ())) 92 { 93 this->right = glm::vec3 (1.0f, 0.0f, 0.0f); 94 } 95 else 96 { 97 this->right = glm::normalize (glm::cross (this->up (), this->toEyePoint)); 98 } 99 this->updateView (); 100 } 101 setGazeCamera::Impl102 void setGaze (const glm::vec3& g) 103 { 104 this->gazePoint = g; 105 this->updateView (); 106 } 107 stepAlongGazeCamera::Impl108 void stepAlongGaze (float factor) 109 { 110 constexpr float minD = 0.01f; 111 constexpr float maxD = 1000.0f; 112 113 const float d = glm::length (this->toEyePoint); 114 const float newD = d * factor; 115 116 if (minD <= newD && newD <= maxD) 117 { 118 this->toEyePoint *= glm::vec3 (factor); 119 } 120 this->updateView (); 121 } 122 verticalRotationCamera::Impl123 void verticalRotation (float angle) 124 { 125 const glm::quat q = glm::angleAxis (angle, this->up ()); 126 this->right = glm::rotate (q, this->right); 127 this->toEyePoint = glm::rotate (q, this->toEyePoint); 128 this->updateView (); 129 } 130 horizontalRotationCamera::Impl131 void horizontalRotation (float angle) 132 { 133 const glm::quat q = glm::angleAxis (angle, this->right); 134 this->toEyePoint = glm::rotate (q, this->toEyePoint); 135 this->updateView (); 136 } 137 viewportCamera::Impl138 glm::vec4 viewport () const { return glm::vec4 (0, 0, this->resolution.x, this->resolution.y); } 139 fromWorldCamera::Impl140 glm::vec2 fromWorld (const glm::vec3& p, const glm::mat4x4& model, bool onlyRotation) const 141 { 142 const glm::mat4x4 mv = onlyRotation ? this->viewRotation * model : this->view * model; 143 const glm::vec3 w = glm::project (p, mv, this->projection, this->viewport ()); 144 145 return glm::vec2 (w.x, float(resolution.y) - w.y); 146 } 147 toWorldCamera::Impl148 glm::vec3 toWorld (const glm::ivec2& p, float z = 0.0f) const 149 { 150 const float invY = this->resolution.y - float(p.y); 151 const float normZ = z / this->farClipping; 152 return glm::unProject (glm::vec3 (float(p.x), invY, normZ), this->view, this->projection, 153 this->viewport ()); 154 } 155 toWorldCamera::Impl156 float toWorld (float length, float z) 157 { 158 const float onNearPlane = 2.0f * this->nearClipping * glm::tan (this->fieldOfView * 0.5f) * 159 length / float(this->resolution.x); 160 return onNearPlane * (this->nearClipping + z) / this->nearClipping; 161 } 162 rayCamera::Impl163 PrimRay ray (const glm::ivec2& p) const 164 { 165 const glm::vec3 w = this->toWorld (p); 166 const glm::vec3 eye = this->position (); 167 return PrimRay (eye, w - eye); 168 } 169 updateProjectionCamera::Impl170 void updateProjection () 171 { 172 OpenGL::glViewport (0, 0, this->resolution.x, this->resolution.y); 173 this->projection = 174 glm::perspective (this->fieldOfView, float(this->resolution.x) / float(this->resolution.y), 175 this->nearClipping, this->farClipping); 176 } 177 updateViewCamera::Impl178 void updateView () 179 { 180 const glm::vec3 up = this->realUp (); 181 182 this->view = glm::lookAt (this->position (), this->gazePoint, up); 183 this->viewRotation = glm::lookAt (glm::normalize (this->toEyePoint), glm::vec3 (0.0f), up); 184 this->renderer.setEyePoint (this->position ()); 185 } 186 primaryDimensionCamera::Impl187 Dimension primaryDimension () const 188 { 189 const glm::vec3 t = glm::abs (this->toEyePoint); 190 if (t.x > t.y && t.x > t.z) 191 { 192 return Dimension::X; 193 } 194 else if (t.y > t.z) 195 { 196 return Dimension::Y; 197 } 198 return Dimension::Z; 199 } 200 planeIntersectionCamera::Impl201 glm::vec3 planeIntersection (const glm::ivec2& p, const PrimPlane& plane) const 202 { 203 const PrimRay ray = this->ray (p); 204 float t; 205 206 if (IntersectionUtil::intersects (ray, plane, &t)) 207 { 208 return ray.pointAt (t); 209 } 210 else 211 { 212 DILAY_WARN ("No view-plane intersection"); 213 return plane.point (); 214 } 215 } 216 viewPlaneIntersectionCamera::Impl217 glm::vec3 viewPlaneIntersection (const glm::ivec2& p) const 218 { 219 const PrimPlane plane (this->gazePoint, this->toEyePoint); 220 return this->planeIntersection (p, plane); 221 } 222 primaryPlaneIntersectionCamera::Impl223 glm::vec3 primaryPlaneIntersection (const glm::ivec2& p) const 224 { 225 const PrimPlane plane (this->gazePoint, DimensionUtil::vector (this->primaryDimension ())); 226 return this->planeIntersection (p, plane); 227 } 228 runFromConfigCamera::Impl229 void runFromConfig (const Config& config) 230 { 231 this->renderer.fromConfig (config); 232 233 this->nearClipping = config.get<float> ("editor/camera/near-clipping"); 234 this->farClipping = config.get<float> ("editor/camera/far-clipping"); 235 this->fieldOfView = glm::radians (config.get<float> ("editor/camera/field-of-view")); 236 237 this->updateProjection (); 238 } 239 }; 240 241 DELEGATE1_BIG3_SELF (Camera, const Config&) 242 243 GETTER_CONST (Renderer&, Camera, renderer) 244 GETTER_CONST (const glm::uvec2&, Camera, resolution) 245 GETTER_CONST (const glm::vec3&, Camera, gazePoint) 246 GETTER_CONST (const glm::vec3&, Camera, toEyePoint) 247 DELEGATE_CONST (glm::vec3, Camera, realUp) 248 GETTER_CONST (const glm::vec3&, Camera, right) 249 GETTER_CONST (const glm::mat4x4&, Camera, view) 250 GETTER_CONST (const glm::mat4x4&, Camera, viewRotation) 251 DELEGATE_CONST (glm::vec3, Camera, position) 252 DELEGATE_CONST (glm::mat4x4, Camera, world) 253 DELEGATE1 (void, Camera, updateResolution, const glm::uvec2&) 254 DELEGATE3 (void, Camera, setModelViewProjection, const glm::mat4x4&, const glm::mat3x3&, bool) 255 DELEGATE2 (void, Camera, set, const glm::vec3&, const glm::vec3&) 256 DELEGATE1 (void, Camera, setGaze, const glm::vec3&) 257 DELEGATE1 (void, Camera, stepAlongGaze, float) 258 DELEGATE1 (void, Camera, verticalRotation, float) 259 DELEGATE1 (void, Camera, horizontalRotation, float) 260 DELEGATE3_CONST (glm::vec2, Camera, fromWorld, const glm::vec3&, const glm::mat4x4&, bool) 261 DELEGATE2_CONST (glm::vec3, Camera, toWorld, const glm::ivec2&, float) 262 DELEGATE2_CONST (float, Camera, toWorld, float, float) 263 DELEGATE1_CONST (PrimRay, Camera, ray, const glm::ivec2&) 264 DELEGATE_CONST (Dimension, Camera, primaryDimension) 265 DELEGATE1_CONST (glm::vec3, Camera, viewPlaneIntersection, const glm::ivec2&) 266 DELEGATE1_CONST (glm::vec3, Camera, primaryPlaneIntersection, const glm::ivec2&) 267 DELEGATE1 (void, Camera, runFromConfig, const Config&) 268