commit 1cfd59c95cf9ab70569af3c237464ee9a93eaa10
parent 557e7a11122ab62f50917fe46ebd0fd4654de9f6
Author: Brian Swetland <swetland@frotz.net>
Date: Sat, 9 Feb 2013 03:33:49 -0800
multiplatform: add GL3.3/GLES3.0 platform implementation
This breaks the existing API a bit, adding an InputConfig object
which both owns the VAO and when to change that state as well as
manages (inefficiently for now) the GL Program object
Diffstat:
A | common/glapp.cc | | | 569 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | common/glapp.h | | | 202 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 771 insertions(+), 0 deletions(-)
diff --git a/common/glapp.cc b/common/glapp.cc
@@ -0,0 +1,569 @@
+/* Copyright 2013 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "glapp.h"
+#include "util.h"
+
+#if 1
+#define CHECK() do {} while (0)
+#else
+const char *__gl_error_string(unsigned e) {
+ static char buf[16];
+ switch (e) {
+ case GL_INVALID_ENUM: return "INVALID ENUM";
+ case GL_INVALID_VALUE: return "INVALID VALUE";
+ case GL_INVALID_OPERATION: return "INVALID OPERATION";
+ case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID FRAMEBUFFER OPERATION";
+ case GL_OUT_OF_MEMORY: return "OUT OF MEMORY";
+ default:
+ sprintf(buf,"0x%08x", e);
+ return buf;
+ }
+}
+
+void __check_gl_error(const char *fn, int line) {
+ unsigned e;
+ while ((e = glGetError()) != GL_NO_ERROR)
+ printx("%s:%d: GL ERROR %s\n", fn, line, __gl_error_string(e));
+}
+#define CHECK() __check_gl_error(__func__, __LINE__)
+#endif
+
+static void die(const char *fmt, ...) {
+ fprintf(stderr,"ERROR: %s\n", fmt);
+ exit(-1);
+}
+
+static void quit(void) {
+ SDL_Quit();
+ exit(0);
+}
+
+void App::handleEvents(void) {
+ SDL_Event ev;
+
+ while (SDL_PollEvent(&ev)) {
+ switch (ev.type) {
+ case SDL_KEYDOWN:
+ if (ev.key.keysym.sym < 256)
+ keystate[ev.key.keysym.sym] = 1;
+ if (ev.key.keysym.sym == SDLK_ESCAPE)
+ quit();
+ break;
+ case SDL_KEYUP:
+ if (ev.key.keysym.sym < 256)
+ keystate[ev.key.keysym.sym] = 0;
+ break;
+ case SDL_QUIT:
+ quit();
+ }
+ }
+}
+
+#if 0
+void App::setOptions(int argc, char **argv) {
+ char *x;
+ argc--;
+ argv++;
+ while (argc--) {
+ if (!strcmp("-nosync",argv[0])) {
+ _vsync = 0;
+ } else if (isdigit(argv[0][0]) && (x = strchr(argv[0],'x'))) {
+ _width = atoi(argv[0]);
+ _height = atoi(x + 1);
+ } else {
+ fprintf(stderr,"unknown argument '%s'\n",argv[0]);
+ }
+ argv++;
+ }
+}
+#endif
+
+int App::start(void) {
+ memset(keystate, 0, sizeof(keystate));
+
+ if (SDL_Init(SDL_INIT_VIDEO))
+ die("sdl video init failed");
+
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
+
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
+ SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
+
+ /* enable vsync */
+ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, _vsync);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, _vsync);
+
+ if (SDL_SetVideoMode(width, height, 32,
+ SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL) == NULL)
+ die("sdl cannot set mode");
+
+ if (init())
+ return -1;
+
+ for (;;) {
+ handleEvents();
+
+ unsigned b = SDL_GetRelativeMouseState(&mouseDX, &mouseDY);
+ mouseBTN = (b & 1) | ((b & 2) << 1) | ((b & 4) >> 1);
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glEnable(GL_DEPTH_TEST);
+ render();
+
+ if (_vsync) {
+ SDL_GL_SwapBuffers();
+ } else {
+ glFlush();
+ }
+ }
+ return -1;
+}
+
+App::App() : width(800), height(600), _vsync(1) {
+}
+
+App::~App() {
+}
+
+int main(int argc, char **argv) {
+ App *app = createApp();
+ app->start();
+ app->release();
+ return 0;
+}
+
+
+// ----
+
+static inline void IGNORE(int x) {}
+
+void printx(const char *fmt, ...) {
+ char buf[128];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ buf[127] = 0;
+ va_end(ap);
+ IGNORE(write(2, buf, strlen(buf)));
+}
+
+void printmtx(float *m, const char *name) {
+ printx("| %8.4f %8.4f %8.4f %8.4f | \"%s\"\n", m[0], m[1], m[2], m[3], name);
+ printx("| %8.4f %8.4f %8.4f %8.4f |\n", m[4], m[5], m[6], m[7]);
+ printx("| %8.4f %8.4f %8.4f %8.4f |\n", m[8], m[9], m[10], m[11]);
+ printx("| %8.4f %8.4f %8.4f %8.4f |\n", m[12], m[13], m[14], m[15]);
+}
+
+int error(const char *fmt, ...) {
+ char buf[128];
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ buf[127] = 0;
+ printx("ERROR: %s\n", buf);
+ return -1;
+}
+
+#define OFF2PTR(off) (((char*) NULL) + off)
+
+void config_attr_info(GLAttrInfo *ai, unsigned count, int vidx, unsigned stride) {
+ unsigned n;
+ for (n = 0; n < count; n++, ai++) {
+ if (ai->vidx != vidx) continue;
+ if (ai->kind == KIND_ATTRIB_POINTER) {
+ printf("VTX ATR idx=%d sz=%d type=%d norm=%d stride=%d off=%d\n",
+ ai->index, ai->size, ai->type, ai->normalized, stride, ai->pointer);
+ glVertexAttribPointer(ai->index, ai->size, ai->type,
+ ai->normalized, stride, OFF2PTR(ai->pointer));
+ CHECK();
+ } else {
+ printf("VTX ATR idx=%d sz=%d type=%d stride=%d off=%d\n",
+ ai->index, ai->size, ai->type, stride, ai->pointer);
+ glVertexAttribIPointer(ai->index, ai->size, ai->type,
+ stride, OFF2PTR(ai->pointer));
+ CHECK();
+ }
+ if (ai->divisor)
+ glVertexAttribDivisor(ai->index, ai->divisor);
+ glEnableVertexAttribArray(ai->index);
+ }
+}
+
+/* map attribute names to indices, and adapt layout parameters for GL */
+int setup_attr_info(GLAttrInfo **out, AttribInfo *in, unsigned count, unsigned pgm) {
+ GLAttrInfo *ai = (GLAttrInfo*) malloc(sizeof(GLAttrInfo) * count);
+ unsigned n;
+ if (!ai)
+ return error("Out of Memory");
+ memset(ai, 0, sizeof(GLAttrInfo) * count);
+ for (n = 0; n < count; n++, in++) {
+ ai[n].vidx = in->slot;
+ ai[n].index = glGetAttribLocation(pgm, in->name);
+ if (ai[n].index < 0)
+ return error("cannot find attribute '%s'", in->name);
+ ai[n].pointer = in->offset;
+ ai[n].divisor = in->divisor;
+ switch (in->format) {
+ case FMT_32x4_FLOAT:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 4;
+ ai[n].type = GL_FLOAT;
+ ai[n].normalized = GL_FALSE;
+ break;
+ case FMT_32x3_FLOAT:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 3;
+ ai[n].type = GL_FLOAT;
+ ai[n].normalized = GL_FALSE;
+ break;
+ case FMT_32x2_FLOAT:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 2;
+ ai[n].type = GL_FLOAT;
+ ai[n].normalized = GL_FALSE;
+ break;
+ case FMT_32x1_FLOAT:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 1;
+ ai[n].type = GL_FLOAT;
+ ai[n].normalized = GL_FALSE;
+ break;
+ case FMT_8x4_SNORM:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 4;
+ ai[n].type = GL_BYTE;
+ ai[n].normalized = GL_TRUE;
+ break;
+ case FMT_8x4_UNORM:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 4;
+ ai[n].type = GL_UNSIGNED_BYTE;
+ ai[n].normalized = GL_TRUE;
+ break;
+ case FMT_8x2_UNORM:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 2;
+ ai[n].type = GL_UNSIGNED_BYTE;
+ ai[n].normalized = GL_TRUE;
+ break;
+ case FMT_8x1_UNORM:
+ ai[n].kind = KIND_ATTRIB_POINTER;
+ ai[n].size = 1;
+ ai[n].type = GL_UNSIGNED_BYTE;
+ ai[n].normalized = GL_TRUE;
+ break;
+ case FMT_8x4_UINT:
+ ai[n].kind = KIND_ATTRIB_IPOINTER;
+ ai[n].size = 4;
+ ai[n].type = GL_BYTE;
+ ai[n].normalized = GL_FALSE;
+ break;
+ case FMT_8x2_UINT:
+ ai[n].kind = KIND_ATTRIB_IPOINTER;
+ ai[n].size = 2;
+ ai[n].type = GL_BYTE;
+ ai[n].normalized = GL_FALSE;
+ break;
+ case FMT_8x1_UINT:
+ ai[n].kind = KIND_ATTRIB_IPOINTER;
+ ai[n].size = 1;
+ ai[n].type = GL_BYTE;
+ ai[n].normalized = GL_FALSE;
+ break;
+ default:
+ return error("unknown format %d\n", in->format);
+ }
+ }
+ *out = ai;
+ return 0;
+};
+
+void dump_compile_error(unsigned id) {
+ int len;
+ char *buf;
+ glGetShaderiv(id, GL_INFO_LOG_LENGTH, &len);
+ buf = (char*) malloc(len + 1);
+ if (buf != 0) {
+ memset(buf, 0, len);
+ glGetShaderInfoLog(id, len, &len, buf);
+ buf[len] = 0;
+ fprintf(stderr,"-- shader compiler error --\n%s\n", buf);
+ free(buf);
+ }
+}
+
+void dump_link_error(unsigned id) {
+ int len;
+ char *buf;
+ glGetProgramiv(id, GL_INFO_LOG_LENGTH, &len);
+ buf = (char*) malloc(len + 1);
+ if (buf != 0) {
+ memset(buf, 0, len);
+ glGetProgramInfoLog(id, len, &len, buf);
+ buf[len] = 0;
+ fprintf(stderr,"-- shader link error --\n%s\n", buf);
+ free(buf);
+ }
+}
+
+int App::compileShader(VertexShader *vs, const char *fn,
+ void *data, unsigned len, int raw,
+ AttribInfo *layout, unsigned lcount) {
+
+ unsigned pgm, id;
+ int r;
+
+ id = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(id, 1, (const char**) &data, NULL);
+ glCompileShader(id);
+ glGetShaderiv(id, GL_COMPILE_STATUS, &r);
+ if (!r) {
+ dump_compile_error(id);
+ glDeleteShader(id);
+ return error("vertex shader '%s' compile error" ,fn);
+ }
+ fprintf(stderr,"COMPILE %d\n", r);
+
+ /* we need to compile a program to lookup the indices... */
+ pgm = glCreateProgram();
+ glAttachShader(pgm, id);
+ glLinkProgram(pgm);
+ glGetProgramiv(pgm, GL_LINK_STATUS, &r);
+ if (!r) {
+ dump_link_error(id);
+ glDeleteProgram(pgm);
+ glDeleteShader(id);
+ return error("vertex shader '%s' link error", fn);
+ }
+
+ r = setup_attr_info(&vs->ai, layout, lcount, pgm);
+ glDeleteProgram(pgm);
+ if (r < 0) {
+ glDeleteShader(id);
+ return error("vertex shader '%s' bind error", fn);
+ }
+ vs->count = lcount;
+ vs->vs = id;
+ printx("VertexShader %d compiled\n", id);
+ return 0;
+}
+
+int App::compileShader(PixelShader *ps, const char *fn,
+ void *data, unsigned len, int raw) {
+ unsigned id;
+ int r;
+ id = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(id, 1, (const char **) &data, NULL);
+ glCompileShader(id);
+ glGetShaderiv(id, GL_COMPILE_STATUS, &r);
+ if (!r) {
+ dump_compile_error(id);
+ glDeleteShader(id);
+ return error("pixel shader '%s' compile error", fn);
+ }
+ ps->ps = id;
+ printx("PixelShader %d compiled\n", id);
+ return 0;
+}
+
+int App::loadShader(VertexShader *vs, const char *fn,
+ AttribInfo *layout, unsigned lcount) {
+ void *data;
+ unsigned sz;
+ int r;
+ if (!(data = load_file(fn, &sz)))
+ return error("cannot load file '%s'", fn);
+ r = compileShader(vs, fn, data, sz, 0, layout, lcount);
+ free(data);
+ return r;
+}
+
+int App::loadShader(PixelShader *ps, const char *fn) {
+ void *data;
+ unsigned sz;
+ int r;
+ if (!(data = load_file(fn, &sz)))
+ return error("cannot load file '%s'", fn);
+ r = compileShader(ps, fn, data, sz, 0);
+ free(data);
+ return r;
+}
+
+int App::initConfig(InputConfiguration *ic, VertexShader *vs, PixelShader *ps) {
+ unsigned pgm, vao;
+ int r;
+ // TODO program cache
+ pgm = glCreateProgram();
+ glAttachShader(pgm, vs->vs);
+ glAttachShader(pgm, ps->ps);
+ glLinkProgram(pgm);
+ glGetProgramiv(pgm, GL_LINK_STATUS, &r);
+ if (!r) {
+ dump_link_error(pgm);
+ glDeleteProgram(pgm);
+ return error("program link error");
+ }
+ glGenVertexArrays(1, &vao);
+ CHECK();
+ glBindVertexArray(ic->vao);
+ CHECK();
+ ic->vao = vao;
+ ic->pgm = pgm;
+ ic->vs = vs;
+ ic->ps = ps;
+ printx("Program %d linked\n", pgm);
+ return 0;
+}
+
+void App::useConfig(InputConfiguration *ic) {
+ this->ic = ic;
+ glBindVertexArray(ic->vao);
+ CHECK();
+ glUseProgram(ic->pgm);
+ CHECK();
+}
+
+int App::loadTextureRGBA(Texture2D *tex, const char *fn, int genmips) {
+ void *data;
+ unsigned dw, dh;
+ int r;
+ if (!(data = load_png_rgba(fn, &dw, &dh, 0)))
+ return error("cannot load '%s'", fn);
+ r = createTextureRGBA(tex, data, dw, dh, genmips);
+ free(data);
+ return r;
+}
+int App::createTextureRGBA(Texture2D *tex, void *data, unsigned w, unsigned h, int genmips) {
+ unsigned id;
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
+ if (genmips)
+ glGenerateMipmap(GL_TEXTURE_2D);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ tex->tex = id;
+ return 0;
+}
+
+int App::initBuffer(VertexBuffer *vb, void *data, int sz) {
+ if (vb->buf)
+ glDeleteBuffers(1, &vb->buf);
+ glGenBuffers(1, &vb->buf);
+ vb->sz = sz;
+ if (data) {
+ glBindBuffer(GL_ARRAY_BUFFER, vb->buf);
+ CHECK();
+ glBufferData(GL_ARRAY_BUFFER, sz, data, GL_STATIC_DRAW);
+ CHECK();
+ }
+ return 0;
+}
+int App::initBuffer(IndexBuffer *ib, void *data, int sz) {
+ glGenBuffers(1, &ib->buf);
+ ib->sz = sz;
+ if (data) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->buf);
+ CHECK();
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sz, data, GL_STATIC_DRAW);
+ CHECK();
+ }
+ return 0;
+}
+int App::initBuffer(UniformBuffer *ub, void *data, int sz) {
+ glGenBuffers(1, &ub->buf);
+ ub->sz = sz;
+ if (data) {
+ glBindBuffer(GL_UNIFORM_BUFFER, ub->buf);
+ CHECK();
+ glBufferData(GL_UNIFORM_BUFFER, sz, data, GL_STATIC_DRAW);
+ CHECK();
+ }
+ return 0;
+}
+
+void App::updateBuffer(VertexBuffer *vb, void *data) {
+ glBindBuffer(GL_ARRAY_BUFFER, vb->buf);
+ CHECK();
+ glBufferData(GL_ARRAY_BUFFER, vb->sz, data, GL_STATIC_DRAW);
+ CHECK();
+}
+void App::updateBuffer(IndexBuffer *ib, void *data) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->buf);
+ CHECK();
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, ib->sz, data, GL_STATIC_DRAW);
+ CHECK();
+}
+void App::updateBuffer(UniformBuffer *ub, void *data) {
+ glBindBuffer(GL_UNIFORM_BUFFER, ub->buf);
+ CHECK();
+ glBufferData(GL_UNIFORM_BUFFER, ub->sz, data, GL_STATIC_DRAW);
+ CHECK();
+}
+
+void App::useBuffer(VertexBuffer *vb, int slot, unsigned stride, unsigned offset) {
+ glBindBuffer(GL_ARRAY_BUFFER, vb->buf);
+ CHECK();
+ config_attr_info(ic->vs->ai, ic->vs->count, slot, stride);
+ ic->vbuf[slot] = vb;
+}
+void App::useBuffer(IndexBuffer *ib) {
+ ic->ibuf = ib;
+}
+void App::useBuffer(UniformBuffer *ub, int slot) {
+ if (ic->ubuf[slot] != ub) {
+ ic->ubuf[slot] = ub;
+ /* simple 1:1 binding */
+ // TODO: smarting binding, avoid having to glBindBufferBase() on every draw
+ glUniformBlockBinding(ic->pgm, slot, slot);
+ }
+}
+void App::useTexture(Texture2D *tex, int slot) {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex->tex);
+}
+
+void _prepare(InputConfiguration *ic) {
+ int n;
+ if (ic->ibuf)
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ic->ibuf->buf);
+ for (n = 0; n < 16; n++)
+ if (ic->ubuf[n])
+ glBindBufferBase(GL_UNIFORM_BUFFER, n, ic->ubuf[n]->buf);
+}
+void App::drawIndexedInstanced(unsigned numindices, unsigned numinstances) {
+ _prepare(ic);
+ glDrawElementsInstanced(GL_TRIANGLES, numindices, GL_UNSIGNED_SHORT, NULL, numinstances);
+ CHECK();
+}
+void App::drawInstanced(unsigned numvertices, unsigned numinstances) {
+ _prepare(ic);
+ glDrawArraysInstanced(GL_TRIANGLES, 0, numvertices, numinstances);
+ CHECK();
+}
+void App::drawIndexed(unsigned numindices) {
+ _prepare(ic);
+ glDrawElements(GL_TRIANGLES, numindices, GL_UNSIGNED_SHORT, NULL);
+ CHECK();
+}
diff --git a/common/glapp.h b/common/glapp.h
@@ -0,0 +1,202 @@
+/* Copyright 2013 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _GL_APP_H_
+#define _GL_APP_H_
+
+#define GL_GLEXT_PROTOTYPES 1
+#define NO_SDL_GLEXT 1
+#include <SDL.h>
+#include <SDL_opengl.h>
+#include <GL/glext.h>
+#include <math.h>
+
+#define SL "glsl"
+
+enum {
+ FMT_32x4_FLOAT = 1,
+ FMT_32x3_FLOAT = 2,
+ FMT_32x2_FLOAT = 3,
+ FMT_32x1_FLOAT = 4,
+
+ FMT_8x4_SNORM = 5,
+
+ FMT_8x4_UNORM = 6,
+ FMT_8x2_UNORM = 7,
+ FMT_8x1_UNORM = 8,
+
+ FMT_8x4_UINT = 9,
+ FMT_8x2_UINT = 10,
+ FMT_8x1_UINT = 11,
+};
+
+enum {
+ VERTEX_DATA = 1,
+ INSTANCE_DATA = 2,
+};
+
+struct AttribInfo {
+ const char *name;
+ unsigned nidx;
+ unsigned format;
+ unsigned slot;
+ unsigned offset;
+ unsigned type;
+ unsigned divisor;
+};
+
+
+struct GLAttrInfo {
+#define KIND_ATTRIB_POINTER 0
+#define KIND_ATTRIB_IPOINTER 1
+ int kind;
+
+/* which VBO {0...} to associate with */
+ int vidx;
+
+/* arguments for VertexAttrib[I]Pointer() */
+ int index;
+ int size;
+ int type;
+ int normalized;
+ int pointer;
+ int divisor;
+};
+
+struct PixelShader {
+ unsigned ps;
+ PixelShader() : ps(0) {};
+ ~PixelShader() { if (ps) glDeleteShader(ps); };
+};
+
+struct VertexShader {
+ unsigned vs;
+ GLAttrInfo *ai;
+ AttribInfo *info;
+ unsigned count;
+ VertexShader() : vs(0), ai(NULL), info(NULL), count(0) {};
+ ~VertexShader() { if (vs) { glDeleteShader(vs); free(ai); } };
+};
+
+struct Texture2D {
+ unsigned tex;
+ Texture2D() : tex(0) {};
+ ~Texture2D() { if (tex) { glDeleteTextures(1, &tex); } };
+};
+
+struct UniformBuffer {
+ unsigned buf;
+ unsigned sz;
+ UniformBuffer() : buf(0) {};
+ ~UniformBuffer() { if (buf) glDeleteBuffers(1, &buf); };
+};
+
+struct VertexBuffer {
+ unsigned buf;
+ unsigned sz;
+ VertexBuffer() : buf(0) {};
+ ~VertexBuffer() { if (buf) glDeleteBuffers(1, &buf); };
+};
+
+struct IndexBuffer {
+ unsigned buf;
+ unsigned sz;
+ IndexBuffer() : buf(0) {};
+ ~IndexBuffer() { if (buf) glDeleteBuffers(1, &buf); };
+};
+
+struct InputConfiguration {
+ PixelShader *ps;
+ VertexShader *vs;
+ unsigned vao;
+ unsigned pgm;
+ VertexBuffer *vbuf[16];
+ UniformBuffer *ubuf[16];
+ IndexBuffer *ibuf;
+ InputConfiguration() : ps(NULL), vs(NULL), vao(0), pgm(0), ibuf(NULL) {
+ memset(vbuf, 0, sizeof(vbuf)); memset(ubuf, 0, sizeof(ubuf)); };
+ ~InputConfiguration() { if (vao) glDeleteVertexArrays(1, &vao); if (pgm) glDeleteProgram(pgm); };
+};
+
+class App {
+public:
+ App();
+ virtual ~App();
+
+ virtual int init(void) = 0;
+ virtual void render(void) = 0;
+ virtual void release(void) {};
+
+ /* glue - do not use */
+ int start(void);
+ void handleEvents(void);
+
+ // TODO: move away from D3D10_INPUT...
+ int compileShader(VertexShader *vs, const char *fn,
+ void *data, unsigned len, int raw,
+ AttribInfo *layout, unsigned lcount);
+ int compileShader(PixelShader *ps, const char *fn,
+ void *data, unsigned len, int raw);
+ int loadShader(VertexShader *vs, const char *fn,
+ AttribInfo *layout, unsigned lcount);
+ int loadShader(PixelShader *ps, const char *fn);
+
+ int loadTextureRGBA(Texture2D *tex, const char *fn, int genmips);
+ int createTextureRGBA(Texture2D *tex, void *data, unsigned w, unsigned h, int genmips);
+
+ int initConfig(InputConfiguration *ic, VertexShader *vs, PixelShader *ps);
+ int initBuffer(VertexBuffer *vb, void *data, int sz);
+ int initBuffer(IndexBuffer *ib, void *data, int sz);
+ int initBuffer(UniformBuffer *ub, void *data, int sz);
+
+ void updateBuffer(VertexBuffer *vb, void *data);
+ void updateBuffer(IndexBuffer *ib, void *data);
+ void updateBuffer(UniformBuffer *ub, void *data);
+
+ void useConfig(InputConfiguration *ic);
+ void useBuffer(VertexBuffer *vb, int slot, unsigned stride, unsigned offset);
+ void useBuffer(IndexBuffer *ib);
+ void useBuffer(UniformBuffer *ub, int slot);
+ void useTexture(Texture2D *tex, int slot);
+ void drawIndexedInstanced(unsigned numindices, unsigned numinstances);
+ void drawInstanced(unsigned numvertices, unsigned numinstances);
+ void drawIndexed(unsigned numindices);
+
+protected:
+ int width;
+ int height;
+
+ /* mouse motion since last frame */
+ int mouseDX, mouseDY, mouseDZ;
+ /* mouse button state */
+ int mouseBTN;
+ /* mouse position in window coordinates */
+ int mouseWX, mouseWY;
+ /* keys down */
+ unsigned char keystate[256];
+
+ InputConfiguration *ic;
+
+private:
+ int _vsync;
+};
+
+App *createApp(void);
+
+void printx(const char *fmt, ...);
+void printmtx(float *m, const char *name);
+int error(const char *fmt, ...);
+
+#endif