/* 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(GLuint ground_id_imported) { 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}; this->mat=new Material("Ground"); this->mat->setDiffuseMap("../data/pictures/ground.tga"); this->mat->setIllum(3); 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_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,SIZE,SIZE,0,GL_RGB,GL_UNSIGNED_BYTE,NULL); this->player_id=glGenLists(1); glNewList(this->player_id,GL_COMPILE); this->player->draw(); glEndList(); this->image=(unsigned char*)malloc(SIZE*SIZE*4); this->ground_id=ground_id_imported; } void Shadow::createShadow() { glViewport(0,0,SIZE,SIZE); glScissor(0,0,SIZE,SIZE); glEnable(GL_SCISSOR_TEST); glBindTexture(GL_TEXTURE_2D,this->shadow_id); float m[16],m2[16]; glClearColor(1,1,1,1); //glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glGetFloatv(GL_PROJECTION_MATRIX,m); glLoadIdentity(); glOrtho(-4.2,4.2,-4.2,4.2,-100,100); glMatrixMode(GL_MODELVIEW); glGetFloatv(GL_MODELVIEW_MATRIX,m2); 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,30,30,30,SIZE,SIZE,GL_RGB,GL_UNSIGNED_BYTE,this->image); glDisable(GL_SCISSOR_TEST); glViewport(0,0,1024,768); //Achtung: hier Aufloesung von Orxonox einstellen! //Die oben veraenderten und zwischengespeicherten werte wieder herstellen! glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMultMatrixf(m); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMultMatrixf(m2); } void Shadow::draw() { float m[16],im[16]; createShadow(); //glClearColor(0,0,0,1); //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(); //this->mat->select(); //glCallList(this->ground_id); glDisable(GL_TEXTURE_2D); /*shadow*/ 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(1.5,.5,0); glScalef(.5f,1.0,.5f); glOrtho(-1,1,-1,1,-100,100); 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); } /** 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 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 */ /** \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