Related
I'm writing an OpenGL library and I stumbled upon a problem regarding multiple vertex types and vertex shaders. Do I need to write a new vertex/fragment shader for each new vertex type that handles its attributes? Or do I need to write a single vertex/fragment shader that handles all the possible attributes?
These are some basic class "patterns" that I use for the vertex types.
struct simple_vertex
{
glm::vec3 position;
simple_vertex(glm::vec3 pos) {
position = pos;
}
simple_vertex() {
position = glm::vec3(0, 0, 0);
}
static void enable_attributes() {
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(simple_vertex),
(const GLvoid *) offsetof(simple_vertex, position));
glEnableVertexAttribArray(0);
}
glm::vec3 get_position() const {
return position;
}
};
struct colored_vertex {//vertex that holds position and color data
glm::vec3 position;
glm::vec3 color;
colored_vertex(glm::vec3 pos, glm::vec3 c) {
color = c;
position = pos;
}
colored_vertex() {
color = glm::vec3(0, 0, 0);
position = glm::vec3(0, 0, 0);
}
static void enable_attributes() {
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(colored_vertex),
(const GLvoid *) offsetof(colored_vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(colored_vertex),
(const GLvoid *) offsetof(colored_vertex, color));
glEnableVertexAttribArray(1);
}
glm::vec3 get_position() const {
return position;
}
};
struct textured_vertex {//vertex that holds position and texture coordinates
glm::vec3 position;
glm::vec2 texture_coords;
textured_vertex(glm::vec3 pos, glm::vec2 text_coords) {
texture_coords = text_coords;
position = pos;
}
textured_vertex() {
texture_coords = glm::vec2(0, 0);
position = glm::vec3(0, 0, 0);
}
static void enable_attributes() {
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(textured_vertex),
(const GLvoid *) offsetof(textured_vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(textured_vertex),
(const GLvoid *) offsetof(textured_vertex, texture_coords));
glEnableVertexAttribArray(1);
}
glm::vec3 get_position() const {
return position;
}
};
struct normal_textured_vertex {//vertex that holds position normal and texture coordinates
glm::vec3 position;
glm::vec2 texture_coords;
glm::vec3 normal;
normal_textured_vertex(glm::vec3 pos, glm::vec2 text_coords, glm::vec3 n) {
texture_coords = text_coords;
position = pos;
normal = n;
}
normal_textured_vertex() {
texture_coords = glm::vec2(0, 0);
position = glm::vec3(0, 0, 0);
normal = glm::vec3(0, 0, 0);
}
static void enable_attributes() {
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(normal_textured_vertex),
(const GLvoid *) offsetof(normal_textured_vertex, position));
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(normal_textured_vertex),
(const GLvoid *) offsetof(normal_textured_vertex, texture_coords));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(normal_textured_vertex),
(const GLvoid *) offsetof(normal_textured_vertex, normal));
glEnableVertexAttribArray(2);
}
glm::vec3 get_position() const {
return position;
}
};
The correct solution is option 3: don't have lots of meshes with wildly different vertex formats. By "vertex format", I mean the set of attributes (including how they're encoded in buffers) that a mesh provides.
In general, you should settle on a fairly limited set of vertex formats and adjust meshes (offline) to fit within those formats. You might have a format for non-skinned meshes, a format for skinned meshes, a format for GUI objects, maybe a format for particles, and perhaps one or two others.
If you are writing an application that has no control over the form of data it is given and has to work with whatever, even then I would suggest creating innocuous data for attributes not provided by the data. For example, if someone gives you a mesh with positions and UVs but no colors, create color data that is just repeated values of (1.0, 1.0, 1.0, 1.0). Your lighting equation should handle that color just fine. If someone gives you a mesh with positions and colors but no texture coordinates, create UV values that are just 0s (and it should be given a small, white texture to sample from). Etc.
Don't adjust your code to your data; adjust your data to your code.
I want to draw a cube and a sphere and apply a different texture to each.
I use blender to create the scene and then export to an obj file which then includes the vertices, normals, uvs and faces for both objects as well as the textures.
I have created a routine which loads all the data from the obj file. This all works as I can load the objects and display them etc but with only one texture. As I say I have gone through pages and pages of code and posts and 99% only deal with 1 texture to 1 object and those that deal with multiple textures only deal with one object or are in a very old version of openGL.
The one thing I haven't tried is uniform sample2D arrays in the fragment shader but I haven't found an explanation on that so haven't tried it.
My code that I have below:
ObjLoader *obj = new ObjLoader();
string _filepath = "objects\\" + _filename;
//bool res = obj->loadObjWithStaticColor(_filepath.c_str(), _vertices, _normals, vertex_colors, _colors, 1.0);
bool res = obj->loadObjWithTextures(_filepath.c_str(), _objects, _textures);
program = InitShader("shaders\\vshader.glsl", "shaders\\fshader.glsl");
glUseProgram(program);
GLuint vao_world_objects;
glGenVertexArrays(1, &vao_world_objects);
glBindVertexArray(vao_world_objects);
//GLuint vbo_world_objects;
//glGenBuffers(1, &vbo_world_objects);
//glBindBuffer(GL_ARRAY_BUFFER, vbo_world_objects);
NumVertices = _objects[_objects.size() - 1]._stop + 1;
for (size_t i = 0; i < _objects.size(); i++)
{
_vertices.insert(_vertices.end(), _objects[i]._vertices.begin(), _objects[i]._vertices.end());
_normals.insert(_normals.end(), _objects[i]._normals.begin(), _objects[i]._normals.end());
_uvs.insert(_uvs.end(), _objects[i]._uvs.begin(), _objects[i]._uvs.end());
}
GLuint _vSize = _vertices.size() * sizeof(point4);
GLuint _nSize = _normals.size() * sizeof(point4);
GLuint _uSize = _uvs.size() * sizeof(point2);
GLuint _totalSize = _vSize + _uSize; // normals + vertices + uvs
GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, _vSize, &_vertices[0], GL_STATIC_DRAW);
GLuint uvbuffer;
glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, _uSize, &_uvs[0], GL_STATIC_DRAW);
TextureID = glGetUniformLocation(program, "myTextureSampler");
TextureObjects = new GLuint[_textures.size()];
glGenTextures(_textures.size(), TextureObjects);
for (size_t i = 0; i < _textures.size(); i++)
{
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, TextureObjects[i]);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _textures[i].width, _textures[i].height, 0, GL_BGR, GL_UNSIGNED_BYTE, _textures[i]._tex_data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
for (size_t i = 0; i < _objects.size(); i++)
{
if (i == 0)
{
glActiveTexture(GL_TEXTURE0);
}
else
{
glActiveTexture(GL_TEXTURE1);
}
glBindTexture(GL_TEXTURE_2D, TextureObjects[i]);
GLuint _v_size = _objects[i]._vertices.size() * sizeof(point4);
GLuint _u_size = _objects[i]._uvs.size() * sizeof(point2);
GLuint vPosition = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(vPosition);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
if (i == 0)
{
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
}
else
{
glVertexAttribPointer(vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(_v_size));
}
GLuint vUV = glGetAttribLocation(program, "vUV");
glEnableVertexAttribArray(vUV);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
if (i == 0)
{
glVertexAttribPointer(vUV, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));
}
else
{
glVertexAttribPointer(vUV, 2, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(_u_size));
}
if (i == 0)
{
glUniform1i(TextureID, 0);
}
else
{
glUniform1i(TextureID, 1);
}
}
_scale = Scale(zoom, zoom, zoom);
_projection = Perspective(45.0, 4.0 / 3.0, 0.1, 100.0);
_view = LookAt(point4(Camera.x, Camera.y, Camera.z, 0), point4(0, 0, 0, 0), point4(0, 1, 0, 0));
_model = mat4(1.0); // identity matrix
_mvp = _projection * _view * _model;
MVP = glGetUniformLocation(program, "MVP");
theta = glGetUniformLocation(program, "theta");
Zoom = glGetUniformLocation(program, "Zoom");
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glEnable(GL_CULL_FACE);
glClearColor(1.0, 1.0, 1.0, 1.0);
I understand that I have to switch between the active textures when drawing an object but I can't figure out how.
UPDATE
#immibis Ok I tried to do that yesterday but it didn't work but it was late and I was highly frustrated. SO just to get my thinking correct here, do I have to create a buffer every time (glGenBuffer) and then fill it, activate texture and then glDrawArrays or do I just create the buffer and then fill it every time with the different vetices and uvs for each object, set the offsets and then call glDrawArray for each object?
When I tried this originally I didn't know where the
glGetAttribLocation / glEnableVertexAttribArray /glBindBuffer
should go. So if I understand correctly every time I do a transformation like rotating around the x axis then buffers have to be filled etc so the code needs to go in the display function. Is that correct?
SOLVED
Ok so thanx to immibus' comments, it got me looking in a different direction. I was staring the whole time into how the data was pumped into the arrays that I never even looked at glDrawArrays. I was searching the web again and I came across a piece of code in a tutorial and the person explained glDrawArrays and I saw that you can tell it what to draw.
So then this became easy, as I originally thought it was supposed to be. I changed my code back to pumping everything in the buffers and since I have a start and stop property on my objects returned from my loader it was real easy to tell glDrawArrays what to do.
Thank you.
I am trying to render 3D models with textures using Assimp. The conversion goes perfect, all textures positions and what not gets loaded. I have tested the texture images by drawing them to the screen in 2D.
For some reason it does not render the textures to the model.
I am a beginner in OpenGL so forgive me if i dont explain it right.
The tutorial I have based the code on is from here, but i stripped a big part since I have my own camera/movement system.
The model renders like this: http://i.stack.imgur.com/5sK9K.png
whilest the texture in use looks like this: http://i.stack.imgur.com/sWGp7.jpg
The relevant rendering code is the following:
Generating textures from data file:
int Mesh::LoadGLTextures(const aiScene* scene){
if (scene->HasTextures()) return -1; //yes this is correct
/* getTexture Filenames and Numb of Textures */
for (unsigned int m = 0; m<scene->mNumMaterials; m++){
int texIndex = 0;
aiReturn texFound;
aiString path; // filename
while ((texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path)) == AI_SUCCESS){
textureIdMap[path.data] = NULL; //fill map with textures, pointers still NULL yet
texIndex++;
}
}
int numTextures = textureIdMap.size();
/* create and fill array with GL texture ids */
GLuint* textureIds = new GLuint[numTextures];
/* get iterator */
std::map<std::string, GLuint>::iterator itr = textureIdMap.begin();
std::string basepath = getBasePath(path);
ALLEGRO_BITMAP *image;
for (int i = 0; i<numTextures; i++){
std::string filename = (*itr).first; // get filename
(*itr).second = textureIds[i]; // save texture id for filename in map
itr++; // next texture
std::string fileloc = basepath + filename; /* Loading of image */
image = al_load_bitmap(fileloc.c_str());
if (image) /* If no error occured: */{
GLuint texId = al_get_opengl_texture(image);
//glGenTextures(numTextures, &textureIds[i]); /* Texture name generation */
glBindTexture(GL_TEXTURE_2D, texId); /* Binding of texture name */
//redefine standard texture values
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* We will use linear
interpolation for magnification filter */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* We will use linear
interpolation for minifying filter */
textureIdMap[filename] = texId;
} else {
/* Error occured */
std::cout << "Couldn't load Image: " << fileloc.c_str() << "\n";
}
}
//Cleanup
delete[] textureIds;
//return success
return true;
}
Generating VBO/VAO:
void Mesh::genVAOsAndUniformBuffer(const aiScene *sc) {
struct MyMesh aMesh;
struct MyMaterial aMat;
GLuint buffer;
// For each mesh
for (unsigned int n = 0; n < sc->mNumMeshes; ++n){
const aiMesh* mesh = sc->mMeshes[n];
// create array with faces
// have to convert from Assimp format to array
unsigned int *faceArray;
faceArray = (unsigned int *)malloc(sizeof(unsigned int) * mesh->mNumFaces * 3);
unsigned int faceIndex = 0;
for (unsigned int t = 0; t < mesh->mNumFaces; ++t) {
const aiFace* face = &mesh->mFaces[t];
memcpy(&faceArray[faceIndex], face->mIndices, 3 * sizeof(unsigned int));
faceIndex += 3;
}
aMesh.numFaces = sc->mMeshes[n]->mNumFaces;
// generate Vertex Array for mesh
glGenVertexArrays(1, &(aMesh.vao));
glBindVertexArray(aMesh.vao);
// buffer for faces
glGenBuffers(1, &buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * mesh->mNumFaces * 3, faceArray, GL_STATIC_DRAW);
// buffer for vertex positions
if (mesh->HasPositions()) {
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(vertexLoc);
glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0);
}
// buffer for vertex normals
if (mesh->HasNormals()) {
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mNormals, GL_STATIC_DRAW);
glEnableVertexAttribArray(normalLoc);
glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0);
}
// buffer for vertex texture coordinates
if (mesh->HasTextureCoords(0)) {
float *texCoords = (float *)malloc(sizeof(float) * 2 * mesh->mNumVertices);
for (unsigned int k = 0; k < mesh->mNumVertices; ++k) {
texCoords[k * 2] = mesh->mTextureCoords[0][k].x;
texCoords[k * 2 + 1] = mesh->mTextureCoords[0][k].y;
}
glGenBuffers(1, &buffer);
glEnableVertexAttribArray(texCoordLoc);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 2 * mesh->mNumVertices, texCoords, GL_STATIC_DRAW);
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
}
// unbind buffers
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// create material uniform buffer
aiMaterial *mtl = sc->mMaterials[mesh->mMaterialIndex];
aiString texPath; //contains filename of texture
if (AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, 0, &texPath)){
//bind texture
unsigned int texId = textureIdMap[texPath.data];
aMesh.texIndex = texId;
aMat.texCount = 1;
} else {
aMat.texCount = 0;
}
float c[4];
set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
aiColor4D diffuse;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
color4_to_float4(&diffuse, c);
memcpy(aMat.diffuse, c, sizeof(c));
set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
aiColor4D ambient;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
color4_to_float4(&ambient, c);
memcpy(aMat.ambient, c, sizeof(c));
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
aiColor4D specular;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
color4_to_float4(&specular, c);
memcpy(aMat.specular, c, sizeof(c));
set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
aiColor4D emission;
if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
color4_to_float4(&emission, c);
memcpy(aMat.emissive, c, sizeof(c));
float shininess = 0.0;
unsigned int max;
aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
aMat.shininess = shininess;
glGenBuffers(1, &(aMesh.uniformBlockIndex));
glBindBuffer(GL_UNIFORM_BUFFER, aMesh.uniformBlockIndex);
glBufferData(GL_UNIFORM_BUFFER, sizeof(aMat), (void *)(&aMat), GL_STATIC_DRAW);
myMeshes.push_back(aMesh);
}
}
Rendering model:
void Mesh::recursive_render(const aiScene *sc, const aiNode* nd){
// draw all meshes assigned to this node
for (unsigned int n = 0; n < nd->mNumMeshes; ++n){
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, myMeshes[nd->mMeshes[n]].texIndex);
// bind VAO
glBindVertexArray(myMeshes[nd->mMeshes[n]].vao);
// draw
glDrawElements(GL_TRIANGLES, myMeshes[nd->mMeshes[n]].numFaces * 3, GL_UNSIGNED_INT, 0);
}
// draw all children
for (unsigned int n = 0; n < nd->mNumChildren; ++n){
recursive_render(sc, nd->mChildren[n]);
}
}
Any other relevant code parts can be found in my open github project https://github.com/kwek20/StrategyGame/tree/master/Strategy
Mesh.cpp is relevant, as well as main.cpp and Camera.cpp.
As far as I understaind I followed the guidelines well, created a VAO, created VBOs, added data and enabled the proper vertex array attriute tot render the scene with.
I have checked all the data variables and everything is filled according to plan
Could anyone here spot the mistake I have made and or explain it?
Some links are typed weird because of the limit I have :(
It would help if you posted your shaders also.
I can post some rendering code with textures if that helps you out:
Generating the texture for opengl and loading a grayscale (UC8) image with width and height into the GPU
void GLRenderer::getTexture(unsigned char * image, int width, int height)
{
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &mTextureID);
glBindTexture(GL_TEXTURE_2D, mTextureID);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, width, height);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, image);
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
glBindTexture(GL_TEXTURE_2D, 0);
}
Loading the vertices from assimp onto the gpu
//** buffer a obj file-style model, initialize the VAO
void GLRenderer::bufferModel(float* aVertexArray, int aNumberOfVertices, float* aNormalArray, int aNumberOfNormals, float* aUVList, int aNumberOfUVs, unsigned int* aIndexList, int aNumberOfIndices)
{
//** just to be sure we are current
glfwMakeContextCurrent(mWin);
//** Buffer all data in VBOs
glGenBuffers(1, &mVertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfVertices * 3, aVertexArray, GL_STATIC_DRAW);
glGenBuffers(1, &mNormal_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfNormals * 3, aNormalArray, GL_STATIC_DRAW);
glGenBuffers(1, &mUV_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfUVs * 2, aUVList, GL_STATIC_DRAW);
glGenBuffers(1, &mIndex_buffer_object);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * aNumberOfIndices, aIndexList, GL_STATIC_DRAW);
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
//** VAO tells our shaders how to match up data from buffer to shader input variables
glGenVertexArrays(1, &mVertex_array_object);
glBindVertexArray(mVertex_array_object);
//** vertices first
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
//** normals next
if (aNumberOfNormals > 0){
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
}
//** UVs last
if (aNumberOfUVs > 0){
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, NULL);
}
//** indexing for reusing vertices in triangle-meshes
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);
//** check errors and store the number of vertices
if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
mNumVert = aNumberOfVertices;
mNumNormals = aNumberOfNormals;
mNumUVs = aNumberOfUVs;
mNumIndices = aNumberOfIndices;
}
The code above is called like:
//read vertices from file
std::vector<float> vertex, normal, uv;
std::vector<unsigned int> index;
//assimp-wrapping function to load obj to vectors
aux::loadObjToVectors("Resources\\vertices\\model.obj", vertex, normal, index, uv);
mPtr->bufferModel(&vertex[0], static_cast<int>(vertex.size()) / 3, &normal[0], static_cast<int>(normal.size()) / 3, &uv[0], static_cast<int>(uv.size()) / 2, &index[0], static_cast<int>(index.size()));
Then comes the shader-part:
In the vertex shader you just hand-through the UV-coordinate layer
#version 400 core
layout (location = 0) in vec3 vertexPosition_modelspace;
layout (location = 1) in vec3 vertexNormal_modelspace;
layout (location = 2) in vec2 vertexUV;
out vec2 UV;
[... in main then ...]
UV = vertexUV;
While in the fragment shader you assign the value to the pixel:
#version 400 core
in vec2 UV;
uniform sampler2D textureSampler;
layout(location = 0) out vec4 outColor;
[... in main then ...]
// you probably want to calculate lighting here then too, so its just the simplest way to get the texture inside
outColor = vec4(texture2D(textureSampler, UV).rgb, cosAngle);
//you can also check whether the UV coords are correctly bound by using:
outColor = vec4(UV.x, UV.y,1,1);
//and then checking the pixel-values in the resulting image (e.g. render it to a PBO and then download it onto the CPU for)
In the rendering loop also make sure that all the uniforms are correctly bound (especially texture related ones) and that the texture is active and bound
if (mTextureID != -1) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mTextureID);
}
GLint textureLocation = glGetUniformLocation(mShaderProgram, "textureSampler");
glUniform1i(textureLocation, 0);
//**set the poligon mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//**drawElements because of indexing
glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, 0);
I hope I could help you!
Kind regards,
VdoP
I'm using VC++10 + OpenGL + the Assimp Library to consume and then render some 3D models.
The code is rendering the positions correctly, but for some reason the textures are seriously bugged. My texcoords appear to be loading correctly as are the texture files themselves - however I can't help but feel that the issue must be located with the loaded textures themselves.
www.flickr.com/photos/95269725#N02/8685913640/in/photostream
{ i seem to have a lack of rep to post inline images }
********** EDIT1 : ***********
So, I've been using the awesome GDebugger application to debug and interrogate the OpenGL pipeline in realtime. 2 things stand out really :
1. The biggy here is that the loaded texture is meant to look like this ->
http://www.flickr.com/photos/95269725#N02/8688860034/in/photostream
but actually looks like this when loaded into OpenGL memory :
http://www.flickr.com/photos/95269725#N02/8688860042/in/photostream/
2. Not sure if this is still applicable(as discussed in the comments), however the GL_TEXTURE_2D state variable is always FALSE throughout the game loop.
So I'm going to have to play with the texture loading code to see if I can get any traction there and post another update.
A few big relevant code chunks{sorry!} :
* Vertex Shader *
#version 420
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texCoord;
uniform mat4 cameraToClipMatrix;
uniform mat4 modelToCameraMatrix;
out vec2 oTexCoord;
out vec4 oNormal;
void main()
{
oTexCoord = texCoord;
vec4 cameraPos = modelToCameraMatrix * vec4(position,1.0);
gl_Position = cameraToClipMatrix * cameraPos;
oNormal = normalize(vec4(modelToCameraMatrix * vec4(normal,0.0)));
}
* Fragment Shader *
#version 420
in vec4 Normal;
in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
uniform sampler2D gSampler;
void main()
{
FragColor = texture(gSampler, TexCoord);
//FragColor = vec4(1.1, 0.0, 1.1, 1.0);
}
* GL Init etc *
void GLSystem::init() {
InitializeProgram();
glClearColor(0.75f, 0.75f, 1.0f, 1.0f);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glDepthFunc(GL_LEQUAL);
glDepthRange(0.0f, 1.0f);
}
void GLSystem::InitializeProgram()
{
std::vector<GLuint> shaderList;
shaderList.push_back(LoadShader(GL_VERTEX_SHADER, "VertShader1.vert"));
shaderList.push_back(LoadShader(GL_FRAGMENT_SHADER, "FragShader1.frag"));
theProgram = CreateProgram(shaderList);
modelToCameraMatrixUnif = glGetUniformLocation(theProgram, "modelToCameraMatrix"); // view matrix
cameraToClipMatrixUnif = glGetUniformLocation(theProgram, "cameraToClipMatrix"); // projection matrix
m_samplerUnif = glGetUniformLocation(theProgram, "gSampler"); // grab the gSampler uniform location reference in the fragment shader
float fzNear = 1.0f; float fzFar = 45.0f;
cameraToClipMatrix[0].x = fFrustumScale;
cameraToClipMatrix[1].y = fFrustumScale;
cameraToClipMatrix[2].z = (fzFar + fzNear) / (fzNear - fzFar);
cameraToClipMatrix[2].w = -1.0f;
cameraToClipMatrix[3].z = (2 * fzFar * fzNear) / (fzNear - fzFar);
glUseProgram(theProgram);
glUniformMatrix4fv(cameraToClipMatrixUnif, 1, GL_FALSE, glm::value_ptr(cameraToClipMatrix));
glUseProgram(0);
}
* Texture Loading *
bool CTexture::Load() {
m_texObj = 0; // init to zero
std::auto_ptr<glimg::ImageSet> pImgSet;
try {
pImgSet.reset( glimg::loaders::stb::LoadFromFile(m_filename) );
m_texObj = glimg::CreateTexture( &(*pImgSet), 0); // generates a texture and returns the related texture id
//glimg::SingleImage image = pImgSet->GetImage(0, 0, 0);
//glimg::Dimensions dims = image.GetDimensions();
//GLuint targetTexType = glimg::GetTextureType( &(*pImgSet), 0); // not using this yet - but potentially might need to base this objects targetType on this interpreted value.
//glimg::OpenGLPixelTransferParams params = GetUploadFormatType(image.GetFormat(), 0);
//glPixelStorei(GL_UNPACK_ALIGNMENT, image.GetFormat().LineAlign());
//glGenTextures(1, &m_texObj);
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, m_texObj);
//glTexImage2D(m_targetType, 0, glimg::GetInternalFormat(image.GetFormat(), 0), dims.width, dims.height, 0, params.format, params.type, image.GetImageData());
//glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, dims.width, dims.height, 0, GL_RGB, GL_UNSIGNED_BYTE, image.GetImageData() );
/*glTexParameterf(m_targetType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(m_targetType, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(m_targetType, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(m_targetType, GL_TEXTURE_WRAP_T, GL_REPEAT);*/
}
catch(glimg::loaders::stb::StbLoaderException &e) {
std::cout << "Warning : " << e.what() << " || .Image file loading failed for file : '" << m_filename << std::endl;
return false;
}
glBindTexture(m_targetType, 0); // Bind to default texture
return true;
}
* Mesh Loading *
#include "MeshModel.h"
// ----------------------------------------------------------------------------------------
#include "Texture.h"
#include "GLSystem.h"
#include "Game.h"
// ----------------------------------------------------------------------------------------
#include <assert.h>
// ----------------------------------------------------------------------------------------
MeshItem::MeshItem() {
}
MeshItem::MeshItem(MeshModel& p_meshModel) {
m_pmeshModel = &p_meshModel;
p_delete_object_data = true;
VBO = INVALID_OGL_VALUE;
IBO = INVALID_OGL_VALUE;
NBO = INVALID_OGL_VALUE;
TBO = INVALID_OGL_VALUE;
NumVertices = 0;
NumFaces = 0;
NumIndices = 0;
MaterialIndex = INVALID_MATERIAL;
};
MeshItem::~MeshItem() {
if (VBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &VBO);
}
if (IBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &IBO);
}
if (NBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &NBO);
}
if (TBO != INVALID_OGL_VALUE) {
glDeleteBuffers(1, &TBO);
}
}
void MeshItem::BuildVBO() {
glGenVertexArrays(1, &VAO); /* Generate a vertex array object - container for all vertex attribute arrays */
glBindVertexArray(VAO); /* Bind this VAO as the current Vertex Attribute Array container [ Holds the state for all attributes i.e. not the Vertex and Index data ] */
// Positions
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 3, &Positions[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); // Positions
// Indices
glGenBuffers(1, &IBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * NumFaces * 3, &Indices[0], GL_STATIC_DRAW);
// Normals
glGenBuffers(1, &NBO);
glBindBuffer(GL_ARRAY_BUFFER, NBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 3, &Normals[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); // Normals
// TexCoords
glGenBuffers(1, &TBO);
glBindBuffer(GL_ARRAY_BUFFER, TBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * NumVertices * 2, &TexCoords[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0); // TexCoords
glBindVertexArray(0); // Unbind the VAO
glBindBuffer(GL_ARRAY_BUFFER,0); // Unbind the vertices array buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // Unbind the indices array buffer
// Our copy of the data is no longer necessary, it is safe in the graphics card memory
if(p_delete_object_data) {
Positions.erase( Positions.begin(), Positions.end() );
Indices.erase( Indices.begin(), Indices.end() );
Normals.erase( Normals.begin(), Normals.end() );
TexCoords.erase( TexCoords.begin(), TexCoords.end() );
}
}
// ********************* MESHMODEL *********************
MeshModel::MeshModel(GLSystem& p_gls)
: m_pgls(&p_gls)
{
m_texUnit = 0;
m_samplerObj = 0;
}
MeshModel::~MeshModel() {
Clear();
}
GLSystem& MeshModel::getGLSystem() {
return *m_pgls;
}
void MeshModel::Clear() {
//for (unsigned int i = 0 ; i < m_textures.size() ; i++) {
// m_textures[i]);
//}
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);
}
bool MeshModel::LoadMesh(const std::string& p_filename) {
Clear(); // Release the previously loaded mesh (if it exists)
bool Ret = false;
Assimp::Importer Importer;
const aiScene* pScene = Importer.ReadFile(p_filename.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals /* | aiProcess_FlipWindingOrder*/ /* | aiProcess_FlipUVs*/ | aiProcess_ValidateDataStructure);
//const aiScene* pScene = aiImportFile(p_filename.c_str(), aiProcessPreset_TargetRealtime_MaxQuality);
if (pScene) {
printf("3D Object File '%s' loaded successfully.\n", p_filename.c_str() );
Ret = InitFromScene(pScene, p_filename);
}
else {
printf("Error parsing '%s': '%s'.\n", p_filename.c_str(), Importer.GetErrorString());
}
return Ret;
}
bool MeshModel::InitFromScene(const aiScene* pScene, const std::string& p_filename) {
//m_meshItems.resize(pScene->mNumMeshes);
m_textures.resize(pScene->mNumMaterials);
InitMaterials(pScene, p_filename); // load materials/textures etc
// Initialize the meshes in the scene one by one
for (unsigned int i = 0 ; i < pScene->mNumMeshes ; i++) {
const aiMesh* paiMesh = pScene->mMeshes[i];
MeshItem mItem(*this);
InitMesh(mItem, paiMesh);
mItem.BuildVBO();
m_meshItems.push_back(mItem);
}
return true;
}
void MeshModel::InitMesh(MeshItem& p_meshItem, const aiMesh* p_paiMesh) {
p_meshItem.MaterialIndex = p_paiMesh->mMaterialIndex;
// Indices
p_meshItem.NumFaces = p_paiMesh->mNumFaces;
p_meshItem.NumIndices = p_meshItem.NumFaces * 3;
p_meshItem.Indices.resize(p_meshItem.NumIndices);
for (unsigned int i = 0 ; i < p_paiMesh->mNumFaces ; ++i) {
const aiFace& face = p_paiMesh->mFaces[i];
assert(face.mNumIndices == 3);
p_meshItem.Indices[i*3+0] = face.mIndices[0];
p_meshItem.Indices[i*3+1] = face.mIndices[1];
p_meshItem.Indices[i*3+2] = face.mIndices[2];
}
p_meshItem.NumVertices = p_paiMesh->mNumVertices;
p_meshItem.Positions.resize(p_meshItem.NumVertices * 3);
p_meshItem.Normals.resize(p_meshItem.NumVertices * 3);
p_meshItem.TexCoords.resize(p_meshItem.NumVertices * 2);
for (unsigned int i = 0 ; i < p_paiMesh->mNumVertices ; ++i) {
// Positions
if( p_paiMesh->HasPositions() ) {
p_meshItem.Positions[i*3+0] = p_paiMesh->mVertices[i].x;
p_meshItem.Positions[i*3+1] = p_paiMesh->mVertices[i].y;
p_meshItem.Positions[i*3+2] = p_paiMesh->mVertices[i].z;
}
// Normals
if( p_paiMesh->HasNormals() ) {
p_meshItem.Normals[i*3+0] = p_paiMesh->mNormals[i].x;
p_meshItem.Normals[i*3+1] = p_paiMesh->mNormals[i].y;
p_meshItem.Normals[i*3+2] = p_paiMesh->mNormals[i].z;
}
// TexCoords
if( p_paiMesh->HasTextureCoords(0) ) {
p_meshItem.TexCoords[i*2+0] = p_paiMesh->mTextureCoords[0][i].x;
p_meshItem.TexCoords[i*2+1] = p_paiMesh->mTextureCoords[0][i].y;
}
}
}
bool MeshModel::InitMaterials(const aiScene* pScene, const std::string& p_filename) {
// Extract the directory part from the file name
std::string::size_type SlashIndex = p_filename.find_last_of("/");
std::string Dir;
if (SlashIndex == std::string::npos) {
Dir = ".";
}
else if (SlashIndex == 0) {
Dir = "/";
}
else {
Dir = p_filename.substr(0, SlashIndex);
}
bool Ret = true;
// Initialize the materials
for (unsigned int i = 0 ; i < pScene->mNumMaterials ; i++) {
const aiMaterial* pMaterial = pScene->mMaterials[i];
m_textures[i] = NULL;
std::string FullPath = "";
if (pMaterial->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
aiString Path;
if (pMaterial->GetTexture(aiTextureType_DIFFUSE, 0, &Path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
FullPath = Dir + "/" + Path.data;
m_textures[i] = std::make_shared<CTexture>( GL_TEXTURE_2D, FullPath.c_str() );
if ( !m_textures[i]->Load() ) {
printf("Error loading texture '%s'.\n", FullPath.c_str());
m_textures[i].reset();
m_textures[i] = NULL;
Ret = false;
}
else {
printf("Texture File '%s' loaded successfully\n", FullPath.c_str());
}
}
}
// Load a white texture in case the model does not include its own texture
if (!m_textures[i]) {
m_textures[i] = std::make_shared<CTexture>( GL_TEXTURE_2D, "..//Data/Textures/white.png");
printf("A default Texture File was loaded for '%s'.\n", FullPath.c_str());
Ret = m_textures[i]->Load();
}
}
// Genertate a Sampler object
glGenSamplers(1, &m_samplerObj);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glSamplerParameteri(m_samplerObj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
return Ret;
}
void MeshModel::DrawMesh() {
for (unsigned int i = 0 ; i < m_meshItems.size() ; i++) {
glUseProgram( getGLSystem().getProgram() ); // Bind to our selected shader program
glBindVertexArray(m_meshItems[i].VAO);
const unsigned int MaterialIndex = m_meshItems[i].MaterialIndex;
// If textures exist then bind them to samplers etc
if (MaterialIndex < m_textures.size() && m_textures[MaterialIndex]) {
glUniform1i(m_pgls->m_samplerUnif, 0);
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, m_textures[MaterialIndex]->m_texObj);
glBindSampler(0, m_samplerObj);
} else {
printf("MeshItem has no material!");
}
// RTS
glutil::MatrixStack currMatrix;
currMatrix.Translate(glm::vec3(0.0f, -3.0f, -10.0f));
currMatrix.Scale(0.1f, 0.1f, 0.1f);
currMatrix.RotateX(-90);
float a = Game::m_tick.asSeconds() /10;
float fAngRad = m_pgls->ComputeAngleRad(a, 2.0);
float fCos = cosf(fAngRad);
float fSin = sinf(fAngRad);
glm::mat3 theMat(1.0f);
theMat[0].x = fCos; theMat[1].x = -fSin;
theMat[0].y = fSin; theMat[1].y = fCos;
currMatrix.ApplyMatrix(glm::mat4(theMat));
glUniformMatrix4fv(m_pgls->modelToCameraMatrixUnif, 1, GL_FALSE, glm::value_ptr(currMatrix.Top()));
glDrawElements(GL_TRIANGLES, m_meshItems[i].NumIndices, GL_UNSIGNED_INT, 0);
glBindVertexArray(0); // Unbind the VAO
glUseProgram(0); // Close the link to the bound shader programs
}
}
I notice your vertex shader declares:
out vec2 oTexCoord;
but your fragment shader declares:
in vec2 TexCoord;
This might leave your texture coordinates undefined.
I think you need to enable textures with glEnable(GL_TEXTURES_2D) in your init section. I get the same look by commenting out that line from my project. Here's the code, if that helps:
EnableGraphics::EnableGraphics()
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 1.0);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // typical alpha transparency
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}
EDIT:
In case your link dies, I should add that your screenshot shows a 3D model with no textures or shading, although it has colors.
I have implemented a simple (slow) method that would imitate OpenGL immediate mode for drawing lines.
Each frame, I add a pair of vertices, that indicate lines to vector structure, as well as add some specified or default color to another vector structure.
void WindowsGraphicsManager::vertex(float x, float y, float z) {
vertices_.push_back(x);
vertices_.push_back(y);
vertices_.push_back(z);
colors_.push_back(vertexColor_.getR());
colors_.push_back(vertexColor_.getG());
colors_.push_back(vertexColor_.getB());
colors_.push_back(vertexColor_.getA());
}
And at the end of each frame I clear these vectors.
My render code looks like this:
void WindowsGraphicsManager::renderVertices(Mat4 mat) {
if (vertices_.size() == 0) {
return;
}
static Shader* shader = (Shader*) services_->getRM()->get(
Resource::SHADER, "openglimmediate");
glUseProgram(shader->getId());
shader->setMatrix4(Shader::WVP, mat);
glEnableVertexAttribArray(shader->getHandle(Shader::POS));
glVertexAttribPointer(shader->getHandle(Shader::POS),
3, GL_FLOAT, GL_FALSE, 0, &vertices_[0]);
glEnableVertexAttribArray(shader->getHandle(Shader::COL));
glVertexAttribPointer(shader->getHandle(Shader::COL),
4, GL_FLOAT, GL_FALSE, 0, &colors_[0]);
//LOGI("Before crash.");
//LOGI("Vertices size: %d", vertices_.size());
//LOGI("Colors size: %d", colors_.size());
//INFO: Vertices size: 607590
//INFO: Colors size: 810120
glDrawArrays(GL_LINES, 0, vertices_.size() / 3);
CHECK_GL_ERROR("Rendering lines.");
//LOGI("After crash.");
glDisableVertexAttribArray(shader->getHandle(Shader::COL));
glDisableVertexAttribArray(shader->getHandle(Shader::POS));
vertices_.clear();
colors_.clear();
}
When I add 607590 floats (divide by 3 for vertices) to vertices vector, rendering crashes on line with glDrawArrays function. Strange thing though, when I first maximize the window and render, then it works fine for model with 607590 floats, though it still crashes for model with ~800k flaots.
What might be causing this?
[Edit] Before rendering vertices I call one other method. After removing it, rendering stopped crashing, so I guess I do something wrong here.
inline void WindowsGraphicsManager::renderNode(
Node* node, Mat4 mat, bool ortho)
{
if (!node->getState(Node::RENDERABLE)) {
return;
}
// Retrieve model data.
Renderable* renderable = 0;
Resource* resource = 0;
if (node->hasResource(Resource::SPRITE)) {
resource = node->getResource(Resource::SPRITE);
renderable = dynamic_cast<Renderable*>(resource);
}
else if (node->hasResource(Resource::STATIC_OBJECT)) {
resource = node->getResource(Resource::STATIC_OBJECT);
renderable = dynamic_cast<Renderable*>(resource);
StaticObject* so = static_cast<StaticObject*>(resource);
// Check for frustum culling.
if (so->getBoundingVolume() != 0
&& so->getBoundingVolume()->isInFrustum(
services_->getCamera(), node->getPos())
== BoundingVolume::OUTSIDE)
{
return;
}
}
else if (node->hasResource(Resource::DYNAMIC_OBJECT)) {
resource = node->getResource(Resource::DYNAMIC_OBJECT);
renderable = dynamic_cast<Renderable*>(resource);
}
if (renderable == 0) {
LOGW("Renderable with name \"%s\" is null.",
node->getName().c_str());
return;
}
// Retrieve node shader or use default.
Shader* shader = static_cast<Shader*>(
node->getResource(Resource::SHADER));
if (shader == 0) {
LOGW("Unable to retrieve shader for node: %s.",
node->getName().c_str());
return;
}
int shaderId = shader->getId();
// Select shader program to use.
glUseProgram(shaderId);
CHECK_GL_ERROR("glUseProgram");
Mat4 res;
if (!ortho) {
Matrix::multiply(mat, node->getMatrix(), res);
}
else {
Mat4 tmp;
Mat4 pos;
Mat4 rot;
Mat4 scale;
Vec3 p = node->getPos();
Vec3 r = node->getRot();
Vec3 s = node->getScale();
float width = s.getX();
float height = s.getY();
float x = p.getX();
float y = p.getY();
Matrix::translate(pos, x, y, p.getZ());
Matrix::rotateXYZ(rot, r.getX(), r.getY(), r.getZ());
Matrix::scale(scale, width, height, 1.0f);
Matrix::multiply(mat, pos, res);
Matrix::multiply(res, rot, tmp);
Matrix::multiply(tmp, scale, res);
}
// World * View * Projection matrix.
shader->setMatrix4(Shader::WVP, res);
// World matrix.
shader->setMatrix4(Shader::W, node->getMatrix());
// Normal matrix.
if (shader->hasHandle(Shader::N)) {
Mat3 normalMatrix;
Matrix::toMat3(node->getMatrix(), normalMatrix);
shader->setMatrix3(Shader::N, normalMatrix);
}
// Light position.
float* lightPos = new float[lights_.size() * 3];
if (lights_.size() > 0 && shader->hasHandle(Shader::LIGHT_POS)) {
for (UINT i = 0; i < lights_.size(); i++) {
Vec3& pos = lights_[i]->getPos();
lightPos[i * 3 + 0] = pos.getX();
lightPos[i * 3 + 1] = pos.getY();
lightPos[i * 3 + 2] = pos.getZ();
}
shader->setVector3(Shader::LIGHT_POS, lightPos, lights_.size());
}
delete lightPos;
// Light count.
shader->setInt(Shader::LIGHT_COUNT, lights_.size());
//shader->setVector3(Shader::LIGHT_POS,
// services_->getEnv()->getSunPos()->toArray());
// Eye position.
shader->setVector3(Shader::EYE_POS,
services_->getCamera()->getPos().toArray());
// Fog color.
if (shader->hasHandle(Shader::FOG_COLOR)) {
shader->setVector3(Shader::FOG_COLOR,
services_->getEnv()->getFogColor());
}
// Fog density.
shader->setFloat(Shader::FOG_DENSITY, services_->getEnv()->getFogDensity());
// Timer.
shader->setFloat(Shader::TIMER,
(float) services_->getSystem()->getTimeElapsed());
// Bind combined buffer object.
if (renderable->getCBO() > 0) {
int stride = renderable->getVertexStride();
glBindBuffer(GL_ARRAY_BUFFER, renderable->getCBO());
if (shader->hasHandle(Shader::POS)) {
glEnableVertexAttribArray(shader->getHandle(Shader::POS));
glVertexAttribPointer(
shader->getHandle(Shader::POS), 3, GL_FLOAT, GL_FALSE,
stride, ((char*) 0) + renderable->getPosOffset());
}
if (renderable->getNormalOffset() != -1
&& shader->hasHandle(Shader::NORMAL)) {
glEnableVertexAttribArray(shader->getHandle(Shader::NORMAL));
glVertexAttribPointer(
shader->getHandle(Shader::NORMAL), 3, GL_FLOAT, GL_FALSE,
stride, ((char*) 0) + renderable->getNormalOffset());
}
if (renderable->getUVOffset() != -1 && shader->hasHandle(Shader::UV)) {
glEnableVertexAttribArray(shader->getHandle(Shader::UV));
glVertexAttribPointer(
shader->getHandle(Shader::UV), 2, GL_FLOAT, GL_FALSE,
stride, ((char*) 0) + renderable->getUVOffset());
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
else {
return;
}
// Bind cube map.
if (node->hasResource(Resource::CUBE_MAP)
&& shader->hasHandle(Shader::CUBE_MAP)) {
glActiveTexture(GL_TEXTURE0);
CHECK_GL_ERROR("glActiveTexture");
CubeMap* t = static_cast<CubeMap*>(
node->getResource(Resource::CUBE_MAP));
glBindTexture(GL_TEXTURE_CUBE_MAP, t->getId());
CHECK_GL_ERROR("glBindTexture");
glUniform1i(shader->getHandle(Shader::CUBE_MAP), 0);
CHECK_GL_ERROR("glUniform1i");
}
int hTextures[8];
hTextures[0] = glGetUniformLocation(shader->getId(),
SHADER_MAIN_TEXTURE);
// Bind the texture.
vector<Resource*> textures = node->getResources(Resource::TEXTURE_2D);
UINT size = textures.size() < 8 ? textures.size() : 7;
UINT texture = 0;
for (UINT i = 0; i < size; i++) {
texture = i + 1;
const string& name = textures[i]->getName();
Texture2D* tex = static_cast<Texture2D*>(textures[i]);
string textName = name.substr(0, name.length() - 4);
hTextures[texture] = glGetUniformLocation(shader->getId(),
textName.c_str());
if (hTextures[texture] == -1) {
continue;
}
glActiveTexture(GL_TEXTURE0 + i + 1);
CHECK_GL_ERROR("glActiveTexture");
glBindTexture(GL_TEXTURE_2D, tex->getId());
CHECK_GL_ERROR("glBindTexture");
glUniform1i(hTextures[texture], texture);
CHECK_GL_ERROR("glUniform1i");
}
// Render node.
// BoundingVolume* volume = (*model->getBoundingVolumes())[i];
// if (model->hasBoundingVolumes()) {
// if (volume->isInFrustum(services_->getCamera(), node)
// == BoundingVolume::OUTSIDE) {
// continue;
// }
// }
int renderType;
switch (renderable->getRenderType()) {
case Renderable::RENDER_TYPE_POINTS:
renderType = GL_POINTS;
//glPointSize(renderable->getPointSize());
break;
case Renderable::RENDER_TYPE_LINES:
renderType = GL_LINES;
glLineWidth(renderable->getLineWidth());
break;
case Renderable::RENDER_TYPE_TRIANGLE_FAN:
renderType = GL_TRIANGLE_FAN;
break;
case Renderable::RENDER_TYPE_TRIANGLE_STRIP:
renderType = GL_TRIANGLE_STRIP;
break;
default:
renderType = GL_TRIANGLES;
break;
}
if (renderable->getWindingType() == Renderable::WINDING_TYPE_CCW) {
glFrontFace(GL_CCW);
}
else {
glFrontFace(GL_CW);
}
if (renderable->getCullFace()) {
glEnable(GL_CULL_FACE);
}
else {
glDisable(GL_CULL_FACE);
}
UINT renderCount = renderable->getRenderCount();
int lastTexture = 0;
for (UINT i = 0; i < renderable->getRenderCount(); i++) {
renderable->setRenderable(i);
// Ambient material color.
if (shader->hasHandle(Shader::AMBIENT)) {
shader->setVector3(Shader::AMBIENT,
renderable->getAmbient().toArray());
}
// Diffuse material color.
if (shader->hasHandle(Shader::DIFFUSE)) {
shader->setVector3(Shader::DIFFUSE,
renderable->getDiffuse().toArray());
}
// Specular material color.
if (shader->hasHandle(Shader::SPECULAR)) {
shader->setVector3(Shader::SPECULAR,
renderable->getSpecular().toArray());
}
// Specular material color intensity.
shader->setFloat(Shader::SPECULARITY, renderable->getSpecularity());
// Model transparency.
shader->setFloat(Shader::TRANSPARENCY, renderable->getTransparency());
// Bind main texture.
if (renderable->getTexture() != lastTexture
&& hTextures[0] != -1) {
lastTexture = renderable->getTexture();
if (shader->hasHandle(Shader::MAIN_TEXTURE)) {
if (lastTexture == 0) {
shader->setFloat(Shader::MAIN_TEXTURE, 0.0f);
}
else {
shader->setFloat(Shader::MAIN_TEXTURE, 1.0f);
}
}
glActiveTexture(GL_TEXTURE0);
CHECK_GL_ERROR("glActiveTexture");
glBindTexture(GL_TEXTURE_2D, renderable->getTexture());
CHECK_GL_ERROR("glBindTexture");
glUniform1i(hTextures[0], 0);
CHECK_GL_ERROR("glUniform1i");
}
if (renderable->getIBO() > 0) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
renderable->getIBO());
if (renderable->getIndexType() ==
Renderable::INDEX_TYPE_USHORT) {
glDrawElements(renderType,
renderable->getIndexCount(),
GL_UNSIGNED_SHORT,
0);
CHECK_GL_ERROR("glDrawElements");
}
else {
glDrawElements(renderType,
renderable->getIndexCount(),
GL_UNSIGNED_INT,
0);
CHECK_GL_ERROR("glDrawElementsInt");
}
}
else {
glDrawArrays(renderType, 0, renderable->getVertexCount() / 3);
CHECK_GL_ERROR("glDrawArrays");
}
}
//// Unbind the cube map.
//if (node->hasResource(Resource::CUBE_MAP)) {
// glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
//}
//// Unbind the textures.
//for (UINT i = 0; i < 8; i++) {
// glActiveTexture(GL_TEXTURE0 + i);
// CHECK_GL_ERROR("glActiveTexture");
// glBindTexture(GL_TEXTURE_2D, 0);
//}
}
So the problem was glBindBuffer() call after this part of code:
// Bind combined buffer object.
if (renderable->getCBO() > 0) {
int stride = renderable->getVertexStride();
glBindBuffer(GL_ARRAY_BUFFER, renderable->getCBO());
if (shader->hasHandle(Shader::POS)) {
glEnableVertexAttribArray(shader->getHandle(Shader::POS));
glVertexAttribPointer(
shader->getHandle(Shader::POS), 3, GL_FLOAT, GL_FALSE,
stride, ((char*) 0) + renderable->getPosOffset());
}
if (renderable->getNormalOffset() != -1
&& shader->hasHandle(Shader::NORMAL)) {
glEnableVertexAttribArray(shader->getHandle(Shader::NORMAL));
glVertexAttribPointer(
shader->getHandle(Shader::NORMAL), 3, GL_FLOAT, GL_FALSE,
stride, ((char*) 0) + renderable->getNormalOffset());
}
if (renderable->getUVOffset() != -1 && shader->hasHandle(Shader::UV)) {
glEnableVertexAttribArray(shader->getHandle(Shader::UV));
glVertexAttribPointer(
shader->getHandle(Shader::UV), 2, GL_FLOAT, GL_FALSE,
stride, ((char*) 0) + renderable->getUVOffset());
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
I had to move glBindBuffer() to the end of same method, and I also wrote glDisableVertexAttribArray for position, normal and UV buffers. This solved the problem, but I'm not sure why. I thought there is no need to call glDisableVertexAttribArray for VBO.
I think this problem is specific for NVIDIA drivers and gives first chance exception for nvoglv32.dll.