// // Lol Engine // // Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net> // This program is free software; you can redistribute it and/or // modify it under the terms of the Do What The Fuck You Want To // Public License, Version 2, as published by Sam Hocevar. See // http://sam.zoy.org/projects/COPYING.WTFPL for more details. // #if defined HAVE_CONFIG_H # include "config.h" #endif #include <cstdlib> #include <cmath> #ifdef WIN32 # define WIN32_LEAN_AND_MEAN # include <windows.h> #endif #include "core.h" #include "lolgl.h" namespace lol { struct Tile { uint32_t prio, code; int x, y, z, o; }; #if defined HAVE_GL_2X || defined HAVE_GLES_2X extern Shader *stdshader; #endif extern mat4 model_matrix; /* * Scene implementation class */ class SceneData { friend class Scene; private: static int Compare(void const *p1, void const *p2) { Tile const *t1 = (Tile const *)p1; Tile const *t2 = (Tile const *)p2; return t2->prio - t1->prio; } Tile *tiles; int ntiles; float angle; #if defined HAVE_GL_2X GLuint vao; #endif GLuint *bufs; int nbufs; static Scene *scene; }; Scene *SceneData::scene = NULL; /* * Public Scene class */ Scene::Scene(float angle) : data(new SceneData()) { data->tiles = 0; data->ntiles = 0; data->angle = angle; data->bufs = 0; data->nbufs = 0; #if defined HAVE_GL_2X glGenVertexArrays(1, &data->vao); #endif } Scene::~Scene() { /* FIXME: this must be done while the GL context is still active. * Change the architecture to make sure of that. */ glDeleteBuffers(data->nbufs, data->bufs); #if defined HAVE_GL_2X glDeleteVertexArrays(1, &data->vao); #endif free(data->bufs); delete data; } Scene *Scene::GetDefault() { if (!SceneData::scene) SceneData::scene = new Scene(0.0f); return SceneData::scene; } void Scene::Reset() { if (SceneData::scene) delete SceneData::scene; SceneData::scene = NULL; } void Scene::AddTile(uint32_t code, int x, int y, int z, int o) { if ((data->ntiles % 1024) == 0) data->tiles = (Tile *)realloc(data->tiles, (data->ntiles + 1024) * sizeof(Tile)); /* FIXME: this sorting only works for a 45-degree camera */ data->tiles[data->ntiles].prio = -y - 2 * 32 * z + (o ? 0 : 32); data->tiles[data->ntiles].code = code; data->tiles[data->ntiles].x = x; data->tiles[data->ntiles].y = y; data->tiles[data->ntiles].z = z; data->tiles[data->ntiles].o = o; data->ntiles++; } void Scene::Render() // XXX: rename to Blit() { #if 0 // Randomise, then sort. for (int i = 0; i < data->ntiles; i++) { Tile tmp = data->tiles[i]; int j = rand() % data->ntiles; data->tiles[i] = data->tiles[j]; data->tiles[j] = tmp; } #endif qsort(data->tiles, data->ntiles, sizeof(Tile), SceneData::Compare); // XXX: debug stuff model_matrix = mat4::translate(320.0f, 240.0f, 0.0f); model_matrix *= mat4::rotate(-data->angle, 1.0f, 0.0f, 0.0f); #if 0 static float f = 0.0f; f += 0.01f; model_matrix *= mat4::rotate(0.1f * sinf(f), 1.0f, 0.0f, 0.0f); model_matrix *= mat4::rotate(0.3f * cosf(f), 0.0f, 0.0f, 1.0f); #endif model_matrix *= mat4::translate(-320.0f, -240.0f, 0.0f); // XXX: end of debug stuff #if defined HAVE_GL_2X || defined HAVE_GLES_2X GLuint uni_mat, uni_tex, attr_pos, attr_tex; attr_pos = stdshader->GetAttribLocation("in_Position"); attr_tex = stdshader->GetAttribLocation("in_TexCoord"); stdshader->Bind(); uni_mat = stdshader->GetUniformLocation("model_matrix"); glUniformMatrix4fv(uni_mat, 1, GL_FALSE, &model_matrix[0][0]); uni_tex = stdshader->GetUniformLocation("in_Texture"); glUniform1i(uni_tex, 0); glEnable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); # if !defined HAVE_GLES_2X glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GEQUAL, 0.01f); # endif glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); #else glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GEQUAL, 0.01f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMultMatrixf(&model_matrix[0][0]); /* Set up state machine */ glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnable(GL_TEXTURE_2D); glEnableClientState(GL_VERTEX_ARRAY); #endif for (int buf = 0, i = 0, n; i < data->ntiles; i = n, buf += 2) { /* Generate new vertex / texture coord buffers if necessary */ if (buf + 2 > data->nbufs) { data->bufs = (GLuint *)realloc(data->bufs, (buf + 2) * sizeof(GLuint)); glGenBuffers(buf + 2 - data->nbufs, data->bufs + data->nbufs); data->nbufs = buf + 2; } /* Count how many quads will be needed */ for (n = i + 1; n < data->ntiles; n++) if (data->tiles[i].code >> 16 != data->tiles[n].code >> 16) break; /* Create a vertex array object */ float *vertex = (float *)malloc(6 * 3 * (n - i) * sizeof(float)); float *texture = (float *)malloc(6 * 2 * (n - i) * sizeof(float)); for (int j = i; j < n; j++) { Tiler::BlitTile(data->tiles[j].code, data->tiles[j].x, data->tiles[j].y, data->tiles[j].z, data->tiles[j].o, vertex + 18 * (j - i), texture + 12 * (j - i)); } #if defined HAVE_GL_2X || defined HAVE_GLES_2X stdshader->Bind(); #endif /* Bind texture */ Tiler::Bind(data->tiles[i].code); /* Bind vertex, color and texture coordinate buffers */ #if defined HAVE_GL_2X || defined HAVE_GLES_2X # if !defined HAVE_GLES_2X glBindVertexArray(data->vao); # endif glEnableVertexAttribArray(attr_pos); glEnableVertexAttribArray(attr_tex); glBindBuffer(GL_ARRAY_BUFFER, data->bufs[buf]); glBufferData(GL_ARRAY_BUFFER, 18 * (n - i) * sizeof(GLfloat), vertex, GL_STATIC_DRAW); glVertexAttribPointer(attr_pos, 3, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, data->bufs[buf + 1]); glBufferData(GL_ARRAY_BUFFER, 12 * (n - i) * sizeof(GLfloat), texture, GL_STATIC_DRAW); glVertexAttribPointer(attr_tex, 2, GL_FLOAT, GL_FALSE, 0, 0); #elif defined HAVE_GL_1X glBindBuffer(GL_ARRAY_BUFFER, data->bufs[buf]); glBufferData(GL_ARRAY_BUFFER, 18 * (n - i) * sizeof(GLfloat), vertex, GL_STATIC_DRAW); glVertexPointer(3, GL_FLOAT, 0, NULL); glBindBuffer(GL_ARRAY_BUFFER, data->bufs[buf + 1]); glBufferData(GL_ARRAY_BUFFER, 12 * (n - i) * sizeof(GLfloat), texture, GL_STATIC_DRAW); glTexCoordPointer(2, GL_FLOAT, 0, NULL); #else glVertexPointer(3, GL_FLOAT, 0, vertex); glTexCoordPointer(2, GL_FLOAT, 0, texture); #endif /* Draw arrays */ glDrawArrays(GL_TRIANGLES, 0, (n - i) * 6); #if defined HAVE_GL_2X || defined HAVE_GLES_2X # if !defined HAVE_GLES_2X glBindVertexArray(0); # endif glDisableVertexAttribArray(attr_pos); glDisableVertexAttribArray(attr_tex); #endif free(vertex); free(texture); } #if defined HAVE_GL_1X || defined HAVE_GLES_1X /* Disable state machine features */ glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); /* Restore matrices */ glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); #endif free(data->tiles); data->tiles = 0; data->ntiles = 0; } } /* namespace lol */