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

5761 lines
203 KiB
C++

//
// PlantsGrassRenderer - contains lowlevel rendering classes for CPlantMgr: CGrassRenderer & CPPTriPlantBuffer;
//
// 30/06/2005 - Andrzej: - initial port from SA to Rage;
// 14/07/2005 - Andrzej: - port to Rage fully working;
// 02/02/2007 - Andrzej: - support for deferred mode added;
// 24/05/2007 - Andrzej: - support for SPU renderer added;
// 22/06/2007 - Andrzej: - SPU: v2.0 master job system; sphere test for individual plants added;
// 22/11/2007 - Andrzej: - support for SPU_GCM_FIFO and CBs in VRAM added;
// ---------- - -------- ------------------------------------------------------------------------
// 08/07/2008 - Andrzej: - PlantsMgr 2.0: code cleanup, refactor, instancing used by default;
// 14/07/2008 - Andrzej: - custom instanced rendering added (DL_RENDERPHASE_GBUG faster by over 1ms on CPU);
// 16/07/2008 - Andrzej: - PlantsMgr 2.0: micro-movements support added (local micro-movements scales are encoded in vertex colors);
// 10/11/2008 - Andrzej: - PlantsMgr 2.0: OPTIMIZED_VS_GRASS_SETUP added (only really required matrix regs are set into CB);
// 26/11/2008 - Andrzej: - PlantsMgr 2.0: SpuRenderloop 2.0 and code cleanup (grassModelSPU4 removed, etc.);
// 12/12/2008 - Andrzej: - PlantsMgr 2.0: SpuRenderloop 2.0: big buffer size optimizations
// (using CALLs to bind shaders, textures and geometries instead of copying whole buffers);
// 26/04/2011 - Andrzej: - PlantsMgr 2.0: spuTriPlantBlockTab and BigHeap dynamically allocated from streaming heap;
//
//
//
//
//
//
//
// Rage headers
#include "grmodel/shader.h"
#include "rmcore/drawable.h"
#include "grcore/im.h"
#include "grmodel/geometry.h"
#include "grcore/grcorespu.h"
#include "rmcore/Instance.h"
#include "system/task.h"
#include "grcore/indexbuffer.h"
#include "grcore/texturegcm.h"
#include "grcore/vertexbuffer.h"
#include "grcore/wrapper_gcm.h"
#include "physics/inst.h"
#include "phbound/boundgeom.h"
#include "profile/timebars.h"
#include "system/findsize.h"
// Framework headers
#include "fwmaths\random.h"
#include "fwmaths\vector.h"
#include "fwsys\timer.h"
// Game headers
#include "scene\world\gameWorld.h"
#include "debug\debug.h"
#include "system\UseTuner.h"
#include "renderer\Renderer.h"
#include "renderer\Lights\lights.h"
#include "Objects\ProceduralInfo.h" // PROCPLANT_xxx
#include "Renderer\PlantsGrassRenderer.h"
#include "..\shader_source\Vegetation\Grass\grass_regs.h"
#if PLANTS_USE_LOD_SETTINGS
#include "renderer\PlantsGrassRenderer_parser.h"
#endif
#if RSG_ORBIS
#include "grcore/gfxcontext_gnm.h"
#endif
extern mthRandom g_PlantsRendRand[NUMBER_OF_RENDER_THREADS];
extern CPlantMgr gPlantMgr;
RENDER_OPTIMISATIONS();
//
//
// if 1, then commandbuffers are in VRAM:
//
#define GCMFIFO_IN_VRAM (0)
//
// few helper allocators to access & map memory,
// depending where CBs are located:
//
#if GCMFIFO_IN_VRAM
#define PLANTS_MALLOC(SIZE,ALIGN) physical_new((SIZE),(ALIGN))
#define PLANTS_FREE(P) delete [] (P)
#define PLANTS_GCM_OFFSET gcm::LocalOffset
#else
#define PLANTS_MALLOC(SIZE,ALIGN) rage_aligned_new((ALIGN)) u8[(SIZE)]
#define PLANTS_FREE(P) delete [] ((u8*)(P))
#define PLANTS_GCM_OFFSET gcm::MainOffset
#endif
#define GtaMallocAlign(SIZE, ALIGN) rage_aligned_new((ALIGN)) u8[(SIZE)]
#define GtaFreeAlign(P) delete [] ((u8*)(P))
#define GtaMallocVRamAlign(SIZE,ALIGN) physical_new((SIZE),(ALIGN))
#define GtaFreeVRamAlign(P) delete [] (P)
#define ASSERT16(P) Assert((u32(P)&0x0F)==0)
#define ASSERT128(P) Assert((u32(P)&0x7F)==0)
#if PSN_PLANTSMGR_SPU_RENDER
// game includes:
#include "PlantsGrassRendererSPU.h"
#include "grcore\wrapper_gcm.h"
using namespace cell::Gcm;
#include "grcore\indexbuffer.h"
#include "grcore\vertexbuffer.h"
#include "grmodel\modelfactory.h"
// default alignment for command buffers in Main memory:
#define PLANTSMGR_CMD_DEF_ALIGN (16) // 128 align is not required, as SPU doesn't touch this
u32 CGrassRenderer::sm_nNumGrassJobsAdded = 0;
u32 CGrassRenderer::sm_nNumGrassJobsAddedTotal = 0;
sysTaskHandle CGrassRenderer::sm_GrassTaskHandle = NULL;
u32 CGrassRenderer::sm_BigHeapID = 0;
#if PLANTSMGR_BIG_HEAP_IN_BLOCKS
grassHeapBlock CGrassRenderer::sm_BigHeapBlockArray[2][PLANTSMGR_BIG_HEAP_BLK_ARRAY_SIZE] ={0};
s16 CGrassRenderer::sm_BigHeapBlockArrayCount[2] = {0};
#else
u32* CGrassRenderer::sm_BigHeap[2] = {NULL};
u32 CGrassRenderer::sm_BigHeapOffset[2] = {0};
u32 CGrassRenderer::sm_BigHeapSize[2] = {0};
#endif
u32 CGrassRenderer::sm_BigHeapAllocatedSize[2] = {0};
u32 CGrassRenderer::sm_BigHeapConsumed[2] = {0};
u32 CGrassRenderer::sm_BigHeapBiggestConsumed[2] = {0};
u32 CGrassRenderer::sm_BigHeapOverfilled[2] = {0};
#if SPU_GCM_FIFO
#define STARTUP_HEAP_SIZE (256) // 256 bytes
u32* CGrassRenderer::sm_StartupHeapPtr[2] = {NULL};
u32 CGrassRenderer::sm_StartupHeapOffset[2] = {0};
#endif
u32* CGrassRenderer::gpShaderLOD0Cmd0[2]={NULL}; // BindShaderLOD0
u32* CGrassRenderer::gpShaderLOD1Cmd0[2]={NULL}; // BindShaderLOD1
u32* CGrassRenderer::gpShaderLOD2Cmd0[2]={NULL}; // BindShaderLOD2
u32 CGrassRenderer::gpShaderLOD0Offset[2]={0};
u32 CGrassRenderer::gpShaderLOD1Offset[2]={0};
u32 CGrassRenderer::gpShaderLOD2Offset[2]={0};
u32* CGrassRenderer::gpPlantLOD2GeometryCmd0=NULL;
u32 CGrassRenderer::gpPlantLOD2GeometryOffset=0;
u32 CGrassRenderer::rsxLabel5Index = u32(-1); // indices of RSX label1
volatile u32* CGrassRenderer::rsxLabel5Ptr = NULL; // RSX: lock/unlock() buf1
u32 CGrassRenderer::rsxLabel5_CurrentTaskID = 0;// every next task waits for bigger value, so many SPU tasks can be synchronized during the same frame
// private aligned PPTriPlant space for SPU job to dma from:
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
// single buffer of PPTriPlant[size] must fit into 4KB block
CompileTimeAssert(sizeof(PPTriPlant)*PPTRIPLANT_BUFFER_SIZE <= PLANTSMGR_TRIPLANT_BLOCK_SIZE);
PPTriPlant* CGrassRenderer::spuTriPlantBlockTab[PLANTSMGR_MAX_NUM_OF_RENDER_JOBS]={0};
s32 CGrassRenderer::spuTriPlantBlockTabCount = 0;
#else
PPTriPlant CGrassRenderer::spuTriPlantTab[PLANTSMGR_MAX_NUM_OF_RENDER_JOBS][PPTRIPLANT_BUFFER_SIZE] ALIGNED(128);
#endif
CompileTimeAssert(sizeof(PPTriPlant)==PSN_SIZEOF_PPTRIPLANT);
CompileTimeAssert(sizeof(PPTriPlant)*PPTRIPLANT_BUFFER_SIZE <= 4*1024); // must fit into 4KB
//FindSize(PPTriPlant);
spuGrassParamsStruct CGrassRenderer::inSpuGrassStructTab[PLANTSMGR_MAX_NUM_OF_RENDER_JOBS] ALIGNED(128);
CompileTimeAssert(sizeof(grassModel) == PSN_SIZEOF_GRASSMODEL);
//FindSize(grassModel);
CompileTimeAssert(sizeof(spuGrassParamsStruct) == PSN_SIZEOF_SPUGRASSPARAMSTRUCT);
//FindSize(spuGrassParamsStruct);
#endif //PSN_PLANTSMGR_SPU_RENDER...
#if RSG_DURANGO && GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
PARAM(grassInstanceCBSize, "Size of the instanced constant buffer for grass" );
#endif // RSG_DURANGO && GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
// 360: Leaving it disabled for now, because this doesn't help much on Xenos for some reason
// and total GPU execution time/CB size is longer/bigger when enabled (?!?);
// probably D3D does similiar caching tricks under the hood when calling SetVertexShaderConstant();
//
#define OPTIMIZED_VS_GRASS_SETUP (1 && __PPU)
//
//
//
//
#if 0
struct instGrassDataDrawStruct
{
Matrix44 m_WorldMatrix;
#if GRASS_INSTANCING
Color32 m_PlantColor32;
Color32 m_GroundColorAmbient32;
#else
Vector4 m_PlantColor;
Vector4 m_GroundColor;
#endif
///////////////////////////////////////////////////////
#if OPTIMIZED_VS_GRASS_SETUP
Matrix34 m_prevWorldMatrix;
#endif
public:
inline void Reset() {
#if OPTIMIZED_VS_GRASS_SETUP
m_prevWorldMatrix.a =
m_prevWorldMatrix.b =
m_prevWorldMatrix.c =
m_prevWorldMatrix.d = Vector3(-1,-1,-1);
#endif //OPTIMIZED_VS_GRASS_SETUP...
}
#if OPTIMIZED_VS_GRASS_SETUP
inline bool UseOptimizedVsSetup(const Matrix34& m34)
{
const bool bDifferent = m_prevWorldMatrix.a.IsNotEqual(m34.a) ||
m_prevWorldMatrix.b.IsNotEqual(m34.b) ||
m_prevWorldMatrix.c.IsNotEqual(m34.c);
if(bDifferent)
{
m_prevWorldMatrix.a = m34.a;
m_prevWorldMatrix.b = m34.b;
m_prevWorldMatrix.c = m34.c;
return(FALSE);
}
return(TRUE);
}
#else //OPTIMIZED_VS_GRASS_SETUP
inline bool UseOptimizedVsSetup(const Matrix34& ) {return(FALSE);}
#endif //OPTIMIZED_VS_GRASS_SETUP
#if GRASS_INSTANCING
// n/a
#else
// full setup: 6 registers
inline s32 GetRegisterCountFull() const { return( (sizeof(m_WorldMatrix)/16) + (sizeof(m_PlantColor)/16) + (sizeof(m_GroundColor)/16) );}
inline const float* GetRegisterPointerFull() const { return( (const float*)&m_WorldMatrix ); }
// optimized setup: 3 registers (only position from matrix is taken):
inline s32 GetRegisterCountOpt() const { return( (sizeof(m_WorldMatrix.d)/16) + (sizeof(m_PlantColor)/16) + (sizeof(m_GroundColor)/16) );}
inline const float* GetRegisterPointerOpt() const { return( (const float*)&m_WorldMatrix.d ); }
#endif
};
#endif
//
//
//
//
Vector3 CGrassRenderer::ms_vecCameraPos[2] = {Vector3(0,0,0)};
Vector3 CGrassRenderer::ms_vecCameraFront[2] = {Vector3(0,0,0)};
bool CGrassRenderer::ms_cameraFppEnabled[2] = {false, false};
bool CGrassRenderer::ms_cameraUnderwater[2] = {false, false};
bool CGrassRenderer::ms_forceHdGrassGeom[2] = {false, false};
Vector3 CGrassRenderer::ms_vecPlayerPos[2] = {Vector3(0,0,0)};
float CGrassRenderer::ms_fPlayerCollRSqr[2] = {1.0f, 1.0f};
spdTransposedPlaneSet8 CGrassRenderer::ms_cullFrustum[2];
bool CGrassRenderer::ms_interiorCullAll[2] = {false};
Vector2 CGrassRenderer::ms_windBending[2] = {Vector2(0.0f, 0.0f)};
Vector3 CGrassRenderer::ms_fakeGrassNormal[2] = {Vector3(0,0,1)};
#if __BANK
bool CGrassRenderer::ms_enableDebugDraw[2] = {false};
#endif
#if FURGRASS_TEST_V4
Vector4 CGrassRenderer::ms_vecPlayerLFootPos[2] = {Vector4(0,0,0,0)};
Vector4 CGrassRenderer::ms_vecPlayerRFootPos[2] = {Vector4(0,0,0,0)};
#endif
bool CGrassRenderer::ms_bVehCollisionEnabled[2][NUMBER_OF_RENDER_THREADS][NUM_COL_VEH] = {FALSE};
Vector4 CGrassRenderer::ms_vecVehCollisionB[2][NUMBER_OF_RENDER_THREADS][NUM_COL_VEH] = {Vector4(0,0,0,0)};
Vector4 CGrassRenderer::ms_vecVehCollisionM[2][NUMBER_OF_RENDER_THREADS][NUM_COL_VEH] = {Vector4(0,0,0,0)};
Vector4 CGrassRenderer::ms_vecVehCollisionR[2][NUMBER_OF_RENDER_THREADS][NUM_COL_VEH] = {Vector4(0,0,0,0)};
grcVertexBuffer* CGrassRenderer::ms_plantLOD2VertexBuffer = NULL;
grcVertexDeclaration* CGrassRenderer::ms_plantLOD2VertexDecl = NULL;
static
CPPTriPlantBuffer PPTriPlantBuffer;
//
//
//
//
grmShaderGroup* CGrassRenderer::ms_ShaderGroup = NULL;
grmShader* CGrassRenderer::ms_Shader = NULL;
grcEffectVar CGrassRenderer::ms_shdTextureID = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdGBuf0TextureID = grcevNONE;
#if PSN_PLANTSMGR_SPU_RENDER
u32 CGrassRenderer::ms_shdTextureTexUnit = (u32)-1;
#endif
grcEffectVar CGrassRenderer::ms_grassRegTransform = grcevNONE;
grcEffectVar CGrassRenderer::ms_grassRegPlantCol = grcevNONE;
grcEffectVar CGrassRenderer::ms_grassRegGroundCol = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdCameraPosID = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdPlayerPosID = grcevNONE;
grcEffectGlobalVar CGrassRenderer::ms_shdVehCollisionEnabledID[NUM_COL_VEH]= {grcegvNONE};
grcEffectVar CGrassRenderer::ms_shdVehCollisionBID[NUM_COL_VEH] = {grcevNONE};
grcEffectVar CGrassRenderer::ms_shdVehCollisionMID[NUM_COL_VEH] = {grcevNONE};
grcEffectVar CGrassRenderer::ms_shdVehCollisionRID[NUM_COL_VEH] = {grcevNONE};
grcEffectVar CGrassRenderer::ms_shdFadeAlphaDistUmTimerID= grcevNONE;
grcEffectVar CGrassRenderer::ms_shdFadeAlphaLOD1DistID = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdFadeAlphaLOD2DistID = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdFadeAlphaLOD2DistFarID= grcevNONE;
#if !PSN_PLANTSMGR_SPU_RENDER
grcEffectVar CGrassRenderer::ms_shdPlantColorID = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdUMovementParamsID = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdDimensionLOD2ID = grcevNONE;
grcEffectVar CGrassRenderer::ms_shdCollParamsID = grcevNONE;
#endif
grcEffectVar CGrassRenderer::ms_shdFakedGrassNormal = grcevNONE;
#if CPLANT_WRITE_GRASS_NORMAL
grcEffectVar CGrassRenderer::ms_shdTerrainNormal = grcevNONE;
#endif
#if DEVICE_MSAA
grcEffectVar CGrassRenderer::ms_shdAlphaToCoverageScale = grcevNONE;
#endif
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD0TechniqueID= grcetNONE;
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD1TechniqueID= grcetNONE;
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD2TechniqueID= grcetNONE;
#if __BANK
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD0DbgTechniqueID= grcetNONE;
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD1DbgTechniqueID= grcetNONE;
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD2DbgTechniqueID= grcetNONE;
#endif
grcEffectTechnique CGrassRenderer::ms_shdBlitFakedGBufID = grcetNONE;
#if !PSN_PLANTSMGR_SPU_RENDER
DECLARE_MTR_THREAD grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD0TechniqueID_ToUse= grcetNONE;
DECLARE_MTR_THREAD grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD1TechniqueID_ToUse= grcetNONE;
DECLARE_MTR_THREAD grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD2TechniqueID_ToUse= grcetNONE;
spdTransposedPlaneSet8 CGrassRenderer::ms_cullingFrustum[NUMBER_OF_RENDER_THREADS];
#endif
#if GRASS_INSTANCING
CGrassRenderer::InstanceBucket CGrassRenderer::ms_instanceBucketsLOD0[CGRASS_RENDERER_LOD_BUCKET_SIZE];
CGrassRenderer::InstanceBucket CGrassRenderer::ms_instanceBucketsLOD1[CGRASS_RENDERER_LOD_BUCKET_SIZE];
CGrassRenderer::InstanceBucket_LOD2 CGrassRenderer::ms_instanceBucketsLOD2[CGRASS_RENDERER_LOD_BUCKET_SIZE];
CGrassRenderer::LODBucket < CGRASS_RENDERER_LOD_BUCKET_SIZE > CGrassRenderer::ms_LOD0Bucket;
CGrassRenderer::LODBucket < CGRASS_RENDERER_LOD_BUCKET_SIZE > CGrassRenderer::ms_LOD1Bucket;
CGrassRenderer::LODBucket < CGRASS_RENDERER_LOD_BUCKET_SIZE > CGrassRenderer::ms_LOD2Bucket;
#endif // GRASS_INSTANCING
#if PLANTS_CAST_SHADOWS
#if __BANK
bool CGrassRenderer::ms_drawShadows = true;
#endif
grcEffectGlobalVar CGrassRenderer::ms_depthValueBias = grcegvNONE;
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD0ShadowTechniqueID = grcetNONE;
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD1ShadowTechniqueID = grcetNONE;
grcEffectTechnique CGrassRenderer::ms_shdDeferredLOD2ShadowTechniqueID = grcetNONE;
CGrassShadowParams CGrassRenderer::ms_currentShadowEnvelope;
#if PLANTS_USE_LOD_SETTINGS
CGrassShadowParams_AllLODs CGrassRenderer::ms_shadowEnvelope_AllLods;
#endif
#endif //PLANTS_CAST_SHADOWS
#if PLANTS_USE_LOD_SETTINGS
CGrassScalingParams CGrassRenderer::ms_currentScalingParams;
CGrassScalingParams_AllLODs CGrassRenderer::ms_scalingParams_AllLods;
#endif //PLANTS_USE_LOD_SETTINGS
//
//
//
//
CGrassRenderer::CGrassRenderer()
{
// do nothing
}
//
//
//
//
CGrassRenderer::~CGrassRenderer()
{
// do nothing
}
//
//
//
//
bool CGrassRenderer::Initialise()
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
sysMemUseMemoryBucket b(MEMBUCKET_RENDER);
//
// shader stuff:
//
ms_Shader = grmShaderFactory::GetInstance().Create();
Assert(ms_Shader);
ms_ShaderGroup = grmShaderFactory::GetInstance().CreateGroup();
Assert(ms_ShaderGroup);
if(!ms_Shader->Load("grass"))
{
Assertf(FALSE, "Error loading 'grass' shader!");
}
ms_ShaderGroup->InitCount(1);
ms_ShaderGroup->Add(ms_Shader);
ms_shdDeferredLOD0TechniqueID = ms_Shader->LookupTechnique("deferredLOD0_draw", TRUE);
Assert(ms_shdDeferredLOD0TechniqueID);
ms_shdDeferredLOD1TechniqueID = ms_Shader->LookupTechnique("deferredLOD1_draw", TRUE);
Assert(ms_shdDeferredLOD1TechniqueID);
ms_shdDeferredLOD2TechniqueID = ms_Shader->LookupTechnique("deferredLOD2_draw", TRUE);
Assert(ms_shdDeferredLOD2TechniqueID);
#if __BANK
ms_shdDeferredLOD0DbgTechniqueID = ms_Shader->LookupTechnique("deferredLOD0_dbgdraw", TRUE);
Assert(ms_shdDeferredLOD0DbgTechniqueID);
ms_shdDeferredLOD1DbgTechniqueID = ms_Shader->LookupTechnique("deferredLOD1_dbgdraw", TRUE);
Assert(ms_shdDeferredLOD1DbgTechniqueID);
ms_shdDeferredLOD2DbgTechniqueID = ms_Shader->LookupTechnique("deferredLOD2_dbgdraw", TRUE);
Assert(ms_shdDeferredLOD2DbgTechniqueID);
#endif
ms_shdBlitFakedGBufID = ms_Shader->LookupTechnique("blitFakedGBuf", TRUE);
Assert(ms_shdBlitFakedGBufID);
ms_shdTextureID = ms_Shader->LookupVar("grassTexture0", TRUE);
Assert(ms_shdTextureID);
#if !PSN_PLANTSMGR_SPU_RENDER
ms_shdDeferredLOD0TechniqueID_ToUse = ms_shdDeferredLOD0TechniqueID;
ms_shdDeferredLOD1TechniqueID_ToUse = ms_shdDeferredLOD1TechniqueID;
ms_shdDeferredLOD2TechniqueID_ToUse = ms_shdDeferredLOD2TechniqueID;
#endif
#if CPLANT_STORE_LOCTRI_NORMAL
ms_shdGBuf0TextureID = ms_Shader->LookupVar("gbufferTexture0", TRUE);
Assert(ms_shdGBuf0TextureID);
#endif
#if PSN_PLANTSMGR_SPU_RENDER
// grab texUnit from shader's instanceData:
const grcInstanceData& instanceData = ms_Shader->GetInstanceData();
const int index = (int)ms_shdTextureID - 1;
grcInstanceData::Entry *pEntry = &instanceData.Data()[index];
//plantsDebugf1("\n CGrassRenderer::Initialise: grassTexture0: count=0x%02X, reg=0x%02X, handle=0x%02X, tex=0x%X", pEntry->Count, pEntry->Register, pEntry->Handle, (u32)pEntry->Texture);
ms_shdTextureTexUnit = pEntry->Register;
#endif //PSN_PLANTSMGR_SPU_RENDER...
ms_grassRegTransform = ms_Shader->LookupVar("matGrassTransform", TRUE);
Assert(ms_grassRegTransform );
ms_grassRegPlantCol = ms_Shader->LookupVar("plantColor", TRUE);
Assert(ms_grassRegPlantCol);
ms_grassRegGroundCol = ms_Shader->LookupVar("groundColor", TRUE);
Assert(ms_grassRegGroundCol);
ms_shdCameraPosID = ms_Shader->LookupVar("vecCameraPos0", TRUE);
Assert(ms_shdCameraPosID);
ms_shdPlayerPosID = ms_Shader->LookupVar("vecPlayerPos0", TRUE);
Assert(ms_shdPlayerPosID);
for(u32 i=0; i<NUM_COL_VEH; i++)
{
char var_name[128];
sprintf(var_name, "bVehColl%dEnabled0", i); // "bVehColl0Enabled0"
ms_shdVehCollisionEnabledID[i] = grcEffect::LookupGlobalVar(var_name, TRUE);
Assert(ms_shdVehCollisionEnabledID[i]);
sprintf(var_name, "vecVehColl%dB0", i); // "vecVehColl0B0"
ms_shdVehCollisionBID[i] = ms_Shader->LookupVar(var_name, TRUE);
Assert(ms_shdVehCollisionBID[i]);
sprintf(var_name, "vecVehColl%dM0", i); // "vecVehColl0M0"
ms_shdVehCollisionMID[i] = ms_Shader->LookupVar(var_name, TRUE);
Assert(ms_shdVehCollisionMID[i]);
sprintf(var_name, "vecVehColl%dR0", i); // "vecVehColl0R0"
ms_shdVehCollisionRID[i] = ms_Shader->LookupVar(var_name, TRUE);
Assert(ms_shdVehCollisionRID[i]);
}
ms_shdFadeAlphaDistUmTimerID = ms_Shader->LookupVar("fadeAlphaDistUmTimer0", TRUE);
Assert(ms_shdFadeAlphaDistUmTimerID);
ms_shdFadeAlphaLOD1DistID = ms_Shader->LookupVar("fadeAlphaLOD1Dist0", TRUE);
Assert(ms_shdFadeAlphaLOD1DistID);
ms_shdFadeAlphaLOD2DistID = ms_Shader->LookupVar("fadeAlphaLOD2Dist0", TRUE);
Assert(ms_shdFadeAlphaLOD2DistID);
ms_shdFadeAlphaLOD2DistFarID = ms_Shader->LookupVar("fadeAlphaLOD2DistFar0", TRUE);
Assert(ms_shdFadeAlphaLOD2DistFarID);
#if !PSN_PLANTSMGR_SPU_RENDER
ms_shdCollParamsID = ms_Shader->LookupVar("vecCollParams0", TRUE);
Assert(ms_shdCollParamsID);
ms_shdPlantColorID = ms_Shader->LookupVar("plantColor0");
Assert(ms_shdPlantColorID);
ms_shdUMovementParamsID = ms_Shader->LookupVar("uMovementParams0", TRUE);
Assert(ms_shdUMovementParamsID);
ms_shdDimensionLOD2ID = ms_Shader->LookupVar("dimensionLOD20", TRUE);
Assert(ms_shdDimensionLOD2ID);
#endif
ms_shdFakedGrassNormal = ms_Shader->LookupVar("fakedGrassNormal0", TRUE);
Assert(ms_shdFakedGrassNormal);
#if CPLANT_WRITE_GRASS_NORMAL
ms_shdTerrainNormal = ms_Shader->LookupVar("terrainNormal0", TRUE);
Assert(ms_shdTerrainNormal);
#endif
#if DEVICE_MSAA
ms_shdAlphaToCoverageScale = ms_Shader->LookupVar("gAlphaToCoverageScale", false);
#endif
#if PSN_PLANTSMGR_SPU_RENDER
Assert(SPU_GCM_FIFO >= 1); // always make sure we have SPU_GCM_FIFO
PPTriPlantBuffer.AllocateTextureBuffers();
PPTriPlantBuffer.AllocateModelsBuffers();
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
s32 nTriPlantBytesToAlloc = PLANTSMGR_TRIPLANTTAB_SIZE0;
while(nTriPlantBytesToAlloc > 0)
{
const u32 i = spuTriPlantBlockTabCount;
spuTriPlantBlockTab[i] = (PPTriPlant*)PLANTS_MALLOC_STR(PLANTSMGR_TRIPLANT_BLOCK_SIZE, 128);
Assert(spuTriPlantBlockTab[i]);
ASSERT128(spuTriPlantBlockTab[i]);
spuTriPlantBlockTabCount++;
Assertf(spuTriPlantBlockTabCount <= PLANTSMGR_MAX_NUM_OF_RENDER_JOBS, "spuTriPlantBlockTabCount exceeded!");
nTriPlantBytesToAlloc -= PLANTSMGR_TRIPLANT_BLOCK_SIZE;
}
#endif
#if PLANTSMGR_BIG_HEAP_IN_BLOCKS
// allocate 768KB in chunks of 8KB:
s32 nHeapBytesToAlloc = PLANTSMGR_BIG_HEAP_SIZE0;
while(nHeapBytesToAlloc > 0)
{
const u32 b0 = sm_BigHeapBlockArrayCount[0];
const u32 b1 = sm_BigHeapBlockArrayCount[1];
sm_BigHeapBlockArray[0][b0].ea = (u32*)PLANTS_MALLOC_STR(PLANTSMGR_LOCAL_HEAP_SIZE, 128);
sm_BigHeapBlockArray[0][b0].offset = PLANTS_GCM_OFFSET(sm_BigHeapBlockArray[0][b0].ea);
sm_BigHeapBlockArray[1][b1].ea = (u32*)PLANTS_MALLOC_STR(PLANTSMGR_LOCAL_HEAP_SIZE, 128);
sm_BigHeapBlockArray[1][b1].offset = PLANTS_GCM_OFFSET(sm_BigHeapBlockArray[1][b1].ea);
Assert(sm_BigHeapBlockArray[0][b0].ea);
Assert(sm_BigHeapBlockArray[1][b1].ea);
ASSERT128(sm_BigHeapBlockArray[0][b0].ea);
ASSERT128(sm_BigHeapBlockArray[1][b1].ea);
sm_BigHeapBlockArrayCount[0]++;
sm_BigHeapBlockArrayCount[1]++;
Assertf(sm_BigHeapBlockArrayCount[0] <= PLANTSMGR_BIG_HEAP_BLK_ARRAY_SIZE, "sm_BigHeapBlockArrayCount[0] exceeded!");
Assertf(sm_BigHeapBlockArrayCount[1] <= PLANTSMGR_BIG_HEAP_BLK_ARRAY_SIZE, "sm_BigHeapBlockArrayCount[1] exceeded!");
nHeapBytesToAlloc -= PLANTSMGR_LOCAL_HEAP_SIZE;
}
#else
sm_BigHeapSize[0] =
sm_BigHeapSize[1] = PLANTSMGR_BIG_HEAP_SIZE0;
sm_BigHeap[0] = (u32*)PLANTS_MALLOC_STR(sm_BigHeapSize[0], 128);
Assert(sm_BigHeap[0]);
sm_BigHeap[1] = (u32*)PLANTS_MALLOC_STR(sm_BigHeapSize[1], 128);
Assert(sm_BigHeap[1]);
sm_BigHeapOffset[0] = PLANTS_GCM_OFFSET(sm_BigHeap[0]);
sm_BigHeapOffset[1] = PLANTS_GCM_OFFSET(sm_BigHeap[1]);
Assert(sm_BigHeap[0]);
Assert(sm_BigHeap[1]);
ASSERT128(sm_BigHeap[0]);
ASSERT128(sm_BigHeap[1]);
plantsDebugf1("RenderSPU: BigHeap0=%X, BigHeapOffset0=%X", (u32)sm_BigHeap[0], (u32)sm_BigHeapOffset[0]);
plantsDebugf1("RenderSPU: BigHeap1=%X, BigHeapOffset1=%X", (u32)sm_BigHeap[1], (u32)sm_BigHeapOffset[1]);
#endif
#if SPU_GCM_FIFO
sm_StartupHeapPtr[0] = (u32*)PLANTS_MALLOC(STARTUP_HEAP_SIZE, 128);
sm_StartupHeapPtr[1] = (u32*)PLANTS_MALLOC(STARTUP_HEAP_SIZE, 128);
sm_StartupHeapOffset[0] = PLANTS_GCM_OFFSET(sm_StartupHeapPtr[0]);
sm_StartupHeapOffset[1] = PLANTS_GCM_OFFSET(sm_StartupHeapPtr[1]);
Assert(sm_StartupHeapPtr[0]);
Assert(sm_StartupHeapPtr[1]);
ASSERT128(sm_StartupHeapPtr[0]);
ASSERT128(sm_StartupHeapPtr[1]);
#endif //SPU_GCM_FIFO...
// Map the main memory into io offset space so it can be viewed by the gpu
// if( cellGcmMapMainMemory(mHeaps[0], HEAP_SIZE, &mHeapOffsets[0]) != CELL_OK)
// plantsErrorf("Failure in cellGcmMapMainMemory\n");
// if( cellGcmMapMainMemory(mHeaps[1], HEAP_SIZE, &mHeapOffsets[1]) != CELL_OK)
// plantsErrorf("Failure in cellGcmMapMainMemory\n");
gpShaderLOD0Cmd0[0] = (u32*)PLANTS_MALLOC(PLANTSMGR_LOCAL_BINDSHADERLOD0_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
Assert(gpShaderLOD0Cmd0[0]);
gpShaderLOD0Cmd0[1] = (u32*)PLANTS_MALLOC(PLANTSMGR_LOCAL_BINDSHADERLOD0_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
Assert(gpShaderLOD0Cmd0[1]);
gpShaderLOD1Cmd0[0] = (u32*)PLANTS_MALLOC(PLANTSMGR_LOCAL_BINDSHADERLOD1_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
Assert(gpShaderLOD1Cmd0[0]);
gpShaderLOD1Cmd0[1] = (u32*)PLANTS_MALLOC(PLANTSMGR_LOCAL_BINDSHADERLOD1_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
Assert(gpShaderLOD1Cmd0[1]);
gpShaderLOD2Cmd0[0] = (u32*)PLANTS_MALLOC(PLANTSMGR_LOCAL_BINDSHADERLOD2_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
Assert(gpShaderLOD2Cmd0[0]);
gpShaderLOD2Cmd0[1] = (u32*)PLANTS_MALLOC(PLANTSMGR_LOCAL_BINDSHADERLOD2_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
Assert(gpShaderLOD2Cmd0[1]);
gpShaderLOD0Offset[0] =PLANTS_GCM_OFFSET(gpShaderLOD0Cmd0[0]);
Assert(gpShaderLOD0Offset[0]);
gpShaderLOD0Offset[1] =PLANTS_GCM_OFFSET(gpShaderLOD0Cmd0[1]);
Assert(gpShaderLOD0Offset[1]);
gpShaderLOD1Offset[0] =PLANTS_GCM_OFFSET(gpShaderLOD1Cmd0[0]);
Assert(gpShaderLOD1Offset[0]);
gpShaderLOD1Offset[1] =PLANTS_GCM_OFFSET(gpShaderLOD1Cmd0[1]);
Assert(gpShaderLOD1Offset[1]);
gpShaderLOD2Offset[0] =PLANTS_GCM_OFFSET(gpShaderLOD2Cmd0[0]);
Assert(gpShaderLOD2Offset[0]);
gpShaderLOD2Offset[1] =PLANTS_GCM_OFFSET(gpShaderLOD2Cmd0[1]);
Assert(gpShaderLOD2Offset[1]);
gpPlantLOD2GeometryCmd0 = (u32*)PLANTS_MALLOC(PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
Assert(gpPlantLOD2GeometryCmd0);
gpPlantLOD2GeometryOffset = PLANTS_GCM_OFFSET(gpPlantLOD2GeometryCmd0);
Assert(gpPlantLOD2GeometryOffset);
// allocate RSX labels:
rsxLabel5Index = gcm::RsxSemaphoreRegistrar::Allocate(); // index of RSX label1
rsxLabel5Ptr = cellGcmGetLabelAddress(rsxLabel5Index); // ptr of the label in RSX memory
Assert(rsxLabel5Ptr);
ASSERT16(rsxLabel5Ptr); // enough to be 16 byte aligned for cellDmaSmallGet/Put()
plantsDebugf1("RenderSPU: Label5Index=%d (0x%X), ptr=0x%p", rsxLabel5Index, rsxLabel5Index, rsxLabel5Ptr);
*(rsxLabel5Ptr) = 0xFF000000|(((sm_BigHeapID-1)&0x1)<<8)|((rsxLabel5_CurrentTaskID-1)&0xFF); // prevTaskID1
#endif //PSN_PLANTSMGR_SPU_RENDER...
#if PLANTS_USE_LOD_SETTINGS
// Load the data in.
PARSER.LoadObject("common:/data/GrassLODSettings", "xml", CGrassRenderer::ms_scalingParams_AllLods);
#if PLANTS_CAST_SHADOWS
PARSER.LoadObject("common:/data/GrassShadowLODSettings", "xml", CGrassRenderer::ms_shadowEnvelope_AllLods);
#endif
// Say we`re initialized.
ms_isInitialized = true;
// Set up initial plant LOD.
SetPlantLODToUse((CGrassRenderer_PlantLod)ms_LODToInitialiseWith);
#if __BANK
// Update the one being edited.
ms_LODBeingEdited = ms_LODToInitialiseWith;
ms_LODNextToEdit = ms_LODToInitialiseWith;
#endif
#endif //PLANTS_USE_LOD_SETTINGS
#if PLANTS_CAST_SHADOWS
ms_shdDeferredLOD0ShadowTechniqueID = ms_Shader->LookupTechnique("deferredLOD0_drawshadow", TRUE);
ms_shdDeferredLOD1ShadowTechniqueID = ms_Shader->LookupTechnique("deferredLOD1_drawshadow", TRUE);
ms_shdDeferredLOD2ShadowTechniqueID = ms_Shader->LookupTechnique("deferredLOD2_drawshadow", TRUE);
ms_depthValueBias = grcEffect::LookupGlobalVar("depthValueBias0", TRUE);
SetDepthBiasOff();
#endif //PLANTS_CAST_SHADOWS
#if GRASS_INSTANCING
u32 i;
for(i=0; i<CGRASS_RENDERER_LOD_BUCKET_SIZE; i++)
{
ms_instanceBucketsLOD0[i].Init(ms_Shader, ms_shdTextureID);
ms_instanceBucketsLOD1[i].Init(ms_Shader, ms_shdTextureID);
ms_instanceBucketsLOD2[i].Init(ms_Shader, ms_shdTextureID);
ms_LOD0Bucket.SetBucket(i, &ms_instanceBucketsLOD0[i]);
ms_LOD1Bucket.SetBucket(i, &ms_instanceBucketsLOD1[i]);
ms_LOD2Bucket.SetBucket(i, &ms_instanceBucketsLOD2[i]);
}
#endif // GRASS_INSTANCING
return(TRUE);
} // end of Initialise()...
//
//
//
//
grmShaderGroup* CGrassRenderer::GetGrassShaderGroup()
{
return(ms_ShaderGroup);
}
//
//
//
//
grmShader* CGrassRenderer::GetGrassShader()
{
grmShader *pShader = ms_Shader;
Assert(pShader);
return(pShader);
}
grcEffectTechnique CGrassRenderer::GetFakedGBufTechniqueID()
{
return ms_shdBlitFakedGBufID;
}
grcEffectVar CGrassRenderer::GetGBuf0TextureID()
{
return ms_shdGBuf0TextureID;
}
//
//
//
//
void CGrassRenderer::Shutdown()
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
#define DELETE_PTR(PTR) { if(PTR) { delete(PTR); PTR=NULL; } }
ms_Shader->SetVar(ms_shdTextureID, (grcTexture*)NULL);
DELETE_PTR(ms_ShaderGroup);
#undef DELETE_PTR
#if PSN_PLANTSMGR_SPU_RENDER
PPTriPlantBuffer.DestroyTextureBuffers();
PPTriPlantBuffer.DestroyModelsBuffers();
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
while( spuTriPlantBlockTabCount-- )
{
const u32 i = spuTriPlantBlockTabCount;
PLANTS_FREE_STR( spuTriPlantBlockTab[i] );
spuTriPlantBlockTab[i] = NULL;
}
#endif
#if PLANTSMGR_BIG_HEAP_IN_BLOCKS
while( sm_BigHeapBlockArrayCount[0]-- )
{
const u32 i = sm_BigHeapBlockArrayCount[0];
PLANTS_FREE_STR( sm_BigHeapBlockArray[0][i].ea );
sm_BigHeapBlockArray[0][i].ea = NULL;
sm_BigHeapBlockArray[0][i].offset = 0;
}
while( sm_BigHeapBlockArrayCount[1]-- )
{
const u32 i = sm_BigHeapBlockArrayCount[1];
PLANTS_FREE_STR( sm_BigHeapBlockArray[1][i].ea );
sm_BigHeapBlockArray[1][i].ea = NULL;
sm_BigHeapBlockArray[1][i].offset = 0;
}
#else
PLANTS_FREE_STR(sm_BigHeap[0]);
PLANTS_FREE_STR(sm_BigHeap[1]);
sm_BigHeap[0] = NULL;
sm_BigHeap[1] = NULL;
sm_BigHeapOffset[0] = 0;
sm_BigHeapOffset[1] = 0;
#endif
#if SPU_GCM_FIFO
PLANTS_FREE(sm_StartupHeapPtr[0]);
PLANTS_FREE(sm_StartupHeapPtr[1]);
sm_StartupHeapPtr[0] = NULL;
sm_StartupHeapPtr[1] = NULL;
sm_StartupHeapOffset[0] = 0;
sm_StartupHeapOffset[1] = 0;
#endif
PLANTS_FREE(gpShaderLOD0Cmd0[0]);
gpShaderLOD0Cmd0[0] = NULL;
gpShaderLOD0Offset[0] = 0;
PLANTS_FREE(gpShaderLOD0Cmd0[1]);
gpShaderLOD0Cmd0[1] = NULL;
gpShaderLOD0Offset[1] = 0;
PLANTS_FREE(gpShaderLOD1Cmd0[0]);
gpShaderLOD1Cmd0[0] = NULL;
gpShaderLOD1Offset[0] = 0;
PLANTS_FREE(gpShaderLOD1Cmd0[1]);
gpShaderLOD1Cmd0[1] = NULL;
gpShaderLOD1Offset[1] = 0;
PLANTS_FREE(gpShaderLOD2Cmd0[0]);
gpShaderLOD2Cmd0[0] = NULL;
gpShaderLOD2Offset[0] = 0;
PLANTS_FREE(gpShaderLOD2Cmd0[1]);
gpShaderLOD2Cmd0[1] = NULL;
gpShaderLOD2Offset[1] = 0;
PLANTS_FREE(gpPlantLOD2GeometryCmd0);
gpPlantLOD2GeometryCmd0 = NULL;
gpPlantLOD2GeometryOffset = 0;
#endif //PSN_PLANTSMGR_SPU_RENDER...
#if GRASS_INSTANCING
u32 i;
for(i=0; i<CGRASS_RENDERER_LOD_BUCKET_SIZE; i++)
{
ms_instanceBucketsLOD0[i].ShutDown();
ms_instanceBucketsLOD1[i].ShutDown();
ms_instanceBucketsLOD2[i].ShutDown();
}
#endif // GRASS_INSTANCING
}// end of Shutdown()...
//
//
// Update buffers allocated from streaming heap:
//
bool CGrassRenderer::UpdateStr()
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
bool bDoneAlloc = false; // nothing done yet
bool bDoneFree = false; // nothing done yet
#if PSN_PLANTSMGR_SPU_RENDER
// make sure SPU render job (using the str buffers as I/O buffers) is not running:
if(sm_GrassTaskHandle)
{
if(!sysTaskManager::Poll(sm_GrassTaskHandle))
{
plantsDebugf1("\nGrassRendererSPU::UpdateStr(): Waiting for SPU render job to finish.");
//plantsAssertf(0, "GrassRendererSPU::UpdateStr(): Waiting for SPU render job to finish.");
sysTaskManager::Wait(sm_GrassTaskHandle);
}
sm_GrassTaskHandle=NULL;
}
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
// 1. UpdateStr for spuTriPlantBlockTab:
if(1)
{
static dev_u32 dbgStrTriPlantExtra=16;
const u32 numUsed = sm_nNumGrassJobsAddedTotal + dbgStrTriPlantExtra; // allow few more blocks as a safeguard buffer
const u32 numAllocated = spuTriPlantBlockTabCount;
if(numUsed <= numAllocated)
{ // too many allocated - free unwanted blocks:
const u32 numToFree = numAllocated - numUsed;
bDoneFree = bDoneFree || numToFree?true:false; // true if any frees are done
for(u32 i=0; i<numToFree; i++)
{
spuTriPlantBlockTabCount--;
Assert(spuTriPlantBlockTabCount>=0);
const u32 b = spuTriPlantBlockTabCount;
PLANTS_FREE_STR( spuTriPlantBlockTab[b] );
spuTriPlantBlockTab[b] = NULL;
}
}
else
{ // too few allocated - allocate more blocks:
const u32 numToAlloc = numUsed - numAllocated;
bDoneAlloc = bDoneAlloc || numToAlloc?true:false; // true if any allocs are done
for(u32 i=0; i<numToAlloc; i++)
{
const u32 b = spuTriPlantBlockTabCount;
if(b >= PLANTSMGR_MAX_NUM_OF_RENDER_JOBS)
break; // no more space!
spuTriPlantBlockTab[b] = (PPTriPlant*)PLANTS_MALLOC_STR(PLANTSMGR_TRIPLANT_BLOCK_SIZE, 128);
//Assert(spuTriPlantBlockTab[b]);
//ASSERT128(spuTriPlantBlockTab[b]);
if(!spuTriPlantBlockTab[b])
break; // no more str memory available!
spuTriPlantBlockTabCount++;
Assert(spuTriPlantBlockTabCount<=PLANTSMGR_MAX_NUM_OF_RENDER_JOBS);
}
}
}// end of UpdateStr for spuTriPlantBlockTab...
#endif //PLANTSMGR_TRIPLANTTAB_IN_BLOCKS...
#if PLANTSMGR_BIG_HEAP_IN_BLOCKS
// 2. UpdateStr for BigHeap:
if(1)
{
static dev_u32 dbgStrHeapBlockExtra=32;
const u32 numUsed = rage::Max(sm_BigHeapConsumed[0], sm_BigHeapConsumed[1]) +
rage::Max(sm_BigHeapOverfilled[0], sm_BigHeapOverfilled[1]) +
dbgStrHeapBlockExtra; // allow few more blocks as a safeguard buffer
const u32 numAllocated = rage::Max(sm_BigHeapBlockArrayCount[0], sm_BigHeapBlockArrayCount[1]);
if(numUsed <= numAllocated)
{ // too many blocks allocated - free some:
const u32 numToFree = numAllocated - numUsed;
bDoneFree = bDoneFree || numToFree?true:false;
for(u32 i=0; i<numToFree; i++)
{
sm_BigHeapBlockArrayCount[0]--;
sm_BigHeapBlockArrayCount[1]--;
const s32 b0 = sm_BigHeapBlockArrayCount[0];
Assert(b0 >= 0);
const s32 b1 = sm_BigHeapBlockArrayCount[1];
Assert(b1 >= 0);
PLANTS_FREE_STR( sm_BigHeapBlockArray[0][b0].ea );
sm_BigHeapBlockArray[0][b0].ea = NULL;
sm_BigHeapBlockArray[0][b0].offset = 0;
PLANTS_FREE_STR( sm_BigHeapBlockArray[1][b1].ea );
sm_BigHeapBlockArray[1][b1].ea = NULL;
sm_BigHeapBlockArray[1][b1].offset = 0;
}
}
else
{ // not enough blocks allocated - allocate some:
const u32 numToAlloc = numUsed - numAllocated;
bDoneAlloc = bDoneAlloc || numToAlloc?true:false;
for(u32 i=0; i<numToAlloc; i++)
{
const u32 b0 = sm_BigHeapBlockArrayCount[0];
const u32 b1 = sm_BigHeapBlockArrayCount[1];
if( (b0 >= PLANTSMGR_BIG_HEAP_BLK_ARRAY_SIZE) ||
(b1 >= PLANTSMGR_BIG_HEAP_BLK_ARRAY_SIZE) )
break; // no more space!
sm_BigHeapBlockArray[0][b0].ea = (u32*)PLANTS_MALLOC_STR(PLANTSMGR_LOCAL_HEAP_SIZE, 128);
//Assert(sm_BigHeapBlockArray[0][b0].ea);
//ASSERT128(sm_BigHeapBlockArray[0][b0].ea);
if(!sm_BigHeapBlockArray[0][b0].ea)
{
break; // no more str memory!
}
sm_BigHeapBlockArray[1][b1].ea = (u32*)PLANTS_MALLOC_STR(PLANTSMGR_LOCAL_HEAP_SIZE, 128);
//Assert(sm_BigHeapBlockArray[1][b1].ea);
//ASSERT128(sm_BigHeapBlockArray[1][b1].ea);
if(!sm_BigHeapBlockArray[1][b1].ea)
{
PLANTS_FREE_STR( sm_BigHeapBlockArray[0][b0].ea );
sm_BigHeapBlockArray[0][b0].ea = NULL;
sm_BigHeapBlockArray[0][b0].offset = 0;
break; // no more str memory!
}
sm_BigHeapBlockArray[0][b0].offset = PLANTS_GCM_OFFSET(sm_BigHeapBlockArray[0][b0].ea);
sm_BigHeapBlockArray[1][b1].offset = PLANTS_GCM_OFFSET(sm_BigHeapBlockArray[1][b1].ea);
sm_BigHeapBlockArrayCount[0]++;
sm_BigHeapBlockArrayCount[1]++;
Assert(sm_BigHeapBlockArrayCount[0] <= PLANTSMGR_BIG_HEAP_BLK_ARRAY_SIZE);
Assert(sm_BigHeapBlockArrayCount[1] <= PLANTSMGR_BIG_HEAP_BLK_ARRAY_SIZE);
}
}
}// end of UpdateStr for BigHeap...
#endif //PLANTSMGR_BIG_HEAP_IN_BLOCKS...
#endif // PSN_PLANTSMGR_SPU_RENDER...
return(bDoneAlloc || bDoneFree);
}// end of CGrassRenderer::UpdateStr()...
//
//
//
//
void CGrassRenderer::AddTriPlant(PPTriPlant *pSrcPlant, u32 ePlantModelSet)
{
PPTriPlantBuffer.ChangeCurrentPlantModelsSet(ePlantModelSet);
PPTriPlant *pDstPlant = PPTriPlantBuffer.GetPPTriPlantPtr(1);
sysMemCpy(pDstPlant, pSrcPlant, sizeof(PPTriPlant));;
PPTriPlantBuffer.IncreaseBufferIndex(ePlantModelSet, 1);
}// end of CGrassRenderer::AddPlantCluster()...
//
//
//
//
void CGrassRenderer::FlushTriPlantBuffer()
{
PPTriPlantBuffer.Flush();
}
//
//
//
//
bool CGrassRenderer::SetPlantModelsTab(u32 index, grassModel* plantModels)
{
return(PPTriPlantBuffer.SetPlantModelsTab(index, plantModels));
}
#if !PSN_PLANTSMGR_SPU_RENDER
void CGrassRenderer::BeginUseNormalTechniques(spdTransposedPlaneSet8 &cullFrustum)
{
ms_cullingFrustum[g_RenderThreadIndex] = cullFrustum;
#if GRASS_INSTANCING
#if PLANTSMGR_MULTI_RENDER
ms_LOD0Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD0TechniqueID);
ms_LOD1Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD1TechniqueID);
ms_LOD2Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD2TechniqueID);
#else
ms_LOD0Bucket.Reset(ms_shdDeferredLOD0TechniqueID);
ms_LOD1Bucket.Reset(ms_shdDeferredLOD1TechniqueID);
ms_LOD2Bucket.Reset(ms_shdDeferredLOD2TechniqueID);
#endif
#if __BANK
if(GetDebugModeEnable())
{
#if PLANTSMGR_MULTI_RENDER
ms_LOD0Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD0DbgTechniqueID);
ms_LOD1Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD1DbgTechniqueID);
ms_LOD2Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD2DbgTechniqueID);
#else
ms_LOD0Bucket.Reset(ms_shdDeferredLOD0DbgTechniqueID);
ms_LOD1Bucket.Reset(ms_shdDeferredLOD1DbgTechniqueID);
ms_LOD2Bucket.Reset(ms_shdDeferredLOD2DbgTechniqueID);
#endif
}
#endif
#else // GRASS_INSTANCING
ms_shdDeferredLOD0TechniqueID_ToUse = ms_shdDeferredLOD0TechniqueID;
ms_shdDeferredLOD1TechniqueID_ToUse = ms_shdDeferredLOD1TechniqueID;
ms_shdDeferredLOD2TechniqueID_ToUse = ms_shdDeferredLOD2TechniqueID;
#if __BANK
if(GetDebugModeEnable())
{
ms_shdDeferredLOD0TechniqueID_ToUse = ms_shdDeferredLOD0DbgTechniqueID;
ms_shdDeferredLOD1TechniqueID_ToUse = ms_shdDeferredLOD1DbgTechniqueID;
ms_shdDeferredLOD2TechniqueID_ToUse = ms_shdDeferredLOD2DbgTechniqueID;
}
#endif
#endif // GRASS_INSTANCING
}
void CGrassRenderer::EndUseNormalTechniques()
{
#if GRASS_INSTANCING
#if PLANTSMGR_MULTI_RENDER
ms_LOD0Bucket.Flush(g_RenderThreadIndex);
ms_LOD1Bucket.Flush(g_RenderThreadIndex);
ms_LOD2Bucket.Flush(g_RenderThreadIndex);
#else
ms_LOD0Bucket.Flush();
ms_LOD1Bucket.Flush();
ms_LOD2Bucket.Flush();
#endif
#else // GRASS_INSTANCING
ms_shdDeferredLOD0TechniqueID_ToUse = grcetNONE;
ms_shdDeferredLOD1TechniqueID_ToUse = grcetNONE;
ms_shdDeferredLOD2TechniqueID_ToUse = grcetNONE;
#endif // GRASS_INSTANCING
}
#endif //!__PS3
#if PLANTS_CAST_SHADOWS
#if !PSN_PLANTSMGR_SPU_RENDER
void CGrassRenderer::BeginUseShadowTechniques(spdTransposedPlaneSet8 &cullFrustum)
{
SetDepthBiasOn();
ms_cullingFrustum[g_RenderThreadIndex] = cullFrustum;
#if GRASS_INSTANCING
#if PLANTSMGR_MULTI_RENDER
ms_LOD0Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD0ShadowTechniqueID);
ms_LOD1Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD1ShadowTechniqueID);
ms_LOD2Bucket.Reset(g_RenderThreadIndex, ms_shdDeferredLOD2ShadowTechniqueID);
#else
ms_LOD0Bucket.Reset(ms_shdDeferredLOD0ShadowTechniqueID);
ms_LOD1Bucket.Reset(ms_shdDeferredLOD1ShadowTechniqueID);
ms_LOD2Bucket.Reset(ms_shdDeferredLOD2ShadowTechniqueID);
#endif
#else // GRASS_INSTANCING
ms_shdDeferredLOD0TechniqueID_ToUse = ms_shdDeferredLOD0ShadowTechniqueID;
ms_shdDeferredLOD1TechniqueID_ToUse = ms_shdDeferredLOD1ShadowTechniqueID;
ms_shdDeferredLOD2TechniqueID_ToUse = ms_shdDeferredLOD2ShadowTechniqueID;
#endif
}
void CGrassRenderer::EndUseShadowTechniques()
{
#if GRASS_INSTANCING
#if PLANTSMGR_MULTI_RENDER
ms_LOD0Bucket.Flush(g_RenderThreadIndex);
ms_LOD1Bucket.Flush(g_RenderThreadIndex);
ms_LOD2Bucket.Flush(g_RenderThreadIndex);
#else
ms_LOD0Bucket.Flush();
ms_LOD1Bucket.Flush();
ms_LOD2Bucket.Flush();
#endif
#else // GRASS_INSTANCING
ms_shdDeferredLOD0TechniqueID_ToUse = grcetNONE;
ms_shdDeferredLOD1TechniqueID_ToUse = grcetNONE;
ms_shdDeferredLOD2TechniqueID_ToUse = grcetNONE;
#endif // GRASS_INSTANCING
SetDepthBiasOff();
}
#endif //!__PS3
#endif //PLANTS_CAST_SHADOWS
//
//
//
//
grassModel* CGrassRenderer::GetPlantModelsTab(u32 index)
{
return(PPTriPlantBuffer.GetPlantModelsTab(index));
}
void CGrassRenderer::SetGeometryLOD2(grcVertexBuffer *vb, grcVertexDeclaration *decl)
{
if(ms_plantLOD2VertexBuffer)
{
// destroy this
delete ms_plantLOD2VertexBuffer;
ms_plantLOD2VertexBuffer = NULL;
}
if(ms_plantLOD2VertexDecl)
{
// destroy this
ms_plantLOD2VertexDecl->Release();
ms_plantLOD2VertexDecl = NULL;
}
ms_plantLOD2VertexBuffer = vb;
ms_plantLOD2VertexDecl = decl;
}
//
//
//
// updates global camera pos; should be called every frame;
//
void CGrassRenderer::SetGlobalCameraPos(const Vector3& camPos)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_vecCameraPos[idx] = camPos;
}
const Vector3& CGrassRenderer::GetGlobalCameraPos()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_vecCameraPos[idx];
}
void CGrassRenderer::SetGlobalCameraFront(const Vector3& camFront)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_vecCameraFront[idx] = camFront;
}
const Vector3& CGrassRenderer::GetGlobalCameraFront()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_vecCameraFront[idx];
}
void CGrassRenderer::SetGlobalCameraFppEnabled(bool enable)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_cameraFppEnabled[idx] = enable;
}
bool CGrassRenderer::GetGlobalCameraFppEnabled()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_cameraFppEnabled[idx];
}
void CGrassRenderer::SetGlobalCameraUnderwater(bool enable)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_cameraUnderwater[idx] = enable;
}
bool CGrassRenderer::GetGlobalCameraUnderwater()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_cameraUnderwater[idx];
}
void CGrassRenderer::SetGlobalForceHDGrassGeometry(bool enable)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_forceHdGrassGeom[idx] = enable;
}
bool CGrassRenderer::GetGlobalForceHDGrassGeometry()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_forceHdGrassGeom[idx];
}
void CGrassRenderer::SetGlobalPlayerPos(const Vector3& playerPos)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_vecPlayerPos[idx] = playerPos;
}
const Vector3& CGrassRenderer::GetGlobalPlayerPos()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_vecPlayerPos[idx];
}
void CGrassRenderer::SetGlobalPlayerCollisionRSqr(float radSqr)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_fPlayerCollRSqr[idx] = radSqr;
}
float CGrassRenderer::GetGlobalPlayerCollisionRSqr()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_fPlayerCollRSqr[idx];
}
void CGrassRenderer::SetGlobalCullFrustum(const spdTransposedPlaneSet8& frustum)
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_cullFrustum[idx] = frustum;
}
void CGrassRenderer::GetGlobalCullFrustum(spdTransposedPlaneSet8 *pFrustum)
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
Assert(pFrustum);
*pFrustum = ms_cullFrustum[idx];
}
void CGrassRenderer::SetGlobalInteriorCullAll(bool bCullAll)
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_interiorCullAll[idx] = bCullAll;
}
bool CGrassRenderer::GetGlobalInteriorCullAll()
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_interiorCullAll[idx];
}
void CGrassRenderer::SetGlobalVehCollisionParams(u32 n, bool bEnable, const Vector3& vecB, const Vector3& vecM, float radius, float groundZ)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
Assert(n<NUM_COL_VEH);
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
//float4 vecVehCollB : vecVehCollB0 = float4(0,0,0,0); // [ segment start point B.xyz | 0 ]
//float4 vecVehCollM : vecVehCollM0 = float4(0,0,0,0); // [ segment vector M.xyz | 1.0f/dot(M,M) ]
//float4 vecVehCollR : vecVehCollR0 = float4(0,0,0,0); // [ R | R*R | 1.0f/(R*R) | worldPosZ ]
for (u32 uThread = 0; uThread < NUMBER_OF_RENDER_THREADS; uThread++)
{
ms_bVehCollisionEnabled[idx][uThread][n] = bEnable;
Vector4 &vehCollisionB = ms_vecVehCollisionB[idx][uThread][n];
vehCollisionB.x = vecB.x;
vehCollisionB.y = vecB.y;
vehCollisionB.z = vecB.z;
vehCollisionB.w = 0.0f;
Vector2 vecM2;
vecM2.x = vecM.x;
vecM2.y = vecM.y;
float dotMM = vecM2.Dot(vecM2);
Vector4 &vehCollisionM = ms_vecVehCollisionM[idx][uThread][n];
vehCollisionM.x = vecM.x;
vehCollisionM.y = vecM.y;
vehCollisionM.z = vecM.z;
vehCollisionM.w = (dotMM>0.0f)? (1.0f/dotMM) : (0.0f);
Vector4 &vehCollisionR = ms_vecVehCollisionR[idx][uThread][n];
vehCollisionR.x = radius;
vehCollisionR.y = radius * radius;
vehCollisionR.z = (radius>0.0f)? (1.0f/(radius * radius)) : (0.0f);
vehCollisionR.w = groundZ;
}
}
void CGrassRenderer::GetGlobalVehCollisionParams(u32 n, bool *pbEnable, Vector4 *pVecB, Vector4 *pVecM, Vector4 *pVecR)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
Assert(n<NUM_COL_VEH);
Assert(pbEnable);
Assert(pVecB);
Assert(pVecM);
Assert(pVecR);
const s32 idx = gPlantMgr.GetRenderRTBufferID();
*pbEnable = ms_bVehCollisionEnabled[idx][g_RenderThreadIndex][n];
*pVecB = ms_vecVehCollisionB[idx][g_RenderThreadIndex][n];
*pVecM = ms_vecVehCollisionM[idx][g_RenderThreadIndex][n];
*pVecR = ms_vecVehCollisionR[idx][g_RenderThreadIndex][n];
}
//
//
//
//
void CGrassRenderer::SetGlobalWindBending(const Vector2& bending)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_windBending[idx] = bending;
}
// GetGlobalWindBendingUT() - version for UpdateThread
void CGrassRenderer::GetGlobalWindBendingUT(Vector2 &outBending)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
outBending = ms_windBending[idx];
}
// GetGlobalWindBending() - version for RenderThread
void CGrassRenderer::GetGlobalWindBending(Vector2 &outBending)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
outBending = ms_windBending[idx];
}
void CGrassRenderer::SetFakeGrassNormal(const Vector3& grassNormal)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_fakeGrassNormal[idx] = grassNormal;
}
const Vector3& CGrassRenderer::GetFakeGrassNormal()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_fakeGrassNormal[idx];
}
#if __BANK
void CGrassRenderer::SetDebugModeEnable(bool enable)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_enableDebugDraw[idx] = enable;
}
bool CGrassRenderer::GetDebugModeEnable()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_enableDebugDraw[idx];
}
#endif //__BANK...
#if FURGRASS_TEST_V4
void CGrassRenderer::SetGlobalPlayerFeetPos(const Vector4& LFootPos, const Vector4& RFootPos)
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_UPDATE));
const s32 idx = gPlantMgr.GetUpdateRTBufferID();
ms_vecPlayerLFootPos[idx] = LFootPos;
ms_vecPlayerRFootPos[idx] = RFootPos;
}
const Vector4& CGrassRenderer::GetGlobalPlayerLFootPos()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_vecPlayerLFootPos[idx];
}
const Vector4& CGrassRenderer::GetGlobalPlayerRFootPos()
{
Assert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const s32 idx = gPlantMgr.GetRenderRTBufferID();
return ms_vecPlayerRFootPos[idx];
}
#endif //FURGRASS_TEST_V4...
#if PLANTS_USE_LOD_SETTINGS
float CGrassRenderer::GetDistanceMultiplier()
{
return ms_currentScalingParams.distanceMultiplier;
}
float CGrassRenderer::GetDensityMultiplier()
{
return ms_currentScalingParams.densityMultiplier;
}
void CGrassRenderer::SetPlantLODToUse(CGrassRenderer_PlantLod plantLOD)
{
if(ms_isInitialized)
{
ms_currentScalingParams = ms_scalingParams_AllLods.m_AllLods[plantLOD];
#if PLANTS_CAST_SHADOWS
ms_currentShadowEnvelope = ms_shadowEnvelope_AllLods.m_AllLods[plantLOD];
#endif //PLANTS_CAST_SHADOWS
}
else
{
ms_LODToInitialiseWith = (int)plantLOD;
}
}
#endif //PLANTS_USE_LOD_SETTINGS
#if PLANTS_USE_LOD_SETTINGS
bool CGrassRenderer::ms_isInitialized = false;
int CGrassRenderer::ms_LODToInitialiseWith = 0;
#if __BANK
int CGrassRenderer::ms_LODBeingEdited = 0;
int CGrassRenderer::ms_LODNextToEdit = 0;
static const char *g_LODTypes[3] =
{
"Low",
"Medium",
"High",
};
void CGrassRenderer::OnComboBox_LODToEdit(void)
{
// Record the ones being edited.
ms_scalingParams_AllLods.m_AllLods[ms_LODBeingEdited] = ms_currentScalingParams;
#if PLANTS_CAST_SHADOWS
ms_shadowEnvelope_AllLods.m_AllLods[ms_LODBeingEdited] = ms_currentShadowEnvelope;
#endif //PLANTS_CAST_SHADOWS
// Set the next one to edit.
SetPlantLODToUse((CGrassRenderer_PlantLod)ms_LODNextToEdit);
ms_LODBeingEdited = ms_LODNextToEdit;
}
void CGrassRenderer::SaveParams(void)
{
// Record the ones being edited.
ms_scalingParams_AllLods.m_AllLods[ms_LODBeingEdited] = ms_currentScalingParams;
#if PLANTS_CAST_SHADOWS
ms_shadowEnvelope_AllLods.m_AllLods[ms_LODBeingEdited] = ms_currentShadowEnvelope;
#endif //PLANTS_CAST_SHADOWS
// Save the data out.
PARSER.SaveObject("platform:/data/GrassLODSettings", "xml", &CGrassRenderer::ms_scalingParams_AllLods, parManager::XML);
#if PLANTS_CAST_SHADOWS
PARSER.SaveObject("platform:/data/GrassShadowLODSettings", "xml", &CGrassRenderer::ms_shadowEnvelope_AllLods, parManager::XML);
#endif //PLANTS_CAST_SHADOWS
}
void CGrassRenderer::LoadParams(void)
{
// Load the data in.
PARSER.LoadObject("platform:/data/GrassLODSettings", "xml", CGrassRenderer::ms_scalingParams_AllLods);
#if PLANTS_CAST_SHADOWS
PARSER.LoadObject("platform:/data/GrassShadowLODSettings", "xml", CGrassRenderer::ms_shadowEnvelope_AllLods);
#endif //PLANTS_CAST_SHADOWS
// Update the one being edited.
SetPlantLODToUse((CGrassRenderer_PlantLod)ms_LODBeingEdited);
}
#endif //__BANK
#endif //PLANTS_USE_LOD_SETTINGS
#if __BANK
void CGrassRenderer::InitWidgets(bkBank& bank)
{
#if PLANTS_USE_LOD_SETTINGS
bank.PushGroup("LOD Settings");
bank.AddCombo("LOD type", &ms_LODNextToEdit, 3, g_LODTypes, 0, datCallback(CFA(CGrassRenderer::OnComboBox_LODToEdit)));
bank.AddSlider("Grass distance", &CGrassRenderer::ms_currentScalingParams.distanceMultiplier, 1.0f, 15.0f, 0.1f );
bank.AddSlider("Grass density", &CGrassRenderer::ms_currentScalingParams.densityMultiplier, 1.0f, 250.0f, 0.1f );
#if PLANTS_CAST_SHADOWS
bank.AddToggle("Draw shadows", &CGrassRenderer::ms_drawShadows);
bank.AddSlider("Shadow near", &CGrassRenderer::ms_currentShadowEnvelope.fadeNear, 1.0f, 1000.0f, 0.1f );
bank.AddSlider("Shadow far", &CGrassRenderer::ms_currentShadowEnvelope.fadeFar, 1.0f, 1000.f, 0.1f );
#endif //PLANTS_CAST_SHADOWS
bank.AddButton("Save", CGrassRenderer::SaveParams);
bank.AddButton("Load", CGrassRenderer::LoadParams);
bank.PopGroup();
#endif //PLANTS_USE_LOD_SETTINGS
}
#endif //__BANK
#if PLANTS_CAST_SHADOWS
float g_Bias = 0.00005f;
float g_zBiasMax = 1.0f;
void CGrassRenderer::SetDepthBiasOn()
{
float A = GetShadowFadeNearRadius()*GetShadowFadeNearRadius();
float B = GetShadowFadeFarRadius()*GetShadowFadeFarRadius();
float C1 = -1.0f/(B - A);
float C2 = 1.0f + A/(B - A);
Vec4V bias = Vec4V(g_Bias, g_zBiasMax, C1, C2);
grcEffect::SetGlobalVar(ms_depthValueBias, bias);
}
void CGrassRenderer::SetDepthBiasOff()
{
Vec4V Zero = Vec4V(V_ZERO_WONE);
grcEffect::SetGlobalVar(ms_depthValueBias, Zero);
}
float CGrassRenderer::GetShadowFadeNearRadius()
{
return ms_currentShadowEnvelope.fadeNear;
}
float CGrassRenderer::GetShadowFadeFarRadius()
{
return ms_currentShadowEnvelope.fadeFar;
}
#endif //PLANTS_CAST_SHADOWS
#if PSN_PLANTSMGR_SPU_RENDER
//
//
//
//
static
void SetupContextData(CellGcmContextData *context, const uint32_t *addr, const uint32_t size, CellGcmContextCallback callback)
{
context->begin = (uint32_t*)addr;
context->current = (uint32_t*)addr;
context->end = (uint32_t*)addr + size/sizeof(uint32_t) - 1;
context->callback = callback;
}
static
int32_t CustomGcmReserveFailed(CellGcmContextData* context, uint32_t count)
{
#if __ASSERT
Errorf("\nCustomGcmReserveFailed: Custom context 0x%p overfilled! CurrentPos=%d. MaxSize=%d. CountRequired=%d.",
context,
context->current - context->begin, context->end - context->begin, count);
Assert(FALSE);
#else
Quitf("CustomGcmReserveFailed: Custom context 0x%p overfilled! CurrentPos=%d. MaxSize=%d. CountRequired=%d.",
context,
context->current - context->begin, context->end - context->begin, count);
#endif
return(CELL_OK);
}
//
// SetupGrassTextureForSPU():
// - generates small command buffer to set particular texture on SPU
// - most of this textrue stuff is stolen from grcFragmentProgram::SetParameter();
//
//
bool CGrassRenderer::SpuRecordGrassTexture(u32 texIndex, grcTexture *data)
{
const s32 _cmdBufSize = 128;
u32 _cmdBuf[_cmdBufSize]={0}; // fill with NOPs
CellGcmContext con1;
#if __ASSERT
CellGcmContextData *conData1 = (CellGcmContextData*)&con1;
#endif
SetupContextData(&con1, _cmdBuf, _cmdBufSize*sizeof(u32), &CustomGcmReserveFailed);
const u32 texUnit = ms_shdTextureTexUnit;
if(!data)
{
// generate stuff to switch off texture:
//con1.SetTextureControl(texUnit, CELL_GCM_FALSE, 0, 0, CELL_GCM_TEXTURE_MAX_ANISO_1);
con1.SetNopCommand(PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE-1); // fill up with NOPs
}
else
{
const CellGcmTexture *gcmTex = reinterpret_cast<const CellGcmTexture*>(data->GetTexturePtr());
con1.SetTexture(texUnit,gcmTex);
const u8 anisotropy = CELL_GCM_TEXTURE_MAX_ANISO_1;
union { int i; float f; } x;
x.i = 0; //states[grcessMIPMAPLODBIAS];
int fixedBias = int(x.f * 256.0f) & 0x1fff; // gcm doesn't mask this unless CELL_GCM_BITFIELD is set!
// controls anisotropic filtering for texture sampler 1
s32 minMipLevelIndex = static_cast<u32>(gcmTex->mipmap - 1);
s32 maxMipLevelIndex = MIN(minMipLevelIndex, 0/*MAXMIPLEVEL*/) << 8;
minMipLevelIndex = MIN(minMipLevelIndex, 12/*grcessMINMIPLEVEL]*/) << 8;
con1.SetTextureControl(texUnit, CELL_GCM_TRUE, // disable / enable texture sampler
maxMipLevelIndex, minMipLevelIndex, anisotropy);
con1.SetTextureFilter(texUnit, fixedBias,
CELL_GCM_TEXTURE_LINEAR_LINEAR, //remapMin[states[grcessMIPFILTER]][states[grcessMINFILTER]],
CELL_GCM_TEXTURE_LINEAR, //remapMag[states[grcessMAGFILTER]],
CELL_GCM_TEXTURE_CONVOLUTION_QUINCUNX); //remapConv[Max(states[grcessMINFILTER],states[grcessMAGFILTER])]));
// texture address mode is wrap ...
con1.SetTextureAddress(texUnit,
/*grcessADDRESSU*/CELL_GCM_TEXTURE_WRAP,
/*grcessADDRESSV*/CELL_GCM_TEXTURE_WRAP,
/*grcessADDRESSW*/CELL_GCM_TEXTURE_WRAP,
CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL,
// specify the depth texture test method used when applying the shadow map
// CELL_GCM_TEXTURE_ZFUNC_GREATER worked well in the past with a depth test of _LESS
// switches on and off hardware comparison and hardware PCF filtering
CELL_GCM_TEXTURE_ZFUNC_ALWAYS,
gcmTex->_padding & CELL_GCM_TEXTURE_GAMMA_MASK); // sRGB reverse conversion
// Just to be safe - I'm not sure what version this function was actually added to the Cell SDK
// "brilinear" optimization
con1.SetTextureOptimization(texUnit, 8/*states[grcessTRILINEARTHRESHOLD]*/, CELL_GCM_TEXTURE_ISO_HIGH, CELL_GCM_TEXTURE_ANISO_HIGH);
// texture border color
con1.SetTextureBorderColor(texUnit, 0);
}// if(data)...
con1.SetReturnCommand(); // fillup to 20 commands...
#if __ASSERT
const u32 cbSize = ((u8*)conData1->current) - ((u8*)conData1->begin);
Assert((cbSize/4) == PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE);
#endif
// plantsDebugf1("\n SetupGrassTexture: CBsize=%d, cbSize4=%d", cbSize, cbSize/4);
PPTriPlantBuffer.SetPlantTexturesCmd(0, texIndex, _cmdBuf);
return(TRUE);
}// end of SpuRecordGrassTexture()....
#if 1
static void DumpMemoryAsRSXDump(const char*,u32*,u32) { /*do nothing*/}
#else
//
// useful routine to print any given memory as RSX dump buffer:
// [000] 00000330:00000207 00000334:00000000 00000338:000000ff 00000330:00000207
// [004] 00000334:00000000 00000338:000000ff 00001d6c:00000440 00001d74:00000000
// [008] 00001d7c:00000001 00001efc:00000021 00001f00:3d808081 00001f04:41287aa8
// [00c] 00001f08:3f7eff00 00001f0c:3eea4f76 00001d6c:00000440 00001d74:35082214
//
static
void DumpMemoryAsRSXDump(const char *dumpName, u32 *startPtr, u32 sizeInWords)
{
u32 *endPtr = startPtr + sizeInWords;
#define GETP(PTR) (PTR<endPtr?(*(PTR)):(0x0000BACA))
plantsDisplayf("\n xxxxx %s:Begin", dumpName);
const u32 countW = (sizeInWords+8)/8;
u32 *p=startPtr;
u32 i=0;
for(; i<(countW-1); i++)
{
plantsDisplayf("\n [%03x] %08x:%08x %08x:%08x %08x:%08x %08x:%08x", i*4,
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
p += 8;
}
// last line:
plantsDisplayf("\n [%03x] %08x:%08x %08x:%08x %08x:%08x %08x:%08x", i*4,
GETP(&p[0]), GETP(&p[1]), GETP(&p[2]), GETP(&p[3]), GETP(&p[4]), GETP(&p[5]), GETP(&p[6]), GETP(&p[7]));
plantsDisplayf("\n xxxxx %s:End", dumpName);
}// end of DumpMemoryAsRSXDump()...
#endif //#if 0...
#if __ASSERT
namespace rage { extern u16 g_VertexShaderInputs; }
#endif
//
//
//
//
u32 CGrassRenderer::SpuRecordShaderBind(CellGcmContextData *conData1, grcEffectTechnique forcedTech, u32 *dstCmd, u32 maxCmdSize)
{
conData1->current = conData1->begin; // restart context
// Bind technique:
ASSERT_ONLY(s32 numPasses =) ms_Shader->RecordBeginDraw(grmShader::RMC_DRAW, /*restoreState*/FALSE, /*techOverride*/forcedTech);
Assert(numPasses>0);
const s32 pass=0;
ms_Shader->RecordBind(pass);
cellGcmSetReturnCommand(conData1);
// at this point g_VertexShaderInputs should contain valid input mask:
Assertf(g_VertexShaderInputs && g_VertexShaderInputs!=0x0001, "g_VertexShaderInputs is 0x%X. Possibly something have gone wrong with grass VS binding?",g_VertexShaderInputs);
// fillup to extexted CB size in words
const u32 cbSize4 = (((u8*)conData1->current) - ((u8*)conData1->begin))/4;
if(cbSize4 < maxCmdSize)
{ // fill CB with extra nops:
s32 numNops = maxCmdSize - cbSize4;
cellGcmSetNopCommand(conData1,numNops);
}
#if __ASSERT
{
if((maxCmdSize-cbSize4) > 7)
plantsDebugf1("\n SpuRecordShader: Bind: cbSize4=%d (maxCmdSize=%d): WastedSpace=%d.", cbSize4, maxCmdSize,maxCmdSize-cbSize4);
}
{
const u32 cbSize = ((u8*)conData1->current) - ((u8*)conData1->begin);
if((cbSize/4) != maxCmdSize)
plantsWarningf("\n SpuRecordShader: Bind: cbSize=%d, cbSize4=%d, maxCmdSize4=%d", cbSize, cbSize/4, maxCmdSize);
Assert((cbSize/4) == maxCmdSize);
}
#endif
sysMemCpy(dstCmd, conData1->begin, maxCmdSize*sizeof(u32));
return(cbSize4);
}
//
//
//
//
u32 CGrassRenderer::SpuRecordShaderUnBind(CellGcmContextData *conData1)
{
conData1->current = conData1->begin;
// Unbind:
GRCDEVICE.ClearStreamSource(0); // this doesn't actually do anything with the context, so it can stay
ms_Shader->RecordUnBind();
ms_Shader->RecordEndDraw();
#if __ASSERT
{
const u32 cbSize = ((u8*)conData1->current) - ((u8*)conData1->begin);
if((cbSize/4) != 0)
plantsWarningf("\n SpuRecordShader: UnBind: cbSize=%d, cbSize4=%d, maxCmdSize=%d", cbSize, cbSize/4, 0);
// Assert((cbSize/4) == 0);
}
#endif
return(1);
}
//
//
//
//
u32 CGrassRenderer::SpuRecordGeometry(CellGcmContextData *con, grmGeometry *pGeometry, u32 *dstCmd, u32 maxCmdSize)
{
grcVertexBuffer* pVB = pGeometry->GetVertexBuffer(true);
grcVertexDeclaration* pVDecl= pGeometry->GetDecl();
return SpuRecordGeometry(con, pVB, pVDecl, dstCmd, maxCmdSize);
}
u32 CGrassRenderer::SpuRecordGeometry(CellGcmContextData *conData1, grcVertexBuffer* pVertexBuffer, grcVertexDeclaration* pVertexDeclaration,
u32 *dstCmd, u32 maxCmdSize)
{
conData1->current = conData1->begin;
Assert(pVertexBuffer);
Assert(pVertexDeclaration);
GRCDEVICE.RecordSetVertexDeclaration(pVertexDeclaration);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
cellGcmSetReturnCommand(conData1);
// fillup to extexted CB size in words
const u32 cbSize4 = (((u8*)conData1->current) - ((u8*)conData1->begin))/4;
if(cbSize4 < maxCmdSize)
{ // fill CB with extra nops:
s32 numNops = maxCmdSize - cbSize4;
cellGcmSetNopCommand(conData1,numNops);
}
#if __ASSERT
{
if((maxCmdSize-cbSize4) > 7)
plantsDebugf1("\n SpuRecordGeometry: cbSize4=%d (maxCmdSize=%d): WastedSpace=%d.", cbSize4, maxCmdSize,maxCmdSize-cbSize4);
}
{
const u32 cbSize = ((u8*)conData1->current) - ((u8*)conData1->begin);
if((cbSize/4) != maxCmdSize)
plantsWarningf("\n SpuRecordGeometry: cbSize=%d, cbSize4=%d, maxCmdSize=%d", cbSize, cbSize/4, maxCmdSize);
Assert((cbSize/4) == maxCmdSize);
}
#endif
sysMemCpy(dstCmd, conData1->begin, maxCmdSize*sizeof(u32));
return(cbSize4);
}
//
//
//
//
bool CGrassRenderer::SpuRecordGeometries()
{
const s32 _cmdBufSize = 1024;
u32 _cmdBuf[_cmdBufSize]={0}; // fill with NOPs
CellGcmContext con1;
//#if __ASSERT
// CellGcmContextData *conData1 = (CellGcmContextData*)&con1;
//#endif
SetupContextData(&con1, _cmdBuf, _cmdBufSize*sizeof(u32), &CustomGcmReserveFailed);
CellGcmContextData *previousContext = GCM_CONTEXT;
GCM_CONTEXT = &con1;
// Bind LOD0 technique:
SpuRecordShaderBind(&con1, ms_shdDeferredLOD0TechniqueID, gpShaderLOD0Cmd0[0], PLANTSMGR_LOCAL_BINDSHADERLOD0_MAXCMDSIZE);
DumpMemoryAsRSXDump("BindShaderLOD0", gpShaderLOD0Cmd0[0], PLANTSMGR_LOCAL_BINDSHADERLOD0_MAXCMDSIZE);
{
// 2. VertexDeclaration & SetStreamSource:
// relies on g_VertexShaderInputs set in grcVertexProgram::Bind()
for(s32 slotID=0; slotID<PPTRIPLANT_MODELS_TAB_SIZE; slotID++)
{
grassModel *pPlantModelsTab = PPTriPlantBuffer.GetPlantModels(slotID);
if(pPlantModelsTab)
{
for(s32 i=0; i<CPLANT_SLOT_NUM_MODELS; i++)
{
// LOD0 geometries only:
const s32 modelIdx = i;
grassModel *pPlantModel = &pPlantModelsTab[modelIdx];
grmGeometry *pGeometry = pPlantModel->m_pGeometry;
Assert(pGeometry);
grcIndexBufferGCM* pIndexBuffer = (grcIndexBufferGCM*)pGeometry->GetIndexBuffer(true);
Assert(pIndexBuffer);
u16* ptr = pIndexBuffer->GetGCMBuffer();
pPlantModel->m_IdxOffset = pIndexBuffer->GetGCMOffset();
// note: IsLocalPtr() may not always work at startup - depends on rage::g_LocalAddress:
pPlantModel->m_IdxLocation = gcm::IsLocalPtr(ptr)?CELL_GCM_LOCATION_LOCAL:CELL_GCM_LOCATION_MAIN;
pPlantModel->m_IdxCount = pIndexBuffer->GetIndexCount();
pPlantModel->m_DrawMode = CELL_GCM_PRIMITIVE_TRIANGLES;
pPlantModel->m_GeometryCmdOffset= PPTriPlantBuffer.GetPlantModelsCmdOffset(slotID, modelIdx);
u32 *destGeomCmds = PPTriPlantBuffer.GetPlantModelsCmd(slotID, modelIdx);
SpuRecordGeometry(&con1, pGeometry, destGeomCmds, PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE);
DumpMemoryAsRSXDump("GeometryLOD0", destGeomCmds, PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE);
}//for(s32 i=0; i<CPLANT_SLOT_NUM_MODELS; i++)...
}
} //for(s32 slotID=0; i<PPTRIPLANT_MODELS_TAB_SIZE; slotID++)...
}
// Unbind LOD0:
SpuRecordShaderUnBind(&con1);
// Bind LOD1 technique:
SpuRecordShaderBind(&con1, ms_shdDeferredLOD1TechniqueID, gpShaderLOD1Cmd0[0], PLANTSMGR_LOCAL_BINDSHADERLOD1_MAXCMDSIZE);
DumpMemoryAsRSXDump("BindShaderLOD1", gpShaderLOD1Cmd0[0], PLANTSMGR_LOCAL_BINDSHADERLOD1_MAXCMDSIZE);
{
// 2. VertexDeclaration & SetStreamSource:
// relies on g_VertexShaderInputs set in grcVertexProgram::Bind()
for(s32 slotID=0; slotID<PPTRIPLANT_MODELS_TAB_SIZE; slotID++)
{
grassModel *pPlantModelsTab = PPTriPlantBuffer.GetPlantModels(slotID);
if(pPlantModelsTab)
{
for(s32 i=0; i<CPLANT_SLOT_NUM_MODELS; i++)
{
// LOD1 geometries only:
const s32 modelIdx = i+CPLANT_SLOT_NUM_MODELS;
grassModel *pPlantModel = &pPlantModelsTab[modelIdx];
grmGeometry *pGeometry = pPlantModel->m_pGeometry;
Assert(pGeometry);
grcIndexBufferGCM* pIndexBuffer = (grcIndexBufferGCM*)pGeometry->GetIndexBuffer(true);
Assert(pIndexBuffer);
u16* ptr = pIndexBuffer->GetGCMBuffer();
pPlantModel->m_IdxOffset = pIndexBuffer->GetGCMOffset();
// IsLocalPtr() may not always work at startup - depends on rage::g_LocalAddress:
pPlantModel->m_IdxLocation = gcm::IsLocalPtr(ptr)?CELL_GCM_LOCATION_LOCAL:CELL_GCM_LOCATION_MAIN;
pPlantModel->m_IdxCount = pIndexBuffer->GetIndexCount();
pPlantModel->m_DrawMode = CELL_GCM_PRIMITIVE_TRIANGLES;
pPlantModel->m_GeometryCmdOffset = PPTriPlantBuffer.GetPlantModelsCmdOffset(slotID, modelIdx);
u32 *destGeomCmds = PPTriPlantBuffer.GetPlantModelsCmd(slotID, modelIdx);
SpuRecordGeometry(&con1, pGeometry, destGeomCmds, PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE);
}//for(s32 i=0; i<CPLANT_SLOT_NUM_MODELS; i++)...
}
} //for(s32 slotID=0; i<PPTRIPLANT_MODELS_TAB_SIZE; slotID++)...
}
// Unbind LOD1
SpuRecordShaderUnBind(&con1);
// Bind LOD2 technique:
SpuRecordShaderBind(&con1, ms_shdDeferredLOD2TechniqueID, gpShaderLOD2Cmd0[0], PLANTSMGR_LOCAL_BINDSHADERLOD2_MAXCMDSIZE);
DumpMemoryAsRSXDump("BindShaderLOD2", gpShaderLOD2Cmd0[0], PLANTSMGR_LOCAL_BINDSHADERLOD2_MAXCMDSIZE);
{
// there's only one LOD2 geometry to deal with:
SpuRecordGeometry(&con1, ms_plantLOD2VertexBuffer, ms_plantLOD2VertexDecl, &gpPlantLOD2GeometryCmd0[0], PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE);
//DumpMemoryAsRSXDump("LOD2 geometry", gpPlantLOD2GeometryCmd0, PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE);
}
// Unbind LOD2
SpuRecordShaderUnBind(&con1);
GCM_CONTEXT = previousContext;
// reset internal Rage state:
if(1)
{
// CGrassRenderer::BindPlantShader(m_shdDeferredLOD0TechniqueID);
// CGrassRenderer::UnBindPlantShader();
grcEffectTechnique forcedTech = ms_shdDeferredLOD0TechniqueID;
ASSERT_ONLY(s32 numPasses =) ms_Shader->BeginDraw(grmShader::RMC_DRAW, /*restoreState*/FALSE, /*techOverride*/forcedTech);
Assert(numPasses>0);
const s32 pass=0;
ms_Shader->Bind(pass);
GRCDEVICE.ClearStreamSource(0);
ms_Shader->UnBind();
ms_Shader->EndDraw();
}
return(TRUE);
}// end of SpuRecordGeometries()...
//
//
//
//
bool CGrassRenderer::SpuRecordShaders(u32 bufID)
{
const s32 _cmdBufSize = 1024;
u32 _cmdBuf[_cmdBufSize]={0}; // fill with NOPs
CellGcmContext con1;
SetupContextData(&con1, _cmdBuf, _cmdBufSize*sizeof(u32), &CustomGcmReserveFailed);
CellGcmContextData *previousContext = GCM_CONTEXT;
GCM_CONTEXT = &con1;
grcEffectTechnique techLOD0ID = ms_shdDeferredLOD0TechniqueID;
grcEffectTechnique techLOD1ID = ms_shdDeferredLOD1TechniqueID;
grcEffectTechnique techLOD2ID = ms_shdDeferredLOD2TechniqueID;
#if __BANK
if(GetDebugModeEnable())
{
techLOD0ID = ms_shdDeferredLOD0DbgTechniqueID;
techLOD1ID = ms_shdDeferredLOD1DbgTechniqueID;
techLOD2ID = ms_shdDeferredLOD2DbgTechniqueID;
}
#endif
// 1. Records LOD0 technique:
SpuRecordShaderBind(&con1, techLOD0ID, gpShaderLOD0Cmd0[bufID], PLANTSMGR_LOCAL_BINDSHADERLOD0_MAXCMDSIZE);
// DumpMemoryAsRSXDump("BindShaderLOD0", gpShaderLOD0Cmd0, PLANTSMGR_LOCAL_BINDSHADERLOD0_MAXCMDSIZE);
SpuRecordShaderUnBind(&con1);
// 2. Record LOD1 technique:
SpuRecordShaderBind(&con1, techLOD1ID, gpShaderLOD1Cmd0[bufID], PLANTSMGR_LOCAL_BINDSHADERLOD1_MAXCMDSIZE);
// DumpMemoryAsRSXDump("BindShaderLOD1", gpShaderLOD1Cmd0, PLANTSMGR_LOCAL_BINDSHADERLOD1_MAXCMDSIZE);
SpuRecordShaderUnBind(&con1);
// 3. Records LOD2 technique:
SpuRecordShaderBind(&con1, techLOD2ID, gpShaderLOD2Cmd0[bufID], PLANTSMGR_LOCAL_BINDSHADERLOD2_MAXCMDSIZE);
// DumpMemoryAsRSXDump("BindShaderLOD2", gpShaderLOD2Cmd0, PLANTSMGR_LOCAL_BINDSHADERLOD2_MAXCMDSIZE);
SpuRecordShaderUnBind(&con1);
GCM_CONTEXT = previousContext;
return(TRUE);
}// end of SpuRecordShaders()...
#endif //PSN_PLANTSMGR_SPU_RENDER...
#if PSN_PLANTSMGR_SPU_RENDER
static spuGrassParamsStructMaster inSpuParamsMaster ALIGNED(128);
static spuGrassParamsStructMaster outSpuParamsMaster ALIGNED(128);
//
//
//
//
bool CGrassRenderer::DrawTriPlantsSPU(PPTriPlant *pCurrTriPlant, const u32 numTriPlants, u32 UNUSED_PARAM(slotID))
{
// enough space for new jobs data?
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
if(sm_nNumGrassJobsAdded >= spuTriPlantBlockTabCount)
#else
if(sm_nNumGrassJobsAdded >= PLANTSMGR_MAX_NUM_OF_RENDER_JOBS)
#endif
{
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
plantsWarningf("\nGrassRendererSPU: Max num of batches (%d) already running! Recycling some space to continue (taskID=0x%X).", spuTriPlantBlockTabCount, rsxLabel5_CurrentTaskID);
#else
// should it EVER happen during 1 frame?
plantsAssertf(0, "GrassRendererSPU: Max num of batches (%d) already running! Recycling some space to continue (taskID=0x%X).", PLANTSMGR_MAX_NUM_OF_RENDER_JOBS, rsxLabel5_CurrentTaskID);
#endif
// launch and wait for some launched jobs to finish first:
Assert(sm_GrassTaskHandle==NULL);
CGrassRenderer::SpuFinalisePerRenderFrame();
sysTaskManager::Wait(sm_GrassTaskHandle);
sm_GrassTaskHandle = NULL;
sm_nNumGrassJobsAdded = 0;
// update str memory usage stats for BigHeap:
SpuUpdateStrStats(&outSpuParamsMaster);
GRCDEVICE.KickOffGpu(); // flush drawablespu to make RSX consume just issued command buffers
}
const u32 jobIndex = sm_nNumGrassJobsAdded;
// copy data to aligned structures
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
PPTriPlant *spuTriPlantPtr0 = spuTriPlantBlockTab[jobIndex];
#else
PPTriPlant *spuTriPlantPtr0 = &spuTriPlantTab[jobIndex][0];
#endif
sysMemCpy(spuTriPlantPtr0, pCurrTriPlant, sizeof(PPTriPlant)*numTriPlants);
// setup data for this job:
spuGrassParamsStruct *inSpuGrassStruct = &inSpuGrassStructTab[jobIndex];
ASSERT16(inSpuGrassStruct);
inSpuGrassStruct->m_pTriPlant = spuTriPlantPtr0;
#if PLANTSMGR_TRIPLANTTAB_IN_BLOCKS
ASSERT128(spuTriPlantPtr0);
#else
ASSERT16(spuTriPlantPtr0);
#endif
inSpuGrassStruct->m_numTriPlant = numTriPlants;
Assert(numTriPlants <= PPTRIPLANT_BUFFER_SIZE);
Assert(numTriPlants < 65536); // must fit into u16
sm_nNumGrassJobsAdded++; // # of jobs added this batch
sm_nNumGrassJobsAddedTotal++; // total # of jobs
return(TRUE);
}
//
//
// launches master job to draw grass:
//
bool CGrassRenderer::SpuFinalisePerRenderFrame()
{
// is there anything to do?
if(!sm_nNumGrassJobsAdded)
return(FALSE);
Assert(sm_GrassTaskHandle==NULL);
GRCDBG_PUSH("GrassSpuFinalisePerRenderFrame");
const Vector3 globalCameraPos(GetGlobalCameraPos());
const Vector3 globalPlayerPos(GetGlobalPlayerPos());
ms_Shader->RecordSetVar(ms_shdCameraPosID, globalCameraPos);
ms_Shader->RecordSetVar(ms_shdPlayerPosID, globalPlayerPos);
for(u32 v=0; v<NUM_COL_VEH; v++)
{
bool bVehCollisionEnabled=FALSE;
Vector4 vecVehCollisionB, vecVehCollisionM, vecVehCollisionR;
GetGlobalVehCollisionParams(v, &bVehCollisionEnabled, &vecVehCollisionB, &vecVehCollisionM, &vecVehCollisionR);
grcEffect::SetGlobalVar(ms_shdVehCollisionEnabledID[v], bVehCollisionEnabled);
ms_Shader->RecordSetVar(ms_shdVehCollisionBID[v], vecVehCollisionB);
ms_Shader->RecordSetVar(ms_shdVehCollisionMID[v], vecVehCollisionM);
ms_Shader->RecordSetVar(ms_shdVehCollisionRID[v], vecVehCollisionR);
}
Vector4 fakeNormal;
fakeNormal.SetVector3ClearW(GetFakeGrassNormal());
fakeNormal.SetW(1.0f);
ms_Shader->RecordSetVar(ms_shdFakedGrassNormal, fakeNormal);
#if __BANK
extern bool gbPlantsGeometryCulling;
dev_bool bGeomCullingTestEnabled = gbPlantsGeometryCulling;
extern bool gbPlantsPremultiplyNdotL;
dev_bool bPremultiplyNdotL = gbPlantsPremultiplyNdotL;
#endif
#if __BANK
extern bool gbPlantsFlashLOD0, gbPlantsFlashLOD1, gbPlantsFlashLOD2;
extern bool gbPlantsDisableLOD0, gbPlantsDisableLOD1, gbPlantsDisableLOD2;
extern float gbPlantsLOD0AlphaCloseDist, gbPlantsLOD0AlphaFarDist, gbPlantsLOD0FarDist;
extern float gbPlantsLOD1CloseDist, gbPlantsLOD1FarDist, gbPlantsLOD1Alpha0CloseDist, gbPlantsLOD1Alpha0FarDist, gbPlantsLOD1Alpha1CloseDist, gbPlantsLOD1Alpha1FarDist;
extern float gbPlantsLOD2CloseDist, gbPlantsLOD2FarDist, gbPlantsLOD2Alpha0CloseDist, gbPlantsLOD2Alpha0FarDist, gbPlantsLOD2Alpha1CloseDist, gbPlantsLOD2Alpha1FarDist;
extern float gbPlantsLOD2Alpha1CloseDistFar, gbPlantsLOD2Alpha1FarDistFar;
const float globalLOD0AlphaCloseDist = gbPlantsLOD0AlphaCloseDist;
float globalLOD0AlphaFarDist = gbPlantsLOD0AlphaFarDist;
const float globalLOD0FarDist = gbPlantsLOD0FarDist;
const float globalLOD1CloseDist = gbPlantsLOD1CloseDist;
const float globalLOD1FarDist = gbPlantsLOD1FarDist;
const float globalLOD1Alpha0CloseDist = gbPlantsLOD1Alpha0CloseDist;
const float globalLOD1Alpha0FarDist = gbPlantsLOD1Alpha0FarDist;
const float globalLOD1Alpha1CloseDist = gbPlantsLOD1Alpha1CloseDist;
const float globalLOD1Alpha1FarDist = gbPlantsLOD1Alpha1FarDist;
const float globalLOD2CloseDist = gbPlantsLOD2CloseDist;
const float globalLOD2FarDist = gbPlantsLOD2FarDist;
const float globalLOD2Alpha0CloseDist = gbPlantsLOD2Alpha0CloseDist;
const float globalLOD2Alpha0FarDist = gbPlantsLOD2Alpha0FarDist;
const float globalLOD2Alpha1CloseDist = gbPlantsLOD2Alpha1CloseDist;
const float globalLOD2Alpha1FarDist = gbPlantsLOD2Alpha1FarDist;
const float globalLOD2Alpha1CloseDistFar= gbPlantsLOD2Alpha1CloseDistFar;
const float globalLOD2Alpha1FarDistFar = gbPlantsLOD2Alpha1FarDistFar;
#else
const float globalLOD0AlphaCloseDist = CPLANT_LOD0_ALPHA_CLOSE_DIST;
float globalLOD0AlphaFarDist = CPLANT_LOD0_ALPHA_FAR_DIST;
const float globalLOD0FarDist = CPLANT_LOD0_FAR_DIST;
const float globalLOD1CloseDist = CPLANT_LOD1_CLOSE_DIST;
const float globalLOD1FarDist = CPLANT_LOD1_FAR_DIST;
const float globalLOD1Alpha0CloseDist = CPLANT_LOD1_ALPHA0_CLOSE_DIST;
const float globalLOD1Alpha0FarDist = CPLANT_LOD1_ALPHA0_FAR_DIST;
const float globalLOD1Alpha1CloseDist = CPLANT_LOD1_ALPHA1_CLOSE_DIST;
const float globalLOD1Alpha1FarDist = CPLANT_LOD1_ALPHA1_FAR_DIST;
const float globalLOD2CloseDist = CPLANT_LOD2_CLOSE_DIST;
const float globalLOD2FarDist = CPLANT_LOD2_FAR_DIST;
const float globalLOD2Alpha0CloseDist = CPLANT_LOD2_ALPHA0_CLOSE_DIST;
const float globalLOD2Alpha0FarDist = CPLANT_LOD2_ALPHA0_FAR_DIST;
const float globalLOD2Alpha1CloseDist = CPLANT_LOD2_ALPHA1_CLOSE_DIST;
const float globalLOD2Alpha1FarDist = CPLANT_LOD2_ALPHA1_FAR_DIST;
const float globalLOD2Alpha1CloseDistFar= CPLANT_LOD2_ALPHA1_CLOSE_DIST_FAR;
const float globalLOD2Alpha1FarDistFar = CPLANT_LOD2_ALPHA1_FAR_DIST_FAR;
#endif
float globalLOD0FarDist2 = globalLOD0FarDist*globalLOD0FarDist;
float globalLOD1CloseDist2 = globalLOD1CloseDist*globalLOD1CloseDist;
float globalLOD1FarDist2 = globalLOD1FarDist*globalLOD1FarDist;
float globalLOD2CloseDist2 = globalLOD2CloseDist*globalLOD2CloseDist;
float globalLOD2FarDist2 = globalLOD2FarDist*globalLOD2FarDist;
if(CGrassRenderer::GetGlobalCameraFppEnabled() || CGrassRenderer::GetGlobalForceHDGrassGeometry())
{ // special case: camera is in FPP mode - force LOD0 to be visible everywhere:
globalLOD0AlphaFarDist = globalLOD2Alpha1FarDistFar;
globalLOD0FarDist2 = globalLOD2FarDist2;
globalLOD1CloseDist2 =
globalLOD1FarDist2 =
globalLOD2CloseDist2 =
globalLOD2FarDist2 = 999999.0f;
}
// LOD0 alpha, umTimer:
{
Vector4 vecAlphaFade4; // [XY=alphaFade01 | Z=umTimer | ?]
#if 1
// square distance approach:
const float closeDist = globalLOD0AlphaCloseDist;
const float farDist = globalLOD0AlphaFarDist;
const float alphaDenom = 1.0f / (farDist*farDist - closeDist*closeDist);
vecAlphaFade4.x = (farDist*farDist) * alphaDenom; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom; // y = (-1.0)/ (f2-c2)
#else
// linear distance approach:
const float alphaDenom = 1.0f / (m_farDist - m_closeDist);
vecAlphaFade4.x = (m_farDist) * alphaDenom; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom; // y = (-1.0)/ (f2-c2)
#endif
vecAlphaFade4.z = (float)(2.0*PI*(double(fwTimer::GetTimeInMilliseconds())/1000.0));
vecAlphaFade4.w = 0.0f;
ms_Shader->RecordSetVar(ms_shdFadeAlphaDistUmTimerID, vecAlphaFade4);
}
// LOD1 alpha:
{
Vector4 vecAlphaFade4; // [XY=alpha0Fade01 | ZW=alpha1Fade01]
const float closeDist0 = globalLOD1Alpha0CloseDist;
const float farDist0 = globalLOD1Alpha0FarDist;
const float alphaDenom0 = 1.0f / (farDist0*farDist0 - closeDist0*closeDist0);
vecAlphaFade4.x = (farDist0*farDist0) * alphaDenom0; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom0; // y = (-1.0)/ (f2-c2)
const float closeDist1 = globalLOD1Alpha1CloseDist;
const float farDist1 = globalLOD1Alpha1FarDist;
const float alphaDenom1 = 1.0f / (farDist1*farDist1 - closeDist1*closeDist1);
vecAlphaFade4.z = (farDist1*farDist1) * alphaDenom1; // x = (f2) / (f2-c2)
vecAlphaFade4.w = (-1.0f) * alphaDenom1; // y = (-1.0)/ (f2-c2)
ms_Shader->RecordSetVar(ms_shdFadeAlphaLOD1DistID, vecAlphaFade4);
}
// LOD2 alpha:
{
Vector4 vecAlphaFade4; // [XY=alpha0Fade01 | ZW=alpha1Fade01]
const float closeDist0 = globalLOD2Alpha0CloseDist;
const float farDist0 = globalLOD2Alpha0FarDist;
const float alphaDenom0 = 1.0f / (farDist0*farDist0 - closeDist0*closeDist0);
vecAlphaFade4.x = (farDist0*farDist0) * alphaDenom0; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom0; // y = (-1.0)/ (f2-c2)
const float closeDist1 = globalLOD2Alpha1CloseDist;
const float farDist1 = globalLOD2Alpha1FarDist;
const float alphaDenom1 = 1.0f / (farDist1*farDist1 - closeDist1*closeDist1);
vecAlphaFade4.z = (farDist1*farDist1) * alphaDenom1; // x = (f2) / (f2-c2)
vecAlphaFade4.w = (-1.0f) * alphaDenom1; // y = (-1.0)/ (f2-c2)
ms_Shader->RecordSetVar(ms_shdFadeAlphaLOD2DistID, vecAlphaFade4);
Vector4 vecAlphaFadeFar4; // [XY=alpha1FadeFar01 | 0 | 0]
const float closeDistFar0 = globalLOD2Alpha1CloseDistFar;
const float farDistFar0 = globalLOD2Alpha1FarDistFar;
const float alphaDenomFar0 = 1.0f / (farDistFar0*farDistFar0 - closeDistFar0*closeDistFar0);
vecAlphaFadeFar4.x = (farDistFar0*farDistFar0) * alphaDenomFar0; // x = (f2) / (f2-c2)
vecAlphaFadeFar4.y = (-1.0f) * alphaDenomFar0; // y = (-1.0)/ (f2-c2)
vecAlphaFadeFar4.z = 0.0f;
vecAlphaFadeFar4.w = 0.0f;
ms_Shader->RecordSetVar(ms_shdFadeAlphaLOD2DistFarID, vecAlphaFadeFar4);
}
PF_PUSH_TIMEBAR_DETAIL("PlantsMgr::Render::WaitForRSX");
// make sure RSX is not executing our previous buffer:
// (another way to check this is to set label after RSX has returned to main CB
// and read it here, however this way seem quicker):
if(1)
{
CellGcmControl volatile *control = cellGcmGetControlRegister();
uint32_t get = control->get;
u32 deadman=20;
#if PLANTSMGR_BIG_HEAP_IN_BLOCKS
while( BigHeapBlockArrayContainsOffset(sm_BigHeapID, PLANTSMGR_LOCAL_HEAP_SIZE, get)
#else
while(((get >= sm_BigHeapOffset[sm_BigHeapID]) && (get < (sm_BigHeapOffset[sm_BigHeapID]+sm_BigHeapSize[sm_BigHeapID])))
#endif
#if SPU_GCM_FIFO
|| ((get >= sm_StartupHeapOffset[sm_BigHeapID]) && (get < (sm_StartupHeapOffset[sm_BigHeapID]+STARTUP_HEAP_SIZE)))
#endif
)
{
sys_timer_usleep(30);
get = control->get;
if(!(--deadman))
{
deadman=20;
plantsDebugf1("\n PlantsMgrSPU: Task %X has to wait - RSX is still executing previous command buffer!", rsxLabel5_CurrentTaskID);
}
}
}
// make sure RSX really executed prevPrev command buffer:
if(1)
{
const u32 prevPrevTaskID= 0xFF000000|(((sm_BigHeapID )&0x1)<<8)| ((rsxLabel5_CurrentTaskID-2)&0xFF);
const u32 prevTaskID0 = ((rsxLabel5_CurrentTaskID-1)&0xFF);
const u32 prevTaskID1 = 0xFF000000|(((sm_BigHeapID-1)&0x1)<<8)| ((rsxLabel5_CurrentTaskID-1)&0xFF);
u32 label5 = *rsxLabel5Ptr;
//plantsDebugf1("\n**Grass: label5=%X, prevTaskID=%X", label5, prevTaskID);
u32 deadman=40;
while((label5 != prevPrevTaskID) && (label5 != prevTaskID0) && (label5 != prevTaskID1))
{
sys_timer_usleep(30);
if(!(--deadman))
{
deadman=40;
plantsDebugf1("\nPPU: PlantsMgrSPU: PrevPrevJob %X still not executed by RSX [label5=%08X]!", prevPrevTaskID,label5);
}
label5 = *rsxLabel5Ptr;
}
}
PF_POP_TIMEBAR_DETAIL(); //WaitForRSX()...
GRCDBG_PUSH("RecordShaders");
// record all shaders for SPU:
CGrassRenderer::SpuRecordShaders(sm_BigHeapID);
BindPlantShader(ms_shdDeferredLOD0TechniqueID);
GRCDBG_POP();
GRCDBG_PUSH("RenderBegin");
{
// set waiting marker for SPU:
#if SPU_GCM_FIFO
// mStartupHeapPtr = small startup "bridge" command buffer from Rage's FIFO to plants CB and then to jump back:
CellGcmContext startupCon;
CellGcmContextData* startupConData = (CellGcmContextData*)&startupCon;
SetupContextData(&startupCon, (u32*)sm_StartupHeapPtr[sm_BigHeapID], STARTUP_HEAP_SIZE, &CustomGcmReserveFailed);
startupCon.SetWriteCommandLabel(rsxLabel5Index, rsxLabel5_CurrentTaskID);
#else //SPU_GCM_FIFO
GCM_DEBUG(cellGcmSetWriteCommandLabel(GCM_CONTEXT, rsxLabel5Index, rsxLabel5_CurrentTaskID));
#endif //SPU_GCM_FIFO...
//GRCDEVICE.KickOffGpu(); // set PUT pointer to end of current buffer
//plantsDebugf1("\n PlantsRenderer: setLabel5=%X at %X(%X)", rsxLabel5_CurrentTaskID, u32(GCM_CONTEXT->current),gcm::MainOffset((u8*)(GCM_CONTEXT->current)));
#if SPU_GCM_FIFO
// JTS in startupCon (will be patched to jump to big heap):
u32 offsetJTS0 = ((u8*)startupConData->current) - ((u8*)startupConData->begin);
while( offsetJTS0 & 0x0000000f ) // fix pointer to be 16 byte aligned
{
startupCon.SetNopCommand(1);
offsetJTS0 = ((u8*)startupConData->current) - ((u8*)startupConData->begin);
}
ASSERT16(offsetJTS0);
u32 eaMainJTS = u32(sm_StartupHeapPtr[sm_BigHeapID]) + offsetJTS0;
startupCon.SetJumpCommand(u32(sm_StartupHeapOffset[sm_BigHeapID]) + offsetJTS0); // JTS
startupCon.SetNopCommand(7);
const u32 rsxReturnOffsetToMainCB = (u32)PLANTS_GCM_OFFSET((u8*)startupCon.current);
startupCon.SetNopCommand(8);
// "job executed" marker:
startupCon.SetWriteCommandLabel(rsxLabel5Index, 0xFF000000|(sm_BigHeapID<<8)|rsxLabel5_CurrentTaskID);
//plantsDebugf1("\n setting label5=%X",0xFF000000|(mBigHeapID<<8)|rsxLabel5_CurrentTaskID);
#else
Assert(0);
#endif
#if SPU_GCM_FIFO
// jump from Rage's GCM FIFO to plants' startup FIFO and patch jump back:
SPU_COMMAND(grcDevice__Jump, 0);
// Local address of the jump target
cmd->jumpLocalOffset = PLANTS_GCM_OFFSET(startupCon.begin);
// PPU address of the return jump
cmd->jumpBack = startupCon.current;
ASSERT16(startupCon.current);
// plantsDebugf1("\n startupCon.current=%X",(u32)startupCon.current);
if(0)
{
plantsDebugf1("\n grcDevice__Jump: jumpLocalAddr=%X, jumpBack=%p", cmd->jumpLocalOffset, cmd->jumpBack);
plantsDebugf1("\n (jumpLocalAddr: L=%X, M=%X) (jumpBack: L=%X, M=%X)", (u32)PLANTS_GCM_OFFSET(startupCon.begin), (u32)startupCon.begin, (u32)PLANTS_GCM_OFFSET(startupCon.current), (u32)startupCon.current);
}
#endif //SPU_GCM_FIFO...
sysTaskParameters p;
sysMemSet(&p,0x00,sizeof(p));
// "master" job data:
inSpuParamsMaster.m_nNumGrassJobsLaunched = sm_nNumGrassJobsAdded;
inSpuParamsMaster.m_inSpuGrassStructTab = &inSpuGrassStructTab[0];
#if PLANTSMGR_BIG_HEAP_IN_BLOCKS
inSpuParamsMaster.m_heapBlockArray = sm_BigHeapBlockArray[sm_BigHeapID];
inSpuParamsMaster.m_heapBlockArrayCount = sm_BigHeapBlockArrayCount[sm_BigHeapID];
Assert128(inSpuParamsMaster.m_heapBlockArray);
Assert(inSpuParamsMaster.m_heapBlockArrayCount > 0);
#else
inSpuParamsMaster.m_heapBegin = (u32)sm_BigHeap[sm_BigHeapID];
inSpuParamsMaster.m_heapBeginOffset = sm_BigHeapOffset[sm_BigHeapID];
inSpuParamsMaster.m_heapSize = sm_BigHeapSize[sm_BigHeapID];
AlignedAssertf(inSpuParamsMaster.m_heapSize/1024, PLANTSMGR_LOCAL_HEAP_SIZE/1024, "heapSize must be multiple of PLANTSMGR_LOCAL_HEAP_SIZE!");
Assert(inSpuParamsMaster.m_heapSize >= PLANTSMGR_LOCAL_HEAP_SIZE);
#endif
inSpuParamsMaster.m_heapID = sm_BigHeapID;
inSpuParamsMaster.m_ReturnOffsetToMainCB = rsxReturnOffsetToMainCB;
inSpuParamsMaster.m_eaMainJTS = eaMainJTS;
inSpuParamsMaster.m_rsxLabel5_CurrentTaskID = rsxLabel5_CurrentTaskID; // value of wait tag in Label4 for current job
inSpuParamsMaster.m_DeviceFrameCounter = GRCDEVICE.GetFrameCounter();
inSpuParamsMaster.m_rsxLabel5Index = rsxLabel5Index;
inSpuParamsMaster.m_eaRsxLabel5 = (u32)rsxLabel5Ptr;
const Vector3 vecCameraPos = CGrassRenderer::GetGlobalCameraPos();
inSpuParamsMaster.m_vecCameraPos.SetVector3(vecCameraPos);
inSpuParamsMaster.m_vecCameraPos.w = 0.0f;
GetGlobalWindBending(inSpuParamsMaster.m_windBending);
#if CPLANT_PRE_MULTIPLY_LIGHTING
inSpuParamsMaster.m_mainDirectionalLight_natAmbient = Vector4(-Lights::GetRenderDirectionalLight().GetDirection());
inSpuParamsMaster.m_mainDirectionalLight_natAmbient.w = Lights::m_lightingGroup.GetNaturalAmbientBase().w;
#endif
inSpuParamsMaster.m_LOD0FarDist2 = globalLOD0FarDist2;
inSpuParamsMaster.m_LOD1CloseDist2 = globalLOD1CloseDist2;
inSpuParamsMaster.m_LOD1FarDist2 = globalLOD1FarDist2;
inSpuParamsMaster.m_LOD2CloseDist2 = globalLOD2CloseDist2;
inSpuParamsMaster.m_LOD2FarDist2 = globalLOD2FarDist2;
inSpuParamsMaster.m_BindShaderLOD0Offset = gpShaderLOD0Offset[sm_BigHeapID];
inSpuParamsMaster.m_BindShaderLOD1Offset = gpShaderLOD1Offset[sm_BigHeapID];
inSpuParamsMaster.m_BindShaderLOD2Offset = gpShaderLOD2Offset[sm_BigHeapID];
inSpuParamsMaster.m_GeometryLOD2Offset = gpPlantLOD2GeometryOffset;
inSpuParamsMaster.m_PlantTexturesOffsetTab = PPTriPlantBuffer.GetPlantTexturesCmdOffset(0, 0);
inSpuParamsMaster.m_PlantModelsTab = PPTriPlantBuffer.GetPlantModelsTab(0);
ASSERT128(inSpuParamsMaster.m_PlantModelsTab);
ASSERT16(PLANTSMGR_LOCAL_SLOTSIZE_TEXTURE);
ASSERT16(PLANTSMGR_LOCAL_SLOTSIZE_MODEL);
#if CPLANT_DYNAMIC_CULL_SPHERES
const u32 id = gPlantMgr.GetRenderRTBufferID();
inSpuParamsMaster.m_CullSphereArray = &(gPlantMgr.GetCullSphereArray(id));
#endif
inSpuParamsMaster.m_DecrementerTicks = 0;
#if __BANK
// statistics & debug:
inSpuParamsMaster.m_bPlantsFlashLOD0 = gbPlantsFlashLOD0;
inSpuParamsMaster.m_bPlantsFlashLOD1 = gbPlantsFlashLOD1;
inSpuParamsMaster.m_bPlantsFlashLOD2 = gbPlantsFlashLOD2;
inSpuParamsMaster.m_bPlantsDisableLOD0 = gbPlantsDisableLOD0;
inSpuParamsMaster.m_bPlantsDisableLOD1 = gbPlantsDisableLOD1;
inSpuParamsMaster.m_bPlantsDisableLOD2 = gbPlantsDisableLOD2;
inSpuParamsMaster.m_bGeomCullingTestEnabled = bGeomCullingTestEnabled;
inSpuParamsMaster.m_bPremultiplyNdotL = bPremultiplyNdotL;
inSpuParamsMaster.m_LocTriPlantsLOD0DrawnPerSlot = 0;
inSpuParamsMaster.m_LocTriPlantsLOD1DrawnPerSlot = 0;
inSpuParamsMaster.m_LocTriPlantsLOD2DrawnPerSlot = 0;
#endif //__BANK...
// current culling frustum (current VP or from portal):
GetGlobalCullFrustum( &inSpuParamsMaster.m_cullFrustum );
ASSERT128(&inSpuParamsMaster);
ASSERT128(&outSpuParamsMaster);
ASSERT16(sizeof(inSpuParamsMaster));
ASSERT16(sizeof(outSpuParamsMaster));
p.Input.Data = (void*)&inSpuParamsMaster;
p.Input.Size = sizeof(inSpuParamsMaster);
p.Output.Data = (void*)&outSpuParamsMaster;
p.Output.Size = sizeof(outSpuParamsMaster);
p.Scratch.Size = 28 * 1024; // allocate 28KB for scratch
p.SpuStackSize = 16 * 1024; // allocate 16KB for stack
// run the category update job
sys_lwsync();
sm_GrassTaskHandle = sysTaskManager::Create(TASK_INTERFACE(PlantsGrassRendererSPU),p, sysTaskManager::SCHEDULER_GRAPHICS_OTHER);
Assertf(sm_GrassTaskHandle, "PSN: Error creating SPU grass job!");
//plantsDebugf1("\n GrassRendererSPU: launching job: (id=%X) (numjobs=%d) (gcm::current=%X(%X))", rsxLabel5_CurrentTaskID, sm_nNumGrassJobsAdded, u32(GCM_CONTEXT->current),gcm::MainOffset((u8*)GCM_CONTEXT->current) );
(++sm_BigHeapID)&=0x01;
(++rsxLabel5_CurrentTaskID)&=0xFF;
}
GRCDBG_POP();
GRCDBG_PUSH("RenderEnd");
UnBindPlantShader();
InvalidateSpuGcmState(CachedStates.VertexFormats, -1);
InvalidateSpuGcmState(CachedStates.SamplerState[ms_shdTextureTexUnit], -1);
// make sure processing is finished
if(0 && sm_GrassTaskHandle)
{
sysTaskManager::Wait(sm_GrassTaskHandle);
sm_GrassTaskHandle=NULL;
//plantsDebugf1("\n pad0=%d, pad1=%d, pad2=%d\n",outSpuGrassStruct.pad0,outSpuGrassStruct.pad1,outSpuGrassStruct.pad2);
}
GRCDBG_POP();
GRCDBG_POP(); // "CGrassRenderer::SpuFinalisePerRenderFrame"...
return(TRUE);
}// end of FinalizePerRenderFrame()...
static bool bInitialisedPerRenderFrameRunOnce = FALSE;
//
//
//
//
bool CGrassRenderer::SpuInitialisePerRenderFrame()
{
// run once:
if(!bInitialisedPerRenderFrameRunOnce)
{
bInitialisedPerRenderFrameRunOnce = TRUE;
if(!CGrassRenderer::SpuRecordGeometries())
{
return(FALSE);
}
}
if(sm_GrassTaskHandle)
{
// plantsDebugf1("\n GrassRendererSPU: Still waiting for master job to finish.");
sysTaskManager::Wait(sm_GrassTaskHandle);
sm_GrassTaskHandle=NULL;
}
// plantsDebugf1("\n GrassSPU: Execution ticks: %.2f", float(outSpuParamsMaster.m_DecrementerTicks)/1000000.0f);
// reset num jobs launched:
sm_nNumGrassJobsAdded = 0;
sm_nNumGrassJobsAddedTotal = 0;
// str memory usage stats for BigBuffer:
SpuUpdateStrStats(&outSpuParamsMaster);
#if __BANK
// rendering stats:
extern u32 nLocTriPlantsLOD0Drawn;
extern u32 nLocTriPlantsLOD1Drawn;
extern u32 nLocTriPlantsLOD2Drawn;
nLocTriPlantsLOD0Drawn = outSpuParamsMaster.m_LocTriPlantsLOD0DrawnPerSlot;
nLocTriPlantsLOD1Drawn = outSpuParamsMaster.m_LocTriPlantsLOD1DrawnPerSlot;
nLocTriPlantsLOD2Drawn = outSpuParamsMaster.m_LocTriPlantsLOD2DrawnPerSlot;
outSpuParamsMaster.m_LocTriPlantsLOD0DrawnPerSlot=0;
outSpuParamsMaster.m_LocTriPlantsLOD1DrawnPerSlot=0;
outSpuParamsMaster.m_LocTriPlantsLOD2DrawnPerSlot=0;
#endif //__BANK...
return(TRUE);
}// end of InitialisePerRenderFrame()...
//
//
// str memory usage stats for BigBuffer:
//
bool CGrassRenderer::SpuUpdateStrStats(spuGrassParamsStructMaster *outParams)
{
FastAssert(CSystem::IsThisThreadId(SYS_THREAD_RENDER));
const u32 heapID = outParams->m_heapID;
#if PLANTSMGR_BIG_HEAP_IN_BLOCKS
sm_BigHeapAllocatedSize[heapID] = outParams->m_heapBlockArrayCount;
#else
sm_BigHeapAllocatedSize[heapID] = outParams->m_heapSize;
#endif
sm_BigHeapConsumed[heapID] = outParams->m_AmountOfBigHeapConsumed;
sm_BigHeapOverfilled[heapID] = outParams->m_AmountOfBigHeapOverfilled;
if(sm_BigHeapConsumed[heapID] > sm_BigHeapBiggestConsumed[heapID])
{
sm_BigHeapBiggestConsumed[heapID] = sm_BigHeapConsumed[heapID];
}
// reset mem usage stats:
outParams->m_AmountOfBigHeapConsumed = 0;
outParams->m_AmountOfBigHeapOverfilled = 0;
return(TRUE);
}// end of SpuUpdateStrStats()...
#else //PSN_PLANTSMGR_SPU_RENDER
//
//
//
//
#if PLANTSMGR_MULTI_RENDER
#if GRASS_INSTANCING==0
#error "Multirender requires GRASS_INSTANCING!"
#endif
static sysCriticalSectionToken sm_csAddInstLod0Token[NUMBER_OF_RENDER_THREADS]; // AddInstanced access token during multi render
static sysCriticalSectionToken sm_csAddInstLod1Token[NUMBER_OF_RENDER_THREADS];
static sysCriticalSectionToken sm_csAddInstLod2Token[NUMBER_OF_RENDER_THREADS];
static CGrassRenderer::CParamsDrawTriPlantsMulti sm_paramsDrawMulti[NUMBER_OF_RENDER_THREADS];
//
//
//
//
bool CGrassRenderer::DrawTriPlantsMulti(const u8 rti, const bool bMainRT, PPTriPlant *triPlants, const u32 numTriPlants, grassModel* plantModelsTab,
CParamsDrawTriPlantsMulti *pParamsDrawMulti)
{
#if __BANK
extern bool gbPlantsFlashLOD0, gbPlantsFlashLOD1, gbPlantsFlashLOD2;
extern bool gbPlantsDisableLOD0, gbPlantsDisableLOD1, gbPlantsDisableLOD2;
#endif // __BANK
mthRandom plantsRand;
const float fLOD0FarDist2 = pParamsDrawMulti->m_LOD0FarDist2;
const float fLOD1CloseDist2 = pParamsDrawMulti->m_LOD1CloseDist2;
const float fLOD1FarDist2 = pParamsDrawMulti->m_LOD1FarDist2;
const float fLOD2CloseDist2 = pParamsDrawMulti->m_LOD2CloseDist2;
const float fLOD2FarDist2 = pParamsDrawMulti->m_LOD2FarDist2;
const Vector3 globalCameraPos(pParamsDrawMulti->m_CameraPos);
const Vector3 globalPlayerPos(pParamsDrawMulti->m_PlayerPos);
const Vector2 globalWindBending(pParamsDrawMulti->m_WindBending);
spdTransposedPlaneSet8 cullFrustum = ms_cullingFrustum[rti];
#if __BANK
extern bool gbPlantsGeometryCulling;
dev_bool bGeomCullingTestEnabled = gbPlantsGeometryCulling;
extern bool gbPlantsPremultiplyNdotL;
dev_bool bPremultiplyNdotL = gbPlantsPremultiplyNdotL;
#else
dev_bool bGeomCullingTestEnabled = TRUE;
dev_bool bPremultiplyNdotL = TRUE;
#endif
#if CPLANT_PRE_MULTIPLY_LIGHTING
const Vector3 sunDir = pParamsDrawMulti->m_SunDir;
const float naturalAmbient = pParamsDrawMulti->m_NaturalAmbient;
#endif
instGrassDataDrawStruct customInstDraw;
Matrix34 plantMatrix;
Matrix34 skewingMtx;
customInstDraw.Reset();
for(u32 i=0; i<numTriPlants; i++)
{
PPTriPlant *pCurrTriPlant = &triPlants[i];
const float wind_bend_scale = pCurrTriPlant->V1.w; // wind bending scale
const float wind_bend_var = pCurrTriPlant->V2.w; // wind bending variation
const bool bApplyGroundSkewing = (pCurrTriPlant->skewAxisAngle.w.GetBinaryData()!=0); // technically this tests for +0 but not -0
if(bApplyGroundSkewing)
{
const Vec4V unpacked_skewAxisAngle = Float16Vec4Unpack(&pCurrTriPlant->skewAxisAngle);
skewingMtx.MakeRotateUnitAxis(RCC_VECTOR3(unpacked_skewAxisAngle), unpacked_skewAxisAngle.GetWf());
}
const bool bEnableCulling = pCurrTriPlant->flags.IsClear(PROCPLANT_CAMERADONOTCULL);
float NdotL = 1.0f;
#if CPLANT_WRITE_GRASS_NORMAL || CPLANT_PRE_MULTIPLY_LIGHTING
// set normal for whole pTriLoc:
Vector4 groundNormal;
groundNormal.SetX(float(pCurrTriPlant->loctri_normal[0])/127.0f);
groundNormal.SetY(float(pCurrTriPlant->loctri_normal[1])/127.0f);
groundNormal.SetZ(float(pCurrTriPlant->loctri_normal[2])/127.0f);
groundNormal.SetW(0.0f);
#if CPLANT_PRE_MULTIPLY_LIGHTING
if(bPremultiplyNdotL)
{
// For pre-multiplying lighting into the color channel
Vector4 sunDir_Vec4(sunDir.x, sunDir.y, sunDir.z, 0.0f);
NdotL = Saturate(groundNormal.Dot(sunDir_Vec4)) + naturalAmbient;
}
#endif
#if CPLANT_WRITE_GRASS_NORMAL
ms_Shader->SetVar(ms_shdTerrainNormal, groundNormal);
#endif
#endif // CPLANT_WRITE_GRASS_NORMAL || CPLANT_PRE_MULTIPLY_LIGHTING
// want same values for plant positions each time...
plantsRand.Reset(pCurrTriPlant->seed);
// intensity support: final_I = I + VarI*rand01()
const s16 intensityVar = pCurrTriPlant->intensity_var;
u16 intensity = pCurrTriPlant->intensity + u16(float(intensityVar)*plantsRand.GetRanged(0.0f, 1.0f));
intensity = MIN(static_cast<u16>(intensity * NdotL), 255);
Color32 plantColour32;
plantColour32.SetAlpha( pCurrTriPlant->color.GetAlpha());
#define CALC_COLOR_INTENSITY(COLOR, I) u8((u16(COLOR)*I) >> 8)
plantColour32.SetRed( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetRed(), intensity));
plantColour32.SetGreen( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetGreen(), intensity));
plantColour32.SetBlue( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetBlue(), intensity));
#undef CALC_COLOR_INTENSITY
grcTexture *pPlantTextureLOD0 = pCurrTriPlant->texture_ptr;
Assert(pPlantTextureLOD0);
grcTexture *pPlantTextureLOD1 = pCurrTriPlant->textureLOD1_ptr;
Assert(pPlantTextureLOD1);
grmGeometry *pGeometryLOD0 = plantModelsTab[pCurrTriPlant->model_id].m_pGeometry;
const Vector4 boundSphereLOD0 = plantModelsTab[pCurrTriPlant->model_id].m_BoundingSphere;
grmGeometry *pGeometryLOD1 = plantModelsTab[pCurrTriPlant->model_id+CPLANT_SLOT_NUM_MODELS].m_pGeometry;
const Vector4 boundSphereLOD1 = plantModelsTab[pCurrTriPlant->model_id+CPLANT_SLOT_NUM_MODELS].m_BoundingSphere;
const Vector2 GeometryDimLOD2 = plantModelsTab[pCurrTriPlant->model_id+CPLANT_SLOT_NUM_MODELS].m_dimensionLOD2;
const Vector4 boundSphereLOD2(0,0,0,1.0f);
// set global micro-movement params: [ scaleH | scaleV | freqH | freqV ]
const Vec4V unpacked_um_params = Float16Vec4Unpack(&pCurrTriPlant->um_param);
// set global collision radius scale:
const Vec2V unpacked_coll_params = Float16Vec2Unpack(&pCurrTriPlant->coll_params);
// LOD2 billboard dimensions:
Vector4 dimLOD2;
dimLOD2.x = GeometryDimLOD2.x;
dimLOD2.y = GeometryDimLOD2.y;
dimLOD2.z = pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2FARFADE)? 1.0f : 0.0f;
dimLOD2.w = 0.0f;
u8 ambientScale = pCurrTriPlant->ambient_scl;
Vector3 *v1 = (Vector3*)&pCurrTriPlant->V1;
Vector3 *v2 = (Vector3*)&pCurrTriPlant->V2;
Vector3 *v3 = (Vector3*)&pCurrTriPlant->V3;
Color32 *c1 = &pCurrTriPlant->groundColorV1;
Color32 *c2 = &pCurrTriPlant->groundColorV2;
Color32 *c3 = &pCurrTriPlant->groundColorV3;
// local per-vertex data: ground color(XYZ) + scaleZ/XYZ:
Vector4 groundColorV1, groundColorV2, groundColorV3;
Vector3 groundScaleV1, groundScaleV2, groundScaleV3;
Vector4 groundColorf;
Vector3 groundScalef;
groundColorV1.x = c1->GetRedf();
groundColorV1.y = c1->GetGreenf();
groundColorV1.z = c1->GetBluef();
groundColorV1.w = 1.0f;
groundScaleV1.x = CPlantLocTri::pv8UnpackAndMapScaleXYZ(c1->GetAlpha());
groundScaleV1.y = CPlantLocTri::pv8UnpackAndMapScaleZ(c1->GetAlpha());
groundScaleV1.z = 0.0f;
groundColorV2.x = c2->GetRedf();
groundColorV2.y = c2->GetGreenf();
groundColorV2.z = c2->GetBluef();
groundColorV2.w = 1.0f;
groundScaleV2.x = CPlantLocTri::pv8UnpackAndMapScaleXYZ(c2->GetAlpha());
groundScaleV2.y = CPlantLocTri::pv8UnpackAndMapScaleZ(c2->GetAlpha());
groundScaleV2.z = 0.0f;
groundColorV3.x = c3->GetRedf();
groundColorV3.y = c3->GetGreenf();
groundColorV3.z = c3->GetBluef();
groundColorV3.w = 1.0f;
groundScaleV3.x = CPlantLocTri::pv8UnpackAndMapScaleXYZ(c3->GetAlpha());
groundScaleV3.y = CPlantLocTri::pv8UnpackAndMapScaleZ(c3->GetAlpha());
groundScaleV3.z = 0.0f;
// unpack ground scale ranges:
const Vec2V unpacked_range_params = Float16Vec2Unpack(&pCurrTriPlant->scaleRangeXYZ_Z);
const float fGroundScaleRangeXYZ = unpacked_range_params.GetXf();
const float fGroundScaleRangeZ = unpacked_range_params.GetYf();
// unpack ScaleXY, ScaleZ, ScaleVarXY, ScaleVarZ:
const Vec4V unpacked_scale_params = Float16Vec4Unpack(&pCurrTriPlant->packedScale);
const Vector4 ScaleV4 = RCC_VECTOR4(unpacked_scale_params);
// const bool bApplyGroundSkewingLod0 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD0);
// const bool bApplyGroundSkewingLod1 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD1);
// const bool bApplyGroundSkewingLod2 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD2);
// want same values for plant positions each time...
plantsRand.Reset(pCurrTriPlant->seed);
customInstDraw.Reset();
//
// render a single triPlant full of the desired model
#if PLANTS_USE_LOD_SETTINGS
const u32 count = (u32)(float(pCurrTriPlant->num_plants) * GetDensityMultiplier());
#else
const u32 count = pCurrTriPlant->num_plants;
#endif
for(u32 j=0; j<count; j++)
{
// MainRT: only check for full LOD buckets bins and flush:
if(bMainRT)
{
// MainRT: only check for full LOD buckets bins and flush:
if(sm_csAddInstLod0Token[g_RenderThreadIndex].TryLock())
{
ms_LOD0Bucket.FlushAlmostFull(g_RenderThreadIndex);
sm_csAddInstLod0Token[g_RenderThreadIndex].Unlock();
}
if(sm_csAddInstLod1Token[g_RenderThreadIndex].TryLock())
{
ms_LOD1Bucket.FlushAlmostFull(g_RenderThreadIndex);
sm_csAddInstLod1Token[g_RenderThreadIndex].Unlock();
}
if(sm_csAddInstLod2Token[g_RenderThreadIndex].TryLock())
{
ms_LOD2Bucket.FlushAlmostFull(g_RenderThreadIndex);
sm_csAddInstLod2Token[g_RenderThreadIndex].Unlock();
}
}
const float s = plantsRand.GetRanged(0.0f, 1.0f);
const float t = plantsRand.GetRanged(0.0f, 1.0f);
Vector3 posPlant;
PPTriPlant::GenPointInTriangle(&posPlant, v1, v2, v3, s, t);
// calculate LOD0 distance:
#if CPLANT_USE_COLLISION_2D_DIST
Vector3 LodDistV3 = posPlant - globalCameraPos;
Vector2 LodDistV(LodDistV3.x, LodDistV3.y);
#else
Vector3 LodDistV = posPlant - globalCameraPos;
#endif
const float fLodDist2 = LodDistV.Mag2();
// interpolate ground color(XYZ):
PPTriPlant::GenPointInTriangle(&groundColorf, &groundColorV1, &groundColorV2, &groundColorV3, s, t);
// interpolate ground scaleXYZ/Z weights:
PPTriPlant::GenPointInTriangle(&groundScalef, &groundScaleV1, &groundScaleV2, &groundScaleV3, s, t);
const float groundScaleXYZ = groundScalef.x;
const float groundScaleZ = groundScalef.y;
// ---- apply various amounts of scaling to the matrix -----
// calculate an x/y scaling value and apply to matrix
float scaleXY = ScaleV4.x;
float scaleZ = ScaleV4.y;
// scale variation:
const float scaleRand01 = plantsRand.GetRanged(0.0f, 1.0f);
const float scvariationXY = ScaleV4.z;
scaleXY += scvariationXY*scaleRand01;
const float scvariationZ = ScaleV4.w;
scaleZ += scvariationZ*scaleRand01;
// apply ground scale variation:
const float _grndScaleXYZ = (1.0f - fGroundScaleRangeXYZ)*groundScaleXYZ + fGroundScaleRangeXYZ;
if(_grndScaleXYZ > 0.0f)
{
scaleXY *= _grndScaleXYZ;
scaleZ *= _grndScaleXYZ;
}
const float _grndScaleZ = (1.0f - fGroundScaleRangeZ)*groundScaleZ + fGroundScaleRangeZ;
if(_grndScaleZ > 0.0f)
{
scaleZ *= _grndScaleZ;
}
//// ----- end of scaling stuff ------
// choose bigger scale for bound frustum check:
const float boundRadiusScale = (scaleXY > scaleZ)? (scaleXY) : (scaleZ);
Vector4 sphereV4;
sphereV4.Set(boundSphereLOD0);
sphereV4.AddVector3XYZ(posPlant);
sphereV4.w *= boundRadiusScale;
spdSphere sphere(VECTOR4_TO_VEC4V(sphereV4));
bool isCulled = bGeomCullingTestEnabled && bEnableCulling && (!cullFrustum.IntersectsOrContainsSphere(sphere));
// skip current geometry if not visible:
if(isCulled)
{
plantsRand.GetInt();
plantsRand.GetInt();
plantsRand.GetInt();
plantsRand.GetInt();
if(intensityVar)
plantsRand.GetInt();
continue;
}
plantMatrix.MakeRotateZ(plantsRand.GetRanged(0.0f, 1.0f)*2.0f*PI);// overwrites a,b,c
plantMatrix.MakeTranslate(posPlant); // overwrites d
plantMatrix.Scale(scaleXY, scaleXY, scaleZ); // scale in XY+Z
if(bApplyGroundSkewing) // TODO: implement PROCPLANT_NOGROUNDSKEW_LOD0/1/2 flags (not important on NG just yet)
{
plantMatrix.Dot3x3(skewingMtx);
}
// ---- muck about with the matrix to make atomic look blown by the wind --------
// use here wind_bend_scale & wind_bend_var
// final_bend = [ 1.0 + (bend_var*rand(-1,1)) ] * [ global_wind * bend_scale ]
const float variationBend = wind_bend_var;
const float bending = (1.0f + (variationBend*plantsRand.GetRanged(0.0f, 1.0f))) *
(wind_bend_scale);
plantMatrix.c.x = bending * globalWindBending.x * plantsRand.GetRanged(-1.0f, 1.0f);
plantMatrix.c.y = bending * globalWindBending.y * plantsRand.GetRanged(-1.0f, 1.0f);
// ----- end of wind stuff -----
// color variation:
if(intensityVar)
{
// intensity support: final_I = I + VarI*rand01()
u16 intensity = pCurrTriPlant->intensity + u16(float(intensityVar)*plantsRand.GetRanged(0.0f, 1.0f));
intensity = MIN(static_cast<u16>(intensity * NdotL), 255);
Color32 col32(plantColour32.GetColor());
#define CALC_COLOR_INTENSITY(COLOR, I) u8((u16(COLOR)*I) >> 8)
col32.SetRed( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetRed(), intensity));
col32.SetGreen( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetGreen(), intensity));
col32.SetBlue( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetBlue(), intensity));
#undef CALC_COLOR_INTENSITY
plantColour32 = col32;
} // if(intensityVar)...
customInstDraw.m_WorldMatrix.FromMatrix34(plantMatrix);
customInstDraw.m_PlantColor32 = plantColour32;
customInstDraw.m_GroundColorAmbient32.SetFromRGBA( RCC_VEC4V(groundColorf) );
customInstDraw.m_GroundColorAmbient32.SetAlpha(ambientScale);
// Do we render a LOD0 version ?
if(fLodDist2 < fLOD0FarDist2)
{
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD0) && !gbPlantsDisableLOD0 && (!gbPlantsFlashLOD0 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD0))
#endif
{
if(bMainRT)
{
sm_csAddInstLod0Token[rti].Lock();
ms_LOD0Bucket.AddInstance(rti, true, pGeometryLOD0, pPlantTextureLOD0, &customInstDraw, unpacked_um_params, unpacked_coll_params);
sm_csAddInstLod0Token[rti].Unlock();
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD0DrawnPerSlot;
(*pLocTriPlantsLOD0DrawnPerSlot)++;
#endif //__BANK
}
else
{
sm_csAddInstLod0Token[rti].Lock();
bool bAdded = ms_LOD0Bucket.AddInstance(rti, false, pGeometryLOD0, pPlantTextureLOD0, &customInstDraw, unpacked_um_params, unpacked_coll_params);
sm_csAddInstLod0Token[rti].Unlock();
if(!bAdded)
{
Printf("\n LOD0 not added!");
}
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD0DrawnPerSlot;
(*pLocTriPlantsLOD0DrawnPerSlot)++;
#endif //__BANK
}
}
}
// Do we render a LOD1 version ?
if((fLodDist2 > fLOD1CloseDist2) && (fLodDist2 < fLOD1FarDist2))
{
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD1) && !gbPlantsDisableLOD1 && (!gbPlantsFlashLOD1 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD1))
#endif
{
if(bMainRT)
{
sm_csAddInstLod1Token[rti].Lock();
ms_LOD1Bucket.AddInstance(rti, true, pGeometryLOD1, pPlantTextureLOD1, &customInstDraw, unpacked_um_params, unpacked_coll_params);
sm_csAddInstLod1Token[rti].Unlock();
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD1DrawnPerSlot;
(*pLocTriPlantsLOD1DrawnPerSlot)++;
#endif //__BANK
}
else
{
sm_csAddInstLod1Token[rti].Lock();
bool bAdded = ms_LOD1Bucket.AddInstance(rti, false, pGeometryLOD1, pPlantTextureLOD1, &customInstDraw, unpacked_um_params, unpacked_coll_params);
sm_csAddInstLod1Token[rti].Unlock();
if(!bAdded)
{
Printf("\n LOD1 not added!");
}
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD0DrawnPerSlot;
(*pLocTriPlantsLOD0DrawnPerSlot)++;
#endif //__BANK
}
}
}
// Do we render a LOD2 version ?
if((fLodDist2 > fLOD2CloseDist2) && (fLodDist2 < fLOD2FarDist2))
{
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2) && !gbPlantsDisableLOD2 && (!gbPlantsFlashLOD2 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2))
#endif
{
if(bMainRT)
{
sm_csAddInstLod2Token[rti].Lock();
ms_LOD2Bucket.AddInstance(rti, true, NULL, pPlantTextureLOD1, &customInstDraw, VECTOR4_TO_VEC4V(dimLOD2), unpacked_coll_params);
sm_csAddInstLod2Token[rti].Unlock();
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD2DrawnPerSlot;
(*pLocTriPlantsLOD2DrawnPerSlot)++;
#endif //__BANK
}
else
{
sm_csAddInstLod2Token[rti].Lock();
bool bAdded = ms_LOD2Bucket.AddInstance(rti, false, NULL, pPlantTextureLOD1, &customInstDraw, VECTOR4_TO_VEC4V(dimLOD2), unpacked_coll_params);
sm_csAddInstLod2Token[rti].Unlock();
if(!bAdded)
{
Printf("\n LOD2 not added!");
}
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD2DrawnPerSlot;
(*pLocTriPlantsLOD2DrawnPerSlot)++;
#endif //__BANK
}
}
}
}// for(u32 j=0; j<count; j++)...
}// for(u32 i=0; i<numTriPlants; i++)...
return(TRUE);
}// end of DrawTriPlantsMulti()...
static sysTaskHandle s_PlantRenderTaskHandle[NUMBER_OF_RENDER_THREADS][PLANTSMGR_MULTI_RENDER_TASKCOUNT] = {0};
//
//
//
//
static void RenderPlantSubTask(sysTaskParameters &params)
{
const s32 taskID = params.UserData[0].asInt;
const u8 rti = (u8)params.UserData[1].asUInt; // render thread index
#if ENABLE_PIX_TAGGING
const char* pixTagName[] = { "PlantsMgrRenderTask0", "PlantsMgrRenderTask1", "PlantsMgrRenderTask2", "PlantsMgrRenderTask3",
"PlantsMgrRenderTask4", "PlantsMgrRenderTask5", "PlantsMgrRenderTask6", "PlantsMgrRenderTask7" };
PIXBegin(1, pixTagName[taskID]);
#else
PIXBegin(1, "PlantsRenderTask");
#endif
PPTriPlant *triPlants = (PPTriPlant*)params.UserData[2].asPtr;
u32 numTriPlants = params.UserData[3].asInt;
grassModel *plantModelsTab = (grassModel*)params.UserData[4].asPtr;
CGrassRenderer::CParamsDrawTriPlantsMulti *pParamsDrawMulti = (CGrassRenderer::CParamsDrawTriPlantsMulti*)params.UserData[5].asPtr;
CGrassRenderer::DrawTriPlantsMulti(rti, false, triPlants, numTriPlants, plantModelsTab, pParamsDrawMulti);
PIXEnd();
}
//
//
//
//
bool CGrassRenderer::DrawTriPlants(PPTriPlant *triPlants, const u32 numTriPlants, grassModel* plantModelsTab)
{
Assert(triPlants);
Assert(plantModelsTab);
const Vector3 globalCameraPos(GetGlobalCameraPos());
const Vector3 globalPlayerPos(GetGlobalPlayerPos());
ms_Shader->SetVar(ms_shdCameraPosID, globalCameraPos);
ms_Shader->SetVar(ms_shdPlayerPosID, globalPlayerPos);
for(u32 v=0; v<NUM_COL_VEH; v++)
{
bool bVehCollisionEnabled=FALSE;
Vector4 vecVehCollisionB, vecVehCollisionM, vecVehCollisionR;
GetGlobalVehCollisionParams(v, &bVehCollisionEnabled, &vecVehCollisionB, &vecVehCollisionM, &vecVehCollisionR);
grcEffect::SetGlobalVar(ms_shdVehCollisionEnabledID[v], bVehCollisionEnabled);
ms_Shader->SetVar(ms_shdVehCollisionBID[v], vecVehCollisionB);
ms_Shader->SetVar(ms_shdVehCollisionMID[v], vecVehCollisionM);
ms_Shader->SetVar(ms_shdVehCollisionRID[v], vecVehCollisionR);
}
Vector4 fakeNormal;
fakeNormal.SetVector3ClearW(GetFakeGrassNormal());
fakeNormal.SetW(1.0f);
ms_Shader->SetVar(ms_shdFakedGrassNormal, fakeNormal);
#if __BANK || PLANTS_USE_LOD_SETTINGS
extern float gbPlantsLOD0AlphaCloseDist, gbPlantsLOD0AlphaFarDist, gbPlantsLOD0FarDist;
extern float gbPlantsLOD1CloseDist, gbPlantsLOD1FarDist, gbPlantsLOD1Alpha0CloseDist, gbPlantsLOD1Alpha0FarDist, gbPlantsLOD1Alpha1CloseDist, gbPlantsLOD1Alpha1FarDist;
extern float gbPlantsLOD2CloseDist, gbPlantsLOD2FarDist, gbPlantsLOD2Alpha0CloseDist, gbPlantsLOD2Alpha0FarDist, gbPlantsLOD2Alpha1CloseDist, gbPlantsLOD2Alpha1FarDist;
extern float gbPlantsLOD2Alpha1CloseDistFar, gbPlantsLOD2Alpha1FarDistFar;
#if PLANTS_USE_LOD_SETTINGS
float grassDistanceMultiplier = GetDistanceMultiplier();
#else
float grassDistanceMultiplier = 1.0f;
#endif
const float globalLOD0AlphaCloseDist = grassDistanceMultiplier*gbPlantsLOD0AlphaCloseDist;
float globalLOD0AlphaFarDist = grassDistanceMultiplier*gbPlantsLOD0AlphaFarDist;
const float globalLOD0FarDist = grassDistanceMultiplier*gbPlantsLOD0FarDist;
const float globalLOD1CloseDist = grassDistanceMultiplier*gbPlantsLOD1CloseDist;
const float globalLOD1FarDist = grassDistanceMultiplier*gbPlantsLOD1FarDist;
const float globalLOD1Alpha0CloseDist = grassDistanceMultiplier*gbPlantsLOD1Alpha0CloseDist;
const float globalLOD1Alpha0FarDist = grassDistanceMultiplier*gbPlantsLOD1Alpha0FarDist;
const float globalLOD1Alpha1CloseDist = grassDistanceMultiplier*gbPlantsLOD1Alpha1CloseDist;
const float globalLOD1Alpha1FarDist = grassDistanceMultiplier*gbPlantsLOD1Alpha1FarDist;
const float globalLOD2CloseDist = grassDistanceMultiplier*gbPlantsLOD2CloseDist;
const float globalLOD2FarDist = grassDistanceMultiplier*gbPlantsLOD2FarDist;
const float globalLOD2Alpha0CloseDist = grassDistanceMultiplier*gbPlantsLOD2Alpha0CloseDist;
const float globalLOD2Alpha0FarDist = grassDistanceMultiplier*gbPlantsLOD2Alpha0FarDist;
const float globalLOD2Alpha1CloseDist = grassDistanceMultiplier*gbPlantsLOD2Alpha1CloseDist;
const float globalLOD2Alpha1FarDist = grassDistanceMultiplier*gbPlantsLOD2Alpha1FarDist;
const float globalLOD2Alpha1CloseDistFar= grassDistanceMultiplier*gbPlantsLOD2Alpha1CloseDistFar;
const float globalLOD2Alpha1FarDistFar = grassDistanceMultiplier*gbPlantsLOD2Alpha1FarDistFar;
#else
const float globalLOD0AlphaCloseDist = CPLANT_LOD0_ALPHA_CLOSE_DIST;
float globalLOD0AlphaFarDist = CPLANT_LOD0_ALPHA_FAR_DIST;
const float globalLOD0FarDist = CPLANT_LOD0_FAR_DIST;
const float globalLOD1CloseDist = CPLANT_LOD1_CLOSE_DIST;
const float globalLOD1FarDist = CPLANT_LOD1_FAR_DIST;
const float globalLOD1Alpha0CloseDist = CPLANT_LOD1_ALPHA0_CLOSE_DIST;
const float globalLOD1Alpha0FarDist = CPLANT_LOD1_ALPHA0_FAR_DIST;
const float globalLOD1Alpha1CloseDist = CPLANT_LOD1_ALPHA1_CLOSE_DIST;
const float globalLOD1Alpha1FarDist = CPLANT_LOD1_ALPHA1_FAR_DIST;
const float globalLOD2CloseDist = CPLANT_LOD2_CLOSE_DIST;
const float globalLOD2FarDist = CPLANT_LOD2_FAR_DIST;
const float globalLOD2Alpha0CloseDist = CPLANT_LOD2_ALPHA0_CLOSE_DIST;
const float globalLOD2Alpha0FarDist = CPLANT_LOD2_ALPHA0_FAR_DIST;
const float globalLOD2Alpha1CloseDist = CPLANT_LOD2_ALPHA1_CLOSE_DIST;
const float globalLOD2Alpha1FarDist = CPLANT_LOD2_ALPHA1_FAR_DIST;
const float globalLOD2Alpha1CloseDistFar= CPLANT_LOD2_ALPHA1_CLOSE_DIST_FAR;
const float globalLOD2Alpha1FarDistFar = CPLANT_LOD2_ALPHA1_FAR_DIST_FAR;
#endif
float globalLOD0FarDist2 = globalLOD0FarDist*globalLOD0FarDist;
float globalLOD1CloseDist2 = globalLOD1CloseDist*globalLOD1CloseDist;
float globalLOD1FarDist2 = globalLOD1FarDist*globalLOD1FarDist;
float globalLOD2CloseDist2 = globalLOD2CloseDist*globalLOD2CloseDist;
float globalLOD2FarDist2 = globalLOD2FarDist*globalLOD2FarDist;
if(CGrassRenderer::GetGlobalCameraFppEnabled() || CGrassRenderer::GetGlobalForceHDGrassGeometry())
{ // special case: camera is in FPP mode - force LOD0 to be visible everywhere:
globalLOD0AlphaFarDist = globalLOD2Alpha1FarDistFar;
globalLOD0FarDist2 = globalLOD2FarDist2;
globalLOD1CloseDist2 =
globalLOD1FarDist2 =
globalLOD2CloseDist2 =
globalLOD2FarDist2 = 999999.0f;
}
// LOD0 alpha, umTimer:
{
Vector4 vecAlphaFade4; // [XY=alphaFade01 | Z=umTimer | ?]
#if 1
// square distance approach:
const float closeDist = globalLOD0AlphaCloseDist;
const float farDist = globalLOD0AlphaFarDist;
const float alphaDenom = 1.0f / (farDist*farDist - closeDist*closeDist);
vecAlphaFade4.x = (farDist*farDist) * alphaDenom; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom; // y = (-1.0)/ (f2-c2)
#else
// linear distance approach:
const float alphaDenom = 1.0f / (m_farDist - m_closeDist);
vecAlphaFade4.x = (m_farDist) * alphaDenom; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom; // y = (-1.0)/ (f2-c2)
#endif
vecAlphaFade4.z = (float)(2.0*PI*(double(fwTimer::GetTimeInMilliseconds())/1000.0));
vecAlphaFade4.w = 0.0f;
ms_Shader->SetVar(ms_shdFadeAlphaDistUmTimerID, vecAlphaFade4);
}
// LOD1 alpha:
{
Vector4 vecAlphaFade4; // [XY=alpha0Fade01 | ZW=alpha1Fade01]
const float closeDist0 = globalLOD1Alpha0CloseDist;
const float farDist0 = globalLOD1Alpha0FarDist;
const float alphaDenom0 = 1.0f / (farDist0*farDist0 - closeDist0*closeDist0);
vecAlphaFade4.x = (farDist0*farDist0) * alphaDenom0; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom0; // y = (-1.0)/ (f2-c2)
const float closeDist1 = globalLOD1Alpha1CloseDist;
const float farDist1 = globalLOD1Alpha1FarDist;
const float alphaDenom1 = 1.0f / (farDist1*farDist1 - closeDist1*closeDist1);
vecAlphaFade4.z = (farDist1*farDist1) * alphaDenom1; // x = (f2) / (f2-c2)
vecAlphaFade4.w = (-1.0f) * alphaDenom1; // y = (-1.0)/ (f2-c2)
ms_Shader->SetVar(ms_shdFadeAlphaLOD1DistID, vecAlphaFade4);
}
// LOD2 alpha:
{
Vector4 vecAlphaFade4; // [XY=alpha0Fade01 | ZW=alpha1Fade01]
const float closeDist0 = globalLOD2Alpha0CloseDist;
const float farDist0 = globalLOD2Alpha0FarDist;
const float alphaDenom0 = 1.0f / (farDist0*farDist0 - closeDist0*closeDist0);
vecAlphaFade4.x = (farDist0*farDist0) * alphaDenom0; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom0; // y = (-1.0)/ (f2-c2)
const float closeDist1 = globalLOD2Alpha1CloseDist;
const float farDist1 = globalLOD2Alpha1FarDist;
const float alphaDenom1 = 1.0f / (farDist1*farDist1 - closeDist1*closeDist1);
vecAlphaFade4.z = (farDist1*farDist1) * alphaDenom1; // x = (f2) / (f2-c2)
vecAlphaFade4.w = (-1.0f) * alphaDenom1; // y = (-1.0)/ (f2-c2)
ms_Shader->SetVar(ms_shdFadeAlphaLOD2DistID, vecAlphaFade4);
Vector4 vecAlphaFadeFar4; // [XY=alpha1FadeFar01 | 0 | 0]
const float closeDistFar0 = globalLOD2Alpha1CloseDistFar;
const float farDistFar0 = globalLOD2Alpha1FarDistFar;
const float alphaDenomFar0 = 1.0f / (farDistFar0*farDistFar0 - closeDistFar0*closeDistFar0);
vecAlphaFadeFar4.x = (farDistFar0*farDistFar0) * alphaDenomFar0; // x = (f2) / (f2-c2)
vecAlphaFadeFar4.y = (-1.0f) * alphaDenomFar0; // y = (-1.0)/ (f2-c2)
vecAlphaFadeFar4.z = 0.0f;
vecAlphaFadeFar4.w = 0.0f;
ms_Shader->SetVar(ms_shdFadeAlphaLOD2DistFarID, vecAlphaFadeFar4);
}
#if DEVICE_MSAA
if (!GRCDEVICE.GetMSAA())
ms_Shader->SetVar(ms_shdAlphaToCoverageScale, 1.0f);
//else: leave artist-tuned value
#endif
CParamsDrawTriPlantsMulti *pDrawParams = &sm_paramsDrawMulti[g_RenderThreadIndex];
pDrawParams->m_CameraPos = GetGlobalCameraPos();
pDrawParams->m_PlayerPos = GetGlobalPlayerPos();
pDrawParams->m_SunDir = -Lights::GetRenderDirectionalLight().GetDirection();
pDrawParams->m_NaturalAmbient = Lights::m_lightingGroup.GetNaturalAmbientBase().w;
Vector2 windBending;
GetGlobalWindBending(windBending);
pDrawParams->m_WindBending = windBending;
pDrawParams->m_LOD0FarDist2 = globalLOD0FarDist2;
pDrawParams->m_LOD1CloseDist2 = globalLOD1CloseDist2;
pDrawParams->m_LOD1FarDist2 = globalLOD1FarDist2;
pDrawParams->m_LOD2CloseDist2 = globalLOD2CloseDist2;
pDrawParams->m_LOD2FarDist2 = globalLOD2FarDist2;;
static dev_float subAmountPerc = 1.00f; // 100% work to subtask
s32 numTriPlantsSub = s32(numTriPlants * subAmountPerc);
s32 numTriPlantsMain = numTriPlants - numTriPlantsSub;
if(numTriPlantsSub > 0) // any tris to share
{
sysTaskParameters params;
const s32 numTriPerTask = numTriPlantsSub / PLANTSMGR_MULTI_RENDER_TASKCOUNT;
PPTriPlant *startTri = &triPlants[numTriPlantsMain];
s32 numTrisLeft = numTriPlantsSub;
for(u32 t=0; t<PLANTSMGR_MULTI_RENDER_TASKCOUNT; t++)
{
Assertf(s_PlantRenderTaskHandle[g_RenderThreadIndex][t]==0, "PlantMgr: Render task %d already running!", t);
params.UserData[0].asInt = t; // taskID
params.UserData[1].asUInt = g_RenderThreadIndex;
params.UserData[2].asPtr = startTri;
if(t!=(PLANTSMGR_MULTI_RENDER_TASKCOUNT-1))
{
params.UserData[3].asInt = numTriPerTask;
startTri += numTriPerTask;
numTrisLeft -= numTriPerTask;
}
else
{
Assert(numTrisLeft > 0);
params.UserData[3].asInt = numTrisLeft;
}
params.UserData[4].asPtr = plantModelsTab;
params.UserData[5].asPtr = pDrawParams;
s_PlantRenderTaskHandle[g_RenderThreadIndex][t] = rage::sysTaskManager::Create(TASK_INTERFACE(RenderPlantSubTask), params);
}
}
// main thread:
DrawTriPlantsMulti(g_RenderThreadIndex, /*this is mainRT*/true, &triPlants[0], numTriPlantsMain, plantModelsTab, pDrawParams);
for(u32 t=0; t<PLANTSMGR_MULTI_RENDER_TASKCOUNT; t++)
{
if(s_PlantRenderTaskHandle[g_RenderThreadIndex][t])
{
do
{
// MainRT: only check for full LOD buckets bins and flush:
if(sm_csAddInstLod0Token[g_RenderThreadIndex].TryLock())
{
ms_LOD0Bucket.FlushAlmostFull(g_RenderThreadIndex);
sm_csAddInstLod0Token[g_RenderThreadIndex].Unlock();
}
if(sm_csAddInstLod1Token[g_RenderThreadIndex].TryLock())
{
ms_LOD1Bucket.FlushAlmostFull(g_RenderThreadIndex);
sm_csAddInstLod1Token[g_RenderThreadIndex].Unlock();
}
if(sm_csAddInstLod2Token[g_RenderThreadIndex].TryLock())
{
ms_LOD2Bucket.FlushAlmostFull(g_RenderThreadIndex);
sm_csAddInstLod2Token[g_RenderThreadIndex].Unlock();
}
}
while( sysTaskManager::Poll(s_PlantRenderTaskHandle[g_RenderThreadIndex][t]) == false );
//sysTaskManager::Wait(s_PlantRenderTaskHandle[g_RenderThreadIndex][t]);
s_PlantRenderTaskHandle[g_RenderThreadIndex][t] = 0;
}
}
return(TRUE);
}// end of CPPTriPlantBuffer::DrawTriPlants()...
#else //PLANTSMGR_MULTI_RENDER
#define NEW_STORE_OPT (1)
bool CGrassRenderer::DrawTriPlants(PPTriPlant *triPlants, const u32 numTriPlants, grassModel* plantModelsTab)
{
Assert(triPlants);
Assert(plantModelsTab);
const Vector3 globalCameraPos(GetGlobalCameraPos());
const Vector3 globalPlayerPos(GetGlobalPlayerPos());
Vector2 globalWindBending;
GetGlobalWindBending(globalWindBending);
spdTransposedPlaneSet8 cullFrustum = ms_cullingFrustum[g_RenderThreadIndex];
#if CPLANT_DYNAMIC_CULL_SPHERES
const u32 id = gPlantMgr.GetRenderRTBufferID();
const CPlantMgr::DynCullSphereArray &cullSpheres = gPlantMgr.GetCullSphereArray(id);
#endif
ms_Shader->SetVar(ms_shdCameraPosID, globalCameraPos);
ms_Shader->SetVar(ms_shdPlayerPosID, globalPlayerPos);
for(u32 v=0; v<NUM_COL_VEH; v++)
{
bool bVehCollisionEnabled=FALSE;
Vector4 vecVehCollisionB, vecVehCollisionM, vecVehCollisionR;
GetGlobalVehCollisionParams(v, &bVehCollisionEnabled, &vecVehCollisionB, &vecVehCollisionM, &vecVehCollisionR);
grcEffect::SetGlobalVar(ms_shdVehCollisionEnabledID[v], bVehCollisionEnabled);
ms_Shader->SetVar(ms_shdVehCollisionBID[v], vecVehCollisionB);
ms_Shader->SetVar(ms_shdVehCollisionMID[v], vecVehCollisionM);
ms_Shader->SetVar(ms_shdVehCollisionRID[v], vecVehCollisionR);
}
Vector4 fakeNormal;
fakeNormal.SetVector3ClearW(GetFakeGrassNormal());
fakeNormal.SetW(1.0f);
ms_Shader->SetVar(ms_shdFakedGrassNormal, fakeNormal);
#if __BANK
extern bool gbPlantsGeometryCulling;
dev_bool bGeomCullingTestEnabled = gbPlantsGeometryCulling;
extern bool gbPlantsPremultiplyNdotL;
dev_bool bPremultiplyNdotL = gbPlantsPremultiplyNdotL;
#else
dev_bool bGeomCullingTestEnabled = TRUE;
dev_bool bPremultiplyNdotL = TRUE;
#endif
#if __BANK
extern bool gbPlantsFlashLOD0, gbPlantsFlashLOD1, gbPlantsFlashLOD2;
extern bool gbPlantsDisableLOD0, gbPlantsDisableLOD1, gbPlantsDisableLOD2;
#endif // __BANK
#if __BANK || PLANTS_USE_LOD_SETTINGS
extern float gbPlantsLOD0AlphaCloseDist, gbPlantsLOD0AlphaFarDist, gbPlantsLOD0FarDist;
extern float gbPlantsLOD1CloseDist, gbPlantsLOD1FarDist, gbPlantsLOD1Alpha0CloseDist, gbPlantsLOD1Alpha0FarDist, gbPlantsLOD1Alpha1CloseDist, gbPlantsLOD1Alpha1FarDist;
extern float gbPlantsLOD2CloseDist, gbPlantsLOD2FarDist, gbPlantsLOD2Alpha0CloseDist, gbPlantsLOD2Alpha0FarDist, gbPlantsLOD2Alpha1CloseDist, gbPlantsLOD2Alpha1FarDist;
extern float gbPlantsLOD2Alpha1CloseDistFar, gbPlantsLOD2Alpha1FarDistFar;
#if PLANTS_USE_LOD_SETTINGS
float grassDistanceMultiplier = GetDistanceMultiplier();
#else
float grassDistanceMultiplier = 1.0f;
#endif
const float globalLOD0AlphaCloseDist = grassDistanceMultiplier*gbPlantsLOD0AlphaCloseDist;
float globalLOD0AlphaFarDist = grassDistanceMultiplier*gbPlantsLOD0AlphaFarDist;
const float globalLOD0FarDist = grassDistanceMultiplier*gbPlantsLOD0FarDist;
const float globalLOD1CloseDist = grassDistanceMultiplier*gbPlantsLOD1CloseDist;
const float globalLOD1FarDist = grassDistanceMultiplier*gbPlantsLOD1FarDist;
const float globalLOD1Alpha0CloseDist = grassDistanceMultiplier*gbPlantsLOD1Alpha0CloseDist;
const float globalLOD1Alpha0FarDist = grassDistanceMultiplier*gbPlantsLOD1Alpha0FarDist;
const float globalLOD1Alpha1CloseDist = grassDistanceMultiplier*gbPlantsLOD1Alpha1CloseDist;
const float globalLOD1Alpha1FarDist = grassDistanceMultiplier*gbPlantsLOD1Alpha1FarDist;
const float globalLOD2CloseDist = grassDistanceMultiplier*gbPlantsLOD2CloseDist;
const float globalLOD2FarDist = grassDistanceMultiplier*gbPlantsLOD2FarDist;
const float globalLOD2Alpha0CloseDist = grassDistanceMultiplier*gbPlantsLOD2Alpha0CloseDist;
const float globalLOD2Alpha0FarDist = grassDistanceMultiplier*gbPlantsLOD2Alpha0FarDist;
const float globalLOD2Alpha1CloseDist = grassDistanceMultiplier*gbPlantsLOD2Alpha1CloseDist;
const float globalLOD2Alpha1FarDist = grassDistanceMultiplier*gbPlantsLOD2Alpha1FarDist;
const float globalLOD2Alpha1CloseDistFar= grassDistanceMultiplier*gbPlantsLOD2Alpha1CloseDistFar;
const float globalLOD2Alpha1FarDistFar = grassDistanceMultiplier*gbPlantsLOD2Alpha1FarDistFar;
#else
const float globalLOD0AlphaCloseDist = CPLANT_LOD0_ALPHA_CLOSE_DIST;
float globalLOD0AlphaFarDist = CPLANT_LOD0_ALPHA_FAR_DIST;
const float globalLOD0FarDist = CPLANT_LOD0_FAR_DIST;
const float globalLOD1CloseDist = CPLANT_LOD1_CLOSE_DIST;
const float globalLOD1FarDist = CPLANT_LOD1_FAR_DIST;
const float globalLOD1Alpha0CloseDist = CPLANT_LOD1_ALPHA0_CLOSE_DIST;
const float globalLOD1Alpha0FarDist = CPLANT_LOD1_ALPHA0_FAR_DIST;
const float globalLOD1Alpha1CloseDist = CPLANT_LOD1_ALPHA1_CLOSE_DIST;
const float globalLOD1Alpha1FarDist = CPLANT_LOD1_ALPHA1_FAR_DIST;
const float globalLOD2CloseDist = CPLANT_LOD2_CLOSE_DIST;
const float globalLOD2FarDist = CPLANT_LOD2_FAR_DIST;
const float globalLOD2Alpha0CloseDist = CPLANT_LOD2_ALPHA0_CLOSE_DIST;
const float globalLOD2Alpha0FarDist = CPLANT_LOD2_ALPHA0_FAR_DIST;
const float globalLOD2Alpha1CloseDist = CPLANT_LOD2_ALPHA1_CLOSE_DIST;
const float globalLOD2Alpha1FarDist = CPLANT_LOD2_ALPHA1_FAR_DIST;
const float globalLOD2Alpha1CloseDistFar= CPLANT_LOD2_ALPHA1_CLOSE_DIST_FAR;
const float globalLOD2Alpha1FarDistFar = CPLANT_LOD2_ALPHA1_FAR_DIST_FAR;
#endif
float globalLOD0FarDist2 = globalLOD0FarDist*globalLOD0FarDist;
float globalLOD1CloseDist2 = globalLOD1CloseDist*globalLOD1CloseDist;
float globalLOD1FarDist2 = globalLOD1FarDist*globalLOD1FarDist;
float globalLOD2CloseDist2 = globalLOD2CloseDist*globalLOD2CloseDist;
float globalLOD2FarDist2 = globalLOD2FarDist*globalLOD2FarDist;
if(CGrassRenderer::GetGlobalCameraFppEnabled() || CGrassRenderer::GetGlobalForceHDGrassGeometry())
{ // special case: camera is in FPP mode - force LOD0 to be visible everywhere:
globalLOD0AlphaFarDist = globalLOD2Alpha1FarDistFar;
globalLOD0FarDist2 = globalLOD2FarDist2;
globalLOD1CloseDist2 =
globalLOD1FarDist2 =
globalLOD2CloseDist2 =
globalLOD2FarDist2 = 999999.0f;
}
// LOD0 alpha, umTimer:
{
Vector4 vecAlphaFade4; // [XY=alphaFade01 | Z=umTimer | ?]
#if 1
// square distance approach:
const float closeDist = globalLOD0AlphaCloseDist;
const float farDist = globalLOD0AlphaFarDist;
const float alphaDenom = 1.0f / (farDist*farDist - closeDist*closeDist);
vecAlphaFade4.x = (farDist*farDist) * alphaDenom; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom; // y = (-1.0)/ (f2-c2)
#else
// linear distance approach:
const float alphaDenom = 1.0f / (m_farDist - m_closeDist);
vecAlphaFade4.x = (m_farDist) * alphaDenom; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom; // y = (-1.0)/ (f2-c2)
#endif
vecAlphaFade4.z = (float)(2.0*PI*(double(fwTimer::GetTimeInMilliseconds())/1000.0));
vecAlphaFade4.w = 0.0f;
ms_Shader->SetVar(ms_shdFadeAlphaDistUmTimerID, vecAlphaFade4);
}
// LOD1 alpha:
{
Vector4 vecAlphaFade4; // [XY=alpha0Fade01 | ZW=alpha1Fade01]
const float closeDist0 = globalLOD1Alpha0CloseDist;
const float farDist0 = globalLOD1Alpha0FarDist;
const float alphaDenom0 = 1.0f / (farDist0*farDist0 - closeDist0*closeDist0);
vecAlphaFade4.x = (farDist0*farDist0) * alphaDenom0; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom0; // y = (-1.0)/ (f2-c2)
const float closeDist1 = globalLOD1Alpha1CloseDist;
const float farDist1 = globalLOD1Alpha1FarDist;
const float alphaDenom1 = 1.0f / (farDist1*farDist1 - closeDist1*closeDist1);
vecAlphaFade4.z = (farDist1*farDist1) * alphaDenom1; // x = (f2) / (f2-c2)
vecAlphaFade4.w = (-1.0f) * alphaDenom1; // y = (-1.0)/ (f2-c2)
ms_Shader->SetVar(ms_shdFadeAlphaLOD1DistID, vecAlphaFade4);
}
// LOD2 alpha:
{
Vector4 vecAlphaFade4; // [XY=alpha0Fade01 | ZW=alpha1Fade01]
const float closeDist0 = globalLOD2Alpha0CloseDist;
const float farDist0 = globalLOD2Alpha0FarDist;
const float alphaDenom0 = 1.0f / (farDist0*farDist0 - closeDist0*closeDist0);
vecAlphaFade4.x = (farDist0*farDist0) * alphaDenom0; // x = (f2) / (f2-c2)
vecAlphaFade4.y = (-1.0f) * alphaDenom0; // y = (-1.0)/ (f2-c2)
const float closeDist1 = globalLOD2Alpha1CloseDist;
const float farDist1 = globalLOD2Alpha1FarDist;
const float alphaDenom1 = 1.0f / (farDist1*farDist1 - closeDist1*closeDist1);
vecAlphaFade4.z = (farDist1*farDist1) * alphaDenom1; // x = (f2) / (f2-c2)
vecAlphaFade4.w = (-1.0f) * alphaDenom1; // y = (-1.0)/ (f2-c2)
ms_Shader->SetVar(ms_shdFadeAlphaLOD2DistID, vecAlphaFade4);
Vector4 vecAlphaFadeFar4; // [XY=alpha1FadeFar01 | 0 | 0]
const float closeDistFar0 = globalLOD2Alpha1CloseDistFar;
const float farDistFar0 = globalLOD2Alpha1FarDistFar;
const float alphaDenomFar0 = 1.0f / (farDistFar0*farDistFar0 - closeDistFar0*closeDistFar0);
vecAlphaFadeFar4.x = (farDistFar0*farDistFar0) * alphaDenomFar0; // x = (f2) / (f2-c2)
vecAlphaFadeFar4.y = (-1.0f) * alphaDenomFar0; // y = (-1.0)/ (f2-c2)
vecAlphaFadeFar4.z = 0.0f;
vecAlphaFadeFar4.w = 0.0f;
ms_Shader->SetVar(ms_shdFadeAlphaLOD2DistFarID, vecAlphaFadeFar4);
}
#if DEVICE_MSAA
if (!GRCDEVICE.GetMSAA())
ms_Shader->SetVar(ms_shdAlphaToCoverageScale, 1.0f);
//else: leave artist-tuned value
#endif
#if CPLANT_PRE_MULTIPLY_LIGHTING
const Vector3 sunDir = -Lights::GetRenderDirectionalLight().GetDirection();
const float naturalAmbient = Lights::m_lightingGroup.GetNaturalAmbientBase().w;
#endif
instGrassDataDrawStruct customInstDraw;
Matrix34 plantMatrix;
Matrix34 skewingMtx;
Vector4 plantColourf;
Color32 plantColour32;
#if !GRASS_INSTANCING
customInstDraw.Reset();
#endif
for(u32 i=0; i<numTriPlants; i++)
{
PPTriPlant *pCurrTriPlant = &triPlants[i];
const float wind_bend_scale = pCurrTriPlant->V1.w; // wind bending scale
const float wind_bend_var = pCurrTriPlant->V2.w; // wind bending variation
const bool bApplyGroundSkewing = (pCurrTriPlant->skewAxisAngle.w.GetBinaryData()!=0); // technically this tests for +0 but not -0
if(bApplyGroundSkewing)
{
const Vec4V unpacked_skewAxisAngle = Float16Vec4Unpack(&pCurrTriPlant->skewAxisAngle);
skewingMtx.MakeRotateUnitAxis(RCC_VECTOR3(unpacked_skewAxisAngle), unpacked_skewAxisAngle.GetWf());
}
const bool bEnableCulling = pCurrTriPlant->flags.IsClear(PROCPLANT_CAMERADONOTCULL);
float NdotL = 1.0f;
#if CPLANT_WRITE_GRASS_NORMAL || CPLANT_PRE_MULTIPLY_LIGHTING
// set normal for whole pTriLoc:
Vector4 groundNormal;
groundNormal.SetX(float(pCurrTriPlant->loctri_normal[0])/127.0f);
groundNormal.SetY(float(pCurrTriPlant->loctri_normal[1])/127.0f);
groundNormal.SetZ(float(pCurrTriPlant->loctri_normal[2])/127.0f);
groundNormal.SetW(0.0f);
#if CPLANT_PRE_MULTIPLY_LIGHTING
if(bPremultiplyNdotL)
{
// For pre-multiplying lighting into the color channel
Vector4 sunDir_Vec4(sunDir.x, sunDir.y, sunDir.z, 0.0f);
NdotL = Saturate(groundNormal.Dot(sunDir_Vec4)) + naturalAmbient;
}
#endif
#if CPLANT_WRITE_GRASS_NORMAL
ms_Shader->SetVar(ms_shdTerrainNormal, groundNormal);
#endif
#endif // CPLANT_WRITE_GRASS_NORMAL || CPLANT_PRE_MULTIPLY_LIGHTING
// want same values for plant positions each time...
g_PlantsRendRand[g_RenderThreadIndex].Reset(pCurrTriPlant->seed);
// intensity support: final_I = I + VarI*rand01()
const s16 intensityVar = pCurrTriPlant->intensity_var;
u16 intensity = pCurrTriPlant->intensity + u16(float(intensityVar)*g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f));
intensity = MIN(static_cast<u16>(intensity * NdotL), 255);
Color32 col32;
col32.SetAlpha( pCurrTriPlant->color.GetAlpha());
#define CALC_COLOR_INTENSITY(COLOR, I) u8((u16(COLOR)*I) >> 8)
col32.SetRed( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetRed(), intensity));
col32.SetGreen( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetGreen(), intensity));
col32.SetBlue( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetBlue(), intensity));
#undef CALC_COLOR_INTENSITY
plantColour32 = col32;
plantColourf.x = col32.GetRedf();
plantColourf.y = col32.GetGreenf();
plantColourf.z = col32.GetBluef();
plantColourf.w = col32.GetAlphaf();
grcTexture *pPlantTextureLOD0 = pCurrTriPlant->texture_ptr;
Assert(pPlantTextureLOD0);
grcTexture *pPlantTextureLOD1 = pCurrTriPlant->textureLOD1_ptr;
Assert(pPlantTextureLOD1);
grmGeometry *pGeometryLOD0 = plantModelsTab[pCurrTriPlant->model_id].m_pGeometry;
const Vector4 boundSphereLOD0 = plantModelsTab[pCurrTriPlant->model_id].m_BoundingSphere;
grmGeometry *pGeometryLOD1 = plantModelsTab[pCurrTriPlant->model_id+CPLANT_SLOT_NUM_MODELS].m_pGeometry;
const Vector4 boundSphereLOD1 = plantModelsTab[pCurrTriPlant->model_id+CPLANT_SLOT_NUM_MODELS].m_BoundingSphere;
const Vector2 GeometryDimLOD2 = plantModelsTab[pCurrTriPlant->model_id+CPLANT_SLOT_NUM_MODELS].m_dimensionLOD2;
const Vector4 boundSphereLOD2(0,0,0,1.0f);
#if !GRASS_INSTANCING
// set color for whole pTriLoc:
ms_Shader->SetVar(ms_shdPlantColorID, plantColourf);
#endif
// set global micro-movement params: [ scaleH | scaleV | freqH | freqV ]
const Vec4V unpacked_um_params = Float16Vec4Unpack(&pCurrTriPlant->um_param);
#if !GRASS_INSTANCING
ms_Shader->SetVar(ms_shdUMovementParamsID, unpacked_um_params);
#endif
// set global collision radius scale:
const Vec2V unpacked_coll_params = Float16Vec2Unpack(&pCurrTriPlant->coll_params);
#if !GRASS_INSTANCING
ms_Shader->SetVar(ms_shdCollParamsID, unpacked_coll_params);
#endif
// LOD2 billboard dimensions:
Vector4 dimLOD2;
dimLOD2.x = GeometryDimLOD2.x;
dimLOD2.y = GeometryDimLOD2.y;
dimLOD2.z = pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2FARFADE)? 1.0f : 0.0f;
dimLOD2.w = 0.0f;
#if !GRASS_INSTANCING
ms_Shader->SetVar(ms_shdDimensionLOD2ID, dimLOD2);
#endif
u8 ambientScale = pCurrTriPlant->ambient_scl;
Vector3 *v1 = (Vector3*)&pCurrTriPlant->V1;
Vector3 *v2 = (Vector3*)&pCurrTriPlant->V2;
Vector3 *v3 = (Vector3*)&pCurrTriPlant->V3;
Color32 *c1 = &pCurrTriPlant->groundColorV1;
Color32 *c2 = &pCurrTriPlant->groundColorV2;
Color32 *c3 = &pCurrTriPlant->groundColorV3;
// local per-vertex data: ground color(XYZ) + scaleZ/XYZ:
Vector4 groundColorV1, groundColorV2, groundColorV3;
Vector3 groundScaleV1, groundScaleV2, groundScaleV3;
Vector4 groundColorf;
Vector3 groundScalef;
groundColorV1.x = c1->GetRedf();
groundColorV1.y = c1->GetGreenf();
groundColorV1.z = c1->GetBluef();
groundColorV1.w = 1.0f;
groundScaleV1.x = CPlantLocTri::pv8UnpackAndMapScaleXYZ(c1->GetAlpha());
groundScaleV1.y = CPlantLocTri::pv8UnpackAndMapScaleZ(c1->GetAlpha());
groundScaleV1.z = 0.0f;
groundColorV2.x = c2->GetRedf();
groundColorV2.y = c2->GetGreenf();
groundColorV2.z = c2->GetBluef();
groundColorV2.w = 1.0f;
groundScaleV2.x = CPlantLocTri::pv8UnpackAndMapScaleXYZ(c2->GetAlpha());
groundScaleV2.y = CPlantLocTri::pv8UnpackAndMapScaleZ(c2->GetAlpha());
groundScaleV2.z = 0.0f;
groundColorV3.x = c3->GetRedf();
groundColorV3.y = c3->GetGreenf();
groundColorV3.z = c3->GetBluef();
groundColorV3.w = 1.0f;
groundScaleV3.x = CPlantLocTri::pv8UnpackAndMapScaleXYZ(c3->GetAlpha());
groundScaleV3.y = CPlantLocTri::pv8UnpackAndMapScaleZ(c3->GetAlpha());
groundScaleV3.z = 0.0f;
// unpack ground scale ranges:
const Vec2V unpacked_range_params = Float16Vec2Unpack(&pCurrTriPlant->scaleRangeXYZ_Z);
const float fGroundScaleRangeXYZ = unpacked_range_params.GetXf();
const float fGroundScaleRangeZ = unpacked_range_params.GetYf();
// unpack ScaleXY, ScaleZ, ScaleVarXY, ScaleVarZ:
const Vec4V unpacked_scale_params = Float16Vec4Unpack(&pCurrTriPlant->packedScale);
const Vector4 ScaleV4 = RCC_VECTOR4(unpacked_scale_params);
#if GRASS_INSTANCING
////////////////////////////////////////////////////////////////////////////////////////////////////
// const bool bApplyGroundSkewingLod0 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD0);
// const bool bApplyGroundSkewingLod1 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD1);
// const bool bApplyGroundSkewingLod2 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD2);
// want same values for plant positions each time...
g_PlantsRendRand[g_RenderThreadIndex].Reset(pCurrTriPlant->seed);
// find store buckets upfront:
#if NEW_STORE_OPT
InstanceBucket *pLod0Bucket = ms_LOD0Bucket.FindBucket(pGeometryLOD0, pPlantTextureLOD0);
InstanceBucket *pLod1Bucket = ms_LOD1Bucket.FindBucket(pGeometryLOD1, pPlantTextureLOD1);
InstanceBucket *pLod2Bucket = ms_LOD2Bucket.FindBucket(NULL, pPlantTextureLOD1);
#endif
//
// render a single triPlant full of the desired model
#if PLANTS_USE_LOD_SETTINGS
const u32 count = (u32)(float(pCurrTriPlant->num_plants) * GetDensityMultiplier());
#else
const u32 count = pCurrTriPlant->num_plants;
#endif
for(u32 j=0; j<count; j++)
{
const float s = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float t = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
Vector3 posPlant;
PPTriPlant::GenPointInTriangle(&posPlant, v1, v2, v3, s, t);
// calculate LOD0 distance:
#if CPLANT_USE_COLLISION_2D_DIST
Vector3 LodDistV3 = posPlant - globalCameraPos;
Vector2 LodDistV(LodDistV3.x, LodDistV3.y);
#else
Vector3 LodDistV = posPlant - globalCameraPos;
#endif
const float fLodDist2 = LodDistV.Mag2();
// interpolate ground color(XYZ):
PPTriPlant::GenPointInTriangle(&groundColorf, &groundColorV1, &groundColorV2, &groundColorV3, s, t);
// interpolate ground scaleXYZ/Z weights:
PPTriPlant::GenPointInTriangle(&groundScalef, &groundScaleV1, &groundScaleV2, &groundScaleV3, s, t);
const float groundScaleXYZ = groundScalef.x;
const float groundScaleZ = groundScalef.y;
// ---- apply various amounts of scaling to the matrix -----
// calculate an x/y scaling value and apply to matrix
float scaleXY = ScaleV4.x;
float scaleZ = ScaleV4.y;
// scale variation:
const float scaleRand01 = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float scvariationXY = ScaleV4.z;
scaleXY += scvariationXY*scaleRand01;
const float scvariationZ = ScaleV4.w;
scaleZ += scvariationZ*scaleRand01;
// apply ground scale variation:
const float _grndScaleXYZ = (1.0f - fGroundScaleRangeXYZ)*groundScaleXYZ + fGroundScaleRangeXYZ;
if(_grndScaleXYZ > 0.0f)
{
scaleXY *= _grndScaleXYZ;
scaleZ *= _grndScaleXYZ;
}
const float _grndScaleZ = (1.0f - fGroundScaleRangeZ)*groundScaleZ + fGroundScaleRangeZ;
if(_grndScaleZ > 0.0f)
{
scaleZ *= _grndScaleZ;
}
//// ----- end of scaling stuff ------
// choose bigger scale for bound frustum check:
const float boundRadiusScale = (scaleXY > scaleZ)? (scaleXY) : (scaleZ);
Vector4 sphereV4;
sphereV4.Set(boundSphereLOD0);
sphereV4.AddVector3XYZ(posPlant);
sphereV4.w *= boundRadiusScale;
spdSphere sphere(VECTOR4_TO_VEC4V(sphereV4));
bool isCulled = bGeomCullingTestEnabled && bEnableCulling && (!cullFrustum.IntersectsOrContainsSphere(sphere));
// skip current geometry if not visible:
if(isCulled)
{
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
if(intensityVar)
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
continue;
}
plantMatrix.MakeRotateZ(g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f)*2.0f*PI);// overwrites a,b,c
plantMatrix.MakeTranslate(posPlant); // overwrites d
plantMatrix.Scale(scaleXY, scaleXY, scaleZ); // scale in XY+Z
if(bApplyGroundSkewing) // TODO: implement PROCPLANT_NOGROUNDSKEW_LOD0/1/2 flags (not important on NG just yet)
{
plantMatrix.Dot3x3(skewingMtx);
}
// ---- muck about with the matrix to make atomic look blown by the wind --------
// use here wind_bend_scale & wind_bend_var
// final_bend = [ 1.0 + (bend_var*rand(-1,1)) ] * [ global_wind * bend_scale ]
const float variationBend = wind_bend_var;
const float bending = (1.0f + (variationBend*g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f))) *
(wind_bend_scale);
plantMatrix.c.x = bending * globalWindBending.x * g_PlantsRendRand[g_RenderThreadIndex].GetRanged(-1.0f, 1.0f);
plantMatrix.c.y = bending * globalWindBending.y * g_PlantsRendRand[g_RenderThreadIndex].GetRanged(-1.0f, 1.0f);
// ----- end of wind stuff -----
// color variation:
if(intensityVar)
{
// intensity support: final_I = I + VarI*rand01()
u16 intensity = pCurrTriPlant->intensity + u16(float(intensityVar)*g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f));
intensity = MIN(static_cast<u16>(intensity * NdotL), 255);
Color32 col32(plantColour32.GetColor());
#define CALC_COLOR_INTENSITY(COLOR, I) u8((u16(COLOR)*I) >> 8)
col32.SetRed( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetRed(), intensity));
col32.SetGreen( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetGreen(), intensity));
col32.SetBlue( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetBlue(), intensity));
#undef CALC_COLOR_INTENSITY
plantColour32 = col32;
} // if(intensityVar)...
customInstDraw.m_WorldMatrix.FromMatrix34(plantMatrix);
customInstDraw.m_PlantColor32 = plantColour32;
customInstDraw.m_GroundColorAmbient32.SetFromRGBA( RCC_VEC4V(groundColorf) );
customInstDraw.m_GroundColorAmbient32.SetAlpha(ambientScale);
// Do we render a LOD0 version ?
if(fLodDist2 < globalLOD0FarDist2)
{
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD0) && !gbPlantsDisableLOD0 && (!gbPlantsFlashLOD0 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD0))
#endif
{
#if NEW_STORE_OPT
pLod0Bucket->AddInstanceDirect(&customInstDraw, unpacked_um_params, unpacked_coll_params);
#else
ms_LOD0Bucket.AddInstance(pGeometryLOD0, pPlantTextureLOD0, &customInstDraw, unpacked_um_params, unpacked_coll_params);
#endif
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD0DrawnPerSlot;
(*pLocTriPlantsLOD0DrawnPerSlot)++;
#endif //__BANK
}
}
// Do we render a LOD1 version ?
if((fLodDist2 > globalLOD1CloseDist2) && (fLodDist2 < globalLOD1FarDist2))
{
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD1) && !gbPlantsDisableLOD1 && (!gbPlantsFlashLOD1 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD1))
#endif
{
#if NEW_STORE_OPT
pLod1Bucket->AddInstanceDirect(&customInstDraw, unpacked_um_params, unpacked_coll_params);
#else
ms_LOD1Bucket.AddInstance(pGeometryLOD1, pPlantTextureLOD1, &customInstDraw, unpacked_um_params, unpacked_coll_params);
#endif
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD1DrawnPerSlot;
(*pLocTriPlantsLOD1DrawnPerSlot)++;
#endif //__BANK
}
}
// Do we render a LOD2 version ?
if((fLodDist2 > globalLOD2CloseDist2) && (fLodDist2 < globalLOD2FarDist2))
{
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2) && !gbPlantsDisableLOD2 && (!gbPlantsFlashLOD2 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2))
#endif
{
#if NEW_STORE_OPT
pLod2Bucket->AddInstanceDirect(&customInstDraw, VECTOR4_TO_VEC4V(dimLOD2), unpacked_coll_params);
#else
ms_LOD2Bucket.AddInstance(NULL, pPlantTextureLOD1, &customInstDraw, VECTOR4_TO_VEC4V(dimLOD2), unpacked_coll_params);
#endif
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD2DrawnPerSlot;
(*pLocTriPlantsLOD2DrawnPerSlot)++;
#endif //__BANK
}
}
}// for(u32 j=0; j<count; j++)...
////////////////////////////////////////////////////////////////////////////////////////////////////
#else //GRASS_INSTANCING
////////////////////////////////////////////////////////////////////////////////////////////////////
const bool bApplyGroundSkewingLod0 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD0);
const bool bApplyGroundSkewingLod1 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD1);
const bool bApplyGroundSkewingLod2 = bApplyGroundSkewing && pCurrTriPlant->flags.IsClear(PROCPLANT_NOGROUNDSKEW_LOD2);
///////////// Render LOD0: /////////////////////////////////////////////////////////////////////////
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD0) && !gbPlantsDisableLOD0 && (!gbPlantsFlashLOD0 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD0))
#endif
{
// want same values for plant positions each time...
g_PlantsRendRand[g_RenderThreadIndex].Reset(pCurrTriPlant->seed);
ms_Shader->SetVar(ms_shdTextureID, pPlantTextureLOD0);
customInstDraw.Reset();
// Setup the vertex and index buffers
grcIndexBuffer* pIndexBuffer = pGeometryLOD0->GetIndexBuffer(true);
grcVertexBuffer* pVertexBuffer = pGeometryLOD0->GetVertexBuffer(true);
const u16 nPrimType = pGeometryLOD0->GetPrimitiveType();
const u32 nIdxCount = pIndexBuffer->GetIndexCount();
grcVertexDeclaration* vertexDeclaration = pGeometryLOD0->GetDecl();
#if !(__D3D11 || RSG_ORBIS)
bool bShaderCmdBufOut = false;
#endif // !(__D3D11 || RSG_ORBIS)
//
// render a single triPlant full of the desired model
const u32 count = pCurrTriPlant->num_plants;
for(u32 j=0; j<count; j++)
{
const float s = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float t = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
Vector3 posPlant;
PPTriPlant::GenPointInTriangle(&posPlant, v1, v2, v3, s, t);
// calculate LOD0 distance:
#if CPLANT_USE_COLLISION_2D_DIST
Vector3 LodDistV3 = posPlant - globalCameraPos;
Vector2 LodDistV(LodDistV3.x, LodDistV3.y);
#else
Vector3 LodDistV = posPlant - globalCameraPos;
#endif
const float fLodDist2 = LodDistV.Mag2();
const bool bLodVisible = (fLodDist2 < globalLOD0FarDist2);
// early skip if current geometry not visible as LOD0:
if(!bLodVisible)
{
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
if(intensityVar)
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
continue;
}
// interpolate ground color(XYZ):
PPTriPlant::GenPointInTriangle(&groundColorf, &groundColorV1, &groundColorV2, &groundColorV3, s, t);
// interpolate ground scaleXYZ/Z weights:
PPTriPlant::GenPointInTriangle(&groundScalef, &groundScaleV1, &groundScaleV2, &groundScaleV3, s, t);
const float groundScaleXYZ = groundScalef.x;
const float groundScaleZ = groundScalef.y;
// ---- apply various amounts of scaling to the matrix -----
// calculate an x/y scaling value and apply to matrix
float scaleXY = ScaleV4.x;
float scaleZ = ScaleV4.y;
// scale variation:
const float scaleRand01 = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float scvariationXY = ScaleV4.z;
scaleXY += scvariationXY*scaleRand01;
const float scvariationZ = ScaleV4.w;
scaleZ += scvariationZ*scaleRand01;
// apply ground scale variation:
const float _grndScaleXYZ = (1.0f - fGroundScaleRangeXYZ)*groundScaleXYZ + fGroundScaleRangeXYZ;
if(_grndScaleXYZ > 0.0f)
{
scaleXY *= _grndScaleXYZ;
scaleZ *= _grndScaleXYZ;
}
const float _grndScaleZ = (1.0f - fGroundScaleRangeZ)*groundScaleZ + fGroundScaleRangeZ;
if(_grndScaleZ > 0.0f)
{
scaleZ *= _grndScaleZ;
}
//// ----- end of scaling stuff ------
// choose bigger scale for bound frustum check:
const float boundRadiusScale = (scaleXY > scaleZ)? (scaleXY) : (scaleZ);
Vector4 sphereV4;
sphereV4.Set(boundSphereLOD0);
sphereV4.AddVector3XYZ(posPlant);
sphereV4.w *= boundRadiusScale;
spdSphere sphere(VECTOR4_TO_VEC4V(sphereV4));
// skip current geometry if not visible:
bool isCulled = bGeomCullingTestEnabled && bEnableCulling && (!cullFrustum.IntersectsOrContainsSphere(sphere));
#if CPLANT_DYNAMIC_CULL_SPHERES
//Check dynamic cull spheres and cull if grass is in a cull sphere.
CPlantMgr::DynCullSphereArray::const_iterator iter;
CPlantMgr::DynCullSphereArray::const_iterator end = cullSpheres.end();
for(iter = cullSpheres.begin(); iter != end && !isCulled; ++iter)
isCulled |= iter->IntersectsSphere(sphere); //iter->ContainsSphere(sphere);
#endif
#if CPLANT_CLIP_EDGE_VERT
//Skip current geometry if it intersects a clipping edge/vert:
isCulled = isCulled ||
(pCurrTriPlant->m_ClipVert_0 && sphere.ContainsPoint(RCC_VEC3V(*v1))) ||
(pCurrTriPlant->m_ClipVert_1 && sphere.ContainsPoint(RCC_VEC3V(*v2))) ||
(pCurrTriPlant->m_ClipVert_2 && sphere.ContainsPoint(RCC_VEC3V(*v3))) ||
(pCurrTriPlant->m_ClipEdge_01 && geomSpheres::TestSphereToSeg(sphere.GetCenter(), sphere.GetRadius(), RCC_VEC3V(*v1), RCC_VEC3V(*v2))) ||
(pCurrTriPlant->m_ClipEdge_12 && geomSpheres::TestSphereToSeg(sphere.GetCenter(), sphere.GetRadius(), RCC_VEC3V(*v2), RCC_VEC3V(*v3))) ||
(pCurrTriPlant->m_ClipEdge_20 && geomSpheres::TestSphereToSeg(sphere.GetCenter(), sphere.GetRadius(), RCC_VEC3V(*v3), RCC_VEC3V(*v1)));
#endif
if(isCulled)
{
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
if(intensityVar)
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
continue;
}
plantMatrix.MakeRotateZ(g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f)*2.0f*PI);// overwrites a,b,c
plantMatrix.MakeTranslate(posPlant); // overwrites d
plantMatrix.Scale(scaleXY, scaleXY, scaleZ); // scale in XY+Z
if(bApplyGroundSkewingLod0)
{
plantMatrix.Dot3x3(skewingMtx);
}
// ---- muck about with the matrix to make atomic look blown by the wind --------
// use here wind_bend_scale & wind_bend_var
// final_bend = [ 1.0 + (bend_var*rand(-1,1)) ] * [ global_wind * bend_scale ]
const float variationBend = wind_bend_var;
const float bending = (1.0f + (variationBend*g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f))) *
(wind_bend_scale);
plantMatrix.c.x = bending * globalWindBending.x * g_PlantsRendRand[g_RenderThreadIndex].GetRanged(-1.0f, 1.0f);
plantMatrix.c.y = bending * globalWindBending.y * g_PlantsRendRand[g_RenderThreadIndex].GetRanged(-1.0f, 1.0f);
// ----- end of wind stuff -----
// color variation:
if(intensityVar)
{
// intensity support: final_I = I + VarI*rand01()
u16 intensity = pCurrTriPlant->intensity + u16(float(intensityVar)*g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f));
intensity = MIN(static_cast<u16>(intensity * NdotL), 255);
Color32 col32;
#define CALC_COLOR_INTENSITY(COLOR, I) u8((u16(COLOR)*I) >> 8)
col32.SetRed( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetRed(), intensity));
col32.SetGreen( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetGreen(), intensity));
col32.SetBlue( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetBlue(), intensity));
#undef CALC_COLOR_INTENSITY
plantColourf.x = col32.GetRedf();
plantColourf.y = col32.GetGreenf();
plantColourf.z = col32.GetBluef();
} // if(intensityVar)...
customInstDraw.m_WorldMatrix.FromMatrix34(plantMatrix);
customInstDraw.m_PlantColor.Set(plantColourf);
customInstDraw.m_GroundColor.Set(groundColorf);
#if !(__D3D11 || RSG_ORBIS)
if(!bShaderCmdBufOut)
{
bShaderCmdBufOut = true;
// bind plant shader
BindPlantShader(ms_shdDeferredLOD0TechniqueID_ToUse);
GRCDEVICE.RecordSetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetIndices(*pIndexBuffer);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
}
const bool bOptimizedSetup = customInstDraw.UseOptimizedVsSetup(plantMatrix);
if(bOptimizedSetup)
{ // optimized setup: 3 registers:
GRCDEVICE.SetVertexShaderConstant(GRASS_REG_TRANSFORM+3, customInstDraw.GetRegisterPointerOpt(), customInstDraw.GetRegisterCountOpt());
GRCDEVICE.DrawIndexedPrimitive((grcDrawMode)nPrimType, 0, nIdxCount);
}
else
{ // full setup: 6 registers:
GRCDEVICE.SetVertexShaderConstant(GRASS_REG_TRANSFORM, customInstDraw.GetRegisterPointerFull(), customInstDraw.GetRegisterCountFull());
GRCDEVICE.DrawIndexedPrimitive((grcDrawMode)nPrimType, 0, nIdxCount);
}
#else // !(__D3D11 || RSG_ORBIS)
ms_Shader->SetVar( ms_grassRegTransform, customInstDraw.m_WorldMatrix );
ms_Shader->SetVar( ms_grassRegPlantCol, customInstDraw.m_PlantColor );
ms_Shader->SetVar( ms_grassRegGroundCol, customInstDraw.m_GroundColor );
BindPlantShader(ms_shdDeferredLOD0TechniqueID_ToUse);
GRCDEVICE.RecordSetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetIndices(*pIndexBuffer);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
GRCDEVICE.DrawIndexedPrimitive((grcDrawMode)nPrimType, 0, nIdxCount);
UnBindPlantShader();
#endif // !(__D3D11 || RSG_ORBIS)
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD0DrawnPerSlot;
(*pLocTriPlantsLOD0DrawnPerSlot)++;
#endif //__BANK
}// for(u32 j=0; j<count; j++)...
#if !(__D3D11 || RSG_ORBIS)
if(bShaderCmdBufOut)
{
UnBindPlantShader();
}
#endif // !(__D3D11 || RSG_ORBIS)
}// render LOD0...
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////// Render LOD1: /////////////////////////////////////////////////////////////////////////
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD1) && !gbPlantsDisableLOD1 && (!gbPlantsFlashLOD1 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD1))
#endif
{
g_PlantsRendRand[g_RenderThreadIndex].Reset(pCurrTriPlant->seed);
ms_Shader->SetVar(ms_shdTextureID, pPlantTextureLOD1);
customInstDraw.Reset();
// Setup the vertex and index buffers
grcIndexBuffer* pIndexBuffer = pGeometryLOD1->GetIndexBuffer(true);
grcVertexBuffer* pVertexBuffer = pGeometryLOD1->GetVertexBuffer(true);
const u16 nPrimType = pGeometryLOD1->GetPrimitiveType();
const u32 nIdxCount = pIndexBuffer->GetIndexCount();
grcVertexDeclaration* vertexDeclaration = pGeometryLOD1->GetDecl();
#if !(__D3D11 || RSG_ORBIS)
bool bShaderCmdBufOut = false;
#endif // !(__D3D11 || RSG_ORBIS)
//
// render a single triPlant full of the desired model
const u32 count = pCurrTriPlant->num_plants;
for(u32 j=0; j<count; j++)
{
const float s = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float t = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
Vector3 posPlant;
PPTriPlant::GenPointInTriangle(&posPlant, v1, v2, v3, s, t);
// calculate LOD1 distance:
#if CPLANT_USE_COLLISION_2D_DIST
Vector3 LodDistV3 = posPlant - globalCameraPos;
Vector2 LodDistV(LodDistV3.x, LodDistV3.y);
#else
Vector3 LodDistV = posPlant - globalCameraPos;
#endif
const float fLodDist2 = LodDistV.Mag2();
const bool bLodVisible = (fLodDist2 > globalLOD1CloseDist2) && (fLodDist2 < globalLOD1FarDist2);
// early skip if current geometry not visible as LOD1:
if(!bLodVisible)
{
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
if(intensityVar)
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
continue;
}
// interpolate ground color(XYZ):
PPTriPlant::GenPointInTriangle(&groundColorf, &groundColorV1, &groundColorV2, &groundColorV3, s, t);
// interpolate ground scaleXYZ/Z weights:
PPTriPlant::GenPointInTriangle(&groundScalef, &groundScaleV1, &groundScaleV2, &groundScaleV3, s, t);
const float groundScaleXYZ = groundScalef.x;
const float groundScaleZ = groundScalef.y;
// ---- apply various amounts of scaling to the matrix -----
// calculate an x/y scaling value and apply to matrix
float scaleXY = ScaleV4.x;
float scaleZ = ScaleV4.y;
// scale variation:
const float scaleRand01 = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float scvariationXY = ScaleV4.z;
scaleXY += scvariationXY*scaleRand01;
const float scvariationZ = ScaleV4.w;
scaleZ += scvariationZ*scaleRand01;
// apply ground scale variation:
const float _grndScaleXYZ = (1.0f - fGroundScaleRangeXYZ)*groundScaleXYZ + fGroundScaleRangeXYZ;
if(_grndScaleXYZ > 0.0f)
{
scaleXY *= _grndScaleXYZ;
scaleZ *= _grndScaleXYZ;
}
const float _grndScaleZ = (1.0f - fGroundScaleRangeZ)*groundScaleZ + fGroundScaleRangeZ;
if(_grndScaleZ > 0.0f)
{
scaleZ *= _grndScaleZ;
}
//// ----- end of scaling stuff ------
// choose bigger scale for bound frustum check:
const float boundRadiusScale = (scaleXY > scaleZ)? (scaleXY) : (scaleZ);
Vector4 sphereV4;
sphereV4.Set(boundSphereLOD1);
sphereV4.AddVector3XYZ(posPlant);
sphereV4.w *= boundRadiusScale;
spdSphere sphere(VECTOR4_TO_VEC4V(sphereV4));
// skip current geometry if not visible:
bool isCulled = bGeomCullingTestEnabled && bEnableCulling && (!cullFrustum.IntersectsOrContainsSphere(sphere));
#if CPLANT_DYNAMIC_CULL_SPHERES
//Check dynamic cull spheres and cull if grass is in a cull sphere.
CPlantMgr::DynCullSphereArray::const_iterator iter;
CPlantMgr::DynCullSphereArray::const_iterator end = cullSpheres.end();
for(iter = cullSpheres.begin(); iter != end && !isCulled; ++iter)
isCulled |= iter->IntersectsSphere(sphere);
#endif
#if CPLANT_CLIP_EDGE_VERT
//Skip current geometry if it intersects a clipping edge/vert:
isCulled = isCulled ||
(pCurrTriPlant->m_ClipVert_0 && sphere.ContainsPoint(RCC_VEC3V(*v1))) ||
(pCurrTriPlant->m_ClipVert_1 && sphere.ContainsPoint(RCC_VEC3V(*v2))) ||
(pCurrTriPlant->m_ClipVert_2 && sphere.ContainsPoint(RCC_VEC3V(*v3))) ||
(pCurrTriPlant->m_ClipEdge_01 && geomSpheres::TestSphereToSeg(sphere.GetCenter(), sphere.GetRadius(), RCC_VEC3V(*v1), RCC_VEC3V(*v2))) ||
(pCurrTriPlant->m_ClipEdge_12 && geomSpheres::TestSphereToSeg(sphere.GetCenter(), sphere.GetRadius(), RCC_VEC3V(*v2), RCC_VEC3V(*v3))) ||
(pCurrTriPlant->m_ClipEdge_20 && geomSpheres::TestSphereToSeg(sphere.GetCenter(), sphere.GetRadius(), RCC_VEC3V(*v3), RCC_VEC3V(*v1)));
#endif
if(isCulled)
{
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
if(intensityVar)
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
continue;
}
plantMatrix.MakeRotateZ(g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f)*2.0f*PI);// overwrites a,b,c
plantMatrix.MakeTranslate(posPlant); // overwrites d
plantMatrix.Scale(scaleXY, scaleXY, scaleZ); // scale in XY+Z
if(bApplyGroundSkewingLod1)
{
plantMatrix.Dot3x3(skewingMtx);
}
// ---- muck about with the matrix to make atomic look blown by the wind --------
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
// ----- end of wind stuff -----
// color variation:
if(intensityVar)
{
// intensity support: final_I = I + VarI*rand01()
u16 intensity = pCurrTriPlant->intensity + u16(float(intensityVar)*g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f));
intensity = MIN(static_cast<u16>(intensity * NdotL), 255);
Color32 col32;
#define CALC_COLOR_INTENSITY(COLOR, I) u8((u16(COLOR)*I) >> 8)
col32.SetRed( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetRed(), intensity));
col32.SetGreen( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetGreen(), intensity));
col32.SetBlue( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetBlue(), intensity));
#undef CALC_COLOR_INTENSITY
plantColourf.x = col32.GetRedf();
plantColourf.y = col32.GetGreenf();
plantColourf.z = col32.GetBluef();
} // if(intensityVar)...
customInstDraw.m_WorldMatrix.FromMatrix34(plantMatrix);
customInstDraw.m_PlantColor.Set(plantColourf);
customInstDraw.m_GroundColor.Set(groundColorf);
#if !(__D3D11 || RSG_ORBIS)
if(!bShaderCmdBufOut)
{
bShaderCmdBufOut = true;
// Bind the grass shader
BindPlantShader(ms_shdDeferredLOD1TechniqueID_ToUse);
GRCDEVICE.RecordSetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetIndices(*pIndexBuffer);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
}
const bool bOptimizedSetup = customInstDraw.UseOptimizedVsSetup(plantMatrix);
if(bOptimizedSetup)
{ // optimized setup: 3 registers:
GRCDEVICE.SetVertexShaderConstant(GRASS_REG_TRANSFORM+3, customInstDraw.GetRegisterPointerOpt(), customInstDraw.GetRegisterCountOpt());
GRCDEVICE.DrawIndexedPrimitive((grcDrawMode)nPrimType, 0, nIdxCount);
}
else
{ // full setup: 6 registers:
GRCDEVICE.SetVertexShaderConstant(GRASS_REG_TRANSFORM, customInstDraw.GetRegisterPointerFull(), customInstDraw.GetRegisterCountFull());
GRCDEVICE.DrawIndexedPrimitive((grcDrawMode)nPrimType, 0, nIdxCount);
}
#else // !(__D3D11 || RSG_ORBIS)
ms_Shader->SetVar( ms_grassRegTransform, customInstDraw.m_WorldMatrix );
ms_Shader->SetVar( ms_grassRegPlantCol, customInstDraw.m_PlantColor );
ms_Shader->SetVar( ms_grassRegGroundCol, customInstDraw.m_GroundColor );
BindPlantShader(ms_shdDeferredLOD1TechniqueID_ToUse);
GRCDEVICE.RecordSetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetIndices(*pIndexBuffer);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
GRCDEVICE.DrawIndexedPrimitive((grcDrawMode)nPrimType, 0, nIdxCount);
UnBindPlantShader();
#endif // !(__D3D11 || RSG_ORBIS)
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD1DrawnPerSlot;
(*pLocTriPlantsLOD1DrawnPerSlot)++;
#endif //__BANK
}// for(u32 j=0; j<count; j++)...
//////////////////////////////////////////////////////////////////
#if !(__D3D11 || RSG_ORBIS)
if(bShaderCmdBufOut)
{
UnBindPlantShader();
}
#endif // !(__D3D11 || RSG_ORBIS)
}// renderLOD1...
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////// Render LOD2: /////////////////////////////////////////////////////////////////////////
#if __BANK
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2) && !gbPlantsDisableLOD2 && (!gbPlantsFlashLOD2 || (GRCDEVICE.GetFrameCounter()&0x01)))
#else
if(pCurrTriPlant->flags.IsSet(PROCPLANT_LOD2))
#endif
{
g_PlantsRendRand[g_RenderThreadIndex].Reset(pCurrTriPlant->seed);
ms_Shader->SetVar(ms_shdTextureID, pPlantTextureLOD1);
customInstDraw.Reset();
// Setup the vertex and index buffers
grcVertexBuffer* pVertexBuffer = ms_plantLOD2VertexBuffer;
grcVertexDeclaration* vertexDeclaration = ms_plantLOD2VertexDecl;
#if !(__D3D11 || RSG_ORBIS)
bool bShaderCmdBufOut = false;
#endif // !(__D3D11 || RSG_ORBIS)
//
// render a single triPlant full of the desired model
const u32 count = pCurrTriPlant->num_plants;
for(u32 j=0; j<count; j++)
{
const float s = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float t = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
Vector3 posPlant;
PPTriPlant::GenPointInTriangle(&posPlant, v1, v2, v3, s, t);
// calculate LOD2 distance:
#if CPLANT_USE_COLLISION_2D_DIST
Vector3 LodDistV3 = posPlant - globalCameraPos;
Vector2 LodDistV(LodDistV3.x, LodDistV3.y);
#else
Vector3 LodDistV = posPlant - globalCameraPos;
#endif
const float fLodDist2 = LodDistV.Mag2();
const bool bLodVisible = (fLodDist2 > globalLOD2CloseDist2) && (fLodDist2 < globalLOD2FarDist2);
// early skip if current geometry not visible as LOD2:
if(!bLodVisible)
{
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
if(intensityVar)
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
continue;
}
// interpolate ground color(XYZ):
PPTriPlant::GenPointInTriangle(&groundColorf, &groundColorV1, &groundColorV2, &groundColorV3, s, t);
// interpolate ground scaleXYZ/Z weights:
PPTriPlant::GenPointInTriangle(&groundScalef, &groundScaleV1, &groundScaleV2, &groundScaleV3, s, t);
const float groundScaleXYZ = groundScalef.x;
const float groundScaleZ = groundScalef.y;
// ---- apply various amounts of scaling to the matrix -----
// calculate an x/y scaling value and apply to matrix
float scaleXY = ScaleV4.x;
float scaleZ = ScaleV4.y;
// scale variation:
const float scaleRand01 = g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f);
const float scvariationXY = ScaleV4.z;
scaleXY += scvariationXY*scaleRand01;
const float scvariationZ = ScaleV4.w;
scaleZ += scvariationZ*scaleRand01;
// apply ground scale variation:
const float _grndScaleXYZ = (1.0f - fGroundScaleRangeXYZ)*groundScaleXYZ + fGroundScaleRangeXYZ;
if(_grndScaleXYZ > 0.0f)
{
scaleXY *= _grndScaleXYZ;
scaleZ *= _grndScaleXYZ;
}
const float _grndScaleZ = (1.0f - fGroundScaleRangeZ)*groundScaleZ + fGroundScaleRangeZ;
if(_grndScaleZ > 0.0f)
{
scaleZ *= _grndScaleZ;
}
//// ----- end of scaling stuff ------
// choose bigger scale for bound frustum check:
//const float boundRadiusScale = (scaleXY > scaleZ)? (scaleXY) : (scaleZ);
Vector4 sphereV4;
sphereV4.Set(boundSphereLOD2);
sphereV4.AddVector3XYZ(posPlant);
//sphereV4.w *= boundRadiusScale;
spdSphere sphere(VECTOR4_TO_VEC4V(sphereV4));
// skip current geometry if not visible:
bool isCulled = bGeomCullingTestEnabled && bEnableCulling && (!cullFrustum.IntersectsOrContainsSphere(sphere));
#if CPLANT_DYNAMIC_CULL_SPHERES || CPLANT_CLIP_EDGE_VERT
// choose bigger scale for bound frustum check:
const float boundRadiusScale = (scaleXY > scaleZ)? (scaleXY) : (scaleZ);
//The above sphere is not a tight enough bound for DynSphereCull/Poly Cull. Recompute a more accurate bounding sphere.
float lod2MaxOffset = Max(GeometryDimLOD2.x, GeometryDimLOD2.y);
Vec3V lod2halfOffset = Vec3V(lod2MaxOffset, lod2MaxOffset, lod2MaxOffset) * ScalarV(V_HALF);
Vec3V lod2SphereCenter = sphere.GetCenter();// + lod2halfOffset;
ScalarV lod2SphereRadius = lod2halfOffset.GetX() * LoadScalar32IntoScalarV(boundRadiusScale);
spdSphere lod2Sphere(lod2SphereCenter, lod2SphereRadius);
#endif
#if CPLANT_DYNAMIC_CULL_SPHERES
//Check dynamic cull spheres and cull if grass is in a cull sphere.
CPlantMgr::DynCullSphereArray::const_iterator iter;
CPlantMgr::DynCullSphereArray::const_iterator end = cullSpheres.end();
for(iter = cullSpheres.begin(); iter != end && !isCulled; ++iter)
isCulled |= iter->IntersectsSphere(lod2Sphere);
#endif
#if CPLANT_CLIP_EDGE_VERT
//Skip current geometry if it intersects a clipping edge/vert:
isCulled = isCulled ||
(pCurrTriPlant->m_ClipVert_0 && lod2Sphere.ContainsPoint(RCC_VEC3V(*v1))) ||
(pCurrTriPlant->m_ClipVert_1 && lod2Sphere.ContainsPoint(RCC_VEC3V(*v2))) ||
(pCurrTriPlant->m_ClipVert_2 && lod2Sphere.ContainsPoint(RCC_VEC3V(*v3))) ||
(pCurrTriPlant->m_ClipEdge_01 && geomSpheres::TestSphereToSeg(lod2Sphere.GetCenter(), lod2Sphere.GetRadius(), RCC_VEC3V(*v1), RCC_VEC3V(*v2))) ||
(pCurrTriPlant->m_ClipEdge_12 && geomSpheres::TestSphereToSeg(lod2Sphere.GetCenter(), lod2Sphere.GetRadius(), RCC_VEC3V(*v2), RCC_VEC3V(*v3))) ||
(pCurrTriPlant->m_ClipEdge_20 && geomSpheres::TestSphereToSeg(lod2Sphere.GetCenter(), lod2Sphere.GetRadius(), RCC_VEC3V(*v3), RCC_VEC3V(*v1)));
#endif
if(isCulled)
{
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
if(intensityVar)
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
continue;
}
// plantMatrix.MakeRotateZ(g_PlantsRendRand.GetRanged(0.0f, 1.0f)*2.0f*PI);// overwrites a,b,c
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
plantMatrix.Identity3x3();
plantMatrix.MakeTranslate(posPlant); // overwrites d
plantMatrix.Scale(scaleXY, scaleXY, scaleZ); // scale in XY+Z
if(bApplyGroundSkewingLod2)
{
plantMatrix.Dot3x3(skewingMtx);
}
// ---- muck about with the matrix to make atomic look blown by the wind --------
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
g_PlantsRendRand[g_RenderThreadIndex].GetInt();
// ----- end of wind stuff -----
// color variation:
if(intensityVar)
{
#if 1
// intensity support: final_I = I + VarI*rand01()
u16 intensity = pCurrTriPlant->intensity + u16(float(intensityVar)*g_PlantsRendRand[g_RenderThreadIndex].GetRanged(0.0f, 1.0f));
intensity = MIN(static_cast<u16>(intensity * NdotL), 255);
Color32 col32;
#define CALC_COLOR_INTENSITY(COLOR, I) u8((u16(COLOR)*I) >> 8)
col32.SetRed( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetRed(), intensity));
col32.SetGreen( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetGreen(), intensity));
col32.SetBlue( CALC_COLOR_INTENSITY(pCurrTriPlant->color.GetBlue(), intensity));
#undef CALC_COLOR_INTENSITY
plantColourf.x = col32.GetRedf();
plantColourf.y = col32.GetGreenf();
plantColourf.z = col32.GetBluef();
#else
g_PlantsRendRand.GetInt();
#endif
} // if(intensityVar)...
customInstDraw.m_WorldMatrix.FromMatrix34(plantMatrix);
customInstDraw.m_PlantColor.Set(plantColourf);
customInstDraw.m_GroundColor.Set(groundColorf);
#if !(__D3D11 || RSG_ORBIS)
if(!bShaderCmdBufOut)
{
bShaderCmdBufOut = true;
// Bind the grass shader
BindPlantShader(ms_shdDeferredLOD2TechniqueID_ToUse);
GRCDEVICE.RecordSetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
}
const bool bOptimizedSetup = customInstDraw.UseOptimizedVsSetup(plantMatrix);
const grcDrawMode lod2Draw = __WIN32PC? drawTriFan : drawQuads;
if(bOptimizedSetup)
{ // optimized setup: 3 registers:
GRCDEVICE.SetVertexShaderConstant(GRASS_REG_TRANSFORM+3, customInstDraw.GetRegisterPointerOpt(), customInstDraw.GetRegisterCountOpt());
GRCDEVICE.DrawPrimitive(lod2Draw, /*startVertex*/0, /*vertexCount*/4 );
}
else
{ // full setup: 6 registers:
GRCDEVICE.SetVertexShaderConstant(GRASS_REG_TRANSFORM, customInstDraw.GetRegisterPointerFull(), customInstDraw.GetRegisterCountFull());
GRCDEVICE.DrawPrimitive(lod2Draw, /*startVertex*/0, /*vertexCount*/4 );
}
#else
ms_Shader->SetVar( ms_grassRegTransform, customInstDraw.m_WorldMatrix );
ms_Shader->SetVar( ms_grassRegPlantCol, customInstDraw.m_PlantColor );
ms_Shader->SetVar( ms_grassRegGroundCol, customInstDraw.m_GroundColor );
BindPlantShader(ms_shdDeferredLOD2TechniqueID_ToUse);
GRCDEVICE.RecordSetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
GRCDEVICE.DrawPrimitive(drawTriStrip/*lod2Draw*/, /*startVertex*/0, /*vertexCount*/4 );
UnBindPlantShader();
#endif // !(__D3D11 || RSG_ORBIS)
#if __BANK
// rendering stats:
extern u32 *pLocTriPlantsLOD2DrawnPerSlot;
(*pLocTriPlantsLOD2DrawnPerSlot)++;
#endif //__BANK
}// for(u32 j=0; j<count; j++)...
//////////////////////////////////////////////////////////////////
#if !(__D3D11 || RSG_ORBIS)
if(bShaderCmdBufOut)
{
UnBindPlantShader();
}
#endif // !(__D3D11 || RSG_ORBIS)
}// renderLOD2's...
////////////////////////////////////////////////////////////////////////////////////////////////////
#endif // GRASS_INSTANCING
}// for(u32 i=0; i<numTriPlants; i++)...
return(TRUE);
}// end of CPPTriPlantBuffer::DrawTriPlants()...
#endif //PLANTSMGR_MULTI_RENDER...
#endif // PSN_PLANTSMGR_SPU_RENDER...
//
//
//
//
void CGrassRenderer::BindPlantShader(grcEffectTechnique forcedTech)
{
// Bind the grass shader
ASSERT_ONLY(s32 numPasses =) ms_Shader->BeginDraw(grmShader::RMC_DRAW, /*restoreState*/FALSE, /*techOverride*/forcedTech);
Assert(numPasses>0);
const s32 pass=0;
ms_Shader->Bind(pass);
}
void CGrassRenderer::UnBindPlantShader()
{
// Unbind the shader
GRCDEVICE.ClearStreamSource(0);
ms_Shader->UnBind();
ms_Shader->EndDraw();
}
#if PSN_PLANTSMGR_SPU_RENDER && PLANTSMGR_BIG_HEAP_IN_BLOCKS
//
//
// returns true, if given offset is inside any of heap blocks
//
bool CGrassRenderer::BigHeapBlockArrayContainsOffset(u32 heapID, u32 blockSize, u32 offset)
{
grassHeapBlock *heapArray = sm_BigHeapBlockArray[heapID];
u32 count = sm_BigHeapBlockArrayCount[heapID];
while( count-- )
{
if( (offset >= heapArray[count].offset) && (offset < heapArray[count].offset+blockSize) )
return(true);
}
return(false);
}
#endif //PSN_PLANTSMGR_SPU_RENDER
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//
//
//#pragma mark --- PPTriPlantBuffer stuff ---
//
//
//
//
CPPTriPlantBuffer::CPPTriPlantBuffer()
{
int i;
for(i=0; i<NUMBER_OF_RENDER_THREADS; i++)
{
this->m_PerThreadData[i].m_currentIndex = 0;
this->m_PerThreadData[i].m_plantModelsSet = PPPLANTBUF_MODEL_SET0;
//this->SetPlantModelsSet(PPPLANTBUF_MODEL_SET0);
}
for(s32 i=0; i<PPTRIPLANT_MODELS_TAB_SIZE; i++)
{
m_pPlantModelsTab[i] = NULL;
}
}
//
//
//
//
CPPTriPlantBuffer::~CPPTriPlantBuffer()
{
}
//
//
// flush the buffer:
//
void CPPTriPlantBuffer::Flush()
{
if(m_PerThreadData[g_RenderThreadIndex].m_currentIndex>0)
{
#if !PSN_PLANTSMGR_SPU_RENDER
grassModel* plantModels = NULL;
switch(this->m_PerThreadData[g_RenderThreadIndex].m_plantModelsSet)
{
case(PPPLANTBUF_MODEL_SET0): plantModels = m_pPlantModelsTab[0]; break;
case(PPPLANTBUF_MODEL_SET1): plantModels = m_pPlantModelsTab[1]; break;
case(PPPLANTBUF_MODEL_SET2): plantModels = m_pPlantModelsTab[2]; break;
case(PPPLANTBUF_MODEL_SET3): plantModels = m_pPlantModelsTab[3]; break;
default: plantModels = NULL; break;
}
Assertf(plantModels, "Unknown PlantModelsSet!");
#endif
// save current seed:
const u32 storedSeed = g_PlantsRendRand[g_RenderThreadIndex].GetSeed();
#if PSN_PLANTSMGR_SPU_RENDER
{
CGrassRenderer::DrawTriPlantsSPU(m_PerThreadData[g_RenderThreadIndex].m_Buffer, m_PerThreadData[g_RenderThreadIndex].m_currentIndex, m_PerThreadData[g_RenderThreadIndex].m_plantModelsSet);
m_PerThreadData[g_RenderThreadIndex].m_currentIndex = 0;
}
#else //PSN_PLANTSMGR_SPU_RENDER...
{
CGrassRenderer::DrawTriPlants(m_PerThreadData[g_RenderThreadIndex].m_Buffer, m_PerThreadData[g_RenderThreadIndex].m_currentIndex, plantModels);
m_PerThreadData[g_RenderThreadIndex].m_currentIndex = 0;
}
#endif //PSN_PLANTSMGR_SPU_RENDER...
// restore randomness:
g_PlantsRendRand[g_RenderThreadIndex].Reset(storedSeed);
} // if(m_currentIndex>0)...
}// end of CPPTriPlantBuffer::Flush()...
//
//
//
//
PPTriPlant* CPPTriPlantBuffer::GetPPTriPlantPtr(s32 amountToAdd)
{
if((this->m_PerThreadData[g_RenderThreadIndex].m_currentIndex+amountToAdd) > PPTRIPLANT_BUFFER_SIZE)
{
this->Flush();
}
return(&this->m_PerThreadData[g_RenderThreadIndex].m_Buffer[m_PerThreadData[g_RenderThreadIndex].m_currentIndex]);
}
//
//
//
//
void CPPTriPlantBuffer::ChangeCurrentPlantModelsSet(s32 newSet)
{
// different modes of pipeline?
if(this->GetPlantModelsSet() != newSet)
{
// flush contents of old pipeline mode:
this->Flush();
// set new pipeline mode:
this->SetPlantModelsSet(newSet);
}
}
//
//
//
//
void CPPTriPlantBuffer::IncreaseBufferIndex(s32 pipeMode, s32 amount)
{
if(this->GetPlantModelsSet() != pipeMode)
{
// incompatible pipeline modes!
Assertf(FALSE, "Incompatible pipeline modes!");
return;
}
this->m_PerThreadData[g_RenderThreadIndex].m_currentIndex += amount;
if(this->m_PerThreadData[g_RenderThreadIndex].m_currentIndex >= PPTRIPLANT_BUFFER_SIZE)
{
this->Flush();
}
}
//
//
//
//
bool CPPTriPlantBuffer::SetPlantModelsTab(u32 index, grassModel* pPlantModels)
{
if(index >= PPTRIPLANT_MODELS_TAB_SIZE)
return(FALSE);
this->m_pPlantModelsTab[index] = pPlantModels;
return(TRUE);
}
//
//
//
//
grassModel* CPPTriPlantBuffer::GetPlantModelsTab(u32 index)
{
if(index >= PPTRIPLANT_MODELS_TAB_SIZE)
return(NULL);
grassModel* plantModels = this->m_pPlantModelsTab[index];
return(plantModels);
}
#if PSN_PLANTSMGR_SPU_RENDER
//
//
// internal buffers for storing texture command buffers:
//
bool CPPTriPlantBuffer::SetPlantTexturesCmd(u32 slotID, u32 index, u32 *cmd)
{
Assert(slotID < PPTRIPLANT_MODELS_TAB_SIZE);
Assert(index < CPLANT_SLOT_NUM_TEXTURES*2); // base textures + LOD textures
u32 *dst0 = m_pPlantsTexturesCmdTab[slotID];
u32 *dst = dst0 + index*PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE;
::sysMemCpy(dst, cmd, PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE*sizeof(u32));
return(TRUE);
}
u32* CPPTriPlantBuffer::GetPlantTexturesCmd(u32 slotID, u32 index)
{
u32 *dst0 = m_pPlantsTexturesCmdTab[slotID];
u32 *dst = dst0 + index*PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE;
return(dst);
}
u32 CPPTriPlantBuffer::GetPlantTexturesCmdOffset(u32 slotID, u32 index)
{
u32 offset0 = m_pPlantsTexturesCmdOffsetTab[slotID];
u32 dst = offset0 + (index*PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE)*sizeof(u32);
return(dst);
}
//
//
//
//
bool CPPTriPlantBuffer::AllocateTextureBuffers()
{
m_pPlantsTexturesCmd0 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_TEXTURES*2*PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsTexturesCmdOffset0 = gcm::MainOffset(m_pPlantsTexturesCmd0);
Assert(m_pPlantsTexturesCmd0);
Assert(m_pPlantsTexturesCmdOffset0);
m_pPlantsTexturesCmd1 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_TEXTURES*2*PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsTexturesCmdOffset1 = gcm::MainOffset(m_pPlantsTexturesCmd1);
Assert(m_pPlantsTexturesCmd1);
Assert(m_pPlantsTexturesCmdOffset1);
m_pPlantsTexturesCmd2 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_TEXTURES*2*PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsTexturesCmdOffset2 = gcm::MainOffset(m_pPlantsTexturesCmd2);
Assert(m_pPlantsTexturesCmd2);
Assert(m_pPlantsTexturesCmdOffset2);
m_pPlantsTexturesCmd3 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_TEXTURES*2*PLANTSMGR_LOCAL_TEXTURE_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsTexturesCmdOffset3 = gcm::MainOffset(m_pPlantsTexturesCmd3);
Assert(m_pPlantsTexturesCmd3);
Assert(m_pPlantsTexturesCmdOffset3);
m_pPlantsTexturesCmdTab[0] = m_pPlantsTexturesCmd0;
m_pPlantsTexturesCmdTab[1] = m_pPlantsTexturesCmd1;
m_pPlantsTexturesCmdTab[2] = m_pPlantsTexturesCmd2;
m_pPlantsTexturesCmdTab[3] = m_pPlantsTexturesCmd3;
m_pPlantsTexturesCmdOffsetTab[0] = m_pPlantsTexturesCmdOffset0;
m_pPlantsTexturesCmdOffsetTab[1] = m_pPlantsTexturesCmdOffset1;
m_pPlantsTexturesCmdOffsetTab[2] = m_pPlantsTexturesCmdOffset2;
m_pPlantsTexturesCmdOffsetTab[3] = m_pPlantsTexturesCmdOffset3;
return(TRUE);
}
void CPPTriPlantBuffer::DestroyTextureBuffers()
{
#define DELETE_PTR(P, OFFSET) {if(P) {GtaFreeAlign(P); P=NULL; OFFSET=0;}}
DELETE_PTR(m_pPlantsTexturesCmd0, m_pPlantsTexturesCmdOffset0);
DELETE_PTR(m_pPlantsTexturesCmd1, m_pPlantsTexturesCmdOffset1);
DELETE_PTR(m_pPlantsTexturesCmd2, m_pPlantsTexturesCmdOffset2);
DELETE_PTR(m_pPlantsTexturesCmd3, m_pPlantsTexturesCmdOffset3);
#undef DELETE_PTR
m_pPlantsTexturesCmdTab[0] =
m_pPlantsTexturesCmdTab[1] =
m_pPlantsTexturesCmdTab[2] =
m_pPlantsTexturesCmdTab[3] = NULL;
m_pPlantsTexturesCmdOffsetTab[0] =
m_pPlantsTexturesCmdOffsetTab[1] =
m_pPlantsTexturesCmdOffsetTab[2] =
m_pPlantsTexturesCmdOffsetTab[3] = 0;
}
//
//
// internal buffers for storing geometry command buffers:
//
bool CPPTriPlantBuffer::SetPlantModelsCmd(u32 slotID, u32 index, u32 *cmd)
{
Assert(slotID < PPTRIPLANT_MODELS_TAB_SIZE);
Assert(index < CPLANT_SLOT_NUM_MODELS*2); // base models + LOD models
u32 *dst0 = m_pPlantsModelsCmdTab[slotID];
u32 *dst = dst0 + index*PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE;
::sysMemCpy(dst, cmd, PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE*sizeof(u32));
return(TRUE);
}
u32* CPPTriPlantBuffer::GetPlantModelsCmd(u32 slotID, u32 index)
{
u32 *dst0 = m_pPlantsModelsCmdTab[slotID];
u32 *dst = dst0 + index*PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE;
return(dst);
}
u32 CPPTriPlantBuffer::GetPlantModelsCmdOffset(u32 slotID, u32 index)
{
u32 offset0 = m_pPlantsModelsCmdOffsetTab[slotID];
u32 dst = offset0 + (index*PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE)*sizeof(u32);
return(dst);
}
//
//
//
//
bool CPPTriPlantBuffer::AllocateModelsBuffers()
{
m_pPlantsModelsCmd0 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_MODELS*2*PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsModelsCmdOffset0 = gcm::MainOffset(m_pPlantsModelsCmd0);
Assert(m_pPlantsModelsCmd0);
Assert(m_pPlantsModelsCmdOffset0);
m_pPlantsModelsCmd1 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_MODELS*2*PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsModelsCmdOffset1 = gcm::MainOffset(m_pPlantsModelsCmd1);
Assert(m_pPlantsModelsCmd1);
Assert(m_pPlantsModelsCmdOffset1);
m_pPlantsModelsCmd2 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_MODELS*2*PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsModelsCmdOffset2 = gcm::MainOffset(m_pPlantsModelsCmd2);
Assert(m_pPlantsModelsCmd2);
Assert(m_pPlantsModelsCmdOffset2);
m_pPlantsModelsCmd3 = (u32*)GtaMallocAlign(CPLANT_SLOT_NUM_MODELS*2*PLANTSMGR_LOCAL_GEOMETRY_MAXCMDSIZE*sizeof(u32), PLANTSMGR_CMD_DEF_ALIGN);
m_pPlantsModelsCmdOffset3 = gcm::MainOffset(m_pPlantsModelsCmd3);
Assert(m_pPlantsModelsCmd3);
Assert(m_pPlantsModelsCmdOffset3);
m_pPlantsModelsCmdTab[0] = m_pPlantsModelsCmd0;
m_pPlantsModelsCmdTab[1] = m_pPlantsModelsCmd1;
m_pPlantsModelsCmdTab[2] = m_pPlantsModelsCmd2;
m_pPlantsModelsCmdTab[3] = m_pPlantsModelsCmd3;
m_pPlantsModelsCmdOffsetTab[0] = m_pPlantsModelsCmdOffset0;
m_pPlantsModelsCmdOffsetTab[1] = m_pPlantsModelsCmdOffset1;
m_pPlantsModelsCmdOffsetTab[2] = m_pPlantsModelsCmdOffset2;
m_pPlantsModelsCmdOffsetTab[3] = m_pPlantsModelsCmdOffset3;
return(TRUE);
}
void CPPTriPlantBuffer::DestroyModelsBuffers()
{
#define DELETE_PTR(P, OFFSET) {if(P) {GtaFreeAlign(P); P=NULL; OFFSET=0;}}
DELETE_PTR(m_pPlantsModelsCmd0, m_pPlantsModelsCmdOffset0);
DELETE_PTR(m_pPlantsModelsCmd1, m_pPlantsModelsCmdOffset1);
DELETE_PTR(m_pPlantsModelsCmd2, m_pPlantsModelsCmdOffset2);
DELETE_PTR(m_pPlantsModelsCmd3, m_pPlantsModelsCmdOffset3);
#undef DELETE_PTR
m_pPlantsModelsCmdTab[0] =
m_pPlantsModelsCmdTab[1] =
m_pPlantsModelsCmdTab[2] =
m_pPlantsModelsCmdTab[3] = NULL;
m_pPlantsModelsCmdOffsetTab[0] =
m_pPlantsModelsCmdOffsetTab[1] =
m_pPlantsModelsCmdOffsetTab[2] =
m_pPlantsModelsCmdOffsetTab[3] = 0;
}
#endif //PSN_PLANTSMGR_SPU_RENDER...
///////////////////////////////////////////////////////////////////////////////////////////////////////
#if GRASS_INSTANCING
CGrassRenderer::InstanceBucket::InstanceBucket()
{
}
CGrassRenderer::InstanceBucket::~InstanceBucket()
{
}
#if PLANTSMGR_MULTI_RENDER
void CGrassRenderer::InstanceBucket::Init(grmShader *pShader, grcEffectVar textureVar)
{
// Record shader details.
m_pShader = pShader;
m_textureVar = textureVar;
for(s32 i=0; i<NUMBER_OF_RENDER_THREADS; i++)
{
m_PerThreadData0[i].m_isInstanceTextureLocked = false;
}
InitialiseTransport();
}
void CGrassRenderer::InstanceBucket::ShutDown()
{
CleanUpTransport();
}
void CGrassRenderer::InstanceBucket::Reset(u8 rti, grcEffectTechnique techId)
{
AssertMsg((m_PerThreadData0[rti].m_isInstanceTextureLocked == false), "CGrassRenderer::InstanceBucket::Reset()...Intance texture should not be locked.");
m_PerThreadData0[rti].m_techId = techId;
m_PerThreadData0[rti].m_noOfInstances = 0;
m_PerThreadData0[rti].m_pCurrentGeometry = NULL;
m_PerThreadData0[rti].m_pCurrentTexture = NULL;
}
bool CGrassRenderer::InstanceBucket::AddInstance(u8 rti, bool bMainRT, grmGeometry *pGeometry, grcTexture *pTexture, struct instGrassDataDrawStruct *pInstance, const Vec4V &umParams, const Vec2V &collParams)
{
// Have we reached capacity or is the geometry/texture different ?
if(IsFull(rti) || (pGeometry != m_PerThreadData0[rti].m_pCurrentGeometry) || (pTexture != m_PerThreadData0[rti].m_pCurrentTexture))
{
if(GetCount(rti)==0)
{
// Record texture/geometry.
m_PerThreadData0[rti].m_pCurrentTexture = pTexture;
m_PerThreadData0[rti].m_pCurrentGeometry = pGeometry;
}
else
{
if(bMainRT)
{
// Send all instances.
Flush(rti);
// Record texture/geometry.
m_PerThreadData0[rti].m_pCurrentTexture = pTexture;
m_PerThreadData0[rti].m_pCurrentGeometry = pGeometry;
}
else
{
return(false); // subtask: instance not added
}
}
}
if(!m_PerThreadData0[rti].m_isInstanceTextureLocked)
{
//LockTransport(rti);
m_PerThreadData0[rti].m_isInstanceTextureLocked = true;
}
Vector4 *pDest = GetDataPtr(rti);
// Pack the instance data.
#if 1
Vector4 C0 = pInstance->m_WorldMatrix.a;
Vector4 C1 = pInstance->m_WorldMatrix.b;
Vector4 C2 = pInstance->m_WorldMatrix.c;
Vector4 C3 = pInstance->m_WorldMatrix.d;
*((Color32*)(&C0.w)) = pInstance->m_PlantColor32; // pack directly as u32
*((Color32*)(&C1.w)) = pInstance->m_GroundColorAmbient32; // pack directly as u32
C2.w = collParams.GetXf();
C3.w = collParams.GetYf();
pDest[0] = C0;
pDest[1] = C1;
pDest[2] = C2;
pDest[3] = C3;
pDest[4] = VEC4V_TO_VECTOR4(umParams);
#else
const Vec4V vXYZ = Vec4V(V_ONE_WZERO); // 1,1,1, 0
const Vec4V vW = Vec4V(V_ZERO_WONE); // 0,0,0, 1
Mat44V M = MATRIX44_TO_MAT44V(pInstance->m_WorldMatrix);
Vector4 groundColor_Ambient = pInstance->m_GroundColor;
groundColor_Ambient.w = float(ambient)/255.0f;
Vec4V C0 = M.GetCol0()*vXYZ + ScalarV(PackColour(pInstance->m_PlantColor))*vW;
Vec4V C1 = M.GetCol1()*vXYZ + ScalarV(PackColour(groundColor_Ambient))*vW;
Vec4V C2 = M.GetCol2()*vXYZ + collParams.GetX()*vW;
Vec4V C3 = M.GetCol3()*vXYZ + collParams.GetY()*vW;
pDest[0] = VEC4V_TO_VECTOR4(C0);
pDest[1] = VEC4V_TO_VECTOR4(C1);
pDest[2] = VEC4V_TO_VECTOR4(C2);
pDest[3] = VEC4V_TO_VECTOR4(C3);
pDest[4] = VEC4V_TO_VECTOR4(umParams);
#endif
// Increment the count.
m_PerThreadData0[rti].m_noOfInstances++;
return(true); // succcess
}
void CGrassRenderer::InstanceBucket::Flush(u8 rti)
{
if(m_PerThreadData0[rti].m_noOfInstances)
{
AssertMsg(m_PerThreadData0[rti].m_isInstanceTextureLocked, "CGrassRenderer::InstanceBucket::Flush(): Instance texture should be locked.");
if (m_PerThreadData0[rti].m_isInstanceTextureLocked)
UnlockTransport();
m_PerThreadData0[rti].m_isInstanceTextureLocked = false;
// Set the textures.
m_pShader->SetVar(m_textureVar, m_PerThreadData0[rti].m_pCurrentTexture);
#if GRASS_INSTANCING_TRANSPORT_TEXTURE
BindTransport();
#endif // GRASS_INSTANCING_TRANSPORT_TEXTURE
// Bind the technique/shader.
CGrassRenderer::BindPlantShader(m_PerThreadData0[rti].m_techId);
// Set up the geometry etc.
grcIndexBuffer* pIndexBuffer = m_PerThreadData0[rti].m_pCurrentGeometry->GetIndexBuffer(true);
grcVertexBuffer* pVertexBuffer = m_PerThreadData0[rti].m_pCurrentGeometry->GetVertexBuffer(true);
grcVertexDeclaration* vertexDeclaration = m_PerThreadData0[rti].m_pCurrentGeometry->GetDecl();
// Render the instances.
GRCDEVICE.SetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetIndices(*pIndexBuffer);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
#if RSG_DURANGO || RSG_ORBIS
GRCDEVICE.SetUpPriorToDraw((grcDrawMode)m_PerThreadData0[rti].m_pCurrentGeometry->GetPrimitiveType());
#endif // RSG_DURANGO || RSG_ORBIS
#if GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
BindTransport(rti);
#endif // GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
#if __D3D11
const bool alreadySetupPriorToDraw = RSG_DURANGO;
GRCDEVICE.DrawInstancedIndexedPrimitive((grcDrawMode)m_PerThreadData0[rti].m_pCurrentGeometry->GetPrimitiveType(), m_PerThreadData0[rti].m_pCurrentGeometry->GetIndexBuffer(true)->GetIndexCount(), m_PerThreadData0[rti].m_noOfInstances, 0, 0, 0, alreadySetupPriorToDraw);
#elif RSG_ORBIS
gfxc.setNumInstances(m_PerThreadData0[rti].m_noOfInstances);
if (GRCDEVICE.NotifyDrawCall(1))
gfxc.drawIndex(pIndexBuffer->GetIndexCount(), pIndexBuffer->GetUnsafeReadPtr());
gfxc.setNumInstances(1);
#endif
// Unbind.
UnbindTransport();
CGrassRenderer::UnBindPlantShader();
m_PerThreadData0[rti].m_noOfInstances = 0;
}
}
#else //PLANTSMGR_MULTI_RENDER...
void CGrassRenderer::InstanceBucket::Init(grmShader *pShader, grcEffectVar textureVar)
{
int i;
// Record shader details.
m_pShader = pShader;
m_textureVar = textureVar;
for(i=0; i<NUMBER_OF_RENDER_THREADS; i++)
{
m_PerThreadData[i].m_isInstanceTextureLocked = false;
}
InitialiseTransport();
}
void CGrassRenderer::InstanceBucket::ShutDown()
{
CleanUpTransport();
}
void CGrassRenderer::InstanceBucket::Reset(grcEffectTechnique techId)
{
AssertMsg((m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked == false), "CGrassRenderer::InstanceBucket::Reset()...Intance texture should not be locked.");
m_PerThreadData[g_RenderThreadIndex].m_techId = techId;
m_PerThreadData[g_RenderThreadIndex].m_noOfInstances = 0;
m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry = NULL;
m_PerThreadData[g_RenderThreadIndex].m_pCurrentTexture = NULL;
}
void CGrassRenderer::InstanceBucket::AddInstance(grmGeometry *pGeometry, grcTexture *pTexture, struct instGrassDataDrawStruct *pInstance, const Vec4V &umParams, const Vec2V &collParams)
{
// Have we reached capacity or is the geometry/texture different ?
if(IsFull() || (pGeometry != m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry) || (pTexture != m_PerThreadData[g_RenderThreadIndex].m_pCurrentTexture))
{
// Send all instances.
Flush();
// Record texture/geometry.
m_PerThreadData[g_RenderThreadIndex].m_pCurrentTexture = pTexture;
m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry = pGeometry;
}
if(!m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked)
{
if (LockTransport())
m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked = true;
}
Vector4 *pDest = GetDataPtr();
// Pack the instance data.
#if 1
Vector4 C0 = pInstance->m_WorldMatrix.a;
Vector4 C1 = pInstance->m_WorldMatrix.b;
Vector4 C2 = pInstance->m_WorldMatrix.c;
Vector4 C3 = pInstance->m_WorldMatrix.d;
*((Color32*)(&C0.w)) = pInstance->m_PlantColor32; // pack directly as u32
*((Color32*)(&C1.w)) = pInstance->m_GroundColorAmbient32; // pack directly as u32
C2.w = collParams.GetXf();
C3.w = collParams.GetYf();
pDest[0] = C0;
pDest[1] = C1;
pDest[2] = C2;
pDest[3] = C3;
pDest[4] = VEC4V_TO_VECTOR4(umParams);
#else
const Vec4V vXYZ = Vec4V(V_ONE_WZERO); // 1,1,1, 0
const Vec4V vW = Vec4V(V_ZERO_WONE); // 0,0,0, 1
Mat44V M = MATRIX44_TO_MAT44V(pInstance->m_WorldMatrix);
Vector4 groundColor_Ambient = pInstance->m_GroundColor;
groundColor_Ambient.w = float(ambient)/255.0f;
Vec4V C0 = M.GetCol0()*vXYZ + ScalarV(PackColour(pInstance->m_PlantColor))*vW;
Vec4V C1 = M.GetCol1()*vXYZ + ScalarV(PackColour(groundColor_Ambient))*vW;
Vec4V C2 = M.GetCol2()*vXYZ + collParams.GetX()*vW;
Vec4V C3 = M.GetCol3()*vXYZ + collParams.GetY()*vW;
pDest[0] = VEC4V_TO_VECTOR4(C0);
pDest[1] = VEC4V_TO_VECTOR4(C1);
pDest[2] = VEC4V_TO_VECTOR4(C2);
pDest[3] = VEC4V_TO_VECTOR4(C3);
pDest[4] = VEC4V_TO_VECTOR4(umParams);
#endif
// Increment the count.
m_PerThreadData[g_RenderThreadIndex].m_noOfInstances++;
}
void CGrassRenderer::InstanceBucket::Flush()
{
if(m_PerThreadData[g_RenderThreadIndex].m_noOfInstances)
{
AssertMsg(m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked, "CGrassRenderer::SendTriPlantInstances_Indexed()...Instance texture should be locked.");
if (m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked)
UnlockTransport();
m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked = false;
// Set the textures.
m_pShader->SetVar(m_textureVar, m_PerThreadData[g_RenderThreadIndex].m_pCurrentTexture);
#if GRASS_INSTANCING_TRANSPORT_TEXTURE
BindTransport();
#endif // GRASS_INSTANCING_TRANSPORT_TEXTURE
// Bind the technique/shader.
CGrassRenderer::BindPlantShader(m_PerThreadData[g_RenderThreadIndex].m_techId);
// Set up the geometry etc.
grcIndexBuffer* pIndexBuffer = m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry->GetIndexBuffer(true);
grcVertexBuffer* pVertexBuffer = m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry->GetVertexBuffer(true);
grcVertexDeclaration* vertexDeclaration = m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry->GetDecl();
// Render the instances.
GRCDEVICE.SetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetIndices(*pIndexBuffer);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
#if RSG_DURANGO || RSG_ORBIS
GRCDEVICE.SetUpPriorToDraw((grcDrawMode)m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry->GetPrimitiveType());
#endif // RSG_DURANGO || RSG_ORBIS
#if GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
BindTransport();
#endif // GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
#if __D3D11
const bool alreadySetupPriorToDraw = RSG_DURANGO;
GRCDEVICE.DrawInstancedIndexedPrimitive((grcDrawMode)m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry->GetPrimitiveType(), m_PerThreadData[g_RenderThreadIndex].m_pCurrentGeometry->GetIndexBuffer(true)->GetIndexCount(), m_PerThreadData[g_RenderThreadIndex].m_noOfInstances, 0, 0, 0, alreadySetupPriorToDraw);
#elif RSG_ORBIS
gfxc.setNumInstances(m_PerThreadData[g_RenderThreadIndex].m_noOfInstances);
if (GRCDEVICE.NotifyDrawCall(1))
gfxc.drawIndex(pIndexBuffer->GetIndexCount(), pIndexBuffer->GetUnsafeReadPtr());
gfxc.setNumInstances(1);
#endif
// Unbind.
UnbindTransport();
CGrassRenderer::UnBindPlantShader();
m_PerThreadData[g_RenderThreadIndex].m_noOfInstances = 0;
}
else
{
#if NEW_STORE_OPT
// no of instances=0, but still locked because buckets are locked upfront:
m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked = false;
#endif
}
}
#endif//PLANTSMGR_MULTI_RENDER...
float CGrassRenderer::InstanceBucket::PackColour(Vector4 &c)
{
Vector4 cStar = c*255.0f;
cStar.RoundToNearestIntZero();
return 256.0f*256.0f*256.0f*cStar.w + 256.0f*256.0f*cStar.x + 256.0f*cStar.y + cStar.z;
}
#if GRASS_INSTANCING_TRANSPORT_TEXTURE
void CGrassRenderer::InstanceBucket::InitialiseTransport()
{
grcTextureFactory::TextureCreateParams TextureParams(grcTextureFactory::TextureCreateParams::VIDEO,
grcTextureFactory::TextureCreateParams::LINEAR, grcsWrite /*| grcsRead*/, NULL,
grcTextureFactory::TextureCreateParams::NORMAL,
grcTextureFactory::TextureCreateParams::MSAA_NONE);
m_pInstanceTexture = grcTextureFactory::GetInstance().Create((0x1 << GRASS_INSTANCING_TEXTURE_WIDTH_IN_INSTANCES_POWER_OF_2)*GRASS_INSTANCING_INSTANCE_SIZE,
GRASS_INSTANCING_TEXTURE_HEIGHT_IN_INSTANCES, grctfA32B32G32R32F, NULL, 1U /*numMips*/, &TextureParams);
m_instanceTextureVar = ms_Shader->LookupVar("instanceTexture0", TRUE);
}
void CGrassRenderer::InstanceBucket::CleanUpTransport()
{
if(m_pInstanceTexture)
{
m_pInstanceTexture->Release();
m_pInstanceTexture = NULL;
}
}
bool CGrassRenderer::InstanceBucket::LockTransport()
{
PF_AUTO_PUSH_TIMEBAR_BUDGETED("Grass Instance->LockRect", 4.0f);
// Lock the instance texture if needs be.
return m_pInstanceTexture->LockRect(0, 0, m_instanceTextureLock, grcsWrite | grcsNoOverwrite);
}
void CGrassRenderer::InstanceBucket::UnlockTransport()
{
m_pInstanceTexture->UnlockRect(m_instanceTextureLock);
}
Vector4 *CGrassRenderer::InstanceBucket::GetDataPtr()
{
// "Index" into the instance texture.
char *pTexels = (char *)m_instanceTextureLock.Base;
u32 x = m_noOfInstances & ((0x1 << GRASS_INSTANCING_TEXTURE_WIDTH_IN_INSTANCES_POWER_OF_2) - 1);
u32 y = m_noOfInstances >> GRASS_INSTANCING_TEXTURE_WIDTH_IN_INSTANCES_POWER_OF_2;
Vector4 *pDest = (Vector4 *)&pTexels[y*m_instanceTextureLock.Pitch + x*sizeof(Vector4)*GRASS_INSTANCING_INSTANCE_SIZE];
return pDest;
}
void CGrassRenderer::InstanceBucket::BindTransport()
{
m_pShader->SetVar(m_instanceTextureVar, m_pInstanceTexture);
}
void CGrassRenderer::InstanceBucket::UnbindTransport()
{
}
#endif // GRASS_INSTANCING_TRANSPORT_TEXTURE
#if GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
void CGrassRenderer::InstanceBucket::InitialiseTransport()
{
grcEffectGlobalVar var = grcEffect::LookupGlobalVar("instanceData0");
grcCBuffer *pBuff = grcEffect::GetGlobalParentCBuf(var);
m_VertexShaderRegister = pBuff->GetRegister(rage::VS_TYPE);
// Must be less than 4096 bytes.
m_pCBuffer = rage_new rage::grcCBuffer(GRASS_INSTANCING_BUCKET_SIZE*GRASS_INSTANCING_INSTANCE_SIZE*sizeof(Vector4), true);
// Set the registers/slots the constant buffer is bound to.
u16 registers[NONE_TYPE] = { 0 };
registers[VS_TYPE] = (u16)pBuff->GetRegister(rage::VS_TYPE);
registers[PS_TYPE] = (u16)pBuff->GetRegister(rage::PS_TYPE);
m_pCBuffer->SetRegisters(registers);
for(int i=0; i<NUMBER_OF_RENDER_THREADS; i++)
{
#if PLANTSMGR_MULTI_RENDER
m_pLockBase0[i] = NULL;
m_pLockBase0[i] = (Vector4*)rage_new char[m_pCBuffer->GetSize()];
#else
m_pLockBase[i] = NULL;
m_pLockBase[i] = (Vector4*)rage_new char[m_pCBuffer->GetSize()];
#endif
}
}
void CGrassRenderer::InstanceBucket::CleanUpTransport()
{
if(m_pCBuffer)
{
delete m_pCBuffer;
m_pCBuffer = NULL;
}
for(int i=0; i<NUMBER_OF_RENDER_THREADS; i++)
{
#if PLANTSMGR_MULTI_RENDER
if(m_pLockBase0[i])
{
delete [] m_pLockBase0[i];
m_pLockBase0[i] = NULL;
}
#else
if(m_pLockBase[i])
{
delete [] m_pLockBase[i];
m_pLockBase[i] = NULL;
}
#endif
}
}
#if PLANTSMGR_MULTI_RENDER
void CGrassRenderer::InstanceBucket::BindTransport(u8 rti)
{
// This is during an IsInDraw() session, so effect_gnm.cpp will bind the buffer.
void *pDest = m_pCBuffer->BeginUpdate(m_PerThreadData0[rti].m_noOfInstances*GRASS_INSTANCING_INSTANCE_SIZE*sizeof(Vector4));
sysMemCpy(pDest, m_pLockBase0[rti], m_PerThreadData0[rti].m_noOfInstances*GRASS_INSTANCING_INSTANCE_SIZE*sizeof(Vector4));
m_pCBuffer->EndUpdate();
#if __D3D11
GRCDEVICE.SetVertexShaderConstantBufferOverrides(m_VertexShaderRegister, &m_pCBuffer, 1);
#endif // __D3D11
}
#else //PLANTSMGR_MULTI_RENDER...
void CGrassRenderer::InstanceBucket::BindTransport()
{
// This is during an IsInDraw() session, so effect_gnm.cpp will bind the buffer.
void *pDest = m_pCBuffer->BeginUpdate(m_PerThreadData[g_RenderThreadIndex].m_noOfInstances*GRASS_INSTANCING_INSTANCE_SIZE*sizeof(Vector4));
sysMemCpy(pDest, m_pLockBase[g_RenderThreadIndex], m_PerThreadData[g_RenderThreadIndex].m_noOfInstances*GRASS_INSTANCING_INSTANCE_SIZE*sizeof(Vector4));
m_pCBuffer->EndUpdate();
#if RSG_PC && __D3D11
GRCDEVICE.SetVertexShaderConstantBufferOverrides(m_VertexShaderRegister, &m_pCBuffer, 1);
#endif
}
#endif //PLANTSMGR_MULTI_RENDER...
#endif // GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
///////////////////////////////////////////////////////////////////////////////////////////////////////
CGrassRenderer::InstanceBucket_LOD2::InstanceBucket_LOD2() : InstanceBucket()
{
}
CGrassRenderer::InstanceBucket_LOD2::~InstanceBucket_LOD2()
{
}
#if PLANTSMGR_MULTI_RENDER
void CGrassRenderer::InstanceBucket_LOD2::Flush(u8 rti)
{
if(m_PerThreadData0[rti].m_noOfInstances)
{
// Unlock the instance texture.
AssertMsg(m_PerThreadData0[rti].m_isInstanceTextureLocked, "CGrassRenderer::InstanceBucket_LOD2::Flush(): Instance texture should be locked.");
if (m_PerThreadData0[rti].m_isInstanceTextureLocked)
UnlockTransport();
m_PerThreadData0[rti].m_isInstanceTextureLocked = false;
// Set the textures.
m_pShader->SetVar(m_textureVar, m_PerThreadData0[rti].m_pCurrentTexture);
#if GRASS_INSTANCING_TRANSPORT_TEXTURE
BindTransport();
#endif // GRASS_INSTANCING_TRANSPORT_TEXTURE
// Bind the technique/shader.
CGrassRenderer::BindPlantShader(m_PerThreadData0[rti].m_techId);
// Set up the geometry etc.
grcVertexBuffer *pVertexBuffer = ms_plantLOD2VertexBuffer;
grcVertexDeclaration *vertexDeclaration = ms_plantLOD2VertexDecl;
// Render the instances.
GRCDEVICE.SetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
#if RSG_DURANGO || RSG_ORBIS
GRCDEVICE.SetUpPriorToDraw(drawTriStrip);
#endif // RSG_DURANGO || RSG_ORBIS
#if GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
BindTransport(rti);
#endif // GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
#if __D3D11
const bool alreadySetupPriorToDraw = RSG_DURANGO;
GRCDEVICE.DrawInstancedPrimitive(drawTriStrip, 4, m_PerThreadData0[rti].m_noOfInstances, 0, 0, alreadySetupPriorToDraw);
#elif RSG_ORBIS
gfxc.setNumInstances(m_PerThreadData0[rti].m_noOfInstances);
if (GRCDEVICE.NotifyDrawCall(1))
gfxc.drawIndexAuto(4);
gfxc.setNumInstances(1);
#endif
// Unbind.
UnbindTransport();
CGrassRenderer::UnBindPlantShader();
m_PerThreadData0[rti].m_noOfInstances = 0;
}
}
#else //PLANTSMGR_MULTI_RENDER...
void CGrassRenderer::InstanceBucket_LOD2::Flush()
{
if(m_PerThreadData[g_RenderThreadIndex].m_noOfInstances)
{
// Unlock the instance texture.
AssertMsg(m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked, "CGrassRenderer::SendTriPlantInstances_Indexed()...Instance texture should be locked.");
if (m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked)
UnlockTransport();
m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked = false;
// Set the textures.
m_pShader->SetVar(m_textureVar, m_PerThreadData[g_RenderThreadIndex].m_pCurrentTexture);
#if GRASS_INSTANCING_TRANSPORT_TEXTURE
BindTransport();
#endif // GRASS_INSTANCING_TRANSPORT_TEXTURE
// Bind the technique/shader.
CGrassRenderer::BindPlantShader(m_PerThreadData[g_RenderThreadIndex].m_techId);
// Set up the geometry etc.
grcVertexBuffer *pVertexBuffer = ms_plantLOD2VertexBuffer;
grcVertexDeclaration *vertexDeclaration = ms_plantLOD2VertexDecl;
// Render the instances.
GRCDEVICE.SetVertexDeclaration(vertexDeclaration);
GRCDEVICE.SetStreamSource(0, *pVertexBuffer, 0, pVertexBuffer->GetVertexStride());
#if RSG_DURANGO || RSG_ORBIS
GRCDEVICE.SetUpPriorToDraw(drawTriStrip);
#endif // RSG_DURANGO || RSG_ORBIS
#if GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
BindTransport();
#endif // GRASS_INSTANCING_TRANSPORT_CONSTANT_BUFFERS
#if __D3D11
const bool alreadySetupPriorToDraw = RSG_DURANGO;
GRCDEVICE.DrawInstancedPrimitive(drawTriStrip, 4, m_PerThreadData[g_RenderThreadIndex].m_noOfInstances, 0, 0, alreadySetupPriorToDraw);
#elif RSG_ORBIS
gfxc.setNumInstances(m_PerThreadData[g_RenderThreadIndex].m_noOfInstances);
if (GRCDEVICE.NotifyDrawCall(1))
gfxc.drawIndexAuto(4);
gfxc.setNumInstances(1);
#endif
// Unbind.
UnbindTransport();
CGrassRenderer::UnBindPlantShader();
m_PerThreadData[g_RenderThreadIndex].m_noOfInstances = 0;
}
else
{
#if NEW_STORE_OPT
// no of instances=0, but still locked because buckets are locked upfront:
m_PerThreadData[g_RenderThreadIndex].m_isInstanceTextureLocked = false;
#endif
}
}
#endif//PLANTSMGR_MULTI_RENDER...
#endif // GRASS_INSTANCING