/* orxonox - the future of 3D-vertical scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: David Gruetter co-programmer: ... Created by Dave, in this file shadow will be implemented in a quite sexy and fast way, with a lot of opengl-code, to keep it fast! The origin of the code comes form an example at www.frustum.org, slitly different although, so that it works with Orxonox:) */ #include "importer/material.h" #include "stdincl.h" #include #include #include #include #include "shadow.h" #include "vector.h" #define SIZE 128 #define GL_CLAMP_TO_EDGE_EXT 0x812F using namespace std; /** \brief default Constructor */ Shadow::Shadow(OBJModel* player,Player* playerangle) { this->player=player; this->playerangle=playerangle; } /** \brief default destructor */ Shadow::~Shadow() { } void Shadow::init() { float plane_s[] ={1.0f,0.0f,0.0f,0.0f}; float plane_t[] ={0.0f,1.0f,0.0f,0.0f}; float plane_r[] ={0.0f,0.0f,1.0f,0.0f}; float plane_q[] ={0.0f,0.0f,0.0f,1.0f}; glClearDepth(1); glDepthFunc(GL_LEQUAL); glTexGenfv(GL_S,GL_EYE_PLANE,plane_s); glTexGenfv(GL_T,GL_EYE_PLANE,plane_t); glTexGenfv(GL_R,GL_EYE_PLANE,plane_r); glTexGenfv(GL_Q,GL_EYE_PLANE,plane_q); glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_Q,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE_EXT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE_EXT); glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,SIZE,SIZE,0,GL_RGB,GL_UNSIGNED_BYTE,NULL); this->ground_id=glGenLists(1); glNewList(this->ground_id,GL_COMPILE); setGround(); glEndList(); this->player_id=glGenLists(1); glNewList(this->player_id,GL_COMPILE); this->player->draw(); glEndList(); this->image=(unsigned char*)malloc(SIZE*SIZE*4); } void Shadow::createShadow() { glViewport(0,0,SIZE,SIZE); glScissor(0,0,SIZE,SIZE); glEnable(GL_SCISSOR_TEST); glBindTexture(GL_TEXTURE_2D,this->shadow_id); glClearColor(1,1,1,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-4.2,4.2,-4.2,4.2,-100,100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(this->lightPos[0],this->lightPos[1],this->lightPos[2],this->playerPos[0],this->playerPos[1],this->playerPos[2],0,0,1); glColor3f(.1,.1,.1); glTranslatef(this->playerPos[0],this->playerPos[1],this->playerPos[2]); //die Variable angle ist im Player.h als public definiert!!! deshalb kann //von hier aus darauf zugegriffen werden glRotatef(this->playerangle->angle,1.0,0.0,0.0); //Lighting has to be disabled for the shadow to become nice black respectively grey (color(.1,.1,.1)) glDisable(GL_LIGHTING); glCallList(this->player_id); glEnable(GL_LIGHTING); glColor3f(1,1,1); glCopyTexSubImage2D(GL_TEXTURE_2D,0,0,0,0,0,SIZE,SIZE); //glReadPixels(0,0,SIZE,SIZE,GL_RGB,GL_UNSIGNED_BYTE,this->image); //blur(this->image,SIZE); //glTexSubImage2D(GL_TEXTURE_2D,0,0,0,SIZE,SIZE,GL_RGB,GL_UNSIGNED_BYTE,this->image); glDisable(GL_SCISSOR_TEST); glViewport(0,0,400,300); //Achtung: hier Aufloesung von Orxonox einstellen! } /** brief updatePosition is used in the same kind as for skysphere, because the light has got a static orientation(parallel light), we have to always add the same relative coordinates being 0,10,19 to the players position in order to receive the lightPosition. This is needed to calculate the Shadow!! */ void Shadow::updatePosition(float x,float y,float z) { this->playerPos[0]=x; this->playerPos[1]=y; this->playerPos[2]=z; this->lightPos[0]=this->playerPos[0]; this->lightPos[1]=this->playerPos[1]+10; this->lightPos[2]=this->playerPos[2]+19; } /** brief takes the ground data from the world.cc class and passes it to shadow.cc */ void Shadow::setGround() { glBegin(GL_QUADS); for(int i=0;i<100;i+=1) for(int j=0;j<80;j+=1) { glNormal3f(normal_vectors[i][j].x,normal_vectors[i][j].y,normal_vectors[i][j].z); glTexCoord2f(0.0f,0.0f); glVertex3f(vertexes[0][i][j].x,vertexes[0][i][j].y,vertexes[0][i][j].z); glNormal3f(normal_vectors[i+1][j].x,normal_vectors[i+1][j].y,normal_vectors[i+1][j].z); glTexCoord2f(1.0f,0.0f); glVertex3f(vertexes[1][i][j].x,vertexes[1][i][j].y,vertexes[1][i][j].z); glNormal3f(normal_vectors[i+1][j+1].x,normal_vectors[i+1][j+1].y,normal_vectors[i+1][j+1].z); glTexCoord2f(1.0f,1.0f); glVertex3f(vertexes[2][i][j].x,vertexes[2][i][j].y,vertexes[2][i][j].z); glNormal3f(normal_vectors[i][j+1].x,normal_vectors[i][j+1].y,normal_vectors[i][j+1].z); glTexCoord2f(0.0f,1.0f); glVertex3f(vertexes[3][i][j].x,vertexes[3][i][j].y,vertexes[3][i][j].z); } glEnd(); } void Shadow::setNormal(Vector a,int i,int j) { normal_vectors[i][j]=a; } void Shadow::setVertexNum(Vector a,int i,int j,int num) { vertexes[num][i][j]=a; } /** brief m_inverse simply inverses the *m matrix and stores the result back to *out. This is needed further down in the draw() method */ void Shadow::m_inverse(const float *m,float *out) { float det; det= m[0]*m[5]*m[10]; det+= m[4]*m[9]*m[2]; det+= m[8]*m[1]*m[6]; det-= m[8]*m[5]*m[2]; det-= m[4]*m[1]*m[10]; det-= m[0]*m[9]*m[6]; if(det!= 0.0) det=1.0/det; out[0]= (m[5]*m[10]-m[9]*m[6])*det; out[1]= -(m[1]*m[10]-m[9]*m[2])*det; out[2]= (m[1]*m[6]-m[5]*m[2])*det; out[3]= 0.0; out[4]= -(m[4]*m[10]-m[8]*m[6])*det; out[5]= (m[0]*m[10]-m[8]*m[2])*det; out[6]= -(m[0]*m[6]-m[4]*m[2])*det; out[7]= 0.0; out[8]= (m[4]*m[9]-m[8]*m[5])*det; out[9]= -(m[0]*m[9]-m[8]*m[1])*det; out[10]= (m[0]*m[5]-m[4]*m[1])*det; out[11]= 0.0; out[12]=- (m[12]*out[0]+m[13]*out[4]+m[14]*out[8]); out[13]=- (m[12]*out[1]+m[13]*out[5]+m[14]*out[9]); out[14]=- (m[12]*out[2]+m[13]*out[6]+m[14]*out[10]); out[15]= 1.0; } /** brief Method draw() is called after each tick() from the world.cc class */ void Shadow::draw() { float m[16],im[16]; createShadow(); //glClearColor(0,0,0,0); //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45,4.0/3.0,.5,100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_GEN_Q); glGetFloatv(GL_MODELVIEW_MATRIX,m); m_inverse(m,im); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glTranslatef(-.5,.5,0); glScalef(0.3,0.3,1.0); glOrtho(-1,1,-1,1,-1,1); gluLookAt(this->lightPos[0],this->lightPos[1],this->lightPos[2],this->playerPos[0],this->playerPos[1],this->playerPos[2],0,0,1); glMultMatrixf(im); glEnable(GL_TEXTURE_2D), glBindTexture(GL_TEXTURE_2D,this->shadow_id); glEnable(GL_BLEND); glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR); glCallList(this->ground_id); glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); glDisable(GL_TEXTURE_GEN_Q); } /** \don't ask me how this works, but it adds a blur effect to the shadow \for it doesn't look that edgy */ void Shadow::blur(unsigned char *in,int size) { int x,y,sum,size3=size*3; unsigned char *out,*inp,*outp; out = (unsigned char *)malloc(size * size * 3); memset(out,255,size *size *3); inp=in+size3; outp=out+size3; for(y=1;y