/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DModelDraw.h /////////////////////////////////////////////////////////////////////////
// Author: Colin Day, November 2001
// Desc: Default client update module
///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#ifndef __W3DModelDraw_H_
#define __W3DModelDraw_H_
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
#include "Common/ModelState.h"
#include "Common/DrawModule.h"
#ifdef BRUTAL_TIMING_HACK // hack for collecting model timing info. jba.
class RenderObjClass {
public:
enum AnimMode
{
ANIM_MODE_MANUAL = 0,
ANIM_MODE_LOOP,
ANIM_MODE_ONCE,
ANIM_MODE_LOOP_PINGPONG,
ANIM_MODE_LOOP_BACKWARDS, //make sure only backwards playing animations after this one
ANIM_MODE_ONCE_BACKWARDS,
};
};
#else
#include "WW3D2/RendObj.h"
#endif
#include "Common/SparseMatchFinder.h"
#include "GameClient/ParticleSys.h"
#include "Common/STLTypedefs.h"
// FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
class Thing;
class RenderObjClass;
class Shadow;
class TerrainTracksRenderObjClass;
class HAnimClass;
enum GameLODLevel;
//-------------------------------------------------------------------------------------------------
/** The default client update module */
//-------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------
typedef UnsignedInt64 TransitionSig;
const TransitionSig NO_TRANSITION = 0;
inline TransitionSig buildTransitionSig(NameKeyType src, NameKeyType dst)
{
return (((UnsignedInt64)src) << 32) | ((UnsignedInt64)dst);
}
inline NameKeyType recoverSrcState(TransitionSig sig)
{
return (NameKeyType)((sig >> 32) & 0xffffffff);
}
inline NameKeyType recoverDstState(TransitionSig sig)
{
return (NameKeyType)((sig) & 0xffffffff);
}
//-------------------------------------------------------------------------------------------------
// please do not define RETAIN_ANIM_HANDLES. It prevents us from ever releasing animations.
// old code should be deleting by mid-Jan 2003. (srj)
#define NO_RETAIN_ANIM_HANDLES
//-------------------------------------------------------------------------------------------------
class W3DAnimationInfo
{
private:
AsciiString m_name;
#ifdef RETAIN_ANIM_HANDLES
mutable HAnimClass* m_handle;
#endif
Real m_distanceCovered; // if nonzero, the distance covered by a single loop of the anim
mutable Real m_naturalDurationInMsec;
Bool m_isIdleAnim; // if true, we pick another animation at random when this one completes
public:
W3DAnimationInfo(const AsciiString& name, Bool isIdle, Real distanceCovered);
W3DAnimationInfo(const W3DAnimationInfo &r);
W3DAnimationInfo &operator=(const W3DAnimationInfo& r);
~W3DAnimationInfo();
HAnimClass* getAnimHandle() const;
const AsciiString& getName() const { return m_name; }
Bool isIdleAnim() const { return m_isIdleAnim; }
Real getDistanceCovered() const { return m_distanceCovered; }
Real getNaturalDurationInMsec() const { return m_naturalDurationInMsec; }
};
typedef std::vector W3DAnimationVector;
//-------------------------------------------------------------------------------------------------
struct ParticleSysBoneInfo
{
AsciiString boneName;
const ParticleSystemTemplate* particleSystemTemplate;
};
typedef std::vector ParticleSysBoneInfoVector;
//-------------------------------------------------------------------------------------------------
struct PristineBoneInfo
{
Matrix3D mtx;
Int boneIndex;
};
//typedef std::hash_map< NameKeyType, PristineBoneInfo, rts::hash, rts::equal_to > PristineBoneInfoMap;
typedef std::map< NameKeyType, PristineBoneInfo, std::less > PristineBoneInfoMap;
//-------------------------------------------------------------------------------------------------
struct ModelConditionInfo
{
struct HideShowSubObjInfo
{
AsciiString subObjName;
Bool hide;
};
struct TurretInfo
{
// read from INI
NameKeyType m_turretAngleNameKey;
NameKeyType m_turretPitchNameKey;
Real m_turretArtAngle;
Real m_turretArtPitch;
// calculated at runtime
Int m_turretAngleBone;
Int m_turretPitchBone;
void clear()
{
m_turretAngleNameKey = NAMEKEY_INVALID;
m_turretPitchNameKey = NAMEKEY_INVALID;
m_turretArtAngle = 0;
m_turretArtPitch = 0;
m_turretAngleBone = 0;
m_turretPitchBone = 0;
}
};
struct WeaponBarrelInfo
{
Int m_recoilBone; ///< the W3D bone for this barrel (zero == no bone)
Int m_fxBone; ///< the FX bone for this barrel (zero == no bone)
Int m_muzzleFlashBone; ///< the muzzle-flash subobj bone for this barrel (zero == none)
Matrix3D m_projectileOffsetMtx; ///< where the projectile fires from
#if defined(_DEBUG) || defined(_INTERNAL)
AsciiString m_muzzleFlashBoneName;
#endif
WeaponBarrelInfo()
{
clear();
}
void clear()
{
m_recoilBone = 0;
m_fxBone = 0;
m_muzzleFlashBone = 0;
m_projectileOffsetMtx.Make_Identity();
#if defined(_DEBUG) || defined(_INTERNAL)
m_muzzleFlashBoneName.clear();
#endif
}
void setMuzzleFlashHidden(RenderObjClass *fullObject, Bool hide) const;
};
typedef std::vector WeaponBarrelInfoVec;
#if defined(_DEBUG) || defined(_INTERNAL)
AsciiString m_description;
#endif
std::vector m_conditionsYesVec;
AsciiString m_modelName;
std::vector m_hideShowVec;
mutable std::vector m_publicBones;
AsciiString m_weaponFireFXBoneName[WEAPONSLOT_COUNT];
AsciiString m_weaponRecoilBoneName[WEAPONSLOT_COUNT];
AsciiString m_weaponMuzzleFlashName[WEAPONSLOT_COUNT];
AsciiString m_weaponProjectileLaunchBoneName[WEAPONSLOT_COUNT];
AsciiString m_weaponProjectileHideShowName[WEAPONSLOT_COUNT];
W3DAnimationVector m_animations;
NameKeyType m_transitionKey;
NameKeyType m_allowToFinishKey;
Int m_flags;
Int m_iniReadFlags; // not read from ini, but used for helping with default states
RenderObjClass::AnimMode m_mode;
ParticleSysBoneInfoVector m_particleSysBones; ///< Bone names and attached particle systems.
TransitionSig m_transitionSig;
Real m_animMinSpeedFactor; //Min speed factor (randomized each time it's played)
Real m_animMaxSpeedFactor; //Max speed factor (randomized each time it's played)
mutable PristineBoneInfoMap m_pristineBones;
mutable TurretInfo m_turrets[MAX_TURRETS];
mutable WeaponBarrelInfoVec m_weaponBarrelInfoVec[WEAPONSLOT_COUNT];
mutable Bool m_hasRecoilBonesOrMuzzleFlashes[WEAPONSLOT_COUNT];
mutable Byte m_validStuff;
enum
{
PRISTINE_BONES_VALID = 0x0001,
TURRETS_VALID = 0x0002,
HAS_PROJECTILE_BONES = 0x0004,
BARRELS_VALID = 0x0008,
PUBLIC_BONES_VALID = 0x0010
};
inline ModelConditionInfo()
{
clear();
}
void clear();
void loadAnimations() const;
void preloadAssets( TimeOfDay timeOfDay, Real scale ); ///< preload any assets for time of day
inline Int getConditionsYesCount() const { DEBUG_ASSERTCRASH(m_conditionsYesVec.size() > 0, ("empty m_conditionsYesVec.size(), see srj")); return m_conditionsYesVec.size(); }
inline const ModelConditionFlags& getNthConditionsYes(Int i) const { return m_conditionsYesVec[i]; }
#if defined(_DEBUG) || defined(_INTERNAL)
inline AsciiString getDescription() const { return m_description; }
#endif
const Matrix3D* findPristineBone(NameKeyType boneName, Int* boneIndex) const;
Bool findPristineBonePos(NameKeyType boneName, Coord3D& pos) const;
void addPublicBone(const AsciiString& boneName) const;
Bool matchesMode(Bool night, Bool snowy) const;
void validateStuff(RenderObjClass* robj, Real scale, const std::vector& extraPublicBones) const;
private:
void validateWeaponBarrelInfo() const;
void validateTurretInfo() const;
void validateCachedBones(RenderObjClass* robj, Real scale) const;
};
typedef std::vector ModelConditionVector;
//-------------------------------------------------------------------------------------------------
//typedef std::hash_map< TransitionSig, ModelConditionInfo, std::hash, std::equal_to > TransitionMap;
typedef std::map< TransitionSig, ModelConditionInfo, std::less > TransitionMap;
//-------------------------------------------------------------------------------------------------
// this is more efficient and also helps solve a projectile-launch-offset problem for double-upgraded
// technicals. it is not within risk, however, and WILL NOT WORK if the attach-to-module is animated
// in any meaningful way, so be careful. (srj)
#define CACHE_ATTACH_BONE
//-------------------------------------------------------------------------------------------------
class W3DModelDrawModuleData : public ModuleData
{
public:
mutable ModelConditionVector m_conditionStates;
mutable SparseMatchFinder< ModelConditionInfo, ModelConditionFlags >
m_conditionStateMap;
mutable TransitionMap m_transitionMap;
std::vector m_extraPublicBones;
AsciiString m_trackFile; ///< if present, leaves tracks using this texture
AsciiString m_attachToDrawableBone;
#ifdef CACHE_ATTACH_BONE
mutable Vector3 m_attachToDrawableBoneOffset;
#endif
Int m_defaultState;
Int m_projectileBoneFeedbackEnabledSlots; ///< Hide and show the launch bone geometries according to clip status adjustments.
Real m_initialRecoil;
Real m_maxRecoil;
Real m_recoilDamping;
Real m_recoilSettle;
StaticGameLODLevel m_minLODRequired; ///< minumum game LOD level necessary to use this module.
ModelConditionFlags m_ignoreConditionStates;
Bool m_okToChangeModelColor;
Bool m_animationsRequirePower;///< Should UnderPowered disable type pause animations in this draw module?
#ifdef CACHE_ATTACH_BONE
mutable Bool m_attachToDrawableBoneOffsetValid;
#endif
mutable Byte m_validated;
Bool m_particlesAttachedToAnimatedBones;
Bool m_receivesDynamicLights; ///< just like it sounds... it sets a property of Drawable, actually
W3DModelDrawModuleData();
~W3DModelDrawModuleData();
void validateStuffForTimeAndWeather(const Drawable* draw, Bool night, Bool snowy) const;
static void buildFieldParse(MultiIniFieldParse& p);
AsciiString getBestModelNameForWB(const ModelConditionFlags& c) const;
const ModelConditionInfo* findBestInfo(const ModelConditionFlags& c) const;
void preloadAssets( TimeOfDay timeOfDay, Real scale ) const;
#ifdef CACHE_ATTACH_BONE
const Vector3* getAttachToDrawableBoneOffset(const Drawable* draw) const;
#endif
// ugh, hack
virtual const W3DModelDrawModuleData* getAsW3DModelDrawModuleData() const { return this; }
virtual StaticGameLODLevel getMinimumRequiredGameLOD() const { return m_minLODRequired;}
private:
static void parseConditionState( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ );
public:
virtual void crc( Xfer *xfer );
virtual void xfer( Xfer *xfer );
virtual void loadPostProcess( void );
};
//-------------------------------------------------------------------------------------------------
class W3DModelDraw : public DrawModule, public ObjectDrawInterface
{
MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( W3DModelDraw, "W3DModelDraw" )
MAKE_STANDARD_MODULE_MACRO_WITH_MODULE_DATA( W3DModelDraw, W3DModelDrawModuleData )
public:
W3DModelDraw( Thing *thing, const ModuleData* moduleData );
// virtual destructor prototype provided by memory pool declaration
/// preloading assets
virtual void preloadAssets( TimeOfDay timeOfDay );
/// the draw method
virtual void doDrawModule(const Matrix3D* transformMtx);
virtual void setShadowsEnabled(Bool enable);
virtual void releaseShadows(void); ///< frees all shadow resources used by this module - used by Options screen.
virtual void allocateShadows(void); ///< create shadow resources if not already present. Used by Options screen.
#if defined(_DEBUG) || defined(_INTERNAL)
virtual void getRenderCost(RenderCost & rc) const; ///< estimates the render cost of this draw module
void getRenderCostRecursive(RenderCost & rc,RenderObjClass * robj) const;
#endif
virtual void setFullyObscuredByShroud(Bool fullyObscured);
virtual void setTerrainDecal(TerrainDecalType type);
virtual Bool isVisible() const;
virtual void reactToTransformChange(const Matrix3D* oldMtx, const Coord3D* oldPos, Real oldAngle);
virtual void reactToGeometryChange() { }
// this method must ONLY be called from the client, NEVER From the logic, not even indirectly.
virtual Bool clientOnly_getRenderObjInfo(Coord3D* pos, Real* boundingSphereRadius, Matrix3D* transform) const;
virtual Bool clientOnly_getRenderObjBoundBox(OBBoxClass * boundbox) const;
virtual Bool clientOnly_getRenderObjBoneTransform(const AsciiString & boneName,Matrix3D * set_tm) const;
virtual Int getPristineBonePositionsForConditionState(const ModelConditionFlags& condition, const char* boneNamePrefix, Int startIndex, Coord3D* positions, Matrix3D* transforms, Int maxBones) const;
virtual Int getCurrentBonePositions(const char* boneNamePrefix, Int startIndex, Coord3D* positions, Matrix3D* transforms, Int maxBones) const;
virtual Bool getCurrentWorldspaceClientBonePositions(const char* boneName, Matrix3D& transform) const;
virtual Bool getProjectileLaunchOffset(const ModelConditionFlags& condition, WeaponSlotType wslot, Int specificBarrelToUse, Matrix3D* launchPos, WhichTurretType tur, Coord3D* turretRotPos, Coord3D* turretPitchPos = NULL) const;
virtual void updateProjectileClipStatus( UnsignedInt shotsRemaining, UnsignedInt maxShots, WeaponSlotType slot ); ///< This will do the show/hide work if ProjectileBoneFeedbackEnabled is set.
virtual void updateDrawModuleSupplyStatus( Int maxSupply, Int currentSupply ); ///< This will do visual feedback on Supplies carried
virtual void notifyDrawModuleDependencyCleared( ){}///< if you were waiting for something before you drew, it's ready now
virtual void setHidden(Bool h);
virtual void replaceModelConditionState(const ModelConditionFlags& c);
virtual void replaceIndicatorColor(Color color);
virtual Bool handleWeaponFireFX(WeaponSlotType wslot, Int specificBarrelToUse, const FXList* fxl, Real weaponSpeed, const Coord3D* victimPos, Real damageRadius);
virtual Int getBarrelCount(WeaponSlotType wslot) const;
virtual void setSelectable(Bool selectable); // Change the selectability of the model.
/**
This call says, "I want the current animation (if any) to take n frames to complete a single cycle".
If it's a looping anim, each loop will take n frames. someday, we may want to add the option to insert
"pad" frames at the start and/or end, but for now, we always just "stretch" the animation to fit.
Note that you must call this AFTER setting the condition codes.
*/
virtual void setAnimationLoopDuration(UnsignedInt numFrames);
/**
similar to the above, but assumes that the current state is a "ONCE",
and is smart about transition states... if there is a transition state
"inbetween", it is included in the completion time.
*/
virtual void setAnimationCompletionTime(UnsignedInt numFrames);
/**
This call is used to pause or resume an animation.
*/
virtual void setPauseAnimation(Bool pauseAnim);
//Kris: Manually set a drawable's current animation to specific frame.
virtual void setAnimationFrame( int frame );
virtual void updateSubObjects();
virtual void showSubObject( const AsciiString& name, Bool show );
#ifdef ALLOW_ANIM_INQUIRIES
// srj sez: not sure if this is a good idea, for net sync reasons...
virtual Real getAnimationScrubScalar( void ) const;
#endif
virtual ObjectDrawInterface* getObjectDrawInterface() { return this; }
virtual const ObjectDrawInterface* getObjectDrawInterface() const { return this; }
///@todo: I had to make this public because W3DDevice needs access for casting shadows -MW
inline RenderObjClass *getRenderObject() { return m_renderObject; }
virtual Bool updateBonesForClientParticleSystems( void );///< this will reposition particle systems on the fly ML
virtual void onDrawableBoundToObject();
virtual void setTerrainDecalSize(Real x, Real y);
virtual void setTerrainDecalOpacity(Real o);
protected:
virtual void onRenderObjRecreated(void){};
inline const ModelConditionInfo* getCurState() const { return m_curState; }
void setModelState(const ModelConditionInfo* newState);
const ModelConditionInfo* findBestInfo(const ModelConditionFlags& c) const;
void handleClientTurretPositioning();
void handleClientRecoil();
void recalcBonesForClientParticleSystems();
void stopClientParticleSystems();
void doHideShowSubObjs(const std::vector* vec);
virtual void adjustTransformMtx(Matrix3D& mtx) const;
Real getCurAnimDistanceCovered() const;
Bool setCurAnimDurationInMsec(Real duration);
inline Bool getFullyObscuredByShroud() const { return m_fullyObscuredByShroud; }
private:
struct WeaponRecoilInfo
{
enum RecoilState
{
IDLE, RECOIL_START, RECOIL, SETTLE
};
RecoilState m_state; ///< what state this gun is in
Real m_shift; ///< how far the gun barrel has recoiled
Real m_recoilRate; ///< how fast the gun barrel is recoiling
WeaponRecoilInfo()
{
clear();
}
void clear()
{
m_state = IDLE;
m_shift = 0.0f;
m_recoilRate = 0.0f;
}
};
struct ParticleSysTrackerType
{
ParticleSystemID id;
Int boneIndex;
};
typedef std::vector WeaponRecoilInfoVec;
typedef std::vector ParticleSystemIDVec;
//typedef std::vector ParticleSystemIDVec;
const ModelConditionInfo* m_curState;
const ModelConditionInfo* m_nextState;
UnsignedInt m_nextStateAnimLoopDuration;
Int m_hexColor;
Int m_whichAnimInCurState; ///< the index of the currently playing anim in cur state (if any)
WeaponRecoilInfoVec m_weaponRecoilInfoVec[WEAPONSLOT_COUNT];
Bool m_needRecalcBoneParticleSystems;
Bool m_fullyObscuredByShroud;
Bool m_shadowEnabled; ///< cached state of shadow. Used to determine if shadows should be enabled via options screen.
RenderObjClass* m_renderObject; ///< W3D Render object for this drawable
Shadow* m_shadow; ///< Updates/Renders shadows of this object
Shadow* m_terrainDecal;
TerrainTracksRenderObjClass* m_trackRenderObject; ///< This is rendered under object
ParticleSystemIDVec m_particleSystemIDs; ///< The ID numbers of the particle systems currently running.
std::vector m_subObjectVec;
Bool m_hideHeadlights;
Bool m_pauseAnimation;
Int m_animationMode;
void adjustAnimation(const ModelConditionInfo* prevState, Real prevAnimFraction);
Real getCurrentAnimFraction() const;
void applyCorrectModelStateAnimation();
const ModelConditionInfo* findTransitionForSig(TransitionSig sig) const;
void rebuildWeaponRecoilInfo(const ModelConditionInfo* state);
void doHideShowProjectileObjects( UnsignedInt showCount, UnsignedInt maxCount, WeaponSlotType slot );///< Means effectively, show m of n.
void nukeCurrentRender(Matrix3D* xform);
void doStartOrStopParticleSys();
void adjustAnimSpeedToMovementSpeed();
static void hideAllMuzzleFlashes(const ModelConditionInfo* state, RenderObjClass* renderObject);
void hideAllHeadlights(Bool hide);
#if defined(_DEBUG) || defined(_INTERNAL) //art wants to see buildings without flags as a test.
void hideGarrisonFlags(Bool hide);
#endif
};
#endif // __W3DModelDraw_H_