Files
GTASource/game/renderer/WaterUpdateDynamicCommon.h

1138 lines
34 KiB
C
Raw Normal View History

2025-02-23 17:40:52 +08:00
//
// WaterUpdateDynamicCommon.h (parts taken from Water.cpp):
//
// This header contains all the functions and data required by Water::UpdateDynamicWater()
// which are shared among __SPU and other build targets
// (in other words: this code is included and compiled for __SPU target).
//
// Note: Don't add any game/system specific #includes (probably they will not compile on __SPU anyway);
//
//
// 2007/03/14 - Andrzej: - initial;
//
//
//
//
#ifndef __WATERUPDATEDYNAMICCOMMON_H__
#define __WATERUPDATEDYNAMICCOMMON_H__
#include "math/vecMath.h"
#include "atl/array.h"
#include "waterdefines.h"
#include "control/replay/replay.h"
//
//
// All code & stuff below is shared by SPU and standard target builds:
//
#define __GPUUPDATE (__XENON || __WIN32PC || RSG_DURANGO || RSG_ORBIS)
#define __USEVERTEXSTREAMRENDERTARGETS (__PS3 && 1)
#define MAXNUMWATERQUADS (2048) // actual count for gta5 is currently 769
#define DYNAMICGRIDSIZE (2) // 2 meters apart
#define USE_OPTIMISATIONS 1
#if USE_OPTIMISATIONS
#define FORCE_INLINE_SPU __forceinline
#define UNROLL_LOOP 1
#if __SPU
#define USE_SI_CODE 1
#else
#define USE_SI_CODE 0
#endif
#else
#define FORCE_INLINE_SPU
#define UNROLL_LOOP 0
#define USE_SI_CODE 0
#endif
#if __SPU
#if DMA_HEIGHT_BUFFER_WITH_JOB
#define WATER_HEIGHT_DMA_TAG (0)
#define WATER_NOISE_BUFFER_DMA_TAG (1)
#define WATER_CALM_QUADS_DMA_TAG (2)
#define WATER_WAVE_BUFFER_AND_QUADS_DMA_TAG (3)
#else
#define WATER_HEIGHT_DMA_TAG (0)
#define WATER_NOISE_BUFFER_DMA_TAG (0)
#define WATER_CALM_QUADS_DMA_TAG (0)
#define WATER_WAVE_BUFFER_AND_QUADS_DMA_TAG (0)
#endif
#endif
#define VALIDATE_SI_CODE 0
static s32 m_nNumOfWaterQuads = 0;
static s32 m_nNumOfWaterCalmingQuads = 0;
static s32 m_nNumOfExtraWaterCalmingQuads = 0;
static s32 m_nNumOfWaveQuads = 0;
#if USE_SI_CODE
static const vec_char16 s_vMaskA = { 0x80, 0x80, 0x80, 0,
0x80, 0x80, 0x80, 16,
0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80 };
static const vec_char16 s_vMaskB = { 0x80, 0x80, 0x80, 3,
0x80, 0x80, 0x80, 7,
0x80, 0x80, 0x80, 19,
0x80, 0x80, 0x80, 23 };
#endif
#if __PS3 || __WIN32PC || RSG_DURANGO || RSG_ORBIS
#if __SPU
#define WAVE_NOISE_BUFFER_NUM_ROWS (DYNAMICGRIDELEMENTS)
#define WAVE_NOISE_BUFFER_SIZE (DYNAMICGRIDELEMENTS*sizeof(u8))
#define WAVE_NOISE_BUFFER_ALIGN (128)
static u8 *m_WaveNoiseBuffer[WAVETEXTURERES]={NULL};
#else
static u8 m_WaveNoiseBuffer [WAVETEXTURERES][WAVETEXTURERES] ALIGNED(128);
#endif
struct CWaterUpdateDynamicParams
{
Vector3 m_CenterCoors; // camera pos
float m_timestep;
u32 m_timeInMilliseconds;
float m_disturbAmount;
// The following variables used to add a sinus wave to the speed.
float m_waveLengthScale;
float m_waveMult;
float m_ShoreWavesMult;
s32 m_waveTimePeriod;
float (*m_WaterHeightBuffer)[DYNAMICGRIDELEMENTS]; // double buffered read buffer for Render()
};
class CCPUTexture
{
Vec2V m_scale;
Vec2V m_bias;
ScalarV m_WidthV;
ScalarV m_MaxWidthV;
u8* m_tex;
Vec4V m_ActiveV;
Vec4V m_offsetsX;
Vec4V m_offsetsY;
Vec4V m_empty;
public:
static const int PowWidth = 7;
static const int Width = 1 << PowWidth;
static const int NumBytes = Width * Width;
CCPUTexture( unsigned char* tex = 0 )
{
m_WidthV = ScalarVFromF32((float)Width);
m_MaxWidthV = ScalarVFromF32((float)Width - 1.0f);
m_scale = Vec2V( V_ONE);
m_bias = Vec2V( V_ZERO);
float TexelSize = 1.0f/((float)Width - 1.0f);
m_offsetsX = Vec4V( 0.0f,TexelSize , 0.0f, TexelSize);
m_offsetsY = Vec4V( 0.0f,0.0f , TexelSize, TexelSize);
Set(tex);
}
void Set( unsigned char* tex)
{
m_tex = tex;
m_ActiveV = tex ? Vec4V(V_MASKXYZW) : Vec4V(V_ZERO);
if (!tex) // point to black texture
{
tex =(u8*) &m_empty;
m_empty= Vec4V( V_ONE);
}
}
void SetTopDown( Vec2V_In scale, Vec2V_In bias)
{
m_scale = scale;
m_bias = bias;
}
int GetTiled( int i ) const
{
#if __XENON
int y = i >> 7;
int x = i - (y <<7);
return XGAddress2DTiledOffset<128,1>( x, y);
#else
return i;
#endif
}
FORCE_INLINE_SPU Vec4V_Out Fetch4( Vec4V_In u, Vec4V_In v ) const
{
Vec4V tu = u - RoundToNearestIntNegInf(u); // wrap operation
Vec4V tv = v - RoundToNearestIntNegInf(v);
Vec4V itu = RoundToNearestIntZero(tu * m_MaxWidthV); // convert to int between 0 - 128
Vec4V itv = RoundToNearestIntZero(tv * m_MaxWidthV);
Vec4V idxF =itu;
idxF += itv * m_WidthV; // calculate address
idxF = idxF & m_ActiveV; // clear to
// convert to blocksize
Vec4V idxV = FloatToIntRaw<0>(idxF); // convert address to int
// add m_tex
#if !USE_SI_CODE
int* idx = (int*)&idxV; // convert vector int to 4 ints
Assert( idx[0] >=0 && idx[0] < NumBytes);
Assert( idx[1] >=0 && idx[1] < NumBytes);
Assert( idx[2] >=0 && idx[2] < NumBytes);
Assert( idx[3] >=0 && idx[3] < NumBytes);
#if __WIN32PC || RSG_DURANGO || RSG_ORBIS
Vec4V r;
if (m_tex)
{
r = Vec4V( ScalarVFromU32(m_tex[GetTiled(idx[0])]),
ScalarVFromU32(m_tex[GetTiled(idx[1])]),
ScalarVFromU32(m_tex[GetTiled(idx[2])]),
ScalarVFromU32(m_tex[GetTiled(idx[3])]));
}
else
{
r = Vec4V(V_ZERO);
}
#else
Vec4V r = Vec4V(ScalarVFromU32(m_tex[GetTiled(idx[0])]),
ScalarVFromU32(m_tex[GetTiled(idx[1])]),
ScalarVFromU32(m_tex[GetTiled(idx[2])]),
ScalarVFromU32(m_tex[GetTiled(idx[3])]));
#endif
#else
vec_char16 vIndices = (vec_char16)idxV.GetIntrin128();
vec_char16 address = (vec_char16)spu_splats((u32)&m_tex[0]);
address = si_a(address, vIndices);
vec_char16 lowBits = si_andi(address, 0xf);
vec_char16 vByte0 = si_lqd(address, 0);
vByte0 = si_shlqby(vByte0, lowBits);
vec_char16 address1 = si_shlqbyi(address, 4);
vec_char16 lowBits1 = si_shlqbyi(lowBits, 4);
vec_char16 vByte1 = si_lqd(address1, 0);
vByte1 = si_shlqby(vByte1, lowBits1);
vec_char16 address2 = si_shlqbyi(address, 8);
vec_char16 lowBits2 = si_shlqbyi(lowBits, 8);
vec_char16 vByte2 = si_lqd(address2, 0);
vByte2 = si_shlqby(vByte2, lowBits2);
vec_char16 address3 = si_shlqbyi(address, 12);
vec_char16 lowBits3 = si_shlqbyi(lowBits, 12);
vec_char16 vByte3 = si_lqd(address3, 0);
vByte3 = si_shlqby(vByte3, lowBits3);
vec_char16 vByte01 = si_shufb(vByte0, vByte1, s_vMaskA);
vec_char16 vByte23 = si_shufb(vByte2, vByte3, s_vMaskA);
vec_char16 vByte0123 = si_shufb(vByte01, vByte23, s_vMaskB);
Vec4V r((vec_float4)vByte0123);
#if VALIDATE_SI_CODE
int* idx = (int*)&idxV;
int* vidx = (int*)&r;
Assert( m_tex[idx[0]] == vidx[0]);
Assert( m_tex[idx[1]] == vidx[1]);
Assert( m_tex[idx[2]] == vidx[2]);
Assert( m_tex[idx[3]] == vidx[3]);
#endif
#endif
return IntToFloatRaw<8>(r); // convert vector int 0..255 to float 0..1
}
FORCE_INLINE_SPU ScalarV_Out tex2D( Vec2V_In tuv ) const
{
Vec2V uv = tuv;
Vec4V u4 = Vec4V( uv.GetX() ) + m_offsetsX;
Vec4V v4 = Vec4V( uv.GetY() ) + m_offsetsY;
Vec4V r = Fetch4( u4, v4);
// calculate weights
uv = uv * m_MaxWidthV;
uv = uv - RoundToNearestIntZero(uv);
Vec2V invuv = Vec2V(V_ONE) - uv;
ScalarV u2 = uv.GetX();
ScalarV v2 = uv.GetY();
ScalarV iu2 = invuv.GetX();
ScalarV iv2 = invuv.GetY();
Vec4V weights = Vec4V(iu2 * iv2,
u2 * iv2,
iu2 * v2,
u2 * v2);
return Dot(r, weights);
}
};
#endif //__PS3 || __WIN32PC || RSG_DURANGO || RSG_ORBIS
#if __SPU
#define DYNAMIC_WATER_HEIGHT_NUM_ROWS (DYNAMICGRIDELEMENTS)
#define DYNAMIC_WATER_HEIGHT_SIZE (DYNAMICGRIDELEMENTS*sizeof(float))
#define DYNAMIC_WATER_HEIGHT_ALIGN (128)
static float *m_DynamicWater_Height [DYNAMICGRIDELEMENTS]={NULL}; // Water height as an offset to the height as defined by the artists (2 values for double buffering)
#define DYNAMIC_WATER_DHEIGHT_NUM_ROWS (DYNAMICGRIDELEMENTS)
#define DYNAMIC_WATER_DHEIGHT_SIZE (DYNAMICGRIDELEMENTS*sizeof(float))
#define DYNAMIC_WATER_DHEIGHT_ALIGN (128)
static float *m_DynamicWater_dHeight [DYNAMICGRIDELEMENTS]={NULL}; // Basically the speed of the water going up or down. (meter/sec)
#define MAXNUMWATERCALMINGQUADS (512)
#define MAXNUMWATERWAVEQUADS (512)
static CCalmingQuad m_aCalmingQuads [MAXNUMWATERCALMINGQUADS] ALIGNED(128);
static CWaveQuad m_aWaveQuads [MAXNUMWATERWAVEQUADS] ALIGNED(128);
#define WAVE_BUFFER_NUM_ROWS (DYNAMICGRIDELEMENTS)
#define WAVE_BUFFER_SIZE (DYNAMICGRIDELEMENTS*sizeof(u8))
#define WAVE_BUFFER_ALIGN (128)
static u8 *m_WaveBuffer [WAVETEXTURERES]={NULL};
#else
static float (*m_DynamicWater_Height)[DYNAMICGRIDELEMENTS]; // Water height as an offset to the height as defined by the artists (2 values for double buffering)
static float (*m_DynamicWater_dHeight)[DYNAMICGRIDELEMENTS]; // Basically the speed of the water going up or down. (meter/sec)
static CCalmingQuad (*m_aCalmingQuads);
#if __WIN32PC || __PPU || RSG_DURANGO || RSG_ORBIS
static CWaveQuad (*m_aWaveQuads);
static CCalmingQuad (*m_aExtraCalmingQuads);
#endif // __WIN32PC || __PPU || RSG_DURANGO || RSG_ORBIS
static u8 (*m_WaveBuffer)[WAVETEXTURERES];
#endif //__SPU
static s32 m_WorldBaseX, m_WorldBaseY;
static s32 m_GridBaseX, m_GridBaseY;
#if !__SPU
static mthRandom g_waterRand;
#endif
#define DYNAMICGRIDSIZE_POW2 1
inline u32 FindGridXFromWorldX(s32 WorldX, s32 WorldBaseX){ return (((WorldX - WorldBaseX)>>DYNAMICGRIDSIZE_POW2) + DYNAMICGRIDELEMENTS*1000)&(DYNAMICGRIDELEMENTS-1); }
inline u32 FindGridYFromWorldY(s32 WorldY, s32 WorldBaseY){ return (((WorldY - WorldBaseY)>>DYNAMICGRIDSIZE_POW2) + DYNAMICGRIDELEMENTS*1000)&(DYNAMICGRIDELEMENTS-1); }
inline u32 FindGridXFromWorldX(s32 WorldX){ return (((WorldX - m_WorldBaseX)>>DYNAMICGRIDSIZE_POW2) + DYNAMICGRIDELEMENTS*1000)&(DYNAMICGRIDELEMENTS-1); }
inline u32 FindGridYFromWorldY(s32 WorldY){ return (((WorldY - m_WorldBaseY)>>DYNAMICGRIDSIZE_POW2) + DYNAMICGRIDELEMENTS*1000)&(DYNAMICGRIDELEMENTS-1); }
/////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : ClearDynamicWater
// PURPOSE : Sets all of the dynamic water to 0.
//
/////////////////////////////////////////////////////////////////////////////////
static FORCE_INLINE_SPU void ClearDynamicWater()
{
#if USE_OPTIMISATIONS
const s32 inc = 4;
Vec4V zero = Vec4V(V_ZERO);
#else
const s32 inc = 1;
#endif
for (s32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
for (s32 Y = 0; Y < DYNAMICGRIDELEMENTS; Y += inc)
{
#if USE_OPTIMISATIONS
*(Vec4V*)&m_DynamicWater_Height[X][Y] = zero;
*(Vec4V*)&m_DynamicWater_dHeight[X][Y] = zero;
#else
m_DynamicWater_Height[X][Y] = 0.0f;
m_DynamicWater_dHeight[X][Y] = 0.0f;
#endif
}
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : ClearOneWaterStripX
// PURPOSE : Clears one strip that has just moved into our area.
//
/////////////////////////////////////////////////////////////////////////////////
static void FORCE_INLINE_SPU ClearOneWaterStripX(s32 worldY)
{
s32 Y = FindGridYFromWorldY(worldY);
for (s32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
m_DynamicWater_Height[X][Y] = 0.0f;
m_DynamicWater_dHeight[X][Y] = 0.0f;
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : ClearOneWaterStripY
// PURPOSE : Clears one strip that has just moved into our area.
//
/////////////////////////////////////////////////////////////////////////////////
void FORCE_INLINE_SPU ClearOneWaterStripY(s32 worldX)
{
#if USE_OPTIMISATIONS
const s32 inc = 4;
Vec4V zero = Vec4V(V_ZERO);
#else
const s32 inc = 1;
#endif
s32 X = FindGridXFromWorldX(worldX);
for (s32 Y = 0; Y < DYNAMICGRIDELEMENTS; Y += inc)
{
#if USE_OPTIMISATIONS
*(Vec4V*)&m_DynamicWater_Height[X][Y] = zero;
*(Vec4V*)&m_DynamicWater_dHeight[X][Y] = zero;
#else
m_DynamicWater_Height[X][Y] = 0.0f;
m_DynamicWater_dHeight[X][Y] = 0.0f;
#endif
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// PURPOSE : Grid shifting for PC.
//
/////////////////////////////////////////////////////////////////////////////////
static void ShiftGridsNegativeY(s32 columns)
{
for (s32 X=DYNAMICGRIDELEMENTS - 1; X >= columns; X--)
{
sysMemCpy( &m_DynamicWater_Height[X][0], &m_DynamicWater_Height[X - columns][0], sizeof(float)*DYNAMICGRIDELEMENTS);
sysMemCpy( &m_DynamicWater_dHeight[X][0], &m_DynamicWater_dHeight[X - columns][0], sizeof(float)*DYNAMICGRIDELEMENTS);
}
#if USE_OPTIMISATIONS
const s32 inc = 4;
Vec4V zero = Vec4V(V_ZERO);
#else
const s32 inc = 1;
#endif
for (s32 X = columns - 1; X >= 0; X--)
for (s32 Y = 0; Y < DYNAMICGRIDELEMENTS; Y += inc)
{
#if USE_OPTIMISATIONS
*(Vec4V*)&m_DynamicWater_Height[X][Y] = zero;
*(Vec4V*)&m_DynamicWater_dHeight[X][Y] = zero;
#else
m_DynamicWater_Height[X][Y] = 0.0f;
m_DynamicWater_dHeight[X][Y] = 0.0f;
#endif
}
}
static void ShiftGridsPositiveY(s32 columns)
{
for (s32 X=0; X<DYNAMICGRIDELEMENTS - columns; X++)
{
sysMemCpy( &m_DynamicWater_Height[X][0], &m_DynamicWater_Height[X + columns][0], sizeof(float)*DYNAMICGRIDELEMENTS);
sysMemCpy( &m_DynamicWater_dHeight[X][0], &m_DynamicWater_dHeight[X + columns][0], sizeof(float)*DYNAMICGRIDELEMENTS);
}
#if USE_OPTIMISATIONS
const s32 inc = 4;
Vec4V zero = Vec4V(V_ZERO);
#else
const s32 inc = 1;
#endif
for (s32 X = DYNAMICGRIDELEMENTS - columns; X < DYNAMICGRIDELEMENTS; X++)
for (s32 Y = 0; Y < DYNAMICGRIDELEMENTS; Y += inc)
{
#if USE_OPTIMISATIONS
*(Vec4V*)&m_DynamicWater_Height[X][Y] = zero;
*(Vec4V*)&m_DynamicWater_dHeight[X][Y] = zero;
#else
m_DynamicWater_Height[X][Y] = 0.0f;
m_DynamicWater_dHeight[X][Y] = 0.0f;
#endif
}
}
static void ShiftGridsNegativeX(s32 rows)
{
for (s32 Y=DYNAMICGRIDELEMENTS - 1; Y>=rows; Y--)
for (s32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
m_DynamicWater_Height[X][Y] = m_DynamicWater_Height[X][Y - rows];
m_DynamicWater_dHeight[X][Y] = m_DynamicWater_dHeight[X][Y - rows];
}
for (s32 Y = rows - 1; Y >= 0; Y--)
for (s32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
m_DynamicWater_Height[X][Y] = 0.0f;
m_DynamicWater_dHeight[X][Y] = 0.0f;
}
}
static void ShiftGridsPositiveX(s32 rows)
{
for (s32 Y=0; Y<DYNAMICGRIDELEMENTS - rows; Y++)
for (s32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
m_DynamicWater_Height[X][Y] = m_DynamicWater_Height[X][Y + rows];
m_DynamicWater_dHeight[X][Y] = m_DynamicWater_dHeight[X][Y + rows];
}
for (s32 Y = DYNAMICGRIDELEMENTS - rows; Y < DYNAMICGRIDELEMENTS; Y++)
for (s32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
m_DynamicWater_Height[X][Y] = 0.0f;
m_DynamicWater_dHeight[X][Y] = 0.0f;
}
}
/////////////////////////////////////////////////////////////////////////////////
//
// FUNCTION : UpdateDynamicWater
// PURPOSE : Updates the water. Processes physics and stuff.
//
/////////////////////////////////////////////////////////////////////////////////
static __forceinline void ApplyCalmingQuad(const CCalmingQuad &quad, float timestep, int ASSERT_ONLY(quadIdx))
{
s32 minX = quad.minX;
if (minX < m_WorldBaseX + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE))
{
s32 maxX = quad.maxX;
if (maxX >= m_WorldBaseX)
{
Assertf(minX < maxX, "[Process Calming Quads #%d] %d, %d", quadIdx, minX, maxX);
s32 minY = quad.minY;
if (minY < m_WorldBaseY + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE))
{
s32 maxY = quad.maxY;
if (maxY >= m_WorldBaseY)
{
Assertf(minY < maxY, "[Process Calming Quads #%d] %d, %d", quadIdx, minY, maxY);
// Clip the min and max values to the size of the dynamic area.
minX = MAX(minX, m_WorldBaseX);
maxX = MIN(maxX, m_WorldBaseX + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE));
minY = MAX(minY, m_WorldBaseY);
maxY = MIN(maxY, m_WorldBaseY + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE));
// Make sure we have an area left to apply the dampening to.
Assert(minX <= maxX);
Assert(minY <= maxY);
s32 gridX = FindGridXFromWorldX(minX);
s32 gridY = FindGridYFromWorldY(minY);
s32 maxGridX = gridX + (maxX - minX)/DYNAMICGRIDSIZE;
s32 maxGridY = gridY + (maxY - minY)/DYNAMICGRIDSIZE;
Assertf(maxGridX <= DYNAMICGRIDELEMENTS, "[Process Calming Quads #%d maxGridX %d]", quadIdx, maxGridX);
Assertf(maxGridY <= DYNAMICGRIDELEMENTS, "[Process Calming Quads #%d maxGridY %d]", quadIdx, maxGridY);
float dampenMult = rage::Powf(quad.m_fDampening, timestep);
#if UNROLL_LOOP
s32 firstLoopXStart = gridX;
s32 firstLoopXEnd = MIN(((gridX + 3)/4)*4, maxGridX);
s32 secondLoopXStart = firstLoopXEnd;
s32 secondLoopXEnd = firstLoopXEnd + ((maxGridX - firstLoopXEnd)/4)*4;
s32 thirdLoopXStart = secondLoopXEnd;
s32 thirdLoopXEnd = maxGridX;
ScalarV dampenMultScalar(dampenMult);
for (s32 loopY = gridY; loopY < maxGridY; loopY++)
{
for(s32 loopX = firstLoopXStart; loopX < firstLoopXEnd; loopX++)
m_DynamicWater_dHeight[loopY][loopX] *= dampenMult;
for(s32 loopX = secondLoopXStart; loopX < secondLoopXEnd; loopX += 4)
*(Vec4V*)&m_DynamicWater_dHeight[loopY][loopX] *= dampenMultScalar;
for(s32 loopX = thirdLoopXStart; loopX < thirdLoopXEnd; loopX++)
m_DynamicWater_dHeight[loopY][loopX] *= dampenMult;
}
#else
for (s32 loopY = gridY; loopY < maxGridY; loopY++)
for (s32 loopX = gridX; loopX <maxGridX; loopX++)
m_DynamicWater_dHeight[loopY][loopX] *= dampenMult;
#endif //UNROLL_LOOP
}
}
}
}
}
static void UpdateDynamicWater(CWaterUpdateDynamicParams *pWaterParams)
{
Vector3 CenterCoors = pWaterParams->m_CenterCoors;
s32 OldWorldBaseX = m_WorldBaseX;
s32 OldWorldBaseY = m_WorldBaseY;
m_GridBaseX = FindGridXFromWorldX(m_WorldBaseX);
m_GridBaseY = FindGridYFromWorldY(m_WorldBaseY);
s32 cameraRelX = ((s32)floor(CenterCoors.x/DYNAMICGRIDSIZE))*DYNAMICGRIDSIZE;
s32 cameraRelY = ((s32)floor(CenterCoors.y/DYNAMICGRIDSIZE))*DYNAMICGRIDSIZE;
s32 cameraRangeMinX = cameraRelX - DYNAMICGRIDELEMENTS*DYNAMICGRIDSIZE/2;
s32 cameraRangeMinY = cameraRelY - DYNAMICGRIDELEMENTS*DYNAMICGRIDSIZE/2;
m_WorldBaseX = cameraRangeMinX;
m_WorldBaseY = cameraRangeMinY;
#if GTA_REPLAY
if(CReplayMgr::IsEditModeActive())
return;
#endif
sysMemCpy(m_DynamicWater_Height, pWaterParams->m_WaterHeightBuffer, sizeof(float)*DYNAMICGRIDELEMENTS*DYNAMICGRIDELEMENTS);
if (m_WorldBaseX != OldWorldBaseX || m_WorldBaseY != OldWorldBaseY)
{
#if __SPU && DMA_HEIGHT_BUFFER_WITH_JOB
sysDmaWait(1 << WATER_HEIGHT_DMA_TAG);
#endif
if ( (ABS(OldWorldBaseX - m_WorldBaseX) > DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE) ||
(ABS(OldWorldBaseY - m_WorldBaseY) > DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE))
{
ClearDynamicWater(); // Clear the whole grid in one go
}
else
{
// Clear the strip that has scrolled into our area
if (OldWorldBaseX < m_WorldBaseX)
{
ShiftGridsPositiveX((m_WorldBaseX - OldWorldBaseX) / DYNAMICGRIDSIZE);
}
if (OldWorldBaseX > m_WorldBaseX)
{
ShiftGridsNegativeX((OldWorldBaseX - m_WorldBaseX) / DYNAMICGRIDSIZE);
}
if (OldWorldBaseY < m_WorldBaseY)
{
ShiftGridsPositiveY((m_WorldBaseY - OldWorldBaseY) / DYNAMICGRIDSIZE);
}
if (OldWorldBaseY > m_WorldBaseY)
{
ShiftGridsNegativeY((OldWorldBaseY - m_WorldBaseY) / DYNAMICGRIDSIZE);
}
}
}
#if REPLAY_WATER_ENABLE_DEBUG
static char debugString[256];
sprintf(debugString, "[CPUW] POS %f %f WB %i %i", CenterCoors.GetX(), CenterCoors.GetY(), m_WorldBaseX, m_WorldBaseY);
grcDebugDraw::Text(Vec2V(0.01f, 0.5f), Color32(0.0f, 0.0f, 0.0f), debugString, true, 1.5f);
#endif
const float timestep = pWaterParams->m_timestep;
// Pre-calculate spring value
static dev_float SPRING = (40.0f);
const float PreCalcSpring = timestep * SPRING;
// Pre-calculate fall back value
static dev_float FALLBACK = (0.5f);
const float PreCalcFallBack = timestep * FALLBACK;
// Pre-calculate friction value
static dev_float FRICTION = (0.7f);
const float PreCalcFriction = rage::Powf(FRICTION, timestep);
#if __SPU
mthRandom g_waterRand(spu_read_decrementer());
#endif
#if __DEV
if (pWaterParams->m_waveTimePeriod<1) pWaterParams->m_waveTimePeriod=1;
#endif
#if __PS3 || __WIN32PC || RSG_DURANGO || RSG_ORBIS
float mult = pWaterParams->m_waveMult*timestep;
float timeFactorScale = ( (2.0f * PI) / float(pWaterParams->m_waveTimePeriod))/64.0f;
float timeFactor0 = (pWaterParams->m_timeInMilliseconds % pWaterParams->m_waveTimePeriod) * timeFactorScale;
#if __SPU && DMA_HEIGHT_BUFFER_WITH_JOB
sysDmaWait((1 << WATER_NOISE_BUFFER_DMA_TAG) | (1 << WATER_HEIGHT_DMA_TAG));
#endif
CCPUTexture waterTex(&m_WaveNoiseBuffer[0][0]);
const float texScale = DYNAMICGRIDELEMENTS/WAVETEXTURERES;
// Add waves
float scale0 = (2.0f/DYNAMICGRIDSIZE/DYNAMICGRIDELEMENTS)*pWaterParams->m_waveLengthScale*texScale;
float noiseOffsetX = ((float)g_waterRand.GetRanged(0, WAVETEXTURERES))/WAVETEXTURERES;
float noiseOffsetY = ((float)g_waterRand.GetRanged(0, WAVETEXTURERES))/WAVETEXTURERES;
float noiseX = noiseOffsetX;
static float noiseStep = 4.0f/WAVETEXTURERES;
float disturbScale = pWaterParams->m_disturbAmount*timestep;
float wtxO = ((float)m_WorldBaseX)/DYNAMICGRIDSIZE/DYNAMICGRIDELEMENTS*pWaterParams->m_waveLengthScale*texScale;
float wtyO = ((float)m_WorldBaseY)/DYNAMICGRIDSIZE/DYNAMICGRIDELEMENTS*pWaterParams->m_waveLengthScale*texScale;
float wtx = wtxO + pWaterParams->m_timeInMilliseconds/400000.0f;
float wty = wtyO + timeFactor0;
float ty = wty;
for (s32 loopY = 0; loopY < DYNAMICGRIDELEMENTS; loopY++)
{
float tx = wtx;
float noiseY = noiseOffsetY;
const float offsetHack = 2/255.0f; //Old texture was not normalized, but we want to preserve the old heights for now...
const float offset = -0.5f + offsetHack;
#if UNROLL_LOOP
for (s32 loopX = 0; loopX < DYNAMICGRIDELEMENTS;)
{
float waveA = waterTex.tex2D(Vec2V(tx, ty)).Getf() + offset;
float disturbA = waterTex.tex2D(Vec2V(noiseX, noiseY)).Getf() + offset;
float pushA = disturbA*disturbScale + mult*waveA;
m_DynamicWater_dHeight[loopY][loopX] += pushA;
loopX++;
tx += scale0;
noiseY += noiseStep;
float waveB = waterTex.tex2D(Vec2V(tx, ty)).Getf() + offset;
float disturbB = waterTex.tex2D(Vec2V(noiseX, noiseY)).Getf() + offset;
float pushB = disturbB*disturbScale + mult*waveB;
m_DynamicWater_dHeight[loopY][loopX] += pushB;
loopX++;
tx += scale0;
noiseY += noiseStep;
float waveC = waterTex.tex2D(Vec2V(tx, ty)).Getf() + offset;
float disturbC = waterTex.tex2D(Vec2V(noiseX, noiseY)).Getf() + offset;
float pushC = disturbC*disturbScale + mult*waveC;
m_DynamicWater_dHeight[loopY][loopX] += pushC;
loopX++;
tx += scale0;
noiseY += noiseStep;
float waveD = waterTex.tex2D(Vec2V(tx, ty)).Getf() + offset;
float disturbD = waterTex.tex2D(Vec2V(noiseX, noiseY)).Getf() + offset;
float pushD = disturbD*disturbScale + mult*waveD;
m_DynamicWater_dHeight[loopY][loopX] += pushD;
loopX++;
tx += scale0;
noiseY += noiseStep;
}
#else
for (s32 loopX = 0; loopX < DYNAMICGRIDELEMENTS; loopX++)
{
float disturb = waterTex.tex2D(Vec2V(noiseX, noiseY)).Getf() + offset;
float wave = waterTex.tex2D(Vec2V(tx, ty)).Getf() + offset;
float push = disturb*disturbScale + wave*mult;
m_DynamicWater_dHeight[loopY][loopX] += push;
tx += scale0;
noiseY += noiseStep;
}
#endif
ty += scale0;
noiseX += noiseStep;
}
#endif //__PS3 || _WIN32PC
#if UNROLL_LOOP
ScalarV PreCalcSpringScalar(-1.0f*PreCalcSpring);
ScalarV PreCalcFallBackScalar(-1.0f*PreCalcFallBack);
ScalarV PreCalcFrictionScalar(PreCalcFriction);
for (int xx = 0; xx < DYNAMICGRIDELEMENTS; xx++)
{
int X = xx;
u32 XPlus;
u32 XMin;
if(xx == DYNAMICGRIDELEMENTS-1)
XPlus = xx;
else
XPlus = xx + 1;
if(xx == 0)
XMin = xx;
else
XMin = xx - 1;
for (int yy = 0; yy < DYNAMICGRIDELEMENTS; yy += 4)
{
u32 Y1 = yy + 0;
u32 Y2 = yy + 1;
u32 Y3 = yy + 2;
u32 Y4 = yy + 3;
u32 YMin1;
u32 YPlus1;
u32 YMin2;
u32 YPlus2;
u32 YMin3;
u32 YPlus3;
u32 YMin4;
u32 YPlus4;
if(Y1 == 0)
YMin1 = Y1;
else
YMin1 = Y1 - 1;
YPlus1 = Y2;
YMin2 = Y1;
YPlus2 = Y3;
YMin3 = Y2;
YPlus3 = Y4;
YMin4 = Y3;
if(Y4 == DYNAMICGRIDELEMENTS - 1)
YPlus4 = Y4;
else
YPlus4 = Y4 + 1;
float AverageHeight1 = 0.17f * (m_DynamicWater_Height[XMin][Y1] +
m_DynamicWater_Height[X][YMin1] +
m_DynamicWater_Height[X][YPlus1] +
m_DynamicWater_Height[XPlus][Y1]) +
0.08f * (m_DynamicWater_Height[XMin][YMin1] +
m_DynamicWater_Height[XMin][YPlus1] +
m_DynamicWater_Height[XPlus][YMin1] +
m_DynamicWater_Height[XPlus][YPlus1]);
float AverageHeight2 = 0.17f * (m_DynamicWater_Height[XMin][Y2] +
m_DynamicWater_Height[X][YMin2] +
m_DynamicWater_Height[X][YPlus2] +
m_DynamicWater_Height[XPlus][Y2]) +
0.08f * (m_DynamicWater_Height[XMin][YMin2] +
m_DynamicWater_Height[XMin][YPlus2] +
m_DynamicWater_Height[XPlus][YMin2] +
m_DynamicWater_Height[XPlus][YPlus2]);
float AverageHeight3 = 0.17f * (m_DynamicWater_Height[XMin][Y3] +
m_DynamicWater_Height[X][YMin3] +
m_DynamicWater_Height[X][YPlus3] +
m_DynamicWater_Height[XPlus][Y3]) +
0.08f * (m_DynamicWater_Height[XMin][YMin3] +
m_DynamicWater_Height[XMin][YPlus3] +
m_DynamicWater_Height[XPlus][YMin3] +
m_DynamicWater_Height[XPlus][YPlus3]);
float AverageHeight4 = 0.17f * (m_DynamicWater_Height[XMin][Y4] +
m_DynamicWater_Height[X][YMin4] +
m_DynamicWater_Height[X][YPlus4] +
m_DynamicWater_Height[XPlus][Y4]) +
0.08f * (m_DynamicWater_Height[XMin][YMin4] +
m_DynamicWater_Height[XMin][YPlus4] +
m_DynamicWater_Height[XPlus][YMin4] +
m_DynamicWater_Height[XPlus][YPlus4]);
Vec4V AverageHeight(AverageHeight1, AverageHeight2, AverageHeight3, AverageHeight4);
Vec4V HeightDiff = *(Vec4V*)&m_DynamicWater_Height[X][Y1] - AverageHeight;
// Spring value
*(Vec4V*)&m_DynamicWater_dHeight[X][Y1] = AddScaled(*(Vec4V*)&m_DynamicWater_dHeight[X][Y1], HeightDiff, PreCalcSpringScalar);
// fall back to equilibrium (0.0)
*(Vec4V*)&m_DynamicWater_dHeight[X][Y1] = AddScaled(*(Vec4V*)&m_DynamicWater_dHeight[X][Y1], *(Vec4V*)&m_DynamicWater_Height[X][Y1], PreCalcFallBackScalar);
// Do some friction too
*(Vec4V*)&m_DynamicWater_dHeight[X][Y1] *= PreCalcFrictionScalar;
}
}
#else
// Go through the array and update the water velocities.
for (int X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
u32 XPlus;
u32 XMin;
if(X == DYNAMICGRIDELEMENTS-1)
XPlus = X;
else
XPlus = X + 1;
if(X == 0)
XMin = X;
else
XMin = X - 1;
for (int Y = 0; Y < DYNAMICGRIDELEMENTS; Y++)
{
u32 YPlus;
u32 YMin;
if(Y == DYNAMICGRIDELEMENTS-1)
YPlus = Y;
else
YPlus = Y + 1;
if(Y == 0)
YMin = Y;
else
YMin = Y - 1;
float AverageHeight = 0.17f*( m_DynamicWater_Height[XMin][Y] +
m_DynamicWater_Height[XPlus][Y] +
m_DynamicWater_Height[X][YMin] +
m_DynamicWater_Height[X][YPlus]) +
0.08f*( m_DynamicWater_Height[XPlus][YMin] +
m_DynamicWater_Height[XPlus][YPlus] +
m_DynamicWater_Height[XMin][YMin] +
m_DynamicWater_Height[XMin][YPlus]);
// Spring value
float HeightDiff = m_DynamicWater_Height[X][Y] - AverageHeight;
m_DynamicWater_dHeight[X][Y] -= HeightDiff * PreCalcSpring;
// fall back to equilibrium (0.0)
m_DynamicWater_dHeight[X][Y] -= m_DynamicWater_Height[X][Y] * PreCalcFallBack;
// Do some friction too
m_DynamicWater_dHeight[X][Y] *= PreCalcFriction;
}
}
#endif // UNROLL_LOOP
#if __PS3 || __WIN32PC || RSG_DURANGO || RSG_ORBIS
#if __SPU && DMA_HEIGHT_BUFFER_WITH_JOB
sysDmaWait(1 << WATER_WAVE_BUFFER_AND_QUADS_DMA_TAG);
Assert(m_nNumOfWaveQuads <= MAXNUMWATERWAVEQUADS);
#endif
CCPUTexture waveTex(&m_WaveBuffer[0][0]);
float txO = ((float)m_WorldBaseX)/DYNAMICGRIDSIZE/DYNAMICGRIDELEMENTS;
float tyO = ((float)m_WorldBaseY)/DYNAMICGRIDSIZE/DYNAMICGRIDELEMENTS;
// We go through the wave polys and if they happen to stretch into the dynamic water area their wave effect is applied.
#if __WIN32PC || RSG_DURANGO || RSG_ORBIS
if (m_aWaveQuads != NULL)
#endif // __WIN32PC || RSG_DURANGO || RSG_ORBIS
for (int quad = 0; quad < m_nNumOfWaveQuads; quad++)
{
s32 minX = m_aWaveQuads[quad].minX;
if (minX < m_WorldBaseX + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE))
{
s32 maxX = m_aWaveQuads[quad].maxX;
if (maxX >= m_WorldBaseX)
{
Assertf(minX < maxX, "[Process Wave Quads #%d] %d, %d", quad, minX, maxX);
s32 minY = m_aWaveQuads[quad].minY;
if (minY < m_WorldBaseY + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE))
{
s32 maxY = m_aWaveQuads[quad].maxY;
if (maxY >= m_WorldBaseY)
{
Assertf(minY < maxY, "[Process Wave Quads #%d] %d, %d", quad, minY, maxY);
// Clip the min and max values to the size of the dynamic area.
minX = MAX(minX, m_WorldBaseX);
maxX = MIN(maxX, m_WorldBaseX + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE));
minY = MAX(minY, m_WorldBaseY);
maxY = MIN(maxY, m_WorldBaseY + (DYNAMICGRIDELEMENTS * DYNAMICGRIDSIZE));
// Make sure we have an area left to apply the dampening to.
Assert(minX <= maxX);
Assert(minY <= maxY);
s32 gridX = FindGridXFromWorldX(minX);
s32 gridY = FindGridYFromWorldY(minY);
s32 maxGridX = gridX + (maxX - minX)/DYNAMICGRIDSIZE;
s32 maxGridY = gridY + (maxY - minY)/DYNAMICGRIDSIZE;
Assertf(maxGridX <= DYNAMICGRIDELEMENTS, "[Process Wave Quads #%d maxGridX %d]", quad, maxGridX);
Assertf(maxGridY <= DYNAMICGRIDELEMENTS, "[Process Wave Quads #%d maxGridY %d]", quad, maxGridY);
Vec3V waveDirN = Vec3V(-m_aWaveQuads[quad].GetXDirection(), -m_aWaveQuads[quad].GetYDirection(), 0.0f);
waveDirN = Normalize(waveDirN);
Vec3V y = Vec3V(V_Z_AXIS_WZERO);
Vec3V waveDirT = Normalize(Cross(waveDirN, y));
Vec2V waveTexO = Vec2V(txO, tyO);
Vec2V waveDirNV2 = waveDirN.GetXY();
Vec2V waveDirTV2 = waveDirT.GetXY();
float texScale = 1.0f/WAVETEXTURERES;
float tx = Dot(waveTexO, waveDirNV2).Getf();
float ty = Dot(waveTexO, waveDirTV2).Getf();
tx = tx + pWaterParams->m_timeInMilliseconds/50000.0f;
float dxX = waveDirN.GetXf()*texScale;
float dxY = waveDirN.GetYf()*texScale;
float dyX = waveDirT.GetXf()*texScale;
float dyY = waveDirT.GetYf()*texScale;
float shoreWavesMult = pWaterParams->m_ShoreWavesMult * timestep * m_aWaveQuads[quad].GetAmplitude();
const float waveMult = 1.723f;
const float waveAdd = -0.723f;
#if UNROLL_LOOP
ScalarV shoreWavesMultScalar(shoreWavesMult);
ScalarV vWaveMult(waveMult);
Vec4V vWaveAdd(waveAdd, waveAdd, waveAdd, waveAdd);
s32 firstLoopXStart = gridX;
s32 firstLoopXEnd = MIN(((gridX + 3)/4)*4, maxGridX);
s32 secondLoopXStart = firstLoopXEnd;
s32 secondLoopXEnd = firstLoopXEnd + ((maxGridX - firstLoopXEnd)/4)*4;
s32 thirdLoopXStart = secondLoopXEnd;
s32 thirdLoopXEnd = maxGridX;
Vec2V txV = Vec2V(tx, ty);
Vec2V dtxXV = Vec2V(dxX, dxY);
Vec2V dtxYV = Vec2V(dyX, dyY);
for (s32 loopY = gridY; loopY < maxGridY; loopY++)
{
Vec2V loopXTXV = txV;
for(s32 loopX = firstLoopXStart; loopX < firstLoopXEnd; loopX++)
{
float wave = waveTex.tex2D(loopXTXV).Getf();
m_DynamicWater_dHeight[loopY][loopX] += (waveMult*wave + waveAdd)*shoreWavesMult;
loopXTXV += dtxXV;
}
for(s32 loopX = secondLoopXStart; loopX < secondLoopXEnd; loopX += 4)
{
ScalarV wave1 = waveTex.tex2D(loopXTXV);
loopXTXV += dtxXV;
ScalarV wave2 = waveTex.tex2D(loopXTXV);
loopXTXV += dtxXV;
ScalarV wave3 = waveTex.tex2D(loopXTXV);
loopXTXV += dtxXV;
ScalarV wave4 = waveTex.tex2D(loopXTXV);
loopXTXV += dtxXV;
Vec4V vWave(wave1, wave2, wave3, wave4);
vWave = AddScaled(vWaveAdd, vWave, vWaveMult);
*(Vec4V*)&m_DynamicWater_dHeight[loopY][loopX] = AddScaled(*(Vec4V*)&m_DynamicWater_dHeight[loopY][loopX], vWave, shoreWavesMultScalar);
}
for(s32 loopX = thirdLoopXStart; loopX < thirdLoopXEnd; loopX++)
{
float wave = waveTex.tex2D(loopXTXV).Getf();
m_DynamicWater_dHeight[loopY][loopX] += (waveMult*wave + waveAdd)*shoreWavesMult;
loopXTXV += dtxXV;
}
txV = txV + dtxYV;
}
#else
for (s32 loopY = gridY; loopY < maxGridY; loopY++)
{
float loopXTX = tx;
float loopXTY = ty;
for (s32 loopX = gridX; loopX <maxGridX; loopX++)
{
float wave = waveTex.tex2D(Vec2V(loopXTX, loopXTY)).Getf();
m_DynamicWater_dHeight[loopY][loopX] += (waveMult*wave + waveAdd)*shoreWavesMult;
loopXTX += dxX;
loopXTY += dxY;
}
tx += dyX;
ty += dyY;
}
#endif
}
}
}
}
}
#endif //__PS3 || __WIN32PC || RSG_DURANGO || RSG_ORBIS
#if __SPU && DMA_HEIGHT_BUFFER_WITH_JOB
sysDmaWait(1 << WATER_CALM_QUADS_DMA_TAG);
Assert(m_nNumOfWaterCalmingQuads <= MAXNUMWATERCALMINGQUADS);
#endif
// We go through the calming polys and if they happen to stretch into the dynamic water area their calming effect is applied.
#if RSG_PC || RSG_DURANGO || RSG_XENON || RSG_ORBIS
if (m_aCalmingQuads != NULL)
#endif
for (int quadIdx = 0; quadIdx < m_nNumOfWaterCalmingQuads; quadIdx++)
{
CCalmingQuad quad = m_aCalmingQuads[quadIdx];
ApplyCalmingQuad(quad,timestep,quadIdx);
}
#if RSG_PC || RSG_DURANGO || RSG_XENON || RSG_ORBIS
if (m_aExtraCalmingQuads != NULL)
{
for (int quadIdx = 0; quadIdx < m_nNumOfExtraWaterCalmingQuads; quadIdx++)
{
CCalmingQuad quad = m_aExtraCalmingQuads[quadIdx];
if( quad.m_fDampening < 1.0f )
{
ApplyCalmingQuad(quad,timestep,quadIdx);
}
}
}
#endif
//====================== Update Height =========================
#if __WIN32PC || RSG_DURANGO || RSG_ORBIS
if (m_DynamicWater_Height != NULL)
#endif
{
#if UNROLL_LOOP
ScalarV timestepScalar(timestep);
for (u32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
for (u32 Y = 0; Y < DYNAMICGRIDELEMENTS; Y+=4)
{
*(Vec4V*)&m_DynamicWater_Height[X][Y] = AddScaled(*(Vec4V*)&m_DynamicWater_Height[X][Y], *(Vec4V*)&m_DynamicWater_dHeight[X][Y], timestepScalar);
}
}
#else
for (u32 X = 0; X < DYNAMICGRIDELEMENTS; X++)
{
for (u32 Y = 0; Y < DYNAMICGRIDELEMENTS; Y++)
{
m_DynamicWater_Height[X][Y] += m_DynamicWater_dHeight[X][Y] * timestep;
}
}
#endif // UNROLL_LOOP
}
}
#endif //__WATERUPDATEDYNAMICCOMMON_H__...