t2 engine svn checkout

This commit is contained in:
loop 2024-01-07 04:36:33 +00:00
commit ff569bd2ae
988 changed files with 394180 additions and 0 deletions

1714
terrain/blender.cc Normal file

File diff suppressed because it is too large Load diff

92
terrain/blender.h Normal file
View file

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _BLENDER_H_
#define _BLENDER_H_
#define PAULS_TEST_CODE 0
#if PAULS_TEST_CODE
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned int U32;
typedef float F32;
#define GRIDFLAGS( x, y ) (grid_mats[ yp ][ xp ])
#define MATERIALSTART 1
#define BLENDER_USE_ASM 1
#else
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
#ifndef _TERRDATA_H_
#include "terrain/terrData.h"
#endif
#ifndef _TERRRENDER_H_
#include "terrain/terrRender.h"
#endif
#ifdef USEASSEMBLYTERRBLEND
#define BLENDER_USE_ASM 1
#else
#define BLENDER_USE_ASM 0
#endif
#define GRIDFLAGS( x, y ) (TerrainRender::mCurrentBlock->findSquare( 0, Point2I( xp, yp ) )->flags)
#define MATERIALSTART (GridSquare::MaterialStart)
#endif
class Blender
{
// pointer to big buffer of source textures and mipmaps
U32 *bmp_alloc_ptr;
// One square buffer used for blending...
U32 *blendbuffer;
// List of pointers into bmp buffer. Grouped by mip level,
// so first X pointers are textures 0-X, mip 0.
U32 **bmpdata;
// List of pointers to alpha data for the bmp types.
U8 **alpha_data;
// Number of bmp types
int num_src_bmps;
// Mip levels (including top detail) for each bmp type
int num_mip_levels;
public:
// mips_per_bmp should include top level (always >= 1)
// alphadata is 8 bit 256x256
Blender( int num_bmp_types, int mips_per_bmp, U8 **alphadata );
~Blender();
// blends into 5551 format. X and Y are in blocks (same resolution as
// alpha table, i.e. at high detail, there are 4x4 blocks covered by
// the 128x128 destination bmp.
// lightmap should be 16 bit 512x512.
// destmips is an array of pointers to the bitmap and it's mips to be
// filled in by this function
void blend( int x, int y, int level, const U16 *lightmap, U16 **destmips );
// bmps is an array of pointers to the bitmap and it's mips
// highest detail first. Should be in 24bit format.
// Call this once per bmp type. It copies the bmp into it's own format,
// so you can then delete your versions of the bmp and mips.
void addSourceTexture( int bmp_type, const U8 **bmps );
};
#endif

1351
terrain/blender_asm.asm Normal file

File diff suppressed because it is too large Load diff

178
terrain/bvQuadTree.cc Normal file
View file

@ -0,0 +1,178 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
/******************************************************************************
* FILENAME: D:\Tribes\darkstar\terrain\bvQuadTree.cc
*
* DESCRIPTION:
*
* CREATED: 1/17/00 4:07:04 PM
*
* BY: PeteW
******************************************************************************/
#include "terrain/bvQuadTree.h"
#include "console/console.h"
BVQuadTree::BVQuadTree(BitVector *bv)
{
if (bv != NULL)
init(*bv);
else
{
BitVector localBV;
localBV.setSize(4);
localBV.set();
init(localBV);
}
}
BVQuadTree::~BVQuadTree()
{
while (mQTHierarchy.size() > 0)
{
delete mQTHierarchy.last();
mQTHierarchy.pop_back();
}
}
bool BVQuadTree::isSet(const Point2F &pos, S32 level) const
{
AssertFatal(pos.x >= 0. && pos.x <= 1., "BVQuadTree::isSet: x must be in range [0,1]");
AssertFatal(pos.y >= 0. && pos.y <= 1., "BVQuadTree::isSet: y must be in range [0,1]");
AssertFatal(level >= 0, "BVQuadTree:isSet: level must be greater than or equal to zero");
if (level >= mQTHierarchy.size())
// force to be within resolution of QT
level = mQTHierarchy.size() - 1;
if (level < 0)
level = 0;
F32 dimension = F32(1 << level);
U32 offset = U32((F32(U32(pos.y * dimension)) + pos.x) * dimension);
return(mQTHierarchy[level]->test(offset));
}
bool BVQuadTree::isClear(const Point2F &pos, S32 level) const
{
AssertFatal(pos.x >= 0. && pos.x <= 1., "BVQuadTree::isClear: x must be in range [0,1]");
AssertFatal(pos.y >= 0. && pos.y <= 1., "BVQuadTree::isClear: y must be in range [0,1]");
AssertFatal(level >= 0, "BVQuadTree:isClear: level must be greater than or equal to zero");
if (level >= mQTHierarchy.size())
// force to be within resolution of QT
level = mQTHierarchy.size() - 1;
if (level < 0)
level = 0;
F32 dimension = F32(1 << level);
U32 offset = U32((F32(U32(pos.y * dimension)) + pos.x) * dimension);
return(mQTHierarchy[level]->test(offset) == false);
}
/* Initialize the quadtree with the provided bit vector. Note that the bit vector
* denotes data at each corner of the quadtree cell. Hence the dimension for the
* deepest quadtree level must be 1 less than that of the bit vector.
*/
void BVQuadTree::init(const BitVector &bv)
{
while (mQTHierarchy.size() > 0)
{
delete mQTHierarchy.last();
mQTHierarchy.pop_back();
}
// get the width/height of the square bit vector
U32 bvDim = (U32)mSqrt((F32)bv.getSize());
U32 qtDim = bvDim - 1; // here's where we correct dimension...
AssertFatal(((mSqrt((F32)bv.getSize()) - 1) == (F32)qtDim) && (isPow2(qtDim) == true), "BVQuadTree::init: bit vector size must be power of 4");
// find the power of two we're starting at
mResolution = qtDim;
U32 level = 0;
while ((1 << (level + 1)) <= qtDim)
level++;
BitVector *initBV = new BitVector;
AssertFatal(initBV != NULL, "BVQuadTree::init: failed to allocate highest detail bit vector");
initBV->setSize(qtDim * qtDim);
initBV->clear();
for (S32 i = 0; i < qtDim; i++)
for (S32 j = 0; j < qtDim; j++)
{
S32 k = i * bvDim + j;
if (bv.test(k) || bv.test(k + 1) || bv.test(k + bvDim) || bv.test(k + bvDim + 1))
initBV->set(i * qtDim + j);
}
mQTHierarchy.push_back(initBV);
if (level > 0)
buildHierarchy(level - 1);
}
#ifdef BV_QUADTREE_DEBUG
void BVQuadTree::dump() const
{
char str[256];
U32 strlen;
for (U32 i = 0; i < mQTHierarchy.size(); i++)
{
U32 dimension = 1 << i;
Con::printf("level %d:", i);
for (U32 y = 0; y < dimension; y++)
{
U32 yOffset = y * dimension;
str[0] = '\0';
for (U32 x = 0; x < dimension; x++)
{
U32 offset = yOffset + x;
strlen = dStrlen(str);
if (strlen < 252)
dSprintf(str + strlen, 256 - strlen, mQTHierarchy[i]->isSet(offset) ? "1 " : "0 ");
else
{
dSprintf(str + strlen, 256 - strlen, "...");
break;
}
}
Con::printf("%s", str);
}
}
}
#endif
void BVQuadTree::buildHierarchy(U32 level)
{
BitVector *priorBV = mQTHierarchy.first();
BitVector *levelBV = new BitVector;
AssertFatal(levelBV != NULL, "BVQuadTree::buildHierarchy: failed to allocate bit vector");
U32 levelDim = (1 << level);
levelBV->setSize(levelDim * levelDim);
levelBV->clear();
U32 yOffset, offset, yPriorOffset, priorOffset;
// COULD THIS BE DONE WITH A SINGLE LOOP?
for (U32 y = 0; y < levelDim; y++)
{
yOffset = y * levelDim;
yPriorOffset = yOffset << 2;
for (U32 x = 0; x < levelDim; x++)
{
offset = yOffset + x;
priorOffset = yPriorOffset + (x << 1);
if (priorBV->test(priorOffset) || priorBV->test(priorOffset + 1) ||
priorBV->test(priorOffset + (levelDim << 1)) ||
priorBV->test(priorOffset + (levelDim << 1) + 1))
levelBV->set(offset);
}
}
mQTHierarchy.push_front(levelBV);
if (level > 0)
buildHierarchy(level - 1);
}

57
terrain/bvQuadTree.h Normal file
View file

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
/******************************************************************************
* FILENAME: D:\Tribes\darkstar\terrain\bvQuadTree.h
*
* DESCRIPTION:
*
* CREATED: 1/17/00 4:02:53 PM
*
* BY: PeteW
******************************************************************************/
#ifndef _BVQUADTREE_H_
#define _BVQUADTREE_H_
//#define BV_QUADTREE_DEBUG
#ifndef _TVECTOR_H_
#include "Core/tVector.h"
#endif
#ifndef _BITVECTOR_H_
#include "Core/bitVector.h"
#endif
#ifndef _MPOINT_H_
#include "Math/mPoint.h"
#endif
class BVQuadTree
{
protected:
VectorPtr<BitVector*> mQTHierarchy;
U32 mResolution;
public:
BVQuadTree(BitVector *bv = NULL);
~BVQuadTree();
bool isSet(const Point2F &pos, S32 level) const;
bool isClear(const Point2F &pos, S32 level) const;
void init(const BitVector &bv);
#ifdef BV_QUADTREE_DEBUG
void dump() const;
#endif
U32 countLevels() const { return(mQTHierarchy.size()); }
protected:
void buildHierarchy(U32 level);
private:
BVQuadTree(const BVQuadTree &);
BVQuadTree& operator=(const BVQuadTree &);
};
#endif

326
terrain/fluid.h Normal file
View file

@ -0,0 +1,326 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _FLUID_H_
#define _FLUID_H_
//==============================================================================
// TO DO LIST
//==============================================================================
// - ARB support?
// - Optimize the fog given water is horizontal
// - New fog system
// - Attempt to reject fully fogger low LOD blocks
//==============================================================================
// - Designate specular map?
// - Turn off specular? (for lava)
//==============================================================================
//==============================================================================
// ASSUMPTIONS:
// - A single repetition of the terrain (or a "rep") is 256x256 squares.
// - A terrain square is 8 meters on a side.
// - The fluid can NOT be rotated on any axis.
// - The "anchor point" for the fluid can be on any discrete square corner.
// - The size of the fluid is constrained to be either a multiple of 4 or 8
// squares.
//==============================================================================
// DISCUSSION:
// - If the overall size of the fluid is less than a quarter of a terrain rep,
// then the fluid will use a "double resolution" mode. Thus, when the fluid
// is sufficiently small, it gets more detailed.
// - The fluid renders blocks in one of two sizes, LOW and HIGH.
// - Block are 8 squares in normal resolution mode, and 4 in high.
// - A quad tree is used to quickly cull down to the blocks.
// - Reject and accept masks are used by the quad tree.
//==============================================================================
//==============================================================================
// INCLUDES
//==============================================================================
#ifndef _TYPES_H_
#include "Platform/types.h"
#endif
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _MMATH_H_
#include "Math/mMath.h"
#endif
//==============================================================================
// DEFINES
//==============================================================================
#define s32 S32
#define u32 U32
#define s16 S16
#define u16 U16
#define s8 S8
#define u8 U8
#define byte U8
#define f32 F32
#define PI (3.141592653589793238462643f)
#define SECONDS ((f32)(Platform::getVirtualMilliseconds()) * 0.001f)
#define MALLOC dMalloc
#define REALLOC dRealloc
#define FREE dFree
#define MEMSET dMemset
#define TWO_PI (PI * 2.0f)
#define FMOD(a,b) (f32)fmod( a, b )
#define LENGTH3(a,b,c) (f32)sqrt( (a)*(a) + (b)*(b) + (c)*(c) )
#define SINE(a) (float)sin( a )
#define COSINE(a) (float)cos( a )
#define DISTANCE(x,y,z) LENGTH3( (x)-m_Eye.X, (y)-m_Eye.Y, (z)-m_Eye.Z )
#define ASSERT(exp)
//==============================================================================
// TYPES
//==============================================================================
//
// Order for [4] element arrays...
//
// 2----3 Y
// | | |
// | | |
// 0----1 0----X
//
//==============================================================================
class fluid
{
//------------------------------------------------------------------------------
// Public Types
//
public:
typedef f32 compute_fog_fn( f32 DeltaZ, f32 D );
//------------------------------------------------------------------------------
// Public Functions
//
public:
fluid ( void );
~fluid ();
//
// Render (in FluidRender.cpp):
//
void Render ( bool& EyeSubmerged );
//
// Setup per frame (in FluidSupport.cpp):
//
void SetEyePosition ( f32 X, f32 Y, f32 Z );
void SetFrustrumPlanes ( f32* pFrustrumPlanes );
//
// Setup at initialization (in FluidSupport.cpp):
//
void SetInfo ( f32& X0,
f32& Y0,
f32& SizeX,
f32& SizeY,
f32 SurfaceZ,
f32 WaveAmplitude,
f32& Opacity,
f32& EnvMapIntensity,
s32 RemoveWetEdges );
void SetTerrainData ( u16* pTerrainData );
void SetTextures ( TextureHandle Base,
TextureHandle EnvMap );
void SetLightMapTexture ( TextureHandle LightMapTexture );
void SetFogParameters ( f32 R, f32 G, f32 B, f32 VisibleDistance );
void SetFogFn ( compute_fog_fn* pFogFn );
//
// Run time interrogation (in FluidSupport.cpp):
//
s32 IsFluidAtXY ( f32 X, f32 Y ) const;
//------------------------------------------------------------------------------
// Private Types
//
private:
struct plane { f32 A, B, C, D; };
struct rgba { f32 R, G, B, A; };
struct xyz { f32 X, Y, Z; };
struct uv { f32 U, V; };
struct node
{
s32 Level;
s32 MaskIndexX, MaskIndexY;
s32 BlockX0, BlockY0; // World Block
f32 X0, Y0; // World Position
f32 X1, Y1; // World Position
byte ClipBits[4];
};
struct block
{
f32 X0, Y0;
f32 X1, Y1;
f32 Distance[4]; // Distance from eye
f32 LOD [4]; // Level of detail
};
// Rendering phases:
// Phase 1 - Base texture (two passes of cross faded textures)
// Phase 2 - Shadow map
// Phase 3 - Environment map / Specular
// Phase 4 - Fog
struct vertex
{
xyz XYZ; // All phases - Position
rgba RGBA1a; // Phase 1a - Base alpha, first pass
rgba RGBA1b; // Phase 1b - Base alpha, second pass
uv UV3; // Phase 3 - EnvMap UV
rgba RGBA4; // Phase 4 - Fog Color/Alpha
};
//------------------------------------------------------------------------------
// Private Variables
//
private:
s32 m_SquareX0, m_SquareY0; // Anchor in terrain squares
s32 m_SquaresInX, m_SquaresInY; // Number of squares in fluid region
s32 m_BlocksInX, m_BlocksInY; // Number of blocks in fluid region
f32 m_SurfaceZ; // Altitude of fluid surface
s32 m_RemoveWetEdges; // Dry fill all edges of the fluid block
s32 m_HighResMode; // Blocks are 4x4 (high res) or 8x8 squares (normal)
plane m_Plane[6]; // Frustrum clip planes: 0=T 1=B 2=L 3=R 4=N 5=F
xyz m_Eye;
f32 m_Seconds;
f32 m_BaseSeconds;
rgba m_FogColor;
f32 m_VisibleDistance;
f32 m_Opacity;
f32 m_EnvMapIntensity;
f32 m_WaveAmplitude;
f32 m_WaveFactor;
u16* m_pTerrain; // 256x256 data for the terrain
TextureHandle m_BaseTexture;
TextureHandle m_EnvMapTexture;
TextureHandle m_LightMapTexture;
f32 m_Step[5]; // [0] = 0
// [1] = 1/4 block step
// [2] = 1/2 block step
// [3] = 3/4 block step
// [4] = 1 block step
compute_fog_fn* m_pFogFn;
// Bit masks to trivially accept or reject the progressive levels for the
// quad tree recursion. No need for an accept mask at the highest level
// since it is just the exact opposite of the reject mask at that level.
static s32 m_MaskOffset[6]; // Offset for given level into masks.
byte m_RejectMask[ 1 + 1 + 2 + 8 + 32 + 128 ];
byte m_AcceptMask[ 1 + 1 + 2 + 8 + 32 ];
//
// Shared among instances of fluid.
//
static vertex* m_pVertex;
static s32 m_VAllocated;
static s32 m_VUsed;
static s16* m_pIndex;
static s16* m_pINext;
static s16 m_IOffset;
static s32 m_IAllocated;
static s32 m_IUsed;
static s32 m_Instances;
//
// Debug variables.
//
public:
s32 m_ShowWire;
s32 m_ShowNodes;
s32 m_ShowBlocks;
s32 m_ShowBaseA;
s32 m_ShowBaseB;
s32 m_ShowLightMap;
s32 m_ShowEnvMap;
s32 m_ShowFog;
//------------------------------------------------------------------------------
// Private Functions
//
private:
//
// Functions in FluidSupport.cpp:
//
s32 GetAcceptBit ( s32 Level, s32 IndexX, s32 IndexY ) const;
s32 GetRejectBit ( s32 Level, s32 IndexX, s32 IndexY ) const;
void SetAcceptBit ( s32 Level, s32 IndexX, s32 IndexY, s32 Value );
void SetRejectBit ( s32 Level, s32 IndexX, s32 IndexY, s32 Value );
void BuildLowerMasks ( void );
void RebuildMasks ( void );
void FloodFill ( u8* pGrid, s32 x, s32 y, s32 SizeX, s32 SizeY );
//
// Functions in FluidQuadTree.cpp
//
void RunQuadTree ( bool& EyeSubmerged );
f32 ComputeLOD ( f32 Distance );
byte ComputeClipBits ( f32 X, f32 Y, f32 Z );
void ProcessNode ( node& Node );
void ProcessBlock ( block& Block );
void ProcessBlockLODHigh ( block& Block );
void ProcessBlockLODMorph ( block& Block );
void ProcessBlockLODTrans ( block& Block );
void ProcessBlockLODLow ( block& Block );
void SetupVert ( f32 X, f32 Y, f32 Distance, vertex* pV );
void InterpolateVerts ( vertex* pV0,
vertex* pV1,
vertex* pV2,
vertex* pV3,
vertex* pV4,
f32 LOD0,
f32 LOD4 );
void InterpolateVert ( vertex* pV0,
vertex* pV1,
vertex* pV2,
f32 LOD );
// void BuildFogTable ( void );
void ReleaseVertexMemory ( void );
vertex* AcquireVertices ( s32 Count );
void AddTriangleIndices ( s16 I1, s16 I2, s16 I3 );
};
//==============================================================================
#endif // FLUID_HPP
//==============================================================================

1060
terrain/fluidQuadTree.cc Normal file

File diff suppressed because it is too large Load diff

249
terrain/fluidRender.cc Normal file
View file

@ -0,0 +1,249 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "terrain/Fluid.h"
#include "dgl/dgl.h"
//==============================================================================
// FUNCTIONS
//==============================================================================
void fluid::Render( bool& EyeSubmerged )
{
f32 BaseDriftX, BaseDriftY;
f32 Q1 = 1.0f / 48.0f; // This just looks good.
f32 Q2 = 1.0f / 2048.0f; // This is the size of the terrain.
f32 SBase[] = { Q1, 0, 0, 0 };
f32 TBase[] = { 0, Q1, 0, 0 };
f32 SLMap[] = { Q2, 0, 0, 0 };
f32 TLMap[] = { 0, Q2, 0, 0 };
// Several attributes in the fluid vary over time. Get a definitive time
// reading now to be used throughout this render pass.
m_Seconds = SECONDS - m_BaseSeconds;
// Based on the view frustrum, accumulate the list of triangles that
// comprise the fluid surface for this render pass.
RunQuadTree( EyeSubmerged );
// Quick debug render.
#if 0
if( 0 )
{
s32 i;
for( i = 0; i < m_IUsed / 3; i++ )
{
glDisable ( GL_TEXTURE_2D );
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glBegin ( GL_TRIANGLES );
glColor4f ( 0.0f, 1.0f, 0.0f, 0.5f );
glVertex3f ( m_pVertex[m_pIndex[i*3+0]].XYZ.X, m_pVertex[m_pIndex[i*3+0]].XYZ.Y, m_pVertex[m_pIndex[i*3+0]].XYZ.Z );
glVertex3f ( m_pVertex[m_pIndex[i*3+1]].XYZ.X, m_pVertex[m_pIndex[i*3+1]].XYZ.Y, m_pVertex[m_pIndex[i*3+1]].XYZ.Z );
glVertex3f ( m_pVertex[m_pIndex[i*3+2]].XYZ.X, m_pVertex[m_pIndex[i*3+2]].XYZ.Y, m_pVertex[m_pIndex[i*3+2]].XYZ.Z );
glEnd ();
}
}
#endif
//
// We need to compute some time dependant values before we start rendering.
//
// Base texture drift.
{
#define BASE_DRIFT_CYCLE_TIME 8.0f
#define BASE_DRIFT_RATE 0.02f
#define BASE_DRIFT_SCALAR 0.03f
f32 Phase = FMOD( m_Seconds * (TWO_PI/BASE_DRIFT_CYCLE_TIME), TWO_PI );
BaseDriftX = m_Seconds * BASE_DRIFT_RATE;
BaseDriftY = COSINE( Phase ) * BASE_DRIFT_SCALAR;
}
//--------------------------------------------------------------------------
//--
//-- Let's rock.
//--
//--------------------------------------------------------------------------
//-- Debug - wire
if( m_ShowWire )
{
glPolygonMode ( GL_FRONT_AND_BACK, GL_LINE );
glEnableClientState ( GL_VERTEX_ARRAY );
glVertexPointer ( 3, GL_FLOAT, sizeof(vertex), &(m_pVertex[0].XYZ) );
glDisable ( GL_TEXTURE_2D );
glDisable ( GL_DEPTH_TEST );
glEnable ( GL_BLEND );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glColor4f ( 0.5f, 0.5f, 0.5f, 0.5f );
glDrawElements ( GL_TRIANGLES, m_IUsed, GL_UNSIGNED_SHORT, m_pIndex );
glDisable ( GL_BLEND );
glEnable ( GL_DEPTH_TEST );
glColor4f ( 1.0f, 1.0f, 0.0f, 1.0f );
glDrawElements ( GL_TRIANGLES, m_IUsed, GL_UNSIGNED_SHORT, m_pIndex );
glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL );
}
//--------------------------------------------------------------------------
//-- Initializations for Phase 1 - base textures
glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL );
glEnableClientState ( GL_VERTEX_ARRAY );
glVertexPointer ( 3, GL_FLOAT, sizeof(vertex), &(m_pVertex[0].XYZ) );
glEnable ( GL_TEXTURE_2D );
glEnable ( GL_BLEND );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glMatrixMode ( GL_TEXTURE );
glPushMatrix ();
glLoadIdentity ();
glEnableClientState ( GL_COLOR_ARRAY );
glEnable ( GL_TEXTURE_GEN_S );
glEnable ( GL_TEXTURE_GEN_T );
glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR );
glTexGenfv ( GL_S, GL_OBJECT_PLANE, SBase );
glTexGenfv ( GL_T, GL_OBJECT_PLANE, TBase );
glBindTexture ( GL_TEXTURE_2D, m_BaseTexture.getGLName() );
//--------------------------------------------------------------------------
//-- Initializations for Phase 1a - first base texture
glRotatef ( 30.0f, 0.0f, 0.0f, 1.0f );
glColorPointer ( 4, GL_FLOAT, sizeof(vertex), &(m_pVertex[0].RGBA1a) );
glDepthMask(GL_FALSE);
//--------------------------------------------------------------------------
//-- Render Phase 1a - first base texture
if( m_ShowBaseA )
{
glDrawElements ( GL_TRIANGLES, m_IUsed, GL_UNSIGNED_SHORT, m_pIndex );
}
//--------------------------------------------------------------------------
//-- Initializations for Phase 1b - second base texture
glRotatef ( 30.0f, 0.0f, 0.0f, 1.0f );
glTranslatef ( BaseDriftX, BaseDriftY, 0.0f );
glColorPointer ( 4, GL_FLOAT, sizeof(vertex), &(m_pVertex[0].RGBA1b) );
//--------------------------------------------------------------------------
//-- Render Phase 1b - first base texture
if( m_ShowBaseB )
{
glDrawElements ( GL_TRIANGLES, m_IUsed, GL_UNSIGNED_SHORT, m_pIndex );
}
//--------------------------------------------------------------------------
//-- Cleanup from Phase 1 - base textures
glDisableClientState( GL_COLOR_ARRAY );
glPopMatrix ();
//--------------------------------------------------------------------------
//-- Initializations for Phase 2 - light map
// glColor4f ( 1.0f, 1.0f, 1.0f, 1.0f );
// glBindTexture ( GL_TEXTURE_2D, m_LightMapTexture.getGLName() );
// glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
// glBlendFunc ( GL_DST_COLOR, GL_ZERO );
// glMatrixMode ( GL_TEXTURE );
// glPushMatrix ();
// glLoadIdentity ();
// glTexGenfv ( GL_S, GL_OBJECT_PLANE, SLMap );
// glTexGenfv ( GL_T, GL_OBJECT_PLANE, TLMap );
//--------------------------------------------------------------------------
//-- Render Phase 2 - light map
// if( m_ShowLightMap )
// {
// glDrawElements ( GL_TRIANGLES, m_IUsed, GL_UNSIGNED_SHORT, m_pIndex );
// }
//--------------------------------------------------------------------------
//-- Cleanup from Phase 2 - light map
// glPopMatrix ();
glMatrixMode ( GL_MODELVIEW );
glDisable ( GL_TEXTURE_GEN_S );
glDisable ( GL_TEXTURE_GEN_T );
//--------------------------------------------------------------------------
//-- Initializations for Phase 3 - environment map
glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
glTexCoordPointer ( 2, GL_FLOAT, sizeof(vertex), &(m_pVertex[0].UV3) );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE );
glColor4f ( 1.0f, 1.0f, 1.0f, m_EnvMapIntensity );
glBindTexture ( GL_TEXTURE_2D, m_EnvMapTexture.getGLName() );
//--------------------------------------------------------------------------
//-- Render Phase 3 - environment map / specular
if( m_ShowEnvMap )
{
glDrawElements ( GL_TRIANGLES, m_IUsed, GL_UNSIGNED_SHORT, m_pIndex );
}
//--------------------------------------------------------------------------
//-- Initializations for Phase 4 - fog
glDisable ( GL_TEXTURE_2D );
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glEnableClientState ( GL_COLOR_ARRAY );
glColorPointer ( 4, GL_FLOAT, sizeof(vertex), &(m_pVertex[0].RGBA4) );
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
//--------------------------------------------------------------------------
//-- Render Phase 4 - fog
if( m_ShowFog )
{
glDrawElements ( GL_TRIANGLES, m_IUsed, GL_UNSIGNED_SHORT, m_pIndex );
}
//--------------------------------------------------------------------------
//-- Cleanup from all Phases
glDepthMask(GL_TRUE);
glDisable ( GL_BLEND );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
//--------------------------------------------------------------------------
//-- We're done with the rendering.
}
//==============================================================================

607
terrain/fluidSupport.cc Normal file
View file

@ -0,0 +1,607 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "terrain/Fluid.h"
#include "dgl/dgl.h"
#include <string.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;
}
//==============================================================================
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 );
// 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();
}
//==============================================================================
void fluid::SetInfo( f32& X0,
f32& Y0,
f32& SizeX,
f32& SizeY,
f32 SurfaceZ,
f32 WaveAmplitude,
f32& Opacity,
f32& EnvMapIntensity,
s32 RemoveWetEdges )
{
// 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 = Opacity;
m_EnvMapIntensity = EnvMapIntensity;
m_WaveFactor = m_WaveAmplitude * 0.25f;
// Place the "min" corner.
m_SquareX0 = (s32)((X0 / 8.0f) + 0.5f);
m_SquareY0 = (s32)((Y0 / 8.0f) + 0.5f);
// Constrain the range of values.
if( m_SquareX0 < 0.0f ) m_SquareX0 = 0;
if( m_SquareY0 < 0.0f ) m_SquareY0 = 0;
if( m_SquareX0 > 2040.0f ) m_SquareX0 = 2040;
if( m_SquareY0 > 2040.0f ) m_SquareY0 = 2040;
// Decide on the size of the block.
m_SquaresInX = (s32)((SizeX / 8.0f) + 0.5f);
m_SquaresInY = (s32)((SizeY / 8.0f) + 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.
if( m_HighResMode )
{
m_Step[4] = 32.0f;
m_Step[3] = 24.0f;
m_Step[2] = 16.0f;
m_Step[1] = 8.0f;
m_Step[0] = 0.0f;
}
else
{
m_Step[4] = 64.0f;
m_Step[3] = 48.0f;
m_Step[2] = 32.0f;
m_Step[1] = 16.0f;
m_Step[0] = 0.0f;
}
// Set values back into parameters for caller.
X0 = m_SquareX0 * 8.0f;
Y0 = m_SquareY0 * 8.0f;
SizeX = m_SquaresInX * 8.0f;
SizeY = m_SquaresInY * 8.0f;
// Recompute our masks.
RebuildMasks();
}
//==============================================================================
void fluid::SetTerrainData( u16* pTerrainData )
{
m_pTerrain = pTerrainData;
RebuildMasks();
}
//==============================================================================
void fluid::SetEyePosition( f32 X, f32 Y, f32 Z )
{
m_Eye.X = X;
m_Eye.Y = Y;
m_Eye.Z = Z;
}
//==============================================================================
// 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 EnvMap )
{
m_BaseTexture = Base;
m_EnvMapTexture = EnvMap;
}
//==============================================================================
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.
x = (s32)X;
y = (s32)Y;
// Compensate for min point offset.
x -= (m_SquareX0 << 3);
y -= (m_SquareY0 << 3);
// 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 ) );
}
//==============================================================================

1788
terrain/sky.cc Normal file

File diff suppressed because it is too large Load diff

261
terrain/sky.h Normal file
View file

@ -0,0 +1,261 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _SKY_H_
#define _SKY_H_
#ifndef _MMATH_H_
#include "math/mMath.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#ifndef _SCENESTATE_H_
#include "scenegraph/sceneState.h"
#endif
#ifndef _SCENEGRAPH_H_
#include "scenegraph/sceneGraph.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _MATERIALLIST_H_
#include "dgl/materialList.h"
#endif
#ifndef _GAMEBASE_H_
#include "game/gameBase.h"
#endif
#define MAX_NUM_LAYERS 3
#define MAX_BAN_POINTS 20
class SceneGraph;
class SceneState;
class SceneRenderImage;
enum SkyState
{
isDone = 0,
comingIn = 1,
goingOut = 2
};
typedef struct
{
bool StormOn;
bool FadeIn;
bool FadeOut;
S32 currentCloud;
F32 stormSpeed;
F32 stormDir;
S32 numCloudLayers;
F32 fadeSpeed;
SkyState stormState;
}StormInfo;
typedef struct
{
SkyState state;
F32 speed;
F32 time;
F32 fadeSpeed;
}StormCloudData;
typedef struct
{
SkyState state;
F32 speed;
F32 endPercentage;
F32 lastPercentage;
}StormFogVolume;
typedef struct
{
SkyState state;
U32 startTime;
F32 endPercentage;
F32 time;
S32 current;
U32 lastTime;
StormFogVolume volume[MaxFogVolumes];
}StormFogData;
//---------------------------------------------------------------------------
class Cloud
{
private:
Point3F mPoints[25];
Point2F mSpeed;
F32 mCenterHeight, mInnerHeight, mEdgeHeight;
F32 mAlpha[25];
S32 mDown, mOver;
static F32 mRadius;
F32 mLastTime, mOffset;
Point2F mBaseOffset, mTexCoords[25], mTextureScale;
TextureHandle mCloudHandle;
Point2F alphaCenter;
Point2F stormUpdate;
F32 stormAlpha[25];
F32 mAlphaSave[25];
static StormInfo mGStormData;
public:
Cloud();
~Cloud();
void setPoints();
void setHeights(F32 cHeight, F32 iHeight, F32 eHeight);
void setTexture(TextureHandle);
void setSpeed(Point2F);
void setTextPer(F32 cloudTextPer);
void updateCoord();
void calcAlpha();
void render(U32, U32, bool, S32, PlaneF*);
void updateStorm();
void calcStorm(F32 speed, F32 fadeSpeed);
void calcStormAlpha();
static void startStorm(SkyState);
static void setRadius(F32 rad) {mRadius = rad;}
void setRenderPoints(Point3F* renderPoints, Point2F* renderTexPoints, F32* renderAlpha, F32* renderSAlpha, S32 index);
void clipToPlane(Point3F* points, Point2F* texPoints, F32* alphaPoints, F32* sAlphaPoints, U32& rNumPoints, const PlaneF& rPlane);
};
//--------------------------------------------------------------------------
class Sky : public SceneObject
{
typedef SceneObject Parent;
private:
StormCloudData mStormCloudData;
StormFogData mStormFogData;
TextureHandle mSkyHandle[6];
StringTableEntry mCloudText[MAX_NUM_LAYERS];
F32 mCloudHeight[MAX_NUM_LAYERS];
F32 mCloudSpeed[MAX_NUM_LAYERS];
Cloud mCloudLayer[MAX_NUM_LAYERS];
F32 mRadius;
Point3F mPoints[10];
Point2F mTexCoord[4];
StringTableEntry mMaterialListName;
Point3F mSkyBoxPt;
Point3F mTopCenterPt;
Point3F mSpherePt;
ColorI mRealFogColor;
ColorI mRealSkyColor;
MaterialList mMaterialList;
ColorF mFogColor;
bool mSkyTexturesOn;
bool mRenderBoxBottom;
ColorF mSolidFillColor;
F32 mFogDistance;
F32 mVisibleDistance;
U32 mNumFogVolumes;
FogVolume mFogVolumes[MaxFogVolumes];
F32 mFogLine;
F32 mFogTime;
F32 mFogPercentage;
S32 mFogVolume;
S32 mRealFog;
F32 mRealFogMax;
F32 mRealFogMin;
F32 mRealFogSpeed;
bool mLastForce16Bit;
bool mLastForcePaletted;
SkyState mFogState;
S32 mNumCloudLayers;
Point3F mWindVelocity;
F32 mLastVisDisMod;
static bool smCloudsOn;
static bool smCloudOutlineOn;
static bool smSkyOn;
static S32 smNumCloudsOn;
bool mStormCloudsOn;
bool mStormFogOn;
bool mSetFog;
void calcPoints();
protected:
bool onAdd();
void onRemove();
void renderObject(SceneState*, SceneRenderImage*);
bool prepRenderImage(SceneState*, const U32, const U32, const bool);
void render(SceneState *state);
void calcAlphas_Heights(F32 zCamPos, F32 *banHeights, F32 *alphaBan, F32 DepthInFog);
void renderSkyBox(F32 lowerBanHeight, F32 alphaIn);
void calcBans(F32 *banHeights, Point3F banPoints[][MAX_BAN_POINTS], Point3F *cornerPoints);
void renderBans(F32 *alphaBan, F32 *banHeights, Point3F banPoints[][MAX_BAN_POINTS], Point3F *cornerPoints);
void inspectPostApply();
void startStorm();
void setVisibility();
void initSkyData();
void loadDml();
void updateFog();
void updateRealFog();
void startStormFog();
void setRenderPoints(Point3F* renderPoints, S32 index);
void calcTexCoords(Point2F* texCoords, Point3F* renderPoints, S32 index);
public:
bool mEffectPrecip;
Point2F mWindDir;
enum NetMaskBits {
InitMask = BIT(0),
VisibilityMask = BIT(1),
StormCloudMask = BIT(2),
StormFogMask = BIT(3),
StormRealFogMask = BIT(4),
WindMask = BIT(5),
StormCloudsOnMask = BIT(6),
StormFogOnMask = BIT(7)
};
enum Constants {
EnvMapMaterialOffset = 6,
CloudMaterialOffset = 7
};
Sky();
~Sky();
F32 getVisibleDistance() const { return mVisibleDistance; }
void stormCloudsShow(bool);
void stormFogShow(bool);
void stormCloudsOn(S32 state, F32 time);
void stormFogOn(F32 percentage, F32 time);
void stormRealFog(S32 value, F32 max, F32 min, F32 speed);
void setWindVelocity(const Point3F &);
Point3F getWindVelocity();
TextureHandle getEnvironmentMap() { return mMaterialList.getMaterial(EnvMapMaterialOffset); }
DECLARE_CONOBJECT(Sky);
static void initPersistFields();
static void consoleInit();
bool processArguments(S32 argc, const char **argv);
void unpackUpdate(NetConnection *, BitStream *stream);
U32 packUpdate(NetConnection *, U32 mask, BitStream *stream);
void updateVisibility();
};
#endif

116
terrain/sun.cc Normal file
View file

@ -0,0 +1,116 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "terrain/Sun.h"
#include "console/objectTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "Core/bitStream.h"
#include "console/consoleTypes.h"
#include "terrain/terrData.h"
#include "dgl/gBitmap.h"
#include "Math/mathIO.h"
IMPLEMENT_CO_NETOBJECT_V1(Sun);
//-----------------------------------------------------------------------------
Sun::Sun()
{
mNetFlags.set(Ghostable | ScopeAlways);
mTypeMask = EnvironmentObjectType;
mLight.mType = LightInfo::Vector;
mLight.mDirection.set(0.f, 0.707f, -0.707f);
mLight.mColor.set(0.7f, 0.7f, 0.7f);
mLight.mAmbient.set(0.3f, 0.3f, 0.3f);
}
//-----------------------------------------------------------------------------
void Sun::conformLight()
{
mLight.mDirection.normalize();
mLight.mColor.clamp();
mLight.mAmbient.clamp();
}
//-----------------------------------------------------------------------------
bool Sun::onAdd()
{
if(!Parent::onAdd())
return(false);
if(isClientObject())
Sim::getLightSet()->addObject(this);
else
conformLight();
return(true);
}
void Sun::registerLights(LightManager * lightManager, bool)
{
lightManager->addLight(&mLight);
}
//-----------------------------------------------------------------------------
void Sun::inspectPostApply()
{
conformLight();
setMaskBits(UpdateMask);
}
void Sun::unpackUpdate(NetConnection *, BitStream * stream)
{
if(stream->readFlag())
{
// direction -> color -> ambient
mathRead(*stream, &mLight.mDirection);
stream->read(&mLight.mColor.red);
stream->read(&mLight.mColor.green);
stream->read(&mLight.mColor.blue);
stream->read(&mLight.mColor.alpha);
stream->read(&mLight.mAmbient.red);
stream->read(&mLight.mAmbient.green);
stream->read(&mLight.mAmbient.blue);
stream->read(&mLight.mAmbient.alpha);
}
}
U32 Sun::packUpdate(NetConnection *, U32 mask, BitStream * stream)
{
if(stream->writeFlag(mask & UpdateMask))
{
// direction -> color -> ambient
mathWrite(*stream, mLight.mDirection);
stream->write(mLight.mColor.red);
stream->write(mLight.mColor.green);
stream->write(mLight.mColor.blue);
stream->write(mLight.mColor.alpha);
stream->write(mLight.mAmbient.red);
stream->write(mLight.mAmbient.green);
stream->write(mLight.mAmbient.blue);
stream->write(mLight.mAmbient.alpha);
}
return(0);
}
//-----------------------------------------------------------------------------
void Sun::initPersistFields()
{
Parent::initPersistFields();
addField("direction", TypePoint3F, Offset(mLight.mDirection, Sun));
addField("color", TypeColorF, Offset(mLight.mColor, Sun));
addField("ambient", TypeColorF, Offset(mLight.mAmbient, Sun));
}

54
terrain/sun.h Normal file
View file

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _SUN_H_
#define _SUN_H_
#ifndef _NETOBJECT_H_
#include "Sim/netObject.h"
#endif
#ifndef _COLOR_H_
#include "Core/color.h"
#endif
#ifndef _LIGHTMANAGER_H_
#include "sceneGraph/lightManager.h"
#endif
class Sun : public NetObject
{
private:
typedef NetObject Parent;
LightInfo mLight;
void conformLight();
public:
Sun();
// SimObject
bool onAdd();
void registerLights(LightManager *, bool);
//
void inspectPostApply();
static void initPersistFields();
// NetObject
enum NetMaskBits {
UpdateMask = BIT(0)
};
void unpackUpdate(NetConnection *, BitStream * stream);
U32 packUpdate(NetConnection *, U32 mask, BitStream * stream);
DECLARE_CONOBJECT(Sun);
};
#endif

1018
terrain/terrCollision.cc Normal file

File diff suppressed because it is too large Load diff

1420
terrain/terrData.cc Normal file

File diff suppressed because it is too large Load diff

414
terrain/terrData.h Normal file
View file

@ -0,0 +1,414 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _TERRDATA_H_
#define _TERRDATA_H_
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MPOINT_H_
#include "math/mPoint.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "sim/sceneObject.h"
#endif
#ifndef _RESMANAGER_H_
#include "core/resManager.h"
#endif
#ifndef _MATERIALLIST_H_
#include "dgl/materialList.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _CONVEX_H_
#include "collision/convex.h"
#endif
class GBitmap;
class TerrainFile;
class TerrainBlock;
class ColorF;
class Blender;
//--------------------------------------------------------------------------
class TerrainConvex: public Convex
{
friend class TerrainBlock;
TerrainConvex *square; // Alternate convex if square is concave
bool halfA; // Which half of square
bool split45; // Square split pattern
U32 squareId; // Used to match squares
U32 material;
Point3F point[4]; // 3-4 vertices
VectorF normal[2];
Box3F box; // Bounding box
public:
TerrainConvex() { mType = TerrainConvexType; }
TerrainConvex(const TerrainConvex& cv) {
mType = TerrainConvexType;
// Only a partial copy...
mObject = cv.mObject;
split45 = cv.split45;
squareId = cv.squareId;
material = cv.material;
point[0] = cv.point[0];
point[1] = cv.point[1];
point[2] = cv.point[2];
point[3] = cv.point[3];
normal[0] = cv.normal[0];
normal[1] = cv.normal[1];
box = cv.box;
}
Box3F getBoundingBox() const;
Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
Point3F support(const VectorF& v) const;
void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
void getPolyList(AbstractPolyList* list);
};
//--------------------------------------------------------------------------
struct GridSquare
{
U16 minHeight;
U16 maxHeight;
U16 heightDeviance;
U16 flags;
enum {
Split45 = 1,
Empty = 2,
HasEmpty = 4,
MaterialShift = 3,
MaterialStart = 8,
Material0 = 8,
Material1 = 16,
Material2 = 32,
Material3 = 64,
};
};
struct GridChunk
{
U16 heightDeviance[3]; // levels 0-1, 1-2, 2
U16 emptyFlags;
};
//--------------------------------------------------------------------------
class TerrainBlock : public SceneObject
{
typedef SceneObject Parent;
public:
struct Material {
enum Flags {
Plain = 0,
Rotate = 1,
FlipX = 2,
FlipXRotate = 3,
FlipY = 4,
FlipYRotate = 5,
FlipXY = 6,
FlipXYRotate = 7,
RotateMask = 7,
Empty = 8,
Modified = BIT(7),
// must not clobber TerrainFile::MATERIAL_GROUP_MASK bits!
PersistMask = BIT(7)
};
U8 flags;
U8 index;
};
enum {
BlockSize = 256,
BlockShift = 8,
LightmapSize = 512,
LightmapShift = 9,
ChunkSquareWidth = 64,
ChunkSize = 4,
ChunkDownShift = 2,
ChunkShift = BlockShift - ChunkDownShift,
BlockSquareWidth = 256,
SquareMaxPoints = 1024,
BlockMask = 255,
GridMapSize = 0x15555,
FlagMapWidth = 128, // flags map is for 2x2 squares
FlagMapMask = 127,
MaxMipLevel = 6,
NumBaseTextures = 16,
MaterialGroups = 8,
MaxEmptyRunPairs = 100
};
enum UpdateMaskBits {
InitMask = 1,
VisibilityMask = 2,
EmptyMask = 4,
};
Blender* mBlender;
TextureHandle baseTextures[NumBaseTextures];
TextureHandle mBaseMaterials[MaterialGroups];
TextureHandle mAlphaMaterials[MaterialGroups];
GBitmap *lightMap;
StringTableEntry *mMaterialFileName; // array from the file
TextureHandle mDynLightTexture;
U8 *mBaseMaterialMap;
Material *materialMap;
// fixed point height values
U16 *heightMap;
U16 *flagMap;
StringTableEntry mDetailTextureName;
TextureHandle mDetailTextureHandle;
StringTableEntry mTerrFileName;
Vector<S32> mEmptySquareRuns;
U32 mTextureCallbackKey;
void processTextureEvent(const U32 eventCode);
S32 mMPMIndex[10];
S32 mVertexBuffer;
private:
Resource<TerrainFile> mFile;
GridSquare *gridMap[BlockShift+1];
GridChunk *mChunkMap;
U32 mCRC;
public:
TerrainBlock();
~TerrainBlock();
void buildChunkDeviance(S32 x, S32 y);
void buildGridMap();
U32 getCRC() { return(mCRC); }
bool onAdd();
void onRemove();
void refreshMaterialLists();
void onEditorEnable();
void onEditorDisable();
void rebuildEmptyFlags();
bool unpackEmptySquares();
void packEmptySquares();
TextureHandle getDetailTextureHandle();
Material *getMaterial(U32 x, U32 y);
GridSquare *findSquare(U32 level, Point2I pos);
GridSquare *findSquare(U32 level, S32 x, S32 y);
GridChunk *findChunk(Point2I pos);
void setHeight(const Point2I & pos, float height);
U16 getHeight(U32 x, U32 y) { return heightMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)]; }
U16 *getHeightAddress(U32 x, U32 y) { return &heightMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)]; }
void setBaseMaterial(U32 x, U32 y, U8 matGroup);
U8 *getMaterialAlphaMap(U32 matIndex);
U8* getBaseMaterialAddress(U32 x, U32 y);
U8 getBaseMaterial(U32 x, U32 y);
U16 *getFlagMapPtr(S32 x, S32 y);
// a more useful getHeight for the public...
bool getHeight(const Point2F & pos, F32 * height);
bool getNormal(const Point2F & pos, Point3F * normal, bool normalize = true);
bool getNormalAndHeight(const Point2F & pos, Point3F * normal, F32 * height, bool normalize = true);
// only the editor currently uses this method - should always be using a ray to collide with
bool collideBox(const Point3F &start, const Point3F &end, RayInfo* info){return(castRay(start,end,info));}
private:
S32 squareSize;
public:
void setFile(Resource<TerrainFile> file);
bool save(const char* filename);
static void flushCache();
void relight(const ColorF &lightColor, const ColorF &ambient, const Point3F &lightDir);
S32 getSquareSize();
//--------------------------------------
// SceneGraph functions...
protected:
void setTransform(const MatrixF&);
bool prepRenderImage(SceneState*, const U32, const U32, const bool);
void renderObject(SceneState*, SceneRenderImage*);
//--------------------------------------
// collision info
private:
BSPTree *mTree;
S32 mHeightMin;
S32 mHeightMax;
public:
void buildConvex(const Box3F& box,Convex* convex);
bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere);
BSPNode *buildCollisionBSP(BSPTree *tree, const Box3F &box, const SphereF &sphere);
bool castRay(const Point3F &start, const Point3F &end, RayInfo* info);
bool castRayI(const Point3F &start, const Point3F &end, RayInfo* info, bool emptyCollide);
bool castRayBlock(const Point3F &pStart, const Point3F &pEnd, Point2I blockPos, U32 level, F32 invDeltaX, F32 invDeltaY, F32 startT, F32 endT, RayInfo *info, bool);
private:
BSPNode *buildSquareTree(S32 y, S32 x);
BSPNode *buildXTree(S32 y, S32 xStart, S32 xEnd);
public:
bool buildMaterialMap();
void buildMipMap();
void setBaseMaterials(S32 argc, const char *argv[]);
// bool loadBaseMaterials();
bool initMMXBlender();
// private helper
private:
bool mCollideEmpty;
public:
DECLARE_CONOBJECT(TerrainBlock);
static void initPersistFields();
static void consoleInit();
U32 packUpdate(NetConnection *, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *, BitStream *stream);
};
//--------------------------------------
class TerrainFile : public ResourceInstance
{
public:
enum Constants {
FILE_VERSION = 2,
MATERIAL_GROUP_MASK = 0x7
};
TerrainFile();
~TerrainFile();
U16 mHeightMap[TerrainBlock::BlockSize * TerrainBlock::BlockSize];
U8 mBaseMaterialMap[TerrainBlock::BlockSize * TerrainBlock::BlockSize];
GridSquare mGridMapBase[TerrainBlock::GridMapSize];
GridSquare *mGridMap[TerrainBlock::BlockShift+1];
GridChunk mChunkMap[TerrainBlock::ChunkSquareWidth * TerrainBlock::ChunkSquareWidth];
U16 mFlagMap[TerrainBlock::FlagMapWidth * TerrainBlock::FlagMapWidth];
TerrainBlock::Material mMaterialMap[TerrainBlock::BlockSquareWidth * TerrainBlock::BlockSquareWidth];
// DMMNOTE: This loads all the alpha maps, whether or not they are used. Possible to
// restrict to only the used versions?
StringTableEntry mMaterialFileName[TerrainBlock::MaterialGroups];
U8* mMaterialAlphaMap[TerrainBlock::MaterialGroups];
bool save(const char *filename);
void buildChunkDeviance(S32 x, S32 y);
void buildGridMap();
void heightDevLine(U32 p1x, U32 p1y, U32 p2x, U32 p2y, U32 pmx, U32 pmy, U16 *devPtr);
inline GridSquare *findSquare(U32 level, Point2I pos)
{
return mGridMap[level] + (pos.x >> level) + ((pos.y>>level) << (TerrainBlock::BlockShift - level));
}
inline U16 getHeight(U32 x, U32 y)
{
return mHeightMap[(x & TerrainBlock::BlockMask) + ((y & TerrainBlock::BlockMask) << TerrainBlock::BlockShift)];
}
inline TerrainBlock::Material *getMaterial(U32 x, U32 y)
{
return &mMaterialMap[(x & TerrainBlock::BlockMask) + ((y & TerrainBlock::BlockMask) << TerrainBlock::BlockShift)];
}
};
//--------------------------------------------------------------------------
inline U16 *TerrainBlock::getFlagMapPtr(S32 x, S32 y)
{
return flagMap + ((x >> 1) & TerrainBlock::FlagMapMask) +
((y >> 1) & TerrainBlock::FlagMapMask) * TerrainBlock::FlagMapWidth;
}
inline GridSquare *TerrainBlock::findSquare(U32 level, S32 x, S32 y)
{
return gridMap[level] + ((x & TerrainBlock::BlockMask) >> level) + (((y & TerrainBlock::BlockMask) >> level) << (TerrainBlock::BlockShift - level));
}
inline GridSquare *TerrainBlock::findSquare(U32 level, Point2I pos)
{
return gridMap[level] + (pos.x >> level) + ((pos.y>>level) << (BlockShift - level));
}
inline GridChunk *TerrainBlock::findChunk(Point2I pos)
{
return mChunkMap + (pos.x >> ChunkDownShift) + ((pos.y>>ChunkDownShift) << ChunkShift);
}
inline TerrainBlock::Material *TerrainBlock::getMaterial(U32 x, U32 y)
{
return materialMap + x + (y << BlockShift);
}
inline TextureHandle TerrainBlock::getDetailTextureHandle()
{
return mDetailTextureHandle;
}
inline S32 TerrainBlock::getSquareSize()
{
return squareSize;
}
inline U8 TerrainBlock::getBaseMaterial(U32 x, U32 y)
{
return mBaseMaterialMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)];
}
inline U8* TerrainBlock::getBaseMaterialAddress(U32 x, U32 y)
{
return &mBaseMaterialMap[(x & BlockMask) + ((y & BlockMask) << BlockShift)];
}
inline U8* TerrainBlock::getMaterialAlphaMap(U32 matIndex)
{
if (mFile->mMaterialAlphaMap[matIndex] == NULL) {
mFile->mMaterialAlphaMap[matIndex] = new U8[TerrainBlock::BlockSize * TerrainBlock::BlockSize];
dMemset(mFile->mMaterialAlphaMap[matIndex], 0, TerrainBlock::BlockSize * TerrainBlock::BlockSize);
}
return mFile->mMaterialAlphaMap[matIndex];
}
// 11.5 fixed point - gives us a height range from 0->2048 in 1/32 inc
inline F32 fixedToFloat(U16 val)
{
return F32(val) * 0.03125f;
}
inline U16 floatToFixed(F32 val)
{
return U16(val * 32.0);
}
extern ResourceInstance *constructTerrainFile(Stream &stream);
#endif

766
terrain/terrLighting.cc Normal file
View file

@ -0,0 +1,766 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "terrain/terrData.h"
#include "Math/mMath.h"
#include "dgl/dgl.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/gBitmap.h"
#include "dgl/gTexManager.h"
#include "terrain/terrRender.h"
//void TerrainRender::setupColorAdder(F32 rSquared, F32 r, F32 g, F32 b)
//{
// mCurrentR = r;
// mCurrentG = g;
// mCurrentB = b;
// mCurrentRSq = rSquared;
//}
//
//void TerrainRender::addColor(Color &c, F32 dSquared)
//{
// if(dSquared > mCurrentRSq)
// return;
// F32 scale = 255.0f * ( 1 - (dSquared / mCurrentRSq) );
// c.r += (S32)(mCurrentR * scale);
// c.g += (S32)(mCurrentG * scale);
// c.b += (S32)(mCurrentB * scale);
//}
//
//inline U16 TerrainRender::convertColorBack(Color &c)
//{
// U16 ret;
// if(c.r > 255)
// ret = 0xF800;
// else
// ret = (c.r >> 3) << 11;
// if(c.g > 255)
// ret |= 0x07E0;
// else
// ret |= (c.g >> 2) << 5;
//
// if(c.b > 255)
// ret |= 0x001F;
// else
// ret |= c.b >> 3;
//
// return ret;
//}
//
//inline void TerrainRender::convertColor(Color &c, U16 lix)
//{
// S32 r = lix >> 11;
// S32 g = (lix >> 5) & 0x3F;
// S32 b = lix & 0x1F;
//
// c.r = (r << 3) | ( r >> 2);
// c.g = (g << 2) | ( g >> 4);
// c.b = (b << 3) | ( b >> 2);
//}
//
//void TerrainRender::lightExpandHorz(F32 x, F32 ySquared, F32 dx, S32 j, S32 i, S32 shift)
//{
// S32 size = 1 << shift;
// F32 scale = 1 / F32(size);
//
// F32 z = mLMABuffer[j][i].z;
// F32 dz = (mLMABuffer[j][i+size].z - z) * scale;
//
// for(S32 count = 1; count < size; count++)
// {
// x += dx;
// z += dz;
//
// addColor(mLMABuffer[j][i + count], x * x + z * z + ySquared);
// }
//}
//
//void TerrainRender::lightExpandVert(F32 y, F32 xSquared, F32 dy, S32 j, S32 i, S32 shift)
//{
// S32 size = 1 << shift;
// F32 scale = 1 / F32(size);
//
// F32 z = mLMABuffer[j][i].z;
// F32 dz = (mLMABuffer[j+size][i].z - z) * scale;
//
// for(S32 count = 1; count < size; count++)
// {
// y += dy;
// z += dz;
//
// mLMABuffer[j+count][i].z = z;
// addColor(mLMABuffer[j+count][i], y * y + z * z + xSquared);
// }
//}
//
//void TerrainRender::lmExpandHorz(S32 y, S32 x, S32 shift)
//{
// S32 size = 1 << shift;
// Color d;
// Color c = mLMABuffer[y][x];
// d.r = (mLMABuffer[y][x+size].r - c.r) >> shift;
// d.g = (mLMABuffer[y][x+size].g - c.g) >> shift;
// d.b = (mLMABuffer[y][x+size].b - c.b) >> shift;
//
// for(S32 i = 1; i < size; i++)
// {
// c.r += d.r;
// c.g += d.g;
// c.b += d.b;
//
// mLMABuffer[y][x+i] = c;
// }
//}
//
//void TerrainRender::lmExpandVert(S32 y, S32 x, S32 shift)
//{
// S32 size = 1 << shift;
// Color d;
// Color c = mLMABuffer[y][x];
// d.r = (mLMABuffer[y+size][x].r - c.r) >> shift;
// d.g = (mLMABuffer[y+size][x].g - c.g) >> shift;
// d.b = (mLMABuffer[y+size][x].b - c.b) >> shift;
//
// for(S32 i = 1; i < size; i++)
// {
// c.r += d.r;
// c.g += d.g;
// c.b += d.b;
//
// mLMABuffer[y+i][x] = c;
// }
//}
//void TerrainRender::expandDynamicLight(TerrLightInfo &tl, S32 x, S32 y, S32 level)
//{
// S32 i, j;
// setupColorAdder(tl.radiusSquared, tl.r, tl.g, tl.b);
//
// if(DynamicLightMapShift - level <= 0)
// {
// S32 step = 1 << (level - DynamicLightMapShift);
// S32 count = DynamicLightMapSize;
// for(j = 0; j <= count; j++)
// {
// for(i = 0; i <= count; i++)
// {
// F32 xp = (x + i*step) * mSquareSize - tl.pos.x;
// F32 yp = (y + j*step) * mSquareSize - tl.pos.y;
// F32 zp = fixedToFloat(mCurrentBlock->getHeight((x + i) & TerrainBlock::BlockMask,
// (y + j) & TerrainBlock::BlockMask)) - tl.pos.z;
//
// addColor(mLMABuffer[j][i], xp * xp + yp * yp + zp * zp);
// }
// }
// }
// else
// {
// S32 delt = DynamicLightMapShift - level;
// S32 size = 1 << delt;
// S32 count = 1 << level;
//
// F32 delta = mSquareSize / F32(size);
//
// for(j = 0; j <= count; j++)
// {
// for(i = 0; i <= count; i++)
// {
// F32 xp = (x + i) * mSquareSize - tl.pos.x;
// F32 yp = (y + j) * mSquareSize - tl.pos.y;
// F32 zp = fixedToFloat(mCurrentBlock->getHeight((x + i) & TerrainBlock::BlockMask, (y + j) & TerrainBlock::BlockMask)) - tl.pos.z;
// mLMABuffer[j << delt][i << delt].z = zp;
//
// addColor(mLMABuffer[j << delt][i << delt], xp * xp + yp * yp + zp * zp);
// if(i != 0)
// lightExpandHorz(xp - mSquareSize, yp * yp, delta, j << delt, (i - 1) << delt, delt);
// if(j != 0)
// lightExpandVert(yp - mSquareSize, xp * xp, delta, (j-1) << delt, i << delt, delt);
// if(i != 0 && j != 0)
// {
// F32 xp = (x + i - 1) * mSquareSize - tl.pos.x;
// for(S32 k = 1; k < size; k++)
// {
// F32 yp = (y + j - 1) * mSquareSize + k * delta - tl.pos.y;
// lightExpandHorz(xp, yp * yp, delta, ((j - 1) << delt) + k, (i-1) << delt, delt);
// }
// }
// }
// }
// }
//}
//void TerrainRender::expandLightMap(S32 /*x*/, S32 /*y*/, S32 /*level*/)
//{
// S32 i, j;
// x &= TerrainBlock::BlockMask;
// y &= TerrainBlock::BlockMask;
// GBitmap *lightMap = mCurrentBlock->lightMapHandle.getBitmap();
// if(DynamicLightMapShift - level <= 0)
// {
// S32 step = (level - DynamicLightMapShift);
// S32 count = DynamicLightMapSize;
// S32 lightMask = lightMap->getWidth() - 1;
//
// for(j = 0; j <= count; j++)
// {
// for(i = 0; i <= count; i++)
// {
// U16 lix = * ((U16 *) lightMap->
// getAddress( (x + (i << step)) & lightMask,
// (y + (j << step)) & lightMask ) );
// convertColor(mLMABuffer[j][i], lix);
// }
// }
// }
// else
// {
// S32 delt = DynamicLightMapShift - level;
// S32 size = 1 << delt;
// S32 count = 1 << level;
//
// for(j = 0; j <= count; j++)
// {
// U16 *pixPtr = (U16 *) lightMap->getAddress(x, y + j);
// for(i = 0; i <= count; i++)
// {
// convertColor(mLMABuffer[j << delt][i << delt], *pixPtr++);
// if(i != 0)
// lmExpandHorz(j << delt, (i-1) << delt, delt);
// if(j != 0)
// lmExpandVert((j-1) << delt, i << delt, delt);
// if(i != 0 && j != 0)
// {
// for(S32 k = 1; k < size; k++)
// lmExpandHorz(((j-1) << delt) + k, (i-1) << delt, delt);
// }
// }
// }
// }
//}
U32 TerrainRender::TestSquareLights(GridSquare *sq, S32 level, Point2I pos, U32 lightMask)
{
U32 retMask = 0;
F32 blockX = pos.x * mSquareSize + mBlockPos.x;
F32 blockY = pos.y * mSquareSize + mBlockPos.y;
F32 blockZ = fixedToFloat(sq->minHeight);
F32 blockSize = mSquareSize * (1 << level);
F32 blockHeight = fixedToFloat(sq->maxHeight - sq->minHeight);
Point3F vec;
for(S32 i = 0; (lightMask >> i) != 0; i++)
{
if(lightMask & (1 << i))
{
Point3F *pos = &mTerrainLights[i].pos;
// test the visibility of this light to box
// find closest point on box to light and test
if(pos->z < blockZ)
vec.z = blockZ - pos->z;
else if(pos->z > blockZ + blockHeight)
vec.z = pos->z - (blockZ + blockHeight);
else
vec.z = 0;
if(pos->x < blockX)
vec.x = blockX - pos->x;
else if(pos->x > blockX + blockSize)
vec.x = pos->x - (blockX + blockSize);
else
vec.x = 0;
if(pos->y < blockY)
vec.y = blockY - pos->y;
else if(pos->y > blockY + blockSize)
vec.y = pos->y - (blockY + blockSize);
else
vec.y = 0;
F32 dist = vec.len();
if(dist < mTerrainLights[i].radius)
retMask |= (1 << i);
}
}
return retMask;
}
//jff tmp
#include "sceneGraph/lightManager.h"
#include "sceneGraph/sceneGraph.h"
void TerrainRender::buildLightArray()
{
static Vector<LightInfo*> lights;
//
lights.clear();
gClientSceneGraph->getLightManager()->getBestLights(mClipPlane, mNumClipPlanes, mCamPos, lights, MaxTerrainLights);
// create terrain lights from these...
U32 curIndex = 0;
for(U32 i = 0; i < lights.size(); i++)
{
if(lights[i]->mType != LightInfo::Point)
continue;
// set the 'fo
TerrLightInfo & info = mTerrainLights[curIndex++];
mCurrentBlock->getWorldTransform().mulP(lights[i]->mPos, &info.pos);
info.radius = lights[i]->mRadius; //jff
info.radiusSquared = info.radius * info.radius;
//
info.r = lights[i]->mColor.red;
info.g = lights[i]->mColor.green;
info.b = lights[i]->mColor.blue;
Point3F dVec = mCamPos - lights[i]->mPos;
info.distSquared = mDot(dVec, dVec);
}
mDynamicLightCount = curIndex;
/*const TMat3F & mat = mCamera->getTOW();
TSSceneLighting * sceneLights = trs.renderContext->getLights();
SphereF instSphere ( mat.p, 1000000.0f );
sceneLights->prepare ( instSphere, mat );
S32 lightCount = 0;
TSSceneLighting::iterator ptr;
for ( ptr = sceneLights->begin(); ptr != sceneLights->end() && lightCount < MaxTerrainLights ; ptr++ )
{
TSLight *tsl = *ptr;
if(tsl->fLight.fType == TS::Light::LightPoint && !tsl->isStaticLight())
{
mTerrainLights[lightCount].pos = tsl->fLight.fPosition;
Point3F dVec = mCamPos - mTerrainLights[lightCount].pos;
mTerrainLights[lightCount].distSquared = m_dot(dVec, dVec);
mTerrainLights[lightCount].radius = tsl->fLight.fRange;
mTerrainLights[lightCount].radiusSquared = tsl->fLight.fRange * tsl->fLight.fRange;
mTerrainLights[lightCount].r = tsl->fLight.fRed;
mTerrainLights[lightCount].g = tsl->fLight.fGreen;
mTerrainLights[lightCount].b = tsl->fLight.fBlue;
lightCount++;
}
}
trs.dynamicLightCount = min(lightCount, (S32)MaxVisibleLights);
*/
}
static U16 convertColor(ColorF color)
{
if(color.red > 1)
color.red = 1;
if(color.green > 1)
color.green = 1;
if(color.blue > 1)
color.blue = 1;
return (U32(color.red * 31) << 11) |
(U32(color.green * 31) << 6) |
(U32(color.blue * 31) << 1) | 1;
}
void TerrainBlock::relight(const ColorF &lightColor, const ColorF &ambient, const Point3F &lightDir)
{
if(lightDir.x == 0 && lightDir.y == 0)
return;
if(!lightMap)
return;
S32 generateLevel = Con::getIntVariable("$pref::sceneLighting::terrainGenerateLevel", 0);
generateLevel = mClamp(generateLevel, 0, 4);
U32 generateDim = TerrainBlock::LightmapSize << generateLevel;
U32 generateShift = TerrainBlock::LightmapShift + generateLevel;
U32 generateMask = generateDim - 1;
F32 zStep;
F32 frac;
Point2I blockColStep;
Point2I blockRowStep;
Point2I blockFirstPos;
Point2I lmapFirstPos;
F32 terrainDim = F32(getSquareSize()) * F32(TerrainBlock::BlockSize);
F32 stepSize = F32(getSquareSize()) / F32(generateDim / TerrainBlock::BlockSize);
if(mFabs(lightDir.x) >= mFabs(lightDir.y))
{
if(lightDir.x > 0)
{
zStep = lightDir.z / lightDir.x;
frac = lightDir.y / lightDir.x;
blockColStep.set(1, 0);
blockRowStep.set(0, 1);
blockFirstPos.set(0, 0);
lmapFirstPos.set(0, 0);
}
else
{
zStep = -lightDir.z / lightDir.x;
frac = -lightDir.y / lightDir.x;
blockColStep.set(-1, 0);
blockRowStep.set(0, 1);
blockFirstPos.set(255, 0);
lmapFirstPos.set(TerrainBlock::LightmapSize-1, 0);
}
}
else
{
if(lightDir.y > 0)
{
zStep = lightDir.z / lightDir.y;
frac = lightDir.x / lightDir.y;
blockColStep.set(0, 1);
blockRowStep.set(1, 0);
blockFirstPos.set(0, 0);
lmapFirstPos.set(0, 0);
}
else
{
zStep = -lightDir.z / lightDir.y;
frac = -lightDir.x / lightDir.y;
blockColStep.set(0, -1);
blockRowStep.set(1, 0);
blockFirstPos.set(0, 255);
lmapFirstPos.set(0, TerrainBlock::LightmapSize-1);
}
}
zStep *= stepSize;
F32 * heightArray = new F32[generateDim];
S32 fracStep = -1;
if(frac < 0)
{
fracStep = 1;
frac = -frac;
}
F32 * nextHeightArray = new F32[generateDim];
F32 oneMinusFrac = 1 - frac;
U32 blockShift = generateShift - TerrainBlock::BlockShift;
U32 lightmapShift = generateShift - TerrainBlock::LightmapShift;
U32 blockStep = 1 << blockShift;
U32 blockMask = (1 << blockShift) - 1;
U32 lightmapMask = (1 << lightmapShift) - 1;
Point2I bp = blockFirstPos;
F32 terrainHeights[2][TerrainBlock::BlockSize];
U32 i;
// get first set of heights
for(i = 0; i < TerrainBlock::BlockSize; i++, bp += blockRowStep)
terrainHeights[0][i] = fixedToFloat(getHeight(bp.x, bp.y));
// get second set of heights
bp = blockFirstPos + blockColStep;
for(i = 0; i < TerrainBlock::BlockSize; i++, bp += blockRowStep)
terrainHeights[1][i] = fixedToFloat(getHeight(bp.x, bp.y));
F32 * pTerrainHeights[2];
pTerrainHeights[0] = static_cast<F32*>(terrainHeights[0]);
pTerrainHeights[1] = static_cast<F32*>(terrainHeights[1]);
F32 heightStep = 1.f / blockStep;
F32 terrainZRowStep[2][TerrainBlock::BlockSize];
F32 terrainZColStep[TerrainBlock::BlockSize];
// fill in the row steps
for(i = 0; i < TerrainBlock::BlockSize; i++)
{
terrainZRowStep[0][i] = (terrainHeights[0][(i+1) & TerrainBlock::BlockMask] - terrainHeights[0][i]) * heightStep;
terrainZRowStep[1][i] = (terrainHeights[1][(i+1) & TerrainBlock::BlockMask] - terrainHeights[1][i]) * heightStep;
terrainZColStep[i] = (terrainHeights[1][i] - terrainHeights[0][i]) * heightStep;
}
// get first row of process heights
for(i = 0; i < generateDim; i++)
{
U32 bi = i >> blockShift;
heightArray[i] = terrainHeights[0][bi] + (i & blockMask) * terrainZRowStep[0][bi];
}
bp = blockFirstPos;
if(generateDim == TerrainBlock::BlockSize)
bp += blockColStep;
// generate the initial run
U32 x, y;
for(x = 1; x < generateDim; x++)
{
U32 xmask = x & blockMask;
// generate new height step rows?
if(!xmask)
{
F32 * tmp = pTerrainHeights[0];
pTerrainHeights[0] = pTerrainHeights[1];
pTerrainHeights[1] = tmp;
bp += blockColStep;
Point2I bwalk = bp;
for(i = 0; i < TerrainBlock::BlockSize; i++, bwalk += blockRowStep)
pTerrainHeights[1][i] = fixedToFloat(getHeight(bwalk.x, bwalk.y));
// fill in the row steps
for(i = 0; i < TerrainBlock::BlockSize; i++)
{
terrainZRowStep[0][i] = (pTerrainHeights[0][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[0][i]) * heightStep;
terrainZRowStep[1][i] = (pTerrainHeights[1][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[1][i]) * heightStep;
terrainZColStep[i] = (pTerrainHeights[1][i] - pTerrainHeights[0][i]) * heightStep;
}
}
Point2I bwalk = bp - blockRowStep;
for(y = 0; y < generateDim; y++)
{
U32 ymask = y & blockMask;
if(!ymask)
bwalk += blockRowStep;
U32 bi = y >> blockShift;
U32 binext = (bi + 1) & TerrainBlock::BlockMask;
F32 height;
// 135?
if((bwalk.x ^ bwalk.y) & 1)
{
U32 xsub = blockStep - xmask;
if(xsub > ymask) // bottom
height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] +
ymask * terrainZRowStep[0][bi];
else // top
height = pTerrainHeights[1][bi] - xsub * terrainZColStep[binext] +
ymask * terrainZRowStep[1][bi];
}
else
{
if(xmask > ymask) // bottom
height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] +
ymask * terrainZRowStep[1][bi];
else // top
height = pTerrainHeights[0][bi] + xmask * terrainZColStep[binext] +
ymask * terrainZRowStep[0][bi];
}
F32 intHeight = heightArray[y] * oneMinusFrac + heightArray[(y + fracStep) & generateMask] * frac + zStep;
nextHeightArray[y] = getMax(height, intHeight);
}
// swap the height rows
for(y = 0; y < generateDim; y++)
heightArray[y] = nextHeightArray[y];
}
F32 squareSize = getSquareSize();
F32 squaredSquareSize = squareSize * squareSize;
F32 lexelDim = squareSize * F32(TerrainBlock::BlockSize) / F32(TerrainBlock::LightmapSize);
// calculate normal runs
Point3F normals[2][TerrainBlock::BlockSize];
Point3F * pNormals[2];
pNormals[0] = static_cast<Point3F*>(normals[0]);
pNormals[1] = static_cast<Point3F*>(normals[1]);
// calculate the normal lookup table
F32 * normTable = new F32 [blockStep * blockStep * 4];
Point2F corners[4] = {
Point2F(0.f, 0.f),
Point2F(1.f, 0.f),
Point2F(1.f, 1.f),
Point2F(0.f, 1.f)
};
U32 idx = 0;
F32 step = 1.f / blockStep;
F32 halfStep = step / 2.f;
Point2F pos(halfStep, halfStep);
// fill it
for(x = 0; x < blockStep; x++, pos.x += step, pos.y = halfStep)
for(y = 0; y < blockStep; y++, pos.y += step)
for(U32 i = 0; i < 4; i++, idx++)
normTable[idx] = 1.f - getMin(Point2F(pos - corners[i]).len(), 1.f);
// fill first column
bp = blockFirstPos;
for(x = 0; x < TerrainBlock::BlockSize; x++)
{
terrainHeights[0][x] = fixedToFloat(getHeight(bp.x, bp.y));
Point2F pos(bp.x * squareSize, bp.y * squareSize);
getNormal(pos, &pNormals[1][x]);
bp += blockRowStep;
}
// get swapped on first pass
pTerrainHeights[0] = static_cast<F32*>(terrainHeights[1]);
pTerrainHeights[1] = static_cast<F32*>(terrainHeights[0]);
ColorF colors[TerrainBlock::LightmapSize];
F32 ratio = F32(1 << lightmapShift);
F32 inverseRatioSquared = 1.f / (ratio * ratio);
// walk it...
bp = blockFirstPos - blockColStep;
Point2I lp = lmapFirstPos - blockColStep;
for(x = 0; x < generateDim; x++)
{
U32 xmask = x & blockMask;
// process lightmap?
if(!(x & lightmapMask))
{
dMemset(colors, 0, sizeof(ColorF) * TerrainBlock::LightmapSize);
lp += blockColStep;
}
// generate new runs?
if(!xmask)
{
bp += blockColStep;
// do the normals
Point3F * temp = pNormals[0];
pNormals[0] = pNormals[1];
pNormals[1] = temp;
// fill the row
Point2I bwalk = bp + blockColStep;
for(U32 i = 0; i < TerrainBlock::BlockSize; i++)
{
Point2F pos(bwalk.x * squareSize, bwalk.y * squareSize);
getNormal(pos, &pNormals[1][i]);
bwalk += blockRowStep;
}
// do the heights
F32 * tmp = pTerrainHeights[0];
pTerrainHeights[0] = pTerrainHeights[1];
pTerrainHeights[1] = tmp;
bwalk = bp + blockColStep;
for(i = 0; i < TerrainBlock::BlockSize; i++, bwalk += blockRowStep)
pTerrainHeights[1][i] = fixedToFloat(getHeight(bwalk.x, bwalk.y));
// fill in the row steps
for(i = 0; i < TerrainBlock::BlockSize; i++)
{
terrainZRowStep[0][i] = (pTerrainHeights[0][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[0][i]) * heightStep;
terrainZRowStep[1][i] = (pTerrainHeights[1][(i+1) & TerrainBlock::BlockMask] - pTerrainHeights[1][i]) * heightStep;
terrainZColStep[i] = (pTerrainHeights[1][i] - pTerrainHeights[0][i]) * heightStep;
}
}
Point2I bwalk = bp - blockRowStep;
for(y = 0; y < generateDim; y++)
{
U32 ymask = y & blockMask;
if(!ymask)
bwalk += blockRowStep;
U32 bi = y >> blockShift;
U32 binext = (bi + 1) & TerrainBlock::BlockMask;
F32 height;
// 135?
if((bwalk.x ^ bwalk.y) & 1)
{
U32 xsub = blockStep - xmask;
if(xsub > ymask) // bottom
height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] +
ymask * terrainZRowStep[0][bi];
else // top
height = pTerrainHeights[1][bi] - xsub * terrainZColStep[binext] +
ymask * terrainZRowStep[1][bi];
}
else
{
if(xmask > ymask) // bottom
height = pTerrainHeights[0][bi] + xmask * terrainZColStep[bi] +
ymask * terrainZRowStep[1][bi];
else // top
height = pTerrainHeights[0][bi] + xmask * terrainZColStep[binext] +
ymask * terrainZRowStep[0][bi];
}
F32 intHeight = heightArray[y] * oneMinusFrac + heightArray[(y + fracStep) & generateMask] * frac + zStep;
ColorF & col = colors[y >> lightmapShift];
// non shadowed?
if(height >= intHeight)
{
U32 idx = (xmask + (ymask << blockShift)) << 2;
Point3F normal;
normal = pNormals[0][bi] * normTable[idx++];
normal += pNormals[0][binext] * normTable[idx++];
normal += pNormals[1][binext] * normTable[idx++];
normal += pNormals[1][bi] * normTable[idx];
normal.normalize();
nextHeightArray[y] = height;
F32 colorScale = mDot(normal, lightDir);
if(colorScale >= 0)
col += ambient;
else
col += (ambient + lightColor * -colorScale);
}
else
{
nextHeightArray[y] = intHeight;
col += ambient;
}
}
for(y = 0; y < generateDim; y++)
heightArray[y] = nextHeightArray[y];
// do some lighting stuff?
if(!((x+1) & lightmapMask))
{
Point2I lwalk = lp;
U32 mask = TerrainBlock::LightmapSize - 1;
for(U32 i = 0; i < TerrainBlock::LightmapSize; i++)
{
U16 * ptr = (U16*)lightMap->getAddress(lp.x & mask, lp.y & mask);
*ptr = convertColor(colors[i]);
lp += blockRowStep;
}
}
}
delete [] normTable;
delete [] heightArray;
delete [] nextHeightArray;
}

2298
terrain/terrRender.cc Normal file

File diff suppressed because it is too large Load diff

342
terrain/terrRender.h Normal file
View file

@ -0,0 +1,342 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _TERRRENDER_H_
#define _TERRRENDER_H_
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _WATERBLOCK_H_
#include "terrain/waterBlock.h"
#endif
struct EmitChunk;
struct AllocatedTexture {
U32 level;
S32 x, y;
F32 distance;
EmitChunk *list;
TextureHandle handle;
AllocatedTexture *next;
AllocatedTexture *previous;
AllocatedTexture *nextLink;
U32 mipLevel;
AllocatedTexture()
{
next = previous = NULL;
}
inline void unlink()
{
AssertFatal(next && previous, "Invalid unlink.");
next->previous = previous;
previous->next = next;
next = previous = NULL;
}
inline void linkAfter(AllocatedTexture *t)
{
AssertFatal(next == NULL && previous == NULL, "Cannot link a non-null next & prev");
next = t->next;
previous = t;
t->next->previous = this;
t->next = this;
}
};
struct Render2Point : public Point3F
{
F32 d;
// ColorI color;
};
struct EdgePoint : public Point3F
{
ColorI detailColor;
F32 haze;
F32 distance;
F32 fogRed;
F32 fogGreen;
};
struct ChunkCornerPoint : public EdgePoint
{
U32 pointIndex;
U32 xfIndex;
};
struct EdgeParent
{
ChunkCornerPoint *p1, *p2;
};
struct ChunkScanEdge : public EdgeParent
{
ChunkCornerPoint *mp;
EdgeParent *e1, *e2;
};
struct ChunkEdge : public EdgeParent
{
U32 xfIndex;
U32 pointIndex;
U32 pointCount;
EdgePoint pt[3];
EmitChunk *c1, *c2;
};
struct EmitChunk
{
ChunkEdge *edge[4];
S32 subDivLevel;
F32 growFactor;
S32 x, y;
S32 gridX, gridY;
U32 emptyFlags;
bool clip;
U32 lightMask;
EmitChunk *next;
bool renderDetails;
};
struct SquareStackNode2
{
U32 clipFlags;
U32 lightMask;
Point2I pos;
U32 level;
bool texAllocated;
};
struct SquareStackNode
{
U32 clipFlags;
U32 lightMask;
Point2I pos;
U32 level;
bool texAllocated;
EdgeParent *top, *right, *bottom, *left;
};
struct TerrLightInfo
{
Point3F pos; // world position
F32 radius; // radius of the light
F32 radiusSquared; // radius^2
F32 r, g, b;
F32 distSquared; // distance to camera
};
//struct ScanEdge
//{
// U16 p1, p2, mp;
// U16 firstSubEdge; // two sub edges for each edge, mp = InvalidPointIndex if none
//};
enum EmptyFlags {
SquareEmpty_0_0 = (1 << 0),
SquareEmpty_1_0 = (1 << 1),
SquareEmpty_2_0 = (1 << 2),
SquareEmpty_3_0 = (1 << 3),
SquareEmpty_0_1 = (1 << 4),
SquareEmpty_1_1 = (1 << 5),
SquareEmpty_2_1 = (1 << 6),
SquareEmpty_3_1 = (1 << 7),
SquareEmpty_0_2 = (1 << 8),
SquareEmpty_1_2 = (1 << 9),
SquareEmpty_2_2 = (1 << 10),
SquareEmpty_3_2 = (1 << 11),
SquareEmpty_0_3 = (1 << 12),
SquareEmpty_1_3 = (1 << 13),
SquareEmpty_2_3 = (1 << 14),
SquareEmpty_3_3 = (1 << 15),
CornerEmpty_0_0 = SquareEmpty_0_0 | SquareEmpty_1_0 | SquareEmpty_0_1 | SquareEmpty_1_1,
CornerEmpty_1_0 = SquareEmpty_2_0 | SquareEmpty_3_0 | SquareEmpty_2_1 | SquareEmpty_3_1,
CornerEmpty_0_1 = SquareEmpty_0_2 | SquareEmpty_1_2 | SquareEmpty_0_3 | SquareEmpty_1_3,
CornerEmpty_1_1 = SquareEmpty_2_2 | SquareEmpty_3_2 | SquareEmpty_2_3 | SquareEmpty_3_3,
};
struct RenderPoint : public Point3F
{
F32 dist;
F32 haze; // also used as grow factor
};
enum {
MaxClipPlanes = 8, // left, right, top, bottom - don't need far tho...
MaxTerrainMaterials = 256,
EdgeStackSize = 1024, // value for water/terrain edge stack size.
MaxWaves = 8,
MaxDetailLevel = 9,
MaxMipLevel = 8,
MaxTerrainLights = 64,
MaxVisibleLights = 31,
ClipPlaneMask = (1 << MaxClipPlanes) - 1,
FarSphereMask = 0x80000000,
FogPlaneBoxMask = 0x40000000,
VertexBufferSize = 65 * 65 + 1000,
AllocatedTextureCount = 16 + 64 + 256 + 1024 + 4096,
SmallMipLevel = 6
};
struct Color
{
S32 r, g, b;
F32 z;
};
class SceneState;
struct TerrainRender
{
static MatrixF mCameraToObject;
static AllocatedTexture mTextureFrameListHead;
static AllocatedTexture mTextureFrameListTail;
static AllocatedTexture mTextureFreeListHead;
static AllocatedTexture mTextureFreeListTail;
static AllocatedTexture mTextureFreeBigListHead;
static AllocatedTexture mTextureFreeBigListTail;
static U32 mTextureSlopSize;
static Vector<TextureHandle> mTextureFreeList;
static S32 mTextureMinSquareSize;
static SceneState *mSceneState;
static AllocatedTexture *mCurrentTexture;
static TerrainBlock *mCurrentBlock;
static S32 mSquareSize;
static F32 mScreenSize;
static U32 mFrameIndex;
static U32 mNumClipPlanes;
static AllocatedTexture *mTextureGrid[AllocatedTextureCount];
static AllocatedTexture **mTextureGridPtr[5];
static Point2F mBlockPos;
static Point2I mBlockOffset;
static Point2I mTerrainOffset;
static PlaneF mClipPlane[MaxClipPlanes];
static Point3F mCamPos;
static TextureHandle* mGrainyTexture;
static U32 mDynamicLightCount;
static bool mEnableTerrainDetails;
static bool mEnableTerrainDynLights;
static F32 mPixelError;
static TerrLightInfo mTerrainLights[MaxTerrainLights];
static F32 mScreenError;
static F32 mMinSquareSize;
static F32 mFarDistance;
static S32 mDynamicTextureCount;
static S32 mTextureSpaceUsed;
static S32 mLevelZeroCount;
static S32 mFullMipCount;
static S32 mStaticTextureCount;
static S32 mUnusedTextureCount;
static S32 mStaticTSU;
static S32 mSquareSeqAdd[256];
static U32 mNewGenTextureCount;
static F32 mInvFarDistance;
static F32 mInvHeightRange;
static U32 mMipCap;
static bool mRenderingCommander;
static ColorF mFogColor;
static bool mRenderOutline;
static U32 mMaterialCount;
static GBitmap* mBlendBitmap;
static void (*transformPoint)(U32 point, U32 p1, U32 p2);
static void init();
static void shutdown();
// static void allocReset();
// static void* alloc(U32 byteSize);
static void allocRenderEdges(U32 edgeCount, EdgeParent **dest, bool renderEdge);
static void subdivideChunkEdge(ChunkScanEdge *e, Point2I pos, bool chunkEdge);
static void processCurrentBlock2(SceneState* state, EdgeParent *topEdge, EdgeParent *rightEdge, EdgeParent *bottomEdge, EdgeParent *leftEdge);
static ChunkCornerPoint *allocInitialPoint(Point3F pos);
static ChunkCornerPoint *allocPoint(Point2I pos);
static void emitTerrChunk(SquareStackNode *n, F32 squareDistance, U32 lightMask, bool farClip, bool useDetails);
static void renderChunkOutline(EmitChunk *chunk);
static void renderChunkCommander(EmitChunk *chunk);
static void fixEdge(ChunkEdge *edge, S32 x, S32 y, S32 dx, S32 dy);
static void drawTriFan(U32 vCount, U32 *indexBuffer);
static U32 constructPoint(S32 x, S32 y);
static U32 interpPoint(U32 p1, U32 p2, S32 x, S32 y, F32 growFactor);
static void addEdge(ChunkEdge *edge);
static void clipStart(EdgePoint *pt, U32 index);
static void clipInsert(EdgePoint *pt, U32 index);
static void clipEnd();
static void clip(U32 triFanStart);
static F32 getScreenError() { return(mScreenError); }
static void setScreenError(F32 error) { mScreenError = error; }
static void flushCache();
static void transformTerrainPoint(U32 point, U32 p1, U32 p2);
static U8 getClipFlags(Point2F &pos, U8 clipMask);
static void allocTerrTexture(Point2I pos, U32 level, U32 mipLevel, bool vis, F32 distance);
static void freeTerrTexture(AllocatedTexture *texture);
static void buildBlendMap(AllocatedTexture *texture);
static U32 TestSquareLights(GridSquare *sq, S32 level, Point2I pos, U32 lightMask);
static S32 TestSquareVisibility(Point3F &min, Point3F &max, S32 clipMask, F32 expand);
static S32 allocEdges(S32 count);
static void clampEdge(S32 edge);
static void subdivideEdge(S32 edge, Point2I pos);
static F32 getSquareDistance(const Point3F& minPoint, const Point3F& maxPoint,
F32* zDiff);
static bool subdivideSquare(GridSquare *sq, S32 level, Point2I pos);
static void emitTerrSquare(SquareStackNode *n, U32 flags);
static void emitFullMipSquare(SquareStackNode *n, U32 flags);
static void emitNonzeroSquare(SquareStackNode *n, U32 flags, U32 mipLevel);
static void emitLevelZeroSquare(SquareStackNode *n, U32 flags, U32 material, U32 mipLevel);
static void buildTextureCoor();
static void renderCurrentBlock(S32 firstSquare, S32 lastSquare);
static void textureRecurse(SquareStackNode *n);
static void processCurrentBlock(S32 topEdge, S32 rightEdge, S32 bottomEdge, S32 leftEdge);
static void buildLightArray();
static void buildClippingPlanes(bool flipClipPlanes);
static void buildDetailTable();
static void renderXFCache();
static void renderBlock(TerrainBlock *, SceneState *state);
static void textureRecurse2(SquareStackNode2 *n);
static void tesselate0(SquareStackNode2 *n, U32 flags);
static void tesselate1(SquareStackNode2 *n);
static void tesselate2(SquareStackNode2 *n);
static void tesselate3(SquareStackNode2 *n);
static void farclip(SquareStackNode2 *n, U32 flags);
static void render2();
static void processBlockStack(SquareStackNode2 *stack, S32 curStackSize);
protected:
static void doesWaterBlockSubmergeCamera(SceneObject *sceneObj, S32 key);
};
#endif

655
terrain/terrRender2.cc Normal file
View file

@ -0,0 +1,655 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "terrain/terrData.h"
#include "Math/mMath.h"
#include "dgl/dgl.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "dgl/gBitmap.h"
#include "terrain/terrRender.h"
#include "dgl/materialList.h"
#include "sceneGraph/sceneState.h"
#include "terrain/waterBlock.h"
#include "terrain/blender.h"
#include "Sim/frameAllocator.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sgUtil.h"
Render2Point gEmitPoints2[65536];
U16 gIndexArray2[131000];
Render2Point *mEmitPoints2;
U16 *mIndexArray2;
U32 mCurrPoint2;
U32 mPrimQ[16384];
struct PrimitiveList
{
enum {
MaxCount = 256,
};
U32 count;
U16 *primList[MaxCount];
PrimitiveList *next;
static void alloc(PrimitiveList **ph)
{
PrimitiveList *next = *ph;
*ph = (PrimitiveList *) FrameAllocator::alloc(sizeof(PrimitiveList));
(*ph)->count = 0;
(*ph)->next = next;
}
};
PrimitiveList *blendList[TerrainBlock::MaterialGroups];
PrimitiveList *baseList[TerrainBlock::MaterialGroups];
inline void addPrimitive(U16 *start, PrimitiveList **lp)
{
if(!(*lp) || ( (*lp)->count == PrimitiveList::MaxCount) )
PrimitiveList::alloc(lp);
(*lp)->primList[(*lp)->count++] = start;
}
#if 0
void addPrimitives(U32 flags, U16 *start)
{
if(flags & GridSquare::Material0)
{
addPrimitive(start, &baseList[0]);
goto testBlend1;
}
if(flags & GridSquare::Material1)
{
addPrimitive(start, &baseList[1]);
goto testBlend2;
}
if(flags & GridSquare::Material2)
{
addPrimitive(start, &baseList[2]);
goto testBlend3;
}
if(flags & GridSquare::Material3)
addPrimitive(start, &baseList[3]);
return;
testBlend1:
if(flags & GridSquare::Material1)
addPrimitive(start, &blendList[1]);
testBlend2:
if(flags & GridSquare::Material2)
addPrimitive(start, &blendList[2]);
testBlend3:
if(flags & GridSquare::Material3)
addPrimitive(start, &blendList[3]);
}
#else
#define addPrimitives(x,y)
#endif
void TerrainRender::tesselate3(SquareStackNode2 *n)
{
// generate 9x9 array of points, and emit the 16 fans for the block
U32 i = 0, x, y;
Render2Point * ep = mEmitPoints2 + mCurrPoint2;
F32 xp, yp, step = mSquareSize;
yp = n->pos.y * mSquareSize + mBlockPos.y;
F32 xs = n->pos.x * mSquareSize + mBlockPos.x;
U16 *flp = mCurrentBlock->getFlagMapPtr(n->pos.x, n->pos.y);
for(y = 0; y < 9; y++)
{
xp = xs;
U16 *hp = mCurrentBlock->getHeightAddress(n->pos.x, n->pos.y + y);
for(x = 0; x < 8; x++)
{
ep->x = xp;
ep->y = yp;
ep->z = fixedToFloat(*hp++);
ep++;
xp += step;
}
ep->x = xp;
ep->y = yp;
ep->z = fixedToFloat(mCurrentBlock->getHeight(n->pos.x + x, n->pos.y + y));
ep++;
if((~y) & 1)
{
ep[-9].d = (ep[-9] - mCamPos).len();
ep[-7].d = (ep[-7] - mCamPos).len();
ep[-5].d = (ep[-5] - mCamPos).len();
ep[-3].d = (ep[-3] - mCamPos).len();
ep[-1].d = (ep[-1] - mCamPos).len();
ep[-8].d = (ep[-9].d + ep[-7].d) * 0.5;
ep[-6].d = (ep[-7].d + ep[-5].d) * 0.5;
ep[-4].d = (ep[-5].d + ep[-3].d) * 0.5;
ep[-2].d = (ep[-3].d + ep[-1].d) * 0.5;
if(y > 1)
{
Render2Point *r1 = ep - 27;
Render2Point *r2 = ep - 18;
Render2Point *r3 = ep - 9;
r2[0].d = (r1[0].d + r3[0].d) * 0.5;
r2[2].d = (r1[2].d + r3[2].d) * 0.5;
r2[4].d = (r1[4].d + r3[4].d) * 0.5;
r2[6].d = (r1[6].d + r3[6].d) * 0.5;
r2[8].d = (r1[8].d + r3[8].d) * 0.5;
r2[1].d = (r2[0].d + r2[2].d) * 0.5;
r2[3].d = (r2[2].d + r2[4].d) * 0.5;
r2[5].d = (r2[4].d + r2[6].d) * 0.5;
r2[7].d = (r2[6].d + r2[8].d) * 0.5;
}
}
yp += step;
}
for(y = 0; y < 4; y++)
{
for(x = 0; x < 4; x++)
{
addPrimitives(*flp, mIndexArray2);
flp++;
mIndexArray2[0] = GL_TRIANGLE_FAN;
mIndexArray2[1] = 10;
mIndexArray2[2] = mCurrPoint2 + 10;
mIndexArray2[3] = mCurrPoint2 + 18;
mIndexArray2[4] = mCurrPoint2 + 19;
mIndexArray2[5] = mCurrPoint2 + 20;
mIndexArray2[6] = mCurrPoint2 + 11;
mIndexArray2[7] = mCurrPoint2 + 2;
mIndexArray2[8] = mCurrPoint2 + 1;
mIndexArray2[9] = mCurrPoint2;
mIndexArray2[10] = mCurrPoint2 + 9;
mIndexArray2[11] = mCurrPoint2 + 18;
mIndexArray2 += 12;
mCurrPoint2 += 2;
}
flp += TerrainBlock::FlagMapWidth - 4;
mCurrPoint2 += 10;
}
mCurrPoint2 += 9;
}
void TerrainRender::tesselate2(SquareStackNode2 *n)
{
U32 i = 0, x, y;
Render2Point * ep = mEmitPoints2 + mCurrPoint2;
F32 xp, yp, step = mSquareSize;
F32 xs = n->pos.x * mSquareSize + mBlockPos.x;
yp = n->pos.y * mSquareSize + mBlockPos.y;
U16 *flp = mCurrentBlock->getFlagMapPtr(n->pos.x, n->pos.y);
for(y = 0; y < 5; y++)
{
xp = xs;
for(x = 0; x < 5; x++)
{
ep->x = xp;
ep->y = yp;
ep->z = fixedToFloat(mCurrentBlock->getHeight(n->pos.x + x, n->pos.y + y));
ep->d = (*ep - mCamPos).len();
ep++;
xp += step;
}
yp += step;
}
for(y = 0; y < 2; y++)
{
for(x = 0; x < 2; x++)
{
addPrimitives(*flp, mIndexArray2);
flp++;
mIndexArray2[0] = GL_TRIANGLE_FAN;
mIndexArray2[1] = 10;
mIndexArray2[2] = mCurrPoint2 + 6;
mIndexArray2[3] = mCurrPoint2 + 10;
mIndexArray2[4] = mCurrPoint2 + 11;
mIndexArray2[5] = mCurrPoint2 + 12;
mIndexArray2[6] = mCurrPoint2 + 7;
mIndexArray2[7] = mCurrPoint2 + 2;
mIndexArray2[8] = mCurrPoint2 + 1;
mIndexArray2[9] = mCurrPoint2;
mIndexArray2[10] = mCurrPoint2 + 5;
mIndexArray2[11] = mCurrPoint2 + 10;
mIndexArray2 += 12;
mCurrPoint2 += 2;
}
mCurrPoint2 += 6;
flp += TerrainBlock::FlagMapWidth - 2;
}
mCurrPoint2 += 5;
}
void TerrainRender::tesselate1(SquareStackNode2 *n)
{
U32 i = 0, x, y;
Render2Point * ep = mEmitPoints2 + mCurrPoint2;
F32 xp, yp, step = mSquareSize;
F32 xs = n->pos.x * mSquareSize + mBlockPos.x;
yp = n->pos.y * mSquareSize + mBlockPos.y;
for(y = 0; y < 3; y++)
{
xp = xs;
for(x = 0; x < 3; x++)
{
ep->x = xp;
ep->y = yp;
ep->z = fixedToFloat(mCurrentBlock->getHeight(n->pos.x + x, n->pos.y + y));
ep->d = (*ep - mCamPos).len();
ep++;
xp += step;
}
yp += step;
}
addPrimitives( *(mCurrentBlock->getFlagMapPtr(n->pos.x, n->pos.y)), mIndexArray2);
mIndexArray2[0] = GL_TRIANGLE_FAN;
mIndexArray2[1] = 10;
mIndexArray2[2] = mCurrPoint2 + 4;
mIndexArray2[3] = mCurrPoint2 + 6;
mIndexArray2[4] = mCurrPoint2 + 7;
mIndexArray2[5] = mCurrPoint2 + 8;
mIndexArray2[6] = mCurrPoint2 + 5;
mIndexArray2[7] = mCurrPoint2 + 2;
mIndexArray2[8] = mCurrPoint2 + 1;
mIndexArray2[9] = mCurrPoint2;
mIndexArray2[10] = mCurrPoint2 + 3;
mIndexArray2[11] = mCurrPoint2 + 6;
mIndexArray2 += 12;
mCurrPoint2 += 9;
}
void TerrainRender::tesselate0(SquareStackNode2 *n, U32 flags)
{
}
void TerrainRender::farclip(SquareStackNode2 *n, U32 flags)
{
}
void TerrainRender::render2()
{
glFrontFace(GL_CW);
glEnable(GL_CULL_FACE);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(Render2Point), mEmitPoints2);
TextureHandle hazeTexture = gClientSceneGraph->getFogTexture();
F32 invLevel = 1 / F32(mSquareSize << 2);
Point4F texGenS = Point4F(invLevel, 0, 0, 0);
Point4F texGenT = Point4F(0, invLevel, 0, 0);
glColor4f(1,1,1,1);
glClientActiveTextureARB(GL_TEXTURE0_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texGenS);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texGenT);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
invLevel = 1 / F32(mSquareSize << 8);
texGenS = Point4F(invLevel, 0, 0, 0);
texGenT = Point4F(0, invLevel, 0, 0);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGenfv(GL_S, GL_OBJECT_PLANE, texGenS);
glTexGenfv(GL_T, GL_OBJECT_PLANE, texGenT);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBlendFunc(GL_ONE, GL_ONE);
glLockArraysEXT(0, mCurrPoint2);
//glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//glTexCoordPointer(2, GL_FLOAT, sizeof(Render2Point), &mEmitPoints2->z);
//glMatrixMode(GL_TEXTURE_MATRIX);
for(S32 i = 0; i < TerrainBlock::MaterialGroups; i++)
{
TextureHandle t = mCurrentBlock->mBaseMaterials[i];
TextureHandle a = mCurrentBlock->mAlphaMaterials[i];
if(!bool(t))
break;
glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D, t.getGLName());
glActiveTextureARB(GL_TEXTURE1_ARB);
glBindTexture(GL_TEXTURE_2D, a.getGLName());
glDisable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
for(PrimitiveList *walk = baseList[i]; walk; walk = walk->next)
for(S32 j = 0; j < walk->count; j++)
glDrawElements(walk->primList[j][0], walk->primList[j][1], GL_UNSIGNED_SHORT, walk->primList[j] + 2);
glEnable(GL_BLEND);
for(PrimitiveList *walk = blendList[i]; walk; walk = walk->next)
for(S32 j = 0; j < walk->count; j++)
glDrawElements(walk->primList[j][0], walk->primList[j][1], GL_UNSIGNED_SHORT, walk->primList[j] + 2);
}
glUnlockArraysEXT();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClientActiveTextureARB(GL_TEXTURE1_ARB);
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_2D);
// last pass is the haze pass
glClientActiveTextureARB(GL_TEXTURE0_ARB);
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glBindTexture(GL_TEXTURE_2D, hazeTexture.getGLName());
static F32 texMatrix[16] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
// get the height range from the root of the block map
GridSquare *sq = mCurrentBlock->findSquare(TerrainBlock::BlockShift, Point2I(0,0));
F32 heightOffset = fixedToFloat(sq->minHeight);
F32 heightRange = fixedToFloat(sq->maxHeight) - heightOffset;
texMatrix[4] = - 1 / mFarDistance;
texMatrix[12] = 1;
texMatrix[1] = 1 / heightRange;
texMatrix[13] = -heightOffset / heightRange;
glMatrixMode(GL_TEXTURE);
glLoadMatrixf(texMatrix);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, sizeof(Render2Point), &(mEmitPoints2[0].z));
glLockArraysEXT(0, mCurrPoint2);
U16 *ar = gIndexArray2;
while(ar != mIndexArray2)
{
glDrawElements(ar[0], ar[1], GL_UNSIGNED_SHORT, ar + 2);
ar += ar[1] + 2;
}
glUnlockArraysEXT();
glLoadIdentity();
glDisable(GL_BLEND);
//glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
}
static S32 getPower(S32 x)
{
// Returns 2^n (the highest bit).
S32 i = 0;
if (x)
do
i++;
while (x >>= 1);
return i;
}
void TerrainRender::textureRecurse2(SquareStackNode2 *stack)
{
S32 curStackSize = 1;
Point3F minPoint, maxPoint;
F32 squareDistance;
while(curStackSize)
{
SquareStackNode2 *n = stack + curStackSize - 1;
// see if it's visible
Point2I pos = n->pos;
S32 squareSz = mSquareSize << n->level;
GridSquare *sq = mCurrentBlock->findSquare(n->level, pos);
minPoint.set(mSquareSize * pos.x + mBlockPos.x,
mSquareSize * pos.y + mBlockPos.y,
fixedToFloat(sq->minHeight));
maxPoint.set(minPoint.x + (mSquareSize << n->level),
minPoint.y + (mSquareSize << n->level),
fixedToFloat(sq->maxHeight));
F32 zDiff;
squareDistance = getSquareDistance(minPoint, maxPoint, &zDiff);
// holes only in the primary terrain block
if (squareDistance >= mFarDistance ||
((sq->flags & GridSquare::Empty) && mBlockPos.x == 0 && mBlockPos.y == 0))
{
curStackSize--;
continue;
}
// first check the level - if its 3 or less, we have to just make a bitmap:
// level 3 == 8x8 square - 8x8 * 16x16 == 128x128
S32 mipLevel = 7;
if(n->level > 6)
goto norecalloc;
if(n->level != mTextureMinSquareSize)
{
// get the mip level of the square and see if we're in range
if(squareDistance > 0.001)
{
S32 size = S32(dglProjectRadius(squareDistance + (squareSz >> 1), squareSz));
mipLevel = getPower(size * 0.75);
//if(n->level >= 6)
//{
// if(mipLevel - n->level > 2)
// goto norecalloc;
//}
//else
if(mipLevel > 7) // too big for this square
goto norecalloc;
}
else
goto norecalloc;
}
allocTerrTexture(n->pos, n->level, mipLevel, false, squareDistance);
curStackSize--;
continue;
norecalloc:
// split it up:
S32 nextLevel = n->level - 1;
for(S32 i = 0; i < 4; i++)
n[i].level = nextLevel;
S32 squareHalfSize = 1 << nextLevel;
// push in reverse order of processing.
n[3].pos = pos;
n[2].pos.set(pos.x + squareHalfSize, pos.y);
n[1].pos.set(pos.x, pos.y + squareHalfSize);
n[0].pos.set(pos.x + squareHalfSize, pos.y + squareHalfSize);
curStackSize += 3;
}
}
void TerrainRender::processBlockStack(SquareStackNode2 *stack, S32 curStackSize)
{
Point3F minPoint, maxPoint;
F32 squareDistance;
mCurrPoint2 = 0;
mIndexArray2 = gIndexArray2;
mEmitPoints2 = gEmitPoints2;
U32 i;
for(i = 0; i < TerrainBlock::MaterialGroups; i++)
{
blendList[i] = 0;
baseList[i] = 0;
}
for(i = 0; i < curStackSize; i++)
stack[i].texAllocated = false;
F32 worldToScreenScale = dglProjectRadius(1,1);
F32 zeroFullMipDistance = (mSquareSize * worldToScreenScale) / (1 << 3) - (mSquareSize >> 1);
F32 zeroSmallMipDistance = (mSquareSize * worldToScreenScale) / (1 << 6) - (mSquareSize >> 1);
F32 zeroDetailDistance = (mSquareSize * worldToScreenScale) / (1 << 6) - (mSquareSize >> 1);
while(curStackSize)
{
SquareStackNode2 *n = stack + curStackSize - 1;
// see if it's visible
GridSquare *sq = mCurrentBlock->findSquare(n->level, n->pos.x, n->pos.y);
minPoint.set(mSquareSize * n->pos.x + mBlockPos.x,
mSquareSize * n->pos.y + mBlockPos.y,
fixedToFloat(sq->minHeight));
maxPoint.set(minPoint.x + (mSquareSize << n->level),
minPoint.y + (mSquareSize << n->level),
fixedToFloat(sq->maxHeight));
// holes only in the primary terrain block
if ((sq->flags & GridSquare::Empty) && mBlockPos.x == 0 && mBlockPos.y == 0)
{
curStackSize--;
continue;
}
F32 zDiff;
S32 nextClipFlags = 0;
squareDistance = getSquareDistance(minPoint, maxPoint, &zDiff);
if(n->clipFlags)
{
if(n->clipFlags & FarSphereMask)
{
if(squareDistance >= mFarDistance)
{
curStackSize--;
continue;
}
S32 squareSz = mSquareSize << n->level;
if(squareDistance + maxPoint.z - minPoint.z + squareSz + squareSz > mFarDistance)
nextClipFlags |= FarSphereMask;
}
nextClipFlags |= TestSquareVisibility(minPoint, maxPoint, n->clipFlags, mSquareSize);
if(nextClipFlags == -1)
{
//if(!n->texAllocated)
// textureRecurse(n);
// trivially rejected, so pop it off the stack
curStackSize--;
continue;
}
}
if(!n->texAllocated)
{
S32 squareSz = mSquareSize << n->level;
// first check the level - if its 3 or less, we have to just make a bitmap:
// level 3 == 8x8 square - 8x8 * 16x16 == 128x128
if(n->level > 6)
goto notexalloc;
S32 mipLevel = 7;
if(n->level != mTextureMinSquareSize)
{
// get the mip level of the square and see if we're in range
if(squareDistance > 0.001)
{
S32 size = S32(dglProjectRadius(squareDistance + (squareSz >> 1), squareSz));
mipLevel = getPower(size * 0.75);
if(mipLevel > 7) // too big for this square
goto notexalloc;
}
else
goto notexalloc;
}
allocTerrTexture(n->pos, n->level, mipLevel, true, squareDistance);
n->texAllocated = true;
}
notexalloc:
if(n->lightMask)
n->lightMask = TestSquareLights(sq, n->level, n->pos, n->lightMask);
if(n->level <= 3)
{
bool noHoles = !(sq->flags & GridSquare::HasEmpty);
if(n->level == 3)
{
if(nextClipFlags == 0 && noHoles)
{
tesselate3(n);
curStackSize--;
continue;
}
}
else
{
if(!(nextClipFlags & FarSphereMask) && noHoles)
{
if(n->level == 2)
tesselate2(n);
else if(n->level == 1)
tesselate1(n);
else
tesselate0(n, sq->flags);
curStackSize--;
continue;
}
}
if(n->level == 0)
{
// it's gotta be far clipped...
farclip(n, sq->flags);
curStackSize--;
continue;
}
}
Point2I pos = n->pos;
// subdivide this square and throw it on the stack
S32 nextLevel = n->level - 1;
S32 squareHalfSize = 1 << nextLevel;
n->level = nextLevel;
n->clipFlags = nextClipFlags;
for(S32 i = 1; i < 4; i++)
{
n[i].level = nextLevel;
n[i].clipFlags = nextClipFlags;
n[i].lightMask = n->lightMask;
}
// push in reverse order of processing.
n[3].pos = pos;
n[2].pos.set(pos.x + squareHalfSize, pos.y);
n[1].pos.set(pos.x, pos.y + squareHalfSize);
n[0].pos.set(pos.x + squareHalfSize, pos.y + squareHalfSize);
curStackSize += 3;
}
}

724
terrain/waterBlock.cc Normal file
View file

@ -0,0 +1,724 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "terrain/waterBlock.h"
#include "console/consoleTypes.h"
#include "sceneGraph/sceneGraph.h"
#include "sceneGraph/sceneState.h"
#include "Core/bitStream.h"
#include "Math/mBox.h"
#include "dgl/dgl.h"
#include "Core/color.h"
#include "terrain/terrData.h"
#include "terrain/terrRender.h"
#include "Math/mathIO.h"
#include "sceneGraph/sgUtil.h"
#include "audio/audioDataBlock.h"
//==============================================================================
IMPLEMENT_CO_NETOBJECT_V1(WaterBlock);
//==============================================================================
bool WaterBlock::mCameraSubmerged = false;
U32 WaterBlock::mSubmergedType = 0;
TextureHandle WaterBlock::mSubmergeTexture[WC_NUM_SUBMERGE_TEX];
//==============================================================================
// I know this is a bit of a hack. I've done this in order to avoid a coupling
// between the fluid and the rest of the system.
//
static SceneState* pSceneState = NULL;
static F32 FogFunction( F32 Distance, F32 DeltaZ )
{
return( pSceneState->getHazeAndFog( Distance, DeltaZ ) );
}
//==============================================================================
WaterBlock::WaterBlock()
{
mNetFlags.set(Ghostable | ScopeAlways);
mTypeMask = WaterObjectType;
mObjBox.min.set( 0, 0, 0 );
mObjBox.max.set( 1, 1, 1 );
mLiquidType = eOceanWater;
mDensity = 1;
mViscosity = 15;
mWaveMagnitude = 1.0f;
mSurfaceTexture = TextureHandle();
mSurfaceOpacity = 0.75f;
mEnvMapTexture = TextureHandle();
mEnvMapIntensity = 1.0f;
mRemoveWetEdges = true;
mAudioEnvironment = 0;
// lets be good little programmers and initialize our data!
mSurfaceName = NULL;
mEnvMapName = NULL;
dMemset( mSubmergeName, 0, sizeof( mSubmergeName ) );
mpTerrain = NULL;
mSurfaceZ = 0.0f;
mFluid.SetFogFn( FogFunction );
}
//==============================================================================
WaterBlock::~WaterBlock()
{
}
//==============================================================================
void WaterBlock::SnagTerrain( SceneObject* sceneObj, S32 key )
{
WaterBlock* pWater = (WaterBlock*)key;
pWater->mpTerrain = dynamic_cast<TerrainBlock*>(sceneObj);
}
//==============================================================================
void WaterBlock::UpdateFluidRegion( void )
{
MatrixF M;
Point3F P;
P = mObjToWorld.getPosition();
P.x += 1024.0f;
P.y += 1024.0f;
mSurfaceZ = P.z + mObjScale.z;
mFluid.SetInfo( P.x, P.y,
mObjScale.x, mObjScale.y,
mSurfaceZ,
mWaveMagnitude,
mSurfaceOpacity,
mEnvMapIntensity,
mRemoveWetEdges );
P.x -= 1024.0f;
P.y -= 1024.0f;
M.identity();
M.setPosition( P );
Parent::setTransform( M );
resetWorldBox();
if( isServerObject() )
setMaskBits(1);
}
//==============================================================================
bool WaterBlock::onAdd()
{
if( !isClientObject() )
{
// Make sure that the Fluid has had a chance to tweak the values.
UpdateFluidRegion();
}
if( !Parent::onAdd() )
return false;
if(!mRemoveWetEdges)
{
mObjBox.min.set(-1e4, -1e4, 0);
mObjBox.max.set( 1e4, 1e4, 1);
}
resetWorldBox();
addToScene();
if( isClientObject() )
{
// load textures
mSurfaceTexture = TextureHandle( mSurfaceName, MeshTexture );
mEnvMapTexture = TextureHandle( mEnvMapName, MeshTexture );
for( int i=0; i<WC_NUM_SUBMERGE_TEX; i++ )
{
if( mSubmergeName[i] && mSubmergeName[i][0] )
{
mLocalSubmergeTexture[i] = TextureHandle( mSubmergeName[i], MeshTexture );
mSubmergeTexture[i] = mLocalSubmergeTexture[i];
}
}
// Register these textures with the Fluid.
mFluid.SetTextures( mSurfaceTexture,
mEnvMapTexture );
}
return( true );
}
//==============================================================================
void WaterBlock::onRemove()
{
// clear static texture handles
for( int i=0; i<WC_NUM_SUBMERGE_TEX; i++ )
{
mSubmergeTexture[i] = NULL;
}
removeFromScene();
Parent::onRemove();
}
//==============================================================================
bool WaterBlock::onSceneAdd( SceneGraph* pGraph )
{
if( Parent::onSceneAdd(pGraph) )
{
// Attempt to get the terrain.
if( (mpTerrain == NULL) && (mContainer != NULL) )
{
mContainer->findObjects( mWorldBox, (U32)TerrainObjectType, SnagTerrain, (S32)this );
if( mpTerrain )
mFluid.SetTerrainData( mpTerrain->heightMap );
}
return( true );
}
return( false );
}
//==============================================================================
// The incoming matrix transforms the water into the WORLD. This includes any
// offset in the terrain, so the translation can be negative. We need to get
// the translation to be expressed in "terrain square" terms. The terrain
// offset is always -1024,-1024.
void WaterBlock::setTransform( const MatrixF &mat )
{
mObjToWorld = mat;
UpdateFluidRegion();
}
//==============================================================================
void WaterBlock::setScale( const VectorF & scale )
{
mObjScale = scale;
UpdateFluidRegion();
}
//==============================================================================
bool WaterBlock::prepRenderImage( SceneState* state,
const U32 stateKey,
const U32,
const bool )
{
// Attempt to get the terrain.
if( (mpTerrain == NULL) && (mContainer != NULL) )
{
mContainer->findObjects( mWorldBox, (U32)TerrainObjectType, SnagTerrain, (S32)this );
if( mpTerrain )
mFluid.SetTerrainData( mpTerrain->heightMap );
}
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if (state->isObjectRendered(this)) {
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::Plane;
image->plane = PlaneF(0, 0, 1, -mSurfaceZ);
image->poly[0] = Point3F(mObjBox.min.x, mObjBox.min.y, 1);
image->poly[1] = Point3F(mObjBox.min.x, mObjBox.max.y, 1);
image->poly[2] = Point3F(mObjBox.max.x, mObjBox.max.y, 1);
image->poly[3] = Point3F(mObjBox.max.x, mObjBox.min.y, 1);
for (U32 i = 0; i < 4; i++)
{
image->poly[i].convolve(mObjScale);
getTransform().mulP(image->poly[i]);
}
// Calc the area of this poly
Point3F intermed;
mCross(image->poly[2] - image->poly[0], image->poly[3] - image->poly[1], &intermed);
image->polyArea = intermed.len() * 0.5;
state->insertRenderImage(image);
}
return false;
}
//==============================================================================
void WaterBlock::renderObject( SceneState* state, SceneRenderImage* )
{
AssertFatal( dglIsInCanonicalState(),
"Error, GL not in canonical state on entry" );
RectI viewport;
Point3F Eye; // Camera in water space.
bool CameraSubmergedFlag = false;
dglGetViewport ( &viewport );
glMatrixMode ( GL_PROJECTION );
glPushMatrix ();
state->setupObjectProjection( this );
/****
// Debug assist.
// Render a wire outline around the base of the water block.
if( 0 )
{
glMatrixMode ( GL_MODELVIEW );
glPushMatrix ();
dglMultMatrix ( &mObjToWorld );
F32 X0 = 0;
F32 Y0 = 0;
F32 X1 = mObjScale.x;
F32 Y1 = mObjScale.y;
F32 Z = 0;
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.6f, 0.6f, 0.0f, 0.5f );
glBegin ( GL_QUADS );
glVertex3f ( X0, Y0, Z );
glVertex3f ( X1, Y0, Z );
glVertex3f ( X1, Y1, Z );
glVertex3f ( X0, Y1, Z );
glEnd ();
glEnable ( GL_DEPTH_TEST );
glColor4f ( 0.8f, 0.8f, 0.8f, 0.8f );
glBegin ( GL_QUADS );
glVertex3f ( X0, Y0, Z );
glVertex3f ( X1, Y0, Z );
glVertex3f ( X1, Y1, Z );
glVertex3f ( X0, Y1, Z );
glEnd ();
glPolygonMode ( GL_FRONT_AND_BACK, GL_FILL );
glPopMatrix ();
glMatrixMode ( GL_MODELVIEW );
}
****/
// Handle the fluid.
{
// The water block lives in terrain space which is -1024,-1024.
// To get into world space, we just add 1024,1024.
// The fluid lives in world space.
Point3F W2Lv( 1024.0f, 1024.0f, 0.0f ); // World to Local vector
Point3F L2Wv( -1024.0f, -1024.0f, 0.0f ); // Local to World vector
MatrixF L2Wm; // Local to World matrix
L2Wm.identity();
L2Wm.setPosition( L2Wv );
glMatrixMode ( GL_MODELVIEW );
glPushMatrix ();
dglMultMatrix ( &L2Wm );
// We need the eye in water space.
{
Eye = state->getCameraPosition() + W2Lv;
mFluid.SetEyePosition( Eye.x, Eye.y, Eye.z );
}
// We need the frustrum in water space.
{
MatrixF L2Cm;
dglGetModelview( &L2Cm );
L2Cm.inverse();
Point3F Dummy;
Dummy = L2Cm.getPosition();
F64 frustumParam[6];
dglGetFrustum(&frustumParam[0], &frustumParam[1],
&frustumParam[2], &frustumParam[3],
&frustumParam[4], &frustumParam[5]);
sgComputeOSFrustumPlanes(frustumParam,
L2Cm,
Dummy,
mClipPlane[1],
mClipPlane[2],
mClipPlane[3],
mClipPlane[4],
mClipPlane[5]);
// near plane is needed as well...
PlaneF p(0, 1, 0, -frustumParam[4]);
mTransformPlane(L2Cm, Point3F(1,1,1), p, &mClipPlane[0]);
// F64 left, right, bottom, top, near, far;
// MatrixF L2Cm;
// dglGetFrustum( &left, &right, &bottom, &top, &near, &far );
// far = state->getVisibleDistance();
// Point3F Origin ( 0, 0, 0 );
// Point3F FarOrigin ( 0, far, 0 );
// Point3F UpperLeft ( left, near, top );
// Point3F UpperRight ( right, near, top );
// Point3F LowerLeft ( left, near, bottom );
// Point3F LowerRight ( right, near, bottom );
// dglGetModelview( &L2Cm );
// L2Cm.inverse();
// Point3F Dummy;
// Dummy = L2Cm.getPosition();
// L2Cm.mulP( Origin );
// L2Cm.mulP( FarOrigin );
// L2Cm.mulP( UpperLeft );
// L2Cm.mulP( UpperRight );
// L2Cm.mulP( LowerLeft );
// L2Cm.mulP( LowerRight );
// // Frustrum clip planes: 0=T 1=B 2=L 3=R 4=N 5=F
// mClipPlane[0].set( UpperLeft, Origin, UpperRight );
// mClipPlane[1].set( LowerRight, Origin, LowerLeft );
// mClipPlane[2].set( LowerLeft, Origin, UpperLeft );
// mClipPlane[3].set( UpperRight, Origin, LowerRight );
// mClipPlane[4].set( UpperRight, UpperLeft, LowerRight );
// // far plane is reverse of near plane vector
// mClipPlane[5].x = -mClipPlane[4].x;
// mClipPlane[5].y = -mClipPlane[4].y;
// mClipPlane[5].z = -mClipPlane[4].z;
// mClipPlane[5].d = -mClipPlane[4].d + far;
mFluid.SetFrustrumPlanes( (F32*)mClipPlane );
}
// Fog stuff.
{
pSceneState = state;
ColorF FogColor = state->getFogColor();
mFluid.SetFogParameters( FogColor.red,
FogColor.green,
FogColor.blue,
state->getVisibleDistance() );
}
// And RENDER!
{
mFluid.Render( CameraSubmergedFlag );
}
// Clean up.
glPopMatrix ();
glMatrixMode ( GL_MODELVIEW );
}
//
// And now the closing ceremonies...
//
glMatrixMode ( GL_PROJECTION );
glPopMatrix ();
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
dglSetViewport ( viewport );
//
// Oh yes. We have to set some state information for the scene...
//
if( CameraSubmergedFlag )
{
mCameraSubmerged = true;
mSubmergedType = mLiquidType;
}
AssertFatal( dglIsInCanonicalState(),
"Error, GL not in canonical state on exit" );
}
//==============================================================================
void WaterBlock::inspectPostApply()
{
resetWorldBox();
setMaskBits(1);
}
//==============================================================================
static EnumTable::Enums gLiquidTypeEnums[] =
{
{ WaterBlock::eWater, "Water" },
{ WaterBlock::eOceanWater, "OceanWater" },
{ WaterBlock::eRiverWater, "RiverWater" },
{ WaterBlock::eStagnantWater, "StagnantWater" },
{ WaterBlock::eLava, "Lava" },
{ WaterBlock::eHotLava, "HotLava" },
{ WaterBlock::eCrustyLava, "CrustyLava" },
{ WaterBlock::eQuicksand, "Quicksand" }
};
static EnumTable gLiquidTypeTable( 8, gLiquidTypeEnums );
//------------------------------------------------------------------------------
void WaterBlock::initPersistFields()
{
Parent::initPersistFields();
addField( "liquidType", TypeEnum, Offset( mLiquidType, WaterBlock ), 1, &gLiquidTypeTable );
addField( "density", TypeF32, Offset( mDensity, WaterBlock ) );
addField( "viscosity", TypeF32, Offset( mViscosity, WaterBlock ) );
addField( "waveMagnitude", TypeF32, Offset( mWaveMagnitude, WaterBlock ) );
addField( "surfaceTexture", TypeString, Offset( mSurfaceName, WaterBlock ) );
addField( "surfaceOpacity", TypeF32, Offset( mSurfaceOpacity, WaterBlock ) );
addField( "envMapTexture", TypeString, Offset( mEnvMapName, WaterBlock ) );
addField( "envMapIntensity", TypeF32, Offset( mEnvMapIntensity, WaterBlock ) );
addField( "submergeTexture", TypeString, Offset( mSubmergeName, WaterBlock ), WC_NUM_SUBMERGE_TEX );
addField( "removeWetEdges", TypeBool, Offset( mRemoveWetEdges, WaterBlock ) );
addField( "audioEnvironment", TypeAudioEnvironmentPtr, Offset( mAudioEnvironment, WaterBlock ) );
}
//==============================================================================
void WaterBlock::cToggleWireFrame( SimObject* obj, S32, const char** )
{
WaterBlock* pWaterBlock = static_cast<WaterBlock*>(obj);
if( pWaterBlock )
{
pWaterBlock->mFluid.m_ShowWire = !(pWaterBlock->mFluid.m_ShowWire);
if( pWaterBlock->mFluid.m_ShowWire )
Con::printf( "WaterBlock wire frame ENABLED" );
else
Con::printf( "WaterBlock wire frame DISABLED" );
}
}
//==============================================================================
void WaterBlock::consoleInit()
{
Con::addCommand( "WaterBlock", "toggleWireFrame", cToggleWireFrame, "waterBlock.toggleWireFrame()", 2, 2 );
}
//==============================================================================
U32 WaterBlock::packUpdate( NetConnection* c, U32 mask, BitStream* stream )
{
U32 retMask = Parent::packUpdate( c, mask, stream );
// No masking in here now.
// There's not too much data, and it doesn't change during normal game play.
stream->writeAffineTransform( mObjToWorld );
mathWrite( *stream, mObjScale );
stream->writeString( mSurfaceName );
stream->writeString( mEnvMapName );
for( int i=0; i<WC_NUM_SUBMERGE_TEX; i++ )
{
stream->writeString( mSubmergeName[i] );
}
stream->write( (S32)mLiquidType );
stream->write( mDensity );
stream->write( mViscosity );
stream->write( mWaveMagnitude );
stream->write( mSurfaceOpacity );
stream->write( mEnvMapIntensity );
stream->write( mRemoveWetEdges );
// audio environment:
if(stream->writeFlag(mAudioEnvironment))
stream->writeRangedU32(mAudioEnvironment->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast);
return( retMask );
}
//==============================================================================
void WaterBlock::unpackUpdate( NetConnection* c, BitStream* stream )
{
Parent::unpackUpdate( c, stream );
U32 LiquidType;
// No masking in here now.
// There's not too much data, and it doesn't change during normal game play.
stream->readAffineTransform( &mObjToWorld );
mathRead( *stream, &mObjScale );
mSurfaceName = stream->readSTString();
mEnvMapName = stream->readSTString();
for( int i=0; i<WC_NUM_SUBMERGE_TEX; i++ )
{
mSubmergeName[i] = stream->readSTString();
}
stream->read( &LiquidType );
stream->read( &mDensity );
stream->read( &mViscosity );
stream->read( &mWaveMagnitude );
stream->read( &mSurfaceOpacity );
stream->read( &mEnvMapIntensity );
stream->read( &mRemoveWetEdges );
// audio environment:
if(stream->readFlag())
{
U32 profileId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
mAudioEnvironment = dynamic_cast<AudioEnvironment*>(Sim::findObject(profileId));
}
else
mAudioEnvironment = 0;
mLiquidType = (EWaterType)LiquidType;
UpdateFluidRegion();
if( !isProperlyAdded() )
return;
resetWorldBox();
}
//==============================================================================
// This method can take a point in world space, or water block space. The default
// assumes pos is in world space, and therefore must transform it to waterblock
// space.
bool WaterBlock::isPointSubmerged(const Point3F &pos, bool worldSpace) const
{
return( isPointSubmergedSimple( pos, worldSpace ) );
}
//==============================================================================
bool WaterBlock::isPointSubmergedSimple(const Point3F &pos, bool worldSpace) const
{
Point3F Pos = pos;
if( Pos.z > mSurfaceZ )
return( false );
if( worldSpace )
{
Pos.x += 1024.0f;
Pos.y += 1024.0f;
}
return( mFluid.IsFluidAtXY( Pos.x, Pos.y ) );
}
//==============================================================================
bool WaterBlock::castRay( const Point3F& start, const Point3F& end, RayInfo* info )
{
F32 t, x, y, X, Y;
Point3F Pos;
//
// Looks like the incoming points are in parametric object space. Great.
//
// The water surface is 1.0. Bail if the ray does not cross the surface.
if( (start.z > 1.0f) && (end.z > 1.0f) ) return( false );
if( (start.z < 1.0f) && (end.z < 1.0f) ) return( false );
// The ray crosses the surface plane. Find out where.
t = (start.z - 1.0f) / (start.z - end.z);
x = start.x + (end.x - start.x) * t;
y = start.y + (end.y - start.y) * t;
Pos = mObjToWorld.getPosition();
X = (x * mObjScale.x) + Pos.x + 1024.0f;
Y = (y * mObjScale.y) + Pos.y + 1024.0f;
if( mFluid.IsFluidAtXY( X, Y ) )
{
info->t = t;
info->point.x = x;
info->point.y = y;
info->point.z = 1.0f;
info->normal.x = 0.0f;
info->normal.y = 0.0f;
info->normal.z = 1.0f;
info->object = this;
info->material = 0;
return( true );
}
// Hmm. Guess we missed!
return( false );
}
//==============================================================================
bool WaterBlock::isWater( U32 liquidType )
{
EWaterType wType = EWaterType( liquidType );
return( wType == eWater ||
wType == eOceanWater ||
wType == eRiverWater ||
wType == eStagnantWater );
}
//==============================================================================
bool WaterBlock::isLava( U32 liquidType )
{
EWaterType wType = EWaterType( liquidType );
return( wType == eLava ||
wType == eHotLava ||
wType == eCrustyLava );
}
//==============================================================================
bool WaterBlock::isQuicksand( U32 liquidType )
{
EWaterType wType = EWaterType( liquidType );
return( wType == eQuicksand );
}
//==============================================================================

138
terrain/waterBlock.h Normal file
View file

@ -0,0 +1,138 @@
//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#ifndef _WATERBLOCK_H_
#define _WATERBLOCK_H_
#ifndef _PLATFORM_H_
#include "Platform/platform.h"
#endif
#ifndef _MPOINT_H_
#include "Math/mPoint.h"
#endif
#ifndef _SCENEOBJECT_H_
#include "Sim/sceneObject.h"
#endif
#ifndef _GTEXMANAGER_H_
#include "dgl/gTexManager.h"
#endif
#ifndef _COLOR_H_
#include "Core/color.h"
#endif
#ifndef _TERRDATA_H_
#include "terrain/terrData.h"
#endif
#ifndef _FLUID_H_
#include "terrain/Fluid.h"
#endif
//==============================================================================
class AudioEnvironment;
class WaterBlock : public SceneObject
{
typedef SceneObject Parent;
public:
enum EWaterType
{
eWater = 0,
eOceanWater = 1,
eRiverWater = 2,
eStagnantWater = 3,
eLava = 4,
eHotLava = 5,
eCrustyLava = 6,
eQuicksand = 7,
};
enum WaterConst
{
WC_NUM_SUBMERGE_TEX = 2,
};
private:
fluid mFluid;
TextureHandle mSurfaceTexture;
TextureHandle mEnvMapTexture;
PlaneF mClipPlane[6]; // Frustrum clip planes: 0=T 1=B 2=L 3=R 4=N 5=F
TerrainBlock* mpTerrain; // Terrain block
F32 mSurfaceZ;
// Fields exposed to the editor.
EWaterType mLiquidType; // Water? Lava? What?
F32 mDensity;
F32 mViscosity;
F32 mWaveMagnitude;
StringTableEntry mSurfaceName;
F32 mSurfaceOpacity;
StringTableEntry mEnvMapName;
F32 mEnvMapIntensity;
StringTableEntry mSubmergeName[WC_NUM_SUBMERGE_TEX];
bool mRemoveWetEdges;
AudioEnvironment * mAudioEnvironment;
TextureHandle mLocalSubmergeTexture[WC_NUM_SUBMERGE_TEX];
static TextureHandle mSubmergeTexture[WC_NUM_SUBMERGE_TEX];
public:
WaterBlock();
~WaterBlock();
bool onAdd ( void );
void onRemove ( void );
bool onSceneAdd ( SceneGraph* pGraph );
void setTransform ( const MatrixF& mat );
void setScale ( const VectorF& scale );
bool prepRenderImage ( SceneState*, const U32, const U32, const bool );
void renderObject ( SceneState*, SceneRenderImage* );
void inspectPostApply ( void );
F32 getSurfaceHeight ( void ) { return mSurfaceZ; }
void UpdateFluidRegion ( void );
static void SnagTerrain ( SceneObject* sceneObj, S32 key );
static void cToggleWireFrame( SimObject*, S32, const char** );
DECLARE_CONOBJECT(WaterBlock);
static void initPersistFields();
static void consoleInit();
// bool setLiquidType(EWaterFlag liquidType);
EWaterType getLiquidType() const { return mLiquidType; }
static bool isWater ( U32 liquidType );
static bool isLava ( U32 liquidType );
static bool isQuicksand ( U32 liquidType );
F32 getViscosity() const { return mViscosity; }
F32 getDensity() const { return mDensity; }
U32 packUpdate ( NetConnection*, U32 mask, BitStream *stream );
void unpackUpdate( NetConnection*, BitStream *stream );
bool isPointSubmerged ( const Point3F &pos, bool worldSpace = true ) const;
bool isPointSubmergedSimple( const Point3F &pos, bool worldSpace = true ) const;
AudioEnvironment * getAudioEnvironment() { return(mAudioEnvironment); }
static bool mCameraSubmerged;
static U32 mSubmergedType;
static TextureHandle getSubmergeTexture( U32 index ){ return mSubmergeTexture[index]; }
protected:
bool castRay( const Point3F& start, const Point3F& end, RayInfo* info );
};
#endif