Files
GTASource/game/renderer/PlantsMgr.h
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1234 lines
37 KiB
C++

//
// CPlantsMgr - management of plants seeded around camera;
//
// 24/06/2005 - Andrzej: - initial conversion from SA code;
// 14/07/2005 - Andrzej: - full working port to Rage;
//
//
//
#ifndef __CPLANTSMGR_H__
#define __CPLANTSMGR_H__
//
// PSN: if 1, then SPU renderer is used:
//
#define PSN_PLANTSMGR_SPU_RENDER (1 && (__PPU || __SPU))
#define PSN_PLANTSMGR_SPU_UPDATE (1 && (__PPU || __SPU))
#define PLANTSMGR_DATA_EDITOR (__BANK)
#define PLANTSMGR_MULTI_UPDATE (1 && (RSG_PC || RSG_DURANGO || RSG_ORBIS)) // NG: Update task split into subtasks
#define PLANTSMGR_MULTI_RENDER (0 && (RSG_PC || RSG_DURANGO || RSG_ORBIS)) // NG: Render task split into subtasks
#if !__SPU
#include "render_channel.h"
RAGE_DECLARE_SUBCHANNEL(render, plants)
#define plantsAssertf(cond,fmt,...) RAGE_ASSERTF(render_plants,cond,fmt,##__VA_ARGS__)
#define plantsAssert(cond) RAGE_ASSERT(render_plants,cond)
#define plantsVerifyf(cond,fmt,...) RAGE_VERIFYF(render_plants,cond,fmt,##__VA_ARGS__)
#define plantsErrorf(fmt,...) RAGE_ERRORF(render_plants,fmt,##__VA_ARGS__)
#define plantsWarningf(fmt,...) RAGE_WARNINGF(render_plants,fmt,##__VA_ARGS__)
#define plantsDisplayf(fmt,...) RAGE_DISPLAYF(render_plants,fmt,##__VA_ARGS__)
#define plantsDebugf1(fmt,...) RAGE_DEBUGF1(render_plants,fmt,##__VA_ARGS__)
#define plantsDebugf2(fmt,...) RAGE_DEBUGF2(render_plants,fmt,##__VA_ARGS__)
#define plantsDebugf3(fmt,...) RAGE_DEBUGF3(render_plants,fmt,##__VA_ARGS__)
#endif //!__SPU...
// rage includes:
#include "grcore/effect_config.h"
#include "data/resourceheader.h" // g_rscVirtualLeafSize
#include "spatialdata/transposedplaneset.h" // spdTransposedPlaneSet8
#include "system/memory.h"
#include "system/criticalsection.h"
#include "atl/array.h"
// framework headers:
// game includes:
#if !__SPU
#include "fwscene/stores/staticboundsstore.h" // g_StaticBoundsStore
#include "scene/entity.h"
#endif
#include "scene/RegdRefTypes.h"
#include "tools/SectorToolsParam.h"
#include "renderer/PlantsGrassRendererSwitches.h"
// forward definitions:
namespace rage {
class Vector2;
class Vector3;
class Vector4;
class Color32;
class phPolygon;
class phBound;
class phBoundGeometry;
class grcTexture;
class grmGeometry;
}
class CEntity;
class gtaDrawable;
class CPlantLocTriArray;
#define FURGRASS_TEST_V4 (1 && (RSG_PC || RSG_DURANGO || RSG_ORBIS)) // fur grass LOD v4
#define FURGRASS_MAX_LAYERS (8)
#define CPLANT_STORE_LOCTRI_NORMAL (0) // WIP: store LocTri normal
#define CPLANT_WRITE_GRASS_NORMAL (0)
#define CPLANT_PRE_MULTIPLY_LIGHTING (1)
#define CPLANT_BLIT_MIN_GBUFFER (__PS3) // Bind min number of GBuffer targets when doing the fakeBlit (mutually exclusive with CPLANT_STORE_LOCTRI_NORMAL)
#define CPLANT_USE_OCCLUSION (0)
#define CPLANT_CLIP_EDGE_VERT (0)
#define CPLANT_DYNAMIC_CULL_SPHERES (0)
#define CPLANT_CULLSPHERES_MAX (8) // amount of cullspheres supported
//
//
// max poly points in phPolygon (taken from phBound, etc.):
//
#define CPLANT_MAX_POLY_POINTS (POLY_MAX_VERTICES)
//
// num of models per slot:
//
#if __PS3 || __XENON
#define CPLANT_SLOT_NUM_MODELS (16)
#else // __PS3 || __XENON
#define CPLANT_SLOT_NUM_MODELS (32)
#endif // __PS3 || __XENON
//
// num of textures per slot:
//
#define CPLANT_SLOT_NUM_TEXTURES (32)
#define CPLANT_INCREASE_DIST (80.0f)
// LOD0:
#define CPLANT_LOD0_ALPHA_CLOSE_DIST (22.0f) // LOD0: fade start range
#define CPLANT_LOD0_ALPHA_FAR_DIST (32.0f) // LOD0: fade stop range
#define CPLANT_LOD0_FAR_DIST (32.0f) // LOD0: where LOD0 is culled
// LOD1:
#define CPLANT_LOD1_CLOSE_DIST (15.0f) // LOD1: where LOD1 starts
#define CPLANT_LOD1_ALPHA0_CLOSE_DIST (15.0f) // LOD1: alpha0 fade start
#define CPLANT_LOD1_ALPHA0_FAR_DIST (25.0f) // LOD1: alpha0 fade stop
#define CPLANT_LOD1_ALPHA1_CLOSE_DIST (40.0f) // LOD1: alpha1 fade start
#define CPLANT_LOD1_ALPHA1_FAR_DIST (55.0f) // LOD1: alpha1 fade stop
#define CPLANT_LOD1_FAR_DIST (55.0f) // LOD1: where LOD1 is culled
// LOD2:
#define CPLANT_LOD2_CLOSE_DIST (30.0f) // LOD2: where LOD1 starts
#define CPLANT_LOD2_ALPHA0_CLOSE_DIST (30.0f) // LOD2: alpha0 fade start
#define CPLANT_LOD2_ALPHA0_FAR_DIST (45.0f) // LOD2: alpha0 fade stop
#define CPLANT_LOD2_ALPHA1_CLOSE_DIST (65.0f ) // LOD2: alpha1 fade start
#define CPLANT_LOD2_ALPHA1_FAR_DIST (85.0f ) // LOD2: alpha1 fade stop
#define CPLANT_LOD2_ALPHA1_CLOSE_DIST_FAR (65.0f+CPLANT_INCREASE_DIST) // LOD2: alpha1 fade start
#define CPLANT_LOD2_ALPHA1_FAR_DIST_FAR (85.0f+CPLANT_INCREASE_DIST) // LOD2: alpha1 fade stop
#define CPLANT_LOD2_FAR_DIST (85.0f+CPLANT_INCREASE_DIST) // LOD2: where LOD1 is culled
#if PLANTS_USE_LOD_SETTINGS
//
// radius of test sphere used to detect instances located close enough:
//
#define CPLANTMGR_COL_TEST_RADIUS_INITVAL (80.0f + CPLANT_INCREASE_DIST)
#define CPLANTMGR_COL_TEST_RADIUS (80.0f + CPLANT_INCREASE_DIST)
//
// distance where TriLocs are considered to be used as base for plant sectors:
//
#define CPLANT_TRILOC_FAR_DIST (CPLANT_LOD2_FAR_DIST)
#define CPLANT_TRILOC_FAR_DIST_SQR (CPLANT_TRILOC_FAR_DIST*CPLANT_TRILOC_FAR_DIST)
#define CPLANT_TRILOC_SHORT_FAR_DIST (CPLANT_TRILOC_FAR_DIST - CPLANT_INCREASE_DIST)
#define CPLANT_TRILOC_SHORT_FAR_DIST_SQR (CPLANT_TRILOC_SHORT_FAR_DIST*CPLANT_TRILOC_SHORT_FAR_DIST)
#define CPLANT_TRILOC_FAR_DIST_INITVAL (85.0f + CPLANT_INCREASE_DIST)
#define CPLANT_TRILOC_SHORT_FAR_DIST_INITVAL (CPLANT_TRILOC_FAR_DIST_INITVAL - CPLANT_INCREASE_DIST)
#elif __BANK
//
// radius of test sphere used to detect instances located close enough:
//
#define CPLANTMGR_COL_TEST_RADIUS_INITVAL (80.0f + CPLANT_INCREASE_DIST)
#define CPLANTMGR_COL_TEST_RADIUS (CPlantMgr::ms_bkColTestRadius)
//
// distance where TriLocs are considered to be used as base for plant sectors:
//
#define CPLANT_TRILOC_FAR_DIST_INITVAL (85.0f + CPLANT_INCREASE_DIST)
#if __SPU
#define CPLANT_TRILOC_FAR_DIST_SQR (g_jobParams->m_bkTriLocFarDistSqr)
#else
#define CPLANT_TRILOC_FAR_DIST (CPlantMgr::ms_bkTriLocFarDist)
#define CPLANT_TRILOC_FAR_DIST_SQR (CPlantMgr::ms_bkTriLocFarDistSqr)
#endif
#define CPLANT_TRILOC_SHORT_FAR_DIST_INITVAL (CPLANT_TRILOC_FAR_DIST_INITVAL - CPLANT_INCREASE_DIST)
#if __SPU
#define CPLANT_TRILOC_SHORT_FAR_DIST_SQR (g_jobParams->m_bkTriLocShortFarDistSqr)
#else
#define CPLANT_TRILOC_SHORT_FAR_DIST (CPlantMgr::ms_bkTriLocShortFarDist)
#define CPLANT_TRILOC_SHORT_FAR_DIST_SQR (CPlantMgr::ms_bkTriLocShortFarDistSqr)
#endif
#else
//
// radius of test sphere used to detect instances located close enough:
//
#define CPLANTMGR_COL_TEST_RADIUS (80.0f + CPLANT_INCREASE_DIST)
//
// distance where TriLocs are considered to be used as base for plant sectors:
//
#define CPLANT_TRILOC_FAR_DIST (85.0f + CPLANT_INCREASE_DIST)
#define CPLANT_TRILOC_FAR_DIST_SQR (CPLANT_TRILOC_FAR_DIST*CPLANT_TRILOC_FAR_DIST)
#define CPLANT_TRILOC_SHORT_FAR_DIST (CPLANT_TRILOC_FAR_DIST - CPLANT_INCREASE_DIST)
#define CPLANT_TRILOC_SHORT_FAR_DIST_SQR (CPLANT_TRILOC_SHORT_FAR_DIST*CPLANT_TRILOC_SHORT_FAR_DIST)
#endif //!__BANK...
#define CPLANT_GROUND_SLOPE_ANGLE_MIN (4) // ground slope must be more than 4degs for grass to be skewed
extern bool gbPlantMgrActive; // JB : I need to be able to turn the plantmgr off when exporting navmesh data
//
//
// number of entities hold in our internal CEntity cache:
//
#define CPLANT_COL_ENTITY_CACHE_SIZE (40)
//
//
// update cache every 32th frame (must be power-of-2):
//
#define CPLANT_COL_ENTITY_UPDATE_CACHE (32)
//
//
// amount of parallel lists:
//
#if RSG_PC || RSG_DURANGO || RSG_ORBIS
#ifdef TERRAIN_BAKE_TOOL
#define CPLANT_LOC_TRIS_LIST_NUM (32)
#else
#define CPLANT_LOC_TRIS_LIST_NUM (8)
#endif // TERRAIN_BAKE_TOOL
#else
#define CPLANT_LOC_TRIS_LIST_NUM (4)
#endif
#define PLANTSMGR_MULTI_UPDATE_TASKCOUNT (CPLANT_LOC_TRIS_LIST_NUM) // NG: amount of parallel update tasks
#define PLANTSMGR_MULTI_RENDER_TASKCOUNT (2) //(4) // NG: amount of parallel render tasks
//
//
// max number of triangles kept in loc triangle cache:
//
#if RSG_PC || RSG_DURANGO || RSG_ORBIS
#define CPLANT_MAX_CACHE_LOC_TRIS_NUM (682*4)
#else
#define CPLANT_MAX_CACHE_LOC_TRIS_NUM (682)
#endif
//
// process every 8th TriLoc (must be power-of-2):
//
#define CPLANT_ENTRY_TRILOC_PROCESS_UPDATE (8)
//
// unique mask value to mark case, when we want all LocTris to be processed:
//
#define CPLANT_ENTRY_TRILOC_PROCESS_ALWAYS (0x7A7A7A7A) // changed as it is compared with a signed value
//
// use 2D (rather than 3D) collision calculations for LocTris:
//
#define CPLANT_USE_COLLISION_2D_DIST (1)
// per-vertex scaleXYZ, scaleZ and Density:
#define CPLANT_PV_DEFAULT_SCALEXYZ (0x08) // scaleXYZ:0x08 maps to 0.06666 ("positive zero")
#define CPLANT_PV_DEFAULT_SCALEZ (0x03) // scaleZ: 0x03 maps to "no scale Z"
#define CPLANT_PV_DEFAULT_DENSITY (0x03) // density: 0x03 maps to "no density"
#define CPLANT_PV_DEFAULT_D_SZ_SXYZ ((CPLANT_PV_DEFAULT_DENSITY<<6) | (CPLANT_PV_DEFAULT_SCALEZ<<4) | CPLANT_PV_DEFAULT_SCALEXYZ)
CompileTimeAssert(CPLANT_PV_DEFAULT_D_SZ_SXYZ==0xf8);
#if CPLANT_DYNAMIC_CULL_SPHERES
#define PLANTSMGR_MAX_DYNAMIC_CULL_SPHERES (10)
typedef atFixedArray<spdSphere, PLANTSMGR_MAX_DYNAMIC_CULL_SPHERES> PlantMgr_DynCullSphereArray;
#define PLANTSMGR_INVALID_DYNAMIC_CULL_SPHERE_INDEX (static_cast<u32>(-1))
#endif
#if PSN_PLANTSMGR_SPU_RENDER
#include "PlantsGrassRendererSPU.h"
#endif
//
//
// available for both SPU and non-SPU rendering:
//
struct grassModel
{
grassModel() { sysMemSet(this, 0x00, sizeof(grassModel)); }
gtaDrawable *m_pDrawable; // higher level drawable, used only for memory management
grmGeometry *m_pGeometry; // plant geometry
Vector2 m_dimensionLOD2; // dimensions (w&h) of LOD2 (stored in LOD1)
Vector4 m_BoundingSphere; // model bound sphere
#if PSN_PLANTSMGR_SPU_RENDER
u32 m_IdxOffset;
u16 m_IdxLocation; // LOCATION_LOCAL, LOCATION_MAIN
u16 m_IdxCount;
u16 m_DrawMode; // CELL_GCM_PRIMITIVE_TRIANGLES
u16 m_pad0;
u32 m_GeometryCmdOffset;// offset to geometry commands
#define PSN_SIZEOF_GRASSMODEL (48)
#endif //PSN_PLANTSMGR_SPU_RENDER...
};
//
//
// "Plant Location" triangle:
// it's generated from ColModel data;
//
class CPlantLocTri
{
friend class CPlantMgr;
friend class CPlantLocTriArray;
#if PLANTSMGR_DATA_EDITOR
friend class CPlantMgrDataEditor;
#endif
public:
CPlantLocTri() { }
~CPlantLocTri() { }
public:
CPlantLocTri* Add(CPlantLocTriArray& triTab, const Vector3& v1, const Vector3& v2, const Vector3& v3, phMaterialMgr::Id nSurfaceType, bool bCreatesPlants, bool bCreatesObjects, CEntity* pParentEntity, u16 nColEntIdx, bool IsFarDrawTri);
void Release(CPlantLocTriArray& triTab);
public:
inline static u8 pv8PackDensityScaleZScaleXYZ(u8 density, u8 scaleZ, u8 scaleXYZ);
inline static u8 pv8UnpackScaleXYZ(u8 densityAndScale8);
inline static u8 pv8UnpackScaleZ(u8 densityAndScale8);
inline static u8 pv8UnpackDensity(u8 densityAndScale8);
inline static float pv8MapScaleXYZ(u8 scaleXYZ);
inline static float pv8MapScaleZ(u8 scaleZ);
inline static float pv8MapDensity(u8 density);
inline static float pv8UnpackAndMapScaleXYZ(u8 densityAndScale8);
inline static float pv8UnpackAndMapScaleZ(u8 densityAndScale8);
inline static float pv8UnpackAndMapDensity(u8 densityAndScale8);
private:
static float ms_pvMapScaleZ[4];
static float ms_pvMapDensity[4];
public:
inline Vector3 GetCenter() const;
inline Vector3 GetV1() const { return m_V1.GetVector3(); }
inline Vector3 GetV2() const { return m_V2.GetVector3(); }
inline Vector3 GetV3() const { return m_V3.GetVector3(); }
inline float GetSphereRadius() const { return m_V1.w; }
inline float GetTriArea() const { return m_V2.w; }
inline int GetSeed() const { return m_V3.iw; }
float CalcArea();
static bool IsPtInTriangle2D(float x, float y, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Vector3& normal, float* z);
private:
// 3 vertices of triangle
Vector4 m_V1; // W = SphereRadius
Vector4 m_V2; // W = tri Area
Vector4 m_V3; // W = Seed
public:
inline void SetSphereRadius(float v) { m_V1.w = v; }
inline void SetTriArea(float v) { m_V2.w = v; }
inline void SetSeed(int v) { m_V3.iw= v; }
public:
#if __DEV || SECTOR_TOOLS_EXPORT
u32 m_nMaxNumPlants; // debug info only: max number of plants to generate for this TriLoc
#endif
// local per-vertex variation data: ground color(RGB) + density/scaleZ/ScaleXYZ(A):
Color32 m_GroundColorV1, m_GroundColorV2, m_GroundColorV3;
// 64
phMaterialMgr::Id m_nSurfaceType; // copied from CColTriangle;
Float16Vec4 m_skewAxisAngle; // ground skewing: xyz = skewAxis, w=skewAngle (if 0 then no skewing)
// 80
CEntity *m_pParentEntity;
private:
u16 m_NextTri;
u16 m_PrevTri;
public:
s8 m_normal[3]; // packed loctri normal
u8 m_nColEntIdx; // idx of CPlantColBoundEntry this tri belongs to
u8 m_nAmbientScale[2]; // natural/artificial AO
bool m_bCreatesPlants : 1;
bool m_bCreatesObjects : 1;
bool m_bCreatedObjects : 1;
bool m_bDrawFarTri : 1;
bool m_bCameraDontCull : 1;
bool m_bUnderwater : 1;
bool m_bGroundScale1Vert : 1;
bool m_bRequireAmbScale : 1; // requires TC ambient scale resample
#if CPLANT_USE_OCCLUSION
bool m_bOccluded : 1;
bool m_bNeedsAABB : 1;
u8 m_pad3 : 6;
#elif CPLANT_CLIP_EDGE_VERT
bool m_ClipEdge_01 :1;
bool m_ClipEdge_12 :1;
bool m_ClipEdge_20 :1;
bool m_ClipVert_0 :1;
bool m_ClipVert_1 :1;
bool m_ClipVert_2 :1;
u8 m_pad3 :2;
#endif
#if __PPU || __SPU
#define PSN_SIZEOF_CPLANTLOCTRI (96)
#endif
};
//
//
// wrapper class around {listID:2, idx:14} as one u16:
//
struct CTriHashIdx16
{
private:
union
{
u16 m_u16;
struct
{
#if RSG_PC || RSG_DURANGO || RSG_ORBIS
u16 m_idx :13; // hashed index: 0-8191
u16 m_listID:3; // listID: 0-7
#else
u16 m_idx :14; // hashed index: 0-16383
u16 m_listID:2; // listID: 0-3
#endif
} h;
};
public:
CTriHashIdx16()
{
// do nothing
}
CTriHashIdx16(u16 n)
{
m_u16 = n;
}
public:
// hash idx operators:
inline void Make(u16 listID, u16 idx)
{
#if RSG_PC || RSG_DURANGO || RSG_ORBIS
FastAssert(listID < 8); // no more than 8 lists
FastAssert(idx < 0x1fff); // no more than 8191 indices
#else
FastAssert(listID < 4); // no more than 4 lists
FastAssert(idx < 0x3fff); // no more than 16383 indices
#endif
h.m_idx = idx;
h.m_listID = listID;
}
inline u16 GetIdx() const
{
return h.m_idx;
}
inline u16 GetListID() const
{
return h.m_listID;
}
public:
// u16 operators:
inline u16 operator=(const u16 n)
{
m_u16 = n;
return(m_u16);
}
inline bool operator==(const u16 n)
{
return(m_u16==n);
}
inline operator u16()
{
return m_u16;
}
};
CompileTimeAssert(sizeof(CTriHashIdx16)==sizeof(u16));
// fur grass render settings picked up from underlying entities:
struct CPlantColBoundEntryFurGrassInfo
{
grcTexture* m_furGrassDiffTex;
grcTexture* m_furGrassNormalTex;
grcTexture* m_furGrassComboH4Tex[4];
u32 m_pad_u32;
bool m_bValid :1; // true if pickup fur grass settings contain valid entries
u8 m_pad_u8a :7;
u8 m_pad_u8[3];
};
CompileTimeAssertSize(CPlantColBoundEntryFurGrassInfo,32,56); // must be dma'able
//
//
// collision bound entry in internal "entity" cache:
//
class CPlantColBoundEntry
{
friend class CPlantMgr;
friend class CPlantColBoundEntryArray;
public:
CPlantColBoundEntry() { }
~CPlantColBoundEntry() { }
public:
#if !__SPU
CPlantColBoundEntry* AddEntry(CEntity *pEntity, s32 parentIdx, s32 childIdx);
void ReleaseEntry();
#endif
phInst* GetPhysInst();
phBoundGeometry* GetBound();
#if !__SPU
const Matrix34* GetBoundMatrix();
bool IsParentValid() const
{
if(IsMatBound())
{
return g_StaticBoundsStore.GetPtr(strLocalIndex(m_nBParentIndex))!=NULL;
}
else
{
return (m__pEntity!=NULL) && (m__pEntity->GetCurrentPhysicsInst()!=NULL);
}
}
#endif
bool IsMatBound() const
{
return m_bMaterialBound;
}
#if PLANTSMGR_MULTI_UPDATE
void Lock() { m_csToken.Lock(); }
bool TryLock() { return m_csToken.TryLock(); }
void Unlock() { m_csToken.Unlock(); }
#else
void Lock() { }
bool TryLock() { }
void Unlock() { }
#endif
private:
Matrix34 m_BoundMat; // +64: local copy of above matrix (sometimes phInst, which matrix we use, doesn't exist after some time)
#if FURGRASS_TEST_V4
CPlantColBoundEntryFurGrassInfo m_furInfo;
#endif
atBitSet32 m_BoundMatProps; // +8: bitfield to store bCreatesPlants|bCreatesObjects material flags
RegdEnt m__pEntity; // +4: valid only if m_bIsMatBound==false
s32 m_nBParentIndex; // +4: composite bound index in g_StaticBoundsStore (valid only if m_bMaterialBound==true)
s32 m_nBChildIndex; // +4: child index of above composite bound (valid only if m_bMaterialBound==true)
#if PLANTSMGR_MULTI_UPDATE
sysCriticalSectionToken m_csToken; // entity access token during multi update
atBitSet32 m_processedTris; // caching table to mark processed tris, so subsequent tasks don't re-process them
#endif
CTriHashIdx16* m_LocTriArray; // +4: array of size [phBound->GetNumPolygons()], holds pointers to CPlantLocTri for every phPolygon in the phBound;
u16 m_nNumTris; // +2: size of above array
u16 m_nScancode; // +2: scancode (timestamp) of current frame
bool m_bBoundMatIdentity :1; // +2: true if BoundMat is identity matrix (no transformation required for LocTris then)
bool m_bMaterialBound :1; // true=material bound (from g_StaticBoundsStore); false=entity based bound
ATTR_UNUSED u16 m_pad00 :14;
u16 m_NextEntry; // +2
u16 m_PrevEntry; // +2
};
//
//
// handy wrapper around array of CPlantColBoundEntry to be accessed with hashed 'addresses'/indices (0=NULL, everything else is hashed index to array):
//
class CPlantColBoundEntryArray
{
public:
CPlantColBoundEntryArray() {}
~CPlantColBoundEntryArray() {}
public:
void Init();
// returns entry pointed by hashed index h:
inline CPlantColBoundEntry& operator[](u16 h)
{
FastAssert((h > 0) && (h < (CPLANT_COL_ENTITY_CACHE_SIZE+1)));
return m_tab[h-1];
}
// converts entry ptr inside m_tab into hashed index:
inline u16 GetIdx(CPlantColBoundEntry* e)
{
const u32 h = ptrdiff_t_to_int(e - m_tab + 1);
FastAssert((h > 0) && (h < (CPLANT_COL_ENTITY_CACHE_SIZE+1)));
return (u16)h;
}
private:
CPlantColBoundEntry m_tab[CPLANT_COL_ENTITY_CACHE_SIZE];
public:
u16 m_UnusedListHead;
u16 m_CloseListHead;
};
//
//
// handy wrapper around array of CPlantLocTri to be accessed with hashed 'addresses'/indices (0=NULL, everything else is hashed index to array):
//
class CPlantLocTriArray
{
public:
CPlantLocTriArray() {}
~CPlantLocTriArray() {}
public:
void Init(u16 listID);
// returns entry pointed by hashed index h:
inline CPlantLocTri& operator[](u16 h)
{
FastAssert((h > 0) && (h < (CPLANT_MAX_CACHE_LOC_TRIS_NUM+1)));
return m_tab[h-1];
}
// converts entry ptr inside m_tab into hashed index:
inline u16 GetIdx(CPlantLocTri* e)
{
const u32 h = ptrdiff_t_to_int(e - m_tab + 1);
FastAssert((h > 0) && (h < (CPLANT_MAX_CACHE_LOC_TRIS_NUM+1)));
return (u16)h;
}
private:
CPlantLocTri m_tab[CPLANT_MAX_CACHE_LOC_TRIS_NUM];
public:
u16 m_CloseListHead;
u16 m_UnusedListHead;
u16 m_listID; // 0-4
bool m_bRequireAmbScale :1; // any of tris requires TC ambient scale resample
u8 m_pad0 :7;
};
CompileTimeAssert(__64BIT || sizeof(CPlantLocTriArray) <= 64*1024); // 64KB size limit to stay friendly for 4KB streaming chunk size
//
//
// CPlantMgrBase0: 1st part of data required by the SPU Update process
//
class CPlantMgrBase0
{
public:
CPlantColBoundEntryArray m_ColEntCache;
Vector3 m_CameraPos;
};
class CPlantMgrBase : public CPlantMgrBase0
{
public:
#if __SPU
CPlantLocTriArray m_LocTrisTabSpu; // SPU only
#endif
};
// base size for SPU update stack size
#define SPU_SIZEOF_CPLANTMGRBASE (sizeof(CPlantLocTriArray)+sizeof(CPlantMgrBase))
struct ProcObjectCreationInfo;
struct ProcTriRemovalInfo;
//
//
//
//
struct spuPlantsMgrUpdateStruct
{
public:
CPlantMgrBase* m_pPlantsMgr;
Vector3 m_camPos;
Vector4 m_cullSphere[2];
u32 m_iTriProcessSkipMask;
s32 m_maxAdd;
u32 m_addBufSize;
ProcObjectCreationInfo* m_pAddList;
s32 m_maxRemove;
u32 m_removeBufSize;
ProcTriRemovalInfo* m_pRemoveList;
float m_GroundSlopeAngleMin;
struct CResultSize
{
u32 m_numAdd;
u32 m_numRemove;
} ;
CResultSize* m_pResultSize;
CPlantLocTriArray* m_LocTrisTab[CPLANT_LOC_TRIS_LIST_NUM];
CPlantLocTriArray* m_LocTrisRenderTab[CPLANT_LOC_TRIS_LIST_NUM];
#if FURGRASS_TEST_V4
CPlantColBoundEntryFurGrassInfo* m_furGrassPickupRenderInfo;
#endif
#if __BANK
Color32 m_gbDefaultGroundColor;
float m_bkTriLocFarDistSqr;
float m_bkTriLocShortFarDistSqr;
#endif
u32 m_IsNetworkGameInProgress :1; // g_procObjMan flag
u32 m_bCullSphereEnabled0 :1;
u32 m_bCullSphereEnabled1 :1;
#if __BANK
u32 m_gbForceDefaultGroundColor :1;
// g_procObjMan flags:
u32 m_ignoreMinDist :1;
u32 m_forceOneObjPerTri :1;
u32 m_ignoreSeeding :1;
u32 m_enableSeeding :1;
u32 m_disableCollisionObjects :1;
u32 m_printSpuJobTimings :1;
#if PLANTSMGR_DATA_EDITOR
u32 m_AllCollisionSelectable :1;
u32 m_pad00 :21;
#else
u32 m_pad00 :22;
#endif
#else
u32 m_pad00 :29;
#endif
};
#define CPLANT_LOC_TRI_SHADOW_CANDIDATES_SIZE (2048*2)
//
//
//
//
class CPlantMgr : public CPlantMgrBase
{
friend class CPlantLocTri;
friend class CPlantColBoundEntry;
public:
CPlantMgr();
~CPlantMgr();
public:
static void Init(unsigned initMode);
static void Shutdown(unsigned shutdownMode);
bool CheckProceduralMeta();
bool ReloadConfig();
#if __BANK
static bool InitWidgets(bkBank& bank);
static void MarkReloadConfig();
#endif
public:
bool UpdateBegin();
void UpdateEnd();
void UpdateTask(u32 listID, s32 iTriProcessSkipMask);
void AsyncMemcpyBegin();
void AsyncMemcpyEnd();
void RenderTabMemcpy(u32 listID);
#if CPLANT_USE_OCCLUSION
void UpdateOcclusion();
#endif
void UpdateStr();
static bool PreUpdateOnceForNewCameraPos(const Vector3& newCamPos);
static bool Render();
static bool RenderDecal();
#if __BANK
static bool RenderDebugForward();
#endif
static void SetForceHDGrass(bool enable);
static bool GetForceHDGrass() { return(ms_bForceHDGrass); };
private:
bool RenderInternal();
bool RenderDecalInternal();
public:
bool RenderClosedLists(const u32 bufferID, spdTransposedPlaneSet8 &cullFrustum);
private:
#if __BANK
bool RenderDebugStuff(); // This debug render function render to gbuffers after main grass render.
bool RenderDebugStuffForward(); // This debug render function render during debug render phase near end of frame.
#endif
#if PLANTS_CAST_SHADOWS
public:
static bool ShadowRender();
private:
bool ShadowRenderInternal();
void InitShadowCandidates();
void ResetShadowCandidates();
void AddShadowCandidate(CPlantLocTri *pLocTri);
void RenderShadowCandidates(spdTransposedPlaneSet8 &cullFrustum);
#endif //PLANTS_CAST_SHADOWS
#if FURGRASS_TEST_V4
public:
bool FurGrassStoreRenderInfo(CPlantColBoundEntryFurGrassInfo *dest, u32 dmaTag);
private:
bool FurGrassLODInitialise();
bool FurGrassLODRender(const Vector3& camPos, const Vector3& camFront, const Vector3& playerPos);
#endif //FURGRASS_TEST_V4...
//
//
//
private:
bool InitialiseInternal();
void ShutdownInternal();
bool PreUpdateOnceForNewCameraPosInternal(const Vector3& newCameraPos);
bool AllocateStrLocTriTabs();
bool FreeStrLocTriTabs();
bool UpdateStrLocTriTabs();
bool InitialiseTextures();
bool UpdateStrTextures();
bool ShutdownTextures();
void AdvanceCurrentScanCode() { m_scanCode++; }
u16 GetCurrentScanCode() { return(m_scanCode); }
private:
void CalculateWindBending(Vector2 &outBending);
void CalculateFakeGrassNormal(float rotationAmount);
private:
bool _ColBoundCache_Update(const Vector3& cameraPos, bool bQuickUpdate=FALSE);
bool _ColBoundCache_ProcessBound(phBound *pBound, CEntity *pEntity, s32 parentIdx, s32 childIdx, const u16 nCurrentScanCode);
CPlantColBoundEntry* _ColBoundCache_FindInCache(CEntity *pEntity, s32 parentIdx, s32 childIdx);
CPlantColBoundEntry* _ColBoundCache_Add(CEntity* pEntity, s32 parentIdx, s32 childIdx, bool bCheckCacheFirst=FALSE);
void _ColBoundCache_Remove(CEntity *pEntity, s32 parentIdx, s32 childIdx);
bool IsBoundGeomPlantFriendly(phBoundGeometry *pBound);
bool UpdateAllLocTris(CPlantLocTriArray& triTab, const Vector3& camPos, s32 iTriProcessSkipMask, u32 *pFurgrassTagPresent);
bool _ProcessEntryCollisionData(CPlantLocTriArray& triTab, CPlantColBoundEntry *pEntry, const Vector3& camPos, s32 iTriProcessSkipMask, u32 *pFurgrassTagPresent);
bool _CullDistanceCheck(const Vec3V positions[4], Vec3V_In camPos, bool bIsDrawFarTri, bool checkAllCulled);
bool _CullSphereCheck(const Vec3V positions[4]);
private:
// debug/stats functions:
bool DbgPrintCPlantMgrInfo(u32 nNumLocTrisDrawn, u32 nNumLocTrisTexSkipped, u32 nNumPlantsLOD0Drawn, u32 nNumPlantsLOD1Drawn, u32 nNumPlantsLOD2Drawn);
bool DbgCountCachedBounds(u32 *pCountAll, u32 *pCountColl, u32 *pCountMat);
bool DbgCountLocTrisAndPlants(u32 *pCountLocTris, u32 *pCountPlants, bool bCountCreatesPlants, bool bCountCreatesObjects, bool bCountFree);
void DbgDrawTriangleArrays(atArray<Vector3> &triangleVertices, atArray<Color32>& triangleColors);
void DbgDrawLineArrays(atArray<Vector3> &lineVertices, atArray<Color32>& lineColors);
#if SECTOR_TOOLS_EXPORT
public:
// Statistics Functions for Performance Stats (DHM 23 Jan 2008)
// These are similar to the other debug functions but available in Release Build
void DbgGetCPlantMgrInfo( u32* nNumLocTrisDrawn, u32* nNumPlantsDrawn );
void DbgStatsCountCachedBounds( u32* pCountAll );
void DbgStatsCountLocTrisAndPlants(u32* pCountLocTris, u32* pCountPlants );
#endif // SECTOR_TOOLS_EXPORT
//////////////////////////////////////////////////////////////////////////////////////////////////////
#if CPLANT_DYNAMIC_CULL_SPHERES
//Dynamic culling sphere interface
public:
u32 AddDynamicCullSphere(const spdSphere &s); //Returns cull sphere index which can be used as a handle on success, or PLANTSMGR_INVALID_DYNAMIC_CULL_SPHERE_INDEX on failure.
void RemoveDynamicCullSphere(u32 handle);
void ResetDynamicCullSpheres(); //Resets the dynamic cull sphere array. (All culling spheres are off)
typedef PlantMgr_DynCullSphereArray DynCullSphereArray;
const DynCullSphereArray &GetCullSphereArray(u32 id) const { return m_DynCullSpheresBuffer[id]; }
private:
void CopyCullSpheresToUpdateBuffer();
typedef atRangeArray<spdSphere, PLANTSMGR_MAX_DYNAMIC_CULL_SPHERES> DynCullSphereSourceArray;
DynCullSphereSourceArray m_DynCullSpheres;
atRangeArray<DynCullSphereArray, 2> m_DynCullSpheresBuffer;
#endif //CPLANT_DYNAMIC_CULL_SPHERES...
private:
CPlantColBoundEntry* MoveColEntToList(u16* ppCurrentList, u16* ppNewList, CPlantColBoundEntry *pEntry);
CPlantLocTri* MoveLocTriToList(CPlantLocTriArray& triTab, u16* ppCurrentList, u16* ppNewList, CPlantLocTri *pTri);
public:
// CPlantLocTriArray m_LocTrisTab[CPLANT_LOC_TRIS_LIST_NUM]; // [listID]
CPlantLocTriArray* m_LocTrisTab[CPLANT_LOC_TRIS_LIST_NUM]; // [listID]
private:
// RenderThread stuff: Render() is callled from RT and has to have all necessary data double-buffered:
// CPlantLocTriArray m_LocTrisRenderTab[2][CPLANT_LOC_TRIS_LIST_NUM];//[RT/UP][listID]
CPlantLocTriArray* m_LocTrisRenderTab[2][CPLANT_LOC_TRIS_LIST_NUM];//[RT/UP][listID]
#if PLANTS_CAST_SHADOWS
// Two buffers, one for plant rendering to write candidates to (GetUpdateRTBufferID()), and one for the shader rendering to read from (GetRenderRTBufferID()).
u32 m_noOfShadowCandidates[2];
CPlantLocTri* m_ShadowCandidates[2][CPLANT_LOC_TRI_SHADOW_CANDIDATES_SIZE];
#endif //PLANTS_CAST_SHADOWS
public:
#if FURGRASS_TEST_V4
CPlantColBoundEntryFurGrassInfo m_furGrassPickupRenderInfo[2][CPLANT_COL_ENTITY_CACHE_SIZE] ;
#endif
public:
// helper functions to pick the right buffer:
u32 GetUpdateRTBufferID() const { return(m_RTbufferID); }
u32 GetRenderRTBufferID() const { return((m_RTbufferID+1)&0x01);}
void SwapRTBuffer() { (++m_RTbufferID)&=0x01; }
private:
u32 m_RTbufferID;
// global CullSphere:
public:
void EnableCullSphere(u32 i, const Vector4& sphere) { Assert(i<CPLANT_CULLSPHERES_MAX); m_CullSphere0[i]=sphere; m_CullSphereEnabled0[i]=true; }
void DisableCullSphere(u32 i) { Assert(i<CPLANT_CULLSPHERES_MAX); m_CullSphereEnabled0[i]=false; }
bool IsCullSphereEnabled(u32 i) { Assert(i<CPLANT_CULLSPHERES_MAX); return m_CullSphereEnabled0[i]; }
private:
Vector4 m_CullSphere0[CPLANT_CULLSPHERES_MAX];
bool m_CullSphereEnabled0[CPLANT_CULLSPHERES_MAX];
private:
bool LoadPlantModel(char *fname, grassModel *pGrassModel);
void UnloadPlantModel(grassModel *model);
bool LoadPlantModelLOD2(char *fname, grassModel *pGrassModel);
bool CreateGeometryLOD2();
private:
grassModel m_PlantModelsTab0[CPLANT_SLOT_NUM_MODELS*2] ALIGNED(128); // LOD0 geometry + LOD1 geometry
grcTexture* m_PlantTextureTab0[CPLANT_SLOT_NUM_TEXTURES*2]; // LOD0 texture + LOD1 texture
private:
static Vector2 ms_AvgWindVector;
static bool ms_bForceHDGrass;
u16 m_scanCode;
bool m_bShouldRunAsyncMemcpyJobThisFrame;
bool m_bSuppressObjCreation;
bool m_bSuppressObjCreationPermanently;
public:
void DisableObjCreation() { m_bSuppressObjCreationPermanently=true; }
void EnableObjCreation() { m_bSuppressObjCreationPermanently=false; }
bool ProcObjCreationEnabled() { return !m_bSuppressObjCreationPermanently;} // true=enabled, false=disabled
private:
bool m_bEnableAmbScaleScan;
public:
void EnableAmbScaleScan() { m_bEnableAmbScaleScan=true; }
void DisableAmbScaleScan() { m_bEnableAmbScaleScan=false; }
bool IsAmbScaleScanEnabled() { return m_bEnableAmbScaleScan; }
private:
#if FURGRASS_TEST_V4
u32 m_nFurgrassTagPresentTab[CPLANT_LOC_TRIS_LIST_NUM]; // output for every subupdate task
bool m_bFurgrassDoRender[2]; // accumulated results from UP
bool m_bFurgrassTrisRtPresent[2]; // results from RT's Furgrass::Render()
public:
inline bool DoFurgrassRender() const { FastAssert(CSystem::IsThisThreadId(SYS_THREAD_RENDER)); return m_bFurgrassDoRender[GetRenderRTBufferID()];};
inline bool GetFurgrassTrisRtPresent() const { FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE)); return m_bFurgrassTrisRtPresent[GetUpdateRTBufferID()];};
private:
#endif //FURGRASS_TEST_V4...
#if PLANTSMGR_DATA_EDITOR
public:
inline void SetAllCollisionSelectable(bool b) { FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE)); m_bAllCollisionSelectable[GetUpdateRTBufferID()]=b; };
inline bool GetAllCollisionSelectableUT() const { FastAssert(!CSystem::IsThisThreadId(SYS_THREAD_RENDER)); return m_bAllCollisionSelectable[GetUpdateRTBufferID()];};
inline bool GetAllCollisionSelectable() const { FastAssert(CSystem::IsThisThreadId(SYS_THREAD_RENDER)); return m_bAllCollisionSelectable[GetRenderRTBufferID()];};
inline void ForceRegenTriCaches(bool b) { m_bRegenTriCaches=b; };
inline bool GetRegenTriCaches() const { return(m_bRegenTriCaches); };
private:
bool m_bAllCollisionSelectable[2];
bool m_bRegenTriCaches;
#endif //PLANTSMGR_DATA_EDITOR...
#if __BANK
static float ms_bkColTestRadius;
static float ms_bkTriLocFarDist, ms_bkTriLocFarDistSqr;
static float ms_bkTriLocShortFarDist, ms_bkTriLocShortFarDistSqr;
static bool ms_bkbWindResponseEnabled;
static float ms_bkfWindResponseAmount;
static float ms_bkfWindResponseVelMin;
static float ms_bkfWindResponseVelMax;
static float ms_bkfWindResponseVelExp;
#endif //__BANK...
};
extern CPlantMgr gPlantMgr;
// wrapper class for interfacing the plants manager with the game skeleton code
class CPlantMgrWrapper
{
public:
static void UpdateBegin();
static void UpdateEnd();
#if CPLANT_USE_OCCLUSION
static void UpdateOcclusion();
#endif
static void UpdateSafeMode();
};
#if PLANTSMGR_DATA_EDITOR
namespace rage {
class bkData;
class bkCombo;
}
class CPlantMgrDataEditor
{
public:
CPlantMgrDataEditor ();
bool InitWidgets(bkBank& bank, const char *bankName);
void Update();
void UpdateSafe();
void Render();
static void cbBankToggleAllowPicking();
static void cbBankToggleShowPlantPolys();
static void cbBankToggleSelectAllCollision();
static void cbBankButtonCopyPlayerPosToSphereCenter();
static void cbBankButtonSearchSphere();
static void cbBankButtonClearSelEntries();
static void cbBankTextEditEntryIdxChanged();
static void cbBankButtonTypeNameChanged();
static void cbBankButtonSetProcTypeToAll();
static void cbBankButtonSaveCurEntry();
static void cbBankButtonSaveBatch();
static void cbBankToggleFindClosestEntriesFromFile();
static void cbBankToggleWriteSelectionToFile();
private:
int FindClosestEntry(const Vector3& p0, const Vector3& p1, bool bMultiSelect);
bool FindClosestEntries(const Vector3& p0, float radius, bool bKeepSelection = false);
void Reset(bool bKeepSelection = false);
void ResetEditableEntry();
void UpdateEditableEntry();
void SaveEditableEntryTo(CPlantLocTri* pLocTri);
void AddEditableDataToWidget(bkBank& bank);
bool ReadInputDataFile();
bool WriteOutputDataFile();
private:
atArray<CPlantLocTri*> m_selectedEntries;
public:
inline bool IsAllCollisionSelectable() const { return m_bEdAllCollisionSelectable; }
inline void SetRegenTriCache(bool b) { m_bEdRegenTriCache=b; }
inline bool GetRegenTriCache() const { return m_bEdRegenTriCache; }
private:
Vector3 m_searchPos;
float m_searchRadius;
int m_searchEntriesFound;
bool m_bAllowPicking;
bool m_bShowPlantPolys;
bool m_bEdAllCollisionSelectable;
bool m_bEdRegenTriCache;
bool m_bSphereSearchPending;
bool m_bClearSelectedEntries;
bool m_bUpdateCurEditEntry;
bool m_bSaveCurEditEntry;
bool m_bSetProcTypeToAll;
bool m_bMaxUiSphereSearchFromFilePending;
bool m_bMaxUiWritingSelectionToFile;
bool m_bMaxUiSavingChangesToBatch;
int m_editEntryIdx;
atArray<Vector3> m_triPositions;
atArray<const char*> m_tagNames;
bkCombo* m_tagNamesCombo;
struct EditableData
{
Vector3 m_V[3];
Color32 m_GroundColorV[3];
u32 m_GroundDensity[3];
u32 m_GroundScaleZ[3];
u32 m_GroundScaleXYZ[3];
s32 m_nSurfaceType;
};
EditableData m_editableEntry;
};
extern CPlantMgrDataEditor gPlantMgrEditor;
#endif //PLANTSMGR_DATA_EDITOR...
//
//
//
//
inline Vector3 CPlantLocTri::GetCenter() const
{
static Vector4 one_third(1.0f/3.0f,1.0f/3.0f,1.0f/3.0f,1.0f/3.0f);
Vector4 r = ( m_V1 + m_V2 + m_V3 ) * one_third;
return r.GetVector3();
}
//
// packs density, scaleZ and scaleXYZ weights into 8 bits:
// bit layout:
// 0-3: scaleXYZ
// 4-5: scaleZ
// 6-7: density
//
inline u8 CPlantLocTri::pv8PackDensityScaleZScaleXYZ(u8 density, u8 scaleZ, u8 scaleXYZ)
{
density &= 0x03; // 2 bits
scaleZ &= 0x03; // 2 bits
scaleXYZ&= 0x0f; // 4 bits
return (density << 6) | (scaleZ << 4) | (scaleXYZ);
}
// unpacks ScaleXYZ weight <0; 15>:
inline u8 CPlantLocTri::pv8UnpackScaleXYZ(u8 densityAndScale8)
{
return (densityAndScale8 & 0x0f);
}
// unpacks ScaleZ weight <0; 3>:
inline u8 CPlantLocTri::pv8UnpackScaleZ(u8 densityAndScale8)
{
return ((densityAndScale8 >> 4) & 0x03);
}
// unpacks Density weight <0; 3>:
inline u8 CPlantLocTri::pv8UnpackDensity(u8 densityAndScale8)
{
return ((densityAndScale8 >> 6) & 0x03);
}
// maps scaleXYZ weight <0; 15> to <0; 1>:
inline float CPlantLocTri::pv8MapScaleXYZ(u8 scaleXYZ)
{
scaleXYZ &= 0x0f;
return float(scaleXYZ)/15.0f;
}
#define CPLANTLOCTRI_PV_MAP_ARRAYS float CPlantLocTri::ms_pvMapScaleZ[4] = {1.0f, 2.0f/3.0f, 1.0f/3.0f, 0.0f}; \
float CPlantLocTri::ms_pvMapDensity[4] = {1.0f, 2.0f/3.0f, 1.0f/3.0f, 0.0f};
// maps scaleZ weight <0; 3>:
inline float CPlantLocTri::pv8MapScaleZ(u8 scaleZ)
{
return ms_pvMapScaleZ[scaleZ&0x03];
}
// maps density weight <0; 3>:
inline float CPlantLocTri::pv8MapDensity(u8 density)
{
return ms_pvMapDensity[density&0x03];
}
// unpack and map combo:
inline float CPlantLocTri::pv8UnpackAndMapScaleXYZ(u8 densityAndScale8)
{
return pv8MapScaleXYZ( pv8UnpackScaleXYZ(densityAndScale8) );
}
inline float CPlantLocTri::pv8UnpackAndMapScaleZ(u8 densityAndScale8)
{
return pv8MapScaleZ( pv8UnpackScaleZ(densityAndScale8) );
}
inline float CPlantLocTri::pv8UnpackAndMapDensity(u8 densityAndScale8)
{
return pv8MapDensity( pv8UnpackDensity(densityAndScale8) );
}
__forceinline void* PlantStrAlloc(size_t size, size_t align)
{
MEM_USE_USERDATA(MEMUSERDATA_PLANTMGR);
return strStreamingEngine::GetAllocator().Allocate((size+(g_rscVirtualLeafSize-1))&(~(g_rscVirtualLeafSize-1)), align, MEMTYPE_RESOURCE_VIRTUAL);
}
__forceinline void PlantStrFree(void *ptr)
{
MEM_USE_USERDATA(MEMUSERDATA_PLANTMGR);
strStreamingEngine::GetAllocator().Free(ptr);
}
// helper macros to allocate memory from streaming heap
// (str allocator uses leafsize=g_rscVirtualLeafSize (ps3: 4096; 360: 8192):
#define PLANTS_MALLOC_STR(SIZE,ALIGN) PlantStrAlloc((SIZE), (ALIGN))
#define PLANTS_FREE_STR(P) PlantStrFree(P)
#endif //__CPLANTSMGR_H__...