|
|
|
@@ -0,0 +1,900 @@
|
|
|
|
|
// Shadow volume class
|
|
|
|
|
//
|
|
|
|
|
// Written by Harald JOHNSEN, started June 2005.
|
|
|
|
|
//
|
|
|
|
|
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
|
|
|
|
|
//
|
|
|
|
|
// 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 of the
|
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
// General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program; if not, write to the Free Software
|
|
|
|
|
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include <plib/sg.h>
|
|
|
|
|
#include <plib/ssg.h>
|
|
|
|
|
#include <simgear/props/props.hxx>
|
|
|
|
|
#include <simgear/debug/logstream.hxx>
|
|
|
|
|
#include SG_GLU_H
|
|
|
|
|
|
|
|
|
|
#include "shadowvolume.hxx"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
geometry and edge list
|
|
|
|
|
- traverse object graph until leaf to get geometry
|
|
|
|
|
- what about transform and selection ?
|
|
|
|
|
- use sub objects rather then objects
|
|
|
|
|
- get local transform and selection
|
|
|
|
|
- range anim : ssgRangeSelector ( min, max ) => ssgSelector ( true/false )
|
|
|
|
|
=> selection [0..n], kids [0..n]
|
|
|
|
|
- select/timed anim : ssgSelector ( true/false )
|
|
|
|
|
=> isSelected( nkid )
|
|
|
|
|
- spin/rotate/trans/scale/... : ssgTransform ( matrix )
|
|
|
|
|
=> getNetTransform
|
|
|
|
|
- on new object :
|
|
|
|
|
- for one object branch
|
|
|
|
|
- for each leaf
|
|
|
|
|
- save geometry
|
|
|
|
|
- save address in cache
|
|
|
|
|
- when rendering object
|
|
|
|
|
- for each leaf
|
|
|
|
|
- getNetTransform + object global rotation (ac) => transform for light position
|
|
|
|
|
- go up in tree and check isSelected( self )
|
|
|
|
|
- generate connectivity each time the geometry change
|
|
|
|
|
=> using local light so connectivity never changes
|
|
|
|
|
- generate active edge list when :
|
|
|
|
|
- light moves
|
|
|
|
|
- subpart moves (animation code)
|
|
|
|
|
- ac rotate
|
|
|
|
|
=> cache rotation matrix and generate edge list only if something change
|
|
|
|
|
=> even if it changes, no need to do that every frame
|
|
|
|
|
|
|
|
|
|
- static objects have static edge list if light does not move
|
|
|
|
|
|
|
|
|
|
shadowing a scene
|
|
|
|
|
- render full scene as normal
|
|
|
|
|
- render occluder in stencil buffer with their shadow volumes
|
|
|
|
|
- apply stencil to framebuffer (darkens shadowed parts)
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
// - shadow for objects
|
|
|
|
|
// * aircraft
|
|
|
|
|
// * tile objects (from .stg)
|
|
|
|
|
// - ai objects
|
|
|
|
|
// - random objects => tie shadow geometry to lib objects and reuse them
|
|
|
|
|
// - zfail if camera inside shadow
|
|
|
|
|
// - queue geometry work if lot of objects
|
|
|
|
|
// * add a render property on/off (for aircraft, for scene objects, for ai)
|
|
|
|
|
// * add a render property in rendering dialog
|
|
|
|
|
// * filter : halo, light, shadow, disc, disk, flame, (exhaust), noshadow
|
|
|
|
|
// BUG
|
|
|
|
|
// - ghost objects ?
|
|
|
|
|
// - no shadow : check static 737/747
|
|
|
|
|
// why has the ggb an alpha animation = 0.01 ?
|
|
|
|
|
|
|
|
|
|
static int statSilhouette=0;
|
|
|
|
|
static int statGeom=0;
|
|
|
|
|
static int statObj=0;
|
|
|
|
|
|
|
|
|
|
static SGShadowVolume *states;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SGShadowVolume::ShadowCaster::ShadowCaster( int _num_tri, ssgBranch * _geometry_leaf ) :
|
|
|
|
|
geometry_leaf ( _geometry_leaf ),
|
|
|
|
|
scenery_object ( 0 ),
|
|
|
|
|
first_select ( 0 ),
|
|
|
|
|
frameNumber ( 0 ),
|
|
|
|
|
numTriangles ( 0 ),
|
|
|
|
|
indices ( 0 ),
|
|
|
|
|
vertices ( 0 ),
|
|
|
|
|
planeEquations ( 0 ),
|
|
|
|
|
isFacingLight ( 0 ),
|
|
|
|
|
neighbourIndices ( 0 ),
|
|
|
|
|
isSilhouetteEdge ( 0 )
|
|
|
|
|
{
|
|
|
|
|
int num_tri = _num_tri;
|
|
|
|
|
numTriangles = num_tri;
|
|
|
|
|
indices = new int[num_tri * 3];
|
|
|
|
|
vertices = new sgVec3[num_tri * 3];
|
|
|
|
|
planeEquations = new sgVec4[num_tri];
|
|
|
|
|
neighbourIndices = new int[num_tri * 3];
|
|
|
|
|
isSilhouetteEdge = new bool[num_tri * 3];
|
|
|
|
|
isFacingLight = new bool[num_tri];
|
|
|
|
|
sgSetVec3(last_lightpos, 0.0, 0.0, 0.0);
|
|
|
|
|
statGeom ++;
|
|
|
|
|
|
|
|
|
|
ssgBranch *branch = (ssgBranch *) _geometry_leaf;
|
|
|
|
|
while( branch && branch->getNumParents() > 0 ) {
|
|
|
|
|
if( !first_select && branch->isA(ssgTypeSelector())) {
|
|
|
|
|
first_select = branch;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
branch = branch->getParent(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::ShadowCaster::addLeaf( int & tri_idx, ssgLeaf *geometry_leaf ) {
|
|
|
|
|
int num_tri = geometry_leaf->getNumTriangles();
|
|
|
|
|
for(int i = 0; i < num_tri ; i ++ ) {
|
|
|
|
|
short v1, v2, v3;
|
|
|
|
|
sgVec3 a, b, c;
|
|
|
|
|
geometry_leaf->getTriangle( i, &v1, &v2, &v3 );
|
|
|
|
|
sgCopyVec3(a, geometry_leaf->getVertex(v1));
|
|
|
|
|
sgCopyVec3(b, geometry_leaf->getVertex(v2));
|
|
|
|
|
sgCopyVec3(c, geometry_leaf->getVertex(v3));
|
|
|
|
|
addTri( tri_idx++, a, b, c);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGShadowVolume::ShadowCaster::~ShadowCaster() {
|
|
|
|
|
delete [] indices ;
|
|
|
|
|
delete [] vertices ;
|
|
|
|
|
delete [] planeEquations ;
|
|
|
|
|
delete [] isFacingLight ;
|
|
|
|
|
delete [] neighbourIndices ;
|
|
|
|
|
delete [] isSilhouetteEdge ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::ShadowCaster::addTri(int p, sgVec3 a, sgVec3 b, sgVec3 c) {
|
|
|
|
|
sgVec4 tri_plane;
|
|
|
|
|
assert( p >= 0 && p < numTriangles );
|
|
|
|
|
|
|
|
|
|
sgMakePlane ( tri_plane, a, b, c );
|
|
|
|
|
sgCopyVec4(planeEquations[p], tri_plane);
|
|
|
|
|
sgCopyVec3(vertices[p*3], a);
|
|
|
|
|
sgCopyVec3(vertices[p*3+1], b);
|
|
|
|
|
sgCopyVec3(vertices[p*3+2], c);
|
|
|
|
|
indices[p*3] = p*3;
|
|
|
|
|
indices[p*3+1] = p*3+1;
|
|
|
|
|
indices[p*3+2] = p*3+2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SGShadowVolume::ShadowCaster::sameVertex(int edge1, int edge2) {
|
|
|
|
|
// const float epsilon = 0.01; // 1cm
|
|
|
|
|
// const float epsilon = 0.0; // 1cm
|
|
|
|
|
//return false;
|
|
|
|
|
if( edge1 == edge2)
|
|
|
|
|
return true;
|
|
|
|
|
sgVec3 delta_v;
|
|
|
|
|
sgSubVec3( delta_v, vertices[edge1], vertices[edge2]);
|
|
|
|
|
if( delta_v[SG_X] != 0.0) return false;
|
|
|
|
|
if( delta_v[SG_Y] != 0.0) return false;
|
|
|
|
|
if( delta_v[SG_Z] != 0.0) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Calculate neighbour faces for each edge
|
|
|
|
|
// caution, this is O(n2)
|
|
|
|
|
void SGShadowVolume::ShadowCaster::SetConnectivity(void)
|
|
|
|
|
{
|
|
|
|
|
int edgeCount = 0;
|
|
|
|
|
|
|
|
|
|
//set the neighbour indices to be -1
|
|
|
|
|
for(int ii=0; ii<numTriangles*3; ++ii)
|
|
|
|
|
neighbourIndices[ii]=-1;
|
|
|
|
|
|
|
|
|
|
//loop through triangles
|
|
|
|
|
for(int i=0; i<numTriangles-1; ++i)
|
|
|
|
|
{
|
|
|
|
|
//loop through edges on the first triangle
|
|
|
|
|
for(int edgeI=0; edgeI<3; ++edgeI)
|
|
|
|
|
{
|
|
|
|
|
//continue if this edge already has a neighbour set
|
|
|
|
|
if(neighbourIndices[ i*3+edgeI ]!=-1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
//get the vertex indices on each edge
|
|
|
|
|
int edgeI1=indices[i*3+edgeI];
|
|
|
|
|
int edgeI2=indices[i*3+(edgeI+1)%3];
|
|
|
|
|
|
|
|
|
|
//loop through triangles with greater indices than this one
|
|
|
|
|
for(int j=i+1; j<numTriangles; ++j)
|
|
|
|
|
{
|
|
|
|
|
//loop through edges on triangle j
|
|
|
|
|
for(int edgeJ=0; edgeJ<3; ++edgeJ)
|
|
|
|
|
{
|
|
|
|
|
//get the vertex indices on each edge
|
|
|
|
|
int edgeJ1=indices[j*3+edgeJ];
|
|
|
|
|
int edgeJ2=indices[j*3+(edgeJ == 2 ? 0 : edgeJ+1)];
|
|
|
|
|
|
|
|
|
|
//if these are the same (possibly reversed order), these faces are neighbours
|
|
|
|
|
//no, we only use reverse order because same order means that
|
|
|
|
|
//the triangle is wrongly oriented and that will cause shadow artifact
|
|
|
|
|
if( sameVertex(edgeI1, edgeJ1) && sameVertex(edgeI2, edgeJ2) ) {
|
|
|
|
|
// can happens with 'bad' geometry
|
|
|
|
|
// printf("flipped triangle detected...check your model\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if( false && sameVertex(edgeI1, edgeJ1) && sameVertex(edgeI2, edgeJ2)
|
|
|
|
|
|| sameVertex(edgeI1, edgeJ2) && sameVertex(edgeI2, edgeJ1) )
|
|
|
|
|
{
|
|
|
|
|
int edgeI3=indices[i*3+(edgeI+2)%3];
|
|
|
|
|
int edgeJ3=indices[j*3+(edgeJ+2)%3];
|
|
|
|
|
if( sameVertex(edgeI3, edgeJ3) ) {
|
|
|
|
|
// can happens with 'bad' geometry
|
|
|
|
|
// printf("duplicated tri...check your model\n");
|
|
|
|
|
// exit loop
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
//continue if this edge already has a neighbour set
|
|
|
|
|
// can happens with 'bad' geometry
|
|
|
|
|
if(neighbourIndices[ j*3+edgeJ ]!=-1) {
|
|
|
|
|
// printf("bad edge detected\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
neighbourIndices[i*3+edgeI]=j;
|
|
|
|
|
neighbourIndices[j*3+edgeJ]=i;
|
|
|
|
|
edgeCount ++;
|
|
|
|
|
// exit loop
|
|
|
|
|
j = numTriangles;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// printf("submodel has %d triangles and %d shared edges\n", numTriangles, edgeCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//calculate silhouette edges
|
|
|
|
|
void SGShadowVolume::ShadowCaster::CalculateSilhouetteEdges(sgVec3 lightPosition)
|
|
|
|
|
{
|
|
|
|
|
//Calculate which faces face the light
|
|
|
|
|
for(int i=0; i<numTriangles; ++i)
|
|
|
|
|
{
|
|
|
|
|
if( sgDistToPlaneVec3 ( planeEquations[i], lightPosition ) > 0.0 )
|
|
|
|
|
isFacingLight[i]=true;
|
|
|
|
|
else
|
|
|
|
|
isFacingLight[i]=false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//loop through faces
|
|
|
|
|
|
|
|
|
|
for(int t=0; t < numTriangles; t++) {
|
|
|
|
|
int v = t * 3;
|
|
|
|
|
//if this face is not facing the light, not a silhouette edge
|
|
|
|
|
if(!isFacingLight[t])
|
|
|
|
|
{
|
|
|
|
|
isSilhouetteEdge[v+0]=false;
|
|
|
|
|
isSilhouetteEdge[v+1]=false;
|
|
|
|
|
isSilhouetteEdge[v+2]=false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
//loop through edges
|
|
|
|
|
for(int i = v ; i < v+3 ; i++) {
|
|
|
|
|
//this face is facing the light
|
|
|
|
|
//if the neighbouring face is not facing the light, or there is no neighbouring face,
|
|
|
|
|
//then this is a silhouette edge
|
|
|
|
|
if(neighbourIndices[i]==-1 || !isFacingLight[neighbourIndices[i]])
|
|
|
|
|
isSilhouetteEdge[i]=true;
|
|
|
|
|
else
|
|
|
|
|
isSilhouetteEdge[i]=false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: everyhting here is constant, store the vertex in a cache buffer and call drawelements
|
|
|
|
|
void SGShadowVolume::ShadowCaster::DrawInfiniteShadowVolume(sgVec3 lightPosition, bool drawCaps)
|
|
|
|
|
{
|
|
|
|
|
glColor4f(1.0, 1.0, 0.0, 0.5);
|
|
|
|
|
//TODO: no need for a quad here
|
|
|
|
|
// glBegin(GL_QUADS);
|
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
|
|
|
{
|
|
|
|
|
for(int i=0; i<numTriangles; ++i)
|
|
|
|
|
{
|
|
|
|
|
//if this face does not face the light, continue
|
|
|
|
|
if(!isFacingLight[i])
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
int v = i*3;
|
|
|
|
|
//Loop through edges on this face
|
|
|
|
|
for(int j=0; j<3; ++j)
|
|
|
|
|
{
|
|
|
|
|
//Draw the shadow volume "edge" if this is a silhouette edge
|
|
|
|
|
if(isSilhouetteEdge[v+j])
|
|
|
|
|
{
|
|
|
|
|
sgVec3 vertex1, vertex2;
|
|
|
|
|
sgCopyVec3(vertex1, vertices[indices[v+j]]);
|
|
|
|
|
sgCopyVec3(vertex2, vertices[indices[v+(j == 2 ? 0 : j+1)]]);
|
|
|
|
|
|
|
|
|
|
glVertex3fv(vertex2);
|
|
|
|
|
glVertex3fv(vertex1);
|
|
|
|
|
// w == 0 for infinite shadow
|
|
|
|
|
#if 0
|
|
|
|
|
glVertex4f( vertex1[SG_X]-lightPosition[SG_X],
|
|
|
|
|
vertex1[SG_Y]-lightPosition[SG_Y],
|
|
|
|
|
vertex1[SG_Z]-lightPosition[SG_Z], 0.0f);
|
|
|
|
|
// glVertex4f( vertex2[SG_X]-lightPosition[SG_X],
|
|
|
|
|
// vertex2[SG_Y]-lightPosition[SG_Y],
|
|
|
|
|
// vertex2[SG_Z]-lightPosition[SG_Z], 0.0f);
|
|
|
|
|
#else
|
|
|
|
|
glVertex3f( vertex1[SG_X]-lightPosition[SG_X],
|
|
|
|
|
vertex1[SG_Y]-lightPosition[SG_Y],
|
|
|
|
|
vertex1[SG_Z]-lightPosition[SG_Z]);
|
|
|
|
|
// glVertex3f( vertex2[SG_X]-lightPosition[SG_X],
|
|
|
|
|
// vertex2[SG_Y]-lightPosition[SG_Y],
|
|
|
|
|
// vertex2[SG_Z]-lightPosition[SG_Z]);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
glEnd();
|
|
|
|
|
|
|
|
|
|
//Draw caps if required
|
|
|
|
|
if(drawCaps)
|
|
|
|
|
{
|
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
|
|
|
{
|
|
|
|
|
for(int i=0; i<numTriangles; ++i)
|
|
|
|
|
{
|
|
|
|
|
int v = i*3;
|
|
|
|
|
for(int j=0; j<3; ++j)
|
|
|
|
|
{
|
|
|
|
|
sgVec3 vertex;
|
|
|
|
|
sgCopyVec3(vertex, vertices[indices[v+j]]);
|
|
|
|
|
|
|
|
|
|
if(isFacingLight[i])
|
|
|
|
|
glVertex3fv(vertex);
|
|
|
|
|
else
|
|
|
|
|
glVertex4f( vertex[SG_X]-lightPosition[SG_X],
|
|
|
|
|
vertex[SG_Y]-lightPosition[SG_Y],
|
|
|
|
|
vertex[SG_Z]-lightPosition[SG_Z], 0.0f);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
glEnd();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::ShadowCaster::getNetTransform ( ssgBranch * branch, sgMat4 xform )
|
|
|
|
|
{
|
|
|
|
|
// one less matmult...
|
|
|
|
|
bool first = true;
|
|
|
|
|
// sgMakeIdentMat4 ( xform ) ;
|
|
|
|
|
while( branch && branch != lib_object ) {
|
|
|
|
|
if( branch->isA(ssgTypeTransform()) ) {
|
|
|
|
|
sgMat4 transform;
|
|
|
|
|
if( first ) {
|
|
|
|
|
((ssgTransform *) branch)->getTransform( xform );
|
|
|
|
|
first = false;
|
|
|
|
|
} else {
|
|
|
|
|
((ssgTransform *) branch)->getTransform(transform);
|
|
|
|
|
sgPostMultMat4 ( xform, transform ) ;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
branch = branch->getParent( 0 );
|
|
|
|
|
}
|
|
|
|
|
if( first )
|
|
|
|
|
sgMakeIdentMat4 ( xform ) ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check the value of <select> and <range> animation
|
|
|
|
|
// wich have been computed during the rendering
|
|
|
|
|
bool SGShadowVolume::ShadowCaster::isSelected ( ssgBranch * branch ) {
|
|
|
|
|
while( branch && branch != lib_object) {
|
|
|
|
|
if( branch->isA(ssgTypeSelector()) )
|
|
|
|
|
if( !((ssgSelector *) branch)->isSelected(0) )
|
|
|
|
|
return false;
|
|
|
|
|
branch = branch->getParent(0);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
trans ----- personality ---- shared object
|
|
|
|
|
perso1 *-----
|
|
|
|
|
\
|
|
|
|
|
*--------* shared object
|
|
|
|
|
/
|
|
|
|
|
perso2 *-----
|
|
|
|
|
*/
|
|
|
|
|
void SGShadowVolume::ShadowCaster::computeShadows(sgMat4 rotation, sgMat4 rotation_translation) {
|
|
|
|
|
|
|
|
|
|
// check the select and range ssgSelector node
|
|
|
|
|
// object can't cast shadow if it is not visible
|
|
|
|
|
|
|
|
|
|
if( first_select && ! isSelected( first_select) )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
sgMat4 transform ;
|
|
|
|
|
sgMat4 invTransform;
|
|
|
|
|
// get the transformations : this comes from animation code for example
|
|
|
|
|
// or static transf
|
|
|
|
|
getNetTransform( (ssgBranch *) geometry_leaf, transform );
|
|
|
|
|
sgMat4 transf;
|
|
|
|
|
sgCopyMat4( transf, transform );
|
|
|
|
|
sgPostMultMat4( transf, rotation_translation );
|
|
|
|
|
sgPostMultMat4( transform, rotation );
|
|
|
|
|
// sgInvertMat4( invTransform, transform );
|
|
|
|
|
sgTransposeNegateMat4 ( invTransform, transform );
|
|
|
|
|
|
|
|
|
|
glLoadMatrixf ( (float *) states->CameraViewM ) ;
|
|
|
|
|
glMultMatrixf( (float *) transf );
|
|
|
|
|
|
|
|
|
|
sgVec3 lightPos;
|
|
|
|
|
sgCopyVec3( lightPos, states->sunPos );
|
|
|
|
|
sgXformPnt3( lightPos, invTransform );
|
|
|
|
|
|
|
|
|
|
sgVec3 lightPosNorm;
|
|
|
|
|
sgNormaliseVec3( lightPosNorm, lightPos );
|
|
|
|
|
float deltaPos = sgAbs(lightPosNorm[0] - last_lightpos[0]) +
|
|
|
|
|
sgAbs(lightPosNorm[1] - last_lightpos[1]) +
|
|
|
|
|
sgAbs(lightPosNorm[2] - last_lightpos[2]);
|
|
|
|
|
// if the geometry has rotated/moved enought then
|
|
|
|
|
// we need to recompute the silhouette
|
|
|
|
|
// but this computation does not need to be done each frame
|
|
|
|
|
// -6 fps
|
|
|
|
|
if( (deltaPos > 0.0) && ( states->frameNumber - frameNumber > 0)) {
|
|
|
|
|
CalculateSilhouetteEdges( lightPos );
|
|
|
|
|
sgCopyVec3( last_lightpos, lightPosNorm );
|
|
|
|
|
frameNumber = states->frameNumber ;
|
|
|
|
|
statSilhouette ++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( states->shadowsDebug_enabled )
|
|
|
|
|
{
|
|
|
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
|
|
|
glDisable( GL_CULL_FACE );
|
|
|
|
|
glDisable( GL_DEPTH_TEST );
|
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
|
glColorMask(1, 1, 1, 1);
|
|
|
|
|
glColor4f(0.0, 0.0, 1.0, 0.8);
|
|
|
|
|
glBegin(GL_LINES);
|
|
|
|
|
for(int i=0; i<numTriangles; ++i)
|
|
|
|
|
{
|
|
|
|
|
if(!isFacingLight[i])
|
|
|
|
|
continue;
|
|
|
|
|
int v = i*3;
|
|
|
|
|
//Loop through edges on this face
|
|
|
|
|
sgVec3 vertex1, vertex2, vertex3;
|
|
|
|
|
sgCopyVec3(vertex1, vertices[indices[v+0]]);
|
|
|
|
|
sgCopyVec3(vertex2, vertices[indices[v+1]]);
|
|
|
|
|
sgCopyVec3(vertex3, vertices[indices[v+2]]);
|
|
|
|
|
|
|
|
|
|
if(isSilhouetteEdge[i*3+0]) {
|
|
|
|
|
glVertex3fv(vertex2);
|
|
|
|
|
glVertex3fv(vertex1);
|
|
|
|
|
}
|
|
|
|
|
if(isSilhouetteEdge[i*3+1]) {
|
|
|
|
|
glVertex3fv(vertex2);
|
|
|
|
|
glVertex3fv(vertex3);
|
|
|
|
|
}
|
|
|
|
|
if(isSilhouetteEdge[i*3+2]) {
|
|
|
|
|
glVertex3fv(vertex3);
|
|
|
|
|
glVertex3fv(vertex1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
glEnd();
|
|
|
|
|
glColorMask(0, 0, 0, 0);
|
|
|
|
|
glEnable( GL_CULL_FACE );
|
|
|
|
|
glEnable( GL_DEPTH_TEST );
|
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// -11 fps
|
|
|
|
|
|
|
|
|
|
// TODO:compute intersection with near clip plane...
|
|
|
|
|
bool needZFail=false;
|
|
|
|
|
|
|
|
|
|
// GL_INCR_WRAP_EXT, GL_DECR_WRAP_EXT
|
|
|
|
|
|
|
|
|
|
if(needZFail)
|
|
|
|
|
{
|
|
|
|
|
//Increment stencil buffer for back face depth fail
|
|
|
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
|
|
|
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
|
|
|
|
|
glCullFace(GL_FRONT);
|
|
|
|
|
|
|
|
|
|
DrawInfiniteShadowVolume( lightPos, true);
|
|
|
|
|
|
|
|
|
|
//Decrement stencil buffer for front face depth fail
|
|
|
|
|
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
|
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
|
|
|
|
|
|
DrawInfiniteShadowVolume( lightPos, true);
|
|
|
|
|
}
|
|
|
|
|
else //using zpass
|
|
|
|
|
{
|
|
|
|
|
//Increment stencil buffer for front face depth pass
|
|
|
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
|
|
|
|
glCullFace(GL_BACK);
|
|
|
|
|
|
|
|
|
|
DrawInfiniteShadowVolume( lightPos, false);
|
|
|
|
|
|
|
|
|
|
//Decrement stencil buffer for back face depth pass
|
|
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
|
|
|
|
|
glCullFace(GL_FRONT);
|
|
|
|
|
|
|
|
|
|
DrawInfiniteShadowVolume( lightPos, false);
|
|
|
|
|
}
|
|
|
|
|
// -15 fps
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::SceneryObject::computeShadows(void) {
|
|
|
|
|
|
|
|
|
|
bool intersect = true;
|
|
|
|
|
// compute intersection with view frustum
|
|
|
|
|
// use pending_object (pointer on obj transform) & tile transform
|
|
|
|
|
// to get position
|
|
|
|
|
sgMat4 position, CamXpos;
|
|
|
|
|
sgFrustum *f = ssgGetFrustum();
|
|
|
|
|
pending_object->getParent(0)->getNetTransform( position );
|
|
|
|
|
sgCopyMat4 ( CamXpos, states->CameraViewM ) ;
|
|
|
|
|
sgPreMultMat4 ( CamXpos, position ) ;
|
|
|
|
|
|
|
|
|
|
sgSphere tmp = *(pending_object->getBSphere()) ;
|
|
|
|
|
if ( tmp.isEmpty () )
|
|
|
|
|
intersect = false;
|
|
|
|
|
else {
|
|
|
|
|
// 7000
|
|
|
|
|
float max_dist = 5000.0f;
|
|
|
|
|
tmp . orthoXform ( CamXpos ) ;
|
|
|
|
|
// cull if too far
|
|
|
|
|
if ( -tmp.center[2] - tmp.radius > max_dist )
|
|
|
|
|
intersect = false;
|
|
|
|
|
else if( tmp.center[2] == 0.0 )
|
|
|
|
|
intersect = true;
|
|
|
|
|
// cull if too small on screen
|
|
|
|
|
else if ( tmp.radius / sgAbs(tmp.center[2]) < 1.0 / 40.0 )
|
|
|
|
|
intersect = false;
|
|
|
|
|
else
|
|
|
|
|
intersect = SSG_OUTSIDE != (ssgCullResult) f -> contains ( &tmp );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( intersect ) {
|
|
|
|
|
if( !scenery_object ) {
|
|
|
|
|
find_trans();
|
|
|
|
|
if( scenery_object )
|
|
|
|
|
traverseTree( pending_object );
|
|
|
|
|
else
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
sgMat4 rotation, rotation_translation;
|
|
|
|
|
scenery_object->getNetTransform ( rotation_translation );
|
|
|
|
|
// split placement offset into rotation and offset
|
|
|
|
|
// rotation from model inside world
|
|
|
|
|
// we are not interested in translation since the light is very far away
|
|
|
|
|
// without translation we reduce the frequency of updates of the silouhette
|
|
|
|
|
sgCopyMat4( rotation, rotation_translation );
|
|
|
|
|
sgSetVec4( rotation[3], 0, 0, 0, 1);
|
|
|
|
|
|
|
|
|
|
ShadowCaster_list::iterator iShadowCaster;
|
|
|
|
|
for(iShadowCaster = parts.begin() ; iShadowCaster != parts.end() ; iShadowCaster ++ ) {
|
|
|
|
|
(*iShadowCaster)->computeShadows(rotation, rotation_translation);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
1) starting at the root of a scene object we follow all branch to find vertex leaf.
|
|
|
|
|
each vertex leaf will create a shadow sub object so that the shadow casted by this
|
|
|
|
|
geometry can be correctly rendered according to select or transform nodes.
|
|
|
|
|
2) models are not optimized for shadows because the geometry is cut by
|
|
|
|
|
- select / transform nodes
|
|
|
|
|
- material differences
|
|
|
|
|
since we are not interested in the geometry material we try to merge same level
|
|
|
|
|
leafs. this can divide the number of shadow sub models by a lot (reducing the cpu
|
|
|
|
|
overhead for matrix computation) and at the end this will reduce the number of
|
|
|
|
|
silouhette edge by a lot too.
|
|
|
|
|
*/
|
|
|
|
|
static bool filterName(const char *leaf_name) {
|
|
|
|
|
if( ! leaf_name )
|
|
|
|
|
return true;
|
|
|
|
|
char lname[20];
|
|
|
|
|
int l = 0;
|
|
|
|
|
char *buff;
|
|
|
|
|
for( buff = lname; *leaf_name && l < (sizeof( lname )-1); ++buff, l++ )
|
|
|
|
|
*buff = tolower(*leaf_name++);
|
|
|
|
|
*buff = 0;
|
|
|
|
|
if( strstr(lname, "shadow") || strstr(lname, "light") || strstr(lname, "disk") ||
|
|
|
|
|
strstr(lname, "disk") || strstr(lname, "flame") || strstr(lname, "halo"))
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
void SGShadowVolume::SceneryObject::traverseTree(ssgBranch *branch) {
|
|
|
|
|
int num_tri = 0;
|
|
|
|
|
int num_leaf = 0;
|
|
|
|
|
for(int i = 0 ; i < branch->getNumKids() ; i++) {
|
|
|
|
|
ssgEntity *this_kid = branch->getKid( i );
|
|
|
|
|
if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
|
|
|
|
|
if( filterName( this_kid->getName()) ) {
|
|
|
|
|
num_tri += ((ssgLeaf *) this_kid)->getNumTriangles();
|
|
|
|
|
num_leaf ++;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
traverseTree( (ssgBranch *) this_kid );
|
|
|
|
|
}
|
|
|
|
|
if( num_tri > 0) {
|
|
|
|
|
int tri_idx = 0;
|
|
|
|
|
ShadowCaster *new_part = new ShadowCaster( num_tri, branch);
|
|
|
|
|
new_part->scenery_object = scenery_object;
|
|
|
|
|
new_part->lib_object = lib_object;
|
|
|
|
|
for(int i = 0 ; i < branch->getNumKids() ; i++) {
|
|
|
|
|
ssgEntity *this_kid = branch->getKid( i );
|
|
|
|
|
if( this_kid->isAKindOf(ssgTypeLeaf()) ) {
|
|
|
|
|
if( filterName( this_kid->getName()) )
|
|
|
|
|
new_part->addLeaf( tri_idx, (ssgLeaf *) this_kid );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
new_part->SetConnectivity();
|
|
|
|
|
parts.push_back( new_part );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::SceneryObject::find_trans(void) {
|
|
|
|
|
ssgBranch *branch = pending_object;
|
|
|
|
|
ssgBranch *prev_branch = pending_object;
|
|
|
|
|
// check the existence of the root node
|
|
|
|
|
while( branch && branch->getNumParents() > 0 ) {
|
|
|
|
|
prev_branch = branch;
|
|
|
|
|
branch = branch->getParent(0);
|
|
|
|
|
}
|
|
|
|
|
// check if we are connected to the scene graph
|
|
|
|
|
if( !branch->isA(ssgTypeRoot() ) )
|
|
|
|
|
return;
|
|
|
|
|
scenery_object = pending_object;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGShadowVolume::SceneryObject::SceneryObject(ssgBranch *_scenery_object, OccluderType _occluder_type) :
|
|
|
|
|
pending_object ( _scenery_object ),
|
|
|
|
|
occluder_type ( _occluder_type ),
|
|
|
|
|
scenery_object ( 0 )
|
|
|
|
|
{
|
|
|
|
|
// queue objects, don't do all the work in the first frames because of
|
|
|
|
|
// massive cpu power needed
|
|
|
|
|
statObj++;
|
|
|
|
|
if( occluder_type == SGShadowVolume::occluderTypeAircraft )
|
|
|
|
|
lib_object = _scenery_object;
|
|
|
|
|
else
|
|
|
|
|
lib_object = (ssgBranch *) ((ssgBranch *)_scenery_object->getKid(0))->getKid(0);
|
|
|
|
|
#if 0
|
|
|
|
|
find_trans();
|
|
|
|
|
if( scenery_object )
|
|
|
|
|
traverseTree( pending_object );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGShadowVolume::SceneryObject::~SceneryObject()
|
|
|
|
|
{
|
|
|
|
|
// parts.erase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::computeShadows(void) {
|
|
|
|
|
|
|
|
|
|
// intensity = ambient + lights
|
|
|
|
|
// lights = sun_const * (sun_angle . normal)
|
|
|
|
|
double dot_light = cos(sun_angle);
|
|
|
|
|
// do nothing if sun is under horizon
|
|
|
|
|
if( dot_light < 0.2 )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
//Draw shadow volumes
|
|
|
|
|
glPushAttrib(GL_ALL_ATTRIB_BITS);
|
|
|
|
|
glClear(GL_STENCIL_BUFFER_BIT);
|
|
|
|
|
|
|
|
|
|
glDisable( GL_LIGHTING );
|
|
|
|
|
glDisable( GL_FOG );
|
|
|
|
|
glDisable(GL_ALPHA);
|
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
|
glEnable( GL_CULL_FACE );
|
|
|
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
|
|
|
|
|
|
glColorMask(0, 0, 0, 0);
|
|
|
|
|
glShadeModel(GL_FLAT);
|
|
|
|
|
|
|
|
|
|
glDepthMask( false );
|
|
|
|
|
glEnable( GL_DEPTH_TEST );
|
|
|
|
|
glDepthFunc(GL_LESS);
|
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
|
|
|
|
|
|
SceneryObject_map::iterator iSceneryObject;
|
|
|
|
|
// compute shadows for each objects
|
|
|
|
|
for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); iSceneryObject++ ) {
|
|
|
|
|
SceneryObject *an_occluder = iSceneryObject->second;
|
|
|
|
|
if( shadowsTO_enabled && (an_occluder->occluder_type == occluderTypeTileObject) ||
|
|
|
|
|
shadowsAI_enabled && (an_occluder->occluder_type == occluderTypeAI ) ||
|
|
|
|
|
shadowsAC_enabled && (an_occluder->occluder_type == occluderTypeAircraft ) )
|
|
|
|
|
an_occluder->computeShadows();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// now the stencil contains 0 for scenery in light and != 0 for parts in shadow
|
|
|
|
|
// draw a quad covering the screen, the stencil will be the mask
|
|
|
|
|
// we darken the shadowed parts
|
|
|
|
|
glStencilFunc(GL_NOTEQUAL, 0, ~0);
|
|
|
|
|
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
|
|
|
|
glEnable(GL_STENCIL_TEST);
|
|
|
|
|
glColorMask(1, 1, 1, 1);
|
|
|
|
|
|
|
|
|
|
glMatrixMode ( GL_PROJECTION ) ;
|
|
|
|
|
glPushMatrix () ;
|
|
|
|
|
glLoadIdentity () ;
|
|
|
|
|
glOrtho ( -100, 100, -100, 100, -1, 1 ) ;
|
|
|
|
|
glMatrixMode ( GL_MODELVIEW ) ;
|
|
|
|
|
glPushMatrix () ;
|
|
|
|
|
glLoadIdentity () ;
|
|
|
|
|
|
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
|
|
|
// glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
glPolygonMode(GL_FRONT, GL_FILL);
|
|
|
|
|
glEnable(GL_ALPHA);
|
|
|
|
|
glAlphaFunc(GL_GREATER, 0.0f);
|
|
|
|
|
glEnable(GL_BLEND);
|
|
|
|
|
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
|
|
|
|
|
glColor4f(0.0, 0.0, 0.0, sgLerp(0.1, 0.3, dot_light) );
|
|
|
|
|
// fixed value is better, the previous line is surely wrong
|
|
|
|
|
glColor4f(0.0, 0.0, 0.0, 0.3 );
|
|
|
|
|
// glColor4f(1.0, 0.0, 0.0, 0.5);
|
|
|
|
|
glRectf(-100,-100,100,100);
|
|
|
|
|
|
|
|
|
|
glMatrixMode ( GL_PROJECTION ) ;
|
|
|
|
|
glPopMatrix () ;
|
|
|
|
|
glMatrixMode ( GL_MODELVIEW ) ;
|
|
|
|
|
glPopMatrix () ;
|
|
|
|
|
|
|
|
|
|
glDisable(GL_STENCIL_TEST);
|
|
|
|
|
glPopAttrib();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGShadowVolume::SGShadowVolume() :
|
|
|
|
|
init_done( false ),
|
|
|
|
|
shadows_enabled( false ),
|
|
|
|
|
frameNumber( 0 )
|
|
|
|
|
{
|
|
|
|
|
states = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SGShadowVolume::~SGShadowVolume() {
|
|
|
|
|
// sceneryObjects.erase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::init(SGPropertyNode *sim_rendering_options) {
|
|
|
|
|
init_done = true;
|
|
|
|
|
shadows_enabled = true;
|
|
|
|
|
sim_rendering = sim_rendering_options;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::startOfFrame(void) {
|
|
|
|
|
}
|
|
|
|
|
void SGShadowVolume::deleteOccluderFromTile(ssgBranch *tile) {
|
|
|
|
|
SceneryObject_map::iterator iSceneryObject, iPrevious;
|
|
|
|
|
iPrevious = sceneryObjects.begin();
|
|
|
|
|
for(iSceneryObject = sceneryObjects.begin() ; iSceneryObject != sceneryObjects.end(); iSceneryObject++ ) {
|
|
|
|
|
if( iSceneryObject->second->tile == tile ) {
|
|
|
|
|
delete iSceneryObject->second;
|
|
|
|
|
sceneryObjects.erase( iSceneryObject );
|
|
|
|
|
iSceneryObject = iPrevious;
|
|
|
|
|
}
|
|
|
|
|
iPrevious = iSceneryObject;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::deleteOccluder(ssgBranch *occluder) {
|
|
|
|
|
ssgBranch *branch = occluder;
|
|
|
|
|
// skip first node and go to first transform (placement)
|
|
|
|
|
while( occluder && !occluder->isA(ssgTypeTransform()))
|
|
|
|
|
occluder = (ssgBranch *) occluder->getKid(0);
|
|
|
|
|
|
|
|
|
|
// check if we allready know this object
|
|
|
|
|
SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
|
|
|
|
|
if( iSceneryObject != sceneryObjects.end() ) {
|
|
|
|
|
delete iSceneryObject->second;
|
|
|
|
|
sceneryObjects.erase( occluder );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::addOccluder(ssgBranch *occluder, OccluderType occluder_type, ssgBranch *tile) {
|
|
|
|
|
|
|
|
|
|
ssgBranch *branch = occluder;
|
|
|
|
|
|
|
|
|
|
// skip first node and go to first transform (placement)
|
|
|
|
|
while( occluder && !occluder->isA(ssgTypeTransform()))
|
|
|
|
|
occluder = (ssgBranch *) occluder->getKid(0);
|
|
|
|
|
|
|
|
|
|
// check if we allready know this object
|
|
|
|
|
SceneryObject_map::iterator iSceneryObject = sceneryObjects.find( occluder );
|
|
|
|
|
if( iSceneryObject == sceneryObjects.end() ) {
|
|
|
|
|
// printf("adding model %x %x\n", occluder, tile);
|
|
|
|
|
SceneryObject *entry = new SceneryObject( occluder, occluder_type );
|
|
|
|
|
entry->tile = tile;
|
|
|
|
|
sceneryObjects[ occluder ] = entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::setupShadows( double lon, double lat,
|
|
|
|
|
double gst, double SunRightAscension, double SunDeclination, double sunAngle) {
|
|
|
|
|
|
|
|
|
|
shadowsAC_enabled = sim_rendering->getBoolValue("shadows-ac", false);
|
|
|
|
|
shadowsAI_enabled = sim_rendering->getBoolValue("shadows-ai", false);
|
|
|
|
|
shadowsTO_enabled = sim_rendering->getBoolValue("shadows-to", false);
|
|
|
|
|
shadowsDebug_enabled = sim_rendering->getBoolValue("shadows-debug", false);
|
|
|
|
|
// shadows_enabled = sim_rendering->getBoolValue("shadows", false);
|
|
|
|
|
shadows_enabled = shadowsAC_enabled || shadowsAI_enabled || shadowsTO_enabled;
|
|
|
|
|
|
|
|
|
|
if( ! shadows_enabled )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
sgMat4 view_angle;
|
|
|
|
|
sun_angle = sunAngle;
|
|
|
|
|
{
|
|
|
|
|
sgMat4 LON, LAT;
|
|
|
|
|
sgVec3 axis;
|
|
|
|
|
|
|
|
|
|
sgSetVec3( axis, 0.0, 0.0, 1.0 );
|
|
|
|
|
sgMakeRotMat4( LON, lon, axis );
|
|
|
|
|
|
|
|
|
|
sgSetVec3( axis, 0.0, 1.0, 0.0 );
|
|
|
|
|
sgMakeRotMat4( LAT, 90.0 - lat, axis );
|
|
|
|
|
|
|
|
|
|
sgMat4 TRANSFORM;
|
|
|
|
|
|
|
|
|
|
sgMakeIdentMat4 ( TRANSFORM );
|
|
|
|
|
sgPreMultMat4( TRANSFORM, LON );
|
|
|
|
|
sgPreMultMat4( TRANSFORM, LAT );
|
|
|
|
|
|
|
|
|
|
sgCoord pos;
|
|
|
|
|
sgSetCoord( &pos, TRANSFORM );
|
|
|
|
|
|
|
|
|
|
sgMakeCoordMat4( view_angle, &pos );
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
sgMat4 GST, RA, DEC;
|
|
|
|
|
sgVec3 axis;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sgSetVec3( axis, 0.0, 0.0, -1.0 );
|
|
|
|
|
sgMakeRotMat4( GST, (gst) * 15.0, axis );
|
|
|
|
|
|
|
|
|
|
sgSetVec3( axis, 0.0, 0.0, 1.0 );
|
|
|
|
|
sgMakeRotMat4( RA, (SunRightAscension * SGD_RADIANS_TO_DEGREES) - 90.0, axis );
|
|
|
|
|
|
|
|
|
|
sgSetVec3( axis, 1.0, 0.0, 0.0 );
|
|
|
|
|
sgMakeRotMat4( DEC, SunDeclination * SGD_RADIANS_TO_DEGREES, axis );
|
|
|
|
|
|
|
|
|
|
sgInvertMat4( invViewAngle, view_angle);
|
|
|
|
|
|
|
|
|
|
sgMat4 TRANSFORM;
|
|
|
|
|
sgMakeIdentMat4( TRANSFORM );
|
|
|
|
|
sgPreMultMat4( TRANSFORM, GST );
|
|
|
|
|
sgPreMultMat4( TRANSFORM, RA );
|
|
|
|
|
sgPreMultMat4( TRANSFORM, DEC );
|
|
|
|
|
// sgSetVec3( sunPos, 0.0, 99000.0, 0.0);
|
|
|
|
|
sgSetVec3( sunPos, 0.0, 9900000.0, 0.0);
|
|
|
|
|
sgXformPnt3( sunPos, TRANSFORM );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssgGetModelviewMatrix( CameraViewM );
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SGShadowVolume::endOfFrame(void) {
|
|
|
|
|
if( ! shadows_enabled )
|
|
|
|
|
return;
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
glBindTexture(GL_TEXTURE_1D, 0);
|
|
|
|
|
|
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
|
|
|
|
|
|
computeShadows();
|
|
|
|
|
|
|
|
|
|
frameNumber ++;
|
|
|
|
|
}
|
|
|
|
|
|