diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 23fd0f169..73a8d7ea4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,7 @@ set(Magnum_SRCS AbstractObject.cpp AbstractShaderProgram.cpp Camera.cpp + Mesh.cpp Scene.cpp Shader.cpp ) diff --git a/src/Mesh.cpp b/src/Mesh.cpp new file mode 100644 index 000000000..6fd8148d7 --- /dev/null +++ b/src/Mesh.cpp @@ -0,0 +1,156 @@ +/* + Copyright © 2010 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "Mesh.h" +#include "Buffer.h" + +#include + +using namespace std; + +namespace Magnum { + +Mesh::~Mesh() { + for(map > >::iterator it = buffers.begin(); it != buffers.end(); ++it) + delete it->first; +} + +Buffer* Mesh::addBuffer(bool interleaved) { + Buffer* buffer = new Buffer(Buffer::ArrayBuffer); + buffers.insert(pair > >( + buffer, + pair >(interleaved, vector()) + )); + + return buffer; +} + +void Mesh::draw() { + /* Finalize the mesh, if it is not already */ + finalize(); + + /* Enable vertex arrays for all attributes */ + for(set::const_iterator it = attributes.begin(); it != attributes.end(); ++it) + glEnableVertexAttribArray(*it); + + /* Bind attributes to vertex buffers */ + for(map > >::iterator it = buffers.begin(); it != buffers.end(); ++it) { + /* Bind buffer */ + it->first->bind(); + + /* Bind all attributes to this buffer */ + for(vector::const_iterator ait = it->second.second.begin(); ait != it->second.second.end(); ++ait) + switch(ait->type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_INT: + case GL_UNSIGNED_INT: + glVertexAttribIPointer(ait->location, ait->size, ait->type, ait->stride, ait->pointer); + break; + default: + glVertexAttribPointer(ait->location, ait->size, ait->type, GL_FALSE, ait->stride, ait->pointer); + } + + /* Unbind buffer */ + it->first->unbind(); + } + + glDrawArrays(primitive, 0, count); + + /* Disable vertex arrays for all attributes */ + for(set::const_iterator it = attributes.begin(); it != attributes.end(); ++it) + glDisableVertexAttribArray(*it); +} + +void Mesh::finalize() { + /* Already finalized */ + if(finalized) return; + + /* Finalize attribute positions for every buffer */ + for(map > >::iterator it = buffers.begin(); it != buffers.end(); ++it) { + /* Avoid confustion */ + bool interleaved = it->second.first; + vector& attributes = it->second.second; + + /* Interleaved buffer, set stride and position of first attribute */ + if(interleaved) { + /* Set attribute position and compute stride */ + GLsizei stride = 0; + for(vector::iterator ait = attributes.begin(); ait != attributes.end(); ++ait) { + /* The attribute is positioned at the end of previous */ + ait->pointer = reinterpret_cast(stride); + + /* Add attribute size (per vertex) to stride */ + stride += ait->size*sizeOf(ait->type); + } + + /* Set computed stride for all attributes */ + for(vector::iterator ait = attributes.begin(); ait != attributes.end(); ++ait) + ait->stride = stride; + + /* Non-interleaved buffer, set position of every attribute */ + } else { + /* Set attribute position */ + GLsizei position = 0; + for(vector::iterator ait = attributes.begin(); ait != attributes.end(); ++ait) { + /* The attribute is positioned at the end of previous attribute array */ + ait->pointer = reinterpret_cast(position); + + /* Add attribute size (for all vertices) to position */ + position += ait->size*sizeOf(ait->type)*count; + } + } + } + + /* Mesh is now finalized, attribute binding is not allowed */ + finalized = true; +} + +void Mesh::bindAttribute(Buffer* buffer, GLuint attribute, GLint size, GLenum type) { + /* The mesh is finalized or attribute is already bound, nothing to do */ + if(finalized || attributes.find(attribute) != attributes.end()) return; + + /* If buffer is not managed by this mesh, nothing to do */ + map > >::iterator found = buffers.find(buffer); + if(found == buffers.end()) return; + + Attribute a; + a.location = attribute; + a.size = size; + a.type = type; + a.stride = 0; + a.pointer = 0; + + found->second.second.push_back(a); + attributes.insert(attribute); +} + +GLsizei Mesh::sizeOf(GLenum type) { + switch(type) { + case GL_BYTE: return sizeof(GLbyte); + case GL_UNSIGNED_BYTE: return sizeof(GLubyte); + case GL_SHORT: return sizeof(GLshort); + case GL_UNSIGNED_SHORT: return sizeof(GLushort); + case GL_INT: return sizeof(GLint); + case GL_UNSIGNED_INT: return sizeof(GLuint); + case GL_FLOAT: return sizeof(GLfloat); + case GL_DOUBLE: return sizeof(GLdouble); + default: return 0; + } +} + +} diff --git a/src/Mesh.h b/src/Mesh.h new file mode 100644 index 000000000..0d60012ae --- /dev/null +++ b/src/Mesh.h @@ -0,0 +1,200 @@ +#ifndef Magnum_Mesh_h +#define Magnum_Mesh_h +/* + Copyright © 2010 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::Mesh + */ + +#include +#include +#include + +#include "Magnum.h" + +namespace Magnum { + +class Buffer; + +/** + * @brief Base class for managing non-indexed meshes + * + * @todo Support for normalized values (e.g. for color as char[4] passed to + * shader as floating-point vec4) + */ +class Mesh { + public: + /** @brief Primitive type */ + enum Primitive { + /** + * Single points + */ + Points = GL_POINTS, + + /** + * Each pair of vertices defines a single line, lines aren't + * connected together. + */ + Lines = GL_LINES, + + /** + * Polyline + */ + LineStrip = GL_LINE_STRIP, + + /** + * Polyline, last vertex is connected to first. + */ + LineLoop = GL_LINE_LOOP, + + /** + * Each three vertices define one triangle. + */ + Triangles = GL_TRIANGLES, + + /** + * First three vertices define first triangle, each following + * vertex defines another triangle. + */ + TriangleStrip = GL_TRIANGLE_STRIP, + + /** + * First vertex is center, each following vertex is connected to + * previous and center vertex. + */ + TriangleFan = GL_TRIANGLE_FAN + }; + + /** + * @brief Constructor + * @param _primitive Primitive type + * @param _count Vertex count + */ + inline Mesh(Primitive _primitive, GLsizei _count): primitive(_primitive), count(_count), finalized(false) {} + + /** + * @brief Destructor + * + * Deletes all associated buffers. + */ + ~Mesh(); + + /** + * @brief Add buffer + * @param interleaved If storing more than one attribute data in the + * buffer, the data of one attribute can be either kept together + * or interleaved with data for another attributes, so data for + * every vertex will be in one continuous place. + * + * Adds new buffer to the mesh. The buffer can be then filled with + * Buffer::setData(). + */ + Buffer* addBuffer(bool interleaved); + + /** + * @brief Bind attribute + * @param buffer Buffer where bind the attribute to (pointer + * returned by addBuffer()) + * @param attribute Attribute + * + * Binds attribute of given type with given buffer. If the attribute is + * already bound, given buffer isn't managed with this mesh (wasn't + * initialized with addBuffer) or the mesh was already drawn, the + * function does nothing. + */ + template void bindAttribute(Buffer* buffer, GLuint attribute); + + /** + * @brief Draw a mesh + * + * Binds attributes to buffers and draws the mesh. Expects an active + * shader with all uniforms set. + */ + void draw(); + + protected: + /** + * @brief Finalize the mesh + * + * Computes location and stride of each attribute in its buffer. After + * this function is called, no new attribute can be bound. + */ + void finalize(); + + private: + struct Attribute { + GLuint location; + GLint size; + GLenum type; + GLsizei stride; + const GLvoid* pointer; + }; + + std::map > > buffers; + std::set attributes; + + GLenum primitive; + GLsizei count; + bool finalized; + + void bindAttribute(Buffer* buffer, GLuint attribute, GLint size, GLenum type); + + GLsizei sizeOf(GLenum type); +}; + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_BYTE); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_UNSIGNED_BYTE); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_SHORT); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_UNSIGNED_SHORT); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_INT); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_UNSIGNED_INT); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_FLOAT); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 1, GL_DOUBLE); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 3, GL_FLOAT); +} + +template<> inline void Mesh::bindAttribute(Buffer* buffer, GLuint attribute) { + bindAttribute(buffer, attribute, 4, GL_FLOAT); +} + +} + +#endif