// // 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; iLookupVar(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; iSetVar(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=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= 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= 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= 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(n0.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(nbegin = (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(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(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) (PTRcurrent = 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; slotIDm_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; im_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; iBeginDraw(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; vRecordSetVar(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; iV1.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(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 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(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; jSetVar(ms_shdCameraPosID, globalCameraPos); ms_Shader->SetVar(ms_shdPlayerPosID, globalPlayerPos); for(u32 v=0; vSetVar(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 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; tSetVar(ms_shdCameraPosID, globalCameraPos); ms_Shader->SetVar(ms_shdPlayerPosID, globalPlayerPos); for(u32 v=0; vSetVar(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; iV1.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(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 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(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; jflags.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 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(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; jflags.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 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(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; jflags.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 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(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; jBeginDraw(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; im_PerThreadData[i].m_currentIndex = 0; this->m_PerThreadData[i].m_plantModelsSet = PPPLANTBUF_MODEL_SET0; //this->SetPlantModelsSet(PPPLANTBUF_MODEL_SET0); } for(s32 i=0; i0) { #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; im_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; im_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; iGetSize()]; #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; iBeginUpdate(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