Files
starruler-linux/source/game/render/gl_mesh.cpp
T
2018-07-17 14:15:37 +02:00

239 lines
5.7 KiB
C++

#include "compat/gl.h"
#include "vertex.h"
#include "vec3.h"
#include "mesh.h"
#include "render/render_mesh.h"
#include "render/gl_mesh.h"
#include "render/driver.h"
#include "main/references.h"
#include <assert.h>
namespace render {
bool vertexArraysAvailable = false, vertArraysChecked = false;
bool useVertexArrays() {
if(vertArraysChecked)
return vertexArraysAvailable;
vertexArraysAvailable = GLEW_ARB_vertex_array_object;
vertArraysChecked = true;
return vertexArraysAvailable;
}
const RenderMesh* lastRenderedMesh = 0;
class GLMesh : public RenderMesh {
bool valid;
public:
GLuint vertex_buffer;
GLuint element_buffer;
GLuint tangent_buffer;
GLuint color_buffer;
GLuint uv2_buffer;
GLuint vertex_array;
unsigned int vertices;
unsigned int faces;
AABBoxf bbox;
Mesh mesh;
double lod_distance;
const RenderMesh* lod;
GLMesh() : valid(false), lod_distance(1), lod(0), tangent_buffer(0), vertex_array(0), color_buffer(0), uv2_buffer(0) {
}
GLMesh(const Mesh& mesh) : valid(false), lod_distance(1), lod(0), tangent_buffer(0), vertex_array(0), color_buffer(0), uv2_buffer(0) {
resetToMesh(mesh);
}
~GLMesh() {
clear();
}
const Mesh& getMesh() const {
return mesh;
}
void clear() {
if(!valid)
return;
glDeleteVertexArrays(1, &vertex_array);
glDeleteBuffers(1, &vertex_buffer);
glDeleteBuffers(1, &element_buffer);
if(tangent_buffer != 0) {
glDeleteBuffers(1, &tangent_buffer);
tangent_buffer = 0;
}
if(color_buffer != 0) {
glDeleteBuffers(1, &color_buffer);
color_buffer = 0;
}
if(uv2_buffer != 0) {
glDeleteBuffers(1, &uv2_buffer);
uv2_buffer = 0;
}
valid = false;
}
void setupBuffers() const {
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,position));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,u));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,normal));
if(tangent_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, tangent_buffer);
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(vec4f), nullptr);
}
else {
glDisableVertexAttribArray(3);
}
if(color_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, color_buffer);
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(Colorf), nullptr);
}
else {
glDisableVertexAttribArray(4);
}
if(uv2_buffer) {
glBindBuffer(GL_ARRAY_BUFFER, uv2_buffer);
glEnableVertexAttribArray(5);
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(vec4f), nullptr);
}
else {
glDisableVertexAttribArray(5);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
}
void resetToMesh(const Mesh& mesh) {
this->mesh = mesh;
clear();
if(mesh.vertices.empty() || mesh.faces.empty())
return;
devices.render->reportErrors();
// Generate vertex buffer
vertices = (unsigned)mesh.vertices.size();
glGenBuffers(1, &vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, vertices * sizeof(Vertex),
&mesh.vertices[0], GL_STATIC_DRAW);
if(!mesh.tangents.empty()) {
glGenBuffers(1, &tangent_buffer);
glBindBuffer(GL_ARRAY_BUFFER, tangent_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.tangents.size() * sizeof(vec4f), mesh.tangents.data(), GL_STATIC_DRAW);
}
if(!mesh.colors.empty()) {
glGenBuffers(1, &color_buffer);
glBindBuffer(GL_ARRAY_BUFFER, color_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.colors.size() * sizeof(Colorf), mesh.colors.data(), GL_STATIC_DRAW);
}
if(!mesh.uvs2.empty()) {
glGenBuffers(1, &uv2_buffer);
glBindBuffer(GL_ARRAY_BUFFER, uv2_buffer);
glBufferData(GL_ARRAY_BUFFER, mesh.uvs2.size() * sizeof(vec4f), mesh.uvs2.data(), GL_STATIC_DRAW);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
// Generate element buffer
faces = (unsigned)mesh.faces.size();
glGenBuffers(1, &element_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, faces * sizeof(Mesh::Face),
&mesh.faces[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
devices.render->reportErrors("Filling mesh buffers");
if(useVertexArrays()) {
glGenVertexArrays(1, &vertex_array);
glBindVertexArray(vertex_array);
setupBuffers();
glBindVertexArray(0);
devices.render->reportErrors("Setting up mesh vertex array");
}
//Generate Bounding Box
for(unsigned i = 0; i < vertices; ++i)
bbox.addPoint(mesh.vertices[i].position);
valid = true;
}
const RenderMesh* selectLOD(double distance) const {
if(lod && distance > lod_distance)
return lod->selectLOD(distance);
return this;
}
void setLOD(double distance, const RenderMesh* mesh) {
lod_distance = distance;
lod = mesh;
}
const AABBoxf& getBoundingBox() const {
return bbox;
}
unsigned getMeshBytes() const {
if(!valid)
return 0;
return (vertices * sizeof(float) * 8) + (faces * 3 * sizeof(short));
}
void render() const {
if(lastRenderedMesh != this) {
//We only need to check valid here, because we can't be the rendered mesh if we aren't valid
if(!valid)
return;
lastRenderedMesh = this;
if(vertex_array)
glBindVertexArray(vertex_array);
else
setupBuffers();
}
glDrawElements(
GL_TRIANGLES, //Mode
3 * faces, //Total amount of vertices
GL_UNSIGNED_SHORT, //Index type
(void*)0 //Offset
);
}
};
RenderMesh* createGLMesh(const Mesh& mesh) {
return new GLMesh(mesh);
}
};