diff --git a/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.cpp b/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.cpp index fcf2502c2..426925fd0 100644 --- a/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.cpp +++ b/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.cpp @@ -5,6 +5,8 @@ #include "Shader/Tr2Effect.h" #include "Curves/TriCurveSet.h" #include "Controllers/ITr2Controller.h" +#include "Tr2MeshBase.h" +#include "Resources/TriGeometryRes.h" // -------------------------------------------------------------------------------------- @@ -286,3 +288,92 @@ void EveMeshOverlayEffect::Update( Be::Time realTime, Be::Time simTime ) ( *it )->Update( 0.5f ); } } + +void CollectOverlayAreaBlocks( Tr2MeshBase* mesh, std::vector ( &outAreaBlocks )[EveMeshOverlayEffect::TYPE_COUNT] ) +{ + for( int i = 0; i < EveMeshOverlayEffect::TYPE_COUNT; ++i ) + { + outAreaBlocks[i].clear(); + } + + if( !mesh ) + { + return; + } + + mesh->CollectAreaBlocks( outAreaBlocks[EveMeshOverlayEffect::TYPE_ALL], TRIBATCHTYPE_OPAQUE ); + mesh->CollectAreaBlocks( outAreaBlocks[EveMeshOverlayEffect::TYPE_ALL], TRIBATCHTYPE_TRANSPARENT ); + mesh->CollectAreaBlocks( outAreaBlocks[EveMeshOverlayEffect::TYPE_ALL], TRIBATCHTYPE_DECAL ); + mesh->CollectAreaBlocks( outAreaBlocks[EveMeshOverlayEffect::TYPE_OPAQUEONLY], TRIBATCHTYPE_OPAQUE ); + + // this list is too long, will hold one element for each mesharea at least... Optimize! + for( int i = 0; i < EveMeshOverlayEffect::TYPE_COUNT; ++i ) + { + TriRenderBatchAreaBlock::Optimize( outAreaBlocks[i] ); + } +} + +template +static void EmitOverlayBatchesImpl( + ITriRenderBatchAccumulator* batches, + const Tr2PerObjectData* perObjectData, + TriBatchType batchType, + const OverlayEffectContainer& overlayEffects, + const std::vector ( &areaBlocks )[EveMeshOverlayEffect::TYPE_COUNT], + const TriGeometryResLodData& lod ) +{ + for( const auto& overlay : overlayEffects ) + { + bool success = false; + const PTr2EffectVector& effects = overlay->GetEffects( batchType, success ); + if( !success ) + { + continue; + } + + EveMeshOverlayEffect::OverlayType overlayType = overlay->GetType( batchType ); + for( const auto& effect : effects ) + { + // add all mesh area blocks + for( auto& areaBlock : areaBlocks[overlayType] ) + { + if( auto primCount = GetPrimitiveCount( lod, areaBlock.m_startIndex, areaBlock.m_count ) ) + { + Tr2RenderBatch batch; + batch.SetMaterial( effect ); + batch.SetGeometry( lod.m_mesh->m_vertexDeclarationHandle, lod.m_vertexAllocation, lod.m_indexAllocation ); + batch.SetPerObjectData( perObjectData ); + batch.SetDrawIndexedInstanced( + primCount * 3, + 1, + lod.m_indexAllocation.GetStartIndex() + lod.m_areas[areaBlock.m_startIndex].m_firstIndex, + lod.m_vertexAllocation.GetOffset() / lod.m_vertexAllocation.GetStride(), + 0 ); + batches->Commit( batch ); + } + } + } + } +} + +void EmitOverlayBatches( + ITriRenderBatchAccumulator* batches, + const Tr2PerObjectData* perObjectData, + TriBatchType batchType, + const PEveMeshOverlayEffectVector& overlayEffects, + const std::vector ( &areaBlocks )[EveMeshOverlayEffect::TYPE_COUNT], + const TriGeometryResLodData& lod ) +{ + EmitOverlayBatchesImpl( batches, perObjectData, batchType, overlayEffects, areaBlocks, lod ); +} + +void EmitOverlayBatches( + ITriRenderBatchAccumulator* batches, + const Tr2PerObjectData* perObjectData, + TriBatchType batchType, + const std::vector& overlayEffects, + const std::vector ( &areaBlocks )[EveMeshOverlayEffect::TYPE_COUNT], + const TriGeometryResLodData& lod ) +{ + EmitOverlayBatchesImpl( batches, perObjectData, batchType, overlayEffects, areaBlocks, lod ); +} diff --git a/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.h b/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.h index e62f95026..2e501f5a5 100644 --- a/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.h +++ b/trinity/Eve/SpaceObject/Attachments/EveMeshOverlayEffect.h @@ -7,15 +7,21 @@ #include "Tr2MeshArea.h" #include "ITr2Renderable.h" +#include "TriRenderBatch.h" #include "Controllers/ITr2ControllerOwner.h" #include "ITr2CurveSetOwner.h" BLUE_DECLARE( Tr2Effect ); BLUE_DECLARE( TriCurveSet ); +BLUE_DECLARE( Tr2MeshBase ); BLUE_DECLARE_VECTOR( Tr2Effect ); BLUE_DECLARE_INTERFACE( ITr2Controller ); BLUE_DECLARE_IVECTOR( ITr2Controller ); +struct TriGeometryResLodData; +class ITriRenderBatchAccumulator; +class Tr2PerObjectData; + // -------------------------------------------------------------------------------- // Description: // This class holds curveSets and Tr2Effects used for overlay effects for SpaceObjects. @@ -98,4 +104,22 @@ BLUE_CLASS( EveMeshOverlayEffect ) : TYPEDEF_BLUECLASS( EveMeshOverlayEffect ); BLUE_DECLARE_VECTOR( EveMeshOverlayEffect ); +void CollectOverlayAreaBlocks( Tr2MeshBase* mesh, std::vector ( &outAreaBlocks )[EveMeshOverlayEffect::TYPE_COUNT] ); + +void EmitOverlayBatches( + ITriRenderBatchAccumulator* batches, + const Tr2PerObjectData* perObjectData, + TriBatchType batchType, + const PEveMeshOverlayEffectVector& overlayEffects, + const std::vector ( &areaBlocks )[EveMeshOverlayEffect::TYPE_COUNT], + const TriGeometryResLodData& lod ); + +void EmitOverlayBatches( + ITriRenderBatchAccumulator* batches, + const Tr2PerObjectData* perObjectData, + TriBatchType batchType, + const std::vector& overlayEffects, + const std::vector ( &areaBlocks )[EveMeshOverlayEffect::TYPE_COUNT], + const TriGeometryResLodData& lod ); + #endif // EveMeshOverlayEffect_H diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp index 4bc9ddbef..4a94a3ad0 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp @@ -3,6 +3,7 @@ #include "StdAfx.h" #include "EveChildInstancedMeshes.h" #include "./Tr2RingBuffer.h" +#include "../EveSpaceObject2.h" EveChildInstancedMeshes::EveChildInstancedMeshes( IRoot* lockobj ) @@ -67,6 +68,10 @@ void EveChildInstancedMeshes::UnregisterFromMeshManager() { m_perObjectDataHandle.owner->RemovePerObjectData( m_perObjectDataHandle ); } + if( m_perObjectDataNoClipHandle ) + { + m_perObjectDataNoClipHandle.owner->RemovePerObjectData( m_perObjectDataNoClipHandle ); + } m_allRegistered = false; } @@ -183,10 +188,16 @@ void EveChildInstancedMeshes::SetName( const char* name ) void EveChildInstancedMeshes::UpdateVisibility( const EveUpdateContext& updateContext, const Matrix& parentTransform, Tr2Lod parentLod ) { m_lastCameraFrustum = updateContext.GetFrustum(); + m_lastInvLodFactor = updateContext.GetInvLodFactor(); } 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() ) ) + { + renderables.push_back( this ); + } } bool EveChildInstancedMeshes::GetBoundingSphere( Vector4& sphere, BoundingSphereQuery query ) const @@ -197,6 +208,16 @@ bool EveChildInstancedMeshes::GetBoundingSphere( Vector4& sphere, BoundingSphere void EveChildInstancedMeshes::UpdateSyncronous( const EveUpdateContext& updateContext, const EveChildUpdateParams& params ) { m_worldTransform = params.localToWorldTransform; + + // overlay effect curves must update on the game thread; they may reference attributes that are not thread safe + Be::Time time = updateContext.GetTime(); + for( Mesh& mesh : m_meshes ) + { + for( EveMeshOverlayEffectPtr& overlay : mesh.ownOverlayEffects ) + { + overlay->Update( time, time ); + } + } } void EveChildInstancedMeshes::UpdateAsyncronous( const EveUpdateContext& updateContext, const EveChildUpdateParams& params ) @@ -237,6 +258,12 @@ void EveChildInstancedMeshes::UpdateAsyncronous( const EveUpdateContext& updateC m_perObjectData.customData = vsData.customData; std::copy( std::begin( psData.shLightingCoefficients ), std::end( psData.shLightingCoefficients ), std::begin( m_perObjectData.shLighting ) ); + m_perObjectDataNoClip = m_perObjectData; + m_perObjectDataNoClip.clipRadiusSq = 0.f; + m_perObjectDataNoClip.clipRadius2Sq = 0.f; + m_perObjectDataNoClip.clipSphereFactor = 0.f; + m_perObjectDataNoClip.clipSphereFactor2 = 0.f; + float worldScale = std::sqrtf( std::max( { LengthSq( Vector3( m_worldTransform._11, m_worldTransform._12, m_worldTransform._13 ) ), LengthSq( Vector3( m_worldTransform._21, m_worldTransform._22, m_worldTransform._23 ) ), LengthSq( Vector3( m_worldTransform._31, m_worldTransform._32, m_worldTransform._33 ) ) } ) ); @@ -278,6 +305,23 @@ void EveChildInstancedMeshes::UpdateAsyncronous( const EveUpdateContext& updateC } } + m_parentOverlayEffects = nullptr; + if( params.spaceObjectParent ) + { + if( EveSpaceObject2Ptr spaceObject2Parent = BlueCastPtr( params.spaceObjectParent ) ) + { + const PEveMeshOverlayEffectVector& parentOverlays = spaceObject2Parent->GetOverlayEffects(); + if( !parentOverlays.empty() ) + { + m_parentOverlayEffects = &parentOverlays; + } + } + } + if( ( m_parentOverlayEffects != nullptr && AnyMeshInheritsOverlayEffects() ) || HasAnyOwnOverlayEffects() ) + { + UpdateOverlayInstanceData( vsData, psData ); + } + m_hasUpdated = true; USE_MAIN_THREAD_RENDER_CONTEXT(); @@ -517,6 +561,11 @@ void EveChildInstancedMeshes::AddMeshesToManager( EveInstancedMeshManager& manag continue; } + if( !mesh.inheritOverlayEffects && !m_perObjectDataNoClipHandle ) + { + manager.AddPerObjectData( m_perObjectDataNoClipHandle, &m_perObjectDataNoClip ); + } + if( !mesh.sphereHandle ) { manager.AddBoundingSphereGroup( mesh.sphereHandle, mesh.worldBoundingSphere, mesh.flags, mesh.instanceSpheres.data(), static_cast( mesh.instanceSpheres.size() ) ); @@ -541,7 +590,7 @@ void EveChildInstancedMeshes::AddMeshesToManager( EveInstancedMeshManager& manag area.areaCount, area.effect, area.effectHash, - m_perObjectDataHandle, + mesh.inheritOverlayEffects ? m_perObjectDataHandle : m_perObjectDataNoClipHandle, mesh.sphereHandle, mesh.instances.data(), uint32_t( mesh.instances.size() ), @@ -690,6 +739,46 @@ BluePy EveChildInstancedMeshes::SetMeshDisplay( uint32_t meshId, bool display ) return BluePy( Py_None, true ); } +BluePy EveChildInstancedMeshes::GetMeshInheritOverlayEffects( uint32_t meshId ) const +{ + if( meshId >= m_meshes.size() ) + { + PyErr_SetString( PyExc_IndexError, "Mesh index out of range" ); + return {}; + } + if( m_meshes[meshId].inheritOverlayEffects ) + { + return BluePy( Py_True, true ); + } + else + { + return BluePy( Py_False, true ); + } +} + +BluePy EveChildInstancedMeshes::SetMeshInheritOverlayEffects( uint32_t meshId, bool inherit ) +{ + if( meshId >= m_meshes.size() ) + { + PyErr_SetString( PyExc_IndexError, "Mesh index out of range" ); + return {}; + } + auto& mesh = m_meshes[meshId]; + if( mesh.inheritOverlayEffects != inherit ) + { + mesh.inheritOverlayEffects = inherit; + m_allRegistered = false; + for( auto& area : mesh.areas ) + { + if( area.meshGroupHandle ) + { + area.meshGroupHandle.owner->RemoveMeshGroup( area.meshGroupHandle ); + } + } + } + return BluePy( Py_None, true ); +} + void EveChildInstancedMeshes::ReleaseResources( TriStorage s ) { if( ( s & TRISTORAGE_MANAGEDMEMORY ) != 0 ) @@ -702,3 +791,341 @@ bool EveChildInstancedMeshes::OnPrepareResources() { return true; } + +BluePy EveChildInstancedMeshes::AddMeshOverlayEffect( uint32_t meshId, EveMeshOverlayEffect* overlayEffect ) +{ + if( meshId >= m_meshes.size() ) + { + PyErr_SetString( PyExc_IndexError, "Mesh index out of range" ); + return {}; + } + if( overlayEffect == nullptr ) + { + PyErr_SetString( PyExc_TypeError, "overlayEffect must not be None" ); + return {}; + } + m_meshes[meshId].ownOverlayEffects.push_back( overlayEffect ); + return BluePy( Py_None, true ); +} + +BluePy EveChildInstancedMeshes::RemoveMeshOverlayEffect( uint32_t meshId, EveMeshOverlayEffect* overlayEffect ) +{ + if( meshId >= m_meshes.size() ) + { + PyErr_SetString( PyExc_IndexError, "Mesh index out of range" ); + return {}; + } + auto& overlays = m_meshes[meshId].ownOverlayEffects; + auto it = std::find( overlays.begin(), overlays.end(), EveMeshOverlayEffectPtr( overlayEffect ) ); + if( it != overlays.end() ) + { + overlays.erase( it ); + } + return BluePy( Py_None, true ); +} + +BluePy EveChildInstancedMeshes::ClearMeshOverlayEffects( uint32_t meshId ) +{ + if( meshId >= m_meshes.size() ) + { + PyErr_SetString( PyExc_IndexError, "Mesh index out of range" ); + return {}; + } + m_meshes[meshId].ownOverlayEffects.clear(); + return BluePy( Py_None, true ); +} + +BluePy EveChildInstancedMeshes::GetMeshOverlayEffectCount( uint32_t meshId ) const +{ + if( meshId >= m_meshes.size() ) + { + PyErr_SetString( PyExc_IndexError, "Mesh index out of range" ); + return {}; + } + return BluePy( ToPython( uint32_t( m_meshes[meshId].ownOverlayEffects.size() ) ) ); +} + +bool EveChildInstancedMeshes::HasAnyOwnOverlayEffects() const +{ + for( const Mesh& mesh : m_meshes ) + { + if( !mesh.ownOverlayEffects.empty() ) + { + return true; + } + } + return false; +} + +bool EveChildInstancedMeshes::AnyMeshInheritsOverlayEffects() const +{ + for( const Mesh& mesh : m_meshes ) + { + if( mesh.inheritOverlayEffects ) + { + return true; + } + } + return false; +} + +bool EveChildInstancedMeshes::MeshHasActiveOverlayEffects( const Mesh& mesh ) const +{ + return !mesh.ownOverlayEffects.empty() || ( m_parentOverlayEffects != nullptr && mesh.inheritOverlayEffects ); +} + +uint32_t EveChildInstancedMeshes::OverlayInstancePod::GetPerObjectDataSize( Tr2RenderContextEnum::ShaderType shaderType ) const +{ + if( shaderType == Tr2RenderContextEnum::PIXEL_SHADER ) + { + return sizeof( psData ); + } + return sizeof( vsData ); +} + +void EveChildInstancedMeshes::OverlayInstancePod::UpdatePerObjectBuffer( Tr2RenderContextEnum::ShaderType shaderType, uint32_t size, void* data ) +{ + if( shaderType == Tr2RenderContextEnum::PIXEL_SHADER ) + { + memcpy( data, &psData, sizeof( psData ) ); + } + else + { + memcpy( data, &vsData, sizeof( vsData ) ); + } +} + +void EveChildInstancedMeshes::UpdateOverlayInstanceData( 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 ) ) + { + continue; + } + if( !mesh.overlayPods ) + { + // sized once; the pods own device resources so they must stay at stable addresses + mesh.overlayPods = std::make_unique>( mesh.instances.size() ); + } + + for( size_t i = 0; i < mesh.instances.size(); ++i ) + { + const auto& wt = mesh.instances[i].worldTransform; + OverlayInstancePod& pod = ( *mesh.overlayPods )[i]; + + Matrix local = IdentityMatrix(); + local._11 = wt[0].x; + local._12 = wt[1].x; + local._13 = wt[2].x; + local._21 = wt[0].y; + local._22 = wt[1].y; + local._23 = wt[2].y; + local._31 = wt[0].z; + local._32 = wt[1].z; + local._33 = wt[2].z; + local._41 = wt[0].w; + local._42 = wt[1].w; + local._43 = wt[2].w; + + Matrix worldTransform = Transpose( local * m_worldTransform ); + Matrix worldTransformLast = Transpose( local * prevWorldTransform ); + Matrix invWorldTransform = Inverse( worldTransform ); + Matrix invLocal = Inverse( local ); + + pod.vsData = parentVsData; + pod.vsData.worldTransform = worldTransform; + pod.vsData.worldTransformLast = worldTransformLast; + pod.vsData.invWorldTransform = invWorldTransform; + // need to move the clipdata inversely of the transform of the instance + pod.vsData.clipData = Vector4( TransformCoord( pod.vsData.clipData.GetXYZ(), invLocal ), pod.vsData.clipData.w ); + + pod.psData = parentPsData; + pod.psData.worldTransform = worldTransform; + pod.psData.worldTransformLast = worldTransformLast; + pod.psData.invWorldTransform = invWorldTransform; + pod.psData.clipSphereCenter = TransformCoord( pod.psData.clipSphereCenter, invLocal ); + + if( !mesh.inheritOverlayEffects ) + { + // opted out of the parent's overlay: also neutralize the inherited clip sphere + pod.vsData.clipData.w = 0.f; + pod.psData.clipRadiusSq = 0.f; + pod.psData.clipRadius2Sq = 0.f; + pod.psData.clipSphereFactor = 0.f; + pod.psData.clipSphereFactor2 = 0.f; + } + + pod.vsBuffer.InvalidateBufferData(); + pod.psBuffer.InvalidateBufferData(); + } + } +} + +void EveChildInstancedMeshes::RebuildOverlayAreaBlocks( Mesh& mesh ) +{ + for( int i = 0; i < EveMeshOverlayEffect::TYPE_COUNT; ++i ) + { + mesh.overlayAreaBlocks[i].clear(); + } + + for( const MeshArea& area : mesh.areas ) + { + if( area.batchType == TRIBATCHTYPE_OPAQUE || area.batchType == TRIBATCHTYPE_TRANSPARENT || area.batchType == TRIBATCHTYPE_DECAL ) + { + mesh.overlayAreaBlocks[EveMeshOverlayEffect::TYPE_ALL].emplace_back( area.areaIndex, area.areaCount ); + } + if( area.batchType == TRIBATCHTYPE_OPAQUE ) + { + mesh.overlayAreaBlocks[EveMeshOverlayEffect::TYPE_OPAQUEONLY].emplace_back( area.areaIndex, area.areaCount ); + } + } + + for( int i = 0; i < EveMeshOverlayEffect::TYPE_COUNT; ++i ) + { + TriRenderBatchAreaBlock::Optimize( mesh.overlayAreaBlocks[i] ); + } + mesh.overlayAreaBlocksBuilt = true; +} + +bool EveChildInstancedMeshes::HasTransparentBatches() +{ + if( m_parentOverlayEffects != nullptr && AnyMeshInheritsOverlayEffects() ) + { + for( const auto& overlayEffect : *m_parentOverlayEffects ) + { + if( overlayEffect->HasTransparentArea() ) + { + return true; + } + } + } + for( const Mesh& mesh : m_meshes ) + { + for( const EveMeshOverlayEffectPtr& overlay : mesh.ownOverlayEffects ) + { + if( overlay->HasTransparentArea() ) + { + return true; + } + } + } + return false; +} + +float EveChildInstancedMeshes::GetSortValue() +{ + Vector3 d = Tr2Renderer::GetViewPosition() - m_worldTransform.GetTranslation(); + return Length( d ); +} + +Tr2PerObjectData* EveChildInstancedMeshes::GetPerObjectData( ITriRenderBatchAccumulator* accumulator ) +{ + if( ( m_parentOverlayEffects == nullptr || !AnyMeshInheritsOverlayEffects() ) && !HasAnyOwnOverlayEffects() ) + { + return nullptr; + } + + Tr2PerObjectData* firstPod = nullptr; + for( Mesh& mesh : m_meshes ) + { + if( !mesh.overlayPods ) + { + continue; + } + if( !mesh.display || !MeshHasActiveOverlayEffects( mesh ) ) + { + for( OverlayInstancePod& pod : *mesh.overlayPods ) + { + pod.framePod = nullptr; + } + continue; + } + for( OverlayInstancePod& pod : *mesh.overlayPods ) + { + pod.framePod = nullptr; + auto* perObjectData = accumulator->Allocate>(); + if( perObjectData == nullptr ) + { + return firstPod; + } + perObjectData->Initialize( &pod, &pod.vsBuffer, &pod.psBuffer ); + pod.framePod = perObjectData; + if( firstPod == nullptr ) + { + firstPod = perObjectData; + } + } + } + return firstPod; +} + +void EveChildInstancedMeshes::GetBatches( ITriRenderBatchAccumulator* batches, TriBatchType batchType, const Tr2PerObjectData* perObjectData, Tr2RenderReason reason ) +{ + if( !m_hasUpdated ) + { + return; + } + + for( Mesh& mesh : m_meshes ) + { + const bool hasOwnOverlays = !mesh.ownOverlayEffects.empty(); + const bool hasInheritedOverlays = m_parentOverlayEffects != nullptr && mesh.inheritOverlayEffects; + if( !hasInheritedOverlays && !hasOwnOverlays ) + { + continue; + } + if( !mesh.display || !mesh.overlayPods ) + { + continue; + } + if( reason == TR2RENDERREASON_REFLECTION && !EntityComponents::ShouldReflect( mesh.reflectionMode ) ) + { + continue; + } + if( !mesh.geometry || !mesh.geometry->IsGood() ) + { + continue; + } + if( !mesh.overlayAreaBlocksBuilt ) + { + RebuildOverlayAreaBlocks( mesh ); + } + + for( size_t i = 0; i < mesh.overlayPods->size(); ++i ) + { + OverlayInstancePod& pod = ( *mesh.overlayPods )[i]; + if( pod.framePod == nullptr ) + { + continue; + } + + // per-instance LOD selection matching the base hull in EveInstancedMeshManager + const CcpMath::Sphere& sphere = mesh.instanceSpheres[i]; + if( !m_lastCameraFrustum.IsSphereVisible( sphere.center, sphere.radius ) ) + { + continue; + } + float screenSize = m_lastCameraFrustum.GetPixelSizeAccrossEst( sphere.center, sphere.radius ) * m_lastInvLodFactor; + auto lod = mesh.geometry->GetMeshLod( mesh.meshIndex, screenSize ); + if( !lod || !lod->m_allocationsValid ) + { + continue; + } + + // 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( hasOwnOverlays ) + { + EmitOverlayBatches( batches, pod.framePod, batchType, mesh.ownOverlayEffects, mesh.overlayAreaBlocks, *lod ); + } + if( hasInheritedOverlays ) + { + EmitOverlayBatches( batches, pod.framePod, batchType, *m_parentOverlayEffects, mesh.overlayAreaBlocks, *lod ); + } + } + } +} diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h index 5a25e441d..e652c567f 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h @@ -4,6 +4,9 @@ #include "IEveSpaceObjectChild.h" #include "../../EveInstancedMeshManager.h" +#include "ITr2Renderable.h" +#include "Tr2PersistentPerObjectData.h" +#include "../Attachments/EveMeshOverlayEffect.h" BLUE_DECLARE( TriGeometryRes ); BLUE_DECLARE( Tr2Effect ); @@ -16,6 +19,7 @@ BLUE_CLASS( EveChildInstancedMeshes ) : public IEveShadowCaster, public IEveInstanceMeshProvider, public ITr2DebugRenderable, + public ITr2Renderable, public Tr2DeviceResource { public: @@ -62,6 +66,13 @@ BLUE_CLASS( EveChildInstancedMeshes ) : void GetDebugOptions( Tr2DebugRendererOptions & options ) override; void RenderDebugInfo( ITr2DebugRenderer2 & renderer ) override; + ////////////////////////////////////////////////////////////////////////////////////// + // ITr2Renderable + void GetBatches( ITriRenderBatchAccumulator * batches, TriBatchType batchType, const Tr2PerObjectData* perObjectData, Tr2RenderReason reason = TR2RENDERREASON_NORMAL ) override; + bool HasTransparentBatches() override; + float GetSortValue() override; + Tr2PerObjectData* GetPerObjectData( ITriRenderBatchAccumulator * accumulator ) override; + struct MeshArea { Tr2EffectPtr effect = nullptr; @@ -90,8 +101,28 @@ BLUE_CLASS( EveChildInstancedMeshes ) : BluePy GetAreaInfo( uint32_t meshId, uint32_t areaId ) const; BluePy GetMeshDisplay( uint32_t meshId ) const; BluePy SetMeshDisplay( uint32_t meshId, bool display ); + BluePy GetMeshInheritOverlayEffects( uint32_t meshId ) const; + BluePy SetMeshInheritOverlayEffects( uint32_t meshId, bool inherit ); + + BluePy AddMeshOverlayEffect( uint32_t meshId, EveMeshOverlayEffect* overlayEffect ); + BluePy RemoveMeshOverlayEffect( uint32_t meshId, EveMeshOverlayEffect* overlayEffect ); + BluePy ClearMeshOverlayEffects( uint32_t meshId ); + BluePy GetMeshOverlayEffectCount( uint32_t meshId ) const; private: + // per-instance constant buffers for overlay draws + struct OverlayInstancePod + { + EveSpaceObjectVSData vsData = {}; + EveSpaceObjectPSData psData = {}; + Tr2PersistentPerObjectData vsBuffer; + Tr2PersistentPerObjectData psBuffer; + Tr2PerObjectData* framePod = nullptr; // valid only within the current frame's batch gathering + + uint32_t GetPerObjectDataSize( Tr2RenderContextEnum::ShaderType shaderType ) const; + void UpdatePerObjectBuffer( Tr2RenderContextEnum::ShaderType shaderType, uint32_t size, void* data ); + }; + struct Mesh { std::string geometryPath; @@ -127,22 +158,39 @@ BLUE_CLASS( EveChildInstancedMeshes ) : }; std::vector rtMeshes; + std::unique_ptr> overlayPods; + std::vector overlayAreaBlocks[EveMeshOverlayEffect::TYPE_COUNT]; + bool overlayAreaBlocksBuilt = false; + std::vector ownOverlayEffects; + bool display = true; + bool inheritOverlayEffects = true; }; void ReleaseCachedData( BlueAsyncRes * p ) override; void RebuildCachedData( BlueAsyncRes * p ) override; void UnregisterFromMeshManager(); + void UpdateOverlayInstanceData( const EveSpaceObjectVSData& parentVsData, const EveSpaceObjectPSData& parentPsData ); + static void RebuildOverlayAreaBlocks( Mesh & mesh ); + bool HasAnyOwnOverlayEffects() const; + bool AnyMeshInheritsOverlayEffects() const; + bool MeshHasActiveOverlayEffects( const Mesh& mesh ) const; + void ReleaseResources( TriStorage s ) override; bool OnPrepareResources() override; BlueSharedString m_name; Matrix m_worldTransform = IdentityMatrix(); EveSpacePerObjectData m_perObjectData; + // m_perObjectData with the clip sphere neutralized, for meshes that opt out of the parent's overlays + EveSpacePerObjectData m_perObjectDataNoClip; + const PEveMeshOverlayEffectVector* m_parentOverlayEffects = nullptr; EveInstancedMeshManager::PerObjectDataHandle m_perObjectDataHandle; + EveInstancedMeshManager::PerObjectDataHandle m_perObjectDataNoClipHandle; std::vector m_meshes; TriFrustum m_lastCameraFrustum; + float m_lastInvLodFactor = 1.0f; mutable Tr2ConstantBufferAL m_rtPerObjectData; bool m_allRegistered = false; diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp index 59c4d83b7..5ecc391e8 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp @@ -14,6 +14,7 @@ const Be::ClassInfo* EveChildInstancedMeshes::ExposeToBlue() MAP_INTERFACE( IEveSpaceObjectChild ) MAP_INTERFACE( EveEntity ) MAP_INTERFACE( IEveShadowCaster ) + MAP_INTERFACE( ITr2Renderable ) MAP_ATTRIBUTE( "name", m_name, "", Be::READWRITE | Be::PERSIST ) @@ -58,6 +59,47 @@ const Be::ClassInfo* EveChildInstancedMeshes::ExposeToBlue() ":param meshId: Index of the mesh to modify\n" ":param display: True to render the mesh, False to hide it\n" ":rtype: None" ) + MAP_METHOD_AND_WRAP( + "GetMeshInheritOverlayEffects", + GetMeshInheritOverlayEffects, + "Returns True if the parent space object's overlay effects (e.g. cloak) also render over the given mesh\n\n" + ":param meshId: Index of the mesh to query\n" + ":rtype: bool" ) + MAP_METHOD_AND_WRAP( + "SetMeshInheritOverlayEffects", + SetMeshInheritOverlayEffects, + "Sets whether the parent space object's overlay effects (e.g. cloak) also render over the given mesh.\n" + "When False the mesh also ignores the parent's clip sphere so it stays visible while the rest of the ship dissolves.\n\n" + ":param meshId: Index of the mesh to modify\n" + ":param inherit: True to inherit the parent's overlay effects, False to opt out\n" + ":rtype: None" ) + MAP_METHOD_AND_WRAP( + "AddMeshOverlayEffect", + AddMeshOverlayEffect, + "Adds an overlay effect owned by the given mesh. Rendered over the mesh's areas,\n" + "underneath any overlay effects inherited from the parent space object.\n\n" + ":param meshId: Index of the mesh to modify\n" + ":param overlayEffect: The EveMeshOverlayEffect to add\n" + ":rtype: None" ) + MAP_METHOD_AND_WRAP( + "RemoveMeshOverlayEffect", + RemoveMeshOverlayEffect, + "Removes the first matching overlay effect previously added to the given mesh.\n\n" + ":param meshId: Index of the mesh to modify\n" + ":param overlayEffect: The EveMeshOverlayEffect to remove\n" + ":rtype: None" ) + MAP_METHOD_AND_WRAP( + "ClearMeshOverlayEffects", + ClearMeshOverlayEffects, + "Removes all overlay effects owned by the given mesh.\n\n" + ":param meshId: Index of the mesh to modify\n" + ":rtype: None" ) + MAP_METHOD_AND_WRAP( + "GetMeshOverlayEffectCount", + GetMeshOverlayEffectCount, + "Returns the number of overlay effects owned by the given mesh.\n\n" + ":param meshId: Index of the mesh to query\n" + ":rtype: int" ) EXPOSURE_END() -} \ No newline at end of file +} diff --git a/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp b/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp index a9efa903e..ee3104918 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildMesh.cpp @@ -30,7 +30,9 @@ EveChildMesh::EveChildMesh( IRoot* lockobj ) : PARENTLOCK( m_decals ), PARENTLOCK( m_attachments ), PARENTLOCK( m_lights ), + PARENTLOCK( m_overlayEffects ), m_display( true ), + m_inheritOverlayEffects( true ), m_isVisible( false ), m_instancesVisible( false ), m_castShadow( false ), @@ -369,6 +371,7 @@ void EveChildMesh::UpdateVisibility( const EveUpdateContext& updateContext, cons m_currentScreenSize = -1; m_instancesVisible = false; m_currentInstanceScreenSize = -1.0f; + m_overlayUpdateLod = parentLod; if( !m_hasUpdated ) { @@ -630,7 +633,29 @@ bool EveChildMesh::HasTransparentBatches() { if( m_display && m_mesh ) { - return !( m_mesh->GetAreas( TRIBATCHTYPE_TRANSPARENT )->empty() ); + if( !( m_mesh->GetAreas( TRIBATCHTYPE_TRANSPARENT )->empty() ) ) + { + return true; + } + + for( const auto& overlayEffect : m_overlayEffects ) + { + if( overlayEffect->HasTransparentArea() ) + { + return true; + } + } + + if( m_parentOverlayEffects != nullptr ) + { + for( const auto& overlayEffect : *m_parentOverlayEffects ) + { + if( overlayEffect->HasTransparentArea() ) + { + return true; + } + } + } } return false; @@ -665,6 +690,53 @@ void EveChildMesh::GetBatches( ITriRenderBatchAccumulator* batches, TriBatchType ( *it )->GetBatches( batches, batchType, perObjectData, reason ); } } + + GetBatchesFromOverlayVector( batches, perObjectData, batchType ); + } +} + +void EveChildMesh::RebuildOverlayAreaBlocks() +{ + CollectOverlayAreaBlocks( m_mesh, m_overlayMeshAreaBlocks ); + m_overlayAreaBlocksBuilt = true; +} + +void EveChildMesh::GetBatchesFromOverlayVector( ITriRenderBatchAccumulator* batches, const Tr2PerObjectData* perObjectData, TriBatchType batchType ) +{ + const bool hasParentOverlays = m_parentOverlayEffects != nullptr && !m_parentOverlayEffects->empty(); + if( !m_mesh || ( m_overlayEffects.empty() && !hasParentOverlays ) ) + { + return; + } + + TriGeometryRes* geomRes = m_mesh->GetGeometryResource(); + if( !geomRes || !geomRes->IsGood() ) + { + return; + } + + if( !m_overlayAreaBlocksBuilt ) + { + RebuildOverlayAreaBlocks(); + } + + const float screenSize = min( m_currentInstanceScreenSize, m_currentScreenSize ); + auto lod = geomRes->GetMeshLod( m_mesh->GetMeshIndex(), screenSize ); + if( !lod || !lod->m_allocationsValid ) + { + return; + } + + // own effects are emitted before the inherited ones so the parent's overlays (e.g. cloak) + // draw on top of this child's own overlays (e.g. battle damage) + if( !m_overlayEffects.empty() ) + { + EmitOverlayBatches( batches, perObjectData, batchType, m_overlayEffects, m_overlayMeshAreaBlocks, *lod ); + } + + if( hasParentOverlays ) + { + EmitOverlayBatches( batches, perObjectData, batchType, *m_parentOverlayEffects, m_overlayMeshAreaBlocks, *lod ); } } @@ -822,11 +894,11 @@ Tr2PerObjectData* EveChildMesh::GetPerObjectData( ITriRenderBatchAccumulator* ac } } } - - auto [bones, boneCount] = GetBoneTransforms(); - m_vsData.boneOffsets[2] = uint32_t( boneCount ); - m_boneOffsets.UploadTransforms( Tr2RingBuffer::GetInstance(), reinterpret_cast( bones ), uint32_t( boneCount ) ); } + + auto [bones, boneCount] = GetBoneTransforms(); + m_vsData.boneOffsets[2] = uint32_t( boneCount ); + m_boneOffsets.UploadTransforms( Tr2RingBuffer::GetInstance(), reinterpret_cast( bones ), uint32_t( boneCount ) ); m_vsData.boneOffsets[0] = m_boneOffsets.GetCurrentFrameOffset(); m_vsData.boneOffsets[1] = m_boneOffsets.GetPreviousFrameOffset(); @@ -928,11 +1000,34 @@ void EveChildMesh::UpdateAsyncronous( const EveUpdateContext& updateContext, con } // need to update the data we get from the parent to be relevant to us! + m_parentOverlayEffects = nullptr; if( nullptr != params.spaceObjectParent ) { params.spaceObjectParent->GetPerObjectStructs( m_vsData, m_psData ); params.spaceObjectParent->GetParentData( &m_parentData ); + if( m_inheritOverlayEffects ) + { + if( EveSpaceObject2Ptr spaceObject2Parent = BlueCastPtr( params.spaceObjectParent ) ) + { + m_parentOverlayEffects = &spaceObject2Parent->GetOverlayEffects(); + } + } + else + { + // Opted out of the parent's overlay: also neutralize the inherited clip sphere, + // otherwise the part is dissolved with the rest of the ship instead of staying visible. + m_vsData.clipData.w = 0.f; + m_psData.clipRadiusSq = 0.f; + m_psData.clipRadius2Sq = 0.f; + m_psData.clipSphereFactor = 0.f; + m_psData.clipSphereFactor2 = 0.f; + m_parentData.clipRadiusSq = 0.f; + m_parentData.clipRadius2Sq = 0.f; + m_parentData.clipFactor = 0.f; + m_parentData.clipFactor2 = 0.f; + } + // need to move the clipdata inversely of the translation of the childmesh m_vsData.clipData = Vector4( m_vsData.clipData.GetXYZ() - m_translation, m_vsData.clipData.w ); m_psData.clipSphereCenter = m_psData.clipSphereCenter - m_translation; @@ -999,6 +1094,21 @@ void EveChildMesh::UpdateAsyncronous( const EveUpdateContext& updateContext, con void EveChildMesh::UpdateSyncronous( const EveUpdateContext& updateContext, const EveChildUpdateParams& params ) { + if( !m_overlayEffects.empty() ) + { + Be::Time time = updateContext.GetTime(); + if( EveLODHelper::ShouldUpdate( m_overlayUpdateLod, float( TimeAsDouble( time - m_lastOverlayUpdateTime ) ) ) ) + { + // overlay effect curves need to be updated on the game thread because they may have references + // to attributes that are not thread safe, particularly the parent's clipSphereFactor + m_lastOverlayUpdateTime = time; + for( const auto& overlayEffect : m_overlayEffects ) + { + overlayEffect->Update( time, time ); + } + } + } + bool allowAudioGeometry = !params.spaceObjectParent || params.spaceObjectParent->IsAudioOccluder(); if( !allowAudioGeometry && m_audioGeometryRegistered ) @@ -1060,6 +1170,43 @@ void EveChildMesh::SetMesh( Tr2MeshBase* mesh ) m_mesh = mesh; m_instancedMesh = BlueCastPtr( m_mesh ); + + m_overlayAreaBlocksBuilt = false; + for( int i = 0; i < EveMeshOverlayEffect::TYPE_COUNT; ++i ) + { + m_overlayMeshAreaBlocks[i].clear(); + } +} + +void EveChildMesh::AddOverlayEffect( EveMeshOverlayEffectPtr newOverlayEffect ) +{ + m_overlayEffects.Append( newOverlayEffect->GetRawRoot() ); +} + +void EveChildMesh::RemoveOverlayEffect( EveMeshOverlayEffectPtr overlayEffectToRemove ) +{ + ssize_t index = m_overlayEffects.FindKey( overlayEffectToRemove->GetRawRoot() ); + if( index >= 0 ) + { + m_overlayEffects.Remove( index ); + } +} + +EveMeshOverlayEffectPtr EveChildMesh::GetOverlayEffectByName( const char* name ) const +{ + if( name == nullptr ) + { + return nullptr; + } + + for( auto overlay : m_overlayEffects ) + { + if( strcmp( overlay->m_name.c_str(), name ) == 0 ) + { + return overlay; + } + } + return nullptr; } void EveChildMesh::RegisterAudioGeometry() @@ -1155,6 +1302,11 @@ void EveChildMesh::SetShaderOption( const BlueSharedString& name, const BlueShar m_mesh->SetShaderOption( name, value ); } + for( const auto& overlayEffect : m_overlayEffects ) + { + overlayEffect->SetShaderOption( name, value ); + } + for( EveSpaceObjectDecalVector::iterator it = m_decals.begin(); it != m_decals.end(); ++it ) { ( *it )->SetShaderOption( name, value ); @@ -1287,7 +1439,7 @@ std::pair EveChildMesh::GetBoneTransforms() const if( !m_animationUpdater || !m_animationUpdater->IsInitialized() ) { - return std::make_pair( nullptr, 0 ); + return GetRestPoseBoneTransforms(); } auto accumulatedTransforms = m_animationUpdater->GetAnimationTransforms(); @@ -1301,7 +1453,32 @@ std::pair EveChildMesh::GetBoneTransforms() const { return m_meshBinding->GetBoneTransforms(); } - return std::make_pair( nullptr, 0 ); + return GetRestPoseBoneTransforms(); +} + +std::pair EveChildMesh::GetRestPoseBoneTransforms() const +{ + if( !m_mesh || !m_mesh->GetGeometryResource() ) + { + return std::make_pair( nullptr, 0 ); + } + + // Skinned shaders without an animation get rest-pose (identity) skin matrices, sized to the + // geometry's bone bindings. Unskinned geometry drawn with a skinned shader (e.g. a SOF hull + // flagged isSkinned) has no bindings and no blend indices in the vertex stream, so every + // vertex reads bone 0 - a single identity matrix covers it. + size_t boneCount = 1; + auto cmfData = m_mesh->GetGeometryResource()->GetCMFData(); + auto meshIndex = m_mesh->GetMeshIndex(); + if( cmfData && meshIndex < cmfData->meshes.size() ) + { + boneCount = std::max( cmfData->meshes[meshIndex].boneBindings.size(), 1 ); + } + if( m_restPoseBoneTransforms.size() != boneCount ) + { + m_restPoseBoneTransforms.assign( boneCount, Float4x3( Matrix() ) ); + } + return std::make_pair( m_restPoseBoneTransforms.data(), boneCount ); } std::pair EveChildMesh::GetMorphTargets( MorphTargetAnimationFilter filter ) diff --git a/trinity/Eve/SpaceObject/Children/EveChildMesh.h b/trinity/Eve/SpaceObject/Children/EveChildMesh.h index 54d5e1b52..c235ff7f5 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildMesh.h +++ b/trinity/Eve/SpaceObject/Children/EveChildMesh.h @@ -110,6 +110,7 @@ BLUE_CLASS( EveChildMesh ) : // ITr2Renderable virtual bool HasTransparentBatches(); virtual void GetBatches( ITriRenderBatchAccumulator * batches, TriBatchType batchType, const Tr2PerObjectData* perObjectData, Tr2RenderReason reason = TR2RENDERREASON_NORMAL ); + void GetBatchesFromOverlayVector( ITriRenderBatchAccumulator * batches, const Tr2PerObjectData* perObjectData, TriBatchType batchType ); virtual float GetSortValue(); virtual Tr2PerObjectData* GetPerObjectData( ITriRenderBatchAccumulator * accumulator ); virtual bool IsVisible( const EveUpdateContext& updateContext ) const override; @@ -167,6 +168,14 @@ BLUE_CLASS( EveChildMesh ) : void SetCastShadow( bool castShadow ); void SetMinScreenSize( float minScreenSize ); + void AddOverlayEffect( EveMeshOverlayEffectPtr newOverlayEffect ); + void RemoveOverlayEffect( EveMeshOverlayEffectPtr overlayEffectToRemove ); + EveMeshOverlayEffectPtr GetOverlayEffectByName( const char* name ) const; + const PEveMeshOverlayEffectVector& GetOverlayEffects() const + { + return m_overlayEffects; + } + Tr2GrannyAnimation* GetAnimationController() const override; void SetAnimationController( Tr2GrannyAnimation * animation ); @@ -201,6 +210,7 @@ BLUE_CLASS( EveChildMesh ) : bool PrepareMorphBuffers( Tr2RenderContext & renderContext ); std::pair GetBoneTransforms() const; + std::pair GetRestPoseBoneTransforms() const; const std::pair GetMeshBindingIndices() const; std::pair GetMorphTargets( MorphTargetAnimationFilter filter ); void UpdateMorphAnimationBuffer(); @@ -213,9 +223,20 @@ BLUE_CLASS( EveChildMesh ) : Tr2InstancedMeshPtr m_instancedMesh; // Cached downcast of m_mesh IEveSpaceObject2::ParentData m_parentData; + const PEveMeshOverlayEffectVector* m_parentOverlayEffects = nullptr; + // overlay effects owned by this child mesh, rendered underneath any inherited parent overlays + PEveMeshOverlayEffectVector m_overlayEffects; + Be::Time m_lastOverlayUpdateTime = 0; + Tr2Lod m_overlayUpdateLod = TR2_LOD_UNSPECIFIED; + std::vector m_overlayMeshAreaBlocks[EveMeshOverlayEffect::TYPE_COUNT]; + bool m_overlayAreaBlocksBuilt = false; + void RebuildOverlayAreaBlocks(); + PIEveChildTransformModifierVector m_transformModifiers; Tr2GrannyAnimationPtr m_animationUpdater; std::unique_ptr m_meshBinding; + // identity skin matrices for skinned meshes with no animation (rest pose) + mutable std::vector m_restPoseBoneTransforms; Tr2Lod m_lowestLodVisible; @@ -238,6 +259,7 @@ BLUE_CLASS( EveChildMesh ) : CcpMath::Sphere m_worldBoundingSphere; // bounding sphere in world space bool m_display; + bool m_inheritOverlayEffects; bool m_isVisible; bool m_instancesVisible; bool m_castShadow; diff --git a/trinity/Eve/SpaceObject/Children/EveChildMesh_Blue.cpp b/trinity/Eve/SpaceObject/Children/EveChildMesh_Blue.cpp index de99e6f6a..1e397a76b 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildMesh_Blue.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildMesh_Blue.cpp @@ -23,6 +23,8 @@ const Be::ClassInfo* EveChildMesh::ExposeToBlue() MAP_ATTRIBUTE( "name", m_name, "", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "display", m_display, "", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) + MAP_ATTRIBUTE( "inheritOverlayEffects", m_inheritOverlayEffects, "If true, the parent space object's overlay effects (e.g. cloak) also render over this child mesh.", Be::READWRITE | Be::PERSIST ) + MAP_ATTRIBUTE( "overlayEffects", m_overlayEffects, "Overlay effects owned by this child mesh. Rendered over the child's mesh areas, underneath any inherited parent overlay effects.", Be::READ | Be::PERSIST ) MAP_ATTRIBUTE( "castShadow", m_castShadow, "", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) MAP_ATTRIBUTE( "updateAnimation", m_updateAnimation, "Should the object update its animation updater every frame", Be::READWRITE | Be::PERSIST ) MAP_ATTRIBUTE( "mesh", m_mesh, "", Be::READWRITE | Be::PERSIST | Be::NOTIFY ) diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2.cpp b/trinity/Eve/SpaceObject/EveSpaceObject2.cpp index c580579b1..3a9535e43 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2.cpp +++ b/trinity/Eve/SpaceObject/EveSpaceObject2.cpp @@ -1214,7 +1214,7 @@ void EveSpaceObject2::GetBatchesFromOverlayVector( ITriRenderBatchAccumulator* b } TriGeometryRes* geomRes = mesh->GetGeometryResource(); - if( !geomRes->IsGood() ) + if( !geomRes || !geomRes->IsGood() ) { return; } @@ -1249,39 +1249,7 @@ void EveSpaceObject2::GetBatchesFromOverlayVector( ITriRenderBatchAccumulator* b } // second the effects - for( auto it = m_overlayEffects.begin(); it != m_overlayEffects.end(); ++it ) - { - EveMeshOverlayEffectPtr overlay = *it; - bool success = false; - const PTr2EffectVector& effects = overlay->GetEffects( batchType, success ); - if( success ) - { - EveMeshOverlayEffect::OverlayType overlayType = overlay->GetType( batchType ); - for( auto eff = effects.begin(); eff != effects.end(); ++eff ) - { - Tr2EffectPtr effect = *eff; - - // add all mesh area blocks - for( auto& areaBlock : m_overlayMeshAreaBlocks[overlayType] ) - { - if( auto primCount = GetPrimitiveCount( *lod, areaBlock.m_startIndex, areaBlock.m_count ) ) - { - Tr2RenderBatch batch; - batch.SetMaterial( effect ); - batch.SetGeometry( lod->m_mesh->m_vertexDeclarationHandle, lod->m_vertexAllocation, lod->m_indexAllocation ); - batch.SetPerObjectData( perObjectData ); - batch.SetDrawIndexedInstanced( - primCount * 3, - 1, - lod->m_indexAllocation.GetStartIndex() + lod->m_areas[areaBlock.m_startIndex].m_firstIndex, - lod->m_vertexAllocation.GetOffset() / lod->m_vertexAllocation.GetStride(), - 0 ); - batches->Commit( batch ); - } - } - } - } - } + EmitOverlayBatches( batches, perObjectData, batchType, m_overlayEffects, m_overlayMeshAreaBlocks, *lod ); } const Matrix* EveSpaceObject2::GetLocatorTransform( LocatorType lt, unsigned int lix ) @@ -2079,15 +2047,7 @@ void EveSpaceObject2::RebuildCachedData( BlueAsyncRes* p ) // build list of block areas we need to render for overlay effects if( m_mesh ) { - m_mesh->CollectAreaBlocks( m_overlayMeshAreaBlocks[EveMeshOverlayEffect::TYPE_ALL], TRIBATCHTYPE_OPAQUE ); - m_mesh->CollectAreaBlocks( m_overlayMeshAreaBlocks[EveMeshOverlayEffect::TYPE_ALL], TRIBATCHTYPE_TRANSPARENT ); - m_mesh->CollectAreaBlocks( m_overlayMeshAreaBlocks[EveMeshOverlayEffect::TYPE_ALL], TRIBATCHTYPE_DECAL ); - m_mesh->CollectAreaBlocks( m_overlayMeshAreaBlocks[EveMeshOverlayEffect::TYPE_OPAQUEONLY], TRIBATCHTYPE_OPAQUE ); - // this list is too long will hold one element for each mesharea at least... Optimize! - for( int i = 0; i < EveMeshOverlayEffect::TYPE_COUNT; ++i ) - { - TriRenderBatchAreaBlock::Optimize( m_overlayMeshAreaBlocks[i] ); - } + CollectOverlayAreaBlocks( m_mesh, m_overlayMeshAreaBlocks ); m_mesh->CollectAreaBlocksWithSharedMaterials( m_shadowMeshOpaqueAreas, TRIBATCHTYPE_OPAQUE ); for( auto& collector : m_shadowMeshOpaqueAreas ) diff --git a/trinity/Eve/SpaceObject/EveSpaceObject2.h b/trinity/Eve/SpaceObject/EveSpaceObject2.h index 19ded272d..4e0a9b521 100644 --- a/trinity/Eve/SpaceObject/EveSpaceObject2.h +++ b/trinity/Eve/SpaceObject/EveSpaceObject2.h @@ -448,6 +448,10 @@ BLUE_CLASS( EveSpaceObject2 ) : void AddOverlayEffect( EveMeshOverlayEffectPtr newOverlayEffect ); void RemoveOverlayEffect( EveMeshOverlayEffectPtr newOverlayEffect ); EveMeshOverlayEffectPtr GetOverlayEffectByName( const char* name ) const; + const PEveMeshOverlayEffectVector& GetOverlayEffects() const + { + return m_overlayEffects; + } void AddLocatorSet( const char* name, const Locator* locators, size_t locatorCount ); Vector3 GetDamageLocator( uint32_t index ) const; Vector3 GetDamageLocatorDirectionLocal( uint32_t index ) const;