From 0c76916bddf1c17e8d9245a288bd69d0b85b3e1a Mon Sep 17 00:00:00 2001 From: breki Date: Wed, 26 Aug 2026 16:21:14 +0000 Subject: [PATCH 1/5] actual move instances when setting transform in modular modifer --- trinity/Eve/EveInstancedMeshManager.cpp | 13 ++++ trinity/Eve/EveInstancedMeshManager.h | 3 + .../Children/EveChildInstancedMeshes.cpp | 67 +++++++++++++------ .../Children/EveChildInstancedMeshes.h | 2 + .../Children/EveChildInstancedMeshes_Blue.cpp | 8 +++ .../Children/EveModularObjectModifier.cpp | 4 ++ 6 files changed, 76 insertions(+), 21 deletions(-) diff --git a/trinity/Eve/EveInstancedMeshManager.cpp b/trinity/Eve/EveInstancedMeshManager.cpp index 94ac071b6..904a39500 100644 --- a/trinity/Eve/EveInstancedMeshManager.cpp +++ b/trinity/Eve/EveInstancedMeshManager.cpp @@ -1046,3 +1046,16 @@ bool EveInstancedMeshManager::InstanceFlags::operator!=( const InstanceFlags& ot { return m_flags != other.m_flags; } + +Matrix EveInstancedMeshManager::StaticPerInstanceData::ToMatrix() const +{ + return Transpose( Matrix( + worldTransform[0].x, worldTransform[0].y, worldTransform[0].z, worldTransform[0].w, worldTransform[1].x, worldTransform[1].y, worldTransform[1].z, worldTransform[1].w, worldTransform[2].x, worldTransform[2].y, worldTransform[2].z, worldTransform[2].w, 0.f, 0.f, 0.f, 1.f ) ); +} + +void EveInstancedMeshManager::StaticPerInstanceData::SetTransform( const Matrix& m ) +{ + worldTransform[0] = Vector4( m._11, m._21, m._31, m._41 ); + worldTransform[1] = Vector4( m._12, m._22, m._32, m._42 ); + worldTransform[2] = Vector4( m._13, m._23, m._33, m._43 ); +} diff --git a/trinity/Eve/EveInstancedMeshManager.h b/trinity/Eve/EveInstancedMeshManager.h index e6485245f..fe4bbefa3 100644 --- a/trinity/Eve/EveInstancedMeshManager.h +++ b/trinity/Eve/EveInstancedMeshManager.h @@ -36,6 +36,9 @@ class EveInstancedMeshManager { Vector4 worldTransform[3]; uint32_t sphereIndex = 0; + + Matrix ToMatrix() const; + void SetTransform( const Matrix& m ); }; template diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp index 6d6d18545..1281460cd 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp @@ -449,10 +449,7 @@ void EveChildInstancedMeshes::AddMesh( for( size_t i = 0; i < count; ++i ) { EveInstancedMeshManager::StaticPerInstanceData instanceData; - auto& mat = instanceTransforms[i]; - instanceData.worldTransform[0] = Vector4( mat._11, mat._21, mat._31, mat._41 ); - instanceData.worldTransform[1] = Vector4( mat._12, mat._22, mat._32, mat._42 ); - instanceData.worldTransform[2] = Vector4( mat._13, mat._23, mat._33, mat._43 ); + instanceData.SetTransform( instanceTransforms[i] ); instanceData.sphereIndex = static_cast( existingCount + i ); mesh.instances.push_back( instanceData ); mesh.partTags.push_back( partTag ); @@ -493,10 +490,7 @@ void EveChildInstancedMeshes::AddMesh( for( size_t i = 0; i < count; ++i ) { EveInstancedMeshManager::StaticPerInstanceData instanceData; - auto& mat = instanceTransforms[i]; - instanceData.worldTransform[0] = Vector4( mat._11, mat._21, mat._31, mat._41 ); - instanceData.worldTransform[1] = Vector4( mat._12, mat._22, mat._32, mat._42 ); - instanceData.worldTransform[2] = Vector4( mat._13, mat._23, mat._33, mat._43 ); + instanceData.SetTransform( instanceTransforms[i] ); instanceData.sphereIndex = static_cast( i ); mesh.instances.push_back( instanceData ); mesh.partTags.push_back( partTag ); @@ -594,6 +588,23 @@ void EveChildInstancedMeshes::RemoveInstancesByPartTag( EveSpaceObjectChild::Par } } } +bool EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ) +{ + Matrix m = TransformationMatrix( scale, rotation, translation ); + bool changed = false; + for( auto& mesh : m_meshes ) + { + for( int i = 0; i < mesh.instances.size(); i++ ) + { + if( mesh.partTags[i] == partTag ) + { + mesh.instances[i].SetTransform( m ); + changed = true; + } + } + } + return changed; +} void EveChildInstancedMeshes::ReleaseCachedData( BlueAsyncRes* p ) { @@ -769,6 +780,32 @@ uint32_t EveChildInstancedMeshes::GetMeshCount() const { return static_cast( m_meshes.size() ); } +BluePy EveChildInstancedMeshes::GetInstancesTransforms( uint32_t meshId ) const +{ + if( meshId >= m_meshes.size() ) + { + PyErr_SetString( PyExc_IndexError, "Mesh index out of range" ); + return {}; + } + + auto& mesh = m_meshes[meshId]; + BluePy result( PyTuple_New( mesh.instances.size() ) ); + int i = 0; + for( auto& instance : mesh.instances ) + { + Vector3 scale, translation; + Quaternion rotation; + Decompose( scale, rotation, translation, instance.ToMatrix() ); + + PyObject* transform = PyTuple_New( 3 ); + PyTuple_SetItem( transform, 0, ToPython( translation ) ); + PyTuple_SetItem( transform, 1, ToPython( rotation ) ); + PyTuple_SetItem( transform, 2, ToPython( scale ) ); + PyTuple_SetItem( result, i++, transform ); + } + + return result; +} BluePy EveChildInstancedMeshes::GetMeshInfo( uint32_t meshId ) const { @@ -1040,19 +1077,7 @@ void EveChildInstancedMeshes::UpdateOverlayInstanceData( const EveSpaceObjectVSD 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 local = mesh.instances[i].ToMatrix(); Matrix worldTransform = Transpose( local * m_worldTransform ); Matrix worldTransformLast = Transpose( local * prevWorldTransform ); diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h index ddb9f4673..f9044420b 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h @@ -96,11 +96,13 @@ BLUE_CLASS( EveChildInstancedMeshes ) : EveSpaceObjectChild::PartTag partTag = EveSpaceObjectChild::NO_PART_TAG ); void RemoveInstancesByPartTag( EveSpaceObjectChild::PartTag partTag ); + bool SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ); BluePy GetSofSourceLocator( uint32_t areaId ) const; uint32_t GetMeshCount() const; BluePy GetMeshInfo( uint32_t meshId ) const; BluePy GetAreaInfo( uint32_t meshId, uint32_t areaId ) const; + BluePy GetInstancesTransforms( uint32_t meshId ) const; BluePy GetMeshDisplay( uint32_t meshId ) const; BluePy SetMeshDisplay( uint32_t meshId, bool display ); BluePy GetMeshInheritOverlayEffects( uint32_t meshId ) const; diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp index 4f1bace57..63958610e 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes_Blue.cpp @@ -47,6 +47,14 @@ const Be::ClassInfo* EveChildInstancedMeshes::ExposeToBlue() ":param meshId: Index of the mesh to query\n" ":param areaId: Index of the area to query\n" ":rtype: (trinity.Tr2Effect, int, int, int)" ) + MAP_METHOD_AND_WRAP( + "GetInstancesTransforms", + GetInstancesTransforms, + "Returns the object-local transform of each instance of an instanced mesh, decomposed\n" + "into translation, rotation and scale. Returns one (translation, rotation, scale)\n" + "tuple per instance\n\n" + ":param meshId: Index of the mesh to query\n" + ":rtype: (((float, float, float), (float, float, float, float), (float, float, float)), ...)" ) MAP_METHOD_AND_WRAP( "GetMeshDisplay", GetMeshDisplay, diff --git a/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp b/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp index 3a86747f2..9214693e2 100644 --- a/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp +++ b/trinity/Eve/SpaceObject/Children/EveModularObjectModifier.cpp @@ -222,6 +222,10 @@ BlueStdResult EveModularObjectModifier::SetTransform( EveSpaceObjectChild::PartT { child->Setup( &scale, &rotation, &position, Tr2Lod::TR2_LOD_LOW ); } + if( EveChildInstancedMeshesPtr instancedMeshes = BlueCastPtr( child ) ) + { + instancedMeshes->SetInstanceTransformByPartTag( partId, position, rotation, scale ); + } } m_object->InvalidateMergedLocators( LocatorInvalidationReason::PartMoved ); return BlueStdResultType::BLUE_STD_RESULT_OK; From dd4db3d41b54d7d3a4a1517392a31b0f0aba1e4a Mon Sep 17 00:00:00 2001 From: breki Date: Wed, 26 Aug 2026 16:21:31 +0000 Subject: [PATCH 2/5] fix graphite but where evechildpartdata didnt expose name --- trinity/Eve/SpaceObject/Children/EveChildPartData_Blue.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/trinity/Eve/SpaceObject/Children/EveChildPartData_Blue.cpp b/trinity/Eve/SpaceObject/Children/EveChildPartData_Blue.cpp index 15f1e2961..f84b5444b 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildPartData_Blue.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildPartData_Blue.cpp @@ -11,6 +11,5 @@ const Be::ClassInfo* EveChildPartData::ExposeToBlue() EXPOSURE_BEGIN( EveChildPartData, "Persistent state of a modular space object (per-part transforms and bounds). Edit through EveModularObjectModifier" ) MAP_INTERFACE( EveSpaceObjectChild ); MAP_INTERFACE( IEveSpaceObjectChild ) - MAP_ATTRIBUTE( "name", m_name, "Name of the space object child", Be::READWRITE | Be::PERSIST ) - EXPOSURE_END() + EXPOSURE_CHAINTO( EveSpaceObjectChild ) } From cf5d25703c8cfbdac3332b13829463306804e817 Mon Sep 17 00:00:00 2001 From: Breki Ingibjargarson Date: Thu, 27 Aug 2026 09:00:48 +0000 Subject: [PATCH 3/5] size_t instead of int Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp index 1281460cd..569970621 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp @@ -594,7 +594,7 @@ bool EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, co bool changed = false; for( auto& mesh : m_meshes ) { - for( int i = 0; i < mesh.instances.size(); i++ ) + for( size_t i = 0; i < mesh.instances.size(); ++i ) { if( mesh.partTags[i] == partTag ) { From 044c4359e0c5332d46e72120a8a488793ba9f7ab Mon Sep 17 00:00:00 2001 From: breki Date: Thu, 27 Aug 2026 10:12:23 +0000 Subject: [PATCH 4/5] remove return value for setinstancetransformbyparttag --- trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp | 5 +---- trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp index 569970621..25ee6e023 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp @@ -588,10 +588,9 @@ void EveChildInstancedMeshes::RemoveInstancesByPartTag( EveSpaceObjectChild::Par } } } -bool EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ) +void EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ) { Matrix m = TransformationMatrix( scale, rotation, translation ); - bool changed = false; for( auto& mesh : m_meshes ) { for( size_t i = 0; i < mesh.instances.size(); ++i ) @@ -599,11 +598,9 @@ bool EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, co if( mesh.partTags[i] == partTag ) { mesh.instances[i].SetTransform( m ); - changed = true; } } } - return changed; } void EveChildInstancedMeshes::ReleaseCachedData( BlueAsyncRes* p ) diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h index f9044420b..64be036d9 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.h @@ -96,7 +96,7 @@ BLUE_CLASS( EveChildInstancedMeshes ) : EveSpaceObjectChild::PartTag partTag = EveSpaceObjectChild::NO_PART_TAG ); void RemoveInstancesByPartTag( EveSpaceObjectChild::PartTag partTag ); - bool SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ); + void SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ); BluePy GetSofSourceLocator( uint32_t areaId ) const; uint32_t GetMeshCount() const; From 4d1835e911337ab39862d5331b1eb3f6fbfa5e31 Mon Sep 17 00:00:00 2001 From: breki Date: Thu, 27 Aug 2026 10:49:07 +0000 Subject: [PATCH 5/5] change instances Vector4[3] to Float4x3 to increase readability --- trinity/Eve/EveInstancedMeshManager.cpp | 13 ---------- trinity/Eve/EveInstancedMeshManager.h | 15 +++++------ .../Children/EveChildInstancedMeshes.cpp | 25 ++++++++++--------- 3 files changed, 19 insertions(+), 34 deletions(-) diff --git a/trinity/Eve/EveInstancedMeshManager.cpp b/trinity/Eve/EveInstancedMeshManager.cpp index 904a39500..94ac071b6 100644 --- a/trinity/Eve/EveInstancedMeshManager.cpp +++ b/trinity/Eve/EveInstancedMeshManager.cpp @@ -1046,16 +1046,3 @@ bool EveInstancedMeshManager::InstanceFlags::operator!=( const InstanceFlags& ot { return m_flags != other.m_flags; } - -Matrix EveInstancedMeshManager::StaticPerInstanceData::ToMatrix() const -{ - return Transpose( Matrix( - worldTransform[0].x, worldTransform[0].y, worldTransform[0].z, worldTransform[0].w, worldTransform[1].x, worldTransform[1].y, worldTransform[1].z, worldTransform[1].w, worldTransform[2].x, worldTransform[2].y, worldTransform[2].z, worldTransform[2].w, 0.f, 0.f, 0.f, 1.f ) ); -} - -void EveInstancedMeshManager::StaticPerInstanceData::SetTransform( const Matrix& m ) -{ - worldTransform[0] = Vector4( m._11, m._21, m._31, m._41 ); - worldTransform[1] = Vector4( m._12, m._22, m._32, m._42 ); - worldTransform[2] = Vector4( m._13, m._23, m._33, m._43 ); -} diff --git a/trinity/Eve/EveInstancedMeshManager.h b/trinity/Eve/EveInstancedMeshManager.h index fe4bbefa3..5125ea925 100644 --- a/trinity/Eve/EveInstancedMeshManager.h +++ b/trinity/Eve/EveInstancedMeshManager.h @@ -27,18 +27,15 @@ class EveInstancedMeshManager struct DynamicPerInstanceData { - Vector4 worldTransform[3]; - Vector4 prevWorldTransform[3]; + Float4x3 worldTransform; + Float4x3 prevWorldTransform; uint32_t sphereIndex = 0; }; struct StaticPerInstanceData { - Vector4 worldTransform[3]; + Float4x3 worldTransform; uint32_t sphereIndex = 0; - - Matrix ToMatrix() const; - void SetTransform( const Matrix& m ); }; template @@ -145,14 +142,14 @@ class EveInstancedMeshManager private: struct StaticPerInstanceBufferElement { - Vector4 worldTransform[3]; + Float4x3 worldTransform; uint32_t perObjectDataIndex = 0; }; struct DynamicPerInstanceBufferElement { - Vector4 worldTransform[3]; - Vector4 prevWorldTransform[3]; + Float4x3 worldTransform; + Float4x3 prevWorldTransform; uint32_t perObjectDataIndex = 0; }; diff --git a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp index 25ee6e023..9880eb4d1 100644 --- a/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp +++ b/trinity/Eve/SpaceObject/Children/EveChildInstancedMeshes.cpp @@ -145,9 +145,8 @@ void EveChildInstancedMeshes::PushRtGeometry( Tr2RaytracingManager& rtManager ) } } - XMMATRIX m = *reinterpret_cast( instanceTransform.worldTransform ); - m.r[3] = XMVectorSet( 0, 0, 0, 1 ); - m = XMMatrixMultiply( XMMatrixTranspose( m ), m_worldTransform ); + XMMATRIX m = Matrix( instanceTransform.worldTransform ); + m = XMMatrixMultiply( m, m_worldTransform ); mesh.rtMeshes[lodIndex].instanceWorldTransforms.push_back( Float4x3( Matrix( m ) ) ); } @@ -275,10 +274,11 @@ void EveChildInstancedMeshes::UpdateAsyncronous( const EveUpdateContext& updateC for( const auto& instance : mesh.instances ) { - Vector3 position = Vector3( instance.worldTransform[0].w, instance.worldTransform[1].w, instance.worldTransform[2].w ); - float scale = std::sqrtf( std::max( { LengthSq( instance.worldTransform[0].GetXYZ() ), - LengthSq( instance.worldTransform[1].GetXYZ() ), - LengthSq( instance.worldTransform[2].GetXYZ() ) } ) ); + Matrix m = instance.worldTransform; + Vector3 position = m.GetTranslation(); + float scale = std::sqrtf( std::max( { LengthSq( Vector3( m._11, m._12, m._13 ) ), + LengthSq( Vector3( m._21, m._22, m._23 ) ), + LengthSq( Vector3( m._31, m._32, m._33 ) ) } ) ); position = TransformCoord( position, m_worldTransform ); scale *= worldScale; mesh.instanceSpheres[&instance - mesh.instances.data()] = CcpMath::Sphere( position, radius * scale ); @@ -449,7 +449,7 @@ void EveChildInstancedMeshes::AddMesh( for( size_t i = 0; i < count; ++i ) { EveInstancedMeshManager::StaticPerInstanceData instanceData; - instanceData.SetTransform( instanceTransforms[i] ); + instanceData.worldTransform = Float4x3( instanceTransforms[i] ); instanceData.sphereIndex = static_cast( existingCount + i ); mesh.instances.push_back( instanceData ); mesh.partTags.push_back( partTag ); @@ -490,7 +490,7 @@ void EveChildInstancedMeshes::AddMesh( for( size_t i = 0; i < count; ++i ) { EveInstancedMeshManager::StaticPerInstanceData instanceData; - instanceData.SetTransform( instanceTransforms[i] ); + instanceData.worldTransform = Float4x3( instanceTransforms[i] ); instanceData.sphereIndex = static_cast( i ); mesh.instances.push_back( instanceData ); mesh.partTags.push_back( partTag ); @@ -591,13 +591,14 @@ void EveChildInstancedMeshes::RemoveInstancesByPartTag( EveSpaceObjectChild::Par void EveChildInstancedMeshes::SetInstanceTransformByPartTag( PartTag partTag, const Vector3& translation, const Quaternion& rotation, Vector3 scale ) { Matrix m = TransformationMatrix( scale, rotation, translation ); + const Float4x3 packedTransform( m ); for( auto& mesh : m_meshes ) { for( size_t i = 0; i < mesh.instances.size(); ++i ) { if( mesh.partTags[i] == partTag ) { - mesh.instances[i].SetTransform( m ); + mesh.instances[i].worldTransform = packedTransform; } } } @@ -792,7 +793,7 @@ BluePy EveChildInstancedMeshes::GetInstancesTransforms( uint32_t meshId ) const { Vector3 scale, translation; Quaternion rotation; - Decompose( scale, rotation, translation, instance.ToMatrix() ); + Decompose( scale, rotation, translation, instance.worldTransform ); PyObject* transform = PyTuple_New( 3 ); PyTuple_SetItem( transform, 0, ToPython( translation ) ); @@ -1074,7 +1075,7 @@ void EveChildInstancedMeshes::UpdateOverlayInstanceData( const EveSpaceObjectVSD const auto& wt = mesh.instances[i].worldTransform; OverlayInstancePod& pod = ( *mesh.overlayPods )[i]; - Matrix local = mesh.instances[i].ToMatrix(); + Matrix local = mesh.instances[i].worldTransform; Matrix worldTransform = Transpose( local * m_worldTransform ); Matrix worldTransformLast = Transpose( local * prevWorldTransform );