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

1663 lines
52 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// FILE: DecalCallbacks.cpp
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// INCLUDES
///////////////////////////////////////////////////////////////////////////////
#include "DecalCallbacks.h"
// rage
#include "fragment/instance.h"
#include "fragment/type.h"
#include "grcore/viewport.h"
#include "grmodel/geometry.h"
#include "grmodel/model.h"
#include "grmodel/shader.h"
#include "physics/inst.h"
#include "phbound/boundcomposite.h"
#include "phcore/materialmgr.h"
#include "rmcore/drawable.h"
#include "rmcore/lodgroup.h"
#include "string/stringhash.h"
#include "system/dma.h"
#include "system/nelem.h"
// framework
#include "entity/entity.h"
#include "vfx/channel.h"
#include "vfx/decal/decalasynctasktype.h"
#include "vfx/decal/decalbucket.h"
#include "vfx/decal/decaldmahelpers.h"
#include "vfx/decal/decaldmatags.h"
#include "vfx/decal/decalminiidxbufwriter.h"
// game
#include "modelinfo/VehicleModelInfo.h"
#include "physics/gtaMaterialManager.h"
#include "physics/Deformable.h"
#include "renderer/HierarchyIds.h"
#include "renderer/RenderPhases/RenderPhaseStd.h"
#include "renderer/Util/ShaderUtil.h"
#include "scene/Entity.h"
#include "vfx/VfxHelper.h"
#include "vfx/decals/DecalAsyncTaskDesc.h"
#include "vfx/decals/DecalDmaTags.h"
#include "vfx/systems/VfxWheel.h"
#include "vfx/vehicleglass/VehicleGlassVertex.h"
#if !__SPU
# include "camera/viewports/Viewport.h"
# include "camera/viewports/ViewportManager.h"
# include "modelinfo/PedModelInfo.h"
# include "Peds/ped.h"
# include "Peds/PlayerInfo.h"
# include "Peds/rendering/PedVariationDS.h"
# include "Peds/rendering/PedVariationPack.h"
# include "Peds/rendering/PedVariationStream.h"
# include "physics/gtaArchetype.h"
# include "physics/physics.h"
# include "renderer/rendertargets.h"
# include "renderer/Deferred/GBuffer.h"
# include "scene/world/GameWorld.h"
# include "scene/world/GameWorldWaterHeight.h"
# include "vfx/decals/DecalManager.h"
# include "vfx/vehicleglass/VehicleGlassComponent.h"
#endif
///////////////////////////////////////////////////////////////////////////////
// OPTIMISATIONS - TURN ON IN OPTIMISATIONS.H
///////////////////////////////////////////////////////////////////////////////
VFX_DECAL_OPTIMISATIONS()
///////////////////////////////////////////////////////////////////////////////
// GLOBAL VARIABLES
///////////////////////////////////////////////////////////////////////////////
#if !__SPU
static CDecalCallbacks g_decalCallbacks;
decalCallbacks* g_pDecalCallbacks = &g_decalCallbacks;
#else
static char g_decalCallbacksBuf[sizeof(CDecalCallbacks)] ALIGNED(MAX(__alignof(CDecalCallbacks), 16));
decalCallbacks* g_pDecalCallbacks;
#endif
///////////////////////////////////////////////////////////////////////////////
// CODE
///////////////////////////////////////////////////////////////////////////////
#if __SPU
///////////////////////////////////////////////////////////////////////////
// SpuGlobalInit
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::SpuGlobalInit()
{
// Global ctors don't get run on SPURS jobs, so need to run it here in
// order to jam the vtable pointer. When compiling as PIC, we also need
// to manually fixup the pointers inside the vtable. There is the
// -mspurs-task-initiailze linker option, that should call code to do
// this for us, but that doesn't seem to work. It is very possible that
// -mspurs-task-initialize didn't work simply because the hand hacking
// of the retarted vcproj files was wrong. This method is ok, except
// for the reliance on NUM_SPU_VIRTUAL_FUNCTIONS being manually kept up
// to date.
g_pDecalCallbacks = rage_placement_new(g_decalCallbacksBuf) CDecalCallbacks;
// Calculate the PIC offset that needs to be added to each vtable entry.
// The ila loads the non-relocated address of the relative branch
// target, which can the be subtracted from the "return address" stored
// by the branch.
register u32 picOffset;
register qword t0, t1;
__asm__ __volatile__
(
"ila %1,.+8\n\t"
"brsl %2,.+4\n\t"
"sf %0,%1,%2"
: "=r"(picOffset),
"=r"(t0),
"=r"(t1)
);
// The same SPURS job can run twice in a row on the same SPU, but with
// the job streamer policy module relocating the job binary in the mean
// time (https://ps3.scedev.net/support/issue/114598). This means that
// the vtable can already have been adjusted to a non-zero base address,
// so we need to take that into account. The vtable is stored
// .data.rel.ro section. We'll store our static there too so that it
// will get moved around with the vtable.
static u32 s_prevRelocation __attribute__((__section__(".data.rel.ro"))) = 0;
const s32 adjust = picOffset - s_prevRelocation;
s_prevRelocation = picOffset;
// Relocate the vtable entries
u32 *const vtable = *(u32**)g_decalCallbacksBuf;
for (unsigned i=0; i<NUM_SPU_VIRTUAL_FUNCTIONS; ++i)
{
vtable[i] += adjust;
}
}
///////////////////////////////////////////////////////////////////////////
// SpuTaskInit
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::SpuTaskInit(CDecalAsyncTaskDesc *td, const fwEntity* pEntity)
{
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
if (pActualEntity->GetIsTypeVehicle())
{
// Grab the model info structure
const CVehicleModelInfo* const pVehModelInfoEa = static_cast<CVehicleModelInfo*>(pActualEntity->GetBaseModelInfo());
decalAssert(pVehModelInfoEa);
CompileTimeAssert(__alignof(CVehicleModelInfo) >= 4);
CompileTimeAssert((OffsetOf(CVehicleModelInfo, m_data)&3) == 0);
u32 structEa = sysDmaGetUInt32((u32)&pVehModelInfoEa->m_data, DMATAG_BLOCKING);
const CVehicleStructure* const pStructureEa = (CVehicleStructure*)structEa;
decalAssert(pStructureEa);
// Grab the mapping from bone hierarchy indices to bone indices
CompileTimeAssert(__alignof(CVehicleStructure) >= 16);
CompileTimeAssert((OffsetOf(CVehicleStructure, m_decalBoneFlags)&15) == 0);
enum { BONE_FLAGS_DMA_SIZE = (sizeof(pStructureEa->m_decalBoneFlags)+15)&~15 };
atFixedBitSet<VEH_MAX_DECAL_BONE_FLAGS>* const pBoneFlags = (atFixedBitSet<VEH_MAX_DECAL_BONE_FLAGS>*)sysScratchAlloc(BONE_FLAGS_DMA_SIZE);
sysDmaGet(pBoneFlags, (u32)&pStructureEa->m_decalBoneFlags, BONE_FLAGS_DMA_SIZE, DMATAG_GAME_GETBONEFLAGS);
m_pDecalBoneFlags = pBoneFlags;
// don't need to block on this dma completing, all dma tags will be blocked on before calling decalManager::ProcessInst
}
# if DECAL_MANAGER_ENABLE_PROCESSSMASHGROUP
const u32 totalNumVerts = td->smashgroupNumVerts;
const u32 batchNumVerts = Min<u32>(totalNumVerts, MAX_SMASH_GROUP_VERTS_PER_BATCH);
const u32 batchSize = batchNumVerts*td->smashgroupStride;
const u32 allocSize = (batchSize+31)&~15;
void *const verts = sysScratchAlloc(allocSize);
m_smashgroupVertsLs = verts;
const u32 ea = (u32)(td->smashgroupVerts);
const u32 dmaBeginEa = ea&~15;
const u32 dmaEndEa = (ea+batchSize+15)&~15;
m_smashgroupVertsOffset = ea&15;
sysDmaLargeGet(verts, dmaBeginEa, dmaEndEa-dmaBeginEa, DMATAG_GAME_GETSMASHGROUPVTXS);
td->smashgroupVerts = (const void*)(ea+batchSize);
# else
(void)td;
# endif
}
#endif // __SPU
///////////////////////////////////////////////////////////////////////////////
// GlobalInit
///////////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::GlobalInit(const phMaterialGta* materialArray)
{
m_materialArray = materialArray;
}
///////////////////////////////////////////////////////////////////////////////
// IsNoDecalShader
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsNoDecalShader(const grmShader* pShader)
{
static const u32 sHashes[] =
{
ATSTRINGHASH("cutout_fence", 0x4fa432a7),
ATSTRINGHASH("cutout_fence_normal", 0x063762cf),
};
const u32 hash = pShader->GetHashCode();
for (unsigned i=0; i<NELEM(sHashes); ++i)
{
if (hash == sHashes[i])
{
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// IsVehicleShader
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsVehicleShader(const grmShader* pShader)
{
static const u32 sHashes[] =
{
ATSTRINGHASH("vehicle_licenseplate", 0x8a7a2bef),
ATSTRINGHASH("vehicle_mesh", 0xd963b58b),
ATSTRINGHASH("vehicle_mesh_enveff", 0xed5d39c0),
ATSTRINGHASH("vehicle_mesh2_enveff", 0xa62a444e),
ATSTRINGHASH("vehicle_paint1", 0xf9fb7331),
ATSTRINGHASH("vehicle_paint1_enveff", 0x2327939b),
ATSTRINGHASH("vehicle_paint2", 0xede85b0b),
ATSTRINGHASH("vehicle_paint2_enveff", 0x0fe76347),
ATSTRINGHASH("vehicle_paint3", 0xe029bf8e),
ATSTRINGHASH("vehicle_paint3_enveff", 0xec8e929d),
ATSTRINGHASH("vehicle_paint4", 0xc0cb80d2),
ATSTRINGHASH("vehicle_paint4_emissive", 0x72D0AC88),
ATSTRINGHASH("vehicle_paint4_enveff", 0xaba4513d),
ATSTRINGHASH("vehicle_paint5", 0xb305e547),
ATSTRINGHASH("vehicle_paint5_enveff", 0xe498076b),
ATSTRINGHASH("vehicle_paint6", 0xa17c4234),
ATSTRINGHASH("vehicle_paint6_enveff", 0xc30069b8),
ATSTRINGHASH("vehicle_paint7", 0x953229a0),
ATSTRINGHASH("vehicle_paint7_enveff", 0x6e94ccc6),
ATSTRINGHASH("vehicle_vehglass", 0x7c98d207),
ATSTRINGHASH("vehicle_emissive_opaque", 0x833b47f2),
ATSTRINGHASH("vehicle_paint8", 0x86598BEF),
ATSTRINGHASH("vehicle_paint9", 0x77F6EF2A),
};
const u32 hash = pShader->GetHashCode();
for (unsigned i=0; i<NELEM(sHashes); ++i)
{
if (hash == sHashes[i])
{
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// IsGlassShader
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsGlassShader(const grmShader* pShader)
{
static const u32 sHashes[] =
{
ATSTRINGHASH("glass", 0xd9f8c9bf),
ATSTRINGHASH("glass_displacement", 0x629c08dc),
ATSTRINGHASH("glass_emissive", 0x1a910b87),
ATSTRINGHASH("glass_emissivenight", 0x451700e5),
ATSTRINGHASH("glass_env", 0x85aba20b),
ATSTRINGHASH("glass_pv", 0x706a3722),
ATSTRINGHASH("glass_pv_env", 0xc5098ee2),
ATSTRINGHASH("glass_normal_spec_reflect", 0xa587608a),
ATSTRINGHASH("glass_reflect", 0xb680ab5c),
ATSTRINGHASH("glass_spec", 0xa9a6eb84),
ATSTRINGHASH("vehicle_vehglass", 0x7c98d207),
};
const u32 hash = pShader->GetHashCode();
for (unsigned i=0; i<NELEM(sHashes); ++i)
{
if (hash == sHashes[i])
{
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// IsGlassMaterial
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsGlassMaterial(phMaterialMgr::Id mtlId)
{
const phMaterialMgr::Id idx = mtlId & phMaterialMgrGta::GetMaterialMask();
#if !__SPU
const VfxGroup_e grp = m_materialArray[idx].GetVfxGroup();
#else
CompileTimeAssert(sizeof(__typeof__(*m_materialArray[idx].GetVfxGroupPtr())) == 4);
const VfxGroup_e grp = (VfxGroup_e)sysDmaGetUInt32((u32)m_materialArray[idx].GetVfxGroupPtr(), DMATAG_BLOCKING);
#endif
return phMaterialMgrGta::GetIsGlass(grp);
}
///////////////////////////////////////////////////////////////////////////////
// ShouldProcessCompositeBound
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldProcessCompositeBoundFlags(u32 flags)
{
if ((flags & ArchetypeFlags::GTA_MAP_TYPE_WEAPON) ||
((flags & ArchetypeFlags::GTA_OBJECT_TYPE) && (flags & ArchetypeFlags::GTA_MAP_TYPE_COVER)==0))
{
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// ShouldProcessCompositeChildBoundFlags
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldProcessCompositeChildBoundFlags(u32 flags)
{
if ((flags & ArchetypeFlags::GTA_MAP_TYPE_WEAPON) ||
((flags & ArchetypeFlags::GTA_OBJECT_TYPE) && (flags & ArchetypeFlags::GTA_MAP_TYPE_COVER)==0))
{
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// ShouldProcessDrawableBone
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldProcessDrawableBone(const fwEntity* pEntity, decalBoneHierarchyIdx boneIndex)
{
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
if (pActualEntity->GetIsTypeVehicle())
{
// This debug flag could be made to work on the SPUs, but not worth the
// hassle right now
# if __BANK && !__SPU
if (g_decalMan.GetDebugInterface().GetDisableVehicleBoneTest())
{
return true;
}
# endif
# if !__SPU
const CBaseModelInfo* const pBaseModelInfo = pActualEntity->GetBaseModelInfo();
decalAssertf(dynamic_cast<const CVehicleModelInfo*>(pBaseModelInfo), "can't get vehicle model info (%d)", pActualEntity->GetModelIndex());
const CVehicleModelInfo* const pVehModelInfo = static_cast<const CVehicleModelInfo*>(pBaseModelInfo);
return pVehModelInfo->GetDecalBoneFlag(boneIndex.Val());
# else
return m_pDecalBoneFlags->IsSet(boneIndex.Val());
# endif
}
// else if (pActualEntity->GetIsTypePed())
// {
// decalAssertf(0, "trying to process a ped drawable bone");
// return false;
// }
return true;
}
///////////////////////////////////////////////////////////////////////////////
// ShouldProcessShader
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldProcessShader(const grmShader* pShader, const fwEntity* pEntity)
{
// only process certain shaders on vehicles
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
if (pActualEntity->GetIsTypeVehicle())
{
// This debug flag could be made to work on the SPUs, but not worth the
// hassle right now
# if __BANK && !__SPU
if (g_decalMan.GetDebugInterface().GetDisableVehicleShaderTest())
{
return true;
}
# endif
return IsVehicleShader(pShader);
}
else
{
return !IsNoDecalShader(pShader);
}
}
///////////////////////////////////////////////////////////////////////////////
// ShouldProcessMaterial
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldProcessMaterial(u16 typeSettingsIndex, phMaterialMgr::Id mtlId)
{
if (phMaterialMgrGta::GetPolyFlagNoDecal(mtlId))
{
return false;
}
if (PGTAMATERIALMGR->UnpackMtlId(mtlId)==PGTAMATERIALMGR->g_idTreeBark)
{
if (typeSettingsIndex==DECALTYPE_WEAPON_IMPACT_EXPLOSION_GROUND ||
typeSettingsIndex==DECALTYPE_WEAPON_IMPACT_EXPLOSION_VEHICLE)
{
return false;
}
}
if (PGTAMATERIALMGR->UnpackMtlId(mtlId)==PGTAMATERIALMGR->g_idPhysVehicleSpeedUp ||
PGTAMATERIALMGR->UnpackMtlId(mtlId)==PGTAMATERIALMGR->g_idPhysVehicleSlowDown ||
PGTAMATERIALMGR->UnpackMtlId(mtlId)==PGTAMATERIALMGR->g_idPhysVehicleRefill ||
PGTAMATERIALMGR->UnpackMtlId( mtlId ) == PGTAMATERIALMGR->g_idPhysVehicleBoostCancel ||
PGTAMATERIALMGR->UnpackMtlId( mtlId ) == PGTAMATERIALMGR->g_idPhysPropPlacement
)
{
return false;
}
// note that we don't want to be testing 'shoot through' flags here
// the decal system should reject polys using the 'no decal' flag and should never test the 'shoot through' flag
return true;
}
///////////////////////////////////////////////////////////////////////////////
// ShouldBucketBeRendered
///////////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldBucketBeRendered(const decalBucket* pDecalBucket, decalRenderPass rp)
{
if(rp == DECAL_RENDER_PASS_FORWARD)
{
//This check is only done for the forward decals
vfxAssertf(pDecalBucket, "NULL Bucket passed in");
vfxAssertf(DRAWLISTMGR->IsExecutingDrawSceneDrawList(), "We shouldn't check for interior location when not in draw scene render phase");
return CRenderPhaseDrawSceneInterface::RenderAlphaEntityInteriorLocationCheck(pDecalBucket->GetInteriorLocation());
}
//if its deferred then just return true
return true;
}
///////////////////////////////////////////////////////////////////////////////
// GetLod
///////////////////////////////////////////////////////////////////////////////
const rmcLod* CDecalCallbacks::GetLod(const fwEntity* pEntity, const rmcDrawable* pDrawable)
{
const rmcLodGroup& lodGroup = pDrawable->GetLodGroup();
# if !__SPU
decalAssert(!pEntity || dynamic_cast<const CEntity*>(pEntity));
# endif
const CEntity* const pCEntity = static_cast<const CEntity*>(pEntity);
if (pCEntity && pCEntity->GetIsTypeVehicle())
{
# if !__SPU
# define STR_NON_SPU_ADD_NAME(BASE_STR, NAME) BASE_STR " (%s)", NAME
# else
# define STR_NON_SPU_ADD_NAME(BASE_STR, NAME) BASE_STR
# endif
// this is a vehicle - return the medium vehicle lod
BLOCKING_DMA_GET(CVehicleModelInfo, pVehModelInfo, static_cast<CVehicleModelInfo*>(pCEntity->GetBaseModelInfo()));
if (decalVerifyf(pVehModelInfo, STR_NON_SPU_ADD_NAME("can't get vehicle model info", pVehModelInfo->GetModelName())))
{
if (pVehModelInfo->GetHasHDFiles())
{
// this vehicle uses the new system and has a separate high def drawable
// so we want to be getting the highest lod from the standard drawable (which is the medium lod of the entire vehicle)
if (lodGroup.ContainsLod(LOD_HIGH))
{
return &lodGroup.GetLod(LOD_HIGH);
}
}
else
{
// this vehicle uses the old system and has a single model containing high, medium and low lods
// so we want to be getting the high lod from the standard drawable (which is the medium lod of the entire vehicle)
if (lodGroup.ContainsLod(LOD_MED))
{
return &lodGroup.GetLod(LOD_MED);
}
}
# if __BANK
if (DECALMGRASYNC.GetDebug()->IsEnableSpam())
{
decalErrorf(STR_NON_SPU_ADD_NAME("no medium vehicle lod available - decals will not work on this vehicle", pVehModelInfo->GetModelName()));
}
# endif
}
# if __BANK && !__SPU
if (g_decalMan.GetDebugInterface().GetForceVehicleLod())
{
if (lodGroup.ContainsLod(LOD_HIGH))
{
decalErrorf(STR_NON_SPU_ADD_NAME("no medium vehicle lod available - projecting onto high lod instead", pVehModelInfo->GetModelName()));
return &lodGroup.GetLod(LOD_HIGH);
}
}
# endif
# undef STR_NON_SPU_ADD_NAME
}
else if (pCEntity && pCEntity->GetIsTypePed())
{
// this is a ped - try to use the medium lod first
if (lodGroup.ContainsLod(LOD_MED))
{
return &lodGroup.GetLod(LOD_MED);
}
else if (lodGroup.ContainsLod(LOD_LOW))
{
decalErrorf("no medium ped lod available - projecting onto low lod instead");
return &lodGroup.GetLod(LOD_LOW);
}
else if (lodGroup.ContainsLod(LOD_HIGH))
{
decalErrorf("no medium ped lod available - projecting onto high lod instead");
return &lodGroup.GetLod(LOD_HIGH);
}
}
else
{
// this is not a vehicle - try to use the high lod first
if (lodGroup.ContainsLod(LOD_HIGH))
{
return &lodGroup.GetLod(LOD_HIGH);
}
else if (lodGroup.ContainsLod(LOD_MED))
{
return &lodGroup.GetLod(LOD_MED);
}
else if (lodGroup.ContainsLod(LOD_LOW))
{
return &lodGroup.GetLod(LOD_LOW);
}
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
// DecompressSmashGroup
///////////////////////////////////////////////////////////////////////////////
unsigned CDecalCallbacks::DecompressSmashGroup(decalAsyncTaskDescBase *taskDescBase, Vec3V *pos, Vec3V *nrm)
{
# if !DECAL_MANAGER_ENABLE_PROCESSSMASHGROUP
(void)taskDescBase;
(void)pos;
(void)nrm;
return 0;
# else
CDecalAsyncTaskDesc *const td = static_cast<CDecalAsyncTaskDesc*>(taskDescBase);
const u32 numVerts = td->smashgroupNumVerts;
if (!numVerts)
{
return 0;
}
const u32 batchNumVerts = Min<u32>(numVerts, MAX_SMASH_GROUP_VERTS_PER_BATCH);
const u32 remainingNumVerts = numVerts-batchNumVerts;
td->smashgroupNumVerts = (u16)remainingNumVerts;
# if __SPU
sysDmaWaitTagStatusAll(1<<DMATAG_GAME_GETSMASHGROUPVTXS);
const u8 *vtx0 = (u8*)m_smashgroupVertsLs + m_smashgroupVertsOffset;
# else
const u8 *vtx0 = (u8*)(td->smashgroupVerts);
# endif
const unsigned stride = td->smashgroupStride;
for (unsigned i=0; i<batchNumVerts; i+=3)
{
const u8 *const vtx1 = vtx0+stride;
const u8 *const vtx2 = vtx1+stride;
const Vec3V vPos0 = VehicleGlassVertex_GetVertexP(vtx0);
const Vec3V vPos1 = VehicleGlassVertex_GetVertexP(vtx1);
const Vec3V vPos2 = VehicleGlassVertex_GetVertexP(vtx2);
const Vec3V vNrm0 = VehicleGlassVertex_GetVertexN(vtx0);
const Vec3V vNrm1 = VehicleGlassVertex_GetVertexN(vtx1);
const Vec3V vNrm2 = VehicleGlassVertex_GetVertexN(vtx2);
*pos++ = vPos0;
*pos++ = vPos1;
*pos++ = vPos2;
*nrm++ = vNrm0;
*nrm++ = vNrm1;
*nrm++ = vNrm2;
vtx0 = vtx2+stride;
}
if (remainingNumVerts)
{
td->smashgroupVerts = vtx0;
# if __SPU
const u32 nextBatchNumVerts = Min<u32>(remainingNumVerts, MAX_SMASH_GROUP_VERTS_PER_BATCH);
const u32 batchSize = nextBatchNumVerts*stride;
const uptr ea = (uptr)vtx0;
const u32 dmaBeginEa = ea&~15;
const u32 dmaEndEa = (ea+batchSize+15)&~15;
m_smashgroupVertsOffset = ea&15;
sysDmaLargeGet(m_smashgroupVertsLs, dmaBeginEa, dmaEndEa-dmaBeginEa, DMATAG_GAME_GETSMASHGROUPVTXS);
# endif
}
return batchNumVerts;
# endif // DECAL_MANAGER_ENABLE_PROCESSSMASHGROUP
}
#if !__SPU
///////////////////////////////////////////////////////////////////////////
// AllocAsyncTaskDesk
///////////////////////////////////////////////////////////////////////////
decalAsyncTaskDescBase *CDecalCallbacks::AllocAsyncTaskDesk(decalAsyncTaskType type, const decalSettings& settings, const fwEntity* pEnt, const phInst* pInst)
{
(void) pInst;
(void) pEnt;
const CVehicleGlassVB *vehicleGlassVB = NULL; // 360 compiler complains (incorrectly) this can be used initialized if you remove the =NULL
if (type == DECAL_DO_PROCESSSMASHGROUP)
{
vehicleGlassVB = ((const CVehicleGlassComponent*)(settings.user.bucketSettings.pSmashGroup))->GetStaticVB();
if (!vehicleGlassVB)
{
decalSpamf("no vehicle glass vertex buffer");
return NULL;
}
}
CDecalAsyncTaskDesc* const td = static_cast<CDecalAsyncTaskDesc*>(g_decalMan.AllocAsyncTaskDesk());
if (Likely(td))
{
if (type == DECAL_DO_PROCESSSMASHGROUP)
{
td->smashgroupVerts = vehicleGlassVB->GetRawVertices();
td->smashgroupNumVerts = (u16)vehicleGlassVB->GetVertexCount();
td->smashgroupStride = (u16)vehicleGlassVB->GetVertexStride();
}
else
{
td->materialArray = m_materialArray;
# if __ASSERT
td->debugFwArchetype = pEnt->GetArchetype();
# endif
}
}
return td;
}
///////////////////////////////////////////////////////////////////////////
// FreeAsyncTaskDesc
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::FreeAsyncTaskDesc(decalAsyncTaskDescBase *desc)
{
g_decalMan.FreeAsyncTaskDesc(desc);
}
///////////////////////////////////////////////////////////////////////////
// GetAsyncTaskDescSize
///////////////////////////////////////////////////////////////////////////
size_t CDecalCallbacks::GetAsyncTaskDescSize()
{
return sizeof(CDecalAsyncTaskDesc);
}
///////////////////////////////////////////////////////////////////////////
// GetIncludeFlags
///////////////////////////////////////////////////////////////////////////
u32 CDecalCallbacks::GetIncludeFlags()
{
return ArchetypeFlags::GTA_MAP_TYPE_WEAPON |
ArchetypeFlags::GTA_VEHICLE_TYPE |
// ArchetypeFlags::GTA_PED_TYPE |
// ArchetypeFlags::GTA_RAGDOLL_TYPE |
ArchetypeFlags::GTA_OBJECT_TYPE |
ArchetypeFlags::GTA_GLASS_TYPE;
}
///////////////////////////////////////////////////////////////////////////
// GetPhysicsLevel
///////////////////////////////////////////////////////////////////////////
phLevelNew* CDecalCallbacks::GetPhysicsLevel()
{
return CPhysics::GetLevel();
}
///////////////////////////////////////////////////////////////////////////
// GetEntityFromInst
///////////////////////////////////////////////////////////////////////////
fwEntity* CDecalCallbacks::GetEntityFromInst(phInst* pInst)
{
CEntity* pEntity = CPhysics::GetEntityFromInst(pInst);
return pEntity;
}
///////////////////////////////////////////////////////////////////////////
// GetPhysInstFromEntity
///////////////////////////////////////////////////////////////////////////
phInst* CDecalCallbacks::GetPhysInstFromEntity(const fwEntity* pEntity)
{
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
if (pActualEntity->GetIsTypePed())
{
return static_cast<const CPed*>(pEntity)->GetRagdollInst();
}
else
{
return pEntity->GetCurrentPhysicsInst();
}
}
///////////////////////////////////////////////////////////////////////////
// GetEntityBoundsRadius
///////////////////////////////////////////////////////////////////////////
ScalarV_Out CDecalCallbacks::GetEntityBoundRadius(const fwEntity* pEntity)
{
// Buildings do not necissarily have any model info, so need to get the
// bounds from the collision in that case. Don't want to use the
// collision in the general case though, as for other entity types, it
// can change over time (eg. frags). For entities where the collision
// does change, that is not a problem for our purposes here with decals,
// since the decals are applied in bind pose.
const fwArchetype *const pFwArchetype = pEntity->GetArchetype();
Vec3V center;
ScalarV radius;
if (pFwArchetype)
{
const Vec4V sphere = pFwArchetype->GetBoundingSphereV4();
center = sphere.GetXYZ();
radius = SplatW(sphere);
}
else
{
const phInst *const pPhInst = pEntity->GetCurrentPhysicsInst();
Assert(pPhInst);
const phArchetype *const pPhArchetype = pPhInst->GetArchetype();
Assert(pPhArchetype);
const phBound *const pPhBound = pPhArchetype->GetBound();
Assert(pPhBound);
center = pPhBound->GetCentroidOffset();
radius = pPhBound->GetRadiusAroundCentroidV();
}
const ScalarV radiusExpand = Mag(center);
return radius + radiusExpand;
}
///////////////////////////////////////////////////////////////////////////
// GetEntitySize
///////////////////////////////////////////////////////////////////////////
size_t CDecalCallbacks::GetEntitySize(const fwEntity* pEntity)
{
const CEntity* const pCEntity = static_cast<const CEntity*>(pEntity);
decalAssert(dynamic_cast<const CEntity*>(pEntity));
if (pCEntity->GetIsDynamic())
{
const CDynamicEntity* const pDynamicEntity = static_cast<const CDynamicEntity*>(pCEntity);
decalAssert(dynamic_cast<const CDynamicEntity*>(pCEntity));
if (pDynamicEntity->GetIsTypeVehicle())
{
return sizeof(CVehicle);
}
return sizeof(CDynamicEntity);
}
return sizeof(CEntity);
}
///////////////////////////////////////////////////////////////////////////
// GetPhysInstSize
///////////////////////////////////////////////////////////////////////////
size_t CDecalCallbacks::GetPhysInstSize(const fwEntity* pEntity, const phInst* pPhInst)
{
(void)pPhInst;
const fragInst *const pFragInst = pEntity->GetFragInst();
if (pFragInst)
{
decalAssert(pFragInst == pPhInst);
return sizeof(fragInst);
}
return sizeof(phInst);
}
///////////////////////////////////////////////////////////////////////////
// GetDrawable
///////////////////////////////////////////////////////////////////////////
const rmcDrawable* CDecalCallbacks::GetDrawable(const fwEntity* pEntity, const phInst* pInst, bool onlyApplyToGlass, bool dontApplyToGlass)
{
// get the entity from the physics inst
const CEntity *const pCEntity = static_cast<const CEntity*>(pEntity);
rmcDrawable* pDrawable = pCEntity->GetDrawable();
if (pDrawable)
{
if (ShouldProcessDrawable(pInst, pCEntity, pDrawable, onlyApplyToGlass, dontApplyToGlass))
{
return pDrawable;
}
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////
// ShouldProcessDrawable
///////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldProcessDrawable(const phInst* pInst, const CEntity* pEntity, const rmcDrawable* pDrawable, bool onlyApplyToGlass, bool dontApplyToGlass)
{
# if __BANK
if (g_decalMan.GetDebugInterface().GetAlwaysUseDrawableIfAvailable())
{
return true;
}
# endif
// check if this is a frag
bool isFrag = false;
const fragInst* pFragInst = dynamic_cast<const fragInst*>(pInst);
if (pFragInst)
{
isFrag = true;
}
// get the lod
const rmcLod* pLod = GetLod(pEntity, pDrawable);
if (pLod==NULL)
{
if (isFrag)
{
// not all children of fragments will have lods
return true;
}
else
{
// non fragments should always have lods
decalAssertf(0, "non fragment found without an lod");
return false;
}
}
// count the verts
s32 numVerts = 0;
s32 modelCount = pLod->GetCount();
for (s32 j=0; j<modelCount; j++)
{
grmModel* pLodModel = pLod->GetModel(j);
// go through the models geometry groups
const decalBoneHierarchyIdx boneIndex(pLodModel->GetMatrixIndex());
if (!ShouldProcessDrawableBone(pEntity, boneIndex))
{
continue;
}
s32 geomCount = pLodModel->GetGeometryCount();
for (s32 i=0; i<geomCount; i++)
{
int shaderId = pLodModel->GetShaderIndex(i);
grmShader& shader = pDrawable->GetShaderGroup().GetShader(shaderId);
// check if the shader is glass and respect the settings passed in
bool isGlassShader = IsGlassShader(&shader);
if ((onlyApplyToGlass && !isGlassShader) ||
(dontApplyToGlass && isGlassShader))
{
continue;
}
if (!ShouldProcessShader(&shader, pEntity))
{
continue;
}
grmGeometry& geom = pLodModel->GetGeometry(i);
// get the vertex buffer
numVerts += geom.GetVertexCount();
}
}
return numVerts<1000;
}
///////////////////////////////////////////////////////////////////////////
// GetMatrixFromBoneIndex
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::GetMatrixFromBoneIndex(Mat34V_InOut boneMtx, bool* isDamaged, const fwEntity* pEntity, decalBoneHierarchyIdx boneIndex)
{
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
CVfxHelper::GetMatrixFromBoneIndex(boneMtx, isDamaged, pActualEntity, boneIndex.Val());
}
///////////////////////////////////////////////////////////////////////////
// GetMatrixFromBoneIndex_Skinned
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::GetMatrixFromBoneIndex_Skinned(Mat34V_InOut boneMtx, bool* isDamaged, const fwEntity* pEntity, decalBoneHierarchyIdx boneIndex)
{
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
CVfxHelper::GetMatrixFromBoneIndex_Skinned(boneMtx, isDamaged, pActualEntity, boneIndex.Val());
}
///////////////////////////////////////////////////////////////////////////
// GetMatrixFromBoneIndex_SmashGroup
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::GetMatrixFromBoneIndex_SmashGroup(Mat34V_InOut boneMtx, const fwEntity* pEntity, decalBoneHierarchyIdx boneIndex)
{
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
CVfxHelper::GetMatrixFromBoneIndex_Smash(boneMtx, pActualEntity, boneIndex.Val());
}
///////////////////////////////////////////////////////////////////////////
// ShouldProcessInst
///////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::ShouldProcessInst(const phInst* pInst, const bool bApplyToBreakable)
{
// const CEntity* pEntity = CPhysics::GetEntityFromInst(pInst);
// if (pEntity->GetIsTypePed())
// {
// return false;
// }
// don't let any decals be applied to mutliplayer vehicles that are being 'respotted'
if (NetworkInterface::IsGameInProgress() )
{
const CEntity* pEntity = CPhysics::GetEntityFromInst(pInst);
if (pEntity && pEntity->GetIsTypeVehicle())
{
const CVehicle* pVehicle = static_cast<const CVehicle*>(pEntity);
if (pVehicle && pVehicle->IsBeingRespotted())
{
return false;
}
}
}
if (pInst->IsInLevel())
{
const u32 typeFlags = CPhysics::GetLevel()->GetInstanceTypeFlags(pInst->GetLevelIndex());
bool isWeaponMapType = (typeFlags & ArchetypeFlags::GTA_MAP_TYPE_WEAPON) > 0;
// bool isMoverMapType = (typeFlags & ArchetypeFlags::GTA_MAP_TYPE_MOVER) > 0;
// bool isCameraMapType = (typeFlags & ArchetypeFlags::GTA_MAP_TYPE_CAMERA) > 0;
bool isObjectType = (typeFlags & ArchetypeFlags::GTA_OBJECT_TYPE) > 0;
bool isVehicleType = (typeFlags & ArchetypeFlags::GTA_VEHICLE_TYPE) > 0;
bool isGlassType = (typeFlags & ArchetypeFlags::GTA_GLASS_TYPE) > 0;
if (isWeaponMapType || isObjectType || isVehicleType || isGlassType)
{
return true;
}
}
# if DECAL_PROCESS_BREAKABLES
else
{
// handle breakable glass that is broken
if (bApplyToBreakable)
{
const fragInst* pFragInst = dynamic_cast<const fragInst*>(pInst);
if(pFragInst)
{
if (fragCacheEntry* pCacheEntry = pFragInst->GetCacheEntry())
{
if (fragHierarchyInst* pHierInst = pCacheEntry->GetHierInst())
{
// Find at least one valid glass handle
for (int glassIndex=0; glassIndex!=pHierInst->numGlassInfos; glassIndex++)
{
bgGlassHandle handle = pHierInst->glassInfos[glassIndex].handle;
if (handle != bgGlassHandle_Invalid)
{
return true;
}
}
}
}
}
}
}
# else
(void)bApplyToBreakable;
# endif // DECAL_PROCESS_BREAKABLES
return false;
}
///////////////////////////////////////////////////////////////////////////
// PatchModdedDrawables
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::PatchModdedDrawables(unsigned* numDrawables, unsigned* damagedDrawableMask, const rmcDrawable** pDrawables, unsigned numSkelMtxs, const Vec4V* skelMtxs, DECAL_BONEREMAPLUT(Task,Hierarchy) skelBones, u8* skelDrawables, const fwEntity* pEntity)
{
// TODO: Support modded vehicles by updating the drawable index array
(void) numDrawables;
(void) damagedDrawableMask;
(void) pDrawables;
(void) numSkelMtxs;
(void) skelMtxs;
(void) skelBones;
(void) skelDrawables;
(void) pEntity;
// else if (pEntity->GetIsTypeVehicle())
// {
// *pIsHardSkinned = true;
//
// int numDrawables = 1;
// ppDrawables[0] = pFragType->GetCommonDrawable();
//
// const CVehicle* pVehicle = static_cast<const CVehicle*>(pEntity);
// if (pVehicle && pVehicle->IsModded())
// {
// CVehicleVariationInstance vehVariationInst = pVehicle->GetVariationInstance();
// const u8* pMods = vehVariationInst.GetMods();
// for (u8 i=0; i<VMT_RENDERABLE; i++)
// {
// if (pMods[i]!=INVALID_MOD)
// {
// CVehicleStreamRenderGfx* pModRenderGfx = vehVariationInst.GetVehicleRenderGfx();
// if (pModRenderGfx)
// {
// // get bone index info
// s32 modHierarchId = vehVariationInst.GetBone(i);
// decalBoneHierarchyIdx modBoneIndex = pVehicle->GetBoneIndex((eHierarchyId)modHierarchId);
// modBoneIndex = modBoneIndex;
//
// // get the drawable
// fragType* pModFragType = pModRenderGfx->GetFrag(i);
// if (pModFragType)
// {
// if (numDrawables >= maxDrawables)
// {
// decalAssert(numDrawables == maxDrawables);
// return -1;
// }
// ppDrawables[numDrawables++] = pModFragType->GetCommonDrawable();
//
// }
// }
// }
// }
// }
//
// return numDrawables;
// }
}
///////////////////////////////////////////////////////////////////////////
// PatchModdedDrawables
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::ApplyVehicleSuspensionToMtx(Mat34V_InOut vMtx, const fwEntity* pEntity)
{
CVfxHelper::ApplyVehicleSuspensionToMtx(vMtx, pEntity);
}
///////////////////////////////////////////////////////////////////////////
// BeginRenderDynamic
///////////////////////////////////////////////////////////////////////////
#if CREATE_TARGETS_FOR_DYNAMIC_DECALS
void CDecalCallbacks::BeginRenderDynamic(bool bIsForwardPass)
{
if( bIsForwardPass )
{
const grcRenderTarget *depthTarget = NULL;
const grcRenderTarget *renderTarget = NULL;
if(GRCDEVICE.IsReadOnlyDepthAllowed() == false)
{
// We`ll need to copy the depth buffer.
CRenderTargets::CopyDepthBuffer(CRenderTargets::GetDepthBuffer(), CRenderTargets::GetDepthBufferCopy());
depthTarget = CRenderTargets::GetDepthBufferCopy();
}
else
{
// Use the read only one.
depthTarget = CRenderTargets::GetDepthBuffer_ReadOnly();
}
renderTarget = CRenderTargets::GetBackBuffer();
// Re-lock the render targets using the read only depth.
grcTextureFactory::GetInstance().LockRenderTarget(0,renderTarget,depthTarget);
}
else
{
// Make a copy of the gbuffer1 (the normals), we don`t expect the GBuffer to be resolved.
m_pCopyOfGBuffer1 = GBuffer::CopyGBuffer(GBUFFER_RT_0, false);
const grcRenderTarget *depthTarget = NULL;
const grcRenderTarget *rendertargets[grcmrtColorCount] = {0};
rendertargets[GBUFFER_RT_0] = GBuffer::GetTarget(GBUFFER_RT_0);
rendertargets[GBUFFER_RT_1] = GBuffer::GetTarget(GBUFFER_RT_1);
rendertargets[GBUFFER_RT_2] = GBuffer::GetTarget(GBUFFER_RT_2);
rendertargets[GBUFFER_RT_3] = GBuffer::GetTarget(GBUFFER_RT_3);
if(GRCDEVICE.IsReadOnlyDepthAllowed() == false)
{
// We`ll need to copy the depth buffer.
CRenderTargets::CopyDepthBuffer(CRenderTargets::GetDepthBuffer(), CRenderTargets::GetDepthBufferCopy());
depthTarget = CRenderTargets::GetDepthBufferCopy();
}
else
{
// Use the read only one.
depthTarget = CRenderTargets::GetDepthBuffer_ReadOnly();
}
// Re-lock the render targets using the read only depth.
grcTextureFactory::GetInstance().LockMRT(rendertargets, depthTarget);
}
}
#else
void CDecalCallbacks::BeginRenderDynamic(bool /*bIsForwardPass*/)
{
#if RSG_PC
decalAssertf(0, "CDecalCallbacks::BeginRenderDynamic()...Rendering dynamic decals without necessary GBuffer copies (probably).");
#endif //RSG_PC, CREATE_TARGETS_FOR_DYNAMIC_DECALS
}
#endif
///////////////////////////////////////////////////////////////////////////
// EndRenderDynamic
///////////////////////////////////////////////////////////////////////////
#if CREATE_TARGETS_FOR_DYNAMIC_DECALS
void CDecalCallbacks::EndRenderDynamic(bool bIsForwardPass)
{
if( bIsForwardPass )
{
grcResolveFlags noResolve;
// We don`t want to resolve the targets yet.
noResolve.MipMap = false;
noResolve.NeedResolve = false;
grcTextureFactory::GetInstance().UnlockRenderTarget(0,&noResolve);
}
else
{
grcResolveFlags noResolve;
grcResolveFlagsMrt resolveFlagsMRT;
// We don`t want to resolve the targets yet.
noResolve.MipMap = false;
noResolve.NeedResolve = false;
for(int i=0; i<rage::grcmrtColorCount; i++)
resolveFlagsMRT[i] = &noResolve;
grcTextureFactory::GetInstance().UnlockMRT(&resolveFlagsMRT);
}
}
#else
void CDecalCallbacks::EndRenderDynamic(bool /*bIsForwardPass*/)
{
/* No Op */
}
#endif // CREATE_TARGETS_FOR_DYNAMIC_DECALS
///////////////////////////////////////////////////////////////////////////
// GetDepthTexture
///////////////////////////////////////////////////////////////////////////
const grcTexture* CDecalCallbacks::GetDepthTexture()
{
#if __PPU
return CRenderTargets::GetDepthBufferAsColor();
#else
return CRenderTargets::GetDepthBuffer();
#endif
}
///////////////////////////////////////////////////////////////////////////
// GetNormalTexture
///////////////////////////////////////////////////////////////////////////
const grcTexture* CDecalCallbacks::GetNormalTexture()
{
#if CREATE_TARGETS_FOR_DYNAMIC_DECALS
return m_pCopyOfGBuffer1;
#else
return GBuffer::GetTarget(GBUFFER_RT_1);
#endif
}
///////////////////////////////////////////////////////////////////////////
// InitGlobalRender
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::InitGlobalRender()
{
CShaderLib::SetGlobalNaturalAmbientScale(1.0f);
CShaderLib::SetGlobalArtificialAmbientScale(1.0f);
CShaderLib::SetGlobalEmissiveScale(Lights::GetRenderDirectionalLight().GetIntensity());
}
///////////////////////////////////////////////////////////////////////////
// ShutdownGlobalRender
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::ShutdownGlobalRender()
{
CShaderLib::SetGlobalEmissiveScale(1.0f);
}
///////////////////////////////////////////////////////////////////////////
// InitBucketForwardRender
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::InitBucketForwardRender(Vec4V_In vCentreAndRadius, fwInteriorLocation interiorLocation)
{
const Vec3V centre = vCentreAndRadius.GetXYZ();
const Vec3V extent = Vec3V(vCentreAndRadius.GetW());
const Vec3V boxMin = centre - extent;
const Vec3V boxMax = centre + extent;
Lights::UseLightsInArea(spdAABB(boxMin, boxMax), false, false, false);
CShaderLib::SetGlobalInInterior(interiorLocation.IsValid());
}
///////////////////////////////////////////////////////////////////////////
// SetGlobals
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::SetGlobals(float natural, float artificial, bool isInInterior)
{
CShaderLib::SetGlobalNaturalAmbientScale(natural);
CShaderLib::SetGlobalArtificialAmbientScale(artificial);
CShaderLib::SetGlobalInInterior(isInInterior);
}
///////////////////////////////////////////////////////////////////////////
// GetAmbientFromEntity
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::GetAmbientFromEntity(const fwEntity* fwEnt, float &natural, float &artificial, bool &inInterior)
{
const CEntity *pEntity = static_cast<const CEntity *>(fwEnt);
if( pEntity != NULL )
{
inInterior = pEntity->GetInteriorLocation().IsValid();
if( !pEntity->GetIsTypeBuilding() || inInterior )
{
natural = CShaderLib::DivideBy255(pEntity->GetNaturalAmbientScale());
artificial = CShaderLib::DivideBy255(pEntity->GetArtificialAmbientScale());
}
else
{
natural = 1.0f;
artificial = 0.0f;
}
}
}
///////////////////////////////////////////////////////////////////////////
// CalculateProjectionParams
///////////////////////////////////////////////////////////////////////////
Vector4 CDecalCallbacks::CalculateProjectionParams(const grcViewport* pViewport)
{
return ShaderUtil::CalculateProjectionParams(pViewport);
}
///////////////////////////////////////////////////////////////////////////
// GetCameraPosAndFOVScale
///////////////////////////////////////////////////////////////////////////
Vec4V_Out CDecalCallbacks::GetCameraPosAndFOVScale()
{
return Vec4V(CVfxHelper::GetGameCamPos(), CVfxHelper::GetGameCamFOVScale());
}
///////////////////////////////////////////////////////////////////////////
// GetDistSqrToViewport
///////////////////////////////////////////////////////////////////////////
float CDecalCallbacks::GetDistSqrToViewport(Vec3V_In vPosition)
{
return CVfxHelper::GetDistSqrToCamera(vPosition);
}
///////////////////////////////////////////////////////////////////////////
// GetDistSqrToViewportV
///////////////////////////////////////////////////////////////////////////
ScalarV_Out CDecalCallbacks::GetDistSqrToViewportV(Vec3V_In vPosition)
{
return CVfxHelper::GetDistSqrToCameraV(vPosition);
}
///////////////////////////////////////////////////////////////////////////
// GetCurrentActiveGameViewport
///////////////////////////////////////////////////////////////////////////
const grcViewport* CDecalCallbacks::GetCurrentActiveGameViewport()
{
CViewport* pVp = gVpMan.GetGameViewport();
if(pVp && pVp->IsActive())
{
return &(pVp->GetGrcViewport());
}
else
{
// if we reached here, then current game viewport was not active
decalAssertf(true, "no viewport found");
return NULL;
}
}
///////////////////////////////////////////////////////////////////////////
// IsEntityVisible
///////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsEntityVisible(const fwEntity* pEntity)
{
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
if (pActualEntity)
{
if (pActualEntity->GetIsVisible()==false)
{
return false;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////
// GetEntityInfo
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::GetEntityInfo(const fwEntity* pEntity, u16 typeSettingsIndex, s16 roomId, bool& isPersistent, fwInteriorLocation& interiorLocation, bool& shouldProcess)
{
// persistent flag
isPersistent = false;
CEntity* pPlayerPed = CGameWorld::GetMainPlayerInfo()->GetPlayerPed();
if (pEntity && pEntity==pPlayerPed)
{
isPersistent = true;
}
CEntity* pPlayerVehicleEntity = CGameWorld::GetMainPlayerInfo()->GetPlayerPed()->GetMyVehicle();
if (pEntity && pEntity==pPlayerVehicleEntity)
{
isPersistent = true;
}
if (decalSettingsManager::GetTypeSettings(typeSettingsIndex)->isPersistent)
{
isPersistent = true;
}
// interior flag
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
VfxInteriorTest_e interiorTestResult = CVfxHelper::GetInteriorLocationFromStaticBounds(pActualEntity, roomId, interiorLocation);
if(interiorTestResult == ITR_NOT_STATIC_BOUNDS)
{
interiorLocation = pActualEntity->GetInteriorLocation();
}
// don't process deformable props
shouldProcess = true;
CDeformableObjectManager& refDeformableObjMgr = CDeformableObjectManager::GetInstance();
if (pActualEntity)
{
const CBaseModelInfo* pBaseModelInfo = pActualEntity->GetBaseModelInfo();
if (pBaseModelInfo && refDeformableObjMgr.IsModelDeformable(pBaseModelInfo->GetModelNameHash()))
{
shouldProcess = false;
}
}
// don't process non frag insts where a frag inst is available
phInst* pInst = g_decalCallbacks.GetPhysInstFromEntity(pActualEntity);
if (pActualEntity->m_nFlags.bIsFrag && !IsFragInst(pInst))
{
shouldProcess = false;
}
// don't process trees
// CBaseModelInfo* pBaseModelInfo = pActualEntity->GetBaseModelInfo();
// if (pBaseModelInfo)
// {
// if (pBaseModelInfo->GetIsTree())
// {
// shouldProcess = false;
// }
// }
}
///////////////////////////////////////////////////////////////////////////
// GetDamageData
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::GetDamageData(const fwEntity* pEntity, fwTexturePayload** ppDamageMap, float& damageRadius, float& damageMult)
{
if (decalVerifyf(pEntity, "invalid entity passed"))
{
const CVehicle* pVehicle = NULL;
const CEntity* pActualEntity = static_cast<const CEntity*>(pEntity);
if (pActualEntity && pActualEntity->GetIsTypeVehicle())
{
// it is a vehicle - store the damage texture and bound radius for rendering
pVehicle = static_cast<const CVehicle*>(pActualEntity);
}
else
{
if (pActualEntity->GetIsTypeObject())
{
const CObject* pActualObject = static_cast<const CObject*>(pEntity);
if (pActualObject->GetFragParent() && pActualObject->GetFragParent()->GetIsTypeVehicle())
{
pVehicle = static_cast<const CVehicle*>(pActualObject->GetFragParent());
}
}
}
if (pVehicle)
{
if (decalVerifyf(pVehicle->GetVehicleDamage(), "invalid vehicle damage on vehicle"))
{
if (decalVerifyf(pVehicle->GetVehicleDamage()->GetDeformation(), "invalid deformation on vehicle"))
{
*ppDamageMap = pVehicle->GetVehicleDamage()->GetDeformation()->GetDamagePayload();
damageRadius = pVehicle->GetVehicleDamage()->GetDeformation()->GetBoundRadiusScaled();
damageMult = pVehicle->GetVehicleDamage()->GetDeformation()->GetDamageMultiplier();
return;
}
}
}
}
// default if problems were encountered
*ppDamageMap = NULL;
damageRadius = 0.0f;
}
///////////////////////////////////////////////////////////////////////////
// IsParticleUpdateThread
///////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsParticleUpdateThread()
{
return (sysThreadType::GetCurrentThreadType() & SYS_THREAD_PARTICLES) != 0;
}
///////////////////////////////////////////////////////////////////////////
// PerformUnderwaterCheck
///////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::PerformUnderwaterCheck(Vec3V_In vTestPos)
{
//check if decal is close enough to any water body height wise
if( BANK_ONLY(g_decalMan.GetDebugInterface().IsWaterBodyCheckEnabled() && )
IsLessThanAll(vTestPos.GetZ() - ScalarV(CGameWorldWaterHeight::GetWaterHeightAtPos(vTestPos)), BANK_SWITCH(g_decalMan.GetDebugInterface().GetWaterBodyCheckHeightDiffMin(), DECAL_UNDERWATER_WATER_BODY_HEIGHT_DIFF_MIN)))
{
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////////
// IsUnderwater
///////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsUnderwater(Vec3V_In vTestPos)
{
// float waterDepth;
// CVfxHelper::GetWaterDepth(vTestPos, waterDepth);
// return waterDepth>0.0f;
float oceanWaterZ = 0.0f;
bool foundOceanWater = CVfxHelper::GetOceanWaterZ(vTestPos, oceanWaterZ);
if (foundOceanWater)
{
float oceanWaterDepth = Max(0.0f, oceanWaterZ-vTestPos.GetZf());
return oceanWaterDepth>0.0f;
}
return false;
}
///////////////////////////////////////////////////////////////////////////
// IsAllowedToRecycle
///////////////////////////////////////////////////////////////////////////
bool CDecalCallbacks::IsAllowedToRecycle(u16 typeSettingsIndex)
{
if (g_decalMan.GetDisablePetrolDecalsRecycling() && (typeSettingsIndex==DECALTYPE_TRAIL_LIQUID || typeSettingsIndex==DECALTYPE_POOL_LIQUID))
{
return false;
}
return true;
}
# if HACK_GTA4_MODELINFOIDX_ON_SPU && USE_EDGE
///////////////////////////////////////////////////////////////////////
// GetModelInfoType
///////////////////////////////////////////////////////////////////////
int CDecalCallbacks::GetModelInfoType(u32 modelIndex)
{
return CModelInfo::GetBaseModelInfo(fwModelId(modelIndex))->GetModelType();
}
# endif // HACK_GTA4_MODELINFOIDX_ON_SPU
///////////////////////////////////////////////////////////////////////////
// GetDistanceMult
///////////////////////////////////////////////////////////////////////////
float CDecalCallbacks::GetDistanceMult(u16 typeSettingsIndex)
{
float mult = 1.0f;
if (typeSettingsIndex==(u16)DECALTYPE_TRAIL_SKID)
{
mult *= g_vfxWheel.GetWheelSkidmarkRangeScale();
}
if (CFileLoader::GetPtfxSetting()>=CSettings::High)
{
return mult * DECAL_HIGH_QUALITY_RANGE_MULT;
}
return mult;
}
///////////////////////////////////////////////////////////////////////////
// DecalMerged
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::DecalMerged(int oldDecalId, int newDecalId)
{
g_decalMan.DecalMerged(oldDecalId, newDecalId);
}
///////////////////////////////////////////////////////////////////////////
// LiquidUVMultChanged
///////////////////////////////////////////////////////////////////////////
void CDecalCallbacks::LiquidUVMultChanged(int decalId, float uvMult, Vec3V_In pos)
{
g_decalMan.LiquidUVMultChanged(decalId, uvMult, pos);
}
#endif // !__SPU