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

9168 lines
290 KiB
C++

//
//
// CPlantManager - management of plants seeded (generated) around camera;
// - global manager responsible for processing & drawing plants all over game map;
//
//
// 24/06/2005 - Andrzej: - initial conversion to Rage from SA code;
// 14/07/2005 - Andrzej: - port to Rage fully working;
// 05/10/2005 - Andrzej: - support for collision quads (4 verts) added
// (so far only collision tris (3 verts) were supported);
// 18/10/2006 - Andrzej: - HDR multiply added;
// 28/02/2007 - Andrzej: - ColBoundCache: removed references to phBounds, everything is taken from CEntity now;
// 24/05/2007 - Andrzej: - support for SPU renderer added;
// 10/10/2007 - Matt: - support for SPU update added;
// 28/02/2008 - Andrzej: - CPLANTMGR_ENABLED: if 0, then disables loading of models & textures and Render()
// Update() still works (as its results are required for procedural objects);
// ---------- - -------- ----------------------------------------------------------------------------------------
// 08/07/2008 - Andrzej: - PlantsMgr 2.0: code cleanup, refactor;
// 16/07/2008 - Andrzej: - PlantsMgr 2.0: micro-movements support added (local micro-movements scales are encoded in vertex colors);
// 22/10/2008 - Andrzej: - PlantsMgr 2.0: local per-vartex variation data support (ground color+scaleXY/Z) added;
// 28/10/2008 - Andrzej: - PlantsMgr 2.0: LOD0 and LOD1 support added;
// 06/11/2008 - Andrzej: - PlantsMgr 2.0: PSN: added two-pass rendering into GBuffer to allow to use hardware AlphaToMask() in GBuf0;
// 14/11/2008 - Andrzej: - PlantsMgr 2.0: PSN: revitalised old SPU renderloop;
// 26/11/2008 - Andrzej: - PlantsMgr 2.0: SpuRenderloop 2.0 and code cleanup (grassModelSPU4 removed, etc.);
// 16/12/2008 - Andrzej: - PlantsMgr 2.0: ground slope skewing added;
// 04/02/2011 - Andrzej: - PlantsMgr 2.0: SPU render buffers allocated from streaming heap;
// 10/02/2011 - Andrzej: - Fur grass v4;
// 14/04/2011 - Andrzej: - PlantsMgr 2.0: Update buffers allocated from streaming heap;
// 18/07/2011 - Andrzej: - PlantsMgr 2.0: new layout of ground per-vertex Density/ScaleZ/ScaleXYZ;
// ----------
// 22/10/2013 - Andrzej: - PlantsMgr 3.0: MATERIAL type collision for NG;
//
//
//
//
// Rage headers:
#include "grcore/debugdraw.h"
#include "fwmaths/vector.h"
#include "physics/levelnew.h"
#include "physics/inst.h"
#include "phbound/bound.h"
#include "phbound/boundgeom.h"
#include "phbound/boundbox.h"
#include "grmodel/geometry.h"
#include "grcore/allocscope.h"
#include "grcore/indexbuffer.h"
#include "grcore/vertexbuffer.h"
#include "grcore/vertexbuffereditor.h"
#include "grmodel/modelfactory.h"
#include "pheffects/wind.h"
#include "profile/timebars.h"
#include "grprofile/pix.h"
#include "system/xtl.h"
#include "fwscene/scan/scan.h"
#include "fwscene/stores/txdstore.h"
#include "system/findsize.h"
#include "system/dependencyscheduler.h"
#include "streaming/packfilemanager.h"
#include "vector/geometry.h"
// gta headers:
#include "camera/caminterface.h"
#include "camera/viewports/ViewportManager.h"
#include "camera/base/BaseCamera.h"
#include "camera/gameplay/GameplayDirector.h"
#include "camera/gameplay/aim/FirstPersonAimCamera.h"
#include "camera/cinematic/CinematicDirector.h"
#include "core/game.h"
#include "scene/world/gameworld.h"
#include "physics/gtamaterialmanager.h"
#include "peds/PopCycle.h" // GetCurrentZonePlantsMgrTxdIdx()
#include "physics/iterator.h"
#include "physics/physics.h"
#include "debug/debug.h"
#include "debug/TiledScreenCapture.h"
#include "peds/ped.h" // findplayerped()...
#include "objects/procobjects.h"
#include "game/weather.h" // g_weather.wind()...
#include "game/ModelIndices.h"
#include "scene/ContinuityMgr.h"
#include "scene/InstancePriority.h"
#include "scene/refmacros.h"
#include "shaders/shaderlib.h"
#include "system/taskscheduler.h"
#include "renderer/deferred/deferredlighting.h"
#include "renderer/deferred/gbuffer.h"
#include "renderer/plantsgrassrenderer.h"
#include "renderer/Water.h"
#include "vehicles/vehicle.h"
#include "vehicles/Submarine.h"
#include "game/clock.h"
#include "renderer/lights/lights.h"
#include "renderer/sprite2d.h"
#include "renderer/gtadrawable.h"
#include "streaming/streamingrequest.h"
#include "renderer/PlantsMgr.h"
#include "system/SettingsManager.h"
#include "../shader_source/Vegetation/Grass/grass_regs.h"
#if __BANK
#include "audio/northaudioengine.h"
#endif
CPLANTLOCTRI_PV_MAP_ARRAYS // CPlantLocTri::ms_pvMap/DensityScaleZ
#include "streaming/streaming.h"
#include "debug/DebugScene.h"
#if !__SPU
RAGE_DEFINE_SUBCHANNEL(render, plants)
#endif
#if PSN_PLANTSMGR_SPU_RENDER
#include "PlantsGrassRendererSPU.h" // grassModelSPU
#endif //PSN_PLANTSMGR_SPU_RENDER...
#define PLANTS_TXD_STR (__PS3 || __XENON) // enable dynamic streaming of plant txds
#define LOCTRISTAB_STR (1) // enable dynamic allocation of work buffers from str memory
RENDER_OPTIMISATIONS();
#define PSN_ALPHATOMASK_PASS (1 && __PPU && !CPLANT_WRITE_GRASS_NORMAL)
#define EXTRA_1_5_PASS (0) // experimental: does extra fullscreen passes to eliminate selfshadowing
#define XENON_FILL_PASS (1 && __XENON)
#define USE_SSA_STIPPLE_ALPHA (0)
#define WRITE_SELF_SHADOW_TERM (1)
// RDR3: Temp Fix for a GCM fault. Seems grass rendering might leave GPU in bad state/cascaded shadows don't properly reset GPU state. This resets GPU state to avoid a crash.
#define PSN_CLEANUP_PASS (HACK_RDR3 && __PPU && !PSN_ALPHATOMASK_PASS)
#define PLANT_DEFAULT_WIND_RESPONSE_AMOUNT (0.750f)
#define PLANT_DEFAULT_WIND_RESPONSE_VEL_MIN (1.500f)
#define PLANT_DEFAULT_WIND_RESPONSE_VEL_MAX (22.000f)
#define PLANT_DEFAULT_WIND_RESPONSE_VEL_EXP (0.0f)
static sysDependency g_PlantMgrMemcpyDependency[CPLANT_LOC_TRIS_LIST_NUM];
static u32 g_PlantMgrMemcpyDependenciesRunning;
static BankFloat g_AvgPlantWindInfluence = 0.970f;
#define OBJ_CREATION_SPEED_LIMIT (25.0f)
// per-vertex ground color (RGB) + density/scaleZ/scaleXYZ weights(A)
const Color32 DEFAULT_GROUND_COLOR = Color32(111, 110, 92, CPLANT_PV_DEFAULT_D_SZ_SXYZ);
// CPLANT_BLIT_MIN_GBUFFER and CPLANT_STORE_LOCTRI_NORMAL may not be enabled at the same time
CompileTimeAssert( !(CPLANT_BLIT_MIN_GBUFFER && CPLANT_STORE_LOCTRI_NORMAL) );
PARAM(DisablePlantMgr, "Disable Plant Manager");
#if __BANK
#include "file/asset.h"
#include "bank/Bank.h"
#include "bank/BkMgr.h"
bool gbDisplayCPlantMgrInfo = FALSE;
static bool gbShowCPlantMgrPolys = FALSE;
static bool gbShowCPlantMgrPolysFar = FALSE;
static bool gbShowCPlantMgrPolys_Plants = TRUE;
static bool gbShowCPlantMgrPolys_ProcObj= TRUE;
static bool gbShowCPlantMgrProcMatType = FALSE;
static bool gbShowCPlantMgrAmbScale = FALSE;
bool gbShowAlphaOverdraw = FALSE;
static bool gbShowCPlantMgrPolyNormals = FALSE;
static bool gbShowCPlantMgrGroundColor = FALSE;
static u32 gbShowCPlantMgrGroundCSD = 0; // 0=color, 1=scaleXYZ, 2=scaleZ, 3=density
static bool gbForceDefaultGroundColor = FALSE;
static Color32 gbDefaultGroundColor = DEFAULT_GROUND_COLOR;
static bool gbForceGroundColor = FALSE;
static bool gbChangeGroundColorsAllAsOne= FALSE;
static Color32 gbGroundColorV1(255,0, 0, 255);
static Color32 gbGroundColorV2(0, 255,0, 255);
static Color32 gbGroundColorV3(0, 0, 255,255);
static u32 gbGroundScaleXYZ_V1=0x0f, gbGroundScaleXYZ_V2=0x0f, gbGroundScaleXYZ_V3=0x0f;
static u32 gbGroundScaleZ_V1=0x00, gbGroundScaleZ_V2=0x00, gbGroundScaleZ_V3=0x00;
static u32 gbGroundDensity_V1=0x00, gbGroundDensity_V2=0x00, gbGroundDensity_V3=0x00;
#if PSN_PLANTSMGR_SPU_UPDATE
static bool gbPrintUpdateSpuJobTimings = FALSE;
#endif
static void cbBankChangeGroundColorV1()
{
if(gbChangeGroundColorsAllAsOne)
{
gbGroundColorV2 = gbGroundColorV3 = gbGroundColorV1;
}
}
static void cbBankChangeGroundColorV2()
{
if(gbChangeGroundColorsAllAsOne)
{
gbGroundColorV1 = gbGroundColorV3 = gbGroundColorV2;
}
}
static void cbBankChangeGroundColorV3()
{
if(gbChangeGroundColorsAllAsOne)
{
gbGroundColorV1 = gbGroundColorV2 = gbGroundColorV3;
}
}
bool gbPlantsFlashLOD0 = FALSE;
bool gbPlantsFlashLOD1 = FALSE;
bool gbPlantsFlashLOD2 = FALSE;
bool gbPlantsDisableLOD0 = FALSE;
bool gbPlantsDisableLOD1 = FALSE;
bool gbPlantsDisableLOD2 = FALSE;
// loctris far distance:
float gbPlantsLocTriFarDist = CPLANT_TRILOC_FAR_DIST_INITVAL;
float CPlantMgr::ms_bkColTestRadius = CPLANTMGR_COL_TEST_RADIUS_INITVAL;
float CPlantMgr::ms_bkTriLocFarDist = CPLANT_TRILOC_FAR_DIST_INITVAL;
float CPlantMgr::ms_bkTriLocFarDistSqr = CPLANT_TRILOC_FAR_DIST_INITVAL*CPLANT_TRILOC_FAR_DIST_INITVAL;
float CPlantMgr::ms_bkTriLocShortFarDist = CPLANT_TRILOC_SHORT_FAR_DIST_INITVAL;
float CPlantMgr::ms_bkTriLocShortFarDistSqr=CPLANT_TRILOC_SHORT_FAR_DIST_INITVAL*CPLANT_TRILOC_SHORT_FAR_DIST_INITVAL;
bool CPlantMgr::ms_bkbWindResponseEnabled= true;
float CPlantMgr::ms_bkfWindResponseAmount = PLANT_DEFAULT_WIND_RESPONSE_AMOUNT;
float CPlantMgr::ms_bkfWindResponseVelMin = PLANT_DEFAULT_WIND_RESPONSE_VEL_MIN;
float CPlantMgr::ms_bkfWindResponseVelMax = PLANT_DEFAULT_WIND_RESPONSE_VEL_MAX;
float CPlantMgr::ms_bkfWindResponseVelExp = PLANT_DEFAULT_WIND_RESPONSE_VEL_EXP;
#endif // __BANK
Vector2 CPlantMgr::ms_AvgWindVector = Vector2(0.0f, 0.0f);
bool CPlantMgr::ms_bForceHDGrass = false;
#if __BANK || PLANTS_USE_LOD_SETTINGS
// LOD0:
float gbPlantsLOD0AlphaCloseDist = CPLANT_LOD0_ALPHA_CLOSE_DIST;
float gbPlantsLOD0AlphaFarDist = CPLANT_LOD0_ALPHA_FAR_DIST;
float gbPlantsLOD0FarDist = CPLANT_LOD0_FAR_DIST;
// LOD1:
float gbPlantsLOD1CloseDist = CPLANT_LOD1_CLOSE_DIST;
float gbPlantsLOD1FarDist = CPLANT_LOD1_FAR_DIST;
float gbPlantsLOD1Alpha0CloseDist = CPLANT_LOD1_ALPHA0_CLOSE_DIST;
float gbPlantsLOD1Alpha0FarDist = CPLANT_LOD1_ALPHA0_FAR_DIST;
float gbPlantsLOD1Alpha1CloseDist = CPLANT_LOD1_ALPHA1_CLOSE_DIST;
float gbPlantsLOD1Alpha1FarDist = CPLANT_LOD1_ALPHA1_FAR_DIST;
// LOD2:
float gbPlantsLOD2CloseDist = CPLANT_LOD2_CLOSE_DIST;
float gbPlantsLOD2FarDist = CPLANT_LOD2_FAR_DIST;
float gbPlantsLOD2Alpha0CloseDist = CPLANT_LOD2_ALPHA0_CLOSE_DIST;
float gbPlantsLOD2Alpha0FarDist = CPLANT_LOD2_ALPHA0_FAR_DIST;
float gbPlantsLOD2Alpha1CloseDist = CPLANT_LOD2_ALPHA1_CLOSE_DIST;
float gbPlantsLOD2Alpha1FarDist = CPLANT_LOD2_ALPHA1_FAR_DIST;
float gbPlantsLOD2Alpha1CloseDistFar = CPLANT_LOD2_ALPHA1_CLOSE_DIST_FAR;
float gbPlantsLOD2Alpha1FarDistFar = CPLANT_LOD2_ALPHA1_FAR_DIST_FAR;
float gbPlantsGroundSlopeAngleMin = CPLANT_GROUND_SLOPE_ANGLE_MIN;
bool gbPlantsDarknessTimecycle = TRUE;
float gbPlantsDarknessAmountAdd = 0.0f;
float gbPlantsDarknessAmountPrint = 0.0f;
BANK_ONLY(static bool gbDoubleSidedCulling = FALSE);
bool gbPlantsGeometryCulling = TRUE;
bool gbPlantsPremultiplyNdotL= TRUE;
#if CPLANT_USE_OCCLUSION
static bool gbPlantsUseOcclusion = TRUE;
static float gfPlantsOccCubeHeight = 2.0f;
static bool gbPlantsOccRecalcAABBs = FALSE;
static s32 gnPlantsOccSkipTest = 0; // 0=test all, 1=test 50%, 2=test 33%, 3=test 25%
#endif
BANK_ONLY(static bool bRenderInternalActive = FALSE);
#if FURGRASS_TEST_V4
BANK_ONLY(static u32 gnFurGrassNumPolys = 0); // debug stats only
#endif
void SetupPlantLods(CGrassRenderer_PlantLod plantLod);
#endif //__BANK...
#if FURGRASS_TEST_V4
#define FURGRASS_USE_ALPHA_STIPPLE (1) // use alpha stipple for fading
static bank_float gfFurGrassMaxDist = 30.0f;
static bank_float gfFurGrassAlphaFadeClose = 10.0f;
static bank_float gfFurGrassAlphaFadeFar = 25.0f;
static bank_float gfFurGrassFresnel = 0.960f;
static bank_float gfFurGrassSpecFalloff = 32.0f;
static bank_float gfFurGrassSpecInt = 0.005f;
static bank_float gfFurGrassBumpiness = 2.0000f;
static bank_bool gbFurGrassSeparateTextures = true;
static bank_s32 gnFurGrassSeparateTexturesTech = 0;
static bank_bool gbEnableFurGrassMipmapLodBias = false;
static bank_float gfFurGrassMipmapLodBias = -1.0f;
static bank_float gfFurGrassUvScale = 0.300f;
static bank_float gfFurGrassUvScale2 = 0.300f;
static bank_float gfFurGrassUvScale3 = 0.300f;
static bank_float gfFurGrassOffsetU = 0.0f;
static bank_float gfFurGrassOffsetV = 0.0f;
static bank_u32 gnFurGrassUmScaleX = 100;
static bank_u32 gnFurGrassUmScaleY = 100;
static bank_u32 gnFurGrassFurStep = 80;
static bank_float gfFurGrassZOffset = 0.001f;
static bank_u32 gnFurGrassNumLayers = 8;
static bank_u32 gnFurGrassAlphaClip[FURGRASS_MAX_LAYERS] = {
0, 10, 10, 15,
20, 25, 30, 34
};
static bank_u8 gnFurGrassShadow[FURGRASS_MAX_LAYERS] = {
90, 118, 148, 168,
200, 220, 238, 255
};
#endif //FURGRASS_TEST_V4...
bool gbPlantMgrActive = TRUE;
#if !__FINAL
bool gbPlantMgrRenderActive = TRUE;
#endif
#if CPLANT_CLIP_EDGE_VERT
//Plant clipping
static bank_bool gbPlantMgrClipEnable = false; //Set this to true to enable edge/vert clipping. (Paying cost either way)
static bank_bool gbPlantMgrEdgeClipEnable = true;
static bank_bool gbPlantMgrVertClipEnable = true;
#endif //CPLANT_CLIP_EDGE_VERT...
#if CPLANT_DYNAMIC_CULL_SPHERES
//Dynamic Cull Spheres
static bank_bool gbPlantMgrDynCullSpheresEnable = true;
#if __BANK
static bank_bool gbPlantMgrDCSDebugDraw = false;
static bank_bool gbPlantMgrDCSDebugDrawSolid = false;
static Color32 gPlantMgrDCSDebugDrawColor(0xFFFF0000);
static Vec4V gPlantMgrDCSDbg_Sphere(0.0f, 0.0f, 0.0f, 2.0f); //(V_ZERO);
static u32 gPlantMgrDCSDbg_Handle = 0;
void PlantMgrDCSDbg_AddSphere()
{
u32 handle = gPlantMgr.AddDynamicCullSphere(spdSphere(gPlantMgrDCSDbg_Sphere));
if(handle != PLANTSMGR_INVALID_DYNAMIC_CULL_SPHERE_INDEX)
gPlantMgrDCSDbg_Handle = handle;
}
void PlantMgrDCSDbg_RemoveSphere()
{
gPlantMgr.RemoveDynamicCullSphere(gPlantMgrDCSDbg_Handle);
}
void PlantMgrDCSDbg_CenterSphereOnPlayerPed()
{
if(CPed *pPed = FindPlayerPed())
{
gPlantMgrDCSDbg_Sphere.SetXYZ(pPed->GetTransform().GetPosition());
}
}
#endif //__BANK...
#endif //CPLANT_DYNAMIC_CULL_SPHERES...
#if __BANK
#define PlantsMinGroundAngleSlope ( DtoR * gbPlantsGroundSlopeAngleMin)
#else
const float PlantsMinGroundAngleSlope = ( DtoR * CPLANT_GROUND_SLOPE_ANGLE_MIN);
#endif
//
//
// slot name in TxdStore for CPlantMgr:
//
#define CPLANTMGR_TXDSTORE_SLOTNAME ("plantsmgr")
#define CPLANTMGR_MODELSTORE_SLOTNAME ("plantsmgr")
//
// location of CPlantMgr texture dictionary:
//
static const strStreamingObjectName CPLANTMGR_TXD_PATH("platform:/textures/plantsmgr",0xA9628AAC);
#if PLANTS_TXD_STR
static const strStreamingObjectName CPLANTMGR1_TXD_PATH("platform:/textures/plantsmgr1",0xCE7C7227);
static const strStreamingObjectName CPLANTMGR2_TXD_PATH("platform:/textures/plantsmgr2",0x6A82AA35);
#endif
static const strStreamingObjectName CPLANTMGR_MODELS_PATH("platform:/models/plantsmgr",0xBA15DA67);
static fwTxd* gpPlantMgrTextureDictionary = NULL;
static Dwd* gpPlantMgrModelDictionary = NULL;
#if PLANTS_TXD_STR
static bool gbPlantsMgrTxdActive = false;
#endif
#if __BANK
static u32 nLocTriDrawn =0;
static u32 nLocTriTexSkipped=0; // # of tris skipped because of not valid textures
u32 nLocTriPlantsLOD0Drawn =0;
u32 nLocTriPlantsLOD1Drawn =0;
u32 nLocTriPlantsLOD2Drawn =0;
u32 *pLocTriPlantsLOD0DrawnPerSlot = &nLocTriPlantsLOD0Drawn;
u32 *pLocTriPlantsLOD1DrawnPerSlot = &nLocTriPlantsLOD1Drawn;
u32 *pLocTriPlantsLOD2DrawnPerSlot = &nLocTriPlantsLOD2Drawn;
#if CPLANT_USE_OCCLUSION
static u32 nLocTrisOccluded = 0;
#endif
#endif //__BANK
#if __PS3
CompileTimeAssert(sizeof(CPlantLocTri)==PSN_SIZEOF_CPLANTLOCTRI);
//FindSize(CPlantLocTri);
#endif
//FindSize(phBoundGeometry);
//FindSize(CPlantColBoundEntry);
//FindSize(CPlantMgrBase0);
//FindSize(CPlantMgrBase);
//FindSize(CPlantInfo);
//FindSize(ProcObjData_t);
CompileTimeAssert(CPLANT_MAX_POLY_POINTS==3); // only tris are supported
// private random generator for PlantsMgr (it's used during RenderThread)
// (g_DrawRand is used by stuff in UpdateThread and can't be shared)
mthRandom g_PlantsRendRand[NUMBER_OF_RENDER_THREADS];
//
//
//
//
CPlantMgr gPlantMgr;
#include "renderer/PlantsMgrUpdateCommon.h"
#if PLANTS_USE_LOD_SETTINGS
PARAM(grassandplants,"Detail level for the plants rendering (0=low, 1=medium, 2=high)");
#endif // __WIN32
#if PLANTSMGR_DATA_EDITOR
CPlantMgrDataEditor gPlantMgrEditor;
#endif
//
//
//
//
bool PlantsMgrAsyncMemcpyJob(const sysDependency& dep)
{
const int listID = dep.m_Params[0].m_AsInt;
u32* pDependenciesRunning = static_cast<u32*>(dep.m_Params[1].m_AsPtr);
gPlantMgr.RenderTabMemcpy(listID);
sysInterlockedDecrement(pDependenciesRunning);
return(true);
}
//
//
//
//
CPlantMgr::CPlantMgr()
{
m_scanCode = 0;
m_bShouldRunAsyncMemcpyJobThisFrame = false;
m_bSuppressObjCreation = false;
m_bSuppressObjCreationPermanently = false;
m_bEnableAmbScaleScan = true;
#if FURGRASS_TEST_V4
m_bFurgrassDoRender[0] =
m_bFurgrassDoRender[1] = false;
m_bFurgrassTrisRtPresent[0] =
m_bFurgrassTrisRtPresent[1] = true;
#endif
for(s32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
m_LocTrisTab[i] =
m_LocTrisRenderTab[0][i] =
m_LocTrisRenderTab[1][i] = NULL;
}
sysMemSet(m_PlantTextureTab0, 0, sizeof(m_PlantTextureTab0));
sysMemSet(m_PlantModelsTab0, 0, sizeof(m_PlantModelsTab0));
m_RTbufferID = 0;
for(u32 i=0; i<CPLANT_CULLSPHERES_MAX; i++)
{
m_CullSphereEnabled0[i] = false;
}
#if PLANTSMGR_DATA_EDITOR
m_bAllCollisionSelectable[0] = m_bAllCollisionSelectable[1] = false;
m_bRegenTriCaches = false;
#endif
}// CPlantMgr::CPlantMgr()...
//
//
//
//
CPlantMgr::~CPlantMgr()
{
// do nothing
}
//
//
//
//
static
grcTexture* LoadTexture(char *texname)
{
grcTexture *pTexture = gpPlantMgrTextureDictionary->Lookup(texname);
if(!pTexture)
{
plantsDisplayf("Cannot find plant texture '%s'!", texname);
return(NULL);
}
pTexture->AddRef();
#if __ASSERT
// check plant texture dimensions:
const s32 texWidth = pTexture->GetWidth();
const s32 texHeight= pTexture->GetHeight();
if( (texWidth==8) || (texWidth==16) || (texWidth==32) || (texWidth==64) || (texWidth==128) || (texWidth==256) || (texWidth==512) ||
(texHeight==8) || (texHeight==16) || (texHeight==32) || (texHeight==64) || (texHeight==128)|| (texHeight==256)|| (texHeight==512)
)
{
// do nothing - dimensions are OK
}
else
{
plantsAssertf(FALSE, "texture '%s': %dx%d\n", texname, texWidth, texHeight);
}
if((texWidth > 512) || (texHeight > 512))
plantsAssertf(texWidth <= 512 && texHeight <= 512, "texture '%s': %dx%d\n", texname, texWidth, texHeight);
#endif //__ASSERT...
return(pTexture);
}
//
//
// texture stuff:
//
bool CPlantMgr::InitialiseTextures()
{
#if PLANTS_TXD_STR
return(true); // do nothing
#else //PLANTS_TXD_STR
strLocalIndex txdSlot = strLocalIndex(g_TxdStore.FindSlot(CPLANTMGR_TXD_PATH));
CStreaming::LoadObject(txdSlot, g_TxdStore.GetStreamingModuleId());
g_TxdStore.AddRef(txdSlot, REF_OTHER);
gpPlantMgrTextureDictionary = g_TxdStore.Get(txdSlot);
Assert(gpPlantMgrTextureDictionary);
for(s32 i=0; i<CPLANT_SLOT_NUM_TEXTURES; i++)
{
char txname[128];
sprintf(txname, "txgrass%02d", i); // "txgrass00"
m_PlantTextureTab0[0+i] = LoadTexture(txname);
sprintf(txname, "txgrass%02d_lod1", i); // "txgrass00_lod1"
m_PlantTextureTab0[CPLANT_SLOT_NUM_TEXTURES+i] = LoadTexture(txname);
}
#if PSN_PLANTSMGR_SPU_RENDER
// setup texture commands for SPU (all base and LOD textures):
for(s32 i=0; i<(CPLANT_SLOT_NUM_TEXTURES*2); i++)
{
CGrassRenderer::SpuRecordGrassTexture(i, m_PlantTextureTab0[i]);
}
#endif
return(true);
#endif //!PLANTS_TXD_STR...
}// end of InitialiseTextures()...
#if PLANTS_TXD_STR
static strRequest gpPlantMgrTxdStrReq[2];
#define PLANTS_TXD_MAX (8)
static
const strStreamingObjectName* sm_PlantsMgrTxdPaths[PLANTS_TXD_MAX] = {
NULL, // 0 = none
&CPLANTMGR1_TXD_PATH, // 1 = countryside
&CPLANTMGR2_TXD_PATH, // 2 = ocean
NULL, // 3 = n/a
NULL, // 4 = n/a
NULL, // 5 = n/a
NULL, // 6 = n/a
&CPLANTMGR_TXD_PATH // 7 = default plantsmgr
};
#endif //PLANTS_TXD_STR...
//
//
//
//
bool CPlantMgr::UpdateStrTextures()
{
#if PLANTS_TXD_STR
enum // simple txd state machine:
{
PLANTS_TXD_LOADED = 0,
PLANTS_TXD_REQUESTED
};
static u8 currentState = PLANTS_TXD_LOADED;
static u8 currentTxd = 0;
static u8 requestedTxd = 0;
static bool currentStrReq = false; // 0 or 1
switch(currentState)
{
case(PLANTS_TXD_REQUESTED):
{
Assert(gpPlantMgrTxdStrReq[currentStrReq].IsValid());
if(gpPlantMgrTxdStrReq[currentStrReq].IsValid())
{
if(gpPlantMgrTxdStrReq[currentStrReq].HasLoaded())
{ // finished loading?
// unload old txd:
if(gpPlantMgrTextureDictionary)
{
const strStreamingObjectName *txdPathName = sm_PlantsMgrTxdPaths[ currentTxd ];
plantsDisplayf("Unloading '%s'.", txdPathName->GetCStr());
ShutdownTextures();
gpPlantMgrTextureDictionary = NULL;
g_TxdStore.RemoveRef(g_TxdStore.FindSlot(*txdPathName), REF_OTHER);
gpPlantMgrTxdStrReq[!currentStrReq].Release();
}
const strStreamingObjectName *txdPathName = sm_PlantsMgrTxdPaths[ requestedTxd ];
gpPlantMgrTxdStrReq[currentStrReq].ClearRequiredFlags(STRFLAG_DONTDELETE);
CStreaming::SetDoNotDefrag((s32)gpPlantMgrTxdStrReq[currentStrReq].GetRequestId(), (s32)gpPlantMgrTxdStrReq[currentStrReq].GetModuleId());
plantsDisplayf("Loaded '%s'!", txdPathName->GetCStr());
const s32 txdSlot = g_TxdStore.FindSlot(*txdPathName);
g_TxdStore.AddRef(txdSlot, REF_OTHER);
Assert(gpPlantMgrTextureDictionary==NULL);
gpPlantMgrTextureDictionary = g_TxdStore.Get(txdSlot);
Assert(gpPlantMgrTextureDictionary);
for(s32 i=0; i<CPLANT_SLOT_NUM_TEXTURES; i++)
{
char txname[128];
sprintf(txname, "txgrass%02d", i); // "txgrass00"
m_PlantTextureTab0[0+i] = LoadTexture(txname);
sprintf(txname, "txgrass%02d_lod1", i); // "txgrass00_lod1"
m_PlantTextureTab0[CPLANT_SLOT_NUM_TEXTURES+i] = LoadTexture(txname);
}
#if PSN_PLANTSMGR_SPU_RENDER
// setup texture commands for SPU (all base and LOD textures):
for(s32 i=0; i<(CPLANT_SLOT_NUM_TEXTURES*2); i++)
{
CGrassRenderer::SpuRecordGrassTexture(i, m_PlantTextureTab0[i]);
}
#endif
currentTxd = requestedTxd;
currentStrReq = !currentStrReq;
currentState = PLANTS_TXD_LOADED;
} // HasLoaded()...
}// IsValid()...
}
break; //PLANTS_TXD_REQUESTED...
case(PLANTS_TXD_LOADED):
{
u8 wantedTxd = CPopCycle::GetCurrentZonePlantsMgrTxdIdx();
Assert(wantedTxd < PLANTS_TXD_MAX);
if(wantedTxd >= PLANTS_TXD_MAX)
{
wantedTxd = PLANTS_TXD_MAX-1;
}
if(currentTxd != wantedTxd)
{
// request new txd:
Assert(gpPlantMgrTxdStrReq[currentStrReq].IsValid()==false);
if(!gpPlantMgrTxdStrReq[currentStrReq].IsValid())
{
const strStreamingObjectName *wantedTxdPathName = sm_PlantsMgrTxdPaths[ wantedTxd ];
if(wantedTxdPathName)
{
plantsDisplayf("Requesting '%s'.", wantedTxdPathName->GetCStr());
const s32 txdSlot = g_TxdStore.FindSlot(*wantedTxdPathName);
// STRFLAG_DONTDELETE - Never delete the object, until CanDelete is called
// STRFLAG_FORCE_LOAD - Keep the request alive until the object has been loaded
gpPlantMgrTxdStrReq[currentStrReq].Request(txdSlot, g_TxdStore.GetStreamingModuleId(), STRFLAG_FORCE_LOAD|STRFLAG_DONTDELETE);
requestedTxd = wantedTxd;
currentState = PLANTS_TXD_REQUESTED;
}
else
{
// mark as already loaded (requested txd is NULL)
// unload old txd:
if(gpPlantMgrTextureDictionary)
{
const strStreamingObjectName *txdPathName = sm_PlantsMgrTxdPaths[ currentTxd ];
plantsDisplayf("Unloading '%s'.", txdPathName->GetCStr());
ShutdownTextures();
gpPlantMgrTextureDictionary = NULL;
g_TxdStore.RemoveRef(g_TxdStore.FindSlot(*txdPathName), REF_OTHER);
gpPlantMgrTxdStrReq[!currentStrReq].Release();
}
currentStrReq = !currentStrReq;
currentTxd = wantedTxd;
currentState = PLANTS_TXD_LOADED;
}
}
}//if(currentTxd != wantedTxd)...
}
break; // PLANTS_TXD_LOADED...
default:
Assertf(0, "Invalid plantsmgr txd loading state!");
break;
};
(void)gbPlantsMgrTxdActive;
#if 0
const strStreamingObjectName *txdPathName = &CPLANTMGR1_TXD_PATH;
if(gbPlantsMgrTxdActive)
{
if(!gpPlantMgrTextureDictionary)
{
if(gpPlantMgrTxdStrReq.IsValid())
{
if(gpPlantMgrTxdStrReq.HasLoaded())
{ // finished loading?
gpPlantMgrTxdStrReq.ClearRequiredFlags(STRFLAG_DONTDELETE);
plantsDisplayf("Loaded '%s'!", txdPathName->GetCStr());
const s32 txdSlot = g_TxdStore.FindSlot(*txdPathName);
g_TxdStore.AddRef(txdSlot, REF_OTHER);
gpPlantMgrTextureDictionary = g_TxdStore.Get(txdSlot);
Assert(gpPlantMgrTextureDictionary);
for(s32 i=0; i<CPLANT_SLOT_NUM_TEXTURES; i++)
{
char txname[128];
sprintf(txname, "txgrass%02d", i); // "txgrass00"
m_PlantTextureTab0[0+i] = LoadTexture(txname);
sprintf(txname, "txgrass%02d_lod1", i); // "txgrass00_lod1"
m_PlantTextureTab0[CPLANT_SLOT_NUM_TEXTURES+i] = LoadTexture(txname);
}
#if PSN_PLANTSMGR_SPU_RENDER
// setup texture commands for SPU (all base and LOD textures):
for(s32 i=0; i<(CPLANT_SLOT_NUM_TEXTURES*2); i++)
{
CGrassRenderer::SpuRecordGrassTexture(i, m_PlantTextureTab0[i]);
}
#endif
} // HasLoaded()...
}
else
{
plantsDisplayf("Requesting '%s'.", txdPathName->GetCStr());
const s32 txdSlot = g_TxdStore.FindSlot(*txdPathName);
// STRFLAG_DONTDELETE - Never delete the object, until CanDelete is called
// STRFLAG_FORCE_LOAD - Keep the request alive until the object has been loaded
gpPlantMgrTxdStrReq.Request(txdSlot, g_TxdStore.GetStreamingModuleId(), STRFLAG_FORCE_LOAD|STRFLAG_DONTDELETE);
}
}//if(!gpPlantMgrTextureDictionary)...
}
else
{
if(gpPlantMgrTextureDictionary)
{
plantsDisplayf("Unloading '%s'.", txdPathName->GetCStr());
ShutdownTextures();
gpPlantMgrTextureDictionary = NULL;
g_TxdStore.RemoveRef(g_TxdStore.FindSlot(*txdPathName), REF_OTHER);
gpPlantMgrTxdStrReq.Release();
}
}
#endif //#if 0...
#endif //PLANTS_TXD_STR...
return(true);
}// end of UpdateStrTextures()...
//
//
// destroy allocated textures:
//
bool CPlantMgr::ShutdownTextures()
{
#define DESTROY_TEXTURE(TEXTURE) { if(TEXTURE) {TEXTURE->Release(); TEXTURE=NULL;} }
for(s32 i=0; i<CPLANT_SLOT_NUM_TEXTURES*2; i++)
{
DESTROY_TEXTURE(m_PlantTextureTab0[i]);
}
#undef DESTROY_TEXTURE
#if PSN_PLANTSMGR_SPU_RENDER
for(s32 i=0; i<CPLANT_SLOT_NUM_TEXTURES*2; i++)
{
CGrassRenderer::SpuRecordGrassTexture(i, m_PlantTextureTab0[i]);
}
#endif
#if PLANTS_TXD_STR
// do nothing
#else
g_TxdStore.RemoveRef(strLocalIndex(g_TxdStore.FindSlot(CPLANTMGR_TXD_PATH)), REF_OTHER);
gpPlantMgrTextureDictionary = NULL;
#endif
return(true);
}
//
//
//
//
bool CPlantMgr::LoadPlantModel(char *fname, grassModel *pPlantModel)
{
gtaDrawable *pDrawable = static_cast<gtaDrawable*>(gpPlantMgrModelDictionary->Lookup(fname));
Assertf(pDrawable, "Cannot find plant model!");
// const s32 oldShaderCount = oldShaderGroup->GetCount();
// create new shadergroup with one shader:
grmShaderGroup *pNewShaderGroup = //CGrassRenderer::GetGrassShaderGroup();
grmShaderFactory::GetInstance().CreateGroup();
Assert(pNewShaderGroup);
grmShader *pGrassShader = CGrassRenderer::GetGrassShader();
Assert(pGrassShader);
pNewShaderGroup->InitCount(1); //oldShaderCount);
pNewShaderGroup->Add(pGrassShader);
pDrawable->SetShaderGroup(pNewShaderGroup);
rmcLodGroup *pLodGroup = &pDrawable->GetLodGroup();
Assert(pLodGroup);
rmcLod *pLod = &pLodGroup->GetLod(LOD_HIGH);
Assert(pLod);
grmModel *pModel = pLod->GetModel(0);
Assert(pModel);
const s32 geomCount = pModel->GetGeometryCount();
Assert(geomCount > 0);
// setup model shader indices:
for(s32 i=0; i<geomCount; i++)
{
pModel->SetShaderIndex(i, 0);
#if __ASSERT
// verify boundSphere radius:
Assert(pModel->GetAABBs());
Vector4 boundSphere = VEC4V_TO_VECTOR4(pModel->GetGeometryAABB(i).GetBoundingSphereV4());
if(boundSphere.w < 0.01f)
plantsDisplayf("'%s' boundSphere=(%.3f, %.2f, %.3f, %.3f) usesBoundingBoxes=%d\n", fname, boundSphere.x, boundSphere.y, boundSphere.z, boundSphere.w, (pModel->GetModelCullback() != NULL));
Assert(boundSphere.w >= 0.01f);
// verify vertex stream format available in this geometry:
grmGeometry *pGeometry = &pModel->GetGeometry(i);
Assert(pGeometry);
Assert(pGeometry->GetType() != grmGeometry::GEOMETRYEDGE);
const grcFvf *pFvf = pGeometry->GetFvf();
Assert(pFvf);
plantsAssertf(pFvf->GetPosChannel(), "Geometry '%s' is missing POSITION channel!", fname);
if (!pFvf->GetPosChannel()) plantsWarningf( "Geometry '%s' is missing POSITION channel!", fname);
plantsAssertf(pFvf->GetDiffuseChannel(), "Geometry '%s' is missing COLOR0 channel!", fname);
if (!pFvf->GetDiffuseChannel()) plantsWarningf( "Geometry '%s' is missing COLOR0 channel!", fname);
plantsAssertf(pFvf->GetSpecularChannel(), "Geometry '%s' is missing COLOR1 channel!", fname);
if (!pFvf->GetSpecularChannel()) plantsWarningf( "Geometry '%s' is missing COLOR1 channel!", fname);
plantsAssertf(pFvf->GetTextureChannel(0), "Geometry '%s' is missing TEXCOORD0 channel!", fname);
if (!pFvf->GetTextureChannel(0)) plantsWarningf( "Geometry '%s' is missing TEXCOORD0 channel!", fname);
#endif //__ASSERT
}
// return plantModel:
pPlantModel->m_pDrawable = pDrawable;
Assert(pPlantModel->m_pDrawable);
pPlantModel->m_pGeometry = &pModel->GetGeometry(0);
Assert(pPlantModel->m_pGeometry);
pPlantModel->m_BoundingSphere = VEC4V_TO_VECTOR4(pModel->GetModelAABB().GetBoundingSphereV4());
Assert(pPlantModel->m_BoundingSphere.w > 0.0f);
return(TRUE);
}// end of LoadPlantModel()...
//
//
//
//
void CPlantMgr::UnloadPlantModel(grassModel *model)
{
gtaDrawable *pDrawable = model->m_pDrawable;
if (pDrawable)
{
// Get shader group
grmShaderGroup &shadergroup = pDrawable->GetShaderGroup();
// Remove reference to the shaders : they're owned by the grassRenderer, and probably
// already deleted by now. (only one by now).
for(int i=0;i<shadergroup.GetCount();i++)
{
shadergroup.Remove(shadergroup.GetShaderPtr(i),false);
}
//pDrawable->SetShaderGroup(NULL);
delete pDrawable;
pDrawable = NULL;
}
model->m_pDrawable = NULL;
model->m_pGeometry = NULL;
model->m_BoundingSphere.Set(0);
return;
}// end of UnloadPlantModel()...
PS3_ONLY(namespace rage { extern u32 g_AllowVertexBufferVramLocks; })
//
//
//
//
bool CPlantMgr::LoadPlantModelLOD2(char *fname, grassModel *pPlantModel)
{
plantsAssert(pPlantModel);
gtaDrawable *pDrawable = static_cast<gtaDrawable*>(gpPlantMgrModelDictionary->Lookup(fname));
plantsAssertf(pDrawable, "Cannot find plant model!");
rmcLodGroup *pLodGroup = &pDrawable->GetLodGroup();
plantsAssert(pLodGroup);
rmcLod *pLod = &pLodGroup->GetLod(LOD_HIGH);
plantsAssert(pLod);
grmModel *pModel = pLod->GetModel(0);
plantsAssert(pModel);
#if __ASSERT
plantsAssertf(pModel->GetGeometryCount()==1, "LOD2: Only simple billboard geometries allowed");
#endif
grmGeometry *pGeometry = &pModel->GetGeometry(0);
plantsAssert(pGeometry);
grcVertexBuffer *pVB = pGeometry->GetVertexBuffer();
plantsAssert(pVB);
PS3_ONLY(++g_AllowVertexBufferVramLocks); // PS3: attempting to edit VBs in VRAM
grcVertexBufferEditor vbEditor(pVB,true,true);
#if __WIN32PC
if (!vbEditor.isValid())
{
pPlantModel->m_dimensionLOD2.x = 0.000001f;
pPlantModel->m_dimensionLOD2.y = 0.000001f;
return false;
}
#endif // __WIN32PC
#if __ASSERT
grcIndexBuffer *pIB = pGeometry->GetIndexBuffer();
plantsAssert(pIB);
const s32 indexCount = pIB->GetIndexCount();
const s32 primCount = pGeometry->GetPrimitiveCount();
plantsAssert(indexCount == 6);
plantsAssert(primCount*3 == indexCount); // make sure it's tri list (drawTris);
#if 0
const u16* pIndices = pIB->LockRO();
Printf("\n indexCount=%d, primCount=%d", indexCount, primCount);
for(s32 i=0; i<indexCount; i++)
{
const s32 idx = pIndices[i];
Vector3 pos = vbEditor.GetPosition(idx);
Printf("\n %d: index=%d: xyz=%.2f %.2f %.2f", i, idx, pos.x, pos.y, pos.z);
}
#endif //#if 0...
#endif //__ASSERT...
Vector3 v0 = vbEditor.GetPosition(0);
Vector3 v1 = vbEditor.GetPosition(1);
Vector3 v2 = vbEditor.GetPosition(2);
// Vector3 v3 = vbEditor.GetPosition(3);
#if 0
// old geom LOD2 v1
indexCount=6, primCount=2
0: 0: xyz=-1.58 0.00 0.00
1: 1: xyz=-1.58 0.00 1.53
2: 2: xyz= 1.58 0.00 1.53
3: 2: xyz= 1.58 0.00 1.53
4: 3: xyz= 1.58 0.00 0.00
5: 0: xyz=-1.58 0.00 0.00
0: 0: xyz=-1.58 0.00 0.00
1: 1: xyz=-1.58 0.00 1.53
2: 2: xyz= 1.58 0.00 1.53
4: 3: xyz= 1.58 0.00 0.00
// new geom LOD2 v2
0: index=0: xyz=-0.77 0.00 0.00
1: index=1: xyz= 0.77 0.00 0.00
2: index=2: xyz= 0.77 0.00 14.46
3: index=2: xyz= 0.77 0.00 14.46
4: index=3: xyz=-0.77 0.00 14.46
5: index=0: xyz=-0.77 0.00 0.00
// new geom LOD2 v3
0: index=0: xyz=0.00 -1.58 0.00
1: index=1: xyz=0.00 1.58 0.00
2: index=2: xyz=0.00 1.58 1.53
3: index=2: xyz=0.00 1.58 1.53
4: index=3: xyz=0.00 -1.58 1.53
5: index=0: xyz=0.00 -1.58 0.00
#endif //#if 0...
// v1: first try to find dimentions:
float width = rage::Abs(v0.x) + rage::Abs(v2.x);
float height= rage::Abs(v0.z) + rage::Abs(v1.z);
// v2: second try (try to pick different verts to calculate dimensions):
if((width <= 0.0f) || (width <= 0.0f))
{
width = rage::Abs(v0.x) + rage::Abs(v1.x);
height= rage::Abs(v0.z) + rage::Abs(v2.z);
}
// v3: third try (try to pick different verts to calculate dimensions):
if((width <= 0.0f) || (width <= 0.0f))
{
width = rage::Abs(v0.y) + rage::Abs(v1.y);
height= rage::Abs(v0.z) + rage::Abs(v2.z);
}
Assert(width > 0.0f);
Assert(height > 0.0f);
#if __ASSERT
plantsDebugf1("LOD2 '%s': detected billboard W=%.2f and H=%.2f.", fname, width, height);
#endif
PS3_ONLY(--g_AllowVertexBufferVramLocks); // PS3: finished with editing VBs in VRAM
// return results:
pPlantModel->m_dimensionLOD2.x = width;
pPlantModel->m_dimensionLOD2.y = height;
return(TRUE);
}// end of LoadPlantModelLOD2()...
//
//
// Create LOD2 vertexbuffer and declaration:
//
bool CPlantMgr::CreateGeometryLOD2()
{
#if GRASS_LOD2_BATCHING
grcVertexElement elements[1+6] =
{
// stream#0:
grcVertexElement( 0, grcVertexElement::grcvetTexture, 0/*TEXCOORD0*/, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat2), grcFvf::grcdsFloat2 , grcFvf::grcsfmModulo, 4 ),
// stream#1:
grcVertexElement( 1, grcVertexElement::grcvetTexture, 1/*TEXCOORD1*/, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat4), grcFvf::grcdsFloat4 , grcFvf::grcsfmDivide, 4 ),
grcVertexElement( 1, grcVertexElement::grcvetTexture, 2/*TEXCOORD2*/, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat4), grcFvf::grcdsFloat4 , grcFvf::grcsfmDivide, 4 ),
grcVertexElement( 1, grcVertexElement::grcvetTexture, 3/*TEXCOORD3*/, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat4), grcFvf::grcdsFloat4 , grcFvf::grcsfmDivide, 4 ),
grcVertexElement( 1, grcVertexElement::grcvetTexture, 4/*TEXCOORD4*/, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat4), grcFvf::grcdsFloat4 , grcFvf::grcsfmDivide, 4 ),
grcVertexElement( 1, grcVertexElement::grcvetTexture, 5/*TEXCOORD5*/, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat4), grcFvf::grcdsFloat4 , grcFvf::grcsfmDivide, 4 ),
grcVertexElement( 1, grcVertexElement::grcvetTangent, 0/*TEXCOORD6*/, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat4), grcFvf::grcdsFloat4 , grcFvf::grcsfmDivide, 4 )
};
#else
grcVertexElement elements[1] =
{
#if (RSG_PC || RSG_DURANGO || RSG_ORBIS)
// I can't see any reason why the LOD code even needs a divider, it always renders only one quad. But let's not risk breaking other builds.
grcVertexElement( 0, grcVertexElement::grcvetTexture, 0, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat2), grcFvf::grcdsFloat2 , grcFvf::grcsfmDivide, 0 )
#else
grcVertexElement( 0, grcVertexElement::grcvetTexture, 0, grcFvf::GetDataSizeFromType(grcFvf::grcdsFloat2), grcFvf::grcdsFloat2 , grcFvf::grcsfmModulo, 4 )
#endif
};
#endif // GRASS_LOD2_BATCHING
#if !(__D3D11 || RSG_ORBIS)
// Verts for quads or triangle fans
static ALIGNAS(16) Vector2 quadVerts[4] =
{
Vector2(1.0f, 0.0f),
Vector2(0.0f, 0.0f),
Vector2(0.0f, 1.0f),
Vector2(1.0f, 1.0f)
};
#else
// Verts for a triangle strip
static ALIGNAS(16) Vector2 quadVerts[4] =
{
Vector2(1.0f, 0.0f),
Vector2(0.0f, 0.0f),
Vector2(1.0f, 1.0f),
Vector2(0.0f, 1.0f)
};
#endif // !(__D3D11 || RSG_ORBIS)
// set up quad vertex buffer for instancing.
const s32 repeatAmount = 4;
const s32 channel = 0;
grcFvf fvf0;
fvf0.SetTextureChannel( channel, true, grcFvf::grcdsFloat2);
fvf0.SetPosChannel( false );
const bool bReadWrite = !(__D3D11 && RSG_PC);
const bool bDynamic = false;
#if RSG_PC
void *const preAllocatedMemory = quadVerts;
#else
void *const preAllocatedMemory = NULL;
#endif
grcVertexBuffer* vbuf = grcVertexBuffer::Create(repeatAmount, fvf0, bReadWrite, bDynamic, preAllocatedMemory);
Assert(vbuf);
PS3_ONLY(++g_AllowVertexBufferVramLocks); // PS3: attempting to edit VBs in VRAM
#if !(__D3D11 && RSG_PC)
{
grcVertexBufferEditor editor(vbuf);
for(s32 j=0; j<repeatAmount; j++ )
{
s32 index = j;
editor.SetUV(index, channel, quadVerts[index], false );
}
}
vbuf->MakeReadOnly();
#endif // !(__D3D11 && RSG_PC)
PS3_ONLY(--g_AllowVertexBufferVramLocks); // PS3: finished with editing VBs in VRAM
grcVertexDeclaration *decl = GRCDEVICE.CreateVertexDeclaration( elements, NELEM(elements) );
Assert(decl);
CGrassRenderer::SetGeometryLOD2(vbuf, decl);
return(TRUE);
}// end of CreateGeometryLOD2()...
static grcRasterizerStateHandle hGrassAlphaToMaskRS_pass1 = grcStateBlock::RS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass1 = grcStateBlock::DSS_Invalid;
static grcBlendStateHandle hGrassAlphaToMaskBS_pass1 = grcStateBlock::BS_Invalid;
static grcBlendStateHandle hGrassAlphaToMaskBS_pass1_end = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass1_end = grcStateBlock::DSS_Invalid;
#if EXTRA_1_5_PASS
static grcBlendStateHandle hGrassAlphaToMaskBS_pass1_5a = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass1_5a = grcStateBlock::DSS_Invalid;
static grcBlendStateHandle hGrassAlphaToMaskBS_pass1_5b = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass1_5b = grcStateBlock::DSS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass1_5c = grcStateBlock::DSS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass1_5_end= grcStateBlock::DSS_Invalid;
static grcBlendStateHandle hGrassAlphaToMaskBS_pass1_5_end = grcStateBlock::BS_Invalid;
#endif
#if XENON_FILL_PASS
static grcBlendStateHandle hGbuffFillPassBS = grcStateBlock::BS_Invalid;
static grcBlendStateHandle hGbuffFillPassBS_end = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hGbuffFillPassDSS = grcStateBlock::DSS_Invalid;
static grcDepthStencilStateHandle hGbuffFillPassRestoreHiStDSS = grcStateBlock::DSS_Invalid;
static grcDepthStencilStateHandle hGbuffFillPassDSS_end = grcStateBlock::DSS_Invalid;
#endif
#if PSN_ALPHATOMASK_PASS
static grcBlendStateHandle hGrassAlphaToMaskBS_pass2 = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass2 = grcStateBlock::DSS_Invalid;
static grcRasterizerStateHandle hGrassAlphaToMaskRS_pass2 = grcStateBlock::RS_Invalid;
static grcDepthStencilStateHandle hGrassAlphaToMaskDSS_pass2_end = grcStateBlock::DSS_Invalid;
static grcRasterizerStateHandle hGrassAlphaToMaskRS_pass2_end = grcStateBlock::RS_Invalid;
static grcBlendStateHandle hGrassAlphaToMaskBS_pass2_end = grcStateBlock::BS_Invalid;
#endif
#if PLANTS_CAST_SHADOWS
static grcRasterizerStateHandle hGrassShadowRS = grcStateBlock::RS_Invalid;
static grcRasterizerStateHandle hGrassShadowRS_end = grcStateBlock::RS_Invalid;
#endif //PLANTS_CAST_SHADOWS
static bool InitialiseGrassRenderStateBlocks()
{
#if __BANK
const bool sm_bEnableCulling = gbDoubleSidedCulling;
#else
static dev_bool sm_bEnableCulling = FALSE;
#endif
if(hGrassAlphaToMaskRS_pass1 == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.FillMode = grcRSV::FILL_SOLID;
rasDesc.CullMode = sm_bEnableCulling? grcRSV::CULL_BACK : grcRSV::CULL_NONE;
hGrassAlphaToMaskRS_pass1 = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hGrassAlphaToMaskRS_pass1 != grcStateBlock::RS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass1 == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
// enable stencil:
dsDesc.StencilEnable = TRUE;
dsDesc.StencilReadMask = DEFERRED_MATERIAL_INTERIOR_VEH;
#if EXTRA_1_5_PASS
dsDesc.StencilWriteMask = 0xf0; // do not overwrite original materialID (bits: 3-0)
#else
dsDesc.StencilWriteMask = 0xff;
#endif
dsDesc.FrontFace.StencilFunc =
dsDesc.BackFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp =
dsDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_REPLACE;
dsDesc.FrontFace.StencilFailOp =
dsDesc.BackFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp =
dsDesc.BackFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
hGrassAlphaToMaskDSS_pass1 = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass1 != grcStateBlock::DSS_Invalid);
}
#if USE_SSA_STIPPLE_ALPHA
//RDR3 uses SSA style stipple to eliminate pixels for grass alpha, so these states need to be changed.
static dev_bool debugEnableAlphaToMask1 = FALSE;
static dev_bool debugUseSolidAlphaToMask1 = TRUE;
#else
static dev_bool debugEnableAlphaToMask1 = TRUE;
static dev_bool debugUseSolidAlphaToMask1 = FALSE;
#endif
if(hGrassAlphaToMaskBS_pass1 == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
// Standard srcAlpha, 1-srcAlpha blend
bsDesc.BlendRTDesc[0].BlendOp = grcRSV::BLENDOP_ADD;
bsDesc.BlendRTDesc[0].SrcBlend = grcRSV::BLEND_SRCALPHA;
bsDesc.BlendRTDesc[0].DestBlend = grcRSV::BLEND_INVSRCALPHA;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_RGB;
#if PSN_ALPHATOMASK_PASS
bsDesc.IndependentBlendEnable = FALSE;
#else
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#if WRITE_SELF_SHADOW_TERM
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#else
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_RGB; // respect selfshadow term from underlying ground
#endif
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#endif //PSN_ALPHATOMASK_PASS...
bsDesc.AlphaToCoverageEnable = debugEnableAlphaToMask1;
bsDesc.AlphaToMaskOffsets = debugUseSolidAlphaToMask1 ? grcRSV::ALPHATOMASKOFFSETS_SOLID : grcRSV::ALPHATOMASKOFFSETS_DITHERED;
hGrassAlphaToMaskBS_pass1 = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGrassAlphaToMaskBS_pass1 != grcStateBlock::BS_Invalid);
}
if(hGrassAlphaToMaskBS_pass1_end == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.AlphaToCoverageEnable = FALSE;
hGrassAlphaToMaskBS_pass1_end = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGrassAlphaToMaskBS_pass1_end != grcStateBlock::BS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass1_end == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
// enable stencil:
dsDesc.StencilEnable = TRUE;
dsDesc.StencilReadMask = 0xff;
dsDesc.StencilWriteMask = 0xff;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_ALWAYS;
dsDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_REPLACE;
dsDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
hGrassAlphaToMaskDSS_pass1_end = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass1_end != grcStateBlock::DSS_Invalid);
}
#if EXTRA_1_5_PASS
if(hGrassAlphaToMaskBS_pass1_5a == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
#if __XENON
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALPHA; // write only to MRT2.a=selfshadow
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#else // __XENON
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALPHA; // write only to MRT2.a=selfshadow
#endif // __XENON
hGrassAlphaToMaskBS_pass1_5a = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGrassAlphaToMaskBS_pass1_5a != grcStateBlock::BS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass1_5a == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = FALSE;
dsDesc.DepthWriteMask = FALSE;
dsDesc.StencilEnable = TRUE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_NOTEQUAL;
dsDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.StencilReadMask = 0x1f; // read bits 4,3..0
dsDesc.StencilWriteMask = 0x00;
hGrassAlphaToMaskDSS_pass1_5a = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass1_5a != grcStateBlock::DSS_Invalid);
}
if(hGrassAlphaToMaskBS_pass1_5b == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
hGrassAlphaToMaskBS_pass1_5b = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGrassAlphaToMaskBS_pass1_5b != grcStateBlock::BS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass1_5b == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = FALSE;
dsDesc.DepthWriteMask = FALSE;
dsDesc.StencilEnable = TRUE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_REPLACE;
dsDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.StencilReadMask = 0x10; // read special bit only
dsDesc.StencilWriteMask = 0x0f; // overwrite original material with 0x03
hGrassAlphaToMaskDSS_pass1_5b = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass1_5b != grcStateBlock::DSS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass1_5c == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = FALSE;
dsDesc.DepthWriteMask = FALSE;
dsDesc.StencilEnable = TRUE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_ZERO;
dsDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.StencilReadMask = 0x1f; // read full material
dsDesc.StencilWriteMask = 0x10; // clear special bit only
hGrassAlphaToMaskDSS_pass1_5c = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass1_5c != grcStateBlock::DSS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass1_5_end == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.StencilEnable = FALSE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_ALWAYS;
hGrassAlphaToMaskDSS_pass1_5_end = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass1_5_end != grcStateBlock::DSS_Invalid);
}
if(hGrassAlphaToMaskBS_pass1_5_end == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
hGrassAlphaToMaskBS_pass1_5_end = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGrassAlphaToMaskBS_pass1_5_end != grcStateBlock::BS_Invalid);
}
#endif //EXTRA_1_5_PASS...
#if XENON_FILL_PASS
if(hGbuffFillPassRestoreHiStDSS == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = FALSE;
dsDesc.DepthWriteMask = FALSE;
dsDesc.StencilEnable = TRUE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.StencilReadMask = 0x7; // read bits 2..0
hGbuffFillPassRestoreHiStDSS = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGbuffFillPassRestoreHiStDSS != grcStateBlock::DSS_Invalid);
}
if(hGbuffFillPassDSS_end == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.StencilEnable = FALSE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_ALWAYS;
hGbuffFillPassDSS_end = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGbuffFillPassDSS_end != grcStateBlock::DSS_Invalid);
}
if(hGbuffFillPassBS == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
hGbuffFillPassBS = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGbuffFillPassBS != grcStateBlock::BS_Invalid);
}
if(hGbuffFillPassDSS == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = FALSE;
dsDesc.DepthWriteMask = FALSE;
dsDesc.StencilEnable = TRUE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_ZERO;
dsDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.StencilReadMask = 0xFF;
dsDesc.StencilWriteMask = 0x10;
hGbuffFillPassDSS = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGbuffFillPassDSS != grcStateBlock::DSS_Invalid);
}
if(hGbuffFillPassBS_end == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
hGbuffFillPassBS_end = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGbuffFillPassBS_end != grcStateBlock::BS_Invalid);
}
#endif //XENON_FILL_PASS...
#if PSN_ALPHATOMASK_PASS
if(hGrassAlphaToMaskBS_pass2 == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = TRUE;
#if CPLANT_STORE_LOCTRI_NORMAL
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL; // need to overwrite decompressed color
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#if WRITE_SELF_SHADOW_TERM
bsDesc.BlendRTDesc[2].BlendEnable = TRUE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#else
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_RGB; // respect selfshadow term from underlying ground
#endif
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#elif CPLANT_BLIT_MIN_GBUFFER
// Everything shifts down by 1 slot
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#if WRITE_SELF_SHADOW_TERM
bsDesc.BlendRTDesc[1].BlendEnable = TRUE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#else
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_RGB; // respect selfshadow term from underlying ground
#endif
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#else
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#if WRITE_SELF_SHADOW_TERM
bsDesc.BlendRTDesc[2].BlendEnable = TRUE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#else
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_RGB; // respect selfshadow term from underlying ground
#endif
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
#endif
hGrassAlphaToMaskBS_pass2 = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGrassAlphaToMaskBS_pass2 != grcStateBlock::BS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass2 == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = FALSE;
dsDesc.DepthWriteMask = FALSE;
dsDesc.StencilEnable = TRUE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp = grcRSV::STENCILOP_ZERO; // reset special bit
dsDesc.FrontFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.StencilReadMask = 0x17; // read bits 4,2..0
dsDesc.StencilWriteMask = 0x10; // write only to bit 4
hGrassAlphaToMaskDSS_pass2 = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass2 != grcStateBlock::DSS_Invalid);
}
if(hGrassAlphaToMaskRS_pass2 == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
hGrassAlphaToMaskRS_pass2 = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hGrassAlphaToMaskRS_pass2 != grcStateBlock::RS_Invalid);
}
if(hGrassAlphaToMaskDSS_pass2_end == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.StencilEnable = FALSE;
dsDesc.FrontFace.StencilFunc = grcRSV::CMP_ALWAYS;
hGrassAlphaToMaskDSS_pass2_end = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hGrassAlphaToMaskDSS_pass2_end != grcStateBlock::DSS_Invalid);
}
if(hGrassAlphaToMaskRS_pass2_end == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_BACK;
hGrassAlphaToMaskRS_pass2_end = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hGrassAlphaToMaskRS_pass2_end != grcStateBlock::RS_Invalid);
}
if(hGrassAlphaToMaskBS_pass2_end == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
hGrassAlphaToMaskBS_pass2_end = grcStateBlock::CreateBlendState(bsDesc);
Assert(hGrassAlphaToMaskBS_pass2_end != grcStateBlock::BS_Invalid);
}
#endif //PSN_ALPHATOMASK_PASS...
#if PLANTS_CAST_SHADOWS
if(hGrassShadowRS == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
hGrassShadowRS = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hGrassShadowRS != grcStateBlock::RS_Invalid);
}
if(hGrassShadowRS_end == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_BACK;
hGrassShadowRS_end = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hGrassShadowRS_end != grcStateBlock::RS_Invalid);
}
#endif //PLANTS_CAST_SHADOWS
return(TRUE);
}// end of InitialiseGrassRenderStateBlocks()...
//
//
//
//
void CPlantMgr::Init(unsigned initMode)
{
if(initMode == INIT_CORE)
{
if(!gPlantMgr.InitialiseInternal())
{
Assertf(FALSE, "Failed to initialise CPlantMgr!");
}
}
}
bool CPlantMgr::InitialiseInternal()
{
USE_MEMBUCKET(MEMBUCKET_RENDER);
// PLANTS_MALLOC_STR requirement:
Assertf(_IsPowerOfTwo(g_rscVirtualLeafSize), "g_rscVirtualLeafSize is NOT power-of-2!");
#if LOCTRISTAB_STR
// do nothing
#else
AllocateStrLocTriTabs();
#endif
const u32 flags = 0;
for(int i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
g_PlantMgrMemcpyDependency[i].Init( PlantsMgrAsyncMemcpyJob, 0, flags );
g_PlantMgrMemcpyDependency[i].m_Priority = sysDependency::kPriorityLow;
}
if(!ReloadConfig())
{
return(FALSE);
}
if(!CGrassRenderer::Initialise())
{
return(FALSE);
}
if(!InitialiseGrassRenderStateBlocks())
{
return(FALSE);
}
// texture stuff:
InitialiseTextures();
//
// model stuff:
//
strLocalIndex modelSlot = g_DwdStore.FindSlot(CPLANTMGR_MODELS_PATH);
CStreaming::LoadObject(modelSlot, g_DwdStore.GetStreamingModuleId());
g_DwdStore.AddRef(modelSlot, REF_OTHER);
gpPlantMgrModelDictionary = g_DwdStore.Get(modelSlot);
Assert(gpPlantMgrModelDictionary);
for(s32 i=0; i<CPLANT_SLOT_NUM_MODELS; i++)
{
char modelname[128];
sprintf(modelname, "grass%02d", i); // "grass00"
LoadPlantModel(modelname, &m_PlantModelsTab0[0+i] );
sprintf(modelname, "grass%02d_lod1", i); // "grass00_lod1"
LoadPlantModel(modelname, &m_PlantModelsTab0[CPLANT_SLOT_NUM_MODELS+i]);
sprintf(modelname, "grass%02d_lod2", i); // "grass00_lod2"
LoadPlantModelLOD2(modelname, &m_PlantModelsTab0[CPLANT_SLOT_NUM_MODELS+i]);
}
// setup geometry buffers for GrassRenderer:
CGrassRenderer::SetPlantModelsTab(PPPLANTBUF_MODEL_SET0, m_PlantModelsTab0);
CGrassRenderer::SetPlantModelsTab(PPPLANTBUF_MODEL_SET1, NULL);
CGrassRenderer::SetPlantModelsTab(PPPLANTBUF_MODEL_SET2, NULL);
CGrassRenderer::SetPlantModelsTab(PPPLANTBUF_MODEL_SET3, NULL);
if(!CreateGeometryLOD2())
{
return(FALSE);
}
#if FURGRASS_TEST_V4
if(!FurGrassLODInitialise())
return(FALSE);
#endif
#if PLANTS_USE_LOD_SETTINGS
// Default to high detail for now.
CGrassRenderer_PlantLod plantLod = PLANTLOD_HIGH;
#if __BANK
if (PARAM_grassandplants.Get())
{
u32 grassAndPlantsDetail=0;
PARAM_grassandplants.Get(grassAndPlantsDetail);
plantLod = (CGrassRenderer_PlantLod)grassAndPlantsDetail;
}
#endif // __BANK
SetupPlantLods(plantLod);
#endif //PLANTS_USE_LOD_SETTINGS
#if PLANTS_CAST_SHADOWS
InitShadowCandidates();
#endif //PLANTS_CAST_SHADOWS
return(TRUE);
}// end of CPlantMgr::Initialise()...
//
//
//
//
bool CPlantMgr::AllocateStrLocTriTabs()
{
const u32 nLocTrisTabSize = sizeof(CPlantLocTriArray);
bool bSuccess=true;
s32 numAllocated=0;
// allocate update buffers from streaming heap:
for(s32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
Assert(m_LocTrisTab[i]==NULL);
m_LocTrisTab[i] = (CPlantLocTriArray*)PLANTS_MALLOC_STR(nLocTrisTabSize, 128);
if(m_LocTrisTab[i])
{
numAllocated++;
plantsDisplayf("LocTris[]: Allocated %d/12 chunk of 64K.", numAllocated);
}
else
{
bSuccess = false;
}
Assert(m_LocTrisRenderTab[0][i]==NULL);
m_LocTrisRenderTab[0][i] = (CPlantLocTriArray*)PLANTS_MALLOC_STR(nLocTrisTabSize, 128);
if(m_LocTrisRenderTab[0][i])
{
numAllocated++;
plantsDisplayf("LocTris[]: Allocated %d/12 chunk of 64K.", numAllocated);
}
else
{
bSuccess = false;
}
Assert(m_LocTrisRenderTab[1][i]==NULL);
m_LocTrisRenderTab[1][i] = (CPlantLocTriArray*)PLANTS_MALLOC_STR(nLocTrisTabSize, 128);
if(m_LocTrisRenderTab[1][i])
{
numAllocated++;
plantsDisplayf("LocTris[]: Allocated %d/12 chunk of 64K.", numAllocated);
}
else
{
bSuccess = false;
}
}
if(!bSuccess)
{ // allocations failed
plantsDisplayf("Allocation of LocTris[] from str memory failed (done %d out of 12)!", numAllocated);
FreeStrLocTriTabs(); // free whatever was allocated
return(false);
}
plantsDisplayf("Allocation of LocTris[] from str memory successful.");
for(s32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
Assert(m_LocTrisTab[i]);
Assert16(m_LocTrisTab[i]);
sysMemSet(m_LocTrisTab[i], 0x00, nLocTrisTabSize);
#if LOCTRISTAB_STR
m_LocTrisTab[i]->Init( (u16)i );
#endif
Assert(m_LocTrisRenderTab[0][i]);
Assert16(m_LocTrisRenderTab[0][i]);
sysMemSet(m_LocTrisRenderTab[0][i], 0x00, nLocTrisTabSize);
Assert(m_LocTrisRenderTab[1][i]);
Assert16(m_LocTrisRenderTab[1][i]);
sysMemSet(m_LocTrisRenderTab[1][i], 0x00, nLocTrisTabSize);
}
return(true);
}
//
//
//
//
bool CPlantMgr::FreeStrLocTriTabs()
{
#if LOCTRISTAB_STR
// clear whole BoundsCache and Procedural Objects (via CPlantLocTri::Release()):
u16 entry = gPlantMgr.m_ColEntCache.m_CloseListHead;
while(entry)
{
CPlantColBoundEntry *pEntry = &gPlantMgr.m_ColEntCache[entry];
entry = pEntry->m_NextEntry;
pEntry->ReleaseEntry();
}
#endif
for(s32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
if(m_LocTrisTab[i])
{
PLANTS_FREE_STR(m_LocTrisTab[i]);
m_LocTrisTab[i] = NULL;
}
if(m_LocTrisRenderTab[0][i])
{
PLANTS_FREE_STR(m_LocTrisRenderTab[0][i]);
m_LocTrisRenderTab[0][i] = NULL;
}
if(m_LocTrisRenderTab[1][i])
{
PLANTS_FREE_STR(m_LocTrisRenderTab[1][i]);
m_LocTrisRenderTab[1][i] = NULL;
}
}
plantsDisplayf("Freeing LocTris[] from str memory.");
return(true);
}
//
//
//
//
bool CPlantMgr::UpdateStrLocTriTabs()
{
#if LOCTRISTAB_STR
enum // simple txd state machine:
{
PLANTS_STR_FREED = 0,
PLANTS_STR_ALLOCATING,
PLANTS_STR_ALLOCATED
};
// want LocTrisTabs[] allocated whenever txd!=0:
const bool bWantAllocated = (CPopCycle::GetCurrentZonePlantsMgrTxdIdx() != 0);
const s32 COUNTDOWN_INIT= 400; // wait 400 frames before state change
static s32 countdown = COUNTDOWN_INIT;
static u8 currentState = PLANTS_STR_FREED;
switch(currentState)
{
case(PLANTS_STR_FREED):
{
if(bWantAllocated)
{
currentState = PLANTS_STR_ALLOCATING;
}
}
break; //PLANTS_STR_FREED...
case(PLANTS_STR_ALLOCATING):
{
if(AllocateStrLocTriTabs())
{
currentState = PLANTS_STR_ALLOCATED;
}
else
{ // failed to allocate and don't want allocated anymore?
if(bWantAllocated==false)
{
currentState = PLANTS_STR_FREED;
}
}
}
break; // PLANTS_STR_ALLOCATING...
case(PLANTS_STR_ALLOCATED):
{
if(bWantAllocated)
{
countdown = COUNTDOWN_INIT;
}
else
{
if((--countdown)==0)
{
countdown = COUNTDOWN_INIT;
FreeStrLocTriTabs();
currentState = PLANTS_STR_FREED;
}
}
}
break; // PLANTS_STR_ALLOCATED...
default:
Assertf(0, "Invalid plant alloc str state!");
break;
};
#endif
return(true);
}// end of UpdateStrLocTrisTabs()...
#if PLANTS_USE_LOD_SETTINGS
void SetupPlantLods(CGrassRenderer_PlantLod plantLod)
{
CGrassRenderer::SetPlantLODToUse(plantLod);
}
#endif //PLANTS_USE_LOD_SETTINGS
//
//
// checks consistency of plant definitions in procedural meta:
// - textures;
//
bool CPlantMgr::CheckProceduralMeta()
{
#if PLANTS_TXD_STR
return(true);
#else
const u32 count = g_procInfo.m_plantInfos.GetCount();
for(u32 i=0; i<count; i++)
{
CPlantInfo *pPlantInfo = &g_procInfo.m_plantInfos[i];
#if !__FINAL
const char* name = g_procInfo.m_plantInfos[i].m_Tag.GetCStr();
#elif RSG_PC || !__NO_OUTPUT
const char* name = "";
#endif
if(!m_PlantTextureTab0[pPlantInfo->m_TextureId])
{
Quitf(ERR_GFX_CHECK_PROC_1,"\n PlantsMgr: %s trying to use empty texture from slot %d!", name, pPlantInfo->m_TextureId);
return false;
}
if(!m_PlantTextureTab0[pPlantInfo->m_TextureId+CPLANT_SLOT_NUM_TEXTURES])
{
Quitf(ERR_GFX_CHECK_PROC_2,"\n PlantsMgr: %s trying to use empty LOD texture from slot %d!", name, pPlantInfo->m_TextureId);
return false;
}
}
return true;
#endif //PLANTS_TXD_STR...
}// end of CheckProceduralMeta()...
#if __BANK
static bool gbProceduralMetaChanged = FALSE;
void CPlantMgr::MarkReloadConfig()
{
gbProceduralMetaChanged = true;
}
void ReloadProceduralMeta()
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
if(gbProceduralMetaChanged)
{
gbProceduralMetaChanged = false;
// wait for any active renderThread stuff to finish:
while(bRenderInternalActive)
{
sysIpcSleep(10);
}
const bool prevPlantMgrActive = gbPlantMgrActive;
gbPlantMgrActive = FALSE;
{
gPlantMgr.ReloadConfig();
}
gbPlantMgrActive = prevPlantMgrActive;
}
}
void ReloadProceduralDotDatCB()
{
Printf("\n Reloading 'procedural.dat' (%d)...", GRCDEVICE.GetFrameCounter());
// wait for any active renderThread stuff to finish:
while(bRenderInternalActive)
{
sysIpcSleep(10);
}
const bool prevPlantMgrActive = gbPlantMgrActive;
gbPlantMgrActive = FALSE;
{
// now reload procedural.dat data:
g_procObjMan.ReloadData();
gPlantMgr.ReloadConfig();
}
gbPlantMgrActive = prevPlantMgrActive;
}
static void ToggleCullingCB()
{
// wait for any active renderThread stuff to finish:
while(bRenderInternalActive)
{
sysIpcSleep(10);
}
const bool prevPlantMgrActive = gbPlantMgrActive;
gbPlantMgrActive = FALSE;
{
// invalidate rasterizer state:
grcStateBlock::DestroyRasterizerState(hGrassAlphaToMaskRS_pass1);
hGrassAlphaToMaskRS_pass1 = grcStateBlock::RS_Invalid;
// recreate rasterizer state:
InitialiseGrassRenderStateBlocks();
}
gbPlantMgrActive = prevPlantMgrActive;
}
//
//
// debug widgets:
//
bool CPlantMgr::InitWidgets(bkBank& bank)
{
// bkBank& bank = *BANKMGR.FindBank("Renderer");
bank.PushGroup("PlantsMgr", false);
// debug widgets:
bank.AddToggle("CPlantMgr active", &gbPlantMgrActive);
bank.AddToggle("CPlantMgrRender active", &gbPlantMgrRenderActive);
bank.AddToggle("Display Debug Info", &gbDisplayCPlantMgrInfo);
#if PSN_PLANTSMGR_SPU_UPDATE
bank.AddToggle("Print UpdateSpu job timings", &gbPrintUpdateSpuJobTimings);
#endif
bank.PushGroup("Show LocTri Polys");
bank.AddToggle("All Polys", &gbShowCPlantMgrPolys);
bank.AddToggle("Far Polys", &gbShowCPlantMgrPolysFar);
bank.AddToggle("Show Plant", &gbShowCPlantMgrPolys_Plants);
bank.AddToggle("Show ProcObj", &gbShowCPlantMgrPolys_ProcObj);
bank.PopGroup();
bank.AddToggle("Show Proc Material Types", &gbShowCPlantMgrProcMatType);
bank.AddToggle("Show Ambient Scales", &gbShowCPlantMgrAmbScale);
bank.AddToggle("Show Alpha Overdraw", &gbShowAlphaOverdraw);
bank.AddToggle("Show LocTri Normals", &gbShowCPlantMgrPolyNormals);
bank.AddToggle("LOD0: Flash Instances", &gbPlantsFlashLOD0);
bank.AddToggle("LOD1: Flash Instances", &gbPlantsFlashLOD1);
bank.AddToggle("LOD2: Flash Instances", &gbPlantsFlashLOD2);
bank.AddToggle("LOD0: Disable Render", &gbPlantsDisableLOD0);
bank.AddToggle("LOD1: Disable Render", &gbPlantsDisableLOD1);
bank.AddToggle("LOD2: Disable Render", &gbPlantsDisableLOD2);
CGrassRenderer::InitWidgets(bank);
bank.PushGroup("Wind Movement", false);
{
bank.AddToggle("Response Enabled", &ms_bkbWindResponseEnabled);
bank.AddSlider("Response Amount", &ms_bkfWindResponseAmount, 0.0f, 50.0f, 1.0f/256.0f);
bank.AddSlider("Response Vel Min", &ms_bkfWindResponseVelMin, 0.0f, 64.0f, 1.0f/256.0f);
bank.AddSlider("Response Vel Max", &ms_bkfWindResponseVelMax, 0.0f, 64.0f, 1.0f/256.0f);
bank.AddSlider("Response Vel Exp", &ms_bkfWindResponseVelExp, -4.0f, 4.0f, 1.0f/256.0f);
bank.AddSlider("Average Wind Influence",&g_AvgPlantWindInfluence, 0.0f, 1.0f, 0.001f);
}
bank.PopGroup();
#if CPLANT_CLIP_EDGE_VERT
bank.PushGroup("Plant Edge/Vert Clipping", false);
{
bank.AddToggle("Enable plant/poly intersection clipping", &gbPlantMgrClipEnable);
bank.AddTitle("");
bank.AddToggle("Enable poly edge intersection clipping", &gbPlantMgrEdgeClipEnable);
bank.AddToggle("Enable poly vert intersection clipping", &gbPlantMgrVertClipEnable);
}
bank.PopGroup();
#endif //CPLANT_CLIP_EDGE_VERT...
#if CPLANT_DYNAMIC_CULL_SPHERES
bank.PushGroup("Dynamic Cull Spheres", false);
{
bank.AddToggle("Enable Dynamic Cull Spheres", &gbPlantMgrDynCullSpheresEnable);
bank.PushGroup("--- Debugging ---", false);
{
bank.AddToggle("Debug Draw Cull Spheres", &gbPlantMgrDCSDebugDraw);
bank.AddSeparator("");
bank.AddVector("Sphere XYZ & Radius", &gPlantMgrDCSDbg_Sphere, -FLT_MAX / 1000.0f, FLT_MAX / 1000.0f, 1.0f);
bank.AddSlider("Cull Sphere Handle", &gPlantMgrDCSDbg_Handle, 0, gPlantMgr.m_DynCullSpheres.size() - 1, 1);
bank.AddButton("Add Sphere", PlantMgrDCSDbg_AddSphere, "Add sphere above to the Dynamic Cull Sphere List. Successful adds update the handle.");
bank.AddButton("Remove Sphere", PlantMgrDCSDbg_RemoveSphere, "Remove dynamic cull sphere at the handle above.");
bank.AddSeparator("");
bank.AddButton("Center Sphere around Player", PlantMgrDCSDbg_CenterSphereOnPlayerPed, "Sets the center of the dbg sphere to the player ped's position. Useful shortcut function.");
bank.PushGroup("Debug Draw Options", false);
{
bank.AddToggle("Draw Cull Spheres as solid", &gbPlantMgrDCSDebugDrawSolid);
bank.AddColor("Debug Cull Sphere Color", &gPlantMgrDCSDebugDrawColor);
}
bank.PopGroup();
}
bank.PopGroup();
bank.PushGroup("Cull Sphere Array", false);
{
for(DynCullSphereSourceArray::size_type i = 0; i < gPlantMgr.m_DynCullSpheres.size(); ++i)
{
char nameBuff[256];
formatf(nameBuff, "Cull Sphere %d", i);
bank.AddVector(nameBuff, &(gPlantMgr.m_DynCullSpheres[i].GetV4Ref()), -FLT_MAX / 1000.0f, FLT_MAX / 1000.0f, 0.1f);
}
}
bank.PopGroup();
}
bank.PopGroup();
#endif //CPLANT_DYNAMIC_CULL_SPHERES...
#if PLANTSMGR_DATA_EDITOR
gPlantMgrEditor.InitWidgets(bank, "Data Edit");
#endif
bank.AddSlider("Tris Far Distance:", &gbPlantsLocTriFarDist, CPLANT_TRILOC_FAR_DIST_INITVAL, CPLANT_TRILOC_FAR_DIST_INITVAL*2, 0.25f);
bank.PushGroup("LOD Fading Control", false);
bank.AddSlider("LOD0: Alpha Close Distance:", &gbPlantsLOD0AlphaCloseDist,0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD0: Alpha Far Distance:", &gbPlantsLOD0AlphaFarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD0: Far Distance:", &gbPlantsLOD0FarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD1: Close Distance:", &gbPlantsLOD1CloseDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD1: Far Distance:", &gbPlantsLOD1FarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD1: Close Alpha Close Distance:", &gbPlantsLOD1Alpha0CloseDist,0.0f,CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD1: Close Alpha Far Distance:", &gbPlantsLOD1Alpha0FarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD1: Far Alpha Close Distance:", &gbPlantsLOD1Alpha1CloseDist,0.0f,CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD1: Far Alpha Far Distance:", &gbPlantsLOD1Alpha1FarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Close Distance:", &gbPlantsLOD2CloseDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Far Distance:", &gbPlantsLOD2FarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Close Alpha Close Distance:", &gbPlantsLOD2Alpha0CloseDist,0.0f,CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Close Alpha Far Distance:", &gbPlantsLOD2Alpha0FarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Far Alpha Close Distance:", &gbPlantsLOD2Alpha1CloseDist,0.0f,CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Far Alpha Far Distance:", &gbPlantsLOD2Alpha1FarDist, 0.0f, CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Far Alpha Close Distance Far:",&gbPlantsLOD2Alpha1CloseDistFar,0.0f,CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.AddSlider("LOD2: Far Alpha Far Distance Far:", &gbPlantsLOD2Alpha1FarDistFar, 0.0f,CPLANT_TRILOC_FAR_DIST*2, 0.25f);
bank.PopGroup();
bank.PushGroup("Per-vertex Local Variation Data", false);
bank.AddToggle("Show per-vertex Ground Color/Scale/Density:", &gbShowCPlantMgrGroundColor);
bank.AddSlider("(0=color, 1=scaleXYZ, 2=scaleZ, 3=density", &gbShowCPlantMgrGroundCSD, 0, 3, 1);
bank.AddToggle("Force Default Ground Color", &gbForceDefaultGroundColor);
bank.AddColor( "Default Ground Color", &gbDefaultGroundColor);
bank.AddToggle("Force per-vertex Ground Colors", &gbForceGroundColor);
bank.AddToggle("Change all 3 Ground Colors at once",&gbChangeGroundColorsAllAsOne);
bank.AddColor( "Ground Color at vertex#0", &gbGroundColorV1, datCallback(cbBankChangeGroundColorV1));
bank.AddColor( "Ground Color at vertex#1", &gbGroundColorV2, datCallback(cbBankChangeGroundColorV2));
bank.AddColor( "Ground Color at vertex#2", &gbGroundColorV3, datCallback(cbBankChangeGroundColorV3));
bank.AddSlider("Ground ScaleXYZ weight at vertex#0 (maps to <0; 1>)", &gbGroundScaleXYZ_V1, 0, 15, 1);
bank.AddSlider("Ground ScaleXYZ weight at vertex#1 (maps to <0; 1>)", &gbGroundScaleXYZ_V2, 0, 15, 1);
bank.AddSlider("Ground ScaleXYZ weight at vertex#2 (maps to <0; 1>)", &gbGroundScaleXYZ_V3, 0, 15, 1);
bank.AddSlider("Ground ScaleZ weight at vertex#0 (maps to [1, 0.6, 0.3, 0])", &gbGroundScaleZ_V1, 0, 3, 1);
bank.AddSlider("Ground ScaleZ weight at vertex#1 (maps to [1, 0.6, 0.3, 0])", &gbGroundScaleZ_V2, 0, 3, 1);
bank.AddSlider("Ground ScaleZ weight at vertex#2 (maps to [1, 0.6, 0.3, 0])", &gbGroundScaleZ_V3, 0, 3, 1);
bank.AddSlider("Ground Density weight at vertex#0 (maps to [1, 0.6, 0.3, 0])", &gbGroundDensity_V1, 0, 3, 1);
bank.AddSlider("Ground Density weight at vertex#1 (maps to [1, 0.6, 0.3, 0])", &gbGroundDensity_V2, 0, 3, 1);
bank.AddSlider("Ground Density weight at vertex#2 (maps to [1, 0.6, 0.3, 0])", &gbGroundDensity_V3, 0, 3, 1);
bank.PopGroup();
bank.AddSlider("Skewing: Ground slope min angle (deg):", &gbPlantsGroundSlopeAngleMin, 1.0f, 90.0, 0.5f);
bank.AddToggle("Darkness: use timecycle (6-7am, 5-6pm)", &gbPlantsDarknessTimecycle);
bank.AddSlider("Darkness amount to add:", &gbPlantsDarknessAmountAdd, -1.0f,1.0, 0.01f);
#if CPLANT_PRE_MULTIPLY_LIGHTING
bank.AddToggle("Premultiply NdotL + ambient", &gbPlantsPremultiplyNdotL);
#endif
#if PLANTS_TXD_STR
bank.AddToggle("PlantsMgrTxd str request", &gbPlantsMgrTxdActive);
#endif
bank.AddToggle("Geometry frustum culling", &gbPlantsGeometryCulling);
#if CPLANT_USE_OCCLUSION
bank.PushGroup("Occlusion", false);
bank.AddToggle("Use occlusion", &gbPlantsUseOcclusion);
bank.AddSlider("Tri cube box height", &gfPlantsOccCubeHeight, 0.0f, 5.0f, 0.1f);
bank.AddToggle("Recalc AABBs", &gbPlantsOccRecalcAABBs);
bank.AddSlider("Test skip (0=all, 1=50%, 2=33%, 3=25%)",&gnPlantsOccSkipTest, 0, 3, 1);
bank.PopGroup();
#endif
bank.AddToggle("Double-sided culling", &gbDoubleSidedCulling, ToggleCullingCB);
bank.AddButton("Reload \"procedural.dat\" file", ReloadProceduralDotDatCB, "Reloads \"procedural.dat\" and resets CPlantMgr.");
#if FURGRASS_TEST_V4
bank.PushGroup("Fur grass", false);
bank.AddSlider("Draw distance:", &gfFurGrassMaxDist, 0.0f, 75.0f, 0.25f);
bank.AddSlider("Alpha fade close:", &gfFurGrassAlphaFadeClose, 0.0f, 75.0f, 0.25f);
bank.AddSlider("Alpha fade far:", &gfFurGrassAlphaFadeFar, 0.0f, 75.0f, 0.25f);
bank.AddToggle("Unique texture per every layer", &gbFurGrassSeparateTextures);
//bank.AddSlider("Fur height combo tech (0=combo4tex, 1=combo2tex):", &gnFurGrassSeparateTexturesTech, 0, 1, 1);
bank.AddSlider("# of fur layers:", &gnFurGrassNumLayers, 1, FURGRASS_MAX_LAYERS, 1);
bank.PushGroup("Alpha clip", true);
bank.AddSlider("Layer #7 (top-most):", &gnFurGrassAlphaClip[7], 0, 255, 1);
bank.AddSlider("Layer #6:", &gnFurGrassAlphaClip[6], 0, 255, 1);
bank.AddSlider("Layer #5:", &gnFurGrassAlphaClip[5], 0, 255, 1);
bank.AddSlider("Layer #4:", &gnFurGrassAlphaClip[4], 0, 255, 1);
bank.AddSlider("Layer #3:", &gnFurGrassAlphaClip[3], 0, 255, 1);
bank.AddSlider("Layer #2:", &gnFurGrassAlphaClip[2], 0, 255, 1);
bank.AddSlider("Layer #1:", &gnFurGrassAlphaClip[1], 0, 255, 1);
bank.AddSlider("Layer #0 (bottom-most):", &gnFurGrassAlphaClip[0], 0, 255, 1);
bank.PopGroup();
bank.PushGroup("Grass 'darkening'", true);
bank.AddSlider("Layer #7 (top-most):", &gnFurGrassShadow[7], 0, 255, 1);
bank.AddSlider("Layer #6:", &gnFurGrassShadow[6], 0, 255, 1);
bank.AddSlider("Layer #5:", &gnFurGrassShadow[5], 0, 255, 1);
bank.AddSlider("Layer #4:", &gnFurGrassShadow[4], 0, 255, 1);
bank.AddSlider("Layer #3:", &gnFurGrassShadow[3], 0, 255, 1);
bank.AddSlider("Layer #2:", &gnFurGrassShadow[2], 0, 255, 1);
bank.AddSlider("Layer #1:", &gnFurGrassShadow[1], 0, 255, 1);
bank.AddSlider("Layer #0 (bottom-most):", &gnFurGrassShadow[0], 0, 255, 1);
bank.PopGroup();
bank.AddSlider("Fur Z offset:", &gfFurGrassZOffset, 0.0f, 0.2f, 0.001f);
bank.AddSlider("Fur step (divided by 10,000):", &gnFurGrassFurStep, 0, 1000, 1);
bank.AddSlider("UV scale (diffuse):", &gfFurGrassUvScale, 0.0f, 16.0f, 0.001f);
bank.AddSlider("UV scale (layer mask):", &gfFurGrassUvScale2, 0.0f, 16.0f, 0.001f);
bank.AddSlider("UV scale (normal):", &gfFurGrassUvScale3, 0.0f, 16.0f, 0.001f);
bank.AddSlider("Offset U:", &gfFurGrassOffsetU, 0.0f, 1.0f, 0.001f);
bank.AddSlider("Offset V:", &gfFurGrassOffsetV, 0.0f, 1.0f, 0.001f);
bank.AddSlider("Um scale X (divided by 100,000):", &gnFurGrassUmScaleX, 0, 10000, 1);
bank.AddSlider("Um scale Y (divided by 100,000):", &gnFurGrassUmScaleY, 0, 10000, 1);
bank.AddSlider("Fresnel:", &gfFurGrassFresnel, 0.0f, 1.0f, 0.01f);
bank.AddSlider("Spec Falloff:", &gfFurGrassSpecFalloff, 0.0f, 512.0f, 0.1f);
bank.AddSlider("Spec Intensity:", &gfFurGrassSpecInt, 0.0f, 1.0f, 0.01f);
bank.AddSlider("Bumpiness (divided by 1000):", &gfFurGrassBumpiness, 0.0f, 10.0f, 0.01f);
bank.AddToggle("Enable Mip Adjust:", &gbEnableFurGrassMipmapLodBias);
bank.AddSlider("Mip Adjust:", &gfFurGrassMipmapLodBias, -8.0f,8.0f, 0.125f);
bank.PopGroup();
#endif
bank.PopGroup();
return(TRUE);
}
#endif //__BANK...
//
//
//
//
void CPlantColBoundEntryArray::Init()
{
m_CloseListHead = 0; // NULL
m_UnusedListHead = 1; // address of first valid element of m_ColEntCache
for(u16 i=1; i<(CPLANT_COL_ENTITY_CACHE_SIZE+1); i++)
{
CPlantColBoundEntry *pEntry = &(*this)[i];
pEntry->m__pEntity = NULL;
pEntry->m_bMaterialBound= false;
pEntry->m_nBParentIndex = -1;
pEntry->m_nBChildIndex = -1;
pEntry->m_LocTriArray = NULL;
pEntry->m_nNumTris = 0;
pEntry->m_BoundMatProps.Kill();
#if PLANTSMGR_MULTI_UPDATE
pEntry->m_processedTris.Kill();
#endif
if(i==1)
pEntry->m_PrevEntry = 0;
else
pEntry->m_PrevEntry = GetIdx(pEntry-1);
if(i==CPLANT_COL_ENTITY_CACHE_SIZE)
pEntry->m_NextEntry = 0;
else
pEntry->m_NextEntry = GetIdx(pEntry+1);
}
}// end of CPlantColBoundEntryArray::Init()...
//
//
//
//
void CPlantLocTriArray::Init(u16 listID)
{
m_listID = listID;
m_CloseListHead = 0; // NULL
m_UnusedListHead = 1; // address of first valid entry in m_LocTrisTab[]
m_bRequireAmbScale = false;
for(u16 i=1; i<(CPLANT_MAX_CACHE_LOC_TRIS_NUM+1); i++)
{
CPlantLocTri *pLocTri = &(*this)[i];
pLocTri->m_V1 = Vector4(0,0,0,0);
pLocTri->m_V2 = Vector4(0,0,0,0);
pLocTri->m_V3 = Vector4(0,0,0,0);
pLocTri->m_nSurfaceType = 0;
pLocTri->m_skewAxisAngle.w.SetFloat16_Zero();
pLocTri->m_skewAxisAngle.x.SetFloat16_Zero();
pLocTri->m_skewAxisAngle.y.SetFloat16_Zero();
pLocTri->m_skewAxisAngle.z.SetFloat16_Zero();
#if CPLANT_STORE_LOCTRI_NORMAL || CPLANT_PRE_MULTIPLY_LIGHTING
pLocTri->m_normal[0] =
pLocTri->m_normal[1] =
pLocTri->m_normal[2] = 0;
#endif
if(i==1)
pLocTri->m_PrevTri = 0;
else
pLocTri->m_PrevTri = this->GetIdx(pLocTri-1);
if(i==CPLANT_MAX_CACHE_LOC_TRIS_NUM)
pLocTri->m_NextTri = 0;
else
pLocTri->m_NextTri = this->GetIdx(pLocTri+1);
}
}// end of CPlantLocTriArray::Init()...
//
//
// loads surface properties, initializes internal CPlantMgr lists:
//
bool CPlantMgr::ReloadConfig()
{
#if LOCTRISTAB_STR
if(m_LocTrisTab[0])
#endif
for(u16 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
m_LocTrisTab[listID]->Init( listID );
}
m_ColEntCache.Init();
#if !__FINAL
if (PARAM_DisablePlantMgr.Get())
gbPlantMgrActive = false;
#endif
#if RSG_PC
if(CSettingsManager::GetInstance().IsLowQualitySystem())
{
// AMD min spec video card + 4-core CPU. Extremely render thread
// bound in this case because of single-threaded driver, we cut
// plants manager (procedural grass + procedural trash, etc.) in
// this case.
gbPlantMgrActive = false;
}
#endif
return(TRUE);
}// end of CPlantMgr::ReloadConfig()...
//
//
//
//
void CPlantMgr::Shutdown(unsigned shutdownMode)
{
if(shutdownMode == SHUTDOWN_CORE)
{
gPlantMgr.ShutdownInternal();
}
else if(shutdownMode == SHUTDOWN_SESSION)
{
// clear whole BoundsCache:
u16 entry = gPlantMgr.m_ColEntCache.m_CloseListHead;
while(entry)
{
CPlantColBoundEntry *pEntry = &gPlantMgr.m_ColEntCache[entry];
entry = pEntry->m_NextEntry;
pEntry->ReleaseEntry();
}
}
}
void CPlantMgr::ShutdownInternal()
{
CPlantMgr::Shutdown(SHUTDOWN_WITH_MAP_LOADED);
CGrassRenderer::Shutdown();
FreeStrLocTriTabs();
ShutdownTextures();
for(s32 i=0; i<CPLANT_SLOT_NUM_MODELS*2; i++)
{
UnloadPlantModel(&m_PlantModelsTab0[i]);
}
// destroy allocated drawables:
g_DwdStore.RemoveRef(g_DwdStore.FindSlot(CPLANTMGR_MODELS_PATH), REF_OTHER);
gpPlantMgrModelDictionary = NULL;
}// end of CPlantMgr::Shutdown()...
#if !PSN_PLANTSMGR_SPU_UPDATE
//
// name: CPlantMgr::UpdateTask
// description: Task to do most of the plant mgr update
//
void CPlantMgr::UpdateTask(u32 listID, s32 iTriProcessSkipMask)
{
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
#if FURGRASS_TEST_V4
u32 *pFurgrassTagPresent = &m_nFurgrassTagPresentTab[listID];
#else
u32 *pFurgrassTagPresent = NULL;
#endif
//
// update LocTris in CEntities in _ColEntityCache:
//
UpdateAllLocTris(triTab, m_CameraPos, iTriProcessSkipMask, pFurgrassTagPresent);
}
void CPlantMgr::RenderTabMemcpy(u32 listID)
{
Assert(listID < CPLANT_LOC_TRIS_LIST_NUM);
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
if(triTab.m_CloseListHead)
{
sysMemCpy(m_LocTrisRenderTab[GetUpdateRTBufferID()][listID], &triTab, sizeof(CPlantLocTriArray));
}
else
{ // final list is empty after update - just erase target RT close list (it will be recopied next time src update list becomes non-empty):
m_LocTrisRenderTab[GetUpdateRTBufferID()][listID]->m_CloseListHead = 0;
}
}
#if PLANTSMGR_MULTI_UPDATE
static void UpdatePlantMgrSubTask(sysTaskParameters &params)
{
// sysThreadType::AddCurrentThreadType(sysThreadType::THREAD_TYPE_UPDATE);
const u32 listID = params.UserData[0].asInt;
Assert(listID < CPLANT_LOC_TRIS_LIST_NUM);
const s32 iTriProcessSkipMask = params.UserData[1].asInt;
#if ENABLE_PIX_TAGGING
const char* pixTagName[] = { "PlantsMgrUpdateTask0", "PlantsMgrUpdateTask1", "PlantsMgrUpdateTask2", "PlantsMgrUpdateTask3",
"PlantsMgrUpdateTask4", "PlantsMgrUpdateTask5", "PlantsMgrUpdateTask6", "PlantsMgrUpdateTask7" };
PIXBegin(1, pixTagName[listID]);
#else
PIXBegin(1, "PlantsMgrUpdateTask");
#endif
gPlantMgr.UpdateTask(listID, iTriProcessSkipMask);
PIXEnd();
}
#else //PLANTSMGR_MULTI_UPDATE
static void UpdatePlantMgrTask(sysTaskParameters &params)
{
PIXBegin(1, "PlantsMgrUpdateTask");
const s32 iTriProcessSkipMask = params.UserData[1].asInt;
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
gPlantMgr.UpdateTask(listID, iTriProcessSkipMask);
}
#if FURGRASS_TEST_V4
// store fur grass pickup info for RT:
gPlantMgr.FurGrassStoreRenderInfo(gPlantMgr.m_furGrassPickupRenderInfo[gPlantMgr.GetUpdateRTBufferID()], 0);
#endif
PIXEnd();
}
#endif //PLANTSMGR_MULTI_UPDATE...
#endif //!PSN_PLANTSMGR_SPU_UPDATE...
//
//
//
//
#if PLANTSMGR_MULTI_UPDATE
static sysTaskHandle s_PlantMgrTaskHandle[PLANTSMGR_MULTI_UPDATE_TASKCOUNT] = {0};
#else
static sysTaskHandle s_PlantMgrTaskHandle = 0;
#endif
#if PSN_PLANTSMGR_SPU_UPDATE
DECLARE_TASK_INTERFACE(PlantsMgrUpdateSPU);
static ProcObjectCreationInfo s_SpuProcObjectCreationInfo[512*3+384] ;
static ProcTriRemovalInfo s_SpuProcTriRemovalInfo[512*3] ;
static spuPlantsMgrUpdateStruct::CResultSize s_PlantMgrResultSize;
#endif //PSN_PLANTSMGR_SPU_UPDATE...
//
//
//
//
bool CPlantMgr::UpdateBegin()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
USE_MEMBUCKET(MEMBUCKET_RENDER);
if(!gbPlantMgrActive) // skip updating, if varconsole switch is off
return(FALSE);
#if LOCTRISTAB_STR
if(m_LocTrisTab[0] == NULL)
{ // loc tris tabs not allocated, so skip:
return(TRUE);
}
#endif
#if __BANK
if (TiledScreenCapture::IsEnabled())
return(FALSE);
ReloadProceduralMeta();
#endif
#if __BANK
// recalculate editable loctri far distances as required:
ms_bkColTestRadius = gbPlantsLocTriFarDist - 5.0f;
ms_bkTriLocFarDist = gbPlantsLocTriFarDist;
ms_bkTriLocFarDistSqr = ms_bkTriLocFarDist*ms_bkTriLocFarDist;
ms_bkTriLocShortFarDist = ms_bkTriLocFarDist - CPLANT_INCREASE_DIST;
ms_bkTriLocShortFarDistSqr = ms_bkTriLocShortFarDist*ms_bkTriLocShortFarDist;
#endif
#if __BANK
CGrassRenderer::SetDebugModeEnable(gbShowAlphaOverdraw);
#endif
CPlantMgr::AdvanceCurrentScanCode();
const Vector3& cameraPos = camInterface::GetPos();
const Vec3V cameraPosV = RCC_VEC3V(cameraPos);
const Vector3& cameraFront = camInterface::GetFront();
#if PLANTSMGR_DATA_EDITOR
if(m_bRegenTriCaches)
{
m_bRegenTriCaches = false;
CPlantMgr::PreUpdateOnceForNewCameraPos(cameraPos+Vector3(10000,10000,10000));
}
#endif
m_CameraPos = cameraPos;
CGrassRenderer::SetGlobalCameraPos(cameraPos);
CGrassRenderer::SetGlobalCameraFront(cameraFront);
// is camera in first person perspective mode?
bool bCameraFppEnabled = false;
if(camInterface::GetGameplayDirectorSafe())
{
const camGameplayDirector& GameplayDirector = camInterface::GetGameplayDirector();
const camFirstPersonAimCamera* pAimCamera = GameplayDirector.GetFirstPersonAimCamera();
if(pAimCamera && camInterface::IsDominantRenderedCamera(*pAimCamera))
{
bCameraFppEnabled = true;
}
}
CGrassRenderer::SetGlobalCameraFppEnabled(bCameraFppEnabled);
CGrassRenderer::SetGlobalCameraUnderwater(Water::IsCameraUnderwater());
CGrassRenderer::SetGlobalForceHDGrassGeometry(GetForceHDGrass());
// get current player pos:
const Vector3 vecPlayerPos = CGameWorld::FindLocalPlayerCoors();
CGrassRenderer::SetGlobalPlayerPos(vecPlayerPos);
CGrassRenderer::SetGlobalPlayerCollisionRSqr(1.0f);
CPed* pPlayerPed = CGameWorld::FindLocalPlayer();
if(pPlayerPed)
{
CVehicle *pPlayerVeh = pPlayerPed->GetVehiclePedInside();
if(pPlayerVeh)
{
const VehicleType vehType = pPlayerVeh->GetVehicleType();
if(vehType==VEHICLE_TYPE_SUBMARINE)
{
CGrassRenderer::SetGlobalPlayerCollisionRSqr(3.2f*3.2f); // BS#1494465: hack: override player collision for submersible
}
if(vehType==VEHICLE_TYPE_SUBMARINECAR)
{
CSubmarineCar *pSubmarineCar = (CSubmarineCar*)pPlayerVeh;
if(pSubmarineCar->IsInSubmarineMode())
{
CGrassRenderer::SetGlobalPlayerCollisionRSqr(1.4f*1.4f); // BS#1778095: hack: override player collision for underwater stromberg
}
}
}
if(pPlayerPed->GetPedType() == PEDTYPE_ANIMAL)
{
static dev_float peyoteScale = 0.01f;
CGrassRenderer::SetGlobalPlayerCollisionRSqr(peyoteScale*peyoteScale);
}
}
spdTransposedPlaneSet8 cullFrustum;
#if NV_SUPPORT
cullFrustum.SetStereo( *gVpMan.GetUpdateGameGrcViewport() );
#else
cullFrustum.Set( *gVpMan.GetUpdateGameGrcViewport() );
#endif
// Interior portal:
bool bInteriorCullAll = false;
static dev_bool bEnableInteriorTest = true;
if(bEnableInteriorTest && g_SceneToGBufferPhaseNew && g_SceneToGBufferPhaseNew->GetPortalVisTracker() && g_SceneToGBufferPhaseNew->GetPortalVisTracker()->GetInteriorLocation().IsValid())
{
// the camera is in interior:
// is the exterior visible at all with the current camera?
bInteriorCullAll = !fwScan::GetScanResults().GetIsExteriorVisibleInGbuf();
if(bInteriorCullAll==false)
{
// the part of the screen the exterior is visible in:
fwScreenQuad quad = fwScan::GetScanResults().GetExteriorGbufScreenQuad();
// calculate new 3D frustum to cull proc plants through portals:
quad.GenerateFrustum(g_SceneToGBufferPhaseNew->GetGrcViewport().GetCullCompositeMtx(), cullFrustum);
}
}
CGrassRenderer::SetGlobalCullFrustum(cullFrustum); // current viewport's or interior portal's culling frustum
CGrassRenderer::SetGlobalInteriorCullAll(bInteriorCullAll);
bool bCameraVehicleFPV = false; // camera in First Person View in vehicle?
#if FPS_MODE_SUPPORTED
bCameraVehicleFPV = camInterface::GetCinematicDirector().IsRenderingAnyInVehicleFirstPersonCinematicCamera();
#endif
// find 4 nearest vehicles to camera:
CVehicle* pVehicles[CGrassRenderer::NUM_COL_VEH] = {NULL, NULL, NULL, NULL };
float fVehicleDist[CGrassRenderer::NUM_COL_VEH] = {999999.0f, 999999.0f, 999999.0f, 999999.0f }; // vehicle dist sqr to camera
// Grab the spatial array.
const CSpatialArray& vehSpatialArray = CVehicle::GetSpatialArray();
const int sMaxResults = 16;
static dev_float fMaxDistance = 40.0f;
CSpatialArray::FindResult result[sMaxResults];
const int iFound = vehSpatialArray.FindInSphere(cameraPosV, fMaxDistance, &result[0], sMaxResults);
int iEmptySlot = 0;
//Iterate over the results.
for(int iVeh=0; iVeh<iFound; iVeh++)
{
CVehicle* pVehicle = CVehicle::GetVehicleFromSpatialArrayNode(result[iVeh].m_Node);
Assert(pVehicle);
const VehicleType vehType = pVehicle->GetVehicleType();
// disable vehicle collision for vehicles with 0 wheels (e.g. proptrailer):
pVehicle = (!pVehicle || pVehicle->GetNumWheels()==0)? NULL : pVehicle;
// disable vehicle collision for helis and planes:
pVehicle = ((vehType==VEHICLE_TYPE_HELI) || (vehType==VEHICLE_TYPE_PLANE) || (vehType==VEHICLE_TYPE_BLIMP))? NULL : pVehicle;
pVehicle = ((vehType==VEHICLE_TYPE_BOAT) || (vehType==VEHICLE_TYPE_SUBMARINE))? NULL : pVehicle;
pVehicle = (vehType==VEHICLE_TYPE_BICYCLE)? NULL : pVehicle;
// BS#1778095: special case for stromberg - collide only when NOT in submarine mode:
if(pVehicle && vehType==VEHICLE_TYPE_SUBMARINECAR)
{
CSubmarineCar *pSubmarineCar = (CSubmarineCar*)pVehicle;
pVehicle = pSubmarineCar->IsInSubmarineMode()? NULL : pVehicle;
if(pVehicle)
{
// is stromberg attached to another vehicle (e.g. cargoplane - BS#1786565)?:
const fwAttachmentEntityExtension *vehExt = pVehicle->GetAttachmentExtension();
if(vehExt)
{
pVehicle = vehExt->GetIsAttached()? NULL : pVehicle;
}
}
}
if(pVehicle)
{ // [HACK GTAV] BS#1105797: disable grass collision for dump:
// [HACK GTAV] BS#1945307: disable grass collision for monster;
// [HACK GTAV] BS#1998362: disable grass collision for trailers;
const u32 uModelNameHash = pVehicle->GetBaseModelInfo()->GetModelNameHash();
pVehicle = (uModelNameHash==MID_DUMP)? NULL : pVehicle;
pVehicle = (uModelNameHash==MID_MONSTER)? NULL : pVehicle;
pVehicle = (uModelNameHash==MID_TRAILERS)? NULL : pVehicle;
pVehicle = (uModelNameHash==MID_TRAILERS2)? NULL : pVehicle;
pVehicle = (uModelNameHash==MID_TRAILERS3)? NULL : pVehicle;
pVehicle = (uModelNameHash==MID_TRAILERLOGS)? NULL : pVehicle;
}
if(pVehicle)
{
const float distSqr = result[iVeh].m_DistanceSq;
if(iEmptySlot < CGrassRenderer::NUM_COL_VEH)
{
fVehicleDist[iEmptySlot]= distSqr;
pVehicles[iEmptySlot] = pVehicle;
iEmptySlot++;
continue;
}
// find vehicle's index which is most far away from the camera:
u32 fari = 0;
float farDist = fVehicleDist[0];
for(u32 i=1; i<CGrassRenderer::NUM_COL_VEH; i++)
{
if( fVehicleDist[i] > farDist )
{
farDist = fVehicleDist[i];
fari = i;
}
}
Assert(fari < CGrassRenderer::NUM_COL_VEH);
// check against current vehicle:
// dist2 smaller than furthest found?
if( distSqr < farDist )
{
fVehicleDist[fari] = distSqr;
pVehicles[fari] = pVehicle;
}
}
}// (int iVeh=0; iVeh<iFound; iVeh++)...
for(u32 v=0; v<CGrassRenderer::NUM_COL_VEH; v++)
{
CVehicle *pVehicle = pVehicles[v];
if(bCameraVehicleFPV)
{
// BS#2057861: FPV in vehicle - always enable grass collision
}
else
{
if(pVehicle && (pVehicle->IsUpsideDown() || pVehicle->IsOnItsSide()))
{
pVehicle = NULL; // no grass collision if vehicle is not firmly on the ground
}
// BS#2509157: check if lowrider hydraulics car is bouncing:
bool bIsCarHydraulicsBouncing = false;
if(pVehicle && pVehicle->InheritsFromAutomobile())
{
CAutomobile *pCar = (CAutomobile*)pVehicle;
if(pCar && (pVehicle->m_nVehicleFlags.bPlayerModifiedHydraulics || pCar->m_nAutomobileFlags.bHydraulicsBounceRaising || pCar->m_nAutomobileFlags.bHydraulicsBounceLanding || pCar->m_nAutomobileFlags.bHydraulicsJump))
{
bIsCarHydraulicsBouncing = true;
}
}
const u32 modelNameHash = pVehicle? pVehicle->GetBaseModelInfo()->GetModelNameHash() : 0;
// BS#4036767 - Deluxo - Changing from driving to flying/floating affects grass collision
bool bIsHooveringDeluxo = false;
if(pVehicle && modelNameHash==MID_DELUXO)
{
bIsHooveringDeluxo = pVehicle->HoveringCloseToGround();
}
if(bIsCarHydraulicsBouncing)
{
// do nothing - vehicle is bouncing via hydraulics
}
else if(pVehicle && modelNameHash==MID_BLAZER5)
{
// do nothing - special case for blazer5
}
else if(bIsHooveringDeluxo)
{
// do nothing - deluxo is hoovering close to the ground
}
else if(pVehicle && (pVehicle->GetNumContactWheels() < pVehicle->GetNumWheels()))
{
pVehicle = NULL; // no grass collision if vehicle is not firmly on the ground
}
// B*2491123: disable collision for attached vehicles:
if(pVehicle && pVehicle->GetIsAttached())
{
CPhysical* pAttachParent = (CPhysical*)pVehicle->GetAttachParent();
if(pAttachParent)
{
pVehicle = NULL; // no grass collision for attached vehicles
}
}
}
if(pVehicle)
{
// grab unmodified (e.g. by opened cardoors, etc.) vehicle's bbox:
const Vector3 bboxmin = pVehicle->GetBaseModelInfo()->GetBoundingBoxMin(); //pVehicle->GetBoundingBoxMin();
const Vector3 bboxmax = pVehicle->GetBaseModelInfo()->GetBoundingBoxMax(); //pVehicle->GetBoundingBoxMax();
// Printf("\n cars bboxmin: %.2f, %.2f, %.2f", bboxmin.x, bboxmin.y, bboxmin.z);
// Printf("\n cars bboxmax: %.2f, %.2f, %.2f", bboxmax.x, bboxmax.y, bboxmax.z);
// Vector3 bboxminW = pVehicle->TransformIntoWorldSpace(bboxmin);
// Vector3 bboxmaxW = pVehicle->TransformIntoWorldSpace(bboxmax);
// Printf("\n cars bboxminW: %.2f, %.2f, %.2f", bboxminW.x, bboxminW.y, bboxminW.z);
// Printf("\n cars bboxmaxW: %.2f, %.2f, %.2f", bboxmaxW.x, bboxmaxW.y, bboxmaxW.z);
// example:
// cars bboxmin0: -1.07, -2.99, -0.83
// cars bboxmax0: 1.07, 2.53, 0.76
// cars bboxmin: -1232.78, -970.34, 398.32
// cars bboxmax: -1230.58, -964.85, 399.89
// two points: on the front and back of the car:
float middleX = bboxmin.x + (bboxmax.x - bboxmin.x)*0.5f;
float middleZ = bboxmin.z + (bboxmax.z - bboxmin.z)*0.5f;
static dev_float radiusPerc = 1.05f; // how much make bounding shape "shorter" to fit better around vehicle
const u32 vehNameHash = pVehicle->GetBaseModelInfo()->GetModelNameHash();
// front/rear Y shift overrides:
float frontShiftY = 0.0f;
float rearShiftY = 0.0f;
float sideShift = 0.0f;
// BS#1777745: bodhi2: magic shift grass collision is not producing "halo" at the rear of vehicle:
if(vehNameHash == MID_BODHI2)
{
rearShiftY = -0.7f;
}
// BS#1918403: Bulldozer flattens grass without being touched when bucket is up;
if(vehNameHash == MID_BULLDOZER)
{
rearShiftY = -0.8f;
// loop through gadgets until we found the digger arm
for(int i = 0; i < pVehicle->GetNumberOfVehicleGadgets(); i++)
{
CVehicleGadget *pVehicleGadget = pVehicle->GetVehicleGadget(i);
if(pVehicleGadget->GetType() == VGT_DIGGER_ARM)
{
CVehicleGadgetDiggerArm *pDiggerArm = static_cast<CVehicleGadgetDiggerArm*>(pVehicleGadget);
Assert(pDiggerArm);
if(pDiggerArm)
{
const float fArmPosRatio = pDiggerArm->GetJointPositionRatio();
static dev_float minPosRatio = 0.00f;
static dev_float maxPosRatio = 0.36f;
float fArmLerpRatio = Clamp((fArmPosRatio-minPosRatio)/(maxPosRatio-minPosRatio), 0.0f, 1.0f);
frontShiftY = Lerp(fArmLerpRatio, -0.25f, 1.9f);
}
}
}
}// if(vehNameHash == MID_BULLDOZER)...
// BS#1992186: granger side adjustments;
if (vehNameHash == MID_GRANGER)
{
sideShift = 0.42f;
}
else if(vehNameHash == MID_GRANGER2)
{
sideShift = 0.50f;
}
// BS#1992186: riot side adjustments;
else if (vehNameHash == MID_RIOT)
{
sideShift = 0.40f;
}
// BS#1925880: zentorno side adjustments;
else if (vehNameHash == MID_ZENTORNO)
{
sideShift = 0.40f;
}
// BS#2278312: rhino front/rear adjustments:
else if (vehNameHash == MID_RHINO)
{
frontShiftY = 2.6f;
rearShiftY = -0.3f;
}
// BS#2333618: T20 side adjustments:
else if (vehNameHash == MID_T20)
{
sideShift = 0.45f;
}
// BS#3120202: Large Area around Phantom2 is cleared when driving off-road:
else if (vehNameHash == MID_PHANTOM2)
{
frontShiftY = 3.0f;
sideShift = 0.75f;
}
// BS#3334666 - GP1 - Large area cleared around vehicle
else if (vehNameHash == MID_INFERNUS2)
{
sideShift = 0.40f;
}
// BS#3334666 - GP1 - Large area cleared around vehicle
else if (vehNameHash == MID_GP1)
{
sideShift = 0.80f;
}
// BS#3477152 - XA-21 - Large area cleared around the vehicle
else if (vehNameHash == MID_XA21)
{
sideShift = 0.70f;
}
// BS#3888877 - Visione - Excessive collision of vegetation
else if (vehNameHash == MID_VISIONE)
{
sideShift = 0.50f;
}
// BS#4028275 - Riot2 - Excess grass & vegeatation clearance
else if (vehNameHash == MID_RIOT2)
{
sideShift = 0.6f;
rearShiftY = -0.4f;
}
// BS#4068209 - Barrage - Too much foliage cleared behind the vehicle
else if (vehNameHash == MID_BARRAGE)
{
frontShiftY = 0.4f;
rearShiftY = -1.0f;
sideShift = 0.3f;
}
// BS#4176577 - Chernobog - Area for flattening grass is too big
else if (vehNameHash == MID_CHERNOBOG)
{
frontShiftY = 1.0f;
rearShiftY = -0.5f;
sideShift = 0.6f;
}
// BS#4194918 - Khanjali - Grass flattening area is larger than vehicle
else if (vehNameHash == MID_KHANJALI)
{
frontShiftY = 1.0f;
rearShiftY = -0.4f;
sideShift = 0.3f;
}
// BS#198885 - Autarch - Grass collision bounds:
else if (vehNameHash == MID_AUTARCH)
{
sideShift = 0.5f;
rearShiftY = -0.2f;
}
// BS#4623613 - Excessive clearing of foliage around Mule4:
else if (vehNameHash == MID_MULE4)
{
frontShiftY = 0.5f;
rearShiftY = -0.4f;
sideShift = 0.85f;
}
// BS#4623697 - Pounder2 - Excessive clearing of foliage;
else if (vehNameHash == MID_POUNDER2)
{
frontShiftY = 0.5f;
sideShift = 0.8f;
}
// BS#4702207 - pbus2 has too large area that flattens grass
else if (vehNameHash == MID_PBUS2)
{
frontShiftY = 1.0f;
rearShiftY = -3.1f;
sideShift = 1.3f;
}
// BS#5459805 - Monster3 - Foliage grass flatten area is very large on this vehicle.
else if (vehNameHash == MID_MONSTER3 || vehNameHash == MID_MONSTER4 || vehNameHash == MID_MONSTER5)
{
frontShiftY = 3.2f;
rearShiftY = -0.5f;
sideShift = 0.5f;
}
// BS#5388815 - Bruiser - Vehicle has a large foliage flatten area
else if (vehNameHash == MID_BRUISER || vehNameHash == MID_BRUISER2 || vehNameHash == MID_BRUISER3)
{
frontShiftY = 1.2f;
rearShiftY = -1.0f;
sideShift = 0.7f;
}
// BS#5354204 - Scarab - Excessive foliage suppression
else if (vehNameHash == MID_SCARAB || vehNameHash == MID_SCARAB2 || vehNameHash == MID_SCARAB3)
{
frontShiftY = 1.4f;
rearShiftY = -0.3f;
sideShift = 0.4f;
}
// BS#5350080 - Deveste - Vegetation cleared quite far
else if (vehNameHash == MID_DEVESTE)
{
frontShiftY = -0.05f;
rearShiftY = 0.0f;
sideShift = 0.4f;
}
// BS#5415156 - ZR380 - Area that flattens grass is too big
else if (vehNameHash == MID_ZR380 || vehNameHash == MID_ZR3802 || vehNameHash == MID_ZR3803)
{
frontShiftY = 0.6f;
rearShiftY = -0.2f;
sideShift = 0.35f;
}
// BS#5423756 - Cerberus - Vehicle has a large foliage flatten area
else if (vehNameHash == MID_CERBERUS || vehNameHash == MID_CERBERUS2 || vehNameHash == MID_CERBERUS3)
{
frontShiftY = 1.6f;
rearShiftY = -1.0f;
sideShift = 0.6f;
}
// BS#5459616 - Slamvan4 - Foliage suppression too large
else if (vehNameHash == MID_SLAMVAN4 || vehNameHash == MID_SLAMVAN5 || vehNameHash == MID_SLAMVAN6)
{
frontShiftY = 0.35f;
rearShiftY = -0.75f;
sideShift = 0.1f;
}
// BS#5476478 - Dominator5 - excess foliage collision
else if (vehNameHash == MID_DOMINATOR3)
{
rearShiftY = -0.2f;
sideShift = 0.2f;
}
else if (vehNameHash == MID_DOMINATOR4 || vehNameHash == MID_DOMINATOR5 || vehNameHash == MID_DOMINATOR6)
{
frontShiftY = 0.85f;
rearShiftY = 0.1f;
sideShift = 0.25f;
}
// BS#5457801 - Vehicles with Scoop Mods have large foliage flatten around it
else if (vehNameHash == MID_BRUTUS || vehNameHash == MID_BRUTUS2 || vehNameHash == MID_BRUTUS3)
{
frontShiftY = 1.3f;
rearShiftY = -0.5f;
sideShift = 0.3f;
}
else if (vehNameHash == MID_IMPALER2 || vehNameHash == MID_IMPALER3 || vehNameHash == MID_IMPALER4)
{
frontShiftY = 0.7f;
rearShiftY = -0.1f;
sideShift = 0.2f;
}
else if (vehNameHash == MID_IMPERATOR || vehNameHash == MID_IMPERATOR3)
{
frontShiftY = 0.7f;
rearShiftY = -0.35f;
sideShift = 0.3f;
}
else if (vehNameHash == MID_IMPERATOR2)
{
frontShiftY = 0.4f;
rearShiftY = -0.35f;
sideShift = 0.3f;
}
else if (vehNameHash == MID_ISSI4 || vehNameHash == MID_ISSI5 || vehNameHash == MID_ISSI6)
{
frontShiftY = 0.8f;
rearShiftY = -0.1f;
sideShift = 0.3f;
}
else if (vehNameHash == MID_DEATHBIKE || vehNameHash == MID_DEATHBIKE2 || vehNameHash == MID_DEATHBIKE3)
{
sideShift = 0.4f;
}
else if (vehNameHash == MID_EMERUS)
{
rearShiftY = -0.3f;
sideShift = 0.15f;
}
else if (vehNameHash == MID_NEO)
{
sideShift = 0.3f;
}
else if (vehNameHash == MID_KRIEGER)
{
rearShiftY = -0.2f;
sideShift = 0.1f;
}
else if (vehNameHash == MID_S80)
{
frontShiftY = -0.1f;
rearShiftY = -0.2f;
}
else if (vehNameHash == MID_IMORGON)
{
frontShiftY = -0.05f;
rearShiftY = -0.15f;
sideShift = 0.1f;
}
else if (vehNameHash == MID_FORMULA)
{
frontShiftY = -0.3f;
sideShift = 0.2f;
}
else if (vehNameHash == MID_REBLA)
{
sideShift = 0.1f;
}
else if(vehNameHash == MID_POLBUFFALO)
{
frontShiftY = -0.08f;
rearShiftY = 0.05f;
sideShift = 0.05f;
}
else if(vehNameHash == MID_YOUGA3)
{
frontShiftY = 0.3f;
rearShiftY = 0.1f;
sideShift = 0.1f;
}
else if(vehNameHash == MID_YOUGA4)
{
frontShiftY = 0.3f;
rearShiftY = -0.2f;
sideShift = 0.4f;
}
else if(vehNameHash == MID_OPENWHEEL1)
{
frontShiftY = -0.05f;
rearShiftY = -0.2f;
sideShift = 0.2f;
}
else if(vehNameHash == MID_VERUS)
{
frontShiftY = 0.15f;
rearShiftY = -0.3f;
sideShift = 0.1f;
}
else if(vehNameHash == MID_IGNUS)
{
rearShiftY = -0.2f;
sideShift = 0.2f;
}
else if(vehNameHash == MID_DEITY)
{
sideShift = 0.15f;
}
else if(vehNameHash == MID_ZENO)
{
sideShift = 0.75f;
}
else if(vehNameHash == MID_COMET7)
{
sideShift = 0.15f;
}
else if(vehNameHash == MID_BALLER7)
{
sideShift = 0.15f;
}
else if(vehNameHash == MID_PATRIOT3)
{
sideShift = 0.3f;
}
else if(vehNameHash == MID_TORERO2)
{
rearShiftY = -0.1f;
sideShift = 0.4f;
}
else if(vehNameHash == MID_CORSITA)
{
frontShiftY = 0.1f;
rearShiftY = -0.2f;
sideShift = 1.05f;
}
else if(vehNameHash == MID_KANJOSJ)
{
frontShiftY = 0.2f;
sideShift = 0.2f;
}
else if(vehNameHash == MID_WEEVIL2)
{
frontShiftY = 0.3f;
rearShiftY = -0.5f;
sideShift = 0.4f;
}
static dev_bool bOverrideFrontShiftY=false;
if(bOverrideFrontShiftY)
{
static dev_float frontShiftYOverride = 1.9f;
frontShiftY = frontShiftYOverride;
}
static dev_bool bOverrideRearShiftY=false;
if(bOverrideRearShiftY)
{
static dev_float rearShiftYOverride = -0.7f;
rearShiftY = rearShiftYOverride;
}
static dev_bool bOverrideSideShift=false;
if(bOverrideSideShift)
{
static dev_float sideShiftOverride = 0.2f;
sideShift = sideShiftOverride;
}
float Radius = (bboxmax.x - bboxmin.x)*0.5f - sideShift; // radius
Vector3 ptb(middleX, bboxmax.y - Radius*radiusPerc - frontShiftY, middleZ);
Vector3 pta(middleX, bboxmin.y + Radius*radiusPerc - rearShiftY, middleZ);
// first point on segment:
Vector3 ptB = pVehicle->TransformIntoWorldSpace(ptb);
// last point on segment:
Vector3 ptA = pVehicle->TransformIntoWorldSpace(pta);
// segment vector:
Vector3 vecM = ptA - ptB;
// Printf("\n ptB: %.2f, %.2f, %2.f", ptB.x, ptB.y, ptB.z);
// Printf("\n ptA: %.2f, %.2f, %2.f", ptA.x, ptA.y, ptA.z);
// Printf("\n vecM: %.2f, %.2f, %2.f; radius=%.2f", vecM.x, vecM.y, vecM.z, Radius);
// detect groundZ pos (from wheel hit pos):
Vector3 wheelHitPos(0,0,100000);
const s32 numWheels = pVehicle->GetNumWheels();
for(s32 i=0; i<numWheels; i++)
{
if(pVehicle->GetWheel(i))
{
// select wheelHitPos with lowest Z (to allow for steep slopes - see BS#978552, BS#1725071):
Vector3 wheelPos = pVehicle->GetWheel(i)->GetHitPos();
if(wheelPos.z < wheelHitPos.z)
{
wheelHitPos = wheelPos;
}
}
}
static dev_bool bOverrideZ = FALSE;
static float overrideZ = 398.44f; // test for testbed
float groundZ = bOverrideZ? overrideZ : wheelHitPos.z;
static dev_float tweakRadiusScale = 1.0f; // v1 collision
//static dev_float tweakRadiusScale = 1.25f; // v2 collision
CGrassRenderer::SetGlobalVehCollisionParams(v, TRUE, ptB, vecM, Radius*tweakRadiusScale, groundZ);
}
else
{
CGrassRenderer::SetGlobalVehCollisionParams(v, FALSE, Vector3(0,0,0), Vector3(0,0,0), 0, 0);
}
}// for(u32 v=0; v<CGrassRenderer::NUM_COL_VEH; v++)...
#if FURGRASS_TEST_V4
if(pPlayerPed)
{
const s32 leftFootBoneIdx = pPlayerPed->GetBoneIndexFromBoneTag(BONETAG_L_FOOT);
const s32 rightFootBoneIdx = pPlayerPed->GetBoneIndexFromBoneTag(BONETAG_R_FOOT);
if (leftFootBoneIdx != -1 && rightFootBoneIdx != -1)
{
Matrix34 leftFootMat;
Matrix34 rightFootMat;
pPlayerPed->GetGlobalMtx(leftFootBoneIdx, leftFootMat);
pPlayerPed->GetGlobalMtx(rightFootBoneIdx, rightFootMat);
// move positions forward, so fur collision circle better encircles both feet:
const Vector3 vecForward(1,0,0);
Vector3 LForward, RForward;
leftFootMat.Transform3x3(vecForward, LForward);
rightFootMat.Transform3x3(vecForward, RForward);
static dev_float scaleLFwdX = 0.1f;
static dev_float scaleLFwdY = 0.1f;
static dev_float scaleLFwdZ = 0.0f;
const Vector3 vecScaleFwd(scaleLFwdX, scaleLFwdY, scaleLFwdZ);
Vector4 lFootPos(leftFootMat.d + LForward*vecScaleFwd);
Vector4 rFootPos(rightFootMat.d + RForward*vecScaleFwd);
lFootPos.w = rFootPos.w = 0.0f;
CGrassRenderer::SetGlobalPlayerFeetPos(lFootPos, rFootPos);
}
}
#endif //FURGRASS_TEST_V4...
Vector2 bending;
CPlantMgr::CalculateWindBending(bending);
bending.x = rage::Abs(bending.x);
bending.y = rage::Abs(bending.y);
CGrassRenderer::SetGlobalWindBending(bending);
#if __BANK
const float amount = gbPlantsDarknessAmountAdd;
#else
const float amount = 0.0f;
#endif
CPlantMgr::CalculateFakeGrassNormal(amount);
//
// update ColEntityCache:
//
static u8 nUpdateEntCache = 0;
//
// do not update this stuff too often:
//
if( !((++nUpdateEntCache)&(CPLANT_COL_ENTITY_UPDATE_CACHE-1)) )
{
// full update of entry cache (FULL update):
_ColBoundCache_Update(m_CameraPos);
}
else
{
// quick update:
_ColBoundCache_Update(m_CameraPos, TRUE);
}
#if PSN_PLANTSMGR_SPU_UPDATE
// launch SPU job:
const u32 outputSize = 0;
const u32 scratchSize = 0;
const u32 plantsBaseSize= (SPU_SIZEOF_CPLANTMGRBASE+1024)/1024; // 68KB
#if CPLANT_DYNAMIC_CULL_SPHERES || CPLANT_CLIP_EDGE_VERT
const u32 stackSize = (86+plantsBaseSize)*1024;
#else
const u32 stackSize = (89+plantsBaseSize)*1024;
#endif
sysTaskContext c(TASK_INTERFACE(PlantsMgrUpdateSPU), outputSize, scratchSize, stackSize);
c.SetInputOutput();
c.AddInput(&g_procInfo, sizeof(g_procInfo)); // 27.64KB
c.AddInput(g_procInfo.m_procObjInfos.GetElements(), sizeof(CProcObjInfo) * g_procInfo.m_procObjInfos.GetCount());
c.AddInput(g_procInfo.m_plantInfos.GetElements(), sizeof(CPlantInfo) * g_procInfo.m_plantInfos.GetCount());
spuPlantsMgrUpdateStruct& structUpdate = *c.AllocUserDataAs<spuPlantsMgrUpdateStruct>(); // 0.08KB
structUpdate.m_pPlantsMgr = this;
structUpdate.m_camPos = cameraPos;
structUpdate.m_GroundSlopeAngleMin = PlantsMinGroundAngleSlope;
structUpdate.m_bCullSphereEnabled0 = m_CullSphereEnabled[0];
structUpdate.m_bCullSphereEnabled1 = m_CullSphereEnabled[1];
structUpdate.m_cullSphere[0] = m_CullSphere[0];
structUpdate.m_cullSphere[1] = m_CullSphere[1];
#if __BANK
structUpdate.m_gbForceDefaultGroundColor = gbForceDefaultGroundColor;
structUpdate.m_gbDefaultGroundColor = gbDefaultGroundColor;
structUpdate.m_bkTriLocFarDistSqr = ms_bkTriLocFarDistSqr;
structUpdate.m_bkTriLocShortFarDistSqr = ms_bkTriLocShortFarDistSqr;
// g_procObjMan flags:
structUpdate.m_ignoreMinDist = g_procObjMan.m_ignoreMinDist;
structUpdate.m_forceOneObjPerTri = g_procObjMan.m_forceOneObjPerTri;
structUpdate.m_ignoreSeeding = g_procObjMan.m_ignoreSeeding;
structUpdate.m_enableSeeding = g_procObjMan.m_enableSeeding;
structUpdate.m_disableCollisionObjects = g_procObjMan.m_disableCollisionObjects;
structUpdate.m_printSpuJobTimings = gbPrintUpdateSpuJobTimings;
#if PLANTSMGR_DATA_EDITOR
structUpdate.m_AllCollisionSelectable = GetAllCollisionSelectableUT();
#endif
#endif
structUpdate.m_IsNetworkGameInProgress = NetworkInterface::IsGameInProgress();
structUpdate.m_addBufSize = 256;
c.AddOutput(structUpdate.m_addBufSize * sizeof(ProcObjectCreationInfo)); // 8KB
structUpdate.m_removeBufSize = 256;
c.AddOutput(structUpdate.m_removeBufSize * sizeof(ProcTriRemovalInfo)); // 2KB
c.AddOutput(sizeof(spuPlantsMgrUpdateStruct::CResultSize)); // 0.02KB
structUpdate.m_maxAdd = NELEM(s_SpuProcObjectCreationInfo);
structUpdate.m_maxRemove = NELEM(s_SpuProcTriRemovalInfo);
structUpdate.m_pAddList = &s_SpuProcObjectCreationInfo[0];
structUpdate.m_pRemoveList = &s_SpuProcTriRemovalInfo[0];
structUpdate.m_pResultSize = &s_PlantMgrResultSize;
structUpdate.m_iTriProcessSkipMask = fwTimer::GetSystemFrameCount() & (CPLANT_ENTRY_TRILOC_PROCESS_UPDATE-1);
const u32 bufferID = GetUpdateRTBufferID();
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
structUpdate.m_LocTrisTab[listID] = m_LocTrisTab[listID];
structUpdate.m_LocTrisRenderTab[listID] = m_LocTrisRenderTab[bufferID][listID];
}
#if FURGRASS_TEST_V4
structUpdate.m_furGrassPickupRenderInfo = m_furGrassPickupRenderInfo[bufferID];
#endif
Assertf(s_PlantMgrTaskHandle==0, "PlantMgr: SPU Update task already running!");
s_PlantMgrTaskHandle = c.Start(sysTaskManager::SCHEDULER_DEFAULT_SHORT);
#else //PSN_PLANTSMGR_SPU_UPDATE...
static u8 nLocTriSkipCounter=0;
const s32 iTriProcessSkipMask = (nLocTriSkipCounter++)&(CPLANT_ENTRY_TRILOC_PROCESS_UPDATE-1);
// Stop creating proc objects when the player is traveling over a certain velocity
m_bSuppressObjCreation = bool(g_ContinuityMgr.GetPlayerVelocity() >= OBJ_CREATION_SPEED_LIMIT);
m_bSuppressObjCreation |= m_bSuppressObjCreationPermanently;
#if RSG_PC
if(CSettingsManager::GetInstance().GetSettings().m_graphics.m_ShaderQuality == CSettings::Low)
{
m_bSuppressObjCreation |= true;
}
#endif
#if FURGRASS_TEST_V4
for(u32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
m_nFurgrassTagPresentTab[i] = false;
}
#endif
// add update plantmgr task
#if PLANTSMGR_MULTI_UPDATE
sysTaskParameters params;
for(u32 t=0; t<PLANTSMGR_MULTI_UPDATE_TASKCOUNT; t++)
{
Assertf(s_PlantMgrTaskHandle[t]==0, "PlantMgr: Update task %d already running!", t);
params.UserData[0].asInt = t; // 0-7
params.UserData[1].asInt = iTriProcessSkipMask;
s_PlantMgrTaskHandle[t] = rage::sysTaskManager::Create(TASK_INTERFACE(UpdatePlantMgrSubTask), params);
}
#else //PLANTSMGR_MULTI_UPDATE
sysTaskParameters params;
params.UserData[1].asInt = iTriProcessSkipMask;
Assertf(s_PlantMgrTaskHandle==0, "PlantMgr: Update task already running!");
s_PlantMgrTaskHandle = rage::sysTaskManager::Create(TASK_INTERFACE(UpdatePlantMgrTask), params);
#endif //PLANTSMGR_MULTI_UPDATE...
#endif //PSN_PLANTSMGR_SPU_UPDATE...
// we ran the update tasks this frame, so we also want to run the memcpy jobs
m_bShouldRunAsyncMemcpyJobThisFrame = true;
return(TRUE);
}// end of Update()...
//
//
//
//
void CPlantMgr::UpdateEnd()
{
if(!gbPlantMgrActive) // skip updating, if varconsole switch is off
return;
#if __BANK
if (TiledScreenCapture::IsEnabled())
return;
#endif
#if LOCTRISTAB_STR
if(m_LocTrisTab[0] == NULL)
{ // loc tris tabs not allocated, so skip:
return;
}
#endif
#if CPLANT_DYNAMIC_CULL_SPHERES
CopyCullSpheresToUpdateBuffer();
#endif
#if PLANTSMGR_MULTI_UPDATE
for(u32 t=0; t<PLANTSMGR_MULTI_UPDATE_TASKCOUNT; t++)
{
if(s_PlantMgrTaskHandle[t])
{
sysTaskManager::Wait( s_PlantMgrTaskHandle[t] );
s_PlantMgrTaskHandle[t] = 0;
}
}
#if FURGRASS_TEST_V4
// store fur grass pickup info for RT:
gPlantMgr.FurGrassStoreRenderInfo(gPlantMgr.m_furGrassPickupRenderInfo[gPlantMgr.GetUpdateRTBufferID()], 0);
#endif
#else //PLANTSMGR_MULTI_UPDATE
if(s_PlantMgrTaskHandle)
{
sysTaskManager::Wait(s_PlantMgrTaskHandle);
s_PlantMgrTaskHandle = 0;
}
#endif //PLANTSMGR_MULTI_UPDATE...
#if FURGRASS_TEST_V4
const u32 bufferIdx = GetUpdateRTBufferID();
// establish wheter to render fur grass or not:
m_bFurgrassDoRender[bufferIdx] = false;
for(u32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
m_bFurgrassDoRender[bufferIdx] |= bool(m_nFurgrassTagPresentTab[i]!=0); // results from subupdate tasks
}
m_bFurgrassDoRender[bufferIdx] |= GetFurgrassTrisRtPresent(); // + feedback from Furgrass::Render()
#endif
#if PLANTSMGR_DATA_EDITOR
gPlantMgrEditor.UpdateSafe();
#endif
#if PSN_PLANTSMGR_SPU_UPDATE
// CProcObjectMan::ProcessAddAndRemoveLists():
// process SPU generated lists of objects to add/remove
const ProcTriRemovalInfo* pRemove = s_SpuProcTriRemovalInfo;
const ProcTriRemovalInfo* pRemoveEnd = pRemove + s_PlantMgrResultSize.m_numRemove;
for(; pRemove != pRemoveEnd; pRemove++)
{
const CTriHashIdx16 hashIdx = u16(u32(pRemove->pLocTri));
const u16 listID = hashIdx.GetListID();
const u16 idx = hashIdx.GetIdx();
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
CPlantLocTri *pRemoveLocTri = &triTab[idx];
g_procObjMan.ProcessTriangleRemoved(pRemoveLocTri, pRemove->procTagId);
}// for(; pRemove != pRemoveEnd; pRemove++)...
const ProcObjectCreationInfo* pAdd = s_SpuProcObjectCreationInfo;
const ProcObjectCreationInfo* pAddEnd = pAdd + s_PlantMgrResultSize.m_numAdd;
for(; pAdd != pAddEnd; pAdd++)
{
CProcObjInfo* pProcObjInfo = (CProcObjInfo*)(pAdd->pos.uw + (u8*)&g_procInfo.m_procObjInfos[0]);
const CTriHashIdx16 hashIdx = (u16)pAdd->normal.uw;
const u16 listID = hashIdx.GetListID();
const u16 idx = hashIdx.GetIdx();
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
CPlantLocTri* pLocTri = &triTab[idx];
if(pLocTri->m_pParentEntity)
{
CEntityItem* pEntityItem = g_procObjMan.AddObject(pAdd->pos, pAdd->normal,
pProcObjInfo, &pProcObjInfo->m_EntityList,
const_cast<CEntity*>(pLocTri->m_pParentEntity), pLocTri->GetSeed());
if (pEntityItem)
{
pEntityItem->m_pParentTri = pLocTri;
#if __BANK
pEntityItem->triVert1[0] = pLocTri->m_V1.x;
pEntityItem->triVert1[1] = pLocTri->m_V1.y;
pEntityItem->triVert1[2] = pLocTri->m_V1.z;
pEntityItem->triVert2[0] = pLocTri->m_V2.x;
pEntityItem->triVert2[1] = pLocTri->m_V2.y;
pEntityItem->triVert2[2] = pLocTri->m_V2.z;
pEntityItem->triVert3[0] = pLocTri->m_V3.x;
pEntityItem->triVert3[1] = pLocTri->m_V3.y;
pEntityItem->triVert3[2] = pLocTri->m_V3.z;
#endif // __BANK
}
else
{
#if __BANK
g_procObjMan.m_bkNumProcObjsSkipped++;
#endif
}
}
}// for(; pAdd != pAddEnd; pAdd++)...
#if __BANK
g_procObjMan.m_bkNumProcObjsToAdd = s_PlantMgrResultSize.m_numAdd;
g_procObjMan.m_bkNumProcObjsToAddSize = NELEM(s_SpuProcObjectCreationInfo);
g_procObjMan.m_bkNumTrisToRemove = s_PlantMgrResultSize.m_numRemove;
g_procObjMan.m_bkNumTrisToRemoveSize = NELEM(s_SpuProcTriRemovalInfo);
#endif
#if __DEV && 0
static u32 maxAdd = 0;
if (s_PlantMgrResultSize.m_numAdd > maxAdd)
{
maxAdd = s_PlantMgrResultSize.m_numAdd;
Displayf("PlantsMgrUpdateSPU: most added=%d.", maxAdd);
}
static u32 maxRemove = 0;
if (s_PlantMgrResultSize.m_numRemove > maxRemove)
{
maxRemove = s_PlantMgrResultSize.m_numRemove;
Displayf("PlantsMgrUpdateSPU: most removed=%d.", maxRemove);
}
#endif //__DEV...
#endif //PSN_PLANTSMGR_SPU_UPDATE...
const fwInteriorLocation interiorLocation;
// recalculate ambient scale from TC:
if(IsAmbScaleScanEnabled())
{
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
if(triTab.m_bRequireAmbScale)
{
triTab.m_bRequireAmbScale = false;
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
// sample ambient TC:
if(pLocTri->m_bRequireAmbScale)
{
pLocTri->m_bRequireAmbScale = false;
const Vector3 center = pLocTri->GetCenter();
const Vec3V centerV3 = RCC_VEC3V(center);
const Vec2V vAmbientScale = g_timeCycle.CalcAmbientScale(centerV3, interiorLocation);
Assertf(vAmbientScale.GetXf() >= 0.0f && vAmbientScale.GetXf() <= 1.0f,"Dynamic Natural Ambient is out of range (%f, should be between 0.0f and 1.0f)",vAmbientScale.GetXf());
Assertf(vAmbientScale.GetYf() >= 0.0f && vAmbientScale.GetYf() <= 1.0f,"Dynamic Artificial Ambient is out of range (%f, should be between 0.0f and 1.0f)",vAmbientScale.GetYf());
const Vec2V vScaledAmbientScale = vAmbientScale * ScalarV(255.0f);
pLocTri->m_nAmbientScale[0] = u8(vScaledAmbientScale.GetXf()); // natural ambient
pLocTri->m_nAmbientScale[1] = u8(vScaledAmbientScale.GetYf()); // artificial ambient
}
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
}//if(IsAmbScaleScanEnabled())...
}// end of CPlantMgr::EndUpdate()...
//
//
//
//
void CPlantMgr::AsyncMemcpyBegin()
{
if(m_bShouldRunAsyncMemcpyJobThisFrame)
{
g_PlantMgrMemcpyDependenciesRunning = CPLANT_LOC_TRIS_LIST_NUM;
for(int i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
g_PlantMgrMemcpyDependency[i].m_Params[0].m_AsInt = i;
g_PlantMgrMemcpyDependency[i].m_Params[1].m_AsPtr = &g_PlantMgrMemcpyDependenciesRunning;
sysDependencyScheduler::Insert(&g_PlantMgrMemcpyDependency[i]);
}
}
m_bShouldRunAsyncMemcpyJobThisFrame = false;
}
void CPlantMgr::AsyncMemcpyEnd()
{
PF_PUSH_TIMEBAR_IDLE("CPlantMgr AsyncMemcpyEnd");
while(true)
{
volatile u32 *pDependenciesRunning = &g_PlantMgrMemcpyDependenciesRunning;
if(*pDependenciesRunning == 0)
{
break;
}
sysIpcYield(PRIO_NORMAL);
}
PF_POP_TIMEBAR();
}
//
//
//
//
#if CPLANT_USE_OCCLUSION
static spdAABB sm_loctriAABB[CPLANT_LOC_TRIS_LIST_NUM][CPLANT_MAX_CACHE_LOC_TRIS_NUM];
//
//
//
//
void CPlantMgr::UpdateOcclusion()
{
#if __BANK
nLocTrisOccluded=0;
if(!gbPlantsUseOcclusion)
return;
if(gbPlantsOccRecalcAABBs)
{
gbPlantsOccRecalcAABBs = false;
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
pLocTri->m_bNeedsAABB = true;
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}
}
#endif//__BANK...
extern rage::fwScanBaseInfo g_scanBaseInfo;
rage::fwScanBaseInfo *scanBaseInfo = &g_scanBaseInfo;
const Vec4V *pHiZBuffer = COcclusion::GetHiZBuffer(SCAN_OCCLUSION_STORAGE_PRIMARY);
Assert(pHiZBuffer);
const float fTriCubeHeight = BANK_SWITCH(gfPlantsOccCubeHeight,2.0f);
const s32 nSkipTest = BANK_SWITCH(gnPlantsOccSkipTest,0);
s32 skipTest = nSkipTest;
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
spdAABB *tabAabb = sm_loctriAABB[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
spdAABB *aabb = &tabAabb[loctri-1];
if(pLocTri->m_bNeedsAABB)
{
pLocTri->m_bNeedsAABB = false;
// calculate AABB for the loc tri:
aabb->Invalidate();
// construct AABB for the loctri: base + base offseted by 2.0m up:
Vector3 V1 = pLocTri->GetV1();
Vector3 V2 = pLocTri->GetV2();
Vector3 V3 = pLocTri->GetV3();
aabb->GrowPoint(RCC_VEC3V(V1));
aabb->GrowPoint(RCC_VEC3V(V2));
aabb->GrowPoint(RCC_VEC3V(V3));
V1.z += fTriCubeHeight;
V2.z += fTriCubeHeight;
V3.z += fTriCubeHeight;
aabb->GrowPoint(RCC_VEC3V(V1));
aabb->GrowPoint(RCC_VEC3V(V2));
aabb->GrowPoint(RCC_VEC3V(V3));
}
#if __BANK
if(skipTest)
{
skipTest--;
pLocTri->m_bOccluded = false;
}
else
{
skipTest = nSkipTest;
ScalarV area = rstTestAABBExactEdgeList( scanBaseInfo->m_transform[SCAN_OCCLUSION_STORAGE_PRIMARY],
ScalarVFromF32(scanBaseInfo->m_depthRescale[SCAN_OCCLUSION_STORAGE_PRIMARY]),
pHiZBuffer, RCC_VEC3V(m_CameraPos), aabb->GetMin(), aabb->GetMax(),
ScalarV(V_ZERO), scanBaseInfo->m_minMaxBounds[SCAN_OCCLUSION_STORAGE_PRIMARY],
scanBaseInfo->m_minZ[SCAN_OCCLUSION_STORAGE_PRIMARY]
DEV_ONLY(, &(scanBaseInfo->m_TrivialAcceptActiveFrustumCount[SCAN_OCCLUSION_STORAGE_PRIMARY]), &(scanBaseInfo->m_TrivialAcceptMinZCount[SCAN_OCCLUSION_STORAGE_PRIMARY]), &(scanBaseInfo->m_TrivialAcceptVisiblePixelCount[SCAN_OCCLUSION_STORAGE_PRIMARY]))
#if __BANK
, THRESHOLD_PIXELS_SIMPLE_TEST
, /*useTrivialAcceptTest*/true, /*useTrivialAcceptVisiblePixelTest*/true
#endif
);
pLocTri->m_bOccluded = IsGreaterThanAll(area, ScalarV(V_ZERO))==0;
}
#else
ScalarV area = rstTestAABBExactEdgeList( scanBaseInfo->m_transform[SCAN_OCCLUSION_STORAGE_PRIMARY],
ScalarVFromF32(scanBaseInfo->m_depthRescale[SCAN_OCCLUSION_STORAGE_PRIMARY]),
pHiZBuffer, RCC_VEC3V(m_CameraPos), aabb->GetMin(), aabb->GetMax(),
ScalarV(V_ZERO), scanBaseInfo->m_minMaxBounds[SCAN_OCCLUSION_STORAGE_PRIMARY],
scanBaseInfo->m_minZ[SCAN_OCCLUSION_STORAGE_PRIMARY]
DEV_ONLY(, &(scanBaseInfo->m_TrivialAcceptActiveFrustumCount[SCAN_OCCLUSION_STORAGE_PRIMARY]), &(scanBaseInfo->m_TrivialAcceptMinZCount[SCAN_OCCLUSION_STORAGE_PRIMARY]), &(scanBaseInfo->m_TrivialAcceptVisiblePixelCount[SCAN_OCCLUSION_STORAGE_PRIMARY]))
#if __BANK
, THRESHOLD_PIXELS_SIMPLE_TEST
, /*useTrivialAcceptTest*/true, /*useTrivialAcceptVisiblePixelTest*/true
#endif
);
pLocTri->m_bOccluded = IsGreaterThanAll(area, ScalarV(V_ZERO))==0;
#endif
#if __BANK
nLocTrisOccluded += u32(pLocTri->m_bOccluded);
#endif
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}
}
#endif // CPLANT_USE_OCCLUSION...
//
//
// Update streaming buffers:
//
void CPlantMgr::UpdateStr()
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
CGrassRenderer::UpdateStr();
#if PLANTS_TXD_STR
UpdateStrTextures();
#endif
#if LOCTRISTAB_STR
UpdateStrLocTriTabs();
#endif
}// end of CPlantMgr::UpdateStr()...
// wrapper for interfacing the plants manager with the game skeleton code
void CPlantMgrWrapper::UpdateBegin()
{
gPlantMgr.UpdateBegin();
}
void CPlantMgrWrapper::UpdateEnd()
{
gPlantMgr.UpdateEnd();
}
#if CPLANT_USE_OCCLUSION
void CPlantMgrWrapper::UpdateOcclusion()
{
gPlantMgr.UpdateOcclusion();
}
#endif
void CPlantMgrWrapper::UpdateSafeMode()
{
gPlantMgr.UpdateStr();
gPlantMgr.SwapRTBuffer(); // swap buffer for update task
#if PLANTSMGR_DATA_EDITOR
// pass over signals from Editor to gPlantMgr:
gPlantMgr.SetAllCollisionSelectable(gPlantMgrEditor.IsAllCollisionSelectable());
if(gPlantMgrEditor.GetRegenTriCache())
{
gPlantMgrEditor.SetRegenTriCache(false);
gPlantMgr.ForceRegenTriCaches(true);
}
#endif
}
//
// this function helps to avoid ugly visual "rebuilding" of plants while
// camera is teleported into new remote location;
//
// to be called ONCE before teleporting camera / player to new far position:
//
bool CPlantMgr::PreUpdateOnceForNewCameraPos(const Vector3& newCameraPos)
{
#if __BANK
if(audNorthAudioEngine::GetOcclusionManager()->GetIsOcclusionBuildEnabled() && audNorthAudioEngine::GetOcclusionManager()->GetIsBuildingOcclusionPaths())
{
return true;
}
#endif
return gPlantMgr.PreUpdateOnceForNewCameraPosInternal(newCameraPos);
}
bool CPlantMgr::PreUpdateOnceForNewCameraPosInternal(const Vector3& newCameraPos)
{
#if LOCTRISTAB_STR
if(m_LocTrisTab[0]==NULL)
{
return(TRUE);
}
#endif
g_procObjMan.Shutdown();
g_procObjMan.Init();
CPlantMgr::AdvanceCurrentScanCode();
CGrassRenderer::SetGlobalCameraPos(newCameraPos);
Vector2 bending;
CPlantMgr::CalculateWindBending(bending);
CGrassRenderer::SetGlobalWindBending(bending);
m_bSuppressObjCreation = false;
m_bSuppressObjCreation |= m_bSuppressObjCreationPermanently;
//
// empty + update ColEntityCache:
//
// force updating all EntityCache:
_ColBoundCache_Update(newCameraPos);
_ColBoundCache_Update(newCameraPos);
//
// update ALL LocTris in CEntities in _ColEntityCache:
//
#if PSN_PLANTSMGR_SPU_UPDATE
for(u32 update=0; update<2; update++)
{
// launch SPU job:
const u32 outputSize = 0;
const u32 scratchSize = 0;
const u32 plantsBaseSize= (SPU_SIZEOF_CPLANTMGRBASE+1024)/1024; // 68KB
#if CPLANT_DYNAMIC_CULL_SPHERES || CPLANT_CLIP_EDGE_VERT
const u32 stackSize = (86+plantsBaseSize)*1024;
#else
const u32 stackSize = (89+plantsBaseSize)*1024;
#endif
sysTaskContext c(TASK_INTERFACE(PlantsMgrUpdateSPU), outputSize, scratchSize, stackSize);
c.SetInputOutput();
c.AddInput(&g_procInfo, sizeof(g_procInfo)); // 27.64KB
c.AddInput(g_procInfo.m_procObjInfos.GetElements(), sizeof(CProcObjInfo) * g_procInfo.m_procObjInfos.GetCount());
c.AddInput(g_procInfo.m_plantInfos.GetElements(), sizeof(CPlantInfo) * g_procInfo.m_plantInfos.GetCount());
spuPlantsMgrUpdateStruct& structUpdate = *c.AllocUserDataAs<spuPlantsMgrUpdateStruct>(); // 0.08KB
structUpdate.m_pPlantsMgr = this;
structUpdate.m_camPos = newCameraPos;
structUpdate.m_GroundSlopeAngleMin = PlantsMinGroundAngleSlope;
structUpdate.m_bCullSphereEnabled0 = m_CullSphereEnabled[0];
structUpdate.m_bCullSphereEnabled1 = m_CullSphereEnabled[1];
structUpdate.m_cullSphere[0] = m_CullSphere[0];
structUpdate.m_cullSphere[1] = m_CullSphere[1];
#if __BANK
structUpdate.m_gbForceDefaultGroundColor = gbForceDefaultGroundColor;
structUpdate.m_gbDefaultGroundColor = gbDefaultGroundColor;
// g_procObjMan flags:
structUpdate.m_ignoreMinDist = g_procObjMan.m_ignoreMinDist;
structUpdate.m_forceOneObjPerTri = g_procObjMan.m_forceOneObjPerTri;
structUpdate.m_ignoreSeeding = g_procObjMan.m_ignoreSeeding;
structUpdate.m_enableSeeding = g_procObjMan.m_enableSeeding;
structUpdate.m_disableCollisionObjects = g_procObjMan.m_disableCollisionObjects;
structUpdate.m_printSpuJobTimings = gbPrintUpdateSpuJobTimings;
#if PLANTSMGR_DATA_EDITOR
structUpdate.m_AllCollisionSelectable = GetAllCollisionSelectableUT();
#endif
#endif
structUpdate.m_IsNetworkGameInProgress = NetworkInterface::IsGameInProgress();
structUpdate.m_addBufSize = 256;
c.AddOutput(structUpdate.m_addBufSize * sizeof(ProcObjectCreationInfo)); // 8KB
structUpdate.m_removeBufSize = 256;
c.AddOutput(structUpdate.m_removeBufSize * sizeof(ProcTriRemovalInfo)); // 2KB
c.AddOutput(sizeof(spuPlantsMgrUpdateStruct::CResultSize)); // 0.02KB
structUpdate.m_maxAdd = NELEM(s_SpuProcObjectCreationInfo);
structUpdate.m_maxRemove = NELEM(s_SpuProcTriRemovalInfo);
structUpdate.m_pAddList = &s_SpuProcObjectCreationInfo[0];
structUpdate.m_pRemoveList = &s_SpuProcTriRemovalInfo[0];
structUpdate.m_pResultSize = &s_PlantMgrResultSize;
structUpdate.m_iTriProcessSkipMask = CPLANT_ENTRY_TRILOC_PROCESS_ALWAYS;
const u32 bufferID = GetUpdateRTBufferID();
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
structUpdate.m_LocTrisTab[listID] = m_LocTrisTab[listID];
structUpdate.m_LocTrisRenderTab[listID] = m_LocTrisRenderTab[bufferID][listID];
}
#if FURGRASS_TEST_V4
structUpdate.m_furGrassPickupRenderInfo = m_furGrassPickupRenderInfo[bufferID];
#endif
if(s_PlantMgrTaskHandle)
{
sysTaskManager::Wait(s_PlantMgrTaskHandle);
s_PlantMgrTaskHandle = 0;
}
Assertf(s_PlantMgrTaskHandle==0, "PlantMgr: SPU Update task already running!");
s_PlantMgrTaskHandle = c.Start(sysTaskManager::SCHEDULER_DEFAULT_SHORT);
UpdateEnd();
}
#else //PSN_PLANTSMGR_SPU_UPDATE...
#if FURGRASS_TEST_V4
for(u32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
m_nFurgrassTagPresentTab[i] = false;
}
#endif
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
#if FURGRASS_TEST_V4
u32 *pFurgrassTagPresent = &m_nFurgrassTagPresentTab[listID];
#else
u32 *pFurgrassTagPresent = NULL;
#endif
UpdateAllLocTris(*m_LocTrisTab[listID], newCameraPos, CPLANT_ENTRY_TRILOC_PROCESS_ALWAYS, pFurgrassTagPresent);
UpdateAllLocTris(*m_LocTrisTab[listID], newCameraPos, CPLANT_ENTRY_TRILOC_PROCESS_ALWAYS, pFurgrassTagPresent);
}
#if FURGRASS_TEST_V4
// establish wheter to render fur grass or not:
const u32 bufferIdx = GetUpdateRTBufferID();
m_bFurgrassDoRender[bufferIdx] = false;
for(u32 i=0; i<CPLANT_LOC_TRIS_LIST_NUM; i++)
{
m_bFurgrassDoRender[bufferIdx] |= bool(m_nFurgrassTagPresentTab[i]!=0);
}
m_bFurgrassDoRender[bufferIdx] |= GetFurgrassTrisRtPresent(); // + feedback from Furgrass::Render()
#endif
#endif //PSN_PLANTSMGR_SPU_UPDATE...
return(TRUE);
}
//
//
//
//
bool CPlantMgr::_ColBoundCache_Update(const Vector3& cameraPos, bool bQuickUpdate)
{
const u16 nCurrentScanCode = CPlantMgr::GetCurrentScanCode();
if(bQuickUpdate)
{
// Quick Update:
if(m_ColEntCache.m_CloseListHead)
{
u16 entry = m_ColEntCache.m_CloseListHead;
while(entry)
{
CPlantColBoundEntry *pEntry = &m_ColEntCache[entry];
entry = pEntry->m_NextEntry;
// not valid pointer to parent (pEntity or bound in static bounds store)?
if(!pEntry->IsParentValid())
{
pEntry->ReleaseEntry();
}
else
{
#if PLANTSMGR_MULTI_UPDATE
// reset processed tris cache:
pEntry->m_processedTris.Reset();
#endif
}
}
}
}
else // if(bQuickUpdate)...
{
// Full update:
// Use this option to get all objects matching the req'd state
#if PLANTS_USE_LOD_SETTINGS
float fColTestRadius = CPLANTMGR_COL_TEST_RADIUS*CGrassRenderer::GetDistanceMultiplier();
#else
float fColTestRadius = CPLANTMGR_COL_TEST_RADIUS;
#endif
#if PLANTSMGR_DATA_EDITOR
if(GetAllCollisionSelectableUT())
{
fColTestRadius *= 0.333f; // use 1/3 of range when editing wuth "select all" mode
}
#endif
#if 0 // WEAPON based collision is disabled
phLevelNew * phLevel = CPhysics::GetLevel();
Assert(phLevel);
#if ENABLE_PHYSICS_LOCK
phIterator iterator(phIterator::PHITERATORLOCKTYPE_READLOCK);
#else // ENABLE_PHYSICS_LOCK
phIterator iterator;
#endif // ENABLE_PHYSICS_LOCK
iterator.InitCull_Sphere(cameraPos, fColTestRadius); // ::InitCull_All();
iterator.SetStateIncludeFlags(phLevelNew::STATE_FLAG_FIXED);// | phLevelNew::STATE_FLAG_ACTIVE | phLevelNew::STATE_FLAG_INACTIVE);
u16 iIndexInLevel = phLevel->GetFirstCulledObject(iterator);
while(iIndexInLevel != phInst::INVALID_INDEX)
{
phInst *pInstance = phLevel->GetInstance(iIndexInLevel);
if(pInstance)
{
phArchetype *pArchetype = pInstance->GetArchetype();
if(pArchetype)
{
phBound *pBound = pArchetype->GetBound();
Assert(pBound);
if(pInstance->GetArchetype()->GetTypeFlags()&ArchetypeFlags::GTA_MAP_TYPE_WEAPON)
{
CEntity* pEntity = CPhysics::GetEntityFromInst(pInstance);
Assert(pEntity);
_ColBoundCache_ProcessBound(pBound, pEntity, -1, -1, nCurrentScanCode);
}
}
}//if(pInstance)...
iIndexInLevel = phLevel->GetNextCulledObject(iterator);
}// while(iIndexInLevel != phInst::INVALID_INDEX)...
#endif //#if 0...
// MATERIAL-type collision:
if(true)
{
//g_StaticBoundsStore
Vector4 sphereV4(cameraPos);
sphereV4.w = fColTestRadius;
spdSphere searchVol(VECTOR4_TO_VEC4V(sphereV4));
// first search for static bounds files that are asset type MATERIAL
atArray<u32> slotIndices;
g_StaticBoundsStore.GetBoxStreamer().GetIntersectingAABB(spdAABB(searchVol), fwBoxStreamerAsset::FLAG_STATICBOUNDS_MATERIAL, slotIndices, false);
// run over slots for any ones which are loaded
for(s32 i=0; i<slotIndices.GetCount(); i++)
{
strLocalIndex index = strLocalIndex((s32)slotIndices[i]);
if((index!=-1) && (g_StaticBoundsStore.GetPtr(index)!=NULL))
{
fwBoundDef* pDef = g_StaticBoundsStore.GetSlot(index);
if (pDef && pDef->m_pObject && pDef->m_pObject->GetType() == phBound::COMPOSITE)
{
phBoundComposite* pComposite = (phBoundComposite*)(pDef->m_pObject);
const s32 numBounds = pComposite->GetNumBounds();
for(s32 i=0; i<numBounds; i++)
{
phBound* pBound = pComposite->GetBound(i);
if(pBound)
{
_ColBoundCache_ProcessBound(pBound, NULL, index.Get(), i, nCurrentScanCode);
}
}
}
}
}
}
// go through BoundCache and remove Entrys with no current scancode
// (probably they are too far from iterator):
if(m_ColEntCache.m_CloseListHead)
{
u16 entry = m_ColEntCache.m_CloseListHead;
while(entry)
{
CPlantColBoundEntry *pEntry = &m_ColEntCache[entry];
entry = pEntry->m_NextEntry;
if( (pEntry->m_nScancode != nCurrentScanCode) ||
// not valid pointer to parent (entity or bound in static bounds store)?
(!pEntry->IsParentValid()) )
{
pEntry->ReleaseEntry();
}
else
{
#if PLANTSMGR_MULTI_UPDATE
// reset processed tris cache:
pEntry->m_processedTris.Reset();
#endif
}
}
}
} // Full update...
return(TRUE);
}// end of _ColBoundCache_Update()...
//
//
//
//
bool CPlantMgr::_ColBoundCache_ProcessBound(phBound *pBound, CEntity *pEntity, s32 parentIdx, s32 childIdx, const u16 nCurrentScanCode)
{
if(pBound->GetType() == phBound::BVH || pBound->GetType() == phBound::GEOMETRY)
{
phBoundGeometry *pBoundGeom = (phBoundGeometry*)pBound;
#if PLANTSMGR_DATA_EDITOR
if(CPlantMgr::IsBoundGeomPlantFriendly(pBoundGeom) || GetAllCollisionSelectableUT()) // allow all bounds regardless of their materials in "select all" mode
#else
if(CPlantMgr::IsBoundGeomPlantFriendly(pBoundGeom))
#endif
{
CPlantColBoundEntry *pEntry = _ColBoundCache_FindInCache(pEntity, parentIdx, childIdx);
if(!pEntry)
{
pEntry = _ColBoundCache_Add(pEntity, parentIdx, childIdx);
}
if(pEntry)
{
pEntry->m_nScancode = nCurrentScanCode; // update scanecode to indicate this bound is colliding with iterator
}
}
}
return(TRUE);
}
//
//
// checks if given Bound is already in BoundCache:
//
CPlantColBoundEntry* CPlantMgr::_ColBoundCache_FindInCache(CEntity *pEntity, s32 parentIdx, s32 childIdx)
{
(void)parentIdx; // shut up the compiler about unused params
(void)childIdx;
if(m_ColEntCache.m_CloseListHead)
{
u16 entry = m_ColEntCache.m_CloseListHead;
while(entry)
{
CPlantColBoundEntry *pEntry = &m_ColEntCache[entry];
if(pEntry->IsMatBound())
{
if((pEntry->m_nBParentIndex == parentIdx) && (pEntry->m_nBChildIndex == childIdx))
{
return(pEntry);
}
}
else
{
if(pEntry->m__pEntity == pEntity)
{
return(pEntry);
}
}
entry = pEntry->m_NextEntry;
}
}
return(NULL);
}
//
//
// adds Bound to BoundCache:
//
CPlantColBoundEntry* CPlantMgr::_ColBoundCache_Add(CEntity* pEntity, s32 parentIdx, s32 childIdx, bool bCheckCacheFirst)
{
CPlantColBoundEntry *pEntry=NULL;
if(bCheckCacheFirst)
{
pEntry = _ColBoundCache_FindInCache(pEntity, parentIdx, childIdx);
if(pEntry)
{
// seems that this CEntity if already in the cache:
return(pEntry);
}
}
if(m_ColEntCache.m_UnusedListHead)
{
pEntry = &m_ColEntCache[m_ColEntCache.m_UnusedListHead];
if(pEntry->AddEntry(pEntity, parentIdx, childIdx))
{
return(pEntry);
}
}
return(NULL);
}
//
//
// removes Bound from BoundCache:
//
void CPlantMgr::_ColBoundCache_Remove(CEntity *pEntity, s32 parentIdx, s32 childIdx)
{
CPlantColBoundEntry *pEntry = _ColBoundCache_FindInCache(pEntity, parentIdx, childIdx);
if(pEntry)
{
pEntry->ReleaseEntry();
}
}
//
//
//
//
bool CPlantMgr::IsBoundGeomPlantFriendly(phBoundGeometry *pBound)
{
Assert(pBound);
const s32 numMaterials = pBound->GetNumMaterials();
if(numMaterials <= 0)
{
return(FALSE);
}
// check all materials in given bound if they're "plant friendly":
for(s32 i=0; i<numMaterials; i++)
{
s32 procTagId = PGTAMATERIALMGR->UnpackProcId(pBound->GetMaterialId(i));
if (procTagId>0)
{
if (g_procInfo.CreatesPlants(procTagId) || g_procInfo.CreatesProcObjects(procTagId))
{
return(TRUE);
}
}
}
return(FALSE);
}// end of IsBoundGeomPlantFriendly()...
//
//
//
//
void CPlantMgr::SetForceHDGrass(bool enable)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
ms_bForceHDGrass = enable;
}
//
//
// top amount of "darkness" for given hours:
//
static float tabFakeGrassRotAmountPerHour[24] =
{
0.0f, // 0hrs
0.0f, // 1hrs
0.0f, // 2hrs
0.0f, // 3hrs
0.0f, // 4hrs
0.0f, // 5hrs
0.5f, // 6hrs
0.4f, // 7hrs
0.4f, // 8hrs
0.4f, // 9hrs
0.2f, //10hrs
0.2f, //11hrs
0.2f, //12hrs
0.2f, //13hrs
0.2f, //14hrs
0.2f, //15hrs
0.2f, //16hrs
0.2f, //17hrs
0.4f, //18hrs
0.4f, //19hrs
0.4f, //20hrs
0.4f, //21hrs
0.0f, //22hrs
0.0f //23hrs
};
//
//
// calculates faked grass normal (taken from main SunDirection);
// rotates it accordingly to make overall grass darker;
//
// rot=0: no rotation -> full lighting amount
// rot=1: full rotation -> no rotation amount
//
void CPlantMgr::CalculateFakeGrassNormal(float rotationAmountAddDbg)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
float rotationAmount = rotationAmountAddDbg;
#if __BANK
// use simplified timecycle below?
if(gbPlantsDarknessTimecycle)
#else
if(1)
#endif
{
const s32 hour = CClock::GetHour();
const s32 minutes = CClock::GetMinute();
s32 hourNext = hour+1;
if(hourNext > 23)
hourNext = 0;
const float rotAmount0 = tabFakeGrassRotAmountPerHour[hour];
const float rotAmount1 = tabFakeGrassRotAmountPerHour[hourNext];
const float rotAmountAdd = rotAmount0 + (rotAmount1-rotAmount0) * (minutes/60.0f);
rotationAmount += rotAmountAdd;
}// use darkness timecycle...
rotationAmount = rage::Max(rotationAmount, 0.0f);
rotationAmount = rage::Min(rotationAmount, 1.0f);
#if __BANK
gbPlantsDarknessAmountPrint = rotationAmount;
#endif
Vector3 fakedNormal;
Vector3 lightSunDir = Lights::GetUpdateDirectionalLight().GetDirection();
if(lightSunDir == Vector3(0,0,-1))
{ // detect wrong vector direction case:
lightSunDir = Vector3(1,1,1);
}
static dev_bool bFakedNormalEnabled=FALSE;
if(bFakedNormalEnabled)
{
Vector3 rotAxis = CrossProduct(lightSunDir, Vector3(0,0,1));
rotAxis.Normalize();
float rotation = 90.0f * rotationAmount;
Matrix34 rotMtx;
rotMtx.Identity();
rotMtx.MakeRotateUnitAxis(rotAxis,( DtoR * rotation));
Vector3 newLightSunDir;
rotMtx.Transform3x3(lightSunDir, newLightSunDir);
fakedNormal = -newLightSunDir;
}
else
{
fakedNormal = -lightSunDir;
}
static dev_bool bUseForcedFakeNormal= TRUE;
if(bUseForcedFakeNormal)
{
Vector3 fakeNormal3;
static dev_float fakeNormalX = 0.0f;
static dev_float fakeNormalY = 0.0f;
static dev_float fakeNormalZ = 1.0f;
fakeNormal3.SetX(fakeNormalX);
fakeNormal3.SetY(fakeNormalY);
fakeNormal3.SetZ(fakeNormalZ);
fakeNormal3.Normalize();
fakedNormal = fakeNormal3;
}
CGrassRenderer::SetFakeGrassNormal(fakedNormal);
}// end of CalculateFakeGrassNormal()...
//
//
//
//
void CPlantMgr::CalculateWindBending(Vector2 &outBending)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
outBending.Set(0.0f, 0.0f);
//Here we use the wind velocity in XY Plane to determine how much bend we want (includes direction
#if __BANK
if (ms_bkbWindResponseEnabled && (ms_bkfWindResponseAmount > 0.0f))
#endif
{
Vector3 wind(0.0f, 0.0f, 0.0f);
Vector2 wind2d(0.0f, 0.0f);
//Using global velocity as we dont have access to each patches position here.
// Plus it would be more expensive to calculate bending for each group.
WIND.GetGlobalVelocity(WIND_TYPE_AIR, RC_VEC3V(wind));
wind.GetVector2XY(wind2d);
const float windVel = wind2d.Mag();
const float windVelMin = BANK_SWITCH(ms_bkfWindResponseVelMin, PLANT_DEFAULT_WIND_RESPONSE_VEL_MIN);
const float windVelMax = BANK_SWITCH(ms_bkfWindResponseVelMax, PLANT_DEFAULT_WIND_RESPONSE_VEL_MAX);
const float windVelExp = BANK_SWITCH(ms_bkfWindResponseVelExp, PLANT_DEFAULT_WIND_RESPONSE_VEL_EXP);
//Calculate wind response in [0.0, 1.0f]
float windResponse = Clamp<float>((windVel - windVelMin)/(windVelMax - windVelMin), 0.0f, 1.0f);
if (windVelExp != 0.0f)
{
windResponse = powf(windResponse, powf(2.0f, windVelExp));
}
//Scale wind response
const float currentStrength = windResponse*BANK_SWITCH(ms_bkfWindResponseAmount, PLANT_DEFAULT_WIND_RESPONSE_AMOUNT);
//re-scale bending vector based on calculated strength
wind2d.NormalizeSafe(Vector2(1.0f, 0.0));
wind2d *= currentStrength;
// smoothening bend movement to avoid jerks
ms_AvgWindVector = ms_AvgWindVector*g_AvgPlantWindInfluence + wind2d*(1.0f - g_AvgPlantWindInfluence);
outBending = ms_AvgWindVector;
}
}
//
//
//
// IsInsideCurrentViewFrustum?
//
static
__forceinline bool IsLocTriVisibleByCamera(const spdTransposedPlaneSet8& cullFrustum, CPlantLocTri *pLocTri)
{
if(pLocTri->m_bCameraDontCull)
{
return(true); // do not cull when requested
}
// is sphere containing triangle visible?
Vector4 sphereV4( pLocTri->GetCenter() );
sphereV4.w = pLocTri->GetSphereRadius();
spdSphere sphere(VECTOR4_TO_VEC4V(sphereV4));
return cullFrustum.IntersectsOrContainsSphere(sphere);
}
static
inline bool IsLocTriVisibleUnderwater(CPlantLocTri *pLocTri, bool bIsCameraUnderwater)
{
if(pLocTri->m_bUnderwater)
{
return bIsCameraUnderwater? true : false; // proc types flagged as underwater are visible only underwater
}
else
{
return(true);
}
}
#if PLANTS_CAST_SHADOWS
static bool IsLocTriAShadowCandidate(CPlantLocTri *pLocTri)
{
const float R1PlusR2 = pLocTri->GetSphereRadius() + CGrassRenderer::GetShadowFadeFarRadius();
const Vector3 A = pLocTri->GetCenter() - CGrassRenderer::GetGlobalCameraPos();
if(Dot(A, A) > R1PlusR2*R1PlusR2)
{
return(false);
}
return(true);
}
#endif //PLANTS_CAST_SHADOWS
#if PSN_ALPHATOMASK_PASS || EXTRA_1_5_PASS
const u32 DEFERRED_MATERIAL_GRASS = DEFERRED_MATERIAL_TREE+DEFERRED_MATERIAL_SPECIALBIT; //0x03+0x10
CompileTimeAssert(DEFERRED_MATERIAL_GRASS==0x13);
#elif XENON_FILL_PASS
const u32 DEFERRED_MATERIAL_GRASS = DEFERRED_MATERIAL_TREE+DEFERRED_MATERIAL_SPECIALBIT; //0x03+0x10
CompileTimeAssert(DEFERRED_MATERIAL_GRASS==0x13);
#else
const u32 DEFERRED_MATERIAL_GRASS = DEFERRED_MATERIAL_TREE;
#endif
//
//
// PSN render pass#1:
// draw only to color0 , depth and stencil mask
//
static void BeginGrassAlphaToMaskPass1()
{
grcStateBlock::SetRasterizerState(hGrassAlphaToMaskRS_pass1);
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass1, DEFERRED_MATERIAL_GRASS);
grcStateBlock::SetBlendState(hGrassAlphaToMaskBS_pass1);
#if __XENON
static dev_bool debugHiZEnable = TRUE;
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HIZENABLE, debugHiZEnable?D3DHIZ_AUTOMATIC:D3DHIZ_DISABLE);
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HIZWRITEENABLE,D3DHIZ_AUTOMATIC);
#endif
#if PSN_ALPHATOMASK_PASS
// render only to color0+depth/stencil:
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE); // ps3: calls BlockAlphaToMask() under the hood, so must be called before stateblocks
#endif
}
static void EndGrassAlphaToMaskPass1()
{
#if PSN_ALPHATOMASK_PASS
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
#if __XENON
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HIZENABLE, D3DHIZ_AUTOMATIC );
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HIZWRITEENABLE,D3DHIZ_AUTOMATIC );
#endif // __XENON
grcStateBlock::SetBlendState(hGrassAlphaToMaskBS_pass1_end);
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass1_end, DEFERRED_MATERIAL_DEFAULT);
}
#if EXTRA_1_5_PASS
// clears MRT2.a (selfshadow term) for anything but default material:
static void RenderGrassAlphaToMaskPass1_5()
{
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS)
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_2, TRUE);
#endif
if(1) // clear selfshadowing in MRT2.a
{
grcStateBlock::SetBlendState(hGrassAlphaToMaskBS_pass1_5a);
const u32 stencilRef1 = DEFERRED_MATERIAL_SPECIALBIT+DEFERRED_MATERIAL_TERRAIN;
CompileTimeAssert(stencilRef1==0x14);
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass1_5a, stencilRef1); // anything greater than default material+grass
#if __XENON
// MRT write technique:
grmShader *shader = CGrassRenderer::GetGrassShader();
Assert(shader);
grcEffectTechnique tech = CGrassRenderer::GetFakedGBufTechniqueID();
Assert(tech);
shader->TWODBlit( -1.0f,1.0f, 1.0f,-1.0f, 0.0f,
0.0f,0.0f, 1.0f,1.0f, Color32(0,0,0,0), tech);
#else
// single RT technique:
CSprite2d::DrawRect(0, 0, 1, 1, 0, Color32(0,0,0,0));
#endif
}
if(1) // write full grass matID=0x03
{
grcStateBlock::SetBlendState(hGrassAlphaToMaskBS_pass1_5b);
const u32 stencilRef1 = DEFERRED_MATERIAL_GRASS;
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass1_5b, stencilRef1); // anything greater than default material+grass
CSprite2d::DrawRect(0, 0, 1, 1, 0, Color32(0,0,0,0));
}
#if __XENON
if(1) // clear special bit - restore proper matID (PS3 does this in fakeGBuf pass):
{
const u32 stencilRef1 = DEFERRED_MATERIAL_GRASS;
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass1_5c, stencilRef1); // anything greater than default material+grass
CSprite2d::DrawRect(0, 0, 1, 1, 0, Color32(0,0,0,0));
}
#endif
// cleanup:
const u32 stencilRef2 = DEFERRED_MATERIAL_DEFAULT;
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass1_5_end, stencilRef2);
grcStateBlock::SetBlendState(hGrassAlphaToMaskBS_pass1_5_end);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS)
GBuffer::UnlockSingleTarget(GBUFFER_RT_2);
GBuffer::LockTargets();
#endif
}
#endif //EXTRA_1_5_PASS...
#if XENON_FILL_PASS
static void RestoreHiStencilForFoliagePass(const u32 stencilRef, bool useEqual = true);
//
//
// fillup pass for Xenon:
//
static void XenonFillPass()
{
RestoreHiStencilForFoliagePass(DEFERRED_MATERIAL_GRASS);
if(1) // clear selfshadowing in MRT2.a
{
grcStateBlock::SetBlendState(hGbuffFillPassBS);
const u32 stencilRef1 = DEFERRED_MATERIAL_GRASS;
grcStateBlock::SetDepthStencilState(hGbuffFillPassDSS, stencilRef1); // anything greater than default material+grass
// MRT write technique:
grmShader *shader = CGrassRenderer::GetGrassShader();
Assert(shader);
grcEffectTechnique tech = CGrassRenderer::GetFakedGBufTechniqueID();
Assert(tech);
shader->TWODBlit( -1.0f,1.0f, 1.0f,-1.0f, 0.0f,
0.0f,0.0f, 1.0f,1.0f, Color32(0,0,0,0), tech);
}
// cleanup:
const u32 stencilRef2 = DEFERRED_MATERIAL_DEFAULT;
GRCDEVICE.GetCurrent()->SetRenderState( D3DRS_HISTENCILENABLE, FALSE );
GRCDEVICE.GetCurrent()->SetRenderState( D3DRS_HISTENCILFUNC, D3DHSCMP_EQUAL );
GRCDEVICE.GetCurrent()->SetRenderState( D3DRS_HISTENCILWRITEENABLE, FALSE );
grcStateBlock::SetDepthStencilState(hGbuffFillPassDSS_end, stencilRef2);
grcStateBlock::SetBlendState(hGbuffFillPassBS_end);
}
//
//
//
//
static void RestoreHiStencilForFoliagePass(const u32 stencilRef, bool useEqual)
{
// Can't change render targets b/c of predicated tiling. So this custom version doesn't try to change render targets before refresh.
// That makes it slightly slower, but makes the fullscreen foliage pass faster overall.
PF_PUSH_TIMEBAR_DETAIL("Mark HiStencil");
grcStateBlock::SetDepthStencilState(hGbuffFillPassRestoreHiStDSS, stencilRef);
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HISTENCILENABLE, FALSE);
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HISTENCILWRITEENABLE, TRUE);
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HISTENCILFUNC, useEqual ? D3DHSCMP_NOTEQUAL : D3DHSCMP_EQUAL); //Reversing useEqual, just like in GBuffer.cpp. This matches PS3 functionality.
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HISTENCILREF, stencilRef);
CSprite2d restoreHiZ;
restoreHiZ.BeginCustomList(CSprite2d::CS_RESTORE_HIZ, NULL);
grcBeginQuads(1);
grcDrawQuadf(0,0,1,1,0,0,0,1,1,Color32(255,255,255,255));
grcEndQuads();
restoreHiZ.EndCustomList();
GRCDEVICE.GetCurrent()->FlushHiZStencil(D3DFHZS_SYNCHRONOUS);
// Should be ready to use now.
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HISTENCILENABLE, TRUE);
GRCDEVICE.GetCurrent()->SetRenderState(D3DRS_HISTENCILWRITEENABLE, FALSE);
PF_POP_TIMEBAR_DETAIL();
}
#endif //XENON_FILL_PASS...
#if PSN_ALPHATOMASK_PASS
//
// PSN render pass#2:
// fullscreen blit to color1 (faked normals) and color2 (spec params)
//
static void RenderGrassAlphaToMaskPass2(float fNearZ, float fFarZ)
{
#if CPLANT_BLIT_MIN_GBUFFER
GBuffer::UnlockTargets();
const grcRenderTarget* pRendertargets[grcmrtColorCount] = { GBuffer::GetTarget(GBUFFER_RT_1), GBuffer::GetTarget(GBUFFER_RT_2), GBuffer::GetTarget(GBUFFER_RT_3), NULL };
grcTextureFactory::GetInstance().LockMRT(pRendertargets, GBuffer::GetDepthTarget());
#endif
grcStateBlock::SetBlendState(hGrassAlphaToMaskBS_pass2);
const u32 stencilRef1 = DEFERRED_MATERIAL_GRASS;
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass2, stencilRef1);
// grcStateBlock::SetRasterizerState(hGrassAlphaToMaskRS_pass2);
grmShader *shader = CGrassRenderer::GetGrassShader();
Assert(shader);
grcEffectTechnique tech = CGrassRenderer::GetFakedGBufTechniqueID();
Assert(tech);
#if CPLANT_STORE_LOCTRI_NORMAL
shader->SetVar(CGrassRenderer::GetGBuf0TextureID(), GBuffer::GetTarget(GBUFFER_RT_0));
#endif
grcDevice::SetDepthBoundsTestEnable(true);
CRenderer::SetDepthBoundsFromRange(grcViewport::GetCurrent()->GetNearClip(), grcViewport::GetCurrent()->GetFarClip(), fNearZ, fFarZ);
shader->TWODBlit( -1.0f,1.0f, 1.0f,-1.0f, 0.0f,
0.0f,0.0f, 1.0f,1.0f, Color32(255,255,255,255), tech);
grcDevice::SetDepthBoundsTestEnable(false);
#if CPLANT_BLIT_MIN_GBUFFER
grcTextureFactory::GetInstance().UnlockMRT();
GBuffer::LockTargets();
#endif
// cleanup:
const u32 stencilRef2 = DEFERRED_MATERIAL_DEFAULT;
grcStateBlock::SetDepthStencilState(hGrassAlphaToMaskDSS_pass2_end, stencilRef2);
// grcStateBlock::SetRasterizerState(hGrassAlphaToMaskRS_pass2_end);
grcStateBlock::SetBlendState(hGrassAlphaToMaskBS_pass2_end);
}
#elif PSN_CLEANUP_PASS
static void RenderGrassAlphaToMaskPass2()
{
grmShader *shader = CGrassRenderer::GetGrassShader();
Assert(shader);
grcEffectTechnique tech = CGrassRenderer::GetFakedGBufTechniqueID();
Assert(tech);
#if CPLANT_STORE_LOCTRI_NORMAL
shader->SetVar(CGrassRenderer::GetGBuf0TextureID(), GBuffer::GetTarget(GBUFFER_RT_0));
#endif
const Vector2 topLeft (-2.0f, -1.01f);
const Vector2 botRight(-1.01f, -2.0f);
shader->TWODBlit( topLeft.x, topLeft.y, botRight.x, botRight.y, 0.0f,
0.0f,0.0f, 1.0f,1.0f, Color32(255,255,255,255), tech);
}
#endif //PSN_ALPHATOMASK_PASS...
//
//
//
//
bool CPlantMgr::Render()
{
return gPlantMgr.RenderInternal();
}
bool CPlantMgr::RenderInternal()
{
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
if(!gbPlantMgrActive) // skip updating, if varconsole switch is off
return(FALSE);
#if __BANK
if (TiledScreenCapture::IsEnabled())
return(FALSE);
bRenderInternalActive = TRUE; // RenderInternal() active
#endif
#if __BANK
CPlantMgr::RenderDebugStuff();
#endif
#if LOCTRISTAB_STR
if(m_LocTrisTab[0] == NULL)
{ // loc tris tabs not allocated, so skip:
#if __BANK
bRenderInternalActive = FALSE; // RenderInternal() inactive
#endif
return(TRUE);
}
#endif
if(CGrassRenderer::GetGlobalInteriorCullAll())
{
#if __BANK
bRenderInternalActive = FALSE; // RenderInternal() inactive
#endif
return(TRUE); // camera is in interior and exterior is not visible through a portal
}
PIXBegin(0, "PlantsMgrRender");
PF_PUSH_TIMEBAR_DETAIL("PlantsMgrRender");
GRCDBG_PUSH("RenderBegin");
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
// setup render state:
grcBindTexture(NULL);
grcWorldIdentity();
// set the hdr value
const float hdrVal = Lights::GetRenderDirectionalLight().GetIntensity();
CShaderLib::SetGlobalEmissiveScale(hdrVal);
DeferredLighting::PrepareCutoutPass(false,true,false);
GRCDBG_POP();
GRCDBG_PUSH("BeginGrassAlphaToMaskPass1");
BeginGrassAlphaToMaskPass1();
GRCDBG_POP();
GRCDBG_PUSH("SpuInitialisePerRenderFrame");
CGrassRenderer::SpuInitialisePerRenderFrame();
GRCDBG_POP();
GRCDBG_PUSH("RenderActive");
/////////////////////////////////////////////////////////////////////////////////////////////////
const s32 oldStoredSeed = g_PlantsRendRand[g_RenderThreadIndex].GetSeed();
const u32 bufferID = GetRenderRTBufferID();
spdTransposedPlaneSet8 cullFrustum;
CGrassRenderer::GetGlobalCullFrustum(&cullFrustum);
bool bPlantsRendered = RenderClosedLists(bufferID, cullFrustum);
#if !PSN_ALPHATOMASK_PASS && !PSN_CLEANUP_PASS
(void)bPlantsRendered;
#endif
/////////////////////////////////////////////////////////////////////////////////////////////////
GRCDBG_POP();
GRCDBG_PUSH("SpuFinalPerRndrFrme");
CGrassRenderer::SpuFinalisePerRenderFrame();
// Restore original state.
GRCDBG_POP();
GRCDBG_PUSH("EndGrassAlphaToMaskPass1");
EndGrassAlphaToMaskPass1();
DeferredLighting::FinishCutoutPass();
#if EXTRA_1_5_PASS
GRCDBG_POP();
GRCDBG_PUSH("RenderGrassAlphaToMaskPass1_5");
if(bPlantsRendered)
{
RenderGrassAlphaToMaskPass1_5();
}
#endif
#if XENON_FILL_PASS
GRCDBG_POP();
GRCDBG_PUSH("360 Fill Pass");
if(bPlantsRendered)
{
XenonFillPass();
}
#endif
#if PSN_ALPHATOMASK_PASS || PSN_CLEANUP_PASS
if(bPlantsRendered)
{
GRCDBG_POP();
GRCDBG_PUSH("RenderGrassAlphaToMaskPass2");
RenderGrassAlphaToMaskPass2(0.0f, CPLANT_TRILOC_FAR_DIST); // We could use a tighter near & far bounds if we got that info from the PlantsMgr.
}
#endif
GRCDBG_POP();
GRCDBG_PUSH("RenderEnd");
CShaderLib::SetGlobalEmissiveScale(1.0f);
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
GRCDBG_POP();
PF_POP_TIMEBAR_DETAIL();
PIXEnd();
g_PlantsRendRand[g_RenderThreadIndex].Reset(oldStoredSeed);
#if __BANK
bRenderInternalActive = FALSE; // RenderInternal() inactive
#endif
return(TRUE);
}// end of RenderInternal()...
static dev_bool sm_bLodDistanceCheck = true;
//
//
//
//
bool CPlantMgr::RenderClosedLists(const u32 bufferID, spdTransposedPlaneSet8 &cullFrustum)
{
PF_PUSH_TIMEBAR_DETAIL("RenderClosedLists");
bool bPlantsRendered = false;
#if PLANTS_CAST_SHADOWS
ResetShadowCandidates();
#endif
#if !__PS3
CGrassRenderer::BeginUseNormalTechniques(cullFrustum);
#endif
const bool bIsCameraUnderwater = CGrassRenderer::GetGlobalCameraUnderwater();
const float fGlobaPlayerCollisionRadiusSqr = CGrassRenderer::GetGlobalPlayerCollisionRSqr();
#if __BANK || PLANTS_USE_LOD_SETTINGS
#if PLANTS_USE_LOD_SETTINGS
float grassDistanceMultiplier = CGrassRenderer::GetDistanceMultiplier();
#else
float grassDistanceMultiplier = 1.0f;
#endif
const float globalLOD0FarDist = grassDistanceMultiplier*gbPlantsLOD0FarDist;
const float globalLOD1CloseDist = grassDistanceMultiplier*gbPlantsLOD1CloseDist;
const float globalLOD1FarDist = grassDistanceMultiplier*gbPlantsLOD1FarDist;
const float globalLOD2CloseDist = grassDistanceMultiplier*gbPlantsLOD2CloseDist;
const float globalLOD2FarDist = grassDistanceMultiplier*gbPlantsLOD2FarDist;
#else
const float globalLOD0FarDist = CPLANT_LOD0_FAR_DIST;
const float globalLOD1CloseDist = CPLANT_LOD1_CLOSE_DIST;
const float globalLOD1FarDist = CPLANT_LOD1_FAR_DIST;
const float globalLOD2CloseDist = CPLANT_LOD2_CLOSE_DIST;
const float globalLOD2FarDist = CPLANT_LOD2_FAR_DIST;
#endif
const float globalLOD0FarDist2 = globalLOD0FarDist*globalLOD0FarDist;
const float globalLOD1CloseDist2 = globalLOD1CloseDist*globalLOD1CloseDist;
const float globalLOD1FarDist2 = globalLOD1FarDist*globalLOD1FarDist;
const float globalLOD2CloseDist2 = globalLOD2CloseDist*globalLOD2CloseDist;
const float globalLOD2FarDist2 = globalLOD2FarDist*globalLOD2FarDist;
//
// render all close lists:
//
#if !__FINAL
if(gbPlantMgrRenderActive)
#endif
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triRenderTab = *m_LocTrisRenderTab[bufferID][listID];
u16 loctri = triRenderTab.m_CloseListHead;
#if __BANK
// rendering stats:
pLocTriPlantsLOD0DrawnPerSlot = &nLocTriPlantsLOD0Drawn;
pLocTriPlantsLOD1DrawnPerSlot = &nLocTriPlantsLOD1Drawn;
pLocTriPlantsLOD2DrawnPerSlot = &nLocTriPlantsLOD2Drawn;
#endif
while(loctri)
{
CPlantLocTri *pLocTri = &triRenderTab[loctri];
bool bCreatesPlants = pLocTri->m_bCreatesPlants;
#if PLANTSMGR_DATA_EDITOR
// special case: edit mode allows all collision to be selected, possibly invalid tris are in the list:
if(GetAllCollisionSelectable())
{
const s32 procTagId = PGTAMATERIALMGR->UnpackProcId(pLocTri->m_nSurfaceType);
bCreatesPlants &= g_procInfo.CreatesPlants(procTagId);
}
#endif
#if CPLANT_USE_OCCLUSION
BANK_ONLY( if(gbPlantsUseOcclusion) )
{
// skip occluded tris:
bCreatesPlants &= (!pLocTri->m_bOccluded);
}
#endif
// draw only if it creates plants
if(bCreatesPlants)
{
#if PLANTS_CAST_SHADOWS
if(IsLocTriAShadowCandidate(pLocTri))
{
AddShadowCandidate(pLocTri);
}
#endif //PLANTS_CAST_SHADOWS
// check if pLocTri inside current View Frustum:
if(IsLocTriVisibleByCamera(cullFrustum, pLocTri) && IsLocTriVisibleUnderwater(pLocTri, bIsCameraUnderwater))
{
#if CPLANT_USE_COLLISION_2D_DIST
Vector3 vertDistV0a(pLocTri->GetV1() - CGrassRenderer::GetGlobalCameraPos());
Vector2 vertDistV0(vertDistV0a.x, vertDistV0a.y);
Vector3 vertDistV1a(pLocTri->GetV2() - CGrassRenderer::GetGlobalCameraPos());
Vector2 vertDistV1(vertDistV1a.x, vertDistV1a.y);
Vector3 vertDistV2a(pLocTri->GetV3() - CGrassRenderer::GetGlobalCameraPos());
Vector2 vertDistV2(vertDistV2a.x, vertDistV2a.y);
#else
Vector3 vertDistV0(pLocTri->GetV1() - CGrassRenderer::GetGlobalCameraPos());
Vector3 vertDistV1(pLocTri->GetV2() - CGrassRenderer::GetGlobalCameraPos());
Vector3 vertDistV2(pLocTri->GetV3() - CGrassRenderer::GetGlobalCameraPos());
#endif
const float fVert0Dist2 = vertDistV0.Mag2();
const float fVert1Dist2 = vertDistV1.Mag2();
const float fVert2Dist2 = vertDistV2.Mag2();
#if __DEV || SECTOR_TOOLS_EXPORT
pLocTri->m_nMaxNumPlants = 0;
#endif
s32 procTagId = PGTAMATERIALMGR->UnpackProcId(pLocTri->m_nSurfaceType);
s32 plantInfoIndex = g_procInfo.m_procTagTable[procTagId].plantIndex;
#if __ASSERT || __BANK
// help assert to catch evil BS#1010542:
if(plantInfoIndex == -1)
{
Assertf(0, "Helper for BS#1010542: Please call Andrzej when this triggers: pLocTri=0x%p, listID=%d, procTagId=%d, surfaceType=0x%" I64FMT "x.", pLocTri, listID, procTagId, pLocTri->m_nSurfaceType);
Printf("Helper for BS#1010542: Please call Andrzej when this triggers: pLocTri=0x%p, listID=%d, procTagId=%d, surfaceType=0x%" I64FMT "x.", pLocTri, listID, procTagId, pLocTri->m_nSurfaceType);
TrapEQ(0,0);
}
#endif
//PF_PUSH_TIMEBAR_DETAIL("m_plantInfos");
atHashValue currentProcTag = g_procInfo.m_plantInfos[plantInfoIndex].m_Tag;
for( ; plantInfoIndex < g_procInfo.m_plantInfos.GetCount() && g_procInfo.m_plantInfos[plantInfoIndex].m_Tag==currentProcTag ; plantInfoIndex++)
{
CPlantInfo *pPlantInfo = &g_procInfo.m_plantInfos[plantInfoIndex];
PPTriPlant triPlant;
triPlant.V1 = pLocTri->m_V1;
triPlant.V2 = pLocTri->m_V2;
triPlant.V3 = pLocTri->m_V3;
Assert(pPlantInfo->m_ModelId < 255); // must fit into u8
triPlant.model_id = (u8)pPlantInfo->m_ModelId;
#if CPLANT_CLIP_EDGE_VERT
triPlant.m_ClipEdge_01 = pLocTri->m_ClipEdge_01 && gbPlantMgrClipEnable && gbPlantMgrEdgeClipEnable;
triPlant.m_ClipEdge_12 = pLocTri->m_ClipEdge_12 && gbPlantMgrClipEnable && gbPlantMgrEdgeClipEnable;
triPlant.m_ClipEdge_20 = pLocTri->m_ClipEdge_20 && gbPlantMgrClipEnable && gbPlantMgrEdgeClipEnable;
triPlant.m_ClipVert_0 = pLocTri->m_ClipVert_0 && gbPlantMgrClipEnable && gbPlantMgrVertClipEnable;
triPlant.m_ClipVert_1 = pLocTri->m_ClipVert_1 && gbPlantMgrClipEnable && gbPlantMgrVertClipEnable;
triPlant.m_ClipVert_2 = pLocTri->m_ClipVert_2 && gbPlantMgrClipEnable && gbPlantMgrVertClipEnable;
#endif
g_PlantsRendRand[g_RenderThreadIndex].Reset(pLocTri->GetSeed());
#if __BANK
// forced ground color:
if(gbForceGroundColor)
{
const u8 gbGroundDensityM = u8(float(gbGroundDensity_V1+gbGroundDensity_V2+gbGroundDensity_V3)/3.0f + 0.5f);
triPlant.groundColorV1 = gbGroundColorV1;
triPlant.groundColorV1.SetAlpha(CPlantLocTri::pv8PackDensityScaleZScaleXYZ(gbGroundDensityM, (u8)gbGroundScaleZ_V1, (u8)gbGroundScaleXYZ_V1));
triPlant.groundColorV2 = gbGroundColorV2;
triPlant.groundColorV2.SetAlpha(CPlantLocTri::pv8PackDensityScaleZScaleXYZ(gbGroundDensityM, (u8)gbGroundScaleZ_V2, (u8)gbGroundScaleXYZ_V2));
triPlant.groundColorV3 = gbGroundColorV3;
triPlant.groundColorV3.SetAlpha(CPlantLocTri::pv8PackDensityScaleZScaleXYZ(gbGroundDensityM, (u8)gbGroundScaleZ_V3, (u8)gbGroundScaleXYZ_V3));
}
else
#endif //__BANK
{
triPlant.groundColorV1 = pLocTri->m_GroundColorV1;
triPlant.groundColorV2 = pLocTri->m_GroundColorV2;
triPlant.groundColorV3 = pLocTri->m_GroundColorV3;
}
//
// max number of plants to generate for this TriLoc
// this is connected with triangle area + surface type:
//
// grab medium pv density weight for tri:
const float pvDensityWeight = CPlantLocTri::pv8UnpackAndMapDensity(triPlant.groundColorV1.GetAlpha());
float fDensity = pPlantInfo->m_Density.GetFloat32_FromFloat16() - pvDensityWeight*pPlantInfo->m_DensityRange.GetFloat32_FromFloat16();
fDensity = fDensity>=0.0f? fDensity : 0.0f; // density must be positive
float numPlants = pLocTri->GetTriArea() * fDensity;
s32 nMaxNumPlants = (s32)numPlants;
float remainder = numPlants - nMaxNumPlants;
if (g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f)<remainder)
{
nMaxNumPlants++;
}
if (nMaxNumPlants==0)
{
continue;
}
#if __DEV || SECTOR_TOOLS_EXPORT
pLocTri->m_nMaxNumPlants += nMaxNumPlants;
#endif
triPlant.num_plants = (u16)nMaxNumPlants;
// skip the tri - no valid textures to render:
if( (!m_PlantTextureTab0[pPlantInfo->m_TextureId]) ||
(!m_PlantTextureTab0[pPlantInfo->m_TextureId+CPLANT_SLOT_NUM_TEXTURES]) )
{
#if __BANK
nLocTriTexSkipped++;
#endif
continue;
}
#if PSN_PLANTSMGR_SPU_RENDER
triPlant.texture_id = pPlantInfo->m_TextureId; // no of texture to use
triPlant.textureLOD1_id = pPlantInfo->m_TextureId+CPLANT_SLOT_NUM_TEXTURES; // LOD1 texture
#else
triPlant.texture_ptr = m_PlantTextureTab0[pPlantInfo->m_TextureId]; // texture ptr to use
triPlant.textureLOD1_ptr= m_PlantTextureTab0[pPlantInfo->m_TextureId+CPLANT_SLOT_NUM_TEXTURES]; // textureLOD1 ptr to use
#endif
triPlant.color = pPlantInfo->m_Color;
triPlant.intensity = pPlantInfo->m_Intensity;
triPlant.intensity_var = pPlantInfo->m_IntensityVar;
triPlant.seed = pLocTri->GetSeed() + (plantInfoIndex*0x33); // g_PlantsRendRand.GetRanged(0.0f, 1.0f)
triPlant.packedScale.x = pPlantInfo->m_ScaleXY; // scale in XY
triPlant.packedScale.y = pPlantInfo->m_ScaleZ; // scale in Z
triPlant.packedScale.z = pPlantInfo->m_ScaleVariationXY; // scale xyz variation
triPlant.packedScale.w = pPlantInfo->m_ScaleVariationZ;
triPlant.scaleRangeXYZ_Z.x = pPlantInfo->m_ScaleRangeXYZ;
triPlant.scaleRangeXYZ_Z.y = pPlantInfo->m_ScaleRangeZ;
triPlant.um_param.x = pPlantInfo->m_MicroMovementsScaleH; // micro-movements: global horizontal scale
triPlant.um_param.y = pPlantInfo->m_MicroMovementsScaleV; // micro-movements: global vertical scale
triPlant.um_param.z = pPlantInfo->m_MicroMovementsFreqH; // micro-movements: global horizontal frequency
triPlant.um_param.w = pPlantInfo->m_MicroMovementsFreqV; // micro-movements: global vertical frequency
triPlant.V1.w = pPlantInfo->m_WindBendScale.GetFloat32_FromFloat16(); // wind bending scale
triPlant.V2.w = pPlantInfo->m_WindBendVariation.GetFloat32_FromFloat16(); // wind bending variation
static dev_float fPlayerDefaultCollRadiusSqr = 1.0f*1.0f; //0.75f; // radius of influence
const float collisionRadiusSqr = pPlantInfo->GetCollisionRadiusSqr() * fGlobaPlayerCollisionRadiusSqr * fPlayerDefaultCollRadiusSqr;
// x = collision radius sqr:
triPlant.coll_params.x.SetFloat16_FromFloat32(collisionRadiusSqr);
// y = inv collision radius sqr:
triPlant.coll_params.y.SetFloat16_FromFloat32(1.0f/collisionRadiusSqr);
triPlant.flags = pPlantInfo->m_Flags;
if(pLocTri->m_bDrawFarTri)
{
triPlant.flags.Set(PROCPLANT_LOD2FARFADE); // set "far far fade" flag
}
if(triPlant.flags.IsClear(PROCPLANT_LOD0) && triPlant.flags.IsClear(PROCPLANT_LOD1) && triPlant.flags.IsClear(PROCPLANT_LOD2))
{
continue; // if no visible LODs for procedural grass then skip this tri
}
if(sm_bLodDistanceCheck)
{
// distance check vs available LODs:
if(pPlantInfo->m_Flags.IsClear(PROCPLANT_LOD0))
{
if( (fVert0Dist2 < globalLOD0FarDist2) &&
(fVert1Dist2 < globalLOD0FarDist2) &&
(fVert2Dist2 < globalLOD0FarDist2) )
{
bool bSkipLOD0 = true;
// does tri overlap with LOD1?
if(pPlantInfo->m_Flags.IsSet(PROCPLANT_LOD1))
{
if( ((fVert0Dist2 >= globalLOD1CloseDist2) && (fVert0Dist2 <= globalLOD1FarDist2)) ||
((fVert1Dist2 >= globalLOD1CloseDist2) && (fVert1Dist2 <= globalLOD1FarDist2)) ||
((fVert2Dist2 >= globalLOD1CloseDist2) && (fVert2Dist2 <= globalLOD1FarDist2)) )
{
bSkipLOD0 = false;
}
}
if(bSkipLOD0)
{
continue; // tri falls into LOD0, but flag is cleared
}
}
}// LOD0...
if(pPlantInfo->m_Flags.IsClear(PROCPLANT_LOD1))
{
if( ((fVert0Dist2 > globalLOD1CloseDist2) && (fVert0Dist2 < globalLOD1FarDist2)) &&
((fVert1Dist2 > globalLOD1CloseDist2) && (fVert1Dist2 < globalLOD1FarDist2)) &&
((fVert2Dist2 > globalLOD1CloseDist2) && (fVert2Dist2 < globalLOD1FarDist2)) )
{
bool bSkipLOD1 = true;
// does tri overlap with LOD0?
if(pPlantInfo->m_Flags.IsSet(PROCPLANT_LOD0))
{
if( (fVert0Dist2 <= globalLOD0FarDist2) ||
(fVert1Dist2 <= globalLOD0FarDist2) ||
(fVert2Dist2 <= globalLOD0FarDist2) )
{
bSkipLOD1 = false;
}
}
// does tri overlap with LOD2?
if(pPlantInfo->m_Flags.IsSet(PROCPLANT_LOD2))
{
if( ((fVert0Dist2 >= globalLOD2CloseDist2) && (fVert0Dist2 <= globalLOD2FarDist2)) ||
((fVert1Dist2 >= globalLOD2CloseDist2) && (fVert1Dist2 <= globalLOD2FarDist2)) ||
((fVert2Dist2 >= globalLOD2CloseDist2) && (fVert2Dist2 <= globalLOD2FarDist2)) )
{
bSkipLOD1 = false;
}
}
if(bSkipLOD1)
{
continue; // tri falls into LOD1, but flag is cleared
}
}
}// LOD1...
if(pPlantInfo->m_Flags.IsClear(PROCPLANT_LOD2))
{
if( ((fVert0Dist2 > globalLOD2CloseDist2) && (fVert0Dist2 < globalLOD2FarDist2)) &&
((fVert1Dist2 > globalLOD2CloseDist2) && (fVert1Dist2 < globalLOD2FarDist2)) &&
((fVert2Dist2 > globalLOD2CloseDist2) && (fVert2Dist2 < globalLOD2FarDist2)) )
{
bool bSkipLOD2 = true;
// does tri overlap with LOD1?
if(pPlantInfo->m_Flags.IsSet(PROCPLANT_LOD1))
{
if( ((fVert0Dist2 >= globalLOD1CloseDist2) && (fVert0Dist2 <= globalLOD1FarDist2)) ||
((fVert1Dist2 >= globalLOD1CloseDist2) && (fVert1Dist2 <= globalLOD1FarDist2)) ||
((fVert2Dist2 >= globalLOD1CloseDist2) && (fVert2Dist2 <= globalLOD1FarDist2)) )
{
bSkipLOD2 = false;
}
}
if(bSkipLOD2)
{
continue; // tri falls into LOD2, but flag is clered
}
}
}// LOD2...
}//LodDistanceCheck...
// skewing info:
triPlant.skewAxisAngle = pLocTri->m_skewAxisAngle;
// lame: m_normal should be u32:
triPlant.loctri_normal[0] = pLocTri->m_normal[0];
triPlant.loctri_normal[1] = pLocTri->m_normal[1];
triPlant.loctri_normal[2] = pLocTri->m_normal[2];
triPlant.ambient_scl = pLocTri->m_nAmbientScale[0];
//PF_PUSH_TIMEBAR_DETAIL("AddTriPlant");
CGrassRenderer::AddTriPlant(&triPlant, 0);
//PF_POP_TIMEBAR_DETAIL();
#if __BANK
nLocTriDrawn++;
#endif
bPlantsRendered = true;
}// for( ; (g_procInfo.m_plantInfos[plantInfoIndex].m_procTagId==procTagId) && (plantInfoIndex < g_procInfo.m_numPlantInfos); plantInfoIndex++)...
//PF_POP_TIMEBAR_DETAIL();
}// if(IsLocTriVisibleByCamera(pLocTri))...
} // if(!pLocTri->m_createsPlants)...
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
PF_PUSH_TIMEBAR_DETAIL("FlushTriPlantBuffer");
CGrassRenderer::FlushTriPlantBuffer();
#if PLANTS_CAST_SHADOWS
AddShadowCandidate(NULL);
#endif
PF_POP_TIMEBAR_DETAIL();
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
#if !__PS3
CGrassRenderer::EndUseNormalTechniques();
#endif
PF_POP_TIMEBAR_DETAIL();
return bPlantsRendered;
}
//
//
//
//
bool CPlantMgr::RenderDecal()
{
return gPlantMgr.RenderDecalInternal();
}
bool CPlantMgr::RenderDecalInternal()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
if(!gbPlantMgrActive)
return(FALSE);
#if __BANK
if (TiledScreenCapture::IsEnabled())
return(FALSE);
bRenderInternalActive = TRUE; // RenderInternal() active
#endif
#if LOCTRISTAB_STR
if(m_LocTrisTab[0] == NULL)
{ // loc tris tabs not allocated, so skip:
#if __BANK
bRenderInternalActive = FALSE; // RenderInternal() inactive
#endif
return(TRUE);
}
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
#if FURGRASS_TEST_V4
PIXBegin(0, "Furgrass::Render");
PF_PUSH_TIMEBAR_DETAIL("Furgrass::Render");
if(DoFurgrassRender())
{
m_bFurgrassTrisRtPresent[GetRenderRTBufferID()] =
FurGrassLODRender(CGrassRenderer::GetGlobalCameraPos(), CGrassRenderer::GetGlobalCameraFront(), CGrassRenderer::GetGlobalPlayerPos());
}
PF_POP_TIMEBAR_DETAIL();
PIXEnd();
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
#if __BANK
bRenderInternalActive = FALSE; // RenderInternal() inactive
#endif
return(TRUE);
}// end of RenderDecalInternal()...
#if PLANTS_CAST_SHADOWS
bool CPlantMgr::ShadowRender()
{
#if !__FINAL
if(gbPlantMgrRenderActive)
{
#endif
#if __BANK
if(CGrassRenderer::ms_drawShadows)
{
#endif
bool ret = gPlantMgr.ShadowRenderInternal();
return ret;
#if __BANK
}
else
{
return true;
}
#endif
#if !__FINAL
}
else
{
return true;
}
#endif
}
bool CPlantMgr::ShadowRenderInternal()
{
PIXBegin(0, "PlantsMgrRenderShadow");
GRC_ALLOC_SCOPE_AUTO_PUSH_POP()
grcWorldIdentity();
const s32 oldStoredSeed = g_PlantsRendRand[g_RenderThreadIndex].GetSeed();
spdTransposedPlaneSet8 cullFrustum;
cullFrustum.Set(*grcViewport::GetCurrent());
grcStateBlock::SetRasterizerState(hGrassShadowRS);
RenderShadowCandidates(cullFrustum);
grcStateBlock::SetRasterizerState(hGrassShadowRS_end);
g_PlantsRendRand[g_RenderThreadIndex].Reset(oldStoredSeed);
PIXEnd();
return true;
}
void CPlantMgr::InitShadowCandidates()
{
for(u32 i=0; i<2; i++)
{
m_noOfShadowCandidates[i] = 0;
}
}
void CPlantMgr::ResetShadowCandidates()
{
#if __BANK
static u8 printWarn=0;
if(m_noOfShadowCandidates[GetUpdateRTBufferID()] == CPLANT_LOC_TRI_SHADOW_CANDIDATES_SIZE && (!((printWarn++)&0x7f)))
{
plantsWarningf("CPlantMgr::ResetShadowCandidates(): Need to up the shadow candidate buffer!");
}
#endif
m_noOfShadowCandidates[GetUpdateRTBufferID()] = 0;
}
void CPlantMgr::AddShadowCandidate(CPlantLocTri *pLocTri)
{
if(m_noOfShadowCandidates[GetUpdateRTBufferID()] < CPLANT_LOC_TRI_SHADOW_CANDIDATES_SIZE)
{
m_ShadowCandidates[GetUpdateRTBufferID()][ m_noOfShadowCandidates[GetUpdateRTBufferID()]++ ] = pLocTri;
}
}
void CPlantMgr::RenderShadowCandidates(spdTransposedPlaneSet8 &cullFrustum)
{
const bool bIsCameraUnderwater = CGrassRenderer::GetGlobalCameraUnderwater();
const float fGlobaPlayerCollisionRadiusSqr = CGrassRenderer::GetGlobalPlayerCollisionRSqr();
const u32 shadowCandidatesCount = m_noOfShadowCandidates[GetRenderRTBufferID()];
if(shadowCandidatesCount == 0)
{
return;
}
u32 noOfLocTris = 0;
CGrassRenderer::BeginUseShadowTechniques(cullFrustum);
for(u32 candidateIdx=0; candidateIdx<shadowCandidatesCount; candidateIdx++)
{
CPlantLocTri *pLocTri = m_ShadowCandidates[GetRenderRTBufferID()][candidateIdx];
if(pLocTri == NULL)
{
if(noOfLocTris != 0)
{
CGrassRenderer::FlushTriPlantBuffer();
noOfLocTris = 0;
}
continue;
}
// Check if pLocTri inside current View Frustum:
if(IsLocTriVisibleByCamera(cullFrustum, pLocTri) && IsLocTriVisibleUnderwater(pLocTri, bIsCameraUnderwater))
{
s32 procTagId = PGTAMATERIALMGR->UnpackProcId(pLocTri->m_nSurfaceType);
s32 plantInfoIndex = g_procInfo.m_procTagTable[procTagId].plantIndex;
#if (RSG_PC || RSG_DURANGO || RSG_ORBIS)
if (plantInfoIndex < 0)
{
continue;
}
#endif // (RSG_PC || RSG_DURANGO || RSG_ORBIS)
atHashValue currentProcTag = g_procInfo.m_plantInfos[plantInfoIndex].m_Tag;
for( ; plantInfoIndex < g_procInfo.m_plantInfos.GetCount() && g_procInfo.m_plantInfos[plantInfoIndex].m_Tag==currentProcTag ; plantInfoIndex++)
{
CPlantInfo *pPlantInfo = &g_procInfo.m_plantInfos[plantInfoIndex];
if(pPlantInfo->m_Flags.IsSet(PROCPLANT_NOSHADOW))
{
continue;
}
PPTriPlant triPlant;
triPlant.V1 = pLocTri->m_V1;
triPlant.V2 = pLocTri->m_V2;
triPlant.V3 = pLocTri->m_V3;
Assert(pPlantInfo->m_ModelId < 255); // must fit into u8
triPlant.model_id = (u8)pPlantInfo->m_ModelId;
g_PlantsRendRand[g_RenderThreadIndex].Reset(pLocTri->GetSeed());
triPlant.groundColorV1 = pLocTri->m_GroundColorV1;
triPlant.groundColorV2 = pLocTri->m_GroundColorV2;
triPlant.groundColorV3 = pLocTri->m_GroundColorV3;
//
// max number of plants to generate for this TriLoc
// this is connected with triangle area + surface type:
//
// grab medium pv density weight for tri:
const float pvDensityWeight = CPlantLocTri::pv8UnpackAndMapDensity(triPlant.groundColorV1.GetAlpha());
float fDensity = pPlantInfo->m_Density.GetFloat32_FromFloat16() - pvDensityWeight*pPlantInfo->m_DensityRange.GetFloat32_FromFloat16();
fDensity = fDensity>=0.0f? fDensity : 0.0f; // density must be positive
float numPlants = pLocTri->GetTriArea() * fDensity;
s32 nMaxNumPlants = (s32)numPlants;
float remainder = numPlants - nMaxNumPlants;
if (g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f)<remainder)
{
nMaxNumPlants++;
}
if (nMaxNumPlants==0)
{
continue;
}
triPlant.num_plants = (u16)nMaxNumPlants;
Assert(m_PlantTextureTab0[pPlantInfo->m_TextureId]);
Assert(m_PlantTextureTab0[pPlantInfo->m_TextureId+CPLANT_SLOT_NUM_TEXTURES]);
triPlant.texture_ptr = m_PlantTextureTab0[pPlantInfo->m_TextureId]; // texture ptr to use
triPlant.textureLOD1_ptr= m_PlantTextureTab0[pPlantInfo->m_TextureId+CPLANT_SLOT_NUM_TEXTURES]; // textureLOD1 ptr to use
triPlant.color = pPlantInfo->m_Color;
triPlant.intensity = pPlantInfo->m_Intensity;
triPlant.intensity_var = pPlantInfo->m_IntensityVar;
triPlant.seed = pLocTri->GetSeed() + (plantInfoIndex*0x33); // g_PlantsRendRand.GetRanged(0.0f, 1.0f)
triPlant.packedScale.x = pPlantInfo->m_ScaleXY; // scale in XY
triPlant.packedScale.y = pPlantInfo->m_ScaleZ; // scale in Z
triPlant.packedScale.z = pPlantInfo->m_ScaleVariationXY; // scale xyz variation
triPlant.packedScale.w = pPlantInfo->m_ScaleVariationZ;
triPlant.scaleRangeXYZ_Z.x = pPlantInfo->m_ScaleRangeXYZ;
triPlant.scaleRangeXYZ_Z.y = pPlantInfo->m_ScaleRangeZ;
triPlant.um_param.x = pPlantInfo->m_MicroMovementsScaleH; // micro-movements: global horizontal scale
triPlant.um_param.y = pPlantInfo->m_MicroMovementsScaleV; // micro-movements: global vertical scale
triPlant.um_param.z = pPlantInfo->m_MicroMovementsFreqH; // micro-movements: global horizontal frequency
triPlant.um_param.w = pPlantInfo->m_MicroMovementsFreqV; // micro-movements: global vertical frequency
triPlant.V1.w = pPlantInfo->m_WindBendScale.GetFloat32_FromFloat16(); // wind bending scale
triPlant.V2.w = pPlantInfo->m_WindBendVariation.GetFloat32_FromFloat16(); // wind bending variation
static dev_float fPlayerDefaultCollRadiusSqr = 1.0f*1.0f; //0.75f; // radius of influence
const float collisionRadiusSqr = pPlantInfo->GetCollisionRadiusSqr() * fGlobaPlayerCollisionRadiusSqr * fPlayerDefaultCollRadiusSqr;
// x = collision radius sqr:
triPlant.coll_params.x.SetFloat16_FromFloat32(collisionRadiusSqr);
// y = inv collision radius sqr:
triPlant.coll_params.y.SetFloat16_FromFloat32(1.0f/collisionRadiusSqr);
triPlant.flags = pPlantInfo->m_Flags;
if(pLocTri->m_bDrawFarTri)
{
triPlant.flags.Set(PROCPLANT_LOD2FARFADE); // set "far far fade" flag
}
if(triPlant.flags.IsClear(PROCPLANT_LOD0) && triPlant.flags.IsClear(PROCPLANT_LOD1) && triPlant.flags.IsClear(PROCPLANT_LOD2))
{
continue; // if no visible LODs for procedural grass then skip this tri
}
// skewing info:
triPlant.skewAxisAngle = pLocTri->m_skewAxisAngle;
// lame: m_normal should be u32:
triPlant.loctri_normal[0] = pLocTri->m_normal[0];
triPlant.loctri_normal[1] = pLocTri->m_normal[1];
triPlant.loctri_normal[2] = pLocTri->m_normal[2];
CGrassRenderer::AddTriPlant(&triPlant, 0);
noOfLocTris++;
}
}
}
if(noOfLocTris != 0)
{
CGrassRenderer::FlushTriPlantBuffer();
}
CGrassRenderer::EndUseShadowTechniques();
}
#endif //PLANTS_CAST_SHADOWS
//#pragma mark -
//#pragma mark --- CPlantColEntEntry stuff ---
//
//
//
//
CPlantColBoundEntry* CPlantColBoundEntry::AddEntry(CEntity *pEntity, s32 parentIdx, s32 childIdx)
{
Assert(gPlantMgr.m_ColEntCache.m_UnusedListHead);
this->m__pEntity = pEntity; // this assigment must be first, otherwise all Get...() methods below won't work...
this->m_bMaterialBound = false;
this->m_nBParentIndex = -1;
this->m_nBChildIndex = -1;
#if __ASSERT // can't have both systems valid at the same time
if(pEntity)
{
Assert(parentIdx==-1);
Assert(childIdx ==-1);
}
else
{
Assert(parentIdx!=-1);
Assert(childIdx !=-1);
}
#endif
if((parentIdx!=-1) && (childIdx!=-1))
{
Assert(pEntity==NULL);
this->m_bMaterialBound = true;
this->m_nBParentIndex = parentIdx;
this->m_nBChildIndex = childIdx;
}
phBoundGeometry* pBound = this->GetBound();
Assert(pBound); // should it ever happen?
if(!pBound)
{
return(NULL); // nothing added...
}
const Matrix34 *pSrcBoundMat = this->GetBoundMatrix();
if(pSrcBoundMat)
{
this->m_BoundMat.Set(*pSrcBoundMat);
// check whether BoundMat is an identity matrix (so no transformations are required for LocTris):
this->m_bBoundMatIdentity = (!pSrcBoundMat->IsNotEqual(M34_IDENTITY));
}
else
{ // should it ever happen?
// idea: maybe flag this situation and get matrix later?
//Assertf(FALSE, "Bound matrix not available!");
this->m_BoundMat.Set(M34_IDENTITY);
this->m_bBoundMatIdentity = TRUE;
}
if(pBound->GetNumPolygons() > 0)
{
const u32 numTris = pBound->GetNumPolygons();
Assert(numTris < 65536); // must fit into u16
#if !__FINAL
if(numTris >= 65536)
{
Displayf("\n Invalid amount of polygons in bound: %d", numTris);
return(NULL);
}
#endif
this->m_nNumTris = (u16)numTris;
this->m_LocTriArray = rage_aligned_new(16) CTriHashIdx16[numTris];
Assert(this->m_LocTriArray);
if(!this->m_LocTriArray)
{
return(NULL);
}
Assert16(this->m_LocTriArray); // must be dma'able by PlantsMgrUpdateSpu
sysMemSet(this->m_LocTriArray, 0x00, numTris*sizeof(CTriHashIdx16));
u32 netSkipCount=0;
// initialize material props for all polys of the bound:
this->m_BoundMatProps.Init(numTris*2);
for(u32 i=0; i<numTris; i++)
{
// check surface type of polygon:
const s32 boundMaterialID = pBound->GetPolygonMaterialIndex(i);
const phMaterialMgr::Id actualMaterialID = pBound->GetMaterialId(boundMaterialID);
s32 procTagId = PGTAMATERIALMGR->UnpackProcId(actualMaterialID);
bool bCreatesPlants = g_procInfo.CreatesPlants(procTagId);
bool bCreatesObjects = g_procInfo.CreatesProcObjects(procTagId);
#if 0 // this check is now done during export
const phPolygon& poly = pBound->GetPolygon(i);
// measure sides of triangles in current bound (potential pLocTris)
// and reject them if they are too small / too thin:
const phPolygon::Index vIndex0 = poly.GetVertexIndex(0);
const phPolygon::Index vIndex1 = poly.GetVertexIndex(1);
const phPolygon::Index vIndex2 = poly.GetVertexIndex(2);
const Vector3 tv0 = pBound->GetVertex(vIndex0);
const Vector3 tv1 = pBound->GetVertex(vIndex1);
const Vector3 tv2 = pBound->GetVertex(vIndex2);
// calculate lengths of triangle's sides:
const Vector3 side0 = tv0 - tv1;
const Vector3 side1 = tv1 - tv2;
const Vector3 side2 = tv2 - tv0;
const float sideLenSqr0 = side0.Mag2();
const float sideLenSqr1 = side1.Mag2();
const float sideLenSqr2 = side2.Mag2();
const float TRILOC_MINIMUM_SIDE_LEN = 2.5f;
if( (sideLenSqr0 < TRILOC_MINIMUM_SIDE_LEN*TRILOC_MINIMUM_SIDE_LEN) ||
(sideLenSqr1 < TRILOC_MINIMUM_SIDE_LEN*TRILOC_MINIMUM_SIDE_LEN) ||
(sideLenSqr2 < TRILOC_MINIMUM_SIDE_LEN*TRILOC_MINIMUM_SIDE_LEN) )
{ // triangle is too small, don't allow procedural stuff to be created on it:
bCreatesObjects = FALSE;
bCreatesPlants = FALSE;
}
#endif //#if 0...
if(NetworkInterface::IsGameInProgress())
{
if(bCreatesPlants)
{
if((netSkipCount&0x03)==3)
{
bCreatesObjects =
bCreatesPlants = false;
}
netSkipCount++;
}
}
this->m_BoundMatProps.Set(i*2+0, bCreatesPlants);
this->m_BoundMatProps.Set(i*2+1, bCreatesObjects);
}
#if PLANTSMGR_MULTI_UPDATE
this->m_processedTris.Init(numTris);
#endif
gPlantMgr.MoveColEntToList(&gPlantMgr.m_ColEntCache.m_UnusedListHead, &gPlantMgr.m_ColEntCache.m_CloseListHead, this);
#if FURGRASS_TEST_V4
// Furgrass: pick up textures and settings from underlying map object:
this->m_furInfo.m_bValid = FALSE; // mark as invalid for a start
if(!IsMatBound() && pEntity && pEntity->IsArchetypeSet())
{
CBaseModelInfo* pMI = pEntity->GetBaseModelInfo();
Assert(pMI);
rmcDrawable *pDrawable = pMI->GetDrawable();
if(pDrawable)
{
grmShaderGroup *pShaderGroup = &pDrawable->GetShaderGroup();
Assert(pShaderGroup);
grmShader *pShader = pShaderGroup->LookupShader("grass_fur_lod");
if(pShader)
{
// DiffuseTexFur:
grcEffectVar varDiffTex = pShader->LookupVar("DiffuseTexFur", false);
if(varDiffTex)
{
pShader->GetVar(varDiffTex, this->m_furInfo.m_furGrassDiffTex);
}
else
{
this->m_furInfo.m_furGrassDiffTex = NULL;
}
// BumpTexFur:
grcEffectVar varBumpTex = pShader->LookupVar("BumpTexFur", false);
if(varBumpTex)
{
pShader->GetVar(varBumpTex, this->m_furInfo.m_furGrassNormalTex);
}
else
{
this->m_furInfo.m_furGrassNormalTex = NULL;
}
// ComboHeightTexFur:
grcEffectVar varComboHeighTex0 = pShader->LookupVar("ComboHeightTexFur", false);
if(varComboHeighTex0)
{
pShader->GetVar(varComboHeighTex0, this->m_furInfo.m_furGrassComboH4Tex[0]);
}
else
{
this->m_furInfo.m_furGrassComboH4Tex[0] = NULL;
}
// ComboHeightTexFur2:
grcEffectVar varComboHeighTex2 = pShader->LookupVar("ComboHeightTexFur2", false);
if(varComboHeighTex2)
{
pShader->GetVar(varComboHeighTex2, this->m_furInfo.m_furGrassComboH4Tex[1]);
}
else
{
this->m_furInfo.m_furGrassComboH4Tex[1] = NULL;
}
// ComboHeightTexFur3:
grcEffectVar varComboHeighTex3 = pShader->LookupVar("ComboHeightTexFur3", false);
if(varComboHeighTex3)
{
pShader->GetVar(varComboHeighTex3, this->m_furInfo.m_furGrassComboH4Tex[2]);
}
else
{
this->m_furInfo.m_furGrassComboH4Tex[2] = NULL;
}
// ComboHeightTexFur4:
grcEffectVar varComboHeighTex4 = pShader->LookupVar("ComboHeightTexFur4", false);
if(varComboHeighTex4)
{
pShader->GetVar(varComboHeighTex4, this->m_furInfo.m_furGrassComboH4Tex[3]);
}
else
{
this->m_furInfo.m_furGrassComboH4Tex[3] = NULL;
}
this->m_furInfo.m_bValid = TRUE; // mark as valid
}// if(pShader)...
}// if(pDrawable)...
}// if(pEntity->GetModelIndex() != INVALID_MI)...
#endif //FURGRASS_TEST_V4...
return(this);
}
return(NULL);
}
//
//
//
//
void CPlantColBoundEntry::ReleaseEntry()
{
if(this->m_LocTriArray)
{
// release LocTris first:
const u32 count = this->m_nNumTris;
for(u32 i=0; i<count; i++)
{
const CTriHashIdx16 hashIdx = m_LocTriArray[i];
const u16 listID = hashIdx.GetListID();
const u16 tri = hashIdx.GetIdx();
if(tri)
{
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
CPlantLocTri *pTri = &triTab[tri];
pTri->Release(triTab);
}
}
delete[] this->m_LocTriArray;
this->m_LocTriArray = NULL;
this->m_BoundMatProps.Kill();
#if PLANTSMGR_MULTI_UPDATE
this->m_processedTris.Kill();
#endif
this->m_nNumTris = 0;
#if FURGRASS_TEST_V4
this->m_furInfo.m_bValid= FALSE;
#endif
}
if(IsMatBound())
{
Assert(this->m__pEntity==NULL);
this->m_nBParentIndex =-1;
this->m_nBChildIndex =-1;
}
else
{
Assert(this->m_nBParentIndex==-1);
Assert(this->m_nBChildIndex==-1);
this->m__pEntity = NULL;
}
gPlantMgr.MoveColEntToList(&gPlantMgr.m_ColEntCache.m_CloseListHead, &gPlantMgr.m_ColEntCache.m_UnusedListHead, this);
}
//#pragma mark -
//#pragma mark --- MoveToList stuff ---
#if CPLANT_DYNAMIC_CULL_SPHERES
//
//
//
//
u32 CPlantMgr::AddDynamicCullSphere(const spdSphere &s)
{
u32 handle = PLANTSMGR_INVALID_DYNAMIC_CULL_SPHERE_INDEX;
//Don't add an invalid sphere.
if(s.GetRadiusf() <= 0.0f)
{
Assertf(s.GetRadiusf() > 0.0f, "WARNING: Call to add a dynamic culling sphere for grass ignored. An invalid sphere was passed in:\t\tCenter = {%f, %f, %f}\tRadius = %f",
VEC3V_ARGS(s.GetCenter()), s.GetRadiusf() );
return handle;
}
//Search for an empty slot in the cull sphere array to add the current sphere.
for(DynCullSphereSourceArray::size_type i = 0; i < m_DynCullSpheres.size(); ++i)
{
if(m_DynCullSpheres[i].GetRadiusf() == 0.0f)
{
m_DynCullSpheres[i] = s;
handle = i;
break;
}
}
Assertf( handle != PLANTSMGR_INVALID_DYNAMIC_CULL_SPHERE_INDEX,
"WARNING: Grass dynamic cull sphere array is full! Ignoring call to add new cull sphere:\t\tCenter = {%f, %f, %f}\tRadius = %f",
VEC3V_ARGS(s.GetCenter()), s.GetRadiusf() );
return handle;
}
void CPlantMgr::RemoveDynamicCullSphere(u32 handle)
{
if(Verifyf( handle < m_DynCullSpheres.size(), "WARNING: Invalid index/handle passed into RemoveDynamicCullSphere()!\t\tMax Index = %d\thandle = %d %s",
m_DynCullSpheres.size(), handle, (handle == PLANTSMGR_INVALID_DYNAMIC_CULL_SPHERE_INDEX ? "(PLANTSMGR_INVALID_DYNAMIC_CULL_SPHERE_INDEX)" : "") ))
{
m_DynCullSpheres[handle].SetV4(Vec4V(V_ZERO)); //Same as default spdSphere constructor
}
}
void CPlantMgr::ResetDynamicCullSpheres()
{
DynCullSphereSourceArray::iterator iter;
DynCullSphereSourceArray::iterator end = m_DynCullSpheres.end();
for(iter = m_DynCullSpheres.begin(); iter != end; ++iter)
{
iter->SetV4(Vec4V(V_ZERO));
}
}
void CPlantMgr::CopyCullSpheresToUpdateBuffer()
{
const u32 id = gPlantMgr.GetUpdateRTBufferID();
CPlantMgr::DynCullSphereArray &dest = m_DynCullSpheresBuffer[id];
dest.Reset();
//Loop through source array and add only valid spheres
DynCullSphereSourceArray::const_iterator iter;
DynCullSphereSourceArray::const_iterator end = m_DynCullSpheres.end();
for(iter = m_DynCullSpheres.begin(); iter != end; ++iter)
{
if(iter->GetRadiusf() > 0.0f && gbPlantMgrDynCullSpheresEnable)
dest.Push(*iter);
}
}
#endif //CPLANT_DYNAMIC_CULL_SPHERES...
//
//
//
//
CPlantColBoundEntry* CPlantMgr::MoveColEntToList(u16* ppCurrentList, u16* ppNewList, CPlantColBoundEntry *pEntry)
{
Assertf(*ppCurrentList, "CPlantMgr::MoveColEntToList(): m_CurrentList==NULL!");
// First - Cut out of old list
if(pEntry->m_PrevEntry==0)
{ // if at head of old list
*ppCurrentList = pEntry->m_NextEntry;
if(*ppCurrentList)
m_ColEntCache[*ppCurrentList].m_PrevEntry = 0;
}
else if(pEntry->m_NextEntry==0)
{ // if at tail of old list
m_ColEntCache[pEntry->m_PrevEntry].m_NextEntry = 0;
}
else
{ // else if in middle of old list
m_ColEntCache[pEntry->m_NextEntry].m_PrevEntry = pEntry->m_PrevEntry;
m_ColEntCache[pEntry->m_PrevEntry].m_NextEntry = pEntry->m_NextEntry;
}
// Second - Insert at start of new list
pEntry->m_NextEntry = *ppNewList;
pEntry->m_PrevEntry = 0;
*ppNewList = m_ColEntCache.GetIdx(pEntry); // get hashed index of pEntry
if(pEntry->m_NextEntry)
m_ColEntCache[pEntry->m_NextEntry].m_PrevEntry = *ppNewList;
return(pEntry);
}// end of CPlantMgr::MoveColEntToList()...
////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// DEBUG STUFF:
// This debug render function render to gbuffers after main grass render
//
////////////////////////////////////////////////////////////////////////////////////////////////////////
#if __BANK
bool CPlantMgr::RenderDebugStuff()
{
DbgPrintCPlantMgrInfo(nLocTriDrawn, nLocTriTexSkipped, nLocTriPlantsLOD0Drawn, nLocTriPlantsLOD1Drawn, nLocTriPlantsLOD2Drawn);
#if __BANK
// reset statistics
nLocTriDrawn =
nLocTriTexSkipped =
nLocTriPlantsLOD0Drawn =
nLocTriPlantsLOD1Drawn =
nLocTriPlantsLOD2Drawn = 0; // reset statistics
#endif
#if LOCTRISTAB_STR
if(m_LocTrisTab[0] == NULL)
{
return(FALSE);
}
#endif
#if PLANTSMGR_DATA_EDITOR
gPlantMgrEditor.Update();
#endif
#if __BANK
static dev_float dbgHdrVal = 1.0f;
CShaderLib::SetGlobalEmissiveScale(dbgHdrVal);
// debug render of all plant polygons:
if(gbShowCPlantMgrPolys || gbShowCPlantMgrPolysFar)
{
atArray<Vector3> dbgTriangleVertices;
atArray<Color32> dbgTriangleColors;
dbgTriangleVertices.Reset();
dbgTriangleVertices.Reserve(64);
dbgTriangleColors.Reset();
dbgTriangleColors.Reserve(64);
Color32 testColor0(255, 255, 255, 255);
Color32 testColor1(255, 0, 0, 255);
Color32 testColor2(0, 0, 255, 255);
//
// render all close lists:
//
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
Vector3 pVert0 = pLocTri->GetV1();
Vector3 pVert1 = pLocTri->GetV2();
Vector3 pVert2 = pLocTri->GetV3();
bool bAddPoly = true;
if(gbShowCPlantMgrPolysFar && !gbShowCPlantMgrPolys)
{
bAddPoly = pLocTri->m_bDrawFarTri;
}
#if CPLANT_USE_OCCLUSION
if(gbPlantsUseOcclusion)
{
bAddPoly = (!pLocTri->m_bOccluded); // skip occluded polys
}
#endif
bool bAddPoly2 = false;
if(gbShowCPlantMgrPolys_Plants)
{
if(pLocTri->m_bCreatesPlants)
{
bAddPoly2 = true;
}
}
if(gbShowCPlantMgrPolys_ProcObj)
{
if(pLocTri->m_bCreatesObjects)
{
bAddPoly2 = true;
}
}
if(bAddPoly && bAddPoly2)
{
dbgTriangleVertices.PushAndGrow(pVert0);
dbgTriangleColors.PushAndGrow(testColor0);
dbgTriangleVertices.PushAndGrow(pVert1);
dbgTriangleColors.PushAndGrow(testColor1);
dbgTriangleVertices.PushAndGrow(pVert2);
dbgTriangleColors.PushAndGrow(testColor2);
}
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
// setup render state:
{
grcWorldIdentity();
grcBindTexture(NULL);
static grcRasterizerStateHandle hRasterizerState = grcStateBlock::RS_Invalid;
if(hRasterizerState == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
rasDesc.FillMode = grcRSV::FILL_SOLID;
hRasterizerState = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hRasterizerState != grcStateBlock::RS_Invalid);
}
grcStateBlock::SetRasterizerState(hRasterizerState);
static grcBlendStateHandle hBlendState = grcStateBlock::BS_Invalid;
if(hBlendState == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
#if __XENON
// REL-1-11: im rendering supports only 1 MRT
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[1].BlendEnable = FALSE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[2].BlendEnable = FALSE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[3].BlendEnable = FALSE;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#endif
hBlendState = grcStateBlock::CreateBlendState(bsDesc);
Assert(hBlendState != grcStateBlock::BS_Invalid);
}
grcStateBlock::SetBlendState(hBlendState);
static grcDepthStencilStateHandle hDepthStencilState = grcStateBlock::DSS_Invalid;
if(hDepthStencilState == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
hDepthStencilState = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hDepthStencilState != grcStateBlock::DSS_Invalid);
}
grcStateBlock::SetDepthStencilState(hDepthStencilState);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
DbgDrawTriangleArrays(dbgTriangleVertices, dbgTriangleColors);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
}
}// if(gbShowCPlantMgrPolys)...
// debug render of all plant polygons:
if(gbShowCPlantMgrPolyNormals)
{
atArray<Vector3> dbgLineVertices;
atArray<Color32> dbgLineColors;
Color32 testColor0(255, 255, 255, 255);
// Color32 testColor1(255, 0, 0, 255);
// Color32 testColor2(0, 0, 255, 255);
dbgLineVertices.Reset();
dbgLineVertices.Reserve(64);
dbgLineColors.Reset();
dbgLineColors.Reserve(64);
//
// render all close lists:
//
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
Vector3 pVert0 = pLocTri->GetV1();
//Vector3 pVert1 = pLocTri->GetV2();
//Vector3 pVert2 = pLocTri->GetV3();
Vector3 polyNormal(pLocTri->m_normal[0]/127.0f, pLocTri->m_normal[1]/127.0f, pLocTri->m_normal[2]/127.0f);
dbgLineVertices.PushAndGrow(pVert0);
dbgLineColors.PushAndGrow(testColor0);
dbgLineVertices.PushAndGrow(pVert0+polyNormal*10.0f);
dbgLineColors.PushAndGrow(testColor0);
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
// setup render state:
{
grcWorldIdentity();
grcBindTexture(NULL);
static grcRasterizerStateHandle hRasterizerState = grcStateBlock::RS_Invalid;
if(hRasterizerState == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
rasDesc.FillMode = grcRSV::FILL_SOLID;
hRasterizerState = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hRasterizerState != grcStateBlock::RS_Invalid);
}
grcStateBlock::SetRasterizerState(hRasterizerState);
static grcBlendStateHandle hBlendState = grcStateBlock::BS_Invalid;
if(hBlendState == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
#if __XENON
// REL-1-11: im rendering supports only 1 MRT
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[1].BlendEnable = FALSE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[2].BlendEnable = FALSE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[3].BlendEnable = FALSE;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#endif
hBlendState = grcStateBlock::CreateBlendState(bsDesc);
Assert(hBlendState != grcStateBlock::BS_Invalid);
}
grcStateBlock::SetBlendState(hBlendState);
static grcDepthStencilStateHandle hDepthStencilState = grcStateBlock::DSS_Invalid;
if(hDepthStencilState == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
hDepthStencilState = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hDepthStencilState != grcStateBlock::DSS_Invalid);
}
grcStateBlock::SetDepthStencilState(hDepthStencilState);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
DbgDrawLineArrays(dbgLineVertices, dbgLineColors);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
}
}// if(gbShowCPlantMgrPolyNormals)...
// debug render of all plant polygons:
if(gbShowCPlantMgrGroundColor)
{
atArray<Vector3> dbgTriangleVertices;
atArray<Color32> dbgTriangleColors;
dbgTriangleVertices.Reset();
dbgTriangleVertices.Reserve(128);
dbgTriangleColors.Reset();
dbgTriangleColors.Reserve(128);
//
// render all close lists:
//
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
Vector3 pVert0 = pLocTri->GetV1();
Vector3 pVert1 = pLocTri->GetV2();
Vector3 pVert2 = pLocTri->GetV3();
Color32 colorV1, colorV2, colorV3;
if(gbForceGroundColor)
{ // assign debug colors:
colorV1 = gbGroundColorV1;
colorV2 = gbGroundColorV2;
colorV3 = gbGroundColorV3;
// ... and debug densities & scales:
colorV1.SetAlpha(CPlantLocTri::pv8PackDensityScaleZScaleXYZ((u8)gbGroundDensity_V1, (u8)gbGroundScaleZ_V1, (u8)gbGroundScaleXYZ_V1));
colorV2.SetAlpha(CPlantLocTri::pv8PackDensityScaleZScaleXYZ((u8)gbGroundDensity_V2, (u8)gbGroundScaleZ_V2, (u8)gbGroundScaleXYZ_V2));
colorV3.SetAlpha(CPlantLocTri::pv8PackDensityScaleZScaleXYZ((u8)gbGroundDensity_V3, (u8)gbGroundScaleZ_V3, (u8)gbGroundScaleXYZ_V3));
}
else
{
colorV1 = pLocTri->m_GroundColorV1;
colorV2 = pLocTri->m_GroundColorV2;
colorV3 = pLocTri->m_GroundColorV3;
}
switch(gbShowCPlantMgrGroundCSD)
{
case(0): // color
{
colorV1.SetAlpha(255);
colorV2.SetAlpha(255);
colorV3.SetAlpha(255);
}
break;
case(1): // scaleXYZ
{
const float scale1 = CPlantLocTri::pv8UnpackAndMapScaleXYZ(colorV1.GetAlpha());
const float scale2 = CPlantLocTri::pv8UnpackAndMapScaleXYZ(colorV2.GetAlpha());
const float scale3 = CPlantLocTri::pv8UnpackAndMapScaleXYZ(colorV3.GetAlpha());
const u8 cScale1 = u8(rage::Abs(scale1)*255.0f);
const u8 cScale2 = u8(rage::Abs(scale2)*255.0f);
const u8 cScale3 = u8(rage::Abs(scale3)*255.0f);
if(scale1 >= 0.0f)
{ // white: positive mapping <8; 15> -> <0.0666; 1>:
colorV1.SetBytes(cScale1, cScale1, cScale1, 255);
}
else
{ // red: negative mapping <0; 7> -> <-1; -0.0666>:
colorV1.SetBytes(cScale1, 0, 0, 255);
}
if(scale2 >= 0.0f)
{ // white: positive mapping <8; 15> -> <0.0666; 1>:
colorV2.SetBytes(cScale2, cScale2, cScale2, 255);
}
else
{ // red: negative mapping <0; 7> -> <-1; -0.0666>:
colorV2.SetBytes(cScale2, 0, 0, 255);
}
if(scale3 >= 0.0f)
{ // white: positive mapping <8; 15> -> <0.0666; 1>:
colorV3.SetBytes(cScale3, cScale3, cScale3, 255);
}
else
{ // red: negative mapping <0; 7> -> <-1; -0.0666>:
colorV3.SetBytes(cScale3, 0, 0, 255);
}
}
break;
case(2): // scaleZ
{
const float scale1 = CPlantLocTri::pv8UnpackScaleZ(colorV1.GetAlpha());
const float scale2 = CPlantLocTri::pv8UnpackScaleZ(colorV2.GetAlpha());
const float scale3 = CPlantLocTri::pv8UnpackScaleZ(colorV3.GetAlpha());
const u8 cScale1 = u8((rage::Abs(scale1)/3.0f)*255.0f);
const u8 cScale2 = u8((rage::Abs(scale2)/3.0f)*255.0f);
const u8 cScale3 = u8((rage::Abs(scale3)/3.0f)*255.0f);
colorV1.SetBytes(cScale1, cScale1, cScale1, 255);
colorV2.SetBytes(cScale2, cScale2, cScale2, 255);
colorV3.SetBytes(cScale3, cScale3, cScale3, 255);
}
break;
case(3): // density
{
const float density1 = CPlantLocTri::pv8UnpackDensity(colorV1.GetAlpha());
const float density2 = CPlantLocTri::pv8UnpackDensity(colorV2.GetAlpha());
const float density3 = CPlantLocTri::pv8UnpackDensity(colorV3.GetAlpha());
const u8 bDensity1 = u8((rage::Abs(density1)/3.0f)*255.0f);
const u8 bDensity2 = u8((rage::Abs(density2)/3.0f)*255.0f);
const u8 bDensity3 = u8((rage::Abs(density3)/3.0f)*255.0f);
colorV1.SetBytes(bDensity1, bDensity1, bDensity1, 255);
colorV2.SetBytes(bDensity2, bDensity2, bDensity2, 255);
colorV3.SetBytes(bDensity3, bDensity3, bDensity3, 255);
}
break;
default:
Assert(0); // invalid state
break;
}
dbgTriangleVertices.PushAndGrow(pVert0);
dbgTriangleColors.PushAndGrow(colorV1);
dbgTriangleVertices.PushAndGrow(pVert1);
dbgTriangleColors.PushAndGrow(colorV2);
dbgTriangleVertices.PushAndGrow(pVert2);
dbgTriangleColors.PushAndGrow(colorV3);
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
// setup render state:
{
grcWorldIdentity();
grcBindTexture(NULL);
static grcRasterizerStateHandle hRasterizerState = grcStateBlock::RS_Invalid;
if(hRasterizerState == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
rasDesc.FillMode = grcRSV::FILL_SOLID;
hRasterizerState = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hRasterizerState != grcStateBlock::RS_Invalid);
}
grcStateBlock::SetRasterizerState(hRasterizerState);
static grcBlendStateHandle hBlendState = grcStateBlock::BS_Invalid;
if(hBlendState == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
#if __XENON
// REL-1-11: im rendering supports only 1 MRT
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[1].BlendEnable = FALSE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[2].BlendEnable = FALSE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[3].BlendEnable = FALSE;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#endif
hBlendState = grcStateBlock::CreateBlendState(bsDesc);
Assert(hBlendState != grcStateBlock::BS_Invalid);
}
grcStateBlock::SetBlendState(hBlendState);
static grcDepthStencilStateHandle hDepthStencilState = grcStateBlock::DSS_Invalid;
if(hDepthStencilState == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
hDepthStencilState = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hDepthStencilState != grcStateBlock::DSS_Invalid);
}
grcStateBlock::SetDepthStencilState(hDepthStencilState);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
DbgDrawTriangleArrays(dbgTriangleVertices, dbgTriangleColors);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
}
}// if(gbShowCPlantMgrPolys)...
// print extracted & mapped CPV ScaleXYZ / ScaleZ / Density:
if(gbShowCPlantMgrGroundColor && (gbShowCPlantMgrGroundCSD > 0))
{
grcWorldIdentity();
grcBindTexture(NULL);
static grcRasterizerStateHandle hRasterizerState = grcStateBlock::RS_Invalid;
if(hRasterizerState == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
rasDesc.FillMode = grcRSV::FILL_SOLID;
hRasterizerState = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hRasterizerState != grcStateBlock::RS_Invalid);
}
grcStateBlock::SetRasterizerState(hRasterizerState);
static grcBlendStateHandle hBlendState = grcStateBlock::BS_Invalid;
if(hBlendState == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
#if __XENON
// REL-1-11: im rendering supports only 1 MRT
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[1].BlendEnable = FALSE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[2].BlendEnable = FALSE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[3].BlendEnable = FALSE;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#endif
hBlendState = grcStateBlock::CreateBlendState(bsDesc);
Assert(hBlendState != grcStateBlock::BS_Invalid);
}
grcStateBlock::SetBlendState(hBlendState);
static grcDepthStencilStateHandle hDepthStencilState = grcStateBlock::DSS_Invalid;
if(hDepthStencilState == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
hDepthStencilState = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hDepthStencilState != grcStateBlock::DSS_Invalid);
}
grcStateBlock::SetDepthStencilState(hDepthStencilState);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
#if __XENON
// 360: reset screen vp, so grcDrawLabelf() renders correctly:
const u32 oldDeviceWidth = GRCDEVICE.GetWidth();
const u32 oldDeviceHeight= GRCDEVICE.GetHeight();
GRCDEVICE.SetSize(1280, 720);
grcViewport *pScreenVP = (grcViewport*)grcViewport::GetDefaultScreen();
grcViewport oldScreenVP = *pScreenVP;
pScreenVP->ResetWindow();
pScreenVP->Screen();
#endif // __XENON...
grcColor( Color32(255,255,255,255) );
//
// render all close lists:
//
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
Color32 colorV1 = pLocTri->m_GroundColorV1;
Color32 colorV2 = pLocTri->m_GroundColorV2;
Color32 colorV3 = pLocTri->m_GroundColorV3;
float printVal[3]={0, 0, 0};
switch(gbShowCPlantMgrGroundCSD)
{
case(0): // color
break;
case(1): // scaleXYZ
{
printVal[0] = CPlantLocTri::pv8UnpackAndMapScaleXYZ(colorV1.GetAlpha());
printVal[1] = CPlantLocTri::pv8UnpackAndMapScaleXYZ(colorV2.GetAlpha());
printVal[2] = CPlantLocTri::pv8UnpackAndMapScaleXYZ(colorV3.GetAlpha());
}
break;
case(2): // scaleZ
{
printVal[0] = CPlantLocTri::pv8UnpackAndMapScaleZ(colorV1.GetAlpha());
printVal[1] = CPlantLocTri::pv8UnpackAndMapScaleZ(colorV2.GetAlpha());
printVal[2] = CPlantLocTri::pv8UnpackAndMapScaleZ(colorV3.GetAlpha());
}
break;
case(3): // density
{
printVal[0] = CPlantLocTri::pv8UnpackAndMapDensity(colorV1.GetAlpha());
printVal[1] = CPlantLocTri::pv8UnpackAndMapDensity(colorV2.GetAlpha());
printVal[2] = CPlantLocTri::pv8UnpackAndMapDensity(colorV3.GetAlpha());
}
break;
default:
Assert(0); // invalid state
break;
}
Vector3 v0 = pLocTri->GetV1();
Vector3 v1 = pLocTri->GetV2();
Vector3 v2 = pLocTri->GetV3();
// move towards middle of tri:
static BankFloat bShiftVal = 0.2f;
const Vector3 center = pLocTri->GetCenter();
v0 += (center - v0) * bShiftVal;
v1 += (center - v1) * bShiftVal;
v2 += (center - v2) * bShiftVal;
v0.z += 0.5f;
v1.z += 0.5f;
v2.z += 0.5f;
grcDrawLabelf(v0, "%.3f", printVal[0]);
grcDrawLabelf(v1, "%.3f", printVal[1]);
grcDrawLabelf(v2, "%.3f", printVal[2]);
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
#if __XENON
GRCDEVICE.SetSize(oldDeviceWidth, oldDeviceHeight);
*pScreenVP = oldScreenVP;
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
}// ifif(gbShowCPlantMgrGroundColor && (gbShowCPlantMgrGroundCSD > 0))...
#endif //__BANK...
// debug render of all proc mat type:
if(gbShowCPlantMgrProcMatType)
{
grcWorldIdentity();
grcBindTexture(NULL);
static grcRasterizerStateHandle hRasterizerState = grcStateBlock::RS_Invalid;
if(hRasterizerState == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
rasDesc.FillMode = grcRSV::FILL_SOLID;
hRasterizerState = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hRasterizerState != grcStateBlock::RS_Invalid);
}
grcStateBlock::SetRasterizerState(hRasterizerState);
static grcBlendStateHandle hBlendState = grcStateBlock::BS_Invalid;
if(hBlendState == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
#if __XENON
// REL-1-11: im rendering supports only 1 MRT
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[1].BlendEnable = FALSE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[2].BlendEnable = FALSE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[3].BlendEnable = FALSE;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#endif
hBlendState = grcStateBlock::CreateBlendState(bsDesc);
Assert(hBlendState != grcStateBlock::BS_Invalid);
}
grcStateBlock::SetBlendState(hBlendState);
static grcDepthStencilStateHandle hDepthStencilState = grcStateBlock::DSS_Invalid;
if(hDepthStencilState == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
hDepthStencilState = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hDepthStencilState != grcStateBlock::DSS_Invalid);
}
grcStateBlock::SetDepthStencilState(hDepthStencilState);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
#if __XENON
// 360: reset screen vp, so grcDrawLabelf() renders correctly:
const u32 oldDeviceWidth = GRCDEVICE.GetWidth();
const u32 oldDeviceHeight= GRCDEVICE.GetHeight();
GRCDEVICE.SetSize(1280, 720);
grcViewport *pScreenVP = (grcViewport*)grcViewport::GetDefaultScreen();
grcViewport oldScreenVP = *pScreenVP;
pScreenVP->ResetWindow();
pScreenVP->Screen();
#endif // __XENON...
grcColor( Color32(255,255,255,255) );
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
Vector3 pTriCenter = pLocTri->GetCenter();
pTriCenter.z += 0.5f;
s32 procTagId = PGTAMATERIALMGR->UnpackProcId(pLocTri->m_nSurfaceType);
s32 procInfoIdx = g_procInfo.m_procTagTable[procTagId].procObjIndex;
s32 plantInfoIdx= g_procInfo.m_procTagTable[procTagId].plantIndex;
const char* procName = procInfoIdx>-1? g_procInfo.m_procObjInfos[procInfoIdx].m_Tag.GetCStr() : "none";
const char* plantName = plantInfoIdx>-1? g_procInfo.m_plantInfos[plantInfoIdx].m_Tag.GetCStr() : "none";
grcDrawLabelf(pTriCenter, "%s [%s / %s]", (const char*)g_procInfo.m_procTagTable[procTagId].GetName(), procName, plantName);
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
#if __XENON
GRCDEVICE.SetSize(oldDeviceWidth, oldDeviceHeight);
*pScreenVP = oldScreenVP;
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
}// if(gbShowCPlantMgrProcMatType)...
// debug render of all proc mat type:
if(gbShowCPlantMgrAmbScale)
{
grcWorldIdentity();
grcBindTexture(NULL);
static grcRasterizerStateHandle hRasterizerState = grcStateBlock::RS_Invalid;
if(hRasterizerState == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = grcRSV::CULL_NONE;
rasDesc.FillMode = grcRSV::FILL_SOLID;
hRasterizerState = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hRasterizerState != grcStateBlock::RS_Invalid);
}
grcStateBlock::SetRasterizerState(hRasterizerState);
static grcBlendStateHandle hBlendState = grcStateBlock::BS_Invalid;
if(hBlendState == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
#if __XENON
// REL-1-11: im rendering supports only 1 MRT
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[1].BlendEnable = FALSE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[2].BlendEnable = FALSE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
bsDesc.BlendRTDesc[3].BlendEnable = FALSE;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
#endif
hBlendState = grcStateBlock::CreateBlendState(bsDesc);
Assert(hBlendState != grcStateBlock::BS_Invalid);
}
grcStateBlock::SetBlendState(hBlendState);
static grcDepthStencilStateHandle hDepthStencilState = grcStateBlock::DSS_Invalid;
if(hDepthStencilState == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = TRUE;
dsDesc.DepthWriteMask = TRUE;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESS);
hDepthStencilState = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hDepthStencilState != grcStateBlock::DSS_Invalid);
}
grcStateBlock::SetDepthStencilState(hDepthStencilState);
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
#if __XENON
// 360: reset screen vp, so grcDrawLabelf() renders correctly:
const u32 oldDeviceWidth = GRCDEVICE.GetWidth();
const u32 oldDeviceHeight= GRCDEVICE.GetHeight();
GRCDEVICE.SetSize(1280, 720);
grcViewport *pScreenVP = (grcViewport*)grcViewport::GetDefaultScreen();
grcViewport oldScreenVP = *pScreenVP;
pScreenVP->ResetWindow();
pScreenVP->Screen();
#endif // __XENON...
grcColor( Color32(255,255,255,255) );
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
Vector3 pTriCenter = pLocTri->GetCenter();
pTriCenter.z += 0.5f;
grcDrawLabelf(pTriCenter, "%d:%d", pLocTri->m_nAmbientScale[0],pLocTri->m_nAmbientScale[1]);
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS) // REL-1-11: im rendering supports only 1 MRT:
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
#if __XENON
GRCDEVICE.SetSize(oldDeviceWidth, oldDeviceHeight);
*pScreenVP = oldScreenVP;
#endif
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
}// if(gbShowCPlantMgrAmbScale)...
return(TRUE);
}// end of RenderDebugStuff()...
//
//
//
// This debug render function render during debug render phase near end of frame
//
bool CPlantMgr::RenderDebugStuffForward()
{
#if CPLANT_DYNAMIC_CULL_SPHERES
if(gbPlantMgrDCSDebugDraw)
{
const u32 id = gPlantMgr.GetRenderRTBufferID();
const CPlantMgr::DynCullSphereArray &cullSpheres = gPlantMgr.GetCullSphereArray(id);
CPlantMgr::DynCullSphereArray::const_iterator iter;
CPlantMgr::DynCullSphereArray::const_iterator end = cullSpheres.end();
for(iter = cullSpheres.begin(); iter != end; ++iter)
grcDebugDraw::Sphere(iter->GetCenter(), iter->GetRadiusf(), gPlantMgrDCSDebugDrawColor, gbPlantMgrDCSDebugDrawSolid);
}// if(gbPlantMgrDCSDebugDraw)...
#endif //CPLANT_DYNAMIC_CULL_SPHERES...
return(TRUE);
}
bool CPlantMgr::RenderDebugForward()
{
return gPlantMgr.RenderDebugStuffForward();
}
#endif //__BANK...
//
//
//
//
bool CPlantMgr::DbgPrintCPlantMgrInfo(u32 BANK_ONLY(nNumLocTrisDrawn), u32 BANK_ONLY(nNumLocTrisTexSkipped), u32 BANK_ONLY(nNumPlantsLOD0Drawn), u32 BANK_ONLY(nNumPlantsLOD1Drawn), u32 BANK_ONLY(nNumPlantsLOD2Drawn))
{
#if __BANK
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// print some debug/stats info:
if(gbDisplayCPlantMgrInfo)
{
u32 nNumCachedBounds=0, nNumCachedCollBounds=0, nNumCachedMatBounds=0;
DbgCountCachedBounds(&nNumCachedBounds, &nNumCachedCollBounds, &nNumCachedMatBounds);
u32 nNumPlantLocTris=0, nNumPlants=0;
DbgCountLocTrisAndPlants(&nNumPlantLocTris, &nNumPlants, true, false, false);
u32 nNumObjLocTris=0;
DbgCountLocTrisAndPlants(&nNumObjLocTris, NULL, false, true, false);
u32 nNumFreeLocTris=0;
DbgCountLocTrisAndPlants(&nNumFreeLocTris, NULL, false, false, true);
char buf[256];
const s32 StartX = 8;
const s32 StartY = 26;
const bool bPrevDisplayDebugText = grcDebugDraw::GetDisplayDebugText();
grcDebugDraw::SetDisplayDebugText(TRUE);
::sprintf(buf, "CPlantMgr::Info:");
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+0);
::sprintf(buf, "----------------");
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+1);
::sprintf(buf, "Bounds: All=%d (collision: %d, material: %d)", nNumCachedBounds, nNumCachedCollBounds, nNumCachedMatBounds);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+2);
::sprintf(buf, "LocTris[0]: All=%d (drawn=%d, txskipped=%d)", nNumPlantLocTris, nNumLocTrisDrawn, nNumLocTrisTexSkipped);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+3);
::sprintf(buf, "LocTris[0]: Plants=%d (LOD0: dr=%d (%.2f%%), LOD1: dr=%d (%.2f%%), LOD2: dr=%d (%.2f%%))", nNumPlants, nNumPlantsLOD0Drawn, 100.0f*float(nNumPlantsLOD0Drawn)/float(nNumPlants+1), nNumPlantsLOD1Drawn, 100.0f*float(nNumPlantsLOD1Drawn)/float(nNumPlants+1), nNumPlantsLOD2Drawn, 100.0f*float(nNumPlantsLOD2Drawn)/float(nNumPlants+1));
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+4);
::sprintf(buf, "LocTris[1]: All=%d", nNumObjLocTris);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+5);
::sprintf(buf, "LocTris free: All=%d", nNumFreeLocTris);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+6);
#if LOCTRISTAB_STR
::sprintf(buf, "LocTris str allocated: %d/12", m_LocTrisTab[0]? 12 : 0);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+7);
#endif
#if CPLANT_USE_OCCLUSION
if(gbPlantsUseOcclusion)
{
::sprintf(buf, "LocTris occluded: %d", nLocTrisOccluded);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+8);
}
#endif
#if FURGRASS_TEST_V4
::sprintf(buf, "Fur grass polys: %d", gnFurGrassNumPolys);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+10);
#endif
::sprintf(buf, "TriLocFarDist: %.1fm", rage::Sqrtf(CPLANT_TRILOC_FAR_DIST_SQR));
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+11);
::sprintf(buf, "AlphaClose/FarDist: %.1fm/%.1fm", CPLANT_LOD0_ALPHA_CLOSE_DIST, CPLANT_LOD0_ALPHA_FAR_DIST);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+12);
::sprintf(buf, "Darkness amount: %.3f", gbPlantsDarknessAmountPrint);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+13);
#if PSN_PLANTSMGR_SPU_RENDER
::sprintf(buf, "SPU jobs: %d (input buffer: used: %dKB/allocated: %dKB).", CGrassRenderer::SpuGetAmountOfJobs(), CGrassRenderer::SpuGetAmountOfJobs()*PLANTSMGR_TRIPLANT_BLOCK_SIZE/1024, CGrassRenderer::SpuGetAmountOfAllocatedJobs()*PLANTSMGR_TRIPLANT_BLOCK_SIZE/1024);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+14);
const u32 bigHeapSize = CGrassRenderer::SpuGetBigHeapAllocatedSize()*PLANTSMGR_LOCAL_HEAP_SIZE;
const u32 bigHeapConsumed = CGrassRenderer::SpuGetBigHeapConsumed()*PLANTSMGR_LOCAL_HEAP_SIZE;
const u32 bigHeapBiggestConsumed= CGrassRenderer::SpuGetBigHeapBiggestConsumed()*PLANTSMGR_LOCAL_HEAP_SIZE;
const u32 bigHeapOverfilled = CGrassRenderer::SpuGetBigHeapOverfilled()*PLANTSMGR_LOCAL_HEAP_SIZE;
::sprintf(buf, "SPU BigBuffer: used: %dKB/allocated: %dKB (%.2f%%), biggest used: %dKB, overfilled: %dKB", bigHeapConsumed/1024, bigHeapSize/1024, float(bigHeapConsumed)/float(bigHeapSize)*100.0f, bigHeapBiggestConsumed/1024, bigHeapOverfilled/1024);
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+13);
#endif
#if PLANTS_TXD_STR
::sprintf(buf, "Current Zone: %s, Txd: %d", CPopCycle::sm_nCurrentZoneName.TryGetCStr(), CPopCycle::GetCurrentZonePlantsMgrTxdIdx());
grcDebugDraw::PrintToScreenCoors(buf, StartX+0, StartY+16);
#endif
grcDebugDraw::SetDisplayDebugText(bPrevDisplayDebugText);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif //__BANK...
return(TRUE);
}
//
//
//
//
bool CPlantMgr::DbgCountCachedBounds(u32* BANK_ONLY(pCountAll), u32* BANK_ONLY(pCountColl), u32* BANK_ONLY(pCountMat))
{
#if __BANK
u32 nCountEnt = 0;
u32 nCountColl= 0; // (old) collision type bounds
u32 nCountMat = 0; // (new) material type bounds
u16 entry = m_ColEntCache.m_CloseListHead;
while(entry)
{
CPlantColBoundEntry *pEntry = &m_ColEntCache[entry];
if(pEntry->GetBound())
{
nCountEnt++;
if(pEntry->IsMatBound())
{
nCountMat++;
}
else
{
nCountColl++;
}
}
entry = pEntry->m_NextEntry;
}
if(pCountAll)
{
*pCountAll = nCountEnt;
}
if(pCountColl)
{
*pCountColl = nCountColl;
}
if(pCountMat)
{
*pCountMat = nCountMat;
}
#endif//__BANK
return(TRUE);
}// end of CPlantMgr::DbgCountCachedEntities()...
//
//
//
//
bool CPlantMgr::DbgCountLocTrisAndPlants(u32* BANK_ONLY(pCountLocTris) , u32* BANK_ONLY(pCountPlants), bool BANK_ONLY(bCountCreatesPlants), bool BANK_ONLY(bCountCreatesObjects), bool BANK_ONLY(bCountFree))
{
#if __BANK
Assert(bCountCreatesPlants || bCountCreatesObjects || bCountFree); // at least one count option must be selected
u32 nCountTris = 0,
nCountPlants = 0;
const u32 bufferID = GetUpdateRTBufferID()&0x01; // read previous buffer for stats; __BANK: make its range to be safe (as UpdateThread sometimes collides here with SwapRTBuffer())
#if LOCTRISTAB_STR
if(m_LocTrisRenderTab[bufferID][0])
#endif
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triRenderTab = *m_LocTrisRenderTab[bufferID][listID];
u16 locTri = triRenderTab.m_CloseListHead;
while(locTri)
{
CPlantLocTri *pLocTri = &triRenderTab[locTri];
#if CPLANT_USE_OCCLUSION
if( !(gbPlantsUseOcclusion && pLocTri->m_bOccluded) )
#endif
{
if( (pLocTri->m_bCreatesPlants ==bCountCreatesPlants) ||
(pLocTri->m_bCreatesObjects ==bCountCreatesObjects) )
{
nCountTris++;
nCountPlants += pLocTri->m_nMaxNumPlants;
}
}
locTri = pLocTri->m_NextTri;
}//while(pLocTri)...
}
if(bCountFree)
{
nCountTris = 0;
#if LOCTRISTAB_STR
if(m_LocTrisRenderTab[bufferID][0])
#endif
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triRenderTab = *m_LocTrisRenderTab[bufferID][listID];
u16 locTri = triRenderTab.m_UnusedListHead;
while(locTri)
{
CPlantLocTri *pLocTri = &triRenderTab[locTri];
nCountTris++;
locTri = pLocTri->m_NextTri;
}//while(pLocTri)...
}
}// if(bCountFree)...
if(pCountLocTris)
{
*pCountLocTris = nCountTris;
}
if(pCountPlants)
{
*pCountPlants = nCountPlants;
}
#endif// __BANK...
return(TRUE);
}// end of CPlantMgr::DbgCountLocTrix()...
//
//
//
//
void CPlantMgr::DbgDrawTriangleArrays(atArray<Vector3>& BANK_ONLY(triangleVertices) , atArray<Color32>& BANK_ONLY(triangleColors) )
{
#if __BANK
//const u32 nNumVerts= triangleVertices.size();
const u32 nNumTris = triangleVertices.GetCount() / 3;
Assert(triangleVertices.GetCount() == triangleColors.GetCount());
#define Z_OFFSET (0.25f)
s32 numTrisLeftToDraw = nNumTris;
s32 actualVertIndex = 0;
while(numTrisLeftToDraw > 0)
{
s32 trisToDraw = numTrisLeftToDraw;
if((numTrisLeftToDraw*3) > grcBeginMax)
{
trisToDraw = grcBeginMax/3;
numTrisLeftToDraw -= grcBeginMax/3;
}
else
{
trisToDraw = numTrisLeftToDraw;
numTrisLeftToDraw -= trisToDraw;
}
grcNormal3f(0.0f, 0.0f, 1.0f);
grcTexCoord2f(0.0f, 0.0f);
grcBegin(drawTris, trisToDraw*3);
for(s32 v=0; v<trisToDraw; v++)
{
Color32 *pCol0 = &triangleColors [actualVertIndex];
Vector3 *pVert0 = &triangleVertices [actualVertIndex];
actualVertIndex++;
Color32 *pCol1 = &triangleColors [actualVertIndex];
Vector3 *pVert1 = &triangleVertices [actualVertIndex];
actualVertIndex++;
Color32 *pCol2 = &triangleColors [actualVertIndex];
Vector3 *pVert2 = &triangleVertices [actualVertIndex];
actualVertIndex++;
//grcTexCoord2f(0.0f, 0.0f);
grcColor(*pCol0);
grcVertex3f(pVert0->x, pVert0->y, pVert0->z + Z_OFFSET);
//grcTexCoord2f(1.0f, 0.0f);
grcColor(*pCol1);
grcVertex3f(pVert1->x, pVert1->y, pVert1->z + Z_OFFSET);
//grcTexCoord2f(0.0f, 1.0f);
grcColor(*pCol2);
grcVertex3f(pVert2->x, pVert2->y, pVert2->z + Z_OFFSET);
}
grcEnd();
}
#endif //__BANK...
#if PLANTSMGR_DATA_EDITOR
gPlantMgrEditor.Render();
#endif
} // end of DbgDrawTriangleArrays()...
//
//
//
//
void CPlantMgr::DbgDrawLineArrays(atArray<Vector3>& BANK_ONLY(lineVertices) , atArray<Color32>& BANK_ONLY(lineColors) )
{
#if __BANK
const u32 nNumLines = lineVertices.GetCount() / 2;
Assert(lineVertices.GetCount() == lineColors.GetCount());
#define Z_OFFSET (0.25f)
s32 numLinesLeftToDraw = nNumLines;
s32 actualVertIndex = 0;
while(numLinesLeftToDraw > 0)
{
s32 linesToDraw = numLinesLeftToDraw;
if((numLinesLeftToDraw*2) > grcBeginMax)
{
linesToDraw = grcBeginMax/2;
numLinesLeftToDraw -= grcBeginMax/2;
}
else
{
linesToDraw = numLinesLeftToDraw;
numLinesLeftToDraw -= linesToDraw;
}
grcNormal3f(0.0f, 0.0f, 1.0f);
grcTexCoord2f(0.0f, 0.0f);
grcBegin(drawLines, linesToDraw*2);
for(s32 v=0; v<linesToDraw; v++)
{
Color32 *pCol0 = &lineColors [actualVertIndex];
Vector3 *pVert0 = &lineVertices [actualVertIndex];
actualVertIndex++;
Color32 *pCol1 = &lineColors [actualVertIndex];
Vector3 *pVert1 = &lineVertices [actualVertIndex];
actualVertIndex++;
//grcTexCoord2f(0.0f, 0.0f);
grcColor(*pCol0);
grcVertex3f(pVert0->x, pVert0->y, pVert0->z + Z_OFFSET);
//grcTexCoord2f(1.0f, 0.0f);
grcColor(*pCol1);
grcVertex3f(pVert1->x, pVert1->y, pVert1->z + Z_OFFSET);
}
grcEnd();
}
#endif //__BANK...
} // end of DbgDrawTriangleArrays()...
#if SECTOR_TOOLS_EXPORT
void CPlantMgr::DbgGetCPlantMgrInfo( u32* nNumLocTrisDrawn, u32* nNumPlantsDrawn )
{
*nNumLocTrisDrawn = 0;
*nNumPlantsDrawn = 0;
u32 nNumCachedBounds=0;
DbgCountCachedBounds(&nNumCachedBounds, NULL, NULL);
u32 nNumLocTris=0, nNumPlants=0;
DbgCountLocTrisAndPlants(&nNumLocTris, &nNumPlants, true, true, false);
*nNumLocTrisDrawn += nNumLocTris;
*nNumPlantsDrawn += nNumPlants;
}
//
// name: DbgStatsCountCachedBounds
// desc:
//
void CPlantMgr::DbgStatsCountCachedBounds( u32* pCountAll )
{
u32 nCountEnt=0;
u16 entry = m_ColEntCache.m_CloseListHead;
while(entry)
{
CPlantColBoundEntry *pEntry = &m_ColEntCache[entry];
Assert(pEntry->GetBound());
nCountEnt++;
entry = pEntry->m_NextEntry;
}
if(pCountAll)
{
*pCountAll = nCountEnt;
}
}// end of CPlantMgr::DbgCountCachedEntities()...
//
// name: DbgStatsCountLocTrisAndPlants
// desc:
//
void CPlantMgr::DbgStatsCountLocTrisAndPlants( u32* pCountLocTris, u32* pCountPlants )
{
s32 nCountTris = 0;
s32 nCountPlants = 0;
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *m_LocTrisTab[listID];
u16 locTri = triTab.m_CloseListHead;
while(locTri)
{
CPlantLocTri *pLocTri = &triTab[locTri];
nCountTris++;
nCountPlants += pLocTri->m_nMaxNumPlants;
locTri = pLocTri->m_NextTri;
}
}
if(pCountLocTris)
{
*pCountLocTris = nCountTris;
}
if(pCountPlants)
{
*pCountPlants = nCountPlants;
}
}
#endif // SECTOR_TOOLS_EXPORT
//////////////////////////////////////////////////////////////////////////////////////////////
#if FURGRASS_TEST_V4
static grmShader *gFurGrassLODShader=NULL;
static grcEffectTechnique gFurGrassLODComboTechniqueID=grcetNONE;
static grcEffectTechnique gFurGrassDitheredTechniqueID=grcetNONE;
static grcEffectVar varID_FurFresnel=grcevNONE;
static grcEffectVar varID_FurSpecFalloff=grcevNONE;
static grcEffectVar varID_FurSpecInt=grcevNONE;
static grcEffectVar varID_FurBumpiness=grcevNONE;
static grcEffectVar varID_FurLODDiffuseTex=grcevNONE, varID_FurLODComboHeightTex=grcevNONE, varID_FurLODNormalTex=grcevNONE, varID_FurLODStippleTex=grcevNONE;
static grcEffectVar varID_FurLODComboHeightTexMask=grcevNONE;
static grcEffectVar varID_FurDitherAlphaFadeParams=grcevNONE;
static grcEffectVar varID_FurLayerParams=grcevNONE;
static grcEffectVar varID_FurLayerParams2=grcevNONE;
static grcEffectVar varID_FurLayerParams3=grcevNONE;
static grcEffectVar varID_FurPlayerLFeetPos=grcevNONE;
static grcEffectVar varID_FurPlayerRFeetPos=grcevNONE;
static grcTexture* gFurGrassLODComboDiffTex=NULL;
//static grcTexture* gFurGrassLODComboH2Tex[2]={NULL};
//static Color32 gFurGrassLODComboH2TexMask[8] = {
// Color32( Vector4(1,0,0,0) ),
// Color32( Vector4(0,1,0,0) ),
// Color32( Vector4(0,0,1,0) ),
// Color32( Vector4(0,0,0,1) ),
// Color32( Vector4(1,0,0,0) ),
// Color32( Vector4(0,1,0,0) ),
// Color32( Vector4(0,0,1,0) ),
// Color32( Vector4(0,0,0,1) )
// };
static grcTexture* gFurGrassLODComboH4Tex[4]={NULL};
static Color32 gFurGrassLODComboH4TexMask[8] = {
Color32( Vector4(0,1,0,0) ),
Color32( Vector4(0,0,0,1) ),
Color32( Vector4(0,1,0,0) ),
Color32( Vector4(0,0,0,1) ),
Color32( Vector4(0,1,0,0) ),
Color32( Vector4(0,0,0,1) ),
Color32( Vector4(0,1,0,0) ),
Color32( Vector4(0,0,0,1) )
};
static grcTexture* gFurGrassLODTexN=NULL;
static grcTexture* gFurGrassLODStippleTex=NULL;
static grcRasterizerStateHandle hFurGrassRS_pass1 = grcStateBlock::RS_Invalid;
static grcBlendStateHandle hFurGrassBS_pass1 = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hFurGrassDSS_pass1 = grcStateBlock::DSS_Invalid;
static grcBlendStateHandle hFurGrassBS_pass1_end = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hFurGrassDSS_pass2a = grcStateBlock::DSS_Invalid;
static grcDepthStencilStateHandle hFurGrassDSS_pass2b = grcStateBlock::DSS_Invalid;
static grcDepthStencilStateHandle hFurGrassDSS_pass2 = grcStateBlock::DSS_Invalid;
static grcBlendStateHandle hFurGrassBS_pass3 = grcStateBlock::BS_Invalid;
static grcDepthStencilStateHandle hFurGrassDSS_pass3 = grcStateBlock::DSS_Invalid;
static grcBlendStateHandle hFurGrassBS_pass3_end = grcStateBlock::BS_Invalid;
//
//
//
//
static grcTexture* LoadFurGrassLODTexture(const char *texname)
{
grcTexture *pTexture = gpPlantMgrTextureDictionary->Lookup(texname);
Assertf(pTexture, "Cannot find fur grass texture '%s'!", texname);
pTexture->AddRef();
return(pTexture);
}
static grmShader* LoadGrassFurLODShader(const char *shaderName)
{
grmShader *pShader=NULL;
pShader = grmShaderFactory::GetInstance().Create();
Assert(pShader);
if(!pShader->Load(shaderName))
{
Assertf(FALSE, "Error loading '%s' shader!", shaderName);
return(NULL);
}
return(pShader);
}
bool CPlantMgr::FurGrassLODInitialise()
{
// load fur grass shader and dics:
gFurGrassLODShader = LoadGrassFurLODShader("grass_fur_lod");
Assert(gFurGrassLODShader);
gFurGrassLODComboTechniqueID = gFurGrassLODShader->LookupTechnique("deferred_furgrasstriCombo", TRUE);
Assert(gFurGrassLODComboTechniqueID);
gFurGrassDitheredTechniqueID = gFurGrassLODShader->LookupTechnique("furgrasstri_dithered", TRUE);
Assert(gFurGrassDitheredTechniqueID);
varID_FurFresnel = gFurGrassLODShader->LookupVar("Fresnel", TRUE);
Assert(varID_FurFresnel);
varID_FurSpecFalloff = gFurGrassLODShader->LookupVar("Specular", TRUE);
Assert(varID_FurSpecFalloff);
varID_FurSpecInt = gFurGrassLODShader->LookupVar("SpecularColor", TRUE);
Assert(varID_FurSpecInt);
varID_FurBumpiness = gFurGrassLODShader->LookupVar("Bumpiness", TRUE);
Assert(varID_FurBumpiness);
varID_FurLODDiffuseTex = gFurGrassLODShader->LookupVar("DiffuseTexFur", TRUE);
Assert(varID_FurLODDiffuseTex);
varID_FurLODComboHeightTex = gFurGrassLODShader->LookupVar("ComboHeightTexFur", TRUE);
Assert(varID_FurLODComboHeightTex);
varID_FurLODComboHeightTexMask = gFurGrassLODShader->LookupVar("ComboHeightTexMask", TRUE);
Assert(varID_FurLODComboHeightTexMask);
varID_FurLODNormalTex = gFurGrassLODShader->LookupVar("BumpTexFur"); // this texture is optional
#if FURGRASS_USE_ALPHA_STIPPLE
varID_FurLODStippleTex = gFurGrassLODShader->LookupVar("StippleTexture", TRUE);
Assert(varID_FurLODStippleTex);
#endif
varID_FurDitherAlphaFadeParams = gFurGrassLODShader->LookupVar("furDitherAlphaFadeParams",TRUE);
Assert(varID_FurDitherAlphaFadeParams);
varID_FurLayerParams = gFurGrassLODShader->LookupVar("furLayerParams",TRUE);
Assert(varID_FurLayerParams);
varID_FurLayerParams2 = gFurGrassLODShader->LookupVar("furLayerParams2",TRUE);
Assert(varID_FurLayerParams2);
varID_FurLayerParams3 = gFurGrassLODShader->LookupVar("furLayerParams3",TRUE);
Assert(varID_FurLayerParams3);
varID_FurPlayerLFeetPos = gFurGrassLODShader->LookupVar("playerLFootPos",TRUE);
Assert(varID_FurPlayerLFeetPos);
varID_FurPlayerRFeetPos = gFurGrassLODShader->LookupVar("playerRFootPos",TRUE);
Assert(varID_FurPlayerRFeetPos);
gFurGrassLODComboDiffTex = LoadFurGrassLODTexture("fur_grass_d_0");
Assert(gFurGrassLODComboDiffTex);
// gFurGrassLODComboH2Tex[0] = LoadFurGrassLODTexture("fur_grass_rgba2_0");
// Assert(gFurGrassLODComboH2Tex[0]);
// gFurGrassLODComboH2Tex[1] = LoadFurGrassLODTexture("fur_grass_rgba2_1");
// Assert(gFurGrassLODComboH2Tex[1]);
gFurGrassLODComboH4Tex[0] = LoadFurGrassLODTexture("fur_grass_rgba4_0");
Assert(gFurGrassLODComboH4Tex[0]);
gFurGrassLODComboH4Tex[1] = LoadFurGrassLODTexture("fur_grass_rgba4_1");
Assert(gFurGrassLODComboH4Tex[1]);
gFurGrassLODComboH4Tex[2] = LoadFurGrassLODTexture("fur_grass_rgba4_2");
Assert(gFurGrassLODComboH4Tex[2]);
gFurGrassLODComboH4Tex[3] = LoadFurGrassLODTexture("fur_grass_rgba4_3");
Assert(gFurGrassLODComboH4Tex[3]);
gFurGrassLODTexN = LoadFurGrassLODTexture("fur_grass_n");
Assert(gFurGrassLODTexN);
#if FURGRASS_USE_ALPHA_STIPPLE
gFurGrassLODStippleTex = LoadFurGrassLODTexture("txstipple");
Assert(gFurGrassLODStippleTex);
#endif
// initialize renderstate blocks:
const u32 rsCullMode = grcRSV::CULL_BACK;
static dev_bool bDepthWrite = true;
const bool rsDepthWrite = bDepthWrite;
const bool rsDepthTest = TRUE;
// pass1:
if(hFurGrassRS_pass1 == grcStateBlock::RS_Invalid)
{
grcRasterizerStateDesc rasDesc;
rasDesc.CullMode = rsCullMode;
hFurGrassRS_pass1 = grcStateBlock::CreateRasterizerState(rasDesc);
Assert(hFurGrassRS_pass1 != grcStateBlock::RS_Invalid);
}
if(hFurGrassBS_pass1 == grcStateBlock::BS_Invalid)
{
static dev_bool debugEnableAlphaToMask1 = true;
static dev_bool debugUseSolidAlphaToMask1 = false;
grcBlendStateDesc bsDesc;
bsDesc.AlphaToCoverageEnable = debugEnableAlphaToMask1;
bsDesc.AlphaToMaskOffsets = debugUseSolidAlphaToMask1 ? grcRSV::ALPHATOMASKOFFSETS_SOLID : grcRSV::ALPHATOMASKOFFSETS_DITHERED;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
hFurGrassBS_pass1 = grcStateBlock::CreateBlendState(bsDesc);
Assert(hFurGrassBS_pass1 != grcStateBlock::BS_Invalid);
}
if(hFurGrassDSS_pass1 == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = rsDepthTest;
dsDesc.DepthWriteMask = rsDepthWrite;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
// stencil state:
dsDesc.StencilEnable = TRUE;
dsDesc.StencilReadMask = 0xff;
dsDesc.StencilWriteMask = 0x10; // write only to 5th bit;
dsDesc.FrontFace.StencilFunc =
dsDesc.BackFace.StencilFunc = grcRSV::CMP_ALWAYS;
dsDesc.FrontFace.StencilPassOp =
dsDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_REPLACE;
dsDesc.FrontFace.StencilFailOp =
dsDesc.BackFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp =
dsDesc.BackFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
hFurGrassDSS_pass1 = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hFurGrassDSS_pass1 != grcStateBlock::DSS_Invalid);
}
if(hFurGrassBS_pass1_end == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.AlphaToCoverageEnable = FALSE;
bsDesc.IndependentBlendEnable = TRUE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
// SSA: don't overwrite GBuffer0.alpha
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_RED | grcRSV::COLORWRITEENABLE_GREEN | grcRSV::COLORWRITEENABLE_BLUE;
bsDesc.BlendRTDesc[1].BlendEnable = FALSE;
bsDesc.BlendRTDesc[1].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[2].BlendEnable = FALSE;
bsDesc.BlendRTDesc[2].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
bsDesc.BlendRTDesc[3].BlendEnable = FALSE;
bsDesc.BlendRTDesc[3].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
hFurGrassBS_pass1_end = grcStateBlock::CreateBlendState(bsDesc);
Assert(hFurGrassBS_pass1_end != grcStateBlock::BS_Invalid);
}
// pass2:
if(hFurGrassDSS_pass2a == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = rsDepthTest;
dsDesc.DepthWriteMask = false;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
// stencil state:
dsDesc.StencilEnable = TRUE;
dsDesc.StencilReadMask = 0x10; // read 5th bit
dsDesc.StencilWriteMask = 0x00; // do not touch stencil
dsDesc.FrontFace.StencilFunc =
dsDesc.BackFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp =
dsDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_ZERO; // reset special bit
dsDesc.FrontFace.StencilFailOp =
dsDesc.BackFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp =
dsDesc.BackFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
hFurGrassDSS_pass2a = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hFurGrassDSS_pass2a != grcStateBlock::DSS_Invalid);
}
if(hFurGrassDSS_pass2b == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = rsDepthTest;
dsDesc.DepthWriteMask = false;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
// stencil state:
dsDesc.StencilEnable = TRUE;
dsDesc.StencilReadMask = 0x10; // read 5th bit
dsDesc.StencilWriteMask = 0x10; // write only to 5th bit;
dsDesc.FrontFace.StencilFunc =
dsDesc.BackFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp =
dsDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_ZERO; // reset special bit
dsDesc.FrontFace.StencilFailOp =
dsDesc.BackFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp =
dsDesc.BackFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
hFurGrassDSS_pass2b = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hFurGrassDSS_pass2b != grcStateBlock::DSS_Invalid);
}
if(hFurGrassDSS_pass2 == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = rsDepthTest;
dsDesc.DepthWriteMask = false;//rsDepthWrite;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
// stencil state:
dsDesc.StencilEnable = FALSE;
dsDesc.FrontFace.StencilFunc =
dsDesc.BackFace.StencilFunc = grcRSV::CMP_ALWAYS;
dsDesc.FrontFace.StencilPassOp =
dsDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilFailOp =
dsDesc.BackFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp =
dsDesc.BackFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
hFurGrassDSS_pass2 = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hFurGrassDSS_pass2 != grcStateBlock::DSS_Invalid);
}
// pass3:
if(hFurGrassBS_pass3 == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_NONE;
hFurGrassBS_pass3 = grcStateBlock::CreateBlendState(bsDesc);
Assert(hFurGrassBS_pass3 != grcStateBlock::BS_Invalid);
}
if(hFurGrassDSS_pass3 == grcStateBlock::DSS_Invalid)
{
grcDepthStencilStateDesc dsDesc;
dsDesc.DepthEnable = rsDepthTest;
dsDesc.DepthWriteMask = rsDepthWrite;
dsDesc.DepthFunc = rage::FixupDepthDirection(grcRSV::CMP_LESSEQUAL);
// stencil state:
dsDesc.StencilEnable = TRUE;
dsDesc.StencilReadMask = 0x10; // read 5th bit
dsDesc.StencilWriteMask = 0x10; // write only to 5th bit;
dsDesc.FrontFace.StencilFunc =
dsDesc.BackFace.StencilFunc = grcRSV::CMP_EQUAL;
dsDesc.FrontFace.StencilPassOp =
dsDesc.BackFace.StencilPassOp = grcRSV::STENCILOP_ZERO; // reset special bit
dsDesc.FrontFace.StencilFailOp =
dsDesc.BackFace.StencilFailOp = grcRSV::STENCILOP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp =
dsDesc.BackFace.StencilDepthFailOp = grcRSV::STENCILOP_KEEP;
hFurGrassDSS_pass3 = grcStateBlock::CreateDepthStencilState(dsDesc);
Assert(hFurGrassDSS_pass3 != grcStateBlock::DSS_Invalid);
}
if(hFurGrassBS_pass3_end == grcStateBlock::BS_Invalid)
{
grcBlendStateDesc bsDesc;
bsDesc.IndependentBlendEnable = FALSE;
bsDesc.BlendRTDesc[0].BlendEnable = FALSE;
bsDesc.BlendRTDesc[0].RenderTargetWriteMask = grcRSV::COLORWRITEENABLE_ALL;
hFurGrassBS_pass3_end = grcStateBlock::CreateBlendState(bsDesc);
Assert(hFurGrassBS_pass3_end != grcStateBlock::BS_Invalid);
}
return(TRUE);
}
//
//
//
//
static
void furDrawTriangleArrays(atArray<Vector3> &triVerts)
{
const float zOffset = gfFurGrassZOffset;
const u32 nNumTris = triVerts.GetCount() / 3;
s32 numTrisLeftToDraw = nNumTris;
u32 actualVertIdx = 0;
while(numTrisLeftToDraw > 0)
{
s32 trisToDraw = numTrisLeftToDraw;
if((numTrisLeftToDraw*3) > grcBeginMax)
{
trisToDraw = grcBeginMax/3;
numTrisLeftToDraw -= grcBeginMax/3;
}
else
{
trisToDraw = numTrisLeftToDraw;
numTrisLeftToDraw -= trisToDraw;
}
grcNormal3f(0.0f, 0.0f, 1.0f);
grcTexCoord2f(0.0f, 0.0f);
grcColor( Color32(255, 255, 255, 255) );
Color32 colV0, colV1, colV2;
grcBegin(drawTris, trisToDraw*3);
for(s32 v=0; v<trisToDraw; v++)
{
Vector3 *pVert0 = &triVerts[ actualVertIdx ];
colV0.Set((u32)pVert0->uw);
actualVertIdx++;
Vector3 *pVert1 = &triVerts[ actualVertIdx ];
colV1.Set((u32)pVert1->uw);
actualVertIdx++;
Vector3 *pVert2 = &triVerts[ actualVertIdx ];
colV2.Set((u32)pVert2->uw);
actualVertIdx++;
static dev_float uvMultiplier = 1.0f;
Vector2 uvOffset0, uvOffset1, uvOffset2;
uvOffset0.x = rage::Abs(pVert0->x) *uvMultiplier; //rage::Abs( rage::Modf( pVert0->x, &int_modf) );
uvOffset0.y = rage::Abs(pVert0->y) *uvMultiplier; //rage::Abs( rage::Modf( pVert0->y, &int_modf) );
uvOffset1.x = rage::Abs(pVert1->x)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->x, &int_modf) );
uvOffset1.y = rage::Abs(pVert1->y)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->y, &int_modf) );
uvOffset2.x = rage::Abs(pVert2->x)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->x, &int_modf) );
uvOffset2.y = rage::Abs(pVert2->y)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->y, &int_modf) );
grcColor(colV0);
grcTexCoord2f(uvOffset0.x, uvOffset0.y);
grcVertex3f(pVert0->x, pVert0->y, pVert0->z + zOffset);
grcColor(colV1);
grcTexCoord2f(uvOffset1.x, uvOffset1.y);
grcVertex3f(pVert1->x, pVert1->y, pVert1->z + zOffset);
grcColor(colV2);
grcTexCoord2f(uvOffset2.x, uvOffset2.y);
grcVertex3f(pVert2->x, pVert2->y, pVert2->z + zOffset);
}
grcEnd();
}
} // end of furDrawTriangleArrays()...
//
//
//
//
static
void furDrawTriangleBatch(atArray<Vector3> &triVerts, const u32 nNumTris, atArray<u8> &triBatchIdx, const u8 batchID)
{
const float zOffset = gfFurGrassZOffset;
u32 actualTriIdx=0;
s32 numTrisLeftToDraw = nNumTris;
while(numTrisLeftToDraw > 0)
{
s32 trisToDraw = numTrisLeftToDraw;
if((numTrisLeftToDraw*3) > grcBeginMax)
{
trisToDraw = grcBeginMax/3;
numTrisLeftToDraw -= grcBeginMax/3;
}
else
{
trisToDraw = numTrisLeftToDraw;
numTrisLeftToDraw -= trisToDraw;
}
grcNormal3f(0.0f, 0.0f, 1.0f);
grcTexCoord2f(0.0f, 0.0f);
grcColor( Color32(255, 255, 255, 255) );
Color32 colV0, colV1, colV2;
grcBegin(drawTris, trisToDraw*3);
for(s32 v=0; v<trisToDraw; v++)
{
while(triBatchIdx[actualTriIdx++] != batchID) // skip tri if not belonging to current batch
continue;
u32 actualVertIdx = (actualTriIdx-1)*3;
Vector3 *pVert0 = &triVerts[ actualVertIdx ];
colV0.Set((u32)pVert0->uw);
actualVertIdx++;
Vector3 *pVert1 = &triVerts[ actualVertIdx ];
colV1.Set((u32)pVert1->uw);
actualVertIdx++;
Vector3 *pVert2 = &triVerts[ actualVertIdx ];
colV2.Set((u32)pVert2->uw);
actualVertIdx++;
static dev_float uvMultiplier = 1.0f;
Vector2 uvOffset0, uvOffset1, uvOffset2;
uvOffset0.x = rage::Abs(pVert0->x) *uvMultiplier; //rage::Abs( rage::Modf( pVert0->x, &int_modf) );
uvOffset0.y = rage::Abs(pVert0->y) *uvMultiplier; //rage::Abs( rage::Modf( pVert0->y, &int_modf) );
uvOffset1.x = rage::Abs(pVert1->x)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->x, &int_modf) );
uvOffset1.y = rage::Abs(pVert1->y)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->y, &int_modf) );
uvOffset2.x = rage::Abs(pVert2->x)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->x, &int_modf) );
uvOffset2.y = rage::Abs(pVert2->y)*uvMultiplier; //rage::Abs( rage::Modf( pVert1->y, &int_modf) );
grcColor(colV0);
grcTexCoord2f(uvOffset0.x, uvOffset0.y);
grcVertex3f(pVert0->x, pVert0->y, pVert0->z + zOffset);
grcColor(colV1);
grcTexCoord2f(uvOffset1.x, uvOffset1.y);
grcVertex3f(pVert1->x, pVert1->y, pVert1->z + zOffset);
grcColor(colV2);
grcTexCoord2f(uvOffset2.x, uvOffset2.y);
grcVertex3f(pVert2->x, pVert2->y, pVert2->z + zOffset);
}
grcEnd();
}
} // end of furDrawTriangleBatch()...
//
//
//
//
bool CPlantMgr::FurGrassLODRender(const Vector3& cameraPos, const Vector3& UNUSED_PARAM(camFront), const Vector3& UNUSED_PARAM(playerPos))
{
Assert(gFurGrassLODShader);
Assert(gFurGrassLODComboTechniqueID);
#if RSG_PC || RSG_DURANGO || RSG_ORBIS
const u32 cFurMaxNumTris = CPLANT_MAX_CACHE_LOC_TRIS_NUM*CPLANT_LOC_TRIS_LIST_NUM/4;
#else
const u32 cFurMaxNumTris = CPLANT_MAX_CACHE_LOC_TRIS_NUM*CPLANT_LOC_TRIS_LIST_NUM/2;
#endif
const bool bIsCameraUnderwater = CGrassRenderer::GetGlobalCameraUnderwater();
atUserArray<Vector3> tabFurTriVerts( Alloca(Vector3,cFurMaxNumTris*3), cFurMaxNumTris*3 );
atUserArray<u8> tabFurTriVertsColEntIdx( Alloca(u8,cFurMaxNumTris), cFurMaxNumTris );
u16 tabCountColEntTri[CPLANT_COL_ENTITY_CACHE_SIZE]; // counts all tris belonging to ColEntities
sysMemSet(tabCountColEntTri, 0x00, sizeof(tabCountColEntTri));
const float fFurGrassMaxDist2 = gfFurGrassMaxDist*gfFurGrassMaxDist;
const u32 bufferID = GetRenderRTBufferID();
// find default batch (first batch with "invalid" pickup info),
// so other "invalid" batches can be gathered and rendered together
// (avoiding seams from batching - collision entity meshes can be split during export):
u8 defaultBatchIdx=255;
for(u32 batch=0; batch<CPLANT_COL_ENTITY_CACHE_SIZE; batch++)
{
if(!m_furGrassPickupRenderInfo[bufferID][batch].m_bValid)
{
defaultBatchIdx = (u8)batch;
break;
}
}
Assertf(defaultBatchIdx < CPLANT_COL_ENTITY_CACHE_SIZE, "Error finding default batch!");
spdTransposedPlaneSet8 cullFrustum;
CGrassRenderer::GetGlobalCullFrustum(&cullFrustum);
bool bFurGrassTagsPresent = false;
//
// render all close lists:
//
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM && (tabFurTriVerts.GetCapacity()!=tabFurTriVerts.GetCount()); listID++)
{
CPlantLocTriArray& triRenderTab = *m_LocTrisRenderTab[bufferID][listID];
u16 loctri = triRenderTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triRenderTab[loctri];
// check if pLocTri inside current View Frustum and creates plants:
if(pLocTri->m_bCreatesPlants)
{
s32 procTagId = PGTAMATERIALMGR->UnpackProcId(pLocTri->m_nSurfaceType);
s32 plantInfoIndex = g_procInfo.m_procTagTable[procTagId].plantIndex;
CPlantInfo *pPlantInfo = &g_procInfo.m_plantInfos[plantInfoIndex];
if(pPlantInfo->m_Flags.IsSet(PROCPLANT_FURGRASS))
{
bFurGrassTagsPresent = true;
if(IsLocTriVisibleByCamera(cullFrustum, pLocTri) && IsLocTriVisibleUnderwater(pLocTri, bIsCameraUnderwater))
{
Vector3 Vert0 = pLocTri->GetV1();
Vector3 Vert1 = pLocTri->GetV2();
Vector3 Vert2 = pLocTri->GetV3();
const Vector3 Center= pLocTri->GetCenter();
Color32 colV0, colV1, colV2;
#if __BANK
if(gbForceGroundColor)
{
colV0 = gbGroundColorV1; colV0.SetAlpha(CPLANT_PV_DEFAULT_D_SZ_SXYZ);
colV1 = gbGroundColorV2; colV1.SetAlpha(CPLANT_PV_DEFAULT_D_SZ_SXYZ);
colV2 = gbGroundColorV3; colV2.SetAlpha(CPLANT_PV_DEFAULT_D_SZ_SXYZ);
}
else
#endif //__BANK
{
colV0 = pLocTri->m_GroundColorV1;
colV1 = pLocTri->m_GroundColorV2;
colV2 = pLocTri->m_GroundColorV3;
}
// TODO: PPU: vectorise properly
#if CPLANT_USE_COLLISION_2D_DIST
const Vector3 _colDistV0 = cameraPos - Vert0;
const Vector3 _colDistV1 = cameraPos - Vert1;
const Vector3 _colDistV2 = cameraPos - Vert2;
const Vector3 _colDistV3 = cameraPos - Center;
const Vector2 colDistV0(_colDistV0.x, _colDistV0.y);
const Vector2 colDistV1(_colDistV1.x, _colDistV1.y);
const Vector2 colDistV2(_colDistV2.x, _colDistV2.y);
const Vector2 colDistV3(_colDistV3.x, _colDistV3.y);
#else
const Vector3 colDistV0 = cameraPos - Vert0;
const Vector3 colDistV1 = cameraPos - Vert1;
const Vector3 colDistV2 = cameraPos - Vert2;
const Vector3 colDistV3 = cameraPos - Center;
#endif
const float colDistSqr0 = colDistV0.Mag2();
const float colDistSqr1 = colDistV1.Mag2();
const float colDistSqr2 = colDistV2.Mag2();
const float colDistSqr3 = colDistV3.Mag2();
// calc middle points distances (distance between camera and tri edges' middle points):
#if CPLANT_USE_COLLISION_2D_DIST
const Vector2 _colDistMV01( (colDistV0+colDistV1) * 0.5f); // V0-1
const Vector2 _colDistMV12( (colDistV1+colDistV2) * 0.5f); // V1-2
const Vector2 _colDistMV20( (colDistV2+colDistV0) * 0.5f); // V2-0
const Vector2 colDistMV01(_colDistMV01.x, _colDistMV01.y);
const Vector2 colDistMV12(_colDistMV12.x, _colDistMV12.y);
const Vector2 colDistMV20(_colDistMV20.x, _colDistMV20.y);
#else
const Vector3 colDistMV01( (colDistV0+colDistV1) * 0.5f ); // V0-1
const Vector3 colDistMV12( (colDistV1+colDistV2) * 0.5f ); // V1-2
const Vector3 colDistMV20( (colDistV2+colDistV0) * 0.5f ); // V2-0
#endif
const float colDistSqr4 = colDistMV01.Mag2();
const float colDistSqr5 = colDistMV12.Mag2();
const float colDistSqr6 = colDistMV20.Mag2();
if( (colDistSqr0 < fFurGrassMaxDist2) ||
(colDistSqr1 < fFurGrassMaxDist2) ||
(colDistSqr2 < fFurGrassMaxDist2) ||
(colDistSqr3 < fFurGrassMaxDist2) ||
(colDistSqr4 < fFurGrassMaxDist2) ||
(colDistSqr5 < fFurGrassMaxDist2) ||
(colDistSqr6 < fFurGrassMaxDist2) )
{
Vert0.uw = colV0.GetColor();
Vert1.uw = colV1.GetColor();
Vert2.uw = colV2.GetColor();
tabFurTriVerts.Push(Vert0);
tabFurTriVerts.Push(Vert1);
tabFurTriVerts.Push(Vert2);
// HACK: translate ColEntIdx into index for this furtri:
u8 idx = pLocTri->m_nColEntIdx-1;
FastAssert(idx < CPLANT_COL_ENTITY_CACHE_SIZE);
// pickup settings are invalid? - add this tri to "default" batch:
if(!m_furGrassPickupRenderInfo[bufferID][idx].m_bValid)
{
idx = defaultBatchIdx;
}
tabFurTriVertsColEntIdx.Push(idx);
// count furtri as belonging to given ColEnt:
tabCountColEntTri[idx]++;
// array fully filled up?
if(tabFurTriVerts.GetCapacity()==tabFurTriVerts.GetCount())
{
break;
}
}
}// if(IsLocTriVisibleByCamera(cullFrustum, pLocTri) && IsLocTriVisibleUnderwater(pLocTri, bIsCameraUnderwater)))...
}// if(LODMASK_FUR)...
}// if(pLocTri->m_bCreatesPlants && IsLocTriVisibleByCamera(pLocTri))...
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
#if __BANK
gnFurGrassNumPolys = tabFurTriVerts.GetCount()/3; // debug stats
#endif
if(!tabFurTriVerts.GetCount())
{
return(bFurGrassTagsPresent); // nothing to render
}
DeferredLighting::PrepareCutoutPass(true, true, false);
grmShader *shader = gFurGrassLODShader;
Matrix34 mtx;
mtx.Identity();
grcViewport::SetCurrentWorldMtx(RCC_MAT34V(mtx));
bool bRestoreSamplers = false;
grcSamplerStateHandle sshFurLODDiffuseTex = grcStateBlock::SS_Invalid,
sshFurLODComboHeightTex = grcStateBlock::SS_Invalid,
sshFurLODNormalTex = grcStateBlock::SS_Invalid;
if(gbEnableFurGrassMipmapLodBias)
{
grcSamplerStateHandle h;
grcSamplerStateDesc d;
// quantize with accuracy of SamplerStateSet:
const float fFurGrassMipmapLodBias = float((s32(int(gfFurGrassMipmapLodBias*256.0f)&0x1FFF)<<19)>>19) * (1.0f / 256.0f);
h = gFurGrassLODShader->GetSamplerState(varID_FurLODDiffuseTex);
grcStateBlock::GetSamplerStateDesc(h,d);
d.MipLodBias = fFurGrassMipmapLodBias;
sshFurLODDiffuseTex = grcStateBlock::CreateSamplerState(d);
Assert(sshFurLODDiffuseTex != grcStateBlock::SS_Invalid);
gFurGrassLODShader->PushSamplerState(varID_FurLODDiffuseTex, sshFurLODDiffuseTex);
h = gFurGrassLODShader->GetSamplerState(varID_FurLODComboHeightTex);
grcStateBlock::GetSamplerStateDesc(h,d);
d.MipLodBias = fFurGrassMipmapLodBias;
sshFurLODComboHeightTex = grcStateBlock::CreateSamplerState(d);
Assert(sshFurLODComboHeightTex != grcStateBlock::SS_Invalid);
gFurGrassLODShader->PushSamplerState(varID_FurLODComboHeightTex, sshFurLODComboHeightTex);
if(varID_FurLODNormalTex)
{
h = gFurGrassLODShader->GetSamplerState(varID_FurLODNormalTex);
grcStateBlock::GetSamplerStateDesc(h,d);
d.MipLodBias = fFurGrassMipmapLodBias;
sshFurLODNormalTex = grcStateBlock::CreateSamplerState(d);
Assert(sshFurLODNormalTex != grcStateBlock::SS_Invalid);
gFurGrassLODShader->PushSamplerState(varID_FurLODNormalTex, sshFurLODNormalTex);
}
bRestoreSamplers = true;
}
shader->SetVar(varID_FurFresnel, gfFurGrassFresnel);
shader->SetVar(varID_FurSpecFalloff,gfFurGrassSpecFalloff);
shader->SetVar(varID_FurSpecInt, gfFurGrassSpecInt);
shader->SetVar(varID_FurBumpiness, gfFurGrassBumpiness * 0.001f);
shader->SetVar(varID_FurLODDiffuseTex, gFurGrassLODComboDiffTex);
if(varID_FurLODNormalTex)
shader->SetVar(varID_FurLODNormalTex, gFurGrassLODTexN);
#if FURGRASS_USE_ALPHA_STIPPLE
shader->SetVar(varID_FurLODStippleTex, gFurGrassLODStippleTex);
#endif
Vector4 alphaFadeParams;
const float invAlphaFade = 1.0f / (gfFurGrassAlphaFadeFar*gfFurGrassAlphaFadeFar - gfFurGrassAlphaFadeClose*gfFurGrassAlphaFadeClose);
alphaFadeParams.x = gfFurGrassAlphaFadeFar*gfFurGrassAlphaFadeFar * invAlphaFade; // f2 / (f2-c2)
alphaFadeParams.y = -1.0f * invAlphaFade; //-1.0 / (f2-c2)
alphaFadeParams.z = 0.0f;
alphaFadeParams.w = 0.0f;
shader->SetVar(varID_FurDitherAlphaFadeParams, alphaFadeParams);
Vector4 furLayerParams;
furLayerParams.x = 1.0f; // layerShd
furLayerParams.y = float(gnFurGrassFurStep)/10000.0f; // furStep
furLayerParams.z = 1.0f / 255.0f; // alphaClip
furLayerParams.w = 0.0f;
Vector4 furLayerParams2;
furLayerParams2.x = gfFurGrassOffsetU; // offset U
furLayerParams2.y = gfFurGrassOffsetV; // offset V
furLayerParams2.z = float(gnFurGrassUmScaleX)/100000.0f;// um scale X
furLayerParams2.w = float(gnFurGrassUmScaleY)/100000.0f;// um scale Y
shader->SetVar(varID_FurLayerParams2, furLayerParams2);
Vector4 furLayerParams3;
furLayerParams3.x = gfFurGrassUvScale; // UV scale
furLayerParams3.y = gfFurGrassUvScale2; // UV scale 2
furLayerParams3.z = gfFurGrassUvScale3; // UV scale 3
furLayerParams3.w = 0.0f;
shader->SetVar(varID_FurLayerParams3, furLayerParams3);
shader->SetVar(varID_FurPlayerLFeetPos, CGrassRenderer::GetGlobalPlayerLFootPos());
shader->SetVar(varID_FurPlayerRFeetPos, CGrassRenderer::GetGlobalPlayerRFootPos());
const u32 DEFERRED_MATERIAL_FURGRASS = DEFERRED_MATERIAL_SPECIALBIT; //0x10
CompileTimeAssert(DEFERRED_MATERIAL_FURGRASS==0x10);
static dev_bool bEnableDitherPass=!FURGRASS_USE_ALPHA_STIPPLE;
// 1. draw dithered alphafade mask into stencil:
if(bEnableDitherPass)
{
// render only to depth/stencil:
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS)
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
grcStateBlock::SetRasterizerState( hFurGrassRS_pass1 );
static dev_u64 sampleMask = ~0ULL;
grcStateBlock::SetBlendState( hFurGrassBS_pass1, ~0U,sampleMask);
CShaderLib::SetAlphaTestRef(0.f);
const u32 materialID = DEFERRED_MATERIAL_FURGRASS;
grcStateBlock::SetDepthStencilState(hFurGrassDSS_pass1, materialID);
// draw dithered:
grcEffectTechnique forcedTech = gFurGrassDitheredTechniqueID;
ASSERT_ONLY(const u32 numPasses=) shader->BeginDraw(grmShader::RMC_DRAW, false, forcedTech);
Assert(numPasses>0);
shader->Bind(0);
{
furDrawTriangleArrays(tabFurTriVerts);
}
shader->UnBind();
shader->EndDraw();
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS)
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
}
grcStateBlock::SetBlendState(hFurGrassBS_pass1_end);
// 2. main furgrass draw:
if(1)
{
grcEffectTechnique forcedTech = gFurGrassLODComboTechniqueID;
ASSERT_ONLY(const u32 numShaderPasses=) shader->BeginDraw(grmShader::RMC_DRAW, false, forcedTech);
Assert(numShaderPasses>0);
Assert(numShaderPasses==FURGRASS_MAX_LAYERS);
Assert(gnFurGrassNumLayers <= numShaderPasses);
const u32 numPasses = gnFurGrassNumLayers;
// draw in batches - one batch for every ColEntity:
for(u32 batch=0; batch<CPLANT_COL_ENTITY_CACHE_SIZE; batch++)
{
if(!tabCountColEntTri[batch])
continue; // skip if nothing to draw for this batch
CPlantColBoundEntryFurGrassInfo *pFurGrassInfo = &m_furGrassPickupRenderInfo[bufferID][batch];
grcTexture *pDiffuseTex = (pFurGrassInfo->m_bValid && pFurGrassInfo->m_furGrassDiffTex)?
pFurGrassInfo->m_furGrassDiffTex : gFurGrassLODComboDiffTex;
shader->SetVar(varID_FurLODDiffuseTex, pDiffuseTex);
if(varID_FurLODNormalTex)
{
grcTexture *pNormalTex = (pFurGrassInfo->m_bValid && pFurGrassInfo->m_furGrassNormalTex)?
pFurGrassInfo->m_furGrassNormalTex : gFurGrassLODTexN;
shader->SetVar(varID_FurLODNormalTex, pNormalTex);
}
for(u32 pass=0; pass<numPasses; pass++)
{
if(bEnableDitherPass)
{
if(pass < (numPasses-1))
{ // earlier passes: do not touch stencil
const u32 stencilRef = DEFERRED_MATERIAL_FURGRASS;
grcStateBlock::SetDepthStencilState( hFurGrassDSS_pass2a, stencilRef );
}
else
{ // last pass: clear stencil:
const u32 stencilRef = DEFERRED_MATERIAL_FURGRASS;
grcStateBlock::SetDepthStencilState( hFurGrassDSS_pass2b, stencilRef );
}
}
else
{
grcStateBlock::SetDepthStencilState(hFurGrassDSS_pass2, 0);
}
// fur params settable for every layer:
Assert(pass < FURGRASS_MAX_LAYERS);
furLayerParams.x = float(gnFurGrassShadow[pass]) / 255.0f;
furLayerParams.z = float(gnFurGrassAlphaClip[pass]) / 255.0f;
shader->SetVar(varID_FurLayerParams, furLayerParams);
// different texture for every pass:
if(gbFurGrassSeparateTextures)
{
switch(gnFurGrassSeparateTexturesTech)
{
case(0): // 4 separate combo height GA textures
{
const u32 texIdx = pass>>1;
grcTexture *pComboHTex = (pFurGrassInfo->m_bValid && pFurGrassInfo->m_furGrassComboH4Tex[texIdx])?
pFurGrassInfo->m_furGrassComboH4Tex[texIdx] :
gFurGrassLODComboH4Tex[texIdx];
shader->SetVar(varID_FurLODComboHeightTex, pComboHTex);
shader->SetVar(varID_FurLODComboHeightTexMask, gFurGrassLODComboH4TexMask[ pass ]);
}
break;
//case(1): // 2 separate combo height RGBA textures
// shader->SetVar(varID_FurLODComboHeightTex, gFurGrassLODComboH2Tex[ pass>3?1:0 ]);
// shader->SetVar(varID_FurLODComboHeightTexMask, gFurGrassLODComboH2TexMask[ pass ]);
// break;
}
}
shader->Bind(pass);
{
furDrawTriangleBatch(tabFurTriVerts, tabCountColEntTri[batch], tabFurTriVertsColEntIdx, (u8)batch);
}
shader->UnBind();
}//for(u32 pass=0; pass<numPasses; pass++)...
}//for(u32 batch=0; batch<CPLANT_COL_ENTITY_CACHE_SIZE; batch++)
shader->EndDraw();
}
// 3. clear stencil
if(bEnableDitherPass)
{
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS)
GBuffer::UnlockTargets();
GBuffer::LockSingleTarget(GBUFFER_RT_0, TRUE);
#endif
grcStateBlock::SetBlendState( hFurGrassBS_pass3 );
const u32 stencilRef = DEFERRED_MATERIAL_FURGRASS;
grcStateBlock::SetDepthStencilState( hFurGrassDSS_pass3, stencilRef );
grcEffectTechnique forcedTech = gFurGrassDitheredTechniqueID;
ASSERT_ONLY(const u32 numPasses=) shader->BeginDraw(grmShader::RMC_DRAW, false, forcedTech);
Assert(numPasses>0);
shader->Bind(0);
{
furDrawTriangleArrays(tabFurTriVerts);
}
shader->UnBind();
shader->EndDraw();
#if __PS3 || (RSG_PC || RSG_DURANGO || RSG_ORBIS)
GBuffer::UnlockSingleTarget(GBUFFER_RT_0);
GBuffer::LockTargets();
#endif
}
grcStateBlock::SetBlendState(hFurGrassBS_pass3_end);
// cleanup:
if(bRestoreSamplers)
{
gFurGrassLODShader->PopSamplerState(varID_FurLODDiffuseTex);
grcStateBlock::DestroySamplerState(sshFurLODDiffuseTex);
gFurGrassLODShader->PopSamplerState(varID_FurLODComboHeightTex);
grcStateBlock::DestroySamplerState(sshFurLODComboHeightTex);
if(varID_FurLODNormalTex)
{
gFurGrassLODShader->PopSamplerState(varID_FurLODNormalTex);
grcStateBlock::DestroySamplerState(sshFurLODNormalTex);
}
}
DeferredLighting::FinishCutoutPass();
grcStateBlock::SetBlendState(grcStateBlock::BS_Default);
grcStateBlock::SetDepthStencilState(CShaderLib::DSS_Default_Invert, DEFERRED_MATERIAL_DEFAULT);
grcStateBlock::SetRasterizerState(grcStateBlock::RS_Default);
return(bFurGrassTagsPresent);
}// end of CPlantMgr::FurGrassLODRender()...
#endif //FURGRASS_TEST_V4...
#if PLANTSMGR_DATA_EDITOR
//////////////////////////////////////////////////////////////////////////
// CPlantMgrDataEditor
//
//
//
//
CPlantMgrDataEditor::CPlantMgrDataEditor()
{
m_selectedEntries.Reset();
m_selectedEntries.Reserve(64);
m_bAllowPicking = false;
m_bShowPlantPolys = true;
m_bEdAllCollisionSelectable = false;
m_bEdRegenTriCache = false;
m_bSphereSearchPending = false;
m_bClearSelectedEntries = false;
m_bUpdateCurEditEntry = false;
m_bSaveCurEditEntry = false;
m_bSetProcTypeToAll = false;
m_bMaxUiSphereSearchFromFilePending = false;
m_bMaxUiWritingSelectionToFile = false;
m_bMaxUiSavingChangesToBatch = false;
m_searchEntriesFound = 0;
m_editEntryIdx = -1;
m_searchRadius = 0.05f;
m_triPositions.Reset();
m_triPositions.Reserve(4);
ResetEditableEntry();
m_tagNamesCombo = NULL;
}
bool CPlantMgrDataEditor::InitWidgets(bkBank& bank, const char *bankName)
{
bank.PushGroup(bankName, false);
bank.PushGroup("Search Options", false);
bank.AddToggle("Allow Picking", &m_bAllowPicking, datCallback(cbBankToggleAllowPicking));
bank.AddToggle("Show Plant Polys", &m_bShowPlantPolys, datCallback(cbBankToggleShowPlantPolys));
bank.AddToggle("All Collision Selectable", &m_bEdAllCollisionSelectable, datCallback(cbBankToggleSelectAllCollision));
bank.AddSeparator();
bank.AddTitle("Search Using Sphere Volume");
bank.AddVector("Sphere Center", &m_searchPos, -10000.0f, 10000.0f, 0.001f);
bank.AddButton("Copy Player's Position to Sphere Center", datCallback(cbBankButtonCopyPlayerPosToSphereCenter));
bank.AddSlider("Sphere Radius", &m_searchRadius, 0.01f, 50.0f, 0.001f);
bank.AddButton("Search", datCallback(cbBankButtonSearchSphere));
bank.AddSeparator();
bank.AddText("Entries found:", &m_searchEntriesFound, true);
bank.AddSeparator();
bank.AddButton("Clear Selected Entries", datCallback(cbBankButtonClearSelEntries));
bank.PopGroup();
bank.PushGroup("Edit Options", false);
bank.AddText("Entry Index:", &m_editEntryIdx, false, datCallback(cbBankTextEditEntryIdxChanged));
AddEditableDataToWidget(bank);
bank.PopGroup();
bank.PopGroup();
return true;
}
void CPlantMgrDataEditor::ResetEditableEntry()
{
m_editableEntry.m_nSurfaceType = 0;
for (int i = 0; i < 3; i++)
{
m_editableEntry.m_V[i].Zero();
m_editableEntry.m_GroundColorV[i].Set(0,0,0,0);
m_editableEntry.m_GroundDensity[i] =0;
m_editableEntry.m_GroundScaleZ[i] = 0;
m_editableEntry.m_GroundScaleXYZ[i] = 0;
}
}
void CPlantMgrDataEditor::AddEditableDataToWidget(bkBank& bank)
{
bank.AddVector("Vertex#0", &m_editableEntry.m_V[0], -5000.0f, 5000.0f, 0.001f);
bank.AddVector("Vertex#1", &m_editableEntry.m_V[1], -5000.0f, 5000.0f, 0.001f);
bank.AddVector("Vertex#2", &m_editableEntry.m_V[2], -5000.0f, 5000.0f, 0.001f);
bank.AddSeparator();
m_editableEntry.m_nSurfaceType = 0;
bank.AddSlider("Type", &m_editableEntry.m_nSurfaceType, 0, MAX_PROCEDURAL_TAGS, 1);
if(m_tagNames.GetCount()==0)
{
m_tagNames.Resize(MAX_PROCEDURAL_TAGS+1);
for(s32 i=0; i<=MAX_PROCEDURAL_TAGS; i++)
{
m_tagNames[i] = "n/a";
}
}
if(!m_tagNamesCombo)
{
// create tag names only once - only for _LiveEdit_ group:
m_tagNamesCombo = bank.AddCombo("Type name", &m_editableEntry.m_nSurfaceType, MAX_PROCEDURAL_TAGS, &m_tagNames[0], datCallback(cbBankButtonTypeNameChanged));
Assert(m_tagNamesCombo);
}
bank.AddSeparator();
bank.AddColor("Ground Color at vertex#0", &m_editableEntry.m_GroundColorV[0]);
bank.AddColor("Ground Color at vertex#1", &m_editableEntry.m_GroundColorV[1]);
bank.AddColor("Ground Color at vertex#2", &m_editableEntry.m_GroundColorV[2]);
bank.AddSeparator();
bank.AddSlider("Ground Density weight at vertex#0 (maps to [1, 0.6, 0.3, 0])", &m_editableEntry.m_GroundDensity[0], 0, 3, 1);
bank.AddSlider("Ground Density weight at vertex#1 (maps to [1, 0.6, 0.3, 0])", &m_editableEntry.m_GroundDensity[1], 0, 3, 1);
bank.AddSlider("Ground Density weight at vertex#2 (maps to [1, 0.6, 0.3, 0])", &m_editableEntry.m_GroundDensity[2], 0, 3, 1);
bank.AddSeparator();
bank.AddSlider("Ground ScaleZ weight at vertex#0 (maps to [1, 0.6, 0.3, 0])", &m_editableEntry.m_GroundScaleZ[0], 0, 3, 1);
bank.AddSlider("Ground ScaleZ weight at vertex#1 (maps to [1, 0.6, 0.3, 0])", &m_editableEntry.m_GroundScaleZ[1], 0, 3, 1);
bank.AddSlider("Ground ScaleZ weight at vertex#2 (maps to [1, 0.6, 0.3, 0])", &m_editableEntry.m_GroundScaleZ[2], 0, 3, 1);
bank.AddSeparator();
bank.AddSlider("Ground ScaleXYZ weight at vertex#0 (maps to <0; 1>)", &m_editableEntry.m_GroundScaleXYZ[0], 0, 15, 1);
bank.AddSlider("Ground ScaleXYZ weight at vertex#1 (maps to <0; 1>)", &m_editableEntry.m_GroundScaleXYZ[1], 0, 15, 1);
bank.AddSlider("Ground ScaleXYZ weight at vertex#2 (maps to <0; 1>)", &m_editableEntry.m_GroundScaleXYZ[2], 0, 15, 1);
bank.AddSeparator();
bank.AddButton("Override proc type for all active tris", datCallback(cbBankButtonSetProcTypeToAll));
bank.AddSeparator();
bank.AddButton("Save changes to current entry", datCallback(cbBankButtonSaveCurEntry));
bank.AddSeparator();
bank.AddButton("Save changes to batch", datCallback(cbBankButtonSaveBatch));
bank.AddSeparator();
bank.AddButton("Read input from file", datCallback(cbBankToggleFindClosestEntriesFromFile));
bank.AddSeparator();
bank.AddButton("Write selection to file", datCallback(cbBankToggleWriteSelectionToFile));
bank.PushGroup("Max UI", false); // flags for MaxUI<->Game communication only
bank.AddToggle("Reading input from file", &m_bMaxUiSphereSearchFromFilePending);
bank.AddToggle("Writing selection to file", &m_bMaxUiWritingSelectionToFile);
bank.AddToggle("Saving changes to batch", &m_bMaxUiSavingChangesToBatch);
bank.PopGroup();
}
void CPlantMgrDataEditor::Update()
{
if (m_bClearSelectedEntries)
{
Reset();
m_bClearSelectedEntries = false;
}
// manual in-game picking
if (m_bAllowPicking)
{
if(ioMouse::GetButtons() & ioMouse::MOUSE_LEFT) // single select mode
{
Vector3 mouseStart, mouseEnd;
CDebugScene::GetMousePointing(mouseStart, mouseEnd, false);
if(FindClosestEntry(mouseStart, mouseEnd, false)==1)
{
// when only 1 tri picked via mouse, then automatically select it in the interface:
m_editEntryIdx = 0;
cbBankTextEditEntryIdxChanged();
}
}
else if(ioMouse::GetButtons() & ioMouse::MOUSE_RIGHT) // multiselect mode
{
Vector3 mouseStart, mouseEnd;
CDebugScene::GetMousePointing(mouseStart, mouseEnd, false);
if(FindClosestEntry(mouseStart, mouseEnd, true)==1)
{
// when only 1 tri picked via mouse, then automatically select it in the interface:
m_editEntryIdx = 0;
cbBankTextEditEntryIdxChanged();
}
}
}
// default spherical volume search
if (m_bSphereSearchPending)
{
Reset();
FindClosestEntries(m_searchPos, m_searchRadius);
m_bSphereSearchPending = false;
}
else if (m_bMaxUiSphereSearchFromFilePending)
{
for (int i = 0; i < m_triPositions.GetCount(); i++)
{
FindClosestEntries(m_triPositions[i], m_searchRadius, true);
}
Reset(true);
m_triPositions.clear();
m_bMaxUiSphereSearchFromFilePending = false;
}
}
void CPlantMgrDataEditor::UpdateSafe()
{
if (m_bUpdateCurEditEntry)
{
UpdateEditableEntry();
m_bUpdateCurEditEntry = false;
}
if (m_bMaxUiSavingChangesToBatch)
{
for (int i = 0; i < m_selectedEntries.GetCount(); i++)
{
CPlantLocTri* pLocTri = m_selectedEntries[i];
SaveEditableEntryTo(pLocTri);
}
m_bMaxUiSavingChangesToBatch = false; // signal to MaxUI we're done
Reset();
}
else if (m_bSaveCurEditEntry)
{
m_bSaveCurEditEntry = false;
if (m_editEntryIdx < 0 || m_editEntryIdx >= m_selectedEntries.GetCount())
{
ResetEditableEntry();
return;
}
CPlantLocTri* pLocTri = m_selectedEntries[m_editEntryIdx];
SaveEditableEntryTo(pLocTri);
}
else if (m_bSetProcTypeToAll)
{
m_bSetProcTypeToAll = false;
if(g_procInfo.CreatesPlants(m_editableEntry.m_nSurfaceType)) // apply only valid proc tags
{
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &triTab[loctri];
pLocTri->m_nSurfaceType = PGTAMATERIALMGR->PackProcId(pLocTri->m_nSurfaceType, u8(m_editableEntry.m_nSurfaceType));
pLocTri->m_bCreatesPlants = true;
pLocTri->m_bCreatesObjects = false; // avoid creating procObjects
pLocTri->m_bCreatedObjects = false;
loctri = pLocTri->m_NextTri;
}
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
}
}
}
void CPlantMgrDataEditor::Render()
{
for (int i = 0; i < m_selectedEntries.GetCount(); i++)
{
const CPlantLocTri* pLocTri = m_selectedEntries[i];
if (pLocTri == NULL)
{
continue;
}
const Color32 col(255, 255, 255, 128);
Vector3 v0 = pLocTri->GetV1();
v0.z += Z_OFFSET*2.0f;
Vector3 v1 = pLocTri->GetV2();
v1.z += Z_OFFSET*2.0f;
Vector3 v2 = pLocTri->GetV3();
v2.z += Z_OFFSET*2.0f;
grcBegin(drawTris, 3);
grcColor(col);
grcVertex3f(v0.x, v0.y, v0.z);
grcColor(col);
grcVertex3f(v1.x, v1.y, v1.z);
grcColor(col);
grcVertex3f(v2.x, v2.y, v2.z);
grcEnd();
}
}
void CPlantMgrDataEditor::Reset(bool bKeepSelection)
{
if (bKeepSelection == false)
{
m_selectedEntries.clear();
m_triPositions.clear();
}
m_searchEntriesFound = 0;
m_editEntryIdx = -1;
m_bUpdateCurEditEntry = true;
m_bSaveCurEditEntry = false;
}
int CPlantMgrDataEditor::FindClosestEntry(const Vector3& p0, const Vector3& p1, bool bMultiSelect)
{
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab = *gPlantMgr.m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &(triTab[loctri]);
Vector3 pVert0 = pLocTri->GetV1();
Vector3 pVert1 = pLocTri->GetV2();
Vector3 pVert2 = pLocTri->GetV3();
bool bIntersects = geomSegments::TestSegmentToTriangle(p0, p1, pVert0, pVert1, pVert2);
if (bIntersects)
{
if(bMultiSelect)
{
// add tri to m_selectedEntries only if not picked already:
bool bFound = false;
if(m_selectedEntries.GetCount()>0)
{
for(int i=0; i<m_selectedEntries.GetCount(); i++)
{
if(m_selectedEntries[i] == pLocTri)
{
bFound = true;
break;
}
}
}
if(!bFound)
{
Reset(true);
m_selectedEntries.PushAndGrow(pLocTri);
}
}
else
{
Reset(false);
m_selectedEntries.PushAndGrow(pLocTri);
}
break;
}
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
m_searchEntriesFound = m_selectedEntries.GetCount();
return m_searchEntriesFound;
}
bool CPlantMgrDataEditor::FindClosestEntries(const Vector3& p0, float radius, bool bKeepSelection)
{
bool bFoundAny = false;
Reset(bKeepSelection);
for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)
{
CPlantLocTriArray& triTab= *gPlantMgr.m_LocTrisTab[listID];
u16 loctri = triTab.m_CloseListHead;
while(loctri)
{
CPlantLocTri *pLocTri = &(triTab[loctri]);
Vector3 tri[3];
tri[0] = pLocTri->GetV1();
tri[1] = pLocTri->GetV2();
tri[2] = pLocTri->GetV3();
Vector3 triNormal;
triNormal.Cross(tri[1]-tri[0], tri[2]-tri[0]);
triNormal.Normalize();
bool bIntersects = geomSpheres::TestSphereToTriangle (Vec4V(RCC_VEC3V(p0), ScalarV(radius)), (const Vec3V*)&tri[0], RCC_VEC3V(triNormal));
if (bIntersects)
{
m_selectedEntries.PushAndGrow(pLocTri);
bFoundAny = true;
}
loctri = pLocTri->m_NextTri;
}//while(pLocTri)...
}//for(u32 listID=0; listID<CPLANT_LOC_TRIS_LIST_NUM; listID++)...
m_searchEntriesFound = m_selectedEntries.GetCount();
return bFoundAny;
}
void CPlantMgrDataEditor::UpdateEditableEntry()
{
if (m_editEntryIdx < 0 || m_editEntryIdx >= m_selectedEntries.GetCount())
{
ResetEditableEntry();
return;
}
CPlantLocTri* pLocTri = m_selectedEntries[m_editEntryIdx];
if (pLocTri)
{
m_editableEntry.m_V[0] = pLocTri->GetV1();
m_editableEntry.m_V[1] = pLocTri->GetV2();
m_editableEntry.m_V[2] = pLocTri->GetV3();
m_editableEntry.m_nSurfaceType = PGTAMATERIALMGR->UnpackProcId(pLocTri->m_nSurfaceType);
m_editableEntry.m_GroundColorV[0] = pLocTri->m_GroundColorV1;
m_editableEntry.m_GroundColorV[1] = pLocTri->m_GroundColorV2;
m_editableEntry.m_GroundColorV[2] = pLocTri->m_GroundColorV3;
m_editableEntry.m_GroundDensity[0] = (u8)CPlantLocTri::pv8UnpackDensity(pLocTri->m_GroundColorV1.GetAlpha());
m_editableEntry.m_GroundDensity[1] = (u8)CPlantLocTri::pv8UnpackDensity(pLocTri->m_GroundColorV2.GetAlpha());
m_editableEntry.m_GroundDensity[2] = (u8)CPlantLocTri::pv8UnpackDensity(pLocTri->m_GroundColorV3.GetAlpha());
m_editableEntry.m_GroundScaleZ[0] = (u8)CPlantLocTri::pv8UnpackScaleZ(pLocTri->m_GroundColorV1.GetAlpha());
m_editableEntry.m_GroundScaleZ[1] = (u8)CPlantLocTri::pv8UnpackScaleZ(pLocTri->m_GroundColorV2.GetAlpha());
m_editableEntry.m_GroundScaleZ[2] = (u8)CPlantLocTri::pv8UnpackScaleZ(pLocTri->m_GroundColorV3.GetAlpha());
m_editableEntry.m_GroundScaleXYZ[0] = (u8)CPlantLocTri::pv8UnpackScaleXYZ(pLocTri->m_GroundColorV1.GetAlpha());
m_editableEntry.m_GroundScaleXYZ[1] = (u8)CPlantLocTri::pv8UnpackScaleXYZ(pLocTri->m_GroundColorV2.GetAlpha());
m_editableEntry.m_GroundScaleXYZ[2] = (u8)CPlantLocTri::pv8UnpackScaleXYZ(pLocTri->m_GroundColorV3.GetAlpha());
}
}
void CPlantMgrDataEditor::SaveEditableEntryTo(CPlantLocTri* pLocTri)
{
if (pLocTri == NULL)
{
return;
}
if(g_procInfo.CreatesPlants(m_editableEntry.m_nSurfaceType)) // apply only valid proc tags
{
pLocTri->m_nSurfaceType = PGTAMATERIALMGR->PackProcId(pLocTri->m_nSurfaceType, u8(m_editableEntry.m_nSurfaceType));
pLocTri->m_bCreatesPlants = true;
pLocTri->m_bCreatesObjects = false; // avoid creating procObjects
pLocTri->m_bCreatedObjects = false;
}
pLocTri->m_GroundColorV1 = m_editableEntry.m_GroundColorV[0];
pLocTri->m_GroundColorV2 = m_editableEntry.m_GroundColorV[1];
pLocTri->m_GroundColorV3 = m_editableEntry.m_GroundColorV[2];
u8 packedAlpha = CPlantLocTri::pv8PackDensityScaleZScaleXYZ((u8)m_editableEntry.m_GroundDensity[0],
(u8)m_editableEntry.m_GroundScaleZ[0],
(u8)m_editableEntry.m_GroundScaleXYZ[0]);
pLocTri->m_GroundColorV1.SetAlpha(packedAlpha);
packedAlpha = CPlantLocTri::pv8PackDensityScaleZScaleXYZ((u8)m_editableEntry.m_GroundDensity[1],
(u8)m_editableEntry.m_GroundScaleZ[1],
(u8)m_editableEntry.m_GroundScaleXYZ[1]);
pLocTri->m_GroundColorV2.SetAlpha(packedAlpha);
packedAlpha = CPlantLocTri::pv8PackDensityScaleZScaleXYZ((u8)m_editableEntry.m_GroundDensity[2],
(u8)m_editableEntry.m_GroundScaleZ[2],
(u8)m_editableEntry.m_GroundScaleXYZ[2]);
pLocTri->m_GroundColorV3.SetAlpha(packedAlpha);
}
// tools cannot currently handle the bkData widget, which would be ideal
// for sending this data, so instead we rely on exchanging data via a
// binary file ("x:/procdata") containing the positions of the triangles
// that the tool wants to applies changes to.
//
// if the reading operation is successful, CPlantMgrDataEditor will try
// matching the received positions with triangles in the cache.
//
// the tool at the other end can then save changes to whatever selection
// was found
bool CPlantMgrDataEditor::ReadInputDataFile()
{
const char* INPUT_TRI_POS_FILE = "x:/procdata";
const char* INPUT_TRI_POS_EXT = "bin";
// temporary debug code to test this locally
#if 0
static bool bCreateTestfile = true;
if (bCreateTestfile)
{
bCreateTestfile = false;
fiStream* pOutFile = ASSET.Create(INPUT_TRI_POS_FILE,INPUT_TRI_POS_EXT);
if (pOutFile == NULL)
{
return false;
}
const int cNumElems = 16*3;
float test[cNumElems] = {
-187.704f,1053.67f,37.267f,
-187.037f,1054.34f,37.2651f,
-187.704f,1055.67f,37.4712f,
-187.037f,1056.34f,37.4905f,
-187.704f,1057.67f,37.5569f,
-187.037f,1058.34f,37.6333f,
-187.704f,1059.67f,37.6802f,
-187.037f,1060.34f,37.8504f,
-187.704f,1061.67f,37.9711f,
-187.037f,1062.34f,38.1309f,
-187.704f,1063.67f,38.217f,
-187.037f,1064.34f,38.2152f,
-187.704f,1065.67f,38.184f,
-187.037f,1066.34f,38.0744f,
-187.704f,1067.67f,38.062f,
-187.037f,1068.34f,37.9975f };
u32 written = 0;
written = pOutFile->WriteInt(&cNumElems, 1);
written = pOutFile->WriteFloat(&test[0], cNumElems);
Assert (written == cNumElems);
pOutFile->Close();
}
#endif
fiStream* pFile = ASSET.Open(INPUT_TRI_POS_FILE,INPUT_TRI_POS_EXT);
if (pFile == NULL)
{
return false;
}
int numElems = -1;
int count = pFile->ReadInt(&numElems, 1);
if (count == 0 || numElems < 0 || numElems > 1500 || numElems%3 != 0)
{
pFile->Close();
return false;
}
m_triPositions.clear();
float pos[3] = {0.0f,0.0f,0.0f};
while (pFile->ReadFloat(&pos[0], 3) == 3)
{
m_triPositions.PushAndGrow(Vector3(pos[0],pos[1],pos[2]));
}
pFile->Close();
pFile = NULL;
return true;
}
//
//
//
//
bool CPlantMgrDataEditor::WriteOutputDataFile()
{
const char* OUTPUT_TRI_DATA_FILE = "x:/procdataOut";
const char* OUTPUT_TRI_DATA_FILE_EXT = "bin";
fiStream* pOutFile = ASSET.Create(OUTPUT_TRI_DATA_FILE, OUTPUT_TRI_DATA_FILE_EXT);
if (pOutFile == NULL)
{
return false;
}
const int nNumTris = m_selectedEntries.GetCount();
u32 written = 0;
written = pOutFile->WriteInt(&nNumTris, 1);
Assert(written == 1);
if(nNumTris>0)
{
// total: 36+9+3+3+3+2 = 56 bytes per tri:
for (int i=0; i<nNumTris; i++)
{
CPlantLocTri* pLocTri = m_selectedEntries[i];
// 3x position (3*float (XYZ) = 9 floats/36 bytes):
written = pOutFile->WriteFloat((const float*)&pLocTri->m_V1.x, 3);
Assert(written == 3);
written = pOutFile->WriteFloat((const float*)&pLocTri->m_V2.x, 3);
Assert(written == 3);
written = pOutFile->WriteFloat((const float*)&pLocTri->m_V3.x, 3);
Assert(written == 3);
// 3x RGB ground color data (3*3 bytes = 9 bytes):
u8 rgb1[3], rgb2[3], rgb3[3];
rgb1[0] = pLocTri->m_GroundColorV1.GetRed();
rgb1[1] = pLocTri->m_GroundColorV1.GetGreen();
rgb1[2] = pLocTri->m_GroundColorV1.GetBlue();
rgb2[0] = pLocTri->m_GroundColorV2.GetRed();
rgb2[1] = pLocTri->m_GroundColorV2.GetGreen();
rgb2[2] = pLocTri->m_GroundColorV2.GetBlue();
rgb3[0] = pLocTri->m_GroundColorV3.GetRed();
rgb3[1] = pLocTri->m_GroundColorV3.GetGreen();
rgb3[2] = pLocTri->m_GroundColorV3.GetBlue();
written = pOutFile->WriteByte((const char*)&rgb1[0], 3);
Assert(written == 3);
written = pOutFile->WriteByte((const char*)&rgb2[0], 3);
Assert(written == 3);
written = pOutFile->WriteByte((const char*)&rgb3[0], 3);
Assert(written == 3);
// 3x ScaleXYZ data [0-15] (3 bytes):
u8 scaleXYZ[3];
scaleXYZ[0] = CPlantLocTri::pv8UnpackScaleXYZ(pLocTri->m_GroundColorV1.GetAlpha());
scaleXYZ[1] = CPlantLocTri::pv8UnpackScaleXYZ(pLocTri->m_GroundColorV2.GetAlpha());
scaleXYZ[2] = CPlantLocTri::pv8UnpackScaleXYZ(pLocTri->m_GroundColorV3.GetAlpha());
written = pOutFile->WriteByte((const char*)&scaleXYZ[0], 3);
Assert(written == 3);
// 3x ScaleZ data [0-3] (3 bytes):
u8 scaleZ[3];
scaleZ[0] = CPlantLocTri::pv8UnpackScaleZ(pLocTri->m_GroundColorV1.GetAlpha());
scaleZ[1] = CPlantLocTri::pv8UnpackScaleZ(pLocTri->m_GroundColorV2.GetAlpha());
scaleZ[2] = CPlantLocTri::pv8UnpackScaleZ(pLocTri->m_GroundColorV3.GetAlpha());
written = pOutFile->WriteByte((const char*)&scaleZ[0], 3);
Assert(written == 3);
// 3x Density data [0-3] (3 bytes):
u8 density[3];
density[0] = CPlantLocTri::pv8UnpackDensity(pLocTri->m_GroundColorV1.GetAlpha());
density[1] = CPlantLocTri::pv8UnpackDensity(pLocTri->m_GroundColorV2.GetAlpha());
density[2] = CPlantLocTri::pv8UnpackDensity(pLocTri->m_GroundColorV3.GetAlpha());
written = pOutFile->WriteByte((const char*)&density[0], 3);
Assert(written == 3);
// control data (0xBEEF) (2 bytes):
u8 controlData[2];
controlData[0] = 0xBE;
controlData[1] = 0xEF;
written = pOutFile->WriteByte((const char*)&controlData[0], 2);
Assert(written == 2);
}
}
pOutFile->Close();
pOutFile = NULL;
return true;
}
void CPlantMgrDataEditor::cbBankToggleAllowPicking()
{
gbShowCPlantMgrPolys = gPlantMgrEditor.m_bAllowPicking && gPlantMgrEditor.m_bShowPlantPolys;
if(gPlantMgrEditor.m_bAllowPicking)
{
for(s32 i=0; i<MAX_PROCEDURAL_TAGS; i++)
{
gPlantMgrEditor.m_tagNames[i] = (const char*)g_procInfo.m_procTagTable[i].GetName();
if(gPlantMgrEditor.m_tagNames[i][0]==0)
{
gPlantMgrEditor.m_tagNames[i] = "n/a";
}
gPlantMgrEditor.m_tagNamesCombo->SetString(i, gPlantMgrEditor.m_tagNames[i]);
}
}
else
{
if(gPlantMgrEditor.m_bEdAllCollisionSelectable)
{
gPlantMgrEditor.m_bEdAllCollisionSelectable = false;
cbBankToggleSelectAllCollision();
}
}
}
void CPlantMgrDataEditor::cbBankToggleShowPlantPolys()
{
gbShowCPlantMgrPolys = gPlantMgrEditor.m_bAllowPicking && gPlantMgrEditor.m_bShowPlantPolys;
}
void CPlantMgrDataEditor::cbBankToggleSelectAllCollision()
{
gPlantMgrEditor.m_bEdRegenTriCache = true;
}
void CPlantMgrDataEditor::cbBankButtonCopyPlayerPosToSphereCenter()
{
gPlantMgrEditor.m_searchPos = CGameWorld::FindLocalPlayerCoors();
}
void CPlantMgrDataEditor::cbBankButtonSearchSphere()
{
gPlantMgrEditor.m_bSphereSearchPending = true;
}
void CPlantMgrDataEditor::cbBankButtonClearSelEntries()
{
gPlantMgrEditor.m_bClearSelectedEntries = true;
}
void CPlantMgrDataEditor::cbBankTextEditEntryIdxChanged()
{
gPlantMgrEditor.m_bUpdateCurEditEntry = true;
}
void CPlantMgrDataEditor::cbBankButtonTypeNameChanged()
{
#if 0
s32& type = gPlantMgrEditor.m_editableEntry.m_nSurfaceType;
if(type < PROCEDURAL_TAG_PLANTS_BEGIN)
type = PROCEDURAL_TAG_PLANTS_BEGIN;
else if(type > MAX_PROCEDURAL_TAGS)
type = MAX_PROCEDURAL_TAGS;
#endif
}
void CPlantMgrDataEditor::cbBankButtonSetProcTypeToAll()
{
gPlantMgrEditor.m_bSetProcTypeToAll = true;
}
void CPlantMgrDataEditor::cbBankButtonSaveCurEntry()
{
gPlantMgrEditor.m_bSaveCurEditEntry = true;
}
void CPlantMgrDataEditor::cbBankButtonSaveBatch()
{
gPlantMgrEditor.m_bMaxUiSavingChangesToBatch = true;
}
void CPlantMgrDataEditor::cbBankToggleFindClosestEntriesFromFile()
{
if (gPlantMgrEditor.m_bMaxUiSphereSearchFromFilePending == false)
{
gPlantMgrEditor.m_bMaxUiSphereSearchFromFilePending = gPlantMgrEditor.ReadInputDataFile();
}
}
void CPlantMgrDataEditor::cbBankToggleWriteSelectionToFile()
{
if (gPlantMgrEditor.m_bMaxUiWritingSelectionToFile == false)
{
gPlantMgrEditor.m_bMaxUiWritingSelectionToFile = true;
gPlantMgrEditor.WriteOutputDataFile();
gPlantMgrEditor.m_bMaxUiWritingSelectionToFile = false;
}
}
#endif // PLANTSMGR_DATA_EDITOR...