//----------------------------------------------------------------------------- // 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< 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); //-------------------------------------------------------------------------- } //==============================================================================