//Build a Vertex Buffer from a mesh filter //Project: Open GL Framework //https://github.com/Illation/GLFramework //Author: Robert Lindner //Mesh filters cotain all the data loaded from 3d files like vertex positions, normal, texcoords etc //Various shaders can require defferent types of data, some form of uber shader might need most of the data a mesh has, // while an emissive shader only needs the positions and maybe the color, but doesnt really need normals //A material knows what data is required by the shader //Both the mesh filter and the material are known by a model component in the framework //When the model component renders, it needs a vertex buffer from the mesh filter that is suited to the material needs //the mesh filter can hold various buffers for different materials //******************* //Static Map containing all possible flags for the input layout //******************* enum VertexFlags { POSITION = 1 << 0, NORMAL = 1 << 1, BINORMAL = 1 << 2, TANGENT = 1 << 3, COLOR = 1 << 4, TEXCOORD = 1 << 5 }; class MeshFilter { public: //[...] static std::map LayoutAttributes; //[...] } std::map MeshFilter::LayoutAttributes = { { POSITION, { "position", GL_FLOAT, 3 } }, { NORMAL, { "normal", GL_FLOAT, 3 } }, { BINORMAL, { "binormal", GL_FLOAT, 3 } }, { TANGENT, { "tangent", GL_FLOAT, 3 } }, { COLOR, { "color", GL_FLOAT, 3 } }, { TEXCOORD, { "texcoord", GL_FLOAT, 2 } } }; //******************** //build an unsigned int containing all layout flags for a material //******************** void Material::SpecifyInputLayout() { unsigned stride = 0; for (auto it = MeshFilter::LayoutAttributes.begin(); it != MeshFilter::LayoutAttributes.end(); ++it) { if (m_LayoutFlags & it->first) stride += it->second.dataSize; } unsigned startPos = 0; for (auto it = MeshFilter::LayoutAttributes.begin(); it != MeshFilter::LayoutAttributes.end(); ++it) { if (m_LayoutFlags & it->first) { const char* name = it->second.name.c_str(); GLint attrib = glGetAttribLocation(m_Shader->GetProgram(), name); if (attrib >= 0) { glEnableVertexAttribArray(attrib); glVertexAttribPointer(attrib, it->second.dataSize, it->second.dataType, GL_FALSE, stride * sizeof(GLfloat), (void*)(startPos * sizeof(GLfloat))); startPos += it->second.dataSize; } else LOGGER::Log("Could not bind attribute '" + string(name) + "' to shader: " + m_Shader->GetName(), LogLevel::Error); } } } //******************* //Build a vertex buffer containing all the layout attributes required by a material //******************* void MeshFilter::BuildVertexBuffer(Material* pMaterial) { unsigned layoutFlags = pMaterial->GetLayoutFlags(); //Check if VertexBufferInfo already exists with requested InputLayout if (GetVertexObjectId(layoutFlags) >= 0) return; //Determine stride of vertex layout unsigned stride = 0; for (auto it = LayoutAttributes.begin(); it != LayoutAttributes.end(); ++it) { if (layoutFlags & it->first)//material requires this data { if (m_SupportedFlags & it->first)stride += it->second.dataSize;//filter can provide this data else { string FailString = "Failed to build vertex buffer, mesh filter cannot provide required data\n"; FailString += "required data: "; FailString += PrintFlags(layoutFlags); FailString += "\nprovided data: "; FailString += PrintFlags(m_SupportedFlags);FailString += "\n"; LOGGER::Log(FailString.c_str(), LogLevel::Error); return; } } } //Initialize datastructure std::vector vertices; vertices.reserve(m_VertexCount*stride); //Add data if material uses attribute for (size_t i = 0; i < m_VertexCount; i++) { if (layoutFlags & VertexFlags::POSITION) { vertices.push_back(m_Positions[i].x); vertices.push_back(m_Positions[i].y); vertices.push_back(m_Positions[i].z); } if (layoutFlags & VertexFlags::NORMAL) { vertices.push_back(m_Normals[i].x); vertices.push_back(m_Normals[i].y); vertices.push_back(m_Normals[i].z); } if (layoutFlags & VertexFlags::BINORMAL) { vertices.push_back(m_BiNormals[i].x); vertices.push_back(m_BiNormals[i].y); vertices.push_back(m_BiNormals[i].z); } if (layoutFlags & VertexFlags::TANGENT) { vertices.push_back(m_Tangents[i].x); vertices.push_back(m_Tangents[i].y); vertices.push_back(m_Tangents[i].z); } if (layoutFlags & VertexFlags::COLOR) { vertices.push_back(m_Colors[i].x); vertices.push_back(m_Colors[i].y); vertices.push_back(m_Colors[i].z); //vertices.push_back(m_Colors[i][3]);//not sure which variable to choose } if (layoutFlags & VertexFlags::TEXCOORD) { vertices.push_back(m_TexCoords[i].x); vertices.push_back(m_TexCoords[i].y); } } VertexObject obj; glGenVertexArrays(1, &obj.array); glBindVertexArray(obj.array); glBindBuffer(GL_ARRAY_BUFFER, obj.array); //Vertex Buffer Object glGenBuffers(1, &obj.buffer); glBindBuffer(GL_ARRAY_BUFFER, obj.buffer); glBufferData(GL_ARRAY_BUFFER, sizeof(float)*vertices.size(), vertices.data(), GL_STATIC_DRAW); //Specify Input Layout pMaterial->SpecifyInputLayout(); //index buffer glGenBuffers(1, &obj.index); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj.index); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(GLuint)*m_Indices.size(),m_Indices.data(),GL_STATIC_DRAW); //Add flags for later reference obj.flags = layoutFlags; //Add to VertexObject datastructure m_Objects.push_back(obj); }