02061d74c2
(as received from Jonas Echterhoff)
236 lines
7.9 KiB
C++
236 lines
7.9 KiB
C++
#include <OpenGL/gl.h>
|
|
#include <math.h>
|
|
#include "vectors.h"
|
|
#include "environment.h"
|
|
#include "entities.h"
|
|
#include "textures.h"
|
|
#include "renderframe.h"
|
|
#include "roads.h"
|
|
#include "collision.h"
|
|
#include "config.h"
|
|
#include "stencil.h"
|
|
|
|
#define kLightOffset 0.6
|
|
#define kLightReflectionOffset 15
|
|
|
|
void RenderEntityLights(tGameEntity *entity,int numLights,int lightFlags,float reflectionDim,tLightDefinition *lights)
|
|
{
|
|
glPushAttrib(GL_DEPTH_BUFFER_BIT+GL_CURRENT_BIT+GL_LIGHTING_BIT+GL_COLOR_BUFFER_BIT+GL_FOG_BIT);
|
|
glDepthMask(false);
|
|
glDisable(GL_LIGHTING);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE);
|
|
glDisable(GL_FOG);
|
|
TexturesSelectTex(FileGetReference("Particle.pct"));
|
|
|
|
for(int i=0;i<numLights;i++)
|
|
//is the light on?
|
|
if(lightFlags&lights[i].onFlags&&!(lightFlags&lights[i].offFlags))
|
|
//is this a dot light? (a light which only needs a particle texture to be drawn)
|
|
if(lights[i].type!=kLightTypeSpot)
|
|
{
|
|
float visibility=-lights[i].dir*entity->dir**MatrixGetZVector(gCameraEntity->dir);
|
|
//is the light pointing towards the camera?
|
|
|
|
if(lights[i].type==kLightTypeDirectionlessDot||lights[i].type==kLightTypeDirectionlessDotReflective)
|
|
visibility=1;
|
|
else if(lights[i].type==kLightTypeSpecularDot)
|
|
visibility=sign(visibility)*powf(visibility*!((lights[i].pos*entity->dir+entity->pos)-gCameraEntity->pos)**MatrixGetZVector(gCameraEntity->dir),100);
|
|
|
|
if(visibility>0){
|
|
if(visibility>1)visibility=1;
|
|
|
|
float size=lights[i].size;
|
|
|
|
//draw particle texture
|
|
|
|
tVector3 lightPos=lights[i].pos*entity->dir+entity->pos;
|
|
float camDist=~(lightPos-gCameraEntity->pos);
|
|
tVector3 drawlightPos=lightPos-(lightPos-gCameraEntity->pos)*kLightOffset/camDist;
|
|
|
|
size*=(camDist-kLightOffset)/camDist;
|
|
|
|
SetupTranslation(drawlightPos,gCameraEntity->dir);
|
|
glColor4f(lights[i].rgb.x,lights[i].rgb.y,lights[i].rgb.z,visibility);
|
|
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glTexCoord2d(1,1); glVertex2f(size,size);
|
|
glTexCoord2d(1,0); glVertex2f(size,-size);
|
|
glTexCoord2d(0,1); glVertex2f(-size,size);
|
|
glTexCoord2d(0,0); glVertex2f(-size,-size);
|
|
glEnd();
|
|
|
|
//does the light reflect on the ground?
|
|
if(lights[i].type==kLightTypeDotRefelective||lights[i].type==kLightTypeDirectionlessDotReflective)
|
|
{
|
|
int lastRoadIndex=entity->lastRoadIndex;
|
|
int surface=-1;
|
|
lightPos=lightPos-Vector(0,GetGroundOffset(lightPos,&lastRoadIndex,NULL,&surface),0);
|
|
float camDist=~(lightPos-gCameraEntity->pos);
|
|
float size=lights[i].size;
|
|
if(camDist>2*kLightReflectionOffset)
|
|
{
|
|
drawlightPos=lightPos-(lightPos-gCameraEntity->pos)*kLightReflectionOffset/camDist;
|
|
size*=(camDist-kLightReflectionOffset)/camDist;
|
|
}
|
|
else
|
|
drawlightPos=lightPos;
|
|
//is the ground reflective
|
|
if(surface!=-1)
|
|
if(gSurfaceTypes->types[surface].reflectionEnable)
|
|
{
|
|
//draw reflection texture
|
|
SetupTranslation(drawlightPos,gCameraEntity->dir);
|
|
if(camDist<=2*kLightReflectionOffset)
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
visibility*=1-reflectionDim*0.6;
|
|
glColor4f(lights[i].rgb.x,lights[i].rgb.y,lights[i].rgb.z,visibility);
|
|
|
|
TexturesSelectTex(FileGetReference("lightreflection.pct"));
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glTexCoord2d(1,1); glVertex2f(size,size);
|
|
glTexCoord2d(1,0); glVertex2f(size,-5*size);
|
|
glTexCoord2d(0,1); glVertex2f(-size,size);
|
|
glTexCoord2d(0,0); glVertex2f(-size,-5*size);
|
|
glEnd();
|
|
TexturesSelectTex(FileGetReference("Particle.pct"));
|
|
#ifdef __POLYCOUNT
|
|
gPolyCount+=2;
|
|
#endif
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
}
|
|
#ifdef __POLYCOUNT
|
|
gPolyCount+=2;
|
|
#endif
|
|
}
|
|
}
|
|
glPopAttrib();
|
|
}
|
|
|
|
#define kConeSections 16
|
|
#define kConeLength 6.5
|
|
|
|
void DrawLightCone(float scale)
|
|
{
|
|
tVector3 c[kConeSections];
|
|
for(int i=0;i<kConeSections;i++)
|
|
c[i]=Vector(cos((2*PI*i)/kConeSections)*gStencilZoom,sin((2*PI*i)/kConeSections)*gStencilZoom,4)*scale*gStencilZoom;
|
|
for(int i=0;i<kConeSections;i++)
|
|
{
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glVertex3f(0,0,(1-gStencilZoom)*10);
|
|
glVertex3fv(&c[i].x);
|
|
glVertex3fv(&c[(i+1)%kConeSections].x);
|
|
glVertex3f(0,0,kConeLength*scale);
|
|
glEnd();
|
|
}
|
|
#ifdef __POLYCOUNT
|
|
gPolyCount+=2*kConeSections;
|
|
#endif
|
|
}
|
|
|
|
void DrawLightConeClippedFar(float scale,float clipPlanePos,tVector3 clipPlaneNormal)
|
|
{
|
|
tVector3 c[kConeSections];
|
|
tVector3 clipPos=Vector(0,0,kConeLength*scale*clipPlanePos);
|
|
float baseClipDist=-clipPos*clipPlaneNormal;
|
|
float cClipDist;
|
|
for(int i=0;i<kConeSections;i++)
|
|
c[i]=Vector(cos((2*PI*i)/kConeSections)*gStencilZoom,sin((2*PI*i)/kConeSections)*gStencilZoom,4)*scale*gStencilZoom;
|
|
for(int i=0;i<kConeSections;i++)
|
|
if((cClipDist=((c[i]-clipPos)*clipPlaneNormal))>0)
|
|
{
|
|
float cClipPos=(1-(cClipDist/(cClipDist-baseClipDist)));
|
|
if(cClipPos<=0)return;
|
|
c[i]=c[i]*cClipPos;
|
|
}
|
|
for(int i=0;i<kConeSections;i++)
|
|
{
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glVertex3f(0,0,(1-gStencilZoom)*10);
|
|
glVertex3fv(&c[i].x);
|
|
glVertex3fv(&c[(i+1)%kConeSections].x);
|
|
glVertex3f(0,0,clipPos.z);
|
|
glEnd();
|
|
}
|
|
#ifdef __POLYCOUNT
|
|
gPolyCount+=2*kConeSections;
|
|
#endif
|
|
}
|
|
|
|
void DrawLightConeClippedNear(float scale,tVector3 clipPlanePos,tVector3 clipPlaneNormal)
|
|
{
|
|
}
|
|
|
|
//draws a car's light cones (usually headlights)
|
|
void RenderEntityLightCones(tGameEntity *entity,int numLights,int lightFlags,tLightDefinition *lights)
|
|
{
|
|
glPushAttrib(GL_STENCIL_BUFFER_BIT+GL_COLOR_BUFFER_BIT+GL_POLYGON_BIT+GL_DEPTH_BUFFER_BIT+GL_ENABLE_BIT);
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_FOG);
|
|
|
|
for(int i=0;i<numLights;i++)
|
|
//is the light on?
|
|
if(lightFlags&lights[i].onFlags&&!(lightFlags&lights[i].offFlags))
|
|
//is it a spot light
|
|
if(lights[i].type==kLightTypeSpot)
|
|
//is stencil buffering enabled, and are spotlights enabled for this environment?
|
|
if(gConfig->stencil&&gEnvironment->spotLightEnable)
|
|
{
|
|
//translate to draw the light cone
|
|
tVector3 lightPos=lights[i].pos*entity->dir+entity->pos;
|
|
tMatrix3 m;
|
|
MatrixIdentity(m);
|
|
*MatrixGetZVector(m)=lights[i].dir;
|
|
*MatrixGetXVector(m)=!(lights[i].dir%Vector(0,1,0));
|
|
*MatrixGetYVector(m)=(lights[i].dir%*MatrixGetXVector(m));
|
|
MatrixMult(m,entity->dir,m);
|
|
SetupTranslation(lightPos,m);
|
|
|
|
//setup stencil buffer
|
|
//drawing light cones works just like stencil buffer shadow volumes
|
|
glDepthMask(GL_FALSE);
|
|
glEnable(GL_STENCIL_TEST);
|
|
glColorMask(0, 0, 0, 0);
|
|
glStencilFunc(GL_ALWAYS, 1, 0xffffffff);
|
|
|
|
tVector3 camDir=*MatrixGetZVector(gCameraEntity->dir);
|
|
float clip1=(lightPos-gCameraEntity->pos)*camDir;
|
|
float clip2=((lightPos+(lights[i].dir*kConeLength*lights[i].size)*entity->dir)-gCameraEntity->pos)*camDir;
|
|
//test if the light cone volume intersects the clipping volume
|
|
|
|
if(clip1>ClipDistance()&&clip2>ClipDistance())
|
|
{//clip near and far (not implemented)
|
|
}
|
|
else if(clip1>ClipDistance())
|
|
{//clip near (not implemented)
|
|
}
|
|
else if(clip2>ClipDistance()-20)
|
|
{//clip far
|
|
tMatrix3 mInv;
|
|
MatrixTranspose(m,mInv);
|
|
tVector3 clipPlaneDir=camDir*mInv;
|
|
glFrontFace(GL_CW);
|
|
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
|
|
DrawLightConeClippedFar(lights[i].size*gStencilZoom,(ClipDistance()-20-clip1)/(clip2-clip1),clipPlaneDir);
|
|
|
|
glFrontFace(GL_CCW);
|
|
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
|
|
DrawLightConeClippedFar(lights[i].size*gStencilZoom,(ClipDistance()-20-clip1)/(clip2-clip1),clipPlaneDir);
|
|
}
|
|
else
|
|
{//no clip
|
|
glFrontFace(GL_CW);
|
|
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
|
|
DrawLightCone(lights[i].size*gStencilZoom);
|
|
|
|
glFrontFace(GL_CCW);
|
|
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
|
|
DrawLightCone(lights[i].size*gStencilZoom);
|
|
}
|
|
}
|
|
glPopAttrib();
|
|
}
|