/** * Copyright (c) 2006-2017 LOVE Development Team * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. **/ // LOVE #include "Mesh.h" #include "common/Exception.h" #include "Shader.h" #include "graphics/Graphics.h" // C++ #include #include namespace love { namespace graphics { namespace opengl { Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, const void *data, size_t datasize, DrawMode drawmode, vertex::Usage usage) : love::graphics::Mesh(gfx, vertexformat, data, datasize, drawmode, usage) { } Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, int vertexcount, DrawMode drawmode, vertex::Usage usage) : love::graphics::Mesh(gfx, vertexformat, vertexcount, drawmode, usage) { } Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertices, DrawMode drawmode, vertex::Usage usage) : Mesh(gfx, getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage) { } Mesh::Mesh(graphics::Graphics *gfx, int vertexcount, DrawMode drawmode, vertex::Usage usage) : Mesh(gfx, getDefaultVertexFormat(), vertexcount, drawmode, usage) { } Mesh::~Mesh() { } int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname) { const AttribFormat &format = vertexFormat[attributeindex]; GLint attriblocation = -1; // If the attribute is one of the LOVE-defined ones, use the constant // attribute index for it, otherwise query the index from the shader. VertexAttribID builtinattrib; if (vertex::getConstant(inputname.c_str(), builtinattrib)) attriblocation = (GLint) builtinattrib; else if (Shader::current) attriblocation = ((Shader *) Shader::current)->getAttribLocation(inputname); // The active shader might not use this vertex attribute name. if (attriblocation < 0) return attriblocation; // Make sure the buffer isn't mapped (sends data to GPU if needed.) vbo->unmap(); gl.bindBuffer(BUFFER_VERTEX, (GLuint) vbo->getHandle()); const void *gloffset = BUFFER_OFFSET(getAttributeOffset(attributeindex)); GLenum datatype = getGLDataType(format.type); GLboolean normalized = (datatype == GL_UNSIGNED_BYTE); glVertexAttribPointer(attriblocation, format.components, datatype, normalized, (GLsizei) vertexStride, gloffset); return attriblocation; } void Mesh::drawInstanced(love::graphics::Graphics *gfx, const love::Matrix4 &m, int instancecount) { if (vertexCount <= 0 || instancecount <= 0) return; if (instancecount > 1 && !gl.isInstancingSupported()) throw love::Exception("Instancing is not supported on this system."); gfx->flushStreamDraws(); if (Shader::isDefaultActive()) Shader::attachDefault(Shader::STANDARD_DEFAULT); if (Shader::current && texture.get()) Shader::current->checkMainTexture(texture); OpenGL::TempDebugGroup debuggroup("Mesh draw"); uint32 enabledattribs = 0; uint32 instancedattribs = 0; for (const auto &attrib : attachedAttributes) { if (!attrib.second.enabled) continue; love::graphics::Mesh *mesh = attrib.second.mesh; int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first); if (location >= 0) { uint32 bit = 1u << (uint32) location; enabledattribs |= bit; if (attrib.second.step == STEP_PER_INSTANCE) instancedattribs |= bit; } } // Not supported on all platforms or GL versions, I believe. if (!(enabledattribs & ATTRIBFLAG_POS)) throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn."); gl.useVertexAttribArrays(enabledattribs, instancedattribs); gl.bindTextureToUnit(texture, 0, false); Graphics::TempTransform transform(gfx, m); gl.prepareDraw(); if (useIndexBuffer && ibo && elementCount > 0) { // Use the custom vertex map (index buffer) to draw the vertices. gl.bindBuffer(BUFFER_INDEX, (GLuint) ibo->getHandle()); // Make sure the index buffer isn't mapped (sends data to GPU if needed.) ibo->unmap(); int start = std::min(std::max(0, rangeStart), (int) elementCount - 1); int count = (int) elementCount; if (rangeCount > 0) count = std::min(count, rangeCount); count = std::min(count, (int) elementCount - start); size_t elementsize = vertex::getIndexDataSize(elementDataType); const void *indices = BUFFER_OFFSET(start * elementsize); GLenum type = OpenGL::getGLIndexDataType(elementDataType); if (count > 0) gl.drawElements(getGLDrawMode(drawMode), count, type, indices, instancecount); } else { int start = std::min(std::max(0, rangeStart), (int) vertexCount - 1); int count = (int) vertexCount; if (rangeCount > 0) count = std::min(count, rangeCount); count = std::min(count, (int) vertexCount - start); // Normal non-indexed drawing (no custom vertex map.) if (count > 0) gl.drawArrays(getGLDrawMode(drawMode), start, count, instancecount); } } GLenum Mesh::getGLDrawMode(DrawMode mode) { switch (mode) { case DRAWMODE_FAN: return GL_TRIANGLE_FAN; case DRAWMODE_STRIP: return GL_TRIANGLE_STRIP; case DRAWMODE_TRIANGLES: default: return GL_TRIANGLES; case DRAWMODE_POINTS: return GL_POINTS; } } GLenum Mesh::getGLDataType(DataType type) { switch (type) { case DATA_BYTE: return GL_UNSIGNED_BYTE; case DATA_FLOAT: return GL_FLOAT; default: return 0; } } } // opengl } // graphics } // love