// BlockyFroggy
// Copyright © 2017 John Ryland.
// All rights reserved.
#pragma once
#ifndef GL_PROGRAM_H
#define GL_PROGRAM_H
#include <functional>
#include <memory>
#include <string>
#include <map>
#include <cassert>
#include "OpenGL.h"
struct StrCmp { bool operator()(const char *a, const char *b) const { return ::strcmp(a, b) < 0; } };
typedef std::map<const char *, bool, StrCmp> OptionsType;
enum Precision
{
High,
Medium,
Low
};
class GLProgram
{
public:
GLProgram();
~GLProgram();
typedef std::function<void(uint32_t a_program)> ProgramCallback;
bool loadShaders(const char *a_vertexShader, const char *a_fragmentShader,
Precision a_defaultPrecision, const OptionsType& a_options,
ProgramCallback a_bindAttribs, ProgramCallback a_setupUniforms);
void useProgram();
void deleteProgram();
private:
struct Pimpl;
std::unique_ptr<Pimpl> m_pimpl;
};
/*
template <typename T, size_t size>
size_t ArraySize(T(&)[size]) { return size; }
template <typename T>
size_t ArraySize(std::vector<T> x) { return x.size(); }
template <typename T, size_t size>
const T& GetElement(const T (&array)[size], size_t index, const T& nullValue) { return (index < 0 || index >= size) ? nullValue : array[index]; }
*/
inline GLenum glType(bool) { return GL_UNSIGNED_INT; }
inline GLenum glType(uint8_t) { return GL_UNSIGNED_BYTE; }
inline GLenum glType(uint16_t) { return GL_UNSIGNED_SHORT; }
inline GLenum glType(uint32_t) { return GL_UNSIGNED_INT; }
inline GLenum glType(int8_t) { return GL_BYTE; }
inline GLenum glType(int16_t) { return GL_SHORT; }
inline GLenum glType(int32_t) { return GL_INT; }
inline GLenum glType(float) { return GL_FLOAT; }
#ifndef USE_OPENGLES2
inline GLenum glType(double) { return GL_DOUBLE; }
#endif
// Not sure this works, but idea is that this templated version might prevent type coersion to one of the functions above and catch an error
template <typename T>
inline GLenum glType(T) { assert("bad attribute unittype, try adding the type you need to above" && false); return GL_INT; }
#define DECLARE_ATTRIB_TYPE(name, unittype, ...) \
struct name##attrib { \
union { \
struct { unittype __VA_ARGS__; }; \
unittype attrib[4]; \
}; \
static GLenum getUnitType() { return glType(unittype(0)); } \
static GLint getSize() { struct { unittype __VA_ARGS__; } x; return sizeof(x)/sizeof(unittype); } \
};
#define DECLARE_VERTEX(name) \
struct name { \
typedef name this_type; \
static const char* this_name() { return #name; } \
typedef struct { \
static GLuint attribIndex() { return -1; } \
template <typename T> static inline void visit(T&) { }
// Using "Ryland's Device"
#define DECLARE_ATTRIB(attribtype, name, normalized) \
} detail_##name; \
attribtype##attrib m_##name; \
typedef struct { \
static GLuint attribIndex() { return detail_##name::attribIndex() + 1; } \
template <typename T> \
static inline void visit(T& v) { \
detail_##name::visit(v); \
v(#name, attribIndex(), attribtype##attrib::getSize(), attribtype##attrib::getUnitType(), normalized, sizeof(this_type), &((this_type*)(nullptr))->m_##name); \
}
#define DECLARE_VERTEX_END \
} detail_last; \
template <typename T> \
static void visit(T& v) { \
detail_last::visit(v); \
} \
};
#define VERTEX_ATTRIB_INDEX(vertextype, attrib) \
GLint(vertextype::detail_##attrib::attribIndex() + 1)
DECLARE_ATTRIB_TYPE(vec2, float, x, y)
DECLARE_ATTRIB_TYPE(vec3, float, x, y, z)
DECLARE_ATTRIB_TYPE(vec4, float, x, y, z, w)
DECLARE_ATTRIB_TYPE(col3f, float, r, g, b)
DECLARE_ATTRIB_TYPE(col4f, float, r, g, b, a)
DECLARE_ATTRIB_TYPE(col4i, uint8_t, r, g, b, a)
//DECLARE_ATTRIB_TYPE(col4i, uint8_t, a, b, g, r)
void AttribPointerVisitor(const char* name, GLuint index, GLsizei size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
void glSetUniform(GLint a_location, mat3 a_matrix3);
void glSetUniform(GLint a_location, mat4 a_matrix4);
void glSetUniform(GLint a_location, int a_int);
void glSetUniform(GLint a_location, bool a_bool);
void glSetUniform(GLint a_location, float a_flt);
void glSetUniform(GLint a_location, vec2attrib a_vec2);
void glSetUniform(GLint a_location, vec3attrib a_vec3);
void glSetUniform(GLint a_location, vec4attrib a_vec4);
#ifndef USE_OPENGLES2
GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name);
#endif
// Not sure this works, but idea is that this templated version might prevent type coersion to one of the functions above and catch an error
template <typename T>
inline void glSetUniform(GLint a_location, T val) { assert("bad uniform type, try adding to above" && false); }
#define DECLARE_PROGRAM_UNIFORMS(name) \
struct name { \
typedef name this_type; \
static const char* this_name() { return #name; } \
typedef struct { \
static void getLocation(GLint, this_type*) { } \
static void setUniform(this_type*) { }
// Using Ryland's device
#define DECLARE_UNIFORM(uniformType, name) \
} detail_##name; \
bool m_inited_##name = false; \
uniformType m_last_##name; \
uniformType m_##name; \
GLint m_##name##_index = -1; \
typedef struct { \
static void getLocation(GLint a_program, this_type* _this) { \
detail_##name::getLocation(a_program, _this); \
_this->m_##name##_index = glGetUniformLocation(a_program, #name); \
if (glGetError() != GL_NO_ERROR) \
LogTag(LL_Error, "GLP", "bad uniform location: %s::%s\n", this_name(), #name); \
} \
static void setUniform(this_type* _this) { \
detail_##name::setUniform(_this); \
if (true || memcmp(&_this->m_##name, &_this->m_last_##name, sizeof(uniformType))==0) { \
/* if (_this->m_inited_##name == false || memcmp(&_this->m_##name, &_this->m_last_##name, sizeof(uniformType))==0) { */ \
glSetUniform(_this->m_##name##_index, _this->m_##name); \
_this->m_inited_##name = true; \
memcpy(&_this->m_last_##name, &_this->m_##name, sizeof(uniformType)); \
/* _this->m_last_##name = _this->m_##name; */ \
} \
}
#define DECLARE_PROGRAM_UNIFORMS_END \
} detail_last; \
void getLocations(GLint a_program) { \
detail_last::getLocation(a_program, this); \
} \
void setUniforms() { \
detail_last::setUniform(this); \
} \
};
#endif // GL_PROGRAM_H