/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTreeBuffer.cpp ////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DTreeBuffer.cpp
//
// Created: John Ahlquist, May 2001
//
// Desc: Draw buffer to handle all the trees in a scene.
//
//-----------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
/** Topple options */
// ------------------------------------------------------------------------------------------------
enum
{
W3D_TOPPLE_OPTIONS_NONE = 0x00000000,
W3D_TOPPLE_OPTIONS_NO_BOUNCE = 0x00000001, ///< do not bounce when hit the ground
W3D_TOPPLE_OPTIONS_NO_FX = 0x00000002 ///< do not play any FX when hit the ground
};
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DTreeBuffer.h"
#include
#include
#include
#include
#include "Common/MapReaderWriterInfo.h"
#include "Common/FileSystem.h"
#include "Common/file.h"
#include "Common/PerfTimer.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "GameLogic/ScriptEngine.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameLogic/PartitionManager.h"
#include "GameClient/ClientRandomValue.h"
#include "GameClient/FXList.h"
#include "W3DDevice/GameClient/TerrainTex.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "W3DDevice/GameClient/W3DDynamicLight.h"
#include "W3DDevice/GameClient/Module/W3DTreeDraw.h"
#include "W3DDevice/GameClient/W3DShaderManager.h"
#include "W3DDevice/GameClient/W3DShadow.h"
#include "W3DDevice/GameClient/W3DShroud.h"
#include "W3DDevice/GameClient/W3DProjectedShadow.h"
#include "WW3D2/Camera.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/DX8Renderer.h"
#include "WW3D2/Matinfo.h"
#include "WW3D2/Mesh.h"
#include "WW3D2/MeshMdl.h"
#include "d3dx8tex.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// If TEST_AND_BLEND is defined, it will do an alpha test and blend. Otherwise just alpha test. jba. [5/30/2003]
#define dontTEST_AND_BLEND 1
#define USE_STATIC 1
#define END_OF_PARTITION (-1)
#define DELETED_TREE_TYPE (-2)
/******************************************************************************
W3DTreeTextureClass
******************************************************************************/
//-----------------------------------------------------------------------------
// Public Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DTreeBuffer::W3DTreeTextureClass::W3DTreeTextureClass
//=============================================================================
/** Constructor. Calls parent constructor to create a 16 bit per pixel D3D
texture of the desired height and mip level. */
//=============================================================================
W3DTreeBuffer::W3DTreeTextureClass::W3DTreeTextureClass(unsigned width, unsigned height) :
TextureClass(width, height,
WW3D_FORMAT_A8R8G8B8, MIP_LEVELS_ALL )
{
}
//=============================================================================
// W3DTreeBuffer::W3DTreeTextureClass::update
//=============================================================================
/** Sets the tile bitmap data into the texture. The tiles are placed with 4
pixel borders around them, so that when the tiles are scaled and bilinearly
interpolated, you don't get seams between the tiles. */
//=============================================================================
int W3DTreeBuffer::W3DTreeTextureClass::update(W3DTreeBuffer *buffer)
{
//Set to clamp.
Get_Filter().Set_U_Addr_Mode(TextureFilterClass::TEXTURE_ADDRESS_CLAMP);
Get_Filter().Set_V_Addr_Mode(TextureFilterClass::TEXTURE_ADDRESS_CLAMP);
IDirect3DSurface8 *surface_level;
D3DSURFACE_DESC surface_desc;
D3DLOCKED_RECT locked_rect;
DX8_ErrorCode(Peek_D3D_Texture()->GetSurfaceLevel(0, &surface_level));
DX8_ErrorCode(surface_level->GetDesc(&surface_desc));
DX8_ErrorCode(surface_level->LockRect(&locked_rect, NULL, 0));
Int tilePixelExtent = TILE_PIXEL_EXTENT;
// Int numRows = surface_desc.Height/(tilePixelExtent+TILE_OFFSET);
#ifdef _DEBUG
//DASSERT_MSG(tilesPerRow*numRows >= htMap->m_numBitmapTiles,Debug::Format ("Too many tiles."));
//DEBUG_ASSERTCRASH((Int)surface_desc.Width >= tilePixelExtent*tilesPerRow, ("Bitmap too small."));
#endif
if (surface_desc.Format == D3DFMT_A8R8G8B8) {
Int tileNdx;
Int pixelBytes = 4;
#if 0 // Fill unused texture for debug display.
UnsignedInt cellX, cellY;
for (cellX = 0; cellX < surface_desc.Width; cellX++) {
for (cellY = 0; cellY < surface_desc.Height; cellY++) {
UnsignedByte *pBGR = ((UnsignedByte *)locked_rect.pBits)+(cellY*surface_desc.Width+cellX)*pixelBytes;
//*((Short*)pBGR) = 0x8000 + (((255-2*cellY)>>3)<<10) + ((4*cellX)>>4);
*((Int*)pBGR) = 0xFF000000 | ( (((255-cellY))<<16) + ((cellX)) );
}
}
#endif
for (tileNdx=0; tileNdx < buffer->getNumTiles(); tileNdx++) {
TileData *pTile = buffer->getSourceTile(tileNdx);
if (!pTile) continue;
ICoord2D position = pTile->m_tileLocationInTexture;
if (position.x<0) {
continue;
}
Int i,j;
for (j=0; jgetRGBDataForWidth(tilePixelExtent);
pBGR += (tilePixelExtent-(1+j))*TILE_BYTES_PER_PIXEL*tilePixelExtent; // invert to match.
Int row = position.y+j;
UnsignedByte *pBGRA = ((UnsignedByte*)locked_rect.pBits) +
(row)*surface_desc.Width*pixelBytes;
Int column = position.x;
pBGRA += column*pixelBytes;
for (i=0; i>3)<<10) + ((pBGR[1]>>3)<<5) + (pBGR[0]>>3);
*((Int *)pBGRA) = (pBGR[3]<<24) + (pBGR[2]<<16) + (pBGR[1]<<8) + (pBGR[0]);
pBGRA +=pixelBytes;
pBGR +=TILE_BYTES_PER_PIXEL;
}
}
}
}
DX8_ErrorCode(surface_level->UnlockRect());
surface_level->Release();
DX8_ErrorCode(D3DXFilterTexture(Peek_D3D_Texture(), NULL, (UINT)0, D3DX_FILTER_BOX));
if (TheWritableGlobalData->m_textureReductionFactor) {
DX8_ErrorCode(Peek_D3D_Texture()->SetLOD((DWORD)TheWritableGlobalData->m_textureReductionFactor));
}
return(surface_desc.Height);
}
//=============================================================================
// W3DTreeBuffer::W3DTreeTextureClass::setLOD
//=============================================================================
/** Sets the lod of the texture to be loaded into the video card. */
//=============================================================================
void W3DTreeBuffer::W3DTreeTextureClass::setLOD(Int LOD) const
{
if (Peek_D3D_Texture()) {
DX8_ErrorCode(Peek_D3D_Texture()->SetLOD((DWORD)LOD));
}
}
//=============================================================================
// W3DTreeBuffer::W3DTreeTextureClass::Apply
//=============================================================================
/** Sets the texture as the current D3D texture, and does some custom setup
(standard D3D setup, but beyond the scope of W3D). */
//=============================================================================
void W3DTreeBuffer::W3DTreeTextureClass::Apply(unsigned int stage)
{
// Do the base apply.
TextureClass::Apply(stage);
}
//-----------------------------------------------------------------------------
// Private Data
//-----------------------------------------------------------------------------
#ifdef TEST_AND_BLEND
// A W3D shader that does alpha, texturing, tests zbuffer, doesn't update zbuffer.
#define SC_ALPHA_DETAIL ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_SRC_ALPHA, \
ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_ENABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
#define SC_ALPHA_DETAIL_2X ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_SRC_ALPHA, \
ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE2X, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_ENABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
#else
#define SC_ALPHA_DETAIL ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_ENABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
#define SC_ALPHA_DETAIL_2X ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE2X, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_ENABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
#endif
static ShaderClass detailAlphaShader(SC_ALPHA_DETAIL);
static ShaderClass detailAlphaShader2X(SC_ALPHA_DETAIL_2X);
/*
#define SC_ALPHA_DETAIL ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_ENABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailAlphaShader(SC_ALPHA_DETAIL);
*/
/*
#define SC_ALPHA_DETAIL ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_SRC_ALPHA, \
ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_ENABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailAlphaShader(SC_ALPHA_DETAIL);
*/
/*
#define SC_ALPHA_MIRROR ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_SRC_ALPHA, \
ShaderClass::DSTBLEND_ONE_MINUS_SRC_ALPHA, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass mirrorAlphaShader(SC_ALPHA_DETAIL);
// ShaderClass::PASS_ALWAYS,
#define SC_ALPHA_2D ( SHADE_CNST(PASS_ALWAYS, DEPTH_WRITE_DISABLE, COLOR_WRITE_ENABLE, \
SRCBLEND_SRC_ALPHA, DSTBLEND_ONE_MINUS_SRC_ALPHA, FOG_DISABLE, GRADIENT_DISABLE, \
SECONDARY_GRADIENT_DISABLE, TEXTURING_ENABLE, DETAILCOLOR_DISABLE, DETAILALPHA_DISABLE, \
ALPHATEST_DISABLE, CULL_MODE_ENABLE, DETAILCOLOR_DISABLE, DETAILALPHA_DISABLE) )
ShaderClass ShaderClass::_PresetAlpha2DShader(SC_ALPHA_2D);
*/
//-----------------------------------------------------------------------------
// Private Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DTreeBuffer::cull
//=============================================================================
/** Culls the trees, marking the visible flag. If a tree becomes visible, it sets
it's sortKey */
//=============================================================================
void W3DTreeBuffer::cull(const CameraClass * camera)
{
Int curTree;
// Calulate the vector direction that the camera is looking at.
Matrix3D camera_matrix = camera->Get_Transform();
float zmod = -1;
float x = zmod * camera_matrix[0][2] ;
float y = zmod * camera_matrix[1][2] ;
float z = zmod * camera_matrix[2][2] ;
m_cameraLookAtVector.Set(x,y,z);
for (curTree=0; curTreeCull_Sphere(m_trees[curTree].bounds);
if (visible != m_trees[curTree].visible) {
m_trees[curTree].visible=visible;
m_anythingChanged = true;
if (visible) {
doKey = true;
}
}
// Also calculate sort key if a tree is visible, and the view changed setting m_updateAllKeys to true.
if (doKey || (visible&&m_updateAllKeys)) {
// The sort key is essentially the distance of location in the direction of the
// camera look at.
m_trees[curTree].sortKey = Vector3::Dot_Product(m_trees[curTree].location, m_cameraLookAtVector);
}
}
m_updateAllKeys = false;
}
//=============================================================================
// W3DTreeBuffer::getPartitionBucket
//=============================================================================
/** Returns the bucket index into m_areaPartition for a given location. */
//=============================================================================
Int W3DTreeBuffer::getPartitionBucket(const Coord3D &pos) const
{
Real x = pos.x;
Real y = pos.y;
if (xm_bounds.hi.x) x = m_bounds.hi.x;
if (y>m_bounds.hi.y) y = m_bounds.hi.y;
Int xIndex = REAL_TO_INT_FLOOR ( (x/(m_bounds.hi.x-m_bounds.lo.x)) * (PARTITION_WIDTH_HEIGHT-0.1f) );
Int yIndex = REAL_TO_INT_FLOOR ( (y/(m_bounds.hi.y-m_bounds.lo.y)) * (PARTITION_WIDTH_HEIGHT-0.1f) );
DEBUG_ASSERTCRASH(xIndex>=0 && yIndex>=0 && xIndex m_trees[i].sortKey) {
TTree tmp = m_trees[cur];
m_trees[cur] = m_trees[i];
m_trees[i] = tmp;
swap = true;
}
cur = i;
}
}
if (!swap) {
return;
}
m_anythingChanged = true;
}
}
#endif
/********** GDIFileStream2 class ****************************/
class GDIFileStream2 : public InputStream
{
protected:
File* m_file;
public:
GDIFileStream2():m_file(NULL) {};
GDIFileStream2(File* pFile):m_file(pFile) {};
virtual Int read(void *pData, Int numBytes) {
return(m_file?m_file->read(pData, numBytes):0);
};
};
//=============================================================================
// W3DTreeBuffer::updateTexture
//=============================================================================
/** Creates a new texture. */
//=============================================================================
void W3DTreeBuffer::updateTexture(void)
{
const Int MAX_TEX_WIDTH = 2048;
Int i, j;
Int maxHeight = 0;
const Int maxTilesPerRow = MAX_TEX_WIDTH/(TILE_PIXEL_EXTENT);
REF_PTR_RELEASE(m_treeTexture);
Bool availableGrid[maxTilesPerRow][maxTilesPerRow];
Int row, column;
for (row=0; rowm_textureName.str() );
theFile = TheFileSystem->openFile( texturePath, File::READ|File::BINARY);
if (theFile==NULL) {
sprintf( texturePath, "%s%s", TGA_DIR_PATH, m_treeTypes[i].m_data->m_textureName.str() );
theFile = TheFileSystem->openFile( texturePath, File::READ|File::BINARY);
}
if (theFile != NULL) {
GDIFileStream2 theStream(theFile);
InputStream *pStr = &theStream;
Bool halfTile;
Int numTiles = WorldHeightMap::countTiles(pStr, &halfTile);
Int width;
for (width = 10; width >= 1; width--) {
if (numTiles >= width*width) {
numTiles = width*width;
break;
}
}
Bool texFound = false;
for (j=0; jm_textureName.compareNoCase(m_treeTypes[i].m_data->m_textureName)==0) {
m_treeTypes[i].m_firstTile = 0;
m_treeTypes[i].m_tileWidth = width;
m_treeTypes[i].m_numTiles = 0;
texFound = true;
break;
}
}
if (texFound) {
theFile->close();
continue;
}
if (m_numTiles+numTiles<=MAX_TILES) {
theFile->seek(0, File::START);
m_treeTypes[i].m_firstTile = m_numTiles;
m_treeTypes[i].m_tileWidth = width;
m_treeTypes[i].m_numTiles = numTiles;
m_treeTypes[i].m_halfTile = halfTile;
WorldHeightMap::readTiles(pStr, m_sourceTiles+m_treeTypes[i].m_firstTile, width);
m_numTiles += numTiles;
} else {
m_treeTypes[i].m_firstTile = 0;
m_treeTypes[i].m_tileWidth = 0;
m_treeTypes[i].m_numTiles = 0;
}
theFile->close();
} else {
DEBUG_CRASH(("Could not find texture %s\n", m_treeTypes[i].m_data->m_textureName.str()));
m_treeTypes[i].m_firstTile = 0;
m_treeTypes[i].m_tileWidth = 0;
m_treeTypes[i].m_numTiles = 0;
}
}
Int tmpWidth = 8;
while (tmpWidth*tmpWidthMAX_TEX_WIDTH) {
m_textureWidth = 64;
m_textureHeight = 64;
if (m_treeTexture==NULL) {
m_treeTexture = new TextureClass("missing.tga");
}
DEBUG_CRASH(("Too many trees in a scene."));
return;
}
for (i=0; im_tileLocationInTexture.x = -1;
m_sourceTiles[i]->m_tileLocationInTexture.y = -1;
}
}
/* put the tree tiles into the texture */
Int texClass;
Int tileWidth;
for (tileWidth = tilesPerRow; tileWidth>0; tileWidth--) {
for (texClass=0; texClassm_textureName.compareNoCase(m_treeTypes[texClass].m_data->m_textureName)==0) {
m_treeTypes[texClass].m_textureOrigin.x = m_treeTypes[i].m_textureOrigin.x;
m_treeTypes[texClass].m_textureOrigin.y = m_treeTypes[i].m_textureOrigin.y;
texFound = true;
break;
}
}
if (texFound) {
continue;
}
// Find an available block of space.
Bool found = false;
for (row=0; row<(tilesPerRow-width)+1 && !found; row++) {
for (column=0; column<(tilesPerRow-width)+1 && !found; column++) {
if (availableGrid[row][column]) {
Bool open = true;
for (i=0; im_tileLocationInTexture.x = x;
m_sourceTiles[baseNdx]->m_tileLocationInTexture.y = y;
}
}
}
}
DEBUG_ASSERTCRASH(maxHeight<=m_textureWidth, ("Bad max height."));
W3DTreeTextureClass *tex = new W3DTreeTextureClass((DWORD)m_textureWidth, (DWORD)m_textureWidth);
m_textureHeight = tex->update(this);
m_treeTexture = tex;
for (i=0; isetLOD(lod);
}
//=============================================================================
// W3DTreeBuffer::doLighting
//=============================================================================
/** Calculates the diffuse lighting as affected by dynamic lighting. */
//=============================================================================
UnsignedInt W3DTreeBuffer::doLighting(const Vector3 *normal,
const GlobalData::TerrainLighting *objectLighting,
const Vector3 *emissive, UnsignedInt vertDiffuse, Real scale) const
{
Real shadeR, shadeG, shadeB;
Real shade;
shadeR = objectLighting[0].ambient.red+emissive->X; //only the first light contributes to ambient
shadeG = objectLighting[0].ambient.green+emissive->Y;
shadeB = objectLighting[0].ambient.blue+emissive->Z;
Int i;
for (i=0; i 1.0) shade = 1.0;
if(shade < 0.0f) shade = 0.0f;
shadeR += shade*objectLighting[i].diffuse.red;
shadeG += shade*objectLighting[i].diffuse.green;
shadeB += shade*objectLighting[i].diffuse.blue;
}
shadeR *= scale;
shadeG *= scale;
shadeB *= scale;
if (shadeR > 1.0) shadeR = 1.0;
if(shadeR < 0.0f) shadeR = 0.0f;
if (shadeG > 1.0) shadeG = 1.0;
if(shadeG < 0.0f) shadeG = 0.0f;
if (shadeB > 1.0) shadeB = 1.0;
if(shadeB < 0.0f) shadeB = 0.0f;
if (vertDiffuse!=0xFFFFFFFF) {
shade = vertDiffuse&0xff; //blue;
shadeB *= shade/255.0f;
shade = (vertDiffuse>>8)&0xFF; // green;
shadeG *= shade/255.0f;
shade = (vertDiffuse>>16)&0xFF; // red;
shadeR *= shade/255.0f;
}
shadeR*=255.0f;
shadeG*=255.0f;
shadeB*=255.0f;
const Real alpha = 255.0;
return REAL_TO_UNSIGNEDINT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16) | ((Int)alpha << 24);
}
//=============================================================================
// W3DTreeBuffer::loadTreesInVertexAndIndexBuffers
//=============================================================================
/** Loads the trees into the vertex buffer for drawing. */
//=============================================================================
void W3DTreeBuffer::loadTreesInVertexAndIndexBuffers(RefRenderObjListIterator *pDynamicLightsIterator)
{
if (!m_indexTree[0] || !m_vertexTree[0] || !m_initialized) {
return;
}
if (!m_anythingChanged) {
return;
}
if (m_shadow == NULL && TheW3DProjectedShadowManager) {
Shadow::ShadowTypeInfo shadowInfo;
shadowInfo.m_ShadowName[0] = 0;
shadowInfo.allowUpdates=FALSE; //shadow image will never update
shadowInfo.allowWorldAlign=TRUE; //shadow image will wrap around world objects
shadowInfo.m_type = (ShadowType)SHADOW_DECAL;
shadowInfo.m_sizeX=20;
shadowInfo.m_sizeY=20;
shadowInfo.m_offsetX=0;
shadowInfo.m_offsetY=0;
m_shadow = TheW3DProjectedShadowManager->createDecalShadow(&shadowInfo);
}
m_anythingChanged = false;
Int curTree=0;
Int bNdx;
const GlobalData::TerrainLighting *objectLighting = TheGlobalData->m_terrainObjectsLighting[TheGlobalData->m_timeOfDay];
for (bNdx=0; bNdx= m_numTrees) {
break;
}
VertexFormatXYZNDUV1 *vb;
UnsignedShort *ib;
// Lock the buffers.
#ifdef USE_STATIC
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexTree[bNdx], 0);
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexTree[bNdx], 0);
#else
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexTree[bNdx], D3DLOCK_DISCARD);
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexTree[bNdx], D3DLOCK_DISCARD);
#endif
vb=(VertexFormatXYZNDUV1*)lockVtxBuffer.Get_Vertex_Array();
ib = lockIdxBuffer.Get_Index_Array();
// Add to the index buffer & vertex buffer.
Vector2 lookAtVector(m_cameraLookAtVector.X, m_cameraLookAtVector.Y);
lookAtVector.Normalize();
// We draw from back to front, so we put the indexes in the buffer
// from back to front.
UnsignedShort *curIb = ib;
VertexFormatXYZNDUV1 *curVb = vb;
for ( ;curTreeFirst(); !pDynamicLightsIterator->Is_Done(); pDynamicLightsIterator->Next())
{
W3DDynamicLight *pLight = (W3DDynamicLight*)pDynamicLightsIterator->Peek_Obj();
if (!pLight->isEnabled()) {
continue; // he is turned off.
}
if (CollisionMath::Overlap_Test(m_trees[curTree].bounds, pLight->Get_Bounding_Sphere()) == CollisionMath::OUTSIDE) {
continue; // this tree is outside of the light's influence.
}
doVertexLighting = true;
}
#endif
Vector3 emissive(0.0f,0.0f,0.0f);
MaterialInfoClass * matInfo = m_treeTypes[type].m_mesh->Get_Material_Info();
if (matInfo) {
VertexMaterialClass *vertMat = matInfo->Peek_Vertex_Material(0);
if (vertMat) {
vertMat->Get_Emissive(&emissive);
}
}
REF_PTR_RELEASE(matInfo);
Int startVertex = m_curNumTreeVertices[bNdx];
m_trees[curTree].firstIndex = startVertex;
m_trees[curTree].bufferNdx = bNdx;
Int i;
Int numVertex = m_treeTypes[type].m_mesh->Peek_Model()->Get_Vertex_Count();
Vector3 *pVert = m_treeTypes[type].m_mesh->Peek_Model()->Get_Vertex_Array();
// If we happen to have too many trees, stop.
if (m_curNumTreeVertices[bNdx]+numVertex+2>= MAX_TREE_VERTEX) {
break;
}
Int numIndex = m_treeTypes[type].m_mesh->Peek_Model()->Get_Polygon_Count();
const TriIndex *pPoly = m_treeTypes[type].m_mesh->Peek_Model()->Get_Polygon_Array();
if (m_curNumTreeIndices[bNdx]+3*numIndex+6 >= MAX_TREE_INDEX) {
break;
}
const Vector2*uvs=m_treeTypes[type].m_mesh->Peek_Model()->Get_UV_Array_By_Index(0);
const Vector3*normals = m_treeTypes[type].m_mesh->Peek_Model()->Get_Vertex_Normal_Array();
const unsigned *vecDiffuse = m_treeTypes[type].m_mesh->Peek_Model()->Get_Color_Array(0, false);
Int diffuse = 0;
if (normals == NULL) {
doVertexLighting = false;
Vector3 normal(0.0f,0.0f,1.0f);
diffuse = doLighting(&normal, objectLighting, &emissive, 0xFFFFFFFF, 1.0f);
}
/*
*
// If we are doing reduced resolution terrain, do reduced
// poly trees.
Bool doPanel = (TheGlobalData->m_useHalfHeightMap || TheGlobalData->m_stretchTerrain);
if (doPanel) {
if (m_trees[curTree].rotates) {
theSin = -lookAtVector.X;
theCos = lookAtVector.Y;
}
// panel start is index offset, there are 3 index per triangle.
if (m_trees[curTree].panelStart/3 + 2 > numIndex) {
continue; // not enought polygons for the offset. jba.
}
for (j=0; j<6; j++) {
i = ((Int *)pPoly)[j+m_trees[curTree].panelStart];
if (m_curNumTreeVertices >= MAX_TREE_VERTEX)
break;
// Update the uv values. The W3D models each have their own texture, and
// we use one texture with all images in one, so we have to change the uvs to
// match.
Real U, V;
if (type==SHRUB) {
// shrub texture is tucked in the corner
U = ((512-64)+uvs[i].U*64.0f)/512.0f;
V = ((256-64)+uvs[i].V*64.0f)/256.0f;
} else if (type==FENCE) {
U = uvs[i].U*0.5f;
V = 1.0f + uvs[i].V;
} else {
U = typeOffset+uvs[i].U*0.5f;
V = uvs[i].V;
}
curVb->u1 = U;
curVb->v1 = V/2.0;
Vector3 vLoc;
vLoc.X = pVert[i].X*scale*theCos - pVert[i].Y*scale*theSin;
vLoc.Y = pVert[i].Y*scale*theCos + pVert[i].X*scale*theSin;
vLoc.X += loc.X;
vLoc.Y += loc.Y;
vLoc.Z = loc.Z + pVert[i].Z*scale;
curVb->x = vLoc.X;
curVb->y = vLoc.Y;
curVb->z = vLoc.Z;
if (doVertexLighting) {
curVb->diffuse = doLighting(&vLoc, shadeR, shadeG, shadeB, m_trees[curTree].bounds, pDynamicLightsIterator);
} else {
curVb->diffuse = diffuse;
}
curVb++;
m_curNumTreeVertices++;
}
for (i=0; i<6; i++) {
if (m_curNumTreeIndices+4 > MAX_TREE_INDEX)
break;
curIb--;
*curIb = startVertex + i;
m_curNumTreeIndices++;
}
} else {
*/
Real Uscale = m_treeTypes[type].m_tileWidth * (Real)TILE_PIXEL_EXTENT / (Real)m_textureWidth;
Real Vscale = m_treeTypes[type].m_tileWidth * (Real)TILE_PIXEL_EXTENT / (Real)m_textureHeight;
Real UOffset = m_treeTypes[type].m_textureOrigin.x/(Real)m_textureWidth;
Real VOffset = m_treeTypes[type].m_textureOrigin.y/(Real)m_textureHeight;
if (m_treeTypes[type].m_halfTile) {
Uscale *= 0.5f;
Vscale *= 0.5f;
VOffset += (TILE_PIXEL_EXTENT/2) / (Real)m_textureHeight;
}
for (i=0; i= MAX_TREE_VERTEX)
break;
// Update the uv values. The W3D models each have their own texture, and
// we use one texture with all images in one, so we have to change the uvs to
// match.
Real U, V;
U = uvs[i].U;
V = uvs[i].V;
if (U>1.0f) U=1.0f;
if (U<0.0f) U=0.0f;
if (V>1.0f) V=1.0f;
if (V<0.0f) V=0.0f;
curVb->u1 = U*Uscale + UOffset;
curVb->v1 = V*Vscale + VOffset;
Real x = pVert[i].X;
Real y = pVert[i].Y;
Vector3 vLoc;
x += m_treeTypes[type].m_offset.X;
y += m_treeTypes[type].m_offset.Y;
vLoc.X = x*scale*theCos - y*scale*theSin;
vLoc.Y = y*scale*theCos + x*scale*theSin;
vLoc.Z = pVert[i].Z*scale;
vLoc.Z += m_treeTypes[type].m_offset.Z;
if (m_trees[curTree].m_toppleState != TOPPLE_UPRIGHT) {
Matrix3D::Transform_Vector(m_trees[curTree].m_mtx, vLoc, &vLoc);
} else {
if (m_trees[curTree].pushAside>0.0f) {
vLoc.X += pVert[i].Z * m_trees[curTree].pushAside * m_trees[curTree].pushAsideCos * m_treeTypes[type].m_data->m_maxOutwardMovement;
vLoc.Y += pVert[i].Z * m_trees[curTree].pushAside * m_trees[curTree].pushAsideSin* m_treeTypes[type].m_data->m_maxOutwardMovement;
}
vLoc.X += loc.X;
vLoc.Y += loc.Y;
vLoc.Z += loc.Z;
}
curVb->x = vLoc.X;
curVb->y = vLoc.Y;
curVb->z = vLoc.Z;
curVb->nx = m_trees[curTree].swayType;
curVb->ny = 1.0f - m_treeTypes[type].m_data->m_darkening*m_trees[curTree].pushAside;
curVb->nz = loc.Z;
if (doVertexLighting) {
Vector3 normal(0.0f, 0.0f, 1.0f);
if (normals) {
normal.X = normals[i].X*theCos - normals[i].Y*theSin;
normal.Y = normals[i].Y*theCos + normals[i].X*theSin;
normal.Z = normals[i].Z;
}
UnsignedInt vertexDiffuse;
if (vecDiffuse) {
vertexDiffuse = vecDiffuse[i];
} else {
vertexDiffuse = 0xffffffff;
}
curVb->diffuse = doLighting(&normal, objectLighting, &emissive,
vertexDiffuse, 1.0f);
} else {
curVb->diffuse = diffuse;
}
curVb++;
m_curNumTreeVertices[bNdx]++;
}
try {
for (i=0; i MAX_TREE_INDEX)
break;
*curIb++ = startVertex + pPoly[i].I;
*curIb++ = startVertex + pPoly[i].J;
*curIb++ = startVertex + pPoly[i].K;
m_curNumTreeIndices[bNdx]+=3;
}
IndexBufferExceptionFunc();
} catch(...) {
IndexBufferExceptionFunc();
}
}
}
}
//=============================================================================
// W3DTreeBuffer::updateVertexBuffer
//=============================================================================
/** Updates the push aside offset in vertex buffer. */
//=============================================================================
void W3DTreeBuffer::updateVertexBuffer(void)
{
if (!m_indexTree[0] || !m_vertexTree[0] || !m_initialized) {
return;
}
Int bNdx;
for (bNdx = 0; bNdxPeek_Model()->Get_Vertex_Count();
Vector3 *pVert = m_treeTypes[type].m_mesh->Peek_Model()->Get_Vertex_Array();
for (i=0; i0.0f) {
vLoc.X += pVert[i].Z * m_trees[curTree].pushAside * m_trees[curTree].pushAsideCos * m_treeTypes[type].m_data->m_maxOutwardMovement;
vLoc.Y += pVert[i].Z * m_trees[curTree].pushAside * m_trees[curTree].pushAsideSin* m_treeTypes[type].m_data->m_maxOutwardMovement;
}
vLoc.X += loc.X;
vLoc.Y += loc.Y;
vLoc.Z += loc.Z;
}
curVb->x = vLoc.X;
curVb->y = vLoc.Y;
curVb->z = vLoc.Z;
curVb->ny = 1.0f - m_treeTypes[type].m_data->m_darkening*m_trees[curTree].pushAside;
curVb++;
}
}
}
}
//-----------------------------------------------------------------------------
// Public Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DTreeBuffer::~W3DTreeBuffer
//=============================================================================
/** Destructor. Releases w3d assets. */
//=============================================================================
W3DTreeBuffer::~W3DTreeBuffer(void)
{
freeTreeBuffers();
REF_PTR_RELEASE(m_treeTexture);
Int i;
for (i=0; iDeletePixelShader(m_dwTreePixelShader);
m_dwTreePixelShader = 0;
if (m_dwTreeVertexShader)
DX8Wrapper::_Get_D3D_Device8()->DeleteVertexShader(m_dwTreeVertexShader);
m_dwTreeVertexShader = 0;
}
//=============================================================================
// W3DTreeBuffer::unitMoved
//=============================================================================
/** Check to see if a unit collided with a tree/grass/bush. */
//=============================================================================
void W3DTreeBuffer::unitMoved(Object *unit)
{
if (unit->isKindOf(KINDOF_IMMOBILE)) {
// This is the initial positioning of the object, and we don't care. jba. [6/5/2003]
return;
}
Real radius = unit->getGeometryInfo().getMajorRadius();
if (unit->getGeometryInfo().getGeomType()==GEOMETRY_BOX) {
if (radius>unit->getGeometryInfo().getMinorRadius()) {
radius = unit->getGeometryInfo().getMinorRadius();
}
}
// Value to assume for the tree radius.
#define TREE_RADIUS_APPROX 7.0f
radius += TREE_RADIUS_APPROX;
Coord3D pos = *unit->getPosition();
Real x = pos.x-radius;
Real y = pos.y-radius;
if (xm_bounds.hi.x) x = m_bounds.hi.x;
if (y>m_bounds.hi.y) y = m_bounds.hi.y;
Int xIndex = REAL_TO_INT_FLOOR ( (x/(m_bounds.hi.x-m_bounds.lo.x)) * (PARTITION_WIDTH_HEIGHT-0.1f) );
Int yIndex = REAL_TO_INT_FLOOR ( (y/(m_bounds.hi.y-m_bounds.lo.y)) * (PARTITION_WIDTH_HEIGHT-0.1f) );
DEBUG_ASSERTCRASH(xIndex>=0 && yIndex>=0 && xIndexm_bounds.hi.x) x = m_bounds.hi.x;
if (y>m_bounds.hi.y) y = m_bounds.hi.y;
Int xMax = REAL_TO_INT_CEIL ( (x/(m_bounds.hi.x-m_bounds.lo.x)) * (PARTITION_WIDTH_HEIGHT-0.1f) );
Int yMax = REAL_TO_INT_CEIL ( (y/(m_bounds.hi.y-m_bounds.lo.y)) * (PARTITION_WIDTH_HEIGHT-0.1f) );
DEBUG_ASSERTCRASH(xMax>=0 && yMax>=0 && xMax<=PARTITION_WIDTH_HEIGHT && yMax<=PARTITION_WIDTH_HEIGHT, ("Invalid range."));
Int i, j;
for (i=xIndex; i=m_numTrees) {
DEBUG_CRASH(("Invalid index."));
break;
}
if (m_trees[treeNdx].treeType<0) {
treeNdx = m_trees[treeNdx].nextInPartition;
continue; // Tree is deleted. [7/11/2003]
}
Coord3D delta;
delta.set(m_trees[treeNdx].location.X, m_trees[treeNdx].location.Y, m_trees[treeNdx].location.Z );
delta.sub(&pos);
if (radius*radius>delta.lengthSqr()) {
bool canTopple = unit->getCrusherLevel() > 1;
if (canTopple && m_treeTypes[m_trees[treeNdx].treeType].m_data->m_doTopple) {
// Give a vector with direction to thing.
Coord3D toppleVector;
toppleVector.set(m_trees[treeNdx].location.X, m_trees[treeNdx].location.Y, 0);
toppleVector.x -= unit->getPosition()->x;
toppleVector.y -= unit->getPosition()->y;
applyTopplingForce(m_trees+treeNdx, &toppleVector, 0, W3D_TOPPLE_OPTIONS_NONE);
} else if (m_treeTypes[m_trees[treeNdx].treeType].m_data->m_framesToMoveOutward>1) {
pushAsideTree(m_trees[treeNdx].drawableID, &pos, unit->getUnitDirectionVector2D(), unit->getID());
}
}
treeNdx = m_trees[treeNdx].nextInPartition;
}
}
}
}
//=============================================================================
// W3DTreeBuffer::allocateTreeBuffers
//=============================================================================
/** Allocates the index and vertex buffers. */
//=============================================================================
void W3DTreeBuffer::allocateTreeBuffers(void)
{
Int i;
for (i=0; igeomCollidesWithGeom( pos, geom, angle, &treePos, info, 0.0f)) {
// remove it [7/11/2003]
m_trees[i].treeType = DELETED_TREE_TYPE;
m_anythingChanged = true;
}
}
}
//=============================================================================
// W3DTreeBuffer::addTreeTypes
//=============================================================================
/** Adds a type of tree (model & texture). */
//=============================================================================
Int W3DTreeBuffer::addTreeType(const W3DTreeDrawModuleData *data)
{
if (m_numTreeTypes>=MAX_TYPES) {
DEBUG_CRASH(("Too many kinds of trees in map. Reduce kinds of trees, or raise tree limit. jba."));
return 0;
}
m_needToUpdateTexture = true;
m_treeTypes[m_numTreeTypes].m_mesh = NULL;
RenderObjClass *robj=WW3DAssetManager::Get_Instance()->Create_Render_Obj(data->m_modelName.str());
if (robj==NULL) {
DEBUG_CRASH(("Unable to find model for tree %s\n", data->m_modelName.str()));
return 0;
}
AABoxClass box;
robj->Get_Obj_Space_Bounding_Box(box);
Vector3 offset(0,0,0);
if (robj->Class_ID() == RenderObjClass::CLASSID_HLOD) {
RenderObjClass *hlod = robj;
robj = hlod->Get_Sub_Object(0);
const Matrix3D xfm = robj->Get_Bone_Transform(0);
xfm.Get_Translation(&offset);
REF_PTR_RELEASE(hlod);
}
if (robj->Class_ID() == RenderObjClass::CLASSID_MESH)
m_treeTypes[m_numTreeTypes].m_mesh = (MeshClass*)robj;
if (m_treeTypes[m_numTreeTypes].m_mesh==NULL) {
DEBUG_CRASH(("Tree %s is not simple mesh. Tell artist to re-export. Don't Ignore!!!\n", data->m_modelName.str()));
return 0;
}
Int numVertex = m_treeTypes[m_numTreeTypes].m_mesh->Peek_Model()->Get_Vertex_Count();
Vector3 *pVert = m_treeTypes[m_numTreeTypes].m_mesh->Peek_Model()->Get_Vertex_Array();
const Matrix3D xfm = m_treeTypes[m_numTreeTypes].m_mesh->Get_Transform();
SphereClass bounds(pVert, numVertex);
bounds.Center += offset;
m_treeTypes[m_numTreeTypes].m_bounds = bounds;
m_treeTypes[m_numTreeTypes].m_textureOrigin.x = 0;
m_treeTypes[m_numTreeTypes].m_textureOrigin.y = 0;
m_treeTypes[m_numTreeTypes].m_data = data;
m_treeTypes[m_numTreeTypes].m_offset = offset;
m_treeTypes[m_numTreeTypes].m_shadowSize = (box.Extent.X + box.Extent.Y); // Average extent * 2. jba.
m_treeTypes[m_numTreeTypes].m_doShadow = data->m_doShadow;
m_numTreeTypes++;
return m_numTreeTypes-1;
}
//=============================================================================
// W3DTreeBuffer::addTree
//=============================================================================
/** Adds a tree. Name is the W3D model name, supported models are
ALPINE, DECIDUOUS and SHRUB. */
//=============================================================================
void W3DTreeBuffer::addTree(DrawableID id, Coord3D location, Real scale, Real angle,
Real randomScaleAmount, const W3DTreeDrawModuleData *data)
{
if (m_numTrees >= MAX_TREES) {
return;
}
if (!m_initialized) {
return;
}
Int treeType = DELETED_TREE_TYPE;
Int i;
for (i=0; im_modelName.compareNoCase(data->m_modelName)==0 &&
m_treeTypes[i].m_data->m_textureName.compareNoCase(data->m_textureName)==0) {
treeType = i;
break;
}
}
if (treeType<0) {
treeType = addTreeType(data);
if (treeType<0) {
return;
}
m_needToUpdateTexture = true;
}
if (data->m_framesToMoveOutward > 2 || data->m_doTopple) {
// Trees/grass that topples or gets pushed aside (outward) gets put in the area partition. jba [7/7/2003]
Short bucket = getPartitionBucket(location);
m_trees[m_numTrees].nextInPartition = m_areaPartition[bucket];
m_areaPartition[bucket] = m_numTrees;
} else {
m_trees[m_numTrees].nextInPartition = END_OF_PARTITION;
}
Real randomScale = GameClientRandomValueReal( 1.0f - randomScaleAmount, 1.0f+ randomScaleAmount );
m_trees[m_numTrees].sin = WWMath::Sin(angle);
m_trees[m_numTrees].cos = WWMath::Cos(angle);
if (randomScaleAmount>0.0f) {
// Randomizes the scale and orientation of trees.
m_trees[m_numTrees].scale = scale*randomScale;
} else {
// Don't randomly scale & orient
m_trees[m_numTrees].scale = scale;
}
m_trees[m_numTrees].location = Vector3(location.x, location.y, location.z);
m_trees[m_numTrees].treeType = treeType;
// Translate the bounding sphere of the model.
m_trees[m_numTrees].bounds = m_treeTypes[treeType].m_bounds;
m_trees[m_numTrees].bounds.Center *= m_trees[m_numTrees].scale;
m_trees[m_numTrees].bounds.Radius *= m_trees[m_numTrees].scale;
m_trees[m_numTrees].bounds.Center += m_trees[m_numTrees].location;
// Initially set it invisible. cull will update it's visiblity flag.
m_trees[m_numTrees].visible = false;
m_trees[m_numTrees].drawableID = id;
m_trees[m_numTrees].firstIndex = 0;
m_trees[m_numTrees].bufferNdx = -1;
m_trees[m_numTrees].swayType = GameClientRandomValue(0, MAX_SWAY_TYPES-1);
m_trees[m_numTrees].pushAside = 0;
m_trees[m_numTrees].lastFrameUpdated = 0;
m_trees[m_numTrees].pushAsideSource = INVALID_ID;
m_trees[m_numTrees].pushAsideDelta = 0;
m_trees[m_numTrees].pushAsideCos = 1;
m_trees[m_numTrees].pushAsideSin = 1;
m_trees[m_numTrees].m_toppleState = TOPPLE_UPRIGHT;
m_numTrees++;
}
//=============================================================================
// W3DTreeBuffer::updateTreePosition
//=============================================================================
/** Updates a tree's position */
//=============================================================================
Bool W3DTreeBuffer::updateTreePosition(DrawableID id, Coord3D location, Real angle)
{
Int i;
for (i=0; igetFrame();
if(m_trees[i].pushAsideSource == pusherID) {
if (m_trees[i].lastFrameUpdated - lastFrame < 3)
return; // already pushing. [5/28/2003]
}
if(m_trees[i].pushAside != 0.0f) {
return; // already pushing. [5/28/2003]
}
m_trees[i].pushAsideSource = pusherID;
Coord3D delta;
delta.set(m_trees[i].location.X, m_trees[i].location.Y, m_trees[i].location.Z);
delta.sub(pusherPos);
if (pusherDirection->x*delta.y - pusherDirection->y*delta.x > 0.0f) {
m_trees[i].pushAsideCos = -pusherDirection->y;
m_trees[i].pushAsideSin = pusherDirection->x;
} else {
m_trees[i].pushAsideCos = pusherDirection->y;
m_trees[i].pushAsideSin = -pusherDirection->x;
}
m_anyPushChanged = true;
m_trees[i].pushAsideDelta = 1.0f/(Real)m_treeTypes[m_trees[i].treeType].m_data->m_framesToMoveOutward;
}
}
}
DECLARE_PERF_TIMER(Tree_Render)
//=============================================================================
// W3DTreeBuffer::drawTrees
//=============================================================================
/** Draws the trees. Uses camera to cull. */
//=============================================================================
void W3DTreeBuffer::drawTrees(CameraClass * camera, RefRenderObjListIterator *pDynamicLightsIterator)
{
USE_PERF_TIMER(Tree_Render)
if (!m_isTerrainPass) {
return;
}
// if breeze changes, always process the full update, even if not visible,
// so that things offscreen won't 'pop' when first viewed
const BreezeInfo& info = TheScriptEngine->getBreezeInfo();
Bool pause = TheScriptEngine->isTimeFrozenScript() || TheScriptEngine->isTimeFrozenDebug();
if (TheGameLogic && TheGameLogic->isGamePaused()) {
pause = true;
}
Int i;
if (!pause) {
if (info.m_breezeVersion != m_curSwayVersion)
{
updateSway(info);
}
}
Vector3 swayFactor[MAX_SWAY_TYPES];
for (i=0; i NUM_SWAY_ENTRIES-1) {
m_curSwayOffset[i] -= NUM_SWAY_ENTRIES-1;
}
}
Int minOffset = REAL_TO_INT_FLOOR(m_curSwayOffset[i]);
if (minOffset>=0 && minOffset+1m_useShadowDecals) {
for (curTree=0; curTreesetSize(m_treeTypes[type].m_shadowSize, -m_treeTypes[type].m_shadowSize*factor);
m_shadow->setPosition(m_trees[curTree].location.X, m_trees[curTree].location.Y, m_trees[curTree].location.Z);
TheW3DProjectedShadowManager->queueDecal(m_shadow);
}
TheW3DProjectedShadowManager->flushDecals(m_shadow->getTexture(0), SHADOW_DECAL);
}
// Update pushed aside and toppling trees.
for (curTree=0; curTreem_killWhenToppled) {
if (m_trees[curTree].m_sinkFramesLeft==0) {
m_trees[curTree].treeType = DELETED_TREE_TYPE; // delete it. [7/11/2003]
m_anythingChanged = true; // need to regenerate trees. [7/11/2003]
}
m_trees[curTree].m_sinkFramesLeft--;
m_trees[curTree].location.Z -= m_treeTypes[type].m_data->m_sinkDistance/m_treeTypes[type].m_data->m_sinkFrames;
m_trees[curTree].m_mtx.Set_Translation(m_trees[curTree].location);
}
} else if (m_trees[curTree].pushAsideDelta!=0.0f) {
m_trees[curTree].pushAside += m_trees[curTree].pushAsideDelta;
if (m_trees[curTree].pushAside>=1.0f) {
m_trees[curTree].pushAsideDelta = -1.0/(Real)m_treeTypes[type].m_data->m_framesToMoveInward;
} else if (m_trees[curTree].pushAside<=0.0f) {
m_trees[curTree].pushAsideDelta = 0.0f;
m_trees[curTree].pushAside = 0.0f;
}
}
}
if (m_anythingChanged) {
loadTreesInVertexAndIndexBuffers(pDynamicLightsIterator);
m_anythingChanged = false;
} else if (m_anyPushChanged) {
m_anyPushChanged = false;
updateVertexBuffer();
}
//#define DEBUG_TEXTURE 1
#ifdef DEBUG_TEXTURE // Draw the combined texture for debugging. jba. [4/21/2003]
// Setup the vertex buffer, shader & texture.
DX8Wrapper::Set_Shader(detailAlphaShader);
DX8Wrapper::Set_Texture(0,m_treeTexture);
DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8, 6);
//draw an infinite sky plane
DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8, DX8_FVF_XYZNDUV2, 4);
{
DynamicIBAccessClass::WriteLockClass ibLock(&ib_access);
UnsignedShort *ndx = ibLock.Get_Index_Array();
if (ndx) {
ndx[0] = 0;
ndx[1] = 1;
ndx[2] = 2;
ndx[3] = 1;
ndx[4] = 3;
ndx[5] = 2;
}
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* verts=lock.Get_Formatted_Vertex_Array();
if(verts)
{
Real width = 300;
Real origin = 40;
verts[0].x=origin;
verts[0].y=origin;
verts[0].z=15;
verts[0].u1=0;
verts[0].v1=0;
verts[0].diffuse=0xffffffff;
verts[1].x=origin+width;
verts[1].y=origin;
verts[1].z=15;
verts[1].u1=1;
verts[1].v1=0;
verts[1].diffuse=0xffffffff;
verts[2].x=origin;
verts[2].y=origin+width;
verts[2].z=15;
verts[2].u1=0;
verts[2].v1=1;
verts[2].diffuse=0xffffffff;
verts[3].x=origin+width;
verts[3].y=origin+width;
verts[3].z=15;
verts[3].u1=1;
verts[3].v1=1;
verts[3].diffuse=0xffffffff;
}
}
DX8Wrapper::Set_Index_Buffer(ib_access,0);
DX8Wrapper::Set_Vertex_Buffer(vb_access);
Matrix3D tm(1);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
DX8Wrapper::Draw_Triangles( 0,2, 0, 4); //draw a quad, 2 triangles, 4 verts
#endif
if (m_curNumTreeIndices[0] == 0) {
return;
}
DX8Wrapper::Set_Shader(detailAlphaShader);
DX8Wrapper::Set_Texture(0,m_treeTexture);
DX8Wrapper::Set_Texture(1,NULL);
DX8Wrapper::Set_DX8_Texture_Stage_State(0, D3DTSS_TEXCOORDINDEX, 0);
DX8Wrapper::Set_DX8_Texture_Stage_State(1, D3DTSS_TEXCOORDINDEX, 1);
// Draw all the trees.
DX8Wrapper::Apply_Render_State_Changes();
W3DShaderManager::setShroudTex(1);
DX8Wrapper::Apply_Render_State_Changes();
if (m_dwTreeVertexShader) {
D3DXMATRIX matProj, matView, matWorld;
DX8Wrapper::_Get_DX8_Transform(D3DTS_WORLD, *(Matrix4x4*)&matWorld);
DX8Wrapper::_Get_DX8_Transform(D3DTS_VIEW, *(Matrix4x4*)&matView);
DX8Wrapper::_Get_DX8_Transform(D3DTS_PROJECTION, *(Matrix4x4*)&matProj);
D3DXMATRIX mat;
D3DXMatrixMultiply( &mat, &matView, &matProj );
D3DXMatrixMultiply( &mat, &matWorld, &mat );
D3DXMatrixTranspose( &mat, &mat );
// c4 - Composite World-View-Projection Matrix
DX8Wrapper::_Get_D3D_Device8()->SetVertexShaderConstant( 4, &mat, 4 );
Vector4 noSway(0,0,0,0);
DX8Wrapper::_Get_D3D_Device8()->SetVertexShaderConstant( 8, &noSway, 1 );
// c8 - c8+MAX_SWAY_TYPES - the sway amount.
for (i=0; iSetVertexShaderConstant( 9+i, &sway4, 1 );
}
W3DShroud *shroud;
if ((shroud=TheTerrainRenderObject->getShroud()) != 0) {
// Setup shroud texture info [6/6/2003]
float xoffset = 0;
float yoffset = 0;
Real width=shroud->getCellWidth();
Real height=shroud->getCellHeight();
xoffset = -(float)shroud->getDrawOriginX() + width;
yoffset = -(float)shroud->getDrawOriginY() + height;
Vector4 offset(xoffset, yoffset, 0, 0);
DX8Wrapper::_Get_D3D_Device8()->SetVertexShaderConstant( 32, &offset, 1 );
width = 1.0f/(width*shroud->getTextureWidth());
height = 1.0f/(height*shroud->getTextureHeight());
offset.Set(width, height, 1, 1);
DX8Wrapper::_Get_D3D_Device8()->SetVertexShaderConstant( 33, &offset, 1 );
} else {
Vector4 offset(0,0,0,0);
DX8Wrapper::_Get_D3D_Device8()->SetVertexShaderConstant( 32, &offset, 1 );
DX8Wrapper::_Get_D3D_Device8()->SetVertexShaderConstant( 33, &offset, 1 );
}
DX8Wrapper::Set_Vertex_Shader(m_dwTreeVertexShader);
#if 0
DX8Wrapper::Set_Pixel_Shader(m_dwTreePixelShader);
// a.c. 6/16 - allow switching between normal and 2X mode for terrain
Real mulTwoX = 0.5f;
if(TheGlobalData && TheGlobalData->m_useOverbright)
mulTwoX = 1.0f;
DX8Wrapper::_Get_D3D_Device8()->SetPixelShaderConstant(1, D3DXVECTOR4(mulTwoX, mulTwoX, mulTwoX, mulTwoX), 1);
#endif
} else {
DX8Wrapper::Set_Vertex_Shader(DX8_FVF_XYZNDUV1);
}
Int bNdx;
for (bNdx=0;bNdxSetVertexShader(m_dwTreeVertexShader);
DX8Wrapper::_Get_D3D_Device8()->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
DX8Wrapper::_Get_D3D_Device8()->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1);
DX8Wrapper::_Get_D3D_Device8()->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
}
DX8Wrapper::Draw_Triangles( 0, m_curNumTreeIndices[bNdx]/3, 0, m_curNumTreeVertices[bNdx]);
}
DX8Wrapper::Set_Vertex_Shader(DX8_FVF_XYZNDUV1);
DX8Wrapper::Set_Pixel_Shader(NULL);
DX8Wrapper::Invalidate_Cached_Render_States(); //code above mucks around with W3D states so make sure we reset
}
//-------------------------------------------------------------------------------------------------
///< Start the toppling process by giving a force vector
//-------------------------------------------------------------------------------------------------
void W3DTreeBuffer::applyTopplingForce( TTree *tree, const Coord3D* toppleDirection, Real toppleSpeed,
UnsignedInt options )
{
if (tree->m_toppleState != TOPPLE_UPRIGHT) {
return;
}
const W3DTreeDrawModuleData* d = m_treeTypes[tree->treeType].m_data;
// Having a low toppleSpeed is BAD. In particular, if the toppleSpeed is exactly 0, the
// tree will stay upright forever, frozen in place (because the sway update is dead)
// but never dying
if ( toppleSpeed < d->m_minimumToppleSpeed )
{
toppleSpeed = d->m_minimumToppleSpeed;
}
tree->m_toppleDirection = *toppleDirection;
tree->m_toppleDirection.normalize();
tree->m_angularAccumulation = 0;
tree->m_angularVelocity = toppleSpeed * d->m_initialVelocityPercent;
tree->m_angularAcceleration = toppleSpeed * d->m_initialAccelPercent;
tree->m_toppleState = TOPPLE_FALLING;
tree->m_options = options;
Coord3D pos;
pos.set(tree->location.X, tree->location.Y, tree->location.Z);
FXList::doFXPos(d->m_toppleFX, &pos);
m_anyPushChanged = true;
tree->m_mtx.Make_Identity();
tree->m_mtx.Set_Translation(tree->location);
}
// this is our "bounce" limit -- slightly less that 90 degrees, to account for slop.
static const Real ANGULAR_LIMIT = PI/2 - PI/64;
//-------------------------------------------------------------------------------------------------
///< Keep track of rotational fall distance, bounce and/or stop when needed.
//-------------------------------------------------------------------------------------------------
void W3DTreeBuffer::updateTopplingTree(TTree *tree)
{
//DLOG(Debug::Format("updating W3DTreeBuffer %08lx\n",this));
DEBUG_ASSERTCRASH(tree->m_toppleState != TOPPLE_UPRIGHT, ("hmm, we should be sleeping here"));
if ( (tree->m_toppleState == TOPPLE_UPRIGHT) || (tree->m_toppleState == TOPPLE_DOWN) )
return;
const W3DTreeDrawModuleData* d = m_treeTypes[tree->treeType].m_data;
Int localPlayerIndex = ThePlayerList ? ThePlayerList->getLocalPlayer()->getPlayerIndex() : 0;
Coord3D pos;
pos.set(tree->location.X, tree->location.Y, tree->location.Z);
ObjectShroudStatus ss = ThePartitionManager->getPropShroudStatusForPlayer(localPlayerIndex, &pos);
if (ss==OBJECTSHROUD_FOGGED) {
// Don't update fogged trees. [8/11/2003]
tree->m_toppleState = TOPPLE_FOGGED;
return;
} else if (tree->m_toppleState == TOPPLE_FOGGED) {
// was fogged, now isn't.
tree->m_angularVelocity = 0;
tree->m_toppleState = TOPPLE_DOWN;
tree->m_mtx.In_Place_Pre_Rotate_X(-ANGULAR_LIMIT * tree->m_toppleDirection.y);
tree->m_mtx.In_Place_Pre_Rotate_Y(ANGULAR_LIMIT * tree->m_toppleDirection.x);
if (d->m_killWhenToppled) {
// If got killed in the fog, just remove. jba [8/11/2003]
tree->m_sinkFramesLeft = 0;
}
return;
}
const Real VELOCITY_BOUNCE_LIMIT = 0.01f; // if the velocity after a bounce will be this or lower, just stop at zero
const Real VELOCITY_BOUNCE_SOUND_LIMIT = 0.03f; // and if this low, then skip the bounce sound
Real curVelToUse = tree->m_angularVelocity;
if (tree->m_angularAccumulation + curVelToUse > ANGULAR_LIMIT)
curVelToUse = ANGULAR_LIMIT - tree->m_angularAccumulation;
tree->m_mtx.In_Place_Pre_Rotate_X(-curVelToUse * tree->m_toppleDirection.y);
tree->m_mtx.In_Place_Pre_Rotate_Y(curVelToUse * tree->m_toppleDirection.x);
tree->m_angularAccumulation += curVelToUse;
if ((tree->m_angularAccumulation >= ANGULAR_LIMIT) && (tree->m_angularVelocity > 0))
{
// Hit so either bounce or stop if too little remaining velocity.
tree->m_angularVelocity *= -d->m_bounceVelocityPercent;
if( BitTest( tree->m_options, W3D_TOPPLE_OPTIONS_NO_BOUNCE ) == TRUE ||
fabs(tree->m_angularVelocity) < VELOCITY_BOUNCE_LIMIT )
{
// too slow, just stop
tree->m_angularVelocity = 0;
tree->m_toppleState = TOPPLE_DOWN;
if (d->m_killWhenToppled) {
tree->m_sinkFramesLeft = d->m_sinkFrames;
}
}
else if( fabs(tree->m_angularVelocity) >= VELOCITY_BOUNCE_SOUND_LIMIT )
{
// fast enough bounce to warrant the bounce fx
if( BitTest( tree->m_options, W3D_TOPPLE_OPTIONS_NO_FX ) == FALSE ) {
Vector3 loc(0, 0, 3*TREE_RADIUS_APPROX); // Kinda towards the top of the tree. jba. [7/11/2003]
Vector3 xloc;
tree->m_mtx.Transform_Vector(tree->m_mtx, loc, &xloc);
Coord3D pos;
pos.set(xloc.X, xloc.Y, xloc.Z);
FXList::doFXPos(d->m_bounceFX, &pos);
}
}
}
else
{
tree->m_angularVelocity += tree->m_angularAcceleration;
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTreeBuffer::crc( Xfer *xfer )
{
// empty. jba [8/11/2003]
} // end CRC
// ------------------------------------------------------------------------------------------------
/** Xfer
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DTreeBuffer::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
Int i;
Int numTrees = m_numTrees;
xfer->xferInt(&numTrees);
if (xfer->getXferMode() == XFER_LOAD) {
m_numTrees = 0;
for (i=0; igetXferMode() != XFER_LOAD) {
tree = m_trees[i];
treeType = m_trees[i].treeType;
if (treeType != DELETED_TREE_TYPE) {
modelName = m_treeTypes[treeType].m_data->m_modelName;
modelTexture = m_treeTypes[treeType].m_data->m_textureName;
}
}
xfer->xferAsciiString(&modelName);
xfer->xferAsciiString(&modelTexture);
if (xfer->getXferMode() == XFER_LOAD) {
Int j;
for (j=0; jm_modelName.compareNoCase(modelName)==0 &&
m_treeTypes[j].m_data->m_textureName.compareNoCase(modelTexture)==0) {
treeType = j;
break;
}
}
}
xfer->xferReal(&tree.location.X);
xfer->xferReal(&tree.location.Y);
xfer->xferReal(&tree.location.Z);
xfer->xferReal(&tree.scale); ///< Scale at location.
xfer->xferReal(&tree.sin); ///< Sine of the rotation angle at location.
xfer->xferReal(&tree.cos); ///< Cosine of the rotation angle at location.
xfer->xferDrawableID(&tree.drawableID); ///< Drawable this tree corresponds to.
// Topple parameters. [7/7/2003]
xfer->xferReal(&tree.m_angularVelocity); ///< Velocity in degrees per frame (or is it radians per frame?)
xfer->xferReal(&tree.m_angularAcceleration); ///< Acceleration angularVelocity is increasing
xfer->xferCoord3D(&tree.m_toppleDirection); ///< Z-less direction we are toppling
xfer->xferUser(&tree.m_toppleState, sizeof(tree.m_toppleState)); ///< Stage this module is in.
xfer->xferReal(&tree.m_angularAccumulation); ///< How much have I rotated so I know when to bounce.
xfer->xferUnsignedInt(&tree.m_options); ///< topple options
xfer->xferMatrix3D(&tree.m_mtx);
xfer->xferUnsignedInt(&tree.m_sinkFramesLeft); ///< Toppled trees sink into the terrain & disappear, how many frames left.
if (xfer->getXferMode() == XFER_LOAD && treeType != DELETED_TREE_TYPE && treeType < m_numTreeTypes) {
Coord3D pos;
pos.set(tree.location.X, tree.location.Y, tree.location.Z);
Real angle = 0;
addTree(tree.drawableID, pos, tree.scale, angle, 0, m_treeTypes[treeType].m_data);
if (m_numTrees) {
TTree *curTree = &m_trees[m_numTrees-1];
curTree->m_angularAcceleration = tree.m_angularAcceleration;
curTree->m_angularVelocity = tree.m_angularVelocity;
curTree->m_toppleDirection = tree.m_toppleDirection;
curTree->m_toppleState = tree.m_toppleState;
curTree->m_options = tree.m_options;
curTree->m_mtx = tree.m_mtx;
curTree->m_sinkFramesLeft = tree.m_sinkFramesLeft;
}
}
}
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTreeBuffer::loadPostProcess( void )
{
// empty. jba [8/11/2003]
} // end loadPostProcess