///////////////////////////////////////////////////////////////////////////////// // Title : DynamicEntityDrawHandler.cpp // Author : Russ Schaaf // Started : 18/11/2010 // ///////////////////////////////////////////////////////////////////////////////// #include "renderer/Entities/DynamicEntityDrawHandler.h" // Rage Headers #include "breakableglass/bgdrawable.h" #include "breakableglass/breakable.h" #include "grcore/matrix43.h" #include "profile/cputrace.h" // Framework Headers #if __BANK #include "fwdebug/picker.h" #endif // __BANK // Game Headers #include "cloth/charactercloth.h" #include "physics/gtaInst.h" #include "physics/physics.h" #include "scene/DynamicEntity.h" #include "renderer/Renderer.h" #include "renderer/DrawLists/drawListMgr.h" #include "renderer/DrawLists/DrawListProfileStats.h" #include "renderer/Entities/VehicleDrawHandler.h" #include "shaders/CustomShaderEffectVehicle.h" #include "shaders/ShaderLib.h" #if __BANK #include "vehicles/VehicleFactory.h" #endif // __BANK RENDER_OPTIMISATIONS() RAGETRACE_DECL(CDynamicEntity_AddToDrawList); dlCmdBase* CDynamicEntityDrawHandler::AddToDrawList(fwEntity* pBaseEntity, fwDrawDataAddParams* pBaseParams) { DL_PF_FUNC( TotalAddToDrawList ); DL_PF_FUNC( TotalDynamicEntityAddToDrawList ); DL_PF_FUNC( DynamicEntityAddToDrawList ); RAGETRACE_SCOPE(CDynamicEntity_AddToDrawList); CDynamicEntity* pEntity = static_cast(pBaseEntity); #if __BANK if (ShouldSkipEntity(pEntity)) return NULL; #endif // __BANK // fragments need a fragInst to render if(pEntity->m_nFlags.bIsFrag && pEntity->GetFragInst() == NULL) return NULL; // want to return the draw command that was created here dlCmdBase *pReturnDC = NULL; // if we're not a fragment and don't have a skeleton then use basic render if(!pEntity->m_nFlags.bIsFrag && pEntity->GetSkeleton() == NULL) { pReturnDC = CEntityDrawHandler::AddBasicToDrawList(this, pBaseEntity, pBaseParams); } else { if (pEntity->m_nFlags.bIsFrag) { CDrawListPrototypeManager::Flush(); pReturnDC = CDynamicEntityDrawHandler::AddFragmentToDrawList(this, pBaseEntity, pBaseParams); } else { CDrawListPrototypeManager::Flush(); pReturnDC = CDynamicEntityDrawHandler::AddSkinnedToDrawList(this, pBaseEntity, pBaseParams); } } return pReturnDC; } dlCmdBase* CDynamicEntityDrawHandler::AddFragmentToDrawList(CEntityDrawHandler* pDrawHandler, fwEntity* pBaseEntity, fwDrawDataAddParams* pParams) { DL_PF_FUNC( TotalAddToDrawList ); DL_PF_FUNC( TotalDynamicEntityAddToDrawList ); DL_PF_FUNC( DynamicEntityFragmentAddToDrawList ); RAGETRACE_SCOPE(CDynamicEntity_AddToDrawList); CDynamicEntity* pEntity = static_cast(pBaseEntity); #if __BANK if (ShouldSkipEntity(pEntity)) return NULL; #endif // __BANK // fragments need a fragInst to render Assertf(pEntity->m_nFlags.bIsFrag, "Only fragments are allowed here!"); fragInst* pFragInst = pEntity->GetFragInst(); if(!pEntity->m_nFlags.bIsFrag || pFragInst == NULL) return NULL; // Make sure its really really a fragment #if __ASSERT Assertf(IsFragInst(pFragInst), "Object (%s) claimed to be a fragment, but the physics isn't a fraginst", pEntity->GetBaseModelInfo()->GetModelName()); #endif // want to return the draw command that was created here dlCmdBase *pReturnDC = NULL; eRenderMode renderMode = DRAWLISTMGR->GetUpdateRenderMode(); u32 bucket = DRAWLISTMGR->GetUpdateBucket(); u32 bucketMask = DRAWLISTMGR->GetUpdateBucketMask(); pDrawHandler->BeforeAddToDrawList(pEntity, pEntity->GetModelIndex(), renderMode, bucket, bucketMask, pParams, true); CBaseModelInfo* pMI = pEntity->GetBaseModelInfo(); Assert(pMI); // Search for a cloth drawable fragCacheEntry *cacheEntry = pFragInst->GetCacheEntry(); rmcDrawable *pClothDrawable = NULL; bool captureStats = false; BANK_ONLY(captureStats = pBaseEntity == g_PickerManager.GetSelectedEntity();) bool onlyCloth = true; bool isClothReady = true; environmentCloth* pEnvCloth = NULL; if( cacheEntry ) { fragHierarchyInst* hierInst = cacheEntry->GetHierInst(); Assertf( hierInst,"No HierInst on a cached frag" ); pEnvCloth = hierInst->envCloth; if( pEnvCloth REPLAY_ONLY(&& !pEnvCloth->GetFlag(environmentCloth::CLOTH_ForceInactiveForReplay))) { pClothDrawable = pEnvCloth->GetDrawable(); pEnvCloth->SetFlag(environmentCloth::CLOTH_IsVisible,true); Assert( pFragInst->GetType() ); onlyCloth = (NULL == pFragInst->GetType()->GetClothDrawable()) ; Assert( pEnvCloth->GetClothController() ); Assert( pEnvCloth->GetClothController()->GetClothInstance() ); isClothReady = ((clothInstance*) pEnvCloth->GetClothController()->GetClothInstance())->IsReady(); } } bool bIsVehicleCloth = false; // Draw a cloth ? if( pClothDrawable && onlyCloth && isClothReady ) { #if __BANK if(g_DrawCloth) #endif // __BANK { Assert( pEnvCloth ); gDrawListMgr->AddClothReference(pEnvCloth); Assert( pClothDrawable ); // Set material to trees to get back lighting to work u8 matid = pEntity->GetDeferredMaterial(); if ((matid&DEFERRED_MATERIAL_CLEAR)==0){ matid = (matid&~DEFERRED_MATERIAL_CLEAR) |DEFERRED_MATERIAL_TREE; } Vector3 v = VEC3V_TO_VECTOR3(pEnvCloth->GetOffset()); grcDepthStencilStateHandle DSS = static_cast(gDrawListMgr)->IsBuildingMirrorReflectionDrawList() ? CPhysics::GetClothManager()->GetInvertPassDSS() : CPhysics::GetClothManager()->GetPassDSS(); DLC( dlCmdDrawTwoSidedDrawable, (pClothDrawable, v, pEntity->GetModelIndex(), pParams->AlphaFade, pEntity->GetNaturalAmbientScale(), pEntity->GetArtificialAmbientScale(), matid, pEntity->GetInteriorLocation().IsValid(), CPhysics::GetClothManager()->GetPassRS(), DSS, captureStats, true, bIsVehicleCloth, pEntity)); } } else if( pMI->GetModelType() != MI_TYPE_PED ) { // No Peds if (pFragInst->GetSkeleton()) { GET_DLC(pReturnDC, CDrawFragDC, (pEntity, false)); // render a second time the damaged part // if we have a skeleton, then we must have a frag cache entry (that's where the skeleton comes from) // also must have a hierachy instance, since that is contained in the frag cache entry fragCacheEntry* entry = pFragInst->GetCacheEntry(); Assert(entry); Assert(entry->GetHierInst()); if (entry->GetHierInst()->anyGroupDamaged) { DLC(CDrawFragDC, (pEntity, true)); } } #if ENABLE_FRAG_OPTIMIZATION else if(pMI->GetModelType() == MI_TYPE_VEHICLE) { DLC(CDrawFragTypeVehicleDC, (pEntity)); } #endif // ENABLE_FRAG_OPTIMIZATION else { DLC(CDrawFragTypeDC, (pEntity)); } // NOTE: If there is cloth attached to the vehicle, need to be rendered separately if( !onlyCloth && isClothReady ) { Assert( pClothDrawable ); #if __BANK if(g_DrawCloth) #endif // __BANK { if(pMI->GetModelType()==MI_TYPE_VEHICLE) { bIsVehicleCloth = true; } else { Assert( pEnvCloth ); gDrawListMgr->AddClothReference(pEnvCloth); Vector3 v = VEC3V_TO_VECTOR3(pEnvCloth->GetOffset()); grcDepthStencilStateHandle DSS = static_cast(gDrawListMgr)->IsBuildingMirrorReflectionDrawList() ? CPhysics::GetClothManager()->GetInvertPassDSS() : CPhysics::GetClothManager()->GetPassDSS(); DLC( dlCmdDrawTwoSidedDrawable, (pClothDrawable, v, pEntity->GetModelIndex(), pParams->AlphaFade, pEntity->GetNaturalAmbientScale(), pEntity->GetArtificialAmbientScale(), pEntity->GetDeferredMaterial(), pEntity->GetInteriorLocation().IsValid(), CPhysics::GetClothManager()->GetPassRS(), DSS, captureStats, true, bIsVehicleCloth, pEntity)); } } } } if(!DRAWLISTMGR->IsBuildingShadowDrawList() && !DRAWLISTMGR->IsBuildingWaterReflectionDrawList() && !DRAWLISTMGR->IsBuildingGlobalReflectionDrawList() && (bucket == CRenderer::RB_ALPHA || bucket == CRenderer::RB_CUTOUT)) // Skip unsupported modes/buckets { if(fragCacheEntry* pCacheEntry = pFragInst->GetCacheEntry()) { if(fragHierarchyInst* pHierInst = pCacheEntry->GetHierInst()) { for(int glassIndex = 0; glassIndex != pHierInst->numGlassInfos; ++ glassIndex) { bgGlassHandle handle = pHierInst->glassInfos[glassIndex].handle; if(handle != bgGlassHandle_Invalid) { bgDrawable& glassDrawable = bgGlassManager::GetGlassDrawable(handle); if( glassDrawable.IsReadyToDraw() ) { bgBreakable& glassBreakable = bgGlassManager::GetGlassBreakable(handle); // Copy the transform matrices on a fire&forget DC buffer atArray &brTransforms = glassBreakable.GetCurrentMatrices(); float *transforms = NULL; const int numTransforms = brTransforms.GetCount(); if(numTransforms) { // Allocate the buffer u32 dataSize = numTransforms * sizeof(Matrix43); dataSize += 0xf; dataSize &= 0xfffffff0; transforms = (float*)gDCBuffer->AllocatePagedMemory(DPT_LIFETIME_RENDER_THREAD, dataSize, false, 16); Assert16(transforms); sysMemCpy(transforms, brTransforms.GetElements(), dataSize); //Setup Draw Data bgDrawableDrawData bgDrawData; bgDrawData.SetupDrawData(glassBreakable, glassDrawable, transforms, numTransforms); //Pass draw data into the breakable Glass DC DLC(CDrawBreakableGlassDC, (glassDrawable, bgDrawData, pEntity)); } } } } } } } // VehicleMods: Andrzej's note to self: this must be called as last item as it directly modifies current vehicle's CSE instance: if (pMI->GetModelType() == MI_TYPE_VEHICLE && pEntity->GetIsTypeVehicle()) { CVehicleDrawHandler* vdh = (CVehicleDrawHandler*)pDrawHandler; if (vdh->GetVariationInstance().GetNumMods() > 0 && vdh->GetVariationInstance().GetVehicleRenderGfx()) { #if __BANK if (CVehicleFactory::ms_variationRenderMods) #endif // __BANK { dlCmdBase* varDlc = NULL; GET_DLC(varDlc, CDrawVehicleVariationDC, (pEntity)); Assert(varDlc); CCustomShaderEffectVehicle* pShaderEffect = static_cast(pEntity->GetDrawHandler().GetShaderEffect()); Assert(pShaderEffect); if(pShaderEffect->GetIsBurstWheel() && (vdh->GetVariationInstance().HasCustomWheels() || vdh->GetVariationInstance().HasCustomRearWheels())) { ((CDrawVehicleVariationDC*)varDlc)->SetupBurstWheels(pShaderEffect->GetBurstAndSideRatios(0)); } } } } pDrawHandler->AfterAddToDrawList(pEntity, renderMode, pParams); if(bIsVehicleCloth && isClothReady) { CVehicleDrawHandler *pVehDrawHandler = (CVehicleDrawHandler*)pDrawHandler; Assert(pVehDrawHandler->GetShaderEffect()->GetType()==CSE_VEHICLE); CCustomShaderEffectVehicle *pOrigVehCSE = (CCustomShaderEffectVehicle*)pVehDrawHandler->GetShaderEffect(); Assert(pOrigVehCSE); if(pVehDrawHandler->GetShaderEffectSD()) { CCustomShaderEffectVehicle *pVehCseHD = pOrigVehCSE; CCustomShaderEffectVehicle *pVehCseSD = (CCustomShaderEffectVehicle*)pVehDrawHandler->GetShaderEffectSD(); // copy CSE HD->std settings: pVehCseSD->CopySettings(pVehCseHD, pEntity); pVehDrawHandler->SetShaderEffect(pVehCseSD); // hack: replace CSE for SD } // disable veh damage for cloth: CCustomShaderEffectVehicle *pVehClothCSE = (CCustomShaderEffectVehicle*)pVehDrawHandler->GetShaderEffect(); Assert(pVehClothCSE); pVehClothCSE->UpdateVehicleColors(pEntity); const bool bOrigVehDamageEnabled = pVehClothCSE->GetEnableDamage(); pVehClothCSE->SetEnableDamage(false); pDrawHandler->BeforeAddToDrawList(pEntity, pEntity->GetModelIndex(), renderMode, bucket, bucketMask, pParams, true); Assertf(pClothDrawable->GetShaderGroup().GetShaderGroupVarCount()>0, "CSEVehicle: Uninitialized cloth drawable in model %s!", pEntity->GetModelName()); Assert( pEnvCloth ); gDrawListMgr->AddClothReference(pEnvCloth); Vector3 v = VEC3V_TO_VECTOR3(pEnvCloth->GetOffset()); grcDepthStencilStateHandle DSS = static_cast(gDrawListMgr)->IsBuildingMirrorReflectionDrawList() ? CPhysics::GetClothManager()->GetInvertPassDSS() : CPhysics::GetClothManager()->GetPassDSS(); DLC( dlCmdDrawTwoSidedDrawable, (pClothDrawable, v, pEntity->GetModelIndex(), pParams->AlphaFade, pEntity->GetNaturalAmbientScale(), pEntity->GetArtificialAmbientScale(), pEntity->GetDeferredMaterial(), pEntity->GetInteriorLocation().IsValid(), CPhysics::GetClothManager()->GetPassRS(), DSS, captureStats, true, bIsVehicleCloth, pEntity)); pDrawHandler->AfterAddToDrawList(pEntity, renderMode, pParams); pVehClothCSE->SetEnableDamage(bOrigVehDamageEnabled); // restore vehicle damage pVehDrawHandler->SetShaderEffect(pOrigVehCSE); // hack: set original CSE back } return pReturnDC; } dlCmdBase* CDynamicEntityDrawHandler::AddSkinnedToDrawList(CEntityDrawHandler* pDrawHandler, fwEntity* pBaseEntity, fwDrawDataAddParams* pBaseParams) { DL_PF_FUNC( TotalAddToDrawList ); DL_PF_FUNC( TotalDynamicEntityAddToDrawList ); DL_PF_FUNC( DynamicEntitySkinnedAddToDrawList ); RAGETRACE_SCOPE(CDynamicEntity_AddToDrawList); CDynamicEntity* pEntity = static_cast(pBaseEntity); #if __BANK if (ShouldSkipEntity(pEntity)) return NULL; #endif // __BANK // want to return the draw command that was created here dlCmdBase *pReturnDC = NULL; // if we're not a fragment and don't have a skeleton then use basic render Assert(!pEntity->m_nFlags.bIsFrag && pEntity->GetSkeleton() != NULL); eRenderMode renderMode = DRAWLISTMGR->GetUpdateRenderMode(); u32 bucket = DRAWLISTMGR->GetUpdateBucket(); u32 bucketMask = DRAWLISTMGR->GetUpdateBucketMask(); pDrawHandler->BeforeAddToDrawList(pEntity, pEntity->GetModelIndex(), renderMode, bucket, bucketMask, pBaseParams); // copy off skeleton data into the drawlist GET_DLC(pReturnDC, CDrawSkinnedEntityDC, (pEntity)); pDrawHandler->AfterAddToDrawList(pEntity, renderMode, pBaseParams); return pReturnDC; }