I have encountered an strange issue with ASSIMP mesh load.
Here is the thing, I have a complex model obj file to show a character, this model has 4 child Meshes, and in my code, I encapsulate the Mesh class to get the relevant data(vertices, indices, normals...), When I draw the 4 meshes together(in a for circle), I always get the wrong result:
But if I only load one mesh a time(hard code my assimp load class to do that), and only draw the one mesh, then I can draw the mesh correctly(but this issue only occured when I load the complex model obj file, not with the cube or plane, I can load multiple cube and plane obj files and draw them together):
Here is my ASSIMP load class code, I don't know where my issues are, please help me:
#include "AssetImport.h"
#include "Mesh.h"
#include "Model.h"
#include "ModelMgr.h"
#include "TextureMgr.h"
#include <string>
using namespace Assimp;
AssetImport::AssetImport()
{
}
AssetImport::~AssetImport()
{
}
int AssetImport::LoadModel(const char *path)
{
Importer import;
const aiScene *scene = import.ReadFile(path, aiProcess_Triangulate
| aiProcess_FlipUVs
| aiProcess_CalcTangentSpace
| aiProcess_GenNormals);
if (!scene || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
return -1;
}
m_prePath = path;
auto index = m_prePath.lastIndexOf("/");
if (-1 != index) {
m_prePath = m_prePath.left(index + 1);
}
HandleChildNode(scene, scene->mRootNode);
return 0;
}
int AssetImport::HandleChildNode(const aiScene *scene, aiNode *node)
{
unsigned int numChild = node->mNumChildren;
for (unsigned int a = 0; a < 1; ++a)
{
**auto child = node->mChildren[3];// here is the hard code**
if (child->mNumChildren > 0) {
HandleChildNode(scene, child);
}
if (0) {
continue;
}
// create a new model for a child
Model *mod = ModelMgr::Instance().CreateNewModel();
mod->SetModelName(child->mName.C_Str());
auto numMeshes = child->mNumMeshes;
for (unsigned int i = 0; i < numMeshes; ++i)
{
// create new sub mesh, then you can add it to one model
Mesh *m = new Mesh;
auto mesh = scene->mMeshes[child->mMeshes[i]];
//handle the mesh's materials
auto mat = scene->mMaterials[mesh->mMaterialIndex];
HandleMeshMaterial(mat, m);
// add vertex info
for (unsigned int j = 0; j < mesh->mNumVertices; ++j)
{
auto vert = mesh->mVertices[j];
Mesh::VertInfo info;
info.pos = QVector3D(mesh->mVertices[j].x, mesh->mVertices[j].y, mesh->mVertices[j].z);
info.color = QVector4D(0.8f, 0.8f, 0.8f, 1);
info.normal = QVector3D(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z);
if (nullptr != mesh->mTangents)
{
info.tangent = QVector4D(mesh->mTangents[j].x, mesh->mTangents[j].y, mesh->mTangents[j].z, 0);
}
if (nullptr != mesh->mBitangents)
{
info.bitangent = QVector4D(mesh->mBitangents[j].x, mesh->mBitangents[j].y, mesh->mBitangents[j].z, 0);
}
if (nullptr != mesh->mTextureCoords[0])
{
info.uv1 = QVector2D(mesh->mTextureCoords[0][j].x, mesh->mTextureCoords[0][j].y);
}
m->AddVertInfo(info);
}
// add index info
for (unsigned int j = 0; j < mesh->mNumFaces; ++j)
{
auto face = mesh->mFaces[j];
for (unsigned int k = 0; k < face.mNumIndices; ++k)
{
m->AddIndex(face.mIndices[k]);
}
}
m->BindBuffer();
mod->AddMesh(m);
}
}
return 0;
}
int AssetImport::HandleMeshMaterial(aiMaterial *mat, Mesh *mesh)
{
auto diffuseTexCount = mat->GetTextureCount(aiTextureType_DIFFUSE);
auto specularTexCount = mat->GetTextureCount(aiTextureType_SPECULAR);
for (unsigned int i = 0; i < diffuseTexCount; ++i)
{
aiString path;
mat->GetTexture(aiTextureType_DIFFUSE, i, &path);
QString strPath = path.C_Str();
strPath.replace("/", "\\");
auto index = strPath.lastIndexOf("\\");
if (-1 != index) {
strPath = strPath.right(strPath.length() - index - 1);
strPath.prepend(m_prePath);
}
auto texID = TextureMgr::Instance().LoadTexture(strPath.toLocal8Bit());
mesh->SetDiffuseTexID(texID);
}
for (unsigned int i = 0; i < specularTexCount; ++i)
{
aiString strPath;
mat->GetTexture(aiTextureType_SPECULAR, i, &strPath);
}
return 0;
}
Here is my Mesh Class(with Draw Function):
#include "Mesh.h"
#include "TextureMgr.h"
#include "ShaderHelper.h"
#include "PreDef.h"
#include "QMatrix4x4"
Mesh::Mesh()
:m_vao(0), m_vbo(0), m_vaeo(0), m_instanceBufferId(0), m_tbo1(0)
, m_drawType(Triangle)
, m_skyboxTexID(0), m_projTexID(0), m_normalmapTexID(0)
, m_diffuseTex1ID(0)
{
initializeOpenGLFunctions();
m_normalmapTexID = TextureMgr::Instance().LoadTexture("./models/brickwall_normal.jpg");
}
Mesh::~Mesh()
{
}
void Mesh::AddVertex(QVector3D vert)
{
m_vertices.push_back(vert.x());
m_vertices.push_back(vert.y());
m_vertices.push_back(vert.z());
}
const float* Mesh::GetVertices() const
{
return m_vertices.data();
}
int Mesh::GetVerticesMemSize() const
{
return m_vertices.size() * sizeof(float);
}
int Mesh::GetVerticesNum() const
{
return m_vertices.size() / 3;
}
void Mesh::AddColor(QVector4D color)
{
m_colors.push_back(color.x());
m_colors.push_back(color.y());
m_colors.push_back(color.z());
m_colors.push_back(color.w());
}
const float* Mesh::GetColors() const
{
return m_colors.data();
}
int Mesh::GetColorsMemSize() const
{
return m_colors.size() * sizeof(float);
}
void Mesh::AddNormal(QVector3D normal)
{
m_normals.append(normal.x());
m_normals.append(normal.y());
m_normals.append(normal.z());
}
const float* Mesh::GetNormals() const
{
return m_normals.data();
}
int Mesh::GetNormalsMemSize() const
{
return m_normals.size() * sizeof(float);
}
int Mesh::GetNormalsNum() const
{
return m_normals.size() / 3;
}
void Mesh::AddTangent(QVector4D tangent)
{
m_tangents.append(tangent.x());
m_tangents.append(tangent.y());
m_tangents.append(tangent.z());
m_tangents.append(tangent.w());
}
const float* Mesh::GetTangents() const
{
return m_tangents.data();
}
int Mesh::GetTangentsMemSize() const
{
return m_tangents.size() * sizeof(float);
}
int Mesh::GetTangentsNum() const
{
return m_tangents.size() / 4;
}
void Mesh::AddBitangent(QVector4D binormal)
{
m_binormals.append(binormal.x());
m_binormals.append(binormal.y());
m_binormals.append(binormal.z());
m_binormals.append(binormal.w());
}
const float* Mesh::GetBitangents() const
{
return m_binormals.data();
}
int Mesh::GetBitangentsMemSize() const
{
return m_binormals.size() * sizeof(float);
}
int Mesh::GetBitangentsNum() const
{
return m_binormals.size() / 4;
}
void Mesh::AddUv1(QVector2D uv)
{
m_uvs1.append(uv.x());
m_uvs1.append(uv.y());
}
const float* Mesh::GetUvs1() const
{
return m_uvs1.data();
}
int Mesh::GetUvs1MemSize() const
{
return m_uvs1.size() * sizeof(float);
}
int Mesh::GetUvs1Num() const
{
return m_uvs1.size() / 2;
}
void Mesh::AddVertInfo(const VertInfo &info)
{
m_vertInfoVec.push_back(info);
AddVertex(info.pos);
AddColor(info.color);
AddNormal(info.normal);
AddTangent(info.tangent);
AddBitangent(info.bitangent);
AddUv1(info.uv1);
}
void Mesh::BindBuffer()
{
// init the shaders first
ShaderHelper::Instance();
BindVertexRelevantBuffer();
}
void Mesh::BindVertexRelevantBuffer()
{
const GLuint *vertex_indices = GetIndices();
const GLfloat *vertex_positions = GetVertices();
const GLfloat *vertex_uvs = GetUvs1();
const GLfloat *vertex_tangents = GetTangents();
const GLfloat *vertex_bitangents = GetBitangents();
const GLfloat *vertex_normals = GetNormals();
// set element array(index array) buffer
glGenBuffers(1, &m_vaeo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vaeo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, GetIndicesMemSize(), vertex_indices, GL_STATIC_DRAW);
// set vertex array object
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glVertexArrayElementBuffer(m_vao, m_vaeo);
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize()
+ GetTangentsMemSize() + GetBitangentsMemSize() + GetNormalsMemSize() /*+ 16 * sizeof(GLfloat) * 10*/, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, GetVerticesMemSize(), vertex_positions);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize(), GetUvs1MemSize(), vertex_uvs);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize(), GetTangentsMemSize(), vertex_tangents);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize(),
GetBitangentsMemSize(), vertex_bitangents);
glBufferSubData(GL_ARRAY_BUFFER, GetVerticesMemSize() + GetUvs1MemSize()
+ GetTangentsMemSize() + GetBitangentsMemSize(),
GetNormalsMemSize(), vertex_normals);
GLint positionLoc = 0;
GLint uvLoc = 1;
GLint tangentLoc = 2;
GLint bitangentLoc = 3;
GLint normalLoc = 4;
GLint modelMatLoc = -1;
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, (0));
glEnableVertexAttribArray(positionLoc);
glVertexAttribPointer(uvLoc, 2, GL_FLOAT, GL_FALSE, 0, (void*)(GetVerticesMemSize()));
glEnableVertexAttribArray(uvLoc);
glVertexAttribPointer(tangentLoc, 3, GL_FLOAT, GL_FALSE, 0, (void*)(GetVerticesMemSize() + GetUvs1MemSize()));
glEnableVertexAttribArray(tangentLoc);
glVertexAttribPointer(bitangentLoc, 3, GL_FLOAT, GL_FALSE, 0,
(void*)(GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize()));
glEnableVertexAttribArray(bitangentLoc);
glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0,
(void*)(GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize() + GetBitangentsMemSize()));
glEnableVertexAttribArray(normalLoc);
// multi instances model matrix
if (-1 != modelMatLoc) {
// generate the new buffer for instances
glBindBuffer(GL_ARRAY_BUFFER, 0);
glGenBuffers(1, &m_instanceBufferId);
glBindBuffer(GL_ARRAY_BUFFER, m_instanceBufferId);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 16 * 10, nullptr, GL_STATIC_DRAW);
// m_instanceBufferId = m_vbo;
// mat4 type take space of 4 vec4, so we should circle 4 times
for (int i = 0; i < 4; ++i)
{
glVertexAttribPointer(modelMatLoc + i, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 16,
(void*)(/*GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize() + GetBitangentsMemSize() + GetNormalsMemSize() +*/ sizeof(GLfloat) * 4 * i));
glEnableVertexAttribArray(modelMatLoc + i);
// implement the multi instances
glVertexAttribDivisor(modelMatLoc + i, 1);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
// sampler buffer bind
glGenBuffers(1, &m_tbo1);
glBindBuffer(GL_TEXTURE_BUFFER, m_tbo1);
glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 16 * 10, nullptr, GL_STATIC_DRAW);
GLuint tex1;
glCreateTextures(GL_TEXTURE_BUFFER, 1, &tex1);
glTextureBuffer(tex1, GL_RGBA32F, m_tbo1);
glBindTextureUnit(0, m_tbo1);
glBindBuffer(GL_TEXTURE_BUFFER, 0);
}
GLuint Mesh::GetVao()
{
return m_vao;
}
void Mesh::AddIndex(unsigned int index)
{
m_indices.push_back(index);
}
void Mesh::AddIndexVec(QVector<unsigned int> &indexVec)
{
m_indices = indexVec;
}
const unsigned int* Mesh::GetIndices() const
{
return m_indices.data();
}
int Mesh::GetIndicesMemSize() const
{
return m_indices.size() * sizeof(unsigned int);
}
int Mesh::GetIndicesNum() const
{
return m_indices.size();
}
GLuint Mesh::GetMultiInstanceModelMatrixOffset() const
{
return (GetVerticesMemSize() + GetUvs1MemSize() + GetTangentsMemSize()
+ GetBitangentsMemSize() + GetNormalsMemSize());
}
GLuint Mesh::GetInstancesBufferId() const
{
return m_instanceBufferId;
}
void Mesh::SetDiffuseTexID(GLuint id)
{
m_diffuseTex1ID = id;
}
void Mesh::AddSpecularTexture(GLuint id)
{
m_specularTex1ID = id;
}
GLuint Mesh::GetTextureBuffer1() const
{
return m_tbo1;
}
void Mesh::InitSkybox()
{
m_skyboxTexID = TextureMgr::Instance().LoadTexture("skybox");
}
void Mesh::InitProjTex()
{
m_projTexID = TextureMgr::Instance().LoadTexture("./textures/proj.jpg");
}
void Mesh::SetDrawType(eDrawType type)
{
m_drawType = type;
}
void Mesh::Draw(QMatrix4x4 matVP, QMatrix4x4 matModel, QVector3D camPos, QMatrix4x4 matProj, QMatrix4x4 matView,
QMatrix4x4 matOrtho)
{
if (0 != m_diffuseTex1ID)
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_diffuseTex1ID);
}
if (0 != m_normalmapTexID)
{
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_normalmapTexID);
}
if (0 != m_skyboxTexID) {
glBindTexture(GL_TEXTURE_CUBE_MAP, m_skyboxTexID);
matModel.translate(camPos);
matModel.scale(10000);
glCullFace(GL_FRONT);
glDepthMask(0);
ShaderHelper::Instance().SetShaderType(ShaderHelper::Skybox);
}
else if (0 != m_projTexID) {
glActiveTexture(GL_TEXTURE10);
glBindTexture(GL_TEXTURE_2D, m_projTexID);
ShaderHelper::Instance().SetShaderType(ShaderHelper::Decal);
}
glBindVertexArray(GetVao());
matVP = matVP * matModel;
ShaderHelper::Instance().SetMVPMatrix(matVP);
ShaderHelper::Instance().SetWorldMatrix(matModel);
ShaderHelper::Instance().SetCamWorldPos(camPos);
ShaderHelper::Instance().SetProjMat(matProj);
ShaderHelper::Instance().SetViewMat(matView);
ShaderHelper::Instance().SetOrthoMat(matOrtho);
// Draw element(with indices)
if (Triangle == m_drawType)
{
glDrawElements(GL_TRIANGLES, GetIndicesNum(), GL_UNSIGNED_INT, 0);
}
else if (Point == m_drawType)
{
glDrawArrays(GL_POINTS, 0, GetVerticesNum());
}
if (0 != m_skyboxTexID) {
glCullFace(GL_BACK);
glDepthMask(1);
}
glBindVertexArray(0);
}
Here is the main draw function:
void OpenWidget::paintGL()
{
static int i = 0;
if (0 == i){
++i;
CreateOffScreenFrameBufferTexture();
}
glBindFramebuffer(GL_FRAMEBUFFER, m_fb);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
return;
}
paintClearAndReset();
QMatrix4x4 matVP = m_cam->GetVPMatrix();
QMatrix4x4 matProj = m_cam->GetProjectionMatrix();
QMatrix4x4 matOrtho = m_cam->GetOrthographicMatrix();
QMatrix4x4 matView = m_cam->GetViewMatrix();
QVector3D camPos = m_cam->GetCamPos().toVector3D();
auto modelNum = ModelMgr::Instance().GetModelNum();
for (unsigned int i = 0; i < modelNum; ++i)
{
Model *mod = ModelMgr::Instance().GetModel(i);
SwitchShader(ShaderHelper::Default);
QMatrix4x4 matModel = mod->GetWorldMat();
mod->Draw(matVP, matModel, camPos, matProj, matView, matOrtho);
if (modelNum - 1 == i) {
Model *pBox = ModelMgr::Instance().FindModelByName("Box001");
// SwitchShader(ShaderHelper::PlaneClip);
// glFrontFace(GL_CW);
// glEnable(GL_CLIP_PLANE0);
// glStencilMask(0xff);
// glStencilFunc(GL_ALWAYS, 1, 0xff);
// glColorMask(0, 0, 0, 0);
//
// pBox->Draw(matVP, pBox->GetWorldMat(), camPos);
// glFrontFace(GL_CCW);
// glDisable(GL_CLIP_PLANE0);
// glColorMask(1, 1, 1, 1);
//
// glStencilMask(0);
// glStencilFunc(GL_EQUAL, 1, 0xff);
// SwitchShader(ShaderHelper::PureColor);
// pBox->Draw(matVP, pBox->GetWorldMat(), camPos);
}
}
//-----------------------------------------------
// test the frame buffer
DrawOffScreenTexture();
}
Finally, I found where the problem is, I give up on this issue for a while, but today, I encountered another problem with my skybox(cubemap), I can only draw my skybox correctly at the last of my draw queue, otherwise the cubemap only show one face, so, no shortcut for me anymore, I decided to resolve this issue, after many hours debug, I found this issue caused by the create sequence of VAO and ElementBuffer, here is the wrong code:
// set element array(index array) buffer
glGenBuffers(1, &m_vaeo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vaeo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, GetIndicesMemSize(), vertex_indices, GL_STATIC_DRAW);
// set vertex array object
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glVertexArrayElementBuffer(m_vao, m_vaeo);
you should always create and bind VAO first:
// set vertex array object
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// set element array(index array) buffer
glGenBuffers(1, &m_vaeo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vaeo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, GetIndicesMemSize(), vertex_indices, GL_STATIC_DRAW);
glVertexArrayElementBuffer(m_vao, m_vaeo);
But, after all, I think it's so implicit, because of you can bind the element to vao after you create the element buffer, it seems like when you gen the element buffer it's nothing to do with the vao, what I can do, only be careful next time.
Related
I am trying to render a cylinder in OpenGL, by first starting to render top and lower caps using TRIANGLE_FAN. I store all the vertex data in one VBO and create two index buffers one for each cap. The top cap gets rendered correctly however the low cap does not get rendered at all, you can see it in the snapshot.
Here is my client application.
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/vec3.hpp> // glm::vec3
#include <glm/vec4.hpp> // glm::vec4
#include <glm/mat4x4.hpp> // glm::mat4
#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
using namespace std;
#define PI 3.14159265359
static string ParseShader(string filepath) {
ifstream stream(filepath);
string line;
stringstream stringStream;
while (getline(stream, line))
{
stringStream << line << '\n';
}
return stringStream.str();
}
static unsigned int CompileShader(unsigned int type, const string& source) {
unsigned int id = glCreateShader(type);
const char* src = source.c_str(); // this returns a pointer to data inside the string, the first character
glShaderSource(id, 1, &src, nullptr); // shader id, count of source codes, a pointer to the array that holds the strings
glCompileShader(id);
int result;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) {
int length;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
char* message = (char*)alloca(length * sizeof(char));
glGetShaderInfoLog(id, length, &length, message);
cout << type << endl;
cout << message << endl;
glDeleteShader(id);
return 0;
}
return id;
}
// takes the shader codes as a string parameters
static unsigned int CreateShader(const string& vertexShader, const string& fragmentShader)
{
GLuint program = glCreateProgram();
unsigned int vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
unsigned int fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
glAttachShader(program, vs);
glAttachShader(program, fs);
glLinkProgram(program);
glValidateProgram(program); // validate if the program is valid and can be run in the current state of opengl
glDeleteShader(vs);
glDeleteShader(fs);
return program;
}
std::vector<float> GenerateVertexData(float radius, float cylinderHeight, int vertexCount) {
int bufferSize = 3 * vertexCount;
std::vector<float> result(3 * vertexCount);
result[0] = 0;
result[1] = 0;
result[2] = cylinderHeight / 2;
float angleDelta = (360 / (vertexCount - 1)) * PI / 180;
float currentAngle = 0;
for (int i = 1; i < vertexCount; ++i) {
result[ 3 * i ] = cos(currentAngle) * radius;
result[3 * i + 1] = sin(currentAngle) * radius;
result[3 * i + 2] = cylinderHeight / 2;
currentAngle += angleDelta;
}
for (int i = 0; i < bufferSize; ++i) {
std::cout << "Result [ " << i << " ]" << result[i] << std::endl;
}
return result;
}
std::vector<GLuint> GenerateIndexData(int vertexCount) {
std::vector<GLuint> result(vertexCount + 1);
for (int i = 0; i < vertexCount; ++i) {
result[i] = i;
}
result[vertexCount] = 1;
return result;
}
std::vector<GLuint> GenerateLowCapIndexData(int vertexCount) {
std::vector<GLuint> result(vertexCount + 1);
for (int i = 0; i < vertexCount; ++i) {
result[i] = i + vertexCount;
std::cout << "Index [ " << i << " ]" << result[i] << std::endl;
}
result[vertexCount] = 1 + vertexCount;
std::cout << "Index [ " << result.size() << " ]" << result.back() << std::endl;
return result;
}
std::vector<GLuint> generateBodyIndexData(int verticalSegments) {
std::vector<GLuint> result(2 * (verticalSegments + 1));
for (int i = 0; i < verticalSegments; ++i) {
result[ 2 * i ] = i;
result[2 * i + 1] = i + verticalSegments;
}
result[result.size() - 2] = result.front();
result.back() = result[1];
return result;
}
int main(int argc, char* argv[]) {
float radius = std::stof(argv[1]);
float cylinderHeight = std::stof(argv[2]);
int verticalSegments = std::stoi(argv[3]);
std::cout << "Vertical segments : " << verticalSegments << std::endl;
int totalVertexCount = verticalSegments + 1;
const int vertexCoordinateCount = 3;
std::vector<float> vertexData = GenerateVertexData(radius, cylinderHeight, totalVertexCount);
std::vector<float> vertexDataLowerHalf = GenerateVertexData(radius, -cylinderHeight, totalVertexCount);
appendVec(vertexData, vertexDataLowerHalf);
std::vector<GLuint> topCapIndexData = GenerateIndexData(totalVertexCount);
std::vector<GLuint> lowCapIndexData = GenerateLowCapIndexData(totalVertexCount);
GLFWwindow* window;
/* Initialize the library */
if (!glfwInit())
return -1;
window = glfwCreateWindow(640, 480, "HW3", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
if (glfwRawMouseMotionSupported())
glfwSetInputMode(window, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
glfwMakeContextCurrent(window);
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
}
//Positions
GLuint positionBufferHandle;
glGenBuffers(1, &positionBufferHandle);
glBindBuffer(GL_ARRAY_BUFFER, positionBufferHandle);
glBufferData(GL_ARRAY_BUFFER, vertexData.size() * sizeof(float), vertexData.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, vertexCoordinateCount, GL_FLOAT, GL_FALSE, sizeof(float) * vertexCoordinateCount, 0);
glEnableVertexAttribArray(0);
//TopCap Indices
GLuint indexBufferHandle;
glGenBuffers(1, &indexBufferHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, topCapIndexData.size() * sizeof(GLuint), topCapIndexData.data(), GL_STATIC_DRAW);
//LowCap Indices
GLuint lowCapIndexBufferHandle;
glGenBuffers(1, &lowCapIndexBufferHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lowCapIndexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lowCapIndexData.size() * sizeof(GLuint), lowCapIndexData.data(), GL_STATIC_DRAW);
string vertexSource = ParseShader("vertex.shader");
string fragmentSource = ParseShader("fragment.shader");
unsigned int program = CreateShader(vertexSource, fragmentSource);
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
// Render here
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(program);
glDrawElements(GL_TRIANGLE_FAN, topCapIndexData.size() + lowCapIndexData.size(), GL_UNSIGNED_INT, nullptr);
//Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
glDeleteProgram(program);
glfwTerminate();
return 0;
}
I have checked my vertex data generating functions and they are correct.
Here is the snapshot of my result.
Only 1 index buffer can be bound at a time. You need 2 draw calls and must bind the index buffer before the draw call:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glDrawElements(GL_TRIANGLE_FAN, topCapIndexData.size(), GL_UNSIGNED_INT, nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lowCapIndexBufferHandle);
glDrawElements(GL_TRIANGLE_FAN, lowCapIndexData.size(), GL_UNSIGNED_INT, nullptr);
Alternatively you can put the list of indices in 1 buffer and separate them with a Primitive Restart index.
GLuint restartIndex = 0xffffffff;
std::vector<GLuint> indices = topCapIndexData;
indices.push_back(restartIndex);
indices.insert(indices.end(), lowCapIndexBufferHandle.begin(), lowCapIndexBufferHandle.end();
GLuint indexBufferHandle;
glGenBuffers(1, &indexBufferHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferHandle);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW);
glEnable(GL_PRIMITIVE_RESTART);
glPrimitiveRestartIndex(restartIndex);
glDrawElements(GL_TRIANGLE_FAN, indices.size(), GL_UNSIGNED_INT, nullptr);
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I am building the game Snake using OpenGL in VS. I have my class that handles the models, VertexData, and then the rest of the game in Main. Whenever I create the first object by calling createModel(), everything works fine. However whenever I call createModel() the second time, like to create the apple, there is about a 40% chance of Visual Studio triggering a breakpoint. everytime I call createModel() after that, like to create another body segment, the chances of it triggering a breakpoint increases. Is it due to where I am creating the object, or is there something else wrong? I saw that adding a copy constructor to the VertexData class would stop the crashing but it did not.
Main.cpp:
//includes
#include "convertToFloat.h"
#include "vertexData.h"
#include <iostream>
#include <vector>
#include <time.h>
//function prototypes
static void error_callback(int error, const char* description);
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void initWindow();
void destroy();
void changeLocation();
void update();
void render();
void loadModels();
void onStartUp();
void onCollect();
void createModel();
int roundUp(int numToRound, int multiple);
int roundDown(int numToRound, int multiple);
//object declerations
GLFWwindow* window;
//variables
int x = 200;
int y = 200;
int appleLoc[2] = { x,y };
int direction = 0;
int stepSize = 20;
bool start = false;
static double limitFPS = 1.0 / 15.0;
double lastTime = glfwGetTime(), timer = lastTime;
double deltaTime = 0, nowTime = 0;
int frames = 0, updates = 0;
std::vector<std::shared_ptr<VertexData>> models;
int main(void)
{
initWindow();
loadModels();
onStartUp();
while (!glfwWindowShouldClose(window))
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
nowTime = glfwGetTime();
deltaTime += (nowTime - lastTime) / limitFPS;
lastTime = nowTime;
while (deltaTime >= 1.0) {
update();
updates++;
deltaTime--;
}
render();
frames++;
if (glfwGetTime() - timer > 1.0) {
timer++;
updates = 0, frames = 0;
}
glfwSwapBuffers(window);
glfwPollEvents();
}
destroy();
}
void onCollect() {
appleLoc[0] = roundUp(rand() % 620, 20);
appleLoc[1] = roundUp(rand() % 460, 20);
//models.at(1)->move(appleLoc[0], appleLoc[1]);
//createModel();
}
void onStartUp() {
srand(time(0));
onCollect();
}
void createModel() {
std::shared_ptr<VertexData> model{ new VertexData("models/snakeHead.md",640,480) };
models.push_back(model);
}
void loadModels() {
createModel();
createModel();
}
void changeLocation() {
switch (direction) {
case(0):
if(y<460)
y += stepSize;
break;
case(1):
if (x < 620)
x += stepSize;
break;
case(2):
if (y > 0)
y -= stepSize;
break;
case(3):
if (x > 0)
x -= stepSize;
break;
}
std::cout << x << " " << y << std::endl;
std::cout << appleLoc[0] << " " << appleLoc[1] << std::endl;
}
void render() {
for(int i=0; i<models.size();i++)
models.at(i)->render();
}
void update() {
if (start)
changeLocation();
models.at(0)->move(x, y);
if (x == appleLoc[0] && y == appleLoc[1]) {
onCollect();
}
}
void initWindow() {
if (!glfwInit())
exit(EXIT_FAILURE);
window = glfwCreateWindow(640, 480, "Snek", NULL, NULL);
if (!window)
{
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(window);
gladLoadGL(glfwGetProcAddress); //important
glfwSwapInterval(1);
glfwSetErrorCallback(error_callback);
glfwSetKeyCallback(window, key_callback);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
}
static void error_callback(int error, const char* description)
{
fprintf(stderr, "Error: %s\n", description);
}
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GLFW_TRUE);
if (key == GLFW_KEY_W && action == GLFW_PRESS) {
direction = 0;
start = true;
}
if (key == GLFW_KEY_S && action == GLFW_PRESS){
direction = 2;
start = true;
}
if (key == GLFW_KEY_A && action == GLFW_PRESS){
direction = 3;
start = true;
}
if (key == GLFW_KEY_D && action == GLFW_PRESS){
direction = 1;
start = true;
}
}
int roundUp(int numToRound, int multiple)
{
if (multiple == 0)
return numToRound;
int remainder = numToRound % multiple;
if (remainder == 0)
return numToRound;
return numToRound + multiple - remainder;
}
int roundDown(int numToRound, int multiple)
{
if (multiple == 0)
return numToRound;
int remainderInverseSorta = multiple-(numToRound % multiple);
if (remainderInverseSorta == 0)
return numToRound;
return numToRound - multiple + remainderInverseSorta;
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
void destroy() {
for (int i = 0; i < models.size(); i++)
models.at(i)->destroy();
glfwDestroyWindow(window);
glfwTerminate();
exit(EXIT_SUCCESS);
}
vertexdata.h:
#ifndef vertextData
#define vertexData
#define GLFW_INCLUDE_NONE
#include "loadFile.h"
#include "convertToFloat.h"
#include "shaderLoader.h"
#include <GLFW/glfw3.h>
#include <glad/gl.h> // include glad to get all the required OpenGL headers
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
class VertexData {
private:
unsigned int VAO,VBO,EBO;
int width = 0;
int height = 0;
std::unique_ptr <Shader> shader{ new Shader("Shaders/3.3.shader.vs", "Shaders/3.3.shader.fs") }; //add shader path to constructor
glm::mat4 trans = glm::mat4(1.0f);
public:
VertexData(const char* modelPath,int width,int height);
VertexData(const VertexData& data);
void render();
void move(int x, int y);
void rotate(int deg);
void destroy();
};
#endif
vertexData.cpp:
#include "vertexData.h"
VertexData::VertexData(const char* modelPath, int width, int height) {
this->width = width;
this->height = height;
std::unique_ptr<ConvertToFloat> conversion{ new ConvertToFloat(width, height) };
std::unique_ptr<LoadFile> file{ new LoadFile() };
std::stringstream modelStream;
std::string substr;
modelStream = file->load(modelPath);
std::getline(modelStream, substr, ',');
int numVertices = stoi(substr);
float* vertices = new float[numVertices*8];
std::getline(modelStream, substr, '\n');
int numIndices = stoi(substr);
int* indices = new int[numIndices];
int step = 0;
for (int i = 0; i < numVertices * 8; i++) {
if(step!=7)
std::getline(modelStream, substr, ',');
else
std::getline(modelStream, substr, '\n');
vertices[i] = stof(substr);
if (step == 7)
step = 0;
else
step++;
}
step = 0;
for (int i = 0; i < numIndices; i++) {
if (step == 2) {
std::getline(modelStream, substr, '\n');
step = 0;
}
else {
std::getline(modelStream, substr, ',');
step++;
}
indices[i] = stoi(substr);
}
conversion->format(vertices, numVertices * 8 * sizeof(float));
//binds id
glGenBuffers(1, &VBO);
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, numVertices*8*sizeof(float), vertices, GL_STATIC_DRAW);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices*4, indices, GL_STATIC_DRAW);
//texture
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
VertexData::VertexData(const VertexData& data) {
VAO = data.VAO;
VBO = data.VBO;
EBO = data.EBO;
width = data.width;
height = data.height;
trans = data.trans;
}
void VertexData::render() {
shader->use();
unsigned int transformLoc = glGetUniformLocation(shader->ID, "location");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
void VertexData::move(int x, int y) {
float coor[2] = { float(x),float(y) };
std::unique_ptr<ConvertToFloat> conversion{ new ConvertToFloat(width,height) };
conversion->convertToGlobal(coor);
glm::mat4 temp = glm::mat4(1.0f);
temp = glm::translate(temp, glm::vec3(coor[0],coor[1], 0.0f));
trans = temp;
}
void VertexData::rotate(int deg) {
}
void VertexData::destroy() {
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
}
loadFile.h:
#pragma once
#ifndef loadFileH
#define loadFileH
#include <fstream>
#include <sstream>
#include <iostream>
class LoadFile {
private:
public:
LoadFile() {}
std::stringstream load(const char* path) {
std::ifstream file;
std::stringstream stream;
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try {
file.open(path);
stream << file.rdbuf();
// close file handlers
file.close();
return stream;
}
catch (std::ifstream::failure e)
{
std::cout << "ERROR::FILE_NOT_SUCCESFULLY_READ" << std::endl;
return stream;
}
}
};
#endif
shaderLoader.h:
#ifndef SHADER_H
#define SHADER_H
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glad/gl.h> // include glad to get all the required OpenGL headers
#include "loadFile.h"
#include <string>
class Shader
{
public:
// the program ID
unsigned int ID;
// constructor reads and builds the shader
Shader(const char* vertexPath, const char* fragmentPath) {
std::unique_ptr<LoadFile> fragFile{ new LoadFile() };
std::unique_ptr<LoadFile> vertexFile{ new LoadFile() };
std::string vertexCode;
std::string fragmentCode;
vertexCode = vertexFile->load(vertexPath).str();
fragmentCode = fragFile->load(fragmentPath).str();
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
// 2. compile shaders
unsigned int vertex, fragment;
int success;
char infoLog[512];
// vertex Shader
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vShaderCode, NULL);
glCompileShader(vertex);
// print compile errors if any
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
};
// similiar for Fragment Shader
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fShaderCode, NULL);
glCompileShader(fragment);
// print compile errors if any
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
};
// shader Program
ID = glCreateProgram();
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
glLinkProgram(ID);
// print linking errors if any
glGetProgramiv(ID, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(ID, 512, NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
// delete the shaders as they're linked into our program now and no longer necessary
glDeleteShader(vertex);
glDeleteShader(fragment);
}
// use/activate the shader
void use(){
glUseProgram(ID);
}
};
#endif
The areas that the breakpoint are triggered according to VS are: line 19 of loadFile.h stream << file.rdbuf();, line 27 of shaderLoader.h const char* vShaderCode = vertexCode.c_str();, and line 75 of vertexData.cpp } which is just a closing bracket.
If I click continue after the breakpoint, I get the error Unhandled exception at 0x7727FA1D (ntdll.dll) in snek.exe: 0xC0000374: A heap has been corrupted (parameters: 0x772BB960).
EDIT:
convertToFloat.h:
#ifndef convertToFloat
#define convertToFloat
class ConvertToFloat {
public:
ConvertToFloat(int width, int height);
ConvertToFloat();
void convertToGlobal(float* input);
void convertFromRGB(float* input, const int size);
void format(float* input, const int size);
private:
int width = 0;
int height = 0;
};
#endif
convertToFloat.cpp
#include "convertToFloat.h"
ConvertToFloat::ConvertToFloat(int width, int height) {
this->width = width;
this->height = height;
}
ConvertToFloat::ConvertToFloat() {
}
void ConvertToFloat::convertToGlobal(float* input) {
input[0] = 2.0*input[0] / width;
input[1] = 2.0*input[1] / height;
}
void ConvertToFloat::convertFromRGB(float* input, const int size) {
for (int i = 0; i < size; i++)
input[i] = input[i] / 255;
}
void ConvertToFloat::format(float* input, const int size) {
int step = 0;
for (int i = 0; i < size; i++) {
if (step < 3) {
if (step == 1)
input[i] = ((input[i] * 2) / height) - 1;
else
input[i] = ((input[i] * 2) / width) - 1;
}
else if (step < 6) {
input[i] = input[i] / 255;
}
if (step == 7)
step = 0;
else
step++;
}
}
Assuming ConvertToFloat::format takes a pointer and a length, here is your problem:
conversion->format(vertices, numVertices * 8 * sizeof(float));
vertices only has numVertices * 8 elements and you are multiplying that by sizeof(float). So that function will happily corrupt lots of memory after the vertices buffer.
The immediate fix is trivial: lose the * sizeof(float), but I implore you to stop using raw memory and pointers and embrace std::vector.
If you declare std::vector<float> vertices you can simply pass that to ConvertToFloat::format and it will automatically a) know the size and b) alert if you do an out-of-bounds access.
You can always get a pointer to the backing array back by calling vertices.data().
Also, if you define a
struct Vertex { float position[3]; float color[3]; float texcoord[2]; }
you can document your VBO format and greatly clear up your attribute assignment code:
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(float), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, color));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoord));
glEnableVertexAttribArray(2);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size()*sizeof(int), indices.data(), GL_STATIC_DRAW);
Ideally vertices would just be a std::vector<Vertex>, but I can imagine that does not immediately fit your loading code.
I used opengl in qt5 on ubuntu 18.4, i wanted to load a simple cube(store in an .obj file) and render it. I followed the tutorial here http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/, but i got a black window at last. Here is my code, thank you very much:
myopengl.hpp
class MyOpenGL : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
MyOpenGL(const QString& obj_file);
protected:
virtual void initializeGL() Q_DECL_OVERRIDE;
virtual void paintGL() Q_DECL_OVERRIDE;
virtual void resizeGL(int width, int height) Q_DECL_OVERRIDE;
void CheckGLerror();
private:
std::vector< glm::vec3 > vertices;
std::vector< glm::vec2 > uvs;
std::vector< glm::vec3 > normals;
/* Render data */
GLuint VAO, VBO, EBO;
};
myopengl.cpp
MyOpenGL ::MyOpenGL (const QString& obj_file) :
VAO(0),
VBO(0),
EBO(0)
{
std::vector< unsigned int > vertex_indices, uv_indices, normal_indices;
std::vector< glm::vec3 > temp_vertices;
std::vector< glm::vec2 > temp_uvs;
std::vector< glm::vec3 > temp_normals;
//parse obj file
QFile file(obj_file);
file.open(QFile::ReadOnly | QFile::Text);
QTextStream in(&file);
while(!in.atEnd()) {
QString line = in.readLine();
QStringList list = line.replace(",","").split(' ', QString::SkipEmptyParts);
if (list.size() >= 3) {
if (list.at(0) == "v") { //veertex
bool ok1, ok2, ok3;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
float v3 = list.at(3).toFloat(&ok3);
if (ok1 && ok2 && ok3) {
glm::vec3 vertex;
vertex.x = v1;
vertex.y = v2;
vertex.z = v3;
temp_vertices.push_back(vertex);
}
} else if (list.at(0) == "vn") {
bool ok1, ok2, ok3;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
float v3 = list.at(3).toFloat(&ok3);
if (ok1 && ok2 && ok3) {
glm::vec3 normal;
normal.x = v1;
normal.y = v2;
normal.z = v3;
temp_normals.push_back(normal);
}
} else if (list.at(0) == "vt") {
bool ok1, ok2;
float v1 = list.at(1).toFloat(&ok1);
float v2 = list.at(2).toFloat(&ok2);
if (ok1 && ok2) {
glm::vec2 uv;
uv.x = v1;
uv.y = v2;
temp_uvs.push_back(uv);
}
} else if (list.at(0) == "f") {
bool f_ok1, f_ok2, f_ok3;
bool t_ok1, t_ok2, t_ok3;
bool n_ok1, n_ok2, n_ok3;
unsigned int v_index1, v_index2, v_index3;
unsigned int t_index1, t_index2, t_index3;
unsigned int n_index1, n_index2, n_index3;
QStringList f_list = list.at(1).split('/');
if (f_list.size() >= 3) {
v_index1 = f_list.at(0).toUInt(&f_ok1);
if (f_ok1) {
v_index1 -= 1;
}
t_index1 = f_list.at(1).toUInt(&t_ok1);
if (t_ok1) {
t_index1 -= 1;
}
n_index1 = f_list.at(2).toUInt(&n_ok1);
if (n_ok1) {
n_index1 -= 1;
}
}
f_list = list.at(2).split('/');
if (f_list.size() >= 3) {
v_index2 = f_list.at(0).toUInt(&f_ok2);
if (f_ok2) {
v_index2 -= 1;
}
t_index2 = f_list.at(1).toUInt(&t_ok2);
if (t_ok2) {
t_index2 -= 1;
}
n_index2 = f_list.at(2).toUInt(&n_ok2);
if (n_ok2) {
n_index2 -= 1;
}
}
f_list = list.at(3).split('/');
if (f_list.size() >= 3) {
v_index3 = f_list.at(0).toUInt(&f_ok3);
if (f_ok3) {
v_index3 -= 1;
}
t_index3 = f_list.at(1).toUInt(&t_ok3);
if (t_ok3) {
t_index3 -= 1;
}
n_index3 = f_list.at(2).toUInt(&n_ok3);
if (n_ok3) {
n_index3 -= 1;
}
}
if (f_ok1 && f_ok2 && f_ok3 && n_ok1 && n_ok2 && n_ok3
&& t_ok1 && t_ok2 && t_ok3) {
vertex_indices.push_back(v_index1);
vertex_indices.push_back(v_index2);
vertex_indices.push_back(v_index3);
uv_indices.push_back(t_index1);
uv_indices.push_back(t_index2);
uv_indices.push_back(t_index3);
normal_indices.push_back(n_index1);
normal_indices.push_back(n_index2);
normal_indices.push_back(n_index3);
}
}
}
}
file.close();
for (unsigned int i = 0; i < vertex_indices.size(); ++i) {
glm::vec3 vertex = temp_vertices.at(vertex_indices.at(i));
vertices.push_back(vertex);
}
for (unsigned int i = 0; i < uv_indices.size(); ++i) {
glm::vec2 uv = temp_uvs.at(uv_indices.at(i));
uvs.push_back(uv);
}
for (unsigned int i = 0; i < normal_indices.size(); ++i) {
glm::vec3 normal = temp_normals.at(normal_indices.at(i));
normals.push_back(normal);
}
}
void MyOpenGL::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0,0.0,0.0,1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glEnable(GL_LIGHT0);
glewExperimental = GL_TRUE;
GLenum status = glewInit();
if (status != GLEW_OK)
{
glfwTerminate();
std::system("pause");
return;
}
glGenVertexArrays(1, &this->VAO);
glBindVertexArray(this->VAO);
glGenBuffers(1, &this->VBO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(glm::vec3), &this->vertices[0], GL_STATIC_DRAW);
glBindVertexArray(0);
}
void MyOpenGL::resizeGL(int width, int height)
{
glViewport(0,0,width,height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-width/2,width/2,-height/2,height/2,-1,1);
glMatrixMode(GL_MODELVIEW);
}
void MyOpenGL::paintGL()
{
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glDisableVertexAttribArray(0);
}
You have to define an array of generic vertex attribute data by glVertexAttribPointer.
Note, the vertex buffer object is only a data store for the vertex data. But you have to specify how to "use" them. The specification and states of the vertex attributes are stored in the vertex array object. It is sufficient to bind the vertex array object when you want to draw the mesh:
specification:
glBindVertexArray(this->VAO);
glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
draw:
glBindVertexArray(this->VAO);
glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glBindVertexArray(0);
Extension:
Is it essential to use vertex shader and fragment shader? I didn't use them for current.
If you don't have a shader, then you have to use the Fixed Function Pipeline and to define the fixed function attributes, by glVertexPointer respectively glEnableClientState( GL_VERTEX_ARRAY ).
But since you only use one attribute, the vertex coordiante with attribute index 0, you can still define the attribute by glVertexAttribPointer and glEnableVertexAttribArray.
See What are the Attribute locations for fixed function pipeline in OpenGL 4.0++ core profile?
Note, in this case you have to use a compatibility OpenGL Context.
Correct Rendering:
Incorrect Rendering:
The correct rendering is from me using 6 vertices per square. The incorrect rendering is when I try to re-use vertices and get it down to 4 vertices per square.
Here is the code (I call addSquare 6 times in the example):
module src.UltraMesh;
import grape;
import std.conv;
import std.stdio;
import grape.shader;
import std.math;
import std.stdio;
import std.traits;
import std.conv;
import std.algorithm;
import std.array;
import std.range;
class UltraMesh
{
Appender!(float[]) vertices;
Appender!(int[]) indices;
Appender!(ubyte[]) color;
bool wireframe;
ubyte colorR, colorB, colorG, colorA;
GLuint indiceBuffer;
GLuint vertexBuffer;
GLuint colorBuffer;
GLuint vaoID;
int indiceBufferSize = 0;
int vertexBufferSize = 0;
int colorBufferSize = 0;
bool updateBuffers = true;
GLint cameraUniformLocation;
this()
{
if (program is null) {
initializeShader();
}
wireframe = false;
colorR = to!ubyte(255);
colorB = to!ubyte(0);
colorG = to!ubyte(0);
colorA = to!ubyte(255);
vertices = appender!(float[]); //Points
indices = appender!(int[]); //Edges
color = appender!(ubyte[]);
glGenVertexArrays(1, &vaoID); // Create our Vertex Array Object
glBindVertexArray(vaoID); // Bind our Vertex Array Object so we can use it
//Create buffer for indices
glGenBuffers(1, &indiceBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, int.sizeof*3, null, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//Create buffer for vertices
glGenBuffers(1, &vertexBuffer);
//Bind the buffer so we can work on it
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, float.sizeof*9, null, GL_DYNAMIC_DRAW);
//Bind to the attribute so we can do stuff to it
glBindAttribLocation(program, 0, cast(char*)"position");
//Get the attribute location?
GLint _location = glGetAttribLocation(program, cast(char*)"position");
//Enable it for drawing
glEnableVertexAttribArray(_location);
//Describe the data of the attribute?
glVertexAttribPointer(_location, 3, GL_FLOAT, GL_FALSE, 0, null);
//Unbind the buffer
glBindBuffer(GL_ARRAY_BUFFER, 0);
//Create buffer for colors
glGenBuffers(1, &colorBuffer);
//Bind the buffer so we can work on it
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferData(GL_ARRAY_BUFFER, byte.sizeof*12, null, GL_DYNAMIC_DRAW);
//Bind to the attribute so we can do stuff to it
glBindAttribLocation(program, 1, cast(char*)"color");
//Get the attribute location?
_location = glGetAttribLocation(program, cast(char*)"color");
//Enable it for drawing
glEnableVertexAttribArray(_location);
//Describe the data of the attribute?
glVertexAttribPointer(_location, 4, GL_UNSIGNED_BYTE, GL_FALSE, 0, null);
//Unbind the buffer
glBindBuffer(GL_ARRAY_BUFFER, 0);
cameraUniformLocation = glGetUniformLocation(program, cast(char*)"pvmMatrix");
glEnableVertexAttribArray(0); // Disable our Vertex Array Object
glBindVertexArray(0); // Disable our Vertex Buffer Object
}
~this() {
glDeleteBuffers(1, &indiceBuffer);
glDeleteBuffers(1, &vertexBuffer);
glDeleteBuffers(1, &colorBuffer);
}
void updateAllBuffers() {
updateIndiceBufferPartial(0, to!int(indices.data.length));
updateVertexBufferPartial(0, to!int(vertices.data.length));
updateColorBufferPartial(0, to!int(color.data.length));
}
void updateColorBuffer() {
updateColorBufferPartial(0, to!int(color.data.length));
}void updateVertexBuffer() {
updateVertexBufferPartial(0, to!int(vertices.data.length));
}void updateIndiceBuffer() {
updateIndiceBufferPartial(0, to!int(indices.data.length));
}
void reserveIndiceBuffer(int length) {
indiceBufferSize = length;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, int.sizeof*length, null, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void updateIndiceBufferPartial(int start, int length) {
if (indiceBufferSize <= start+length) {
indiceBufferSize = to!int(indiceBufferSize * 2 + 3);
if (indiceBufferSize < start+length) {indiceBufferSize=start+length+3;}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, int.sizeof*indiceBufferSize, null, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indices.data.length*int.sizeof, indices.data.ptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
} else {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, start*int.sizeof, length*int.sizeof, indices.data[start..start+length].ptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
void reserveVertexBuffer(int length) {
vertexBufferSize = length;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, float.sizeof*length, null, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void updateVertexBufferPartial(int start, int length) {
if (vertexBufferSize <= start+length) {
vertexBufferSize = to!int(vertexBufferSize * 2 + 9);
if (vertexBufferSize < start+length) {vertexBufferSize=start+length+9;}
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, float.sizeof*vertexBufferSize, null, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, vertices.data.length*float.sizeof, vertices.data.ptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
} else {
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferSubData(GL_ARRAY_BUFFER, start*float.sizeof, length*float.sizeof, vertices.data[start..start+length].ptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
void reserveColorBuffer(int length) {
colorBufferSize = length;
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferData(GL_ARRAY_BUFFER, byte.sizeof*length, null, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void updateColorBufferPartial(int start, int length) {
if (colorBufferSize <= start+length) {
colorBufferSize = to!int(colorBufferSize * 2 + 12);
if (colorBufferSize < start+length) {colorBufferSize=start+length+12;}
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferData(GL_ARRAY_BUFFER, byte.sizeof*colorBufferSize, null, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, color.data.length*byte.sizeof, color.data.ptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
} else {
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferSubData(GL_ARRAY_BUFFER, start*byte.sizeof, length*byte.sizeof, color.data[start..start+length].ptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
}
VertexGroup addTriangle(double triangleX, double triangleY, double triangleZ, double width) {
if (vertices.capacity() == 0) {
vertices.reserve(vertices.data.length*2 + 9);
}
if (indices.capacity() == 0) {
indices.reserve(indices.data.length*2 + 3);
}
if (color.capacity() == 0) {
color.reserve(color.data.length*2 + 12);
}
int firstVertex = to!int(vertices.data.length);
int firstIndice = to!int(indices.data.length);
int firstColor = to!int( color.data.length);
vertices.put( [triangleX-width/2.0, triangleY-width/2.0, triangleZ,
triangleX+width/2.0, triangleY-width/2.0, triangleZ,
triangleX, triangleY+width/2.0, triangleZ ] );
//Add the lines to connect vertices
int n = to!int(indices.data.length);
indices.put( [n, n+1, n+2] );
//For each vertex we need a color R, G, B, A
color.put( [colorR, colorG, colorB, colorA,
colorR, colorG, colorB, colorA,
colorR, colorG, colorB, colorA] );
int lastVertex = to!int(vertices.data.length);
int lastIndice = to!int(indices.data.length);
int lastColor = to!int(color.data.length);
if (updateBuffers) {
updateIndiceBufferPartial(firstIndice, lastIndice-firstIndice);
updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
updateColorBufferPartial(firstColor, lastColor-firstColor);
}
return VertexGroup(firstVertex, lastVertex, firstColor, lastColor, firstIndice, lastIndice, this);
}
VertexGroup addSquare(double squareX, double squareY, double squareZ, double size) {
if (vertices.capacity() == 0) {
vertices.reserve(vertices.data.length*2 + 12);
}
if (indices.capacity() == 0) {
indices.reserve(indices.data.length*2 + 6);
}
if (color.capacity() == 0) {
color.reserve(color.data.length*2 + 16);
}
int firstVertex = to!int(vertices.data.length);
int firstIndice = to!int(indices.data.length);
int firstColor = to!int( color.data.length);
vertices.put(
[ squareX-size/2.0, squareY-size/2.0, squareZ,
squareX-size/2.0, squareY+size/2.0, squareZ,
squareX+size/2.0, squareY-size/2.0, squareZ,
squareX+size/2.0, squareY+size/2.0, squareZ] );
//Add the lines to connect vertices
int n = to!int(indices.data.length);
indices.put( [n, n+1, n+2, n+3, n+2, n+1] );
//For each vertex we need a color R, G, B, A
color.put( [colorR, colorG, colorB, colorA,
colorR, colorG, colorB, colorA,
colorR, colorG, colorB, colorA,
colorR, colorG, colorB, colorA] );
int lastVertex = to!int(vertices.data.length);
int lastIndice = to!int(indices.data.length);
int lastColor = to!int(color.data.length);
if (updateBuffers) {
updateIndiceBufferPartial(firstIndice, lastIndice-firstIndice);
updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
updateColorBufferPartial(firstColor, lastColor-firstColor);
}
return VertexGroup(firstVertex, lastVertex, firstColor, lastColor, firstIndice, lastIndice, this);
}
void render(Camera camera) {
program.use();
//Set the camera location uniform
glUniformMatrix4fv(cameraUniformLocation, 1, GL_FALSE, camera.pvMat4.mat.ptr);
// Wireframe Checking
if (wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
scope(exit) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//Draw the indices
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);
glBindVertexArray(vaoID); // Bind our Vertex Array Object
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indiceBuffer);
glDrawArrays(DrawMode.Triangles, 0, to!int(indices.data.length));
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
//glBindVertexArray(0); // Unbind our Vertex Array Object
//glDrawElements(DrawMode.Triangles, to!int(indices.data.length), GL_UNSIGNED_INT, cast(void*)(0) );
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
}
static Shader vs;
static Shader fs;
static ShaderProgram program = null;
static void initializeShader() {
vs = new Shader(ShaderType.Vertex, vertexShaderSource);
fs = new Shader(ShaderType.Fragment, fragmentShaderSource);
program = new ShaderProgram(vs, fs);
}
static immutable vertexShaderSource = q{
#version 150
in vec3 position;
in vec4 color;
uniform mat4 pvmMatrix;
out vec4 vColor;
void main() {
vColor = color;
gl_Position = pvmMatrix * vec4(position, 1.0);
}
};
static immutable fragmentShaderSource = q{
#version 150
in vec4 vColor;
out vec4 FragColor;
void main() {
FragColor = vColor;
}
};
struct VertexGroup {
int firstVertex, lastVertex;
int firstColor, lastColor;
int firstIndice, lastIndice;
UltraMesh ultraMesh;
float centerX = 0;
float centerY = 0;
float centerZ = 0;
static VertexGroup opCall(int fV, int lV, int fC, int lC, int fI, int lI, UltraMesh uM)
{
VertexGroup vg = VertexGroup.init;
vg.firstVertex = fV;
vg.lastVertex = lV;
vg.firstColor = fC;
vg.lastColor = lC;
vg.firstIndice = fI;
vg.lastIndice = lI;
vg.ultraMesh = uM;
vg.calculateCenter();
return vg;
}
void calculateCenter() {
int vCount = (lastVertex - firstVertex)/3;
for (int i = firstVertex; i < lastVertex; i+= 3) {
//writeln("X: ", ultraMesh.vertices.data[i], ", Y: ", ultraMesh.vertices.data[i+1]);
centerX += ultraMesh.vertices.data[i]/vCount;
centerY += ultraMesh.vertices.data[i+1]/vCount;
centerZ += ultraMesh.vertices.data[i+2]/vCount;
}
}
void rotateX(float angle) {
float s = sin(angle);
float c = cos(angle);
for (int i = firstVertex; i < lastVertex; i+=3) {
float z = ultraMesh.vertices.data[i+2] - centerZ;
float y = ultraMesh.vertices.data[i+1] - centerY;
ultraMesh.vertices.data[i+2] = z * c - y * s + centerZ;
ultraMesh.vertices.data[i+1] = y * c + z * s + centerY;
}
if (ultraMesh.updateBuffers) ultraMesh.updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
}
void rotateY(float angle) {
float s = sin(angle);
float c = cos(angle);
for (int i = firstVertex; i < lastVertex; i+=3) {
float x = ultraMesh.vertices.data[i] - centerX;
float z = ultraMesh.vertices.data[i+2] - centerZ;
ultraMesh.vertices.data[i] = x * c - z * s + centerX;
ultraMesh.vertices.data[i+2] = z * c + x * s + centerZ;
}
if (ultraMesh.updateBuffers) ultraMesh.updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
}
void rotateZ(float angle) {
float s = sin(angle);
float c = cos(angle);
for (int i = firstVertex; i < lastVertex; i+=3) {
float x = ultraMesh.vertices.data[i] - centerX;
float y = ultraMesh.vertices.data[i+1] - centerY;
ultraMesh.vertices.data[i] = x * c - y * s + centerX;
ultraMesh.vertices.data[i+1] = y * c + x * s + centerY;
}
if (ultraMesh.updateBuffers) ultraMesh.updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
}
void translateX(float value) {
for (int i = firstVertex; i < lastVertex; i+= 3) {
ultraMesh.vertices.data[i] += value;
}
centerX += value;
if (ultraMesh.updateBuffers) ultraMesh.updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
}
void translateY(float value) {
for (int i = firstVertex; i < lastVertex; i+= 3) {
ultraMesh.vertices.data[i+1] += value;
}
centerY += value;
if (ultraMesh.updateBuffers) ultraMesh.updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
}
void translateZ(float value) {
for (int i = firstVertex; i < lastVertex; i+= 3) {
ultraMesh.vertices.data[i+2] += value;
}
centerZ += value;
if (ultraMesh.updateBuffers) ultraMesh.updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
}
void setPosition(float xPos, float yPos, float zPos) {
float moveX = xPos-centerX;
float moveY = yPos-centerY;
float moveZ = zPos-centerZ;
centerX = xPos;
centerY = yPos;
centerZ = zPos;
for (int i = firstVertex; i < lastVertex; i+= 3) {
ultraMesh.vertices.data[i] += moveX;
ultraMesh.vertices.data[i+1] += moveY;
ultraMesh.vertices.data[i+2] += moveZ;
}
if (ultraMesh.updateBuffers) ultraMesh.updateVertexBufferPartial(firstVertex, lastVertex-firstVertex);
}
void setColor(float r, float g, float b, float a) {
ubyte rByte = to!ubyte(to!int(r*255));
ubyte gByte = to!ubyte(to!int(g*255));
ubyte bByte = to!ubyte(to!int(b*255));
ubyte aByte = to!ubyte(to!int(a*255));
for (int i = firstColor; i < lastColor; i += 4) {
ultraMesh.color.data[i] = rByte;
ultraMesh.color.data[i+1] = gByte;
ultraMesh.color.data[i+2] = bByte;
ultraMesh.color.data[i+3] = aByte;
}
if (ultraMesh.updateBuffers) ultraMesh.updateColorBufferPartial(firstColor, lastColor-firstColor);
}
}
Pastebin for better formatting:
http://pastebin.com/1B9B2KsU
I believe I'm setting the indices correctly, any idea why this happens?
n should be equal to vertices/3 instead of indices.
Im trying to add Phong lighting to my scene in which I have loaded mesh with textures. This is my mesh loadin class:
#define INVALID_OGL_VALUE 0xFFFFFFFF
#define INVALID_MATERIAL 0xFFFFFFFF
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
Mesh::MeshEntry::MeshEntry()
{
VB = INVALID_OGL_VALUE;
IB = INVALID_OGL_VALUE;
NumIndices = 0;
MaterialIndex = INVALID_MATERIAL;
};
Mesh::MeshEntry::~MeshEntry()
{
if (VB != INVALID_OGL_VALUE)
{
glDeleteBuffers(1, &VB);
}
if (IB != INVALID_OGL_VALUE)
{
glDeleteBuffers(1, &IB);
}
}
void Mesh::MeshEntry::Init(const std::vector<Vertex>& Vertices,
const std::vector<unsigned int>& Indices)
{
NumIndices = Indices.size();
glGenBuffers(1, &VB);
glBindBuffer(GL_ARRAY_BUFFER, VB);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * Vertices.size(), &Vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &IB);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IB);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * NumIndices, &Indices[0], GL_STATIC_DRAW);
}
Mesh::Mesh()
{
}
Mesh::~Mesh()
{
Clear();
}
void Mesh::Clear()
{
for (unsigned int i = 0 ; i < textures_cnt.size() ; i++) {
SAFE_DELETE(textures_cnt[i]);
}
}
/*Loads mesh
*#Filename - name of the .obj file
**********************************************************/
bool Mesh::LoadMesh(const std::string& Filename){
Assimp::Importer Importer;
bool rc = false;
//clear previous loaded mesh
Clear();
//read file content
const aiScene* oScene = Importer.ReadFile(Filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs );
if(oScene){
printf (" %i animations\n", oScene->mNumAnimations);
printf (" %i cameras\n", oScene->mNumCameras);
printf (" %i lights\n", oScene->mNumLights);
printf (" %i materials\n", oScene->mNumMaterials);
printf (" %i meshes\n", oScene->mNumMeshes);
printf (" %i textures\n", oScene->mNumTextures);
submeshes_cnt.resize(oScene->mNumMeshes);
textures_cnt.resize(oScene->mNumMaterials);
/*.........Initialize the meshes in the scene one by one...........*/
for (unsigned int i = 0 ; i < submeshes_cnt.size() ; i++) {
const aiMesh* paiMesh = oScene->mMeshes[i];
InitMesh(i, paiMesh);
}
// Extract the directory part from the file name
std::string::size_type SlashIndex = Filename.find_last_of("/");
std::string Dir;
if (SlashIndex == std::string::npos) {
Dir = ".";
}
else if (SlashIndex == 0) {
Dir = "/";
}
else {
Dir = Filename.substr(0, SlashIndex);
}
/*.................Initialization of meshes end....................*/
/*.............Initialize the materials.............................*/
for(unsigned int i = 0 ; i < oScene->mNumMaterials ; i++) {
const aiMaterial* pMaterial = oScene->mMaterials[i];
textures_cnt[i] = NULL;
if (pMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
aiString Path;
if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
std::string FullPath = Dir + "/" + Path.data;
textures_cnt[i] = new Texture(GL_TEXTURE_2D, FullPath.c_str());
if (!textures_cnt[i]->Load()) {
printf("Error loading texture '%s'\n", FullPath.c_str());
delete textures_cnt[i];
textures_cnt[i] = NULL;
rc = false;
}
else {
printf("Loaded texture '%s'\n", FullPath.c_str());
}
}
}
}
/*.................Initialization of materials end....................*/
}
else {
printf("Error parsing '%s': '%s'\n", Filename.c_str(), Importer.GetErrorString());
}
return rc;
}
void Mesh::InitMesh(unsigned int Index, const aiMesh* paiMesh)
{
submeshes_cnt[Index].MaterialIndex = paiMesh->mMaterialIndex;
std::vector<Vertex> Vertices;
std::vector<unsigned int> Indices;
const aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
for (unsigned int i = 0 ; i < paiMesh->mNumVertices ; i++) {
const aiVector3D* pPos = &(paiMesh->mVertices[i]);
const aiVector3D* pNormal = &(paiMesh->mNormals[i]);
const aiVector3D* pTexCoord = paiMesh->HasTextureCoords(0) ? &(paiMesh->mTextureCoords[0][i]) : &Zero3D;
Vertex v(Vector3f(pPos->x, pPos->y, pPos->z),
Vector2f(pTexCoord->x, pTexCoord->y),
Vector3f(pNormal->x, pNormal->y, pNormal->z));
Vertices.push_back(v);
}
for (unsigned int i = 0 ; i < paiMesh->mNumFaces ; i++) {
const aiFace& Face = paiMesh->mFaces[i];
assert(Face.mNumIndices == 3);
Indices.push_back(Face.mIndices[0]);
Indices.push_back(Face.mIndices[1]);
Indices.push_back(Face.mIndices[2]);
}
submeshes_cnt[Index].Init(Vertices, Indices);
}
void Mesh::Render(){
//enable VAOs for vertices, normals, textures
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
for(unsigned int i = 0 ; i < submeshes_cnt.size() ; i++){
glBindBuffer(GL_ARRAY_BUFFER, submeshes_cnt[i].VB);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)12);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)20);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, submeshes_cnt[i].IB);
const unsigned int MaterialIndex = submeshes_cnt[i].MaterialIndex;
if (MaterialIndex < textures_cnt.size() && textures_cnt[MaterialIndex]) {
textures_cnt[MaterialIndex]->Bind(GL_TEXTURE0);
}
glDrawElements(GL_TRIANGLES, submeshes_cnt[i].NumIndices, GL_UNSIGNED_INT, 0);
}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
vertex shader:
#version 420
uniform mat4 camera;
uniform mat4 model;
in vec3 vert;
in vec2 vertTexCoord;
out vec2 fragTexCoord;
void main() {
// Pass the tex coord straight through to the fragment shader
fragTexCoord = vertTexCoord;
// Apply all matrix transformations to vert
gl_Position = camera * model * vec4(vert, 1);
}
fragment shader:
#version 420
uniform sampler2D tex;
in vec2 fragTexCoord;
out vec4 finalColor;
void main() {
//note: the texture function was called texture2D in older versions of GLSL
finalColor = texture(tex, fragTexCoord);
}
this is what i get:
before
but when i try to add phong lighting, when I add in attribute for normals to my vertex shader this is what I get:
after
Where is the problem?
Thank you for help.
//edit
shader loading and link
shprogram::shprogram(const std::vector<shader>& shaders) :
_object(0)
{
if(shaders.size() <= 0)
throw std::runtime_error("No shaders were provided to create the program");
//create the program object
_object = glCreateProgram();
if(_object == 0)
throw std::runtime_error("glCreateProgram failed");
//attach all the shaders
for(unsigned i = 0; i < shaders.size(); ++i)
glAttachShader(_object, shaders[i].getObject());
//link the shaders together
glLinkProgram(_object);
//detach all the shaders
for(unsigned i = 0; i < shaders.size(); ++i)
glDetachShader(_object, shaders[i].getObject());
//throw exception if linking failed
GLint status;
glGetProgramiv(_object, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
std::string msg("Program linking failure: ");
GLint infoLogLength;
glGetProgramiv(_object, GL_INFO_LOG_LENGTH, &infoLogLength);
char* strInfoLog = new char[infoLogLength + 1];
glGetProgramInfoLog(_object, infoLogLength, NULL, strInfoLog);
msg += strInfoLog;
delete[] strInfoLog;
glDeleteProgram(_object); _object = 0;
throw std::runtime_error(msg);
}
}
shprogram::~shprogram() {
//might be 0 if ctor fails by throwing exception
if(_object != 0) glDeleteProgram(_object);
}
GLuint shprogram::object() const {
return _object;
}
void shprogram::use() const {
glUseProgram(_object);
}
bool shprogram::isInUse() const {
GLint currentProgram = 0;
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
return (currentProgram == (GLint)_object);
}
void shprogram::stopUsing() const {
assert(isInUse());
glUseProgram(0);
}
GLint shprogram::attrib(const GLchar* attribName) const {
if(!attribName)
throw std::runtime_error("attribName was NULL");
GLint attrib = glGetAttribLocation(_object, attribName);
if(attrib == -1)
throw std::runtime_error(std::string("Program attribute not found: ") + attribName);
return attrib;
}
GLint shprogram::uniform(const GLchar* uniformName) const {
if(!uniformName)
throw std::runtime_error("uniformName was NULL");
GLint uniform = glGetUniformLocation(_object, uniformName);
if(uniform == -1)
throw std::runtime_error(std::string("Program uniform not found: ") + uniformName);
return uniform;
}
void shprogram::setUniformMatrix2(const GLchar* name, const GLfloat* v, GLsizei count, GLboolean transpose) {
assert(isInUse());
glUniformMatrix2fv(uniform(name), count, transpose, v);
}
void shprogram::setUniformMatrix3(const GLchar* name, const GLfloat* v, GLsizei count, GLboolean transpose) {
assert(isInUse());
glUniformMatrix3fv(uniform(name), count, transpose, v);
}
void shprogram::setUniformMatrix4(const GLchar* name, const GLfloat* v, GLsizei count, GLboolean transpose) {
assert(isInUse());
glUniformMatrix4fv(uniform(name), count, transpose, v);
}
void shprogram::setUniform(const GLchar* name, const glm::mat2& m, GLboolean transpose) {
assert(isInUse());
glUniformMatrix2fv(uniform(name), 1, transpose, glm::value_ptr(m));
}
void shprogram::setUniform(const GLchar* name, const glm::mat3& m, GLboolean transpose) {
assert(isInUse());
glUniformMatrix3fv(uniform(name), 1, transpose, glm::value_ptr(m));
}
void shprogram::setUniform(const GLchar* name, const glm::mat4& m, GLboolean transpose) {
assert(isInUse());
glUniformMatrix4fv(uniform(name), 1, transpose, glm::value_ptr(m));
}
void shprogram::setUniform(const GLchar* uniformName, const glm::vec3& v) {
setUniform3v(uniformName, glm::value_ptr(v));
}
void shprogram::setUniform(const GLchar* uniformName, const glm::vec4& v) {
setUniform4v(uniformName, glm::value_ptr(v));
}
setting up uniforms
//bind shader program
gProgram->use();
//set the "camera" uniform
gProgram->setUniform("camera", gCamera.matrix());
//set the "model" uniform in the vertex shader, based on the gDegreesRotated global
gProgram->setUniform("model", glm::rotate(glm::mat4(), 150.0f ,glm::vec3(0,1,0)));
gProgram->setUniform("light.position", gLight.position);
gProgram->setUniform("light.intensities", gLight.intensities);
You neither explicitely set the attribute locations in the shader source, nor bind them before linking the shader, nor query them. The GL is free to use any valid attribute index for any active input attribute.
You just assume that 0 is position, 1 is texcoord and 2 is normal. It might work by accident for the position+texcoord case, but when you add the normal, the indices might get all mixed up. Either use layout(location=...) (see GL_ARB_explicit_attrib_location for details) in the GLSL code, or use glBindAttribLocation() to specify the mapping you want.