679 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			679 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
//-----------------------------------------------------------------------------
 | 
						|
// Torque Game Engine
 | 
						|
// Copyright (C) GarageGames.com, Inc.
 | 
						|
//-----------------------------------------------------------------------------
 | 
						|
 | 
						|
#include "terrain/fluid.h"
 | 
						|
#include "dgl/dgl.h"
 | 
						|
#include "console/simBase.h"
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
//  VARIABLES
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
s32 fluid::m_Instances = 0;
 | 
						|
s32 fluid::m_MaskOffset[6] = { 0, 1, 2, 4, 12, 44 };
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
//  FUNCTIONS
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
fluid::fluid( void )
 | 
						|
{
 | 
						|
    m_Instances     +=  1;
 | 
						|
 | 
						|
    // Fill out fields with a stable, if useless, state.
 | 
						|
    m_SquareX0        =  0;
 | 
						|
    m_SquareY0        =  0;
 | 
						|
    m_SquaresInX      =  4;
 | 
						|
    m_SquaresInY      =  4;
 | 
						|
    m_BlocksInX       =  1;
 | 
						|
    m_BlocksInY       =  1;
 | 
						|
    m_HighResMode     =  1;
 | 
						|
    m_RemoveWetEdges  =  0;
 | 
						|
    m_SurfaceZ        = 0.0f;
 | 
						|
    m_WaveAmplitude   = 0.0f;
 | 
						|
    m_Opacity         = 0.0f;
 | 
						|
    m_EnvMapIntensity = 1.0f;
 | 
						|
    m_BaseSeconds     = SECONDS;
 | 
						|
    m_pTerrain        = NULL;
 | 
						|
 | 
						|
    // Set values on the debug flags.
 | 
						|
    m_ShowWire        = 0;
 | 
						|
    m_ShowBlocks      = 0;
 | 
						|
    m_ShowNodes       = 0;
 | 
						|
    m_ShowBaseA       = 1;
 | 
						|
    m_ShowBaseB       = 1;
 | 
						|
    m_ShowLightMap    = 1;
 | 
						|
    m_ShowEnvMap      = 1;
 | 
						|
    m_ShowFog         = 1;
 | 
						|
 | 
						|
   m_TerrainSize = 2048;
 | 
						|
   m_TerrainBlockSize = 8;
 | 
						|
   m_TerrainBlockShift = 3;
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
fluid::~fluid()
 | 
						|
{
 | 
						|
    m_Instances -= 1;
 | 
						|
 | 
						|
    if( m_Instances == 0 )
 | 
						|
    {
 | 
						|
        ReleaseVertexMemory();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
s32 fluid::GetRejectBit( s32 Level, s32 IndexX, s32 IndexY ) const
 | 
						|
{
 | 
						|
    s32  BitNumber  = (IndexY << Level) + IndexX;
 | 
						|
    s32  ByteNumber = m_MaskOffset[Level] + (BitNumber >> 3);
 | 
						|
    byte Byte       = m_RejectMask[ ByteNumber ];
 | 
						|
    s32  Bit        = (Byte >> (BitNumber & 0x07)) & 0x01;
 | 
						|
 | 
						|
    return( Bit );
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
s32 fluid::GetAcceptBit( s32 Level, s32 IndexX, s32 IndexY ) const
 | 
						|
{
 | 
						|
    if( Level == 5 )
 | 
						|
        return( !GetRejectBit( 5, IndexX, IndexY ) );
 | 
						|
 | 
						|
    s32  BitNumber  = (IndexY << Level) + IndexX;
 | 
						|
    s32  ByteNumber = m_MaskOffset[Level] + (BitNumber >> 3);
 | 
						|
    byte Byte       = m_AcceptMask[ ByteNumber ];
 | 
						|
    s32  Bit        = (Byte >> (BitNumber & 0x07)) & 0x01;
 | 
						|
 | 
						|
    return( Bit );
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::SetRejectBit( s32 Level, s32 IndexX, s32 IndexY, s32 Value )
 | 
						|
{
 | 
						|
    s32  BitNumber  = (IndexY << Level) + IndexX;
 | 
						|
    s32  ByteNumber = m_MaskOffset[Level] + (BitNumber >> 3);
 | 
						|
    byte Byte       = 1 << (BitNumber & 0x07);
 | 
						|
 | 
						|
    if( Value )     m_RejectMask[ ByteNumber ] |=  Byte;
 | 
						|
    else            m_RejectMask[ ByteNumber ] &= ~Byte;
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::SetAcceptBit( s32 Level, s32 IndexX, s32 IndexY, s32 Value )
 | 
						|
{
 | 
						|
    if( Level == 5 )
 | 
						|
        SetRejectBit( 5, IndexX, IndexY, !Value );
 | 
						|
 | 
						|
    s32  BitNumber  = (IndexY << Level) + IndexX;
 | 
						|
    s32  ByteNumber = m_MaskOffset[Level] + (BitNumber >> 3);
 | 
						|
    byte Byte       = 1 << (BitNumber & 0x07);
 | 
						|
 | 
						|
    if( Value )     m_AcceptMask[ ByteNumber ] |=  Byte;
 | 
						|
    else            m_AcceptMask[ ByteNumber ] &= ~Byte;
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::BuildLowerMasks( void )
 | 
						|
{
 | 
						|
    s32 X, Y;
 | 
						|
 | 
						|
    // Initially, at all non-level-5 mask levels, we want to both accept and
 | 
						|
    // reject everything.  Then, we'll go back and correct it all.
 | 
						|
 | 
						|
    MEMSET( m_AcceptMask, 0xFF, 1+1+2+8+32 );
 | 
						|
    MEMSET( m_RejectMask, 0xFF, 1+1+2+8+32 );
 | 
						|
 | 
						|
    // Now, for each entry in the level 5 mask, push its implications down
 | 
						|
    // through all the other levels.
 | 
						|
 | 
						|
    for( Y = 0; Y < 32; Y++ )
 | 
						|
    for( X = 0; X < 32; X++ )
 | 
						|
    {
 | 
						|
        if( GetRejectBit( 5, X, Y ) )
 | 
						|
        {
 | 
						|
            // The block is set for reject.
 | 
						|
            // We cannot accept it on the lower levels.
 | 
						|
            SetAcceptBit( 4, X>>1, Y>>1, 0 );
 | 
						|
            SetAcceptBit( 3, X>>2, Y>>2, 0 );
 | 
						|
            SetAcceptBit( 2, X>>3, Y>>3, 0 );
 | 
						|
            SetAcceptBit( 1, X>>4, Y>>4, 0 );
 | 
						|
            SetAcceptBit( 0, X>>5, Y>>5, 0 );
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            // The block is set for accept.
 | 
						|
            // We cannot reject it on the lower levels.
 | 
						|
            SetRejectBit( 4, X>>1, Y>>1, 0 );
 | 
						|
            SetRejectBit( 3, X>>2, Y>>2, 0 );
 | 
						|
            SetRejectBit( 2, X>>3, Y>>3, 0 );
 | 
						|
            SetRejectBit( 1, X>>4, Y>>4, 0 );
 | 
						|
            SetRejectBit( 0, X>>5, Y>>5, 0 );
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
struct fill_segment
 | 
						|
{
 | 
						|
    s32 Y;
 | 
						|
    s32 X0, X1;
 | 
						|
    s32 DY;             // +1 or -1
 | 
						|
};
 | 
						|
 | 
						|
#define STACK_SIZE  50
 | 
						|
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
#define PUSH(y,x0,x1,dy)                                \
 | 
						|
{                                                       \
 | 
						|
    if( ((y+(dy)) >= 0) && ((y+(dy)) < SizeY) )         \
 | 
						|
    {                                                   \
 | 
						|
        if( Count < STACK_SIZE )                        \
 | 
						|
        {                                               \
 | 
						|
            Stack[Count].Y  = y;                        \
 | 
						|
            Stack[Count].X0 = x0;                       \
 | 
						|
            Stack[Count].X1 = x1;                       \
 | 
						|
            Stack[Count].DY = dy;                       \
 | 
						|
            Count++;                                    \
 | 
						|
        }                                               \
 | 
						|
        else                                            \
 | 
						|
        {                                               \
 | 
						|
            FloodFill( pGrid, x0, y, SizeX, SizeY );    \
 | 
						|
        }                                               \
 | 
						|
    }                                                   \
 | 
						|
}
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
#define POP(y,x0,x1,dy)                                 \
 | 
						|
{                                                       \
 | 
						|
    Count--;                                            \
 | 
						|
    Y  = Stack[Count].Y + Stack[Count].DY;              \
 | 
						|
    X0 = Stack[Count].X0;                               \
 | 
						|
    X1 = Stack[Count].X1;                               \
 | 
						|
    DY = Stack[Count].DY;                               \
 | 
						|
}
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
void fluid::FloodFill( u8* pGrid, s32 x, s32 y, s32 SizeX, s32 SizeY )
 | 
						|
{
 | 
						|
    fill_segment Stack[ STACK_SIZE ];
 | 
						|
    s32          Count = 0;
 | 
						|
    s32          X, Y, X0, X1, DY, Left;
 | 
						|
    u8*          p;
 | 
						|
 | 
						|
    if( !pGrid[ (y*SizeX) + x ] )
 | 
						|
        return;
 | 
						|
 | 
						|
    PUSH( y,   x, x,  1 );  // Needed in a few cases.
 | 
						|
    PUSH( y+1, x, x, -1 );  // Primary seed point.  Popped first.
 | 
						|
 | 
						|
    while( Count > 0 )
 | 
						|
    {
 | 
						|
        POP( Y, X0, X1, DY );
 | 
						|
 | 
						|
        // A span in y=(Y-DY) for X0<=x<=X1 was previously filled.  Now consider
 | 
						|
        // adjacent entries in y=Y.
 | 
						|
 | 
						|
        // Clear going towards decreasing X.
 | 
						|
        X = X0;
 | 
						|
        p = &pGrid[ (Y*SizeX) + X ];
 | 
						|
        while( (X >= 0) && *p )
 | 
						|
        {
 | 
						|
            *p = 0;
 | 
						|
            X--;
 | 
						|
            p--;
 | 
						|
        }
 | 
						|
 | 
						|
        if( X >= X0 )
 | 
						|
            goto Skip;
 | 
						|
 | 
						|
        Left = X + 1;
 | 
						|
        if( Left < X0 )
 | 
						|
            PUSH( Y, Left, X0-1, -DY )
 | 
						|
 | 
						|
        X = X0 + 1;
 | 
						|
 | 
						|
        do
 | 
						|
        {
 | 
						|
            // Clear going towards increasing X.
 | 
						|
            p = &pGrid[ (Y*SizeX) + X ];
 | 
						|
            while( (X < SizeX) && *p )
 | 
						|
            {
 | 
						|
                *p = 0;
 | 
						|
                X++;
 | 
						|
                p++;
 | 
						|
            }
 | 
						|
 | 
						|
            PUSH( Y, Left, X-1, DY );
 | 
						|
            if( X > X1+1 )
 | 
						|
                PUSH( Y, X1+1, X-1, -DY );
 | 
						|
 | 
						|
Skip:       X++;
 | 
						|
            p = &pGrid[ (Y*SizeX) + X ];
 | 
						|
            while( (X <= X1) && !(*p) )
 | 
						|
            {
 | 
						|
                X++;
 | 
						|
                p++;
 | 
						|
            }
 | 
						|
            Left = X;
 | 
						|
        }
 | 
						|
        while( X <= X1 );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::RebuildMasks( void )
 | 
						|
{
 | 
						|
    u8* pGrid;
 | 
						|
    u8* pG;         // Traveling grid pointer
 | 
						|
    s32 GridSize;
 | 
						|
    s32 X, Y;
 | 
						|
    s32 x, y;
 | 
						|
    s32 i;          // Index
 | 
						|
 | 
						|
    s32 SquaresPerBlock = m_HighResMode ? 4 : 8;
 | 
						|
    s32 ShiftPerBlock   = m_HighResMode ? 2 : 3;
 | 
						|
 | 
						|
    //
 | 
						|
    // We need a grid to classify all terrain data points which are within the
 | 
						|
    // fluid area.  We will use this grid to reject underground blocks, reject
 | 
						|
    // "wet" edges if requested, and to dry fill where requested.
 | 
						|
    //
 | 
						|
 | 
						|
    GridSize = (m_SquaresInX+1) * (m_SquaresInY+1);
 | 
						|
    pGrid    = (u8*)MALLOC( GridSize );
 | 
						|
 | 
						|
    // Default to allow all waterblocks in case we don't have a terrain object
 | 
						|
    dMemset(pGrid, 1, GridSize);
 | 
						|
 | 
						|
    // Classify each point as above or below ground.
 | 
						|
 | 
						|
    if( m_pTerrain )
 | 
						|
    {
 | 
						|
        u16 FluidLevel = (u16)((m_SurfaceZ + (m_WaveAmplitude/2.0f)) * 32.0f);
 | 
						|
 | 
						|
        pG = pGrid;
 | 
						|
        for( Y = 0; Y < m_SquaresInY+1; Y++ )
 | 
						|
        for( X = 0; X < m_SquaresInX+1; X++ )
 | 
						|
        {
 | 
						|
            i = (((m_SquareY0+Y) & 255) << 8) + ((m_SquareX0+X) & 255);
 | 
						|
            *pG = (u8)(FluidLevel > m_pTerrain[i]);
 | 
						|
            pG++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // If requested, "dry up" all edges which "protrude" into the air.
 | 
						|
 | 
						|
    if( m_RemoveWetEdges && m_pTerrain )
 | 
						|
    {
 | 
						|
        for( X = 0; X < m_SquaresInX+1; X++ )
 | 
						|
        {
 | 
						|
            FloodFill( pGrid, X, 0           , m_SquaresInX+1, m_SquaresInY+1 );
 | 
						|
            FloodFill( pGrid, X, m_SquaresInY, m_SquaresInX+1, m_SquaresInY+1 );
 | 
						|
        }
 | 
						|
 | 
						|
        for( Y = 0; Y < m_SquaresInY+1; Y++ )
 | 
						|
        {
 | 
						|
            FloodFill( pGrid, 0           , Y, m_SquaresInX+1, m_SquaresInY+1 );
 | 
						|
            FloodFill( pGrid, m_SquaresInX, Y, m_SquaresInX+1, m_SquaresInY+1 );
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Time to build the masks.  First, reject everything!  We will work on the
 | 
						|
    // level 5 reject mask.  (Level 5 is the most detailed mask, and there is no
 | 
						|
    // accept mask at that level.)
 | 
						|
 | 
						|
    MEMSET( m_RejectMask + m_MaskOffset[5], 0xFF, 128 );
 | 
						|
 | 
						|
    // Any block which as useful points left in the grid is to be kept.
 | 
						|
 | 
						|
    for( Y = 0; Y < m_BlocksInY; Y++ )
 | 
						|
    for( X = 0; X < m_BlocksInX; X++ )
 | 
						|
    {
 | 
						|
        s32 Accept = 0;
 | 
						|
 | 
						|
        // If ANY point in the block is acceptable, then accept the whole block.
 | 
						|
 | 
						|
        for( y = 0; y <= SquaresPerBlock; y++ )
 | 
						|
        for( x = 0; x <= SquaresPerBlock; x++ )
 | 
						|
        {
 | 
						|
            s32 GridX = (X << ShiftPerBlock) + x;
 | 
						|
            s32 GridY = (Y << ShiftPerBlock) + y;
 | 
						|
 | 
						|
            i = (GridY * (m_SquaresInX+1)) + GridX;
 | 
						|
 | 
						|
            if( pGrid[i] )
 | 
						|
            {
 | 
						|
                Accept = 1;
 | 
						|
                goto BailOut;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        BailOut:
 | 
						|
 | 
						|
        if( Accept )
 | 
						|
            SetRejectBit( 5, X, Y, 0 );
 | 
						|
    }
 | 
						|
 | 
						|
    FREE( pGrid );
 | 
						|
    BuildLowerMasks();
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
S32 getPower(S32 x)
 | 
						|
{
 | 
						|
    // Returns 2^n (the highest bit).
 | 
						|
    S32 i = 0;
 | 
						|
    if (x)
 | 
						|
        do
 | 
						|
            i++;
 | 
						|
        while (x >>= 1);
 | 
						|
    return i;
 | 
						|
}
 | 
						|
 | 
						|
void fluid::SetInfo( f32& X0,
 | 
						|
                     f32& Y0,
 | 
						|
                     f32& SizeX,
 | 
						|
                     f32& SizeY,
 | 
						|
                     f32  SurfaceZ,
 | 
						|
                     f32  WaveAmplitude,
 | 
						|
                     f32& Opacity,
 | 
						|
                     f32& EnvMapIntensity,
 | 
						|
                     s32  RemoveWetEdges,
 | 
						|
					 bool UseDepthMap,
 | 
						|
					 f32  TessellationSurface,
 | 
						|
					 f32  TessellationShore,
 | 
						|
					 f32  SurfaceParallax,
 | 
						|
					 f32  FlowAngle,
 | 
						|
					 f32  FlowRate,
 | 
						|
				     f32  mDistortGridScale,
 | 
						|
				     f32  mDistortMagnitude,
 | 
						|
				     f32  mDistortTime,
 | 
						|
                 ColorF SpecColor,
 | 
						|
                 F32 SpecPower,
 | 
						|
                 bool tiling,
 | 
						|
				 u32 terrainSize,
 | 
						|
                 u32 terrainBlockSize)	// MM: Added Various Parameters.
 | 
						|
{
 | 
						|
   m_TerrainSize = terrainSize;
 | 
						|
   m_TerrainBlockSize = terrainBlockSize;
 | 
						|
   m_TerrainBlockShift = getPower(terrainBlockSize-1);
 | 
						|
 | 
						|
   m_SpecColor = SpecColor;
 | 
						|
   m_SpecPower = SpecPower;
 | 
						|
 | 
						|
	// MM: Calculate Depth-map Texel X/Y.
 | 
						|
	m_DepthTexelX = 1.0f / SizeX;
 | 
						|
	m_DepthTexelY = 1.0f / SizeY;
 | 
						|
 | 
						|
	// MM: Added Depth-Map Toggle.
 | 
						|
	m_UseDepthMap = UseDepthMap;
 | 
						|
 | 
						|
	// MM: Tessellations.
 | 
						|
	m_TessellationSurface = TessellationSurface;
 | 
						|
	m_TessellationShore = TessellationShore;
 | 
						|
 | 
						|
	// MM: Surface Parallax.
 | 
						|
	m_SurfaceParallax = SurfaceParallax;
 | 
						|
 | 
						|
	// MM: Flow Control.
 | 
						|
	m_FlowAngle			= FlowAngle;
 | 
						|
	m_FlowRate			= FlowRate;
 | 
						|
	m_FlowMagnitudeS	=
 | 
						|
	m_FlowMagnitudeT	= 0.0f;
 | 
						|
 | 
						|
	// MM: Surface Disturbance.  RemoveMe!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
	m_DistortGridScale	= mDistortGridScale;
 | 
						|
	m_DistortMagnitude	= mDistortMagnitude;
 | 
						|
	m_DistortTime		= mDistortTime;
 | 
						|
 | 
						|
	// MM: Put in neater constraints.
 | 
						|
	m_EnvMapIntensity = mClampF(EnvMapIntensity, 0.0f, 1.0f);
 | 
						|
 | 
						|
	// MM: Removed Section.
 | 
						|
/*
 | 
						|
    // Constrain the range of parameters.
 | 
						|
    if( Opacity         > 1.0f )    Opacity         = 1.0f;
 | 
						|
    if( Opacity         < 0.0f )    Opacity         = 0.0f;
 | 
						|
    if( EnvMapIntensity > 1.0f )    EnvMapIntensity = 1.0f;
 | 
						|
    if( EnvMapIntensity < 0.0f )    EnvMapIntensity = 0.0f;
 | 
						|
*/
 | 
						|
    // Get the easy stuff first.
 | 
						|
    m_SurfaceZ        = SurfaceZ;
 | 
						|
    m_WaveAmplitude   = WaveAmplitude;
 | 
						|
    m_RemoveWetEdges  = RemoveWetEdges;
 | 
						|
    m_Opacity         = mClampF(Opacity,0.0f,1.0f);	// MM: Put in neater constraints.
 | 
						|
    m_EnvMapIntensity = EnvMapIntensity;
 | 
						|
 | 
						|
    m_WaveFactor     = m_WaveAmplitude * 0.25f;
 | 
						|
 | 
						|
    // Place the "min" corner.
 | 
						|
    m_SquareX0 = (s32)((X0 / F32(m_TerrainBlockSize)) + 0.5f);
 | 
						|
    m_SquareY0 = (s32)((Y0 / F32(m_TerrainBlockSize)) + 0.5f);
 | 
						|
 | 
						|
    // Constrain the range of values.
 | 
						|
    if( m_SquareX0 <    0.0f )  m_SquareX0 =    0;
 | 
						|
    if( m_SquareY0 <    0.0f )  m_SquareY0 =    0;
 | 
						|
 | 
						|
	F32 max = terrainSize - terrainBlockSize;
 | 
						|
    if( m_SquareX0 > max )  m_SquareX0 = max;
 | 
						|
    if( m_SquareY0 > max )  m_SquareY0 = max;
 | 
						|
 | 
						|
    // Decide on the size of the block.
 | 
						|
    m_SquaresInX = (s32)((SizeX / F32(m_TerrainBlockSize)) + 0.5f);
 | 
						|
    m_SquaresInY = (s32)((SizeY / F32(m_TerrainBlockSize)) + 0.5f);
 | 
						|
 | 
						|
    //
 | 
						|
    // If the fluid is meant to cover less than 1/4 of a terrain rep, then we
 | 
						|
    // will enter "High Resolution Mode".  The fluid will cover the same area
 | 
						|
    // as specified, but it will have twice the vertex resolution in each
 | 
						|
    // direction.  So, on "small lakes", we get better memory utilization,
 | 
						|
    // better terrain fitting, and so on.
 | 
						|
    //
 | 
						|
 | 
						|
    if( (m_SquaresInX <= 128) && (m_SquaresInY <= 128) )
 | 
						|
    {
 | 
						|
        // High Resolution Mode!
 | 
						|
        m_HighResMode = 1;
 | 
						|
 | 
						|
        // A Block is now 4x4 terrain squares.  And the number of squares in
 | 
						|
        // the fluid must be a multiple of 4 so we get whole blocks.
 | 
						|
 | 
						|
        m_SquaresInX = (m_SquaresInX + 3) & ~0x03;
 | 
						|
        m_SquaresInY = (m_SquaresInY + 3) & ~0x03;
 | 
						|
 | 
						|
        // Constrain the range of values.
 | 
						|
        if( m_SquaresInX <=   0 )   m_SquaresInX =   4;
 | 
						|
        if( m_SquaresInY <=   0 )   m_SquaresInY =   4;
 | 
						|
 | 
						|
        m_BlocksInX = m_SquaresInX >> 2;
 | 
						|
        m_BlocksInY = m_SquaresInY >> 2;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Normal resolution.
 | 
						|
        m_HighResMode = 0;
 | 
						|
 | 
						|
        // A Block is now 8x8 terrain squares.  And the number of squares in
 | 
						|
        // the fluid must be a multiple of 8 so we get whole blocks.
 | 
						|
 | 
						|
        m_SquaresInX = (m_SquaresInX + 7) & ~0x07;
 | 
						|
        m_SquaresInY = (m_SquaresInY + 7) & ~0x07;
 | 
						|
 | 
						|
        // Constrain the range of values.
 | 
						|
        if( m_SquaresInX >  256 )   m_SquaresInX = 256;
 | 
						|
        if( m_SquaresInY >  256 )   m_SquaresInY = 256;
 | 
						|
        if( m_SquaresInX <=   0 )   m_SquaresInX =   8;
 | 
						|
        if( m_SquaresInY <=   0 )   m_SquaresInY =   8;
 | 
						|
 | 
						|
        m_BlocksInX  = m_SquaresInX >> 3;
 | 
						|
        m_BlocksInY  = m_SquaresInY >> 3;
 | 
						|
    }
 | 
						|
 | 
						|
    // Set some internal values for later usage.
 | 
						|
	for(U32 i=0; i<5; i++)
 | 
						|
	{
 | 
						|
		if(m_HighResMode)
 | 
						|
			m_Step[i] = i * m_TerrainBlockSize;
 | 
						|
		else
 | 
						|
			m_Step[i] = i * m_TerrainBlockSize * 2;
 | 
						|
	}
 | 
						|
 | 
						|
    // Set values back into parameters for caller.
 | 
						|
    X0    = m_SquareX0   * F32(m_TerrainBlockSize);
 | 
						|
    Y0    = m_SquareY0   * F32(m_TerrainBlockSize);
 | 
						|
    SizeX = m_SquaresInX * F32(m_TerrainBlockSize);
 | 
						|
    SizeY = m_SquaresInY * F32(m_TerrainBlockSize);
 | 
						|
 | 
						|
    // Recompute our masks.
 | 
						|
    RebuildMasks();
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::SetTerrainData( u16* pTerrainData )
 | 
						|
{
 | 
						|
    m_pTerrain = pTerrainData;
 | 
						|
    RebuildMasks();
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
// Frustrum clip planes: 0=T 1=B 2=L 3=R 4=N 5=F
 | 
						|
 | 
						|
void fluid::SetFrustrumPlanes( f32* pFrustrumPlanes )
 | 
						|
{
 | 
						|
    f32 BackOff = m_WaveAmplitude * 0.5f;
 | 
						|
 | 
						|
    m_Plane[0].A = pFrustrumPlanes[ 0];
 | 
						|
    m_Plane[0].B = pFrustrumPlanes[ 1];
 | 
						|
    m_Plane[0].C = pFrustrumPlanes[ 2];
 | 
						|
    m_Plane[0].D = pFrustrumPlanes[ 3] + BackOff;
 | 
						|
 | 
						|
    m_Plane[1].A = pFrustrumPlanes[ 4];
 | 
						|
    m_Plane[1].B = pFrustrumPlanes[ 5];
 | 
						|
    m_Plane[1].C = pFrustrumPlanes[ 6];
 | 
						|
    m_Plane[1].D = pFrustrumPlanes[ 7] + BackOff;
 | 
						|
 | 
						|
    m_Plane[2].A = pFrustrumPlanes[ 8];
 | 
						|
    m_Plane[2].B = pFrustrumPlanes[ 9];
 | 
						|
    m_Plane[2].C = pFrustrumPlanes[10];
 | 
						|
    m_Plane[2].D = pFrustrumPlanes[11] + BackOff;
 | 
						|
 | 
						|
    m_Plane[3].A = pFrustrumPlanes[12];
 | 
						|
    m_Plane[3].B = pFrustrumPlanes[13];
 | 
						|
    m_Plane[3].C = pFrustrumPlanes[14];
 | 
						|
    m_Plane[3].D = pFrustrumPlanes[15] + BackOff;
 | 
						|
 | 
						|
    m_Plane[4].A = pFrustrumPlanes[16];
 | 
						|
    m_Plane[4].B = pFrustrumPlanes[17];
 | 
						|
    m_Plane[4].C = pFrustrumPlanes[18];
 | 
						|
    m_Plane[4].D = pFrustrumPlanes[19] + BackOff;
 | 
						|
 | 
						|
    m_Plane[5].A = pFrustrumPlanes[20];
 | 
						|
    m_Plane[5].B = pFrustrumPlanes[21];
 | 
						|
    m_Plane[5].C = pFrustrumPlanes[22];
 | 
						|
    m_Plane[5].D = pFrustrumPlanes[23];
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::SetTextures( TextureHandle Base,
 | 
						|
                         TextureHandle EnvMapOverTexture,
 | 
						|
                         TextureHandle EnvMapUnderTexture,
 | 
						|
						 TextureHandle ShoreTexture,
 | 
						|
						 TextureHandle DepthTexture,
 | 
						|
						 TextureHandle ShoreDepthTexture,
 | 
						|
                   TextureHandle SpecMaskTexture )	// MM: Added Various Textures.
 | 
						|
{
 | 
						|
    m_BaseTexture			= Base;
 | 
						|
    m_EnvMapOverTexture		= EnvMapOverTexture;	// MM: Added Over/Under Env Texture Support.
 | 
						|
    m_EnvMapUnderTexture	= EnvMapUnderTexture;	// MM: Added Over/Under Env Texture Support.
 | 
						|
	m_ShoreTexture			= ShoreTexture;			// MM: Added Shore Texture.
 | 
						|
	m_DepthTexture			= DepthTexture;			// MM: Added Depth-Map Texture.
 | 
						|
	m_ShoreDepthTexture		= ShoreDepthTexture;	// MM: Added Depth-Map Texture.
 | 
						|
   m_SpecMaskTex = SpecMaskTexture;
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::SetLightMapTexture( TextureHandle LightMapTexture )
 | 
						|
{
 | 
						|
    m_LightMapTexture = LightMapTexture;
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::SetFogParameters( f32 R, f32 G, f32 B, f32 VisibleDistance )
 | 
						|
{
 | 
						|
    m_FogColor.R      = R;
 | 
						|
    m_FogColor.G      = G;
 | 
						|
    m_FogColor.B      = B;
 | 
						|
    m_FogColor.A      = 1.0f;
 | 
						|
    m_VisibleDistance = VisibleDistance;
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
void fluid::SetFogFn( compute_fog_fn* pFogFn )
 | 
						|
{
 | 
						|
    m_pFogFn = pFogFn;
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 | 
						|
 | 
						|
s32 fluid::IsFluidAtXY( f32 X, f32 Y ) const
 | 
						|
{
 | 
						|
    s32 x, y;
 | 
						|
    s32 ShiftPerBlock = m_HighResMode ? 5 : 6;
 | 
						|
 | 
						|
    //
 | 
						|
    // Convert fluid space (X,Y) to block (x,y).  Use the accept mask.  Note
 | 
						|
    // that the masks are anchored at the min point rather than terrain (0,0).
 | 
						|
    //
 | 
						|
 | 
						|
    // Convert to integer.
 | 
						|
	F32 scale = 8.0f / F32(m_TerrainBlockSize);
 | 
						|
    x = (s32)(X * scale);
 | 
						|
    y = (s32)(Y * scale);
 | 
						|
 | 
						|
    // Compensate for min point offset.
 | 
						|
    x -= (m_SquareX0 * F32(m_TerrainBlockSize) * scale);
 | 
						|
    y -= (m_SquareY0 * F32(m_TerrainBlockSize) * scale);
 | 
						|
 | 
						|
    // If we're outside the range and not tiling, ignore it.
 | 
						|
    if(!mTile)
 | 
						|
    {
 | 
						|
      if(x < 0 || x > 2047)
 | 
						|
         return false;
 | 
						|
      if(y < 0 || y > 2047)
 | 
						|
         return false;
 | 
						|
    }
 | 
						|
 | 
						|
    // We only want points in the range [0,2048).
 | 
						|
    x &= 2047;
 | 
						|
    y &= 2047;
 | 
						|
 | 
						|
    // Convert to block coordinate.
 | 
						|
    x >>= ShiftPerBlock;
 | 
						|
    y >>= ShiftPerBlock;
 | 
						|
 | 
						|
    // When we are in high res mode, there are "virtually" 64 blocks per terrain
 | 
						|
    // along a particular axis.  But only the first 32 of them are used.
 | 
						|
    if( x >= 32 )   return( 0 );
 | 
						|
    if( y >= 32 )   return( 0 );
 | 
						|
 | 
						|
    // Consult mask.
 | 
						|
    return( GetAcceptBit( 5, x, y ) );
 | 
						|
}
 | 
						|
 | 
						|
//==============================================================================
 |