Initial commit of Command & Conquer Generals and Command & Conquer Generals Zero Hour source code.

This commit is contained in:
LFeenanEA 2025-02-27 17:34:39 +00:00
parent 2e338c00cb
commit 3d0ee53a05
No known key found for this signature in database
GPG key ID: C6EBE8C2EA08F7E0
6072 changed files with 2283311 additions and 0 deletions

View file

@ -0,0 +1,332 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DDebrisDraw.cpp ////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Default w3d draw module
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "Common/FileSystem.h" // this is only here to pull in LOAD_TEST_ASSETS
#include "Common/GlobalData.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameLogic/Object.h"
#include "GameClient/Shadow.h"
#include "GameClient/FXList.h"
#include "GameLogic/TerrainLogic.h"
#include "WW3D2/HAnim.h"
#include "WW3D2/HLod.h"
#include "WW3D2/RendObj.h"
#include "W3DDevice/GameClient/Module/W3DDebrisDraw.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "W3DDevice/GameClient/W3DShadow.h"
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DDebrisDraw::W3DDebrisDraw(Thing *thing, const ModuleData* moduleData) : DrawModule(thing, moduleData)
{
m_renderObject = NULL;
for (int i = 0; i < STATECOUNT; ++i)
m_anims[i] = NULL;
m_fxFinal = NULL;
m_state = INITIAL;
m_frames = 0;
m_shadow = NULL;
m_finalStop = false;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DDebrisDraw::~W3DDebrisDraw(void)
{
if (TheW3DShadowManager && m_shadow)
{
TheW3DShadowManager->removeShadow(m_shadow);
m_shadow = NULL;
}
if (m_renderObject)
{
W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject);
REF_PTR_RELEASE(m_renderObject);
m_renderObject = NULL;
}
for (int i = 0; i < STATECOUNT; ++i)
{
REF_PTR_RELEASE(m_anims[i]);
m_anims[i] = NULL;
}
}
//-------------------------------------------------------------------------------------------------
void W3DDebrisDraw::setShadowsEnabled(Bool enable)
{
if (m_shadow)
m_shadow->enableShadowRender(enable);
}
//-------------------------------------------------------------------------------------------------
void W3DDebrisDraw::setFullyObscuredByShroud(Bool fullyObscured)
{
if (m_shadow)
m_shadow->enableShadowInvisible(fullyObscured);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DDebrisDraw::setModelName(AsciiString name, Color color, ShadowType t)
{
if (m_renderObject == NULL && !name.isEmpty())
{
Int hexColor = 0;
if (color != 0)
hexColor = color | 0xFF000000;
m_renderObject = W3DDisplay::m_assetManager->Create_Render_Obj(name.str(), getDrawable()->getScale(), hexColor);
DEBUG_ASSERTCRASH(m_renderObject, ("Debris model %s not found!\n",name.str()));
if (m_renderObject)
{
W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject);
m_renderObject->Set_User_Data(getDrawable()->getDrawableInfo());
Matrix3D transform;
///@todo: Change back to identity once we figure out why objects show up at 0,0,0
/// OBJECT_PILE
// transform.Set(Vector3(0,0,9999));
transform.Set(Vector3(0,0,0));
m_renderObject->Set_Transform(transform);
}
if (t != SHADOW_NONE)
{
Shadow::ShadowTypeInfo shadowInfo;
shadowInfo.m_type = t;
shadowInfo.m_sizeX=0;
shadowInfo.m_sizeY=0;
m_shadow = TheW3DShadowManager->addShadow(m_renderObject, &shadowInfo);
}
else
{
if (TheW3DShadowManager && m_shadow)
{
TheW3DShadowManager->removeShadow(m_shadow);
m_shadow = NULL;
}
}
// save the model name and color
m_modelName = name;
m_modelColor = color;
}
}
//-------------------------------------------------------------------------------------------------
void W3DDebrisDraw::setAnimNames(AsciiString initial, AsciiString flying, AsciiString final, const FXList* finalFX)
{
int i;
for (i = 0; i < STATECOUNT; ++i)
{
REF_PTR_RELEASE(m_anims[i]);
m_anims[i] = NULL;
}
m_anims[INITIAL] = initial.isEmpty() ? NULL : W3DDisplay::m_assetManager->Get_HAnim(initial.str());
m_anims[FLYING] = flying.isEmpty() ? NULL : W3DDisplay::m_assetManager->Get_HAnim(flying.str());
if (stricmp(final.str(), "STOP") == 0)
{
m_finalStop = true;
final = flying;
}
else
{
m_finalStop = false;
}
m_anims[FINAL] = final.isEmpty() ? NULL : W3DDisplay::m_assetManager->Get_HAnim(final.str());
m_state = 0;
m_frames = 0;
m_fxFinal = finalFX;
m_animInitial = initial;
m_animFlying = flying;
m_animFinal = final;
}
//-------------------------------------------------------------------------------------------------
static Bool isAnimationComplete(RenderObjClass* r)
{
if (r->Class_ID() == RenderObjClass::CLASSID_HLOD)
{
HLodClass *hlod = (HLodClass*)r;
return hlod->Is_Animation_Complete();
}
return true;
}
//-------------------------------------------------------------------------------------------------
static Bool isNearlyZero(const Coord3D* vel)
{
const Real TINY = 0.01f;
return fabs(vel->x) < TINY && fabs(vel->y) < TINY && fabs(vel->z) < TINY;
}
// ------------------------------------------------------------------------------------------------
void W3DDebrisDraw::reactToTransformChange( const Matrix3D *oldMtx,
const Coord3D *oldPos,
Real oldAngle )
{
if( m_renderObject )
m_renderObject->Set_Transform( *getDrawable()->getTransformMatrix() );
}
//-------------------------------------------------------------------------------------------------
void W3DDebrisDraw::doDrawModule(const Matrix3D* transformMtx)
{
if (m_renderObject)
{
Matrix3D scaledTransform;
if (getDrawable()->getInstanceScale() != 1.0f)
{ //do custom scaling of the W3D model.
scaledTransform=*transformMtx;
scaledTransform.Scale(getDrawable()->getInstanceScale());
transformMtx = &scaledTransform;
m_renderObject->Set_ObjectScale(getDrawable()->getInstanceScale());
}
m_renderObject->Set_Transform(*transformMtx);
static const RenderObjClass::AnimMode TheAnimModes[STATECOUNT] =
{
RenderObjClass::ANIM_MODE_ONCE,
RenderObjClass::ANIM_MODE_LOOP,
RenderObjClass::ANIM_MODE_ONCE
};
Int oldState = m_state;
Object* obj = getDrawable()->getObject();
const Int MIN_FINAL_FRAMES = 3;
if (m_state != FINAL && obj != NULL && !obj->isAboveTerrain() && m_frames > MIN_FINAL_FRAMES)
{
m_state = FINAL;
}
else if (m_state < FINAL && (isAnimationComplete(m_renderObject)))
{
++m_state;
}
HAnimClass* hanim = m_anims[m_state];
if (hanim != NULL && (hanim != m_renderObject->Peek_Animation() || oldState != m_state))
{
RenderObjClass::AnimMode m = TheAnimModes[m_state];
if (m_state == FINAL)
{
FXList::doFXPos(m_fxFinal, getDrawable()->getPosition(), getDrawable()->getTransformMatrix(), 0, NULL, 0.0f);
if (m_finalStop)
m = RenderObjClass::ANIM_MODE_MANUAL;
}
m_renderObject->Set_Animation(hanim, 0, m);
}
++m_frames;
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DDebrisDraw::crc( Xfer *xfer )
{
// extend base class
DrawModule::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DDebrisDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
DrawModule::xfer( xfer );
// model name
xfer->xferAsciiString( &m_modelName );
// model color
xfer->xferColor( &m_modelColor );
// set the model and color
if( xfer->getXferMode() == XFER_LOAD )
setModelName( m_modelName, m_modelColor, SHADOW_NONE );
// animation initial
xfer->xferAsciiString( &m_animInitial );
// anim flying
xfer->xferAsciiString( &m_animFlying );
// anim final
xfer->xferAsciiString( &m_animFinal );
// when loading, set the animations
if( xfer->getXferMode() == XFER_LOAD )
setAnimNames( m_animInitial, m_animFlying, m_animFinal, NULL );
// state
xfer->xferInt( &m_state );
// frames
xfer->xferInt( &m_frames );
// final stop
xfer->xferBool( &m_finalStop );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DDebrisDraw::loadPostProcess( void )
{
// extend base class
DrawModule::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,202 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DDefaultDraw.cpp ///////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Default w3d draw module
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "Common/FileSystem.h" // this is only here to pull in LOAD_TEST_ASSETS
#include "Common/GlobalData.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameLogic/Object.h"
#include "GameClient/Shadow.h"
#include "GameClient/FXList.h"
#include "GameLogic/TerrainLogic.h"
#include "WW3D2/HAnim.h"
#include "WW3D2/HLod.h"
#include "WW3D2/RendObj.h"
#include "W3DDevice/GameClient/Module/W3DDefaultDraw.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "W3DDevice/GameClient/W3DShadow.h"
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DDefaultDraw::W3DDefaultDraw(Thing *thing, const ModuleData* moduleData) : DrawModule(thing, moduleData)
{
#ifdef LOAD_TEST_ASSETS
m_renderObject = NULL;
m_shadow = NULL;
if (!getDrawable()->getTemplate()->getLTAName().isEmpty())
{
m_renderObject = W3DDisplay::m_assetManager->Create_Render_Obj(getDrawable()->getTemplate()->getLTAName().str(), getDrawable()->getScale(), 0);
Shadow::ShadowTypeInfo shadowInfo;
shadowInfo.m_type=(ShadowType)SHADOW_VOLUME;
shadowInfo.m_sizeX=0; //use defaults
shadowInfo.m_sizeY=0;
shadowInfo.m_offsetX=0;
shadowInfo.m_offsetY=0;
m_shadow = TheW3DShadowManager->addShadow(m_renderObject, &shadowInfo);
DEBUG_ASSERTCRASH(m_renderObject, ("Test asset %s not found", getDrawable()->getTemplate()->getLTAName().str()));
if (m_renderObject)
{
W3DDisplay::m_3DScene->Add_Render_Object(m_renderObject);
m_renderObject->Set_User_Data(getDrawable()->getDrawableInfo());
Matrix3D transform;
///@todo: Change back to identity once we figure out why objects show up at 0,0,0
/// OBJECT_PILE
// transform.Set(Vector3(0,0,9999));
transform.Set(Vector3(0,0,0));
m_renderObject->Set_Transform(transform);
}
}
#endif
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DDefaultDraw::reactToTransformChange( const Matrix3D *oldMtx,
const Coord3D *oldPos,
Real oldAngle )
{
if( m_renderObject )
m_renderObject->Set_Transform( *getDrawable()->getTransformMatrix() );
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DDefaultDraw::~W3DDefaultDraw(void)
{
#ifdef LOAD_TEST_ASSETS
if (TheW3DShadowManager && m_shadow)
{
TheW3DShadowManager->removeShadow(m_shadow);
m_shadow = NULL;
}
if (m_renderObject)
{
W3DDisplay::m_3DScene->Remove_Render_Object(m_renderObject);
REF_PTR_RELEASE(m_renderObject);
m_renderObject = NULL;
}
#endif
}
//-------------------------------------------------------------------------------------------------
void W3DDefaultDraw::setShadowsEnabled(Bool enable)
{
#ifdef LOAD_TEST_ASSETS
if (m_shadow)
m_shadow->enableShadowRender(enable);
#endif
}
//-------------------------------------------------------------------------------------------------
void W3DDefaultDraw::setFullyObscuredByShroud(Bool fullyObscured)
{
#ifdef LOAD_TEST_ASSETS
if (m_shadow)
m_shadow->enableShadowInvisible(fullyObscured);
#endif
}
//-------------------------------------------------------------------------------------------------
void W3DDefaultDraw::doDrawModule(const Matrix3D* transformMtx)
{
#ifdef LOAD_TEST_ASSETS
if(m_renderObject)
{
Matrix3D scaledTransform;
if (getDrawable()->getInstanceScale() != 1.0f)
{ //do custom scaling of the W3D model.
scaledTransform=*transformMtx;
scaledTransform.Scale(getDrawable()->getInstanceScale());
transformMtx = &scaledTransform;
m_renderObject->Set_ObjectScale(getDrawable()->getInstanceScale());
}
else
{
m_renderObject->Set_Transform(*transformMtx);
}
}
#endif
return;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DDefaultDraw::crc( Xfer *xfer )
{
// extend base class
DrawModule::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DDefaultDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
DrawModule::xfer( xfer );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DDefaultDraw::loadPostProcess( void )
{
// extend base class
DrawModule::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,171 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DDependencyModelDraw.cpp ////////////////////////////////////////////////////////////////////////////
// Author: Graham Smallwood, October 2002
// Desc: Draw module just like Model, except it can't draw unless somebody else explicitly says to, since they
// have to draw first.
//
// This draw module can be used in a general case (although I don't see why), m_attachToDrawableBoneInContainer
// is for the one present and main reason to use this module. Our transport needs to tell us it is okay to
// draw after he draws.
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameLogic/Object.h"
#include "GameLogic/Module/ContainModule.h"
#include "W3DDevice/GameClient/Module/W3DDependencyModelDraw.h"
//-------------------------------------------------------------------------------------------------
W3DDependencyModelDrawModuleData::W3DDependencyModelDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
W3DDependencyModelDrawModuleData::~W3DDependencyModelDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DDependencyModelDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DModelDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "AttachToBoneInContainer", INI::parseAsciiString, NULL, offsetof(W3DDependencyModelDrawModuleData, m_attachToDrawableBoneInContainer) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DDependencyModelDraw::W3DDependencyModelDraw( Thing *thing, const ModuleData* moduleData ) : W3DModelDraw( thing, moduleData )
{
m_dependencyCleared = FALSE;
}
//-------------------------------------------------------------------------------------------------
W3DDependencyModelDraw::~W3DDependencyModelDraw()
{
}
//-------------------------------------------------------------------------------------------------
// All this does is stop the call path if we haven't been cleared to draw yet
void W3DDependencyModelDraw::doDrawModule(const Matrix3D* transformMtx)
{
if( m_dependencyCleared )
{
// We've been cleared by the thing we were waiting to draw, so we can draw.
W3DModelDraw::doDrawModule( transformMtx );
m_dependencyCleared = FALSE;
}
}
//-------------------------------------------------------------------------------------------------
void W3DDependencyModelDraw::notifyDrawModuleDependencyCleared( )
{
m_dependencyCleared = TRUE;
}
// ------------------------------------------------------------------------------------------------
void W3DDependencyModelDraw::adjustTransformMtx(Matrix3D& mtx) const
{
W3DModelDraw::adjustTransformMtx(mtx);
// We have an additional adjustment to make, we want to use a bone in our container if there is one
const Object *me = getDrawable()->getObject();
const W3DDependencyModelDrawModuleData *md = getW3DDependencyModelDrawModuleData();
if( md->m_attachToDrawableBoneInContainer.isNotEmpty()
&& me
&& me->getContainedBy()
&& !me->getContainedBy()->getContain()->isEnclosingContainerFor(me)
)
{
// If we are currently "riding on", then our client position is determined by the client position of
// a particular bone in our container object. Our logic position is updated by OpenContain.
const Drawable *theirDrawable = me->getContainedBy()->getDrawable();
if( theirDrawable )
{
Matrix3D theirBoneMtx;
if( theirDrawable->getCurrentWorldspaceClientBonePositions( md->m_attachToDrawableBoneInContainer.str(), theirBoneMtx ) )
{
mtx = theirBoneMtx;
}
else
{
DEBUG_LOG(("m_attachToDrawableBoneInContainer %s not found\n",getW3DDependencyModelDrawModuleData()->m_attachToDrawableBoneInContainer.str()));
}
}
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DDependencyModelDraw::crc( Xfer *xfer )
{
// extend base class
W3DModelDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DDependencyModelDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DModelDraw::xfer( xfer );
// Dependency status
xfer->xferBool( &m_dependencyCleared );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DDependencyModelDraw::loadPostProcess( void )
{
// extend base class
W3DModelDraw::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,466 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DLaserDraw.cpp /////////////////////////////////////////////////////////////////////////
// Author: Colin Day, May 2001
// Desc: W3DLaserDraw
// Updated: Kris Morness July 2002 -- made it data driven and added new features to make it flexible.
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include "Common/Thing.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/Color.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameClient.h"
#include "GameClient/RayEffect.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameLogic/TerrainLogic.h"
#include "GameLogic/Module/LaserUpdate.h"
#include "W3DDevice/GameClient/Module/W3DLaserDraw.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "WW3D2/RInfo.h"
#include "WW3D2/Camera.h"
#include "WW3D2/Segline.h"
#include "WWMath/Vector3.h"
#include "WW3D2/AssetMgr.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DLaserDrawModuleData::W3DLaserDrawModuleData()
{
m_innerBeamWidth = 0.0f; //The total width of beam
m_outerBeamWidth = 1.0f; //The total width of beam
m_numBeams = 1; //Number of overlapping cylinders that make the beam. 1 beam will just use inner data.
m_maxIntensityFrames = 0; //Laser stays at max intensity for specified time in ms.
m_fadeFrames = 0; //Laser will fade and delete.
m_scrollRate = 0.0f;
m_tile = false;
m_segments = 1;
m_arcHeight = 0.0f;
m_segmentOverlapRatio = 0.0f;
m_tilingScalar = 1.0f;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DLaserDrawModuleData::~W3DLaserDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DLaserDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
ModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "NumBeams", INI::parseUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_numBeams ) },
{ "InnerBeamWidth", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_innerBeamWidth ) },
{ "OuterBeamWidth", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_outerBeamWidth ) },
{ "InnerColor", INI::parseColorInt, NULL, offsetof( W3DLaserDrawModuleData, m_innerColor ) },
{ "OuterColor", INI::parseColorInt, NULL, offsetof( W3DLaserDrawModuleData, m_outerColor ) },
{ "MaxIntensityLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_maxIntensityFrames ) },
{ "FadeLifetime", INI::parseDurationUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_fadeFrames ) },
{ "Texture", INI::parseAsciiString, NULL, offsetof( W3DLaserDrawModuleData, m_textureName ) },
{ "ScrollRate", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_scrollRate ) },
{ "Tile", INI::parseBool, NULL, offsetof( W3DLaserDrawModuleData, m_tile ) },
{ "Segments", INI::parseUnsignedInt, NULL, offsetof( W3DLaserDrawModuleData, m_segments ) },
{ "ArcHeight", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_arcHeight ) },
{ "SegmentOverlapRatio", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_segmentOverlapRatio ) },
{ "TilingScalar", INI::parseReal, NULL, offsetof( W3DLaserDrawModuleData, m_tilingScalar ) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DLaserDraw::W3DLaserDraw( Thing *thing, const ModuleData* moduleData ) :
DrawModule( thing, moduleData ),
m_line3D(NULL),
m_texture(NULL),
m_textureAspectRatio(1.0f),
m_selfDirty(TRUE)
{
Vector3 dummyPos1( 0.0f, 0.0f, 0.0f );
Vector3 dummyPos2( 1.0f, 1.0f, 1.0f );
Int i;
const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
m_texture = WW3DAssetManager::Get_Instance()->Get_Texture( data->m_textureName.str() );
if (m_texture)
{
SurfaceClass::SurfaceDescription surfaceDesc;
m_texture->Get_Level_Description(surfaceDesc);
m_textureAspectRatio = (Real)surfaceDesc.Width/(Real)surfaceDesc.Height;
}
//Get the color components for calculation purposes.
Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
//Make sure our beams range between 1 and the maximum cap.
#ifdef I_WANT_TO_BE_FIRED
// srj sez: this data is const for a reason. casting away the constness because we don't like the values
// isn't an acceptable solution. if you need to constrain the values, do so at parsing time, when
// it's still legal to modify these values. (In point of fact, there's not even really any reason to limit
// the numBeams or segments anymore.)
data->m_numBeams = __min( __max( 1, data->m_numBeams ), MAX_LASER_LINES );
data->m_segments = __min( __max( 1, data->m_segments ), MAX_SEGMENTS );
data->m_tilingScalar = __max( 0.01f, data->m_tilingScalar );
#endif
//Allocate an array of lines equal to the number of beams * segments
m_line3D = NEW SegmentedLineClass *[ data->m_numBeams * data->m_segments ];
for( int segment = 0; segment < data->m_segments; segment++ )
{
//We don't care about segment positioning yet until we actually set the position
// create all the lines we need at the right transparency level
for( i = data->m_numBeams - 1; i >= 0; i-- )
{
int index = segment * data->m_numBeams + i;
Real red, green, blue, alpha, width;
if( data->m_numBeams == 1 )
{
width = data->m_innerBeamWidth;
alpha = innerAlpha;
red = innerRed * innerAlpha;
green = innerGreen * innerAlpha;
blue = innerBlue * innerAlpha;
}
else
{
//Calculate the scale between min and max values
//0 means use min value, 1 means use max value
//0.2 means min value + 20% of the diff between min and max
Real scale = i / ( data->m_numBeams - 1.0f);
width = data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth);
alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
red = innerRed + scale * (outerRed - innerRed) * innerAlpha;
green = innerGreen + scale * (outerGreen - innerGreen) * innerAlpha;
blue = innerBlue + scale * (outerBlue - innerBlue) * innerAlpha;
}
m_line3D[ index ] = NEW SegmentedLineClass;
SegmentedLineClass *line = m_line3D[ index ];
if( line )
{
line->Set_Texture( m_texture );
line->Set_Shader( ShaderClass::_PresetAdditiveShader ); //pick the alpha blending mode you want - see shader.h for others.
line->Set_Width( width );
line->Set_Color( Vector3( red, green, blue ) );
line->Set_UV_Offset_Rate( Vector2(0.0f, data->m_scrollRate) ); //amount to scroll texture on each draw
if( m_texture )
{
line->Set_Texture_Mapping_Mode(SegLineRendererClass::TILED_TEXTURE_MAP); //this tiles the texture across the line
}
// add to scene
W3DDisplay::m_3DScene->Add_Render_Object( line ); //add it to our scene so it gets rendered with other objects.
// hide the render object until the first time we come to draw it and
// set the correct position
line->Set_Visible( 0 );
}
} // end for i
} //end segment loop
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DLaserDraw::~W3DLaserDraw( void )
{
const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
for( int i = 0; i < data->m_numBeams * data->m_segments; i++ )
{
// remove line from scene
W3DDisplay::m_3DScene->Remove_Render_Object( m_line3D[ i ] );
// delete line
REF_PTR_RELEASE( m_line3D[ i ] );
} // end for i
delete [] m_line3D;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
Real W3DLaserDraw::getLaserTemplateWidth() const
{
const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
return data->m_outerBeamWidth * 0.5f;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DLaserDraw::doDrawModule(const Matrix3D* transformMtx)
{
//UnsignedInt currentFrame = TheGameClient->getFrame();
const W3DLaserDrawModuleData *data = getW3DLaserDrawModuleData();
//Get the updatemodule that drives it...
Drawable *draw = getDrawable();
static NameKeyType key_LaserUpdate = NAMEKEY( "LaserUpdate" );
LaserUpdate *update = (LaserUpdate*)draw->findClientUpdateModule( key_LaserUpdate );
if( !update )
{
DEBUG_ASSERTCRASH( 0, ("W3DLaserDraw::doDrawModule() expects its owner drawable %s to have a ClientUpdate = LaserUpdate module.", draw->getTemplate()->getName().str() ));
return;
}
//If the update has moved the laser, it requires a reset of the laser.
if (update->isDirty() || m_selfDirty)
{
update->setDirty(false);
m_selfDirty = false;
Vector3 laserPoints[ 2 ];
for( int segment = 0; segment < data->m_segments; segment++ )
{
if( data->m_arcHeight > 0.0f && data->m_segments > 1 )
{
//CALCULATE A CURVED LINE BASED ON TOTAL LENGTH AND DESIRED HEIGHT INCREASE
//To do this we will use a portion of the cos wave ranging between -0.25PI
//and +0.25PI. 0PI is 1.0 and 0.25PI is 0.70 -- resulting in a somewhat
//gentle curve depending on the line height and length. We also have to make
//the line *level* for this phase of the calculations.
//Get the desired direct line
Coord3D lineStart, lineEnd, lineVector;
lineStart.set( update->getStartPos() );
lineEnd.set( update->getEndPos() );
//This is critical -- in the case we have sloped lines (at the end, we'll fix it)
// lineEnd.z = lineStart.z;
//Get the length of the line
lineVector.set( &lineEnd );
lineVector.sub( &lineStart );
Real lineLength = lineVector.length();
//Get the middle point (we'll use this to determine how far we are from
//that to calculate our height -- middle point is the highest).
Coord3D lineMiddle;
lineMiddle.set( &lineStart );
lineMiddle.add( &lineEnd );
lineMiddle.scale( 0.5 );
//The half length is used to scale with the distance from middle to
//get our cos( 0 to 0.25 PI) cos value
Real halfLength = lineLength * 0.5f;
//Now calculate which segment we will use.
Real startSegmentRatio = segment / ((Real)data->m_segments);
Real endSegmentRatio = (segment + 1.0f) / ((Real)data->m_segments);
//Offset the segment ever-so-slightly to minimize overlap -- only apply
//to segments that are not the start/end point
if( segment > 0 )
{
startSegmentRatio -= data->m_segmentOverlapRatio;
}
if( segment < data->m_segments - 1 )
{
endSegmentRatio += data->m_segmentOverlapRatio;
}
//Calculate our start segment position on the *ground*.
Coord3D segmentStart, segmentEnd, vector;
vector.set( &lineVector );
vector.scale( startSegmentRatio );
segmentStart.set( &lineStart );
segmentStart.add( &vector );
//Calculate our end segment position on the *ground*.
vector.set( &lineVector );
vector.scale( endSegmentRatio );
segmentEnd.set( &lineStart );
segmentEnd.add( &vector );
//--------------------------------------------------------------------------------
//Now at this point, we have our segment line in the level positions that we want.
//Calculate the raised height for the start/end segment positions using cosine.
//--------------------------------------------------------------------------------
//Calculate the distance from midpoint for the start positions.
vector.set( &lineMiddle );
vector.sub( &segmentStart );
Real dist = vector.length();
Real scaledRadians = dist / halfLength * PI * 0.5f;
Real height = cos( scaledRadians );
height *= data->m_arcHeight;
segmentStart.z += height;
//Now do the same thing for the end position.
vector.set( &lineMiddle );
vector.sub( &segmentEnd );
dist = vector.length();
scaledRadians = dist / halfLength * PI * 0.5f;
height = cos( scaledRadians );
height *= data->m_arcHeight;
segmentEnd.z += height;
//This makes the laser skim the ground rather than penetrate it!
laserPoints[ 0 ].Set( segmentStart.x, segmentStart.y,
MAX( segmentStart.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentStart.x, segmentStart.y) ) );
laserPoints[ 1 ].Set( segmentEnd.x, segmentEnd.y,
MAX( segmentEnd.z, 2.0f + TheTerrainLogic->getGroundHeight(segmentEnd.x, segmentEnd.y) ) );
}
else
{
//No arc -- way simpler!
laserPoints[ 0 ].Set( update->getStartPos()->x, update->getStartPos()->y, update->getStartPos()->z );
laserPoints[ 1 ].Set( update->getEndPos()->x, update->getEndPos()->y, update->getEndPos()->z );
}
//Get the color components for calculation purposes.
Real innerRed, innerGreen, innerBlue, innerAlpha, outerRed, outerGreen, outerBlue, outerAlpha;
GameGetColorComponentsReal( data->m_innerColor, &innerRed, &innerGreen, &innerBlue, &innerAlpha );
GameGetColorComponentsReal( data->m_outerColor, &outerRed, &outerGreen, &outerBlue, &outerAlpha );
for( Int i = data->m_numBeams - 1; i >= 0; i-- )
{
Real alpha, width;
int index = segment * data->m_numBeams + i;
if( data->m_numBeams == 1 )
{
width = data->m_innerBeamWidth * update->getWidthScale();
alpha = innerAlpha;
}
else
{
//Calculate the scale between min and max values
//0 means use min value, 1 means use max value
//0.2 means min value + 20% of the diff between min and max
Real scale = i / ( data->m_numBeams - 1.0f);
Real ultimateScale = update->getWidthScale();
width = (data->m_innerBeamWidth + scale * (data->m_outerBeamWidth - data->m_innerBeamWidth));
width *= ultimateScale;
alpha = innerAlpha + scale * (outerAlpha - innerAlpha);
}
//Calculate the number of times to tile the line based on the height of the texture used.
if( m_texture && data->m_tile )
{
//Calculate the length of the line.
Vector3 lineVector;
Vector3::Subtract( laserPoints[1], laserPoints[0], &lineVector );
Real length = lineVector.Length();
//Adjust tile factor so texture is NOT stretched but tiled equally in both width and length.
Real tileFactor = length/width*m_textureAspectRatio*data->m_tilingScalar;
//Set the tile factor
m_line3D[ index ]->Set_Texture_Tile_Factor( tileFactor ); //number of times to tile texture across each segment
}
m_line3D[ index ]->Set_Width( width );
m_line3D[ index ]->Set_Points( 2, &laserPoints[0] );
}
}
}
return;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DLaserDraw::crc( Xfer *xfer )
{
// extend base class
DrawModule::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DLaserDraw::xfer( Xfer *xfer )
{
// version
const XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
DrawModule::xfer( xfer );
// Kris says there is no data to save for these, go ask him.
// m_selfDirty is not saved, is runtime only
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DLaserDraw::loadPostProcess( void )
{
// extend base class
DrawModule::loadPostProcess();
m_selfDirty = true; // so we update the first time after reload
} // end loadPostProcess

View file

@ -0,0 +1,151 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FIEL: W3DOverlordTankDraw.cpp ////////////////////////////////////////////////////////////////////////////
// Author: Graham Smallwood, October 2002
// Desc: The Overlord has a super specific special need. He needs his rider to draw explicitly after him,
// and he needs direct access to get that rider when everyone else can't see it because of the OverlordContain.
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameLogic/Object.h"
#include "GameLogic/Module/ContainModule.h"
#include "W3DDevice/GameClient/Module/W3DOverlordTankDraw.h"
//-------------------------------------------------------------------------------------------------
W3DOverlordTankDrawModuleData::W3DOverlordTankDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
W3DOverlordTankDrawModuleData::~W3DOverlordTankDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DOverlordTankDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DTankDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DOverlordTankDraw::W3DOverlordTankDraw( Thing *thing, const ModuleData* moduleData )
: W3DTankDraw( thing, moduleData )
{
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DOverlordTankDraw::~W3DOverlordTankDraw()
{
}
//-------------------------------------------------------------------------------------------------
void W3DOverlordTankDraw::doDrawModule(const Matrix3D* transformMtx)
{
W3DTankDraw::doDrawModule(transformMtx);
// Our big thing is that we get our specific passenger (the turret thing) and then wake it up and make it draw
// It depends on us because our renderObject is only made correct in the act of drawing.
Object *me = getDrawable()->getObject();
if( me
&& me->getContain()
&& me->getContain()->friend_getRider()
&& me->getContain()->friend_getRider()->getDrawable()
)
{
Drawable *riderDraw = me->getContain()->friend_getRider()->getDrawable();
riderDraw->setColorTintEnvelope( *getDrawable()->getColorTintEnvelope() );
riderDraw->notifyDrawableDependencyCleared();
riderDraw->draw( NULL );// What the hell? This param isn't used for anything
}
}
//-------------------------------------------------------------------------------------------------
void W3DOverlordTankDraw::setHidden(Bool h)
{
W3DTankDraw::setHidden(h);
// We need to hide our rider, since he won't realize he's being contained in a contained container
Object *me = getDrawable()->getObject();
if( me
&& me->getContain()
&& me->getContain()->friend_getRider()
&& me->getContain()->friend_getRider()->getDrawable()
)
{
me->getContain()->friend_getRider()->getDrawable()->setDrawableHidden(h);
}
}
//-------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DOverlordTankDraw::crc( Xfer *xfer )
{
// extend base class
W3DTankDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DOverlordTankDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DTankDraw::xfer( xfer );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DOverlordTankDraw::loadPostProcess( void )
{
// extend base class
W3DTankDraw::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,199 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DPoliceCarDraw.cpp /////////////////////////////////////////////////////////////////////
// Author: Colin Day, May 2001
// Desc: W3DPoliceCarDraw
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include "Common/STLTypedefs.h"
#include "Common/Thing.h"
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "W3DDevice/GameClient/Module/W3DPoliceCarDraw.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "common/RandomValue.h"
#include "WW3D2/HAnim.h"
#include "W3DDevice/GameClient/W3DScene.h"
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
/** Create a dynamic light for the search light */
//-------------------------------------------------------------------------------------------------
W3DDynamicLight *W3DPoliceCarDraw::createDynamicLight( void )
{
W3DDynamicLight *light = NULL;
// get me a dynamic light from the scene
light = W3DDisplay::m_3DScene->getADynamicLight();
if( light )
{
light->setEnabled( TRUE );
light->Set_Ambient( Vector3( 0.0f, 0.0f, 0.0f ) );
// Use all ambient, and no diffuse. This produces a circle of light on
// even and uneven ground. Diffuse lighting shows up ground unevenness, which looks
// funny on a searchlight. So no diffuse. jba.
light->Set_Diffuse( Vector3( 0.0f, 0.0f, 0.0f ) );
light->Set_Position( Vector3( 0.0f, 0.0f, 0.0f ) );
light->Set_Far_Attenuation_Range( 5, 15 );
} // end if
return light;
} // end createDynamicSearchLight
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DPoliceCarDraw::W3DPoliceCarDraw( Thing *thing, const ModuleData* moduleData ) : W3DTruckDraw( thing, moduleData )
{
m_light = NULL;
m_curFrame = GameClientRandomValueReal(0, 10 );
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DPoliceCarDraw::~W3DPoliceCarDraw( void )
{
// disable the light ... the scene will re-use it later
if( m_light )
{
// Have it fade out over 5 frames.
m_light->setFrameFade(0, 5);
m_light->setDecayRange();
m_light->setDecayColor();
m_light = NULL;
} // end if
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DPoliceCarDraw::doDrawModule(const Matrix3D* transformMtx)
{
const Real floatAmt = 8.0f;
const Real animAmt = 0.25;
// get pointers to our render objects that we'll need
RenderObjClass* policeCarRenderObj = getRenderObject();
if( policeCarRenderObj == NULL )
return;
HAnimClass *anim = policeCarRenderObj->Peek_Animation();
if (anim)
{
Real frames = anim->Get_Num_Frames();
m_curFrame += animAmt;
if (m_curFrame > frames-1) {
m_curFrame = 0;
}
policeCarRenderObj->Set_Animation(anim, m_curFrame);
}
Real red = 0;
Real green = 0;
Real blue = 0;
if (m_curFrame < 3) {
red = 1; green = 0.5;
} else if (m_curFrame < 6) {
red = 1;
} else if (m_curFrame < 7) {
red = 1; green = 0.5;
} else if (m_curFrame < 9) {
red = 0.5+(9-m_curFrame)/4;
blue = (m_curFrame-5)/6;
} else if (m_curFrame < 12) {
blue=1;
} else if (m_curFrame <= 14) {
green = (m_curFrame-11)/3;
blue = (14-m_curFrame)/2;
red = (m_curFrame-11)/3;
}
// make us a light if we don't already have one
if( m_light == NULL )
m_light = createDynamicLight();
// if we have a search light, position it
if( m_light )
{
Coord3D pos = *getDrawable()->getPosition();
m_light->Set_Diffuse( Vector3( red, green, blue) );
m_light->Set_Ambient( Vector3( red/2, green/2, blue/2) );
m_light->Set_Far_Attenuation_Range( 3, 20 );
m_light->Set_Position( Vector3( pos.x,pos.y,pos.z+floatAmt ) );
}
W3DTruckDraw::doDrawModule(transformMtx);
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DPoliceCarDraw::crc( Xfer *xfer )
{
// extend base class
W3DTruckDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DPoliceCarDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DTruckDraw::xfer( xfer );
// John A says there is no data for these to save
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DPoliceCarDraw::loadPostProcess( void )
{
// extend base class
W3DTruckDraw::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,264 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DProjectileStreamDraw.cpp ////////////////////////////////////////////////////////////
// Tile a texture strung between Projectiles
// Graham Smallwood, May 2002
/////////////////////////////////////////////////////////////////////////////////////////////////
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameLogic/Object.h"
#include "W3DDevice/GameClient/Module/W3DProjectileStreamDraw.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "WW3D2/AssetMgr.h"
#include "WW3D2/Segline.h"
#include "WWMath/Vector3.h"
//-------------------------------------------------------------------------------------------------
W3DProjectileStreamDrawModuleData::W3DProjectileStreamDrawModuleData()
{
m_textureName = "";
m_width = 0.0f;
m_tileFactor = 0.0f;
m_scrollRate = 0.0f;
m_maxSegments = 0;
}
//-------------------------------------------------------------------------------------------------
W3DProjectileStreamDrawModuleData::~W3DProjectileStreamDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DProjectileStreamDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
ModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "Texture", INI::parseAsciiString, NULL, offsetof(W3DProjectileStreamDrawModuleData, m_textureName) },
{ "Width", INI::parseReal, NULL, offsetof(W3DProjectileStreamDrawModuleData, m_width) },
{ "TileFactor", INI::parseReal, NULL, offsetof(W3DProjectileStreamDrawModuleData, m_tileFactor) },
{ "ScrollRate", INI::parseReal, NULL, offsetof(W3DProjectileStreamDrawModuleData, m_scrollRate) },
{ "MaxSegments", INI::parseInt, NULL, offsetof(W3DProjectileStreamDrawModuleData, m_maxSegments) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DProjectileStreamDraw::~W3DProjectileStreamDraw()
{
for( Int lineIndex = 0; lineIndex < m_linesValid; lineIndex++ )
{
SegmentedLineClass *deadLine = m_allLines[lineIndex];
if (deadLine)
{ if (deadLine->Peek_Scene())
W3DDisplay::m_3DScene->Remove_Render_Object( deadLine );
REF_PTR_RELEASE( deadLine );
}
}
REF_PTR_RELEASE( m_texture );
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DProjectileStreamDraw::W3DProjectileStreamDraw( Thing *thing, const ModuleData* moduleData ) : DrawModule( thing, moduleData )
{
const W3DProjectileStreamDrawModuleData* d = getW3DProjectileStreamDrawModuleData();
m_texture = WW3DAssetManager::Get_Instance()->Get_Texture( d->m_textureName.str() );
for( Int index = 0; index < MAX_PROJECTILE_STREAM; index++ )
m_allLines[index] = NULL;
m_linesValid = 0;
}
void W3DProjectileStreamDraw::setFullyObscuredByShroud(Bool fullyObscured)
{
if (fullyObscured)
{ //we need to remove all our lines from the scene because they are hidden
for( Int lineIndex = 0; lineIndex < m_linesValid; lineIndex++ )
{
SegmentedLineClass *deadLine = m_allLines[lineIndex];
if (deadLine && deadLine->Peek_Scene())
deadLine->Remove();
}
}
else
{ //we need to restore lines into scene
for( Int lineIndex = 0; lineIndex < m_linesValid; lineIndex++ )
{
SegmentedLineClass *deadLine = m_allLines[lineIndex];
if (deadLine && !deadLine->Peek_Scene())
W3DDisplay::m_3DScene->Add_Render_Object(deadLine);
}
}
}
//-------------------------------------------------------------------------------------------------
/** Map behavior states into W3D animations. */
//-------------------------------------------------------------------------------------------------
void W3DProjectileStreamDraw::doDrawModule(const Matrix3D* )
{
// get object from logic
Object *me = getDrawable()->getObject();
if (me == NULL)
return;
static NameKeyType key_ProjectileStreamUpdate = NAMEKEY("ProjectileStreamUpdate");
ProjectileStreamUpdate* update = (ProjectileStreamUpdate*)me->findUpdateModule(key_ProjectileStreamUpdate);
const W3DProjectileStreamDrawModuleData *data = getW3DProjectileStreamDrawModuleData();
Vector3 allPoints[MAX_PROJECTILE_STREAM];
Int pointsUsed;
update->getAllPoints( allPoints, &pointsUsed );
Vector3 stagingPoints[MAX_PROJECTILE_STREAM];
Vector3 zeroVector(0, 0, 0);
Int linesMade = 0;
Int currentMasterPoint = 0;
UnsignedInt currentStagingPoint = 0;
if( data->m_maxSegments )
{
// If I have a drawing cap, I need to increase the start point in the array. The furthest (oldest)
// point from the tank is in spot zero.
currentMasterPoint = pointsUsed - data->m_maxSegments;
currentMasterPoint = max( 0, currentMasterPoint ); // (but if they say to draw more than exists, draw all)
}
// Okay. I have an array of ordered points that may have blanks in it. I need to copy to the staging area
// until I hit a blank or the end. Then if I have a line made, I'll overwrite it, otherwise I'll make a new one.
// I'll keep doing this until I run out of valid points.
while( currentMasterPoint < pointsUsed )
{
while( currentMasterPoint < pointsUsed && allPoints[currentMasterPoint] != zeroVector )
{
// While I am not looking at a bad point (off edge or zero)
stagingPoints[currentStagingPoint] = allPoints[currentMasterPoint];// copy to the staging
currentStagingPoint++;// increment how many I have
currentMasterPoint++;// increment what I am looking at
}
// Use or reuse a line
if( currentStagingPoint > 1 )
{
// Don't waste a line on a double hole (0) or a one point line (1)
makeOrUpdateLine( stagingPoints, currentStagingPoint, linesMade );
linesMade++;// keep track of how many are real this frame
}
currentMasterPoint++;//I am either pointed off the edge anyway, or I am pointed at a zero I want to skip
currentStagingPoint = 0;//start over in the staging area
}
Int oldLinesValid = m_linesValid;
for( Int lineIndex = linesMade; lineIndex < oldLinesValid; lineIndex++ )
{
// Delete any line we aren't using anymore.
SegmentedLineClass *deadLine = m_allLines[lineIndex];
if (deadLine->Peek_Scene())
W3DDisplay::m_3DScene->Remove_Render_Object( deadLine );
REF_PTR_RELEASE( deadLine );
m_allLines[lineIndex] = NULL;
m_linesValid--;
}
}
void W3DProjectileStreamDraw::makeOrUpdateLine( Vector3 *points, UnsignedInt pointCount, Int lineIndex )
{
Bool newLine = FALSE;
if( m_allLines[lineIndex] == NULL )
{
//Need a new one if this is blank, otherwise I'll reset the existing one
m_allLines[lineIndex] = NEW SegmentedLineClass;
m_linesValid++;
newLine = TRUE;
}
SegmentedLineClass *line = m_allLines[lineIndex];
line->Set_Points(pointCount, points); //tell the line which points to use
if( newLine )
{
// This is one time stuff we only need to do if this is a new and not a change
const W3DProjectileStreamDrawModuleData *data = getW3DProjectileStreamDrawModuleData();
line->Set_Texture(m_texture); //set the texture
line->Set_Shader(ShaderClass::_PresetAdditiveSpriteShader); //pick the alpha blending mode you want - see shader.h for others.
line->Set_Width(data->m_width); //set line width in world units
line->Set_Texture_Mapping_Mode(SegLineRendererClass::TILED_TEXTURE_MAP); //this tiles the texture across the line
line->Set_Texture_Tile_Factor(data->m_tileFactor); //number of times to tile texture across each segment
line->Set_UV_Offset_Rate(Vector2(0.0f,data->m_scrollRate)); //amount to scroll texture on each draw
W3DDisplay::m_3DScene->Add_Render_Object( line); //add it to our scene so it gets rendered with other objects.
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DProjectileStreamDraw::crc( Xfer *xfer )
{
// extend base class
DrawModule::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DProjectileStreamDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
DrawModule::xfer( xfer );
// Graham says there is no data that needs saving here
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DProjectileStreamDraw::loadPostProcess( void )
{
// extend base class
DrawModule::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,296 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DRopeDraw.cpp ////////////////////////////////////////////////////////////////////////
// Author: Steven Johnson, Aug 2002
// Desc: Rope drawing
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <windows.h>
#include "Common/Thing.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/ClientRandomValue.h"
#include "GameClient/Color.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameClient.h"
#include "GameLogic/GameLogic.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/Module/W3DRopeDraw.h"
#include "WW3D2/Line3D.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "Common/GameState.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DRopeDraw::W3DRopeDraw( Thing *thing, const ModuleData* moduleData ) : DrawModule( thing, moduleData )
{
m_curLen = 0.0f;
m_maxLen = 1.0f;
m_width = 0.5f;
m_color.red = 0.0f;
m_color.green = 0.0f;
m_color.blue = 0.0f;
m_curSpeed = 0.0f;
m_maxSpeed = 0.0f;
m_accel = 0.0f;
m_wobbleLen = m_maxLen; // huge
m_wobbleAmp = 0.0f;
m_segments.clear();
m_curWobblePhase = 0.0f;
m_curZOffset = 0.0f;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DRopeDraw::buildSegments()
{
DEBUG_ASSERTCRASH(m_segments.empty(), ("Hmmn, not empty"));
m_segments.clear();
Int numSegs = ceil(m_maxLen / m_wobbleLen);
Real eachLen = m_maxLen / (Real)numSegs;
Coord3D pos = *getDrawable()->getPosition();
for (int i = 0; i < numSegs; ++i, pos.z += eachLen)
{
SegInfo info;
Real axis = GameClientRandomValueReal(0, 2*PI);
info.wobbleAxisX = Cos(axis);
info.wobbleAxisY = Sin(axis);
info.line = NEW Line3DClass( Vector3(pos.x,pos.y,pos.z),
Vector3(pos.x,pos.y,pos.z+eachLen),
m_width * 0.5f, // width
m_color.red, // red
m_color.green, // green
m_color.blue, // blue
1.0f ); // transparency
info.softLine = NEW Line3DClass( Vector3(pos.x,pos.y,pos.z),
Vector3(pos.x,pos.y,pos.z+eachLen),
m_width, // width
m_color.red, // red
m_color.green, // green
m_color.blue, // blue
0.5f ); // transparency
W3DDisplay::m_3DScene->Add_Render_Object( info.line );
W3DDisplay::m_3DScene->Add_Render_Object( info.softLine );
m_segments.push_back(info);
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DRopeDraw::tossSegments()
{
// remove tracer from the scene and delete
for (std::vector<SegInfo>::iterator it = m_segments.begin(); it != m_segments.end(); ++it)
{
if (it->line)
{
W3DDisplay::m_3DScene->Remove_Render_Object(it->line);
REF_PTR_RELEASE((it->line));
}
if (it->softLine)
{
W3DDisplay::m_3DScene->Remove_Render_Object(it->softLine);
REF_PTR_RELEASE((it->softLine));
}
}
m_segments.clear();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DRopeDraw::initRopeParms(Real length, Real width, const RGBColor& color, Real wobbleLen, Real wobbleAmp, Real wobbleRate)
{
m_maxLen = max(1.0f, length);
m_curLen = 0.0f;
m_width = width;
m_color = color;
m_wobbleLen = min(m_maxLen, wobbleLen);
m_wobbleAmp = wobbleAmp;
m_wobbleRate = wobbleRate;
m_curZOffset = 0.0f;
tossSegments();
buildSegments();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DRopeDraw::setRopeCurLen(Real length)
{
m_curLen = length;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DRopeDraw::setRopeSpeed(Real curSpeed, Real maxSpeed, Real accel)
{
m_curSpeed = curSpeed;
m_maxSpeed = maxSpeed;
m_accel = accel;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DRopeDraw::~W3DRopeDraw()
{
tossSegments();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DRopeDraw::doDrawModule(const Matrix3D* transformMtx)
{
if (m_segments.empty())
{
buildSegments();
}
if (!m_segments.empty())
{
Real deflection = Sin(m_curWobblePhase) * m_wobbleAmp;
const Coord3D* pos = getDrawable()->getPosition();
Vector3 start(pos->x, pos->y, pos->z + m_curZOffset);
Real eachLen = m_curLen / m_segments.size();
for (std::vector<SegInfo>::iterator it = m_segments.begin(); it != m_segments.end(); ++it)
{
Vector3 end(pos->x + deflection*it->wobbleAxisX, pos->y + deflection*it->wobbleAxisY, start.Z - eachLen);
if (it->line)
(it->line)->Reset(start, end);
if (it->softLine)
(it->softLine)->Reset(start, end);
start = end;
}
}
m_curWobblePhase += m_wobbleRate;
if (m_curWobblePhase > 2*PI)
m_curWobblePhase -= 2*PI;
m_curZOffset += m_curSpeed;
m_curSpeed += m_accel;
if (m_curSpeed > m_maxSpeed)
m_curSpeed = m_maxSpeed;
else if (m_curSpeed < -m_maxSpeed)
m_curSpeed = -m_maxSpeed;
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DRopeDraw::crc( Xfer *xfer )
{
// extend base class
DrawModule::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DRopeDraw::xfer( Xfer *xfer )
{
// version
const XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
DrawModule::xfer( xfer );
// m_segments is not saved
// cur len
xfer->xferReal( &m_curLen );
// max len
xfer->xferReal( &m_maxLen );
// width
xfer->xferReal( &m_width );
// color
xfer->xferRGBColor( &m_color );
// cur speed
xfer->xferReal( &m_curSpeed );
// max speed
xfer->xferReal( &m_maxSpeed );
// acceleration
xfer->xferReal( &m_accel );
// wobble len
xfer->xferReal( &m_wobbleLen );
// wobble amp
xfer->xferReal( &m_wobbleAmp );
// wobble rate
xfer->xferReal( &m_wobbleRate );
// current wobble phase
xfer->xferReal( &m_curWobblePhase );
// cur Z offset
xfer->xferReal( &m_curZOffset );
if (xfer->getXferMode() == XFER_LOAD)
tossSegments();
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DRopeDraw::loadPostProcess( void )
{
// extend base class
DrawModule::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,137 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DScienceModelDraw.cpp ////////////////////////////////////////////////////////////////////////////
// Author: Graham Smallwood, NOVEMBER 2002
// Desc: Draw module just like Model, except it only draws if the local player has the specified science
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "W3DDevice/GameClient/Module/W3DScienceModelDraw.h"
#include "Common/Player.h"
#include "Common/PlayerList.h"
#include "Common/Science.h"
#include "Common/Xfer.h"
//-------------------------------------------------------------------------------------------------
W3DScienceModelDrawModuleData::W3DScienceModelDrawModuleData()
{
m_requiredScience = SCIENCE_INVALID;
}
//-------------------------------------------------------------------------------------------------
W3DScienceModelDrawModuleData::~W3DScienceModelDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DScienceModelDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DModelDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "RequiredScience", INI::parseScience, NULL, offsetof(W3DScienceModelDrawModuleData, m_requiredScience) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DScienceModelDraw::W3DScienceModelDraw( Thing *thing, const ModuleData* moduleData ) : W3DModelDraw( thing, moduleData )
{
}
//-------------------------------------------------------------------------------------------------
W3DScienceModelDraw::~W3DScienceModelDraw()
{
}
//-------------------------------------------------------------------------------------------------
// All this does is stop the call path if we haven't been cleared to draw yet
void W3DScienceModelDraw::doDrawModule(const Matrix3D* transformMtx)
{
ScienceType science = getW3DScienceModelDrawModuleData()->m_requiredScience;
if( science == SCIENCE_INVALID )
{
DEBUG_ASSERTCRASH(science != SCIENCE_INVALID, ("ScienceModelDraw has invalid science as condition.") );
setHidden( TRUE );
return;
}
if( !ThePlayerList->getLocalPlayer()->hasScience(science)
&& ThePlayerList->getLocalPlayer()->isPlayerActive()
)
{
// We just don't draw for people without our science except for Observers
setHidden( TRUE );
return;
}
W3DModelDraw::doDrawModule(transformMtx);
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DScienceModelDraw::crc( Xfer *xfer )
{
// extend base class
W3DModelDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DScienceModelDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DModelDraw::xfer( xfer );
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DScienceModelDraw::loadPostProcess( void )
{
// extend base class
W3DModelDraw::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,155 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DSupplyDraw.cpp ////////////////////////////////////////////////////////////////////////////
// Author: Graham Smallwood, September 2002
// Desc: Draw module reacts to SupplyStatus setting by hiding an equal number of the specified bone array.
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "W3DDevice/GameClient/Module/W3DSupplyDraw.h"
//-------------------------------------------------------------------------------------------------
W3DSupplyDrawModuleData::W3DSupplyDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
W3DSupplyDrawModuleData::~W3DSupplyDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DSupplyDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DModelDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "SupplyBonePrefix", INI::parseAsciiString, NULL, offsetof(W3DSupplyDrawModuleData, m_supplyBonePrefix) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DSupplyDraw::W3DSupplyDraw( Thing *thing, const ModuleData* moduleData ) : W3DModelDraw( thing, moduleData )
{
m_totalBones = -1;
m_lastNumberShown = 0;
}
//-------------------------------------------------------------------------------------------------
W3DSupplyDraw::~W3DSupplyDraw()
{
}
void W3DSupplyDraw::updateDrawModuleSupplyStatus( Int maxSupply, Int currentSupply )
{
W3DModelDraw::updateDrawModuleSupplyStatus( maxSupply, currentSupply );
AsciiString boneName = getW3DSupplyDrawModuleData()->m_supplyBonePrefix;
if( m_totalBones == -1 )
{
m_totalBones = getDrawable()->getPristineBonePositions( boneName.str(), 1, NULL, NULL, INT_MAX );// The last arg is to guard the size of the arrays. I am not passing any in, I am just counting bones.
m_lastNumberShown = m_totalBones;
}
// Figure the % of our bones we should show, and if it is a different % than last time
// start showing and hiding them.
Int bonesToShow = ceil(m_totalBones * ( currentSupply / (float)maxSupply ));
bonesToShow = min( bonesToShow, m_totalBones );
if( bonesToShow != m_lastNumberShown )
{
// Show/hide the bones that are now different, the indices between last and now (low, high].
Int lowIndex = min( m_lastNumberShown, bonesToShow );
Int highIndex = max( m_lastNumberShown, bonesToShow );
Bool hide = bonesToShow < m_lastNumberShown;
Int currentIndex = lowIndex + 1;
std::vector<ModelConditionInfo::HideShowSubObjInfo> boneVector;
while( currentIndex <= highIndex )
{
char buffer[16];
sprintf( buffer, "%s%02d", boneName.str(), currentIndex );
ModelConditionInfo::HideShowSubObjInfo info;
info.hide = hide;
info.subObjName = buffer;
boneVector.push_back(info);
++currentIndex;
}
doHideShowSubObjs(&boneVector);
m_lastNumberShown = bonesToShow;
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DSupplyDraw::crc( Xfer *xfer )
{
// extend base class
W3DModelDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DSupplyDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DModelDraw::xfer( xfer );
// Graham says there's no data to save here
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DSupplyDraw::loadPostProcess( void )
{
// extend base class
W3DModelDraw::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,452 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTankDraw.cpp //////////////////////////////////////////////////////////////////////////
// Draw turreted tanks
// Michael S. Booth, October 2001
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <math.h>
#include "Common/Thing.h"
#include "Common/ThingFactory.h"
#include "Common/GameAudio.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameLogic/Weapon.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Module/PhysicsUpdate.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/ScriptEngine.h"
#include "GameLogic/Module/AIUpdate.h"
#include "GameClient/Drawable.h"
#include "GameClient/ParticleSys.h"
#include "W3DDevice/GameClient/W3DGameClient.h"
#include "W3DDevice/GameClient/Module/W3DTankDraw.h"
#include "WW3D2/matinfo.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
class Matrix3D;
//-------------------------------------------------------------------------------------------------
W3DTankDrawModuleData::W3DTankDrawModuleData() :
m_treadDebrisNameLeft("TrackDebrisDirtLeft"),
m_treadDebrisNameRight("TrackDebrisDirtRight"),
m_treadAnimationRate(0.0f),
m_treadPivotSpeedFraction(0.6f),
m_treadDriveSpeedFraction(0.3f)
{
}
//-------------------------------------------------------------------------------------------------
W3DTankDrawModuleData::~W3DTankDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DTankDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DModelDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "TreadDebrisLeft", INI::parseAsciiString, NULL, offsetof(W3DTankDrawModuleData, m_treadDebrisNameLeft) },
{ "TreadDebrisRight", INI::parseAsciiString, NULL, offsetof(W3DTankDrawModuleData, m_treadDebrisNameRight) },
{ "TreadAnimationRate", INI::parseVelocityReal, NULL, offsetof(W3DTankDrawModuleData, m_treadAnimationRate) },
{ "TreadPivotSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankDrawModuleData, m_treadPivotSpeedFraction) },
{ "TreadDriveSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankDrawModuleData, m_treadDriveSpeedFraction) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTankDraw::W3DTankDraw( Thing *thing, const ModuleData* moduleData )
: W3DModelDraw( thing, moduleData ),m_prevRenderObj(NULL), m_treadDebrisLeft(NULL), m_treadDebrisRight(NULL)
{
m_treadDebrisLeft = NULL;
m_treadDebrisRight = NULL;
for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
m_treads[i].m_robj = NULL;
m_treadCount=0;
//Assume all things face along x axis when created.
m_lastDirection.x=1.0f;
m_lastDirection.y=0.0f;
m_lastDirection.z=0.0f;
createEmitters();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTankDraw::tossEmitters( void )
{
if (m_treadDebrisLeft)
{
m_treadDebrisLeft->attachToObject(NULL);
m_treadDebrisLeft->destroy();
m_treadDebrisLeft = NULL;
}
if (m_treadDebrisRight)
{
m_treadDebrisRight->attachToObject(NULL);
m_treadDebrisRight->destroy();
m_treadDebrisRight = NULL;
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTankDraw::createEmitters( void )
{
if (!m_treadDebrisLeft)
{
const ParticleSystemTemplate *sysTemplate;
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankDrawModuleData()->m_treadDebrisNameLeft);
if (sysTemplate)
{
m_treadDebrisLeft = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_treadDebrisLeft->attachToDrawable(getDrawable());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_treadDebrisLeft->setSaveable(FALSE);
// they come into being stopped.
m_treadDebrisLeft->stop();
}
}
if (!m_treadDebrisRight)
{
const ParticleSystemTemplate *sysTemplate;
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankDrawModuleData()->m_treadDebrisNameRight);
if (sysTemplate)
{
m_treadDebrisRight = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_treadDebrisRight->attachToDrawable(getDrawable());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_treadDebrisRight->setSaveable(FALSE);
// they come into being stopped.
m_treadDebrisRight->stop();
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTankDraw::~W3DTankDraw()
{
for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
if (m_treads[i].m_robj)
REF_PTR_RELEASE(m_treads[i].m_robj);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Start creating debris from the tank treads
*/
void W3DTankDraw::startMoveDebris( void )
{
if (getDrawable()->isDrawableEffectivelyHidden())
return;
if (m_treadDebrisLeft)
m_treadDebrisLeft->start();
if (m_treadDebrisRight)
m_treadDebrisRight->start();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Stop creating debris from the tank treads
*/
void W3DTankDraw::stopMoveDebris( void )
{
if (m_treadDebrisLeft)
m_treadDebrisLeft->stop();
if (m_treadDebrisRight)
m_treadDebrisRight->stop();
}
//-------------------------------------------------------------------------------------------------
void W3DTankDraw::setHidden(Bool h)
{
W3DModelDraw::setHidden(h);
if (h)
{
stopMoveDebris();
}
}
//-------------------------------------------------------------------------------------------------
void W3DTankDraw::setFullyObscuredByShroud(Bool fullyObscured)
{
if (fullyObscured != getFullyObscuredByShroud())
{
if (fullyObscured)
stopMoveDebris();
}
W3DModelDraw::setFullyObscuredByShroud(fullyObscured);
}
/**Update uv coordinates on each tread object to simulate movement*/
void W3DTankDraw::updateTreadPositions(Real uvDelta)
{
Real offset_u;
TreadObjectInfo *pTread=m_treads;
for (Int i=0; i<m_treadCount; i++)
{
if (pTread->m_type == TREAD_LEFT) //this tread needs to scroll forwards
offset_u = pTread->m_materialSettings.customUVOffset.X + uvDelta;
else
if (pTread->m_type == TREAD_RIGHT) //this tread needs to scroll backwards
offset_u = pTread->m_materialSettings.customUVOffset.X - uvDelta;
// ensure coordinates of offset are in [0, 1] range:
offset_u = offset_u - WWMath::Floor(offset_u);
pTread->m_materialSettings.customUVOffset.Set(offset_u,0);
pTread++;
}
}
/**Grab pointers to the sub-meshes for each tread*/
void W3DTankDraw::updateTreadObjects(void)
{
RenderObjClass *robj=getRenderObject();
//clear all previous tread pointers
for (Int i=0; i<m_treadCount; i++)
REF_PTR_RELEASE(m_treads[i].m_robj);
m_treadCount = 0;
//Make sure this object has defined a speed for tread scrolling.
if (getW3DTankDrawModuleData() && getW3DTankDrawModuleData()->m_treadAnimationRate && robj)
{
for (Int i=0; i < robj->Get_Num_Sub_Objects() && m_treadCount < MAX_TREADS_PER_TANK; i++)
{
RenderObjClass *subObj=robj->Get_Sub_Object(i);
const char *meshName;
//Check if subobject name starts with "TREADS".
if (subObj && subObj->Class_ID() == RenderObjClass::CLASSID_MESH && subObj->Get_Name()
&& ( (meshName=strchr(subObj->Get_Name(),'.') ) != 0 && *(meshName++))
&&_strnicmp(meshName,"TREADS", 6) == 0)
{ //check if sub-object has the correct material to do texture scrolling.
MaterialInfoClass *mat=subObj->Get_Material_Info();
if (mat)
{ for (Int j=0; j<mat->Vertex_Material_Count(); j++)
{
VertexMaterialClass *vmaterial=mat->Peek_Vertex_Material(j);
LinearOffsetTextureMapperClass *mapper=(LinearOffsetTextureMapperClass *)vmaterial->Peek_Mapper();
if (mapper && mapper->Mapper_ID() == TextureMapperClass::MAPPER_ID_LINEAR_OFFSET)
{ mapper->Set_UV_Offset_Delta(Vector2(0,0)); //disable automatic scrolling
subObj->Add_Ref(); //increase reference since we're storing the pointer
m_treads[m_treadCount].m_robj=subObj;
m_treads[m_treadCount].m_type = TREAD_MIDDLE; //default type
subObj->Set_User_Data(&m_treads[m_treadCount].m_materialSettings); //tell W3D about custom material settings
m_treads[m_treadCount].m_materialSettings.customUVOffset=Vector2(0,0);
switch (meshName[6]) //check next character after 'TREADS'
{
case 'L':
case 'l': m_treads[m_treadCount].m_type = TREAD_LEFT;
break;
case 'R':
case 'r': m_treads[m_treadCount].m_type = TREAD_RIGHT;
break;
}
m_treadCount++;
}
}
REF_PTR_RELEASE(mat);
}
}
REF_PTR_RELEASE(subObj);
}
}
m_prevRenderObj = robj;
}
//-------------------------------------------------------------------------------------------------
void W3DTankDraw::onRenderObjRecreated(void)
{
updateTreadObjects();
}
//-------------------------------------------------------------------------------------------------
/** Map behavior states into W3D animations. */
//-------------------------------------------------------------------------------------------------
void W3DTankDraw::doDrawModule(const Matrix3D* transformMtx)
{
const Real DEBRIS_THRESHOLD = 0.00001f;
Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
if (frozen)
return;
if (getRenderObject()==NULL) return;
if (getRenderObject() != m_prevRenderObj) {
updateTreadObjects();
}
// get object from logic
Object *obj = getDrawable()->getObject();
if (obj == NULL)
return;
// get object physics state
PhysicsBehavior *physics = obj->getPhysics();
if (physics == NULL)
return;
const Coord3D *vel = physics->getVelocity();
// if tank is moving, kick up dust and debris
Real velMag = vel->x*vel->x + vel->y*vel->y; // only care about moving on the ground
if (velMag > DEBRIS_THRESHOLD && !getDrawable()->isDrawableEffectivelyHidden() && !getFullyObscuredByShroud())
startMoveDebris();
else
stopMoveDebris();
// kick debris higher the faster we move
Coord3D velMult;
velMag = (Real)sqrt( velMag );
velMult.x = 0.5f * velMag + 0.1f;
if (velMult.x > 1.0f)
velMult.x = 1.0f;
velMult.y = velMult.x;
velMult.z = velMag + 0.1f;
if (velMult.z > 1.0f)
velMult.z = 1.0f;
m_treadDebrisLeft->setVelocityMultiplier( &velMult );
m_treadDebrisRight->setVelocityMultiplier( &velMult );
m_treadDebrisLeft->setBurstCountMultiplier( velMult.z );
m_treadDebrisRight->setBurstCountMultiplier( velMult.z );
//Update movement of treads
if (m_treadCount)
{
PhysicsTurningType turn=physics->getTurning();
Real offset_u;
Real treadScrollSpeed=getW3DTankDrawModuleData()->m_treadAnimationRate;
TreadObjectInfo *pTread=m_treads;
Real maxSpeed=obj->getAIUpdateInterface()->getCurLocomotorSpeed();
//For optimization sake, we only do complex tread scrolling when tank
//is mostly stationary and turning
if (turn != TURN_NONE && physics->getVelocityMagnitude()/maxSpeed < getW3DTankDrawModuleData()->m_treadPivotSpeedFraction)
{
//Check if we have turned enough since last draw to require animation
Coord3D dir;
obj->getUnitDirectionVector2D(dir);
Real angleToGoal = dir.x * m_lastDirection.x + dir.y * m_lastDirection.y;
if (fabs(1.0f-angleToGoal) > 0.00001f) //check if difference in angle cosines is greater than some cutoff.
{
if (turn == TURN_NEGATIVE) //turning right
updateTreadPositions(-treadScrollSpeed);
else //turning left
updateTreadPositions(treadScrollSpeed);
}
m_lastDirection=dir; //update for next frame
}
else
if (physics->isMotive() && physics->getVelocityMagnitude()/maxSpeed >= getW3DTankDrawModuleData()->m_treadDriveSpeedFraction)
{ //do simple scrolling based only on speed when tank is moving straight at high speed.
//we stop scrolling when tank slows down to reduce the appearance of sliding
//tread scrolling speed was not directly tied into tank velocity because it looked odd
//under certain situations when tank moved sideways.
for (Int i=0; i<m_treadCount; i++)
{
offset_u = pTread->m_materialSettings.customUVOffset.X - treadScrollSpeed;
// ensure coordinates of offset are in [0, 1] range:
offset_u = offset_u - WWMath::Floor(offset_u);
pTread->m_materialSettings.customUVOffset.Set(offset_u,0);
pTread++;
}
}
}
W3DModelDraw::doDrawModule(transformMtx);
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTankDraw::crc( Xfer *xfer )
{
// extend base class
W3DModelDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DTankDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DModelDraw::xfer( xfer );
// John A and Mark W say there is no data to save here
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTankDraw::loadPostProcess( void )
{
// extend base class
W3DModelDraw::loadPostProcess();
// toss any existing ones and re-create 'em (since this module expects 'em to always be around)
tossEmitters();
createEmitters();
} // end loadPostProcess

View file

@ -0,0 +1,787 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTankTruckDraw.cpp
// Draw TankTrucks. Actually, this draws quad cannon which has both treads and wheels.
// Author: Mark Wilczynski, August 2002
#include <stdlib.h>
#include <math.h>
#include "Common/Thing.h"
#include "Common/ThingFactory.h"
#include "Common/GameAudio.h"
#include "Common/GlobalData.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameLogic/Weapon.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Module/PhysicsUpdate.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/ScriptEngine.h"
#include "GameLogic/Module/AIUpdate.h"
#include "GameClient/Drawable.h"
#include "GameClient/ParticleSys.h"
#include "W3DDevice/GameClient/W3DGameClient.h"
#include "W3DDevice/GameClient/Module/W3DTankTruckDraw.h"
#include "WW3D2/matinfo.h"
//#define SHOW_TANK_DEBRIS
//-------------------------------------------------------------------------------------------------
W3DTankTruckDrawModuleData::W3DTankTruckDrawModuleData():
m_treadDebrisNameLeft("TrackDebrisDirtLeft"),
m_treadDebrisNameRight("TrackDebrisDirtRight"),
m_treadAnimationRate(0.0f),
m_treadPivotSpeedFraction(0.6f),
m_treadDriveSpeedFraction(0.3f)
{
}
//-------------------------------------------------------------------------------------------------
W3DTankTruckDrawModuleData::~W3DTankTruckDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DTankTruckDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DModelDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "Dust", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_dustEffectName) },
{ "DirtSpray", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_dirtEffectName) },
{ "PowerslideSpray", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_powerslideEffectName) },
{ "LeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_frontLeftTireBoneName) },
{ "RightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_frontRightTireBoneName) },
{ "LeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_rearLeftTireBoneName) },
{ "RightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_rearRightTireBoneName) },
{ "MidLeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midFrontLeftTireBoneName) },
{ "MidRightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midFrontRightTireBoneName) },
{ "MidLeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midRearLeftTireBoneName) },
{ "MidRightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_midRearRightTireBoneName) },
{ "TireRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_rotationSpeedMultiplier) },
{ "PowerslideRotationAddition", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_powerslideRotationAddition) },
{ "TreadDebrisLeft", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadDebrisNameLeft) },
{ "TreadDebrisRight", INI::parseAsciiString, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadDebrisNameRight) },
{ "TreadAnimationRate", INI::parseVelocityReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadAnimationRate) },
{ "TreadPivotSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadPivotSpeedFraction) },
{ "TreadDriveSpeedFraction", INI::parseReal, NULL, offsetof(W3DTankTruckDrawModuleData, m_treadDriveSpeedFraction) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTankTruckDraw::W3DTankTruckDraw( Thing *thing, const ModuleData* moduleData ) : W3DModelDraw( thing, moduleData ),
m_dirtEffect(NULL), m_dustEffect(NULL), m_powerslideEffect(NULL), m_effectsInitialized(false),
m_wasAirborne(false), m_isPowersliding(false), m_frontWheelRotation(0), m_rearWheelRotation(0),
m_frontRightTireBone(0), m_frontLeftTireBone(0), m_rearLeftTireBone(0),m_rearRightTireBone(0),
m_prevRenderObj(NULL)
{
//Truck Data
m_landingSound = *(thing->getTemplate()->getPerUnitSound("TruckLandingSound"));
m_powerslideSound = *(thing->getTemplate()->getPerUnitSound("TruckPowerslideSound"));
//Tank data
m_treadDebrisLeft = NULL;
m_treadDebrisRight = NULL;
for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
m_treads[i].m_robj = NULL;
m_treadCount=0;
#ifdef SHOW_TANK_DEBRIS
if (getW3DTankTruckDrawModuleData())
{
ParticleSystemTemplate *sysTemplate;
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->m_treadDebrisNameLeft);
if (sysTemplate)
{
m_treadDebrisLeft = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_treadDebrisLeft->attachToDrawable(getDrawable());
DEBUG_CRASH(("test me, may not work (srj)"));
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_treadDebrisLeft->setSaveable(FALSE);
}
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->m_treadDebrisNameRight);
if (sysTemplate)
{
m_treadDebrisRight = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_treadDebrisRight->attachToDrawable(getDrawable());
DEBUG_CRASH(("test me, may not work (srj)"));
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_treadDebrisRight->setSaveable(FALSE);
}
}
#endif
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTankTruckDraw::~W3DTankTruckDraw()
{
tossEmitters();
for (Int i=0; i<MAX_TREADS_PER_TANK; i++)
if (m_treads[i].m_robj)
REF_PTR_RELEASE(m_treads[i].m_robj);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Start creating debris from the tank treads
*/
void W3DTankTruckDraw::startMoveDebris( void )
{
if (getDrawable()->isDrawableEffectivelyHidden())
return;
if (m_treadDebrisLeft)
m_treadDebrisLeft->start();
if (m_treadDebrisRight)
m_treadDebrisRight->start();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Stop creating debris from the tank treads
*/
void W3DTankTruckDraw::stopMoveDebris( void )
{
if (m_treadDebrisLeft)
m_treadDebrisLeft->stop();
if (m_treadDebrisRight)
m_treadDebrisRight->stop();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::tossEmitters()
{
if (m_dustEffect)
{
m_dustEffect->attachToObject(NULL);
m_dustEffect->destroy();
m_dustEffect = NULL;
}
if (m_dirtEffect)
{
m_dirtEffect->attachToObject(NULL);
m_dirtEffect->destroy();
m_dirtEffect = NULL;
}
if (m_powerslideEffect)
{
m_powerslideEffect->attachToObject(NULL);
m_powerslideEffect->destroy();
m_powerslideEffect = NULL;
}
}
//-------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::setFullyObscuredByShroud(Bool fullyObscured)
{
if (fullyObscured != getFullyObscuredByShroud())
{
if (fullyObscured)
tossEmitters();
else
createEmitters();
}
W3DModelDraw::setFullyObscuredByShroud(fullyObscured);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Start creating debris from the tank treads
*/
void W3DTankTruckDraw::createEmitters( void )
{
if (getDrawable()->isDrawableEffectivelyHidden())
return;
if (getW3DTankTruckDrawModuleData())
{
const ParticleSystemTemplate *sysTemplate;
if (!m_dustEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->m_dustEffectName);
if (sysTemplate)
{
m_dustEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_dustEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_dustEffect->setSaveable(FALSE);
} else {
if (!getW3DTankTruckDrawModuleData()->m_dustEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTankTruckDrawModuleData()->m_dustEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
if (!m_dirtEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->m_dirtEffectName);
if (sysTemplate)
{
m_dirtEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_dirtEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_dirtEffect->setSaveable(FALSE);
} else {
if (!getW3DTankTruckDrawModuleData()->m_dirtEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTankTruckDrawModuleData()->m_dirtEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
if (!m_powerslideEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTankTruckDrawModuleData()->m_powerslideEffectName);
if (sysTemplate)
{
m_powerslideEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_powerslideEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_powerslideEffect->setSaveable(FALSE);
} else {
if (!getW3DTankTruckDrawModuleData()->m_powerslideEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTankTruckDrawModuleData()->m_powerslideEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Stop creating debris from the tank treads
*/
void W3DTankTruckDraw::enableEmitters( Bool enable )
{
// don't check... if we are hidden the first time thru, then we'll never create the emitters.
// eg, if we are loading a game and the unit is in a tunnel, he'll never get emitteres even when he exits.
//if (!m_effectsInitialized)
{
createEmitters();
m_effectsInitialized=true;
}
if (m_dustEffect)
{
if (enable)
m_dustEffect->start();
else
m_dustEffect->stop();
}
if (m_dirtEffect)
{
if (enable)
m_dirtEffect->start();
else
m_dirtEffect->stop();
}
if (m_powerslideEffect)
{
if (!enable)
m_powerslideEffect->stop();
}
}
//-------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::updateBones( void ) {
if( getW3DTankTruckDrawModuleData() )
{
//Front tires
if( !getW3DTankTruckDrawModuleData()->m_frontLeftTireBoneName.isEmpty() )
{
m_frontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_frontLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_frontLeftTireBone, ("Missing front-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_frontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_frontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_frontRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_frontRightTireBone, ("Missing front-right tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_frontRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_frontRightTireBone )
{
m_frontLeftTireBone = 0;
}
}
//Rear tires
if( !getW3DTankTruckDrawModuleData()->m_rearLeftTireBoneName.isEmpty() )
{
m_rearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_rearLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_rearLeftTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_rearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_rearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_rearRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_rearRightTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_rearRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_rearRightTireBone)
{
m_rearLeftTireBone = 0;
}
}
//midFront tires
if( !getW3DTankTruckDrawModuleData()->m_midFrontLeftTireBoneName.isEmpty() )
{
m_midFrontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midFrontLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midFrontLeftTireBone, ("Missing mid-front-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midFrontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midFrontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midFrontRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midFrontRightTireBone, ("Missing mid-front-right tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midFrontRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midFrontRightTireBone )
{
m_midFrontLeftTireBone = 0;
}
}
//midRear tires
if( !getW3DTankTruckDrawModuleData()->m_midRearLeftTireBoneName.isEmpty() )
{
m_midRearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midRearLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midRearLeftTireBone, ("Missing mid-rear-left tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midRearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midRearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTankTruckDrawModuleData()->m_midRearRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midRearRightTireBone, ("Missing mid-rear-right tire bone %s in model %s\n", getW3DTankTruckDrawModuleData()->m_midRearRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midRearRightTireBone)
{
m_midRearLeftTireBone = 0;
}
}
}
m_prevRenderObj = getRenderObject();
}
//-------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::setHidden(Bool h)
{
W3DModelDraw::setHidden(h);
if (h)
{
enableEmitters(false);
#ifdef SHOW_TANK_DEBRIS
stopMoveDebris();
#endif
}
}
/**Update uv coordinates on each tread object to simulate movement*/
void W3DTankTruckDraw::updateTreadPositions(Real uvDelta)
{
Real offset_u;
TreadObjectInfo *pTread=m_treads;
for (Int i=0; i<m_treadCount; i++)
{
if (pTread->m_type == TREAD_MIDDLE) //this tread needs to scroll backwards
offset_u = pTread->m_materialSettings.customUVOffset.X + uvDelta;
else
if (pTread->m_type == TREAD_LEFT) //this tread needs to scroll forwards
offset_u = pTread->m_materialSettings.customUVOffset.X + uvDelta;
else
if (pTread->m_type == TREAD_RIGHT) //this tread needs to scroll backwards
offset_u = pTread->m_materialSettings.customUVOffset.X - uvDelta;
// ensure coordinates of offset are in [0, 1] range:
offset_u = offset_u - WWMath::Floor(offset_u);
pTread->m_materialSettings.customUVOffset.Set(offset_u,0);
pTread++;
}
}
/**Grab pointers to the sub-meshes for each tread*/
void W3DTankTruckDraw::updateTreadObjects(void)
{
RenderObjClass *robj=getRenderObject();
//clear all previous tread pointers
for (Int i=0; i<m_treadCount; i++)
REF_PTR_RELEASE(m_treads[i].m_robj);
m_treadCount = 0;
//Make sure this object has defined a speed for tread scrolling.
if (getW3DTankTruckDrawModuleData() && getW3DTankTruckDrawModuleData()->m_treadAnimationRate && robj)
{
for (Int i=0; i < robj->Get_Num_Sub_Objects() && m_treadCount < MAX_TREADS_PER_TANK; i++)
{
RenderObjClass *subObj=robj->Get_Sub_Object(i);
const char *meshName;
//Check if subobject name starts with "TREADS".
if (subObj && subObj->Class_ID() == RenderObjClass::CLASSID_MESH && subObj->Get_Name()
&& ( (meshName=strchr(subObj->Get_Name(),'.') ) != 0 && *(meshName++))
&&_strnicmp(meshName,"TREADS", 6) == 0)
{ //check if sub-object has the correct material to do texture scrolling.
MaterialInfoClass *mat=subObj->Get_Material_Info();
if (mat)
{ for (Int j=0; j<mat->Vertex_Material_Count(); j++)
{
VertexMaterialClass *vmaterial=mat->Peek_Vertex_Material(j);
LinearOffsetTextureMapperClass *mapper=(LinearOffsetTextureMapperClass *)vmaterial->Peek_Mapper();
if (mapper && mapper->Mapper_ID() == TextureMapperClass::MAPPER_ID_LINEAR_OFFSET)
{ mapper->Set_UV_Offset_Delta(Vector2(0,0)); //disable automatic scrolling
subObj->Add_Ref(); //increase reference since we're storing the pointer
m_treads[m_treadCount].m_robj=subObj;
m_treads[m_treadCount].m_type = TREAD_MIDDLE; //default type
subObj->Set_User_Data(&m_treads[m_treadCount].m_materialSettings); //tell W3D about custom material settings
m_treads[m_treadCount].m_materialSettings.customUVOffset=Vector2(0,0);
//Commented out since on vehicles with wheels, it makes no sense to turn with treads.
/* switch (meshName[6]) //check next character after 'TREADS'
{
case 'L':
case 'l': m_treads[m_treadCount].m_type = TREAD_LEFT;
break;
case 'R':
case 'r': m_treads[m_treadCount].m_type = TREAD_RIGHT;
break;
}*/
m_treadCount++;
}
}
REF_PTR_RELEASE(mat);
}
}
REF_PTR_RELEASE(subObj);
}
}
m_prevRenderObj = robj;
}
//-------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::onRenderObjRecreated(void)
{
//DEBUG_LOG(("Old obj %x, newObj %x, new bones %d, old bones %d\n",
// m_prevRenderObj, getRenderObject(), getRenderObject()->Get_Num_Bones(),
// m_prevNumBones));
m_prevRenderObj = NULL;
m_frontLeftTireBone = 0;
m_frontRightTireBone = 0;
m_rearLeftTireBone = 0;
m_rearRightTireBone = 0;
m_midFrontLeftTireBone = 0;
m_midFrontRightTireBone = 0;
m_midRearLeftTireBone = 0;
m_midRearRightTireBone = 0;
updateBones();
updateTreadObjects();
}
//-------------------------------------------------------------------------------------------------
/** Map behavior states into W3D animations. */
//-------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::doDrawModule(const Matrix3D* transformMtx)
{
W3DModelDraw::doDrawModule(transformMtx);
if (!TheGlobalData->m_showClientPhysics)
return;
Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
if (frozen)
return;
const Real ACCEL_THRESHOLD = 0.01f;
const Real SIZE_CAP = 2.0f;
// get object from logic
Object *obj = getDrawable()->getObject();
if (obj == NULL)
return;
if (getRenderObject()==NULL) return;
if (getRenderObject() != m_prevRenderObj) {
updateBones();
updateTreadObjects();
}
// get object physics state
PhysicsBehavior *physics = obj->getPhysics();
if (physics == NULL)
return;
const Coord3D *vel = physics->getVelocity();
Real speed = physics->getVelocityMagnitude();
const TWheelInfo *wheelInfo = getDrawable()->getWheelInfo(); // note, can return null!
if (wheelInfo && (m_frontLeftTireBone || m_rearLeftTireBone))
{
static Real rotation = 0;
const Real rotationFactor = getW3DTankTruckDrawModuleData()->m_rotationSpeedMultiplier;
m_frontWheelRotation += rotationFactor*speed;
if (m_isPowersliding)
{
m_rearWheelRotation += rotationFactor*(speed+getW3DTankTruckDrawModuleData()->m_powerslideRotationAddition);
}
else
{
m_rearWheelRotation += rotationFactor*speed;
}
Matrix3D wheelXfrm(1);
if (m_frontLeftTireBone)
{
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_frontWheelRotation);
getRenderObject()->Capture_Bone( m_frontLeftTireBone );
getRenderObject()->Control_Bone( m_frontLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_frontWheelRotation);
getRenderObject()->Capture_Bone( m_frontRightTireBone );
getRenderObject()->Control_Bone( m_frontRightTireBone, wheelXfrm );
}
if (m_rearLeftTireBone)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_rearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_rearLeftTireBone );
getRenderObject()->Control_Bone( m_rearLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_rearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
getRenderObject()->Capture_Bone( m_rearRightTireBone );
getRenderObject()->Control_Bone( m_rearRightTireBone, wheelXfrm );
}
if (m_midFrontLeftTireBone)
{
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
getRenderObject()->Capture_Bone( m_midFrontLeftTireBone );
getRenderObject()->Control_Bone( m_midFrontLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
getRenderObject()->Capture_Bone( m_midFrontRightTireBone );
getRenderObject()->Control_Bone( m_midFrontRightTireBone, wheelXfrm );
}
if (m_midRearLeftTireBone)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_midRearLeftTireBone );
getRenderObject()->Control_Bone( m_midRearLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
getRenderObject()->Capture_Bone( m_midRearRightTireBone );
getRenderObject()->Control_Bone( m_midRearRightTireBone, wheelXfrm );
}
}
Bool wasPowersliding = m_isPowersliding;
m_isPowersliding = false;
if (physics->isMotive() && !obj->isSignificantlyAboveTerrain()) {
enableEmitters(true);
Coord3D accel = *physics->getAcceleration();
accel.z = 0; // ignore gravitational force.
Bool accelerating = accel.length()>ACCEL_THRESHOLD;
//DEBUG_LOG(("Accel %f, speed %f\n", accel.length(), speed));
if (accelerating) {
Real dot = accel.x*vel->x + accel.y*vel->y;
if (dot<0) {
accelerating = false; // decelerating, actually.
}
}
if (m_dustEffect) {
// Need more dust the faster we go.
if (speed>SIZE_CAP) {
speed = SIZE_CAP;
}
m_dustEffect->setSizeMultiplier(speed);
}
if (m_dirtEffect) {
if (wheelInfo && wheelInfo->m_framesAirborne>3) {
Real factor = 1 + wheelInfo->m_framesAirborne/16;
if (factor>2.0) factor = 2.0;
m_dustEffect->setSizeMultiplier(factor*SIZE_CAP);
m_dustEffect->trigger();
m_landingSound.setPosition(obj->getPosition());
TheAudio->addAudioEvent(&m_landingSound);
} else {
if (!accelerating || speed>2.0f) {
m_dirtEffect->stop();
}
}
}
if (m_powerslideEffect) {
if (physics->getTurning() == TURN_NONE) {
m_powerslideEffect->stop();
} else {
m_isPowersliding = true;
m_powerslideEffect->start();
}
}
if (m_dirtEffect) {
if (!accelerating || speed>2.0f) {
m_dirtEffect->stop();
}
}
}
else
enableEmitters(false);
m_wasAirborne = obj->isSignificantlyAboveTerrain();
if(!wasPowersliding && m_isPowersliding) {
// start sound
m_powerslideSound.setObjectID(obj->getID());
m_powerslideSound.setPlayingHandle(TheAudio->addAudioEvent(&m_powerslideSound));
} else if (wasPowersliding && !m_isPowersliding) {
TheAudio->removeAudioEvent(m_powerslideSound.getPlayingHandle());
}
//Tank update
#ifdef SHOW_TANK_DEBRIS
const Real DEBRIS_THRESHOLD = 0.00001f;
// if tank is moving, kick up dust and debris
Real velMag = vel->x*vel->x + vel->y*vel->y; // only care about moving on the ground
if (velMag > DEBRIS_THRESHOLD && !getDrawable()->isDrawableEffectivelyHidden() && !getFullyObscuredByShroud())
startMoveDebris();
else
stopMoveDebris();
// kick debris higher the faster we move
Coord3D velMult;
velMag = (Real)sqrt( velMag );
velMult.x = 0.5f * velMag + 0.1f;
if (velMult.x > 1.0f)
velMult.x = 1.0f;
velMult.y = velMult.x;
velMult.z = velMag + 0.1f;
if (velMult.z > 1.0f)
velMult.z = 1.0f;
m_treadDebrisLeft->setVelocityMultiplier( &velMult );
m_treadDebrisRight->setVelocityMultiplier( &velMult );
m_treadDebrisLeft->setBurstCountMultiplier( velMult.z );
m_treadDebrisRight->setBurstCountMultiplier( velMult.z );
#endif
//Update movement of treads
if (m_treadCount)
{
Real offset_u;
Real treadScrollSpeed=getW3DTankTruckDrawModuleData()->m_treadAnimationRate;
TreadObjectInfo *pTread=m_treads;
Real maxSpeed=obj->getAIUpdateInterface()->getCurLocomotorSpeed();
/* Commented out because these vehicles are presumed not to turn via treads.
PhysicsTurningType turn=physics->getTurning();
//For optimization sake, we only do complex tread scrolling when tank
//is mostly stationary and turning
if ((turn=physics->getTurning()) != TURN_NONE && physics->getSpeed()/maxSpeed < getW3DTankTruckDrawModuleData()->m_treadPivotSpeedFraction)
{
if (turn == TURN_NEGATIVE) //turning right
updateTreadPositions(-treadScrollSpeed);
else //turning left
updateTreadPositions(treadScrollSpeed);
}
else*/
if (physics->isMotive() && physics->getVelocityMagnitude()/maxSpeed >= getW3DTankTruckDrawModuleData()->m_treadDriveSpeedFraction)
{ //do simple scrolling based only on speed when tank is moving straight at high speed.
//we stop scrolling when tank slows down to reduce the appearance of sliding
//tread scrolling speed was not directly tied into tank velocity because it looked odd
//under certain situations when tank moved sideways.
for (Int i=0; i<m_treadCount; i++)
{
offset_u = pTread->m_materialSettings.customUVOffset.X - treadScrollSpeed;
// ensure coordinates of offset are in [0, 1] range:
offset_u = offset_u - WWMath::Floor(offset_u);
pTread->m_materialSettings.customUVOffset.Set(offset_u,0);
pTread++;
}
}
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::crc( Xfer *xfer )
{
// extend base class
W3DModelDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DModelDraw::xfer( xfer );
// John A and Mark W say there is no data to save here
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTankTruckDraw::loadPostProcess( void )
{
// extend base class
W3DModelDraw::loadPostProcess();
// toss any existing ones (no need to re-create; we'll do that on demand)
tossEmitters();
} // end loadPostProcess

View file

@ -0,0 +1,199 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTracerDraw.cpp ////////////////////////////////////////////////////////////////////////
// Author: Colin Day, December 2001
// Desc: Tracer drawing
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include "Common/Thing.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameClient.h"
#include "GameLogic/GameLogic.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/Module/W3DTracerDraw.h"
#include "WW3D2/Line3D.h"
#include "W3DDevice/GameClient/W3DScene.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTracerDraw::W3DTracerDraw( Thing *thing, const ModuleData* moduleData ) : DrawModule( thing, moduleData )
{
// set opacity
m_opacity = 1.0f;
m_length = 20.0f;
m_width = 0.5f;
m_color.red = 0.9f;
m_color.green = 0.8f;
m_color.blue = 0.7f;
m_speedInDistPerFrame = 1.0f;
m_theTracer = NULL;
} // end W3DTracerDraw
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTracerDraw::setTracerParms(Real speed, Real length, Real width, const RGBColor& color, Real initialOpacity)
{
m_speedInDistPerFrame = speed;
m_length = length;
m_width = width;
m_color = color;
m_opacity = initialOpacity;
if (m_theTracer)
{
Vector3 start( 0.0f, 0.0f, 0.0f );
Vector3 stop( m_length, 0.0f, 0.0f );
m_theTracer->Reset(start, stop, m_width);
m_theTracer->Re_Color(m_color.red, m_color.green, m_color.blue);
m_theTracer->Set_Opacity( m_opacity );
// these calls nuke the internal transform, so re-set it here
m_theTracer->Set_Transform( *getDrawable()->getTransformMatrix() );
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTracerDraw::~W3DTracerDraw( void )
{
// remove tracer from the scene and delete
if( m_theTracer )
{
W3DDisplay::m_3DScene->Remove_Render_Object( m_theTracer );
REF_PTR_RELEASE( m_theTracer );
}
}
//-------------------------------------------------------------------------------------------------
void W3DTracerDraw::reactToTransformChange( const Matrix3D *oldMtx,
const Coord3D *oldPos,
Real oldAngle )
{
if( m_theTracer )
m_theTracer->Set_Transform( *getDrawable()->getTransformMatrix() );
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTracerDraw::doDrawModule(const Matrix3D* transformMtx)
{
// create tracer
if( m_theTracer == NULL )
{
Vector3 start( 0.0f, 0.0f, 0.0f );
Vector3 stop( m_length, 0.0f, 0.0f );
// create tracer render object for us to manipulate
// poolify
m_theTracer = NEW Line3DClass( start,
stop,
m_width, // width
m_color.red, // red
m_color.green, // green
m_color.blue, // blue
m_opacity ); // transparency
W3DDisplay::m_3DScene->Add_Render_Object( m_theTracer );
// set the transform for the tracer to that of the drawable
m_theTracer->Set_Transform( *transformMtx );
}
UnsignedInt expDate = getDrawable()->getExpirationDate();
if (expDate != 0)
{
Real decay = m_opacity / (expDate - TheGameLogic->getFrame());
m_opacity -= decay;
m_theTracer->Set_Opacity( m_opacity );
}
// set the position for the tracer
if (m_speedInDistPerFrame != 0.0f)
{
Matrix3D pos = m_theTracer->Get_Transform();
pos.Translate( Vector3( m_speedInDistPerFrame, 0.0f, 0.0 ) );
m_theTracer->Set_Transform( pos );
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTracerDraw::crc( Xfer *xfer )
{
// extend base class
DrawModule::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DTracerDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
DrawModule::xfer( xfer );
// no data to save here, nobody will ever notice
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTracerDraw::loadPostProcess( void )
{
// extend base class
DrawModule::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,687 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTruckDraw.cpp
// Draw Trucks. Actually, this draws rocket buggies.
// Author: John Ahlquist, March 2002
#include <stdlib.h>
#include <math.h>
#include "Common/Thing.h"
#include "Common/ThingFactory.h"
#include "Common/GameAudio.h"
#include "Common/GlobalData.h"
#include "Common/ThingTemplate.h"
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameClient/ParticleSys.h"
#include "GameLogic/AIPathfind.h"
#include "GameLogic/Weapon.h"
#include "GameLogic/GameLogic.h" // for logic frame count
#include "GameLogic/PartitionManager.h"
#include "GameLogic/Locomotor.h"
#include "GameLogic/Module/PhysicsUpdate.h"
#include "GameLogic/Module/BodyModule.h"
#include "GameLogic/Module/AIUpdate.h"
#include "GameLogic/ScriptEngine.h"
#include "W3DDevice/GameClient/W3DGameClient.h"
#include "W3DDevice/GameClient/Module/W3DTruckDraw.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-------------------------------------------------------------------------------------------------
W3DTruckDrawModuleData::W3DTruckDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
W3DTruckDrawModuleData::~W3DTruckDrawModuleData()
{
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDrawModuleData::buildFieldParse(MultiIniFieldParse& p)
{
W3DModelDrawModuleData::buildFieldParse(p);
static const FieldParse dataFieldParse[] =
{
{ "Dust", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_dustEffectName) },
{ "DirtSpray", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_dirtEffectName) },
{ "PowerslideSpray", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_powerslideEffectName) },
{ "LeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_frontLeftTireBoneName) },
{ "RightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_frontRightTireBoneName) },
{ "LeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_rearLeftTireBoneName) },
{ "RightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_rearRightTireBoneName) },
{ "MidLeftFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midFrontLeftTireBoneName) },
{ "MidRightFrontTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midFrontRightTireBoneName) },
{ "MidLeftRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midRearLeftTireBoneName) },
{ "MidRightRearTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midRearRightTireBoneName) },
{ "MidLeftMidTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midMidLeftTireBoneName) },
{ "MidRightMidTireBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_midMidRightTireBoneName) },
{ "TireRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_rotationSpeedMultiplier) },
{ "PowerslideRotationAddition", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_powerslideRotationAddition) },
{ "CabBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_cabBoneName) },
{ "TrailerBone", INI::parseAsciiString, NULL, offsetof(W3DTruckDrawModuleData, m_trailerBoneName) },
{ "CabRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_cabRotationFactor) },
{ "TrailerRotationMultiplier", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_trailerRotationFactor) },
{ "RotationDamping", INI::parseReal, NULL, offsetof(W3DTruckDrawModuleData, m_rotationDampingFactor) },
{ 0, 0, 0, 0 }
};
p.add(dataFieldParse);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTruckDraw::W3DTruckDraw( Thing *thing, const ModuleData* moduleData ) : W3DModelDraw( thing, moduleData ),
m_dirtEffect(NULL), m_dustEffect(NULL), m_powerslideEffect(NULL), m_effectsInitialized(false),
m_wasAirborne(false), m_isPowersliding(false),
m_frontWheelRotation(0), m_rearWheelRotation(0), m_midFrontWheelRotation(0), m_midRearWheelRotation(0),
m_frontRightTireBone(0), m_frontLeftTireBone(0), m_rearLeftTireBone(0),m_rearRightTireBone(0),
m_midFrontRightTireBone(0), m_midFrontLeftTireBone(0), m_midRearLeftTireBone(0),m_midRearRightTireBone(0),
m_midMidRightTireBone(0), m_midMidLeftTireBone(0), m_prevRenderObj(NULL)
{
const AudioEventRTS * event;
event = thing->getTemplate()->getPerUnitSound("TruckLandingSound");
if (event) {
m_landingSound = *event;
}
event = thing->getTemplate()->getPerUnitSound("TruckPowerslideSound");
if (event) {
m_powerslideSound = *event;
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTruckDraw::~W3DTruckDraw()
{
tossEmitters();
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::tossEmitters()
{
if (m_dustEffect)
{
m_dustEffect->attachToObject(NULL);
m_dustEffect->destroy();
m_dustEffect = NULL;
}
if (m_dirtEffect)
{
m_dirtEffect->attachToObject(NULL);
m_dirtEffect->destroy();
m_dirtEffect = NULL;
}
if (m_powerslideEffect)
{
m_powerslideEffect->attachToObject(NULL);
m_powerslideEffect->destroy();
m_powerslideEffect = NULL;
}
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::setFullyObscuredByShroud(Bool fullyObscured)
{
if (fullyObscured != getFullyObscuredByShroud())
{
if (fullyObscured)
tossEmitters();
else
createEmitters();
}
W3DModelDraw::setFullyObscuredByShroud(fullyObscured);
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Start creating debris from the tank treads
*/
void W3DTruckDraw::createEmitters( void )
{
if (getDrawable()->isDrawableEffectivelyHidden())
return;
if (getW3DTruckDrawModuleData())
{
const ParticleSystemTemplate *sysTemplate;
if (!m_dustEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_dustEffectName);
if (sysTemplate)
{
m_dustEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_dustEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_dustEffect->setSaveable(FALSE);
} else {
if (!getW3DTruckDrawModuleData()->m_dustEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTruckDrawModuleData()->m_dustEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
if (!m_dirtEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_dirtEffectName);
if (sysTemplate)
{
m_dirtEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_dirtEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_dirtEffect->setSaveable(FALSE);
} else {
if (!getW3DTruckDrawModuleData()->m_dirtEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTruckDrawModuleData()->m_dirtEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
if (!m_powerslideEffect) {
sysTemplate = TheParticleSystemManager->findTemplate(getW3DTruckDrawModuleData()->m_powerslideEffectName);
if (sysTemplate)
{
m_powerslideEffect = TheParticleSystemManager->createParticleSystem( sysTemplate );
m_powerslideEffect->attachToObject(getDrawable()->getObject());
// important: mark it as do-not-save, since we'll just re-create it when we reload.
m_powerslideEffect->setSaveable(FALSE);
} else {
if (!getW3DTruckDrawModuleData()->m_powerslideEffectName.isEmpty()) {
DEBUG_LOG(("*** ERROR - Missing particle system '%s' in thing '%s'\n",
getW3DTruckDrawModuleData()->m_powerslideEffectName.str(), getDrawable()->getObject()->getTemplate()->getName().str()));
}
}
}
}
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
/**
* Stop creating debris from the tank treads
*/
void W3DTruckDraw::enableEmitters( Bool enable )
{
// don't check... if we are hidden the first time thru, then we'll never create the emitters.
// eg, if we are loading a game and the unit is in a tunnel, he'll never get emitteres even when he exits.
//if (!m_effectsInitialized)
{
createEmitters();
m_effectsInitialized=true;
}
if (m_dustEffect)
{
if (enable)
m_dustEffect->start();
else
m_dustEffect->stop();
}
if (m_dirtEffect)
{
if (enable)
m_dirtEffect->start();
else
m_dirtEffect->stop();
}
if (m_powerslideEffect)
{
if (!enable)
m_powerslideEffect->stop();
}
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::updateBones( void ) {
if( getW3DTruckDrawModuleData() )
{
//Front tires
if( !getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.isEmpty() )
{
m_frontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_frontLeftTireBone, ("Missing front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_frontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_frontRightTireBone, ("Missing front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_frontRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_frontRightTireBone )
{
m_frontLeftTireBone = 0;
}
}
//Rear tires
if( !getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.isEmpty() )
{
m_rearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_rearLeftTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_rearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_rearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_rearRightTireBone, ("Missing rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_rearRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_rearRightTireBone)
{
m_rearLeftTireBone = 0;
}
}
//midFront tires
if( !getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.isEmpty() )
{
m_midFrontLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midFrontLeftTireBone, ("Missing mid-front-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midFrontRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midFrontRightTireBone, ("Missing mid-front-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midFrontRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midFrontRightTireBone )
{
m_midFrontLeftTireBone = 0;
}
}
//midRear tires
if( !getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.isEmpty() )
{
m_midRearLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midRearLeftTireBone, ("Missing mid-rear-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midRearRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midRearRightTireBone, ("Missing mid-rear-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midRearRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midRearRightTireBone)
{
m_midRearLeftTireBone = 0;
}
}
//midMid tires
if( !getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.isEmpty() )
{
m_midMidLeftTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str());
DEBUG_ASSERTCRASH(m_midMidLeftTireBone, ("Missing mid-mid-left tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidLeftTireBoneName.str(), getRenderObject()->Get_Name()));
m_midMidRightTireBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str());
DEBUG_ASSERTCRASH(m_midMidRightTireBone, ("Missing mid-mid-right tire bone %s in model %s\n", getW3DTruckDrawModuleData()->m_midMidRightTireBoneName.str(), getRenderObject()->Get_Name()));
if (!m_midMidRightTireBone)
{
m_midMidLeftTireBone = 0;
}
}
//Cab
if( !getW3DTruckDrawModuleData()->m_cabBoneName.isEmpty() )
{
m_cabBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_cabBoneName.str());
DEBUG_ASSERTCRASH(m_cabBone, ("Missing cab bone %s in model %s\n", getW3DTruckDrawModuleData()->m_cabBoneName.str(), getRenderObject()->Get_Name()));
m_trailerBone = getRenderObject()->Get_Bone_Index(getW3DTruckDrawModuleData()->m_trailerBoneName.str());
}
}
m_prevRenderObj = getRenderObject();
m_prevNumBones = m_prevRenderObj->Get_Num_Bones();
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::setHidden(Bool h)
{
W3DModelDraw::setHidden(h);
if (h)
{
enableEmitters(false);
}
}
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::onRenderObjRecreated(void)
{
//DEBUG_LOG(("Old obj %x, newObj %x, new bones %d, old bones %d\n",
// m_prevRenderObj, getRenderObject(), getRenderObject()->Get_Num_Bones(),
// m_prevNumBones));
m_prevRenderObj = NULL;
m_frontLeftTireBone = 0;
m_frontRightTireBone = 0;
m_rearLeftTireBone = 0;
m_rearRightTireBone = 0;
m_midFrontLeftTireBone = 0;
m_midFrontRightTireBone = 0;
m_midRearLeftTireBone = 0;
m_midRearRightTireBone = 0;
m_midMidLeftTireBone = 0;
m_midMidRightTireBone = 0;
updateBones();
}
//-------------------------------------------------------------------------------------------------
/** Rotate and position wheels and other truck parts. */
//-------------------------------------------------------------------------------------------------
void W3DTruckDraw::doDrawModule(const Matrix3D* transformMtx)
{
W3DModelDraw::doDrawModule(transformMtx);
if (!TheGlobalData->m_showClientPhysics)
return;
const W3DTruckDrawModuleData *moduleData = getW3DTruckDrawModuleData();
if (moduleData==NULL) return; // shouldn't ever happen.
Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
if (frozen)
return;
const Real ACCEL_THRESHOLD = 0.01f;
const Real SIZE_CAP = 2.0f;
// get object from logic
Object *obj = getDrawable()->getObject();
if (obj == NULL)
return;
if (getRenderObject()==NULL) return;
if (getRenderObject() != m_prevRenderObj) {
DEBUG_LOG(("W3DTruckDraw::doDrawModule - shouldn't update bones. jba\n"));
updateBones();
}
// get object physics state
PhysicsBehavior *physics = obj->getPhysics();
if (physics == NULL)
return;
const Coord3D *vel = physics->getVelocity();
Real speed = physics->getVelocityMagnitude();
const TWheelInfo *wheelInfo = getDrawable()->getWheelInfo(); // note, can return null!
AIUpdateInterface *ai = obj->getAI();
if (m_cabBone && wheelInfo) {
Matrix3D cabXfrm(1);
cabXfrm.Make_Identity();
Real desiredAngle = wheelInfo->m_wheelAngle*moduleData->m_cabRotationFactor;
// Check goal angle.
if (ai && ai->getPath())
{
Coord3D pointOnPath;
ai->getPath()->peekCachedPointOnPath(pointOnPath);
Real angleToGoal = ThePartitionManager->getRelativeAngle2D( obj, &pointOnPath );
//DEBUG_LOG(("To goal %f, desired %f ", 180*angleToGoal/PI, 180*desiredAngle/PI));
if (angleToGoal<0) {
if (desiredAngle<angleToGoal) desiredAngle=angleToGoal;
if (desiredAngle>0) desiredAngle = 0;
} else {
if (desiredAngle>angleToGoal) desiredAngle = angleToGoal;
if (desiredAngle<0) desiredAngle = 0;
}
//DEBUG_LOG(("final desired %f ", 180*desiredAngle/PI));
}
Real deltaAngle = desiredAngle - m_curCabRotation;
deltaAngle *= moduleData->m_rotationDampingFactor;
m_curCabRotation += deltaAngle;
cabXfrm.Rotate_Z(m_curCabRotation);
getRenderObject()->Capture_Bone( m_cabBone );
getRenderObject()->Control_Bone( m_cabBone, cabXfrm );
if (m_trailerBone && wheelInfo) {
desiredAngle = -wheelInfo->m_wheelAngle*moduleData->m_trailerRotationFactor;
Real deltaAngle = desiredAngle - m_curTrailerRotation;
deltaAngle *= moduleData->m_rotationDampingFactor;
m_curTrailerRotation += deltaAngle;
cabXfrm.Make_Identity();
cabXfrm.Rotate_Z(m_curTrailerRotation);
getRenderObject()->Capture_Bone( m_trailerBone );
getRenderObject()->Control_Bone( m_trailerBone, cabXfrm );
}
}
if (m_frontLeftTireBone || m_rearLeftTireBone)
{
Real powerslideRotationAddition = moduleData->m_powerslideRotationAddition;
if (ai) {
Locomotor *loco = ai->getCurLocomotor();
if (loco) {
if (loco->isMovingBackwards()) {
speed = -speed; // rotate wheels backwards. jba.
powerslideRotationAddition = -powerslideRotationAddition;
}
}
}
const Real rotationFactor = moduleData->m_rotationSpeedMultiplier;
m_frontWheelRotation += rotationFactor*speed;
if (m_isPowersliding)
{
m_rearWheelRotation += rotationFactor*(speed + powerslideRotationAddition);
}
else
{
m_rearWheelRotation += rotationFactor*speed;
}
// For now, just use the same values for mid wheels -- may want to do independent calcs later...
m_midFrontWheelRotation = m_frontWheelRotation;
m_midRearWheelRotation = m_rearWheelRotation;
Matrix3D wheelXfrm(1);
if (m_frontLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_frontWheelRotation);
getRenderObject()->Capture_Bone( m_frontLeftTireBone );
getRenderObject()->Control_Bone( m_frontLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_frontWheelRotation);
getRenderObject()->Capture_Bone( m_frontRightTireBone );
getRenderObject()->Control_Bone( m_frontRightTireBone, wheelXfrm );
}
if (m_rearLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_rearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_rearLeftTireBone );
getRenderObject()->Control_Bone( m_rearLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_rearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
//@todo TROUBLE HERE, THE BONE INDICES DO NOT MATCH THE RENDEROBJECTS BONES, SOMETIMES
getRenderObject()->Capture_Bone( m_rearRightTireBone );
getRenderObject()->Control_Bone( m_rearRightTireBone, wheelXfrm );
}
if (m_midFrontLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontLeftHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
getRenderObject()->Capture_Bone( m_midFrontLeftTireBone );
getRenderObject()->Control_Bone( m_midFrontLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_frontRightHeightOffset);
wheelXfrm.Rotate_Z(wheelInfo->m_wheelAngle);
wheelXfrm.Rotate_Y(m_midFrontWheelRotation);
getRenderObject()->Capture_Bone( m_midFrontRightTireBone );
getRenderObject()->Control_Bone( m_midFrontRightTireBone, wheelXfrm );
}
if (m_midRearLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_midRearLeftTireBone );
getRenderObject()->Control_Bone( m_midRearLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
getRenderObject()->Capture_Bone( m_midRearRightTireBone );
getRenderObject()->Control_Bone( m_midRearRightTireBone, wheelXfrm );
}
if (m_midMidLeftTireBone && wheelInfo)
{
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearLeftHeightOffset);
getRenderObject()->Capture_Bone( m_midMidLeftTireBone );
getRenderObject()->Control_Bone( m_midMidLeftTireBone, wheelXfrm );
wheelXfrm.Make_Identity();
wheelXfrm.Rotate_Y(m_midRearWheelRotation);
wheelXfrm.Adjust_Z_Translation(wheelInfo->m_rearRightHeightOffset);
getRenderObject()->Capture_Bone( m_midMidRightTireBone );
getRenderObject()->Control_Bone( m_midMidRightTireBone, wheelXfrm );
}
}
Bool wasPowersliding = m_isPowersliding;
m_isPowersliding = false;
if (physics->isMotive() && !obj->isSignificantlyAboveTerrain()) {
enableEmitters(true);
Coord3D accel = *physics->getAcceleration();
accel.z = 0; // ignore gravitational force.
Bool accelerating = accel.length()>ACCEL_THRESHOLD;
//DEBUG_LOG(("Accel %f, speed %f\n", accel.length(), speed));
if (accelerating) {
Real dot = accel.x*vel->x + accel.y*vel->y;
if (dot<0) {
accelerating = false; // decelerating, actually.
}
}
if (m_dustEffect) {
// Need more dust the faster we go.
if (speed>SIZE_CAP) {
speed = SIZE_CAP;
}
m_dustEffect->setSizeMultiplier(speed);
}
if (m_dirtEffect) {
if (wheelInfo && wheelInfo->m_framesAirborne>3) {
Real factor = 1 + wheelInfo->m_framesAirborne/16;
if (factor>2.0) factor = 2.0;
m_dustEffect->setSizeMultiplier(factor*SIZE_CAP);
m_dustEffect->trigger();
m_landingSound.setObjectID(obj->getID());
TheAudio->addAudioEvent(&m_landingSound);
} else {
if (!accelerating || speed>2.0f) {
m_dirtEffect->stop();
}
}
}
if (m_powerslideEffect) {
if (physics->getTurning() == TURN_NONE) {
m_powerslideEffect->stop();
} else {
m_isPowersliding = true;
m_powerslideEffect->start();
}
}
if (m_dirtEffect) {
if (!accelerating || speed>2.0f) {
m_dirtEffect->stop();
}
}
}
else
enableEmitters(false);
m_wasAirborne = obj->isSignificantlyAboveTerrain();
if(!wasPowersliding && m_isPowersliding) {
// start sound
m_powerslideSound.setObjectID(obj->getID());
m_powerslideSound.setPlayingHandle(TheAudio->addAudioEvent(&m_powerslideSound));
} else if (wasPowersliding && !m_isPowersliding) {
TheAudio->removeAudioEvent(m_powerslideSound.getPlayingHandle());
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTruckDraw::crc( Xfer *xfer )
{
// extend base class
W3DModelDraw::crc( xfer );
} // end crc
// ------------------------------------------------------------------------------------------------
/** Xfer method
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DTruckDraw::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 1;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
W3DModelDraw::xfer( xfer );
// John A and Mark W say there is no data to save here
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTruckDraw::loadPostProcess( void )
{
// extend base class
W3DModelDraw::loadPostProcess();
// toss any existing ones (no need to re-create; we'll do that on demand)
tossEmitters();
} // end loadPostProcess

View file

@ -0,0 +1,129 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DMOTD.cpp //////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DMOTD.cpp
//
// Created: Colin Day, August 2001
//
// Desc: Message of the day
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
#include <stdio.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/NameKeyGenerator.h"
#include "GameClient/GameWindow.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/DisplayStringManager.h"
#include "GameClient/GadgetSlider.h"
///////////////////////////////////////////////////////////////////////////////
// DEFINES ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static NameKeyType closeButtonID = NAMEKEY_INVALID;
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// MOTDSystem =================================================================
/** Message of the day */
//=============================================================================
WindowMsgHandledType MOTDSystem( GameWindow *window, UnsignedInt msg,
WindowMsgData mData1, WindowMsgData mData2 )
{
switch( msg )
{
// ------------------------------------------------------------------------
case GWM_CREATE:
{
// load id's needed
closeButtonID = TheNameKeyGenerator->nameToKey( "MOTD.wnd:CloseMOTD" );
break;
} // end create
// ------------------------------------------------------------------------
case GWM_DESTROY:
{
break;
} // end case
// ------------------------------------------------------------------------
case GBM_SELECTED:
{
GameWindow *control = (GameWindow *)mData1;
Int controlID = control->winGetWindowId();
if( controlID == closeButtonID )
window->winHide( !window->winIsHidden() );
break;
} // end selected
default:
return MSG_IGNORED;
} // end switch
return MSG_HANDLED;
} // end MOTDSystem

View file

@ -0,0 +1,336 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DCheckBox.cpp //////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DCheckBox.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D methods needed to implement the checkbox UI control
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GadgetCheckBox.h"
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// drawCheckBoxText ===========================================================
/** Draw the text for a checkbox */
//=============================================================================
static void drawCheckBoxText( GameWindow *window, WinInstanceData *instData )
{
ICoord2D origin, size, textPos;
Int width, height;
Color textColor, dropColor;
DisplayString *text = instData->getTextDisplayString();
// sanity
if( text == NULL || text->getTextLength() == 0 )
return;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the right text color
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
textColor = window->winGetDisabledTextColor();
dropColor = window->winGetDisabledTextBorderColor();
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
textColor = window->winGetHiliteTextColor();
dropColor = window->winGetHiliteTextBorderColor();
} // end else if, hilited
else
{
textColor = window->winGetEnabledTextColor();
dropColor = window->winGetEnabledTextBorderColor();
} // end enabled only
// set our font to that of our parent if not the same
if( text->getFont() != window->winGetFont() )
text->setFont( window->winGetFont() );
// get text size
text->getSize( &width, &height );
// where to draw
textPos.x = origin.x + size.y;//(size.x / 2) - (width / 2);
textPos.y = origin.y + (size.y / 2) - (height / 2);
// draw it
text->draw( textPos.x, textPos.y, textColor, dropColor );
} // end drawCheckBoxText
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetCheckBoxDraw ======================================================
/** Draw colored check box using standard graphics */
//=============================================================================
void W3DGadgetCheckBoxDraw( GameWindow *window, WinInstanceData *instData )
{
Int checkOffsetFromLeft;
Color backColor,
backBorder,
boxColor,
boxBorder;
ICoord2D origin, size, start, end;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// compute start of check offset
checkOffsetFromLeft = size.x / 16;
//
// get the colors we should be using to draw, see GadgetCheckBox.h
// draw appropriate state, see GadgetCheckBox.h for info
//
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
// disabled background
backColor = GadgetCheckBoxGetDisabledColor( window );
backBorder = GadgetCheckBoxGetDisabledBorderColor( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
boxColor = GadgetCheckBoxGetDisabledCheckedBoxColor( window );
boxBorder = GadgetCheckBoxGetDisabledCheckedBoxBorderColor( window );
}
else
{
boxColor = GadgetCheckBoxGetDisabledUncheckedBoxColor( window );
boxBorder = GadgetCheckBoxGetDisabledUncheckedBoxBorderColor( window );
}
} // end if
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
// hilited background
backColor = GadgetCheckBoxGetHiliteColor( window );
backBorder = GadgetCheckBoxGetHiliteBorderColor( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
boxColor = GadgetCheckBoxGetHiliteCheckedBoxColor( window );
boxBorder = GadgetCheckBoxGetHiliteCheckedBoxBorderColor( window );
}
else
{
boxColor = GadgetCheckBoxGetHiliteUncheckedBoxColor( window );
boxBorder = GadgetCheckBoxGetHiliteUncheckedBoxBorderColor( window );
}
} // end else if
else
{
// enabled background
backColor = GadgetCheckBoxGetEnabledColor( window );
backBorder = GadgetCheckBoxGetEnabledBorderColor( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
boxColor = GadgetCheckBoxGetEnabledCheckedBoxColor( window );
boxBorder = GadgetCheckBoxGetEnabledCheckedBoxBorderColor( window );
}
else
{
boxColor = GadgetCheckBoxGetEnabledUncheckedBoxColor( window );
boxBorder = GadgetCheckBoxGetEnabledUncheckedBoxBorderColor( window );
}
} // end else
// draw background border
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winOpenRect( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw the background
start.x++;
start.y++;
end.x--;
end.y--;
TheWindowManager->winFillRect( backColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw box border
start.x = origin.x + checkOffsetFromLeft;
start.y = origin.y + (size.y / 3);
end.x = start.x + (size.y / 3);
end.y = start.y + (size.y / 3);
TheWindowManager->winOpenRect( boxBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw "x" for button
if( boxColor != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winDrawLine( boxColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
TheWindowManager->winDrawLine( boxColor, WIN_DRAW_LINE_WIDTH,
start.x, end.y, end.x, start.y );
} // end if
// draw the button text
if( instData->getTextLength() )
drawCheckBoxText( window, instData );
} // end W3DGadgetCheckBoxDraw
// W3DGadgetCheckBoxImageDraw =================================================
/** Draw check box with user supplied images */
//=============================================================================
void W3DGadgetCheckBoxImageDraw( GameWindow *window, WinInstanceData *instData )
{
Int checkOffsetFromLeft;
const Image *boxImage = NULL;//*backgroundImage = NULL,
ICoord2D origin, start, end, size;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// compute screen coords
start.x = origin.x + instData->m_imageOffset.x;
start.y = origin.y + instData->m_imageOffset.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
// compute start of check offset
checkOffsetFromLeft = 0;
//
// get the colors we should be using to draw, see GadgetCheckBoxButton.h
// draw appropriate state, see GadgetCheckBoxButton.h for info
//
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
// disabled background
// backgroundImage = GadgetCheckBoxGetDisabledImage( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
boxImage = GadgetCheckBoxGetDisabledCheckedBoxImage( window );
else
boxImage = GadgetCheckBoxGetDisabledUncheckedBoxImage( window );
} // end if
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
// hilited background
// backgroundImage = GadgetCheckBoxGetHiliteImage( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
boxImage = GadgetCheckBoxGetHiliteCheckedBoxImage( window );
else
boxImage = GadgetCheckBoxGetHiliteUncheckedBoxImage( window );
} // end else if
else
{
// enabled background
// backgroundImage = GadgetCheckBoxGetEnabledImage( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
boxImage = GadgetCheckBoxGetEnabledCheckedBoxImage( window );
else
boxImage = GadgetCheckBoxGetEnabledUncheckedBoxImage( window );
} // end else
// draw background image
// if( backgroundImage )
// TheWindowManager->winDrawImage( backgroundImage, start.x, start.y,
// end.x, end.y );
// draw the box image
if( boxImage )
{
start.x = origin.x + instData->m_imageOffset.x + checkOffsetFromLeft;
start.y = origin.y + 3;
end.x = start.x + (size.y - 6);
end.y = start.y + (size.y - 6);
TheWindowManager->winDrawImage( boxImage, start.x, start.y,
end.x, end.y );
} // end if
// draw the text
if( instData->getTextLength() )
drawCheckBoxText( window, instData );
} // end W3DGadgetCheckBoxImageDraw

View file

@ -0,0 +1,223 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DComboBox.cpp ///////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DComboBox.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation for the Combo box control
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetComboBox.h"
#include "GameClient/GadgetListBox.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetComboBoxDraw =======================================================
/** Draw colored list box using standard graphics */
//=============================================================================
void W3DGadgetComboBoxDraw( GameWindow *window, WinInstanceData *instData )
{
Int width, height, fontHeight, x, y;
Color background, border, titleColor, titleBorder;
// ComboBoxData *combo = (ComboBoxData *)window->winGetUserData();
ICoord2D size;
DisplayString *title = instData->getTextDisplayString();
// get window position and size
window->winGetScreenPosition( &x, &y );
window->winGetSize( &size.x, &size.y );
// get font height
fontHeight = TheWindowManager->winFontHeight( instData->getFont() );
// alias width and height from size
width = size.x;
height = size.y;
// get the right colors
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
background = GadgetComboBoxGetDisabledColor( window );
border = GadgetComboBoxGetDisabledBorderColor( window );
titleColor = window->winGetDisabledTextColor();
titleBorder = window->winGetDisabledTextBorderColor();
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
background = GadgetComboBoxGetHiliteColor( window );
border = GadgetComboBoxGetHiliteBorderColor( window );
titleColor = window->winGetHiliteTextColor();
titleBorder = window->winGetHiliteTextBorderColor();
} // end else if, hilited
else
{
background = GadgetComboBoxGetEnabledColor( window );
border = GadgetComboBoxGetEnabledBorderColor( window );
titleColor = window->winGetEnabledTextColor();
titleBorder = window->winGetEnabledTextBorderColor();
} // end else, enabled
// Draw the title
if( title && title->getTextLength() )
{
// set the font of this text to that of the window if not already
if( title->getFont() != window->winGetFont() )
title->setFont( window->winGetFont() );
// draw the text
title->draw( x + 1, y, titleColor, titleBorder );
y += fontHeight + 1;
height -= fontHeight + 1;
} // end if
// draw the back border
if( border != WIN_COLOR_UNDEFINED )
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
x, y, x + width, y + height );
// draw background
if( background != WIN_COLOR_UNDEFINED )
TheWindowManager->winFillRect( background, WIN_DRAW_LINE_WIDTH,
x + 1, y + 1,
x + width - 1, y + height - 1 );
} // end W3DGadgetComboBoxDraw
// W3DGadgetComboBoxImageDraw ==================================================
/** Draw combo box with user supplied images */
//=============================================================================
void W3DGadgetComboBoxImageDraw( GameWindow *window, WinInstanceData *instData )
{
Int width, height, x, y;
const Image *image;
// ComboBoxData *combo = (ComboBoxData *)window->winGetUserData();
ICoord2D size;
Color titleColor, titleBorder;
DisplayString *title = instData->getTextDisplayString();
// get window position and size
window->winGetScreenPosition( &x, &y );
window->winGetSize( &size.x, &size.y );
// save off width and height so we can change them
width = size.x;
height = size.y;
// get the image
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
image = GadgetComboBoxGetDisabledImage( window );
titleColor = window->winGetDisabledTextColor();
titleBorder = window->winGetDisabledTextBorderColor();
}
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
image = GadgetComboBoxGetHiliteImage( window );
titleColor = window->winGetHiliteTextColor();
titleBorder = window->winGetHiliteTextBorderColor();
}
else
{
image = GadgetComboBoxGetEnabledImage( window );
titleColor = window->winGetEnabledTextColor();
titleBorder = window->winGetEnabledTextBorderColor();
}
// draw the back image
if( image )
{
ICoord2D start, end;
start.x = x + instData->m_imageOffset.x;
start.y = y + instData->m_imageOffset.y;
end.x = start.x + width;
end.y = start.y + height;
TheWindowManager->winDrawImage( image,
start.x, start.y,
end.x, end.y );
} // end if
// Draw the title
if( title && title->getTextLength() )
{
// set font to font of the window if not already
if( title->getFont() != window->winGetFont() )
title->setFont( window->winGetFont() );
// draw the text
title->draw( x + 1, y, titleColor, titleBorder );
y += TheWindowManager->winFontHeight( instData->getFont() );
height -= TheWindowManager->winFontHeight( instData->getFont() ) + 1;
} // end if
} // end W3DGadgetComboBoxImageDraw

View file

@ -0,0 +1,484 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: .cpp /////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project:
//
// File name: .cpp
//
// Created:
//
// Desc:
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetSlider.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// W3DGadgetHorizontalSliderDraw ==============================================
/** Draw colored horizontal slider using standard graphics */
//=============================================================================
void W3DGadgetHorizontalSliderDraw( GameWindow *window, WinInstanceData *instData )
{
Color backBorder, backColor;
ICoord2D origin, size, start, end;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the right colors
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
backBorder = GadgetSliderGetDisabledBorderColor( window );
backColor = GadgetSliderGetDisabledColor( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
backBorder = GadgetSliderGetHiliteBorderColor( window );
backColor = GadgetSliderGetHiliteColor( window );
} // end else if, hilited
else
{
backBorder = GadgetSliderGetEnabledBorderColor( window );
backColor = GadgetSliderGetEnabledColor( window );
} // end else, enabled
// draw background border and rect over whole control
if( backBorder != WIN_COLOR_UNDEFINED )
{
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winOpenRect( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
if( backColor != WIN_COLOR_UNDEFINED )
{
start.x = origin.x + 1;
start.y = origin.y + 1;
end.x = start.x + size.x - 2;
end.y = start.y + size.y - 2;
TheWindowManager->winFillRect( backColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
} // end W3DGadgetHorizontalSliderDraw
// W3DGadgetHorizontalSliderImageDraw =========================================
/** Draw horizontal slider with user supplied images */
//=============================================================================
void W3DGadgetHorizontalSliderImageDraw( GameWindow *window,
WinInstanceData *instData )
{
const Image *fillSquare, *blankSquare, *highlightSquare;
ICoord2D origin, size, start, end, highlightOffset;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
highlightSquare = GadgetSliderGetHiliteImageLeft( window );
blankSquare = GadgetSliderGetDisabledImageRight( window );
fillSquare = GadgetSliderGetDisabledImageLeft( window );
SliderData *s = (SliderData *)window->winGetUserData();
Real xMulti = INT_TO_REAL(TheDisplay->getWidth()) / 800;
// figure out how many boxes we draw for this slider
Int numBoxes = 0;
Int numSelectedBoxes = 0;
Int numHighlightBoxes = 0;
Int boxWidth = fillSquare->getImageWidth()* xMulti;
Int boxPadding = 2;
start.x = origin.x;
end.x = start.x + boxWidth;
Real selectedPercent = (s->position - s->minVal)/INT_TO_REAL((s->maxVal - s->minVal));
Int maxSelectedX = origin.x + REAL_TO_INT(selectedPercent * size.x);
while(end.x < origin.x + size.x )
{
if (start.x <= maxSelectedX && end.x < origin.x + size.x && s->position != s->minVal)
++numSelectedBoxes;
start.x = end.x + boxPadding;
end.x = start.x + boxWidth;
++numBoxes;
}
numHighlightBoxes = numBoxes + 1;
Int distanceCovered = end.x - origin.x - boxWidth;
highlightOffset.x = -(boxWidth + boxPadding)/2;
highlightOffset.y = boxWidth/3;
Int blankness = size.x - distanceCovered;
origin.x += blankness/2;
Int i;
if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
ICoord2D backgroundStart, backgroundEnd;
backgroundStart.y = origin.y + highlightOffset.y;
backgroundEnd.y = backgroundStart.y + boxWidth + boxPadding;
for (i=0; i<numHighlightBoxes; ++i)
{
backgroundStart.x = origin.x + highlightOffset.x + i*(boxWidth+boxPadding);
backgroundEnd.x = backgroundStart.x + boxWidth + boxPadding;
TheWindowManager->winDrawImage( highlightSquare,
backgroundStart.x, backgroundStart.y,
backgroundEnd.x, backgroundEnd.y );
}
}
start.y = origin.y;
end.y = start.y + boxWidth;
for (i=0; i<numSelectedBoxes; ++i)
{
start.x = origin.x + i*(boxWidth+boxPadding);
end.x = start.x + boxWidth;
TheWindowManager->winDrawImage( fillSquare,
start.x, start.y,
end.x, end.y );
}
for (i=numSelectedBoxes; i<numBoxes; ++i)
{
start.x = origin.x + i*(boxWidth+boxPadding);
end.x = start.x + boxWidth;
TheWindowManager->winDrawImage( blankSquare,
start.x, start.y,
end.x, end.y );
}
}
// W3DGadgetHorizontalSliderImageDraw =========================================
/** Draw horizontal slider with user supplied images */
//=============================================================================
void W3DGadgetHorizontalSliderImageDrawB( GameWindow *window,
WinInstanceData *instData )
{
const Image *fillSquare, *blankSquare, *highlightSquare;//, *progressArrow;
ICoord2D origin, size, start, end;
Int xOffset, yOffset;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
SliderData *s = (SliderData *)window->winGetUserData();
Real xMulti = INT_TO_REAL(TheDisplay->getWidth()) / 800;
Real yMulti = INT_TO_REAL(TheDisplay->getHeight())/ 600;
// get image offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
UnicodeString tooltip, tmp;
tooltip.format(L"mult:%g/%g, img offset:%d,%d", xMulti, yMulti, xOffset, yOffset);
tmp.format(L"\norigin: %d,%d size:%d,%d", origin.x, origin.y, size.x, size.y);
tooltip.concat(tmp);
tmp.format(L"\ns= %d <--> %d, numTicks=%g, pos = %d", s->minVal, s->maxVal, s->numTicks, s->position);
tooltip.concat(tmp);
if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
highlightSquare = GadgetSliderGetHiliteImageLeft( window );
ICoord2D backgroundStart, backgroundEnd;
backgroundStart.x = origin.x - (highlightSquare->getImageWidth() * xMulti)/2;
backgroundStart.y = origin.y + (highlightSquare->getImageHeight() *yMulti)/3;
backgroundEnd.y = backgroundStart.y + highlightSquare->getImageHeight()* yMulti;
backgroundEnd.x = backgroundStart.x + highlightSquare->getImageWidth() * xMulti;
tmp.format(L"\nHighlighted: (%d,%d) -> (%d,%d), step %d/%g, full %d/%d", backgroundStart.x, backgroundStart.y,
backgroundEnd.x, backgroundEnd.y, highlightSquare->getImageWidth(), highlightSquare->getImageWidth() * xMulti,
origin.x, size.x);
tooltip.concat(tmp);
while(backgroundStart.x < origin.x + size.x)
{
TheWindowManager->winDrawImage( highlightSquare,
backgroundStart.x, backgroundStart.y,
backgroundEnd.x, backgroundEnd.y );
backgroundStart.x = backgroundEnd.x;
backgroundEnd.x = backgroundStart.x + highlightSquare->getImageWidth() * xMulti;
}
tmp.format(L"\n bsX = %d, beX = %d (%d < %d+%d or %d?)", backgroundStart.x, backgroundEnd.x,
backgroundStart.x, origin.x, size.x, origin.x + size.x);
tooltip.concat(tmp);
}
fillSquare = GadgetSliderGetDisabledImageLeft( window );
start.x = origin.x;
start.y = origin.y;
end.y = start.y + fillSquare->getImageHeight() * yMulti;
end.x = start.x + fillSquare->getImageWidth()* xMulti;
tmp.format(L"\ntop: start=%d,%d, end=%d,%d", start.x, start.y, end.x, end.y);
tooltip.concat(tmp);
while(start.x <= origin.x + (s->numTicks * (s->position - s->minVal)) && end.x < origin.x + size.x && s->position != s->minVal)
{
TheWindowManager->winDrawImage( fillSquare,
start.x, start.y,
end.x, end.y );
start.x = end.x + 2;
end.x = start.x + fillSquare->getImageWidth()* xMulti;
}
blankSquare = GadgetSliderGetDisabledImageRight( window );
end.x = start.x + blankSquare->getImageWidth()* xMulti;
while(end.x < origin.x + size.x )
{
TheWindowManager->winDrawImage( blankSquare,
start.x, start.y,
end.x, end.y );
start.x = end.x + 2;
end.x = start.x + blankSquare->getImageWidth()* xMulti;
}
instData->setTooltipText(tooltip);
// if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
// {
// progressArrow = GadgetSliderGetHiliteImageRight( window );
// if(!progressArrow)
// return;
// Int transPos = (s->numTicks * (s->position - s->minVal)) - progressArrow->getImageWidth() /2;
// start.x = origin.x + transPos;
// start.y = origin.y + fillSquare->getImageHeight()/3*2;
// end.y = start.y + progressArrow->getImageHeight();
// end.x = start.x + progressArrow->getImageWidth();
// TheWindowManager->winDrawImage( progressArrow,
// start.x, start.y,
// end.x, end.y );
// }
}
// W3DGadgetHorizontalSliderImageDraw =========================================
/** Draw horizontal slider with user supplied images */
//=============================================================================
void W3DGadgetHorizontalSliderImageDrawA( GameWindow *window,
WinInstanceData *instData )
{
const Image *leftImageLeft, *rightImageLeft, *centerImageLeft, *smallCenterImageLeft;
const Image *leftImageRight, *rightImageRight, *centerImageRight, *smallCenterImageRight;
ICoord2D origin, size, start, end;
Int xOffset, yOffset;
Int i;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
SliderData *s = (SliderData *)window->winGetUserData();
Int transPos = (s->numTicks * (s->position - s->minVal)) + HORIZONTAL_SLIDER_THUMB_WIDTH/2;
IRegion2D clipLeft, clipRight;
// get image offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
// get the right images
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
leftImageRight = leftImageLeft = GadgetSliderGetDisabledImageLeft( window );
rightImageRight = rightImageLeft = GadgetSliderGetDisabledImageRight( window );
// centerImageRight = centerImageLeft = GadgetSliderGetDisabledImageCenter( window );
// smallCenterImageRight = smallCenterImageLeft = GadgetSliderGetDisabledImageSmallCenter( window );
} // end if, disabled
else //if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
leftImageLeft = GadgetSliderGetHiliteImageLeft( window );
rightImageLeft = GadgetSliderGetHiliteImageRight( window );
centerImageLeft = GadgetSliderGetHiliteImageCenter( window );
smallCenterImageLeft = GadgetSliderGetHiliteImageSmallCenter( window );
leftImageRight = GadgetSliderGetEnabledImageLeft( window );
rightImageRight = GadgetSliderGetEnabledImageRight( window );
centerImageRight = GadgetSliderGetEnabledImageCenter( window );
smallCenterImageRight = GadgetSliderGetEnabledImageSmallCenter( window );
} // end else, enabled
// sanity, we need to have these images to make it look right
if( leftImageLeft == NULL || rightImageLeft == NULL ||
centerImageLeft == NULL || smallCenterImageLeft == NULL ||
leftImageRight == NULL || rightImageRight == NULL ||
centerImageRight == NULL || smallCenterImageRight == NULL )
return;
// get image sizes for the ends
ICoord2D leftSize, rightSize;
leftSize.x = leftImageLeft->getImageWidth();
leftSize.y = leftImageLeft->getImageHeight();
rightSize.x = rightImageLeft->getImageWidth();
rightSize.y = rightImageLeft->getImageHeight();
// get two key points used in the end drawing
ICoord2D leftEnd, rightStart;
leftEnd.x = origin.x + leftSize.x + xOffset;
leftEnd.y = origin.y + size.y + yOffset;
rightStart.x = origin.x + size.x - rightSize.x + xOffset;
rightStart.y = origin.y + size.y - leftSize.y + yOffset;
// draw the center repeating bar
Int centerWidth, pieces;
// get width we have to draw our repeating center in
centerWidth = rightStart.x - leftEnd.x;
// how many whole repeating pieces will fit in that width
pieces = centerWidth / centerImageLeft->getImageWidth();
// draw the pieces
start.x = leftEnd.x;
start.y = origin.y + size.y - leftSize.y + yOffset;
end.y =origin.y + size.y + yOffset;
clipLeft.lo.x = origin.x;
clipLeft.lo.y = rightStart.y;
clipLeft.hi.y = leftEnd.y;
clipLeft.hi.x = origin.x + transPos;
clipRight.lo.x = origin.x + transPos;
clipRight.lo.y = rightStart.y;
clipRight.hi.y = leftEnd.y;
clipRight.hi.x = origin.x + size.x;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + centerImageLeft->getImageWidth();
TheDisplay->setClipRegion(&clipLeft);
TheWindowManager->winDrawImage( centerImageLeft,
start.x, start.y,
end.x, end.y );
TheDisplay->setClipRegion(&clipRight);
TheWindowManager->winDrawImage( centerImageRight,
start.x, start.y,
end.x, end.y );
start.x += centerImageLeft->getImageWidth();
} // end for i
//
// how many small repeating pieces will fit in the gap from where the
// center repeating bar stopped and the right image, draw them
// and overlapping underneath where the right end will go
//
centerWidth = rightStart.x - start.x;
pieces = centerWidth / smallCenterImageLeft->getImageWidth() + 1;
end.y = leftEnd.y;//start.y + smallCenterImageLeft->getImageHeight();
for( i = 0; i < pieces; i++ )
{
end.x = start.x + smallCenterImageLeft->getImageWidth();
TheDisplay->setClipRegion(&clipLeft);
TheWindowManager->winDrawImage( smallCenterImageLeft,
start.x, start.y,
end.x, end.y );
TheDisplay->setClipRegion(&clipRight);
TheWindowManager->winDrawImage( smallCenterImageRight,
start.x, start.y,
end.x, end.y );
start.x += smallCenterImageLeft->getImageWidth();
} // end for i
// draw left end
start.x = origin.x + xOffset;
start.y = rightStart.y;
end = leftEnd;
TheDisplay->setClipRegion(&clipLeft);
TheWindowManager->winDrawImage(leftImageLeft, start.x, start.y, end.x, end.y);
TheDisplay->setClipRegion(&clipRight);
TheWindowManager->winDrawImage(leftImageRight, start.x, start.y, end.x, end.y);
// draw right end
start = rightStart;
end.x = start.x + rightSize.x;
end.y = leftEnd.y;
TheDisplay->setClipRegion(&clipLeft);
TheWindowManager->winDrawImage(rightImageLeft, start.x, start.y, end.x, end.y);
TheDisplay->setClipRegion(&clipRight);
TheWindowManager->winDrawImage(rightImageRight, start.x, start.y, end.x, end.y);
TheDisplay->enableClipping(FALSE);
} // end W3DGadgetHorizontalSliderImageDraw

View file

@ -0,0 +1,671 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DListBox.cpp ///////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DListBox.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation for the list box control
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetListBox.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// drawHiliteBar ==============================================================
/** Draw image for the hilite bar */
//=============================================================================
static void drawHiliteBar( const Image *left, const Image *right,
const Image *center, const Image *smallCenter,
Int startX, Int startY,
Int endX, Int endY )
{
ICoord2D barWindowSize; // end point of bar from window origin
Int xOffset = 0, yOffset = 0; // incase we want this functionality later
ICoord2D start, end;
Int i;
IRegion2D clipRegion;
barWindowSize.x = endX - startX;
barWindowSize.y = endY - startY;
//
// the bar window size will always be at least big enough to accomodate
// the left and right ends
//
if( barWindowSize.x < left->getImageWidth() + right->getImageWidth() )
barWindowSize.x = left->getImageWidth() + right->getImageWidth();
// get image sizes for the ends
ICoord2D leftSize, rightSize;
leftSize.x = left->getImageWidth();
leftSize.y = left->getImageHeight();
rightSize.x = right->getImageWidth();
rightSize.y = right->getImageHeight();
// get two key points used in the end drawing
ICoord2D leftEnd, rightStart;
leftEnd.x = startX + leftSize.x + xOffset;
leftEnd.y = startY + barWindowSize.y + yOffset;
rightStart.x = startX + barWindowSize.x - rightSize.x + xOffset;
rightStart.y = startY + yOffset;
// draw the center repeating bar
Int centerWidth, pieces;
// get width we have to draw our repeating center in
centerWidth = rightStart.x - leftEnd.x;
// how many whole repeating pieces will fit in that width
pieces = centerWidth / center->getImageWidth();
// draw the pieces
start.x = leftEnd.x;
start.y = startY + yOffset;
end.y = start.y + barWindowSize.y;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + center->getImageWidth();
TheWindowManager->winDrawImage( center,
start.x, start.y,
end.x, end.y );
start.x += center->getImageWidth();
} // end for i
//
// how many small repeating pieces will fit in the gap from where the
// center repeating bar stopped and the right image, draw them
// and overlapping underneath where the right end will go
//
// set the text clip region to the outline of the listbox
clipRegion.lo.x = leftEnd.x;
clipRegion.lo.y = startY + yOffset;
clipRegion.hi.x = leftEnd.x + centerWidth;
clipRegion.hi.y = start.y + barWindowSize.y;
TheDisplay->setClipRegion(&clipRegion);
centerWidth = rightStart.x - start.x;
if( centerWidth )
{
pieces = centerWidth / smallCenter->getImageWidth() + 1;
end.y = start.y + barWindowSize.y;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + smallCenter->getImageWidth();
TheWindowManager->winDrawImage( smallCenter,
start.x, start.y,
end.x, end.y );
start.x += smallCenter->getImageWidth();
} // end for i
} // end if
TheDisplay->enableClipping(FALSE);
// draw left end
start.x = startX + xOffset;
start.y = startY + yOffset;
end = leftEnd;
TheWindowManager->winDrawImage(left, start.x, start.y, end.x, end.y);
// draw right end
start = rightStart;
end.x = start.x + rightSize.x;
end.y = start.y + barWindowSize.y;
TheWindowManager->winDrawImage(right, start.x, start.y, end.x, end.y);
} // end drawHiliteBar
// drawListBoxText ============================================================
/** Draw the text for a listbox */
//=============================================================================
static void drawListBoxText( GameWindow *window, WinInstanceData *instData,
Int x, Int y, Int width, Int height,
Bool useImages )
{
Int drawY;
ListboxData *list = (ListboxData *)window->winGetUserData();
Int i;
Bool selected;
Int listLineHeight;
Color textColor;
// W3DGameWindow *w3dWindow = static_cast<W3DGameWindow *>(window);
IRegion2D clipRegion;
ICoord2D start, end;
//
// save the clipping information region cause we're going to use it here
// in drawing the text
//
// TheWindowManager->winGetClipRegion( &clipRegion );
// set clip region to inside the outline box.
// TheWindowManager->winSetClipRegion( x, y, width, height );
// set the text clip region to the outline of the listbox
clipRegion.lo.x = x + 1;
clipRegion.lo.y = y -3;
clipRegion.hi.x = x + width - 1;
clipRegion.hi.y = y + height - 1;
drawY = y - list->displayPos;
for( i = 0; ; i++ )
{
if( i > 0 )
if( list->listData[(i - 1)].listHeight >
(list->displayPos + list->displayHeight) )
break;
if( i == list->endPos )
break;
if( list->listData[i].listHeight < list->displayPos )
{
drawY += (list->listData[i].height + 1);
continue;
}
listLineHeight = list->listData[i].height + 1;
//textColor = list->listData[i].textColor;
selected = FALSE;
if( list->multiSelect )
{
Int j = 0;
while( list->selections[j] >= 0 )
{
if( i == list->selections[j] )
{
selected = TRUE;
break;
}
j++;
}
}
else
{
if( i == list->selectPos )
selected = TRUE;
}
// this item is selected, draw the selection color or image
if( selected )
{
if( useImages )
{
const Image *left, *right, *center, *smallCenter;
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
left = GadgetListBoxGetDisabledSelectedItemImageLeft( window );
right = GadgetListBoxGetDisabledSelectedItemImageRight( window );
center = GadgetListBoxGetDisabledSelectedItemImageCenter( window );
smallCenter = GadgetListBoxGetDisabledSelectedItemImageSmallCenter( window );
} // end if
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
left = GadgetListBoxGetHiliteSelectedItemImageLeft( window );
right = GadgetListBoxGetHiliteSelectedItemImageRight( window );
center = GadgetListBoxGetHiliteSelectedItemImageCenter( window );
smallCenter = GadgetListBoxGetHiliteSelectedItemImageSmallCenter( window );
} // end else if
else
{
left = GadgetListBoxGetEnabledSelectedItemImageLeft( window );
right = GadgetListBoxGetEnabledSelectedItemImageRight( window );
center = GadgetListBoxGetEnabledSelectedItemImageCenter( window );
smallCenter = GadgetListBoxGetEnabledSelectedItemImageSmallCenter( window );
} // end else
// draw select image across area
//
// where are we going to draw ... taking into account the clipping
// region of the edge of the listbox
//
start.x = x;
start.y = drawY;
end.x = start.x + width;
end.y = start.y + listLineHeight;
if( end.y > clipRegion.hi.y )
end.y = clipRegion.hi.y;
if( start.y < clipRegion.lo.y )
start.y = clipRegion.lo.y;
if( left && right && center && smallCenter )
drawHiliteBar( left, right, center, smallCenter, start.x + 1, start.y, end.x , end.y );
} // end if, use images
else
{
Color selectColor = WIN_COLOR_UNDEFINED,
selectBorder = WIN_COLOR_UNDEFINED;
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
selectColor = GadgetListBoxGetDisabledSelectedItemColor( window );
selectBorder = GadgetListBoxGetDisabledSelectedItemBorderColor( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
selectColor = GadgetListBoxGetHiliteSelectedItemColor( window );
selectBorder = GadgetListBoxGetHiliteSelectedItemBorderColor( window );
} // end else if, hilited
else
{
selectColor = GadgetListBoxGetEnabledSelectedItemColor( window );
selectBorder = GadgetListBoxGetEnabledSelectedItemBorderColor( window );
} // end else, enabled
// draw border
//
// where are we going to draw ... taking into account the clipping
// region of the edge of the listbox
//
start.x = x;
start.y = drawY;
end.x = start.x + width;
end.y = start.y + listLineHeight;
if( end.y > clipRegion.hi.y )
end.y = clipRegion.hi.y;
if( start.y < clipRegion.lo.y )
start.y = clipRegion.lo.y;
if( selectBorder != WIN_COLOR_UNDEFINED )
TheWindowManager->winOpenRect( selectBorder,
WIN_DRAW_LINE_WIDTH,
start.x, start.y,
end.x, end.y );
// draw filled inner rect
//
// where are we going to draw ... taking into account the clipping
// region of the edge of the listbox
//
start.x = x + 1;
start.y = drawY + 1;
end.x = start.x + width - 2;
end.y = start.y + listLineHeight - 2;
if( end.y > clipRegion.hi.y )
end.y = clipRegion.hi.y;
if( start.y < clipRegion.lo.y )
start.y = clipRegion.lo.y;
if( selectColor != WIN_COLOR_UNDEFINED )
TheWindowManager->winFillRect( selectColor,
WIN_DRAW_LINE_WIDTH,
start.x, start.y,
end.x, end.y );
} // end else, draw selection with colors
} // end if
Color dropColor = TheWindowManager->winMakeColor( 0, 0, 0, 255 );
DisplayString *string;
ListEntryCell *cells = list->listData[i].cell;
Int columnX = x;
IRegion2D columnRegion;
if( cells )
{
// loop through all the cells
for( Int j = 0; j < list->columns; j++ )
{
// setup the Clip Region size
columnRegion.lo.x = columnX;
columnRegion.lo.y = drawY;
if(list->columns == 1 && list->slider && list->slider->winIsHidden())
columnRegion.hi.x = columnX + width-3;
else
columnRegion.hi.x = columnX + list->columnWidth[j];
columnRegion.hi.y = drawY + list->listData[i].height;
if(columnRegion.lo.y < clipRegion.lo.y )
columnRegion.lo.y = clipRegion.lo.y;
if( columnRegion.hi.y > clipRegion.hi.y )
columnRegion.hi.y = clipRegion.hi.y;
// Display the Text Case;
if(cells[j].cellType == LISTBOX_TEXT)
{
textColor = cells[j].color;
string = (DisplayString *)cells[j].data;
if( BitTest( window->winGetStatus(), WIN_STATUS_ONE_LINE ) == TRUE )
{
string->setWordWrap(0);
// make sure the font of the text is the same as the windows
if( string->getFont() != window->winGetFont() )
string->setFont( window->winGetFont() );
// draw this text after setting the clip region for it
string->setClipRegion( &columnRegion );
string->draw( columnX + TEXT_X_OFFSET,
drawY,
textColor,
dropColor );
}
else
{
// make sure the font of the text is the same as the windows
if( string->getFont() != window->winGetFont() )
string->setFont( window->winGetFont() );
// set clip region and draw
string->setClipRegion( &columnRegion );
string->draw( columnX + TEXT_X_OFFSET,
drawY,
textColor,
dropColor );
}//else
}// if
else if(cells[j].cellType == LISTBOX_IMAGE && cells[j].data)
{
Int width, height;
if (cells[j].width > 0)
width = cells[j].width;
else
width = list->columnWidth[j];
if(cells[j].height > 0)
height = cells[j].height;
else
height = list->listData[i].height;
if(j == 0)
width--;
Int offsetX,offsetY;
if(width < list->columnWidth[j])
offsetX = columnX + ((list->columnWidth[j] - width) / 2);
else
offsetX = columnX;
if(height < list->listData[i].height)
offsetY = drawY + ((list->listData[i].height - height) / 2);
else
offsetY = drawY;
offsetY++;
if(offsetX <x+1)
offsetX = x+1;
TheDisplay->setClipRegion( &columnRegion );
TheWindowManager->winDrawImage( (const Image *)cells[j].data,
offsetX, offsetY,
offsetX + width, offsetY + height,cells[j].color );
}//else
columnX = columnX + list->columnWidth[j];
}// for
}//if
drawY += listLineHeight;
TheDisplay->enableClipping(FALSE);
}
// TheWindowManager->winSetClipRegion( clipRegion.lo.x, clipRegion.lo.y,
// clipRegion.hi.x, clipRegion.hi.y );
} // end drawListBoxText
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetListBoxDraw =======================================================
/** Draw colored list box using standard graphics */
//=============================================================================
void W3DGadgetListBoxDraw( GameWindow *window, WinInstanceData *instData )
{
Int width, height, fontHeight, x, y;
Color background, border, titleColor, titleBorder;
ListboxData *list = (ListboxData *)window->winGetUserData();
ICoord2D size;
DisplayString *title = instData->getTextDisplayString();
// get window position and size
window->winGetScreenPosition( &x, &y );
window->winGetSize( &size.x, &size.y );
// get font height
fontHeight = TheWindowManager->winFontHeight( instData->getFont() );
// alias width and height from size
width = size.x;
height = size.y;
// get the right colors
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
background = GadgetListBoxGetDisabledColor( window );
border = GadgetListBoxGetDisabledBorderColor( window );
titleColor = window->winGetDisabledTextColor();
titleBorder = window->winGetDisabledTextBorderColor();
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
background = GadgetListBoxGetHiliteColor( window );
border = GadgetListBoxGetHiliteBorderColor( window );
titleColor = window->winGetHiliteTextColor();
titleBorder = window->winGetHiliteTextBorderColor();
} // end else if, hilited
else
{
background = GadgetListBoxGetEnabledColor( window );
border = GadgetListBoxGetEnabledBorderColor( window );
titleColor = window->winGetEnabledTextColor();
titleBorder = window->winGetEnabledTextBorderColor();
} // end else, enabled
// Draw the title
if( title && title->getTextLength() )
{
// set the font of this text to that of the window if not already
if( title->getFont() != window->winGetFont() )
title->setFont( window->winGetFont() );
// draw the text
title->draw( x + 1, y, titleColor, titleBorder );
y += fontHeight + 1;
height -= fontHeight + 1;
} // end if
// draw the back border
if( border != WIN_COLOR_UNDEFINED )
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
x, y, x + width, y + height );
// draw background
if( background != WIN_COLOR_UNDEFINED )
TheWindowManager->winFillRect( background, WIN_DRAW_LINE_WIDTH,
x + 1, y + 1,
x + width - 1, y + height - 1 );
// If ScrollBar was requested ... adjust width.
if( list->slider && !list->slider->winIsHidden())
{
ICoord2D sliderSize;
list->slider->winGetSize( &sliderSize.x, &sliderSize.y );
width -= (sliderSize.x +3);
} // end if
// draw the text
drawListBoxText( window, instData, x, y + 4 , width, height-4, TRUE );
} // end W3DGadgetListBoxDraw
// W3DGadgetListBoxImageDraw ==================================================
/** Draw list box with user supplied images */
//=============================================================================
void W3DGadgetListBoxImageDraw( GameWindow *window, WinInstanceData *instData )
{
Int width, height, x, y;
const Image *image;
ListboxData *list = (ListboxData *)window->winGetUserData();
ICoord2D size;
Color titleColor, titleBorder;
DisplayString *title = instData->getTextDisplayString();
// get window position and size
window->winGetScreenPosition( &x, &y );
window->winGetSize( &size.x, &size.y );
// save off width and height so we can change them
width = size.x;
height = size.y;
// If ScrollBar was requested ... adjust width.
if( list->slider )
{
ICoord2D sliderSize;
list->slider->winGetSize( &sliderSize.x, &sliderSize.y );
width -= sliderSize.x;
} // end if
// get the image
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
image = GadgetListBoxGetDisabledImage( window );
titleColor = window->winGetDisabledTextColor();
titleBorder = window->winGetDisabledTextBorderColor();
}
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
image = GadgetListBoxGetHiliteImage( window );
titleColor = window->winGetHiliteTextColor();
titleBorder = window->winGetHiliteTextBorderColor();
}
else
{
image = GadgetListBoxGetEnabledImage( window );
titleColor = window->winGetEnabledTextColor();
titleBorder = window->winGetEnabledTextBorderColor();
}
// draw the back image
if( image )
{
ICoord2D start, end;
start.x = x + instData->m_imageOffset.x;
start.y = y + instData->m_imageOffset.y;
end.x = start.x + width;
end.y = start.y + height;
TheWindowManager->winDrawImage( image,
start.x, start.y,
end.x, end.y );
} // end if
// Draw the title
if( title && title->getTextLength() )
{
// set font to font of the window if not already
if( title->getFont() != window->winGetFont() )
title->setFont( window->winGetFont() );
// draw the text
title->draw( x + 1, y, titleColor, titleBorder );
y += TheWindowManager->winFontHeight( instData->getFont() );
height -= TheWindowManager->winFontHeight( instData->getFont() ) + 1;
} // end if
// draw the listbox text
drawListBoxText( window, instData, x, y+4, width, height-4, TRUE );
} // end W3DGadgetListBoxImageDraw

View file

@ -0,0 +1,417 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DProgressBar.cpp ///////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DProgressBar.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation for the progress bar GUI control
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetProgressBar.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetProgressBarDraw ===================================================
/** Draw colored Progress Bar using standard graphics */
//=============================================================================
void W3DGadgetProgressBarDraw( GameWindow *window, WinInstanceData *instData )
{
ICoord2D origin, size, start, end;
Color backColor, backBorder, barColor, barBorder;
Int progress = (Int)window->winGetUserData();
// get window size and position
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the right colors to use
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
backColor = GadgetProgressBarGetDisabledColor( window );
backBorder = GadgetProgressBarGetDisabledBorderColor( window );
barColor = GadgetProgressBarGetDisabledBarColor( window );
barBorder = GadgetProgressBarGetDisabledBarBorderColor( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
backColor = GadgetProgressBarGetHiliteColor( window );
backBorder = GadgetProgressBarGetHiliteBorderColor( window );
barColor = GadgetProgressBarGetHiliteBarColor( window );
barBorder = GadgetProgressBarGetHiliteBarBorderColor( window );
} // end else if, hilited
else
{
backColor = GadgetProgressBarGetEnabledColor( window );
backBorder = GadgetProgressBarGetEnabledBorderColor( window );
barColor = GadgetProgressBarGetEnabledBarColor( window );
barBorder = GadgetProgressBarGetEnabledBarBorderColor( window );
} // end else, enabled
// draw background border
if( backBorder != WIN_COLOR_UNDEFINED )
{
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winOpenRect( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
// draw background fill
if( backColor != WIN_COLOR_UNDEFINED )
{
start.x = origin.x + 1;
start.y = origin.y + 1;
end.x = start.x + size.x - 2;
end.y = start.y + size.y - 2;
TheWindowManager->winFillRect( backColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
// draw the progress so far
if( progress )
{
// draw bar border
if( barBorder != WIN_COLOR_UNDEFINED )
{
start.x = origin.x;
start.y = origin.y;
end.x = start.x + (size.x * progress) / 100;
end.y = start.y + size.y;
if(end.x- start.x > 1 )
{
TheWindowManager->winOpenRect( barBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
}
} // end if
// draw bar fill
if( barColor != WIN_COLOR_UNDEFINED )
{
start.x = origin.x + 1;
start.y = origin.y + 1;
end.x = start.x + (size.x * progress) / 100 - 2;
end.y = start.y + size.y - 2;
// TheWindowManager->winOpenRect( barColor, WIN_DRAW_LINE_WIDTH,
// start.x, start.y, end.x, end.y );
if(end.x- start.x > 1 )
{
TheWindowManager->winFillRect( barColor,WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
TheWindowManager->winDrawLine(GameMakeColor(255,255,255,255),WIN_DRAW_LINE_WIDTH, start.x, start.y, end.x, start.y);
TheWindowManager->winDrawLine(GameMakeColor(200,200,200,255),WIN_DRAW_LINE_WIDTH, start.x, start.y, start.x, end.y);
}
} // end if
} // end if
} // end W3DGadgetProgressBarDraw
// W3DGadgetProgressBarImageDraw ==============================================
/** Draw Progress Bar with user supplied images */
//=============================================================================
void W3DGadgetProgressBarImageDrawA( GameWindow *window, WinInstanceData *instData )
{
ICoord2D origin, size;
const Image *barCenter, *barRight, *left, *right, *center;
Int progress = (Int)window->winGetUserData();
Int xOffset, yOffset;
Int i;
// get window size and position
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
barCenter = GadgetProgressBarGetEnabledBarImageCenter( window );
barRight = GadgetProgressBarGetEnabledBarImageRight( window );
left = GadgetProgressBarGetEnabledImageLeft( window );
right = GadgetProgressBarGetEnabledImageRight( window );
center = GadgetProgressBarGetEnabledImageCenter( window );
if(!barCenter || !barRight || !left || !right || !center)
return;
Int width = barCenter->getImageWidth();
// Int height = barCenter->getImageHeight();
Int drawWidth = (size.x * progress) / 100;
Int pieces = drawWidth / width;
Int x = origin.x;
for( i = 0; i < pieces; i ++)
{
TheWindowManager->winDrawImage( barCenter,
x, origin.y,
x + width, origin.y + size.y );
x += width;
}
}
void W3DGadgetProgressBarImageDraw( GameWindow *window, WinInstanceData *instData )
{
ICoord2D origin, size, start, end;
const Image *backLeft, *backRight, *backCenter,
*barRight, *barCenter;//*backSmallCenter,*barLeft,, *barSmallCenter;
Int progress = (Int)window->winGetUserData();
Int xOffset, yOffset;
Int i;
// get window size and position
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
// get the right images to use
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
backLeft = GadgetProgressBarGetDisabledImageLeft( window );
//barLeft = GadgetProgressBarGetDisabledBarImageLeft( window );
backRight = GadgetProgressBarGetDisabledImageRight( window );
barRight = GadgetProgressBarGetDisabledBarImageRight( window );
backCenter = GadgetProgressBarGetDisabledImageCenter( window );
barCenter = GadgetProgressBarGetDisabledBarImageCenter( window );
//backSmallCenter = GadgetProgressBarGetDisabledImageSmallCenter( window );
//barSmallCenter = GadgetProgressBarGetDisabledBarImageSmallCenter( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
backLeft = GadgetProgressBarGetHiliteImageLeft( window );
//barLeft = GadgetProgressBarGetHiliteBarImageLeft( window );
backRight = GadgetProgressBarGetHiliteImageRight( window );
barRight = GadgetProgressBarGetHiliteBarImageRight( window );
backCenter = GadgetProgressBarGetHiliteImageCenter( window );
barCenter = GadgetProgressBarGetHiliteBarImageCenter( window );
//backSmallCenter = GadgetProgressBarGetHiliteImageSmallCenter( window );
//barSmallCenter = GadgetProgressBarGetHiliteBarImageSmallCenter( window );
} // end else if, hilited
else
{
backLeft = GadgetProgressBarGetEnabledImageLeft( window );
//barLeft = GadgetProgressBarGetEnabledBarImageLeft( window );
backRight = GadgetProgressBarGetEnabledImageRight( window );
barRight = GadgetProgressBarGetEnabledBarImageRight( window );
backCenter = GadgetProgressBarGetEnabledImageCenter( window );
barCenter = GadgetProgressBarGetEnabledBarImageCenter( window );
//backSmallCenter = GadgetProgressBarGetEnabledImageSmallCenter( window );
//barSmallCenter = GadgetProgressBarGetEnabledBarImageSmallCenter( window );
} // end else, enabled
// sanity
if( backLeft == NULL || backRight == NULL ||
backCenter == NULL ||
barRight == NULL)
// backSmallCenter == NULL ||barLeft == NULL ||barCenter == NULL || barSmallCenter == NULL )
return;
// get image sizes for the ends
ICoord2D leftSize, rightSize;
leftSize.x = backLeft->getImageWidth();
leftSize.y = backLeft->getImageHeight();
rightSize.x = backRight->getImageWidth();
rightSize.y = backRight->getImageHeight();
// get two key points used in the end drawing
ICoord2D leftEnd, rightStart;
leftEnd.x = origin.x + leftSize.x + xOffset;
leftEnd.y = origin.y + size.y + yOffset;
rightStart.x = origin.x + size.x - rightSize.x + xOffset;
rightStart.y = origin.y + yOffset;
// draw the center repeating bar
Int centerWidth, pieces;
// get width we have to draw our repeating center in
centerWidth = rightStart.x - leftEnd.x;
// how many whole repeating pieces will fit in that width
pieces = centerWidth / backCenter->getImageWidth();
// draw the pieces
start.x = leftEnd.x;
start.y = origin.y + yOffset;
end.y = start.y + size.y;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + backCenter->getImageWidth();
TheWindowManager->winDrawImage( backCenter,
start.x, start.y,
end.x, end.y );
start.x += backCenter->getImageWidth();
} // end for i
//
// how many small repeating pieces will fit in the gap from where the
// center repeating bar stopped and the right image, draw them
// and overlapping underneath where the right end will go
//
// centerWidth = rightStart.x - start.x;
// pieces = centerWidth / backCenter->getImageWidth() + 1;
// end.y = start.y + size.y;
// IRegion2D clipRegion;
//
// TheDisplay->setClipRegion()
// for( i = 0; i < pieces; i++ )
// {
//
// end.x = start.x + backCenter->getImageWidth();
// TheWindowManager->winDrawImage( backCenter,
// start.x, start.y,
// end.x, end.y );
// start.x += backCenter->getImageWidth();
//
// } // end for i
//
IRegion2D reg;
reg.lo.x = start.x;
reg.lo.y = start.y;
reg.hi.x = rightStart.x;
reg.hi.y = end.y;
centerWidth = rightStart.x - start.x;
if( centerWidth > 0)
{
TheDisplay->setClipRegion(&reg);
end.x = start.x + backCenter->getImageWidth();
TheWindowManager->winDrawImage( backCenter,
start.x, start.y,
end.x, end.y );
TheDisplay->enableClipping(FALSE);
}
// draw left end
start.x = origin.x + xOffset;
start.y = origin.y + yOffset;
end = leftEnd;
TheWindowManager->winDrawImage(backLeft, start.x, start.y, end.x, end.y);
// draw right end
start = rightStart;
end.x = start.x + rightSize.x;
end.y = start.y + size.y;
TheWindowManager->winDrawImage(backRight, start.x, start.y, end.x, end.y);
ICoord2D barWindowSize; // end point of bar from window origin
barWindowSize.x = ((size.x - 20) * progress) / 100;
barWindowSize.y = size.y;
pieces = barWindowSize.x / barCenter->getImageWidth();
// draw the pieces
start.x = origin.x +10;
start.y = origin.y + yOffset +5;
end.y = start.y + size.y - 10;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + barCenter->getImageWidth();
TheWindowManager->winDrawImage( barCenter,
start.x, start.y,
end.x, end.y );
start.x += barCenter->getImageWidth();
} // end for i
start.x = origin.x + 10 + barCenter->getImageWidth() * pieces;
//pieces = (size.x - barWindowSize.x -20) / barRight->getImageWidth();
//Changed By Saad for flashing grey piece
pieces = ((size.x - 20) / barCenter->getImageWidth()) - pieces;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + barRight->getImageWidth();
TheWindowManager->winDrawImage( barRight,
start.x, start.y,
end.x, end.y );
start.x += barRight->getImageWidth();
} // end for i
} // end W3DGadgetProgressBarImageDraw

View file

@ -0,0 +1,697 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DPushButton.cpp ////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DPushButton.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation for the push button control element
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/Gadget.h"
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetPushButton.h"
#include "GameClient/Display.h"
#include "W3DDevice/GameClient/W3DGameWindow.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
void W3DGadgetPushButtonImageDrawThree(GameWindow *window, WinInstanceData *instData );
void W3DGadgetPushButtonImageDrawOne(GameWindow *window, WinInstanceData *instData );
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
// drawButtonText =============================================================
/** Draw button text to the screen */
//=============================================================================
static void drawButtonText( GameWindow *window, WinInstanceData *instData )
{
ICoord2D origin, size, textPos;
Int width, height;
Color textColor, dropColor;
DisplayString *text = instData->getTextDisplayString();
// sanity
if( text == NULL || text->getTextLength() == 0 )
return;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// set whether or not we center the wrapped text
text->setWordWrapCentered( BitTest( instData->getStatus(), WIN_STATUS_WRAP_CENTERED ));
text->setWordWrap(size.x);
// get the right text color
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
textColor = window->winGetDisabledTextColor();
dropColor = window->winGetDisabledTextBorderColor();
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
textColor = window->winGetHiliteTextColor();
dropColor = window->winGetHiliteTextBorderColor();
} // end else if, hilited
else
{
textColor = window->winGetEnabledTextColor();
dropColor = window->winGetEnabledTextBorderColor();
} // end enabled only
// set our font to that of our parent if not the same
if( text->getFont() != window->winGetFont() )
text->setFont( window->winGetFont() );
// get text size
text->getSize( &width, &height );
// where to draw
textPos.x = origin.x + (size.x / 2) - (width / 2);
textPos.y = origin.y + (size.y / 2) - (height / 2);
// draw it
text->draw( textPos.x, textPos.y, textColor, dropColor );
} // end drawButtonText
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetPushButtonDraw ====================================================
/** Draw colored pushbutton using standard graphics */
//=============================================================================
void W3DGadgetPushButtonDraw( GameWindow *window, WinInstanceData *instData )
{
Color color, border;
ICoord2D origin, size, start, end;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
//
// get pointer to image we want to draw depending on our state,
// see GadgetPushButton.h for info
//
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
color = GadgetButtonGetDisabledSelectedColor( window );
border = GadgetButtonGetDisabledSelectedBorderColor( window );
}
else
{
color = GadgetButtonGetDisabledColor( window );
border = GadgetButtonGetDisabledBorderColor( window );
}
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
color = GadgetButtonGetHiliteSelectedColor( window );
border = GadgetButtonGetHiliteSelectedBorderColor( window );
}
else
{
color = GadgetButtonGetHiliteColor( window );
border = GadgetButtonGetHiliteBorderColor( window );
}
} // end else if, hilited and enabled
else
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
color = GadgetButtonGetEnabledSelectedColor( window );
border = GadgetButtonGetEnabledSelectedBorderColor( window );
}
else
{
color = GadgetButtonGetEnabledColor( window );
border = GadgetButtonGetEnabledBorderColor( window );
}
} // end else, enabled only
// compute draw position
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
if( color != WIN_COLOR_UNDEFINED )
{
// draw inside border
start.x++;
start.y++;
end.x--;
end.y--;
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
// draw the button text
if( instData->getTextLength() )
drawButtonText( window, instData );
// if we have a video buffer, draw the video buffer
if ( instData->m_videoBuffer )
{
TheDisplay->drawVideoBuffer( instData->m_videoBuffer, origin.x, origin.y, origin.x + size.x, origin.y + size.y );
}
PushButtonData *pData = (PushButtonData *)window->winGetUserData();
if( pData )
{
if( pData->overlayImage )
{
//Render the overlay image now.
TheDisplay->drawImage( pData->overlayImage, origin.x, origin.y, origin.x + size.x, origin.y + size.y );
}
if( pData->drawClock )
{
if( pData->drawClock == NORMAL_CLOCK )
{
TheDisplay->drawRectClock(origin.x, origin.y, size.x, size.y, pData->percentClock,pData->colorClock);
}
else if( pData->drawClock == INVERSE_CLOCK )
{
TheDisplay->drawRemainingRectClock( origin.x, origin.y, size.x, size.y, pData->percentClock,pData->colorClock );
}
pData->drawClock = NO_CLOCK;
window->winSetUserData(pData);
}
if( pData->drawBorder && pData->colorBorder != GAME_COLOR_UNDEFINED )
{
TheDisplay->drawOpenRect(origin.x -1, origin.y - 1, size.x + 2, size.y + 2,1 , pData->colorBorder);
}
}
} // end W3DGadgetPushButtonDraw
// W3DGadgetPushButtonImageDraw ===============================================
/** Draw pushbutton with user supplied images */
//=============================================================================
void W3DGadgetPushButtonImageDraw( GameWindow *window,
WinInstanceData *instData )
{
// if we return NULL then we'll call the one picture drawing code, if we return a value
// then we'll call the 3 picture drawing code
if( GadgetButtonGetMiddleEnabledImage( window ) )
{
if( BitTest( instData->getState(), WIN_STATUS_USE_OVERLAY_STATES ) )
{
ICoord2D size, start;
// get window position
window->winGetScreenPosition( &start.x, &start.y );
window->winGetSize( &size.x, &size.y );
// offset position by image offset
start.x += instData->m_imageOffset.x;
start.y += instData->m_imageOffset.y;
DEBUG_CRASH( ("Button at %d,%d is attempting to render with W3DGadgetPushButtonImageDrawThree(), but is using overlay states! Forcing the code to use W3DGadgetPushButtonImageDrawOne() instead.", start.x, start.y ) );
W3DGadgetPushButtonImageDrawOne( window, instData );
}
else
{
W3DGadgetPushButtonImageDrawThree( window, instData );
}
}
else
{
W3DGadgetPushButtonImageDrawOne( window, instData );
}
}
void W3DGadgetPushButtonImageDrawOne( GameWindow *window,
WinInstanceData *instData )
{
const Image *image = NULL;
ICoord2D size, start, end;
//
// get pointer to image we want to draw depending on our state,
// see GadgetPushButton.h for info
//
image = GadgetButtonGetEnabledImage( window );
if( !BitTest( window->winGetStatus(), WIN_STATUS_USE_OVERLAY_STATES ) )
{
//Certain buttons have the option to specify specific images for
//altered states. If they do, then we won't render the auto-overlay versions.
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
image = GadgetButtonGetDisabledSelectedImage( window );
else
image = GadgetButtonGetDisabledImage( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
image = GadgetButtonGetHiliteSelectedImage( window );
else
image = GadgetButtonGetHiliteImage( window );
} // end else if, hilited and enabled
else
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
image = GadgetButtonGetHiliteSelectedImage( window );
} // end else, enabled only
}
// draw the image
if( image )
{
// get window position
window->winGetScreenPosition( &start.x, &start.y );
window->winGetSize( &size.x, &size.y );
// offset position by image offset
start.x += instData->m_imageOffset.x;
start.y += instData->m_imageOffset.y;
// find end point
end.x = start.x + size.x;
end.y = start.y + size.y;
Display::DrawImageMode drawMode=Display::DRAW_IMAGE_ALPHA;
Int colorMultiplier = 0xffffffff;
if(BitTest( window->winGetStatus(), WIN_STATUS_USE_OVERLAY_STATES ) )
{
//we're using a new drawing system which does "grayscale" disabled buttons using original color artwork.
if( !BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) )
{
if( !BitTest( window->winGetStatus(), WIN_STATUS_NOT_READY ) )
{
//The button is disabled -- but if the button isn't "ready", we don't want to do this because
//we want to show the button in color with just the clock overlay.
if( !BitTest( window->winGetStatus(), WIN_STATUS_ALWAYS_COLOR ) )
{
drawMode=Display::DRAW_IMAGE_GRAYSCALE;
}
else
{
colorMultiplier = 0xff909090; //RGB values are 144/255 (90) -- Alpha is opaque (ff) --> ff909090;
}
}
}
}
TheDisplay->drawImage( image, start.x, start.y, end.x, end.y, colorMultiplier, drawMode );
} // end if
// draw the button text
if( instData->getTextLength() )
drawButtonText( window, instData );
// get window position
window->winGetScreenPosition( &start.x, &start.y );
window->winGetSize( &size.x, &size.y );
// if we have a video buffer, draw the video buffer
if ( instData->m_videoBuffer )
{
TheDisplay->drawVideoBuffer( instData->m_videoBuffer, start.x, start.y, start.x + size.x, start.y + size.y );
}
PushButtonData *pData = (PushButtonData *)window->winGetUserData();
if( pData )
{
if( pData->overlayImage )
{
//Render the overlay image now.
TheDisplay->drawImage( pData->overlayImage, start.x, start.y, start.x + size.x, start.y + size.y );
}
if( pData->drawClock )
{
if( pData->drawClock == NORMAL_CLOCK )
{
TheDisplay->drawRectClock(start.x, start.y, size.x, size.y, pData->percentClock,pData->colorClock);
}
else if( pData->drawClock == INVERSE_CLOCK )
{
TheDisplay->drawRemainingRectClock( start.x, start.y, size.x, size.y, pData->percentClock,pData->colorClock );
}
pData->drawClock = NO_CLOCK;
window->winSetUserData(pData);
}
if( pData->drawBorder && pData->colorBorder != GAME_COLOR_UNDEFINED )
{
TheDisplay->drawOpenRect(start.x - 1, start.y - 1, size.x + 2, size.y + 2, 1, pData->colorBorder);
}
}
//Now render overlays that pertain to the correct state.
if( BitTest( window->winGetStatus(), WIN_STATUS_FLASHING ) )
{
//Handle cameo flashing (let the flashing stack with overlay states)
static const Image *hilitedOverlayIcon = TheMappedImageCollection->findImageByName( "Cameo_push" );
TheDisplay->drawImage( hilitedOverlayIcon, start.x, start.y, start.x + size.x, start.y + size.y );
}
if( BitTest( window->winGetStatus(), WIN_STATUS_USE_OVERLAY_STATES ) )
{
image = NULL;
static const Image *pushedOverlayIcon = TheMappedImageCollection->findImageByName( "Cameo_push" );
static const Image *hilitedOverlayIcon = TheMappedImageCollection->findImageByName( "Cameo_hilited" );
if( pushedOverlayIcon && hilitedOverlayIcon )
{
if(BitTest(window->winGetStatus(), WIN_STATUS_ENABLED))
{
if (BitTest( instData->getState(), WIN_STATE_HILITED ))
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
//The button is hilited and pushed
TheDisplay->drawImage( pushedOverlayIcon, start.x, start.y, start.x + size.x, start.y + size.y );
}
else
{
//The button is hilited
TheDisplay->drawImage( hilitedOverlayIcon, start.x, start.y, start.x + size.x, start.y + size.y );
}
}
else if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
//The button appears to be pushed -- CHECK_LIKE buttons that are on.
TheDisplay->drawImage( pushedOverlayIcon, start.x, start.y, start.x + size.x, start.y + size.y );
}
}
}
}
} // end W3DGadgetPushButtonImageDraw
void W3DGadgetPushButtonImageDrawThree(GameWindow *window, WinInstanceData *instData )
{
const Image *leftImage, *rightImage, *centerImage;
ICoord2D origin, size, start, end;
Int xOffset, yOffset;
Int i;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get image offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
//
// get pointer to image we want to draw depending on our state,
// see GadgetPushButton.h for info
//
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
leftImage = GadgetButtonGetLeftDisabledSelectedImage( window );
rightImage = GadgetButtonGetRightDisabledSelectedImage( window );
centerImage = GadgetButtonGetMiddleDisabledSelectedImage( window );
}
else
{
leftImage = GadgetButtonGetLeftDisabledImage( window );
rightImage = GadgetButtonGetRightDisabledImage( window );
centerImage = GadgetButtonGetMiddleDisabledImage( window );
}
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
leftImage = GadgetButtonGetLeftHiliteSelectedImage( window );
rightImage = GadgetButtonGetRightHiliteSelectedImage( window );
centerImage = GadgetButtonGetMiddleHiliteSelectedImage( window );
}
else
{
leftImage = GadgetButtonGetLeftHiliteImage( window );
rightImage = GadgetButtonGetRightHiliteImage( window );
centerImage = GadgetButtonGetMiddleHiliteImage( window );
}
} // end else if, hilited and enabled
else
{
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
leftImage = GadgetButtonGetLeftEnabledSelectedImage( window );
rightImage = GadgetButtonGetRightEnabledSelectedImage( window );
centerImage = GadgetButtonGetMiddleEnabledSelectedImage( window );
}
else
{
leftImage = GadgetButtonGetLeftEnabledImage( window );
rightImage = GadgetButtonGetRightEnabledImage( window );
centerImage = GadgetButtonGetMiddleEnabledImage( window );
}
} // end else, enabled only
// sanity, we need to have these images to make it look right
if( leftImage == NULL || rightImage == NULL ||
centerImage == NULL )
return;
// get image sizes for the ends
ICoord2D leftSize, rightSize;
leftSize.x = leftImage->getImageWidth();
leftSize.y = leftImage->getImageHeight();
rightSize.x = rightImage->getImageWidth();
rightSize.y = rightImage->getImageHeight();
// get two key points used in the end drawing
ICoord2D leftEnd, rightStart;
leftEnd.x = origin.x + leftSize.x + xOffset;
leftEnd.y = origin.y + size.y + yOffset;
rightStart.x = origin.x + size.x - rightSize.x + xOffset;
rightStart.y = origin.y + yOffset;
// draw the center repeating bar
Int centerWidth, pieces;
// get width we have to draw our repeating center in
centerWidth = rightStart.x - leftEnd.x;
if( centerWidth <= 0)
{
// draw left end
start.x = origin.x + xOffset;
start.y = origin.y + yOffset;
end.y = leftEnd.y;
end.x = origin.x + xOffset + size.x/2;
TheWindowManager->winDrawImage(leftImage, start.x, start.y, end.x, end.y);
// draw right end
start.y = rightStart.y;
start.x = end.x;
end.x = origin.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winDrawImage(rightImage, start.x, start.y, end.x, end.y);
}
else
{
// how many whole repeating pieces will fit in that width
pieces = centerWidth / centerImage->getImageWidth();
// draw the pieces
start.x = leftEnd.x;
start.y = origin.y + yOffset;
end.y = start.y + size.y + yOffset; //centerImage->getImageHeight() + yOffset;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + centerImage->getImageWidth();
TheWindowManager->winDrawImage( centerImage,
start.x, start.y,
end.x, end.y );
start.x += centerImage->getImageWidth();
} // end for i
// we will draw the image but clip the parts we don't want to show
IRegion2D reg;
reg.lo.x = start.x;
reg.lo.y = start.y;
reg.hi.x = rightStart.x;
reg.hi.y = end.y;
centerWidth = rightStart.x - start.x;
if( centerWidth > 0)
{
TheDisplay->setClipRegion(&reg);
end.x = start.x + centerImage->getImageWidth();
TheWindowManager->winDrawImage( centerImage,
start.x, start.y,
end.x, end.y );
TheDisplay->enableClipping(FALSE);
}
// draw left end
start.x = origin.x + xOffset;
start.y = origin.y + yOffset;
end = leftEnd;
TheWindowManager->winDrawImage(leftImage, start.x, start.y, end.x, end.y);
// draw right end
start = rightStart;
end.x = start.x + rightSize.x;
end.y = start.y + size.y;
TheWindowManager->winDrawImage(rightImage, start.x, start.y, end.x, end.y);
}
// draw the button text
if( instData->getTextLength() )
drawButtonText( window, instData );
// get window position
window->winGetScreenPosition( &start.x, &start.y );
window->winGetSize( &size.x, &size.y );
// if we have a video buffer, draw the video buffer
if ( instData->m_videoBuffer )
{
TheDisplay->drawVideoBuffer( instData->m_videoBuffer, start.x, start.y, start.x + size.x, start.y + size.y );
}
PushButtonData *pData = (PushButtonData *)window->winGetUserData();
if( pData )
{
if( pData->overlayImage )
{
//Render the overlay image now.
TheDisplay->drawImage( pData->overlayImage, origin.x, origin.y, origin.x + size.x, origin.y + size.y );
}
if( pData->drawClock )
{
if( pData->drawClock == NORMAL_CLOCK )
{
TheDisplay->drawRectClock(start.x, start.y, size.x, size.y, pData->percentClock,pData->colorClock);
}
else if( pData->drawClock == INVERSE_CLOCK )
{
TheDisplay->drawRemainingRectClock( start.x, start.y, size.x, size.y, pData->percentClock,pData->colorClock );
}
pData->drawClock = NO_CLOCK;
window->winSetUserData(pData);
}
if( pData->drawBorder && pData->colorBorder != GAME_COLOR_UNDEFINED )
{
TheDisplay->drawOpenRect(start.x - 1, start.y - 1, size.x + 2, size.y + 2, 1, pData->colorBorder);
}
}
}

View file

@ -0,0 +1,387 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DRadioButton.cpp ///////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DRadioButton.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D methods needed to implement the RadioButton UI control
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetRadioButton.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// drawRadioButtonText ========================================================
/** Draw the text for a RadioButton */
//=============================================================================
static void drawRadioButtonText( GameWindow *window, WinInstanceData *instData )
{
ICoord2D origin, size, textPos;
Int width, height;
Color textColor, dropColor;
DisplayString *text = instData->getTextDisplayString();
// sanity
if( text == NULL || text->getTextLength() == 0 )
return;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the right text color
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
textColor = window->winGetDisabledTextColor();
dropColor = window->winGetDisabledTextBorderColor();
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
textColor = window->winGetHiliteTextColor();
dropColor = window->winGetHiliteTextBorderColor();
} // end else if, hilited
else
{
textColor = window->winGetEnabledTextColor();
dropColor = window->winGetEnabledTextBorderColor();
} // end enabled only
// set our font to that of our parent if not the same
if( text->getFont() != window->winGetFont() )
text->setFont( window->winGetFont() );
// get text size
text->getSize( &width, &height );
// set the location for our text
textPos.x = origin.x + (size.x / 2) - (width / 2);
textPos.y = origin.y + (size.y / 2) - (height / 2);
// draw it
text->draw( textPos.x, textPos.y, textColor, dropColor );
} // end drawRadioButtonText
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetRadioButtonDraw ===================================================
/** Draw colored check box using standard graphics */
//=============================================================================
void W3DGadgetRadioButtonDraw( GameWindow *window, WinInstanceData *instData )
{
Int checkOffsetFromLeft;
Color backColor,
backBorder,
boxColor,
boxBorder;
ICoord2D origin, size, start, end;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// compute start of check offset
checkOffsetFromLeft = size.x / 16;
//
// get the colors we should be using to draw, see GadgetRadioButton.h
// draw appropriate state, see GadgetRadioButton.h for info
//
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
// disabled background
backColor = GadgetRadioGetDisabledColor( window );
backBorder = GadgetRadioGetDisabledBorderColor( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
boxColor = GadgetRadioGetDisabledCheckedBoxColor( window );
boxBorder = GadgetRadioGetDisabledCheckedBoxBorderColor( window );
}
else
{
boxColor = GadgetRadioGetDisabledUncheckedBoxColor( window );
boxBorder = GadgetRadioGetDisabledUncheckedBoxBorderColor( window );
}
} // end if
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
// hilited background
backColor = GadgetRadioGetHiliteColor( window );
backBorder = GadgetRadioGetHiliteBorderColor( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
boxColor = GadgetRadioGetHiliteCheckedBoxColor( window );
boxBorder = GadgetRadioGetHiliteCheckedBoxBorderColor( window );
}
else
{
boxColor = GadgetRadioGetHiliteUncheckedBoxColor( window );
boxBorder = GadgetRadioGetHiliteUncheckedBoxBorderColor( window );
}
} // end else if
else
{
// enabled background
backColor = GadgetRadioGetEnabledColor( window );
backBorder = GadgetRadioGetEnabledBorderColor( window );
// check box
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
boxColor = GadgetRadioGetEnabledCheckedBoxColor( window );
boxBorder = GadgetRadioGetEnabledCheckedBoxBorderColor( window );
}
else
{
boxColor = GadgetRadioGetEnabledUncheckedBoxColor( window );
boxBorder = GadgetRadioGetEnabledUncheckedBoxBorderColor( window );
}
} // end else
// draw background border
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winOpenRect( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw the background
start.x++;
start.y++;
end.x--;
end.y--;
TheWindowManager->winFillRect( backColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw box border
start.x = origin.x + size.y;
start.y = origin.y;
end.x = start.x;
end.y = start.y + size.y;
TheWindowManager->winDrawLine( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw box for button
start.x = origin.x + 1;
start.y = origin.y + 1;
end.x = origin.x + size.y -1;
end.y = origin.y + size.y -1;
TheWindowManager->winFillRect( boxColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw box border
start.x = origin.x + size.x - size.y;
start.y = origin.y;
end.x = start.x;
end.y = start.y + size.y;
TheWindowManager->winDrawLine( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw box for button
start.x = origin.x + size.x - size.y;
start.y = origin.y + 1;
end.x = origin.x + size.x -1;
end.y = origin.y + size.y -1;
TheWindowManager->winFillRect( boxColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
// draw the button text
if( instData->getTextLength() )
drawRadioButtonText( window, instData );
} // end W3DGadgetRadioButtonDraw
void W3DGadgetRadioButtonImageDraw( GameWindow *window,
WinInstanceData *instData )
{
const Image *leftImage, *rightImage, *centerImage;
ICoord2D origin, size, start, end;
Int xOffset, yOffset;
Int i;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
IRegion2D clipLeft;
// get image offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
if( BitTest( instData->getState(), WIN_STATE_SELECTED ) )
{
//backgroundImage = GadgetRadioGetEnabledCheckedBoxImage( window );
leftImage = GadgetRadioGetSelectedImage( window );
centerImage = GadgetRadioGetSelectedUncheckedBoxImage( window );
rightImage = GadgetRadioGetSelectedCheckedBoxImage( window );
}
else if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
// disabled background
leftImage = GadgetRadioGetDisabledImage( window );
centerImage = GadgetRadioGetDisabledUncheckedBoxImage( window );
rightImage = GadgetRadioGetDisabledCheckedBoxImage( window );
} // end if
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
// hilited background
leftImage = GadgetRadioGetHiliteImage( window );
centerImage = GadgetRadioGetHiliteUncheckedBoxImage( window );
rightImage = GadgetRadioGetHiliteCheckedBoxImage( window );
} // end else if
else
{
// enabled background
leftImage = GadgetRadioGetEnabledImage( window );
centerImage = GadgetRadioGetEnabledUncheckedBoxImage( window );
rightImage = GadgetRadioGetEnabledCheckedBoxImage( window );
} // end else
// sanity, we need to have these images to make it look right
if( leftImage == NULL || centerImage == NULL ||
rightImage == NULL )
return;
// get image sizes for the ends
ICoord2D leftSize, rightSize;
leftSize.x = leftImage->getImageWidth();
leftSize.y = leftImage->getImageHeight();
rightSize.x = rightImage->getImageWidth();
rightSize.y = rightImage->getImageHeight();
// get two key points used in the end drawing
ICoord2D leftEnd, rightStart;
leftEnd.x = origin.x + leftSize.x + xOffset;
leftEnd.y = origin.y + size.y + yOffset;
rightStart.x = origin.x + size.x - rightSize.x + xOffset;
rightStart.y = origin.y + size.y + yOffset;
// draw the center repeating bar
Int centerWidth, pieces;
// get width we have to draw our repeating center in
centerWidth = rightStart.x - leftEnd.x;
// how many whole repeating pieces will fit in that width
pieces = centerWidth / centerImage->getImageWidth();
pieces++;
// draw the pieces
start.x = leftEnd.x;
start.y = origin.y + yOffset;
end.y =origin.y + size.y + yOffset;
clipLeft.lo.x = leftEnd.x;
clipLeft.lo.y = origin.y;
clipLeft.hi.y = leftEnd.y;
clipLeft.hi.x = rightStart.x ;
TheDisplay->setClipRegion(&clipLeft);
for( i = 0; i < pieces; i++ )
{
end.x = start.x + centerImage->getImageWidth();
TheWindowManager->winDrawImage( centerImage,
start.x, start.y,
end.x, end.y );
start.x += centerImage->getImageWidth();
} // end for i
TheDisplay->enableClipping(FALSE);
// draw left end
start.x = origin.x + xOffset;
start.y = origin.y + yOffset;
end = leftEnd;
TheWindowManager->winDrawImage(leftImage, start.x, start.y, end.x, end.y);
// draw right end
start.x = rightStart.x;
start.y = origin.y + yOffset;
end.x = origin.x + size.x;
end.y = leftEnd.y;
TheWindowManager->winDrawImage(rightImage, start.x, start.y, end.x, end.y);
// draw the text
if( instData->getTextLength() )
drawRadioButtonText( window, instData );
} // end W3DGadgetHorizontalSliderImageDraw

View file

@ -0,0 +1,266 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DStaticText.cpp ////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DStaticText.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation of the static text GUI control
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/GlobalData.h"
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetStaticText.h"
#include "W3DDevice/GameClient/W3DGameWindow.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
//enum { DRAW_BUF_LEN = 2048 };
//static WideChar drawBuf[ DRAW_BUF_LEN ];
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// drawStaticTextText =========================================================
/** Draw the text for a static text window */
//=============================================================================
static void drawStaticTextText( GameWindow *window, WinInstanceData *instData,
Color textColor, Color textDropColor )
{
TextData *tData = (TextData *)window->winGetUserData();
Int textWidth, textHeight, wordWrap;
DisplayString *text = tData->text;
ICoord2D origin, size, textPos;
IRegion2D clipRegion;
// sanity
if( text == NULL || text->getTextLength() == 0 )
return;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// Set the text Wrap width
wordWrap = size.x - 10;
//if(wordWrap == 89)
// wordWrap = 95;
text->setWordWrap(wordWrap);
if( BitTest(window->winGetStatus(), WIN_STATUS_WRAP_CENTERED) )
text->setWordWrapCentered(TRUE);
else
text->setWordWrapCentered(FALSE);
if( BitTest( window->winGetStatus(), WIN_STATUS_HOTKEY_TEXT ) && TheGlobalData)
text->setUseHotkey(TRUE, TheGlobalData->m_hotKeyTextColor);
else
text->setUseHotkey(FALSE, 0);
// how much space will this text take up
text->getSize( &textWidth, &textHeight );
//Init the clip region
clipRegion.lo.x = origin.x ;
clipRegion.lo.y = origin.y ;
clipRegion.hi.x = origin.x + size.x ;
clipRegion.hi.y = origin.y + size.y;
if( tData->centered )
{
textPos.x = origin.x + (size.x / 2) - (textWidth / 2);
textPos.y = origin.y + (size.y / 2) - (textHeight / 2);
text->setClipRegion(&clipRegion);
text->draw( textPos.x, textPos.y, textColor, textDropColor );
} // end if
else
{
// draw the text
textPos.x = origin.x + 7;
textPos.y = origin.y + (size.y / 2) - (textHeight / 2);
text->setClipRegion(&clipRegion);
text->draw( textPos.x, textPos.y, textColor, textDropColor );
} // end else
} // end drawStaticTextText
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetStaticTextDraw ====================================================
/** Draw colored text field using standard graphics */
//=============================================================================
void W3DGadgetStaticTextDraw( GameWindow *window, WinInstanceData *instData )
{
TextData *tData = (TextData *)window->winGetUserData();
Color backColor, backBorder, textColor, textOutlineColor;
ICoord2D size, origin, start, end;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the colors we will use
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
backColor = GadgetStaticTextGetDisabledColor( window );
backBorder = GadgetStaticTextGetDisabledBorderColor( window );
textColor = window->winGetDisabledTextColor();
textOutlineColor = window->winGetDisabledTextBorderColor();
} // end if, disabled
else
{
backColor = GadgetStaticTextGetEnabledColor( window );
backBorder = GadgetStaticTextGetEnabledBorderColor( window );
textColor = window->winGetEnabledTextColor();
textOutlineColor = window->winGetEnabledTextBorderColor();
} // end else, enabled
// draw the back border
if( backBorder != WIN_COLOR_UNDEFINED )
{
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winOpenRect( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
// draw the back fill area
if( backColor != WIN_COLOR_UNDEFINED )
{
start.x = origin.x + 1;
start.y = origin.y + 1;
end.x = start.x + size.x - 2;
end.y = start.y + size.y - 2;
TheWindowManager->winFillRect( backColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
// draw the text
if( tData->text && (textColor != WIN_COLOR_UNDEFINED) )
drawStaticTextText( window, instData, textColor, textOutlineColor );
} // end W3DGadgetStaticTextDraw
// W3DGadgetStaticTextImageDraw ===============================================
/** Draw colored text field with user supplied images */
//=============================================================================
void W3DGadgetStaticTextImageDraw( GameWindow *window, WinInstanceData *instData )
{
TextData *tData = (TextData *)window->winGetUserData();
Color textColor, textOutlineColor;
ICoord2D size, origin, start, end;
const Image *image;
// get window position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the colors we will use
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
image = GadgetStaticTextGetDisabledImage( window );
textColor = window->winGetDisabledTextColor();
textOutlineColor = window->winGetDisabledTextBorderColor();
} // end if, disabled
else
{
image = GadgetStaticTextGetEnabledImage( window );
textColor = window->winGetEnabledTextColor();
textOutlineColor = window->winGetEnabledTextBorderColor();
} // end else, enabled
// draw the back image
if( image )
{
start.x = origin.x + instData->m_imageOffset.x;
start.y = origin.y + instData->m_imageOffset.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winDrawImage( image, start.x, start.y, end.x, end.y );
} // end if
// draw the text
if( tData->text && (textColor != WIN_COLOR_UNDEFINED) )
drawStaticTextText( window, instData, textColor, textOutlineColor );
} // end W3DGadgetStaticTextImageDraw

View file

@ -0,0 +1,652 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTabControl.cpp ///////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: projects\RTS\code\gameenginedevice\Source\W3DDevice\GameClient\GUI\Gadget\W3DTabControl.cpp
//
// Created: Graham Smallwood, November 2001
//
// Desc: W3D methods needed to implement the TabControl UI control
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetTabControl.h"
#include "W3DDevice/GameClient/W3DGameWindow.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetRadioButtonDraw ===================================================
/** Draw tabs with standard graphics */
//=============================================================================
void W3DGadgetTabControlDraw( GameWindow *tabControl, WinInstanceData *instData )
{
ICoord2D origin, size;
// get window position and size
tabControl->winGetScreenPosition( &origin.x, &origin.y );
tabControl->winGetSize( &size.x, &size.y );
W3DGameWinDefaultDraw(tabControl, instData);//draw the background
if( BitTest( tabControl->winGetStatus(), WIN_STATUS_BORDER ) == TRUE &&
!BitTest( tabControl->winGetStatus(), WIN_STATUS_SEE_THRU ) )
{//draw border if desired
tabControl->winDrawBorder();
}
TabControlData *tabData = (TabControlData *)tabControl->winGetUserData();
Int tabX, tabY, tabWidth, tabHeight, tabDeltaX, tabDeltaY;
tabX = origin.x + tabData->tabsLeftLimit;
tabY = origin.y + tabData->tabsTopLimit;
tabWidth = tabData->tabWidth;
tabHeight = tabData->tabHeight;
if( (tabData->tabEdge == TP_TOP_SIDE) || (tabData->tabEdge == TP_BOTTOM_SIDE) )
{
tabDeltaX = tabWidth;
tabDeltaY = 0;
}
else
{
tabDeltaX = 0;
tabDeltaY = tabHeight;
}
Color color, border;
if( tabData->tabCount >= 1 )//Does exist
{
if( tabData->subPaneDisabled[0] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabZero( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabZero( tabControl );
}
else if( tabData->activeTab == 0 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabZero( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabZero( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabZero( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabZero( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 2 )//Does exist
{
if( tabData->subPaneDisabled[1] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabOne( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabOne( tabControl );
}
else if( tabData->activeTab == 1 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabOne( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabOne( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabOne( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabOne( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 3 )//Does exist
{
if( tabData->subPaneDisabled[2] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabTwo( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabTwo( tabControl );
}
else if( tabData->activeTab == 2 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabTwo( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabTwo( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabTwo( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabTwo( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 4 )//Does exist
{
if( tabData->subPaneDisabled[3] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabThree( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabThree( tabControl );
}
else if( tabData->activeTab == 3 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabThree( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabThree( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabThree( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabThree( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 5 )//Does exist
{
if( tabData->subPaneDisabled[4] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabFour( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabFour( tabControl );
}
else if( tabData->activeTab == 4 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabFour( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabFour( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabFour( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabFour( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 6 )//Does exist
{
if( tabData->subPaneDisabled[5] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabFive( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabFive( tabControl );
}
else if( tabData->activeTab == 5 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabFive( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabFive( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabFive( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabFive( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 7 )//Doesn't exist
{
if( tabData->subPaneDisabled[6] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabSix( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabSix( tabControl );
}
else if( tabData->activeTab == 6 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabSix( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabSix( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabSix( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabSix( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 8 )//Doesn't exist
{
if( tabData->subPaneDisabled[7] )
{//Disabled
color = GadgetTabControlGetDisabledColorTabSeven( tabControl );
border = GadgetTabControlGetDisabledBorderColorTabSeven( tabControl );
}
else if( tabData->activeTab == 7 )
{//Hilited/Active
color = GadgetTabControlGetHiliteColorTabSeven( tabControl );
border = GadgetTabControlGetHiliteBorderColorTabSeven( tabControl );
}
else
{//Just enabled
color = GadgetTabControlGetEnabledColorTabSeven( tabControl );
border = GadgetTabControlGetEnabledBorderColorTabSeven( tabControl );
}
// box and border
if( border != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winOpenRect( border, WIN_DRAW_LINE_WIDTH,
tabX, tabY, tabX + tabWidth, tabY + tabHeight );
}
if( color != WIN_COLOR_UNDEFINED )
{
TheWindowManager->winFillRect( color, WIN_DRAW_LINE_WIDTH,
tabX + 1, tabY + 1, tabX + tabWidth - 1, tabY + tabHeight - 1 );
}
}
} // end W3DGadgetTabControlDraw
// W3DGadgetRadioButtonImageDraw ==============================================
/** Draw tabs with user supplied images */
//=============================================================================
void W3DGadgetTabControlImageDraw( GameWindow *tabControl,
WinInstanceData *instData )
{
ICoord2D origin, size;
// get window position and size
tabControl->winGetScreenPosition( &origin.x, &origin.y );
tabControl->winGetSize( &size.x, &size.y );
W3DGameWinDefaultDraw(tabControl, instData);//draw the background
if( BitTest( tabControl->winGetStatus(), WIN_STATUS_BORDER ) == TRUE &&
!BitTest( tabControl->winGetStatus(), WIN_STATUS_SEE_THRU ) )
{//draw border if desired
tabControl->winDrawBorder();
}
TabControlData *tabData = (TabControlData *)tabControl->winGetUserData();
Int tabX, tabY, tabWidth, tabHeight, tabDeltaX, tabDeltaY;
tabX = origin.x + tabData->tabsLeftLimit;
tabY = origin.y + tabData->tabsTopLimit;
tabWidth = tabData->tabWidth;
tabHeight = tabData->tabHeight;
if( (tabData->tabEdge == TP_TOP_SIDE) || (tabData->tabEdge == TP_BOTTOM_SIDE) )
{
tabDeltaX = tabWidth;
tabDeltaY = 0;
}
else
{
tabDeltaX = 0;
tabDeltaY = tabHeight;
}
const Image *image = NULL;
if( tabData->tabCount >= 1 )//Does exist
{
if( tabData->subPaneDisabled[0] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabZero( tabControl );
}
else if( tabData->activeTab == 0 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabZero( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabZero( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 2 )//Does exist
{
if( tabData->subPaneDisabled[1] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabOne( tabControl );
}
else if( tabData->activeTab == 1 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabOne( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabOne( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 3 )//Does exist
{
if( tabData->subPaneDisabled[2] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabTwo( tabControl );
}
else if( tabData->activeTab == 2 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabTwo( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabTwo( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 4 )//Does exist
{
if( tabData->subPaneDisabled[3] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabThree( tabControl );
}
else if( tabData->activeTab == 3 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabThree( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabThree( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 5 )//Does exist
{
if( tabData->subPaneDisabled[4] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabFour( tabControl );
}
else if( tabData->activeTab == 4 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabFour( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabFour( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 6 )//Does exist
{
if( tabData->subPaneDisabled[5] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabFive( tabControl );
}
else if( tabData->activeTab == 5 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabFive( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabFive( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 7 )//Doesn't exist
{
if( tabData->subPaneDisabled[6] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabSix( tabControl );
}
else if( tabData->activeTab == 6 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabSix( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabSix( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
tabX += tabDeltaX;
tabY += tabDeltaY;
if( tabData->tabCount >= 8 )//Doesn't exist
{
if( tabData->subPaneDisabled[7] )
{//Disabled
image = GadgetTabControlGetDisabledImageTabSeven( tabControl );
}
else if( tabData->activeTab == 7 )
{//Hilited/Active
image = GadgetTabControlGetHiliteImageTabSeven( tabControl );
}
else
{//Just enabled
image = GadgetTabControlGetEnabledImageTabSeven( tabControl );
}
if( image != NULL )
{
TheWindowManager->winDrawImage( image,
tabX,
tabY,
tabX + tabWidth,
tabY + tabHeight
);
}
}
} // end W3DGadgetTabControlImageDraw

View file

@ -0,0 +1,468 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTextEntry.cpp /////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DTextEntry.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation for the text entry gadget
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GadgetTextEntry.h"
#include "GameClient/IMEManager.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// drawTextEntryText ==========================================================
//=============================================================================
static void drawTextEntryText( GameWindow *window, WinInstanceData *instData,
Color textColor, Color textDropColor,
Color compositeColor, Color compositeDropColor,
Int x, Int y, Int width, Int fontHeight )
{
static Byte drawCnt = 0;
EntryData *e = (EntryData *)window->winGetUserData();
// Int charPos = e->charPos;
Int cursorPos;
// WideChar buffer[ ENTRY_TEXT_LEN + 1 ];
// WideChar *bufptr = buffer;
// Color constructColor = TheWindowManager->winMakeColor( 192, 0, 192, 255 );
DisplayString *text = e->text;
IRegion2D clipRegion;
ICoord2D origin, size;
Int compositeCursorPos = 0;
// Check to see if the IME manager is composing text
e->constructText->setText(UnicodeString::TheEmptyString);
if ( TheIMEManager && TheIMEManager->isAttachedTo( window) && TheIMEManager->isComposing())
{
// The user is composing a string.
// Show the composition in the text gadget.
UnicodeString composition;
TheIMEManager->getCompositionString( composition );
if ( e->secretText )
{
e->sText->setText( UnicodeString::TheEmptyString );
Int len = composition.getLength() + e->text->getTextLength();
for ( int i = 0; i < len; i++ )
{
e->sText->appendChar( '*' );
}
}
else
{
e->constructText->setText( composition );
compositeCursorPos = TheIMEManager->getCompositionCursorPosition();
}
}
// get out of here if no text color to show up
if( textColor == WIN_COLOR_UNDEFINED )
return;
// if our text is "secret" we will print only '*' characters
if( e->secretText )
text = e->sText;
// make sure our font is the same as our parents
if( text->getFont() != window->winGetFont() )
text->setFont( window->winGetFont() );
if( e->constructText->getFont() != window->winGetFont() )
e->constructText->setFont( window->winGetFont() );
// get the size of our text, and construct text
Int textWidth = text->getWidth();
if (!e->drawTextFromStart)
{
// clip the text to the edit window size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
clipRegion.lo.x = x;
clipRegion.hi.x = x + width ;
clipRegion.lo.y = y;
clipRegion.hi.y = y + fontHeight;
text->setClipRegion( &clipRegion );
e->constructText->setClipRegion( &clipRegion );
// set construct window position if needed
//if( e->constructList && e->constructText->getTextLength() )
// e->constructList->winSetPosition( (x + textWidth1), (y + fontHeight) );
x+= 2;
// draw the text
if(textWidth < width)
{
text->draw( x, y, textColor, textDropColor );
cursorPos = textWidth + x;
}
else
{
Int div = textWidth / (width / 2) - 1;
text->draw(x - (div * (width/2)), y, textColor, textDropColor);
cursorPos = textWidth - (div * (width/2)) + x;
}
//cursorPos = x + textWidth;
}
else
{
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
clipRegion.lo.x = origin.x;
clipRegion.hi.x = origin.x + size.x;
clipRegion.lo.y = origin.y;
clipRegion.hi.y = origin.y + size.y;
text->setClipRegion( &clipRegion );
e->constructText->setClipRegion( &clipRegion );
// set construct window position if needed
//if( e->constructList && e->constructText->getTextLength() )
// e->constructList->winSetPosition( (x + textWidth1), (y + fontHeight) );
x+= 5;
// draw the text
text->draw( x, y, textColor, textDropColor );
cursorPos = textWidth + x;
}
if (e->constructText->getTextLength() > 0 )
{
e->constructText->draw( x + textWidth, y, compositeColor, compositeDropColor );
cursorPos += e->constructText->getWidth( compositeCursorPos );
}
// draw blinking cursor
GameWindow *parent;
parent = window->winGetParent();
if(parent && !BitTest(parent->winGetStyle(), GWS_COMBO_BOX))
parent = NULL;
if( (window == TheWindowManager->winGetFocus() || (parent && parent == TheWindowManager->winGetFocus())) && ((drawCnt++ >> 3) & 0x1) )
TheWindowManager->winFillRect( textColor, WIN_DRAW_LINE_WIDTH,
cursorPos, origin.y + 3,
cursorPos + 2, origin.y + size.y - 3 );
window->winSetCursorPosition( cursorPos + 2 - origin.x, 0 );
} // end drawTextEntryText
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetTextEntryDraw =====================================================
/** Draw colored entry field using standard graphics */
//=============================================================================
void W3DGadgetTextEntryDraw( GameWindow *window, WinInstanceData *instData )
{
EntryData *e = (EntryData *)window->winGetUserData();
ICoord2D origin, size, start, end;
Color backBorder, backColor, textColor, textBorder,
compositeColor, compositeBorder;
// cancel unichar flag
e->receivedUnichar = FALSE;
// get size and position of window
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the right colors
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
compositeColor = window->winGetDisabledTextColor();
compositeBorder = window->winGetDisabledTextBorderColor();
textColor = window->winGetDisabledTextColor();
textBorder = window->winGetDisabledTextBorderColor();
backColor = GadgetTextEntryGetDisabledColor( window );
backBorder = GadgetTextEntryGetDisabledBorderColor( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
compositeColor = window->winGetIMECompositeTextColor();
compositeBorder = window->winGetIMECompositeBorderColor();
textColor = window->winGetHiliteTextColor();
textBorder = window->winGetHiliteTextBorderColor();
backColor = GadgetTextEntryGetHiliteColor( window );
backBorder = GadgetTextEntryGetHiliteBorderColor( window );
} // end else if, hilited
else
{
compositeColor = window->winGetIMECompositeTextColor();
compositeBorder = window->winGetIMECompositeBorderColor();
textColor = window->winGetEnabledTextColor();
textBorder = window->winGetEnabledTextBorderColor();
backColor = GadgetTextEntryGetEnabledColor( window );
backBorder = GadgetTextEntryGetEnabledBorderColor( window );
} // end else, just enabled
// draw the back border
if( backBorder != WIN_COLOR_UNDEFINED )
{
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winOpenRect( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
// draw the filled back
if( backColor != WIN_COLOR_UNDEFINED )
{
start.x = origin.x + 1;
start.y = origin.y + 1;
end.x = start.x + size.x - 2;
end.y = start.y + size.y - 2;
TheWindowManager->winFillRect( backColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
// draw the text
Int fontHeight = TheWindowManager->winFontHeight( instData->getFont() );
Int startOffset = 5;
Int width;
width = size.x - (2 * startOffset);
start.x = origin.x + startOffset; // offset a little bit into the entry
if( BitTest( window->winGetStatus(), WIN_STATUS_ONE_LINE ) )
start.y = size.y / 2 - fontHeight / 2;
else
start.y = origin.y + startOffset; // offset a little bit into the entry
// draw the edit text
drawTextEntryText( window, instData, textColor, textBorder, compositeColor, compositeBorder,
start.x, start.y, width, fontHeight );
} // end W3DGadgetTextEntryDraw
// W3DGadgetTextEntryImageDraw ================================================
/** Draw horizontal slider with user supplied images */
//=============================================================================
void W3DGadgetTextEntryImageDraw( GameWindow *window, WinInstanceData *instData )
{
EntryData *e = (EntryData *)window->winGetUserData();
ICoord2D origin, size, start, end;
Color textColor, textBorder;
Color compositeColor, compositeBorder;
const Image *leftImage, *rightImage, *centerImage, *smallCenterImage;
Int xOffset, yOffset;
Int i;
// cancel unichar flag
e->receivedUnichar = FALSE;
// get size and position of window
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get image offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
// get the right colors
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
textColor = window->winGetDisabledTextColor();
textBorder = window->winGetDisabledTextBorderColor();
compositeColor = window->winGetDisabledTextColor();
compositeBorder = window->winGetDisabledTextBorderColor();
leftImage = GadgetTextEntryGetDisabledImageLeft( window );
rightImage = GadgetTextEntryGetDisabledImageRight( window );
centerImage = GadgetTextEntryGetDisabledImageCenter( window );
smallCenterImage = GadgetTextEntryGetDisabledImageSmallCenter( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
textColor = window->winGetHiliteTextColor();
textBorder = window->winGetHiliteTextBorderColor();
compositeColor = window->winGetIMECompositeTextColor();
compositeBorder = window->winGetIMECompositeBorderColor();
leftImage = GadgetTextEntryGetHiliteImageLeft( window );
rightImage = GadgetTextEntryGetHiliteImageRight( window );
centerImage = GadgetTextEntryGetHiliteImageCenter( window );
smallCenterImage = GadgetTextEntryGetHiliteImageSmallCenter( window );
} // end else if, hilited
else
{
textColor = window->winGetEnabledTextColor();
textBorder = window->winGetEnabledTextBorderColor();
compositeColor = window->winGetIMECompositeTextColor();
compositeBorder = window->winGetIMECompositeBorderColor();
leftImage = GadgetTextEntryGetEnabledImageLeft( window );
rightImage = GadgetTextEntryGetEnabledImageRight( window );
centerImage = GadgetTextEntryGetEnabledImageCenter( window );
smallCenterImage = GadgetTextEntryGetEnabledImageSmallCenter( window );
} // end else, just enabled
// get image sizes for the ends
ICoord2D leftSize, rightSize;
leftSize.x = leftImage->getImageWidth();
leftSize.y = leftImage->getImageHeight();
rightSize.x = rightImage->getImageWidth();
rightSize.y = rightImage->getImageHeight();
// get two key points used in the end drawing
ICoord2D leftEnd, rightStart;
leftEnd.x = origin.x + leftSize.x + xOffset;
leftEnd.y = origin.y + size.y + yOffset;
rightStart.x = origin.x + size.x - rightSize.x + xOffset;
rightStart.y = origin.y + yOffset;
// draw the center repeating bar
Int centerWidth, pieces;
// get width we have to draw our repeating center in
centerWidth = rightStart.x - leftEnd.x;
// how many whole repeating pieces will fit in that width
pieces = centerWidth / centerImage->getImageWidth();
// draw the pieces
start.x = leftEnd.x;
start.y = origin.y + yOffset;
end.y = start.y + size.y;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + centerImage->getImageWidth();
TheWindowManager->winDrawImage( centerImage,
start.x, start.y,
end.x, end.y );
start.x += centerImage->getImageWidth();
} // end for i
//
// how many small repeating pieces will fit in the gap from where the
// center repeating bar stopped and the right image, draw them
// and overlapping underneath where the right end will go
//
centerWidth = rightStart.x - start.x;
pieces = centerWidth / smallCenterImage->getImageWidth() + 1;
end.y = start.y + size.y;
for( i = 0; i < pieces; i++ )
{
end.x = start.x + smallCenterImage->getImageWidth();
TheWindowManager->winDrawImage( smallCenterImage,
start.x, start.y,
end.x, end.y );
start.x += smallCenterImage->getImageWidth();
} // end for i
// draw left end
start.x = origin.x + xOffset;
start.y = origin.y + yOffset;
end = leftEnd;
TheWindowManager->winDrawImage(leftImage, start.x, start.y, end.x, end.y);
// draw right end
start = rightStart;
end.x = start.x + rightSize.x;
end.y = start.y + size.y;
TheWindowManager->winDrawImage(rightImage, start.x, start.y, end.x, end.y);
// draw the text
Int fontHeight = TheWindowManager->winFontHeight( instData->getFont() );
Int startOffset = 5;
Int width;
width = size.x - (2 * startOffset);
start.x = origin.x + startOffset; // offset a little bit into the entry
if( BitTest( window->winGetStatus(), WIN_STATUS_ONE_LINE ) )
start.y = size.y / 2 - fontHeight / 2;
else
start.y = origin.y + startOffset; // offset a little bit into the entry
// draw the edit text
drawTextEntryText( window, instData, textColor, textBorder, compositeColor, compositeBorder,
start.x, start.y, width, fontHeight );
} // end W3DGadgetTextEntryImageDraw

View file

@ -0,0 +1,281 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: .cpp /////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project:
//
// File name: .cpp
//
// Created:
//
// Desc:
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GadgetSlider.h"
#include "GameClient/GameWindowGlobal.h"
#include "GameClient/GameWindowManager.h"
#include "W3DDevice/GameClient/W3DGadget.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGadgetVerticalSliderDraw ================================================
/** Draw colored vertical slider using standard graphics */
//=============================================================================
void W3DGadgetVerticalSliderDraw( GameWindow *window,
WinInstanceData *instData )
{
Color backBorder, backColor;
ICoord2D origin, size, start, end;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get the right colors
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
backBorder = GadgetSliderGetDisabledBorderColor( window );
backColor = GadgetSliderGetDisabledColor( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
backBorder = GadgetSliderGetHiliteBorderColor( window );
backColor = GadgetSliderGetHiliteColor( window );
} // end else if, hilited
else
{
backBorder = GadgetSliderGetEnabledBorderColor( window );
backColor = GadgetSliderGetEnabledColor( window );
} // end else, enabled
// draw background border and rect over whole control
if( backBorder != WIN_COLOR_UNDEFINED )
{
start.x = origin.x;
start.y = origin.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winOpenRect( backBorder, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
if( backColor != WIN_COLOR_UNDEFINED )
{
start.x = origin.x + 1;
start.y = origin.y + 1;
end.x = start.x + size.x - 2;
end.y = start.y + size.y - 2;
TheWindowManager->winFillRect( backColor, WIN_DRAW_LINE_WIDTH,
start.x, start.y, end.x, end.y );
} // end if
} // end W3DGadgetVerticalSliderDraw
// W3DGadgetVerticalSliderImageDraw ===========================================
/** Draw vertical slider with user supplied images */
//=============================================================================
void W3DGadgetVerticalSliderImageDraw( GameWindow *window,
WinInstanceData *instData )
{
const Image *topImage, *bottomImage, *centerImage, *smallCenterImage;
ICoord2D origin, size, start, end;
Int xOffset, yOffset;
Int i;
// get screen position and size
window->winGetScreenPosition( &origin.x, &origin.y );
window->winGetSize( &size.x, &size.y );
// get image offset
xOffset = instData->m_imageOffset.x;
yOffset = instData->m_imageOffset.y;
// get the right images
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
topImage = GadgetSliderGetDisabledImageTop( window );
bottomImage = GadgetSliderGetDisabledImageBottom( window );
centerImage = GadgetSliderGetDisabledImageCenter( window );
smallCenterImage = GadgetSliderGetDisabledImageSmallCenter( window );
} // end if, disabled
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
topImage = GadgetSliderGetHiliteImageTop( window );
bottomImage = GadgetSliderGetHiliteImageBottom( window );
centerImage = GadgetSliderGetHiliteImageCenter( window );
smallCenterImage = GadgetSliderGetHiliteImageSmallCenter( window );
} // end else if, hilited
else
{
topImage = GadgetSliderGetEnabledImageTop( window );
bottomImage = GadgetSliderGetEnabledImageBottom( window );
centerImage = GadgetSliderGetEnabledImageCenter( window );
smallCenterImage = GadgetSliderGetEnabledImageSmallCenter( window );
} // end else, enabled
// sanity, we need to have these images to make it look right
if( topImage == NULL || bottomImage == NULL ||
centerImage == NULL || smallCenterImage == NULL )
return;
// get image sizes for the ends
ICoord2D topSize, bottomSize;
topSize.x = topImage->getImageWidth();
topSize.y = topImage->getImageHeight();
bottomSize.x = bottomImage->getImageWidth();
bottomSize.y = bottomImage->getImageHeight();
if(topSize.y + bottomSize.y >= size.y)
{
// draw top end
start.x = origin.x + xOffset;
start.y = origin.y + yOffset;
end.x = origin.x + xOffset + topSize.x;
end.y = origin.y + size.y /2;
TheWindowManager->winDrawImage(topImage, start.x, start.y, end.x, end.y);
// draw bottom end
start.y = origin.y + size.y /2;
end.x = origin.x + xOffset + bottomSize.x;
end.y = origin.y + yOffset + size.y;
TheWindowManager->winDrawImage(bottomImage, start.x, start.y, end.x, end.y);
}
else
{
// get two key points used in the end drawing
ICoord2D topEnd, bottomStart;
topEnd.x = origin.x + topSize.x + xOffset;
topEnd.y = origin.y + topSize.y + yOffset;
bottomStart.x = origin.x + xOffset;
bottomStart.y = origin.y + size.y - bottomSize.y + yOffset;
// draw the center repeating bar
Int centerHeight, pieces;
// get size we have to draw our repeating center in
centerHeight = bottomStart.y - topEnd.y;
// how many whole repeating pieces will fit in that size
pieces = centerHeight / centerImage->getImageHeight();
// draw the pieces
start.x = origin.x + xOffset;
start.y = topEnd.y;
end.x = start.x + centerImage->getImageWidth();
end.y = start.y + centerImage->getImageHeight();
for( i = 0; i < pieces; i++ )
{
TheWindowManager->winDrawImage( centerImage,
start.x, start.y,
end.x, end.y );
start.y += centerImage->getImageHeight();
end.y += centerImage->getImageHeight();
} // end for i
//
// how many small repeating pieces will fit in the gap from where the
// center repeating bar stopped and the bottom image, draw them
// and overlapping underneath where the bottom end will go
//
centerHeight = bottomStart.y - start.y;
pieces = centerHeight / smallCenterImage->getImageHeight() + 1;
end.y = start.y + smallCenterImage->getImageHeight();
for( i = 0; i < pieces; i++ )
{
TheWindowManager->winDrawImage( smallCenterImage,
start.x, start.y,
end.x, end.y );
start.y += smallCenterImage->getImageHeight();
end.y += smallCenterImage->getImageHeight();
} // end for i
// draw top end
start.x = origin.x + xOffset;
start.y = origin.y + yOffset;
end = topEnd;
TheWindowManager->winDrawImage(topImage, start.x, start.y, end.x, end.y);
// draw bottom end
start = bottomStart;
end.x = start.x + bottomSize.x;
end.y = start.y + bottomSize.y;
TheWindowManager->winDrawImage(bottomImage, start.x, start.y, end.x, end.y);
}
} // end W3DGadgetVerticalSliderImageDraw

View file

@ -0,0 +1,146 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DGameFont.cpp //////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DGameFont.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation for managing font definitions
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/Debug.h"
#include "W3DDevice/GameClient/W3DGameFont.h"
#include "WW3D2/WW3D.h"
#include "WW3D2/AssetMgr.h"
#include "WW3D2/Render2DSentence.h"
#include "GameClient/GlobalLanguage.h"
// DEFINES ////////////////////////////////////////////////////////////////////
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DFontLibrary::loadFontData ===============================================
/** Load a font */
//=============================================================================
Bool W3DFontLibrary::loadFontData( GameFont *font )
{
FontCharsClass *fontChar;
// sanity
if( font == NULL )
return FALSE;
if ((UnsignedInt)font->pointSize > 100) //sanity check the size - anything over 100 is probably wrong. -MW
fontChar = NULL;
else
{ // get the font data from the asset manager
fontChar = WW3DAssetManager::
Get_Instance()->Get_FontChars( font->nameString.str(), font->pointSize,
font->bold ? true : false );
}
if( fontChar == NULL )
{
DEBUG_LOG(( "W3D load font: unable to find font '%s' from asset manager\n",
font->nameString.str() ));
DEBUG_ASSERTCRASH(fontChar, ("Missing or Corrupted Font. Pleas see log for details"));
return FALSE;
} // end if
// assign font data
font->fontData = fontChar;
font->height = fontChar->Get_Char_Height();
FontCharsClass *unicodeFontChar = NULL;
// load unicode of same point size
if(TheGlobalLanguageData)
unicodeFontChar = WW3DAssetManager::
Get_Instance()->Get_FontChars( TheGlobalLanguageData->m_unicodeFontName.str(), font->pointSize,
font->bold ? true : false );
else
unicodeFontChar = WW3DAssetManager::
Get_Instance()->Get_FontChars( "Arial Unicode MS", font->pointSize,
font->bold ? true : false );
if ( unicodeFontChar )
{
fontChar->AlternateUnicodeFont = unicodeFontChar;
}
// all done and loaded
return TRUE;
} // end loadFont
// W3DFontLibrary::releaseFontData ============================================
/** Release font data */
//=============================================================================
void W3DFontLibrary::releaseFontData( GameFont *font )
{
// presently we don't need to do anything because fonts are handled in
// the W3D asset manager which is all taken for of us
if (font && font->fontData)
{
if(((FontCharsClass *)(font->fontData))->AlternateUnicodeFont)
((FontCharsClass *)(font->fontData))->AlternateUnicodeFont->Release_Ref();
((FontCharsClass *)(font->fontData))->Release_Ref();
}
font->fontData = NULL;
} // end releaseFont
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////

View file

@ -0,0 +1,663 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DGameWindow.cpp ////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DGameWindow.cpp
//
// Created: Colin Day, June 2001
//
// Desc: W3D implementation of a game window
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/Gadget.h"
#include "GameClient/GameWindowGlobal.h"
#include "W3DDevice/GameClient/W3DGameWindow.h"
#include "W3DDevice/GameClient/W3DGameWindowManager.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
// DEFINES ////////////////////////////////////////////////////////////////////
enum
{
BORDER_CORNER_SIZE = 15,
BORDER_LINE_SIZE = 20,
};
// PRIVATE TYPES //////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static Bool bordersInit = FALSE;
static const Image *borderPieces[NUM_BORDER_PIECES] = { 0 };
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// initBorders ================================================================
//=============================================================================
static void initBorders( void )
{
borderPieces[ BORDER_CORNER_UL ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerUL" ) );
borderPieces[ BORDER_CORNER_UR ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerUR" ) );
borderPieces[ BORDER_CORNER_LL ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerLL" ) );
borderPieces[ BORDER_CORNER_LR ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderCornerLR" ) );
borderPieces[ BORDER_VERTICAL_LEFT ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderLeft" ) );
borderPieces[ BORDER_VERTICAL_LEFT_SHORT ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderLeftShort" ) );
borderPieces[ BORDER_HORIZONTAL_TOP ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderTop" ) );
borderPieces[ BORDER_HORIZONTAL_TOP_SHORT ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderTopShort" ) );
borderPieces[ BORDER_VERTICAL_RIGHT ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderRight" ) );
borderPieces[ BORDER_VERTICAL_RIGHT_SHORT ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderRightShort" ) );
borderPieces[ BORDER_HORIZONTAL_BOTTOM ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderBottom" ) );
borderPieces[ BORDER_HORIZONTAL_BOTTOM_SHORT ] =
TheMappedImageCollection->findImageByName( AsciiString( "BorderBottomShort" ) );
bordersInit = TRUE;
}
// W3DGameWindow::blitBorderRect ==============================================
//=============================================================================
void W3DGameWindow::blitBorderRect( Int x, Int y, Int width, Int height )
{
Int Offset = 15;
Int OffsetLower = 5;
// init image loc if needed
if( bordersInit == FALSE )
initBorders();
// save original x, y
Int originalX = x;
Int originalY = y;
Int maxX = x + width;
Int maxY = y + height;
Int x2, y2; // used for simultaneous drawing of line pairs
Int size = 20;
Int halfSize = size / 2;
// Draw Horizontal Lines
// All border pieces are based on a 10 pixel offset from the centerline
y = originalY - Offset;
y2 = maxY - OffsetLower;
x2 = maxX - (OffsetLower + BORDER_LINE_SIZE);
for( x=(originalX + OffsetLower); x <= x2; x += BORDER_LINE_SIZE )
{
TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_TOP ],
x, y, x + size, y + size );
TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_BOTTOM ],
x, y2, x + size, y2 + size );
}
x2 = maxX - 5;//BORDER_CORNER_SIZE;
// x == place to draw remainder if any
if( (x2 - x) >= (BORDER_LINE_SIZE / 2) )
{
//Blit Half piece
TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_TOP_SHORT ],
x, y, x + halfSize, y + size );
TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_BOTTOM_SHORT ],
x, y2, x + halfSize, y2 + size );
x += (BORDER_LINE_SIZE / 2);
}
// x2 - x ... must now be less than a half piece
// check for equals and if not blit an adjusted half piece border pieces have
// a two pixel repeat so we will blit one pixel over if necessary to line up
// the art, but we'll cover-up the overlap with the corners
if( x < x2 )
{
x -= ((BORDER_LINE_SIZE / 2) - (((x2 - x) + 1) & ~1));
//Blit Half piece
TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_TOP_SHORT ],
x, y, x + halfSize, y + size );
TheDisplay->drawImage( borderPieces[ BORDER_HORIZONTAL_BOTTOM_SHORT ],
x, y2, x + halfSize, y2 + size );
}
// Draw Vertical Lines
// All border pieces are based on a 10 pixel offset from the centerline
x = originalX - Offset;
x2 = maxX - OffsetLower;
y2 = maxY - (OffsetLower + BORDER_LINE_SIZE);
for( y=(originalY + OffsetLower); y <= y2; y += BORDER_LINE_SIZE )
{
TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_LEFT ],
x, y, x + size, y + size );
TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_RIGHT ],
x2, y, x2 + size, y + size );
}
y2 = maxY - OffsetLower;//BORDER_CORNER_SIZE;
// y == place to draw remainder if any
if( (y2 - y) >= (BORDER_LINE_SIZE / 2) )
{
//Blit Half piece
TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_LEFT_SHORT ],
x, y, x + size, y + halfSize );
TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_RIGHT_SHORT ],
x2, y, x2 + size, y + halfSize );
y += (BORDER_LINE_SIZE / 2);
}
// y2 - y ... must now be less than a half piece
// check for equals and if not blit an adjusted half piece border pieces have
// a two pixel repeat so we will blit one pixel over if necessary to line up
// the art, but we'll cover-up the overlap with the corners
if( y < y2 )
{
y -= ((BORDER_LINE_SIZE / 2) - (((y2 - y) + 1) & ~1));
//Blit Half piece
TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_LEFT_SHORT ],
x, y, x + size, y + halfSize );
TheDisplay->drawImage( borderPieces[ BORDER_VERTICAL_RIGHT_SHORT ],
x2, y, x2 + size, y + halfSize );
}
// Draw Corners
x = originalX - BORDER_CORNER_SIZE ;
y = originalY - BORDER_CORNER_SIZE;
TheDisplay->drawImage( borderPieces[ BORDER_CORNER_UL ],
x, y, x + size, y + size );
x = maxX - 5;//BORDER_CORNER_SIZE;
y = originalY - BORDER_CORNER_SIZE;
TheDisplay->drawImage( borderPieces[ BORDER_CORNER_UR ],
x, y, x + size, y + size );
x = originalX - BORDER_CORNER_SIZE;
y = maxY - 5;//BORDER_CORNER_SIZE;
TheDisplay->drawImage( borderPieces[ BORDER_CORNER_LL ],
x, y, x + size, y + size );
x = maxX - 5;//BORDER_CORNER_SIZE;
y = maxY - 5;//BORDER_CORNER_SIZE;
TheDisplay->drawImage( borderPieces[ BORDER_CORNER_LR ],
x, y, x + size, y + size );
} // end blitBorderRect
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DGameWindow::W3DGameWindow ===============================================
//=============================================================================
W3DGameWindow::W3DGameWindow( void )
{
// override the default draw with our own default draw function for W3D
winSetDrawFunc( TheWindowManager->getDefaultDraw() );
m_textPos.x = m_textPos.y = 0;
m_currTextColor = WIN_COLOR_UNDEFINED;
m_needPolyDraw = FALSE;
m_newTextPos = FALSE;
} // end W3DGameWindow
// W3DGameWindow::~W3DGameWindow ==============================================
//=============================================================================
W3DGameWindow::~W3DGameWindow( void )
{
} // end ~W3DGameWindow
// W3DGameWinDefaultDraw ======================================================
/** The default redraw callback. Draws the background using either
* the drawData or the background color unless the background color
* is set to -1 indicating that default drawing is turned off. */
//=============================================================================
void W3DGameWinDefaultDraw( GameWindow *window, WinInstanceData *instData )
{
Real borderWidth = 1.0f;
ICoord2D origin;
ICoord2D size;
W3DGameWindow *w3dWindow = (W3DGameWindow *)window;
/** @todo NOTE that we're making a W3DGameWindow cast here, it seems
logical because we are in a W3D draw function so it's reasonable to assume
that we have a W3DGameWindow. However, we may want to revisit this
type of casting in the future, we have the same problems with the
ObjectModules where we cast object modules for their individual methods.
Also note that the other W3D implementations of GUI controls are making
a similar cast for their device implementation functions */
// get the window position in the screen coordinates
w3dWindow->winGetScreenPosition( &origin.x, &origin.y );
// get size of window
w3dWindow->winGetSize( &size.x, &size.y );
// image drawing vs color drawing
if( BitTest( window->winGetStatus(), WIN_STATUS_IMAGE ) )
{
const Image *image;
// get image
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
image = window->winGetDisabledImage( 0 );
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
image = window->winGetHiliteImage( 0 );
else
image = window->winGetEnabledImage( 0 );
if( image )
{
ICoord2D start, end;
start.x = origin.x + instData->m_imageOffset.x;
start.y = origin.y + instData->m_imageOffset.y;
end.x = start.x + size.x;
end.y = start.y + size.y;
TheWindowManager->winDrawImage( image, start.x, start.y, end.x, end.y );
} // end if
} // end if
else
{
Color color, borderColor;
// get colors
if( BitTest( window->winGetStatus(), WIN_STATUS_ENABLED ) == FALSE )
{
color = window->winGetDisabledColor( 0 );
borderColor = window->winGetDisabledBorderColor( 0 );
} // end if
else if( BitTest( instData->getState(), WIN_STATE_HILITED ) )
{
color = window->winGetHiliteColor( 0 );
borderColor = window->winGetHiliteBorderColor( 0 );
} // end else if
else
{
color = window->winGetEnabledColor( 0 );
borderColor = window->winGetEnabledBorderColor( 0 );
} // end else
//
// draw the border at the edges
//
if( borderColor != WIN_COLOR_UNDEFINED )
TheWindowManager->winOpenRect( borderColor, borderWidth,
origin.x, origin.y,
origin.x + size.x, origin.y + size.y );
// draw filled background
if( color != WIN_COLOR_UNDEFINED )
TheWindowManager->winFillRect( color, borderWidth,
origin.x + borderWidth,
origin.y + borderWidth,
origin.x + size.x - borderWidth,
origin.y + size.y - borderWidth );
} // end else
// if we have a video buffer, draw the video buffer
if ( instData->m_videoBuffer )
{
ICoord2D pos, size;
window->winGetScreenPosition( &pos.x, &pos.y );
window->winGetSize( &size.x, &size.y );
TheDisplay->drawVideoBuffer( instData->m_videoBuffer, pos.x, pos.y, pos.x + size.x, pos.y + size.y );
}
} // end W3DGameWinDefaultDraw
// W3DGameWindow::winDrawBorder ===============================================
//=============================================================================
void W3DGameWindow::winDrawBorder( void )
{
Bool found = FALSE;
Int originalX, originalY;
Int x, y;
Int width;
Int i, bits;
/** @todo this WinDrawBorder is the old Nox function for drawing the borders
* on various windows and controls. We should derive classes of game
* windows for the different GUI controls and move the specific pieces of
* code that apply for those gadgets to those classes */
// based on window class pass different regions to real draw
winGetScreenPosition( &originalX, &originalY );
for( i = 0; (i < (sizeof(UnsignedInt) * 8)) && (found == FALSE); i++ )
{
bits = (1 << i);
if( m_instData.getStyle() & bits )
{
switch( m_instData.getStyle() & bits )
{
case GWS_CHECK_BOX:
found = TRUE;
break;
case GWS_ENTRY_FIELD:
{
// EntryData *e = (EntryData *)m_userData;
width = m_size.x;
x = originalX;
y = originalY;
// Calculate space for Label
if( m_instData.getTextLength() )
{
Int textWidth = 0;
TheWindowManager->winGetTextSize( m_instData.getFont(),
m_instData.getText(),
&textWidth, NULL, 0 );
width -= textWidth + 6;
x += textWidth + 6;
}
/*
// Colin: The very notion of entry width makes no sense to me since
// we already have a gadget width, and the max characters for
// an entry box so I am removing this.
// Adjust entry box if an entryWidth is provided
if( (e->entryWidth > 0) && (width > e->entryWidth) )
{
width = e->entryWidth;
x = originalX + (m_size.x - e->entryWidth);
}
*/
blitBorderRect( x, y, width, m_size.y );
found = TRUE;
break;
}
// Sliders may need a bar for the button
// it is assumed that the smaller size component (width, height)
// is the axis of the slider
case GWS_VERT_SLIDER:
case GWS_HORZ_SLIDER:
// blitBorderLine( originalX, originalY, window->size.x, window->size.y );
found = TRUE;
break;
case GWS_SCROLL_LISTBOX:
{
ListboxData *list = (ListboxData *)m_userData;
Int sliderAdjustment = 0;
Int labelAdjustment = 0;
if( list->scrollBar )
{
GameWindow *child = list->slider->winGetChild();
ICoord2D size;
child->winGetSize( &size.x, &size.y );
sliderAdjustment = size.y;
} // end if
if( m_instData.getTextLength() )
labelAdjustment = 4;
blitBorderRect( (originalX - 3),
(originalY - (3 + labelAdjustment)),
(m_size.x + 3 - sliderAdjustment),
(m_size.y + 6) );
found = TRUE;
break;
}
case GWS_RADIO_BUTTON:
case GWS_STATIC_TEXT:
case GWS_PROGRESS_BAR:
case GWS_PUSH_BUTTON:
case GWS_USER_WINDOW:
case GWS_TAB_CONTROL:
blitBorderRect( originalX, originalY, m_size.x, m_size.y );
found = TRUE;
break;
} // end switch
} // end if
} // end for i
} // end WinDrawBorder
// W3DGameWindow::winSetFont ==================================================
/** Set the font for a widow */
//=============================================================================
void W3DGameWindow::winSetFont( GameFont *font )
{
// extending functionality
GameWindow::winSetFont( font );
// assign font to text renderer
m_textRenderer.Set_Font( static_cast<FontCharsClass *>(font->fontData) );
// this is a visual change
m_needPolyDraw = TRUE;
} // end WinSetFont
// W3DGameWindow::winSetText ==================================================
/** Set the text for window */
//=============================================================================
Int W3DGameWindow::winSetText( UnicodeString newText )
{
// extending functionality
GameWindow::winSetText( newText );
// rebuild the sentence in our text renderer
m_textRenderer.Build_Sentence( m_instData.getText().str(),NULL, NULL );
// this is a visual change
m_needPolyDraw = TRUE;
return WIN_ERR_OK;
} // end WinSetText
// W3DGameWindow::winSetPosition ==============================================
/** Set window position */
//=============================================================================
Int W3DGameWindow::winSetPosition( Int x, Int y )
{
ICoord2D prevPos;
// get previous position
prevPos.x = m_region.lo.x;
prevPos.y = m_region.lo.y;
// extending functionality
GameWindow::winSetPosition( x, y );
// update any text position change
m_textPos.x += m_region.lo.x - prevPos.x;
m_textPos.y += m_region.lo.y - prevPos.y;
m_newTextPos = TRUE;
return WIN_ERR_OK;
} // end WinSetPosition
// W3DGameWindow::getTextSize =================================================
/** Get the size of the text in our inst data */
//=============================================================================
void W3DGameWindow::getTextSize( Int *width, Int *height )
{
Vector2 extents = m_textRenderer.Get_Text_Extents( m_instData.getText().str() );
if( width )
*width = extents.X;
if( height )
*height = extents.Y;
} // end getTextSize
// W3DGameWindow::getTextLoc ==================================================
// Set our text rendering location */
//=============================================================================
void W3DGameWindow::setTextLoc( Int x, Int y )
{
if( m_textPos.x != x )
{
m_textPos.x = x;
m_newTextPos = TRUE;
}
if( m_textPos.y != y )
{
m_textPos.y = y;
m_newTextPos = TRUE;
} // end if
} // end setTextLoc
// W3DGameWindow::drawText ====================================================
/** Draw the text in our 2d sentence renderer */
//=============================================================================
void W3DGameWindow::drawText( Color color )
{
Bool needDraw = FALSE;
// if new text pos we need to redraw
if( m_newTextPos )
{
m_newTextPos = FALSE;
needDraw = TRUE;
} // end if
// if color switch, set new color
if( m_currTextColor != color )
{
m_currTextColor = color;
needDraw = TRUE;
} // end if
// draw the quads if needed
if( needDraw || m_needPolyDraw )
{
UnsignedInt outline = TheWindowManager->winMakeColor( 0, 0, 0, 255 );
m_textRenderer.Reset_Polys();
m_textRenderer.Set_Location( Vector2( m_textPos.x + 1, m_textPos.y + 1 ) );
m_textRenderer.Draw_Sentence( outline );
m_textRenderer.Set_Location( Vector2( m_textPos.x, m_textPos.y ) );
m_textRenderer.Draw_Sentence( m_currTextColor );
m_needPolyDraw = FALSE;
} // end if
// do the render
m_textRenderer.Render();
} // end drawText

View file

@ -0,0 +1,64 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DGameWindowManager.cpp /////////////////////////////////////////////////////////////////
// Created: Colin Day, June 2001
// Desc: W3D implementation of the window manager, responsible for all
// interactions with the game windowing system for menus and
// window controls.
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include "GameClient/Image.h"
#include "W3DDevice/GameClient/W3DGameWindowManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DGameWindowManager::W3DGameWindowManager( void )
{
} // end W3DGameWindowManager
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DGameWindowManager::~W3DGameWindowManager( void )
{
} // end ~W3DGameWindowManager
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DGameWindowManager::init( void )
{
// extend
GameWindowManager::init();
} // end init

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Motion blur vertex shader
; John Ahlquist April 2002
; Currently unused prototype code. jba.
#define CV_ZERO 0
#define CV_ONE 1
#define V_POSITION v0
#define V_DIFFUSE v1
#define V_TEXTURE v2
vs.1.1
mov oPos, V_POSITION
mov oT0, V_TEXTURE
mov oT1, V_TEXTURE
mov oT2, V_TEXTURE
mov oT3, V_TEXTURE
mov oD0, V_DIFFUSE

View file

@ -0,0 +1,55 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Terrain pixel shader
; Created: Mark Wilczynski, August 2001
; Declare pixel shader version 1.1
ps.1.1
tex t0 ; get texture 0
;dp3 r1, c1, t0
;add r0,r1,-c2
;mul r0,r0,c3
;mov r0,t0 ;do nothing
//Inverted image
;dp3 r1, t0, c0 ;black & white conversion
;mul r0, 1-r1, c1 ;modulate inverted by filter color
;lrp r0, c2, r0, t0 ;blend modified image into original image so smooth fade in/out
//Red inverted image
dp3 r1, t0, c0 ;black & white conversion
mul r1, r1, c1 ;modulate by filter color (inverse of red)
lrp r0, c2, 1-r1, t0 ;blend modified image into original image so smooth fade in/out
;Code to clamp and expand dynamic range
;add_sat r1,r1,-c7 ;clamp out low values - 30
;add_sat r1,r1,c5 ;clamp out upper values +60
;add r1,r1,-c6 ;shift to center range at 0
;add r0,r0,-c5
;mul_x4 r0,r0,c6

View file

@ -0,0 +1,49 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Terrain pixel shader
; Created: Mark Wilczynski, August 2001
; Declare pixel shader version 1.1
ps.1.1
tex t0 ; get texture 0
;dp3 r1, c1, t0
;add r0,r1,-c2
;mul r0,r0,c3
;mov r0,t0 ;do nothing
dp3 r1, t0, c0 ;black & white conversion
mul r1, r1, c1 ;modulate by filter color
lrp r0, c2, r1, t0 ;blend modified image into original image so smooth fade in/out
;Code to clamp and expand dynamic range
;add_sat r1,r1,-c7 ;clamp out low values - 30
;add_sat r1,r1,c5 ;clamp out upper values +60
;add r1,r1,-c6 ;shift to center range at 0
;add r0,r0,-c5
;mul_x4 r0,r0,c6

View file

@ -0,0 +1,47 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Motion blur pixel shader
; Created: John Ahlquist, April 2002
; Currently unused prototype code. jba.
; Declare pixel shader version 1.1
ps.1.1
tex t0
; Define t0 as a standard 3-vector from bumpmap
tex t1
; Perform EMBM to get a local normal bump reflection.
texbem t2, t1 ; compute new (u,v) values
tex t3
; result goes in output color multiplied by diffuse
;mul r0, t2, v0
mul r0.rgb, v0, t0
+add r0.a, v0, t0
mul r1, t2, c0
add r0.rgb, r0, r1
+mul r0.a, r0, t3

View file

@ -0,0 +1,37 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Terrain pixel shader
; Created: Mark Wilczynski, June 2002
; Declare pixel shader version 1.1
ps.1.1
tex t0 ; get road texture
tex t1 ; get cloud shadow texture
tex t2 ; get noise texture
mul r0, t0, t1 ;modulate with cloud
mul r0, r0, t2 ;modulate with noise
mul r0, r0, v0 ;apply diffuse lighting

View file

@ -0,0 +1,35 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Terrain pixel shader
; Created: Mark Wilczynski, August 2001
; Declare pixel shader version 1.1
ps.1.1
tex t0 ; get texture 0
tex t1 ; get texture 1
lrp r0, v0.a, t1, t0 ;alpha blend between 2 textures
mul r0, r0, v0 ;apply diffuse lighting

View file

@ -0,0 +1,37 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Terrain pixel shader
; Created: Mark Wilczynski, August 2001
; Declare pixel shader version 1.1
ps.1.1
tex t0 ; get texture 0
tex t1 ; get texture 1
tex t2 ; get texture 2
lrp r0, v0.a, t1, t0 ;alpha blend between 2 textures
mul r0, r0, v0 ;apply diffuse lighting
mul r0, r0, t2 ;modulate with texture2

View file

@ -0,0 +1,39 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Terrain pixel shader
; Created: Mark Wilczynski, August 2001
; Declare pixel shader version 1.1
ps.1.1
tex t0 ; get texture 0
tex t1 ; get texture 1
tex t2 ; get texture 2
tex t3 ; get texture 3
lrp r0, v0.a, t1, t0 ;alpha blend between 2 textures
mul r0, r0, v0 ;apply diffuse lighting
mul r0, r0, t2 ;modulate with texture 2
mul r0, r0, t3 ;modulate with texture 3

View file

@ -0,0 +1,435 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "Common/Debug.h"
#include "W3DDevice/GameClient/W3DBufferManager.h"
W3DBufferManager *TheW3DBufferManager=NULL; //singleton
static int FVFTypeIndexList[W3DBufferManager::MAX_FVF]=
{
D3DFVF_XYZ,
D3DFVF_XYZ|D3DFVF_DIFFUSE,
D3DFVF_XYZ|D3DFVF_TEX1,
D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1,
D3DFVF_XYZ|D3DFVF_TEX2,
D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX2,
D3DFVF_XYZ|D3DFVF_NORMAL,
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE,
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1,
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX1,
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX2,
D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_DIFFUSE|D3DFVF_TEX2,
D3DFVF_XYZRHW,
D3DFVF_XYZRHW|D3DFVF_DIFFUSE,
D3DFVF_XYZRHW|D3DFVF_TEX1,
D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1,
D3DFVF_XYZRHW|D3DFVF_TEX2,
D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX2
};
Int W3DBufferManager::getDX8Format(VBM_FVF_TYPES format)
{
return FVFTypeIndexList[format];
}
W3DBufferManager::W3DBufferManager(void)
{
m_numEmptySlotsAllocated=0;
m_numEmptyVertexBuffersAllocated=0;
m_numEmptyIndexSlotsAllocated=0;
m_numEmptyIndexBuffersAllocated=0;
for (Int i=0; i<MAX_FVF; i++)
m_W3DVertexBuffers[i]=NULL;
for (i=0; i<MAX_FVF; i++)
for (Int j=0; j<MAX_VB_SIZES; j++)
m_W3DVertexBufferSlots[i][j]=NULL;
m_W3DIndexBuffers=NULL;
for (Int j=0; j<MAX_IB_SIZES; j++)
m_W3DIndexBufferSlots[j]=NULL;
}
W3DBufferManager::~W3DBufferManager(void)
{
freeAllSlots();
freeAllBuffers();
}
void W3DBufferManager::freeAllSlots(void)
{
Int i,j;
for (i=0; i<MAX_FVF; i++)
{
for (j=0; j<MAX_VB_SIZES; j++)
{
//Release all slots allocated for each size
W3DVertexBufferSlot *vbSlot = m_W3DVertexBufferSlots[i][j];
while (vbSlot)
{
if (vbSlot->m_prevSameVB)
vbSlot->m_prevSameVB->m_nextSameVB=vbSlot->m_nextSameVB;
else
vbSlot->m_VB->m_usedSlots=NULL;
if (vbSlot->m_nextSameVB)
vbSlot->m_nextSameVB->m_prevSameVB=vbSlot->m_prevSameVB;
vbSlot=vbSlot->m_nextSameSize;
m_numEmptySlotsAllocated--;
}
m_W3DVertexBufferSlots[i][j]=NULL;
}
}
for (j=0; j<MAX_IB_SIZES; j++)
{
//Release all slots allocated for each size
W3DIndexBufferSlot *ibSlot = m_W3DIndexBufferSlots[j];
while (ibSlot)
{
if (ibSlot->m_prevSameIB)
ibSlot->m_prevSameIB->m_nextSameIB=ibSlot->m_nextSameIB;
else
ibSlot->m_IB->m_usedSlots=NULL;
if (ibSlot->m_nextSameIB)
ibSlot->m_nextSameIB->m_prevSameIB=ibSlot->m_prevSameIB;
ibSlot=ibSlot->m_nextSameSize;
m_numEmptyIndexSlotsAllocated--;
}
m_W3DIndexBufferSlots[j]=NULL;
}
DEBUG_ASSERTCRASH(m_numEmptySlotsAllocated==0, ("Failed to free all empty vertex buffer slots"));
DEBUG_ASSERTCRASH(m_numEmptyIndexSlotsAllocated==0, ("Failed to free all empty index buffer slots"));
}
void W3DBufferManager::freeAllBuffers(void)
{
Int i;
//Make sure all slots are free
freeAllSlots(); ///<release all slots to pool.
for (i=0; i<MAX_FVF; i++)
{
W3DVertexBuffer *vb = m_W3DVertexBuffers[i];
while (vb)
{ DEBUG_ASSERTCRASH(vb->m_usedSlots == NULL, ("Freeing Non-Empty Vertex Buffer"));
REF_PTR_RELEASE(vb->m_DX8VertexBuffer);
m_numEmptyVertexBuffersAllocated--;
vb=vb->m_nextVB; //get next vertex buffer of this type
}
m_W3DVertexBuffers[i]=NULL;
}
W3DIndexBuffer *ib = m_W3DIndexBuffers;
while (ib)
{ DEBUG_ASSERTCRASH(ib->m_usedSlots == NULL, ("Freeing Non-Empty Index Buffer"));
REF_PTR_RELEASE(ib->m_DX8IndexBuffer);
m_numEmptyIndexBuffersAllocated--;
ib=ib->m_nextIB; //get next vertex buffer of this type
}
m_W3DIndexBuffers=NULL;
DEBUG_ASSERTCRASH(m_numEmptyVertexBuffersAllocated==0, ("Failed to free all empty vertex buffers"));
DEBUG_ASSERTCRASH(m_numEmptyIndexBuffersAllocated==0, ("Failed to free all empty index buffers"));
}
void W3DBufferManager::ReleaseResources(void)
{
for (Int i=0; i<MAX_FVF; i++)
{
W3DVertexBuffer *vb = m_W3DVertexBuffers[i];
while (vb)
{
REF_PTR_RELEASE(vb->m_DX8VertexBuffer);
vb=vb->m_nextVB; //get next vertex buffer of this type
}
}
W3DIndexBuffer *ib = m_W3DIndexBuffers;
while (ib)
{
REF_PTR_RELEASE(ib->m_DX8IndexBuffer);
ib=ib->m_nextIB; //get next vertex buffer of this type
}
}
Bool W3DBufferManager::ReAcquireResources(void)
{
for (Int i=0; i<MAX_FVF; i++)
{
W3DVertexBuffer *vb = m_W3DVertexBuffers[i];
while (vb)
{ DEBUG_ASSERTCRASH( vb->m_DX8VertexBuffer == NULL, ("ReAcquire of existing vertex buffer"));
vb->m_DX8VertexBuffer=NEW_REF(DX8VertexBufferClass,(FVFTypeIndexList[vb->m_format],vb->m_size,DX8VertexBufferClass::USAGE_DEFAULT));
DEBUG_ASSERTCRASH( vb->m_DX8VertexBuffer, ("Failed ReAcquire of vertex buffer"));
if (!vb->m_DX8VertexBuffer)
return FALSE;
vb=vb->m_nextVB; //get next vertex buffer of this type
}
}
W3DIndexBuffer *ib = m_W3DIndexBuffers;
while (ib)
{ DEBUG_ASSERTCRASH( ib->m_DX8IndexBuffer == NULL, ("ReAcquire of existing index buffer"));
ib->m_DX8IndexBuffer=NEW_REF(DX8IndexBufferClass,(ib->m_size,DX8IndexBufferClass::USAGE_DEFAULT));
DEBUG_ASSERTCRASH( ib->m_DX8IndexBuffer, ("Failed ReAcquire of index buffer"));
if (!ib->m_DX8IndexBuffer)
return FALSE;
ib=ib->m_nextIB; //get next vertex buffer of this type
}
return TRUE;
}
/**Searches through previously allocated vertex buffer slots and returns a matching type. If none found,
creates a new slot and adds it to the pool. Returns an integer slotId used to reference the VB.
Returns -1 in case of failure.
*/
W3DBufferManager::W3DVertexBufferSlot *W3DBufferManager::getSlot(VBM_FVF_TYPES fvfType, Int size)
{
W3DVertexBufferSlot *vbSlot=NULL;
//round size to next multiple of minimum slot size.
//should help avoid fragmentation.
size = (size + (MIN_SLOT_SIZE-1)) & (~(MIN_SLOT_SIZE-1));
Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1;
DEBUG_ASSERTCRASH(sizeIndex < MAX_VB_SIZES && size, ("Allocating too large vertex buffer slot"));
if ((vbSlot=m_W3DVertexBufferSlots[fvfType][sizeIndex]) != 0)
{ //found a previously allocated slot matching required size
m_W3DVertexBufferSlots[fvfType][sizeIndex]=vbSlot->m_nextSameSize;
if (vbSlot->m_nextSameSize)
vbSlot->m_nextSameSize->m_prevSameSize=NULL;
return vbSlot;
}
else
{ //need to allocate a new slot
return allocateSlotStorage(fvfType, size);
}
return NULL;
}
/**Returns vertex buffer space back to pool so it can be reused later*/
void W3DBufferManager::releaseSlot(W3DVertexBufferSlot *vbSlot)
{
Int sizeIndex = (vbSlot->m_size >> MIN_SLOT_SIZE_SHIFT)-1;
vbSlot->m_nextSameSize=m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex];
if (m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex])
m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex]->m_prevSameSize=vbSlot;
m_W3DVertexBufferSlots[vbSlot->m_VB->m_format][sizeIndex]=vbSlot;
}
/**Reserves space inside existing vertex buffer or allocates a new one to fit the required size.
*/
W3DBufferManager::W3DVertexBufferSlot * W3DBufferManager::allocateSlotStorage(VBM_FVF_TYPES fvfType, Int size)
{
W3DVertexBuffer *pVB;
W3DVertexBufferSlot *vbSlot;
// Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1;
DEBUG_ASSERTCRASH(m_numEmptySlotsAllocated < MAX_NUMBER_SLOTS, ("Nore more VB Slots"));
pVB=m_W3DVertexBuffers[fvfType];
while (pVB)
{
if ((pVB->m_size - pVB->m_startFreeIndex) >= size)
{ //found enough free space in this vertex buffer
if (m_numEmptySlotsAllocated < MAX_NUMBER_SLOTS)
{ //we're allowing more slots to be allocated.
vbSlot=&m_W3DVertexBufferEmptySlots[m_numEmptySlotsAllocated];
vbSlot->m_size=size;
vbSlot->m_start=pVB->m_startFreeIndex;
vbSlot->m_VB=pVB;
//Link to VB list of slots
vbSlot->m_nextSameVB=pVB->m_usedSlots;
vbSlot->m_prevSameVB=NULL; //this will be the new head
if (pVB->m_usedSlots)
pVB->m_usedSlots->m_prevSameVB=vbSlot;
vbSlot->m_prevSameSize=vbSlot->m_nextSameSize=NULL;
pVB->m_usedSlots=vbSlot;
pVB->m_startFreeIndex += size;
m_numEmptySlotsAllocated++;
return vbSlot;
}
}
pVB = pVB->m_nextVB;
}
pVB=m_W3DVertexBuffers[fvfType]; //save old list head
//Didn't find any vertex buffers with room, create a new one
DEBUG_ASSERTCRASH(m_numEmptyVertexBuffersAllocated < MAX_VERTEX_BUFFERS_CREATED, ("Reached Max Static VB Shadow Geometry"));
if (m_numEmptyVertexBuffersAllocated < MAX_VERTEX_BUFFERS_CREATED)
{
m_W3DVertexBuffers[fvfType] = &m_W3DEmptyVertexBuffers[m_numEmptyVertexBuffersAllocated];
m_W3DVertexBuffers[fvfType]->m_nextVB=pVB; //link to list
m_numEmptyVertexBuffersAllocated++;
pVB=m_W3DVertexBuffers[fvfType]; //get new list head
Int vbSize=__max(DEFAULT_VERTEX_BUFFER_SIZE,size);
pVB->m_DX8VertexBuffer=NEW_REF(DX8VertexBufferClass,(FVFTypeIndexList[fvfType],vbSize,DX8VertexBufferClass::USAGE_DEFAULT));
pVB->m_format=fvfType;
pVB->m_startFreeIndex=size;
pVB->m_size=vbSize;
vbSlot=&m_W3DVertexBufferEmptySlots[m_numEmptySlotsAllocated];
m_numEmptySlotsAllocated++;
pVB->m_usedSlots=vbSlot;
vbSlot->m_size=size;
vbSlot->m_start=0;
vbSlot->m_VB=pVB;
vbSlot->m_prevSameVB=vbSlot->m_nextSameVB=NULL;
vbSlot->m_prevSameSize=vbSlot->m_nextSameSize=NULL;
return vbSlot;
}
return NULL;
}
//******************************** Index Buffer code ******************************************************
/**Searches through previously allocated index buffer slots and returns a matching type. If none found,
creates a new slot and adds it to the pool. Returns an integer slotId used to reference the VB.
Returns -1 in case of failure.
*/
W3DBufferManager::W3DIndexBufferSlot *W3DBufferManager::getSlot(Int size)
{
W3DIndexBufferSlot *ibSlot=NULL;
//round size to next multiple of minimum slot size.
//should help avoid fragmentation.
size = (size + (MIN_SLOT_SIZE-1)) & (~(MIN_SLOT_SIZE-1));
Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1;
DEBUG_ASSERTCRASH(sizeIndex < MAX_IB_SIZES && size, ("Allocating too large index buffer slot"));
if ((ibSlot=m_W3DIndexBufferSlots[sizeIndex]) != 0)
{ //found a previously allocated slot matching required size
m_W3DIndexBufferSlots[sizeIndex]=ibSlot->m_nextSameSize;
if (ibSlot->m_nextSameSize)
ibSlot->m_nextSameSize->m_prevSameSize=NULL;
return ibSlot;
}
else
{ //need to allocate a new slot
return allocateSlotStorage(size);
}
return NULL;
}
/**Returns index buffer space back to pool so it can be reused later*/
void W3DBufferManager::releaseSlot(W3DIndexBufferSlot *ibSlot)
{
Int sizeIndex = (ibSlot->m_size >> MIN_SLOT_SIZE_SHIFT)-1;
ibSlot->m_nextSameSize=m_W3DIndexBufferSlots[sizeIndex];
if (m_W3DIndexBufferSlots[sizeIndex])
m_W3DIndexBufferSlots[sizeIndex]->m_prevSameSize=ibSlot;
m_W3DIndexBufferSlots[sizeIndex]=ibSlot;
}
/**Reserves space inside existing index buffer or allocates a new one to fit the required size.
*/
W3DBufferManager::W3DIndexBufferSlot * W3DBufferManager::allocateSlotStorage(Int size)
{
W3DIndexBuffer *pIB;
W3DIndexBufferSlot *ibSlot;
// Int sizeIndex = (size >> MIN_SLOT_SIZE_SHIFT)-1;
DEBUG_ASSERTCRASH(m_numEmptyIndexSlotsAllocated < MAX_NUMBER_SLOTS, ("Nore more IB Slots"));
pIB=m_W3DIndexBuffers;
while (pIB)
{
if ((pIB->m_size - pIB->m_startFreeIndex) >= size)
{ //found enough free space in this index buffer
if (m_numEmptyIndexSlotsAllocated < MAX_NUMBER_SLOTS)
{ //we're allowing more slots to be allocated.
ibSlot=&m_W3DIndexBufferEmptySlots[m_numEmptyIndexSlotsAllocated];
ibSlot->m_size=size;
ibSlot->m_start=pIB->m_startFreeIndex;
ibSlot->m_IB=pIB;
//Link to IB list of slots
ibSlot->m_nextSameIB=pIB->m_usedSlots;
ibSlot->m_prevSameIB=NULL; //this will be the new head
if (pIB->m_usedSlots)
pIB->m_usedSlots->m_prevSameIB=ibSlot;
ibSlot->m_prevSameSize=ibSlot->m_nextSameSize=NULL;
pIB->m_usedSlots=ibSlot;
pIB->m_startFreeIndex += size;
m_numEmptyIndexSlotsAllocated++;
return ibSlot;
}
}
pIB = pIB->m_nextIB;
}
pIB=m_W3DIndexBuffers; //save old list head
//Didn't find any index buffers with room, create a new one
DEBUG_ASSERTCRASH(m_numEmptyIndexBuffersAllocated < MAX_INDEX_BUFFERS_CREATED, ("Reached Max Static IB Shadow Geometry"));
if (m_numEmptyIndexBuffersAllocated < MAX_INDEX_BUFFERS_CREATED)
{
m_W3DIndexBuffers = &m_W3DEmptyIndexBuffers[m_numEmptyIndexBuffersAllocated];
m_W3DIndexBuffers->m_nextIB=pIB; //link to list
m_numEmptyIndexBuffersAllocated++;
pIB=m_W3DIndexBuffers; //get new list head
Int ibSize=__max(DEFAULT_INDEX_BUFFER_SIZE,size);
pIB->m_DX8IndexBuffer=NEW_REF(DX8IndexBufferClass,(ibSize,DX8IndexBufferClass::USAGE_DEFAULT));
pIB->m_startFreeIndex=size;
pIB->m_size=ibSize;
ibSlot=&m_W3DIndexBufferEmptySlots[m_numEmptyIndexSlotsAllocated];
m_numEmptyIndexSlotsAllocated++;
pIB->m_usedSlots=ibSlot;
ibSlot->m_size=size;
ibSlot->m_start=0;
ibSlot->m_IB=pIB;
ibSlot->m_prevSameIB=ibSlot->m_nextSameIB=NULL;
ibSlot->m_prevSameSize=ibSlot->m_nextSameSize=NULL;
return ibSlot;
}
return NULL;
}

View file

@ -0,0 +1,246 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DShadow.cpp ///////////////////////////////////////////////////////////
//
// Real time shadow representations
//
// Author: Mark Wilczynski, February 2002
//
//
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "always.h"
#include "GameClient/View.h"
#include "WW3D2/Camera.h"
#include "WW3D2/Light.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/HLod.h"
#include "WW3D2/mesh.h"
#include "WW3D2/meshmdl.h"
#include "Lib/BaseType.h"
#include "W3DDevice/GameClient/W3DGranny.h"
#include "W3DDevice/GameClient/Heightmap.h"
#include "D3dx8math.h"
#include "common/GlobalData.h"
#include "W3DDevice/GameClient/W3DVolumetricShadow.h"
#include "W3DDevice/GameClient/W3DProjectedShadow.h"
#include "W3DDevice/GameClient/W3DShadow.h"
#include "WW3D2/statistics.h"
#include "Common/Debug.h"
#include "Common/PerfTimer.h"
#define SUN_DISTANCE_FROM_GROUND 10000.0f //distance of sun (our only light source).
// Global Variables and Functions /////////////////////////////////////////////
W3DShadowManager *TheW3DShadowManager=NULL;
const FrustumClass *shadowCameraFrustum;
Vector3 LightPosWorld[ MAX_SHADOW_LIGHTS ] =
{
Vector3( 94.0161f, 50.499f, 200.0f)
};
//DECLARE_PERF_TIMER(shadowsRender)
void DoShadows(RenderInfoClass & rinfo, Bool stencilPass)
{
//USE_PERF_TIMER(shadowsRender)
shadowCameraFrustum=&rinfo.Camera.Get_Frustum();
Int projectionCount=0;
//Projected shadows render first because they may fill the stencil buffer
//which will be used by the shadow volumes
if (stencilPass == FALSE && TheW3DProjectedShadowManager)
{
if (TheW3DShadowManager->isShadowScene())
projectionCount=TheW3DProjectedShadowManager->renderShadows(rinfo);
}
if (stencilPass == TRUE && TheW3DVolumetricShadowManager)
{
// TheW3DShadowManager->loadTerrainShadows();
//This function gets called many times by the W3D renderer
//so we use this flag to make sure shadows rendered only once per frame.
if (TheW3DShadowManager->isShadowScene())
TheW3DVolumetricShadowManager->renderShadows(projectionCount);
}
if (TheW3DShadowManager && stencilPass) //reset so no more shadow processing this frame.
TheW3DShadowManager->queueShadows(FALSE);
}
W3DShadowManager::W3DShadowManager( void )
{
DEBUG_ASSERTCRASH(TheW3DVolumetricShadowManager == NULL && TheW3DProjectedShadowManager == NULL,
("Creating new shadow managers without deleting old ones"));
m_shadowColor = 0x7fa0a0a0;
m_isShadowScene = FALSE;
m_stencilShadowMask = 0; //all bits can be used for storing shadows.
Vector3 lightRay(-TheGlobalData->m_terrainLightPos[0].x,
-TheGlobalData->m_terrainLightPos[0].y, -TheGlobalData->m_terrainLightPos[0].z);
lightRay.Normalize();
LightPosWorld[0]=lightRay*SUN_DISTANCE_FROM_GROUND;
TheW3DVolumetricShadowManager = NEW W3DVolumetricShadowManager;
TheProjectedShadowManager = TheW3DProjectedShadowManager = NEW W3DProjectedShadowManager;
}
W3DShadowManager::~W3DShadowManager( void )
{
delete TheW3DVolumetricShadowManager;
TheW3DVolumetricShadowManager = NULL;
delete TheW3DProjectedShadowManager;
TheProjectedShadowManager = TheW3DProjectedShadowManager = NULL;
}
/** Do one-time initilalization of shadow systems that need to be
active for full duration of game*/
Bool W3DShadowManager::init( void )
{
Bool result=TRUE;
if (TheW3DVolumetricShadowManager && TheW3DVolumetricShadowManager->init())
{
if (TheW3DVolumetricShadowManager->ReAcquireResources())
result = TRUE;
}
if ( TheW3DProjectedShadowManager && TheW3DProjectedShadowManager->init())
{
if (TheW3DProjectedShadowManager->ReAcquireResources())
result = TRUE;
}
return result;
}
/** Do per-map reset. This frees up shadows from all objects since
they may not exist on the next map*/
void W3DShadowManager::Reset( void )
{
if (TheW3DVolumetricShadowManager)
TheW3DVolumetricShadowManager->reset();
if (TheW3DProjectedShadowManager)
TheW3DProjectedShadowManager->reset();
}
Bool W3DShadowManager::ReAcquireResources()
{
Bool result = TRUE;
if (TheW3DVolumetricShadowManager && !TheW3DVolumetricShadowManager->ReAcquireResources())
result = FALSE;
if (TheW3DProjectedShadowManager && !TheW3DProjectedShadowManager->ReAcquireResources())
result = FALSE;
return result;
}
void W3DShadowManager::ReleaseResources(void)
{
if (TheW3DVolumetricShadowManager)
TheW3DVolumetricShadowManager->ReleaseResources();
if (TheW3DProjectedShadowManager)
TheW3DProjectedShadowManager->ReleaseResources();
}
Shadow *W3DShadowManager::addShadow( RenderObjClass *robj, Shadow::ShadowTypeInfo *shadowInfo, Drawable *draw)
{
ShadowType type = SHADOW_VOLUME;
if (shadowInfo)
type = shadowInfo->m_type;
switch(type)
{
case SHADOW_VOLUME:
if (TheW3DVolumetricShadowManager)
return (Shadow *)TheW3DVolumetricShadowManager->addShadow(robj, shadowInfo, draw);
break;
case SHADOW_PROJECTION:
case SHADOW_DECAL:
if (TheW3DProjectedShadowManager)
return (Shadow *)TheW3DProjectedShadowManager->addShadow(robj, shadowInfo, draw);
break;
default:
return NULL;
}
return NULL;
}
void W3DShadowManager::removeShadow(Shadow *shadow)
{
shadow->release();
}
void W3DShadowManager::removeAllShadows(void)
{
if (TheW3DVolumetricShadowManager)
TheW3DVolumetricShadowManager->removeAllShadows();
if (TheW3DProjectedShadowManager)
TheW3DProjectedShadowManager->removeAllShadows();
}
/**Force update of all shadows even when light source and object have not moved*/
void W3DShadowManager::invalidateCachedLightPositions(void)
{
if (TheW3DVolumetricShadowManager)
TheW3DVolumetricShadowManager->invalidateCachedLightPositions();
if (TheW3DProjectedShadowManager)
TheW3DProjectedShadowManager->invalidateCachedLightPositions();
}
Vector3 &W3DShadowManager::getLightPosWorld(Int lightIndex)
{
return LightPosWorld[lightIndex];
}
void W3DShadowManager::setLightPosition(Int lightIndex, Real x, Real y, Real z)
{
if (lightIndex != 0)
return; ///@todo: Add support for multiple lights
LightPosWorld[lightIndex]=Vector3(x,y,z);
}
void W3DShadowManager::setTimeOfDay(TimeOfDay tod)
{
//Ray to light source
const GlobalData::TerrainLighting *ol=&TheGlobalData->m_terrainObjectsLighting[tod][0];
Vector3 lightRay(-ol->lightPos.x,-ol->lightPos.y,-ol->lightPos.z);
lightRay.Normalize();
lightRay *= SUN_DISTANCE_FROM_GROUND;
setLightPosition(0, lightRay.X, lightRay.Y, lightRay.Z);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,107 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// TileData.cpp
// Class to handle tile data.
// Author: John Ahlquist, April 2001
#include "W3DDevice/GameClient/TileData.h"
#include "W3DDevice/GameClient/WorldHeightMap.h"
//
// TileData - no destructor.
//
//
// TileData - create a new texture tile .
//
TileData::TileData()
{
}
#define TILE_PIXEL_EXTENT_MIP1 32
#define TILE_PIXEL_EXTENT_MIP2 16
#define TILE_PIXEL_EXTENT_MIP3 8
#define TILE_PIXEL_EXTENT_MIP4 4
#define TILE_PIXEL_EXTENT_MIP5 2
#define TILE_PIXEL_EXTENT_MIP6 1
Bool TileData::hasRGBDataForWidth(Int width)
{
if (width == TILE_PIXEL_EXTENT) return(true);
if (width == TILE_PIXEL_EXTENT_MIP1) return(true);
if (width == TILE_PIXEL_EXTENT_MIP2) return(true);
if (width == TILE_PIXEL_EXTENT_MIP3) return(true);
if (width == TILE_PIXEL_EXTENT_MIP4) return(true);
if (width == TILE_PIXEL_EXTENT_MIP5) return(true);
if (width == TILE_PIXEL_EXTENT_MIP6) return(true);
return(false);
}
UnsignedByte * TileData::getRGBDataForWidth(Int width)
{
// default
if (width == TILE_PIXEL_EXTENT_MIP1) return(m_tileDataMip32);
if (width == TILE_PIXEL_EXTENT_MIP2) return(m_tileDataMip16);
if (width == TILE_PIXEL_EXTENT_MIP3) return(m_tileDataMip8);
if (width == TILE_PIXEL_EXTENT_MIP4) return(m_tileDataMip4);
if (width == TILE_PIXEL_EXTENT_MIP5) return(m_tileDataMip2);
if (width == TILE_PIXEL_EXTENT_MIP6) return(m_tileDataMip1);
return(m_tileData);
}
void TileData::updateMips(void)
{
doMip(m_tileData, TILE_PIXEL_EXTENT, m_tileDataMip32);
doMip(m_tileDataMip32, TILE_PIXEL_EXTENT_MIP1, m_tileDataMip16);
doMip(m_tileDataMip16, TILE_PIXEL_EXTENT_MIP2, m_tileDataMip8);
doMip(m_tileDataMip8, TILE_PIXEL_EXTENT_MIP3, m_tileDataMip4);
doMip(m_tileDataMip4, TILE_PIXEL_EXTENT_MIP4, m_tileDataMip2);
doMip(m_tileDataMip2, TILE_PIXEL_EXTENT_MIP5, m_tileDataMip1);
}
void TileData::doMip(UnsignedByte *pHiRes, Int hiRow, UnsignedByte *pLoRes)
{
Int i, j;
for (i=0; i<hiRow; i+=2) {
for (j=0; j<hiRow; j+=2) {
Int pxl;
Int ndx = (j*hiRow+i)*TILE_BYTES_PER_PIXEL;
Int loNdx = (j/2)*(hiRow/2) + (i/2);
loNdx *= TILE_BYTES_PER_PIXEL;
Int p;
for (p=0; p<TILE_BYTES_PER_PIXEL; p++,ndx++,loNdx++) {
pxl = pHiRes[ndx] + pHiRes[ndx+TILE_BYTES_PER_PIXEL] + pHiRes[ndx+TILE_BYTES_PER_PIXEL*hiRow] + pHiRes[ndx+TILE_BYTES_PER_PIXEL*hiRow+TILE_BYTES_PER_PIXEL] +2;
pxl /= 4;
pLoRes[loNdx] = pxl;
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "W3DDevice/GameClient/W3DAssetManagerExposed.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
void ReloadAllTextures(void)
{
W3DAssetManager::Get_Instance()->Release_All_Textures();
}

View file

@ -0,0 +1,436 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DBibBuffer.cpp ////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DBibBuffer.cpp
//
// Created: John Ahlquist, May 2001
//
// Desc: Draw buffer to handle all the bibs in a scene.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DBibBuffer.h"
#include <stdio.h>
#include <string.h>
#include <assetmgr.h>
#include <texture.h>
#include "common/GlobalData.h"
#include "common/RandomValue.h"
#include "W3DDevice/GameClient/TerrainTex.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "W3DDevice/GameClient/W3DDynamicLight.h"
#include "WW3D2/Camera.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/DX8Renderer.h"
#include "WW3D2/Mesh.h"
#include "WW3D2/MeshMdl.h"
//-----------------------------------------------------------------------------
// Private Data
//-----------------------------------------------------------------------------
// A W3D shader that does alpha, texturing, tests zbuffer, doesn't update zbuffer.
#define SC_ALPHA_DETAIL ( SHADE_CNST(ShaderClass::PASS_ALWAYS, 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_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailAlphaShader(SC_ALPHA_DETAIL);
//-----------------------------------------------------------------------------
// Private Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DBibBuffer::loadBibsInVertexAndIndexBuffers
//=============================================================================
/** Loads the bibs into the vertex buffer for drawing. */
//=============================================================================
void W3DBibBuffer::loadBibsInVertexAndIndexBuffers(void)
{
if (!m_indexBib || !m_vertexBib || !m_initialized) {
return;
}
if (!m_anythingChanged) {
return;
}
m_curNumBibVertices = 0;
m_curNumBibIndices = 0;
m_curNumNormalBibIndices = 0;
m_curNumNormalBibVertex = 0;
VertexFormatXYZDUV1 *vb;
UnsignedShort *ib;
// Lock the buffers.
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBib);
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexBib);
vb=(VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
ib = lockIdxBuffer.Get_Index_Array();
// Add to the index buffer & vertex buffer.
UnsignedShort *curIb = ib;
VertexFormatXYZDUV1 *curVb = vb;
Int curBib;
// Calculate a static lighting value to use for all the bibs.
Real shadeR, shadeG, shadeB;
shadeR = TheGlobalData->m_terrainAmbient[0].red;
shadeG = TheGlobalData->m_terrainAmbient[0].green;
shadeB = TheGlobalData->m_terrainAmbient[0].blue;
shadeR += TheGlobalData->m_terrainDiffuse[0].red;
shadeG += TheGlobalData->m_terrainDiffuse[0].green;
shadeB += TheGlobalData->m_terrainDiffuse[0].blue;
if (shadeR>1.0f) shadeR=1.0f;
if (shadeG>1.0f) shadeG=1.0f;
if (shadeB>1.0f) shadeB=1.0f;
shadeR*=255.0f;
shadeG*=255.0f;
shadeB*=255.0f;
Int diffuse = (REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16) | (255 << 24));
Int doHighlight;
for (doHighlight=0; doHighlight<=1; doHighlight++)
{
if (doHighlight==1)
{
m_curNumNormalBibIndices = m_curNumBibIndices;
m_curNumNormalBibVertex = m_curNumBibVertices;
}
for (curBib=0; curBib<m_numBibs; curBib++) {
if (m_bibs[curBib].m_unused) continue;
if (m_bibs[curBib].m_highlight != (Bool)doHighlight) continue;
Int startVertex = m_curNumBibVertices;
Int i;
Int numVertex = 4;
if (m_curNumBibVertices+numVertex+2>= m_vertexBibSize) {
break;
}
Int numIndex = 6;
if (m_curNumBibIndices+numIndex+6 >= m_indexBibSize) {
break;
}
for (i=0; i<numVertex; i++) {
// 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;
Vector3 vLoc=m_bibs[curBib].m_corners[i];
switch (i) {
case 0 :
U=0;V=1;
break;
case 1:
U=1;V=1;
break;
case 2:
U=1;V=0;
break;
case 3:
U=0;V=0;
break;
}
curVb->u1 = U;
curVb->v1 = V;
curVb->x = vLoc.X;
curVb->y = vLoc.Y;
curVb->z = vLoc.Z;
curVb->diffuse = diffuse;
curVb++;
m_curNumBibVertices++;
}
*curIb++ = startVertex + 0;
*curIb++ = startVertex + 1;
*curIb++ = startVertex + 2;
*curIb++ = startVertex + 0;
*curIb++ = startVertex + 2;
*curIb++ = startVertex + 3;
m_curNumBibIndices+=6;
}
}
}
//-----------------------------------------------------------------------------
// Public Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DBibBuffer::~W3DBibBuffer
//=============================================================================
/** Destructor. Releases w3d assets. */
//=============================================================================
W3DBibBuffer::~W3DBibBuffer(void)
{
freeBibBuffers();
REF_PTR_RELEASE(m_bibTexture);
REF_PTR_RELEASE(m_highlightBibTexture);
}
//=============================================================================
// W3DBibBuffer::W3DBibBuffer
//=============================================================================
/** Constructor. Sets m_initialized to true if it finds the w3d models it needs
for the bibs. */
//=============================================================================
W3DBibBuffer::W3DBibBuffer(void)
{
m_initialized = false;
m_vertexBib = NULL;
m_indexBib = NULL;
m_bibTexture = NULL;
m_curNumBibVertices=0;
m_curNumBibIndices=0;
clearAllBibs();
m_indexBibSize = INITIAL_BIB_INDEX;
m_vertexBibSize = INITIAL_BIB_VERTEX;
allocateBibBuffers();
m_bibTexture = NEW_REF(TextureClass, ("TBBib.tga"));
m_highlightBibTexture = NEW_REF(TextureClass, ("TBRedBib.tga"));
m_bibTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_bibTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_highlightBibTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_highlightBibTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_initialized = true;
}
//=============================================================================
// W3DBibBuffer::freeBibBuffers
//=============================================================================
/** Frees the index and vertex buffers. */
//=============================================================================
void W3DBibBuffer::freeBibBuffers(void)
{
REF_PTR_RELEASE(m_vertexBib);
REF_PTR_RELEASE(m_indexBib);
}
//=============================================================================
// W3DBibBuffer::allocateBibBuffers
//=============================================================================
/** Allocates the index and vertex buffers. */
//=============================================================================
void W3DBibBuffer::allocateBibBuffers(void)
{
m_vertexBib=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,m_vertexBibSize+4,DX8VertexBufferClass::USAGE_DYNAMIC));
m_indexBib=NEW_REF(DX8IndexBufferClass,(m_indexBibSize+4, DX8IndexBufferClass::USAGE_DYNAMIC));
m_curNumBibVertices=0;
m_curNumBibIndices=0;
}
//=============================================================================
// W3DBibBuffer::clearAllBibs
//=============================================================================
/** Removes all bibs. */
//=============================================================================
void W3DBibBuffer::clearAllBibs(void)
{
m_numBibs=0;
m_anythingChanged = true;
/* test bib
Vector3 corners[4];
corners[0].Set(0, 0, 20);
corners[1].Set(100, 0, 20);
corners[2].Set(100,100,20);
corners[3].Set(0,100,20);
addBib(corners, 1, false);
*/
}
//=============================================================================
// W3DBibBuffer::removeHighlighting
//=============================================================================
/** Clears highlighting flag. */
//=============================================================================
void W3DBibBuffer::removeHighlighting(void)
{
Int bibIndex;
for (bibIndex=0; bibIndex<m_numBibs; bibIndex++) {
m_bibs[bibIndex].m_highlight = false;
}
}
//=============================================================================
// W3DBibBuffer::addBib
//=============================================================================
/** Adds a bib. */
//=============================================================================
void W3DBibBuffer::addBib(Vector3 corners[4], ObjectID id, Bool highlight)
{
Int bibIndex;
for (bibIndex=0; bibIndex<m_numBibs; bibIndex++) {
if (!m_bibs[bibIndex].m_unused && m_bibs[bibIndex].m_objectID == id) {
break;
}
}
if (bibIndex==m_numBibs) {
for (bibIndex=0; bibIndex<m_numBibs; bibIndex++) {
if (m_bibs[bibIndex].m_unused) {
break;
}
}
}
if (bibIndex==m_numBibs) {
if (m_numBibs >= MAX_BIBS) {
return;
}
m_numBibs++;
}
m_anythingChanged = true;
m_bibs[bibIndex].m_corners[0] = corners[0];
m_bibs[bibIndex].m_corners[1] = corners[1];
m_bibs[bibIndex].m_corners[2] = corners[2];
m_bibs[bibIndex].m_corners[3] = corners[3];
m_bibs[bibIndex].m_highlight = highlight;
m_bibs[bibIndex].m_color = 0; // for now.
m_bibs[bibIndex].m_unused = false; // for now.
m_bibs[bibIndex].m_objectID = id;
m_bibs[bibIndex].m_drawableID = INVALID_DRAWABLE_ID;
}
//=============================================================================
// W3DBibBuffer::addBib
//=============================================================================
/** Adds a bib. */
//=============================================================================
void W3DBibBuffer::addBibDrawable(Vector3 corners[4], DrawableID id, Bool highlight)
{
Int bibIndex;
for (bibIndex=0; bibIndex<m_numBibs; bibIndex++) {
if (!m_bibs[bibIndex].m_unused && m_bibs[bibIndex].m_drawableID == id) {
break;
}
}
if (bibIndex==m_numBibs) {
for (bibIndex=0; bibIndex<m_numBibs; bibIndex++) {
if (m_bibs[bibIndex].m_unused) {
break;
}
}
}
if (bibIndex==m_numBibs) {
if (m_numBibs >= MAX_BIBS) {
return;
}
m_numBibs++;
}
m_anythingChanged = true;
m_bibs[bibIndex].m_corners[0] = corners[0];
m_bibs[bibIndex].m_corners[1] = corners[1];
m_bibs[bibIndex].m_corners[2] = corners[2];
m_bibs[bibIndex].m_corners[3] = corners[3];
m_bibs[bibIndex].m_highlight = highlight;
m_bibs[bibIndex].m_color = 0; // for now.
m_bibs[bibIndex].m_unused = false; // for now.
m_bibs[bibIndex].m_objectID = INVALID_ID;
m_bibs[bibIndex].m_drawableID = id;
}
//=============================================================================
// W3DBibBuffer::removeBib
//=============================================================================
/** Removes a bib. */
//=============================================================================
void W3DBibBuffer::removeBib(ObjectID id)
{
Int bibIndex;
for (bibIndex=0; bibIndex<m_numBibs; bibIndex++) {
if (m_bibs[bibIndex].m_objectID == id) {
m_bibs[bibIndex].m_unused = true;
m_bibs[bibIndex].m_objectID = INVALID_ID;
m_bibs[bibIndex].m_drawableID = INVALID_DRAWABLE_ID;
m_anythingChanged = true;
}
}
}
//=============================================================================
// W3DBibBuffer::removeBib
//=============================================================================
/** Removes a bib. */
//=============================================================================
void W3DBibBuffer::removeBibDrawable(DrawableID id)
{
Int bibIndex;
for (bibIndex=0; bibIndex<m_numBibs; bibIndex++) {
if (m_bibs[bibIndex].m_drawableID == id) {
m_bibs[bibIndex].m_unused = true;
m_bibs[bibIndex].m_objectID = INVALID_ID;
m_bibs[bibIndex].m_drawableID = INVALID_DRAWABLE_ID;
m_anythingChanged = true;
}
}
}
//=============================================================================
// W3DBibBuffer::drawBibs
//=============================================================================
/** Draws the bibs. Uses camera to cull. */
//=============================================================================
void W3DBibBuffer::renderBibs()
{
loadBibsInVertexAndIndexBuffers();
if (m_curNumBibIndices == 0) {
return;
}
// Setup the vertex buffer, shader & texture.
DX8Wrapper::Set_Index_Buffer(m_indexBib,0);
DX8Wrapper::Set_Vertex_Buffer(m_vertexBib);
DX8Wrapper::Set_Shader(detailAlphaShader);
if (m_curNumNormalBibIndices) {
DX8Wrapper::Set_Texture(0,m_bibTexture);
DX8Wrapper::Draw_Triangles( 0, m_curNumNormalBibIndices/3, 0, m_curNumNormalBibVertex);
}
if (m_curNumBibIndices>m_curNumNormalBibIndices) {
DX8Wrapper::Set_Texture(0,m_highlightBibTexture);
DX8Wrapper::Draw_Triangles( m_curNumNormalBibIndices, (m_curNumBibIndices-m_curNumNormalBibIndices)/3,
m_curNumNormalBibVertex, m_curNumBibVertices-m_curNumNormalBibVertex);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,434 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DCustomEdging.cpp ////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DCustomEdging.cpp
//
// Created: John Ahlquist, May 2001
//
// Desc: Draw buffer to handle all the custom tile edges in a scene.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DCustomEdging.h"
#include <stdio.h>
#include <string.h>
#include <assetmgr.h>
#include <texture.h>
#include "common/GlobalData.h"
#include "common/RandomValue.h"
#include "W3DDevice/GameClient/TerrainTex.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "W3DDevice/GameClient/W3DDynamicLight.h"
#include "WW3D2/Camera.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/DX8Renderer.h"
#include "WW3D2/Mesh.h"
#include "WW3D2/MeshMdl.h"
//-----------------------------------------------------------------------------
// Private Data
//-----------------------------------------------------------------------------
// 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_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_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailAlphaTestShader(SC_ALPHA_DETAIL);
#define SC_NO_ALPHA ( SHADE_CNST(ShaderClass::PASS_ALWAYS, ShaderClass::DEPTH_WRITE_DISABLE, 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_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailShader(SC_NO_ALPHA);
#define SC_DETAIL_BLEND ( 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_DISABLE, ShaderClass::CULL_MODE_ENABLE, ShaderClass::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE) )
static ShaderClass detailOpaqueShader(SC_DETAIL_BLEND);
/*
#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
//-----------------------------------------------------------------------------
//=============================================================================
// W3DCustomEdging::loadEdgingsInVertexAndIndexBuffers
//=============================================================================
/** Loads the trees into the vertex buffer for drawing. */
//=============================================================================
void W3DCustomEdging::loadEdgingsInVertexAndIndexBuffers(WorldHeightMap *pMap, Int minX, Int maxX, Int minY, Int maxY)
{
if (!m_indexEdging || !m_vertexEdging || !m_initialized) {
return;
}
if (!m_anythingChanged) {
return;
}
m_anythingChanged = false;
m_curNumEdgingVertices = 0;
m_curNumEdgingIndices = 0;
VertexFormatXYZDUV2 *vb;
UnsignedShort *ib;
// Lock the buffers.
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexEdging);
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexEdging);
vb=(VertexFormatXYZDUV2*)lockVtxBuffer.Get_Vertex_Array();
ib = lockIdxBuffer.Get_Index_Array();
UnsignedShort *curIb = ib;
VertexFormatXYZDUV2 *curVb = vb;
if (minX<0) minX = 0;
if (minY<0) minY = 0;
if (maxX >= pMap->getXExtent()) maxX = pMap->getXExtent()-1;
if (maxY >= pMap->getYExtent()) maxY = pMap->getYExtent()-1;
Int row;
Int column;
for (row=minY; row<maxY-1; row++) {
for (column = minX; column < maxX-1; column++) {
Int cellNdx = column+row*pMap->getXExtent();
Int blend = pMap->m_blendTileNdxes[cellNdx];
if (blend == 0) continue; // no blend.
if (pMap->m_blendedTiles[blend].customBlendEdgeClass<0) continue; // alpha blend.
Int i, j;
Real uOffset;
Real vOffset;
if (pMap->m_blendedTiles[blend].horiz) {
uOffset = 0;
vOffset = 0.25f * (1 + (row&1));
if (pMap->m_blendedTiles[blend].inverted) {
uOffset = 0.75f;
}
} else if (pMap->m_blendedTiles[blend].vert) {
vOffset = 0.75;
uOffset = 0.25f * (1 + (column&1));
if (pMap->m_blendedTiles[blend].inverted) {
vOffset = 0.0f;
}
} else if (pMap->m_blendedTiles[blend].rightDiagonal) {
if (pMap->m_blendedTiles[blend].longDiagonal) {
vOffset = 0.25;
uOffset = 0.5;
if (pMap->m_blendedTiles[blend].inverted) {
uOffset = 0.5f;
vOffset = 0.5f;
}
} else {
vOffset = .75;
uOffset = 0;
if (pMap->m_blendedTiles[blend].inverted) {
uOffset = 0.0f;
vOffset = 0.0f;
}
}
} else if (pMap->m_blendedTiles[blend].leftDiagonal) {
if (pMap->m_blendedTiles[blend].longDiagonal) {
uOffset = 0.25f;
vOffset = 0.25f;
if (pMap->m_blendedTiles[blend].inverted) {
uOffset = 0.25f;
vOffset = 0.5f;
}
} else {
vOffset = 0.75;
uOffset = 0.75f;
if (pMap->m_blendedTiles[blend].inverted) {
uOffset = 0.75f;
vOffset = 0.0f;
}
}
} else {
continue;
}
Region2D range;
pMap->getUVForBlend(pMap->m_blendedTiles[blend].customBlendEdgeClass, &range);
uOffset = range.lo.x + range.width()*uOffset;
vOffset = range.lo.y + range.height()*vOffset;
UnsignedByte alpha[4];
float UA[4], VA[4];
Bool flipForBlend;
pMap->getAlphaUVData(column-pMap->getDrawOrgX(), row-pMap->getDrawOrgY(), UA, VA, alpha, &flipForBlend, false);
Int startVertex = m_curNumEdgingVertices;
for (j=0; j<2; j++) {
for (i=0; i<2; i++) {
if (m_curNumEdgingVertices >= MAX_EDGE_VERTEX) return;
cellNdx = column+i+(row+j)*pMap->getXExtent();
Int diffuse = TheTerrainRenderObject->getStaticDiffuse(column+i, row+j);
curVb->diffuse = 0x80000000 + (diffuse&0x00FFFFFF); // set alpha to 5b.
Real theZ;
theZ = ((float)pMap->getDataPtr()[cellNdx])*MAP_HEIGHT_SCALE;
Real X = (column+i)*MAP_XY_FACTOR;
Real Y = (row+j)*MAP_XY_FACTOR;
curVb->u2 = uOffset + i*0.25f*range.width();
curVb->v2 = vOffset + (1-j)*0.25f*range.height();
Int ndx;
if (j==0) ndx=i;
if (j==1) ndx = 3-i;
curVb->u1 = UA[ndx];
curVb->v1 = VA[ndx];
curVb->x = X;
curVb->y = Y;
curVb->z = theZ;
curVb++;
m_curNumEdgingVertices++;
}
}
Int yOffset = 2;
if (m_curNumEdgingIndices+6 > MAX_EDGE_INDEX) return;
#ifdef FLIP_TRIANGLES // jba - reduces "diamonding" in some cases, not others. Better cliffs, though.
if (flipForBlend) {
*curIb++ = startVertex + 1;
*curIb++ = startVertex + yOffset;
*curIb++ = startVertex ;
*curIb++ = startVertex + 1;
*curIb++ = startVertex + 1+yOffset;
*curIb++ = startVertex + yOffset;
}
else
#endif
{
*curIb++ = startVertex;
*curIb++ = startVertex + 1+yOffset;
*curIb++ = startVertex + yOffset;
*curIb++ = startVertex ;
*curIb++ = startVertex + 1;
*curIb++ = startVertex + 1+yOffset;
}
m_curNumEdgingIndices+=6;
}
}
m_anythingChanged = false;
}
//-----------------------------------------------------------------------------
// Public Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DCustomEdging::~W3DCustomEdging
//=============================================================================
/** Destructor. Releases w3d assets. */
//=============================================================================
W3DCustomEdging::~W3DCustomEdging(void)
{
freeEdgingBuffers();
}
//=============================================================================
// W3DCustomEdging::W3DCustomEdging
//=============================================================================
/** Constructor. Sets m_initialized to true if it finds the w3d models it needs
for the trees. */
//=============================================================================
W3DCustomEdging::W3DCustomEdging(void)
{
m_initialized = false;
m_vertexEdging = NULL;
m_indexEdging = NULL;
clearAllEdging();
allocateEdgingBuffers();
m_initialized = true;
}
//=============================================================================
// W3DCustomEdging::freeEdgingBuffers
//=============================================================================
/** Frees the index and vertex buffers. */
//=============================================================================
void W3DCustomEdging::freeEdgingBuffers(void)
{
REF_PTR_RELEASE(m_vertexEdging);
REF_PTR_RELEASE(m_indexEdging);
}
//=============================================================================
// W3DCustomEdging::allocateEdgingBuffers
//=============================================================================
/** Allocates the index and vertex buffers. */
//=============================================================================
void W3DCustomEdging::allocateEdgingBuffers(void)
{
m_vertexEdging=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV2,MAX_EDGE_VERTEX+4,DX8VertexBufferClass::USAGE_DYNAMIC));
m_indexEdging=NEW_REF(DX8IndexBufferClass,(2*MAX_EDGE_INDEX+4, DX8IndexBufferClass::USAGE_DYNAMIC));
m_curNumEdgingVertices=0;
m_curNumEdgingIndices=0;
//m_edgeTexture = MSGNEW("TextureClass") TextureClass("EdgingTemplate.tga","EdgingTemplate.tga", TextureClass::MIP_LEVELS_3);
}
//=============================================================================
// W3DCustomEdging::clearAllEdging
//=============================================================================
/** Removes all trees. */
//=============================================================================
void W3DCustomEdging::clearAllEdging(void)
{
m_curNumEdgingVertices=0;
m_curNumEdgingIndices=0;
m_anythingChanged = true;
}
//=============================================================================
// W3DCustomEdging::drawEdging
//=============================================================================
/** Draws the trees. Uses camera to cull. */
//=============================================================================
void W3DCustomEdging::drawEdging(WorldHeightMap *pMap, Int minX, Int maxX, Int minY, Int maxY,
TextureClass * terrainTexture, TextureClass * cloudTexture, TextureClass * noiseTexture)
{
static Bool foo = false;
if (foo) {
return;
}
//m_anythingChanged = true;
loadEdgingsInVertexAndIndexBuffers(pMap, minX, maxX, minY, maxY);
if (m_curNumEdgingIndices == 0) {
return;
}
TextureClass *edgeTex = pMap->getEdgeTerrainTexture();
// Setup the vertex buffer, shader & texture.
DX8Wrapper::Set_Index_Buffer(m_indexEdging,0);
DX8Wrapper::Set_Vertex_Buffer(m_vertexEdging);
DX8Wrapper::Set_Shader(detailAlphaTestShader);
#ifdef _DEBUG
//DX8Wrapper::Set_Shader(detailShader); // shows clipping.
#endif
DX8Wrapper::Set_Texture(0,terrainTexture);
DX8Wrapper::Set_Texture(1,edgeTex);
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x7B);
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_LESSEQUAL); //pass pixels who's alpha is not zero
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering.
DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices);
DX8Wrapper::Set_Texture(0,edgeTex);
DX8Wrapper::Set_Texture(1, NULL);
// Draw the custom edge.
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x84);
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL); //pass pixels who's alpha is not zero
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering.
DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices);
#if 0 // Dumps out unmasked data.
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHABLENDENABLE,false);
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, false); //test pixels if transparent(clipped) before rendering.
DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices);
#endif
DX8Wrapper::Set_Texture(1, NULL);
if (cloudTexture) {
DX8Wrapper::Set_Shader(detailOpaqueShader);
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Set_Texture(1,edgeTex);
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Set_Texture(0,cloudTexture);
DX8Wrapper::Apply_Render_State_Changes();
#if 1
DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ALPHAARG1, D3DTA_CURRENT );
DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLORARG2, D3DTA_TEXTURE );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAARG1, D3DTA_CURRENT );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAARG2, D3DTA_TEXTURE );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );
DX8Wrapper::Set_DX8_Texture_Stage_State( 1, D3DTSS_TEXCOORDINDEX, 1 );
#endif
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x80);
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_NOTEQUAL); //pass pixels who's alpha is not zero
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering.
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHABLENDENABLE,true);
DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR);
DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND,D3DBLEND_ZERO);
DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices);
}
if (noiseTexture) {
DX8Wrapper::Set_Texture(1, NULL);
DX8Wrapper::Set_Texture(0,noiseTexture);
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Set_Texture(1,edgeTex);
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAREF,0x80);
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHAFUNC,D3DCMP_NOTEQUAL); //pass pixels who's alpha is not zero
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHATESTENABLE, true); //test pixels if transparent(clipped) before rendering.
DX8Wrapper::Set_DX8_Render_State(D3DRS_ALPHABLENDENABLE,true);
DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR);
DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND,D3DBLEND_ZERO);
DX8Wrapper::Draw_Triangles( m_curEdgingIndexOffset, m_curNumEdgingIndices/3, 0, m_curNumEdgingVertices);
}
}

View file

@ -0,0 +1,163 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Generals
//
// Module: Debug
//
// File name: W3DGameDevice/GameClisnt/W3DDebugDisplay.cpp
//
// Created: 11/13/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DDebugDisplay.h"
#include "GameClient/GameFont.h"
#include "GameClient/DisplayStringManager.h"
#include "GameClient/DisplayString.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// W3DDebugDisplay::W3DDebugDisplay
//============================================================================
W3DDebugDisplay::W3DDebugDisplay()
: m_displayString(NULL)
{
}
//============================================================================
// W3DDebugDisplay::~W3DDebugDisplay
//============================================================================
W3DDebugDisplay::~W3DDebugDisplay()
{
if ( m_displayString )
{
TheDisplayStringManager->freeDisplayString( m_displayString );
}
}
//============================================================================
// W3DDebugDisplay::init
//============================================================================
void W3DDebugDisplay::init( void )
{
m_displayString = TheDisplayStringManager->newDisplayString();
}
//============================================================================
// W3DDebugDisplay::drawText
//============================================================================
void W3DDebugDisplay::drawText( Int x, Int y, Char *text )
{
if ( m_font == NULL || m_displayString == NULL )
{
return ;
}
::Color textColor = GameMakeColor( 255, 255, 255, 255 );
::Color dropColor = GameMakeColor( 0, 0, 0, 255 );
UnicodeString unicode;
unicode.translate( AsciiString( text ) );
m_displayString->setText( unicode );
m_displayString->draw( x*m_fontWidth, 13 + y*m_fontHeight, textColor, dropColor );
}
//============================================================================
// W3DDebugDisplay::setFont
//============================================================================
void W3DDebugDisplay::setFont( GameFont *font )
{
m_font = font;
if ( m_displayString )
{
m_displayString->setFont( m_font );
}
}

View file

@ -0,0 +1,315 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DDebugIcons.cpp ////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: Heightmap.cpp
//
// Created: John Ahlquist, March 2002
//
// Desc: Draws huge numbers of debug icons for pathfinding quickly.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DDebugIcons.h"
#include "Common/GlobalData.h"
#include "GameLogic/GameLogic.h"
#include "Common/MapObject.h"
#include "WW3D2/DX8Wrapper.h"
#if defined _DEBUG || defined _INTERNAL
// Texturing, no zbuffer, disabled zbuffer write, primary gradient, alpha blending
#define SC_OPAQUE ( SHADE_CNST(ShaderClass::PASS_ALWAYS, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_DISABLE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Texturing, no zbuffer, disabled zbuffer write, primary gradient, alpha blending
#define SC_ALPHA ( SHADE_CNST(ShaderClass::PASS_ALWAYS, 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_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Texturing, no zbuffer, disabled zbuffer write, primary gradient, alpha blending
#define SC_ALPHA_Z ( 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_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Texturing, no zbuffer, disabled zbuffer write, primary gradient, alpha blending
#define SC_OPAQUE_Z ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_DISABLE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color)
{
W3DDebugIcons::addIcon(pos, width, numFramesDuration, color);
}
struct DebugIcon {
Coord3D position;
Real width; // all are squares centered about pos.
RGBColor color;
Int endFrame; // Frame when this disappears.
};
DebugIcon *W3DDebugIcons::m_debugIcons = NULL;
Int W3DDebugIcons::m_numDebugIcons = 0;
W3DDebugIcons::~W3DDebugIcons(void)
{
REF_PTR_RELEASE(m_vertexMaterialClass);
if (m_debugIcons) delete m_debugIcons;
m_debugIcons = NULL;
m_numDebugIcons = 0;
}
W3DDebugIcons::W3DDebugIcons(void)
{
//go with a preset material for now.
m_vertexMaterialClass=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
allocateIconsArray();
}
bool W3DDebugIcons::Cast_Ray(RayCollisionTestClass & raytest)
{
return false;
}
//@todo: MW Handle both of these properly!!
W3DDebugIcons::W3DDebugIcons(const W3DDebugIcons & src)
{
*this = src;
}
W3DDebugIcons & W3DDebugIcons::operator = (const W3DDebugIcons & that)
{
DEBUG_CRASH(("oops"));
return *this;
}
void W3DDebugIcons::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
{
Vector3 ObjSpaceCenter(TheGlobalData->m_waterExtentX,TheGlobalData->m_waterExtentY,50*MAP_XY_FACTOR);
float length = ObjSpaceCenter.Length();
sphere.Init(ObjSpaceCenter, length);
}
void W3DDebugIcons::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
{
Vector3 minPt(-2*TheGlobalData->m_waterExtentX,-2*TheGlobalData->m_waterExtentY,0);
Vector3 maxPt(2*TheGlobalData->m_waterExtentX,2*TheGlobalData->m_waterExtentY,100*MAP_XY_FACTOR);
box.Init(minPt,maxPt);
}
Int W3DDebugIcons::Class_ID(void) const
{
return RenderObjClass::CLASSID_UNKNOWN;
}
RenderObjClass * W3DDebugIcons::Clone(void) const
{
return NEW W3DDebugIcons(*this); // poolify
}
void W3DDebugIcons::allocateIconsArray(void)
{
m_debugIcons = NEW DebugIcon[MAX_ICONS];
m_numDebugIcons = 0;
}
void W3DDebugIcons::compressIconsArray(void)
{
if (m_debugIcons && m_numDebugIcons > 0) {
Int newNum = 0;
Int i;
for (i=0; i<m_numDebugIcons; i++) {
if (m_debugIcons[i].endFrame >= TheGameLogic->getFrame() && i>newNum) {
m_debugIcons[newNum] = m_debugIcons[i];
newNum++;
}
}
m_numDebugIcons = newNum;
}
}
static Int maxIcons = 0;
void W3DDebugIcons::addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color)
{
if (pos==NULL) {
if (m_numDebugIcons > maxIcons) {
DEBUG_LOG(("Max icons %d\n", m_numDebugIcons));
maxIcons = m_numDebugIcons;
}
m_numDebugIcons = 0;
return;
}
if (m_numDebugIcons>= MAX_ICONS) return;
if (m_debugIcons==NULL) return;
m_debugIcons[m_numDebugIcons].position = *pos;
m_debugIcons[m_numDebugIcons].width = width;
m_debugIcons[m_numDebugIcons].color = color;
m_debugIcons[m_numDebugIcons].endFrame = TheGameLogic->getFrame()+numFramesDuration;
m_numDebugIcons++;
}
/** Render draws into the current 3d context. */
void W3DDebugIcons::Render(RenderInfoClass & rinfo)
{
//
if (WW3D::Are_Static_Sort_Lists_Enabled()) {
WW3D::Add_To_Static_Sort_List(this, 1);
return;
}
//
Bool anyVanished = false;
if (m_numDebugIcons==0) return;
DX8Wrapper::Apply_Render_State_Changes();
DX8Wrapper::Set_Material(m_vertexMaterialClass);
DX8Wrapper::Set_Texture(0, NULL);
DX8Wrapper::Apply_Render_State_Changes();
Matrix3D tm(Transform);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
Int numRect = m_numDebugIcons;
static Real offset = 30;
const Int MAX_RECT = 5000; // cap drawing n rects.
if (numRect > MAX_RECT) numRect = MAX_RECT;
offset+= 0.5f;
Int k;
for (k=0; k<m_numDebugIcons;) {
Int curIndex = 0;
Int numVertex = 0;
DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,DX8_FVF_XYZNDUV2,numRect*4);
DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,numRect*6);
{
DynamicVBAccessClass::WriteLockClass lock(&vb_access);
VertexFormatXYZNDUV2* vb= lock.Get_Formatted_Vertex_Array();
DynamicIBAccessClass::WriteLockClass lockib(&ib_access);
if (!vb) return;
UnsignedShort *ib=lockib.Get_Index_Array();
UnsignedShort *curIb = ib;
// VertexFormatXYZNDUV2 *curVb = vb;
Real shadeR, shadeG, shadeB;
shadeR = 0;
shadeG = 0;
shadeB = 255;
for(; numVertex<numRect*4 && k<m_numDebugIcons; k++) {
Int theAlpha = 64;
const Int FADE_FRAMES = 100;
Int framesLeft = m_debugIcons[k].endFrame - TheGameLogic->getFrame();
if (framesLeft < 1) {
anyVanished = true;
continue;
}
if (framesLeft<FADE_FRAMES) {
theAlpha *= (Real)framesLeft/FADE_FRAMES;
}
RGBColor clr = m_debugIcons[k].color;
Real halfWidth = m_debugIcons[k].width/2;
Int diffuse = clr.getAsInt() | ((int)theAlpha << 24);
Coord3D pt1 = m_debugIcons[k].position;
vb->x= pt1.x-halfWidth;
vb->y= pt1.y-halfWidth;
vb->z= pt1.z;
vb->diffuse=diffuse; // b g<<8 r<<16 a<<24.
vb->u1=0 ;
vb->v1=0 ;
vb++;
vb->x= pt1.x+halfWidth;
vb->y= pt1.y-halfWidth;
vb->z= pt1.z;
vb->diffuse=diffuse; // b g<<8 r<<16 a<<24.
vb->u1=0 ;
vb->v1=0 ;
vb++;
vb->x= pt1.x+halfWidth;
vb->y= pt1.y+halfWidth;
vb->z= pt1.z;
vb->diffuse=diffuse; // b g<<8 r<<16 a<<24.
vb->u1=0 ;
vb->v1=0 ;
vb++;
vb->x= pt1.x-halfWidth;
vb->y= pt1.y+halfWidth;
vb->z= pt1.z;
vb->diffuse=diffuse; // b g<<8 r<<16 a<<24.
vb->u1=0 ;
vb->v1=0 ;
vb++;
*curIb++ = numVertex;
*curIb++ = numVertex+1;
*curIb++ = numVertex+2;
*curIb++ = numVertex;
*curIb++ = numVertex+2;
*curIb++ = numVertex+3;
curIndex += 6;
numVertex += 4;
}
}
if (numVertex == 0) break;
DX8Wrapper::Set_Shader(ShaderClass(SC_ALPHA));
DX8Wrapper::Set_Index_Buffer(ib_access,0);
DX8Wrapper::Set_Vertex_Buffer(vb_access);
DX8Wrapper::Draw_Triangles( 0,curIndex/3, 0, numVertex); //draw a quad, 2 triangles, 4 verts
}
if (anyVanished) {
compressIconsArray();
}
}
#endif // _DEBUG or _INTERNAL

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,401 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DDisplayString.cpp /////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DDisplayString.cpp
//
// Created: Colin Day, July 2001
//
// Desc: Display string W3D implementation, display strings hold
// double byte characters and all the data we need to render
// those strings to the screen.
//
//-----------------------------------------------------------------------------
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "GameClient/GameClient.h"
#include "W3DDevice/GameClient/W3DDisplayString.h"
#include "GameClient/HotKey.h"
#include "GameClient/GameFont.h"
#include "GameClient/GlobalLanguage.h"
// DEFINES ////////////////////////////////////////////////////////////////////
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
// PRIVATE TYPES //////////////////////////////////////////////////////////////
// PRIVATE DATA ///////////////////////////////////////////////////////////////
// PUBLIC DATA ////////////////////////////////////////////////////////////////
// PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
// PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DDisplayString::W3DDisplayString =========================================
/** */
//=============================================================================
W3DDisplayString::W3DDisplayString( void )
{
m_textChanged = FALSE;
m_textPos.x = 0;
m_textPos.y = 0;
m_currTextColor = 0;
m_currDropColor = 0;
m_size.x = 0;
m_size.y = 0;
m_fontChanged = FALSE;
m_clipRegion.lo.x = 0;
m_clipRegion.lo.y = 0;
m_clipRegion.hi.x = 0;
m_clipRegion.hi.y = 0;
m_lastResourceFrame = 0;
m_useHotKey = FALSE;
m_hotKeyPos.x = 0;
m_hotKeyPos.y = 0;
m_hotKeyColor = GameMakeColor(255,255,255,255);
} // end W3DDisplayString
// W3DDisplayString::~W3DDisplayString ========================================
/** */
//=============================================================================
W3DDisplayString::~W3DDisplayString( void )
{
} // end ~W3DDisplayString
// W3DDisplayString::textChanged ==============================================
/** This method automatically gets called from some methods in the display
* class so that we can write our own code here to to appropriate things
* on the changing of string data */
//=============================================================================
void W3DDisplayString::notifyTextChanged( void )
{
// extend functionality
DisplayString::notifyTextChanged();
if(TheGlobalLanguageData)
{
if(TheGlobalLanguageData->m_useHardWrap == TRUE)
{
m_textRenderer.Set_Use_Hard_Word_Wrap(true);
m_textRendererHotKey.Set_Use_Hard_Word_Wrap(true);
}
else
{
m_textRenderer.Set_Use_Hard_Word_Wrap(false);
m_textRendererHotKey.Set_Use_Hard_Word_Wrap(false);
}
}
// get our new text extents
computeExtents();
//
// set a flag so that if it comes that we need to render this string
// we know we must first build the sentence
//
m_textChanged = TRUE;
// reset data for our text renderer
m_textRenderer.Reset();
m_textRendererHotKey.Reset();
} // end notifyTextChanged
// W3DDisplayString::Draw =====================================================
/** Draw the text at the specified location in in the specified colors
* in the parameters. Since we keep an instance of the rendered text
* texture around, this is the time to check to see if any of our
* properties have changed since the last render, like color, or
* position, or content. If they have, we need to rebuild the sentence
* texture for rendering */
//=============================================================================
void W3DDisplayString::draw( Int x, Int y, Color color, Color dropColor )
{
draw(x,y, color, dropColor, 1, 1);
}
void W3DDisplayString::draw( Int x, Int y, Color color, Color dropColor, Int xDrop, Int yDrop )
{
Bool needNewPolys = FALSE;
// sanity
if( getTextLength() == 0 )
return; // nothing to draw
// if our font or text has changed we need to build a new sentence
if( m_fontChanged || m_textChanged )
{
if(m_useHotKey)
{
m_textRenderer.Set_Hot_Key_Parse(TRUE);
m_textRenderer.Build_Sentence( getText().str(), &m_hotKeyPos.x, &m_hotKeyPos.y );
m_hotkey.translate(TheHotKeyManager->searchHotKey(getText()));
if(!m_hotkey.isEmpty())
m_textRendererHotKey.Build_Sentence(m_hotkey.str(), NULL, NULL);
else
{
m_useHotKey = FALSE;
m_textRendererHotKey.Reset();
}
}
else
m_textRenderer.Build_Sentence( getText().str(), NULL, NULL );
m_fontChanged = FALSE;
m_textChanged = FALSE;
needNewPolys = TRUE;
} // end if
//
// if our position has changed, or our colors have chagned, or our
// text data has changed, we need to redo the texture quads
//
if( needNewPolys ||
x != m_textPos.x ||
y != m_textPos.y ||
color != m_currTextColor ||
dropColor != m_currDropColor )
{
// save the new attributes of the text position and color
m_textPos.x = x;
m_textPos.y = y;
m_currTextColor = color;
m_currDropColor = dropColor;
// reset the quads
m_textRenderer.Reset_Polys();
// draw the shadow
m_textRenderer.Set_Location( Vector2( m_textPos.x + xDrop, m_textPos.y + yDrop) );
m_textRenderer.Draw_Sentence( m_currDropColor );
// draw the text
m_textRenderer.Set_Location( Vector2( m_textPos.x, m_textPos.y ) );
m_textRenderer.Draw_Sentence( m_currTextColor );
if(m_useHotKey)
{
m_textRendererHotKey.Reset_Polys();
m_textRendererHotKey.Set_Location( Vector2( m_textPos.x + m_hotKeyPos.x , m_textPos.y +m_hotKeyPos.y) );
m_textRendererHotKey.Draw_Sentence( m_hotKeyColor );
m_textRendererHotKey.Render();
}
} // end if
// render the text
m_textRenderer.Render();
// we are for sure using display resources now
if( TheGameClient )
usingResources( TheGameClient->getFrame() );
} // end draw
// W3DDisplayString::getSize ==================================================
/** Get the render size width and height of the string in this instance
* with the font associated with it */
//=============================================================================
void W3DDisplayString::getSize( Int *width, Int *height )
{
// assign the width and height we have stored to parameters present
if( width )
*width = m_size.x;
if( height )
*height = m_size.y;
} // end getSize
// DisplayString::appendChar ==================================================
/** Get text with up to charPos characters, -1 = all characters */
//=============================================================================
Int W3DDisplayString::getWidth( Int charPos )
{
FontCharsClass * font;
Int width = 0;
Int count = 0;
font = m_textRenderer.Peek_Font();
if ( font )
{
const WideChar *text = m_textString.str();
WideChar ch;
while ( (ch = *text++ ) != 0 && ( charPos == -1 || count < charPos ) )
{
if ( ch != (WideChar)'\n' )
{
width += font->Get_Char_Spacing( ch );
}
count++;
}
}
return width;
}
// W3DDisplayString::setFont ==================================================
/** Set the font for this particular display string */
//=============================================================================
void W3DDisplayString::setFont( GameFont *font )
{
// sanity
if( font == NULL )
return;
// if the new font is the same as our existing font do nothing
if( m_font == font )
return;
// extending functionality
DisplayString::setFont( font );
// set the font in our renderer
m_textRenderer.Set_Font( static_cast<FontCharsClass *>(m_font->fontData) );
m_textRendererHotKey.Set_Font( static_cast<FontCharsClass *>(TheFontLibrary->getFont(font->nameString,font->pointSize, TRUE)->fontData) );
// recompute extents for text with new font
computeExtents();
// set flag telling us the font has changed since last render
m_fontChanged = TRUE;
} // end setFont
// W3DDisplayString::setClipRegion ============================================
/** Set the clipping region for the text */
//=============================================================================
void W3DDisplayString::setClipRegion( IRegion2D *region )
{
// extend functionality
DisplayString::setClipRegion( region );
// only consider regions that are actual changes
if( region->lo.x != m_clipRegion.lo.x ||
region->lo.y != m_clipRegion.lo.y ||
region->hi.x != m_clipRegion.hi.x ||
region->hi.y != m_clipRegion.hi.y )
{
// assign new region
m_clipRegion = *region;
// set new region in renderer
m_textRenderer.Set_Clipping_Rect( RectClass( m_clipRegion.lo.x,
m_clipRegion.lo.y,
m_clipRegion.hi.x,
m_clipRegion.hi.y ) );
m_textRendererHotKey.Set_Clipping_Rect( RectClass( m_clipRegion.lo.x,
m_clipRegion.lo.y,
m_clipRegion.hi.x,
m_clipRegion.hi.y ) );
} // end if
} // end setClipRegion
// W3DDisplayString::computeExtents ===========================================
/** Update the width and height of our string */
//=============================================================================
void W3DDisplayString::computeExtents( void )
{
UnsignedInt len = getTextLength();
// if we have no string, or no font we don't have a size yet
if( len == 0 || m_font == NULL )
{
m_size.x = 0;
m_size.y = 0;
} // end if
else
{
Vector2 extents = m_textRenderer.Get_Formatted_Text_Extents(getText().str()); //Get_Text_Extents( getText().str() );
m_size.x = extents.X;
m_size.y = extents.Y;
} // end else
} // end computeExtents
// W3DDisplayString::setWordWrap ===========================================
/** Set the wordwrap of the m_textRenderer */
//=============================================================================
void W3DDisplayString::setWordWrap( Int wordWrap )
{
// set the Word Wrap
if(m_textRenderer.Set_Wrapping_Width(wordWrap))
notifyTextChanged();
}// void setWordWrap( Int wordWrap )
void W3DDisplayString::setUseHotkey( Bool useHotkey, Color hotKeyColor )
{
m_useHotKey = useHotkey;
m_hotKeyColor = hotKeyColor;
m_textRenderer.Set_Hot_Key_Parse(useHotkey);
notifyTextChanged();
}
// W3DDisplayString::setWordWrapCentered ======================================
/** Set the whether or not we want to center each new line in a text string */
//=============================================================================
void W3DDisplayString::setWordWrapCentered( Bool isCentered )
{
// set the Word Wrap
if( m_textRenderer.Set_Word_Wrap_Centered(isCentered) )
notifyTextChanged();
}// void setWordWrap( Int wordWrap )

View file

@ -0,0 +1,234 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DDisplayStringManager.cpp //////////////////////////////////////////////////////////////
// Created: Colin Day, July 2001
// Desc: Display string Manager for W3D
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include "Common/Debug.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameText.h"
#include "GameClient/DisplayString.h"
#include "GameClient/DrawGroupInfo.h"
#include "GameClient/GlobalLanguage.h"
#include "W3DDevice/GameClient/W3DDisplayStringManager.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
W3DDisplayStringManager::W3DDisplayStringManager( void )
{
for (Int i = 0; i < 10; ++i)
{
m_groupNumeralStrings[i] = NULL;
}
m_formationLetterDisplayString = NULL;
}
//-------------------------------------------------------------------------------------------------
W3DDisplayStringManager::~W3DDisplayStringManager( void )
{
for (Int i = 0; i < 10; ++i)
{
if (m_groupNumeralStrings[i])
freeDisplayString(m_groupNumeralStrings[i]);
m_groupNumeralStrings[i] = NULL;
}
if (m_formationLetterDisplayString)
freeDisplayString( m_formationLetterDisplayString );
m_formationLetterDisplayString = NULL;
}
//-------------------------------------------------------------------------------------------------
void W3DDisplayStringManager::postProcessLoad( void )
{
// Get the font.
GameFont *font = TheFontLibrary->getFont(
TheDrawGroupInfo->m_fontName,
TheDrawGroupInfo->m_fontSize,
TheDrawGroupInfo->m_fontIsBold );
for (Int i = 0; i < 10; ++i)
{
m_groupNumeralStrings[i] = newDisplayString();
m_groupNumeralStrings[i]->setFont(font);
AsciiString displayNumber;
displayNumber.format("NUMBER:%d", i);
m_groupNumeralStrings[i]->setText(TheGameText->fetch(displayNumber));
}
m_formationLetterDisplayString = newDisplayString();
m_formationLetterDisplayString->setFont(font);
AsciiString displayLetter;
displayLetter.format("LABEL:FORMATION");
m_formationLetterDisplayString->setText(TheGameText->fetch(displayLetter));
}
//-------------------------------------------------------------------------------------------------
/** Allocate a new display string and tie it to the master list so we
* can keep track of it */
//-------------------------------------------------------------------------------------------------
DisplayString *W3DDisplayStringManager::newDisplayString( void )
{
DisplayString *newString = newInstance(W3DDisplayString);
// sanity
if( newString == NULL )
{
DEBUG_LOG(( "newDisplayString: Could not allcoate new W3D display string\n" ));
assert( 0 );
return NULL;
} // end if
// assign a default font
if (TheGlobalLanguageData && TheGlobalLanguageData->m_defaultDisplayStringFont.name.isNotEmpty())
{
newString->setFont(TheFontLibrary->getFont(
TheGlobalLanguageData->m_defaultDisplayStringFont.name,
TheGlobalLanguageData->m_defaultDisplayStringFont.size,
TheGlobalLanguageData->m_defaultDisplayStringFont.bold) );
}
else
newString->setFont( TheFontLibrary->getFont( AsciiString("Times New Roman"), 12, FALSE ) );
// link string to list
link( newString );
// return our new string
return newString;
} // end newDisplayString
//-------------------------------------------------------------------------------------------------
/** Remove a display string from the master list and delete the data */
//-------------------------------------------------------------------------------------------------
void W3DDisplayStringManager::freeDisplayString( DisplayString *string )
{
// sanity
if( string == NULL )
return;
// unlink
unLink( string );
// if the string happens to fall where our current checkpoint was, set the checkpoint to null
if ( m_currentCheckpoint == string) {
m_currentCheckpoint = NULL;
}
// free data
string->deleteInstance();
} // end freeDisplayString
//-------------------------------------------------------------------------------------------------
/** Update method for our display string Manager ... if it's been too
* long since the last time a string has been rendered, we will free
* the rendering resources of the string, if it needs to render again
* the DisplayString will have to rebuild the rendering data before
* the draw will work */
//-------------------------------------------------------------------------------------------------
void W3DDisplayStringManager::update( void )
{
// call base in case we add something later
DisplayStringManager::update();
W3DDisplayString *string = static_cast<W3DDisplayString *>(m_stringList);
// if the m_currentCheckpoint is valid, use it for the starting point for the search
if (m_currentCheckpoint) {
string = static_cast<W3DDisplayString *>(m_currentCheckpoint);
}
UnsignedInt currFrame = TheGameClient->getFrame();
const UnsignedInt w3dCleanupTime = 60; /** any string not rendered after
this many frames will have its
render resources freed */
int numStrings = 10;
// looping through all the strings eats up a lot of ambient time. Instead,
// loop through 10 (arbitrarily chosen) or till the end is hit.
while ( numStrings-- && string)
{
//
// has this string "expired" in terms of using resources, a string
// with a resource frame of zero isn't using any resources at all
//
if( string->m_lastResourceFrame != 0 &&
currFrame - string->m_lastResourceFrame > w3dCleanupTime )
{
// free the resources
string->m_textRenderer.Reset();
string->m_textRendererHotKey.Reset();
//
// mark data in the string as changed so that if it needs to
// be drawn again it will know to reconstruct the render data
//
string->m_textChanged = TRUE;
//
// set the last resource frame to zero, this allows us to ignore it
// in future cleanup passes of this update routine
//
string->m_lastResourceFrame = 0;
} // end if
// move to next string
string = static_cast<W3DDisplayString *>(string->next());
} // end while
// reset the starting point for our next search
m_currentCheckpoint = string;
} // end update
//-------------------------------------------------------------------------------------------------
DisplayString *W3DDisplayStringManager::getGroupNumeralString( Int numeral )
{
if (numeral < 0 || numeral > 9) {
DEBUG_CRASH(("Numeral '%d' out of range.\n", numeral));
return m_groupNumeralStrings[0];
}
return m_groupNumeralStrings[numeral];
}

View file

@ -0,0 +1,87 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// W3DDynamicLight.cpp
// Class to handle dynamic lights.
// Author: John Ahlquist, April 2001
#include <stdlib.h>
#include "W3DDevice/GameClient/W3DDynamicLight.h"
W3DDynamicLight::W3DDynamicLight(void):
LightClass(LightClass::POINT)
{
m_priorEnable = false;
m_enabled = true;
}
W3DDynamicLight::~W3DDynamicLight(void)
{
}
void W3DDynamicLight::On_Frame_Update(void)
{
if (!m_enabled) {
return;
}
Real factor = 1.0f;
if (m_curIncreaseFrameCount>0 && m_increaseFrameCount>0) {
// increasing
m_curIncreaseFrameCount--;
factor = (m_increaseFrameCount-m_curIncreaseFrameCount)/(Real)m_increaseFrameCount;
} else if (m_decayFrameCount==0) {
factor = 1.0; // never decays,
} else {
m_curDecayFrameCount--;
if (m_curDecayFrameCount == 0) {
m_enabled = false;
return;
}
factor = m_curDecayFrameCount/(Real)m_decayFrameCount;
}
if (m_decayRange) {
this->FarAttenEnd = factor*m_targetRange;
if (FarAttenEnd < FarAttenStart) {
FarAttenEnd = FarAttenStart;
}
}
if (m_decayColor) {
this->Ambient = m_targetAmbient*factor;
this->Diffuse = m_targetDiffuse*factor;
}
}
void W3DDynamicLight::setFrameFade(UnsignedInt frameIncreaseTime, UnsignedInt decayFrameTime)
{
m_decayFrameCount = decayFrameTime;
m_curDecayFrameCount = decayFrameTime;
m_curIncreaseFrameCount = frameIncreaseTime;
m_increaseFrameCount = frameIncreaseTime;
m_targetAmbient = Ambient;
m_targetDiffuse = Diffuse;
m_targetRange = FarAttenEnd;
}

View file

@ -0,0 +1,453 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DFileSystem.cpp ////////////////////////////////////////////////////////////////////////
//
// W3D implementation of a file factory. This replaces the W3D file factory,
// and uses GDI assets, so that
// W3D files and targa files are loaded using the GDI file interface.
// Note - this only servers up read only files.
//
// Author: John Ahlquist, Sept 2001
// Colin Day, November 2001
//
///////////////////////////////////////////////////////////////////////////////////////////////////
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
// for now we maintain old legacy files
// #define MAINTAIN_LEGACY_FILES
#include "Common/Debug.h"
#include "Common/File.h"
#include "Common/FileSystem.h"
#include "Common/GlobalData.h"
#include "Common/MapObject.h"
#include "Common/Registry.h"
#include "W3DDevice/GameClient/W3DFileSystem.h"
// DEFINES ////////////////////////////////////////////////////////////////////////////////////////
#include <io.h>
//-------------------------------------------------------------------------------------------------
/** Game file access. At present this allows us to access test assets, assets from
* legacy GDI assets, and the current flat directory access for textures, models etc */
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
typedef enum
{
FILE_TYPE_UNKNOWN = 0,
FILE_TYPE_W3D,
FILE_TYPE_TGA,
FILE_TYPE_DDS,
} GameFileType;
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
GameFileClass::GameFileClass( char const *filename )
{
m_fileExists = FALSE;
m_theFile = NULL;
m_filePath[ 0 ] = 0;
m_filename[0] = 0;
if( filename )
Set_Name( filename );
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
GameFileClass::GameFileClass( void )
{
m_fileExists = FALSE;
m_theFile = NULL;
m_filePath[ 0 ] = 0;
m_filename[ 0 ] = 0;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
GameFileClass::~GameFileClass()
{
Close();
}
//-------------------------------------------------------------------------------------------------
/** Gets the file name */
//-------------------------------------------------------------------------------------------------
char const * GameFileClass::File_Name( void ) const
{
return m_filename;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
inline static Bool isImageFileType( GameFileType fileType )
{
return (fileType == FILE_TYPE_TGA || fileType == FILE_TYPE_DDS);
}
//-------------------------------------------------------------------------------------------------
/** Sets the file name, and finds the GDI asset if present. */
//-------------------------------------------------------------------------------------------------
char const * GameFileClass::Set_Name( char const *filename )
{
if( Is_Open() )
Close();
// save the filename
strncpy( m_filename, filename, _MAX_PATH );
char name[_MAX_PATH];
const Int EXT_LEN = 32;
char extension[EXT_LEN];
extension[0] = 0;
strcpy(name, filename);
Int i = strlen(name);
i--;
Int extLen = 1;
while(i>0 && extLen < EXT_LEN) {
if (name[i] == '.') {
strcpy(extension, name+i);
name[i] = 0;
break;
}
i--;
extLen++;
}
Int j = 0;
// Strip out spaces.
for (i=0; name[i]; i++) {
if (name[i] != ' ') {
name[j] = name[i];
j++;
}
}
name[j] = 0;
// test the extension to recognize a few key file types
GameFileType fileType = FILE_TYPE_UNKNOWN;
if( stricmp( extension, ".w3d" ) == 0 )
fileType = FILE_TYPE_W3D;
else if( stricmp( extension, ".tga" ) == 0 )
fileType = FILE_TYPE_TGA;
else if( stricmp( extension, ".dds" ) == 0 )
fileType = FILE_TYPE_DDS;
// all .w3d files are in W3D_DIR_PATH, all .tga files are in TGA_DIR_PATH
if( fileType == FILE_TYPE_W3D )
{
strcpy( m_filePath, W3D_DIR_PATH );
strcat( m_filePath, filename );
} // end if
else if( isImageFileType(fileType) )
{
strcpy( m_filePath, TGA_DIR_PATH );
strcat( m_filePath, filename );
} // end else if
else
strcpy( m_filePath, filename );
// see if the file exists
m_fileExists = TheFileSystem->doesFileExist( m_filePath );
// maintain legacy compatibility directories for now
#ifdef MAINTAIN_LEGACY_FILES
if( m_fileExists == FALSE )
{
if( fileType == FILE_TYPE_W3D )
{
strcpy( m_filePath, LEGACY_W3D_DIR_PATH );
strcat( m_filePath, filename );
} // end if
else if( isImageFileType(fileType) )
{
strcpy( m_filePath, LEGACY_TGA_DIR_PATH );
strcat( m_filePath, filename );
} // end else if
// see if the file exists
m_fileExists = TheFileSystem->doesFileExist( m_filePath );
} // end if
#endif
// if file is still not found, try the test art folders
#ifdef LOAD_TEST_ASSETS
if( m_fileExists == FALSE )
{
if( fileType == FILE_TYPE_W3D )
{
strcpy( m_filePath, TEST_W3D_DIR_PATH );
strcat( m_filePath, filename );
} // end if
else if( isImageFileType(fileType) )
{
strcpy( m_filePath, TEST_TGA_DIR_PATH );
strcat( m_filePath, filename );
} // end else if
// see if the file exists
m_fileExists = TheFileSystem->doesFileExist( m_filePath );
} // end if
#endif
// We allow the user to load their own images for various assets (like the control bar)
if( m_fileExists == FALSE && TheGlobalData)
{
if( fileType == FILE_TYPE_W3D )
{
sprintf(m_filePath,USER_W3D_DIR_PATH, TheGlobalData->getPath_UserData().str());
//strcpy( m_filePath, USER_W3D_DIR_PATH );
strcat( m_filePath, filename );
} // end if
if( isImageFileType(fileType) )
{
sprintf(m_filePath,USER_TGA_DIR_PATH, TheGlobalData->getPath_UserData().str());
//strcpy( m_filePath, USER_TGA_DIR_PATH );
strcat( m_filePath, filename );
} // end else if
// see if the file exists
m_fileExists = TheFileSystem->doesFileExist( m_filePath );
} // end if
// We Need to be able to "temporarily copy over the map preview for whichever directory it came from
if( m_fileExists == FALSE && TheGlobalData)
{
if( fileType == FILE_TYPE_TGA ) // just TGA, since we don't dds previews
{
sprintf(m_filePath,MAP_PREVIEW_DIR_PATH, TheGlobalData->getPath_UserData().str());
//strcpy( m_filePath, USER_TGA_DIR_PATH );
strcat( m_filePath, filename );
} // end else if
// see if the file exists
m_fileExists = TheFileSystem->doesFileExist( m_filePath );
} // end if
// We need to be able to grab images from a localization dir, because Art has a fetish for baked-in text. Munkee.
if( m_fileExists == FALSE )
{
if( isImageFileType(fileType) )
{
static const char *localizedPathFormat = "Data/%s/Art/Textures/";
sprintf(m_filePath,localizedPathFormat, GetRegistryLanguage().str());
strcat( m_filePath, filename );
} // end else if
// see if the file exists
m_fileExists = TheFileSystem->doesFileExist( m_filePath );
} // end if
return m_filename;
}
//-------------------------------------------------------------------------------------------------
/** If we found a gdi asset, the file is available. */
//-------------------------------------------------------------------------------------------------
bool GameFileClass::Is_Available( int forced )
{
// not maintaining any GDF compatibility, all files should be where the m_filePath says
return m_fileExists;
}
//-------------------------------------------------------------------------------------------------
/** Is the file open. */
//-------------------------------------------------------------------------------------------------
bool GameFileClass::Is_Open(void) const
{
return m_theFile != NULL;
}
//-------------------------------------------------------------------------------------------------
/** Open the named file. */
//-------------------------------------------------------------------------------------------------
int GameFileClass::Open(char const *filename, int rights)
{
Set_Name(filename);
if (Is_Available(false)) {
return(Open(rights));
}
return(false);
}
//-------------------------------------------------------------------------------------------------
/** Open the file using the current file name. */
//-------------------------------------------------------------------------------------------------
int GameFileClass::Open(int rights)
{
if( rights != READ )
{
return(false);
}
// just open up the file in m_filePath
m_theFile = TheFileSystem->openFile( m_filePath, File::READ | File::BINARY );
return (m_theFile != NULL);
}
//-------------------------------------------------------------------------------------------------
/** Read. */
//-------------------------------------------------------------------------------------------------
int GameFileClass::Read(void *buffer, int len)
{
if (m_theFile) {
return m_theFile->read(buffer, len);
}
return(0);
}
//-------------------------------------------------------------------------------------------------
/** Seek. */
//-------------------------------------------------------------------------------------------------
int GameFileClass::Seek(int pos, int dir)
{
File::seekMode mode = File::CURRENT;
switch (dir) {
default:
case SEEK_CUR: mode = File::CURRENT; break;
case SEEK_SET: mode = File::START; break;
case SEEK_END: mode = File::END; break;
}
if (m_theFile) {
return m_theFile->seek(pos, mode);
}
return 0xFFFFFFFF;
}
//-------------------------------------------------------------------------------------------------
/** Size. */
//-------------------------------------------------------------------------------------------------
int GameFileClass::Size(void)
{
if (m_theFile) {
return m_theFile->size();
}
return 0xFFFFFFFF;
}
//-------------------------------------------------------------------------------------------------
/** Write. */
//-------------------------------------------------------------------------------------------------
int GameFileClass::Write(void const *buffer, Int len)
{
#ifdef _DEBUG
#endif
return(0);
}
//-------------------------------------------------------------------------------------------------
/** Close. */
//-------------------------------------------------------------------------------------------------
void GameFileClass::Close(void)
{
if (m_theFile) {
m_theFile->close();
m_theFile = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// W3DFileSystem Class ////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
extern W3DFileSystem *TheW3DFileSystem = NULL;
//-------------------------------------------------------------------------------------------------
/** Constructor. Creating an instance of this class overrices the default
W3D file factory. */
//-------------------------------------------------------------------------------------------------
W3DFileSystem::W3DFileSystem(void)
{
_TheFileFactory = this; // override the w3d file factory.
}
//-------------------------------------------------------------------------------------------------
/** Destructor. This removes the W3D file factory, so shouldn't be done until
after W3D is shutdown. */
//-------------------------------------------------------------------------------------------------
W3DFileSystem::~W3DFileSystem(void)
{
_TheFileFactory = NULL; // remove the w3d file factory.
}
//-------------------------------------------------------------------------------------------------
/** Gets a file with the specified filename. */
//-------------------------------------------------------------------------------------------------
FileClass * W3DFileSystem::Get_File( char const *filename )
{
return NEW GameFileClass( filename ); // poolify
}
//-------------------------------------------------------------------------------------------------
/** Releases a file returned by Get_File. */
//-------------------------------------------------------------------------------------------------
void W3DFileSystem::Return_File( FileClass *file )
{
delete file;
}

View file

@ -0,0 +1,224 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DGameClient.cpp /////////////////////////////////////////////////
//
// W3DImplementaion of the GameClient. If there were a client/server
// architecture, this game interface could be thought of as the "client"
// that the user uses to interact with the logic of the game world which
// would be known as the "server"
//
// Author: Colin Day, April 2001
//
///////////////////////////////////////////////////////////////////////////////
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <stdlib.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "Common/ThingTemplate.h"
#include "Common/ThingFactory.h"
#include "Common/ModuleFactory.h"
#include "Common/RandomValue.h"
#include "Common/GlobalData.h"
#include "Common/GameLOD.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameClient.h"
#include "GameClient/ParticleSys.h"
#include "GameClient/RayEffect.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "W3DDevice/GameClient/W3DGameClient.h"
#include "W3DDevice/GameClient/W3DStatusCircle.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "W3DDevice/GameClient/W3DShadow.h"
#include "W3DDevice/GameClient/heightmap.h"
#include "WW3D2/Part_emt.h"
#include "WW3D2/HAnim.h"
#include "WW3D2/HTree.h"
#include "WW3D2/AnimObj.h" ///< @todo superhack for demo, remove!
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DGameClient::W3DGameClient()
{
} // end W3DGameClient
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DGameClient::~W3DGameClient()
{
} // end ~W3DGameClient
//-------------------------------------------------------------------------------------------------
/** Initialize resources for the w3d game client */
//-------------------------------------------------------------------------------------------------
void W3DGameClient::init( void )
{
// extending initialization routine
GameClient::init();
} // end init
//-------------------------------------------------------------------------------------------------
/** Per frame udpate, note we are extending functionality */
//-------------------------------------------------------------------------------------------------
void W3DGameClient::update( void )
{
// call base
GameClient::update();
} // end update
//-------------------------------------------------------------------------------------------------
/** Reset this device client system. Note we are extending reset functionality from
* the device independent client */
//-------------------------------------------------------------------------------------------------
void W3DGameClient::reset( void )
{
// call base class
GameClient::reset();
} // end reset
//-------------------------------------------------------------------------------------------------
/** allocate a new drawable using the thing template for initialization.
* if we want to have the thing manager actually contain the pools of
* object and drawable storage it seems OK to have it be friends with the
* GameLogic/Client for those purposes, or we could put the allocation pools
* in the GameLogic and GameClient themselves */
//-------------------------------------------------------------------------------------------------
Drawable *W3DGameClient::friend_createDrawable( const ThingTemplate *tmplate,
DrawableStatus statusBits )
{
Drawable *draw = NULL;
// sanity
if( tmplate == NULL )
return NULL;
draw = newInstance(Drawable)( tmplate, statusBits );
return draw;
}
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DGameClient::addScorch(const Coord3D *pos, Real radius, Scorches type)
{
if (TheTerrainRenderObject)
{
Vector3 loc(pos->x, pos->y, pos->z);
TheTerrainRenderObject->addScorch(loc, radius, type);
}
}
//-------------------------------------------------------------------------------------------------
/** create an effect that requires a start and end location */
//-------------------------------------------------------------------------------------------------
void W3DGameClient::createRayEffectByTemplate( const Coord3D *start,
const Coord3D *end,
const ThingTemplate* tmpl )
{
Drawable *draw = TheThingFactory->newDrawable(tmpl);
if( draw )
{
Coord3D pos;
// add to world, the location of the drawable is at the midpoint of laser
pos.x = (end->x - start->x) * 0.5f + start->x;
pos.y = (end->y - start->y) * 0.5f + start->y;
pos.z = (end->z - start->z) * 0.5f + start->z;
draw->setPosition( &pos );
// add this ray effect to the list of ray effects
TheRayEffects->addRayEffect( draw, start, end );
} // end if
} // end createRayEffectByTemplate
//-------------------------------------------------------------------------------------------------
/** Tell all the drawables what time of day it is now */
//-------------------------------------------------------------------------------------------------
void W3DGameClient::setTimeOfDay( TimeOfDay tod )
{
GameClient::setTimeOfDay(tod);
//tell cloud/water plane to update its lighting/texture
if (TheWaterRenderObj)
TheWaterRenderObj->setTimeOfDay(tod);
if (TheW3DShadowManager)
TheW3DShadowManager->setTimeOfDay(tod);
//tell the display to update its lighting
TheDisplay->setTimeOfDay( tod );
} // end setTimeOfDay
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DGameClient::setTeamColor(Int red, Int green, Int blue)
{
W3DStatusCircle::setColor(red, green, blue);
} // end setTeamColor
//-------------------------------------------------------------------------------------------------
/** temporary entry point for adjusting LOD for development testing. */
//-------------------------------------------------------------------------------------------------
void W3DGameClient::adjustLOD( Int adj )
{
if (TheGlobalData == NULL)
return;
TheWritableGlobalData->m_textureReductionFactor += adj;
if (TheWritableGlobalData->m_textureReductionFactor > 4)
TheWritableGlobalData->m_textureReductionFactor = 4; //16x less resolution is probably enough.
if (TheWritableGlobalData->m_textureReductionFactor < 0)
TheWritableGlobalData->m_textureReductionFactor = 0;
if (WW3D::Get_Texture_Reduction() != TheWritableGlobalData->m_textureReductionFactor)
{ WW3D::Set_Texture_Reduction(TheWritableGlobalData->m_textureReductionFactor,6);
TheGameLODManager->setCurrentTextureReduction(TheWritableGlobalData->m_textureReductionFactor);
}
//I commented this out because we're no longer using terrain LOD. So I
//stole this function and keys to adjust the texture resolution instead. -MW
// if( TheTerrainRenderObject )
// TheTerrainRenderObject->adjustTerrainLOD( adj );
} // end adjustLOD

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,736 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DInGameUI.cpp //////////////////////////////////////////////////////////////////////////
// Author: Colin Day, April 2001
// Desct: In game user interface implementation for W3D
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdlib.h>
#include "Common/GlobalData.h"
#include "Common/ThingTemplate.h"
#include "Common/ThingFactory.h"
#include "GameLogic/TerrainLogic.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/Object.h"
#include "GameClient/Drawable.h"
#include "GameClient/GadgetListBox.h"
#include "GameClient/GameClient.h"
#include "GameClient/GameWindowManager.h"
#include "GameClient/GadgetSlider.h"
#include "GameClient/ControlBar.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "W3DDevice/GameClient/W3DGUICallbacks.h"
#include "W3DDevice/GameClient/W3DInGameUI.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "W3DDevice/Common/W3DConvert.h"
#include "WW3D2/WW3D.h"
#include "WW3D2/HAnim.h"
#include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#ifdef _DEBUG
#include "W3DDevice/GameClient/HeightMap.h"
#include "WW3D2/DX8IndexBuffer.h"
#include "WW3D2/DX8VertexBuffer.h"
#include "WW3D2/VertMaterial.h"
class DebugHintObject : public RenderObjClass
{
public:
DebugHintObject(void);
DebugHintObject(const DebugHintObject & src);
DebugHintObject & operator = (const DebugHintObject &);
~DebugHintObject(void);
virtual RenderObjClass * Clone(void) const;
virtual int Class_ID(void) const;
virtual void Render(RenderInfoClass & rinfo);
virtual Bool Cast_Ray(RayCollisionTestClass & raytest);
virtual void Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const;
virtual void Get_Obj_Space_Bounding_Box(AABoxClass & aabox) const;
int updateBlock(void);
void freeMapResources(void);
void setLocAndColorAndSize(const Coord3D *loc, Int argb, Int size);
protected:
Coord3D m_myLoc;
Int m_myColor; // argb
Int m_mySize;
DX8IndexBufferClass *m_indexBuffer;
ShaderClass m_shaderClass; //shader or rendering state for heightmap
VertexMaterialClass *m_vertexMaterialClass;
DX8VertexBufferClass *m_vertexBufferTile; //First vertex buffer.
void initData(void);
};
// Texturing, no zbuffer, disabled zbuffer write, primary gradient, alpha blending
#define SC_ALPHA ( SHADE_CNST(ShaderClass::PASS_ALWAYS, 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_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
DebugHintObject::~DebugHintObject(void)
{
freeMapResources();
}
DebugHintObject::DebugHintObject(void) :
m_indexBuffer(NULL),
m_vertexMaterialClass(NULL),
m_vertexBufferTile(NULL),
m_myColor(0),
m_mySize(0)
{
initData();
}
Bool DebugHintObject::Cast_Ray(RayCollisionTestClass & raytest)
{
return false;
}
DebugHintObject::DebugHintObject(const DebugHintObject & src)
{
*this = src;
}
DebugHintObject & DebugHintObject::operator = (const DebugHintObject & that)
{
DEBUG_CRASH(("oops"));
return *this;
}
void DebugHintObject::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
{
Vector3 ObjSpaceCenter((float)1000*0.5f,(float)1000*0.5f,(float)0);
float length = ObjSpaceCenter.Length();
sphere.Init(ObjSpaceCenter, length);
}
void DebugHintObject::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
{
Vector3 minPt(0,0,0);
Vector3 maxPt((float)1000,(float)1000,(float)1000);
box.Init(minPt,maxPt);
}
Int DebugHintObject::Class_ID(void) const
{
return RenderObjClass::CLASSID_UNKNOWN;
}
RenderObjClass * DebugHintObject::Clone(void) const
{
DEBUG_CRASH(("oops"));
return NEW DebugHintObject(*this);
}
void DebugHintObject::freeMapResources(void)
{
REF_PTR_RELEASE(m_indexBuffer);
REF_PTR_RELEASE(m_vertexBufferTile);
REF_PTR_RELEASE(m_vertexMaterialClass);
}
//Allocate a heightmap of x by y vertices.
//data must be an array matching this size.
void DebugHintObject::initData(void)
{
freeMapResources(); //free old data and ib/vb
m_indexBuffer = NEW_REF(DX8IndexBufferClass,(3));
// Fill up the IB
{
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBuffer);
UnsignedShort *ib=lockIdxBuffer.Get_Index_Array();
ib[0]=0;
ib[1]=1;
ib[2]=2;
}
m_vertexBufferTile = NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,3,DX8VertexBufferClass::USAGE_DEFAULT));
//go with a preset material for now.
m_vertexMaterialClass = VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
//use a multi-texture shader: (text1*diffuse)*text2.
m_shaderClass = ShaderClass::ShaderClass(SC_ALPHA);
}
void DebugHintObject::setLocAndColorAndSize(const Coord3D *loc, Int argb, Int size)
{
m_myLoc = *loc;
m_myColor = argb;
m_mySize = size;
if (m_myLoc.z < 0 && TheTerrainRenderObject)
{
m_myLoc.z = TheTerrainRenderObject->getHeightMapHeight(m_myLoc.x, m_myLoc.y, NULL);
}
if (m_vertexBufferTile)
{
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexBufferTile);
VertexFormatXYZDUV1 *vb = (VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
Real x1 = m_mySize * 0.866; // cos(30)
Real y1 = m_mySize * 0.5; // sin(30)
// note, pts must go in a counterclockwise order!
vb[0].x = 0;
vb[0].y = m_mySize;
vb[0].z = 0;
vb[0].diffuse = m_myColor;
vb[0].u1 = 0;
vb[0].v1 = 0;
vb[1].x = -x1;
vb[1].y = -y1;
vb[1].z = 0;
vb[1].diffuse = m_myColor;
vb[1].u1 = 0;
vb[1].v1 = 0;
vb[2].x = x1;
vb[2].y = -y1;
vb[2].z = 0;
vb[2].diffuse = m_myColor;
vb[2].u1 = 0;
vb[2].v1 = 0;
}
}
void DebugHintObject::Render(RenderInfoClass & rinfo)
{
SphereClass bounds(Vector3(m_myLoc.x, m_myLoc.y, m_myLoc.z), m_mySize);
if (!rinfo.Camera.Cull_Sphere(bounds))
{
DX8Wrapper::Set_Material(m_vertexMaterialClass);
DX8Wrapper::Set_Shader(m_shaderClass);
DX8Wrapper::Set_Texture(0, NULL);
DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
DX8Wrapper::Set_Vertex_Buffer(m_vertexBufferTile);
Matrix3D tm(Transform);
Vector3 vec(m_myLoc.x, m_myLoc.y, m_myLoc.z);
tm.Set_Translation(vec);
DX8Wrapper::Set_Transform(D3DTS_WORLD, tm);
DX8Wrapper::Draw_Triangles( 0, 1, 0, 3);
}
}
#endif // _DEBUG
///////////////////////////////////////////////////////////////////////////////////////////////////
// DEFINITIONS
///////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DInGameUI::W3DInGameUI()
{
Int i;
for( i = 0; i < MAX_MOVE_HINTS; i++ )
{
m_moveHintRenderObj[ i ] = NULL;
m_moveHintAnim[ i ] = NULL;
} // end for i
m_buildingPlacementAnchor = NULL;
m_buildingPlacementArrow = NULL;
} // end W3DInGameUI
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DInGameUI::~W3DInGameUI()
{
Int i;
// remove render objects for hints
for( i = 0; i < MAX_MOVE_HINTS; i++ )
{
REF_PTR_RELEASE( m_moveHintRenderObj[ i ] );
REF_PTR_RELEASE( m_moveHintAnim[ i ] );
} // end for i
REF_PTR_RELEASE( m_buildingPlacementAnchor );
REF_PTR_RELEASE( m_buildingPlacementArrow );
} // end ~W3DInGameUI
// loadText ===================================================================
/** Load text from the file */
//=============================================================================
static void loadText( char *filename, GameWindow *listboxText )
{
if (!listboxText)
return;
GadgetListBoxReset(listboxText);
FILE *fp;
// open the file
fp = fopen( filename, "r" );
if( fp == NULL )
return;
char buffer[ 1024 ];
UnicodeString line;
Color color = GameMakeColor(255, 255, 255, 255);
while( fgets( buffer, 1024, fp ) != NULL )
{
line.translate(buffer);
line.trim();
if (line.isEmpty())
line = UnicodeString(L" ");
GadgetListBoxAddEntryText(listboxText, line, color, -1, -1);
} // end while
// close the file
fclose( fp );
} // end loadText
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::init( void )
{
// extending functionality
InGameUI::init();
// for the beta, they didn't want the help menu showing up, but I left this as a bock
// comment because we'll probably want to add this back in.
/*
// create the MOTD
GameWindow *motd = TheWindowManager->winCreateFromScript( AsciiString("MOTD.wnd") );
if( motd )
{
NameKeyType listboxTextID = TheNameKeyGenerator->nameToKey( "MOTD.wnd:ListboxMOTD" );
GameWindow *listboxText = TheWindowManager->winGetWindowFromId(motd, listboxTextID);
loadText( "HelpScreen.txt", listboxText );
// hide it for now
motd->winHide( TRUE );
} // end if*/
} // end init
//-------------------------------------------------------------------------------------------------
/** Update in game UI */
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::update( void )
{
// call base
InGameUI::update();
} // end update
//-------------------------------------------------------------------------------------------------
/** Reset the in game ui */
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::reset( void )
{
// call base
InGameUI::reset();
} // end reset
//-------------------------------------------------------------------------------------------------
/** Draw member for the W3D implemenation of the game user interface */
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::draw( void )
{
preDraw();
// draw selection region if drag selecting
if( m_isDragSelecting )
drawSelectionRegion();
// for each view draw hints
/// @todo should the UI be iterating through views like this?
if( TheDisplay )
{
View *view;
for( view = TheDisplay->getFirstView();
view;
view = TheDisplay->getNextView( view ) )
{
// draw move hints
drawMoveHints( view );
// draw attack hints
drawAttackHints( view );
// draw placement angle selection if needed
drawPlaceAngle( view );
} // end for view
} // end if
// repaint all our windows
#ifdef EXTENDED_STATS
if (!DX8Wrapper::stats.m_disableConsole) {
#endif
#ifdef DO_UNIT_TIMINGS
#pragma MESSAGE("*** WARNING *** DOING DO_UNIT_TIMINGS!!!!")
extern Bool g_UT_startTiming;
if (!g_UT_startTiming)
#endif
postDraw();
TheWindowManager->winRepaint();
#ifdef EXTENDED_STATS
}
#endif
} // end draw
//-------------------------------------------------------------------------------------------------
/** draw 2d selection region on screen */
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::drawSelectionRegion( void )
{
Real width = 2.0f;
UnsignedInt color = 0x9933FF33; //0xAARRGGBB
TheDisplay->drawOpenRect( m_dragSelectRegion.lo.x,
m_dragSelectRegion.lo.y,
m_dragSelectRegion.hi.x - m_dragSelectRegion.lo.x,
m_dragSelectRegion.hi.y - m_dragSelectRegion.lo.y,
width,
color );
} // end drawSelectionRegion
//-------------------------------------------------------------------------------------------------
/** Draw the visual feedback for clicking in the world and telling units
* to move there */
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::drawMoveHints( View *view )
{
Int i;
// Real width = 1.0f;
// UnsignedInt color = 0x9933FF33; //0xAARRGGBB
for( i = 0; i < MAX_MOVE_HINTS; i++ )
{
Int elapsed = TheGameClient->getFrame() - m_moveHint[i].frame;
if( elapsed <= 40 )
{
RectClass rect;
// if this hint is not in this view ignore it
/// @todo write this to check if point is visible in view
// if( view->pointInView( &m_moveHint[ i ].pos == FALSE )
// continue;
// create render object and add to scene of needed
if( m_moveHintRenderObj[ i ] == NULL )
{
RenderObjClass *hint;
HAnimClass *anim;
// create hint object
hint = W3DDisplay::m_assetManager->Create_Render_Obj(TheGlobalData->m_moveHintName.str());
AsciiString animName;
animName.format("%s.%s", TheGlobalData->m_moveHintName.str(), TheGlobalData->m_moveHintName.str());
anim = W3DDisplay::m_assetManager->Get_HAnim(animName.str());
// sanity
if( hint == NULL )
{
DEBUG_CRASH(("unable to create hint"));
return;
} // end if
// asign render objects to GUI data
m_moveHintRenderObj[ i ] = hint;
// note that 'anim' is returned from Get_HAnim with an AddRef, so we don't need to addref it again.
// however, we do need to release the contents of moveHintAnim (if any)
REF_PTR_RELEASE(m_moveHintAnim[i]);
m_moveHintAnim[i] = anim;
} // end if, create render objects
// show the render object if hidden
if( m_moveHintRenderObj[ i ]->Is_Hidden() == 1 ) {
m_moveHintRenderObj[ i ]->Set_Hidden( 0 );
// add to scene
W3DDisplay::m_3DScene->Add_Render_Object( m_moveHintRenderObj[ i ] );
if (m_moveHintAnim[i])
m_moveHintRenderObj[i]->Set_Animation(m_moveHintAnim[i], 0, RenderObjClass::ANIM_MODE_ONCE);
}
// move this hint render object to the position and align with terrain
Matrix3D transform;
PathfindLayerEnum layer = TheTerrainLogic->alignOnTerrain( 0, m_moveHint[ i ].pos, true, transform );
Real waterZ;
if (layer == LAYER_GROUND && TheTerrainLogic->isUnderwater(m_moveHint[ i ].pos.x, m_moveHint[ i ].pos.y, &waterZ))
{
Coord3D tmp = m_moveHint[ i ].pos;
tmp.z = waterZ;
Coord3D normal;
normal.x = 0;
normal.y = 0;
normal.z = 1;
makeAlignToNormalMatrix(0, tmp, normal, transform);
}
m_moveHintRenderObj[ i ]->Set_Transform( transform );
#if 0
// if there is a source then draw line from source to destination
Object *obj = TheGameLogic->getObject( m_moveHint[ i ].sourceID );
if( obj )
{
Drawable *source = obj->getDrawable();
if( source )
{
Coord3D pos;
ICoord2D start, end;
// project start and end point to screen point
source->getPosition( &pos );
view->worldToScreen( &pos, &start );
view->worldToScreen( &hintPos, &end );
// draw the line
TheDisplay->drawLine( start.x, start.y, end.x, end.y, width, color );
} // end if
} // end if
#endif
}
else
{
// hide hint marker
if( m_moveHintRenderObj[ i ] )
if( m_moveHintRenderObj[ i ]->Is_Hidden() == 0 ) {
m_moveHintRenderObj[ i ]->Set_Hidden( 1 );
W3DDisplay::m_3DScene->Remove_Render_Object( m_moveHintRenderObj[ i ] );
}
} // end else
} // end for i
} // end drawMoveHints
//-------------------------------------------------------------------------------------------------
/** Draw visual back for clicking to attack a unit in the world */
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::drawAttackHints( View *view )
{
} // end drawAttackHints
//-------------------------------------------------------------------------------------------------
/** Draw the angle selection for placing building if needed */
//-------------------------------------------------------------------------------------------------
void W3DInGameUI::drawPlaceAngle( View *view )
{
// Coord2D v, p, o;
//Real size = 15.0f;
//Create the anchor & arrow if not already created!
if( !m_buildingPlacementAnchor )
{
m_buildingPlacementAnchor = W3DDisplay::m_assetManager->Create_Render_Obj( "Locater01" );
// sanity
if( !m_buildingPlacementAnchor )
{
DEBUG_CRASH( ("Unable to create BuildingPlacementAnchor (Locator01.w3d) -- cursor for placing buildings") );
return;
}
}
if( !m_buildingPlacementArrow )
{
m_buildingPlacementArrow = W3DDisplay::m_assetManager->Create_Render_Obj( "Locater02" );
// sanity
if( !m_buildingPlacementArrow )
{
DEBUG_CRASH( ("Unable to create BuildingPlacementArrow (Locator02.w3d) -- cursor for placing buildings") );
return;
}
}
Bool anchorInScene = m_buildingPlacementAnchor->Peek_Scene() != NULL;
Bool arrowInScene = m_buildingPlacementArrow->Peek_Scene() != NULL;
// get out of here if this display isn't up anyway
if( isPlacementAnchored() == FALSE )
{
if( anchorInScene )
{
//If our anchor is in the scene, remove it from the scene but don't delete it.
W3DDisplay::m_3DScene->Remove_Render_Object( m_buildingPlacementAnchor );
}
if( arrowInScene )
{
//If our arrow is in the scene, remove it from the scene but don't delete it.
W3DDisplay::m_3DScene->Remove_Render_Object( m_buildingPlacementArrow );
}
return;
}
// get the anchor points
ICoord2D start, end;
getPlacementPoints( &start, &end );
Coord3D vector;
vector.x = end.x - start.x;
vector.y = end.y - start.y;
vector.z = 0.0f;
Real length = vector.length();
Bool showArrow = length >= 5.0f;
if( showArrow )
{
if( anchorInScene )
{
//We're switching to the arrow!
W3DDisplay::m_3DScene->Remove_Render_Object( m_buildingPlacementAnchor );
}
if( !arrowInScene )
{
W3DDisplay::m_3DScene->Add_Render_Object( m_buildingPlacementArrow );
arrowInScene = TRUE;
}
}
else
{
if( arrowInScene )
{
//We're switching to the anchor!
W3DDisplay::m_3DScene->Remove_Render_Object( m_buildingPlacementArrow );
}
if( !anchorInScene )
{
W3DDisplay::m_3DScene->Add_Render_Object( m_buildingPlacementAnchor );
anchorInScene = TRUE;
}
}
//The proper way to orient the placement arrow is to copy the matrix from the m_placeIcon[0]!
if( anchorInScene )
{
if ( m_placeIcon[ 0 ] )
m_buildingPlacementAnchor->Set_Transform( *m_placeIcon[ 0 ]->getTransformMatrix() );
}
else if( arrowInScene )
{
if ( m_placeIcon[ 0 ] )
m_buildingPlacementArrow->Set_Transform( *m_placeIcon[ 0 ]->getTransformMatrix() );
}
//m_buildingPlacementArrow->Set_Transform(
// draw a little box at the start to show the "anchor" point
//Real rectSize = 4.0f;
//TheDisplay->drawFillRect( start.x - rectSize / 2, start.y - rectSize / 2,
// rectSize, rectSize, color );
// compute vector for line
//v.x = end.x - start.x;
//v.y = end.y - start.y;
//v.normalize();
// compute opposite vector
//o.x = -v.x;
//o.y = -v.y;
// compute perpendicular vector one way
//p.x = -v.y;
//p.y = v.x;
// draw the line
//start.x = o.x * size + p.x * (size/2.0f) + end.x;
//start.y = o.y * size + p.y * (size/2.0f) + end.y;
//TheDisplay->drawLine( start.x, start.y, end.x, end.y, width, color );
// compute perpendicular vector other way
//p.x = v.y;
//p.y = -v.x;
// draw the line
//start.x = o.x * size + p.x * (size/2.0f) + end.x;
//start.y = o.y * size + p.y * (size/2.0f) + end.y;
//TheDisplay->drawLine( start.x, start.y, end.x, end.y, width, color );
} // end drawPlaceAngle

View file

@ -0,0 +1,693 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DMouse.cpp /////////////////////////////////////////////////////////////////////////////
// Author: Mark W.
// Desc: W3D Mouse cursor implementations
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "Common/GameMemory.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/RendObj.h"
#include "WW3D2/HAnim.h"
#include "WW3D2/Camera.h"
#include "assetmgr.h"
#include "W3DDevice/Common/W3DConvert.h"
#include "W3DDevice/GameClient/W3DMouse.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "GameClient/Display.h"
#include "GameClient/Image.h"
#include "GameClient/InGameUI.h"
#include "mutex.h"
#include "thread.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//Since there can't be more than 1 mouse, might as well keep these static.
static CriticalSectionClass mutex;
static Bool isThread;
static TextureClass *cursorTextures[Mouse::NUM_MOUSE_CURSORS][MAX_2D_CURSOR_ANIM_FRAMES]; ///<Textures for each cursor type
static const Image *cursorImages[Mouse::NUM_MOUSE_CURSORS]; ///<Images for use with the RM_POLYGON method.
static RenderObjClass *cursorModels[Mouse::NUM_MOUSE_CURSORS]; ///< W3D models for each cursor type
static HAnimClass *cursorAnims[Mouse::NUM_MOUSE_CURSORS]; ///< W3D animations for each cursor type
///Mouse polling/update thread function
static class MouseThreadClass : public ThreadClass
{
public:
MouseThreadClass::MouseThreadClass() : ThreadClass() {}
void Thread_Function();
} thread;
void MouseThreadClass::Thread_Function()
{
//poll mouse and update position
while (running)
{
isThread=TRUE;
if (TheMouse)
TheMouse->draw();
isThread=FALSE;
Switch_Thread();
}
}
W3DMouse::W3DMouse( void )
{
// zero our event list
for (Int i=0; i<NUM_MOUSE_CURSORS; i++)
{
for (Int j=0; j<MAX_2D_CURSOR_ANIM_FRAMES; j++)
cursorTextures[i][j]=NULL;
cursorModels[i]=NULL;
cursorAnims[i]=NULL;
}
m_currentD3DCursor=NONE;
m_currentW3DCursor=NONE;
m_currentPolygonCursor=NONE;
m_currentAnimFrame = 0;
m_currentD3DFrame = 0;
m_currentFrames = 0;
m_currentFMS= 1.0f/1000.0f;
m_camera = NULL;
m_drawing = FALSE;
} // end Win32Mouse
W3DMouse::~W3DMouse( void )
{
LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
if (m_pDev)
{
m_pDev->ShowCursor(FALSE); //kill DX8 cursor
Win32Mouse::setCursor(ARROW); //enable default windows cursor
}
freeD3DAssets();
freeW3DAssets();
thread.Stop();
} // end Win32Mouse
void W3DMouse::initPolygonAssets(void)
{
CriticalSectionClass::LockClass m(mutex);
//don't allow the mouse thread to initialize
//wait for main app to do initialization.
if (isThread)
return;
//Check if texture assets already loaded
if (m_currentRedrawMode == RM_POLYGON && cursorImages[1] == NULL)
{
for (Int i=0; i<NUM_MOUSE_CURSORS; i++)
{
m_currentPolygonCursor = m_currentCursor;
if (!m_cursorInfo[i].imageName.isEmpty())
cursorImages[i]=TheMappedImageCollection->findImageByName(m_cursorInfo[i].imageName);
}
}
}
void W3DMouse::freePolygonAssets(void)
{
for (Int i=0; i<NUM_MOUSE_CURSORS; i++)
{
cursorImages[i]=NULL;
}
}
/**Release the textures required to display the selected cursor*/
Bool W3DMouse::releaseD3DCursorTextures(MouseCursor cursor)
{
if (cursor == NONE || !cursorTextures[cursor][0])
return TRUE; //no texture for this cursor or texture never loaded
for (Int i=0; i<MAX_2D_CURSOR_ANIM_FRAMES; i++)
{
REF_PTR_RELEASE(m_currentD3DSurface[i]);
REF_PTR_RELEASE(cursorTextures[cursor][i]);
}
return TRUE;
}
/**Load the textures required to display the selected cursor*/
Bool W3DMouse::loadD3DCursorTextures(MouseCursor cursor)
{
if (cursor == NONE || cursorTextures[cursor][0])
return TRUE; //no texture for this cursor or texture already loaded
WW3DAssetManager *am=WW3DAssetManager::Get_Instance();
Int animFrames=m_cursorInfo[cursor].numFrames;
if (!animFrames)
return FALSE; //no animation frames defined.
const char *baseName=m_cursorInfo[cursor].textureName.str();
char FrameName[64];
//Clamp to reasonable number
if (animFrames > MAX_2D_CURSOR_ANIM_FRAMES)
animFrames = MAX_2D_CURSOR_ANIM_FRAMES;
m_currentFrames=0;
if (animFrames == 1)
{ //single animation frame without trailing numbers
sprintf(FrameName,"%s.tga",baseName);
cursorTextures[cursor][0]= am->Get_Texture(FrameName);
m_currentD3DSurface[0]=cursorTextures[cursor][0]->Get_Surface_Level();
m_currentFrames = 1;
}
else
for (Int i=0; i<animFrames; i++)
{
sprintf(FrameName,"%s%04d.tga",baseName,i);
if ((cursorTextures[cursor][i]=am->Get_Texture(FrameName)) != NULL)
{ m_currentD3DSurface[m_currentFrames]=cursorTextures[cursor][i]->Get_Surface_Level();
m_currentFrames++;
}
}
return TRUE;
}
void W3DMouse::initD3DAssets(void)
{
//Nothing to do here unless we want to preload all possible cursors which would
//probably not be practical for memory reasons.
CriticalSectionClass::LockClass m(mutex);
//don't allow the mouse thread to initialize
//wait for main app to do initialization.
if (isThread)
return;
WW3DAssetManager *am=WW3DAssetManager::Get_Instance();
//Check if texture assets already loaded
if (m_currentRedrawMode == RM_DX8 && cursorTextures[1] == NULL && am)
{
for (Int i=0; i<NUM_MOUSE_CURSORS; i++)
{
for (Int j=0; j < MAX_2D_CURSOR_ANIM_FRAMES; j++)
{
cursorTextures[i][j]=NULL;//am->Get_Texture(m_cursorInfo[i].textureName.str());
m_currentD3DSurface[i]=NULL;
}
}
}
}
void W3DMouse::freeD3DAssets(void)
{
//free pointers to texture surfaces.
for (Int i=0; i<MAX_2D_CURSOR_ANIM_FRAMES; i++)
REF_PTR_RELEASE(m_currentD3DSurface[i]);
//free textures.
for (i=0; i<NUM_MOUSE_CURSORS; i++)
{
for (Int j=0; j<MAX_2D_CURSOR_ANIM_FRAMES; j++)
REF_PTR_RELEASE(cursorTextures[i][j]);
}
}
void W3DMouse::initW3DAssets(void)
{
CriticalSectionClass::LockClass m(mutex);
//don't allow the mouse thread to initialize
//wait for main app to do initialization.
if (isThread)
return;
//Check if model assets already loaded
if ((cursorModels[1] == NULL && W3DDisplay::m_assetManager))
{
for (Int i=1; i<NUM_MOUSE_CURSORS; i++)
{
if (!m_cursorInfo[i].W3DModelName.isEmpty())
{
if (m_orthoCamera)
cursorModels[i] = W3DDisplay::m_assetManager->Create_Render_Obj(m_cursorInfo[i].W3DModelName.str(), m_cursorInfo[i].W3DScale*m_orthoZoom, 0);
else
cursorModels[i] = W3DDisplay::m_assetManager->Create_Render_Obj(m_cursorInfo[i].W3DModelName.str(), m_cursorInfo[i].W3DScale, 0);
if (cursorModels[i])
{
cursorModels[i]->Set_Position(Vector3(0.0f, 0.0f, -1.0f));
//W3DDisplay::m_3DInterfaceScene->Add_Render_Object(cursorModels[i]);
}
}
}
}
if ((cursorAnims[1] == NULL && W3DDisplay::m_assetManager))
{
for (Int i=1; i<NUM_MOUSE_CURSORS; i++)
{
if (!m_cursorInfo[i].W3DAnimName.isEmpty())
{
DEBUG_ASSERTCRASH(cursorAnims[i] == NULL, ("hmm, leak festival"));
cursorAnims[i] = W3DDisplay::m_assetManager->Get_HAnim(m_cursorInfo[i].W3DAnimName.str());
if (cursorAnims[i] && cursorModels[i])
{
cursorModels[i]->Set_Animation(cursorAnims[i], 0, (m_cursorInfo[i].loop) ? RenderObjClass::ANIM_MODE_LOOP : RenderObjClass::ANIM_MODE_ONCE);
}
}
}
}
// create the camera
m_camera = NEW_REF( CameraClass, () );
m_camera->Set_Position( Vector3( 0, 1, 1 ) );
Vector2 min = Vector2( -1, -1 );
Vector2 max = Vector2( +1, +1 );
m_camera->Set_View_Plane( min, max );
m_camera->Set_Clip_Planes( 0.995f, 20.0f );
if (m_orthoCamera)
m_camera->Set_Projection_Type( CameraClass::ORTHO );
}
void W3DMouse::freeW3DAssets(void)
{
for (Int i=0; i<NUM_MOUSE_CURSORS; i++)
{
if (W3DDisplay::m_3DInterfaceScene && cursorModels[i])
{
W3DDisplay::m_3DInterfaceScene->Remove_Render_Object(cursorModels[i]);
}
REF_PTR_RELEASE(cursorModels[i]);
REF_PTR_RELEASE(cursorAnims[i]);
}
REF_PTR_RELEASE(m_camera);
}
//-------------------------------------------------------------------------------------------------
/** Initialize our device */
//-------------------------------------------------------------------------------------------------
void W3DMouse::init( void )
{
//check if system already initialized and texture assets loaded.
Win32Mouse::init();
setCursor(ARROW); //set default starting cursor image
WWASSERT(!thread.Is_Running());
isThread=FALSE;
if (m_currentRedrawMode == RM_DX8)
thread.Execute();
thread.Set_Priority(0);
} // end int
//-------------------------------------------------------------------------------------------------
/** Reset */
//-------------------------------------------------------------------------------------------------
void W3DMouse::reset( void )
{
// extend
Win32Mouse::reset();
} // end reset
//-------------------------------------------------------------------------------------------------
/** Super basic simplistic cursor */
//-------------------------------------------------------------------------------------------------
void W3DMouse::setCursor( MouseCursor cursor )
{
CriticalSectionClass::LockClass m(mutex);
m_directionFrame=0;
if (m_currentRedrawMode == RM_WINDOWS)
{ //Windows default cursor needs to refreshed whenever we get a WM_SETCURSOR
m_currentD3DCursor=NONE;
m_currentW3DCursor=NONE;
m_currentPolygonCursor=NONE;
setCursorDirection(cursor);
if (m_drawing) //only allow cursor to change when drawing the cursor (once per frame) to fix flickering.
Win32Mouse::setCursor(cursor);
m_currentCursor = cursor;
return;
}
// extend
Mouse::setCursor( cursor );
// if we're already on this cursor ignore the rest of code to stop cursor flickering.
if( m_currentCursor == cursor && m_currentD3DCursor == cursor)
return;
//make sure Windows didn't reset our cursor
if (m_currentRedrawMode == RM_DX8)
{
SetCursor(NULL); //Kill Windows Cursor
LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
Bool doImageChange=FALSE;
if (m_pDev != NULL)
{
m_pDev->ShowCursor(FALSE); //disable DX8 cursor
if (cursor != m_currentD3DCursor)
{ if (!isThread)
{ releaseD3DCursorTextures(m_currentD3DCursor);
//Since this type of cursor is updated from a non-D3D thread, we need
//to preallocate all surfaces in main thread.
loadD3DCursorTextures(cursor);
}
}
if (m_currentD3DSurface[0])
doImageChange=TRUE;
}
//For DX8 Cursors, we continually set the image on every call even when
//it didn't change. This is needed to prevent the cursor from flickering.
if (doImageChange)
{
HRESULT res;
m_currentHotSpot = m_cursorInfo[cursor].hotSpotPosition;
m_currentFMS = m_cursorInfo[cursor].fps/1000.0f;
m_currentAnimFrame = 0; //reset animation when cursor changes
res = m_pDev->SetCursorProperties(m_currentHotSpot.x,m_currentHotSpot.y,m_currentD3DSurface[(Int)m_currentAnimFrame]->Peek_D3D_Surface());
m_pDev->ShowCursor(TRUE); //Enable DX8 cursor
m_currentD3DFrame=(Int)m_currentAnimFrame;
m_currentD3DCursor = cursor;
m_lastAnimTime=timeGetTime();
}
}
else if (m_currentRedrawMode == RM_POLYGON)
{
SetCursor(NULL); //Kill Windows Cursor
m_currentD3DCursor=NONE;
m_currentW3DCursor=NONE;
m_currentPolygonCursor = cursor;
m_currentHotSpot = m_cursorInfo[cursor].hotSpotPosition;
}
else if (m_currentRedrawMode == RM_W3D)
{
SetCursor(NULL); //Kill Windows Cursor
m_currentD3DCursor=NONE;
m_currentPolygonCursor=NONE;
if (cursor != m_currentW3DCursor)
{
// set the new model visible
if (!cursorModels[1])
initW3DAssets();
if (cursorModels[1])
{
if (cursorModels[m_currentW3DCursor])
{
W3DDisplay::m_3DInterfaceScene->Remove_Render_Object(cursorModels[m_currentW3DCursor]);
}
m_currentW3DCursor=cursor;
if (cursorModels[m_currentW3DCursor])
{
W3DDisplay::m_3DInterfaceScene->Add_Render_Object(cursorModels[m_currentW3DCursor]);
if (m_cursorInfo[m_currentW3DCursor].loop == FALSE && cursorAnims[m_currentW3DCursor])
{
cursorModels[m_currentW3DCursor]->Set_Animation(cursorAnims[m_currentW3DCursor], 0, RenderObjClass::ANIM_MODE_ONCE);
}
}
}
}
else
{
m_currentW3DCursor=cursor;
}
}
// save current cursor
m_currentCursor = cursor;
} // end setCursor
extern HWND ApplicationHWnd;
void W3DMouse::draw(void)
{
CriticalSectionClass::LockClass m(mutex);
m_drawing = TRUE;
//make sure the correct cursor image is selected
setCursor(m_currentCursor);
if (m_currentRedrawMode == RM_DX8 && m_currentD3DCursor != NONE)
{
//called from upate thread or rendering loop. Tells D3D where
//to draw the mouse cursor.
LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
if (m_pDev)
{ m_pDev->ShowCursor(TRUE); //Enable DX8 cursor
if (TheDisplay && !TheDisplay->getWindowed())
{ //if we're full-screen, need to manually move cursor image
POINT ptCursor;
GetCursorPos( &ptCursor );
ScreenToClient( ApplicationHWnd, &ptCursor );
m_pDev->SetCursorPosition( ptCursor.x, ptCursor.y, D3DCURSOR_IMMEDIATE_UPDATE);
}
//Check if animated cursor and new frame
if (m_currentFrames > 1)
{
Int msTime=timeGetTime();
m_currentAnimFrame += (msTime-m_lastAnimTime) * m_currentFMS;
m_currentAnimFrame=fmod(m_currentAnimFrame,m_currentFrames);
m_lastAnimTime=msTime;
if ((Int)m_currentAnimFrame != m_currentD3DFrame)
{
m_currentD3DFrame=(Int)m_currentAnimFrame;
m_pDev->SetCursorProperties(m_currentHotSpot.x,m_currentHotSpot.y,m_currentD3DSurface[m_currentD3DFrame]->Peek_D3D_Surface());
}
}
}
}
else if (m_currentRedrawMode == RM_POLYGON)
{
const Image *image=cursorImages[m_currentPolygonCursor];
if (image)
{
TheDisplay->drawImage(image,m_currMouse.pos.x-m_currentHotSpot.x,m_currMouse.pos.y-m_currentHotSpot.y,
m_currMouse.pos.x+image->getImageWidth()-m_currentHotSpot.x, m_currMouse.pos.y+image->getImageHeight()-m_currentHotSpot.y);
}
}
else if (m_currentRedrawMode == RM_WINDOWS)
{
}
else if (m_currentRedrawMode == RM_W3D)
{
if ( W3DDisplay::m_3DInterfaceScene && m_camera && m_visible)
{
if (cursorModels[m_currentW3DCursor])
{
Real xPercent = (1.0f - (TheDisplay->getWidth() - m_currMouse.pos.x) / (Real)TheDisplay->getWidth());
Real yPercent = ((TheDisplay->getHeight() - m_currMouse.pos.y) / (Real)TheDisplay->getHeight());
Real x, y, z = -1.0f;
if (m_orthoCamera)
{
x = xPercent*2 - 1;
y = yPercent*2;
}
else
{
//W3D Screen coordinates are -1 to 1, so we need to do some conversion:
Real logX, logY;
PixelScreenToW3DLogicalScreen(m_currMouse.pos.x - 0, m_currMouse.pos.y - 0, &logX, &logY, TheDisplay->getWidth(), TheDisplay->getHeight());
Vector3 rayStart;
Vector3 rayEnd;
rayStart = m_camera->Get_Position(); //get camera location
m_camera->Un_Project(rayEnd,Vector2(logX,logY)); //get world space point
rayEnd -= rayStart; //vector camera to world space point
rayEnd.Normalize(); //make unit vector
rayEnd *= m_camera->Get_Depth(); //adjust length to reach far clip plane
rayEnd += rayStart; //get point on far clip plane along ray from camera.
x = Vector3::Find_X_At_Z(z, rayStart, rayEnd);
y = Vector3::Find_Y_At_Z(z, rayStart, rayEnd);
}
Matrix3D tm(1);
tm.Set_Translation(Vector3(x, y, z));
Coord2D offset = {0, 0};
if (TheInGameUI && TheInGameUI->isScrolling())
{
offset = TheInGameUI->getScrollAmount();
offset.normalize();
Real theta = atan2(-offset.y, offset.x);
theta -= (Real)M_PI/2;
tm.Rotate_Z(theta);
}
cursorModels[m_currentW3DCursor]->Set_Transform(tm);
WW3D::Render( W3DDisplay::m_3DInterfaceScene, m_camera );
}
}
}
//@todo: In DX8 mode the mouse is drawn in another thread which isn't allowed
//access to D3D so we can't do any drawing here.
// draw the cursor text
if (!isThread)
drawCursorText();
// draw tooltip text
if (m_visible && !isThread)
drawTooltip();
m_drawing = FALSE;
}
void W3DMouse::setRedrawMode(RedrawMode mode)
{
MouseCursor cursor = getMouseCursor();
//Turn off the previous cursor mode
setCursor(NONE);
m_currentRedrawMode=mode;
switch (mode)
{
case RM_WINDOWS:
{ //Windows mouse doesn't need an update thread.
if (thread.Is_Running())
thread.Stop();
freeD3DAssets(); //using Windows resources
freeW3DAssets();
freePolygonAssets();
m_currentD3DCursor = NONE;
m_currentW3DCursor = NONE;
m_currentPolygonCursor = NONE;
}
break;
case RM_W3D:
{ //Model mouse updated only at render time so doesn't
//require thread.
if (thread.Is_Running())
thread.Stop();
freeD3DAssets(); //using packed Image data, not textures.
freePolygonAssets();
m_currentD3DCursor = NONE;
m_currentPolygonCursor = NONE;
initW3DAssets();
}
break;
case RM_POLYGON:
{ //Polygon mouse updated only at render time so doesn't
//require thread.
if (thread.Is_Running())
thread.Stop();
freeD3DAssets(); //using packed Image data, not textures.
freeW3DAssets();
m_currentD3DCursor = NONE;
m_currentW3DCursor = NONE;
m_currentPolygonCursor = NONE;
initPolygonAssets();
}
break;
case RM_DX8:
{ //this cursor type is drawn by DX8 and can be refreshed
//independent of rendering rate. Uses another thread to do
//position updates.
initD3DAssets(); //make sure textures loaded.
freeW3DAssets();
freePolygonAssets();
if (!thread.Is_Running())
thread.Execute();
m_currentW3DCursor = NONE;
m_currentPolygonCursor = NONE;
break;
}
}
setCursor(NONE);
setCursor(cursor);
}
void W3DMouse::setCursorDirection(MouseCursor cursor)
{
Coord2D offset = {0, 0};
//Check if we have a directional cursor that needs different images for each direction
if (m_cursorInfo[cursor].numDirections > 1 && TheInGameUI && TheInGameUI->isScrolling())
{
offset = TheInGameUI->getScrollAmount();
if (offset.x || offset.y)
{
offset.normalize();
Real theta = atan2(offset.y, offset.x);
theta = fmod(theta+M_PI*2,M_PI*2);
Int numDirections=m_cursorInfo[m_currentCursor].numDirections;
//Figure out which of our predrawn cursor orientations best matches the
//actual cursor direction. Frame 0 is assumed to point right and continue
//clockwise.
m_directionFrame=(Int)(theta/(2.0f*M_PI/(Real)numDirections)+0.5f);
if (m_directionFrame >= numDirections)
m_directionFrame = 0;
}
else
{
m_directionFrame=0;
}
}
else
m_directionFrame = 0;
}

View file

@ -0,0 +1,332 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// W3DParticleSys.cpp
// W3D Particle System implementation
// Author: Michael S. Booth, November 2001
#include "GameClient/Color.h"
#include "W3DDevice/GameClient/W3DParticleSys.h"
#include "W3DDevice/GameClient/W3DAssetManager.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/heightmap.h"
#include "WW3D2/Camera.h"
//------------------------------------------------------------------------------ Performance Timers
//#include "Common/PerfMetrics.h"
//#include "Common/PerfTimer.h"
//-------------------------------------------------------------------------------------------------
#include "Common/QuickTrig.h"
W3DParticleSystemManager::W3DParticleSystemManager()
{
m_pointGroup = NULL;
m_streakLine = NULL;
m_posBuffer = NULL;
m_RGBABuffer = NULL;
m_sizeBuffer = NULL;
m_angleBuffer = NULL;
m_readyToRender = false;
m_onScreenParticleCount = 0;
m_pointGroup = NEW PointGroupClass();
//m_streakLine = NULL;
m_streakLine = NEW StreakLineClass();
m_posBuffer = NEW_REF( ShareBufferClass<Vector3>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_posBuffer") );
m_RGBABuffer = NEW_REF( ShareBufferClass<Vector4>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_RGBABuffer") );
m_sizeBuffer = NEW_REF( ShareBufferClass<float>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_sizeBuffer") );
m_angleBuffer = NEW_REF( ShareBufferClass<uint8>, (MAX_POINTS_PER_GROUP, "W3DParticleSystemManager::m_angleBuffer") );
}
W3DParticleSystemManager::~W3DParticleSystemManager()
{
delete m_pointGroup;
// W3DDisplay::m_3DScene->Remove_Render_Object( m_streakLine );
if (m_streakLine)
{
REF_PTR_RELEASE(m_streakLine);
}
REF_PTR_RELEASE(m_posBuffer);
REF_PTR_RELEASE(m_RGBABuffer);
REF_PTR_RELEASE(m_sizeBuffer);
REF_PTR_RELEASE(m_angleBuffer);
}
/**
* Hack because DoParticles is called from Flush(), which is called
* multiple times per frame. We only want to render once.
* @todo Clean up the flag/Flush hack.
*/
void W3DParticleSystemManager::queueParticleRender()
{
m_readyToRender = true;
}
/**
* Nasty hack to render particles last. Called directly by WW3D::Flush()
*/
void DoParticles( RenderInfoClass &rinfo )
{
if (TheParticleSystemManager)
TheParticleSystemManager->doParticles(rinfo);
}
void W3DParticleSystemManager::doParticles(RenderInfoClass &rinfo)
{
if (m_readyToRender == false)
return;
// external mechanism must tell us when it's OK to render again...
m_readyToRender = false;
//reset each frame
/// @todo lorenzen sez: this should be debug only:
m_onScreenParticleCount = 0;
const FrustumClass & frustum = rinfo.Camera.Get_Frustum();
AABoxClass bbox;
//Get a bounding box around our visible universe. Bounded by terrain and the sky
//so much tighter fitting volume than what's actually visible. This will cull
//particles falling under the ground.
TheTerrainRenderObject->getMaximumVisibleBox(frustum, &bbox, TRUE);
//@todo lorenzen sez: put these in registers for sure
Real bcX = bbox.Center.X;
Real bcY = bbox.Center.Y;
Real bcZ = bbox.Center.Z;
Real beX = bbox.Extent.X;
Real beY = bbox.Extent.Y;
Real beZ = bbox.Extent.Z;
unsigned int personalities[MAX_POINTS_PER_GROUP];
m_fieldParticleCount = 0;
ParticleSystemManager::ParticleSystemList &particleSysList = TheParticleSystemManager->getAllParticleSystems();
for( ParticleSystemManager::ParticleSystemListIt it = particleSysList.begin(); it != particleSysList.end(); ++it)
{
ParticleSystem *sys = (*it);
if (!sys) {
continue;
}
// only look at particle/point style systems
if (sys->isUsingDrawables())
continue;
/// @todo lorenzen sez: declare these outside the sys loop, and put some in registers
// initialize them here still, of course
// build W3D particle buffer
Int count = 0;
Vector3 *posArray = m_posBuffer->Get_Array();
Real *sizeArray = m_sizeBuffer->Get_Array();
Vector4 *RGBAArray = m_RGBABuffer->Get_Array();
uint8 *angleArray = m_angleBuffer->Get_Array();
const Coord3D *pos;
const RGBColor *color;
Real psize;
//set-up all the per-particle
for (Particle *p = sys->getFirstParticle(); p; p = p->m_systemNext)
{
// do not attempt to render totally invisible particles
if (p->isInvisible())
continue;
pos = p->getPosition();
psize = p->getSize();
//Cull particle to edges of screen and terrain.
if (WWMath::Fabs(pos->x - bcX) > (beX + psize))
continue;
if (WWMath::Fabs(pos->y - bcY) > (beY + psize))
continue;
if (WWMath::Fabs(pos->z - bcZ) > (beZ + psize))
continue;
m_fieldParticleCount += ( sys->getPriority() == AREA_EFFECT && sys->m_isGroundAligned != FALSE );
//@todo lorenzen sez: use pointer arithmetic for these arrays
personalities[count] = p->getPersonality();
posArray[count].X = pos->x;
posArray[count].Y = pos->y;
posArray[count].Z = pos->z;
sizeArray[count] = psize;
color = p->getColor();
RGBAArray[count].X = color->red;
RGBAArray[count].Y = color->green;
RGBAArray[count].Z = color->blue;
RGBAArray[count].W = p->getAlpha();
angleArray[count] = (uint8)(p->getAngle() * 255.0f / (2.0f * PI));
if (++count == MAX_POINTS_PER_GROUP)
break;
}
if ( count == 0 )
continue; //this system has no particles to render
TextureClass *texture = W3DDisplay::m_assetManager->Get_Texture( sys->getParticleTypeName().str() );
if ( m_streakLine && sys->isUsingStreak() && (count >= 2) )
{
m_streakLine->Reset_Line();
m_streakLine->Set_Texture( texture );
texture->Release_Ref();//release reference since it's held by streakline
switch( sys->getShaderType() )
{
case ParticleSystemInfo::ADDITIVE:
m_streakLine->Set_Shader( ShaderClass::_PresetAdditiveSpriteShader );
break;
case ParticleSystemInfo::ALPHA:
m_streakLine->Set_Shader( ShaderClass::_PresetAlphaSpriteShader );
break;
case ParticleSystemInfo::ALPHA_TEST:
m_streakLine->Set_Shader( ShaderClass::_PresetATestSpriteShader );
break;
case ParticleSystemInfo::MULTIPLY:
m_streakLine->Set_Shader( ShaderClass::_PresetMultiplicativeSpriteShader );
break;
}
//UPDATE THE STREAK'S ARRAYS
m_streakLine->Set_LocsWidthsColors(
count,
m_posBuffer->Get_Array(),
m_sizeBuffer->Get_Array(),
m_RGBABuffer->Get_Array(),
&personalities[0]
);
//WWASSERT( m_streakLine->Get_Num_Points() == count );
// This is the happy place for this!
RGBAArray[0].X = 0;//eliminates the scissor edge on the trailing edge of the streak
RGBAArray[0].Y = 0;
RGBAArray[0].Z = 0;
RGBAArray[0].W = 0;
//RENDER STREAK!
m_streakLine->Render( rinfo );
}
else
{
WWASSERT( m_pointGroup );
if ( m_pointGroup ) // this catches the particle and volumeparticle cases
{
// render all the systems' particles
m_pointGroup->Set_Texture( texture );
texture->Release_Ref();//release reference since it's held by pointGroup
m_pointGroup->Set_Flag( PointGroupClass::TRANSFORM, true ); // transform to screen space
switch( sys->getShaderType() )
{
case ParticleSystemInfo::ADDITIVE:
m_pointGroup->Set_Shader( ShaderClass::_PresetAdditiveSpriteShader );
break;
case ParticleSystemInfo::ALPHA:
m_pointGroup->Set_Shader( ShaderClass::_PresetAlphaSpriteShader );
break;
case ParticleSystemInfo::ALPHA_TEST:
m_pointGroup->Set_Shader( ShaderClass::_PresetATestSpriteShader );
break;
case ParticleSystemInfo::MULTIPLY:
m_pointGroup->Set_Shader( ShaderClass::_PresetMultiplicativeSpriteShader );
break;
}
/// @todo Use both QUADS and TRIS for particles
m_pointGroup->Set_Point_Mode( PointGroupClass::QUADS );
m_pointGroup->Set_Arrays( m_posBuffer, m_RGBABuffer, NULL, m_sizeBuffer, m_angleBuffer, NULL, count );
m_pointGroup->Set_Billboard(sys->shouldBillboard());
/// @todo Support animated texture particles
/// @todo lorenzen sez: unimplemented code wastes cpu cycles
m_pointGroup->Set_Point_Frame( 0 );
//RENDER IT!
if( sys->getVolumeParticleDepth() > 1 )
{
m_pointGroup->RenderVolumeParticle( rinfo, sys->getVolumeParticleDepth() );
}
else
m_pointGroup->Render( rinfo );
}
}
/// @todo lorenzen sez: this should be debug only:
//add particle count to total
m_onScreenParticleCount += count;
/*
// draw the wind vector for this particle system on the screen
UnsignedInt width = TheDisplay->getWidth();
UnsignedInt height = TheDisplay->getHeight();
Coord3D worldStart, worldEnd;
ICoord2D pixelStart, pixelEnd;
sys->getPosition( &worldStart );
worldEnd.x = Cos( sys->getWindAngle() ) * 50.0f + worldStart.x;
worldEnd.y = Sin( sys->getWindAngle() ) * 50.0f + worldStart.y;
worldEnd.z = worldStart.z;
TheTacticalView->worldToScreen( &worldStart, &pixelStart );
TheTacticalView->worldToScreen( &worldEnd, &pixelEnd );
Color colorStart = GameMakeColor( 255, 255, 255, 255 );
Color colorEnd = GameMakeColor( 255, 128, 128, 255 );
TheDisplay->drawLine( pixelStart.x, pixelStart.y, pixelEnd.x, pixelEnd.y, 1.0f, colorStart, colorEnd );
*/
}// next system
/// @todo lorenzen sez: this should be debug only:
TheParticleSystemManager->setOnScreenParticleCount(m_onScreenParticleCount);
}

View file

@ -0,0 +1,109 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "W3DDevice/GameClient/W3DPoly.h"
#include "Lib/BaseType.h"
//-------------------------------------------------------------------------------------------------
/** Delete all vertices in polygon */
//-------------------------------------------------------------------------------------------------
void ClipPolyClass::Reset(void)
{
Verts.Delete_All(false);
}
//-------------------------------------------------------------------------------------------------
/** Add a new vertex to polygon */
//-------------------------------------------------------------------------------------------------
void ClipPolyClass::Add_Vertex(const Vector3 & point)
{
Verts.Add(point);
}
//-------------------------------------------------------------------------------------------------
/** Clip polygon to given plane, returning new polygon in dest. */
//-------------------------------------------------------------------------------------------------
void ClipPolyClass::Clip(const PlaneClass & plane,ClipPolyClass & dest) const
{
dest.Reset();
// temporary variables used in clipping
Int i = 0;
Int vcount = Verts.Count();
Int iprev = vcount - 1;
Bool cur_point_in_front;
Bool prev_point_in_front;
Real alpha;
Vector3 int_point;
if (vcount <= 2) return;
// perform clipping
prev_point_in_front = !plane.In_Front(Verts[iprev]); // Note, plane normal is outward so we invert this test
for (Int j=0; j<vcount; j++) {
cur_point_in_front = !plane.In_Front(Verts[i]); // Note, plane nomral is out so we invert this test
if (prev_point_in_front) {
if (cur_point_in_front) {
// Previous vertex was in front of plane and this vertex is in
// front of the plane so we emit this vertex.
dest.Add_Vertex(Verts[i]);
} else {
// Previous vert was in front, this vert is behind, compute
// the intersection and emit the point.
plane.Compute_Intersection(Verts[iprev],Verts[i],&alpha);
Vector3::Lerp(Verts[iprev],Verts[i],alpha,&int_point);
dest.Add_Vertex(int_point);
}
} else {
if (cur_point_in_front) {
// segment is going from the back halfspace to the front halfspace
// compute the intersection and emit it, then continue
// the edge into the front halfspace and emit the end point.
plane.Compute_Intersection(Verts[iprev],Verts[i],&alpha);
Vector3::Lerp(Verts[iprev],Verts[i],alpha,&int_point);
dest.Add_Vertex(int_point);
dest.Add_Vertex(Verts[i]);
}
}
prev_point_in_front = cur_point_in_front;
iprev = i;
//i = (i+1)%(Verts.Count());
i++;
if (i>=vcount) {
i = 0;
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,801 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DShroud.cpp /////////////////////////////////////////////////////////////////////////////
// Created: Mark Wilczynski, Jan 2002
// Desc: Code to support rendering of shrouded units/terrain.
///////////////////////////////////////////////////////////////////////////////////////////////////
#include "Lib/BaseType.h"
#include "camera.h"
#include "simplevec.h"
#include "dx8wrapper.h"
#include "common/MapObject.h"
#include "common/PerfTimer.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "W3DDevice/GameClient/W3DPoly.h"
#include "W3DDevice/GameClient/W3DShaderManager.h"
#include "assetmgr.h"
#include "W3DDevice/GameClient/W3DShroud.h"
#include "WW3D2/textureloader.h"
#include "common/GlobalData.h"
#include "GameLogic/PartitionManager.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//-----------------------------------------------------------------------------
// In Global Data now
//#define SHROUD_COLOR 0x00ffffff //temporary test of gray shroud instead of pure black.
//#define MIN_SHROUD_LEVEL 0 //for gray fog
//Int SHROUD_COLOR=0x00808080; //temporary test of gray shroud instead of pure black.
//Int MIN_SHROUD_LEVEL=50; //for gray fog
//#define SHROUD_COLOR 0x00eeeebff //temporary test of gray shroud instead of pure black.
//#define MIN_SHROUD_LEVEL 254 //for gray fog
//#define SHROUD_COLOR 0x00bbbbbb //temporary test of gray shroud instead of pure black.
//#define SHROUD_COLOR 0x00004080 //temporary test of blue shroud instead of pure black.
//#define MIN_SHROUD_LEVEL 100 //for black fog
//#define MAX_MAP_SHROUDSIZE 1024 //maximum number of shroud cells across entire map.
//#define MAX_VISIBLE_SHROUDSIZE (MAX_MAP_SHROUDSIZE+1) //maximum number of shroud vertices visible at any given time
#define DEFAULT_SHROUD_CELL_SIZE MAP_XY_FACTOR //assume shroud at same resolution as terrain cells.
#define DEFAULT_TERRAIN_SIZE 1024 //assumed size of largest terrain possible (in vertices)
#define DEFAULT_VISIBLE_TERRAIN 96 //assumed size of visible terrain cells.
//-----------------------------------------------------------------------------
W3DShroud::W3DShroud(void)
{
m_finalFogData=NULL;
m_currentFogData=NULL;
m_pSrcTexture=NULL;
m_pDstTexture=NULL;
m_srcTextureData=NULL;
m_srcTexturePitch=NULL;
m_dstTextureWidth=m_numMaxVisibleCellsX=0;
m_dstTextureHeight=m_numMaxVisibleCellsY=0;
m_boderShroudLevel = (W3DShroudLevel)TheGlobalData->m_shroudAlpha; //assume border is black
m_clearDstTexture = TRUE; //force clearing of destination texture;
m_cellWidth=DEFAULT_SHROUD_CELL_SIZE;
m_cellHeight=DEFAULT_SHROUD_CELL_SIZE;
m_numCellsX=0;
m_numCellsY=0;
m_shroudFilter=TextureClass::FILTER_TYPE_DEFAULT;
}
//-----------------------------------------------------------------------------
W3DShroud::~W3DShroud(void)
{
ReleaseResources();
if (m_pSrcTexture)
m_pSrcTexture->Release();
m_pSrcTexture=NULL;
if (m_finalFogData)
delete [] m_finalFogData;
if (m_currentFogData)
delete [] m_currentFogData;
m_drawFogOfWar=FALSE;
}
//-----------------------------------------------------------------------------
/**Called to initialize a new shroud for a new map. Should be done after the map is loaded
into the terrain object. worldCellSize is the world-space dimensions of each shroud cell.
The system will generate enough cells to cover the full map.
*/
void W3DShroud::init(WorldHeightMap *pMap, Real worldCellSizeX, Real worldCellSizeY)
{
DEBUG_ASSERTCRASH( m_pSrcTexture == NULL, ("ReAcquire of existing shroud textures"));
DEBUG_ASSERTCRASH( pMap != NULL, ("Shroud init with NULL WorldHeightMap"));
Int dstTextureWidth=0;
Int dstTextureHeight=0;
m_cellWidth=worldCellSizeX;
m_cellHeight=worldCellSizeY;
//Precompute a bounding box for entire shroud layer
if (pMap)
{
m_numCellsX = REAL_TO_INT_CEIL((Real)(pMap->getXExtent() - 1 - pMap->getBorderSize()*2)*MAP_XY_FACTOR/m_cellWidth);
m_numCellsY = REAL_TO_INT_CEIL((Real)(pMap->getYExtent() - 1 - pMap->getBorderSize()*2)*MAP_XY_FACTOR/m_cellHeight);
//Maximum visible cells will depend on maximum drawable terrain size plus 1 for partial cells (since
//shroud cells are larger than terrain cells).
dstTextureWidth=m_numMaxVisibleCellsX=REAL_TO_INT_FLOOR((Real)(pMap->getDrawWidth()-1)*MAP_XY_FACTOR/m_cellWidth)+1;
dstTextureHeight=m_numMaxVisibleCellsY=REAL_TO_INT_FLOOR((Real)(pMap->getDrawHeight()-1)*MAP_XY_FACTOR/m_cellHeight)+1;
dstTextureWidth += 2; //enlarge by 2 pixels so we can have a border color all the way around.
dstTextureHeight += 2; //enlarge by 2 pixels so we can have border color all the way around.
TextureLoader::Validate_Texture_Size((unsigned int &)dstTextureWidth,(unsigned int &)dstTextureHeight);
}
UnsignedInt srcWidth,srcHeight;
srcWidth=m_numCellsX;
//vertical size is larger by 1 pixel so that we have some unused pixels to use in clearing the video texture.
//To clear the video texture, I will copy pixels from this unused area. There is no other way to clear a video
//memory texture to a known value because you can't lock it - only copy into it.
srcHeight=m_numCellsY;
srcHeight += 1;
#ifdef DO_FOG_INTERPOLATION
m_finalFogData = new W3DShroudLevel[srcWidth*srcHeight];
m_currentFogData = new W3DShroudLevel[srcWidth*srcHeight];
//Clear the fog to black
memset(m_currentFogData,0,srcWidth*srcHeight);
memset(m_finalFogData,0,srcWidth*srcHeight);
#endif
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
m_pSrcTexture = DX8Wrapper::_Create_DX8_Surface(srcWidth,srcHeight, WW3D_FORMAT_A4R4G4B4);
else
#endif
m_pSrcTexture = DX8Wrapper::_Create_DX8_Surface(srcWidth,srcHeight, WW3D_FORMAT_R5G6B5);
DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Failed to Allocate Shroud Src Surface"));
D3DLOCKED_RECT rect;
//Get a pointer to source surface pixels.
HRESULT res = m_pSrcTexture->LockRect(&rect,NULL,D3DLOCK_NO_DIRTY_UPDATE);
m_pSrcTexture->UnlockRect();
DEBUG_ASSERTCRASH( res == D3D_OK, ("Failed to lock shroud src surface"));
res = 0;// just to avoid compiler warnings
m_srcTextureData=rect.pBits;
m_srcTexturePitch=rect.Pitch;
//clear entire texture to black
memset(m_srcTextureData,0,m_srcTexturePitch*srcHeight);
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
fillShroudData(TheGlobalData->m_shroudAlpha); //initialize shroud to a known value
#endif
if (dstTextureWidth != m_dstTextureWidth || dstTextureHeight != m_dstTextureHeight ) ///@todo: Check if size has changed - probably never
ReleaseResources(); //need a new sized shroud
if (!m_pDstTexture )
{ m_dstTextureWidth = dstTextureWidth;
m_dstTextureHeight = dstTextureHeight;
ReAcquireResources(); //allocate video memory surface
}
//Force a refresh of shroud data since we just created a new source texture.
if (ThePartitionManager)
ThePartitionManager->refreshShroudForLocalPlayer();
}
//-----------------------------------------------------------------------------
///Called on map reset.
void W3DShroud::reset()
{
//Free old shroud data since it may no longer fit new map.
if (m_pSrcTexture)
m_pSrcTexture->Release();
m_pSrcTexture=NULL;
if (m_finalFogData)
delete [] m_finalFogData;
m_finalFogData=NULL;
if (m_currentFogData)
delete [] m_currentFogData;
m_currentFogData=NULL;
m_clearDstTexture = TRUE; //always refill the destination texture after a reset
}
//-----------------------------------------------------------------------------
///Release any resources that can't survive a D3D device reset.
void W3DShroud::ReleaseResources(void)
{
REF_PTR_RELEASE (m_pDstTexture);
}
//-----------------------------------------------------------------------------
///Restore resources that are lost on D3D device reset.
Bool W3DShroud::ReAcquireResources(void)
{
if (!m_dstTextureWidth)
return TRUE; //nothing to reaquire since shroud was never initialized with valid data
DEBUG_ASSERTCRASH( m_pDstTexture == NULL, ("ReAcquire of existing shroud texture"));
// Create destination texture (stored in video memory).
// Since we control the video memory copy, we can do partial updates more efficiently. Or do shift blits.
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
m_pDstTexture = MSGNEW("TextureClass") TextureClass(m_dstTextureWidth,m_dstTextureHeight,WW3D_FORMAT_A4R4G4B4,TextureClass::MIP_LEVELS_1, TextureClass::POOL_DEFAULT);
else
#endif
m_pDstTexture = MSGNEW("TextureClass") TextureClass(m_dstTextureWidth,m_dstTextureHeight,WW3D_FORMAT_R5G6B5,TextureClass::MIP_LEVELS_1, TextureClass::POOL_DEFAULT);
DEBUG_ASSERTCRASH( m_pDstTexture != NULL, ("Failed ReAcquire of shroud texture"));
if (!m_pDstTexture)
{ //could not create a valid texture
m_dstTextureWidth = 0;
m_dstTextureHeight = 0;
return FALSE;
}
m_pDstTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_pDstTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_pDstTexture->Set_Mip_Mapping(TextureClass::FILTER_TYPE_NONE);
m_clearDstTexture = TRUE; //force clearing of destination texture first time it's used.
return TRUE;
}
//-----------------------------------------------------------------------------
W3DShroudLevel W3DShroud::getShroudLevel(Int x, Int y)
{
DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Reading empty shroud"));
if (x < m_numCellsX && y < m_numCellsY)
{
UnsignedShort pixel=*(UnsignedShort *)((Byte *)m_srcTextureData + x*2 + y*m_srcTexturePitch);
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
//in this mode, alpha channel holds intensity
return (W3DShroudLevel)((1.0f-((Real)(pixel >> 12)/15.0f))*255.0f);
else
#endif
//in this mode, green has the best precision at 6 bits.
return (W3DShroudLevel)((Real)((pixel >> 5)&0x3f)/63.0f*255.0f);
}
return 0;
}
//-----------------------------------------------------------------------------
void W3DShroud::setShroudLevel(Int x, Int y, W3DShroudLevel level, Bool textureOnly)
{
DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Writing empty shroud. Usually means that map failed to load."));
if (!m_pSrcTexture)
return;
if (x < m_numCellsX && y < m_numCellsY)
{
if (level < TheGlobalData->m_shroudAlpha)
level = TheGlobalData->m_shroudAlpha;
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
{
///@todo: optimize this case if we end up using fog shroud.
Int redVal = TheGlobalData->m_shroudColor.red;
Int greenVal = TheGlobalData->m_shroudColor.green;
Int blueVal = TheGlobalData->m_shroudColor.blue;
// Int redVal = (SHROUD_COLOR >> 16) & 0xff;
// Int greenVal = (SHROUD_COLOR >> 8) & 0xff;
// Int blueVal = SHROUD_COLOR & 0xff;
Int alphaVal = 255 - level;
UnsignedShort pixel=((blueVal>>4)&0xf) | (((greenVal>>4)&0xf)<<4) | (((redVal>>4)&0xf)<<8) | (((alphaVal>>4)&0xf)<<12);
*(UnsignedShort *)((Byte *)m_srcTextureData + x*2 + y*m_srcTexturePitch)=pixel;
}
else
#endif
{
#ifdef DO_FOG_INTERPOLATION
if (!textureOnly)
m_finalFogData[x+y*m_numCellsX]=level;
#endif
UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(TheGlobalData->m_shroudColor.getAsInt()&0xff)/255.0f));
UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff00)>>8)/255.0f));
UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff0000)>>16)/255.0f));
// UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(SHROUD_COLOR&0xff)/255.0f));
// UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff00)>>8)/255.0f));
// UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff0000)>>16)/255.0f));
if (level == 255)
{ //unshrouded pixels should be fully lit
redpixel = 255;
greenpixel = 255;
bluepixel = 255;
}
UnsignedShort *texel = (UnsignedShort *)((Byte *)m_srcTextureData + x*2 + y*m_srcTexturePitch);
// For those interested, MLorenzen has this bock commented out until he gets back on Mon, Sept. 30 2002
// If this code is still here by mid october, nuke it!
// UnsignedInt texelRed = ((*texel >> 8 ) & 0xf8);
// UnsignedInt texelGreen =((*texel >> 3 ) & 0xfc);
// UnsignedInt texelBlue = ((*texel << 3 ) & 0xf8);
// if (texelRed < texelGreen && texelGreen > texelBlue)
// {
// bluepixel += redpixel;
// bluepixel -= redpixel;
// }
*texel = ( ((bluepixel&0xf8) >> 3) | ((greenpixel&0xfc)<<3) | ((redpixel&0xf8)<<8));
}
return;
}
}
//-----------------------------------------------------------------------------
///Quickly sets the shroud level of entire map to a single value
void W3DShroud::fillShroudData(W3DShroudLevel level)
{
Int x,y;
UnsignedShort pixel;
if (level < TheGlobalData->m_shroudAlpha)
level = TheGlobalData->m_shroudAlpha;
#if defined(_DEBUG) || defined(_INTERNAL)
//convert value to pixel format
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
{
Int redVal = TheGlobalData->m_shroudColor.red;
Int greenVal = TheGlobalData->m_shroudColor.green;
Int blueVal = TheGlobalData->m_shroudColor.blue;
// Int redVal = (SHROUD_COLOR >> 16) & 0xff;
// Int greenVal = (SHROUD_COLOR >> 8) & 0xff;
// Int blueVal = SHROUD_COLOR & 0xff;
Int alphaVal = 255 - level;
pixel=((blueVal>>4)&0xf) | (((greenVal>>4)&0xf)<<4) | (((redVal>>4)&0xf)<<8) | (((alphaVal>>4)&0xf)<<12);
}
else
#endif
{
UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(TheGlobalData->m_shroudColor.getAsInt()&0xff)/255.0f));
UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff00)>>8)/255.0f));
UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff0000)>>16)/255.0f));
// UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(SHROUD_COLOR&0xff)/255.0f));
// UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff00)>>8)/255.0f));
// UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((SHROUD_COLOR&0xff0000)>>16)/255.0f));
if (level == 255)
{ //unshrouded pixels should be fully lit
redpixel = 255;
greenpixel = 255;
bluepixel = 255;
}
pixel=( ((bluepixel&0xf8) >> 3) | ((greenpixel&0xfc)<<3) | ((redpixel&0xf8)<<8));
}
UnsignedShort *ptr=(UnsignedShort *)m_srcTextureData;
Int pitch = m_srcTexturePitch >> 1; //2 bytes per pointer increment
for (y=0; y<m_numCellsY; y++)
{
for (x=0; x<m_numCellsX; x++)
ptr[x]=pixel;
ptr += pitch;
}
#ifdef DO_FOG_INTERPOLATION
//Set the final shroud state. May differe from current state because of time interpolation.
W3DShroudLevel *cptr=m_finalFogData;
pitch = m_numCellsX;
for (y=0; y<m_numCellsY; y++)
{
for (x=0; x<m_numCellsX; x++)
cptr[x]=level;
ptr += pitch;
}
#endif
}
void W3DShroud::fillBorderShroudData(W3DShroudLevel level, SurfaceClass* pDestSurface)
{
Int x,y;
UnsignedShort pixel;
if (level < TheGlobalData->m_shroudAlpha)
level = TheGlobalData->m_shroudAlpha;
#if defined(_DEBUG) || defined(_INTERNAL)
//convert value to pixel format
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
{
Int redVal = TheGlobalData->m_shroudColor.red;
Int greenVal = TheGlobalData->m_shroudColor.green;
Int blueVal = TheGlobalData->m_shroudColor.blue;
Int alphaVal = 255 - level;
pixel=((blueVal>>4)&0xf) | (((greenVal>>4)&0xf)<<4) | (((redVal>>4)&0xf)<<8) | (((alphaVal>>4)&0xf)<<12);
}
else
#endif
{
UnsignedInt bluepixel = (UnsignedInt)((Real)level*((Real)(TheGlobalData->m_shroudColor.getAsInt()&0xff)/255.0f));
UnsignedInt greenpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff00)>>8)/255.0f));
UnsignedInt redpixel = (UnsignedInt)((Real)level*((Real)((TheGlobalData->m_shroudColor.getAsInt()&0xff0000)>>16)/255.0f));
if (level == 255)
{ //unshrouded pixels should be fully lit
redpixel = 255;
greenpixel = 255;
bluepixel = 255;
}
pixel=( ((bluepixel&0xf8) >> 3) | ((greenpixel&0xfc)<<3) | ((redpixel&0xf8)<<8));
}
//Skip to unused texels within the shroud data
UnsignedShort *ptr=(UnsignedShort *)m_srcTextureData + m_numCellsY*(m_srcTexturePitch >> 1);
//Fill unused texels with border color
for (x=0; x<m_numCellsX; x++)
ptr[x]=pixel;
//Fill destination texture with border color
RECT srcRect;
//create a rectangle enclosing bottom row of unused pixels long enough
//to cover destination width.
srcRect.left=0;
srcRect.top=m_numCellsY;
srcRect.right= m_numCellsX;
srcRect.bottom= m_numCellsY+1;
POINT dstPoint={0,0};
Int numFullCopies = m_dstTextureWidth/srcRect.right;
Int numExtraPixels = m_dstTextureWidth%srcRect.right;
for (y=0; y<m_dstTextureHeight; y++)
{
dstPoint.y=y;
dstPoint.x=0;
for (x=0; x<numFullCopies; x++)
{
dstPoint.x = x * srcRect.right; //advance to next set of pixel in row.
DX8Wrapper::_Copy_DX8_Rects(
m_pSrcTexture,
&srcRect,
1,
pDestSurface->Peek_D3D_Surface(),
&dstPoint);
}
if (numExtraPixels)
{ Int oldVal=srcRect.right;
dstPoint.x = numFullCopies * oldVal;
srcRect.right = numExtraPixels;
DX8Wrapper::_Copy_DX8_Rects(
m_pSrcTexture,
&srcRect,
1,
pDestSurface->Peek_D3D_Surface(),
&dstPoint);
srcRect.right = oldVal;
}
}
}
/**Set the shroud color within the border area of the map*/
void W3DShroud::setBorderShroudLevel(W3DShroudLevel level)
{
m_boderShroudLevel = level;
m_clearDstTexture = TRUE;
}
//-----------------------------------------------------------------------------
///@todo: remove this
TextureClass *DummyTexture=NULL;
//#define LOAD_DUMMY_SHROUD
//-----------------------------------------------------------------------------
//DECLARE_PERF_TIMER(shroudCopy)
//-----------------------------------------------------------------------------
/** Updates video memory surface with currently visible shroud data */
void W3DShroud::render(CameraClass *cam)
{
if (!m_pSrcTexture)
return; //nothing to update from. Must be in reset state.
if (DX8Wrapper::_Get_D3D_Device8() && (DX8Wrapper::_Get_D3D_Device8()->TestCooperativeLevel()) != D3D_OK)
return; //device not ready to render anything
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData && TheGlobalData->m_fogOfWarOn != m_drawFogOfWar)
{
//fog state has changed since last time shroud system was initialized
reset();
ReleaseResources();
init(TheTerrainRenderObject->getMap(),m_cellWidth,m_cellHeight);
ThePartitionManager->refreshShroudForLocalPlayer();
m_drawFogOfWar=TheGlobalData->m_fogOfWarOn;
m_clearDstTexture=TRUE;
}
#endif
DEBUG_ASSERTCRASH( m_pSrcTexture != NULL, ("Updating unallocated shroud texture"));
#ifdef LOAD_DUMMY_SHROUD
static doInit=1;
if (doInit)
{
//some temporary code here to debug the shroud.
///@todo: remove this debug buffer fill.
fillShroudData(1.0f); //force to all shrouded
Short *src=(Short *)m_srcTextureData;
//fill with some dummy values
src[0]=(char)0xff;
src[1]=(char)0xff;
src[m_numCellsX]=(char)0xff;
src[m_numCellsX+1]=(char)0xff;
src[m_numCellsX*2]=(char)0xff;
src[m_numCellsX*9+8]=(char)0xff;
src[m_numCellsX*8+8]=(char)0xff;
src[m_numCellsX*7+8]=(char)0xff;
src[m_numCellsX*8+9]=(char)0xff;
src[m_numCellsX*8+7]=(char)0xff;
DummyTexture=WW3DAssetManager::Get_Instance()->Get_Texture("shroud1024.tga");
Short *dataDest=(Short *)((char *)m_srcTextureData); //offset to correct row of full sysmem shroud
Int pitchDest = m_srcTexturePitch >> 1; //2 bytes per pixel so divide byte count by 2.
//Copy the dummy shroud into our game shroud.
SurfaceClass *pSurface=DummyTexture->Get_Surface_Level(0);
Int pitch;
Int *dataSrc=(Int *)((char *)pSurface->Lock(&pitch)); //offset to correct row of full sysmem shroud
pitch >>= 2; //4 bytes per pixel so divide byte count by 4.
SurfaceClass::SurfaceDescription desc;
pSurface->Get_Description(desc);
//Check if source data is larger than our current shroud
desc.Width = __min(desc.Width,m_numCellsX);
desc.Height = __min(desc.Height,m_numCellsY);
for (Int y=0; y<desc.Height; y++)
{
for (Int x=0; x<desc.Width; x++)
{
#if defined(_DEBUG) || defined(_INTERNAL)
if (TheGlobalData && TheGlobalData->m_fogOfWarOn)
{
dataDest[x]=((TheGlobalData->m_shroudColor.getAsInt()>>4)&0xf) | (((TheGlobalData->m_shroudColor.getAsInt()>>12)&0xf)<<4) | (((TheGlobalData->m_shroudColor.getAsInt()>>20)&0xf)<<8) | ((((255-(dataSrc[x] & 0xff))>>4)&0xf)<<12);
// dataDest[x]=((SHROUD_COLOR>>4)&0xf) | (((SHROUD_COLOR>>12)&0xf)<<4) | (((SHROUD_COLOR>>20)&0xf)<<8) | ((((255-(dataSrc[x] & 0xff))>>4)&0xf)<<12);
}
else
#endif
{
dataDest[x]=((dataSrc[x]>>3)&0x1f) | (((dataSrc[x]>>10)&0x3f)<<5) | (((dataSrc[x]>>19)&0x1f)<<11);
}
}
dataDest += pitchDest; //skip to next row.
dataSrc += pitch; //skip to next row of shroud
}
pSurface->Unlock();
REF_PTR_RELEASE (pSurface);
REF_PTR_RELEASE (DummyTexture);
doInit=0;
}
#endif //LOAD_DUMMY_SHROUD
WorldHeightMap *hm=TheTerrainRenderObject->getMap();
Int visStartX=REAL_TO_INT_FLOOR((Real)(hm->getDrawOrgX()-hm->getBorderSize())*MAP_XY_FACTOR/m_cellWidth); //start of rendered heightmap rectangle
if (visStartX < 0)
visStartX = 0; //no shroud is applied in border area so it always starts at > 0
Int visStartY=REAL_TO_INT_FLOOR((Real)(hm->getDrawOrgY()-hm->getBorderSize())*MAP_XY_FACTOR/m_cellHeight);
if (visStartY < 0)
visStartY = 0; //no shroud is applied in border area so it always starts at > 0
Int visEndX=visStartX+REAL_TO_INT_FLOOR((Real)(hm->getDrawWidth()-1)*MAP_XY_FACTOR/m_cellWidth)+1; //size of rendered heightmap rectangle
Int visEndY=visStartY+REAL_TO_INT_FLOOR((Real)(hm->getDrawHeight()-1)*MAP_XY_FACTOR/m_cellHeight)+1;
if (visEndX > m_numCellsX)
{
visStartX -= visEndX - m_numCellsX; //shift visible rectangle to fall within terrain bounds
if (visStartX < 0)
visStartX = 0;
visEndX = m_numCellsX;
}
if (visEndY > m_numCellsY)
{
visStartY -= visEndY - m_numCellsY; //shift visible rectangle to fall within terrain bounds
if (visStartY < 0)
visStartY = 0;
visEndY = m_numCellsY;
}
m_drawOriginX = (Real)visStartX * m_cellWidth;
m_drawOriginY = (Real)visStartY * m_cellHeight;
//If system memory usage becomes too large, we should store shroud as Bytes. Update
//a system memory texture with data. Then copy this to video memory. For now we're
//holding shroud data directly in a texture to avoid the extra copy.
/* pSurface=m_pSrcTexture->Get_Surface_Level(0);
data=(Short *)((char *)pSurface->Lock(&pitch) + visStartY*pitch); //offset to correct row of full sysmem shroud
pitch >>= 1; //we have 2 bytes per pixel, so divide pitch by 2
Byte *sd=shroudData+visStartY*MAX_MAP_SHROUDSIZE; //pointer to first shroud row
for (Int y=visStartY; y<(visStartY+visSizeY); y++)
{
for (Int x=visStartX; x<(visStartX+visSizeX); x++)
{
data[x]=sd[x]|(sd[x]<<8);
}
data += pitch; //skip to next row.
sd += MAX_MAP_SHROUDSIZE; //skip to next row of shroud
}
pSurface->Unlock();
*/
if (m_pDstTexture->Get_Mag_Filter() != m_shroudFilter)
{
m_pDstTexture->Set_Mag_Filter(m_shroudFilter);
m_pDstTexture->Set_Min_Filter(m_shroudFilter);
}
//Update video memory texture with sysmem copy
SurfaceClass* pDestSurface;
{
pDestSurface=m_pDstTexture->Get_Surface_Level(0);
}
RECT srcRect;
POINT dstPoint={1,1}; //first row/column is reserved for border.
srcRect.left=visStartX;
srcRect.top=visStartY;
srcRect.right=visEndX;
srcRect.bottom=visEndY;
#ifdef DO_FOG_INTERPOLATION
//interpolate current shroud state to the final one
interpolateFogLevels(&srcRect);
#endif
if (m_clearDstTexture)
{ //we need to clear unused parts of the destination texture to a known
//color in order to keep map border in the state we want.
m_clearDstTexture=FALSE;
fillBorderShroudData(m_boderShroudLevel, pDestSurface);
}
{
//USE_PERF_TIMER(shroudCopy)
DX8Wrapper::_Copy_DX8_Rects(
m_pSrcTexture,
&srcRect,
1,
pDestSurface->Peek_D3D_Surface(),
&dstPoint);
}
REF_PTR_RELEASE (pDestSurface);
}
#define FOG_INTERPOLATION_RATE (255.0f/1000.0f) //take one second to go from black to fully lit.
//-----------------------------------------------------------------------------
void W3DShroud::interpolateFogLevels(RECT *rect)
{
static UnsignedInt prevTime = timeGetTime();
UnsignedInt timeDiff=timeGetTime()-prevTime;
if (!timeDiff)
return; //no time has elapsed
prevTime +=timeDiff; //update for next frame
Int maxFogChange=FOG_INTERPOLATION_RATE * (Real)timeDiff; //maximum amount of fog change allowed in frame.
if (maxFogChange > 255)
maxFogChange = 255;
W3DShroudLevel levelDelta = maxFogChange;
W3DShroudLevel *startLevel=m_currentFogData;
W3DShroudLevel *finalLevel=m_finalFogData;
for (Int j=0; j<m_numCellsY; j++)
{
for (Int i=0; i<m_numCellsX; i++,startLevel++,finalLevel++)
if (*startLevel != *finalLevel)
{ //fog needs fading.
if (*startLevel == *finalLevel)
continue;
else
if (*finalLevel < *startLevel)
{
if ((*startLevel - *finalLevel) < levelDelta)
*startLevel = *finalLevel; //change too large so clamp to final value.
else
*startLevel -= levelDelta;
}
else
if (*finalLevel > *startLevel)
{
if ((*finalLevel - *startLevel) < levelDelta)
*startLevel = *finalLevel; //change too large so clamp to final value.
else
*startLevel += levelDelta;
}
setShroudLevel(i,j,*startLevel,TRUE);
}
}
}
//-----------------------------------------------------------------------------
void W3DShroud::setShroudFilter(Bool enable)
{
if (enable)
m_shroudFilter=TextureClass::FILTER_TYPE_DEFAULT;
else
m_shroudFilter=TextureClass::FILTER_TYPE_NONE;
}
//-----------------------------------------------------------------------------
///Set render states required to draw shroud pass.
void W3DShroudMaterialPassClass::Install_Materials(void) const
{
if (TheTerrainRenderObject->getShroud())
{
W3DShaderManager::setTexture(0,TheTerrainRenderObject->getShroud()->getShroudTexture());
W3DShaderManager::setShader(W3DShaderManager::ST_SHROUD_TEXTURE, 0);
}
}
//-----------------------------------------------------------------------------
///Restore render states that W3D doesn't know about.
void W3DShroudMaterialPassClass::UnInstall_Materials(void) const
{
W3DShaderManager::resetShader(W3DShaderManager::ST_SHROUD_TEXTURE);
}
//-----------------------------------------------------------------------------
///Set render states required to draw shroud pass.
void W3DMaskMaterialPassClass::Install_Materials(void) const
{
W3DShaderManager::setShader(W3DShaderManager::ST_MASK_TEXTURE, 0);
}
//-----------------------------------------------------------------------------
///Restore render states that W3D doesn't know about.
void W3DMaskMaterialPassClass::UnInstall_Materials(void) const
{
if (m_allowUninstall)
W3DShaderManager::resetShader(W3DShaderManager::ST_MASK_TEXTURE);
}

View file

@ -0,0 +1,385 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
#include "W3DDevice/GameClient/W3DStatusCircle.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assetmgr.h>
#include <texture.h>
#include <tri.h>
#include <colmath.h>
#include <coltest.h>
#include <rinfo.h>
#include <camera.h>
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/Shader.h"
#include "Common/GlobalData.h"
#include "common/MapObject.h"
#include "GameLogic/GameLogic.h"
#include "GameLogic/ScriptEngine.h"
#define SC_DETAIL_BLEND ( 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::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE, ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE) )
// Texturing, no zbuffer, disabled zbuffer write, primary gradient, alpha blending
#define SC_ALPHA ( SHADE_CNST(ShaderClass::PASS_ALWAYS, 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_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Texturing, no zbuffer, disabled zbuffer write, primary gradient, alpha blending
#define SC_ALPHA_Z ( 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_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
// Texturing, no zbuffer, disabled zbuffer write, no gradient, add src to dest.
#define SC_ADD ( SHADE_CNST(ShaderClass::PASS_ALWAYS, ShaderClass::DEPTH_WRITE_DISABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
ShaderClass::DSTBLEND_ONE, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_DISABLE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, \
ShaderClass::DETAILCOLOR_DISABLE, ShaderClass::DETAILALPHA_DISABLE) )
#define VERTEX_BUFFER_TILE_LENGTH 32 //tiles of side length 32 (grid of 33x33 vertices).
#define VERTS_IN_BLOCK_ROW (VERTEX_BUFFER_TILE_LENGTH+1)
static ShaderClass detailOpaqueShader(SC_ALPHA);
Bool W3DStatusCircle::m_needUpdate;
Int W3DStatusCircle::m_diffuse=255; // blue.
W3DStatusCircle::~W3DStatusCircle(void)
{
freeMapResources();
}
W3DStatusCircle::W3DStatusCircle(void)
{
m_indexBuffer=NULL;
m_vertexMaterialClass=NULL;
m_vertexBufferCircle=NULL;
m_vertexBufferScreen=NULL;
}
bool W3DStatusCircle::Cast_Ray(RayCollisionTestClass & raytest)
{
return false;
}
//@todo: MW Handle both of these properly!!
W3DStatusCircle::W3DStatusCircle(const W3DStatusCircle & src)
{
*this = src;
}
W3DStatusCircle & W3DStatusCircle::operator = (const W3DStatusCircle & that)
{
assert(false);
return *this;
}
void W3DStatusCircle::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
{
Vector3 ObjSpaceCenter((float)1000*0.5f,(float)1000*0.5f,(float)0);
float length = ObjSpaceCenter.Length();
sphere.Init(ObjSpaceCenter, length);
}
void W3DStatusCircle::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
{
Vector3 minPt(0,0,0);
Vector3 maxPt((float)1000,(float)1000,(float)1000);
box.Init(minPt,maxPt);
}
Int W3DStatusCircle::Class_ID(void) const
{
return RenderObjClass::CLASSID_UNKNOWN;
}
RenderObjClass * W3DStatusCircle::Clone(void) const
{
return NEW W3DStatusCircle(*this);
}
Int W3DStatusCircle::freeMapResources(void)
{
REF_PTR_RELEASE(m_indexBuffer);
REF_PTR_RELEASE(m_vertexBufferScreen);
REF_PTR_RELEASE(m_vertexBufferCircle);
REF_PTR_RELEASE(m_vertexMaterialClass);
return 0;
}
#define NUM_TRI 20
//Allocate a heightmap of x by y vertices.
//data must be an array matching this size.
Int W3DStatusCircle::initData(void)
{
Int i;
m_needUpdate = true;
freeMapResources(); //free old data and ib/vb
m_numTriangles = NUM_TRI;
m_indexBuffer=NEW_REF(DX8IndexBufferClass,(m_numTriangles*3));
// Fill up the IB
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBuffer);
UnsignedShort *ib=lockIdxBuffer.Get_Index_Array();
for (i=0; i<3*m_numTriangles; i+=3)
{
ib[0]=i;
ib[1]=i+1;
ib[2]=i+2;
ib+=3; //skip the 3 indices we just filled
}
m_vertexBufferCircle=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,m_numTriangles*3,DX8VertexBufferClass::USAGE_DEFAULT));
m_vertexBufferScreen=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,2*3,DX8VertexBufferClass::USAGE_DEFAULT));
//go with a preset material for now.
m_vertexMaterialClass=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
m_shaderClass = ShaderClass::ShaderClass(SC_ALPHA);// _PresetOpaque2DShader;//; //_PresetOpaqueShader;
return 0;
}
/** updateCircleVB puts a circle with a team color vertex buffer. */
Int W3DStatusCircle::updateCircleVB(void)
{
Int i, k;
Real shade;
DX8VertexBufferClass *pVB = m_vertexBufferCircle;
if (m_vertexBufferCircle )
{
m_needUpdate = false;
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(pVB);
VertexFormatXYZDUV1 *vb = (VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
const Real theZ = 0.0f;
const Real theRadius = 0.02f;
const Int theAlpha = 127;
Int diffuse = m_diffuse + (theAlpha<<24); // b g<<8 r<<16 a<<24.
Int limit = m_numTriangles;
float curAngle = 0;
float deltaAngle = 2*PI/limit;
for (i=0; i<limit; i++)
{
shade=0.7f*255.0f;
for (k=0; k<3; k++) {
vb->z= theZ;
if (k==0) {
vb->x= 0;
vb->y= 0;
} else if (k==1) {
Vector3 vec(theRadius,0,theZ);
vec.Rotate_Z(curAngle);
vb->x= vec.X;
vb->y= vec.Y;
} else if (k==2) {
Real angle = curAngle+deltaAngle;
if (i==limit-1) {
angle = 0;
}
Vector3 vec(theRadius,0,theZ);
vec.Rotate_Z(angle);
vb->x= vec.X;
vb->y= vec.Y;
}
vb->diffuse = diffuse;
vb->u1=0;
vb->v1=0;
vb++;
}
curAngle += deltaAngle;
}
return 0; //success.
}
return -1;
}
/** updateCircleVB puts a circle with a team color vertex buffer. */
Int W3DStatusCircle::updateScreenVB(Int diffuse)
{
DX8VertexBufferClass *pVB = m_vertexBufferScreen;
if (m_vertexBufferScreen )
{
m_needUpdate = false;
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(pVB);
VertexFormatXYZDUV1 *vb = (VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
vb->x = -1;
vb->y = -1;
vb->z = 0;
vb->diffuse = diffuse;
vb->u1=0;
vb->v1=0;
vb++;
vb->x = 1;
vb->y = 1;
vb->z = 0;
vb->diffuse = diffuse;
vb->u1=0;
vb->v1=0;
vb++;
vb->x = -1;
vb->y = 1;
vb->z = 0;
vb->diffuse = diffuse;
vb->u1=0;
vb->v1=0;
vb++;
vb->x = -1;
vb->y = -1;
vb->z = 0;
vb->diffuse = diffuse;
vb->u1=0;
vb->v1=0;
vb++;
vb->x = 1;
vb->y = -1;
vb->z = 0;
vb->diffuse = diffuse;
vb->u1=0;
vb->v1=0;
vb++;
vb->x = 1;
vb->y = 1;
vb->z = 0;
vb->diffuse = diffuse;
vb->u1=0;
vb->v1=0;
vb++;
return 0; //success.
}
return -1;
}
void W3DStatusCircle::Render(RenderInfoClass & rinfo)
{
if (!TheGameLogic->isInGame() || TheGameLogic->getGameMode() == GAME_SHELL)
return;
if (m_indexBuffer == NULL) {
initData();
}
if (m_indexBuffer == NULL) {
return;
}
Bool setIndex = false;
Matrix3D tm(true);
if( TheGlobalData->m_showTeamDot )
{
if (m_needUpdate) {
updateCircleVB();
}
//Apply the shader and material
DX8Wrapper::Set_Material(m_vertexMaterialClass);
DX8Wrapper::Set_Shader(m_shaderClass);
DX8Wrapper::Set_Texture(0, NULL);
DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
DX8Wrapper::Set_Vertex_Buffer(m_vertexBufferCircle);
setIndex = true;
Vector3 vec(0.95f, 0.67f, 0);
Matrix3 rot(true);
tm.Set_Translation(vec);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
DX8Wrapper::Draw_Triangles( 0,NUM_TRI, 0, (m_numTriangles*3));
}
ScriptEngine::TFade fade = TheScriptEngine->getFade();
if (fade == ScriptEngine::FADE_NONE) {
return;
}
if (!setIndex) {
DX8Wrapper::Set_Material(m_vertexMaterialClass);
DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
DX8Wrapper::Set_Texture(0, NULL);
}
tm.Make_Identity();
Real intensity = TheScriptEngine->getFadeValue();
Int clr = 255*intensity;
Int diffuse = (0xff<<24)|(clr<<16)|(clr<<8)|clr; // b g<<8 r<<16 a<<24.
updateScreenVB(diffuse);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
DX8Wrapper::Set_Shader(ShaderClass(SC_ADD));
DX8Wrapper::Set_Vertex_Buffer(m_vertexBufferScreen);
DX8Wrapper::Apply_Render_State_Changes();
switch (fade) {
default:
case ScriptEngine::FADE_ADD:
DX8Wrapper::Draw_Triangles( 0,2, 0, (2*3));
break;
case ScriptEngine::FADE_SUBTRACT:
DX8Wrapper::Set_DX8_Render_State(D3DRS_BLENDOP, D3DBLENDOP_REVSUBTRACT );
DX8Wrapper::Draw_Triangles( 0,2, 0, (2*3));
DX8Wrapper::Set_DX8_Render_State(D3DRS_BLENDOP, D3DBLENDOP_ADD );
break;
case ScriptEngine::FADE_SATURATE:
// 4x multiply
DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND,D3DBLEND_DESTCOLOR);
DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND,D3DBLEND_SRCCOLOR);
DX8Wrapper::Draw_Triangles( 0,2, 0, (2*3));
DX8Wrapper::Draw_Triangles( 0,2, 0, (2*3));
break;
case ScriptEngine::FADE_MULTIPLY:
// Straight multiply
DX8Wrapper::Set_DX8_Render_State(D3DRS_SRCBLEND,D3DBLEND_ZERO);
DX8Wrapper::Set_DX8_Render_State(D3DRS_DESTBLEND,D3DBLEND_SRCCOLOR);
DX8Wrapper::Draw_Triangles( 0,2, 0, (2*3));
break;
}
ShaderClass::Invalidate();
}

View file

@ -0,0 +1,979 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTerrainTracks.cpp ////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: RTS3
//
// File name: W3DTerrainTracks.cpp
//
// Created: Mark Wilczynski, May 2001
//
// Desc: Draw track marks on the terrain. Uses a sequence of connected
// quads that are oriented to fit the terrain and updated when object
// moves.
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DTerrainTracks.h"
#include "W3DDevice/GameClient/heightmap.h"
#include "Common/PerfTimer.h"
#include "common/GlobalData.h"
#include "common/Debug.h"
#include "texture.h"
#include "colmath.h"
#include "coltest.h"
#include "rinfo.h"
#include "camera.h"
#include "assetmgr.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/Scene.h"
#include "GameLogic/TerrainLogic.h"
#include "GameLogic/Object.h"
#include "GameClient/Drawable.h"
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
#define BRIDGE_OFFSET_FACTOR 0.25f //amount to raise tracks above bridges.
//=============================================================================
// TerrainTracksRenderObjClass::~TerrainTracksRenderObjClass
//=============================================================================
/** Destructor. Releases w3d assets. */
//=============================================================================
TerrainTracksRenderObjClass::~TerrainTracksRenderObjClass(void)
{
freeTerrainTracksResources();
}
//=============================================================================
// TerrainTracksRenderObjClass::TerrainTracksRenderObjClass
//=============================================================================
/** Constructor. Just nulls out some variables. */
//=============================================================================
TerrainTracksRenderObjClass::TerrainTracksRenderObjClass(void)
{
m_stageZeroTexture=NULL;
m_lastAnchor=Vector3(0,1,2.25);
m_haveAnchor=false;
m_haveCap=true;
m_topIndex=0;
m_bottomIndex=0;
m_activeEdgeCount=0;
m_totalEdgesAdded=0;
m_bound=false;
m_ownerDrawable = NULL;
}
//=============================================================================
// TerrainTracksRenderObjClass::Get_Obj_Space_Bounding_Sphere
//=============================================================================
/** WW3D method that returns object bounding sphere used in frustum culling*/
//=============================================================================
void TerrainTracksRenderObjClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
{ /// @todo: Add code to cull track marks to screen by constantly updating bounding volumes
sphere=m_boundingSphere;
}
//=============================================================================
// TerrainTracksRenderObjClass::Get_Obj_Space_Bounding_Box
//=============================================================================
/** WW3D method that returns object bounding box used in collision detection*/
//=============================================================================
void TerrainTracksRenderObjClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
{
box=m_boundingBox;
}
//=============================================================================
// MirrorRenderObjClass::Class_ID
//=============================================================================
/** returns the class id, so the scene can tell what kind of render object it has. */
//=============================================================================
Int TerrainTracksRenderObjClass::Class_ID(void) const
{
return RenderObjClass::CLASSID_IMAGE3D;
}
//=============================================================================
// TerrainTracksRenderObjClass::Clone
//=============================================================================
/** Not used, but required virtual method. */
//=============================================================================
RenderObjClass * TerrainTracksRenderObjClass::Clone(void) const
{
assert(false);
return NULL;
}
//=============================================================================
// TerrainTracksRenderObjClass::freeTerrainTracksResources
//=============================================================================
/** Free any W3D resources associated with this object */
//=============================================================================
Int TerrainTracksRenderObjClass::freeTerrainTracksResources(void)
{
REF_PTR_RELEASE(m_stageZeroTexture);
m_haveAnchor=false;
m_haveCap=true;
m_topIndex=0;
m_bottomIndex=0;
m_activeEdgeCount=0;
m_totalEdgesAdded=0;
m_ownerDrawable = NULL;
return 0;
}
//=============================================================================
// TerrainTracksRenderObjClass::init
//=============================================================================
/** Setup size settings and allocate W3D texture */
//=============================================================================
void TerrainTracksRenderObjClass::init( Real width, Real length, const Char *texturename)
{
freeTerrainTracksResources(); //free old data and ib/vb
m_boundingSphere.Init(Vector3(0,0,0),400*MAP_XY_FACTOR);
m_boundingBox.Center.Set(0.0f, 0.0f, 0.0f);
m_boundingBox.Extent.Set(400.0f*MAP_XY_FACTOR, 400.0f*MAP_XY_FACTOR, 1.0f);
m_width=width;
m_length=length;
//no sense culling these things since they have very irregular shape and fade
//out over time.
Set_Force_Visible(TRUE);
m_stageZeroTexture=WW3DAssetManager::Get_Instance()->Get_Texture(texturename);
}
//=============================================================================
// TerrainTracksRenderObjClass::addCapEdgeToTrack
//=============================================================================
/** Cap the current track (adding an feathered edge) so we're ready to resume
the track at a new location. Used by objects entering FOW where we need to
stop adding edges to the track when they enter the fog boundary but resume
elsewhere if they become visible again.
*/
//=============================================================================
void TerrainTracksRenderObjClass::addCapEdgeToTrack(Real x, Real y)
{
/// @todo: Have object pass its height and orientation so we can remove extra calls.
if (m_haveCap)
{ //we already have a cap or there are no segments to cap
return;
}
if (m_activeEdgeCount == 1)
{ //if we only have one edge, then it must be the current anchor edge.
//since achnors are caps, there is not point in adding another.
m_haveCap=TRUE;
m_haveAnchor=false; //recreate a new anchor when track resumes.
return;
}
Vector3 vPos,vZ;
Coord3D vZTmp;
PathfindLayerEnum objectLayer;
Real eHeight;
if (m_ownerDrawable && (objectLayer=m_ownerDrawable->getObject()->getLayer()) != LAYER_GROUND)
eHeight=BRIDGE_OFFSET_FACTOR+TheTerrainLogic->getLayerHeight(x,y,objectLayer,&vZTmp);
else
eHeight=TheTerrainLogic->getGroundHeight(x,y,&vZTmp);
vZ.X = vZTmp.x;
vZ.Y = vZTmp.y;
vZ.Z = vZTmp.z;
vPos.X=x;
vPos.Y=y;
vPos.Z=eHeight;
Vector3 vDir=Vector3(x,y,eHeight)-m_lastAnchor;
Int maxEdgeCount=TheTerrainTracksRenderObjClassSystem->m_maxTankTrackEdges;
//avoid sqrt() by checking distance squared since last track mark
if (vDir.Length2() < sqr(m_length))
{ //not far enough from anchor to add track
//since this is a cap, we'll force the previous segment to transparent
Int lastAddedEdge=m_topIndex-1;
if (lastAddedEdge < 0)
lastAddedEdge = maxEdgeCount-1;
m_edges[lastAddedEdge].alpha=0.0f; //force the last added edge to transparent.
m_haveCap=TRUE;
m_haveAnchor=false; //recreate a new anchor when track resumes.
return;
}
if (m_activeEdgeCount >= maxEdgeCount)
{ //no more room in buffer so release oldest edge
m_bottomIndex++;
m_activeEdgeCount--;
if (m_bottomIndex >= maxEdgeCount)
m_bottomIndex=0; //roll buffer back to start
}
if (m_topIndex >= maxEdgeCount)
m_topIndex=0; //roll around buffer
//we traveled far enough from last point.
//accept new point
vDir.Z=0; //ignore height
vDir.Normalize();
Vector3 vX;
Vector3::Cross_Product(vDir,vZ,&vX);
//calculate left end point
edgeInfo& topEdge = m_edges[m_topIndex];
topEdge.endPointPos[0]=vPos-(m_width*0.5f*vX); ///@todo: try getting height at endpoint
topEdge.endPointPos[0].Z += 0.2f * MAP_XY_FACTOR; //raise above terrain slightly
if (m_totalEdgesAdded&1) //every other edge has different set of UV's
{
topEdge.endPointUV[0].X=0.0f;
topEdge.endPointUV[0].Y=0.0f;
}
else
{
topEdge.endPointUV[0].X=0.0f;
topEdge.endPointUV[0].Y=1.0f;
}
//calculate right end point
topEdge.endPointPos[1]=vPos+(m_width*0.5f*vX); ///@todo: try getting height at endpoint
topEdge.endPointPos[1].Z += 0.2f * MAP_XY_FACTOR; //raise above terrain slightly
if (m_totalEdgesAdded&1) //every other edge has different set of UV's
{
topEdge.endPointUV[1].X=1.0f;
topEdge.endPointUV[1].Y=0.0f;
}
else
{
topEdge.endPointUV[1].X=1.0f;
topEdge.endPointUV[1].Y=1.0f;
}
topEdge.timeAdded=WW3D::Get_Sync_Time();
topEdge.alpha=0.0f; //fully transparent at cap.
m_lastAnchor=vPos;
m_activeEdgeCount++;
m_totalEdgesAdded++;
m_topIndex++; //make space for new edge
m_haveCap=TRUE;
m_haveAnchor=false;
}
//=============================================================================
// TerrainTracksRenderObjClass::addEdgeToTrack
//=============================================================================
/** Try to add an additional segment to track mark. Will do nothing if distance
* from last edge is too small. Will overwrite the oldest edge if maximum track
* length is reached. Oldest edges should by faded out by that time.
*/
//=============================================================================
void TerrainTracksRenderObjClass::addEdgeToTrack(Real x, Real y)
{
/// @todo: Have object pass its height and orientation so we can remove extra calls.
if (!m_haveAnchor)
{ //no anchor yet, make this point an anchor.
PathfindLayerEnum objectLayer;
if (m_ownerDrawable && (objectLayer=m_ownerDrawable->getObject()->getLayer()) != LAYER_GROUND)
m_lastAnchor=Vector3(x,y,TheTerrainLogic->getLayerHeight(x,y,objectLayer)+BRIDGE_OFFSET_FACTOR);
else
m_lastAnchor=Vector3(x,y,TheTerrainLogic->getGroundHeight(x,y));
m_haveAnchor=true;
m_airborne = true;
m_haveCap = true; //single segment tracks are always capped because nothing is drawn.
return;
}
m_haveCap = false; //have more than 1 segment now so will need to cap if it's interrupted.
Vector3 vPos,vZ;
Coord3D vZTmp;
Real eHeight;
PathfindLayerEnum objectLayer;
if (m_ownerDrawable && (objectLayer=m_ownerDrawable->getObject()->getLayer()) != LAYER_GROUND)
eHeight=BRIDGE_OFFSET_FACTOR+TheTerrainLogic->getLayerHeight(x,y,objectLayer,&vZTmp);
else
eHeight=TheTerrainLogic->getGroundHeight(x,y,&vZTmp);
vZ.X = vZTmp.x;
vZ.Y = vZTmp.y;
vZ.Z = vZTmp.z;
vPos.X=x;
vPos.Y=y;
vPos.Z=eHeight;
Vector3 vDir=Vector3(x,y,eHeight)-m_lastAnchor;
//avoid sqrt() by checking distance squared since last track mark
if (vDir.Length2() < sqr(m_length))
return; //not far enough from anchor to add track
Int maxEdgeCount=TheTerrainTracksRenderObjClassSystem->m_maxTankTrackEdges;
if (m_activeEdgeCount >= maxEdgeCount)
{ //no more room in buffer so release oldest edge
m_bottomIndex++;
m_activeEdgeCount--;
if (m_bottomIndex >= maxEdgeCount)
m_bottomIndex=0; //roll buffer back to start
}
if (m_topIndex >= maxEdgeCount)
m_topIndex=0; //roll around buffer
//we traveled far enough from last point.
//accept new point
vDir.Z=0; //ignore height
vDir.Normalize();
Vector3 vX;
Vector3::Cross_Product(vDir,vZ,&vX);
edgeInfo& topEdge = m_edges[m_topIndex];
//calculate left end point
topEdge.endPointPos[0]=vPos-(m_width*0.5f*vX); ///@todo: try getting height at endpoint
topEdge.endPointPos[0].Z += 0.2f * MAP_XY_FACTOR; //raise above terrain slightly
if (m_totalEdgesAdded&1) //every other edge has different set of UV's
{
topEdge.endPointUV[0].X=0.0f;
topEdge.endPointUV[0].Y=0.0f;
}
else
{
topEdge.endPointUV[0].X=0.0f;
topEdge.endPointUV[0].Y=1.0f;
}
//calculate right end point
topEdge.endPointPos[1]=vPos+(m_width*0.5f*vX); ///@todo: try getting height at endpoint
topEdge.endPointPos[1].Z += 0.2f * MAP_XY_FACTOR; //raise above terrain slightly
if (m_totalEdgesAdded&1) //every other edge has different set of UV's
{
topEdge.endPointUV[1].X=1.0f;
topEdge.endPointUV[1].Y=0.0f;
}
else
{
topEdge.endPointUV[1].X=1.0f;
topEdge.endPointUV[1].Y=1.0f;
}
topEdge.timeAdded=WW3D::Get_Sync_Time();
topEdge.alpha=1.0f; //fully opaque at start.
if (m_airborne || m_activeEdgeCount <= 1) {
topEdge.alpha=0.0f; //smooth out track restarts by setting transparent
}
m_airborne = false;
m_lastAnchor=vPos;
m_activeEdgeCount++;
m_totalEdgesAdded++;
m_topIndex++; //make space for new edge
}
//=============================================================================
// TerrainTracksRenderObjClass::Render
//=============================================================================
/** Does nothing. Just increments a counter of how many track edges were
* requested for rendering this frame. Actual rendering is done in flush().
*/
//=============================================================================
void TerrainTracksRenderObjClass::Render(RenderInfoClass & rinfo)
{ ///@todo: After adding track mark visibility tests, add visible marks to another list.
if (TheGlobalData->m_makeTrackMarks && m_activeEdgeCount >= 2)
TheTerrainTracksRenderObjClassSystem->m_edgesToFlush += m_activeEdgeCount;
}
#define DEFAULT_TRACK_SPACING (MAP_XY_FACTOR * 1.4f)
#define DEFAULT_TRACK_WIDTH 4.0f;
/**Find distance between the "trackfx" bones of the model. This tells us the correct
width for the trackmarks.
*/
static Real computeTrackSpacing(RenderObjClass *renderObj)
{
Real trackSpacing = DEFAULT_TRACK_SPACING;
Int leftTrack;
Int rightTrack;
if ((leftTrack=renderObj->Get_Bone_Index( "TREADFX01" )) != 0 && (rightTrack=renderObj->Get_Bone_Index( "TREADFX02" )) != 0)
{ //both bones found, determine distance between them.
Vector3 leftPos,rightPos;
leftPos=renderObj->Get_Bone_Transform( leftTrack ).Get_Translation();
rightPos=renderObj->Get_Bone_Transform( rightTrack ).Get_Translation();
rightPos -= leftPos; //get distance between centers of tracks
trackSpacing = rightPos.Length() + DEFAULT_TRACK_WIDTH; //add width of each track
///@todo: It's assumed that all tank treads have the same width.
};
return trackSpacing;
}
//=============================================================================
//TerrainTracksRenderObjClassSystem::bindTrack
//=============================================================================
/** Grab a track from the free store. If no free tracks exist, return NULL.
As long as a track is bound to an object (like a tank) it is ready to accept
updates with additional edges. Once it is unbound, it will expire and return
to the free store once all tracks have faded out.
Input: width in world units of each track edge (should probably width of vehicle).
length in world units between edges. Shorter lengths produce more edges and
smoother curves.
texture to use for the tracks - image should be symetrical and include alpha channel.
*/
//=============================================================================
TerrainTracksRenderObjClass *TerrainTracksRenderObjClassSystem::bindTrack( RenderObjClass *renderObject, Real length, const Char *texturename)
{
TerrainTracksRenderObjClass *mod;
mod = m_freeModules;
if( mod )
{
// take module off the free list
if( mod->m_nextSystem )
mod->m_nextSystem->m_prevSystem = mod->m_prevSystem;
if( mod->m_prevSystem )
mod->m_prevSystem->m_nextSystem = mod->m_nextSystem;
else
m_freeModules = mod->m_nextSystem;
// put module on the used list
mod->m_prevSystem = NULL;
mod->m_nextSystem = m_usedModules;
if( m_usedModules )
m_usedModules->m_prevSystem = mod;
m_usedModules = mod;
mod->init(computeTrackSpacing(renderObject),length,texturename);
mod->m_bound=true;
m_TerrainTracksScene->Add_Render_Object( mod);
} // end if
return mod;
} //end bindTrack
//=============================================================================
//TerrainTracksRenderObjClassSystem::unbindTrack
//=============================================================================
/** Called when an object (i.e Tank) will not lay down any more tracks and
doesn't need this object anymore. The track-laying object will be returned
to pool of available tracks as soon as any remaining track edges have faded out.
*/
//=============================================================================
void TerrainTracksRenderObjClassSystem::unbindTrack( TerrainTracksRenderObjClass *mod )
{
//this object should return to free store as soon as there is nothing
//left to render.
mod->m_bound=false;
mod->m_ownerDrawable = NULL;
}
//=============================================================================
//TerrainTracksRenderObjClassSystem::releaseTrack
//=============================================================================
/** Returns a track laying object to free store to be used again later.
*/
void TerrainTracksRenderObjClassSystem::releaseTrack( TerrainTracksRenderObjClass *mod )
{
if (mod==NULL)
return;
DEBUG_ASSERTCRASH(mod->m_bound == false, ("mod is bound."));
// remove module from used list
if( mod->m_nextSystem )
mod->m_nextSystem->m_prevSystem = mod->m_prevSystem;
if( mod->m_prevSystem )
mod->m_prevSystem->m_nextSystem = mod->m_nextSystem;
else
m_usedModules = mod->m_nextSystem;
// add module to free list
mod->m_prevSystem = NULL;
mod->m_nextSystem = m_freeModules;
if( m_freeModules )
m_freeModules->m_prevSystem = mod;
m_freeModules = mod;
mod->freeTerrainTracksResources();
m_TerrainTracksScene->Remove_Render_Object(mod);
}
//=============================================================================
// TerrainTracksRenderObjClassSystem::TerrainTracksRenderObjClassSystem
//=============================================================================
/** Constructor. Just nulls out some variables. */
//=============================================================================
TerrainTracksRenderObjClassSystem::TerrainTracksRenderObjClassSystem()
{
m_usedModules = NULL;
m_freeModules = NULL;
m_TerrainTracksScene = NULL;
m_edgesToFlush = 0;
m_indexBuffer = NULL;
m_vertexMaterialClass = NULL;
m_vertexBuffer = NULL;
m_maxTankTrackEdges=TheGlobalData->m_maxTankTrackEdges;
m_maxTankTrackOpaqueEdges=TheGlobalData->m_maxTankTrackOpaqueEdges;
m_maxTankTrackFadeDelay=TheGlobalData->m_maxTankTrackFadeDelay;
}
//=============================================================================
// TerrainTracksRenderObjClassSystem::~TerrainTracksRenderObjClassSystem
//=============================================================================
/** Destructor. Free all pre-allocated track laying render objects*/
//=============================================================================
TerrainTracksRenderObjClassSystem::~TerrainTracksRenderObjClassSystem( void )
{
// free all data
shutdown();
m_vertexMaterialClass=NULL;
m_TerrainTracksScene=NULL;
}
//=============================================================================
// TerrainTracksRenderObjClassSystem::ReAcquireResources
//=============================================================================
/** (Re)allocates all W3D assets after a reset.. */
//=============================================================================
void TerrainTracksRenderObjClassSystem::ReAcquireResources(void)
{
Int i;
const Int numModules=TheGlobalData->m_maxTerrainTracks;
// just for paranoia's sake.
REF_PTR_RELEASE(m_indexBuffer);
REF_PTR_RELEASE(m_vertexBuffer);
//Create static index buffers. These will index the vertex buffers holding the track segments
m_indexBuffer=NEW_REF(DX8IndexBufferClass,((m_maxTankTrackEdges-1)*6));
// Fill up the IB
{
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBuffer);
UnsignedShort *ib=lockIdxBuffer.Get_Index_Array();
for (i=0; i<(m_maxTankTrackEdges-1); i++)
{
ib[3]=ib[0]=i*2;
ib[1]=i*2+1;
ib[4]=ib[2]=(i+1)*2+1;
ib[5]=(i+1)*2;
ib+=6; //skip the 6 indices we just filled
}
}
DEBUG_ASSERTCRASH(numModules*m_maxTankTrackEdges*2 < 65535, ("Too many terrain track edges"));
m_vertexBuffer=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,numModules*m_maxTankTrackEdges*2,DX8VertexBufferClass::USAGE_DYNAMIC));
}
//=============================================================================
// TerrainTracksRenderObjClassSystem::ReleaseResources
//=============================================================================
/** (Re)allocates all W3D assets after a reset.. */
//=============================================================================
void TerrainTracksRenderObjClassSystem::ReleaseResources(void)
{
REF_PTR_RELEASE(m_indexBuffer);
REF_PTR_RELEASE(m_vertexBuffer);
// Note - it is ok to not release the material, as it is a w3d object that
// has no dx8 resources. jba.
}
//=============================================================================
// TerrainTracksRenderObjClassSystem::init
//=============================================================================
/** initialize the system, allocate all the render objects we will need */
//=============================================================================
void TerrainTracksRenderObjClassSystem::init( SceneClass *TerrainTracksScene )
{
const Int numModules=TheGlobalData->m_maxTerrainTracks;
Int i;
TerrainTracksRenderObjClass *mod;
m_TerrainTracksScene=TerrainTracksScene;
ReAcquireResources();
//go with a preset material for now.
m_vertexMaterialClass=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
//use a multi-texture shader: (text1*diffuse)*text2.
m_shaderClass = ShaderClass::_PresetAlphaShader;//_PresetATestSpriteShader;//_PresetOpaqueShader;
// we cannot initialize a system that is already initialized
if( m_freeModules || m_usedModules )
{
// system already online!
assert( 0 );
return;
} // end if
// allocate our modules for this system
for( i = 0; i < numModules; i++ )
{
mod = NEW_REF( TerrainTracksRenderObjClass, () );
if( mod == NULL )
{
// unable to allocate modules needed
assert( 0 );
return;
} // end if
mod->m_prevSystem = NULL;
mod->m_nextSystem = m_freeModules;
if( m_freeModules )
m_freeModules->m_prevSystem = mod;
m_freeModules = mod;
} // end for i
} // end init
//=============================================================================
// TerrainTracksRenderObjClassSystem::shutdown
//=============================================================================
/** Shutdown and free all memory for this system */
//=============================================================================
void TerrainTracksRenderObjClassSystem::shutdown( void )
{
TerrainTracksRenderObjClass *nextMod,*mod;
//release unbound tracks that may still be fading out
mod=m_usedModules;
while(mod)
{
nextMod=mod->m_nextSystem;
if (!mod->m_bound)
releaseTrack(mod);
mod = nextMod;
} // end while
// free all attached things and used modules
assert( m_usedModules == NULL );
// free all module storage
while( m_freeModules )
{
nextMod = m_freeModules->m_nextSystem;
REF_PTR_RELEASE (m_freeModules);
m_freeModules = nextMod;
} // end while
REF_PTR_RELEASE(m_indexBuffer);
REF_PTR_RELEASE(m_vertexMaterialClass);
REF_PTR_RELEASE(m_vertexBuffer);
} // end shutdown
//=============================================================================
// TerrainTracksRenderObjClassSystem::update
//=============================================================================
/** Update the state of all active track marks - fade, expire, etc. */
//=============================================================================
void TerrainTracksRenderObjClassSystem::update()
{
Int iTime=WW3D::Get_Sync_Time();
Real iDiff;
TerrainTracksRenderObjClass *mod=m_usedModules,*nextMod;
//first update all the tracks
while( mod )
{
Int i,index;
Vector3 *endPoint;
Vector2 *endPointUV;
nextMod = mod->m_nextSystem;
if (!TheGlobalData->m_makeTrackMarks)
mod->m_haveAnchor=false; //force a track restart next time around.
for (i=0,index=mod->m_bottomIndex; i<mod->m_activeEdgeCount; i++,index++)
{
if (index >= m_maxTankTrackEdges)
index=0;
endPoint=&mod->m_edges[index].endPointPos[0]; //left endpoint
endPointUV=&mod->m_edges[index].endPointUV[0];
iDiff=(float)(iTime-mod->m_edges[index].timeAdded);
iDiff = 1.0f - iDiff/(Real)m_maxTankTrackFadeDelay;
if (iDiff < 0.0)
iDiff=0.0f;
if (mod->m_edges[index].alpha>0.0f) {
mod->m_edges[index].alpha=iDiff;
}
if (iDiff == 0.0f)
{ //this edge was invisible, we can remove it
mod->m_bottomIndex++;
mod->m_activeEdgeCount--;
if (mod->m_bottomIndex >= m_maxTankTrackEdges)
mod->m_bottomIndex=0; //roll buffer back to start
}
if (mod->m_activeEdgeCount == 0 && !mod->m_bound)
releaseTrack(mod);
}
mod = nextMod;
} // end while
}
//=============================================================================
// TerrainTracksRenderObjClassSystem::flush
//=============================================================================
/** Draw all active track marks for this frame */
//=============================================================================
void TerrainTracksRenderObjClassSystem::flush()
{
/** @todo: Optimize system by drawing tracks as triangle strips and use dynamic vertex buffer access.
May also try rendering all tracks with one call to W3D/D3D by grouping them by texture.
Try improving the fit to vertical surfaces like cliffs.
*/
Int diffuseLight;
TerrainTracksRenderObjClass *mod=m_usedModules;
if (!mod)
return; //nothing to render
Int trackStartIndex;
Real distanceFade;
if (ShaderClass::Is_Backface_Culling_Inverted())
return; //don't render track marks in reflections.
// adjust shading for time of day.
Real shadeR, shadeG, shadeB;
shadeR = TheGlobalData->m_terrainAmbient[0].red;
shadeG = TheGlobalData->m_terrainAmbient[0].green;
shadeB = TheGlobalData->m_terrainAmbient[0].blue;
shadeR += TheGlobalData->m_terrainDiffuse[0].red/2;
shadeG += TheGlobalData->m_terrainDiffuse[0].green/2;
shadeB += TheGlobalData->m_terrainDiffuse[0].blue/2;
shadeR*=255.0f;
shadeG*=255.0f;
shadeB*=255.0f;
diffuseLight = REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16);
Real numFadedEdges=m_maxTankTrackEdges-m_maxTankTrackOpaqueEdges;
//check if there is anything to draw and fill vertex buffer
if (m_edgesToFlush >= 2)
{
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexBuffer);
VertexFormatXYZDUV1 *verts = (VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
trackStartIndex=0;
mod=m_usedModules;
//Fill our vertex buffer with all the tracks
while( mod )
{
Int i,index;
Vector3 *endPoint;
Vector2 *endPointUV;
if (mod->m_activeEdgeCount >= 2 && mod->Is_Really_Visible())
{
for (i=0,index=mod->m_bottomIndex; i<mod->m_activeEdgeCount; i++,index++)
{
if (index >= m_maxTankTrackEdges)
index=0;
endPoint=&mod->m_edges[index].endPointPos[0]; //left endpoint
endPointUV=&mod->m_edges[index].endPointUV[0];
distanceFade=1.0f;
if ((mod->m_activeEdgeCount -1 -i) >= m_maxTankTrackOpaqueEdges)// && i < (MAX_PER_TRACK_EDGE_COUNT-FORCE_FADE_AT_EDGE))
{ //we're getting close to the limit on the number of track pieces allowed
//so force it to fade out.
distanceFade=1.0f-(float)((mod->m_activeEdgeCount -i)-m_maxTankTrackOpaqueEdges)/numFadedEdges;
}
distanceFade *= mod->m_edges[index].alpha; //adjust fade with distance from start of track
verts->x=endPoint->X;
verts->y=endPoint->Y;
verts->z=endPoint->Z;
verts->u1=endPointUV->X;
verts->v1=endPointUV->Y;
//fade the alpha channel with distance
verts->diffuse=diffuseLight | ( REAL_TO_INT(distanceFade*255.0f) <<24);
verts++;
endPoint=&mod->m_edges[index].endPointPos[1]; //right endpoint
endPointUV=&mod->m_edges[index].endPointUV[1];
verts->x=endPoint->X;
verts->y=endPoint->Y;
verts->z=endPoint->Z;
verts->u1=endPointUV->X;
verts->v1=endPointUV->Y; ///@todo: Add diffuse lighting.
verts->diffuse=diffuseLight | ( REAL_TO_INT(distanceFade*255.0f) <<24);
verts++;
}//for
}// mod has edges to render
mod = mod->m_nextSystem;
} //while (mod)
}//edges to flush
//draw the filled vertex buffers
if (m_edgesToFlush >= 2)
{
ShaderClass::Invalidate();
DX8Wrapper::Set_Material(m_vertexMaterialClass);
DX8Wrapper::Set_Shader(m_shaderClass);
DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
DX8Wrapper::Set_Vertex_Buffer(m_vertexBuffer);
trackStartIndex=0;
mod=m_usedModules;
Matrix3D tm(mod->Transform);
DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
while (mod)
{
if (mod->m_activeEdgeCount >= 2 && mod->Is_Really_Visible())
{
DX8Wrapper::Set_Texture(0,mod->m_stageZeroTexture);
DX8Wrapper::Set_Index_Buffer_Index_Offset(trackStartIndex);
DX8Wrapper::Draw_Triangles( 0,(mod->m_activeEdgeCount-1)*2, 0, mod->m_activeEdgeCount*2);
trackStartIndex += mod->m_activeEdgeCount*2;
}
mod=mod->m_nextSystem;
}
} //there are some edges to render in pool.
m_edgesToFlush=0; //reset count for next flush
}
/**Removes all remaining tracks from the rendering system*/
void TerrainTracksRenderObjClassSystem::Reset(void)
{
TerrainTracksRenderObjClass *nextMod,*mod=m_usedModules;
while(mod)
{
nextMod=mod->m_nextSystem;
releaseTrack(mod);
mod = nextMod;
} // end while
// free all attached things and used modules
assert( m_usedModules == NULL );
m_edgesToFlush=0;
}
/**Clear the treads from each track laying object without freeing the objects.
Mostly used when user changed LOD level*/
void TerrainTracksRenderObjClassSystem::clearTracks(void)
{
TerrainTracksRenderObjClass *mod=m_usedModules;
while(mod)
{
mod->m_haveAnchor=false;
mod->m_haveCap=true;
mod->m_topIndex=0;
mod->m_bottomIndex=0;
mod->m_activeEdgeCount=0;
mod->m_totalEdgesAdded=0;
mod = mod->m_nextSystem;
} // end while
m_edgesToFlush=0;
}
/**Adjust various paremeters which affect the cost of rendering tracks on the map.
Parameters are passed via GlobalData*/
void TerrainTracksRenderObjClassSystem::setDetail(void)
{
//Remove all existing track segments from screen.
clearTracks();
ReleaseResources();
m_maxTankTrackEdges=TheGlobalData->m_maxTankTrackEdges;
m_maxTankTrackOpaqueEdges=TheGlobalData->m_maxTankTrackOpaqueEdges;
m_maxTankTrackFadeDelay=TheGlobalData->m_maxTankTrackFadeDelay;
//We changed the maximum number of visible edges so re-allocate our resources to match.
ReAcquireResources();
};
TerrainTracksRenderObjClassSystem *TheTerrainTracksRenderObjClassSystem=NULL; ///< singleton for track drawing system.

View file

@ -0,0 +1,832 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DTerrainVisual.cpp /////////////////////////////////////////////////////////////////////
// W3D implementation details for visual aspects of terrain
// Author: Colin Day, April 2001
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "Common/GameState.h"
#include "Common/GlobalData.h"
#include "Common/PerfTimer.h"
#include "Common/MapReaderWriterInfo.h"
#include "Common/ThingTemplate.h"
#include "Common/WellKnownKeys.h"
#include "Common/TerrainTypes.h"
#include "Common/Xfer.h"
#include "GameClient/Drawable.h"
#include "GameLogic/Object.h"
#include "W3DDevice/GameClient/W3DScene.h"
#include "W3DDevice/GameClient/W3DTerrainVisual.h"
#include "W3DDevice/GameClient/WorldHeightMap.h"
#include "W3DDevice/GameClient/W3DWater.h"
#include "W3DDevice/GameClient/W3DDisplay.h"
#include "W3DDevice/GameClient/W3DDebugIcons.h"
#include "W3DDevice/GameClient/W3DTerrainTracks.h"
#include "W3DDevice/GameClient/W3DGranny.h"
#include "W3DDevice/GameClient/W3DShadow.h"
#include "W3DDevice/GameClient/heightmap.h"
#include "WW3D2/Light.h"
#include "WW3D2/RendObj.h"
#include "WW3D2/ColType.h"
#include "WW3D2/ColTest.h"
#include "WW3D2/assetmgr.h"
#include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTerrainVisual::W3DTerrainVisual()
{
m_terrainRenderObject = NULL;
m_terrainHeightMap = NULL;
m_waterRenderObject = NULL;
TheWaterRenderObj = NULL;
} // end W3DTerrainVisual
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
W3DTerrainVisual::~W3DTerrainVisual()
{
// release our render object
if (TheTerrainRenderObject == m_terrainRenderObject) {
TheTerrainRenderObject = NULL;
}
if (TheTerrainTracksRenderObjClassSystem)
{
delete TheTerrainTracksRenderObjClassSystem;
TheTerrainTracksRenderObjClassSystem=NULL;
}
#ifdef INCLUDE_GRANNY_IN_BUILD
if (TheGrannyRenderObjSystem)
{
delete TheGrannyRenderObjSystem;
TheGrannyRenderObjSystem=NULL;
}
#endif
if (TheW3DShadowManager)
{
delete TheW3DShadowManager;
TheW3DShadowManager=NULL;
}
REF_PTR_RELEASE( m_waterRenderObject );
TheWaterRenderObj=NULL;
REF_PTR_RELEASE( m_terrainRenderObject );
REF_PTR_RELEASE( m_terrainHeightMap );
} // end ~W3DTerrainVisual
//-------------------------------------------------------------------------------------------------
/** init */
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::init( void )
{
// extend
TerrainVisual::init();
// create a new render object for W3D
m_terrainRenderObject = NEW_REF( HeightMapRenderObjClass, () );
m_terrainRenderObject->Set_Collision_Type( PICK_TYPE_TERRAIN );
TheTerrainRenderObject = m_terrainRenderObject;
// initialize track drawing system
TheTerrainTracksRenderObjClassSystem = NEW TerrainTracksRenderObjClassSystem;
TheTerrainTracksRenderObjClassSystem->init(W3DDisplay::m_3DScene);
#ifdef INCLUDE_GRANNY_IN_BUILD
// initialize Granny model drawing system
TheGrannyRenderObjSystem = NEW GrannyRenderObjSystem;
#endif
// initialize object shadow drawing system
TheW3DShadowManager = NEW W3DShadowManager;
TheW3DShadowManager->init();
// create a water plane render object
TheWaterRenderObj=m_waterRenderObject = NEW_REF( WaterRenderObjClass, () );
m_waterRenderObject->init(TheGlobalData->m_waterPositionZ, TheGlobalData->m_waterExtentX, TheGlobalData->m_waterExtentY, W3DDisplay::m_3DScene, (WaterRenderObjClass::WaterType)TheGlobalData->m_waterType); //create a water plane that's 128x128 units
m_waterRenderObject->Set_Position(Vector3(TheGlobalData->m_waterPositionX,TheGlobalData->m_waterPositionY,TheGlobalData->m_waterPositionZ)); //place water in world
#ifdef DO_UNIT_TIMINGS
#pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ")
#else
if (TheGlobalData->m_waterType == WaterRenderObjClass::WATER_TYPE_1_FB_REFLECTION)
{ // add water render object to the pre-pass scene (to be rendered before main scene)
//W3DDisplay::m_prePass3DScene->Add_Render_Object( m_waterRenderObject);
}
else
{ // add water render object to the post-pass scene (to be rendered after main scene)
W3DDisplay::m_3DScene->Add_Render_Object( m_waterRenderObject);
}
#endif
if (TheGlobalData->m_useCloudPlane)
m_waterRenderObject->toggleCloudLayer(true);
else
m_waterRenderObject->toggleCloudLayer(false);
// set the vertex animated water properties
Int waterSettingIndex = 0; // use index 0 settings by default
TheTerrainVisual->setWaterGridHeightClamps( NULL,
TheGlobalData->m_vertexWaterHeightClampLow[ waterSettingIndex ],
TheGlobalData->m_vertexWaterHeightClampHi[ waterSettingIndex ] );
TheTerrainVisual->setWaterTransform( NULL,
TheGlobalData->m_vertexWaterAngle[ waterSettingIndex ],
TheGlobalData->m_vertexWaterXPosition[ waterSettingIndex ],
TheGlobalData->m_vertexWaterYPosition[ waterSettingIndex ],
TheGlobalData->m_vertexWaterZPosition[ waterSettingIndex ] );
TheTerrainVisual->setWaterGridResolution( NULL,
TheGlobalData->m_vertexWaterXGridCells[ waterSettingIndex ],
TheGlobalData->m_vertexWaterYGridCells[ waterSettingIndex ],
TheGlobalData->m_vertexWaterGridSize[ waterSettingIndex ] );
TheTerrainVisual->setWaterAttenuationFactors( NULL,
TheGlobalData->m_vertexWaterAttenuationA[ waterSettingIndex ],
TheGlobalData->m_vertexWaterAttenuationB[ waterSettingIndex ],
TheGlobalData->m_vertexWaterAttenuationC[ waterSettingIndex ],
TheGlobalData->m_vertexWaterAttenuationRange[ waterSettingIndex ] );
m_isWaterGridRenderingEnabled = FALSE;
} // end init
//-------------------------------------------------------------------------------------------------
/** reset */
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::reset( void )
{
// extend
TerrainVisual::reset();
m_terrainRenderObject->reset();
if (TheW3DShadowManager)
TheW3DShadowManager->Reset();
if (TheTerrainTracksRenderObjClassSystem)
TheTerrainTracksRenderObjClassSystem->Reset();
// reset water render object if present
if( m_waterRenderObject )
{
for (Int i=0; i<5; i++)
{
//check if this texture was ever changed from default
if (m_currentSkyboxTexNames[i] != m_initialSkyboxTexNames[i])
{
m_waterRenderObject->replaceSkyboxTexture(m_currentSkyboxTexNames[i], m_initialSkyboxTexNames[i]);
m_currentSkyboxTexNames[i]=m_initialSkyboxTexNames[i]; //update current state to new texture
}
}
m_waterRenderObject->reset();
}
} // end reset
//-------------------------------------------------------------------------------------------------
/** update */
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::update( void )
{
// extend
TerrainVisual::update();
// if we have a water render object, it has an update method
if( m_waterRenderObject )
m_waterRenderObject->update();
} // end update
//-------------------------------------------------------------------------------------------------
/** load method for W3D visual terrain */
//-------------------------------------------------------------------------------------------------
Bool W3DTerrainVisual::load( AsciiString filename )
{
#if 0
// (gth) Testing exclusion list asset releasing
DynamicVectorClass<StringClass> exclusion_list(8000);
WW3DAssetManager::Get_Instance()->Create_Asset_List(exclusion_list);
exclusion_list.Add(StringClass("avcomanche"));
exclusion_list.Add(StringClass("avcomanche_d"));
exclusion_list.Add(StringClass("ptdogwood08"));
exclusion_list.Add(StringClass("ptdogwood01_b"));
exclusion_list.Add(StringClass("ptpalm01"));
exclusion_list.Add(StringClass("ptpalm01_b"));
exclusion_list.Add(StringClass("avhummer"));
exclusion_list.Add(StringClass("avhummer_d"));
exclusion_list.Add(StringClass("avleopard"));
exclusion_list.Add(StringClass("avleopard_d"));
WW3DAssetManager::Get_Instance()->Free_Assets_With_Exclusion_List(exclusion_list);
#endif
// enhancing functionality specific for W3D terrain
if( TerrainVisual::load( filename ) == FALSE )
return FALSE; // failed
// open the terrain file
CachedFileInputStream fileStrm;
if( !fileStrm.open(filename) )
{
REF_PTR_RELEASE( m_terrainRenderObject );
return FALSE;
} // end if
if( m_terrainRenderObject == NULL )
return FALSE;
REF_PTR_RELEASE( m_terrainHeightMap );
ChunkInputStream *pStrm = &fileStrm;
// allocate new height map data to read from file
m_terrainHeightMap = NEW WorldHeightMap(pStrm);
// Add any lights loaded by map.
MapObject *pMapObj = MapObject::getFirstMapObject();
while (pMapObj)
{
Dict *d = pMapObj->getProperties();
if (pMapObj->isLight())
{
Coord3D loc = *pMapObj->getLocation();
if (loc.z < 0) {
Vector3 vec;
loc.z = m_terrainRenderObject->getHeightMapHeight(loc.x, loc.y, NULL);
loc.z += d->getReal(TheKey_lightHeightAboveTerrain);
}
// It is a light, and handled at the device level. jba.
LightClass* lightP = NEW_REF(LightClass, (LightClass::POINT));
RGBColor c;
c.setFromInt(d->getInt(TheKey_lightAmbientColor));
lightP->Set_Ambient( Vector3( c.red, c.green, c.blue ) );
c.setFromInt(d->getInt(TheKey_lightDiffuseColor));
lightP->Set_Diffuse( Vector3( c.red, c.green, c.blue) );
lightP->Set_Position(Vector3(loc.x, loc.y, loc.z));
lightP->Set_Far_Attenuation_Range(d->getReal(TheKey_lightInnerRadius), d->getReal(TheKey_lightOuterRadius));
W3DDisplay::m_3DScene->Add_Render_Object(lightP);
REF_PTR_RELEASE( lightP );
}
pMapObj = pMapObj->getNext();
}
RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator();
// apply the heightmap to the terrain render object
m_terrainRenderObject->initHeightData( m_terrainHeightMap->getDrawWidth(),
m_terrainHeightMap->getDrawHeight(),
m_terrainHeightMap,
it);
if (it) {
W3DDisplay::m_3DScene->destroyLightsIterator(it);
it = NULL;
}
// add our terrain render object to the scene
W3DDisplay::m_3DScene->Add_Render_Object( m_terrainRenderObject );
#if defined _DEBUG || defined _INTERNAL
// Icon drawing utility object for pathfinding.
W3DDebugIcons *icons = NEW W3DDebugIcons;
W3DDisplay::m_3DScene->Add_Render_Object( icons );
icons->Release_Ref(); // belongs to scene.
#endif
#ifdef DO_UNIT_TIMINGS
#pragma MESSAGE("********************* WARNING- Doing UNIT TIMINGS. ")
#else
if (m_waterRenderObject)
{
W3DDisplay::m_3DScene->Add_Render_Object( m_waterRenderObject);
m_waterRenderObject->enableWaterGrid(false);
}
#endif
pMapObj = MapObject::getFirstMapObject();
while (pMapObj)
{
Dict *d = pMapObj->getProperties();
if (pMapObj->isScorch()) {
const Coord3D *pos = pMapObj->getLocation();
Vector3 loc(pos->x, pos->y, pos->z);
Real radius = d->getReal(TheKey_objectRadius);
Scorches type = (Scorches)d->getInt(TheKey_scorchType);
m_terrainRenderObject->addScorch(loc, radius, type);
}
pMapObj = pMapObj->getNext();
}
// reset water render object if present
if( m_waterRenderObject )
{
m_waterRenderObject->load();
}
return TRUE; // success
} // end load
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::enableWaterGrid( Bool enable )
{
//Get default water type
m_isWaterGridRenderingEnabled = enable;
// make the changes in the water render object
if( m_waterRenderObject )
m_waterRenderObject->enableWaterGrid( enable );
} // end enableWaterGrid
//-------------------------------------------------------------------------------------------------
/** intersect the ray with the terrain, if a hit occurs TRUE is returned
* and the result point on the terrain is returned in "result" */
//-------------------------------------------------------------------------------------------------
Bool W3DTerrainVisual::intersectTerrain( Coord3D *rayStart,
Coord3D *rayEnd,
Coord3D *result )
{
Bool hit = FALSE;
// sanity
if( rayStart == NULL || rayEnd == NULL )
return hit;
if( m_terrainRenderObject )
{
CastResultStruct res;
LineSegClass lineSeg( Vector3( rayStart->x, rayStart->y, rayStart->z ),
Vector3( rayEnd->x, rayEnd->y, rayEnd->z ) );
RayCollisionTestClass rayTest( lineSeg, &res );
hit = m_terrainRenderObject->Cast_Ray( rayTest );
if( hit && result )
{
Vector3 point = rayTest.Result->ContactPoint;
result->x = point.X;
result->y = point.Y;
result->z = point.Z;
} // end if
} // end if
// return hit result
return hit;
} // end intersectTerrain
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
void W3DTerrainVisual::getTerrainColorAt( Real x, Real y, RGBColor *pColor )
{
if( m_terrainHeightMap )
m_terrainHeightMap->getTerrainColorAt( x, y, pColor );
} // end getTerrainColorAt
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
TerrainType *W3DTerrainVisual::getTerrainTile( Real x, Real y )
{
TerrainType *tile = NULL;
if( m_terrainHeightMap )
{
AsciiString tileName = m_terrainHeightMap->getTerrainNameAt( x, y );
tile = TheTerrainTypes->findTerrain( tileName );
} // end if
return tile;
} // end getTerrainTile
// ------------------------------------------------------------------------------------------------
/** set min/max height values allowed in water grid pointed to by waterTable */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterGridHeightClamps( const WaterHandle *waterTable,
Real minZ, Real maxZ )
{
if( m_waterRenderObject )
m_waterRenderObject->setGridHeightClamps( minZ, maxZ );
} // end setWaterGridHeightClamps
// ------------------------------------------------------------------------------------------------
/** adjust fallof parameters for grid change method */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterAttenuationFactors( const WaterHandle *waterTable,
Real a, Real b, Real c, Real range )
{
if( m_waterRenderObject )
m_waterRenderObject->setGridChangeAttenuationFactors( a, b, c, range );
} // end setWaterAttenuationFactors
// ------------------------------------------------------------------------------------------------
/** set the water table position and orientation in world space */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterTransform( const WaterHandle *waterTable,
Real angle, Real x, Real y, Real z )
{
if( m_waterRenderObject )
m_waterRenderObject->setGridTransform( angle, x, y, z );
} // end setWaterTransform
// ------------------------------------------------------------------------------------------------
/** set water table transform by matrix */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterTransform( const Matrix3D *transform )
{
if( m_waterRenderObject )
m_waterRenderObject->setGridTransform( transform );
} // end setWaterTransform
// ------------------------------------------------------------------------------------------------
/** get the water transform matrix */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::getWaterTransform( const WaterHandle *waterTable, Matrix3D *transform )
{
if( m_waterRenderObject )
m_waterRenderObject->getGridTransform( transform );
} // end getWaterTransform
// ------------------------------------------------------------------------------------------------
/** water grid resolution spacing */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setWaterGridResolution( const WaterHandle *waterTable,
Real gridCellsX, Real gridCellsY, Real cellSize )
{
if( m_waterRenderObject )
m_waterRenderObject->setGridResolution( gridCellsX, gridCellsY, cellSize );
} // end setWaterGridResolution
// ------------------------------------------------------------------------------------------------
/** get water grid resolution spacing */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::getWaterGridResolution( const WaterHandle *waterTable,
Real *gridCellsX, Real *gridCellsY, Real *cellSize )
{
if( m_waterRenderObject )
m_waterRenderObject->getGridResolution( gridCellsX, gridCellsY, cellSize );
} // end getWaterGridResolution
// ------------------------------------------------------------------------------------------------
/** adjust the water grid in world coords by the delta */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::changeWaterHeight( Real x, Real y, Real delta )
{
if( m_waterRenderObject )
m_waterRenderObject->changeGridHeight( x, y, delta );
} // end changeWaterHeight
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::addWaterVelocity( Real worldX, Real worldY,
Real velocity, Real preferredHeight )
{
if( m_waterRenderObject )
m_waterRenderObject->addVelocity( worldX, worldY, velocity, preferredHeight );
} // end addWaterVelocity
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
Bool W3DTerrainVisual::getWaterGridHeight( Real worldX, Real worldY, Real *height)
{
Real gridX, gridY;
if (m_isWaterGridRenderingEnabled && m_waterRenderObject &&
m_waterRenderObject->worldToGridSpace(worldX, worldY, gridX, gridY))
{ //point falls within grid, return correct height
m_waterRenderObject->getGridVertexHeight(REAL_TO_INT(gridX),REAL_TO_INT(gridY),height);
return TRUE;
}
return FALSE;
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setRawMapHeight(const ICoord2D *gridPos, Int height)
{
if (m_terrainHeightMap) {
Int x = gridPos->x+m_terrainHeightMap->getBorderSize();
Int y = gridPos->y+m_terrainHeightMap->getBorderSize();
//if (m_terrainHeightMap->getHeight(x,y) != height) //ML changed to prevent scissoring with roads
if (m_terrainHeightMap->getHeight(x,y) > height)
{
m_terrainHeightMap->setRawHeight(x, y, height);
m_terrainRenderObject->staticLightingChanged();
}
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::addFactionBibDrawable(Drawable *factionBuilding, Bool highlight, Real extra)
{
if (m_terrainHeightMap) {
const Matrix3D * mtx = factionBuilding->getTransformMatrix();
Vector3 corners[4];
Coord3D pos;
pos.set(0,0,0);
Real exitWidth = factionBuilding->getTemplate()->getFactoryExitWidth();
Real extraWidth = factionBuilding->getTemplate()->getFactoryExtraBibWidth() + extra;
const GeometryInfo info = factionBuilding->getTemplate()->getTemplateGeometryInfo();
Real sizeX = info.getMajorRadius();
Real sizeY = info.getMinorRadius();
if (info.getGeomType() != GEOMETRY_BOX) {
sizeY = sizeX;
}
corners[0].Set(pos.x, pos.y, pos.z);
corners[0].X -= sizeX+extraWidth;
corners[0].Y -= sizeY+extraWidth;
corners[1].Set(pos.x, pos.y, pos.z);
corners[1].X += sizeX+exitWidth+extraWidth;
corners[1].Y -= sizeY+extraWidth;
corners[2].Set(pos.x, pos.y, pos.z);
corners[2].X += sizeX+exitWidth+extraWidth;
corners[2].Y += sizeY+extraWidth;
corners[3].Set(pos.x, pos.y, pos.z);
corners[3].X -= sizeX+extraWidth;
corners[3].Y += sizeY+extraWidth;
mtx->Transform_Vector(*mtx, corners[0], &corners[0]);
mtx->Transform_Vector(*mtx, corners[1], &corners[1]);
mtx->Transform_Vector(*mtx, corners[2], &corners[2]);
mtx->Transform_Vector(*mtx, corners[3], &corners[3]);
m_terrainRenderObject->addTerrainBibDrawable(corners, factionBuilding->getID(), highlight);
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::addFactionBib(Object *factionBuilding, Bool highlight, Real extra)
{
if (m_terrainHeightMap) {
const Matrix3D * mtx = factionBuilding->getTransformMatrix();
Vector3 corners[4];
Coord3D pos;
pos.set(0,0,0);
Real exitWidth = factionBuilding->getTemplate()->getFactoryExitWidth();
Real extraWidth = factionBuilding->getTemplate()->getFactoryExtraBibWidth() + extra;
const GeometryInfo info = factionBuilding->getGeometryInfo();
Real sizeX = info.getMajorRadius();
Real sizeY = info.getMinorRadius();
if (info.getGeomType() != GEOMETRY_BOX) {
sizeY = sizeX;
}
corners[0].Set(pos.x, pos.y, pos.z);
corners[0].X -= sizeX+extraWidth;
corners[0].Y -= sizeY+extraWidth;
corners[1].Set(pos.x, pos.y, pos.z);
corners[1].X += sizeX+exitWidth+extraWidth;
corners[1].Y -= sizeY+extraWidth;
corners[2].Set(pos.x, pos.y, pos.z);
corners[2].X += sizeX+exitWidth+extraWidth;
corners[2].Y += sizeY+extraWidth;
corners[3].Set(pos.x, pos.y, pos.z);
corners[3].X -= sizeX+extraWidth;
corners[3].Y += sizeY+extraWidth;
mtx->Transform_Vector(*mtx, corners[0], &corners[0]);
mtx->Transform_Vector(*mtx, corners[1], &corners[1]);
mtx->Transform_Vector(*mtx, corners[2], &corners[2]);
mtx->Transform_Vector(*mtx, corners[3], &corners[3]);
m_terrainRenderObject->addTerrainBib(corners, factionBuilding->getID(), highlight);
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeFactionBibDrawable(Drawable *factionBuilding)
{
if (m_terrainHeightMap) {
m_terrainRenderObject->removeTerrainBibDrawable(factionBuilding->getID());
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeFactionBib(Object *factionBuilding)
{
if (m_terrainHeightMap) {
m_terrainRenderObject->removeTerrainBib(factionBuilding->getID());
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeAllBibs(void)
{
if (m_terrainHeightMap) {
m_terrainRenderObject->removeAllTerrainBibs();
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::removeBibHighlighting(void)
{
if (m_terrainHeightMap) {
m_terrainRenderObject->removeTerrainBibHighlighting();
}
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setTerrainTracksDetail(void)
{
if (TheTerrainTracksRenderObjClassSystem)
TheTerrainTracksRenderObjClassSystem->setDetail();
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::setShoreLineDetail(void)
{
if (m_terrainRenderObject)
m_terrainRenderObject->setShoreLineDetail();
}
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
/// Replace the skybox texture
void W3DTerrainVisual::replaceSkyboxTextures(const AsciiString *oldTexName[5], const AsciiString *newTexName[5])
{
if (m_waterRenderObject)
{
for (Int i=0; i<5; i++)
{
//check if this texture was never changed before and is still using the default art.
if (m_initialSkyboxTexNames[i].isEmpty())
{ m_initialSkyboxTexNames[i]=*oldTexName[i];
m_currentSkyboxTexNames[i]=*oldTexName[i];
}
if (m_currentSkyboxTexNames[i] != *newTexName[i])
{ m_waterRenderObject->replaceSkyboxTexture(m_currentSkyboxTexNames[i], *newTexName[i]);
m_currentSkyboxTexNames[i]=*newTexName[i]; //update current state to new texture
}
}
}
}
// ------------------------------------------------------------------------------------------------
/** CRC */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::crc( Xfer *xfer )
{
// extend base class
TerrainVisual::crc( xfer );
} // end CRC
// ------------------------------------------------------------------------------------------------
/** Xfer
* Version Info:
* 1: Initial version */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::xfer( Xfer *xfer )
{
// version
XferVersion currentVersion = 2;
XferVersion version = currentVersion;
xfer->xferVersion( &version, currentVersion );
// extend base class
TerrainVisual::xfer( xfer );
// flag for whether or not the water grid is enabled
Bool gridEnabled = m_isWaterGridRenderingEnabled;
xfer->xferBool( &gridEnabled );
if( gridEnabled != m_isWaterGridRenderingEnabled )
{
DEBUG_CRASH(( "W3DTerrainVisual::xfer - m_isWaterGridRenderingEnabled mismatch\n" ));
throw SC_INVALID_DATA;
} // end if
// xfer grid data if enabled
if( gridEnabled )
xfer->xferSnapshot( m_waterRenderObject );
/*
{
// grid width and height
Int width = getGridWidth();
Int height = getGridheight();
xfer->xferInt( &width );
xfer->xferInt( &height );
if( width != getGridWidth() )
{
DEBUG_CRASH(( "W3DTerainVisual::xfer - grid width mismatch '%d' should be '%d'\n",
width, getGridWidth() ));
throw SC_INVALID_DATA;
} // end if
if( height != getGridHeight() )
{
DEBUG_CRASH(( "W3DTerainVisual::xfer - grid height mismatch '%d' should be '%d'\n",
height, getGridHeight() ));
throw SC_INVALID_DATA;
} // end if
// write data for each grid
} // end if
*/
// Write out the terrain height data.
if (version >= 2) {
UnsignedByte *data = m_terrainHeightMap->getDataPtr();
Int len = m_terrainHeightMap->getXExtent()*m_terrainHeightMap->getYExtent();
Int xferLen = len;
xfer->xferInt(&xferLen);
if (len!=xferLen) {
DEBUG_CRASH(("Bad height map length."));
if (len>xferLen) {
len = xferLen;
}
}
xfer->xferUser(data, len);
if (xfer->getXferMode() == XFER_LOAD) {
// Update the display height map.
m_terrainRenderObject->staticLightingChanged();
}
}
} // end xfer
// ------------------------------------------------------------------------------------------------
/** Load post process */
// ------------------------------------------------------------------------------------------------
void W3DTerrainVisual::loadPostProcess( void )
{
// extend base class
TerrainVisual::loadPostProcess();
} // end loadPostProcess

View file

@ -0,0 +1,694 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (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.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DTreeBuffer.h"
#include <stdio.h>
#include <string.h>
#include <assetmgr.h>
#include <texture.h>
#include "common/GlobalData.h"
#include "GameClient/ClientRandomValue.h"
#include "W3DDevice/GameClient/TerrainTex.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "W3DDevice/GameClient/W3DDynamicLight.h"
#include "WW3D2/Camera.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/DX8Renderer.h"
#include "WW3D2/Mesh.h"
#include "WW3D2/MeshMdl.h"
//-----------------------------------------------------------------------------
// Private Data
//-----------------------------------------------------------------------------
// 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_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_DISABLE, ShaderClass::CULL_MODE_DISABLE, \
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(CameraClass * camera)
{
Int curTree;
m_anythingChanged = false;
// 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; curTree<m_numTrees; curTree++) {
Bool doKey = false; // We calculate the key when a tree becomes visible.
Bool visible = !camera->Cull_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::cullMirror
//=============================================================================
/** Culls the trees, marking the visible flag for the mirror view. Unlike cull(),
doesn't update anything except the visible flag. */
//=============================================================================
void W3DTreeBuffer::cullMirror(CameraClass * camera)
{
Int curTree;
m_anythingChanged = false;
for (curTree=0; curTree<m_numTrees; curTree++) {
if (!m_trees[curTree].mirrorVisible) {
if (m_trees[curTree].visible) {
m_anythingChanged = true;
}
m_trees[curTree].visible = false;
continue;
}
Bool visible = !camera->Cull_Sphere(m_trees[curTree].bounds);
m_trees[curTree].visible=visible;
}
}
//=============================================================================
// W3DTreeBuffer::sort
//=============================================================================
/** Sorts the trees. Does num_iterations of a bubble sort. This is good because
it ends immediately if the trees are already sorted (which is most of the time)
and will perform a fixed amount of work each frame until it becomes sorted. */
//=============================================================================
void W3DTreeBuffer::sort(Int numIterations)
{
// sort in descending order.
Int iter;
Bool swap = false;
for (iter = 0; iter<numIterations; iter++) {
Int cur = 0;
// Note - only sorts the visible trees.
while (cur<m_numTrees-iter && !m_trees[cur].visible) {
cur++;
}
Int i;
for (i=cur+1; i<m_numTrees-iter; i++) {
if (m_trees[i].visible) {
if (m_trees[cur].sortKey > 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;
}
}
//=============================================================================
// W3DTreeBuffer::doLighting
//=============================================================================
/** Calculates the diffuse lighting as affected by dynamic lighting. */
//=============================================================================
Int W3DTreeBuffer::doLighting(Vector3 *loc, Real r, Real g, Real b, SphereClass &bounds, RefRenderObjListIterator *pDynamicLightsIterator)
{
if (pDynamicLightsIterator == NULL) {
return(REAL_TO_INT(b) | (REAL_TO_INT(g) << 8) | (REAL_TO_INT(r) << 16) | (255 << 24));
}
Real shadeR, shadeG, shadeB;
shadeR = r;
shadeG = g;
shadeB = b;
Bool didLight = false;
for (pDynamicLightsIterator->First(); !pDynamicLightsIterator->Is_Done(); pDynamicLightsIterator->Next())
{
W3DDynamicLight *pLight = (W3DDynamicLight*)pDynamicLightsIterator->Peek_Obj();
if (!pLight->isEnabled()) {
continue; // he is turned off.
}
if (CollisionMath::Overlap_Test(bounds, pLight->Get_Bounding_Sphere()) == CollisionMath::OUTSIDE) {
continue; // this tree is outside of the light's influence.
}
Vector3 lightDirection = *loc;
Real factor;
switch(pLight->Get_Type()) {
case LightClass::POINT:
case LightClass::SPOT: {
Vector3 lightLoc = pLight->Get_Position();
lightDirection -= lightLoc;
double range, midRange;
pLight->Get_Far_Attenuation_Range(midRange, range);
Real dist = lightDirection.Length();
if (dist >= range) continue;
if (midRange < 0.1) continue;
factor = 1.0f - (dist - midRange) / (range - midRange);
factor = WWMath::Clamp(factor,0.0f,1.0f);
}
break;
case LightClass::DIRECTIONAL:
pLight->Get_Spot_Direction(lightDirection);
factor = 1.0;
break;
};
Real shade = 0.5f * factor; // Assume adjustment for diffuse.
Vector3 vDiffuse;
pLight->Get_Diffuse(&vDiffuse);
Vector3 ambient;
pLight->Get_Ambient(&ambient);
if (shade > 1.0) shade = 1.0;
if(shade < 0.0f) shade = 0.0f;
shadeR += shade*vDiffuse.X;
shadeG += shade*vDiffuse.Y;
shadeB += shade*vDiffuse.Z;
shadeR += factor*ambient.X;
shadeG += factor*ambient.Y;
shadeB += factor*ambient.Z;
didLight = true;
}
if (!didLight) {
return(REAL_TO_INT(b) | (REAL_TO_INT(g) << 8) | (REAL_TO_INT(r) << 16) | (255 << 24));
}
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;
shadeR*=255.0f;
shadeG*=255.0f;
shadeB*=255.0f;
return(REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16) | (255 << 24));
}
//=============================================================================
// W3DTreeBuffer::loadTreesInVertexAndIndexBuffers
//=============================================================================
/** Loads the trees into the vertex buffer for drawing. */
//=============================================================================
void W3DTreeBuffer::loadTreesInVertexAndIndexBuffers(RefRenderObjListIterator *pDynamicLightsIterator)
{
if (!m_indexTree || !m_vertexTree || !m_initialized) {
return;
}
if (!m_anythingChanged) {
//return;
}
m_curNumTreeVertices = 0;
m_curNumTreeIndices = 0;
VertexFormatXYZDUV1 *vb;
UnsignedShort *ib;
// Lock the buffers.
DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexTree);
DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexTree);
vb=(VertexFormatXYZDUV1*)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+MAX_TREE_INDEX;
VertexFormatXYZDUV1 *curVb = vb;
Int curTree;
// Calculate a static lighting value to use for all the trees.
Real shadeR, shadeG, shadeB;
shadeR = TheGlobalData->m_terrainAmbient[0].red;
shadeG = TheGlobalData->m_terrainAmbient[0].green;
shadeB = TheGlobalData->m_terrainAmbient[0].blue;
shadeR += TheGlobalData->m_terrainDiffuse[0].red;
shadeG += TheGlobalData->m_terrainDiffuse[0].green;
shadeB += TheGlobalData->m_terrainDiffuse[0].blue;
if (shadeR>1.0f) shadeR=1.0f;
if (shadeG>1.0f) shadeG=1.0f;
if (shadeB>1.0f) shadeB=1.0f;
shadeR*=255.0f;
shadeG*=255.0f;
shadeB*=255.0f;
for (curTree=0; curTree<m_numTrees; curTree++) {
if (!m_trees[curTree].visible) continue;
Real scale = m_trees[curTree].scale;
Vector3 loc = m_trees[curTree].location;
Int type = m_trees[curTree].treeType;
Real theSin = m_trees[curTree].sin;
Real theCos = m_trees[curTree].cos;
if (type<0 || m_typeMesh[type] == 0) {
type = 0;
}
Bool doVertexLighting = false;
#if 0 // no dynamic lighting.
for (pDynamicLightsIterator->First(); !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
Int diffuse = 0;
if (!doVertexLighting) {
diffuse = doLighting(&loc, shadeR, shadeG, shadeB, m_trees[curTree].bounds, NULL);
}
Real typeOffset = type*0.5;
Int startVertex = m_curNumTreeVertices;
Int i, j;
Int numVertex = m_typeMesh[type]->Peek_Model()->Get_Vertex_Count();
Vector3 *pVert = m_typeMesh[type]->Peek_Model()->Get_Vertex_Array();
// If we happen to have too many trees, stop.
if (m_curNumTreeVertices+numVertex+2>= MAX_TREE_VERTEX) {
break;
}
Int numIndex = m_typeMesh[type]->Get_Model()->Get_Polygon_Count();
const Vector3i *pPoly = m_typeMesh[type]->Get_Model()->Get_Polygon_Array();
if (m_curNumTreeIndices+3*numIndex+6 >= MAX_TREE_INDEX) {
break;
}
const Vector2*uvs=m_typeMesh[type]->Get_Model()->Get_UV_Array_By_Index(0);
// 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].Z*scale*theSin;
vLoc.Y = pVert[i].Z*scale*theCos + pVert[i].X*scale*theSin;
vLoc.X += loc.X;
vLoc.Y += loc.Y;
vLoc.Z = loc.Z + pVert[i].Y*scale; // In W3D z is up, in 3dMax y is up.
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 {
for (i=0; i<numVertex; i++) {
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].Z*scale*theSin;
vLoc.Y = pVert[i].Z*scale*theCos + pVert[i].X*scale*theSin;
vLoc.X += loc.X;
vLoc.Y += loc.Y;
vLoc.Z = loc.Z + pVert[i].Y*scale; // In W3D z is up, in 3dMax y is up.
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<numIndex; i++) {
if (m_curNumTreeIndices+4 > MAX_TREE_INDEX)
break;
curIb-=3;
*curIb++ = startVertex + pPoly[i].I;
*curIb++ = startVertex + pPoly[i].J;
*curIb++ = startVertex + pPoly[i].K;
curIb-=3;
m_curNumTreeIndices+=3;
}
}
}
m_curTreeIndexOffset = curIb - ib;
}
//-----------------------------------------------------------------------------
// Public Functions
//-----------------------------------------------------------------------------
//=============================================================================
// W3DTreeBuffer::~W3DTreeBuffer
//=============================================================================
/** Destructor. Releases w3d assets. */
//=============================================================================
W3DTreeBuffer::~W3DTreeBuffer(void)
{
freeTreeBuffers();
REF_PTR_RELEASE(m_treeTexture);
Int i;
for (i=0; i<MAX_TYPES; i++) {
REF_PTR_RELEASE(m_typeMesh[i]);
}
}
//=============================================================================
// W3DTreeBuffer::W3DTreeBuffer
//=============================================================================
/** Constructor. Sets m_initialized to true if it finds the w3d models it needs
for the trees. */
//=============================================================================
W3DTreeBuffer::W3DTreeBuffer(void)
{
m_initialized = false;
///@toto - reactivate this optimization if useful. jba.
return;
m_vertexTree = NULL;
m_indexTree = NULL;
m_treeTexture = NULL;
m_curNumTreeVertices=0;
m_curNumTreeIndices=0;
clearAllTrees();
allocateTreeBuffers();
Int i;
for (i=0; i<MAX_TYPES; i++) {
m_typeMesh[i] = 0;
}
if (WW3DAssetManager::Get_Instance()==NULL)
return; // WorldBuilderTool doesn't initialize the asset manager. jba.
m_treeTexture = NEW_REF(TextureClass, ("trees.tga"));
m_treeTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
m_treeTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
for (i=0; i<MAX_TYPES; i++) {
switch(i) {
case 0:
m_typeMesh[i] = (MeshClass*)WW3DAssetManager::Get_Instance()->Create_Render_Obj("ALPINETREE.TREE 1" );
break;
case 1:
m_typeMesh[i] = (MeshClass*)WW3DAssetManager::Get_Instance()->Create_Render_Obj("DECIDUOUS.TREE 1" );
break;
case 2:
m_typeMesh[i] = (MeshClass*)WW3DAssetManager::Get_Instance()->Create_Render_Obj("SHRUB.TREE 1" );
break;
case 3:
m_typeMesh[i] = (MeshClass*)WW3DAssetManager::Get_Instance()->Create_Render_Obj("FENCE.PLANE09" );
break;
}
if (m_typeMesh[i] == NULL) continue;
Int numVertex = m_typeMesh[i]->Peek_Model()->Get_Vertex_Count();
Vector3 *pVert = m_typeMesh[i]->Peek_Model()->Get_Vertex_Array();
const Matrix3D xfm = m_typeMesh[i]->Get_Transform();
SphereClass bounds(pVert, numVertex);
// Model is in y up format, so swap y and z.
Real tmp;
tmp = bounds.Center.Y;
bounds.Center.Y = bounds.Center.Z;
bounds.Center.Z = tmp;
m_typeBounds[i] = bounds;
}
if (m_typeMesh[0] == NULL) {
//DEBUG_LOG("!!!!!!!!!!!!*************** W3DTreeBuffer failed to initialize.\n");
return; // didn't initialize.
}
m_initialized = true;
}
//=============================================================================
// W3DTreeBuffer::freeTreeBuffers
//=============================================================================
/** Frees the index and vertex buffers. */
//=============================================================================
void W3DTreeBuffer::freeTreeBuffers(void)
{
REF_PTR_RELEASE(m_vertexTree);
REF_PTR_RELEASE(m_indexTree);
}
//=============================================================================
// W3DTreeBuffer::allocateTreeBuffers
//=============================================================================
/** Allocates the index and vertex buffers. */
//=============================================================================
void W3DTreeBuffer::allocateTreeBuffers(void)
{
m_vertexTree=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,MAX_TREE_VERTEX+4,DX8VertexBufferClass::USAGE_DYNAMIC));
m_indexTree=NEW_REF(DX8IndexBufferClass,(2*MAX_TREE_INDEX+4, DX8IndexBufferClass::USAGE_DYNAMIC));
m_curNumTreeVertices=0;
m_curNumTreeIndices=0;
}
//=============================================================================
// W3DTreeBuffer::clearAllTrees
//=============================================================================
/** Removes all trees. */
//=============================================================================
void W3DTreeBuffer::clearAllTrees(void)
{
m_numTrees=0;
}
//=============================================================================
// W3DTreeBuffer::addTree
//=============================================================================
/** Adds a tree. Name is the W3D model name, supported models are
ALPINE, DECIDUOUS and SHRUB. */
//=============================================================================
void W3DTreeBuffer::addTree(Coord3D loc, Real scale, Real angle, AsciiString name, Bool mirrorVisible)
{
if (m_numTrees >= MAX_TREES) {
return;
}
if (!m_initialized) {
return;
}
TTreeType treeType = ALPINE_TREE;
if (!name.compare("DECIDUOUS")) {
treeType = DECIDUOUS_TREE;
} else if (!name.compare("SHRUB")) {
treeType = SHRUB;
} else if (!name.compare("FENCE")) {
treeType = FENCE;
}
m_trees[m_numTrees].panelStart = 0;
Real randomScale = GameClientRandomValueReal( 0.7f, 1.3f );
if (treeType == FENCE) {
// Fences don't randomly scale & orient
m_trees[m_numTrees].sin = WWMath::Sin(angle);
m_trees[m_numTrees].scale = scale;
m_trees[m_numTrees].cos = WWMath::Cos(angle);
m_trees[m_numTrees].rotates = false;
m_trees[m_numTrees].panelStart = 48;
} else {
// Randomizes the scale and orientation of trees.
m_trees[m_numTrees].sin = GameClientRandomValueReal( -1.0f, 1.0f );
m_trees[m_numTrees].scale = scale*randomScale;
m_trees[m_numTrees].cos = WWMath::Sqrt(1.0 - m_trees[m_numTrees].sin*m_trees[m_numTrees].sin);
m_trees[m_numTrees].rotates = true;
}
m_trees[m_numTrees].location = Vector3(loc.x, loc.y, loc.z);
m_trees[m_numTrees].treeType = treeType;
// Translate the bounding sphere of the model.
m_trees[m_numTrees].bounds = m_typeBounds[treeType];
m_trees[m_numTrees].bounds.Center *= scale;
m_trees[m_numTrees].bounds.Radius *= 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].mirrorVisible = mirrorVisible;
m_numTrees++;
}
//=============================================================================
// W3DTreeBuffer::drawTrees
//=============================================================================
/** Draws the trees. Uses camera to cull. */
//=============================================================================
void W3DTreeBuffer::drawTrees(CameraClass * camera, RefRenderObjListIterator *pDynamicLightsIterator)
{
if (!m_isTerrainPass) {
return;
}
m_isTerrainPass = false;
if (ShaderClass::Is_Backface_Culling_Inverted()) {
// Mirror inverts backface culling.
cullMirror(camera);
} else {
// Normal draw.
cull(camera);
// Only sort once per frame.
sort(SORT_ITERATIONS_PER_FRAME);
}
loadTreesInVertexAndIndexBuffers(pDynamicLightsIterator);
if (m_curNumTreeIndices == 0) {
return;
}
// Setup the vertex buffer, shader & texture.
DX8Wrapper::Set_Index_Buffer(m_indexTree,0);
DX8Wrapper::Set_Vertex_Buffer(m_vertexTree);
DX8Wrapper::Set_Shader(detailAlphaShader);
DX8Wrapper::Set_Texture(0,m_treeTexture);
// Draw all the trees.
DX8Wrapper::Draw_Triangles( m_curTreeIndexOffset, m_curNumTreeIndices/3, 0, m_curNumTreeVertices);
}

View file

@ -0,0 +1,281 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
//----------------------------------------------------------------------------
//
// Westwood Studios Pacific.
//
// Confidential Information
// Copyright (C) 2001 - All Rights Reserved
//
//----------------------------------------------------------------------------
//
// Project: Generals
//
// Module: Video
//
// File name: W3DDevice/GameClient/W3DVideoBuffer.cpp
//
// Created: 10/23/01 TR
//
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------------
#include "Common/GameMemory.h"
#include "WW3D2/texture.h"
#include "WW3D2/textureloader.h"
#include "W3DDevice/GameClient/W3DVideoBuffer.h"
//----------------------------------------------------------------------------
// Externals
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Defines
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Types
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Data
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Prototypes
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Private Functions
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// Public Functions
//----------------------------------------------------------------------------
//============================================================================
// W3DVideoBuffer::W3DVideoBuffer
//============================================================================
W3DVideoBuffer::W3DVideoBuffer( VideoBuffer::Type format )
: VideoBuffer(format),
m_texture(NULL),
m_surface(NULL)
{
}
//============================================================================
// W3DVideoBuffer::SetBuffer
//============================================================================
Bool W3DVideoBuffer::allocate( UnsignedInt width, UnsignedInt height )
{
free();
m_width = width;
m_height = height;
m_textureWidth = width;;
m_textureHeight = height;;
TextureLoader::Validate_Texture_Size( m_textureWidth, m_textureHeight);
WW3DFormat w3dFormat = TypeToW3DFormat( m_format );
if ( w3dFormat == WW3D_FORMAT_UNKNOWN )
{
return NULL;
}
m_texture = MSGNEW("TextureClass") TextureClass ( m_textureWidth, m_textureHeight, w3dFormat, TextureClass::MIP_LEVELS_1 );
if ( m_texture == NULL )
{
return FALSE;
}
if ( lock() == NULL )
{
free();
return FALSE;
}
unlock();
return TRUE;
}
//============================================================================
// W3DVideoBuffer::~W3DVideoBuffer
//============================================================================
W3DVideoBuffer::~W3DVideoBuffer()
{
free();
}
//============================================================================
// W3DVideoBuffer::lock
//============================================================================
void* W3DVideoBuffer::lock( void )
{
void *mem = NULL;
if ( m_surface != NULL )
{
unlock();
}
m_surface = m_texture->Get_Surface_Level();
if ( m_surface )
{
mem = m_surface->Lock( (Int*) &m_pitch );
}
return mem;
}
//============================================================================
// W3DVideoBuffer::unlock
//============================================================================
void W3DVideoBuffer::unlock( void )
{
if ( m_surface != NULL )
{
m_surface->Unlock();
m_surface->Release_Ref();
m_surface = NULL;
}
}
//============================================================================
// W3DVideoBuffer::valid
//============================================================================
Bool W3DVideoBuffer::valid( void )
{
return m_texture != NULL;
}
//============================================================================
// W3DVideoBuffer::reset
//============================================================================
void W3DVideoBuffer::free( void )
{
unlock();
if ( m_texture )
{
unlock();
m_texture->Release_Ref();
m_texture = NULL;
}
m_surface = NULL;
VideoBuffer::free();
}
//============================================================================
// W3DVideoBuffer::TypeToW3DFormat
//============================================================================
WW3DFormat W3DVideoBuffer::TypeToW3DFormat( VideoBuffer::Type format )
{
WW3DFormat w3dFormat = WW3D_FORMAT_UNKNOWN;
switch ( format )
{
case TYPE_X8R8G8B8:
w3dFormat = WW3D_FORMAT_X8R8G8B8;
break;
case TYPE_R8G8B8:
w3dFormat = WW3D_FORMAT_R8G8B8;
break;
case TYPE_R5G6B5:
w3dFormat = WW3D_FORMAT_R5G6B5;
break;
case TYPE_X1R5G5B5:
w3dFormat = WW3D_FORMAT_X1R5G5B5;
break;
}
return w3dFormat;
}
//============================================================================
// W3DFormatToType
//============================================================================
VideoBuffer::Type W3DVideoBuffer::W3DFormatToType( WW3DFormat w3dFormat )
{
Type format = TYPE_UNKNOWN;
switch ( w3dFormat )
{
case WW3D_FORMAT_X8R8G8B8:
format = VideoBuffer::TYPE_X8R8G8B8;
break;
case WW3D_FORMAT_R8G8B8:
format = VideoBuffer::TYPE_R8G8B8;
break;
case WW3D_FORMAT_R5G6B5:
format = VideoBuffer::TYPE_R5G6B5;
break;
case WW3D_FORMAT_X1R5G5B5:
format = VideoBuffer::TYPE_X1R5G5B5;
break;
}
return format;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
////// W3DWebBrowser.cpp ///////////////
// July 2002 Bryan Cleveland
#include "W3DDevice/GameClient/W3DWebBrowser.h"
#include "WW3D2/Texture.h"
#include "WW3D2/TextureLoader.h"
#include "WW3D2/SurfaceClass.h"
#include "GameClient/Image.h"
#include "GameClient/GameWindow.h"
#include "vector2i.h"
#include <d3dx8.h>
#include "WW3D2/dx8wrapper.h"
#include "WW3D2/dx8WebBrowser.h"
W3DWebBrowser::W3DWebBrowser() : WebBrowser() {
}
Bool W3DWebBrowser::createBrowserWindow(char *tag, GameWindow *win)
{
WinInstanceData *winData = win->winGetInstanceData();
AsciiString windowName = winData->m_decoratedNameString;
Int x, y, w, h;
win->winGetSize(&w, &h);
win->winGetScreenPosition(&x, &y);
WebBrowserURL *url = findURL( AsciiString(tag) );
if (url == NULL) {
DEBUG_LOG(("W3DWebBrowser::createBrowserWindow - couldn't find URL for page %s\n", tag));
return FALSE;
}
CComQIPtr<IDispatch> idisp(m_dispatch);
if (m_dispatch == NULL)
{
return FALSE;
}
DX8WebBrowser::CreateBrowser(windowName.str(), url->m_url.str(), x, y, w, h, 0, BROWSEROPTION_SCROLLBARS | BROWSEROPTION_3DBORDER, (LPDISPATCH)this);
return TRUE;
}
void W3DWebBrowser::closeBrowserWindow(GameWindow *win)
{
DX8WebBrowser::DestroyBrowser(win->winGetInstanceData()->m_decoratedNameString.str());
}

View file

@ -0,0 +1,429 @@
/*
** Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DWaypointBuffer.cpp ////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Electronic Arts Pacific.
//
// Confidential Information
// Copyright (C) 2002 - All Rights Reserved
//
//-----------------------------------------------------------------------------
//
// Project: Command & Conquers: Generals
//
// File name: W3DWaypointBuffer.cpp
//
// Created: Kris Morness, October 2002
//
// Desc: Draw buffer to handle all the waypoints in the scene. Waypoints
// are rendered after terrain, after roads & bridges, and after
// global fog, but before structures, objects, units, trees, etc.
// This way if we have two waypoints at the bottom of a hill but
// going through the hill, the line won't get cut off. However,
// structures and units on top of paths will render above it. Waypoints
// are only shown for selected units while in waypoint plotting mode.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
#include "W3DDevice/GameClient/W3DWaypointBuffer.h"
#include <stdio.h>
#include <string.h>
#include <assetmgr.h>
#include <texture.h>
#include "Common/GlobalData.h"
#include "Common/RandomValue.h"
#include "Common/ThingFactory.h"
#include "Common/ThingTemplate.h"
#include "GameClient/Drawable.h"
#include "GameClient/GameClient.h"
#include "GameClient/InGameUI.h"
#include "GameLogic/Object.h"
#include "GameLogic/Module/AIUpdate.h"
#include "W3DDevice/GameClient/TerrainTex.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "WW3D2/Camera.h"
#include "WW3D2/DX8Wrapper.h"
#include "WW3D2/DX8Renderer.h"
#include "WW3D2/Mesh.h"
#include "WW3D2/MeshMdl.h"
#include "WW3D2/Segline.h"
#define MAX_DISPLAY_NODES 512
#ifdef _INTERNAL
// for occasional debugging...
//#pragma optimize("", off)
//#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
#endif
//=============================================================================
// W3DWaypointBuffer::W3DWaypointBuffer
//=============================================================================
/** Constructor. Sets m_initialized to true if it finds the w3d models it needs
for the bibs. */
//=============================================================================
W3DWaypointBuffer::W3DWaypointBuffer(void)
{
m_waypointNodeRobj = WW3DAssetManager::Get_Instance()->Create_Render_Obj( "SCMNode" );
m_line = new SegmentedLineClass;
m_texture = WW3DAssetManager::Get_Instance()->Get_Texture( "EXLaser.tga" );
if( m_texture )
{
m_line->Set_Texture( m_texture );
}
ShaderClass lineShader=ShaderClass::_PresetAdditiveShader;
lineShader.Set_Depth_Compare(ShaderClass::PASS_ALWAYS);
m_line->Set_Shader( lineShader ); //pick the alpha blending mode you want - see shader.h for others.
m_line->Set_Width( 1.5f );
m_line->Set_Color( Vector3( 0.25f, 0.5f, 1.0f ) );
m_line->Set_Texture_Mapping_Mode( SegLineRendererClass::TILED_TEXTURE_MAP ); //this tiles the texture across the line
}
//=============================================================================
// W3DWaypointBuffer::~W3DWaypointBuffer
//=============================================================================
/** Destructor. Releases w3d assets. */
//=============================================================================
W3DWaypointBuffer::~W3DWaypointBuffer(void)
{
REF_PTR_RELEASE( m_waypointNodeRobj );
}
//=============================================================================
// W3DWaypointBuffer::freeBibBuffers
//=============================================================================
/** Frees the index and vertex buffers. */
//=============================================================================
void W3DWaypointBuffer::freeWaypointBuffers()
{
}
//=============================================================================
// W3DWaypointBuffer::drawWaypoints
//=============================================================================
/** Draws the waypoints. Uses camera to cull */
//=============================================================================
void W3DWaypointBuffer::drawWaypoints(RenderInfoClass &rinfo)
{
if( TheInGameUI && TheInGameUI->isInWaypointMode() )
{
//Create a default light environment with no lights and only full ambient.
//@todo: Fix later by copying default scene light environement from W3DScene.cpp.
LightEnvironmentClass lightEnv;
lightEnv.Reset(Vector3(0,0,0), Vector3(1.0f,1.0f,1.0f));
lightEnv.Pre_Render_Update(rinfo.Camera.Get_Transform());
RenderInfoClass localRinfo(rinfo.Camera);
localRinfo.light_environment=&lightEnv;
Vector3 points[ MAX_DISPLAY_NODES + 1 ]; //Lines have nodes + 1 points.
const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
Drawable *draw;
for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
{
draw = *it;
Object *obj = draw->getObject();
Int numPoints = 1;
if( obj && ! obj->isKindOf( KINDOF_IGNORED_IN_GUI ))//so mobs and stuff sont make a gazillion lines
{
AIUpdateInterface *ai = obj->getAI();
Int goalSize = ai ? ai->friend_getWaypointGoalPathSize() : 0;
Int gpIdx = ai ? ai->friend_getCurrentGoalPathIndex() : 0;
if( ai && gpIdx >= 0 && gpIdx < goalSize )
{
const Coord3D *pos = obj->getPosition();
points[ 0 ].Set( Vector3( pos->x, pos->y, pos->z ) );
for( int i = gpIdx; i < goalSize; i++ )
{
const Coord3D *waypoint = ai->friend_getGoalPathPosition( i );
if( waypoint )
{
//Render line from previous point to current node.
if( numPoints < MAX_DISPLAY_NODES + 1 )
{
points[ numPoints ].Set( Vector3( waypoint->x, waypoint->y, waypoint->z ) );
numPoints++;
}
m_waypointNodeRobj->Set_Position(Vector3(waypoint->x,waypoint->y,waypoint->z));
WW3D::Render(*m_waypointNodeRobj,localRinfo);
}
}
//Now render the lines in one pass!
m_line->Set_Points( numPoints, points );
m_line->Render( localRinfo );
}
}
}
}
else // maybe we want to draw rally points, then?
if (TheInGameUI)
{
//Create a default light environment with no lights and only full ambient.
//@todo: Fix later by copying default scene light environement from W3DScene.cpp.
LightEnvironmentClass lightEnv;
lightEnv.Reset(Vector3(0,0,0), Vector3(1.0f,1.0f,1.0f));
lightEnv.Pre_Render_Update(rinfo.Camera.Get_Transform());
RenderInfoClass localRinfo(rinfo.Camera);
localRinfo.light_environment=&lightEnv;
Vector3 points[ MAX_DISPLAY_NODES + 1 ]; //Lines have nodes + 1 points.
const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
Drawable *draw;
for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
{
draw = *it;
Object *obj = draw->getObject();
Int numPoints = 0;
if( obj )
{
if ( ! obj->isLocallyControlled())
continue;
ExitInterface *exitInterface = obj->getObjectExitInterface();
if( exitInterface )
{
Coord3D exitPoint;
if ( ! exitInterface->getExitPosition(exitPoint))
exitPoint = *obj->getPosition();
points[ numPoints ].Set( Vector3( exitPoint.x, exitPoint.y, exitPoint.z ) );
numPoints++;
Bool boxWrap = TRUE;
Coord3D naturalRallyPoint;
if (exitInterface->getNaturalRallyPoint(naturalRallyPoint, FALSE))//FALSE means "without the extra offset"
{
if( !naturalRallyPoint.equals( exitPoint ) )
{
points[ numPoints ].Set( Vector3( naturalRallyPoint.x, naturalRallyPoint.y, naturalRallyPoint.z ) );
numPoints++;
}
else
{
//Helipad rally point -- so don't use box wrapping.
boxWrap = FALSE;
}
}
else
continue; //next drawable
const Coord3D *rallyPoint = exitInterface->getRallyPoint();
if( rallyPoint )
{
if( boxWrap )
{
//test to se whether the rally point flanks the side of the natural rally point
//to do this we find the two corners of the geometry extents that are nearest the natural rally point
// these define the natural rally point edge
// an intermediate point should be inserted at the corner nearest the rally point
const GeometryInfo& geom = obj->getGeometryInfo();
const Coord3D *ctr = obj->getPosition();
Coord3D NRPDelta;
NRPDelta.x = naturalRallyPoint.x - exitPoint.x;
NRPDelta.y = naturalRallyPoint.y - exitPoint.y;
NRPDelta.z = 0.0f;
//This is a quick idiot test to se whether the rally line needs to wrap the box at all
Coord3D wayOutPoint = NRPDelta;
wayOutPoint.normalize();
wayOutPoint.scale( 99999.9f );
Real wayOutLength = wayOutPoint.length();
wayOutPoint.add(&naturalRallyPoint);
//if the rallypoint is closer to the wayoutpoint than it is to the natural rally point then we definitely do not wrap
Coord3D rallyToWayOutDelta = wayOutPoint;
rallyToWayOutDelta.sub(rallyPoint);
if ( (100.0f + rallyToWayOutDelta.length()) > wayOutLength)
{
//if we passed the above idiot test, now lets be sure by testing the dotproduct of the rp against the wayoutpoint
wayOutPoint.normalize();// a normal shooting straight out the door
//next comes the delta between the NRP and the RP
Coord3D NRPToRPDelta = naturalRallyPoint;
NRPToRPDelta.sub(rallyPoint);
NRPToRPDelta.normalize();
Real dot = NRPToRPDelta.x * wayOutPoint.x + NRPToRPDelta.y * wayOutPoint.y;
if (dot > 0)
{
Real angle = obj->getOrientation();
Real c = (Real)cos(angle);
Real s = (Real)sin(angle);
Coord3D NRPToCtrDelta;
NRPToCtrDelta.x = naturalRallyPoint.x - ctr->x;
NRPToCtrDelta.y = naturalRallyPoint.y - ctr->y;
NRPToCtrDelta.x = 0.0f;
Real exc = geom.getMajorRadius() * c;
Real eyc = geom.getMinorRadius() * c;
Real exs = geom.getMajorRadius() * s;
Real eys = geom.getMinorRadius() * s;
Coord2D corners[ 4 ];
corners[0].x = ctr->x - exc - eys;
corners[0].y = ctr->y + eyc - exs;
corners[1].x = ctr->x + exc - eys;
corners[1].y = ctr->y + eyc + exs;
corners[2].x = ctr->x + exc + eys;
corners[2].y = ctr->y - eyc + exs;
corners[3].x = ctr->x - exc + eys;
corners[3].y = ctr->y - eyc - exs;
Coord2D *pNearElbow = NULL;//find the closest corner to the rallyPoint same end as door
Coord2D *pFarElbow = NULL; //find the closest corner to the rallypoint away from door
Coord2D *nearCandidate = NULL;
Coord3D cornerToRPDelta, cornerToExitDelta;
cornerToRPDelta.z = 0.0f;
cornerToExitDelta.z = 0.0f;
Real elbowDistanceNear = 99999.9f;
Real elbowDistanceFar = 99999.9f;
for (UnsignedInt cornerIndex = 0; cornerIndex < 4; ++ cornerIndex)
{
nearCandidate = &corners[cornerIndex];//for quicker array access
cornerToExitDelta.x = exitPoint.x - nearCandidate->x;
cornerToExitDelta.y = exitPoint.y - nearCandidate->y;
cornerToExitDelta.normalize();
dot = cornerToExitDelta.x * wayOutPoint.x + cornerToExitDelta.y * wayOutPoint.y;
if ( dot < 0.0f )
{
cornerToRPDelta.x = rallyPoint->x - nearCandidate->x;
cornerToRPDelta.y = rallyPoint->y - nearCandidate->y;
if (cornerToRPDelta.length() < elbowDistanceNear)
{
elbowDistanceNear = cornerToRPDelta.length();
pNearElbow = nearCandidate;
}
}
else
{
cornerToRPDelta.x = rallyPoint->x - nearCandidate->x;
cornerToRPDelta.y = rallyPoint->y - nearCandidate->y;
if (cornerToRPDelta.length() < elbowDistanceFar)
{
elbowDistanceFar = cornerToRPDelta.length();
pFarElbow = nearCandidate;
}
}
}
if (pNearElbow)//did we find a nearest corner?
{
m_waypointNodeRobj->Set_Position(Vector3(pNearElbow->x,pNearElbow->y,ctr->z));
WW3D::Render(*m_waypointNodeRobj,localRinfo); //The little hockey puck
points[ numPoints ].Set( Vector3( pNearElbow->x, pNearElbow->y, ctr->z ) );
numPoints++;
//and for that matter did we find a far side coner?
if (pFarElbow)//did we find a nearest corner?
{
// but let's test the dot of the first elbow against the rally point to find out
//whethet the rally point wraps around this one, too
Coord3D firstElbowDelta;
firstElbowDelta.x = naturalRallyPoint.x - pNearElbow->x;
firstElbowDelta.y = naturalRallyPoint.y - pNearElbow->y;
firstElbowDelta.z = 0.0f;
firstElbowDelta.normalize();
Coord3D firstToRPDelta;
firstToRPDelta.x = pNearElbow->x - rallyPoint->x;
firstToRPDelta.y = pNearElbow->y - rallyPoint->y;
firstToRPDelta.z = 0.0f;
firstToRPDelta.normalize();
dot = firstToRPDelta.x * firstElbowDelta.x + firstToRPDelta.y * firstElbowDelta.y;
if (dot < 0)// we have a second elbow
{
m_waypointNodeRobj->Set_Position(Vector3(pFarElbow->x,pFarElbow->y,ctr->z));
WW3D::Render(*m_waypointNodeRobj,localRinfo); //The little hockey puck
points[ numPoints ].Set( Vector3( pFarElbow->x, pFarElbow->y, ctr->z ) );
numPoints++;
}
}
}
}
}
}
// Finally draw the line out to the RallyPoint
points[ numPoints ].Set( Vector3( rallyPoint->x, rallyPoint->y, rallyPoint->z ) );
numPoints++;
}
else
continue;
m_waypointNodeRobj->Set_Position(Vector3(naturalRallyPoint.x,naturalRallyPoint.y,naturalRallyPoint.z));
WW3D::Render(*m_waypointNodeRobj,localRinfo); //The little hockey puck
m_line->Set_Points( numPoints, points );
m_line->Render( localRinfo );
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,54 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Waves pixel shader
; Kenny Mitchell April 2001
; Modified July 2001 Mark Wilczynski
; Declare pixel shader version 1.1
ps.1.1
; Define t0 as a standard 3-vector from bumpmap
tex t0
; Perform EMBM to get a local normal bump reflection.
texbem t1, t0 ; compute new (u,v) values
#ifndef DO_WATER_ALPHA_TEXTURE
; result goes in output color multiplied by diffuse
mul r0, t1, v0
#else
;alternate version which uses a third texture which provides per-pixel alpha.
; result goes in output color multiplied by diffuse
tex t2 ; get alpha texture
mul r0.rgb, t1, v0
+mul r0.a, t2, v0
#endif

View file

@ -0,0 +1,72 @@
;
; Command & Conquer Generals(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 <http://www.gnu.org/licenses/>.
;
;////////////////////////////////////////////////////////////////////////////////
;// //
;// (c) 2001-2003 Electronic Arts Inc. //
;// //
;////////////////////////////////////////////////////////////////////////////////
; Waves vertex shader
; Kenny Mitchell April 2001
; Modified July 2001 Mark Wilczynski
#define CV_ZERO 0
#define CV_ONE 1
#define CV_WORLDVIEWPROJ_0 2
#define CV_WORLDVIEWPROJ_1 3
#define CV_WORLDVIEWPROJ_2 4
#define CV_WORLDVIEWPROJ_3 5
#define CV_TEXPROJ_0 6
#define CV_TEXPROJ_1 7
#define CV_TEXPROJ_2 8
#define CV_TEXPROJ_3 9
#define CV_PATCH_SCALE_OFFSET 10
#define V_POSITION v0
#define V_DIFFUSE v1
#define V_TEXTURE v2
#define V_TEXTURE2 v3
vs.1.1
; Below is Kenny's new optimized version
; Transform position to clip space and output it
dp4 r0.x, V_POSITION, c[CV_WORLDVIEWPROJ_0]
dp4 r0.y, V_POSITION, c[CV_WORLDVIEWPROJ_1]
dp4 r0.z, V_POSITION, c[CV_WORLDVIEWPROJ_2]
dp4 r0.w, V_POSITION, c[CV_WORLDVIEWPROJ_3]
mov oPos, r0
; get 1/w and multiply it onto x and y
rcp r1.w, r0.w
mul r1.xy, r0.xy, r1.w
; scale/flip/offset tex coords to screen
mad oT1.xy, r1.xy, c[CV_TEXPROJ_0].xy,c[CV_TEXPROJ_0].zw
mov oT0, V_TEXTURE
mov oD0, V_DIFFUSE
#ifdef DO_WATER_ALPHA_TEXTURE
;generate uv coordinates for a third texture (alpha channel)
mad oT2.xy,v0.xz,c[CV_PATCH_SCALE_OFFSET].zw,c[CV_PATCH_SCALE_OFFSET].xy
#endif

File diff suppressed because it is too large Load diff