Related
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.
I'm building a 3D model viewer in OpenGL 3.3 but I can't get my textures to show properly. I'm pretty sure the texcoords (coming from the object I'm loading) are correct.
This is my code:
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp"
#include "InputState.h"
#include "Viewer.h"
#include "Shader.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#define TINYOBJLOADER_IMPLEMENTATION
#include "tiny_obj_loader.h"
std::string folder = "cube-tex/";
std::string objPath = folder + "cube-tex.obj";
std::vector<tinyobj::shape_t> shapes;
std::vector<tinyobj::material_t> materials;
std::vector<GLuint> texID;
std::string meshMode = "WIREFRAME";
std::string lightMode = "DIRECTIONAL";
double startTime = glfwGetTime();
float initScale = 1;
float zoom = 0;
float xRot, yRot;
int winX = 500;
int winY = 500;
std::vector<unsigned int> vaoHandle;
ObjectViewer *ObjCam;
Viewer *Camera;
glm::vec3 cameraPos(0.0f, 0.0f, -10.0f);
// Data structure storing mouse input info
InputState Input;
unsigned int programID;
unsigned int debugShader;
unsigned int lightTexShader;
//
// Callbacks
//
void key_callback(GLFWwindow* window,
int key, int scancode, int action, int mods)
{
if (action == GLFW_PRESS) {
switch(key)
{
case GLFW_KEY_S:
if (programID == lightTexShader) {
programID = debugShader;
meshMode = "WIREFRAME";
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
}
else {
programID = lightTexShader;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
break;
case GLFW_KEY_ESCAPE: // escape key pressed
glfwSetWindowShouldClose(window, GL_TRUE);
break;
case GLFW_KEY_D:
if(programID == debugShader){
if(meshMode == "WIREFRAME") {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
meshMode = "NORMAL";
}else if(meshMode == "NORMAL"){
meshMode = "MATERIAL";
}else{
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
meshMode = "WIREFRAME";
}
}
break;
case GLFW_KEY_L:
if(programID == lightTexShader){
if(lightMode == "DIRECTIONAL") {
lightMode = "HEADLIGHT";
}else if(lightMode == "HEADLIGHT"){
lightMode = "ROTATING";
}else{
lightMode = "DIRECTIONAL";
}
}
break;
default:
break;
}
}
}
// Set the projection matrix. Takes into account window aspect ratio, so called
// when the window is resized.
void setProjection(unsigned int id)
{
glm::mat4 projection;
projection = glm::perspective( (float)M_PI/3.0f, (float) winX / winY, 1.0f, 30.0f );
// Load it to the shader program
int projHandle = glGetUniformLocation(id, "projection");
if (projHandle == -1) {
std::cout << "Uniform: projection_matrix is not an active uniform label\n";
}
glUniformMatrix4fv( projHandle, 1, false, glm::value_ptr(projection) );
}
// Called when the window is resized.
void reshape_callback( GLFWwindow *window, int x, int y )
{
winX = x;
winY = y;
setProjection(programID);
glViewport( 0, 0, x, y );
}
void mouse_pos_callback(GLFWwindow* window, double x, double y)
{
// Use a global data structure to store mouse info
// We can then use it however we want
Input.update((float)x, (float)y);
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
Input.rMousePressed = true;
}
else if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_RELEASE) {
Input.rMousePressed = false;
}
else if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
Input.lMousePressed = true;
}
else if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
Input.lMousePressed = false;
}
}
int setupLight()
{
// light position uniform
int lightposHandle = glGetUniformLocation(programID, "light_pos");
if (lightposHandle == -1) {
fprintf(stderr, "Error: can't find matrix uniforms\n");
exit(1);
}
// Uniform lighting variables
int lightambientHandle = glGetUniformLocation(programID, "light_ambient");
int lightdiffuseHandle = glGetUniformLocation(programID, "light_diffuse");
int lightspecularHandle = glGetUniformLocation(programID, "light_specular");
if ( lightambientHandle == -1 ||
lightdiffuseHandle == -1 ||
lightspecularHandle == -1) {
fprintf(stderr, "Error: can't find light uniform variables\n");
return 1;
}
float lightPos[4]; // light position //= { 0.0f, 10.0f, 0.0f, 1.0f };
float lightambient[3]; // ambient light components
float lightdiffuse[3]; // diffuse light components
float lightspecular[3]; // specular light components
if(lightMode == "DIRECTIONAL"){
lightambient[0] = 0.0f;
lightambient[1] = 0.0f;
lightambient[2] = 0.0f;
lightdiffuse[0] = 0.5f;
lightdiffuse[1] = 0.5f;
lightdiffuse[2] = 1.0f;
lightspecular[0] = 1.0f;
lightspecular[1] = 1.0f;
lightspecular[2] = 1.0f;
lightPos[0] = -10.0f;
lightPos[1] = -10.0f;
lightPos[2] = -10.0f;
lightPos[3] = 0.0f;
}else if(lightMode == "HEADLIGHT"){
lightambient[0] = 0.0f;
lightambient[1] = 0.0f;
lightambient[2] = 0.0f;
lightdiffuse[0] = 1.0f;
lightdiffuse[1] = 1.0f;
lightdiffuse[2] = 1.0f;
lightspecular[0] = 1.0f;
lightspecular[1] = 1.0f;
lightspecular[2] = 1.0f;
glm::mat3 rotMat(Camera->getViewMtx());
glm::vec3 d((Camera->getViewMtx())[3]);
glm::vec3 tmp = -d * rotMat;
lightPos[0] = tmp[0];
lightPos[1] = tmp[1];
lightPos[2] = tmp[2];
lightPos[3] = 1.0f;
}else if(lightMode == "ROTATING"){
lightambient[0] = 0.0f;
lightambient[1] = 0.0f;
lightambient[2] = 0.0f;
lightdiffuse[0] = 1.0f;
lightdiffuse[1] = 1.0f;
lightdiffuse[2] = 0.2f;
lightspecular[0] = 1.0f;
lightspecular[1] = 1.0f;
lightspecular[2] = 0.2f;
double now = glfwGetTime();
lightPos[0] = 10 * cos((now - startTime));
lightPos[1] = 0.0f;
lightPos[2] = 10 * sin((now - startTime));
lightPos[3] = 1.0f;
}
glUniform4fv(lightposHandle, 1, lightPos);
glUniform3fv(lightambientHandle, 1, lightambient);
glUniform3fv(lightdiffuseHandle, 1, lightdiffuse);
glUniform3fv(lightspecularHandle, 1, lightspecular);
return 0; // return success
}
void computeInitScale(){
float minX = 0;
float minY = 0;
float maxX = 0;
float maxY = 0;
for(int i = 0; i < shapes.size(); i++){
for(int j = 0; j < shapes[i].mesh.positions.size()/3; j++){
//max and min X
if(shapes[i].mesh.positions[3*j+0] > maxX){
maxX = shapes[i].mesh.positions[3*j+0];
}else if(shapes[i].mesh.positions[3*j+0] < minX){
minX = shapes[i].mesh.positions[3*j+0];
}
//max and min Y
if(shapes[i].mesh.positions[3*j+1] > maxY){
maxY = shapes[i].mesh.positions[3*j+1];
}else if(shapes[i].mesh.positions[3*j+1] < minY){
minY = shapes[i].mesh.positions[3*j+1];
}
}
}
initScale = 2 / std::max(maxX - minX, maxY - minY);
}
void loadRGBTexture(const char *path)
{
int x, y, n;
// Load from file. Force RGB image pixel format
unsigned char *data = stbi_load(path, &x, &y, &n, 3);
// Copy to GPU as data for the currently active texture.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, x, y, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
glGenerateMipmap(GL_TEXTURE_2D);
}
void setMaterials(int i){
// material
int mtlambientHandle = glGetUniformLocation(programID, "mtl_ambient");
int mtldiffuseHandle = glGetUniformLocation(programID, "mtl_diffuse");
int mtlspecularHandle = glGetUniformLocation(programID, "mtl_specular");
if (mtlambientHandle == -1 || mtldiffuseHandle == -1 || mtlspecularHandle == -1) {
fprintf(stderr, "Error: can't find material uniform variables\n");
}
float mtlambient[3];
mtlambient[0] = materials[shapes[i].mesh.material_ids[0]].ambient[0];
mtlambient[1] = materials[shapes[i].mesh.material_ids[0]].ambient[1];
mtlambient[2] = materials[shapes[i].mesh.material_ids[0]].ambient[2];
float mtldiffuse[3];
mtldiffuse[0] = materials[shapes[i].mesh.material_ids[0]].diffuse[0];
mtldiffuse[1] = materials[shapes[i].mesh.material_ids[0]].diffuse[1];
mtldiffuse[2] = materials[shapes[i].mesh.material_ids[0]].diffuse[2];
float mtlspecular[3];
mtlspecular[0] = materials[shapes[i].mesh.material_ids[0]].specular[0];
mtlspecular[1] = materials[shapes[i].mesh.material_ids[0]].specular[1];
mtlspecular[2] = materials[shapes[i].mesh.material_ids[0]].specular[2];
glUniform3fv(mtlambientHandle, 1, mtlambient);
glUniform3fv(mtldiffuseHandle, 1, mtldiffuse);
glUniform3fv(mtlspecularHandle, 1, mtlspecular);
}
void loadObj(){
std::string err;
bool ret = tinyobj::LoadObj(shapes, materials, err, objPath.c_str(), folder.c_str());
if (!err.empty()) {
std::cerr << err << std::endl;
}
if (!ret) {
exit(1);
}
std::cout << "# of shapes : " << shapes.size() << std::endl;
std::cout << "# of materials : " << materials.size() << std::endl;
for (size_t i = 0; i < shapes.size(); i++) {
printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str());
printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size());
printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size());
assert((shapes[i].mesh.indices.size() % 3) == 0);
for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
printf(" idx[%ld] = %d, %d, %d. mat_id = %d\n", f, shapes[i].mesh.indices[3*f+0], shapes[i].mesh.indices[3*f+1], shapes[i].mesh.indices[3*f+2], shapes[i].mesh.material_ids[f]);
}
printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size());
assert((shapes[i].mesh.positions.size() % 3) == 0);
for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) {
printf(" v[%ld] = (%f, %f, %f)\n", v,
shapes[i].mesh.positions[3*v+0],
shapes[i].mesh.positions[3*v+1],
shapes[i].mesh.positions[3*v+2]);
}
}
for (size_t i = 0; i < materials.size(); i++) {
printf("material[%ld].name = %s\n", i, materials[i].name.c_str());
printf(" material.Ka = (%f, %f ,%f)\n", materials[i].ambient[0], materials[i].ambient[1], materials[i].ambient[2]);
printf(" material.Kd = (%f, %f ,%f)\n", materials[i].diffuse[0], materials[i].diffuse[1], materials[i].diffuse[2]);
printf(" material.Ks = (%f, %f ,%f)\n", materials[i].specular[0], materials[i].specular[1], materials[i].specular[2]);
printf(" material.Tr = (%f, %f ,%f)\n", materials[i].transmittance[0], materials[i].transmittance[1], materials[i].transmittance[2]);
printf(" material.Ke = (%f, %f ,%f)\n", materials[i].emission[0], materials[i].emission[1], materials[i].emission[2]);
printf(" material.Ns = %f\n", materials[i].shininess);
printf(" material.Ni = %f\n", materials[i].ior);
printf(" material.dissolve = %f\n", materials[i].dissolve);
printf(" material.illum = %d\n", materials[i].illum);
printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str());
printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str());
printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str());
printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str());
std::map<std::string, std::string>::const_iterator it(materials[i].unknown_parameter.begin());
std::map<std::string, std::string>::const_iterator itEnd(materials[i].unknown_parameter.end());
for (; it != itEnd; it++) {
printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str());
}
printf("\n");
}
computeInitScale();
}
void createVAO()
{
for(int i = 0; i < shapes.size(); i++){
vaoHandle.push_back(0);
glGenVertexArrays(1, &vaoHandle[i]);
glBindVertexArray(vaoHandle[i]);
unsigned int buffer[5];
glGenBuffers(4, buffer);
// Set vertex attribute
glBindBuffer(GL_ARRAY_BUFFER, buffer[0]);
glBufferData(GL_ARRAY_BUFFER,
sizeof(float) * shapes[i].mesh.positions.size(), &shapes[i].mesh.positions[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
// Set vertex attribute
glBindBuffer(GL_ARRAY_BUFFER, buffer[1]);
glBufferData(GL_ARRAY_BUFFER,
sizeof(float) * shapes[i].mesh.normals.size(), &shapes[i].mesh.normals[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
// create material array
float diffuse[shapes[i].mesh.indices.size() * 3];
for(int j = 0; j<shapes[i].mesh.indices.size(); j++){
int index = j*3;
diffuse[index] = materials[shapes[i].mesh.material_ids[j/3]].diffuse[0];
diffuse[index + 1] = materials[shapes[i].mesh.material_ids[j/3]].diffuse[1];
diffuse[index + 2] = materials[shapes[i].mesh.material_ids[j/3]].diffuse[2];
}
// Set vertex attribute
glBindBuffer(GL_ARRAY_BUFFER, buffer[2]);
glBufferData(GL_ARRAY_BUFFER,
shapes[i].mesh.indices.size() * 3 * sizeof(float) , &diffuse[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
// Set element attributes. Notice the change to using GL_ELEMENT_ARRAY_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[3]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
sizeof(float) * shapes[i].mesh.indices.size(), &shapes[i].mesh.indices[0], GL_STATIC_DRAW);
// load textures
texID.push_back(0);
glGenTextures(1, &texID[i] );
glActiveTexture(GL_TEXTURE0);
glBindTexture( GL_TEXTURE_2D, texID[i] );
loadRGBTexture((folder + (std::string) materials[shapes[i].mesh.material_ids[0]].diffuse_texname).c_str());
// load texture coordinates
int texLoc = glGetAttribLocation(programID, "a_tex_coord");
glBindBuffer(GL_ARRAY_BUFFER, buffer[4]);
glBufferData(GL_ARRAY_BUFFER,
shapes[i].mesh.texcoords.size() * sizeof(float) , &shapes[i].mesh.texcoords[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(texLoc);
glVertexAttribPointer(texLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
// Un-bind
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
}
void render()
{
glUseProgram(programID);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
setProjection(programID);
Camera->update(Input);
glm::mat4 viewMatrix;
viewMatrix = Camera->getViewMtx();
// Load viex to the shader program
int viewHandle = glGetUniformLocation(programID, "view");
if (viewHandle == -1) {
std::cout << "Uniform: view is not an active uniform label\n";
}
glUniformMatrix4fv( viewHandle, 1, false, glm::value_ptr(viewMatrix) );
// Load the model matrix
int modelUniformHandle = glGetUniformLocation(programID, "model");
if (modelUniformHandle == -1)
exit(1);
// calculate zoom
if(Input.rMousePressed){
zoom += Input.yDiff/10;
if (zoom < 0) zoom = 0;
if (zoom > 1.5) zoom = 1.5;
}
glm::mat4 model;
model = glm::scale( model, glm::vec3(initScale + zoom, initScale + zoom, initScale + zoom));
glUniformMatrix4fv( modelUniformHandle, 1, false, glm::value_ptr(model) );
if (programID == debugShader){
// set colour mode
int modeHandle = glGetUniformLocation(programID, "mode");
if (modeHandle == -1) {
std::cout << "Uniform: mode is not an active uniform label\n";
}
if(meshMode == "MATERIAL"){
glUniform1i( modeHandle, 1);
}else{
glUniform1i( modeHandle, 0);
}
}else{
setupLight();
}
// Load meshes
for(int i = 0; i < shapes.size(); i++){
glBindVertexArray(vaoHandle[i]);
glDrawElements(GL_TRIANGLES, shapes[i].mesh.indices.size(), GL_UNSIGNED_INT, 0);
if (programID == lightTexShader){
setMaterials(i);
int texHandle = glGetUniformLocation(programID, "texMap");
if (texHandle == -1) {
fprintf(stderr, "Could not find uniform variables\n");
exit(1);
}
glBindTexture(GL_TEXTURE_2D, texID[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_NEAREST);
}
glBindVertexArray(0);
}
}
/**
* Error callback for GLFW. Simply prints error message to stderr.
*/
static void error_callback(int error, const char* description)
{
fputs(description, stderr);
}
int main (int argc, char **argv)
{
GLFWwindow* window;
glfwSetErrorCallback(error_callback);
if (!glfwInit()) {
exit(1);
}
// Specify that we want OpenGL 3.3
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// Create the window and OpenGL context
window = glfwCreateWindow(winX, winY, "Modelling and viewing", NULL, NULL);
if (!window)
{
glfwTerminate();
exit(1);
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
// Initialize GLEW
glewExperimental = true; // Needed for core profile
if (glewInit() != GLEW_OK) {
fprintf(stderr, "Failed to initialize GLEW\n");
exit(1);
}
// load shaders
debugShader = LoadShaders("mview.vert", "mview.frag");
lightTexShader = LoadShaders("light-texture.vert", "light-texture.frag");
if (debugShader == 0 || lightTexShader == 0) {
exit(1);
}
programID = debugShader;
// Set OpenGL state we need for this application.
glClearColor(0.5F, 0.5F, 0.5F, 0.0F);
glEnable(GL_DEPTH_TEST);
glUseProgram(programID);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Set up the scene and the cameras
//setProjection(debugShader);
//setProjection(lightTexShader);
loadObj();
createVAO();
ObjCam = new ObjectViewer( cameraPos );
Camera = ObjCam;
// Define callback functions and start main loop
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, mouse_pos_callback);
glfwSetMouseButtonCallback(window, mouse_button_callback);
glfwSetFramebufferSizeCallback(window, reshape_callback);
while (!glfwWindowShouldClose(window))
{
render();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwDestroyWindow(window);
glfwTerminate();
exit(0);
return 0;
}
Vertex shader
#version 330
uniform mat4 projection_matrix;
uniform mat4 modelview_matrix;
//uniform mat3 normal_matrix;
layout (location = 0) in vec3 a_vertex;
layout (location = 1) in vec3 a_normal;
layout (location = 2) in vec3 a_diffuse;
in vec2 a_tex_coord;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
//uniform int mode;
uniform vec3 mtl_diffuse;
out vec4 vertex; // vertex position in eye space
out vec3 normal; // the eye space normal
out vec2 st;
void main(void)
{
vertex = view * model * vec4(a_vertex, 1.0);
//normal = normalize(normal_matrix * a_normal);
normal = a_normal;
st = a_tex_coord;
gl_Position = projection * view * model * vec4(a_vertex, 1.0);
}
Fragment shader
#version 330
in vec4 vertex;
in vec3 normal;
in vec2 st;
out vec4 fragColour;
uniform sampler2D texMap;
uniform vec4 light_pos;
uniform vec3 light_ambient; // Light ambient RGBA values
uniform vec3 light_diffuse; // Light diffuse RGBA values
uniform vec3 light_specular; // Light specular RGBA values
uniform vec3 mtl_ambient; // Ambient surface colour
uniform vec3 mtl_diffuse; // Diffuse surface colour
uniform vec3 mtl_specular; // Specular surface colour
const float shininess = 32;
vec3 phongPointLight(in vec4 position, in vec3 norm)
{
vec3 s = normalize(vec3(light_pos - position));
if(light_pos.w == 0.0){
s = -normalize(light_pos.xyz);
}
vec3 v = normalize(-position.xyz);
vec3 r = reflect( -s, norm );
vec3 ambient = light_ambient * mtl_ambient;
// The diffuse component
float sDotN = max( dot(s,norm), 0.0 );
vec3 diffuse = light_diffuse * mtl_diffuse * sDotN;
// The specular component BLINN
vec3 spec = vec3(0.0);
vec3 halfwayDir = normalize(s + v);
if ( sDotN > 0.0 )
spec = light_specular * mtl_specular * pow(max(dot(normalize(normal), halfwayDir), 0.0), 32.0);
return ambient + diffuse + spec;
}
void main(void)
{
fragColour = vec4(phongPointLight(vertex, normalize(normal)), 1.0) * texture(texMap, st);
}
This is the result. As you can see the texture is not properly positioned on the car.
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.
I've been trying to make my own basic .obj (Wavefront) renderer using the OpenGL 3.x core profile. I'm using the OpenGL SuperBible 5th ed. and the Swiftless tutorials as reference material.
The geometry appears to load correctly, so now I'm trying to get an ADS Phong lighting model to work, but something is screwy, and I think it has something to do with either my OpenGL calls or else maybe the way I'm loading my normals, but I can't seem to figure out how to fix it. (It could be some other problem too I guess, since I'm not an expert in OpenGL).
When rendering a simple cube, it almost looks right, but there's a weird point of light on one side:
picture of cube
When rendering a sphere, the lighting shows up with "veins":
picture of sphere
Something is obviously very wrong with my code. Here are the sections of code which I think might be relevant. I'll be happy to post more/all of the code if necessary... let me know if I should post more.
file: objFileRenderer.cpp
#include "objFileRenderer.hpp"
namespace def
{
objFileRenderer::objFileRenderer(const char* fileToRender, float objScale)
{
windowWidth = 800;
windowHeight = 600;
settings.majorVersion = 3;
settings.minorVersion = 1;
app = NULL;
shader = NULL;
vaoID = NULL;
vboID = NULL;
iboID = NULL;
vaoID = new unsigned int[1];
vboID = new unsigned int[3];
iboID = new unsigned int[2];
rotSpeed = 50.0f;
rot = 0.0f;
initSFML();
initOpenGL();
std::string objFilename(fileToRender);
std::vector< float > vertices;
std::vector< unsigned short > indices;
std::vector< float > normals;
std::vector< unsigned short > normalIndices;
loadObj(objFilename, vertices, indices, normals, normalIndices);
std::vector< float > colorA;
std::vector< float > colorD;
std::vector< float > colorS;
loadMtl(objFilename, colorA, colorD, colorS);
float* vertexArray = NULL;
int numVertices = 0;
unsigned short* indexArray = NULL;
int numFaces = 0;
vertexArray = vertexVectorToVertexArray(vertices, numVertices);
indexArray = indexVectorToIndexArray(indices, numFaces);
float* colorArrayA = NULL;
float* colorArrayD = NULL;
float* colorArrayS = NULL;
int numColoredObjects = 0;
colorVectorsToColorArrays(colorA, colorD, colorS, colorArrayA, colorArrayD, colorArrayS, numColoredObjects);
float* normalArray = NULL;
unsigned short* normalIndicesArray = NULL;
int numNormals = 0;
int numNormalIndices = 0;
normalVectorsToNormalArrays(normals, normalIndices, normalArray, normalIndicesArray, numNormals, numNormalIndices);
setupScene();
putArraysIntoVAO(vertexArray, numVertices, indexArray, numFaces, normalArray, numNormals, normalIndicesArray, numNormalIndices);
mainLoop(numVertices, numFaces, colorArrayA, colorArrayD, colorArrayD, normalArray, objScale);
delete [] vertexArray;
delete [] indexArray;
delete [] colorArrayA;
delete [] colorArrayD;
delete [] colorArrayS;
delete [] normalArray;
delete [] normalIndicesArray;
}
objFileRenderer::~objFileRenderer()
{
shutdownSFML();
}
void objFileRenderer::loadObj(std::string& objFilename, std::vector< float >& vertices, std::vector< unsigned short >& indices, std::vector< float >& normals, std::vector< unsigned short >& normalIndices)
{
std::ifstream objFile(objFilename.c_str());
if (!objFile.is_open())
{
std::cerr << "Error: unable to open .obj file: " << objFilename << std::endl;
exit(1);
}
std::string line;
while (objFile.good())
{
getline(objFile, line);
// vertices
if (line[0] == 'v' && line[1] == ' ') // if line in .obj file contains vertices
{
std::vector< std::string > tmpStrVerts;
std::string subline;
subline = line.substr(2);
boost::split(tmpStrVerts, subline, boost::is_any_of("\t "));
std::vector< std::string >::iterator it;
for (it = tmpStrVerts.begin(); it != tmpStrVerts.end(); it++)
{
float vertex;
std::stringstream ss;
ss << *it;
ss >> vertex;
vertices.push_back(vertex);
}
}
// normals
else if (line[0] == 'v' && line[1] == 'n')
{
std::vector< std::string > tmpStrNorms;
std::string subline;
subline = line.substr(3);
boost::split(tmpStrNorms, subline, boost::is_any_of("\t "));
std::vector< std::string >::iterator it;
for (it = tmpStrNorms.begin(); it != tmpStrNorms.end(); it++)
{
float normal;
std::stringstream ss;
ss << *it;
ss >> normal;
normals.push_back(normal);
//std::cout << normal << std::endl;
}
}
// indices and normalIndices
else if (line[0] == 'f' && line[1] == ' ') // else if line in .obj file contains indices
{
std::vector< std::string > tmpStrIndices;
std::string subline;
subline = line.substr(2);
// indices
boost::split(tmpStrIndices, subline, boost::is_any_of("\t "));
std::vector< std::string >::iterator it;
for (it = tmpStrIndices.begin(); it != tmpStrIndices.end(); it++)
{
unsigned short index;
std::stringstream ss;
ss << *it;
ss >> index;
indices.push_back(index);
}
// normalIndices
boost::split(tmpStrIndices, subline, boost::is_any_of("/"));
int count = 0;
std::vector< std::string >::iterator it2;
for (it2 = tmpStrIndices.begin(); it2 != tmpStrIndices.end(); it2++)
{
if (count == 2)
{
unsigned short index;
std::stringstream ss;
ss << *it2;
ss >> index;
normalIndices.push_back(index);
count = 0;
}
count++;
}
}
}
objFile.close();
return;
}
void objFileRenderer::loadMtl(std::string& objFilename, std::vector< float >& colorA, std::vector< float >& colorD, std::vector< float >& colorS)
{
int extpos = objFilename.find('.');
std::string mtlFilename = objFilename.substr(0, extpos+1) + "mtl";
std::ifstream mtlFile(mtlFilename.c_str());
if (!mtlFile.is_open())
{
std::cerr << "Error: unable to open .mtl file: " << mtlFilename << std::endl;
exit(1);
}
std::string line;
while (mtlFile.good())
{
getline(mtlFile, line);
if (line[0] == 'K' && line[1] == 'a')
{
std::vector< std::string > tmpStrColorA;
std::string subline;
subline = line.substr(3);
boost::split(tmpStrColorA, subline, boost::is_any_of("\t "));
std::vector< std::string >::iterator it;
for (it = tmpStrColorA.begin(); it != tmpStrColorA.end(); it++)
{
float rgbValue;
std::stringstream ss;
ss << *it;
ss >> rgbValue;
colorA.push_back(rgbValue);
}
}
if (line[0] == 'K' && line[1] == 'd')
{
std::vector< std::string > tmpStrColorD;
std::string subline;
subline = line.substr(3);
boost::split(tmpStrColorD, subline, boost::is_any_of("\t "));
std::vector< std::string >::iterator it;
for (it = tmpStrColorD.begin(); it != tmpStrColorD.end(); it++)
{
float rgbValue;
std::stringstream ss;
ss << *it;
ss >> rgbValue;
colorD.push_back(rgbValue);
}
}
if (line[0] == 'K' && line[1] == 's')
{
std::vector< std::string > tmpStrColorS;
std::string subline;
subline = line.substr(3);
boost::split(tmpStrColorS, subline, boost::is_any_of("\t "));
std::vector< std::string >::iterator it;
for (it = tmpStrColorS.begin(); it != tmpStrColorS.end(); it++)
{
float rgbValue;
std::stringstream ss;
ss << *it;
ss >> rgbValue;
colorS.push_back(rgbValue);
}
}
}
mtlFile.close();
return;
}
float* objFileRenderer::vertexVectorToVertexArray(std::vector< float >& vertices, int& numVertices)
{
numVertices = vertices.size() / 3;
float* vertexArray = NULL;
vertexArray = new float[vertices.size()];
for (unsigned int i = 0; i < vertices.size(); i++)
{
vertexArray[i] = vertices[i];
}
return vertexArray;
}
unsigned short* objFileRenderer::indexVectorToIndexArray(std::vector< unsigned short >& indices, int& numFaces)
{
numFaces = indices.size() / 3;
unsigned short* indexArray = NULL;
indexArray = new unsigned short[indices.size()];
for (unsigned int i = 0; i < indices.size(); i++)
{
indexArray[i] = indices[i]-1;
}
return indexArray;
}
void objFileRenderer::colorVectorsToColorArrays(std::vector< float >& colorA, std::vector< float >& colorD, std::vector< float >& colorS, float*& colorArrayA, float*& colorArrayD, float*& colorArrayS, int& numColoredObjects)
{
numColoredObjects = colorA.size() / 3;
colorArrayA = new float[numColoredObjects*3];
colorArrayD = new float[numColoredObjects*3];
colorArrayS = new float[numColoredObjects*3];
for (int i = 0; i < numColoredObjects; i+=3)
{
colorArrayA[i] = colorA[i]; colorArrayA[i+1] = colorA[i+1]; colorArrayA[i+2] = colorA[i+2];
colorArrayD[i] = colorD[i]; colorArrayD[i+1] = colorD[i+1]; colorArrayD[i+2] = colorD[i+2];
colorArrayS[i] = colorS[i]; colorArrayS[i+1] = colorS[i+1]; colorArrayS[i+2] = colorS[i+2];
}
return;
}
void objFileRenderer::normalVectorsToNormalArrays(std::vector< float >& normals, std::vector< unsigned short >& normalIndices, float*& normalArray, unsigned short*& normalIndicesArray, int& numNormals, int& numNormalIndices)
{
numNormals = normals.size() / 3;
numNormalIndices = normalIndices.size();
normalArray = new float[numNormalIndices];
normalIndicesArray = new unsigned short[numNormalIndices];
for (int i = 0; i < numNormalIndices; i+=3)
{
normalIndicesArray[i] = normalIndices[i]-1;
normalIndicesArray[i+1] = normalIndices[i+1]-1;
normalIndicesArray[i+2] = normalIndices[i+2]-1;
}
// load normals in index order
for (int i = 0; i < numNormalIndices; i+=3)
{
int index = normalIndicesArray[i];
normalArray[i] = normals[index];
normalArray[i+1] = normals[index+1];
normalArray[i+2] = normals[index+2];
}
return;
}
void objFileRenderer::putArraysIntoVAO(float* vertexArray, int& numVertices, unsigned short* indexArray, int& numFaces, float* normalArray, int& numNormals, unsigned short* normalIndicesArray, int& numNormalIndices)
{
glGenVertexArrays(1, &vaoID[0]); // create our vertex array object
glBindVertexArray(vaoID[0]); // bind our vertex array object so we can use it
glGenBuffers(2, &iboID[0]); // generate our index buffer object
glGenBuffers(2, &vboID[0]); // generate our vertex buffer object
// normalArray holds normals in index order, so I shouldn't use this
// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID[1]); // bind our normal index buffer object
// glBufferData(GL_ELEMENT_ARRAY_BUFFER, (numNormalIndices) * sizeof(GLushort), normalIndicesArray, GL_STATIC_DRAW); // set the size and data of our IBO
glBindBuffer(GL_ARRAY_BUFFER, vboID[1]); // bind our normal vertex buffer object
glBufferData(GL_ARRAY_BUFFER, (numNormalIndices) * sizeof(GLfloat), normalArray, GL_STATIC_DRAW); // set the size and data of our VBO and set it to STATIC_DRAW
glVertexAttribPointer((GLuint)1, 3, GL_FLOAT, GL_FALSE, 0, 0); // set up our vertex attributes pointer
glEnableVertexAttribArray(1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iboID[0]); // bind our index buffer object
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (numFaces*3) * sizeof(GLushort), indexArray, GL_STATIC_DRAW); // set the size and data of our IBO
glBindBuffer(GL_ARRAY_BUFFER, vboID[0]); // bind our vertex buffer object
glBufferData(GL_ARRAY_BUFFER, (numVertices*3) * sizeof(GLfloat), vertexArray, GL_STATIC_DRAW); // set the size and data of our VBO and set it to STATIC_DRAW
glVertexAttribPointer((GLuint)0, 3, GL_FLOAT, GL_FALSE, 0, 0); // set up our vertex attributes pointer
glEnableVertexAttribArray(0);
glBindVertexArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return;
}
void objFileRenderer::setupScene()
{
app->setFramerateLimit(60); // max 60 FPS
glClearColor(0.4f, 0.6f, 0.9f, 0.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
shader = new Shader("shader.vert", "shader.frag");
projectionMatrix = glm::perspective(60.0f, (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);
return;
}
void objFileRenderer::renderScene(int& numVertices, int& numFaces, float*& colorArrayA, float*& colorArrayD, float*& colorArrayS, float*& normalArray, float objScale)
{
sf::Time elapsedTime = clock.getElapsedTime();
clock.restart();
if (rot > 360.0f)
rot = 0.0f;
rot += rotSpeed * elapsedTime.asSeconds();
float lightPosition[3] = { -100.0, -100.0, 100.0 };
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
viewMatrix = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, -3.0f, -10.0f)); // move back by 5 units
modelMatrix = glm::scale(glm::mat4(1.0f), glm::vec3(objScale)); // change last arg to 0.5f to shrink model by half
modelMatrix *= glm::rotate< float >(glm::mat4(1.0f), rot, glm::vec3(0, 1, 0));
shader->bind();
int projectionMatrixLocation = glGetUniformLocation(shader->id(), "projectionMatrix");
int viewMatrixLocation = glGetUniformLocation(shader->id(), "viewMatrix");
int modelMatrixLocation = glGetUniformLocation(shader->id(), "modelMatrix");
int ambientLocation = glGetUniformLocation(shader->id(), "ambientColor");
int diffuseLocation = glGetUniformLocation(shader->id(), "diffuseColor");
int specularLocation = glGetUniformLocation(shader->id(), "specularColor");
int lightPositionLocation = glGetUniformLocation(shader->id(), "lightPosition");
int normalMatrixLocation = glGetUniformLocation(shader->id(), "normalMatrix");
glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix[0][0]);
glUniformMatrix4fv(viewMatrixLocation, 1, GL_FALSE, &viewMatrix[0][0]);
glUniformMatrix4fv(modelMatrixLocation, 1, GL_FALSE, &modelMatrix[0][0]);
glUniform3fv(ambientLocation, 1, colorArrayA);
glUniform3fv(diffuseLocation, 1, colorArrayD);
glUniform3fv(specularLocation, 1, colorArrayS);
glUniform3fv(lightPositionLocation, 1, lightPosition);
glUniformMatrix3fv(normalMatrixLocation, 1, GL_FALSE, normalArray);
glBindVertexArray(vaoID[0]);
glDrawRangeElements(GL_TRIANGLES, 0, numFaces*3, numFaces*3, GL_UNSIGNED_SHORT, NULL);
glBindVertexArray(0);
shader->unbind();
app->display();
return;
}
void objFileRenderer::handleEvents()
{
sf::Event event;
while (app->pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
app->close();
}
if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
{
app->close();
}
if (event.type == sf::Event::Resized)
{
glViewport(0, 0, event.size.width, event.size.height);
}
}
return;
}
void objFileRenderer::mainLoop(int& numVertices, int& numFaces, float*& colorArrayA, float*& colorArrayD, float*& colorArrayS, float*& normalArray, float objScale)
{
while (app->isOpen())
{
renderScene(numVertices, numFaces, colorArrayA, colorArrayD, colorArrayS, normalArray, objScale);
handleEvents();
}
}
}
file: shader.cpp
#include "shader.h"
#include <string.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
static char* textFileRead(const char *fileName) {
char* text = NULL;
if (fileName != NULL) {
FILE *file = fopen(fileName, "rt");
if (file != NULL) {
fseek(file, 0, SEEK_END);
int count = ftell(file);
rewind(file);
if (count > 0) {
text = (char*)malloc(sizeof(char) * (count + 1));
count = fread(text, sizeof(char), count, file);
text[count] = '\0';
}
fclose(file);
}
}
return text;
}
Shader::Shader() {
}
Shader::Shader(const char *vsFile, const char *fsFile) {
init(vsFile, fsFile);
}
void Shader::init(const char *vsFile, const char *fsFile) {
shader_vp = glCreateShader(GL_VERTEX_SHADER);
shader_fp = glCreateShader(GL_FRAGMENT_SHADER);
const char* vsText = NULL;
const char* fsText = NULL;
vsText = textFileRead(vsFile);
fsText = textFileRead(fsFile);
if (vsText == NULL)
{
cerr << "Error: vertex shader file not found" << endl;
}
if (fsText == NULL) {
cerr << "Error: fragment shader file not found." << endl;
}
if (vsText == NULL || fsText == NULL)
return;
glShaderSource(shader_vp, 1, &vsText, 0);
glShaderSource(shader_fp, 1, &fsText, 0);
glCompileShader(shader_vp);
glCompileShader(shader_fp);
shader_id = glCreateProgram();
glAttachShader(shader_id, shader_fp);
glAttachShader(shader_id, shader_vp);
glLinkProgram(shader_id);
glBindAttribLocation(shader_id, 0, "in_Position");
//glBindAttribLocation(shader_id, 1, "in_Color");
glBindAttribLocation(shader_id, 1, "in_Normal");
}
Shader::~Shader() {
glDetachShader(shader_id, shader_fp);
glDetachShader(shader_id, shader_vp);
glDeleteShader(shader_fp);
glDeleteShader(shader_vp);
glDeleteProgram(shader_id);
}
unsigned int Shader::id() {
return shader_id;
}
void Shader::bind() {
glUseProgram(shader_id);
}
void Shader::unbind() {
glUseProgram(0);
}
file: shader.vert
#version 150 core
in vec3 in_Position;
in vec3 in_Normal;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;
uniform mat4 modelMatrix;
uniform vec3 lightPosition;
uniform mat3 normalMatrix;
smooth out vec3 vVaryingNormal;
smooth out vec3 vVaryingLightDir;
void main()
{
// derive MVP and MV matrices
mat4 modelViewProjectionMatrix = projectionMatrix * viewMatrix * modelMatrix;
mat4 modelViewMatrix = viewMatrix * modelMatrix;
// get surface normal in eye coordinates
vVaryingNormal = normalMatrix * in_Normal;
// get vertex position in eye coordinates
vec4 vPosition4 = modelViewMatrix * vec4(in_Position, 1.0);
vec3 vPosition3 = vPosition4.xyz / vPosition4.w;
// get vector to light source
vVaryingLightDir = normalize(lightPosition - vPosition3);
// Set the position of the current vertex
gl_Position = modelViewProjectionMatrix * vec4(in_Position, 1.0);
}
file: shader.frag
#version 150 core
out vec4 out_Color;
uniform vec3 ambientColor;
uniform vec3 diffuseColor;
uniform vec3 specularColor;
smooth in vec3 vVaryingNormal;
smooth in vec3 vVaryingLightDir;
void main()
{
// dot product gives us diffuse intensity
float diff = max(0.0, dot(normalize(vVaryingNormal), normalize(vVaryingLightDir)));
// multiply intensity by diffuse color, force alpha to 1.0
out_Color = vec4(diff * diffuseColor, 1.0);
// add in ambient light
out_Color += vec4(ambientColor, 1.0);
// specular light
vec3 vReflection = normalize(reflect(-normalize(vVaryingLightDir), normalize(vVaryingNormal)));
float spec = max(0.0, dot(normalize(vVaryingNormal), vReflection));
if (diff != 0)
{
float fSpec = pow(spec, 128.0);
// Set the output color of our current pixel
out_Color.rgb += vec3(fSpec, fSpec, fSpec);
}
}
I know it's a lot to look through, but I would be very happy to get some help with getting to the bottom of this problem, so thanks in advance to anyone who has some time to help me figure this out!
I don't think you're treating the indices correctly. OBJ's are tricky to parse in opengl because they provide data using a useless multiple-index format. Essentially you have to break down all of the vertices and normals and rebuild the indices such that for every vertex it's position/normal/color/whatever all share the same index. It's not a trivial task to reorder them, but if you look online for "obj vertex buffer" I'm sure you'll find hundreds of references and posts about it.
Try reading this and see if it makes more sense: http://aresio.blogspot.com/2009/07/wavefront-obj-files-vertex-buffer.html