/*
nanogui/glutil.h -- Convenience classes for accessing OpenGL >= 3.x
NanoGUI was developed by Wenzel Jakob <wenzel.jakob@epfl.ch>.
The widget drawing code is based on the NanoVG demo application
by Mikko Mononen.
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE.txt file.
*/
#pragma once
#include <nanogui/opengl.h>
#include <Eigen/Geometry>
#include <map>
#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace half_float { class half; }
#endif
#if !defined(GL_HALF_FLOAT) || defined(DOXYGEN_DOCUMENTATION_BUILD)
#define GL_HALF_FLOAT 0x140B
#endif
NAMESPACE_BEGIN(nanogui)
// bypass template specializations
#ifndef DOXYGEN_SHOULD_SKIP_THIS
NAMESPACE_BEGIN(detail)
template <typename T> struct type_traits;
template <> struct type_traits<uint32_t> { enum { type = GL_UNSIGNED_INT, integral = 1 }; };
template <> struct type_traits<int32_t> { enum { type = GL_INT, integral = 1 }; };
template <> struct type_traits<uint16_t> { enum { type = GL_UNSIGNED_SHORT, integral = 1 }; };
template <> struct type_traits<int16_t> { enum { type = GL_SHORT, integral = 1 }; };
template <> struct type_traits<uint8_t> { enum { type = GL_UNSIGNED_BYTE, integral = 1 }; };
template <> struct type_traits<int8_t> { enum { type = GL_BYTE, integral = 1 }; };
template <> struct type_traits<double> { enum { type = GL_DOUBLE, integral = 0 }; };
template <> struct type_traits<float> { enum { type = GL_FLOAT, integral = 0 }; };
template <> struct type_traits<half_float::half> { enum { type = GL_HALF_FLOAT, integral = 0 }; };
template <typename T> struct serialization_helper;
NAMESPACE_END(detail)
#endif // DOXYGEN_SHOULD_SKIP_THIS
using Eigen::Quaternionf;
class GLUniformBuffer;
// ----------------------------------------------------
class NANOGUI_EXPORT GLShader {
// this friendship breaks the documentation
#ifndef DOXYGEN_SHOULD_SKIP_THIS
template <typename T> friend struct detail::serialization_helper;
#endif
public:
struct Buffer {
GLuint id;
GLuint glType;
GLuint dim;
GLuint compSize;
GLuint size;
int version;
};
GLShader()
: mVertexShader(0), mFragmentShader(0), mGeometryShader(0),
mProgramShader(0), mVertexArrayObject(0) { }
bool init(const std::string &name, const std::string &vertex_str,
const std::string &fragment_str,
const std::string &geometry_str = "");
bool initFromFiles(const std::string &name,
const std::string &vertex_fname,
const std::string &fragment_fname,
const std::string &geometry_fname = "");
const std::string &name() const { return mName; }
void define(const std::string &key, const std::string &value) { mDefinitions[key] = value; }
void bind();
void free();
GLint attrib(const std::string &name, bool warn = true) const;
GLint uniform(const std::string &name, bool warn = true) const;
template <typename Matrix> void uploadAttrib(const std::string &name, const Matrix &M, int version = -1) {
uint32_t compSize = sizeof(typename Matrix::Scalar);
GLuint glType = (GLuint) detail::type_traits<typename Matrix::Scalar>::type;
bool integral = (bool) detail::type_traits<typename Matrix::Scalar>::integral;
uploadAttrib(name, (uint32_t) M.size(), (int) M.rows(), compSize,
glType, integral, M.data(), version);
}
template <typename Matrix> void downloadAttrib(const std::string &name, Matrix &M) {
uint32_t compSize = sizeof(typename Matrix::Scalar);
GLuint glType = (GLuint) detail::type_traits<typename Matrix::Scalar>::type;
auto it = mBufferObjects.find(name);
if (it == mBufferObjects.end())
throw std::runtime_error("downloadAttrib(" + mName + ", " + name + ") : buffer not found!");
const Buffer &buf = it->second;
M.resize(buf.dim, buf.size / buf.dim);
downloadAttrib(name, M.size(), M.rows(), compSize, glType, M.data());
}
template <typename Matrix> void uploadIndices(const Matrix &M, int version = -1) {
uploadAttrib("indices", M, version);
}
void invalidateAttribs();
void freeAttrib(const std::string &name);
bool hasAttrib(const std::string &name) const {
auto it = mBufferObjects.find(name);
if (it == mBufferObjects.end())
return false;
return true;
}
void shareAttrib(const GLShader &otherShader, const std::string &name, const std::string &as = "");
int attribVersion(const std::string &name) const {
auto it = mBufferObjects.find(name);
if (it == mBufferObjects.end())
return -1;
return it->second.version;
}
void resetAttribVersion(const std::string &name) {
auto it = mBufferObjects.find(name);
if (it != mBufferObjects.end())
it->second.version = -1;
}
void drawArray(int type, uint32_t offset, uint32_t count);
void drawIndexed(int type, uint32_t offset, uint32_t count);
template <typename T>
void setUniform(const std::string &name, const Eigen::Matrix<T, 4, 4> &mat, bool warn = true) {
glUniformMatrix4fv(uniform(name, warn), 1, GL_FALSE, mat.template cast<float>().data());
}
template <typename T>
void setUniform(const std::string &name, const Eigen::Transform<T, 3, 3> &affine, bool warn = true) {
glUniformMatrix4fv(uniform(name, warn), 1, GL_FALSE, affine.template cast<float>().data());
}
template <typename T>
void setUniform(const std::string &name, const Eigen::Matrix<T, 3, 3> &mat, bool warn = true) {
glUniformMatrix3fv(uniform(name, warn), 1, GL_FALSE, mat.template cast<float>().data());
}
template <typename T>
void setUniform(const std::string &name, const Eigen::Transform<T, 2, 2> &affine, bool warn = true) {
glUniformMatrix3fv(uniform(name, warn), 1, GL_FALSE, affine.template cast<float>().data());
}
void setUniform(const std::string &name, bool value, bool warn = true) {
glUniform1i(uniform(name, warn), (int)value);
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, T value, bool warn = true) {
glUniform1i(uniform(name, warn), (int) value);
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, T value, bool warn = true) {
glUniform1f(uniform(name, warn), (float) value);
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 2, 1> &v, bool warn = true) {
glUniform2i(uniform(name, warn), (int) v.x(), (int) v.y());
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 2, 1> &v, bool warn = true) {
glUniform2f(uniform(name, warn), (float) v.x(), (float) v.y());
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 3, 1> &v, bool warn = true) {
glUniform3i(uniform(name, warn), (int) v.x(), (int) v.y(), (int) v.z());
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 3, 1> &v, bool warn = true) {
glUniform3f(uniform(name, warn), (float) v.x(), (float) v.y(), (float) v.z());
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 1, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 4, 1> &v, bool warn = true) {
glUniform4i(uniform(name, warn), (int) v.x(), (int) v.y(), (int) v.z(), (int) v.w());
}
template <typename T, typename std::enable_if<detail::type_traits<T>::integral == 0, int>::type = 0>
void setUniform(const std::string &name, const Eigen::Matrix<T, 4, 1> &v, bool warn = true) {
glUniform4f(uniform(name, warn), (float) v.x(), (float) v.y(), (float) v.z(), (float) v.w());
}
void setUniform(const std::string &name, const GLUniformBuffer &buf, bool warn = true);
size_t bufferSize() const {
size_t size = 0;
for (auto const &buf : mBufferObjects)
size += buf.second.size;
return size;
}
const Buffer &attribBuffer(const std::string &name);
public:
/* Low-level API */
void uploadAttrib(const std::string &name, size_t size, int dim,
uint32_t compSize, GLuint glType, bool integral,
const void *data, int version = -1);
void downloadAttrib(const std::string &name, size_t size, int dim,
uint32_t compSize, GLuint glType, void *data);
protected:
std::string mName;
GLuint mVertexShader;
GLuint mFragmentShader;
GLuint mGeometryShader;
GLuint mProgramShader;
GLuint mVertexArrayObject;
std::map<std::string, Buffer> mBufferObjects;
std::map<std::string, std::string> mDefinitions;
};
// ----------------------------------------------------
class NANOGUI_EXPORT GLUniformBuffer {
public:
GLUniformBuffer() : mID(0), mBindingPoint(0) { }
void init();
void free();
void bind(int index);
void release();
void update(const std::vector<uint8_t> &data);
int getBindingPoint() const { return mBindingPoint; }
private:
GLuint mID;
int mBindingPoint;
};
// ----------------------------------------------------
class UniformBufferStd140 : public std::vector<uint8_t> {
public:
using Parent = std::vector<uint8_t>;
using Parent::push_back;
template <typename T, typename std::enable_if<std::is_pod<T>::value, int>::type = 0>
void push_back(T value) {
uint8_t *tmp = (uint8_t*) &value;
for (int i = 0; i < sizeof(T); i++)
Parent::push_back(tmp[i]);
}
template <typename Derived, typename std::enable_if<Derived::IsVectorAtCompileTime, int>::type = 0>
void push_back(const Eigen::MatrixBase<Derived> &value) {
const int n = (int) value.size();
int i;
for (i = 0; i < n; ++i)
push_back(value[i]);
const int pad = n == 1 ? 1 : (n == 2 ? 2 : 4);
while ((i++) % pad != 0)
push_back((typename Derived::Scalar) 0);
}
template <typename Derived, typename std::enable_if<!Derived::IsVectorAtCompileTime, int>::type = 0>
void push_back(const Eigen::MatrixBase<Derived> &value, bool colMajor = true) {
const int n = (int) (colMajor ? value.rows() : value.cols());
const int m = (int) (colMajor ? value.cols() : value.rows());
const int pad = n == 1 ? 1 : (n == 2 ? 2 : 4);
for (int i = 0; i < m; ++i) {
int j;
for (j = 0; j < n; ++j)
push_back(colMajor ? value(j, i) : value(i, j));
while ((j++) % pad != 0)
push_back((typename Derived::Scalar) 0);
}
}
};
// ----------------------------------------------------
class NANOGUI_EXPORT GLFramebuffer {
public:
GLFramebuffer() : mFramebuffer(0), mDepth(0), mColor(0), mSamples(0) { }
void init(const Vector2i &size, int nSamples);
void free();
void bind();
void release();
void blit();
bool ready() { return mFramebuffer != 0; }
int samples() const { return mSamples; }
void downloadTGA(const std::string &filename);
protected:
GLuint mFramebuffer, mDepth, mColor;
Vector2i mSize;
int mSamples;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
// ----------------------------------------------------
struct Arcball {
Arcball(float speedFactor = 2.0f)
: mActive(false), mLastPos(Vector2i::Zero()), mSize(Vector2i::Zero()),
mQuat(Quaternionf::Identity()),
mIncr(Quaternionf::Identity()),
mSpeedFactor(speedFactor) { }
Arcball(const Quaternionf &quat)
: mActive(false), mLastPos(Vector2i::Zero()), mSize(Vector2i::Zero()),
mQuat(quat),
mIncr(Quaternionf::Identity()),
mSpeedFactor(2.0f) { }
Quaternionf &state() { return mQuat; }
const Quaternionf &state() const { return mQuat; }
void setState(const Quaternionf &state) {
mActive = false;
mLastPos = Vector2i::Zero();
mQuat = state;
mIncr = Quaternionf::Identity();
}
void setSize(Vector2i size) { mSize = size; }
const Vector2i &size() const { return mSize; }
void setSpeedFactor(float speedFactor) { mSpeedFactor = speedFactor; }
float speedFactor() const { return mSpeedFactor; }
bool active() const { return mActive; }
void button(Vector2i pos, bool pressed) {
mActive = pressed;
mLastPos = pos;
if (!mActive)
mQuat = (mIncr * mQuat).normalized();
mIncr = Quaternionf::Identity();
}
bool motion(Vector2i pos) {
if (!mActive)
return false;
/* Based on the rotation controller from AntTweakBar */
float invMinDim = 1.0f / mSize.minCoeff();
float w = (float) mSize.x(), h = (float) mSize.y();
float ox = (mSpeedFactor * (2*mLastPos.x() - w) + w) - w - 1.0f;
float tx = (mSpeedFactor * (2*pos.x() - w) + w) - w - 1.0f;
float oy = (mSpeedFactor * (h - 2*mLastPos.y()) + h) - h - 1.0f;
float ty = (mSpeedFactor * (h - 2*pos.y()) + h) - h - 1.0f;
ox *= invMinDim; oy *= invMinDim;
tx *= invMinDim; ty *= invMinDim;
Vector3f v0(ox, oy, 1.0f), v1(tx, ty, 1.0f);
if (v0.squaredNorm() > 1e-4f && v1.squaredNorm() > 1e-4f) {
v0.normalize(); v1.normalize();
Vector3f axis = v0.cross(v1);
float sa = std::sqrt(axis.dot(axis)),
ca = v0.dot(v1),
angle = std::atan2(sa, ca);
if (tx*tx + ty*ty > 1.0f)
angle *= 1.0f + 0.2f * (std::sqrt(tx*tx + ty*ty) - 1.0f);
mIncr = Eigen::AngleAxisf(angle, axis.normalized());
if (!std::isfinite(mIncr.norm()))
mIncr = Quaternionf::Identity();
}
return true;
}
Matrix4f matrix() const {
Matrix4f result2 = Matrix4f::Identity();
result2.block<3,3>(0, 0) = (mIncr * mQuat).toRotationMatrix();
return result2;
}
Quaternionf activeState() const { return mIncr * mQuat; }
void interrupt() { button(Vector2i::Zero(), false); }
protected:
bool mActive;
Vector2i mLastPos;
Vector2i mSize;
Quaternionf mQuat;
Quaternionf mIncr;
float mSpeedFactor;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
// ----------------------------------------------------
extern NANOGUI_EXPORT Vector3f project(const Vector3f &obj,
const Matrix4f &model,
const Matrix4f &proj,
const Vector2i &viewportSize);
extern NANOGUI_EXPORT Vector3f unproject(const Vector3f &win,
const Matrix4f &model,
const Matrix4f &proj,
const Vector2i &viewportSize);
extern NANOGUI_EXPORT Matrix4f lookAt(const Vector3f &origin,
const Vector3f &target,
const Vector3f &up);
extern NANOGUI_EXPORT Matrix4f ortho(float left, float right,
float bottom, float top,
float nearVal, float farVal);
extern NANOGUI_EXPORT Matrix4f frustum(float left, float right,
float bottom, float top,
float nearVal, float farVal);
extern NANOGUI_EXPORT Matrix4f scale(const Vector3f &v);
extern NANOGUI_EXPORT Matrix4f translate(const Vector3f &v);
NAMESPACE_END(nanogui)