Problems with multiple objects, shader & picking

I have an OBJ & MTL file exported from 3DS Max that is a complete chess set. In my end result, I would like to be able to select a game piece & then select a square to move it too. The elements I am having problems tying together are:

  • Object selection
  • Shaders

I have my object importer working in two ways.

  1. Group everything together with like materials into a struct called Mesh and loop through all the model.meshes drawing each frame.

  2. Identify the board, each square, & each game piece and place the relevant vertices, material, etc into a struct called GroupObject. They are placed in model.objects which I loop through drawing each frame.

Here is a screenshot of what each option described in #1 and #2 look like. In the top 1/2 of the SS, I just draw out the meshes and all the shading looks right, but nothing is selectable. In the bottom 1/2, I can select a game piece or a square just fine, but the model looks bad.

http://img815.imageshack.us/img815/2158/chesst.png

Additionally, if I use the shader with the code from #2, panning & zooming slows down to a crawl. It is almost completely unresponsive.

Here is the method called to draw the objects every frame. The first 2 commented lines is where I try option 1, and just draw my struct Mesh’s and it looks good. Rotating & Zooming are smooth as well, but I cannot select an object b/c I can’t do the glPushName of a Mesh. Right now, the code is setup looping through each model.objects so I can run the glPushName( (int)object ); which is necessary for the picking/selection of a game piece or square. The commented out section is where I try and apply the shader that is very slow when looping through model.objects (no slow down when looping model.mesh’s )


     glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    //for (int i = 0; i < model.getNumberOfMeshes(); ++i)
    //{
	for ( int i = 0; i < (int)model.objects.size(); i++ )
	{
		ModelOBJ::GroupObject *object = model.objects[i];

		glPushName( (int)object );
		
		int faceSize = (int)object->faces.size();

		for ( int j = 0; j < faceSize; j++ )
		{

			ModelOBJ::Face *face = object->faces[j];
			pMaterial = &model.getMaterial( face->materialId );


			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat *)pMaterial->ambient);
			glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat *)pMaterial->diffuse);
			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat *)pMaterial->specular);
			glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);

			glDisable(GL_TEXTURE_2D);
			drawFace( *face );

			//if (pMaterial->bumpMapFilename.empty())
			//{
				// Per fragment Blinn-Phong code path.

			//	glUseProgram(blinnPhongShader);

			//	// Bind the color map texture.

			//	texture = nullTexture;

			//	if (enableTextures)
			//	{
			//		iter = modelTextures.find(pMaterial->colorMapFilename);

			//		if (iter != modelTextures.end())
			//			texture = iter->second;
			//	}

			//	glActiveTexture(GL_TEXTURE0);
			//	glEnable(GL_TEXTURE_2D);
			//	glBindTexture(GL_TEXTURE_2D, texture);

			//	// Update shader parameters.

			//	glUniform1i(glGetUniformLocation(
			//		blinnPhongShader, "colorMap"), 0);
			//	glUniform1f(glGetUniformLocation(
			//		blinnPhongShader, "materialAlpha"), pMaterial->alpha);
			//}        
		}
		glPopName();
    }

    glBindTexture(GL_TEXTURE_2D, 0);
    glUseProgram(0);
    glDisable(GL_BLEND);

Here is the drawFace method in case it’s relevant


    void GLEngine::drawFace(ModelOBJ::Face &face)
    {
	if ( (int)face.numVertices <= 3 )
		glBegin(GL_TRIANGLES);
	else 
		glBegin(GL_POLYGON);

	for ( int v = 0; v < (int)face.numVertices; v++ )
	{
		if ( (int)face.numUVWs > v && face.UVWs != NULL )
			glTexCoord2f(face.UVWs[v]->x, face.UVWs[v]->y);

		if ( (int)face.numNormals > v && face.normals != NULL )
			glTexCoord3d(face.normals[v]->x, face.normals[v]->y, face.normals[v]->z);

		if ( (int)face.numVertices > v && face.vertices != NULL )
			glVertex3f(face.vertices[v]->x, face.vertices[v]->y, face.vertices[v]->z);
	}
	glEnd();
    }


And here is the shader I am using


    Per-fragment Blinn-Phong shader for a single directional light source.

    [vert]


    varying vec3 normal;

    void main()
    {
        normal = normalize(gl_NormalMatrix * gl_Normal);

        gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
        gl_TexCoord[0] = gl_MultiTexCoord0;    
    }

    [frag]


    uniform sampler2D colorMap;
    uniform float materialAlpha;

    varying vec3 normal;

    void main()
    {   
        vec3 n = normalize(normal);

        float nDotL = max(0.0, dot(n, gl_LightSource[0].position.xyz));
        float nDotH = max(0.0, dot(normal, vec3(gl_LightSource[0].halfVector)));
        float power = (nDotL == 0.0) ? 0.0 : pow(nDotH, gl_FrontMaterial.shininess);
    
        vec4 ambient = gl_FrontLightProduct[0].ambient;
        vec4 diffuse = gl_FrontLightProduct[0].diffuse * nDotL;
        vec4 specular = gl_FrontLightProduct[0].specular * power;
        vec4 color = gl_FrontLightModelProduct.sceneColor + ambient + diffuse + specular;
    
        gl_FragColor = color * texture2D(colorMap, gl_TexCoord[0].st);
        gl_FragColor.a = materialAlpha;
    }

How would you merge all of these functions together? My first thought is to draw all the model.objects with none of the material information and make them transparent. Then run through a second loop and draw all the model.meshes complete with material & using the shader. But then I thought when I start moving game pieces to a new square, how will I make sure the proper shading is applied to the piece’s new position. When I select the transparent GroupObject and it translate’s to a new square, will the piece still have the proper shading?

What is the best way to fix the problem I am having?

Hello,

First of all, for rendering, my advice would be to group faces by material in your scene and use vertex arrays & element arrays to draw them. That way, you only set-up the material (and shader stuff) once per material and let OpenGL cycle through your faces instead of having your CPU cycle through them.
Direct rendering (e.g. glBegin/glVertex/glEnd) is SLOW!
Even better, once you have the vertex array code set up, you can store your arrays on the GPU using vertex buffer objects.

For picking, you should drop the name buffer stuff altogether because that is not accelerated on consumer-grade cards.
Because there are not too many objects (64 squares + 32 pieces < 255), you can use the (accelerated) stencil buffer for that, retrieving the object index with a simple glReadPixels.
You need to render the stencil buffer only when doing picking and that can be in a code completely separate from the visual rendering.

For rendering to the stencil buffer, you would need to have geometry stored per-object but you no longer need information about material in that representation because you will only be rendering to the stencil and depth buffers. Again, it would be better to store geometry using arrays.
Disable the shader used for rendering the visuals, disable rendering to the color buffer (glColorMask) and just render to stencil and depth, using a different stencil index for each square and each game piece.

You should be able to get some pretty good performance that way.

Thank you for your reply shadocko. I do have all the faces grouped together by material when I run the ‘model.getNumberOfMeshes()’ so that is already setup and drawing everything correctly.

I will try and find a good example of using the stencil buffer with picking & glReadPixels (trying this although its built around a framework. Better tutorial suggestions appreciated). The way I coded above is the only way I knew how. I have all the geometry stored per object in my struct GroupObject(s), and it only contains the materialId which I can disregard for rendering in the stencil buffer.

So when I do get to the point of picking an object and moving it, how do you make sure the shading & colors are applied to the new position of the GroupObject? If each Mesh ( the vertex & element arrays grouped together by Material) is drawn separately from the GroupObject in the stencil buffer, how are they tied together?

You missed the point. What I was telling you is that the rendering code is inefficient, so it comes as no big surprises that the app is slow when changing the point of view.

First of all, you are using direct mode (e.g. glBegin/glVertex/glEnd) when you should be using arrays (glDrawArrays/glDrawElements).

Secondly, lopps should be nested differently. In your code you are setting up the material parameters, shader and texture once for every face. You should only have one setup for many faces. Also, when enabling the shader, you seem to be making a std::map lookup for every face so as to find the proper texture object, which is likely to be the biggest performance hog in your code.

The rendering code should look something like this:


// Vertex arrays setup
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 3, model.vertices );
glEnableClientState( GL_NORMAL_ARRAY );
glNormalPointer( GL_FLOAT, 3, model.normals );
glClientActiveTexture( GL_TEXTURE0 );
glEnableClientState( GL_TEXTURE_ARRAY );
glTexturePointer( 2, GL_FLOAT, 2, model.texCoords );

// Stencil setup
glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
glEnable( GL_STENCIL_TEST );

// Loop through objects...
for( int i=0 ; i<model.objects.size() ; ++i ) {
  ModelOBJ::GroupObject *object = model.objects[i];

  // Draw to stencil buffer with object's index
  glStencilFunc( GL_ALWAYS, object->index /* maybe i+1 */, 0xFF );

  // Loop through materials used by object...
  for( int j=0 ; j<object->materials.size() ; ++j ) {
    Material *m = object->materials[j];

    // Setup material here
    // Note: if using the same shader for every materials, bind
    // shader program outside every loops and just set up shader
    // uniforms.

    // Draw faces, letting OpenGL loop through them
    glDrawElements( m.primitiveType, m.primitiveCount, GL_UNSIGNED_INT, m.vertexIndices );
  }
}

Thanks for clearing all that up. Lots of new stuff in that code you posted. I am going to try plugging it in tonight and reading up on each of those methods.

I am guessing since all my types are triangles, it would look something like this:


glDrawElements( GL_TRIANGLES, m.numberOfVertexCoords, GL_UNSIGNED_INT, m.vertexCache );

Which is this map I created:


std::map<int, std::vector<int> > m.vertexCache;

That is created from this where m.vertexBuffer holds all model vertices:


index = static_cast<int>(m.vertexBuffer.size());
m.vertexCache[hash].push_back(index);

Hopefully after playing around with it tonight, I will be on to picking with correctly rendered objects! Thanks!

The second arg is the number of triangles, so unless struct/class members were named in a misleading way, it should be m.numberOfVertexCoords/3. The rest is right.

The last arg is a pointer to the beginning of an array of integers containing indices of vertices in the vertex array (e.g. a const GLuint*).
If it only contains a series of growing integers (e.g. 0, 1, 2, 3, 4…) then you can alternatively use glDrawArrays( GL_TRIANGLES, 0, m.numberOfVertexCoords ) and don’t need an index array.

Beware that vertex indices in GL start at 0, when they start at 1 in Wavefront OBJ files.

Sorry, it seems I did not read your post till the end.

So, what is the actual data type, content and layout of m.vertexCoords, and the value of m.numberOfVertexCoords?
Does vertexCoords contain vertices to be instanciated multiple times by contiguous faces or directly what would be sent between glBegin and glEnd for drawing raw geometry?
(and by the way, is “m” your model or the data associated with one material as my initial answer suggested?)

Maybe you should start by reading that small tutorial: http://www.songho.ca/opengl/gl_vertexarray.html

Ah ok thanks. I edited my post after you posted, I am thinking my m.vertexCache should be the last param in glDrawElements because I store this:


index = static_cast<int>(m.vertexBuffer.size());
m.vertexCache[hash].push_back(index);

Where m.vertexBuffer is ALL vertices in the model. Maybe changing the index to index+1 will account for the issue you bring up. Lots to try tonight! Thanks! :smiley:

<edit> OK I have confused myself. I will point out all the variables I have shortly as to not confuse you too.

yes sorry the ‘m’ represents model. Here is how I am storing my data:


m.vertexCoords //loop thru every 'v' line in wavefront obj file: &m_vertexCoords[3 * numVertices]; numVertices++;

m.vertexBuffer //when a struct *Vertex is created holding vertices, normals, texCoords: m.vertexBuffer.push_back(Vertex);

m.numberOfVertexCoords //Everytime I see a 'v' line in the wavefront obj file; ++m_numberOfVertexCoords;

m.vertexCache // After we do the m.vertexBuffer.push_back( *Vertex ); 
	      // index = static_cast<int>(m.vertexBuffer.size()); 
              // m.vertexCache[hash].push_back(index);
              // the 'hash' is an actual point in the vertex, so the vertexCache.push_back happens 3 times for every 'f' (face) line in the obj file

But I am thinking with your code I would do something like:


// Vertex arrays setup
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 3, GL_FLOAT, 3, m.vertexCoords );
glEnableClientState( GL_NORMAL_ARRAY );
glNormalPointer( GL_FLOAT, 3, m.normals );
glClientActiveTexture( GL_TEXTURE0 );
glEnableClientState( GL_TEXTURE_ARRAY );
glTexturePointer( 2, GL_FLOAT, 2, m.textureCoords );

// Stencil setup
glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
glEnable( GL_STENCIL_TEST );

// Loop through objects...
for( int i=0 ; i < m.objects.size() ; ++i ) {
  ModelOBJ::GroupObject *object = model.objects[i];

  // Draw to stencil buffer with object's index
  glStencilFunc( GL_ALWAYS, object->index[i + 1], 0xFF );

  // Loop through materials used by object...
  for( int j=0 ; j<object->materials.size() ; ++j ) {
    Material *m = object->materials[j];

    // Setup material here
    // Note: if using the same shader for every materials, bind
    // shader program outside every loops and just set up shader
    // uniforms.

    // Draw faces, letting OpenGL loop through them
    glDrawElements( GL_TRIANGLES, m.numberOfVertexCoords, GL_UNSIGNED_INT, m.vertexCache );
  }
}

I am also reading that tutorial now. Thanks for posting it! Hopefully it will start clicking and when I get home tonight I will be able to piece all this together. You have/are helping a lot. :slight_smile:

<edit> I think it’s starting to click. It’s the picking that was throwing me off. I may come back with another thread in a few days with picking though. But I think you have given me enough ammo to get it done!

And here is my final draw method. I think this should work. I will test it when I get home tonight. (Doing this over remote desktop, so I can’t test it from work).

If it works, we’ll see how picking with the stencil buffer and moving objects goes! I am thinking because a single ‘Mesh’ is all vertices grouped together by a material, I will need to do a second loop on model.objects just for the stencil buffer. Does that sound right to you?

<edit> for clarity:
‘GroupObject’ (model.objects) is a single game piece or game board square comprised of its veritces, texCoords, and normals.
‘Mesh’ (model.meshs) holds all vertices with like material


for ( int i = 0; i < (int)model.objects.size(); i++ )
{
     // Draw to stencil buffer with object's index
     glStencilFunc( GL_ALWAYS, (int)model.objects[i], 0xFF );
}

Thanks for the tips!


void GLEngine::drawModel()
{
	// Vertex arrays setup
	glEnableClientState( GL_VERTEX_ARRAY );
	glVertexPointer( 3, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->position );

	glEnableClientState(GL_NORMAL_ARRAY);
	glNormalPointer(GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->normal);

	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glTexCoordPointer(2, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->texCoord);

	// Stencil setup
	glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
	glEnable( GL_STENCIL_TEST );

	const ModelOBJ::Mesh *pMesh = 0;
    const ModelOBJ::Material *pMaterial = 0;
    const ModelOBJ::Vertex *pVertices = 0;
    ModelTextures::const_iterator iter;

	for (int i = 0; i < model.getNumberOfMeshes(); ++i)
    {
        pMesh = &model.getMesh(i);
        pMaterial = pMesh->pMaterial;
        pVertices = model.getVertexBuffer();
	  

		// Don't draw Mesh->index to stencil buffer; its not a single selectable object (game piece or square)
		//glStencilFunc( GL_ALWAYS, pMesh->startIndex, 0xFF );

		glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat *)pMaterial->ambient);
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat *)pMaterial->diffuse);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat *)pMaterial->specular);
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);

		// Draw faces, letting OpenGL loop through them
		glDrawElements(GL_TRIANGLES, pMesh->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMesh->startIndex);
	  }
}

Here is my final working draw method. I need to decipher the stencil buffer / picking stuff. Still shaky on how to do that. But it works, and is completely smooth. I am also going to change how the data is stored. And go back to storing objects individually and loop through them like you did in your original example. Currently the Mesh’s are a large number of vertices that have the same material. I wanted to just take the existing code and make it work. Thank you so much!

void GLEngine::drawModel()
{
	const ModelOBJ::Mesh *pMesh = 0;
    const ModelOBJ::Material *pMaterial = 0;
    const ModelOBJ::Vertex *pVertices = 0;
    ModelTextures::const_iterator iter;
    GLuint texture = 0;

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glUseProgram(blinnPhongShader);

    for (int i = 0; i < model.getNumberOfMeshes(); ++i)
    {
        pMesh = &model.getMesh(i);
        pMaterial = pMesh->pMaterial;
        pVertices = model.getVertexBuffer();

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient);
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular);
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);

        if (pMaterial->bumpMapFilename.empty())
        {
            texture = nullTexture;

            if (enableTextures)
            {
                iter = modelTextures.find(pMaterial->colorMapFilename);

                if (iter != modelTextures.end())
                    texture = iter->second;
            }

            glActiveTexture(GL_TEXTURE0);
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, texture);

            // Update shader parameters.

            glUniform1i(glGetUniformLocation(
                blinnPhongShader, "colorMap"), 0);
            glUniform1f(glGetUniformLocation(
                blinnPhongShader, "materialAlpha"), pMaterial->alpha);
        }
       
        // Render mesh.

        if (model.hasPositions())
        {
            glEnableClientState(GL_VERTEX_ARRAY);
            glVertexPointer(3, GL_FLOAT, model.getVertexSize(),
                model.getVertexBuffer()->position);
        }

        if (model.hasTextureCoords())
        {
            glClientActiveTexture(GL_TEXTURE0);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glTexCoordPointer(2, GL_FLOAT, model.getVertexSize(),
                model.getVertexBuffer()->texCoord);
        }

        if (model.hasNormals())
        {
            glEnableClientState(GL_NORMAL_ARRAY);
            glNormalPointer(GL_FLOAT, model.getVertexSize(),
                model.getVertexBuffer()->normal);
        }

        if (model.hasTangents())
        {
            glClientActiveTexture(GL_TEXTURE1);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glTexCoordPointer(4, GL_FLOAT, model.getVertexSize(),
                model.getVertexBuffer()->tangent);
        }

        glDrawElements(GL_TRIANGLES, pMesh->triangleCount * 3, GL_UNSIGNED_INT,
            model.getIndexBuffer() + pMesh->startIndex);

        if (model.hasTangents())
        {
            glClientActiveTexture(GL_TEXTURE1);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }

        if (model.hasNormals())
            glDisableClientState(GL_NORMAL_ARRAY);

        if (model.hasTextureCoords())
        {
            glClientActiveTexture(GL_TEXTURE0);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }

        if (model.hasPositions())
            glDisableClientState(GL_VERTEX_ARRAY);
    }

    glBindTexture(GL_TEXTURE_2D, 0);
    glUseProgram(0);
    glDisable(GL_BLEND);
}

Halik,

I think you got the stride wrong in the gl*Pointer function calls. It’s the spacing between vertices or tex coords in your float array, in case you want to interleave data (vertices, normals, tx coords in the same array).
So if, for instance, your vertex array contains x1, y1, z1, x2, y2, z2 and so forth, stride is 3. Shall it contain x1, y1, z1, nx1, ny1, nz1, s1, t1, x2, y2, z2, nx2, ny2, nz2, s2, t2, … stride would be 8.
Note that if your vertex array only contains tightly packed vertices (no interleaved normals, etc, like my first example), passing 0 will use the vertex size (in your case 3) for stride.

Also, the stencil index passed to glStencilFunc is that: an INDEX. It must be in range 0-255 as graphics cards typically support only an 8-bit stencil buffer, so don’t try to pass in a pointer cast to integer. You should better use the index used to loop through objects in your scene (maybe +1 so that stencil index 0 is for the backdrop only).

Finally, when clearing the buffer, clear the stencil buffer too: glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); and initialize your visual with a stencil buffer. That seemed trivial to me but better said than forgotten. :wink:

My bad! I got the stride wrong too: it’s the number of bytes between vertices, so you’ll have to multiply by sizeof(GLfloat).

Still, if data is tightly packed, 0 would be prefered.

In my updated version I am going to attempt this weekend will store data like this


struct Vertex
    {
        float position[3];
        float texCoord[2];
        float normal[3];
        float tangent[4];
        float bitangent[3];
        Material *material;
    };

struct GroupObject
{
      std::vector<Vertex *>	vertices;
      std::string				objectName;
      std::string				groupName;
};


Where my GroupObject is going to be model.objects that I loop through. Each GroupObject will be a game piece or a square on the board. I should probably give each GroupObject an index during the import process as well for the stencil buffer. But doing it in this way, I can use object->vertices[0]->position || texCoord || normal in my gl*Pointers, right?

Saw what you just wrote. So if I understand you correctly, I can just use 0 in the gl*Pointers if I loop through the GroupObjects? Something like:


//loop through objects (game pieces); and draw their vertices
  for( int j=0 ; j< model.objects.size() ; ++j ) {
    GroupObject *obj = model.objects[j];
    for ( int i=0; i < obj->materials.size(); ++i )
    {
       Material *m = obj->materials[i];

       // Setup material here

       // Draw faces, letting OpenGL loop through them
       glDrawElements( GL_TRIANGLES, m->vertices.size(), GL_UNSIGNED_INT, 0 );
    }
  }

Hopefully I got it psuedo right. Although as I look at it, I know its wrong. I need to go back and draw this out. Now I am thinking I should have a GroupObject that holds Materials, and each material will have all its vertices, normals, texCoords, and pass those to glDrawElements. I guess I am just thinking out loud at this point. The loop is correct, but I need to change how data is stored. I will try and just post back when I have it drawing each GroupObject drawing correctly. (and fast)

Shouldn’t this work? I get a black screen even though mVertices contains all the vertex indices. (vector size little over 12K for the chessboard)


glEnableClientState( GL_VERTEX_ARRAY );
	glVertexPointer(3, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->position);

	glEnableClientState( GL_NORMAL_ARRAY );
	glNormalPointer(GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->normal);

	glClientActiveTexture( GL_TEXTURE0 );
	glEnableClientState( GL_TEXTURE_COORD_ARRAY );
	glTexCoordPointer(2, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->texCoord);

	// Stencil setup
	glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
	glEnable( GL_STENCIL_TEST );

	glUseProgram(blinnPhongShader);

	objects = model.getObjects();
	// Loop through objects...
	for( int i=0 ; i < objects.size(); i++ ) 
	{
		ModelOBJ::GroupObject *object = objects[i];

		// Draw to stencil buffer with object's index
		glStencilFunc( GL_ALWAYS, object->index, 0xFF );

		// Loop through materials used by object...
		for( int j=0 ; j<object->materials.size() ; j++ ) 
		{
			ModelOBJ::Material *pMaterial = object->materials[j];

			glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient);
			glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse);
			glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular);
			glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);

                        std::vector<int> mVertices = model.getMaterialVertices(pMaterial->id);

                        glDrawElements( GL_TRIANGLES, pMaterial->vertices.size() * 3, GL_UNSIGNED_INT, &mVertices[0] );
      }
}

Also tried with


glDrawElements( GL_TRIANGLES, pMaterial->vertices.size() * 3, GL_UNSIGNED_INT, 0 );

I also tried this as a hack, and when I did a breakpoint, arr is an int[] which contains all the indices. Everything still black screen. :\


	std::vector<int> mVertices = model.getMaterialVertices(pMaterial->id);
			
			int arr[128688];
			memcpy( arr, &mVertices[0], sizeof( int ) * mVertices.size() );

			// Draw faces, letting OpenGL loop through them
			glDrawElements( GL_TRIANGLES, pMaterial->vertices.size() * 3, GL_UNSIGNED_INT, arr );

I am going to keep searching the forums. Some other people seem to be trying to do what I am doing. If I don’t get this figured out by the end of the weekend, I will probably make a separate thread in the beginners section. :slight_smile:

Here is the routine where I read in the indices from the faces in the obj file, accounting for the 0-based vectors & 1 based OBJ file:


else if (sscanf(buffer, "%d/%d/%d", &v[0], &vt[0], &vn[0]) == 3) // v/vt/vn
        {
            fscanf(pFile, "%d/%d/%d", &v[1], &vt[1], &vn[1]);
            fscanf(pFile, "%d/%d/%d", &v[2], &vt[2], &vn[2]);

            v[0] = (v[0] < 0) ? v[0] + numVertices - 1 : v[0] - 1;
            v[1] = (v[1] < 0) ? v[1] + numVertices - 1 : v[1] - 1;
            v[2] = (v[2] < 0) ? v[2] + numVertices - 1 : v[2] - 1;

currentMaterial->indices.push_back(v[0]);
currentMaterial->indices.push_back(v[1]);
currentMaterial->indices.push_back(v[2]);

if ( currentMaterial->startIndex == -1 )
    currentMaterial->startIndex = v[0];

currentMaterial->triangleCount++;

As you can see, I also tried keeping a triangleCount when processing the faces from the obj file as well as making a “startIndex” in the same routine. Then I try glDrawElements like this:


glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, &pMaterial->startIndex );

//or tried this

glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, &pMaterial->indices.front() );

Still get nothing. The black background is drawn, but no objects. :frowning:

Here is my current draw method, and a screenshot of it’s output. Objects being connected that shouldn’t be, random sections/triangles missing. Any ideas?

void GLEngine::drawModel()
{
ModelTextures::const_iterator iter;
GLuint texture = 0;

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// Vertex arrays setup
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer(3, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->position);

glEnableClientState( GL_NORMAL_ARRAY );
glNormalPointer(GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->normal);

glClientActiveTexture( GL_TEXTURE0 );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer(2, GL_FLOAT, model.getVertexSize(), model.getVertexBuffer()->texCoord);

glUseProgram(blinnPhongShader);

objects = model.getObjects();
// Loop through objects...
for( int i=0 ; i < objects.size(); ++i ) 
{
    ModelOBJ::GroupObject *object = objects[i];

    // Loop through materials used by object...
    for( int j=0 ; j<object->materials.size() ; ++j ) 
    {
        ModelOBJ::Material *pMaterial = object->materials[j];

        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient);
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular);
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * 128.0f);

        if (pMaterial->bumpMapFilename.empty())
        {

             //Bind the color map texture.
            texture = nullTexture;
            if (enableTextures)
            {
                iter = modelTextures.find(pMaterial->colorMapFilename);

                if (iter != modelTextures.end())
                    texture = iter->second;
            }

            glActiveTexture(GL_TEXTURE0);
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, texture);

             //Update shader parameters.
            glUniform1i(glGetUniformLocation(
                blinnPhongShader, "colorMap"), 0);
            glUniform1f(glGetUniformLocation(
                blinnPhongShader, "materialAlpha"), pMaterial->alpha);
        }
        //glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, &pMaterial->indices.front() );
        glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMaterial->startIndex );

    }

}
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindTexture(GL_TEXTURE_2D, 0);
    glUseProgram(0);
    glDisable(GL_BLEND);
}

http://img844.imageshack.us/img844/3793/chess4.png

I have added this Visual Studio solution to github if anyone is willing to take a look. If you change the model path on line 9 in main.cpp, it should compile and run with no other setup.

https://github.com/nbyloff/KinectChess

Getting closer!


glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + (pMaterial->startIndex * 3) ); 

http://img337.imageshack.us/img337/860/chess4s.png

I believe the problem is in the ModelOBJ::buildMeshes() method. I am trying to create a ModelOBJ::buildObjects() routine that does the same thing, but with GroupObjects & Materials instead of the Mesh struct.

<edit> This was the fix. It finally works! Now onto picking with the stencil buffer (and back to the beginners section :wink: ) Haven’t found a good link / tutorial on stencil picking yet though.