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