| 1 | /* |
| 2 | src/glutil.cpp -- Convenience classes for accessing OpenGL >= 3.x |
| 3 | |
| 4 | NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>. |
| 5 | The widget drawing code is based on the NanoVG demo application |
| 6 | by Mikko Mononen. |
| 7 | |
| 8 | All rights reserved. Use of this source code is governed by a |
| 9 | BSD-style license that can be found in the LICENSE.txt file. |
| 10 | */ |
| 11 | |
| 12 | #include <nanogui/glutil.h> |
| 13 | |
| 14 | #if defined(WIN32) |
| 15 | # if !defined(__clang__) |
| 16 | # include <malloc.h> |
| 17 | # endif |
| 18 | #endif |
| 19 | |
| 20 | #include <iostream> |
| 21 | #include <fstream> |
| 22 | #include <Eigen/Geometry> |
| 23 | |
| 24 | NAMESPACE_BEGIN(nanogui) |
| 25 | |
| 26 | static GLuint createShader_helper(GLint type, const std::string &name, |
| 27 | const std::string &defines, |
| 28 | std::string shader_string) { |
| 29 | if (shader_string.empty()) |
| 30 | return (GLuint) 0; |
| 31 | |
| 32 | if (!defines.empty()) { |
| 33 | if (shader_string.length() > 8 && shader_string.substr(0, 8) == "#version" ) { |
| 34 | std::istringstream iss(shader_string); |
| 35 | std::ostringstream oss; |
| 36 | std::string line; |
| 37 | std::getline(iss, line); |
| 38 | oss << line << std::endl; |
| 39 | oss << defines; |
| 40 | while (std::getline(iss, line)) |
| 41 | oss << line << std::endl; |
| 42 | shader_string = oss.str(); |
| 43 | } else { |
| 44 | shader_string = defines + shader_string; |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | GLuint id = glCreateShader(type); |
| 49 | const char *shader_string_const = shader_string.c_str(); |
| 50 | glShaderSource(id, 1, &shader_string_const, nullptr); |
| 51 | glCompileShader(id); |
| 52 | |
| 53 | GLint status; |
| 54 | glGetShaderiv(id, GL_COMPILE_STATUS, &status); |
| 55 | |
| 56 | if (status != GL_TRUE) { |
| 57 | char buffer[512]; |
| 58 | std::cerr << "Error while compiling " ; |
| 59 | if (type == GL_VERTEX_SHADER) |
| 60 | std::cerr << "vertex shader" ; |
| 61 | else if (type == GL_FRAGMENT_SHADER) |
| 62 | std::cerr << "fragment shader" ; |
| 63 | else if (type == GL_GEOMETRY_SHADER) |
| 64 | std::cerr << "geometry shader" ; |
| 65 | std::cerr << " \"" << name << "\":" << std::endl; |
| 66 | std::cerr << shader_string << std::endl << std::endl; |
| 67 | glGetShaderInfoLog(id, 512, nullptr, buffer); |
| 68 | std::cerr << "Error: " << std::endl << buffer << std::endl; |
| 69 | throw std::runtime_error("Shader compilation failed!" ); |
| 70 | } |
| 71 | |
| 72 | return id; |
| 73 | } |
| 74 | |
| 75 | bool GLShader::initFromFiles( |
| 76 | const std::string &name, |
| 77 | const std::string &vertex_fname, |
| 78 | const std::string &fragment_fname, |
| 79 | const std::string &geometry_fname) { |
| 80 | auto file_to_string = [](const std::string &filename) -> std::string { |
| 81 | if (filename.empty()) |
| 82 | return "" ; |
| 83 | std::ifstream t(filename); |
| 84 | return std::string((std::istreambuf_iterator<char>(t)), |
| 85 | std::istreambuf_iterator<char>()); |
| 86 | }; |
| 87 | |
| 88 | return init(name, |
| 89 | file_to_string(vertex_fname), |
| 90 | file_to_string(fragment_fname), |
| 91 | file_to_string(geometry_fname)); |
| 92 | } |
| 93 | |
| 94 | bool GLShader::init(const std::string &name, |
| 95 | const std::string &vertex_str, |
| 96 | const std::string &fragment_str, |
| 97 | const std::string &geometry_str) { |
| 98 | std::string defines; |
| 99 | for (auto def : mDefinitions) |
| 100 | defines += std::string("#define " ) + def.first + std::string(" " ) + def.second + "\n" ; |
| 101 | |
| 102 | glGenVertexArrays(1, &mVertexArrayObject); |
| 103 | mName = name; |
| 104 | mVertexShader = |
| 105 | createShader_helper(GL_VERTEX_SHADER, name, defines, vertex_str); |
| 106 | mGeometryShader = |
| 107 | createShader_helper(GL_GEOMETRY_SHADER, name, defines, geometry_str); |
| 108 | mFragmentShader = |
| 109 | createShader_helper(GL_FRAGMENT_SHADER, name, defines, fragment_str); |
| 110 | |
| 111 | if (!mVertexShader || !mFragmentShader) |
| 112 | return false; |
| 113 | if (!geometry_str.empty() && !mGeometryShader) |
| 114 | return false; |
| 115 | |
| 116 | mProgramShader = glCreateProgram(); |
| 117 | |
| 118 | glAttachShader(mProgramShader, mVertexShader); |
| 119 | glAttachShader(mProgramShader, mFragmentShader); |
| 120 | |
| 121 | if (mGeometryShader) |
| 122 | glAttachShader(mProgramShader, mGeometryShader); |
| 123 | |
| 124 | glLinkProgram(mProgramShader); |
| 125 | |
| 126 | GLint status; |
| 127 | glGetProgramiv(mProgramShader, GL_LINK_STATUS, &status); |
| 128 | |
| 129 | if (status != GL_TRUE) { |
| 130 | char buffer[512]; |
| 131 | glGetProgramInfoLog(mProgramShader, 512, nullptr, buffer); |
| 132 | std::cerr << "Linker error (" << mName << "): " << std::endl << buffer << std::endl; |
| 133 | mProgramShader = 0; |
| 134 | throw std::runtime_error("Shader linking failed!" ); |
| 135 | } |
| 136 | |
| 137 | return true; |
| 138 | } |
| 139 | |
| 140 | void GLShader::bind() { |
| 141 | glUseProgram(mProgramShader); |
| 142 | glBindVertexArray(mVertexArrayObject); |
| 143 | } |
| 144 | |
| 145 | GLint GLShader::attrib(const std::string &name, bool warn) const { |
| 146 | GLint id = glGetAttribLocation(mProgramShader, name.c_str()); |
| 147 | if (id == -1 && warn) |
| 148 | std::cerr << mName << ": warning: did not find attrib " << name << std::endl; |
| 149 | return id; |
| 150 | } |
| 151 | |
| 152 | void GLShader::setUniform(const std::string &name, const GLUniformBuffer &buf, bool warn) { |
| 153 | GLuint blockIndex = glGetUniformBlockIndex(mProgramShader, name.c_str()); |
| 154 | if (blockIndex == GL_INVALID_INDEX) { |
| 155 | if (warn) |
| 156 | std::cerr << mName << ": warning: did not find uniform buffer " << name << std::endl; |
| 157 | return; |
| 158 | } |
| 159 | glUniformBlockBinding(mProgramShader, blockIndex, buf.getBindingPoint()); |
| 160 | } |
| 161 | |
| 162 | GLint GLShader::uniform(const std::string &name, bool warn) const { |
| 163 | GLint id = glGetUniformLocation(mProgramShader, name.c_str()); |
| 164 | if (id == -1 && warn) |
| 165 | std::cerr << mName << ": warning: did not find uniform " << name << std::endl; |
| 166 | return id; |
| 167 | } |
| 168 | |
| 169 | void GLShader::uploadAttrib(const std::string &name, size_t size, int dim, |
| 170 | uint32_t compSize, GLuint glType, bool integral, |
| 171 | const void *data, int version) { |
| 172 | int attribID = 0; |
| 173 | if (name != "indices" ) { |
| 174 | attribID = attrib(name); |
| 175 | if (attribID < 0) |
| 176 | return; |
| 177 | } |
| 178 | |
| 179 | GLuint bufferID; |
| 180 | auto it = mBufferObjects.find(name); |
| 181 | if (it != mBufferObjects.end()) { |
| 182 | Buffer &buffer = it->second; |
| 183 | bufferID = it->second.id; |
| 184 | buffer.version = version; |
| 185 | buffer.size = (GLuint) size; |
| 186 | buffer.compSize = compSize; |
| 187 | } else { |
| 188 | glGenBuffers(1, &bufferID); |
| 189 | Buffer buffer; |
| 190 | buffer.id = bufferID; |
| 191 | buffer.glType = glType; |
| 192 | buffer.dim = dim; |
| 193 | buffer.compSize = compSize; |
| 194 | buffer.size = (GLuint) size; |
| 195 | buffer.version = version; |
| 196 | mBufferObjects[name] = buffer; |
| 197 | } |
| 198 | size_t totalSize = size * (size_t) compSize; |
| 199 | |
| 200 | if (name == "indices" ) { |
| 201 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferID); |
| 202 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, totalSize, data, GL_DYNAMIC_DRAW); |
| 203 | } else { |
| 204 | glBindBuffer(GL_ARRAY_BUFFER, bufferID); |
| 205 | glBufferData(GL_ARRAY_BUFFER, totalSize, data, GL_DYNAMIC_DRAW); |
| 206 | if (size == 0) { |
| 207 | glDisableVertexAttribArray(attribID); |
| 208 | } else { |
| 209 | glEnableVertexAttribArray(attribID); |
| 210 | glVertexAttribPointer(attribID, dim, glType, integral, 0, 0); |
| 211 | } |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | void GLShader::downloadAttrib(const std::string &name, size_t size, int /* dim */, |
| 216 | uint32_t compSize, GLuint /* glType */, void *data) { |
| 217 | auto it = mBufferObjects.find(name); |
| 218 | if (it == mBufferObjects.end()) |
| 219 | throw std::runtime_error("downloadAttrib(" + mName + ", " + name + ") : buffer not found!" ); |
| 220 | |
| 221 | const Buffer &buf = it->second; |
| 222 | if (buf.size != size || buf.compSize != compSize) |
| 223 | throw std::runtime_error(mName + ": downloadAttrib: size mismatch!" ); |
| 224 | |
| 225 | size_t totalSize = size * (size_t) compSize; |
| 226 | |
| 227 | if (name == "indices" ) { |
| 228 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf.id); |
| 229 | glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, totalSize, data); |
| 230 | } else { |
| 231 | glBindBuffer(GL_ARRAY_BUFFER, buf.id); |
| 232 | glGetBufferSubData(GL_ARRAY_BUFFER, 0, totalSize, data); |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | void GLShader::shareAttrib(const GLShader &otherShader, const std::string &name, const std::string &_as) { |
| 237 | std::string as = _as.length() == 0 ? name : _as; |
| 238 | auto it = otherShader.mBufferObjects.find(name); |
| 239 | if (it == otherShader.mBufferObjects.end()) |
| 240 | throw std::runtime_error("shareAttribute(" + otherShader.mName + ", " + name + "): attribute not found!" ); |
| 241 | const Buffer &buffer = it->second; |
| 242 | |
| 243 | if (name != "indices" ) { |
| 244 | int attribID = attrib(as); |
| 245 | if (attribID < 0) |
| 246 | return; |
| 247 | glEnableVertexAttribArray(attribID); |
| 248 | glBindBuffer(GL_ARRAY_BUFFER, buffer.id); |
| 249 | glVertexAttribPointer(attribID, buffer.dim, buffer.glType, buffer.compSize == 1 ? GL_TRUE : GL_FALSE, 0, 0); |
| 250 | } else { |
| 251 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.id); |
| 252 | } |
| 253 | } |
| 254 | |
| 255 | void GLShader::invalidateAttribs() { |
| 256 | for (auto &buffer : mBufferObjects) |
| 257 | buffer.second.version = -1; |
| 258 | } |
| 259 | |
| 260 | void GLShader::freeAttrib(const std::string &name) { |
| 261 | auto it = mBufferObjects.find(name); |
| 262 | if (it != mBufferObjects.end()) { |
| 263 | glDeleteBuffers(1, &it->second.id); |
| 264 | mBufferObjects.erase(it); |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | void GLShader::drawIndexed(int type, uint32_t offset_, uint32_t count_) { |
| 269 | if (count_ == 0) |
| 270 | return; |
| 271 | size_t offset = offset_; |
| 272 | size_t count = count_; |
| 273 | |
| 274 | switch (type) { |
| 275 | case GL_TRIANGLES: offset *= 3; count *= 3; break; |
| 276 | case GL_LINES: offset *= 2; count *= 2; break; |
| 277 | } |
| 278 | |
| 279 | glDrawElements(type, (GLsizei) count, GL_UNSIGNED_INT, |
| 280 | (const void *)(offset * sizeof(uint32_t))); |
| 281 | } |
| 282 | |
| 283 | void GLShader::drawArray(int type, uint32_t offset, uint32_t count) { |
| 284 | if (count == 0) |
| 285 | return; |
| 286 | |
| 287 | glDrawArrays(type, offset, count); |
| 288 | } |
| 289 | |
| 290 | void GLShader::free() { |
| 291 | for (auto &buf: mBufferObjects) |
| 292 | glDeleteBuffers(1, &buf.second.id); |
| 293 | mBufferObjects.clear(); |
| 294 | |
| 295 | if (mVertexArrayObject) { |
| 296 | glDeleteVertexArrays(1, &mVertexArrayObject); |
| 297 | mVertexArrayObject = 0; |
| 298 | } |
| 299 | |
| 300 | glDeleteProgram(mProgramShader); mProgramShader = 0; |
| 301 | glDeleteShader(mVertexShader); mVertexShader = 0; |
| 302 | glDeleteShader(mFragmentShader); mFragmentShader = 0; |
| 303 | glDeleteShader(mGeometryShader); mGeometryShader = 0; |
| 304 | } |
| 305 | |
| 306 | const GLShader::Buffer &GLShader::attribBuffer(const std::string &name) { |
| 307 | for (auto &pair : mBufferObjects) { |
| 308 | if (pair.first == name) |
| 309 | return pair.second; |
| 310 | } |
| 311 | |
| 312 | throw std::runtime_error(mName + ": attribBuffer: " + name + " not found!" ); |
| 313 | } |
| 314 | |
| 315 | // ---------------------------------------------------- |
| 316 | |
| 317 | void GLUniformBuffer::init() { |
| 318 | glGenBuffers(1, &mID); |
| 319 | } |
| 320 | |
| 321 | void GLUniformBuffer::bind(int bindingPoint) { |
| 322 | mBindingPoint = bindingPoint; |
| 323 | glBindBufferBase(GL_UNIFORM_BUFFER, mBindingPoint, mID); |
| 324 | } |
| 325 | |
| 326 | void GLUniformBuffer::release() { |
| 327 | glBindBufferBase(GL_UNIFORM_BUFFER, mBindingPoint, 0); |
| 328 | } |
| 329 | |
| 330 | void GLUniformBuffer::free() { |
| 331 | glDeleteBuffers(1, &mID); |
| 332 | mID = 0; |
| 333 | } |
| 334 | |
| 335 | void GLUniformBuffer::update(const std::vector<uint8_t> &data) { |
| 336 | glBindBuffer(GL_UNIFORM_BUFFER, mID); |
| 337 | glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); |
| 338 | glBindBuffer(GL_UNIFORM_BUFFER, 0); |
| 339 | } |
| 340 | |
| 341 | // ---------------------------------------------------- |
| 342 | |
| 343 | void GLFramebuffer::init(const Vector2i &size, int nSamples) { |
| 344 | mSize = size; |
| 345 | mSamples = nSamples; |
| 346 | |
| 347 | glGenRenderbuffers(1, &mColor); |
| 348 | glBindRenderbuffer(GL_RENDERBUFFER, mColor); |
| 349 | |
| 350 | if (nSamples <= 1) |
| 351 | glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, size.x(), size.y()); |
| 352 | else |
| 353 | glRenderbufferStorageMultisample(GL_RENDERBUFFER, nSamples, GL_RGBA8, size.x(), size.y()); |
| 354 | |
| 355 | glGenRenderbuffers(1, &mDepth); |
| 356 | glBindRenderbuffer(GL_RENDERBUFFER, mDepth); |
| 357 | |
| 358 | if (nSamples <= 1) |
| 359 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, size.x(), size.y()); |
| 360 | else |
| 361 | glRenderbufferStorageMultisample(GL_RENDERBUFFER, nSamples, GL_DEPTH24_STENCIL8, size.x(), size.y()); |
| 362 | |
| 363 | glGenFramebuffers(1, &mFramebuffer); |
| 364 | glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| 365 | |
| 366 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mColor); |
| 367 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, mDepth); |
| 368 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDepth); |
| 369 | |
| 370 | glDrawBuffer(GL_COLOR_ATTACHMENT0); |
| 371 | glReadBuffer(GL_COLOR_ATTACHMENT0); |
| 372 | |
| 373 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| 374 | if (status != GL_FRAMEBUFFER_COMPLETE) |
| 375 | throw std::runtime_error("Could not create framebuffer object!" ); |
| 376 | |
| 377 | release(); |
| 378 | } |
| 379 | |
| 380 | void GLFramebuffer::free() { |
| 381 | glDeleteRenderbuffers(1, &mColor); |
| 382 | glDeleteRenderbuffers(1, &mDepth); |
| 383 | mColor = mDepth = 0; |
| 384 | } |
| 385 | |
| 386 | void GLFramebuffer::bind() { |
| 387 | glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| 388 | if (mSamples > 1) |
| 389 | glEnable(GL_MULTISAMPLE); |
| 390 | } |
| 391 | |
| 392 | void GLFramebuffer::release() { |
| 393 | if (mSamples > 1) |
| 394 | glDisable(GL_MULTISAMPLE); |
| 395 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| 396 | } |
| 397 | |
| 398 | void GLFramebuffer::blit() { |
| 399 | glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebuffer); |
| 400 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); |
| 401 | glDrawBuffer(GL_BACK); |
| 402 | |
| 403 | glBlitFramebuffer(0, 0, mSize.x(), mSize.y(), 0, 0, mSize.x(), mSize.y(), |
| 404 | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); |
| 405 | |
| 406 | glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| 407 | } |
| 408 | |
| 409 | void GLFramebuffer::downloadTGA(const std::string &filename) { |
| 410 | uint8_t *temp = new uint8_t[mSize.prod() * 4]; |
| 411 | |
| 412 | std::cout << "Writing \"" << filename << "\" (" << mSize.x() << "x" << mSize.y() << ") .. " ; |
| 413 | std::cout.flush(); |
| 414 | glPixelStorei(GL_PACK_ALIGNMENT, 1); |
| 415 | glBindFramebuffer(GL_READ_FRAMEBUFFER, mFramebuffer); |
| 416 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| 417 | glReadPixels(0, 0, mSize.x(), mSize.y(), GL_BGRA, GL_UNSIGNED_BYTE, temp); |
| 418 | glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); |
| 419 | |
| 420 | uint32_t rowSize = mSize.x() * 4; |
| 421 | uint32_t halfHeight = mSize.y() / 2; |
| 422 | uint8_t *line = (uint8_t *) alloca(rowSize); |
| 423 | for (uint32_t i=0, j=mSize.y()-1; i<halfHeight; ++i) { |
| 424 | memcpy(line, temp + i * rowSize, rowSize); |
| 425 | memcpy(temp + i * rowSize, temp + j * rowSize, rowSize); |
| 426 | memcpy(temp + j * rowSize, line, rowSize); |
| 427 | j--; |
| 428 | } |
| 429 | |
| 430 | FILE *tga = fopen(filename.c_str(), "wb" ); |
| 431 | if (tga == nullptr) |
| 432 | throw std::runtime_error("GLFramebuffer::downloadTGA(): Could not open output file" ); |
| 433 | fputc(0, tga); /* ID */ |
| 434 | fputc(0, tga); /* Color map */ |
| 435 | fputc(2, tga); /* Image type */ |
| 436 | fputc(0, tga); fputc(0, tga); /* First entry of color map (unused) */ |
| 437 | fputc(0, tga); fputc(0, tga); /* Length of color map (unused) */ |
| 438 | fputc(0, tga); /* Color map entry size (unused) */ |
| 439 | fputc(0, tga); fputc(0, tga); /* X offset */ |
| 440 | fputc(0, tga); fputc(0, tga); /* Y offset */ |
| 441 | fputc(mSize.x() % 256, tga); /* Width */ |
| 442 | fputc(mSize.x() / 256, tga); /* continued */ |
| 443 | fputc(mSize.y() % 256, tga); /* Height */ |
| 444 | fputc(mSize.y() / 256, tga); /* continued */ |
| 445 | fputc(32, tga); /* Bits per pixel */ |
| 446 | fputc(0x20, tga); /* Scan from top left */ |
| 447 | fwrite(temp, mSize.prod() * 4, 1, tga); |
| 448 | fclose(tga); |
| 449 | |
| 450 | delete[] temp; |
| 451 | std::cout << "done." << std::endl; |
| 452 | } |
| 453 | |
| 454 | // ---------------------------------------------------- |
| 455 | |
| 456 | Eigen::Vector3f project(const Eigen::Vector3f &obj, |
| 457 | const Eigen::Matrix4f &model, |
| 458 | const Eigen::Matrix4f &proj, |
| 459 | const Vector2i &viewportSize) { |
| 460 | Eigen::Vector4f tmp; |
| 461 | tmp << obj, 1; |
| 462 | |
| 463 | tmp = model * tmp; |
| 464 | |
| 465 | tmp = proj * tmp; |
| 466 | |
| 467 | tmp = tmp.array() / tmp(3); |
| 468 | tmp = tmp.array() * 0.5f + 0.5f; |
| 469 | tmp(0) = tmp(0) * viewportSize.x(); |
| 470 | tmp(1) = tmp(1) * viewportSize.y(); |
| 471 | |
| 472 | return tmp.head(3); |
| 473 | } |
| 474 | |
| 475 | Eigen::Vector3f unproject(const Eigen::Vector3f &win, |
| 476 | const Eigen::Matrix4f &model, |
| 477 | const Eigen::Matrix4f &proj, |
| 478 | const Vector2i &viewportSize) { |
| 479 | Eigen::Matrix4f Inverse = (proj * model).inverse(); |
| 480 | |
| 481 | Eigen::Vector4f tmp; |
| 482 | tmp << win, 1; |
| 483 | tmp(0) = tmp(0) / viewportSize.x(); |
| 484 | tmp(1) = tmp(1) / viewportSize.y(); |
| 485 | tmp = tmp.array() * 2.0f - 1.0f; |
| 486 | |
| 487 | Eigen::Vector4f obj = Inverse * tmp; |
| 488 | obj /= obj(3); |
| 489 | |
| 490 | return obj.head(3); |
| 491 | } |
| 492 | |
| 493 | Eigen::Matrix4f lookAt(const Eigen::Vector3f &origin, |
| 494 | const Eigen::Vector3f &target, |
| 495 | const Eigen::Vector3f &up) { |
| 496 | Eigen::Vector3f f = (target - origin).normalized(); |
| 497 | Eigen::Vector3f s = f.cross(up).normalized(); |
| 498 | Eigen::Vector3f u = s.cross(f); |
| 499 | |
| 500 | Eigen::Matrix4f result = Eigen::Matrix4f::Identity(); |
| 501 | result(0, 0) = s(0); |
| 502 | result(0, 1) = s(1); |
| 503 | result(0, 2) = s(2); |
| 504 | result(1, 0) = u(0); |
| 505 | result(1, 1) = u(1); |
| 506 | result(1, 2) = u(2); |
| 507 | result(2, 0) = -f(0); |
| 508 | result(2, 1) = -f(1); |
| 509 | result(2, 2) = -f(2); |
| 510 | result(0, 3) = -s.transpose() * origin; |
| 511 | result(1, 3) = -u.transpose() * origin; |
| 512 | result(2, 3) = f.transpose() * origin; |
| 513 | return result; |
| 514 | } |
| 515 | |
| 516 | Eigen::Matrix4f ortho(float left, float right, float bottom, |
| 517 | float top, float nearVal, float farVal) { |
| 518 | Eigen::Matrix4f result = Eigen::Matrix4f::Identity(); |
| 519 | result(0, 0) = 2.0f / (right - left); |
| 520 | result(1, 1) = 2.0f / (top - bottom); |
| 521 | result(2, 2) = -2.0f / (farVal - nearVal); |
| 522 | result(0, 3) = -(right + left) / (right - left); |
| 523 | result(1, 3) = -(top + bottom) / (top - bottom); |
| 524 | result(2, 3) = -(farVal + nearVal) / (farVal - nearVal); |
| 525 | return result; |
| 526 | } |
| 527 | |
| 528 | Eigen::Matrix4f frustum(float left, float right, float bottom, |
| 529 | float top, float nearVal, |
| 530 | float farVal) { |
| 531 | Eigen::Matrix4f result = Eigen::Matrix4f::Zero(); |
| 532 | result(0, 0) = (2.0f * nearVal) / (right - left); |
| 533 | result(1, 1) = (2.0f * nearVal) / (top - bottom); |
| 534 | result(0, 2) = (right + left) / (right - left); |
| 535 | result(1, 2) = (top + bottom) / (top - bottom); |
| 536 | result(2, 2) = -(farVal + nearVal) / (farVal - nearVal); |
| 537 | result(3, 2) = -1.0f; |
| 538 | result(2, 3) = -(2.0f * farVal * nearVal) / (farVal - nearVal); |
| 539 | return result; |
| 540 | } |
| 541 | |
| 542 | Eigen::Matrix4f scale(const Eigen::Vector3f &v) { |
| 543 | return Eigen::Affine3f(Eigen::Scaling(v)).matrix(); |
| 544 | } |
| 545 | |
| 546 | Eigen::Matrix4f translate(const Eigen::Vector3f &v) { |
| 547 | return Eigen::Affine3f(Eigen::Translation<float, 3>(v)).matrix(); |
| 548 | } |
| 549 | |
| 550 | NAMESPACE_END(nanogui) |
| 551 | |