Jump to content
  • Advertisement
Sign in to follow this  
arhwee

OpenGL Shadow Volume help

This topic is 3161 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I was coding this piece.. trying out shadow volume. I do not have much OpenGL knowledge (just basic stuff like drawing, translation, lighting, texture etc) and not very sure about stencil buffer though I had been reading up. So anyway, here is my code. I tried to follow NeHe and adapt whenever necessary.. but I just can't get my shadow! Any kind soul please help? #include <stdio.h> #include <stdlib.h> #include <math.h> #include <gl/glut.h> #include <time.h> #define M_INFINITY 10.0f int width; int height; float eyex=0; float eyey=5; float eyez=11; float lookx=0; float looky=0; float lookz=0; float position0[4] = {0,4,0,0}; // Point light0 at (0,4,0) for Light0 float triangle_pos[3][3] = {{-0.8,2.5,0},{0,2.5,-0.8},{0.8,2.5,0.1}}; float v[3][3]; //extended vertices in shadow volume display list ////////////////////////////////////////////////////////////////// ///// ///// MATERIAL SETUPS ///// ////////////////////////////////////////////////////////////////// //struct for material values typedef struct { GLfloat ambient[4]; GLfloat diffuse[4]; GLfloat specular[4]; GLfloat shininess; }material; material PolishedBronze = {{0.25, 0.148, 0.06475, 1.0}, {0.4, 0.2368, 0.1036, 1.0}, {0.774597, 0.458561, 0.200621, 1.0}, 76.8}; void setMaterial(material M) { glMaterialfv(GL_FRONT, GL_SPECULAR, M.specular); glMaterialfv(GL_FRONT, GL_AMBIENT, M.ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, M.diffuse); glMaterialf(GL_FRONT, GL_SHININESS, M.shininess); } ////////////////////////////////////////////////////////////////// ///// ///// DRAWING ///// ////////////////////////////////////////////////////////////////// // Drawing the ground void drawGround() { glPushMatrix(); { glColor3f(1.0f,1.0f,1.0f); glBegin(GL_QUADS); { glColor3f(0,0,0); glNormal3f(0.0f,1.0f,0.0f); glVertex3f(-5.0f,0.0f,-5.0f); glVertex3f(-5.0f,0.0f,5.0f); glVertex3f(5.0f,0.0f, 5.0f); glVertex3f(5.0f,0.0f,-5.0f); } glEnd(); } glPopMatrix(); } // Drawing the object on the ground void drawObject() { glPushMatrix(); setMaterial(PolishedBronze); glTranslatef(-2,1,0); glRotatef(2,0,1,0); glutSolidTeapot(1); glPopMatrix(); } void drawTriangle() { glPushMatrix(); { glColor3f(0.0f,0.0f,0.0f); glBegin(GL_POLYGON); { glNormal3f(0.0f,1.0f,0.0f); glVertex3f(-0.8,2.5,0); glVertex3f(0,2.5,-0.8); glVertex3f(0.8,2.5,0.1); } glEnd(); } glPopMatrix(); } int checkFront(float v1[3],float v2[3],float v3[3]){ //v1 is point on triangle occluder //v2 is the point extended from v1 //v3 is point extended from next edge float vector1[3]; vector1[0] = v2[0]-v1[0]; vector1[1] = v2[1]-v1[1]; vector1[2] = v2[2]-v2[2]; float vector2[3]; vector2[0] = v3[0]-v1[0]; vector2[1] = v3[1]-v3[1]; vector2[2] = v3[2]-v3[2]; //doing the calculation >.< //vector1 x vector2 float cross[3]; cross[0] = vector1[1]*vector2[2] - vector1[2]*vector2[1]; cross[1] = vector1[2]*vector2[0] - vector1[0]*vector2[2]; cross[2] = vector1[0]*vector2[1] - vector1[1]*vector2[0]; //dot with the eye look at direction float dotted; float eye[3]; eye[0] = lookx-eyex; eye[1] = looky-eyey; eye[2] = lookz-eyez; dotted = cross[0]*eye[0] + cross[1]*eye[1] + cross[2]*eye[2]; if(dotted>0){ //if front printf("This face is front!\n"); return 1; } if(dotted<0){ //if back printf("This face is back!\n"); return 0; } } void createVolume(void){ int i; printf("4. createVolume() called. Displaying the coordinates...\n"); for(i=0;i<3;i++){ v[0] = triangle_pos[0] - position0[0]; v[1] = triangle_pos[1] - position0[1]; v[2] = triangle_pos[2] - position0[2]; v[0] *= M_INFINITY; v[1] *= M_INFINITY; v[2] *= M_INFINITY; v[0] += position0[0]; v[1] += position0[1]; v[2] += position0[2]; printf("extended%i is %f,%f,%f.\n",i,v[0],v[1],v[2]); } } void drawVolume(int numpass){ /* //back cap glBegin(GL_TRIANGLES); { glVertex3f(v[2][0], v[2][1], v[2][2]); glVertex3f(v[1][0], v[1][1], v[1][2]); glVertex3f(v[0][0], v[0][1], v[0][2]); } glEnd(); //front cap glBegin(GL_TRIANGLES); { glVertex3f(triangle_pos[2][0], triangle_pos[2][1], triangle_pos[2][2]); glVertex3f(triangle_pos[1][0], triangle_pos[1][1], triangle_pos[1][2]); glVertex3f(triangle_pos[0][0], triangle_pos[0][1], triangle_pos[0][2]); } glEnd(); */ int i; //Just doing some checking =p glBegin(GL_QUADS); { //glColor3f(1.0f,0.0f,0.0f); glVertex3f(triangle_pos[2][0], triangle_pos[2][1],triangle_pos[2][2]); glVertex3f(triangle_pos[0][0], triangle_pos[0][1],triangle_pos[0][2]); glVertex3f(v[0][0], v[0][1], v[0][2]); glVertex3f(v[2][0], v[2][1], v[2][2]); } glEnd(); for(i=0;i<2;i++){ glBegin(GL_QUADS); { //glColor3f(1.0f,0.0f,0.0f); glVertex3f(triangle_pos[i+1][0], triangle_pos[i+1][1],triangle_pos[i+1][2]); glVertex3f(triangle_pos[0], triangle_pos[1],triangle_pos[2]); glVertex3f(v[0], v[1], v[2]); glVertex3f(v[i+1][0], v[i+1][1],v[i+1][2]); } glEnd(); } /* if(checkFront(triangle_pos[2],v[2],v[0])==1){ printf("YAY");} else {printf("BOO");} */ /* for(i=0;i<3;i++){ glBegin(GL_LINES); { glVertex3f(triangle_pos[0], triangle_pos[1],triangle_pos[2]); glVertex3f(v[0], v[1], v[2]); } glEnd(); } */ /* for(i=0;i<2;i++){ if(numpass==1 && checkFront(triangle_pos,v,v[i+1])==1){ //First pass (front face true) printf("A.\n"); glBegin(GL_QUADS); { //glColor3f(1,0,0); //First vertice is v0 first edge glVertex3f(triangle_pos[0], triangle_pos[1],triangle_pos[2]); //second vertice is the extension of this first edge glVertex3f(v[0], v[1], v[2]); //fourth is the extension of this edge glVertex3f(v[i+1][0], v[i+1][1], v[i+1][2]); //third vertice is the extension of 2nd edge glVertex3f(triangle_pos[i+1][0], triangle_pos[i+1][1], triangle_pos[i+1][2]); } glEnd(); } if(numpass==2 && checkFront(triangle_pos,v,v[i+1])==0){ //Second pass (front face false) printf("B.\n"); glBegin(GL_QUADS); { //glColor3f(1,0,0); //First vertice is v0 first edge glVertex3f(triangle_pos[0], triangle_pos[1],triangle_pos[2]); //second vertice is the extension of this first edge glVertex3f(v[0], v[1], v[2]); //fourth is the extension of this edge glVertex3f(v[i+1][0], v[i+1][1], v[i+1][2]); //third vertice is the extension of 2nd edge glVertex3f(triangle_pos[i+1][0], triangle_pos[i+1][1], triangle_pos[i+1][2]); } glEnd(); } } if(numpass==1 && checkFront(triangle_pos[2],v[2],v[0])==1){ //First pass (front face true) printf("C.\n"); glBegin(GL_QUADS); { //glColor3f(0,0,1); //First vertice is v0 first edge glVertex3f(triangle_pos[2][0], triangle_pos[2][1],triangle_pos[2][2]); //second vertice is the extension of this first edge glVertex3f(v[2][0], v[2][1], v[2][2]); //third vertice is the extension of 2nd edge glVertex3f(triangle_pos[0][0], triangle_pos[0][1], triangle_pos[0][2]); //fourth is the extension of this edge glVertex3f(v[0][0], v[0][0], v[0][0]); } glEnd(); } if(numpass==2 && checkFront(triangle_pos[2],v[2],v[0])==0){ //Second pass (front face false) printf("D.\n"); glBegin(GL_QUADS); { //glColor3f(0,0,1); //First vertice is v0 first edge glVertex3f(triangle_pos[2][0], triangle_pos[2][1],triangle_pos[2][2]); //second vertice is the extension of this first edge glVertex3f(v[2][0], v[2][1], v[2][2]); //third vertice is the extension of 2nd edge glVertex3f(triangle_pos[0][0], triangle_pos[0][1], triangle_pos[0][2]); //fourth is the extension of this edge glVertex3f(v[0][0], v[0][0], v[0][0]); } glEnd(); } */ } void castShadow(){ glClearStencil(0); //clear the stencil buffer //glPushAttrib( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); glDisable(GL_LIGHTING); glDepthMask(GL_FALSE); //turn off writing to depth buffer glDepthFunc(GL_LEQUAL); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); //turn off writing to colour buffer //glEnable(GL_CULL_FACE); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS, 1, 0xffffffff); printf("3. castShadow() - lighting etc disabled and stencil test enabled.\n"); //First pass, stencil operation increases stencil value glFrontFace(GL_CCW); //glCullFace(GL_BACK); glStencilOp(GL_KEEP,GL_KEEP,GL_INCR); printf("5. First pass of stencil operation.\n"); drawVolume(1); //1 for first pass //Second pass, stencil operation decreases stencil value glFrontFace(GL_CW); //glCullFace(GL_FRONT); glStencilOp(GL_KEEP,GL_KEEP,GL_DECR); printf("6. Second pass of stencil operation.\n"); drawVolume(2); //2 for second pass printf("7. Volumes are successfully drawn?\n"); glFrontFace(GL_CCW); glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE); printf("8. ColorMask enabled in castShadown().\n"); //Draw a shadowing rectangle covering the entire screen glColor4f(0.0f,0.0f,0.0f,0.4f); //higher alpha will make the shadow more black glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glStencilFunc(GL_NOTEQUAL,0,0xFFFFFFFFL); glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP); glPushMatrix(); glLoadIdentity(); glBegin(GL_QUADS); { glVertex3f(-5.0f,0.0f,-5.0f); glVertex3f(-5.0f,0.0f,5.0f); glVertex3f(5.0f,0.0f, 5.0f); glVertex3f(5.0f,0.0f,-5.0f); } glEnd(); glPopMatrix(); glDisable(GL_BLEND); printf("9. Shadowing rectangle drawn in castShadow().\n"); glDepthFunc(GL_LEQUAL); glDepthMask(GL_TRUE); glEnable(GL_LIGHTING); //glDisable(GL_CULL_FACE); glDisable(GL_STENCIL_TEST); glShadeModel(GL_SMOOTH); printf("10. Lighting enabled and stencil test disable.\n"); } void init(void) { // 100% white light GLfloat light_ambient[] = {1.0, 1.0, 1.0, 1.0}; GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0}; GLfloat light_specular[] = {1.0, 1.0, 1.0, 1.0}; GLfloat light_diffuseC[] = {1.0, 1.0, 0, 1.0}; // set up ambient, diffuse and specular components for light0 // array inserted from both glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); //Background glClearColor(0.3, 0.3, 0.3, 1.0); glShadeModel(GL_SMOOTH); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); printf("1. init() set up light and enabled lighting + depth test\n"); } void idle() { glutSwapBuffers(); //glutPostRedisplay(); //printf("SCREEN IS SWAPPED!!!\n"); } void reshape (int w, int h) { width=w; height=h; printf("Reshape is called.\n"); glViewport (0, 0, w, h); glMatrixMode (GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0); else glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void renderScene(void) { drawGround(); drawTriangle(); drawObject(); printf("2. Scene is rendered (after display() called). Calling castShadow() next...\n"); createVolume(); glPushMatrix(); glBegin(GL_LINES); { glColor3f(1.0f,0.0f,0.0f); glVertex3f(triangle_pos[0][0], triangle_pos[0][1],triangle_pos[0][2]); glVertex3f(v[0][0], v[0][1], v[0][2]); } glEnd(); glPopMatrix(); castShadow(); glLightfv(GL_LIGHT0, GL_POSITION, position0); //set LIGHT0 glDisable(GL_LIGHTING); glPushMatrix(); { glTranslatef(position0[0],position0[1],position0[2]); glColor3f(1.0f,1.0f,0.5f); glutSolidSphere(0.1,10,10); // Light Sphere } glPopMatrix(); glEnable(GL_LIGHTING); } void display() { //Setup the view of the world glMatrixMode(GL_PROJECTION); //Switch to projection mode (P) glLoadIdentity(); // P <- I gluPerspective(45.0, 1.0, 1.0, 100); // P <- P glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //standard gluLookAt here gluLookAt(eyex, eyey, eyez, lookx, looky, lookz, 0, 1, -1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); //initialize the depth buffer by rendering the scene into it renderScene(); glFlush(); } void keyboard(unsigned char key, int x, int y) { switch (key){ case 27: // ESC exit(0); break; } } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_STENCIL); glutInitWindowSize(600, 600); glutInitWindowPosition(50, 50); glutCreateWindow("Shadow Volume"); init(); glutDisplayFunc(display); glutKeyboardFunc(keyboard); glutReshapeFunc(reshape); glutIdleFunc(idle); glutMainLoop(); return 0; }

Share this post


Link to post
Share on other sites
Advertisement
It's very difficult to read your code. Try editing your post, using [source]code()[/source] tags. That will preserve tabs etc. and put the code in a text-box, like this:

int code() {
return 1;
}

Share this post


Link to post
Share on other sites
Hi, In main loop for first : I visualize all objects and for second : use SHADOW_FOR_OBJECT() for all objects.
my depth pass stencil shadow algorithm for shadow cast is here :



void SHADOW_FOR_OBJECT(object obj, float lp[4]) //lp = light position
{
unsigned int i, j, k, jj;
unsigned int p1=0, p2=0;
vertex v1, v2, v3, v4;
float side;

for (i=0; i < obj.number_of_faces; i++)
{
// chech to see if light is in front or behind the plane (face plane)
side = obj.faces.ro_no.a*lp[0] + obj.faces.ro_no.b*lp[1] + obj.faces.ro_no.c*lp[2] + obj.faces.ro_no.d*lp[3];

if (side > 0.0) obj.faces.light_visible = 1; else obj.faces.light_visible = 0;
}

glEnable(GL_BLEND);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_2D);
glDepthMask(GL_FALSE);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glColorMask(0, 0, 0, 0);
glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
glEnable(GL_CULL_FACE);


glCullFace(GL_BACK);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);

for (i=0; i < obj.number_of_faces; i++)
{
if (obj.faces.light_visible)
{
for (j=0; j<3; j++)
{
k = obj.faces.conectivity[j];
if ((!k) || (!obj.faces[k-1].light_visible))
{
v1.x = obj.vertex[obj.faces.vertex_index[j]].x;
v1.y = obj.vertex[obj.faces.vertex_index[j]].y;
v1.z = obj.vertex[obj.faces.vertex_index[j]].z;
v2.x = obj.vertex[obj.faces.vertex_index[(j+1)%3]].x;
v2.y = obj.vertex[obj.faces.vertex_index[(j+1)%3]].y;
v2.z = obj.vertex[obj.faces.vertex_index[(j+1)%3]].z;
v3.x = (v1.x - lp[0]);
v3.y = (v1.y - lp[1]);
v3.z = (v1.z - lp[2]);
v4.x = (v2.x - lp[0]);
v4.y = (v2.y - lp[1]);
v4.z = (v2.z - lp[2]);


glBegin(GL_TRIANGLE_STRIP);
glVertex3f(v1.x, v1.y, v1.z);
glVertex4f(v3.x, v3.y, v3.z, 0.0);
glVertex3f(v2.x, v2.y, v2.z);
glVertex4f(v4.x, v4.y, v4.z, 0.0);
glEnd();
}
}
}
}

// second pass, stencil operation increases stencil value

glCullFace(GL_FRONT);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
for (i=0; i < obj.number_of_faces; i++)
{
if (obj.faces.light_visible)
{
for (j=0; j<3; j++)
{
k = obj.faces.conectivity[j];
if ((!k) || (!obj.faces[k-1].light_visible))
{
v1.x = obj.vertex[obj.faces.vertex_index[j]].x;
v1.y = obj.vertex[obj.faces.vertex_index[j]].y;
v1.z = obj.vertex[obj.faces.vertex_index[j]].z;
v2.x = obj.vertex[obj.faces.vertex_index[(j+1)%3]].x;
v2.y = obj.vertex[obj.faces.vertex_index[(j+1)%3]].y;
v2.z = obj.vertex[obj.faces.vertex_index[(j+1)%3]].z;
/// Projecting this edge to "infinity"
v3.x = (v1.x - lp[0]);
v3.y = (v1.y - lp[1]);
v3.z = (v1.z - lp[2]);
v4.x = (v2.x - lp[0]);
v4.y = (v2.y - lp[1]);
v4.z = (v2.z - lp[2]);


glBegin(GL_TRIANGLE_STRIP);
glVertex3f(v1.x, v1.y, v1.z);
glVertex4f(v3.x, v3.y, v3.z, 0.0);
glVertex3f(v2.x, v2.y, v2.z);
glVertex4f(v4.x, v4.y, v4.z, 0.0);
glEnd();
}
}
}
}


}






At the finish in main loop apply two rectangle :

glCullFace(GL_BACK);
glColorMask(1, 1, 1, 1);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glStencilFunc(GL_NOTEQUAL, 0, 0xffffffff);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

glPushMatrix();
glLoadIdentity();
glColor4f(0.0, 0.0, 0.0, 0.1);
//first
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(-0.1, 0.1,-0.1);
glVertex3f(-0.1,-0.1,-0.1);
glVertex3f( 0.1, 0.1,-0.1);
glVertex3f( 0.1,-0.1,-0.1);
glEnd();
glPopMatrix();

glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glDisable(GL_STENCIL_TEST);
glEnable(GL_TEXTURE_2D);

glPushMatrix();
glLoadIdentity();
glColor4f(0.7, 0.6, 0.95, 1.0);
//second
glBegin(GL_TRIANGLE_STRIP);
glVertex3f(-1000.0, 1000.0,-1000.0);
glVertex3f(-1000.0,-1000.0,-1000.0);
glVertex3f( 1000.0, 1000.0,-1000.0);
glVertex3f( 1000.0,-1000.0,-1000.0);
glEnd();
glPopMatrix();
glEnable(GL_LIGHTING);






Here is set up for conectivity function :

void SETUP_CONECTIVITY(object *obj)
{
unsigned int p1i, p2i, p1j, p2j;
unsigned int P1i, P2i, P1j, P2j;
unsigned int i,j,ki,kj;

for(i=0; i<obj->number_of_faces-1; i++)
for(j=i+1; j<obj->number_of_faces; j++)
for(ki=0; ki<3; ki++)
if( !obj->plochy.conectivity[ki])
{
for( kj=0; kj<3; kj++)
{
p1i=ki;
p1j=kj;
p2i=(ki+1)%3;
p2j=(kj+1)%3;

p1i=obj->faces.vertex_index[p1i];
p2i=obj->faces.vertex_index[p2i];
p1j=obj->faces[j].vertex_index[p1j];
p2j=obj->faces[j].vertex_index[p2j];

P1i=((p1i+p2i)-abs(p1i-p2i))/2;
P2i=((p1i+p2i)+abs(p1i-p2i))/2;
P1j=((p1j+p2j)-abs(p1j-p2j))/2;
P2j=((p1j+p2j)+abs(p1j-p2j))/2;

if ((P1i==P1j) && (P2i==P2j))
{ //they are neighbours
obj->faces.conectivity[ki] = j+1;
obj->faces[j].conectivity[kj] = i+1;
}
}
}
}






And plane for all faces in obect :

void PLANE_OF_FACE(object *obj)
{
vertex v1, v2, v3;
int p;

for (p=0; p<obj->number_of_faces; p++)
{
v1.x = obj->vertex[obj->faces

.vertex_index[0]].x;
v1.y = obj->vertex[obj->faces

.vertex_index[0]].y;
v1.z = obj->vertex[obj->faces

.vertex_index[0]].z;

v2.x = obj->vertex[obj->faces

.vertex_index[1]].x;
v2.y = obj->vertex[obj->faces

.vertex_index[1]].y;
v2.z = obj->vertex[obj->faces

.vertex_index[1]].z;

v3.x = obj->vertex[obj->faces

.vertex_index[2]].x;
v3.y = obj->vertex[obj->faces

.vertex_index[2]].y;
v3.z = obj->vertex[obj->faces

.vertex_index[2]].z;

obj->faces

.ro_no.a = v1.y*(v2.z-v3.z) + v2.y*(v3.z-v1.z) + v3.y*(v1.z-v2.z);
obj->faces

.ro_no.b = v1.z*(v2.x-v3.x) + v2.z*(v3.x-v1.x) + v3.z*(v1.x-v2.x);
obj->faces

.ro_no.c = v1.x*(v2.y-v3.y) + v2.x*(v3.y-v1.y) + v3.x*(v1.y-v2.y);
obj->faces

.ro_no.d = - (v1.x*(v2.y*v3.z-v3.y*v2.z)
+ v2.x*(v3.y*v1.z-v1.y*v3.z)
+ v3.x*(v1.y*v2.z-v2.y*v1.z));
}

}






Maybe it will help.

[Edited by - Christobal_de_M on February 24, 2010 1:22:02 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!