tge/engine/terrain/fluidQuadTree.cc
2017-04-17 06:17:10 -06:00

1075 lines
36 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "terrain/fluid.h"
#include "dgl/dgl.h"
//==============================================================================
// VARIABLES
//==============================================================================
fluid::vertex* fluid::m_pVertex = NULL;
s32 fluid::m_VAllocated = 0;
s32 fluid::m_VUsed = 0;
s16* fluid::m_pIndex = NULL;
s16* fluid::m_pINext = NULL;
s16 fluid::m_IOffset = 0;
s32 fluid::m_IAllocated = 0;
s32 fluid::m_IUsed = 0;
static f32 sSurfaceAtEye;
static f32 sFogZ;
//atic f32 sFogTable[64];
//atic f32 sFogAccessFactor;
//==============================================================================
// FUNCTIONS
//==============================================================================
#define L0 150.0f
#define L1 75.0f
f32 fluid::ComputeLOD( f32 Distance )
{
f32 LOD;
if( Distance > L0 ) return( 0.0f );
if( Distance < L1 ) return( 1.0f );
LOD = (L0 - Distance) / (L0-L1);
return( LOD );
}
#undef L0
#undef L1
//==============================================================================
// Frustrum clip planes: 0=T 1=B 2=L 3=R 4=N 5=F
byte fluid::ComputeClipBits( f32 X, f32 Y, f32 Z )
{
byte Result = 0x00;
s32 i;
for( i = 0; i < 6; i++ )
if( ((X * m_Plane[i].A) + (Y * m_Plane[i].B) + (Z * m_Plane[i].C)) < -m_Plane[i].D )
Result |= (1<<i);
return( Result );
}
//==============================================================================
fluid::vertex* fluid::AcquireVertices( s32 Count )
{
vertex* pResult;
if( m_VAllocated == 0 )
{
m_VAllocated = 100;
m_pVertex = (vertex*)MALLOC( m_VAllocated * sizeof(vertex) );
ASSERT( m_pVertex );
}
while( Count + m_VUsed > m_VAllocated )
{
m_VAllocated += 100;
m_pVertex = (vertex*)REALLOC( m_pVertex, m_VAllocated * sizeof(vertex) );
ASSERT( m_pVertex );
}
pResult = m_pVertex + m_VUsed;
m_IOffset = m_VUsed;
m_VUsed += Count;
return( pResult );
}
//==============================================================================
void fluid::AddTriangleIndices( s16 I1, s16 I2, s16 I3 )
{
if( m_IAllocated == 0 )
{
m_IAllocated = 100;
m_IUsed = 0;
m_pIndex = (s16*)MALLOC( m_IAllocated * sizeof(s16) );
m_pINext = m_pIndex;
ASSERT( m_pIndex );
}
if( m_IUsed+3 > m_IAllocated )
{
s32 Next = m_pINext - m_pIndex;
m_IAllocated += 100;
m_pIndex = (s16*)REALLOC( m_pIndex, m_IAllocated * sizeof(s16) );
m_pINext = m_pIndex + Next;
ASSERT( m_pIndex );
}
m_IUsed += 3;
*m_pINext = m_IOffset + I1; m_pINext++;
*m_pINext = m_IOffset + I2; m_pINext++;
*m_pINext = m_IOffset + I3; m_pINext++;
}
//==============================================================================
void fluid::ReleaseVertexMemory( void )
{
if( m_pVertex )
{
FREE( m_pVertex );
m_pVertex = NULL;
m_VAllocated = 0;
m_VUsed = 0;
}
if( m_pIndex )
{
FREE( m_pIndex );
m_pINext = NULL;
m_pIndex = NULL;
m_IAllocated = 0;
m_IUsed = 0;
}
}
//==============================================================================
/*
void fluid::BuildFogTable( void )
{
f32 Distance;
f32 Delta;
f32 Z;
s32 i;
Z = m_SurfaceZ - m_Eye.z;
Delta = m_VisibleDistance / 64.0f;
Distance = 0.0f;
for( i = 0; i < 64; i++ )
{
Distance += Delta;
sFogTable[i] = m_pFogFn( Distance, Z );
}
// And, create a multiplier we will use to access the table.
sFogAccessFactor = 64.0f / m_VisibleDistance;
}
*/
//==============================================================================
void fluid::RunQuadTree( bool& EyeSubmerged )
{
node Stack[ 32 ];
s32 Top = -1;
s32 i, j;
s32 I, J;
s32 BlocksPerRep = m_HighResMode ? 64 : 32;
// Build a fog sample table.
// BuildFogTable();
// We will use a single 'altitude' for all fog (regardless of waves).
sFogZ = m_SurfaceZ - m_Eye.z;
// Determine where fluid surface is directly above (or above) the eye.
sSurfaceAtEye = m_SurfaceZ + (SINE( (m_Eye.x * 0.05f) + m_Seconds ) +
SINE( (m_Eye.y * 0.05f) + m_Seconds )) * m_WaveFactor;
// Set a flag to be returned to the caller.
EyeSubmerged = (m_Eye.z < sSurfaceAtEye) && IsFluidAtXY( m_Eye.x, m_Eye.y );
// Prepare to accumulate vertices and indices.
m_VUsed = 0;
m_IUsed = 0;
m_pINext = m_pIndex;
// Determine what "rep" of the terrain we are in.
I = (s32)(m_Eye.x / F32(m_TerrainSize));
J = (s32)(m_Eye.y / F32(m_TerrainSize));
if( m_Eye.x < 0.0f ) I--;
if( m_Eye.y < 0.0f ) J--;
// Push 9 reps of the fluid onto the stack.
for( j = J-1; j <= J+1; j++ )
for( i = I-1; i <= I+1; i++ )
{
// New stack node.
Top++;
// Set up the level based masking.
Stack[Top].Level = 0;
Stack[Top].MaskIndexX = 0;
Stack[Top].MaskIndexY = 0;
// Set up "block" coordinates and factor in the rep of the terrain.
Stack[Top].BlockX0 = (i * BlocksPerRep);
Stack[Top].BlockY0 = (j * BlocksPerRep);
// Note that the MaskIndexX and the BlockX0 are similar, but not the
// same. The BlockX0 is affected by the rep of the terrain, whereas
// MaskIndexX is not. Note that neither takes into consideration
// the offset of the fluid within a terrain rep.
// Compute world coordinates. Account for fluid offset within terrain.
Stack[Top].X0 = (Stack[Top].BlockX0 * m_Step[4]) + (m_SquareX0 * F32(m_TerrainBlockSize));
Stack[Top].Y0 = (Stack[Top].BlockY0 * m_Step[4]) + (m_SquareY0 * F32(m_TerrainBlockSize));
Stack[Top].X1 = Stack[Top].X0 + F32(m_TerrainSize);
Stack[Top].Y1 = Stack[Top].Y0 + F32(m_TerrainSize);
// Compute clip bits for each corner.
Stack[Top].ClipBits[0] = ComputeClipBits( Stack[Top].X0, Stack[Top].Y0, m_SurfaceZ );
Stack[Top].ClipBits[1] = ComputeClipBits( Stack[Top].X1, Stack[Top].Y0, m_SurfaceZ );
Stack[Top].ClipBits[2] = ComputeClipBits( Stack[Top].X0, Stack[Top].Y1, m_SurfaceZ );
Stack[Top].ClipBits[3] = ComputeClipBits( Stack[Top].X1, Stack[Top].Y1, m_SurfaceZ );
}
//
// Let the "recursion" begin.
//
while( Top >= 0 )
{
node& Node = Stack[Top];
ASSERT( Top < 32 );
ASSERT( Node.MaskIndexX < (1 << Node.Level) );
ASSERT( Node.MaskIndexY < (1 << Node.Level) );
// Attempt to trivially reject the whole node. See if the clip bits
// indicate that all 4 verts are outside one of the frustrum planes.
if( Node.ClipBits[0] &
Node.ClipBits[1] &
Node.ClipBits[2] &
Node.ClipBits[3] )
{
Top--;
continue;
}
// Attempt to trivially reject the whole node based on the mask bits.
if( GetRejectBit( Node.Level, Node.MaskIndexX, Node.MaskIndexY ) )
{
Top--;
continue;
}
// If we have reached level 5 (the node is a single block), then
// accept it.
if( Node.Level == 5 )
{
ProcessNode( Node );
Top--;
continue;
}
// Attempt to trivially accept the whole node. It must pass BOTH the
// clip bits and the accept mask.
if( ((Node.ClipBits[0] | Node.ClipBits[1] |
Node.ClipBits[2] | Node.ClipBits[3]) == 0x00) &&
GetAcceptBit( Node.Level, Node.MaskIndexX, Node.MaskIndexY ) )
{
ProcessNode( Node );
Top--;
continue;
}
// If we are here, then we need to subdivide the node. We will break
// the node into 4 equal parts.
//
// *-----* *--@--*
// | | | 2| 3|
// | Top | becomes... @--@--@
// | | | 0| 1|
// *-----* *--@--*
//
{
node& Node0 = Stack[Top+0];
node& Node1 = Stack[Top+1];
node& Node2 = Stack[Top+2];
node& Node3 = Stack[Top+3];
s32 Blocks;
// We're going to copy the original node onto all of the new nodes
// in a moment. Before we do that, we might as well make some
// changes to the original node that will need to be made in all
// of the new nodes anyways. (Node and Node0 are the same thing.)
Node.Level += 1;
Node.MaskIndexX = Node.MaskIndexX << 1;
Node.MaskIndexY = Node.MaskIndexY << 1;
// The number of blocks across a new node is needed repeatedly
// below. Go ahead and compute it now.
Blocks = (32 >> Node.Level);
// Now, just copy everything from the original node into all of
// the new nodes.
Node3 = Node2 = Node1 = Node0;
// Update Node 0.
Node0.X1 = Node0.X0 + (Blocks * m_Step[4]);
Node0.Y1 = Node0.Y0 + (Blocks * m_Step[4]);
Node0.ClipBits[1] = ComputeClipBits( Node0.X1, Node0.Y0, m_SurfaceZ );
Node0.ClipBits[2] = ComputeClipBits( Node0.X0, Node0.Y1, m_SurfaceZ );
Node0.ClipBits[3] = ComputeClipBits( Node0.X1, Node0.Y1, m_SurfaceZ );
// Update Node 1. Take advantage of updated Node 0.
Node1.MaskIndexX += 1;
Node1.BlockX0 += Blocks;
Node1.X0 = Node0.X1;
Node1.Y1 = Node0.Y1;
Node1.ClipBits[0] = Node0.ClipBits[1];
Node1.ClipBits[2] = Node0.ClipBits[3];
Node1.ClipBits[3] = ComputeClipBits( Node1.X1, Node1.Y1, m_SurfaceZ );
// Update Node 2. Take advantage of updated Node 0.
Node2.MaskIndexY += 1;
Node2.BlockY0 += Blocks;
Node2.X1 = Node0.X1;
Node2.Y0 = Node0.Y1;
Node2.ClipBits[0] = Node0.ClipBits[2];
Node2.ClipBits[1] = Node0.ClipBits[3];
Node2.ClipBits[3] = ComputeClipBits( Node2.X1, Node2.Y1, m_SurfaceZ );
// Update Node 3. Take advantage of updated Nodes 0, 1, and 2.
Node3.MaskIndexX += 1;
Node3.MaskIndexY += 1;
Node3.BlockX0 += Blocks;
Node3.BlockY0 += Blocks;
Node3.X0 = Node0.X1;
Node3.Y0 = Node0.Y1;
Node3.ClipBits[0] = Node0.ClipBits[3];
Node3.ClipBits[1] = Node1.ClipBits[3];
Node3.ClipBits[2] = Node2.ClipBits[3];
Top += 3;
}
}
}
//==============================================================================
void fluid::ProcessNode( node& Node )
{
// Quick debug render.
if( m_ShowNodes )
{
glDisable ( GL_TEXTURE_2D );
glDisable ( GL_DEPTH_TEST );
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE );
glColor4f ( 0.5f, 0.5f, 0.0f, 0.5f );
glBegin ( GL_QUADS );
glVertex3f ( Node.X0, Node.Y0, m_SurfaceZ );
glVertex3f ( Node.X1, Node.Y0, m_SurfaceZ );
glVertex3f ( Node.X1, Node.Y1, m_SurfaceZ );
glVertex3f ( Node.X0, Node.Y1, m_SurfaceZ );
glEnd ();
glEnable ( GL_DEPTH_TEST );
glColor4f ( 0.8f, 0.8f, 0.3f, 0.8f );
glBegin ( GL_QUADS );
glVertex3f ( Node.X0, Node.Y0, m_SurfaceZ );
glVertex3f ( Node.X1, Node.Y0, m_SurfaceZ );
glVertex3f ( Node.X1, Node.Y1, m_SurfaceZ );
glVertex3f ( Node.X0, Node.Y1, m_SurfaceZ );
glEnd ();
}
// For each node, render all the blocks it contains.
s32 X, Y;
s32 Blocks;
Blocks = (32 >> Node.Level);
for( Y = 0; Y < Blocks; Y++ )
for( X = 0; X < Blocks; X++ )
{
block Block;
Block.X0 = Node.X0 + X * m_Step[4];
Block.Y0 = Node.Y0 + Y * m_Step[4];
Block.X1 = Block.X0 + m_Step[4];
Block.Y1 = Block.Y0 + m_Step[4];
Block.Distance[0] = DISTANCE( Block.X0, Block.Y0, m_SurfaceZ );
Block.Distance[1] = DISTANCE( Block.X1, Block.Y0, m_SurfaceZ );
Block.Distance[2] = DISTANCE( Block.X0, Block.Y1, m_SurfaceZ );
Block.Distance[3] = DISTANCE( Block.X1, Block.Y1, m_SurfaceZ );
Block.LOD[0] = ComputeLOD( Block.Distance[0] );
Block.LOD[1] = ComputeLOD( Block.Distance[1] );
Block.LOD[2] = ComputeLOD( Block.Distance[2] );
Block.LOD[3] = ComputeLOD( Block.Distance[3] );
ProcessBlock( Block );
}
}
//==============================================================================
void fluid::ProcessBlock( block& Block )
{
// Quick debug render.
if( m_ShowBlocks )
{
glDisable ( GL_TEXTURE_2D );
glDisable ( GL_DEPTH_TEST );
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE );
glColor4f ( 0.0f, 0.0f, 0.0f, 0.5f );
glBegin ( GL_QUADS );
glVertex3f ( Block.X0, Block.Y0, m_SurfaceZ );
glVertex3f ( Block.X1, Block.Y0, m_SurfaceZ );
glVertex3f ( Block.X1, Block.Y1, m_SurfaceZ );
glVertex3f ( Block.X0, Block.Y1, m_SurfaceZ );
glEnd ();
glEnable ( GL_DEPTH_TEST );
glColor4f ( 0.3f, 0.3f, 0.3f, 0.8f );
glBegin ( GL_QUADS );
glVertex3f ( Block.X0, Block.Y0, m_SurfaceZ );
glVertex3f ( Block.X1, Block.Y0, m_SurfaceZ );
glVertex3f ( Block.X1, Block.Y1, m_SurfaceZ );
glVertex3f ( Block.X0, Block.Y1, m_SurfaceZ );
glEnd ();
}
if( (Block.LOD[0] > 0.0f) &&
(Block.LOD[1] > 0.0f) &&
(Block.LOD[2] > 0.0f) &&
(Block.LOD[3] > 0.0f) )
{
if( (Block.LOD[0] == 1.0f) &&
(Block.LOD[1] == 1.0f) &&
(Block.LOD[2] == 1.0f) &&
(Block.LOD[3] == 1.0f) )
{
// LOD on ALL verts is ONE!
ProcessBlockLODHigh( Block );
}
else
{
// LOD on ALL verts is NON-ZERO, but it is NOT ONE on ALL verts!
ProcessBlockLODMorph( Block );
}
}
else
{
if( (Block.LOD[0] > 0.0f) ||
(Block.LOD[1] > 0.0f) ||
(Block.LOD[2] > 0.0f) ||
(Block.LOD[3] > 0.0f) )
{
// LOD on SOME verts is NON-ZERO, and on SOME it is ZERO!
ProcessBlockLODTrans( Block );
}
else
{
// LOD on ALL verts is ZERO!
ProcessBlockLODLow( Block );
}
}
}
//==============================================================================
static s16 HighLODIndices[96] =
{
0, 1, 6, 6, 5, 0, // +Y
2, 7, 6, 6, 1, 2, // 20--21--22--23--24
2, 3, 8, 8, 7, 2, // | \ | / | \ | / |
4, 9, 8, 8, 3, 4, // 15--16--17--18--19
// | / | \ | / | \ |
6, 11, 10, 10, 5, 6, // 10--11--12--13--14
6, 7, 12, 12, 11, 6, // | \ | / | \ | / |
8, 13, 12, 12, 7, 8, // 05--06--07--08--09
8, 9, 14, 14, 13, 8, // | / | \ | / | \ |
// [00]-01--02--03--04 +X
10, 11, 16, 16, 15, 10, //
12, 17, 16, 16, 11, 12,
12, 13, 18, 18, 17, 12,
14, 19, 18, 18, 13, 14,
16, 21, 20, 20, 15, 16,
16, 17, 22, 22, 21, 16,
18, 23, 22, 22, 17, 18,
18, 19, 24, 24, 23, 18,
};
//==============================================================================
void fluid::ProcessBlockLODHigh( block& Block )
{
vertex* pV;
s32 X, Y, i;
f32 x, y;
s16* pIndex = HighLODIndices;
f32 Distance;
// Get vetices.
pV = AcquireVertices( 25 );
// Put data in the corner verts.
SetupVert( Block.X0, Block.Y0, Block.Distance[0], pV + 0 );
SetupVert( Block.X1, Block.Y0, Block.Distance[1], pV + 4 );
SetupVert( Block.X0, Block.Y1, Block.Distance[2], pV + 20 );
SetupVert( Block.X1, Block.Y1, Block.Distance[3], pV + 24 );
// Put data in all verts but the corners.
i = 0;
for( Y = 0; Y < 5; Y++ )
for( X = 0; X < 5; X++ )
{
if( (i != 0) && (i != 4) && (i != 20) && (i != 24) )
{
x = Block.X0 + m_Step[X];
y = Block.Y0 + m_Step[Y];
Distance = DISTANCE( x, y, m_SurfaceZ );
SetupVert( x, y, Distance, pV+i );
}
i++;
}
// Add the triangle indices.
for( i = 0; i < 32; i++ )
{
AddTriangleIndices( *(pIndex+0), *(pIndex+1), *(pIndex+2) );
pIndex += 3;
}
}
//==============================================================================
void fluid::ProcessBlockLODMorph( block& Block )
{
vertex* pV;
s32 X, Y, i;
f32 x, y;
s16* pIndex = HighLODIndices;
f32 Distance;
f32 LOD;
f32 MiddleX = Block.X0 + m_Step[2];
f32 MiddleY = Block.Y0 + m_Step[2];
f32 MiddleD = DISTANCE( MiddleX, MiddleY, m_SurfaceZ );
// Get vetices.
pV = AcquireVertices( 25 );
// Put data in the corner verts.
SetupVert( Block.X0, Block.Y0, Block.Distance[0], pV + 0 );
SetupVert( Block.X1, Block.Y0, Block.Distance[1], pV + 4 );
SetupVert( Block.X0, Block.Y1, Block.Distance[2], pV + 20 );
SetupVert( Block.X1, Block.Y1, Block.Distance[3], pV + 24 );
// Put data in all verts but the corners.
i = 0;
for( Y = 0; Y < 5; Y++ )
for( X = 0; X < 5; X++ )
{
if( (i != 0) && (i != 4) && (i != 20) && (i != 24) )
{
x = Block.X0 + m_Step[X];
y = Block.Y0 + m_Step[Y];
Distance = DISTANCE( x, y, m_SurfaceZ );
SetupVert( x, y, Distance, pV+i );
}
i++;
}
// Get LOD values for the center AND the centers of each edge.
LOD = Block.LOD[1] < Block.LOD[0] ? Block.LOD[1] : Block.LOD[0];
LOD = Block.LOD[2] < LOD ? Block.LOD[2] : LOD;
LOD = Block.LOD[3] < LOD ? Block.LOD[3] : LOD;
// Interpolate all of the edges.
InterpolateVerts( pV + 0, pV + 1, pV + 2, pV + 3, pV + 4, Block.LOD[0], Block.LOD[1] );
InterpolateVerts( pV + 4, pV + 9, pV + 14, pV + 19, pV + 24, Block.LOD[1], Block.LOD[3] );
InterpolateVerts( pV + 24, pV + 23, pV + 22, pV + 21, pV + 20, Block.LOD[3], Block.LOD[2] );
InterpolateVerts( pV + 20, pV + 15, pV + 10, pV + 5, pV + 0, Block.LOD[2], Block.LOD[0] );
// Now interpolate from the center out to get the remaining 8 verts.
InterpolateVert( pV + 12, pV + 6, pV + 0, LOD );
InterpolateVert( pV + 12, pV + 7, pV + 2, LOD );
InterpolateVert( pV + 12, pV + 8, pV + 4, LOD );
InterpolateVert( pV + 12, pV + 13, pV + 14, LOD );
InterpolateVert( pV + 12, pV + 18, pV + 24, LOD );
InterpolateVert( pV + 12, pV + 17, pV + 22, LOD );
InterpolateVert( pV + 12, pV + 16, pV + 20, LOD );
InterpolateVert( pV + 12, pV + 11, pV + 10, LOD );
// Add the triangle indices.
for( i = 0; i < 32; i++ )
{
AddTriangleIndices( *(pIndex+0), *(pIndex+1), *(pIndex+2) );
pIndex += 3;
}
}
//==============================================================================
void fluid::ProcessBlockLODTrans( block& Block )
{
vertex* pV;
s32 Verts;
s32 I;
f32 X, Y;
f32 Distance;
f32 MiddleX = Block.X0 + m_Step[2];
f32 MiddleY = Block.Y0 + m_Step[2];
f32 MiddleD = DISTANCE( MiddleX, MiddleY, m_SurfaceZ );
// Determine how many verts we need.
Verts = 5;
if( (Block.LOD[0] > 0.0f) && (Block.LOD[0] > 0.0f) ) Verts += 3;
if( (Block.LOD[1] > 0.0f) && (Block.LOD[3] > 0.0f) ) Verts += 3;
if( (Block.LOD[3] > 0.0f) && (Block.LOD[2] > 0.0f) ) Verts += 3;
if( (Block.LOD[2] > 0.0f) && (Block.LOD[0] > 0.0f) ) Verts += 3;
// Get vetices.
pV = AcquireVertices( Verts );
// Build the corner and center vertices.
SetupVert( Block.X0, Block.Y0, Block.Distance[0], pV+0 );
SetupVert( Block.X1, Block.Y0, Block.Distance[1], pV+1 );
SetupVert( Block.X0, Block.Y1, Block.Distance[2], pV+2 );
SetupVert( Block.X1, Block.Y1, Block.Distance[3], pV+3 );
SetupVert( MiddleX, MiddleY, MiddleD, pV+4 );
I = 5;
//------------------------------------------------------
if( (Block.LOD[0] > 0.0f) && (Block.LOD[1] > 0.0f) )
{
X = Block.X0 + m_Step[1];
Distance = DISTANCE( X, Block.Y0, m_SurfaceZ );
SetupVert( X, Block.Y0, Distance, pV+I+0 );
X = Block.X0 + m_Step[2];
Distance = DISTANCE( X, Block.Y0, m_SurfaceZ );
SetupVert( X, Block.Y0, Distance, pV+I+1 );
X = Block.X0 + m_Step[3];
Distance = DISTANCE( X, Block.Y0, m_SurfaceZ );
SetupVert( X, Block.Y0, Distance, pV+I+2 );
InterpolateVerts( pV+0, pV+I+0, pV+I+1, pV+I+2, pV+1, Block.LOD[0], Block.LOD[1] );
AddTriangleIndices( 4, 0, I+0 );
AddTriangleIndices( 4, I+0, I+1 );
AddTriangleIndices( 4, I+1, I+2 );
AddTriangleIndices( 4, I+2, 1 );
I += 3;
}
else
{
AddTriangleIndices( 4, 0, 1 );
}
//------------------------------------------------------
if( (Block.LOD[1] > 0.0f) && (Block.LOD[3] > 0.0f) )
{
Y = Block.Y0 + m_Step[1];
Distance = DISTANCE( Block.X1, Y, m_SurfaceZ );
SetupVert( Block.X1, Y, Distance, pV+I+0 );
Y = Block.Y0 + m_Step[2];
Distance = DISTANCE( Block.X1, Y, m_SurfaceZ );
SetupVert( Block.X1, Y, Distance, pV+I+1 );
Y = Block.Y0 + m_Step[3];
Distance = DISTANCE( Block.X1, Y, m_SurfaceZ );
SetupVert( Block.X1, Y, Distance, pV+I+2 );
InterpolateVerts( pV+1, pV+I+0, pV+I+1, pV+I+2, pV+3, Block.LOD[1], Block.LOD[3] );
AddTriangleIndices( 4, 1, I+0 );
AddTriangleIndices( 4, I+0, I+1 );
AddTriangleIndices( 4, I+1, I+2 );
AddTriangleIndices( 4, I+2, 3 );
I += 3;
}
else
{
AddTriangleIndices( 4, 1, 3 );
}
//------------------------------------------------------
if( (Block.LOD[3] > 0.0f) && (Block.LOD[2] > 0.0f) )
{
X = Block.X1 - m_Step[1];
Distance = DISTANCE( X, Block.Y1, m_SurfaceZ );
SetupVert( X, Block.Y1, Distance, pV+I+0 );
X = Block.X1 - m_Step[2];
Distance = DISTANCE( X, Block.Y1, m_SurfaceZ );
SetupVert( X, Block.Y1, Distance, pV+I+1 );
X = Block.X1 - m_Step[3];
Distance = DISTANCE( X, Block.Y1, m_SurfaceZ );
SetupVert( X, Block.Y1, Distance, pV+I+2 );
InterpolateVerts( pV+3, pV+I+0, pV+I+1, pV+I+2, pV+2, Block.LOD[3], Block.LOD[2] );
AddTriangleIndices( 4, 3, I+0 );
AddTriangleIndices( 4, I+0, I+1 );
AddTriangleIndices( 4, I+1, I+2 );
AddTriangleIndices( 4, I+2, 2 );
I += 3;
}
else
{
AddTriangleIndices( 4, 3, 2 );
}
//------------------------------------------------------
if( (Block.LOD[2] > 0.0f) && (Block.LOD[0] > 0.0f) )
{
Y = Block.Y1 - m_Step[1];
Distance = DISTANCE( Block.X0, Y, m_SurfaceZ );
SetupVert( Block.X0, Y, Distance, pV+I+0 );
Y = Block.Y1 - m_Step[2];
Distance = DISTANCE( Block.X0, Y, m_SurfaceZ );
SetupVert( Block.X0, Y, Distance, pV+I+1 );
Y = Block.Y1 - m_Step[3];
Distance = DISTANCE( Block.X0, Y, m_SurfaceZ );
SetupVert( Block.X0, Y, Distance, pV+I+2 );
InterpolateVerts( pV+2, pV+I+0, pV+I+1, pV+I+2, pV+0, Block.LOD[2], Block.LOD[0] );
AddTriangleIndices( 4, 2, I+0 );
AddTriangleIndices( 4, I+0, I+1 );
AddTriangleIndices( 4, I+1, I+2 );
AddTriangleIndices( 4, I+2, 0 );
I += 3;
}
else
{
AddTriangleIndices( 4, 2, 0 );
}
//------------------------------------------------------
}
//==============================================================================
//
// [2]-----[3]
// |\ 3 /|
// | \ / |
// |4 [4] 2| Rendered as four triangles.
// | / \ |
// |/ 1 \|
// [0]-----[1]
//
void fluid::ProcessBlockLODLow( block& Block )
{
vertex* pV;
f32 MiddleX = Block.X0 + m_Step[2];
f32 MiddleY = Block.Y0 + m_Step[2];
f32 MiddleD = DISTANCE( MiddleX, MiddleY, m_SurfaceZ );
// Get vetices.
pV = AcquireVertices( 5 );
// Put data in the verts.
SetupVert( Block.X0, Block.Y0, Block.Distance[0], pV+0 );
SetupVert( Block.X1, Block.Y0, Block.Distance[1], pV+1 );
SetupVert( Block.X0, Block.Y1, Block.Distance[2], pV+2 );
SetupVert( Block.X1, Block.Y1, Block.Distance[3], pV+3 );
SetupVert( MiddleX, MiddleY, MiddleD, pV+4 );
//--Attempt to reject if all fogged.
// Add the triangle indices.
AddTriangleIndices( 0, 1, 4 );
AddTriangleIndices( 1, 3, 4 );
AddTriangleIndices( 3, 2, 4 );
AddTriangleIndices( 2, 0, 4 );
// Quick debug render.
#if 0
if( 0 )
{
glDisable ( GL_TEXTURE_2D );
glBegin ( GL_QUADS );
glColor4f ( Block.LOD[0], 1.0f, Block.LOD[3], 1.0f );
glVertex3f ( pV[0].XYZ.x, pV[0].XYZ.y, pV[0].XYZ.z );
glVertex3f ( pV[1].XYZ.x, pV[1].XYZ.y, pV[1].XYZ.z );
glVertex3f ( pV[4].XYZ.x, pV[4].XYZ.y, pV[4].XYZ.z );
glVertex3f ( pV[2].XYZ.x, pV[2].XYZ.y, pV[2].XYZ.z );
glVertex3f ( pV[4].XYZ.x, pV[4].XYZ.y, pV[4].XYZ.z );
glVertex3f ( pV[1].XYZ.x, pV[1].XYZ.y, pV[1].XYZ.z );
glVertex3f ( pV[3].XYZ.x, pV[3].XYZ.y, pV[3].XYZ.z );
glVertex3f ( pV[2].XYZ.x, pV[2].XYZ.y, pV[2].XYZ.z );
glEnd ();
}
#endif
}
//==============================================================================
void fluid::SetupVert( f32 X, f32 Y, f32 Distance, vertex* pV )
{
f32 Z;
//
// Compute a Z value.
//
{
f32 WarpZ, WarpD0, WarpD1;
f32 WarpFactor;
f32 Delta;
Delta = SINE( (X * 0.05f) + m_Seconds ) +
SINE( (Y * 0.05f) + m_Seconds );
Z = m_SurfaceZ + Delta * m_WaveFactor;
// When the camera is close to the fluid surface, warp the surface to
// prevent the fluid from intersecting the camera glass.
WarpD0 = m_Step[2];
if( Distance < WarpD0 )
{
WarpD1 = m_Step[1];
if( m_Eye.z > sSurfaceAtEye )
{
WarpZ = m_Eye.z - 0.25f;
if( Z > WarpZ )
{
if( Distance < WarpD1 )
{
// Close enough to completely warp the vert.
Z = WarpZ;
}
else
{
// Range is between WarpD0 and WarpD1. Interpolate.
WarpFactor = ((WarpD0 - Distance) / WarpD1);
Z = (WarpZ * WarpFactor) + (Z * (1.0f-WarpFactor));
}
}
}
else
{
WarpZ = m_Eye.z + 0.25f;
if( Z < WarpZ )
{
if( Distance < WarpD1 )
{
// Close enough to completely warp the vert.
Z = WarpZ;
}
else
{
// Range is between WarpD0 and WarpD1. Interpolate.
WarpFactor = ((WarpD0 - Distance) / WarpD1);
Z = (WarpZ * WarpFactor) + (Z * (1.0f-WarpFactor));
}
}
}
}
}
// These fields are easy!
pV->XYZ.set( X, Y, Z );
pV->RGBA4.R = m_FogColor.R;
pV->RGBA4.G = m_FogColor.G;
pV->RGBA4.B = m_FogColor.B;
pV->RGBA4.A = m_pFogFn( Distance, sFogZ );
/*
pV->RGBA4.A = Distance < m_VisibleDistance
? sFogTable[ (s32)(Distance * sFogAccessFactor) ]
: 1.0f;
*/
//
// Compute the UV for the environment map / specular pass.
//
{
xyz Vector;
// The reflection vector is the "eye to point" vector, but with a positive
// Z value.
// MM: I've 'rotated' these so that we get the environment map reflected in the
// correct orientation.
Vector.X = X - m_Eye.x;
Vector.Y = m_Eye.y - Y;
Vector.Z = Z - m_Eye.z;
if( Vector.Z < 0.0f )
Vector.Z = -Vector.Z;
if( Vector.Z < 0.001f )
Vector.Z = 0.001f;
// The UV values are simply the XY values from the normalized reflection
// vector.
if( Distance < 0.001f )
{
pV->UV3.U = 0.0f;
pV->UV3.V = 0.0f;
}
else
{
// The standard UV reflection mapping tends to over emphasize the
// outer edges of the environment map. Try to "adjust" the values
// to get more efficient use of the map. Oh, and normalize, too.
f32 Value = (Distance - Vector.Z) / (Distance * Distance);
pV->UV3.U = Vector.X * Value;
pV->UV3.V = Vector.Y * Value;
}
// Convert from [+1,-1] to [0,1].
pV->UV3.U = pV->UV3.U * 0.5f + 0.5f;
pV->UV3.V = pV->UV3.V * 0.5f + 0.5f;
// Now, based on the XY, tweak the UV in some liquid manner.
volatile static f32 Q1 = 150.00f;
volatile static f32 Q2 = 2.00f;
volatile static f32 Q3 = 0.01f;
// MM: Modified the environment distortion times to match the surface distortion.
f32 A1 = COSINE( ((X / Q1) + (m_Seconds / m_DistortTime)));
f32 A2 = SINE ( ((Y / Q1) + (m_Seconds / m_DistortTime)));
pV->UV3.U += A1 * Q3;
pV->UV3.V += A2 * Q3;
// MM: Removed the fresnel style blending as it makes the depth-map difficult
// to control.
//f32 Swing = (A1+A2) * 0.15f + 0.5f;
//pV->RGBA1a.A = ((1.0f-Swing) * m_Opacity) / (1.0f - (Swing * m_Opacity));
//pV->RGBA1b.A = Swing * m_Opacity;
// MM: Added Fresnel style blending to the environment map.
f32 Swing = (A1+A2) * 0.15f + 0.5f;
pV->RGBA3.R = pV->RGBA3.G = pV->RGBA3.B = 1.0f;
pV->RGBA3.A = Swing * m_EnvMapIntensity;
}
// MM: Calculate Depth-map Position.
f32 S1 = m_DistortMagnitude * COSINE( (X * m_DistortGridScale) + (m_Seconds / m_DistortTime) );
f32 T1 = m_DistortMagnitude * SINE ( (Y * m_DistortGridScale) + (m_Seconds / m_DistortTime) );
pV->UV1.U = ((X-(m_SquareX0*m_TerrainBlockSize)) * m_DepthTexelX) * m_TessellationSurface + S1;
pV->UV1.V = ((Y-(m_SquareY0*m_TerrainBlockSize)) * m_DepthTexelY) * m_TessellationSurface + T1;
pV->UV2.U = ((X-(m_SquareX0*m_TerrainBlockSize)) * m_DepthTexelX) * m_TessellationShore + S1;
pV->UV2.V = ((Y-(m_SquareY0*m_TerrainBlockSize)) * m_DepthTexelY) * m_TessellationShore + T1;
// MM: Calculate Depth-map Position.
pV->UV4.U = (X-(m_SquareX0*m_TerrainBlockSize)) * m_DepthTexelX;
pV->UV4.V = (Y-(m_SquareY0*m_TerrainBlockSize)) * m_DepthTexelY;
}
//==============================================================================
void fluid::InterpolateVerts( vertex* pV0,
vertex* pV1,
vertex* pV2,
vertex* pV3,
vertex* pV4,
f32 LOD0,
f32 LOD4 )
{
f32 DeltaLOD = (LOD4 - LOD0 ) * 0.25f;
f32 DeltaZ = (pV4->XYZ.z - pV0->XYZ.z) * 0.25f;
f32 DeltaU = (pV4->UV3.U - pV0->UV3.U) * 0.25f;
f32 DeltaV = (pV4->UV3.V - pV0->UV3.V) * 0.25f;
f32 Z = pV0->XYZ.z;
uv UV = pV0->UV3;
f32 LODa = LOD0;
f32 LODb = 1.0f;
//--------------------------------------------------------------------------
// Compute interpolated low detail data.
Z += DeltaZ;
UV.U += DeltaU;
UV.V += DeltaV;
LODa += DeltaLOD;
LODb = 1.0f - LODa;
// Interpolate based on LOD.
pV1->UV3.U = (pV1->UV3.U * LODa) + (UV.U * LODb);
pV1->UV3.V = (pV1->UV3.V * LODa) + (UV.V * LODb);
pV1->XYZ.z = (pV1->XYZ.z * LODa) + (Z * LODb);
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Compute complete interpolated low detail data.
Z += DeltaZ;
UV.U += DeltaU;
UV.V += DeltaV;
LODa += DeltaLOD;
LODb = 1.0f - LODa;
// Interpolate based on LOD.
pV2->UV3.U = (pV2->UV3.U * LODa) + (UV.U * LODb);
pV2->UV3.V = (pV2->UV3.V * LODa) + (UV.V * LODb);
pV2->XYZ.z = (pV2->XYZ.z * LODa) + (Z * LODb);
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Compute complete interpolated low detail data.
Z += DeltaZ;
UV.U += DeltaU;
UV.V += DeltaV;
LODa += DeltaLOD;
LODb = 1.0f - LODa;
// Interpolate based on LOD.
pV3->UV3.U = (pV3->UV3.U * LODa) + (UV.U * LODb);
pV3->UV3.V = (pV3->UV3.V * LODa) + (UV.V * LODb);
pV3->XYZ.z = (pV3->XYZ.z * LODa) + (Z * LODb);
//--------------------------------------------------------------------------
}
//==============================================================================
void fluid::InterpolateVert( vertex* pV0,
vertex* pV1,
vertex* pV2,
f32 LODa )
{
f32 Z = (pV2->XYZ.z + pV0->XYZ.z) * 0.5f;
f32 U = (pV2->UV3.U + pV0->UV3.U) * 0.5f;
f32 V = (pV2->UV3.V + pV0->UV3.V) * 0.5f;
f32 LODb = 1.0f - LODa;
//--------------------------------------------------------------------------
// Interpolate based on LOD.
pV1->UV3.U = (pV1->UV3.U * LODa) + (U * LODb);
pV1->UV3.V = (pV1->UV3.V * LODa) + (V * LODb);
pV1->XYZ.z = (pV1->XYZ.z * LODa) + (Z * LODb);
//--------------------------------------------------------------------------
}
//==============================================================================