Related
I'm new to OpenGL, SDL and OBJ-files and am fighting with an issue for many days now.
setting up the SDL-Window (i've dropped the basic SDL-Init-stuff)
sdl_window = SDL_CreateWindow(
"Viewer",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
1024,
768,
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE );
if( sdl_window == NULL )
printf("SDL_CreateWindow failed: %s\n", SDL_GetError());
SDL_GLContext glContext = SDL_GL_CreateContext( sdl_window );
if( glContext == NULL )
printf("SDL_GL_CreateContext failed: %s\n", SDL_GetError());
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
Starting the OpenGL functionality (where "obj" is my class for parsing OBJ and loading Texture)
int my_object;
objloader obj;
glClearColor( 0.5f, 0.5f, 0.5f, 1.0f );
glViewport( 0, 0, 1024, 768 );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
gluPerspective( 45, 1024.0/768.0, 1.0, 500.0 );
//glOrtho( 0, 512, 384, 0, -1, 1 );
glMatrixMode( GL_MODELVIEW );
glEnable( GL_DEPTH_TEST );
my_object = obj.load( "D:\myfile.obj" );
glEnable( GL_LIGHTING );
glEnable( GL_LIGHT0 );
float col[] = {1.0,1.0,1.0,1.0};
glLightfv( GL_LIGHT0, GL_DIFFUSE, col );
a short look into the OBJ-file... (no vn - normals in it)
mtllib ./test.mtl
usemtl Haufwerk
v -0.388155 5.580893 2.490415
v -0.336669 5.566026 2.507601
v -0.408834 5.515674 2.533414
v -0.403397 5.444500 2.540850
v -0.482161 5.577362 2.447329
.
.
.
v -0.533898 5.328258 1.909646
v -0.439773 5.344517 1.849366
vt 0.710938 0.273924
vt 0.711743 0.270957
vt 0.712080 0.277021
vt 0.713371 0.278851
vt 0.711261 0.303822
.
.
.
vt 0.845899 0.687021
vt 0.841930 0.687169
f 5616/687 5617/688 4642/686
f 5617/691 5618/692 4642/690
f 5616/687 4642/686 5618/689
f 5617/688 5616/687 5618/689
f 12657/633 12659/634 13191/637
.
.
.
Texture comes as BMP file with resolution 1024x1024
OBJ texture image:
running the display loop
while( running )
{
start = SDL_GetTicks();
while( SDL_PollEvent(&event) )
{
switch(event.type)
{
case SDL_QUIT:
running = false;
break;
}
}
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glLoadIdentity();
float pos[] = {-1.0,1.0,-2.0,1.0};
glLightfv( GL_LIGHT0, GL_POSITION, pos );
//glScalef( 1.f, 1.f, 1.f );
glTranslatef(0.0, -30.0,-20.0);
//glTranslatef( 10.0, 10.0, -50.0 );
//glTranslatef( 0.0, 1.0, -19.5 );
glCallList( myobject ); //and display it
SDL_GL_SwapWindow( sdl_window );
if( 1000/30 > (SDL_GetTicks() - start) )
SDL_Delay( 1000/30 - (SDL_GetTicks() - start) );
}
...comes to this results:
Results: OpenGL-object in SDL-window:
below picture resulted from changing one display-code-line to:
glTranslatef( 0.0, 1.0, -19.5 );
My aim is to "fit" in the "OpenGL thing" (what's it called...? Model? Framebuffer? Object?) into the bounds of the SDL-window. Or maybe "resize" the SDL-window to the OpenGL object.
Why? Because in the next step I'm "exporting" the whole SDL-window to a bitmap that is going to be used as digitize background in a GIS-software for georeferencing purposes.
Do I have to change the view-settings or is there a possibility to "grab" the size of the OpenGL object for adapting the SDL main window?
For example if I change one line in the displaying code to:
glTranslatef( 0.0, 1.0, -19.5 );
What are exactly the "coordinates" of the vertices "v" in the OBJ file? Which "measurement unit" do they own? Is it a local coosys?
What is the textured OpenGL object "pixel-size" (because the pixel-size of the texture template is 1024x1024)??
Many of the codelines I took from online tutorials, but I'm not sure how they affect my screen in detail. I'm too confused of all the possibilities to change views etc.
So any help is appreciated. This gives me bad sleep...
One way you could do that is, find the object dimensions (iterate through all pixels and determine the minimum and maximum values in each axis), find the depth of the object relative to the camera viewpoint and the view-frustum dimensions at that depth. Finally you can find the best scale to apply to your object so that it covers the entire screen.
But that is complicated. It would be easier to utilize an orthographic projection and then "play around" with the scale of your object. You can still find the object dimensions as I mentioned above for a direct way to do this.
Use these:
glOrtho( -512, 512, -384, 384, -16, 16 );
// Remove the translations you were applying to your object
glScalef( 100.0f, 100.0f, 1.0f );
This should allow you to see your object with an orthographic projection. From here you should be able to tweak the scale values until you get what you want.
Could you explain the "Z" value?
First off it's my mistake. It's not exactly a Z value but a depth value.
By default, when you specify a viewing volume in OpenGL whether using an orthographic or perspective projection, the "camera" will be looking in the direction of the negative Z axis. You can change the direction using the lookAt function, but in your example you don't so you get the default view direction. For this reason you'll often see Z-Depth being used.
An orthographic projection defines a cube/prism viewing-volume whose effect is that all objects, no matter how far from the viewer, will appear at the same scale. The last 2 arguments of the glOrtho function are the near and far values, define the depth of the viewing volume. In the comments I sugested using:
glOrtho( -512, 512, -384, 384, -16, 16 )
Since, by default, the camera is looking in the negative Z direction, centered at 0, we can see objects within the volume in a relative depth of [-16,16]. That is:
At a near value of |-16 * (0,0,-1)| = 16 and a far value of |16 * (0,0,-1)| = -16.
What are exactly the "coordinates" of the vertices "v" in the OBJ file?
It's likely that they are in object coordinates (object space), which define the position of each vertex relative to the object itself, with the origin at 0,0,0. So yes, a local coordinate system of the object.
When you apply transforms to an object, namely translations, the resulting values will be in world coordinates (world space).
Do I have to change the view-settings or is there a possibility to "grab" the size of the OpenGL object for adapting the SDL main window?
That is up to you to decide. But it might make more sense to scale your object to fit the window than the other way around. I showed you a way to scale the object.
What is the textured OpenGL object "pixel-size" (because the pixel-size of the texture template is 1024x1024)??
When referring to texture pixels we call them texels. But I don't understand what you're asking.
(what's it called...? Model? Framebuffer? Object?)
I guess you mean the 3D Object / Model that you're rendering with a texture.
See Default Framebuffer and Framebuffer to be sure.
Important: I have to work with the fixed pipeline (I have no voice in this matter).
I have to modify some existing OpenGL code (a panoramic picture viewer, where the panorama is split into the six faces of a cube) so we're able to draw lines/points on top of the loaded textures, where the points are the mouse coordinates unprojected to object coordinates.
I wrote a test program with a coloured cube just to try the line painting on top of it:
I got this with the code pushing the GL_DEPTH_BUFFER_BIT attribute to the stack, disabling it before painting the points and poping the stack attribute after I have done with the painting.
I tried to use that same approach in the existing application, but I got these results (here, I'm trying only to paint a point):
I specified red as the color for the point but, as you can see, it doesn't have the desired one. I thought it might be due to blending and that it might be mixing its color with the underlying texture, so I pushed the GL_BLEND attribute to the stack as well and disabled it before painting, but the point isn't getting the desired color anyway.
What is happening here? Is there a way to "force" the pipeline to paint the point red?
initCube() : this is call before updating the GL scene.
void panoViewer::initCube() {
makeCurrent();
if(texture){
glDisable( texture );
textName = 0;
texture = 0;
}
glDisable( GL_TEXTURE_GEN_S );
glDisable( GL_TEXTURE_GEN_T );
glDisable( GL_TEXTURE_GEN_R );
glFrontFace( GL_CCW );
glEnableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
texture = GL_TEXTURE_CUBE_MAP;
textName = texnms[1];
glEnableClientState(GL_NORMAL_ARRAY);
glTexGenf( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP );
glEnable( GL_TEXTURE_GEN_S );
glEnable( GL_TEXTURE_GEN_T );
glEnable( GL_TEXTURE_GEN_R );
// Add the textures to the cube faces.
// ...
}
initializeGL() :
void panoViewer::initializeGL() {
qglClearColor(Qt::black);
glShadeModel(GL_SMOOTH);
glEnable(GL_CULL_FACE);
glEnable( GL_DEPTH_TEST );
// create texture objects
glGenTextures( 1, textName );
glBindTexture( GL_TEXTURE_CUBE_MAP, textName );
// find the largest feasible textures
maxTex2Dsqr = maxTexSize( GL_PROXY_TEXTURE_2D, max2d, max2d );
maxTex2Drec = maxTexSize( GL_PROXY_TEXTURE_2D, max2d, max2d / 2 );
maxTexCube = maxTexSize( GL_PROXY_TEXTURE_CUBE_MAP, maxcube, maxcube );
// constant texture mapping parameters...
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
// for cube maps...
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// enable alpha blending for overlay
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_POINT_SMOOTH);
glEnable(GL_LINE_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glDisable(GL_LIGHTING);
// Create display list: dispList
// ...
}
paintGL() :
void panoViewer::paintGL() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(texture) {
glBindTexture(texture, textName);
glEnable( texture );
}
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glRotated( 180, 0, 1, 0 ); // camera looks at the front of the van
glRotated( 180, 0, 0, 1 ); // van's roof points to the sky
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// double hFOV, vFOV; // angular size at sphere center (deg)
// double minFOV, maxFOV; // limits on vFOV
// double wFOV; // vert angle at eye (deg) sets magnification
double hhnear = Znear * tan( 0.5 * RAD(wFOV) ),
hwnear = hhnear * aspectRatio,
dxnear = 2 * hwnear * fcompx,
dynear = 2 * hhnear * fcompy;
glFrustum( -(hwnear + dxnear), hwnear - dxnear,
-(hhnear + dynear), hhnear - dynear,
Znear, Zfar
);
glRotated( 180, 0, 1, 0 );
glTranslated( eyex, eyey, eyez );
glRotated( tiltAngle, 1, 0, 0 );
glRotated( panAngle, 0, 1, 0 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glCallList(dispList);
glPushAttrib(GL_ENABLE_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
// Paint the point in red
// ...
glPopAttrib();
}
UPDATE: I forgot to mention that the code is based in Qt. It uses the QtOpenGL module extensively.
UPDATE #2: I've added some code.
In the fixed function pipeline, there are many states which could lead to the vertex color beeing completely ignored.
As Reto Koradi pointed out in the comments, when lighting is enabled, the colors have no effect (unless GL_COLOR_MATERIAL is enabled, in which case the color value is used to update the material parameters used for the lighting equation.)
As I pointed out in the comments, another case is texturing. Depending on the GL_TEX_ENV_MODE selected, the fragment's color (as determined by lighting, or directly interpolated from the vertex colors) is modulated by the texture color, or completely replaced. In that case, disabling texturing for every texture unit in use can solve the issue.
I'm having the following problem. While glPolygonOffset works perfectly for meshes, for example when I'm trying to draw a wireframe outline on top of the object, it doesn't work for simple lines.
Here is how it works for meshes:
// draw object
mTexture.enableAndBind();
gl::color( Colorf( 1, 1, 1 ) );
gl::draw( mVboMesh );
mTexture.unbind();
// overlay wireframe
gl::enableWireframe();
glLineWidth(1);
glEnable( GL_POLYGON_OFFSET_LINE );
glPolygonOffset( -1, -1 );
glColor3f( 0, 0, 1 );
gl::draw( mVboMesh );
glDisable( GL_POLYGON_OFFSET_LINE );
gl::disableWireframe();
For some reason it doesn't work for lines. What I'm trying to achieve is to draw a coordinate frame's arrows over a grid. I'm using the very same GL_POLYGON_OFFSET_LINE mode as when I was drawing lines, just like I was doing for the wireframe over the object. However in this case glPolygonOffset( -1, -1 ); makes absolutely no difference. I've tried it with huge values like 100 and it's the same. Absolutely no effect. Here is what I'm doing:
// enable 3D rendering
gl::enable( GL_CULL_FACE );
gl::enableDepthRead();
gl::enableDepthWrite();
// drawing the grid
int size = 2000;
int step = 25;
gl::color( Colorf( 0.2f, 0.2f, 0.2f ) );
for( float i = -size; i <= size; i += step )
{
glBegin( GL_LINES );
glVertex3f( i, 0, -size );
glVertex3f( i, 0, size );
glVertex3f( -size, 0, i );
glVertex3f( size, 0, i );
glEnd( );
}
// drawing the arrows
glEnable( GL_POLYGON_OFFSET_LINE );
glPolygonOffset( -1, -1 );
glBegin( GL_LINES );
gl::color( Colorf( 1, 0, 0 ) );
glVertex3f( 0, 0, 0 );
glVertex3f( 100, 0, 0 );
gl::color( Colorf( 0, 1, 0 ) );
glVertex3f( 0, 0, 0 );
glVertex3f( 0, 100, 0 );
gl::color( Colorf( 0, 0, 1 ) );
glVertex3f( 0, 0, 0 );
glVertex3f( 0, 0, 100 );
glEnd( );
glDisable( GL_POLYGON_OFFSET_LINE );
// disable 3D rendering
gl::disableDepthWrite();
gl::disableDepthRead();
gl::disable( GL_CULL_FACE );
and an example of the Z-fighting I get:
One hack I've tried and what worked perfectly is:
disable depth read, enable depth write
draw grid
draw arrows
enable depth read
draw other objects
However this is a very special case and while it works for a flat grid and arrows, it wouldn't work for pretty much anything else with a complex shape.
My questions are:
Why does glPolygonOffset not work for lines-on-lines while it works for lines-on-polygon?
How can I fix it, without resorting to the above hack, what only works in very specific cases?
// I'm using Cinder as a framework, but it shouldn't matter since I'm using raw OpenGL commands
Update
I've checked the answer in the first comment, and tried that method as well, however that one doesn't work either, since the result depends on the distance from the camera.
//// draw coordinate frame and grid
glDepthRange (0.01, 1.0);
drawGrid( 2000.0f, 25.0f );
glDepthRange (0.0, 0.99);
gl::drawCoordinateFrame( 100.0f, 5.0f, 2.0f );
glDepthRange (0.0, 1.0);
// draw object
I guess one hack could be to draw the line just a bit closer to the view point (let's say 0.1 closer). This should avoid z-fighting.
In order to do so, you calculate the normalized directional vector from your point to the POV position. Then you scale it with a small factor, and add it to your line point coordinates
I was trying to create a OpenGL version of an old plasma 8bit effect animation in DOS but I am stuck. Since almost every OpenGL program has included something to generate a palette for Win32 I thought it would not be that hard to apply palette animation on my old program.
My purpose is to generate a texture with color indices that does not change and a palette that is rotating. After digging into the web this weekend I am still not able to fix it. I cannot even display a texture with one color index so in that stage something is wrong (if it would work I can create the palette cycling mechanism).
I can force into palette mode by using PFD_TYPE_COLORINDEX and draw some random pixels using glIndexi. I read that glDrawPixels and glReadPixels are slow and the latter is not that accurate when getting pixels back from the framebuffer (due to inaccurate positioning as a result from rounding errors or something).
I tried the GL_COLOR_INDEX keyword. I also tried:
glPixelTransferi(GL_MAP_COLOR, true);
glPixelMapfv( GL_PIXEL_MAP_I_TO_R, ...);
glTexImage2D... Some of the code I tried so far (latest changes):
init part:
void* rawplasma;
GLuint plastexture;
rawplasma = (void*) malloc(256*256);
memset(rawplasma,rand()%256,256*156);
glEnable( GL_TEXTURE_2D );
glGenTextures(1, plastexture);
glBindTexture(GL_TEXTURE_2D, plastexture);
glTexImage2D( GL_TEXTURE_2D, 0, GL_COLOR_INDEX, 256, 256, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, rawplasma );
update/draw:
float Rmap[256];
float Gmap[256];
float Bmap[256];
float Amap[256];
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
memset(rawplasma,rand()%256,256*256); //check if it works
glTexImage2D( GL_TEXTURE_2D, 0, GL_COLOR_INDEX, 256, 256, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, rawplasma );
glBindTexture(GL_TEXTURE_2D, plastexture );
/*
glPixelTransferi( GL_MAP_COLOR, GL_TRUE );
glPixelMapfv(GL_PIXEL_MAP_I_TO_R,mapSize,Rmap);
glPixelMapfv(GL_PIXEL_MAP_I_TO_G,mapSize,Gmap);
glPixelMapfv(GL_PIXEL_MAP_I_TO_B,mapSize,Bmap);
glPixelMapfv(GL_PIXEL_MAP_I_TO_A,mapSize,Amap);
glPixelTransferi(GL_MAP_COLOR,GL_TRUE);
*/
glBegin(GL_QUADS);
glTexCoord2f(1.0, 0.0); glVertex2f(-1.0, -1.0);
glTexCoord2f(0.0, 0.0); glVertex2f( 1.0, -1.0);
glTexCoord2f(0.0, 1.0); glVertex2f( 1.0, 1.0);
glTexCoord2f(1.0, 1.0); glVertex2f(-1.0, 1.0);
glEnd();
glFlush();
SwapBuffers(hDC);
Or should I use glColorTableEXT in combination with GL_COLOR_INDEX8_EXT? I read somewhere that textured palettes are not supported? I found a link which mentions Paletted Texture Extension: http://www.cs.rit.edu/~ncs/Courses/570/UserGuide/OpenGLonWin-20.html
This is what I want (but then in OpenGL):
http://www.codeforge.com/read/174459/PalAnimDemo.h__html
I am not looking for ES/Shader implementations (I am just a beginner ;)) and DirectDraw might be easier I think but I want to try OpenGL.
Since almost every OpenGL program has included something to generate a palette for Win32
No, definitely not. If you mean the PIXELFORMATDESCRIPTOR, that is not a palette definition. Color index mode was a major PITA to work with in OpenGL, and no current implementation actually support it.
I am not looking for ES/Shader implementations (I am just a beginner ;))
But that's exactly what you should use. Also shaders are mandatory in modern OpenGL. A simple fragment shader, that performs a lookup into a 1D texture turning an index into a color is exactly what you need. And if you aim for modern systems, you'll have to use a shader anyway.
I am trying to figure out the best way to mask of sections of a texture when they ar drawn. My issue comes in the fact that I seem to have run our of alpha masks!
We are using openGL to draw a custom built 2D game engine. The game is built up off of sprites and simple block textures.
My desired outcome is like this:
A character sprite is drawn in place (using it's alpha color to not just be a box)
An item is drawn into the players hand (also using it's alpha color to draw into the scene without being a box)
The item should appear behind the characters arm/hand, but above the rest of the body.
For the moment the only way I can figure out how to accomplish this, is by drawing them in order (Body, Item, Arm) but I would like to avoid this to make art assets a bit easier to deal with. My idea solution would be to draw the character, then draw the item with an alpha mask that blocks out areas of the texture that should be "under" the arm.
Other solutions that I have seen are like this, where the glBlendFuncSeparate() function is used. I am trying to avoid bringing in extensions, as my current version of OpenGL doesn't support it. Not to say that I am opposed to the idea, but it seems a bit of a handle to brig it in just to draw an alpha mask?
I fully admit that this is a learning process for me, and I am using it as an excuse to really see how OpenGL handles. Any suggestions as to where I should head to get this to draw correctly? Is there a way for OpenGL in the fixed pipeline to take a texture, apply an alpha mask on top of it, and THEN draw it into the buffer? Should I give in and separate my character into several parts of its model?
[UPDATE: 8/12/12]
Tried to add the code suggested by Tim, but I seem to be having an issue. When I enable the stencil buffer, everything just gets blocked out, NOT just what I wanted. Here is my test example code.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Disable writing to any of the color fields
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
glStencilFunc(GL_ALWAYS, 0,0);
// Draw our blocking poly
glBegin(GL_POLYGON);
glVertex2f( 50, 50 );
glVertex2f( 50, 50+128 );
glVertex2f( 50+128, 50+128 );
glEnd();
glStencilFunc(GL_GREATER, 0, -1);
glEnable(GL_STENCIL_TEST);
// Re enable drawing of colors
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// Enable use of textures
glEnable(GL_TEXTURE_2D);
// Bind desired texture for drawing
glBindTexture(GL_TEXTURE_2D,(&texture)[0]);
// Draw the box with colors
glBegin(GL_QUADS);
glTexCoord2d( 0, 0 ); glVertex2f( 50, 50 );
glTexCoord2d( 0, 1 ); glVertex2f( 50, 50+128 );
glTexCoord2d( 1, 1 ); glVertex2f( 50+128, 50+128 );
glTexCoord2d( 1, 0 ); glVertex2f( 50+128, 50 );
glEnd();
// Swap buffers and display!
SDL_GL_SwapBuffers();
Just to be clear, here is my init code as well to set this system up.
When the code is run with stencil disabled, I get this:
When I use glEnable(GL_STENCIL_TEST), I get this:
I've tried playing around with various options, but I cannot see a clear reason why my stencil buffer is blocking everything.
[Update#2 8/12/12]
We got some working code, Thanks tim! Here is what I ended up running to work correctly.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Disable writing to any of the color fields
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glStencilOp(GL_INCR, GL_INCR, GL_INCR);
glEnable(GL_STENCIL_TEST);
// Draw our blocking poly
glBegin(GL_POLYGON);
glVertex2f( 50, 50 );
glVertex2f( 50, 50+128 );
glVertex2f( 50+128, 50+128 );
glEnd();
glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// Re enable drawing of colors
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// Enable use of textures
glEnable(GL_TEXTURE_2D);
// Bind desired texture for drawing
glBindTexture(GL_TEXTURE_2D,(&texture)[0]);
// Draw the box with colors
glBegin(GL_QUADS);
glTexCoord2d( 0, 0 ); glVertex2f( 50, 50 );
glTexCoord2d( 0, 1 ); glVertex2f( 50, 50+128 );
glTexCoord2d( 1, 1 ); glVertex2f( 50+128, 50+128 );
glTexCoord2d( 1, 0 ); glVertex2f( 50+128, 50 );
glEnd();
glDisable(GL_STENCIL_TEST);
// Swap buffers and display!
SDL_GL_SwapBuffers();
Here's my idea for the situation where you have one texture and one alpha mask:
Draw the character onto the scene like normal.
Lock the RGB color channels so that it cannot be changed with glColorMask
Setup the stencil buffer with glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); glStencilFunc(GL_ALWAYS, 0,0);
Draw the alpha mask with alpha testing enabled. This will increment the stencil buffer anywhere the alpha test passes (you may have to flip this based on your mask polarity)
At this point, you have a character texture in the framebuffer, and a mask outline in the stencil buffer.
Reenable the color channels with glColorMask
Setup the stencil buffer for the weapon with glStencilFunc(GL_GREATER, 0, -1); This will only draw the weapon texels where the stencil buffer is greater than zero, and reject pixels where the stencil is not updated.
Draw the weapon texture as normal.
Tim was pretty clear in his comment, but I want to present you the solution I find the most intuitive. It's 3D, so hold on... ;)
Basically, you can just use the Z coordinate of your images to create virtual "layers". It then doesnt' matter, in which order you draw them. Just alphatest every image individually, and draw it on correct Z value. If it still isn't enough, you could use separate texture containing "depth" of every pixel, and then use the 2nd texture to perform some sort of depth-testing.
Be sure to call glEnable(GL_DEPTH_TEST); if you want to use this approach.
As I see it, the problem is that you have one texture, but part of it represents the arm and part of it the rest of the character. The issue is that you want to draw the weapon over the character, but draw the arm over both.
This means, while drawing two objects, you want to put them into three different "layers". This fundamentally doesn't make sense, so you're kind of stuck.
Here's an idea though: use a fragment program (i.e., a shader).
I suggest you overload the character's texture's alpha channel to encode both transparency and layer. For example, let's use 0=transparent body, 64=opaque body, 128=transparent arm, 255=opaque arm.
From here, you draw your objects, but conditionally set the depth of your objects into three layers. Basically, you write a fragment program that draws your character into two different layers, the character gets pushed backward while the arm gets pulled forward. When the weapon is drawn, it is drawn without a shader, but it's tested against the characters' pixels' depths. It works something like this (untested, obviously).
Define a shader my_shader, which contains a fragment program:
uniform sampler2D character_texture;
void main(void) {
vec4 sample = texture2D(character_texture,gl_TexCoord[0].st);
int type; //Figure out what type of character texel we're looking at
if (fabs(sample.a-0.00)<0.01) type = 0; //transparent body
else if (fabs(sample.a-0.25)<0.01) type = 1; //opaque body
else if (fabs(sample.a-0.50)<0.01) type = 2; //transparent arm
else if (fabs(sample.a-1.00)<0.01) type = 3; //opaque arm
//Don't draw transparent pixels.
if (type==0 || type==2) discard;
gl_FragColor = vec4(sample.rgb,1.0);
//Normally, you (can) write "gl_FragDepth = gl_FragCoord.z". This
//is how OpenGL will draw your weapon. However, for the character,
//we alter that so that the arm is closer and the body is farther.
//Move body farther
if (type==1) gl_FragDepth = gl_FragCoord.z * 1.1;
//Move arm closer
else if (type==3) gl_FragDepth = gl_FragCoord.z * 0.9;
}
Here's some pseudocode for your draw function:
//...
//Algorithm to draw your character
glUseProgram(my_shader);
glBindTexture(GL_TEXTURE_2D,character.texture.texture_gl_id);
glUniform1i(glGetUniformLocation(my_shader,"character_texture"),1);
character.draw();
glUseProgram(0);
//Draw your weapon
glEnable(GL_DEPTH_TEST);
character.weapon.draw();
glDisable(GL_DEPTH_TEST);
//...