@@ -198,15 +198,36 @@ namespace nexo::editor {
198198 {
199199 const glm::mat4 parentWorldMatrix = getEntityParentWorldMatrix (entity);
200200
201- // Calculate local matrix by inverting parent world matrix and multiplying by entity's world matrix
202- const glm::mat4 localMatrix = glm::inverse (parentWorldMatrix) * worldMatrix;
201+ // Calculate what the local matrix should be to achieve the desired world matrix
202+ const glm::mat4 desiredLocalMatrix = glm::inverse (parentWorldMatrix) * worldMatrix;
203203
204+ // Decompose the desired local matrix back to local transform properties
204205 glm::vec3 skew;
205206 glm::vec4 perspective;
206- glm::decompose (localMatrix , transform.size , transform.quat , transform.pos , skew, perspective);
207+ glm::decompose (desiredLocalMatrix , transform.size , transform.quat , transform.pos , skew, perspective);
207208
208- transform.quat = glm::normalize (transform.quat );
209+ transform.quat = glm::normalize (transform.quat );
209210 transform.worldMatrix = worldMatrix;
211+ transform.dirty = true ;
212+ Application::getInstance ().markHierarchyDirty (entity);
213+ }
214+
215+ static glm::mat4 getCurrentWorldMatrix (const ecs::Entity entity)
216+ {
217+ const auto & coord = nexo::Application::m_coordinator;
218+ const auto transform = coord->tryGetComponent <components::TransformComponent>(entity);
219+
220+ if (!transform) return glm::mat4 (1 .0f );
221+
222+ // If the transform is dirty, we need to compute what the world matrix would be
223+ if (transform->get ().dirty ) {
224+ const glm::mat4 parentWorldMatrix = getEntityParentWorldMatrix (entity);
225+ const glm::mat4 localMatrix = calculateWorldMatrix (transform->get ());
226+ return parentWorldMatrix * localMatrix;
227+ }
228+
229+ // Use the cached world matrix if it's up to date
230+ return transform->get ().worldMatrix ;
210231 }
211232
212233 float * EditorScene::getSnapSettingsForOperation (const ImGuizmo::OPERATION operation)
@@ -231,26 +252,6 @@ namespace nexo::editor {
231252 }
232253 }
233254
234- void EditorScene::applyTransformToEntities (const ecs::Entity sourceEntity, const glm::mat4& oldWorldMatrix,
235- const glm::mat4& newWorldMatrix, const std::vector<int >& targetEntities)
236- {
237- const auto & coord = Application::m_coordinator;
238-
239- const glm::mat4 deltaMatrix = newWorldMatrix * glm::inverse (oldWorldMatrix);
240-
241- // Apply to all selected entities except the source
242- for (const auto & entity : targetEntities) {
243- if (entity == static_cast <int >(sourceEntity)) continue ;
244-
245- const auto entityTransform = coord->tryGetComponent <components::TransformComponent>(entity);
246- if (!entityTransform) continue ;
247-
248- // Apply world space delta and convert back to local space
249- glm::mat4 newEntityWorldMatrix = deltaMatrix * entityTransform->get ().worldMatrix ;
250- updateLocalTransformFromWorld (entityTransform->get (), newEntityWorldMatrix, entity);
251- }
252- }
253-
254255 /* *
255256 * @brief Compares two TransformComponent states to determine if any properties have changed.
256257 *
@@ -359,18 +360,14 @@ namespace nexo::editor {
359360
360361 // 1) M₀ = parentWorld * T(pos) * R(quat) * S(size)
361362 const glm::mat4 parentWorld = getEntityParentWorldMatrix (primaryEntity);
362- const glm::mat4 Tpos = glm::translate (glm::mat4 (1 .0f ), primaryTransform->get ().pos );
363- const glm::mat4 Rrot = glm::toMat4 (primaryTransform->get ().quat );
364- const glm::mat4 Sscale = glm::scale (glm::mat4 (1 .0f ), primaryTransform->get ().size );
365- const glm::mat4 M0 = parentWorld * Tpos * Rrot * Sscale;
363+ const glm::mat4 localMatrix = calculateWorldMatrix (primaryTransform->get ());
364+ const glm::mat4 M0 = parentWorld * localMatrix;
366365
367- // 2) “ centroid offset” = T(centroidLocal)
366+ // 2) Apply centroid offset for gizmo display
368367 const glm::mat4 C_offset = glm::translate (glm::mat4 (1 .0f ), primaryTransform->get ().localCenter );
369-
370- // 3) M1 = M0 * C_offset
371368 glm::mat4 worldTransformMatrix = M0 * C_offset;
372369
373- // (We’ll need “M₀” again after manipulation for decomposing back.)
370+ // Store original for delta calculation
374371 const glm::mat4 originalWorldMatrix_ModelOrigin = M0;
375372
376373 if (!ImGuizmo::IsUsing ()) s_lastOperation = getActiveGuizmoOperation ();
@@ -386,30 +383,30 @@ namespace nexo::editor {
386383 const bool isUsingGizmo = ImGuizmo::IsUsing ();
387384
388385 if (isUsingGizmo) {
389- // Disable camera movement during manipulation
390386 camera.active = false ;
391387
392- const glm::mat4 newWorldMatrix_Centroid = worldTransformMatrix;
393- const glm::mat4 invCentroidOffset = glm::inverse (C_offset);
388+ const glm::mat4 newWorldMatrix_Centroid = worldTransformMatrix;
389+ const glm::mat4 invCentroidOffset = glm::inverse (C_offset);
394390 const glm::mat4 newWorldMatrix_ModelOrigin = newWorldMatrix_Centroid * invCentroidOffset;
395391
396- // Update the primary entity's local transform based on the new world transform
392+ // Update local transform properties (NOT world matrix)
397393 updateLocalTransformFromWorld (primaryTransform->get (), newWorldMatrix_ModelOrigin, primaryEntity);
398394
395+ // Apply same transformation to other selected entities
399396 const glm::mat4 deltaMatrix = newWorldMatrix_ModelOrigin * glm::inverse (originalWorldMatrix_ModelOrigin);
400397
401398 for (const auto entity : selectedEntities) {
402399 if (static_cast <unsigned int >(entity) == primaryEntity) continue ;
400+
403401 const auto tComp = coord->tryGetComponent <components::TransformComponent>(entity);
404402 if (!tComp) continue ;
405403
406- // “OtherEntity_world₀” = tComp->worldMatrix
407- glm::mat4 otherWorldMatrix_0 = tComp->get ().worldMatrix ;
408- // “OtherEntity_world₁” = deltaMatrix * otherWorldMatrix_0
409- glm::mat4 otherWorldMatrix_1 = deltaMatrix * otherWorldMatrix_0;
404+ // Get the current world matrix for this entity
405+ const glm::mat4 otherCurrentWorld = getCurrentWorldMatrix (entity);
406+ const glm::mat4 otherNewWorld = deltaMatrix * otherCurrentWorld;
410407
411- // Now convert that new world matrix back to local space of “entity”:
412- updateLocalTransformFromWorld (tComp->get (), otherWorldMatrix_1 , entity);
408+ // Update local properties only
409+ updateLocalTransformFromWorld (tComp->get (), otherNewWorld , entity);
413410 }
414411 } else if (s_wasUsingGizmo) {
415412 // Re-enable camera when done
0 commit comments