diff --git a/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.cpp b/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.cpp index aa23db5d3..7a675cc51 100644 --- a/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.cpp +++ b/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.cpp @@ -95,6 +95,8 @@ bool EveImpactOverlay::Initialize() // -------------------------------------------------------------------------------- void EveImpactOverlay::UpdateSyncronous( const EveUpdateContext& updateContext, EveSpaceObject2* parent ) { + SpawnImpactDebris( updateContext, parent ); + // do we have something to do at all? if( !HasGeneralActivity() ) { @@ -105,54 +107,75 @@ void EveImpactOverlay::UpdateSyncronous( const EveUpdateContext& updateContext, // this comes from the scene via EveUpdateContext Tr2DataTextureManagerPtr dataTextureMgr = updateContext.GetDataTextureManager(); m_damageOverlay->UpdateBlockData( dataTextureMgr, true ); +} + +// -------------------------------------------------------------------------------- +// Description: +// Spawns particles for armor impacts +// -------------------------------------------------------------------------------- +void EveImpactOverlay::SpawnImpactDebris( const EveUpdateContext& updateContext, EveSpaceObject2* parent ) +{ + if( !g_eveSpaceObjectImpactEffectEnabled ) + { + return; + } - // spawn armor impact particles? - if( updateContext.GetGpuParticleSystem() ) + if( !updateContext.GetGpuParticleSystem() || !m_armorImpactEmitter ) { - if( m_armorImpactEmitter ) + return; + } + + auto spawnFromOverlay = [&]( EveDamageOverlay& overlay, int32_t mergedIndexOffset ) { + float armorImpactParentSize = overlay.GetArmorImpactParentSize(); + if( armorImpactParentSize <= 0.f ) + { + return; + } + for( auto& impact : overlay.ArmorImpacts() ) { - float armorImpactParentSize = m_damageOverlay->GetArmorImpactParentSize(); - if( armorImpactParentSize > 0.f ) + if( !impact.second.requestSpawnDebris ) { - auto& armorImpactData = m_damageOverlay->ArmorImpacts(); - for( auto aidit = armorImpactData.begin(); aidit != armorImpactData.end(); ++aidit ) - { - if( aidit->second.requestSpawnDebris ) - { - // where? - Vector3 impactPosWS( 0.f, 0.f, 0.f ); - parent->GetDamageLocatorPosition( &impactPosWS, aidit->second.damageLocatorIndex, true ); - m_armorImpactEmitter->SetPosition( &impactPosWS ); - // facing? - Vector3 impactDirWS( 0.f, 1.f, 0.f ); - parent->GetDamageLocatorDirection( &impactDirWS, aidit->second.damageLocatorIndex, true ); - m_armorImpactEmitter->SetDirection( &impactDirWS ); - // velocity? - Vector3 parentVelocityWS; - parent->GetWorldVelocity( parentVelocityWS ); - - // scaling? - float scale = aidit->second.size * armorImpactParentSize / ( IMPACT_ARMOR_SIZE_MAX / IMPACT_ARMOR_SIZE_FACTOR ); - // loding for emit rate? - float rateModifier = TriClamp( m_damageOverlay->GetRenderPriority() / IMPACT_ARMOR_PARTICLE_LOD_FACTOR, 0.f, 1.f ); - // put together particle update info - ITr2GenericEmitter::UpdateArguments args( updateContext.GetTime(), updateContext.GetGpuParticleSystem(), IdentityMatrix(), updateContext.GetOriginShift() ); - // do the spawn here once! - m_armorImpactEmitter->SpawnOnce( args, parentVelocityWS, scale, rateModifier ); - aidit->second.requestSpawnDebris = false; - - if( m_hullImpactEmitter && m_damageOverlay->GetImpactConfiguration() == ITriTargetable::IMPACT_HULL ) - { - m_hullImpactEmitter->SetPosition( &impactPosWS ); - m_hullImpactEmitter->SetDirection( &impactDirWS ); - - // do the spawn here once! - m_hullImpactEmitter->SpawnOnce( args, parentVelocityWS, scale, rateModifier ); - } - } - } + continue; + } + // where? + Vector3 impactPosWS( 0.f, 0.f, 0.f ); + parent->GetDamageLocatorPosition( &impactPosWS, mergedIndexOffset + impact.second.damageLocatorIndex, true ); + m_armorImpactEmitter->SetPosition( &impactPosWS ); + // facing? + Vector3 impactDirWS( 0.f, 1.f, 0.f ); + parent->GetDamageLocatorDirection( &impactDirWS, mergedIndexOffset + impact.second.damageLocatorIndex, true ); + m_armorImpactEmitter->SetDirection( &impactDirWS ); + // velocity? + Vector3 parentVelocityWS; + parent->GetWorldVelocity( parentVelocityWS ); + // scaling? + float scale = impact.second.size * armorImpactParentSize / ( IMPACT_ARMOR_SIZE_MAX / IMPACT_ARMOR_SIZE_FACTOR ); + // loding for emit rate? + float rateModifier = TriClamp( overlay.GetRenderPriority() / IMPACT_ARMOR_PARTICLE_LOD_FACTOR, 0.f, 1.f ); + // put together particle update info + ITr2GenericEmitter::UpdateArguments args( updateContext.GetTime(), updateContext.GetGpuParticleSystem(), IdentityMatrix(), updateContext.GetOriginShift() ); + // do the spawn here once! + m_armorImpactEmitter->SpawnOnce( args, parentVelocityWS, scale, rateModifier ); + impact.second.requestSpawnDebris = false; + + if( m_hullImpactEmitter && m_damageOverlay->GetImpactConfiguration() == ITriTargetable::IMPACT_HULL ) + { + m_hullImpactEmitter->SetPosition( &impactPosWS ); + m_hullImpactEmitter->SetDirection( &impactDirWS ); + + // do the spawn here once! + m_hullImpactEmitter->SpawnOnce( args, parentVelocityWS, scale, rateModifier ); } } + }; + + spawnFromOverlay( *m_damageOverlay, 0 ); + + std::vector> partOverlays; + parent->CollectPartDamageOverlays( partOverlays ); + for( auto& partOverlay : partOverlays ) + { + spawnFromOverlay( *partOverlay.first, partOverlay.second ); } } diff --git a/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.h b/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.h index 2c218303d..951c5732f 100644 --- a/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.h +++ b/trinity/Eve/SpaceObject/Attachments/EveImpactOverlay.h @@ -119,6 +119,8 @@ BLUE_CLASS( EveImpactOverlay ) : Vector3 GetShieldImpactPosition( const Matrix& parentInverseWorldTransform, const Vector3& damageLocatorPosWS, const Vector3& impactDirection, const Vector3& shieldEllipsoidCenter, const Vector3& shieldEllipsoidRadii ); + void SpawnImpactDebris( const EveUpdateContext& updateContext, EveSpaceObject2* parent ); + // general data BlueSharedString m_name; bool m_display; diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp index 9880eb4d1..5cbc2956b 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp @@ -4,6 +4,23 @@ #include "EveChildInstancedMeshes.h" #include "./Tr2RingBuffer.h" #include "../EveSpaceObject2.h" +#include "../Attachments/EveDamageOverlay.h" +#include "../Utils/EveLocatorSets.h" + +namespace +{ +const LocatorStructureList* FindDamageLocators( const std::vector& sets ) +{ + for( const auto& entry : sets ) + { + if( entry->HasName( DAMAGE_LOCATOR_SET_NAME ) ) + { + return entry->GetLocators(); + } + } + return nullptr; +} +} EveChildInstancedMeshes::EveChildInstancedMeshes( IRoot* lockobj ) @@ -183,7 +200,7 @@ void EveChildInstancedMeshes::UpdateVisibility( const EveUpdateContext& updateCo void EveChildInstancedMeshes::GetRenderables( std::vector& renderables ) { // only overlay draws render here; the base hull goes through EveInstancedMeshManager - if( m_hasUpdated && ( ( m_parentOverlayEffects != nullptr && AnyMeshInheritsOverlayEffects() ) || HasAnyOwnOverlayEffects() ) ) + if( m_hasUpdated && ( ( m_parentOverlayEffects != nullptr && AnyMeshInheritsOverlayEffects() ) || HasAnyOwnOverlayEffects() || !m_partDamageOverlays.empty() ) ) { renderables.push_back( this ); } @@ -207,6 +224,11 @@ void EveChildInstancedMeshes::UpdateSyncronous( const EveUpdateContext& updateCo overlay->Update( time, time ); } } + + for( auto& entry : m_partDamageOverlays ) + { + entry.second->UpdateSyncronous( updateContext ); + } } void EveChildInstancedMeshes::UpdateAsyncronous( const EveUpdateContext& updateContext, const EveChildUpdateParams& params ) @@ -307,9 +329,38 @@ void EveChildInstancedMeshes::UpdateAsyncronous( const EveUpdateContext& updateC } } } - if( ( m_parentOverlayEffects != nullptr && AnyMeshInheritsOverlayEffects() ) || HasAnyOwnOverlayEffects() ) + + for( auto& entry : m_partDamageOverlays ) { - UpdateOverlayInstanceData( vsData, psData ); + EveDamageOverlay::OwnerInfo info; + size_t instanceIndex = 0; + const Mesh* mesh = FindMeshByPartTag( entry.first, &instanceIndex ); + if( mesh && mesh->geometry && mesh->geometry->IsGood() ) + { + if( auto meshData = mesh->geometry->GetMeshData( mesh->meshIndex ) ) + { + CcpMath::Sphere localSphere( CcpMath::AxisAlignedBox( meshData->m_minBounds, meshData->m_maxBounds ) ); + info.boundingSphere = Vector4( localSphere.center, localSphere.radius ); + } + const CcpMath::Sphere& worldSphere = mesh->instanceSpheres[instanceIndex]; + info.estimatedPixelDiameter = std::max( updateContext.GetFrustum().GetPixelSizeAccrossEst( worldSphere.center, worldSphere.radius ) * updateContext.GetInvLodFactor(), 0.f ); + info.isInFrustum = updateContext.GetFrustum().IsSphereVisible( worldSphere.center, worldSphere.radius ); + const LocatorStructureList* damageLocators = FindDamageLocators( mesh->ownedLocatorSets ); + info.getDamageLocatorPositionOS = [damageLocators]( int index, Vector3& out ) { + if( !damageLocators || index < 0 || index >= int( damageLocators->size() ) ) + { + return false; + } + out = ( *damageLocators )[index].position; + return true; + }; + } + entry.second->UpdateAsyncronous( updateContext, info, 0, false ); + } + + if( ( m_parentOverlayEffects != nullptr && AnyMeshInheritsOverlayEffects() ) || HasAnyOwnOverlayEffects() || !m_partDamageOverlays.empty() ) + { + UpdateOverlayInstanceData( updateContext, vsData, psData ); } m_hasUpdated = true; @@ -400,7 +451,9 @@ void EveChildInstancedMeshes::AddMesh( size_t count, const BlueSharedString& sofHullName, const BlueSharedString& sofLocatorSetName, - EveSpaceObjectChild::PartTag partTag ) + EveSpaceObjectChild::PartTag partTag, + const std::vector& ownedLocatorSets, + Tr2Effect* armorDamageShader ) { if( areaCount == 0 || count == 0 ) { @@ -443,6 +496,10 @@ void EveChildInstancedMeshes::AddMesh( { continue; } + if( mesh.ownedLocatorSets.empty() != ownedLocatorSets.empty() ) + { + continue; + } const size_t existingCount = mesh.instances.size(); mesh.instances.reserve( existingCount + count ); mesh.partTags.reserve( mesh.partTags.size() + count ); @@ -466,6 +523,10 @@ void EveChildInstancedMeshes::AddMesh( area.meshGroupHandle.owner->RemoveMeshGroup( area.meshGroupHandle ); } } + if( !mesh.ownedLocatorSets.empty() && GetOwner() ) + { + GetOwner()->InvalidateMergedLocators( LocatorInvalidationReason::StructureChanged ); + } m_allRegistered = false; return; } @@ -520,11 +581,18 @@ void EveChildInstancedMeshes::AddMesh( } mesh.sofHullName = sofHullName; mesh.sofLocatorSetName = sofLocatorSetName; + mesh.ownedLocatorSets = ownedLocatorSets; + mesh.armorDamageShader = armorDamageShader; + if( !mesh.ownedLocatorSets.empty() && GetOwner() ) + { + GetOwner()->InvalidateMergedLocators( LocatorInvalidationReason::StructureChanged ); + } m_allRegistered = false; } void EveChildInstancedMeshes::RemoveInstancesByPartTag( EveSpaceObjectChild::PartTag partTag ) { + bool removedOwnedLocators = false; for( size_t i = 0; i < m_meshes.size(); ++i ) { auto& mesh = m_meshes[i]; @@ -537,6 +605,7 @@ void EveChildInstancedMeshes::RemoveInstancesByPartTag( EveSpaceObjectChild::Par { continue; } + removedOwnedLocators |= !mesh.ownedLocatorSets.empty(); if( newEnd == begin( mesh.instances ) ) { if( mesh.sphereHandle ) @@ -587,11 +656,17 @@ void EveChildInstancedMeshes::RemoveInstancesByPartTag( EveSpaceObjectChild::Par } } } + m_partDamageOverlays.erase( partTag ); + if( removedOwnedLocators && GetOwner() ) + { + GetOwner()->InvalidateMergedLocators( LocatorInvalidationReason::StructureChanged ); + } } void EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ) { Matrix m = TransformationMatrix( scale, rotation, translation ); const Float4x3 packedTransform( m ); + bool movedOwnedLocators = false; for( auto& mesh : m_meshes ) { for( size_t i = 0; i < mesh.instances.size(); ++i ) @@ -599,9 +674,14 @@ void EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, co if( mesh.partTags[i] == partTag ) { mesh.instances[i].worldTransform = packedTransform; + movedOwnedLocators |= !mesh.ownedLocatorSets.empty(); } } } + if( movedOwnedLocators && GetOwner() ) + { + GetOwner()->InvalidateMergedLocators( LocatorInvalidationReason::PartMoved ); + } } void EveChildInstancedMeshes::ReleaseCachedData( BlueAsyncRes* p ) @@ -1032,6 +1112,56 @@ bool EveChildInstancedMeshes::MeshHasActiveOverlayEffects( const Mesh& mesh ) co return !mesh.ownOverlayEffects.empty() || ( m_parentOverlayEffects != nullptr && mesh.inheritOverlayEffects ); } +const EveChildInstancedMeshes::Mesh* EveChildInstancedMeshes::FindMeshByPartTag( PartTag partTag, size_t* instanceIndex ) const +{ + for( const Mesh& mesh : m_meshes ) + { + for( size_t i = 0; i < mesh.partTags.size(); ++i ) + { + if( mesh.partTags[i] == partTag ) + { + if( instanceIndex ) + { + *instanceIndex = i; + } + return &mesh; + } + } + } + return nullptr; +} + +EveDamageOverlay* EveChildInstancedMeshes::FindPartDamageOverlay( PartTag partTag ) const +{ + auto it = m_partDamageOverlays.find( partTag ); + if( it != m_partDamageOverlays.end() ) + { + return it->second; + } + return nullptr; +} + +bool EveChildInstancedMeshes::MeshHasDamageOverlays( const Mesh& mesh ) const +{ + if( m_partDamageOverlays.empty() ) + { + return false; + } + for( uint32_t tag : mesh.partTags ) + { + if( m_partDamageOverlays.find( tag ) != m_partDamageOverlays.end() ) + { + return true; + } + } + return false; +} + +bool EveChildInstancedMeshes::MeshNeedsOverlayPods( const Mesh& mesh ) const +{ + return MeshHasActiveOverlayEffects( mesh ) || MeshHasDamageOverlays( mesh ); +} + uint32_t EveChildInstancedMeshes::OverlayInstancePod::GetPerObjectDataSize( Tr2RenderContextEnum::ShaderType shaderType ) const { if( shaderType == Tr2RenderContextEnum::PIXEL_SHADER ) @@ -1053,20 +1183,20 @@ void EveChildInstancedMeshes::OverlayInstancePod::UpdatePerObjectBuffer( Tr2Rend } } -void EveChildInstancedMeshes::UpdateOverlayInstanceData( const EveSpaceObjectVSData& parentVsData, const EveSpaceObjectPSData& parentPsData ) +void EveChildInstancedMeshes::UpdateOverlayInstanceData( const EveUpdateContext& updateContext, const EveSpaceObjectVSData& parentVsData, const EveSpaceObjectPSData& parentPsData ) { // worldTransformLast still holds the previous frame's transposed world transform here Matrix prevWorldTransform = Transpose( m_perObjectData.worldTransformLast ); for( Mesh& mesh : m_meshes ) { - if( mesh.instances.empty() || !mesh.display || !MeshHasActiveOverlayEffects( mesh ) ) + if( mesh.instances.empty() || !mesh.display || !MeshNeedsOverlayPods( mesh ) ) { continue; } - if( !mesh.overlayPods ) + if( !mesh.overlayPods || mesh.overlayPods->size() != mesh.instances.size() ) { - // sized once; the pods own device resources so they must stay at stable addresses + // the pods own device resources so they must stay at stable addresses mesh.overlayPods = std::make_unique>( mesh.instances.size() ); } @@ -1105,6 +1235,12 @@ void EveChildInstancedMeshes::UpdateOverlayInstanceData( const EveSpaceObjectVSD pod.psData.clipSphereFactor2 = 0.f; } + if( EveDamageOverlay* damageOverlay = FindPartDamageOverlay( mesh.partTags[i] ) ) + { + pod.psData.shipData.y *= damageOverlay->GetActivationStrength( updateContext ); + pod.psData.impactDataOffset = (float)damageOverlay->GetDataTextureOffset(); + } + pod.vsBuffer.InvalidateBufferData(); pod.psBuffer.InvalidateBufferData(); } @@ -1170,7 +1306,7 @@ float EveChildInstancedMeshes::GetSortValue() Tr2PerObjectData* EveChildInstancedMeshes::GetPerObjectData( ITriRenderBatchAccumulator* accumulator ) { - if( ( m_parentOverlayEffects == nullptr || !AnyMeshInheritsOverlayEffects() ) && !HasAnyOwnOverlayEffects() ) + if( ( m_parentOverlayEffects == nullptr || !AnyMeshInheritsOverlayEffects() ) && !HasAnyOwnOverlayEffects() && m_partDamageOverlays.empty() ) { return nullptr; } @@ -1182,7 +1318,7 @@ Tr2PerObjectData* EveChildInstancedMeshes::GetPerObjectData( ITriRenderBatchAccu { continue; } - if( !mesh.display || !MeshHasActiveOverlayEffects( mesh ) ) + if( !mesh.display || !MeshNeedsOverlayPods( mesh ) ) { for( OverlayInstancePod& pod : *mesh.overlayPods ) { @@ -1220,7 +1356,8 @@ void EveChildInstancedMeshes::GetBatches( ITriRenderBatchAccumulator* batches, T { const bool hasOwnOverlays = !mesh.ownOverlayEffects.empty(); const bool hasInheritedOverlays = m_parentOverlayEffects != nullptr && mesh.inheritOverlayEffects; - if( !hasInheritedOverlays && !hasOwnOverlays ) + const bool hasDamageOverlays = MeshHasDamageOverlays( mesh ); + if( !hasInheritedOverlays && !hasOwnOverlays && !hasDamageOverlays ) { continue; } @@ -1264,6 +1401,16 @@ void EveChildInstancedMeshes::GetBatches( ITriRenderBatchAccumulator* batches, T // own effects are emitted before the inherited ones so the parent's overlays // (e.g. cloak) draw on top of this mesh's own overlays + if( hasDamageOverlays ) + { + if( EveDamageOverlay* damageOverlay = FindPartDamageOverlay( mesh.partTags[i] ) ) + { + if( Tr2Effect* damageShader = damageOverlay->GetArmorDamageShader( batchType ) ) + { + EmitDamageOverlayBatches( batches, pod.framePod, damageShader, mesh.overlayAreaBlocks, *lod ); + } + } + } if( hasOwnOverlays ) { EmitOverlayBatches( batches, pod.framePod, batchType, mesh.ownOverlayEffects, mesh.overlayAreaBlocks, *lod ); @@ -1312,7 +1459,7 @@ void EveChildInstancedMeshes::CollectOwnedGeometry( TriBatchType type, const Mat for( const auto& instance : mesh.instances ) { - Matrix instanceTransform = *(Float4x3*)&instance.worldTransform; + Matrix instanceTransform = instance.worldTransform; EveChildGeometry source; source.childToObject = instanceTransform * parentTransform; source.geometry = mesh.geometry; @@ -1322,3 +1469,67 @@ void EveChildInstancedMeshes::CollectOwnedGeometry( TriBatchType type, const Mat } } } + +void EveChildInstancedMeshes::CollectOwnedLocatorSets( const Matrix& parentTransform, std::vector& out ) const +{ + for( const Mesh& mesh : m_meshes ) + { + for( const auto& sets : mesh.ownedLocatorSets ) + { + if( !sets || sets->GetLocators()->empty() ) + { + continue; + } + for( size_t i = 0; i < mesh.instances.size(); ++i ) + { + Matrix instanceTransform = mesh.instances[i].worldTransform; + EveChildLocatorSetsSource source; + source.childToObject = instanceTransform * parentTransform; + source.owner = this; + source.partTag = mesh.partTags[i]; + source.sets = sets; + out.push_back( source ); + } + } + } +} + +EveDamageOverlayPtr EveChildInstancedMeshes::GetPartDamageOverlay( PartTag partTag ) const +{ + return FindPartDamageOverlay( partTag ); +} + +EveDamageOverlayPtr EveChildInstancedMeshes::EnsurePartDamageOverlay( PartTag partTag ) +{ + EveDamageOverlayPtr& overlay = m_partDamageOverlays[partTag]; + if( !overlay ) + { + overlay.CreateInstance(); + } + return overlay; +} + +Tr2Effect* EveChildInstancedMeshes::GetPartArmorDamageShaderEffect( PartTag partTag ) const +{ + if( const Mesh* mesh = FindMeshByPartTag( partTag ) ) + { + return mesh->armorDamageShader; + } + return nullptr; +} + +bool EveChildInstancedMeshes::GetPartDamageLocatorAnimatedLocal( PartTag partTag, int index, Vector3& position, Vector3& direction ) const +{ + const Mesh* mesh = FindMeshByPartTag( partTag ); + if( !mesh ) + { + return false; + } + const LocatorStructureList* locators = FindDamageLocators( mesh->ownedLocatorSets ); + if( !locators || index < 0 || index >= int( locators->size() ) ) + { + return false; + } + EveGetLocatorPose( nullptr, ( *locators )[index], position, direction ); + return true; +} diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h index 64be036d9..986d2f922 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h @@ -10,6 +10,7 @@ BLUE_DECLARE( TriGeometryRes ); BLUE_DECLARE( Tr2Effect ); +BLUE_DECLARE( EveLocatorSets ); BLUE_CLASS( EveChildInstancedMeshes ) : @@ -93,7 +94,9 @@ BLUE_CLASS( EveChildInstancedMeshes ) : size_t count, const BlueSharedString& sofHullName, const BlueSharedString& sofLocatorSetName, - EveSpaceObjectChild::PartTag partTag = EveSpaceObjectChild::NO_PART_TAG ); + EveSpaceObjectChild::PartTag partTag = EveSpaceObjectChild::NO_PART_TAG, + const std::vector& ownedLocatorSets = {}, + Tr2Effect* armorDamageShader = nullptr ); void RemoveInstancesByPartTag( EveSpaceObjectChild::PartTag partTag ); void SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ); @@ -115,6 +118,13 @@ BLUE_CLASS( EveChildInstancedMeshes ) : void CollectOwnedGeometry( TriBatchType type, const Matrix& parentTransform, std::vector& out, std::vector& areaPool ) const override; + void CollectOwnedLocatorSets( const Matrix& parentTransform, std::vector& out ) const override; + + EveDamageOverlayPtr GetPartDamageOverlay( PartTag partTag ) const override; + EveDamageOverlayPtr EnsurePartDamageOverlay( PartTag partTag ) override; + Tr2Effect* GetPartArmorDamageShaderEffect( PartTag partTag ) const override; + bool GetPartDamageLocatorAnimatedLocal( PartTag partTag, int index, Vector3& position, Vector3& direction ) const override; + private: // per-instance constant buffers for overlay draws struct OverlayInstancePod @@ -150,6 +160,9 @@ BLUE_CLASS( EveChildInstancedMeshes ) : BlueSharedString sofHullName; BlueSharedString sofLocatorSetName; + std::vector ownedLocatorSets; + Tr2EffectPtr armorDamageShader; + struct RayTracingArea { Tr2RaytracingMeshArea* rtMeshArea = nullptr; @@ -178,11 +191,15 @@ BLUE_CLASS( EveChildInstancedMeshes ) : void RebuildCachedData( BlueAsyncRes * p ) override; void UnregisterFromMeshManager(); - void UpdateOverlayInstanceData( const EveSpaceObjectVSData& parentVsData, const EveSpaceObjectPSData& parentPsData ); + void UpdateOverlayInstanceData( const EveUpdateContext& updateContext, const EveSpaceObjectVSData& parentVsData, const EveSpaceObjectPSData& parentPsData ); static void RebuildOverlayAreaBlocks( Mesh & mesh ); bool HasAnyOwnOverlayEffects() const; bool AnyMeshInheritsOverlayEffects() const; bool MeshHasActiveOverlayEffects( const Mesh& mesh ) const; + const Mesh* FindMeshByPartTag( PartTag partTag, size_t* instanceIndex = nullptr ) const; + EveDamageOverlay* FindPartDamageOverlay( PartTag partTag ) const; + bool MeshHasDamageOverlays( const Mesh& mesh ) const; + bool MeshNeedsOverlayPods( const Mesh& mesh ) const; void ReleaseResources( TriStorage s ) override; bool OnPrepareResources() override; @@ -195,6 +212,7 @@ BLUE_CLASS( EveChildInstancedMeshes ) : EveInstancedMeshManager::PerObjectDataHandle m_perObjectDataHandle; EveInstancedMeshManager::PerObjectDataHandle m_perObjectDataNoClipHandle; std::vector m_meshes; + std::map m_partDamageOverlays; TriFrustum m_lastCameraFrustum; float m_lastInvLodFactor = 1.0f; mutable Tr2ConstantBufferAL m_rtPerObjectData; diff --git a/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp b/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp index a8d8aa20f..8591f6eb6 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp @@ -2053,6 +2053,7 @@ void EveChildMesh::CollectOwnedLocatorSets( const Matrix& parentTransform, std:: EveChildLocatorSetsSource source; source.childToObject = localTransform * parentTransform; source.owner = this; + source.partTag = GetPartTag(); source.sets = entry; out.push_back( source ); } @@ -2090,12 +2091,12 @@ void EveChildMesh::InvalidateOwnerMergedLocators( LocatorInvalidationReason reas } } -EveDamageOverlayPtr EveChildMesh::GetDamageOverlay() const +EveDamageOverlayPtr EveChildMesh::GetPartDamageOverlay( PartTag ) const { return m_damageOverlay; } -EveDamageOverlayPtr EveChildMesh::EnsureDamageOverlay() +EveDamageOverlayPtr EveChildMesh::EnsurePartDamageOverlay( PartTag ) { if( !m_damageOverlay ) { @@ -2109,7 +2110,7 @@ void EveChildMesh::SetArmorDamageShaderEffect( Tr2Effect* effect ) m_armorDamageShader = effect; } -Tr2Effect* EveChildMesh::GetArmorDamageShaderEffect() const +Tr2Effect* EveChildMesh::GetPartArmorDamageShaderEffect( PartTag ) const { return m_armorDamageShader; } @@ -2138,7 +2139,7 @@ bool EveChildMesh::GetDamageLocatorBindPositionLocal( int index, Vector3& out ) return true; } -bool EveChildMesh::GetDamageLocatorAnimatedLocal( int index, Vector3& position, Vector3& direction ) const +bool EveChildMesh::GetPartDamageLocatorAnimatedLocal( PartTag, int index, Vector3& position, Vector3& direction ) const { const LocatorStructureList* locators = GetOwnedDamageLocators(); if( !locators || index < 0 || index >= int( locators->size() ) ) diff --git a/trinity/Eve/SpaceObject/Children/EveChildMesh.h b/trinity/Eve/SpaceObject/Children/EveChildMesh.h index 023985f6f..33dc83c78 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildMesh.h +++ b/trinity/Eve/SpaceObject/Children/EveChildMesh.h @@ -203,12 +203,13 @@ BLUE_CLASS( EveChildMesh ) : void SetOwnedLocatorSets( const std::vector& sets ); void InvalidateOwnerMergedLocators( LocatorInvalidationReason reason ); - EveDamageOverlayPtr GetDamageOverlay() const; - EveDamageOverlayPtr EnsureDamageOverlay(); + EveDamageOverlayPtr GetPartDamageOverlay( PartTag partTag ) const override; + EveDamageOverlayPtr EnsurePartDamageOverlay( PartTag partTag ) override; + Tr2Effect* GetPartArmorDamageShaderEffect( PartTag partTag ) const override; + bool GetPartDamageLocatorAnimatedLocal( PartTag partTag, int index, Vector3& position, Vector3& direction ) const override; + void SetArmorDamageShaderEffect( Tr2Effect * effect ); - Tr2Effect* GetArmorDamageShaderEffect() const; bool GetDamageLocatorBindPositionLocal( int index, Vector3& out ) const; - bool GetDamageLocatorAnimatedLocal( int index, Vector3& position, Vector3& direction ) const; protected: const LocatorStructureList* GetOwnedDamageLocators() const; diff --git a/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp b/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp index df2a907e2..8eabf6e55 100644 --- a/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp +++ b/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp @@ -159,15 +159,6 @@ BlueStdResult EveModularObjectModifier::Remove( EveSpaceObjectChild::PartTag par ++i; } - for( auto& set : m_object->GetLocatorSets() ) - { - auto& locators = *set->GetLocators(); - auto removed = std::remove_if( locators.begin(), locators.end(), [partId]( const auto& locator ) { - return locator.partTag == partId; - } ); - locators.Resize( std::distance( locators.begin(), removed ) ); - } - if( m_instancedMeshes ) { m_instancedMeshes->RemoveInstancesByPartTag( partId ); @@ -193,22 +184,6 @@ BlueStdResult EveModularObjectModifier::SetTransform( EveSpaceObjectChild::PartT cmf::Transform newTransform{ position, rotation, scale }; auto invOldTransform = cmf::Inverse( oldTransform ); - for( auto& set : m_object->GetLocatorSets() ) - { - auto& locators = *set->GetLocators(); - for( auto& locator : locators ) - { - if( locator.partTag == partId ) - { - locator.scale.x = scale.x / found->scale.x; - locator.scale.y = scale.y / found->scale.y; - locator.scale.z = scale.z / found->scale.z; - locator.direction = invOldTransform.rotation * rotation; - locator.position = cmf::TransformPoint( cmf::TransformPoint( locator.position, invOldTransform ), newTransform ); - } - } - } - found->boundingSphere.center = cmf::TransformPoint( cmf::TransformPoint( found->boundingSphere.center, invOldTransform ), newTransform ); found->boundingSphere.radius *= std::max( { scale.x, scale.y, scale.z } ) / std::max( { found->scale.x, found->scale.y, found->scale.z } ); diff --git a/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.cpp b/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.cpp index bb7e4c465..7c6e1eb0e 100644 --- a/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.cpp +++ b/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.cpp @@ -135,6 +135,27 @@ void EveSpaceObjectChild::CollectOwnedGeometry( TriBatchType type, const Matrix& { } +EveDamageOverlayPtr EveSpaceObjectChild::GetPartDamageOverlay( PartTag ) const +{ + return nullptr; +} + +EveDamageOverlayPtr EveSpaceObjectChild::EnsurePartDamageOverlay( PartTag ) +{ + CCP_ASSERT( false ); + return nullptr; +} + +Tr2Effect* EveSpaceObjectChild::GetPartArmorDamageShaderEffect( PartTag ) const +{ + return nullptr; +} + +bool EveSpaceObjectChild::GetPartDamageLocatorAnimatedLocal( PartTag, int, Vector3&, Vector3& ) const +{ + return false; +} + void EveCollectAreas( TriBatchType type, Tr2MeshBase* mesh, std::vector& areaPool ) { if( !mesh ) diff --git a/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.h b/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.h index 8023aa3c8..291b4c428 100644 --- a/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.h +++ b/trinity/Eve/SpaceObject/Children/EveSpaceObjectChild.h @@ -7,6 +7,9 @@ BLUE_CLASS_IMPL( EveSpaceObjectChild ); +BLUE_DECLARE( EveDamageOverlay ); +BLUE_DECLARE( Tr2Effect ); + /** * @brief Struct bundling locator sets and owner information as well as transform, to be processed by the parent object. * @see EveSpaceObjectChild::CollectOwnedLocatorSets @@ -15,7 +18,8 @@ struct EveChildLocatorSetsSource { class EveLocatorSets* sets = nullptr; Matrix childToObject = IdentityMatrix(); - const class EveChildMesh* owner = nullptr; + const class EveSpaceObjectChild* owner = nullptr; + uint32_t partTag = 0; }; /** @@ -290,6 +294,37 @@ class EveSpaceObjectChild : public IEveSpaceObjectChild */ virtual void CollectOwnedGeometry( TriBatchType type, const Matrix& parentTransform, std::vector& out, std::vector& areaPool ) const; + /** + * @brief Returns the damage overlay of the given part. + * @param partTag Part tag identifying the part on this child. + * @return The part's damage overlay, or nullptr if the part has none yet. + */ + virtual EveDamageOverlayPtr GetPartDamageOverlay( PartTag partTag ) const; + + /** + * @brief Returns damage overlay of the given part, creating it if non-existent. + * @param partTag Part tag identifying the part on this child. + * @return The part's damage overlay. + */ + virtual EveDamageOverlayPtr EnsurePartDamageOverlay( PartTag partTag ); + + /** + * @brief Returns the armor damage shader effect of the given part's damage overlay. + * @param partTag Part tag identifying the part on this child. + * @return The part's armor damage shader effect, or nullptr if the child has none. + */ + virtual Tr2Effect* GetPartArmorDamageShaderEffect( PartTag partTag ) const; + + /** + * @brief Retrieves the current (animated) pose of one of the given part's damage locators. + * @param partTag Part tag identifying the part on this child. + * @param index Index of the locator within the part's damage locator set. + * @param position Receives the locator position in child-local space. + * @param direction Receives the locator direction in child-local space. + * @return True if the pose was written, false otherwise. + */ + virtual bool GetPartDamageLocatorAnimatedLocal( PartTag partTag, int index, Vector3& position, Vector3& direction ) const; + protected: /** * @brief Helper function that registers a child object with this parent. Assigns the parent, owner and part tag to the child. diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2.cpp b/trinity/Eve/SpaceObject/EveSpaceObject2.cpp index f31588f6d..f52ac57d2 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2.cpp +++ b/trinity/Eve/SpaceObject/EveSpaceObject2.cpp @@ -56,6 +56,8 @@ TRI_REGISTER_SETTING( "secondaryLightingRadiusCutoffFactor", g_secondaryLighting const BlueSharedString DAMAGE_LOCATOR_SET_NAME( "damage" ); +extern bool g_eveSpaceObjectImpactEffectEnabled; + void GetSortedBatchesFromMeshAreaVector( const Tr2MeshAreaVector* areas, ITriRenderBatchAccumulator* batches, const Tr2PerObjectData* perObjectData, @@ -1540,6 +1542,10 @@ void EveSpaceObject2::PushRenderables( std::vector& renderables CCP_STATS_INC( eveLowDetailObjects ); } } + else if( !m_mesh && m_impactOverlay && m_isMeshVisible ) + { + renderables.push_back( this ); + } PushChildrenAndDecalRenderables( renderables ); } @@ -1932,7 +1938,7 @@ void EveSpaceObject2::EnsureChildLocatorMerged() const { LocatorSourceRange range; range.owner = childLocatorSet.owner; - range.partTag = childLocatorSet.owner->GetPartTag(); + range.partTag = childLocatorSet.partTag; range.start = int32_t( ( *mergedLocatorSet )->GetLocators()->size() - childLocatorSet.sets->GetLocators()->size() ); range.count = int32_t( childLocatorSet.sets->GetLocators()->size() ); range.childToObject = childLocatorSet.childToObject; @@ -3522,11 +3528,11 @@ void EveSpaceObject2::SetImpactDamageState( float shield, float armor, float hul // -------------------------------------------------------------------------------- EveDamageOverlayPtr EveSpaceObject2::EnsureChildDamageOverlay( const LocatorSourceRange& range ) { - EveDamageOverlayPtr overlay = range.owner->GetDamageOverlay(); + EveDamageOverlayPtr overlay = range.owner->GetPartDamageOverlay( range.partTag ); if( !overlay ) { - overlay = const_cast( range.owner )->EnsureDamageOverlay(); - overlay->SetArmorDamageShaderEffect( range.owner->GetArmorDamageShaderEffect() ); + overlay = const_cast( range.owner )->EnsurePartDamageOverlay( range.partTag ); + overlay->SetArmorDamageShaderEffect( range.owner->GetPartArmorDamageShaderEffect( range.partTag ) ); // each part gets its own flicker curve instance, the async child updates must not share one if( TriPerlinCurve* flickerCurve = m_impactOverlay->GetHullDamageFlickerCurve() ) { @@ -3534,7 +3540,7 @@ EveDamageOverlayPtr EveSpaceObject2::EnsureChildDamageOverlay( const LocatorSour BeClasses->CopyTo( flickerCurve->GetRootObject(), (IRoot**)&flickerCopy ); overlay->SetHullDamageFlickerCurve( flickerCopy ); } - overlay->SetSeed( m_impactOverlay->GetSeed() + range.owner->GetPartTag() ); + overlay->SetSeed( m_impactOverlay->GetSeed() + range.partTag ); } overlay->SetDamageLocatorCount( uint32_t( range.count ) ); int rangeStart = min( int32_t( m_damageLocatorEnabled.size() ), range.start ); @@ -3545,6 +3551,25 @@ EveDamageOverlayPtr EveSpaceObject2::EnsureChildDamageOverlay( const LocatorSour return overlay; } +// -------------------------------------------------------------------------------- +// Description: +// Collects the damage overlays of parts, with offsets into the merged damage locator set. +// -------------------------------------------------------------------------------- +void EveSpaceObject2::CollectPartDamageOverlays( std::vector>& out ) +{ + EnsureChildLocatorMerged(); + for( const auto& range : m_mergedDamageLocatorSources ) + { + if( range.owner ) + { + if( EveDamageOverlay* overlay = range.owner->GetPartDamageOverlay( range.partTag ) ) + { + out.emplace_back( overlay, range.start ); + } + } + } +} + // -------------------------------------------------------------------------------- // Description: // Toggle an animation on the impacts: for boosters, hardeners, et.c @@ -3588,7 +3613,7 @@ void EveSpaceObject2::ClearImpactDamage() { if( range.owner ) { - if( EveDamageOverlayPtr overlay = range.owner->GetDamageOverlay() ) + if( EveDamageOverlayPtr overlay = range.owner->GetPartDamageOverlay( range.partTag ) ) { overlay->Clear(); } @@ -3613,9 +3638,14 @@ int EveSpaceObject2::CreateImpact( int damageLocatorIndex, const Vector3& direct { if( range.owner && damageLocatorIndex >= range.start && damageLocatorIndex < range.start + range.count ) { + if( !g_eveSpaceObjectImpactEffectEnabled ) + { + return -1; + } if( EveDamageOverlayPtr overlay = EnsureChildDamageOverlay( range ) ) { - return overlay->CreateImpact( damageLocatorIndex - range.start, size, false ); + bool spawnEffects = m_lodLevel != TR2_LOD_LOW; + return overlay->CreateImpact( damageLocatorIndex - range.start, size, spawnEffects ); } } } @@ -3666,7 +3696,7 @@ bool EveSpaceObject2::UpdateImpact( Vector3& out, const Vector3& direction, int { if( range.owner ) { - if( EveDamageOverlayPtr overlay = range.owner->GetDamageOverlay() ) + if( EveDamageOverlayPtr overlay = range.owner->GetPartDamageOverlay( range.partTag ) ) { if( overlay->HasImpact( impactIndex ) ) { @@ -3757,7 +3787,7 @@ void EveSpaceObject2::GetLocatorInObjectSpace( Vector3& position, Vector3& direc { if( range.owner && mergedDamageIndex >= range.start && mergedDamageIndex < range.start + range.count ) { - if( range.owner->GetDamageLocatorAnimatedLocal( mergedDamageIndex - range.start, position, direction ) ) + if( range.owner->GetPartDamageLocatorAnimatedLocal( range.partTag, mergedDamageIndex - range.start, position, direction ) ) { position = XMVector3TransformCoord( position, range.childToObject ); direction = Normalize( (Vector3)XMVector3TransformNormal( direction, range.childToObject ) ); diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2.h b/trinity/Eve/SpaceObject/EveSpaceObject2.h index 74f6522e4..b77b530c9 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2.h +++ b/trinity/Eve/SpaceObject/EveSpaceObject2.h @@ -175,7 +175,7 @@ struct LocatorSourceRange { int32_t start; int32_t count; - const class EveChildMesh* owner; + const class EveSpaceObjectChild* owner; uint32_t partTag; Matrix childToObject = IdentityMatrix(); }; @@ -311,6 +311,7 @@ BLUE_CLASS( EveSpaceObject2 ) : void EnsureChildLocatorMerged() const; void UpdateDamageLocatorFilter(); EveDamageOverlayPtr EnsureChildDamageOverlay( const LocatorSourceRange& range ); + void CollectPartDamageOverlays( std::vector> & out ); ////////////////////////////////////////////////////////////////////////////////////// // IEveShadowCaster @@ -797,6 +798,7 @@ BLUE_CLASS( EveSpaceObject2 ) : private: #if BLUE_WITH_PYTHON static PyObject* PyTransformLocators( PyObject * self, PyObject * args ); + static PyObject* PyGetTransformedLocatorsFromSet( PyObject * self, PyObject * args ); #endif void ReleaseDamageFilterSessions(); diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2_Blue.cpp b/trinity/Eve/SpaceObject/EveSpaceObject2_Blue.cpp index 968e11d58..350ac1d5c 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2_Blue.cpp +++ b/trinity/Eve/SpaceObject/EveSpaceObject2_Blue.cpp @@ -89,7 +89,9 @@ PyObject* EveSpaceObject2::PyTransformLocators( PyObject* self, PyObject* args ) TransformLocator( position, rotation, locator.boneIndex, pThis->m_animationUpdater ); if( modelTranslationCurve || modelRotationCurve ) + { ApplyModelTransform( position, rotation, modelTranslationCurve, modelRotationCurve ); + } PyObject* tuple = PyTuple_New( 3 ); PyTuple_SetItem( tuple, 0, Py_BuildValue( "(fff)", position.x, position.y, position.z ) ); @@ -124,7 +126,9 @@ PyObject* EveSpaceObject2::PyTransformLocators( PyObject* self, PyObject* args ) TransformLocator( position, rotation, boneIndex, pThis->m_animationUpdater ); if( modelTranslationCurve || modelRotationCurve ) + { ApplyModelTransform( position, rotation, modelTranslationCurve, modelRotationCurve ); + } PyObject* tuple = PyTuple_New( 3 ); PyTuple_SetItem( tuple, 0, Py_BuildValue( "(fff)", position.x, position.y, position.z ) ); @@ -138,6 +142,43 @@ PyObject* EveSpaceObject2::PyTransformLocators( PyObject* self, PyObject* args ) PyErr_SetString( PyExc_TypeError, "arument must be a sequence of (position, rotation, boneIndex) tuples" ); return nullptr; } + +PyObject* EveSpaceObject2::PyGetTransformedLocatorsFromSet( PyObject* self, PyObject* args ) +{ + auto pThis = BluePythonCast( self ); + const char* locatorSetName = nullptr; + if( !PyArg_ParseTuple( args, "s", &locatorSetName ) ) + { + return nullptr; + } + + auto modelTranslationCurve = pThis->GetModelTranslationCurve(); + auto modelRotationCurve = pThis->GetModelRotationCurve(); + + const LocatorStructureList* locators = pThis->GetLocatorsForSet( BlueSharedString( locatorSetName ) ); + size_t count = locators ? locators->size() : 0; + PyObject* result = PyList_New( ssize_t( count ) ); + for( size_t i = 0; i < count; ++i ) + { + const Locator& locator = ( *locators )[i]; + + Vector3 position = locator.position; + Quaternion rotation = locator.direction; + + TransformLocator( position, rotation, locator.boneIndex, pThis->m_animationUpdater ); + if( modelTranslationCurve || modelRotationCurve ) + { + ApplyModelTransform( position, rotation, modelTranslationCurve, modelRotationCurve ); + } + + PyObject* tuple = PyTuple_New( 3 ); + PyTuple_SetItem( tuple, 0, Py_BuildValue( "(fff)", position.x, position.y, position.z ) ); + PyTuple_SetItem( tuple, 1, Py_BuildValue( "(ffff)", rotation.x, rotation.y, rotation.z, rotation.w ) ); + PyTuple_SetItem( tuple, 2, ToPython( locator.boneIndex ) ); + PyList_SetItem( result, ssize_t( i ), tuple ); + } + return result; +} #endif const Be::ClassInfo* EveSpaceObject2::ExposeToBlue() @@ -603,6 +644,12 @@ const Be::ClassInfo* EveSpaceObject2::ExposeToBlue() "Transforms a sequence of locators using current bone setup\n" ":param locators: Locator data (position, rotation, boneIndex)\n" ":type locators: sequence[((float, float, float), (float, float, float, float), int)]" ) + MAP_METHOD( + "GetTransformedLocatorsFromSet", + PyGetTransformedLocatorsFromSet, + "Locators of a named set, including locators owned by parts, transformed using current bone setup\n" + ":param locatorSetName: name of locator set\n" + ":returns: list[((float, float, float), (float, float, float, float), int)] of (position, rotation, boneIndex)" ) #endif EXPOSURE_END() diff --git a/trinity/Eve/SpaceObjectFactory/EveSOF.cpp b/trinity/Eve/SpaceObjectFactory/EveSOF.cpp index 8502a7362..d67ab6bdb 100644 --- a/trinity/Eve/SpaceObjectFactory/EveSOF.cpp +++ b/trinity/Eve/SpaceObjectFactory/EveSOF.cpp @@ -391,6 +391,11 @@ bool EveSOF::BuildChild( EveSpaceObject2* newObj, const char* dnaString, uint32_ { child->SetupWithStaticTransform( &scale, &rotation, &translation, Tr2Lod::TR2_LOD_LOW ); } + child->SetOwnedLocatorSets( BuildHullLocalLocatorSets( dna ) ); + if( dna->GetLocatorCount( DAMAGE_LOCATOR_SET_NAME.c_str() ) > 0 ) + { + child->SetArmorDamageShaderEffect( GetOrCreateArmorDamageEffect( armorDamageEffectCache, dna ) ); + } child->SetPartTag( partTag ); if( m_editorMode ) @@ -440,6 +445,14 @@ bool EveSOF::BuildChild( EveSpaceObject2* newObj, const char* dnaString, uint32_ area->IsReversed() } ); } } + + std::vector partLocatorSets = BuildHullLocalLocatorSets( dna ); + Tr2EffectPtr partArmorDamageShader; + if( dna->GetLocatorCount( DAMAGE_LOCATOR_SET_NAME.c_str() ) > 0 ) + { + partArmorDamageShader = GetOrCreateArmorDamageEffect( armorDamageEffectCache, dna ); + } + sharedMeshes->AddMesh( dna->GetHullGeometryResPath().c_str(), dna->CastShadow(), @@ -451,7 +464,9 @@ bool EveSOF::BuildChild( EveSpaceObject2* newObj, const char* dnaString, uint32_ 1, m_editorMode ? BlueSharedString( dna->GetHullNames()[0].c_str() ) : BlueSharedString(), BlueSharedString(), - partTag ); + partTag, + partLocatorSets, + partArmorDamageShader ); SetupAttachments( BlueCastPtr( placementContainer ), dna, placementOffsets, buildFlags ); } @@ -502,7 +517,7 @@ bool EveSOF::BuildChild( EveSpaceObject2* newObj, const char* dnaString, uint32_ { SetupImpactEffects( newObj, dna, armorDamageEffectCache ); } - SetupLocatorSets( newObj, dna, placementOffsets, partTag ); + // setup nested layout int layoutPartTag = static_cast( partTag ); SetupLayout( newObj, placementContainer, sharedMeshes, armorDamageEffectCache, dna, placementOffsets, layoutPartTag, false );