diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index c8a7b7b1f..c06dfe75e 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -28,6 +28,19 @@ SplineModel::~SplineModel() Allocator::Free(_deformationBufferData); } +Quaternion SplineModel::GetPreRotation() const +{ + return _preRotation; +} + +void SplineModel::SetPreRotation(const Quaternion& value) +{ + if (_preRotation == value) + return; + _preRotation = value; + OnSplineUpdated(); +} + float SplineModel::GetQuality() const { return _quality; @@ -108,12 +121,11 @@ void SplineModel::OnSplineUpdated() const int32 segments = keyframes.Count() - 1; const int32 chunksPerSegment = Math::Clamp(Math::CeilToInt(SPLINE_RESOLUTION * _quality), 2, 1024); const float chunksPerSegmentInv = 1.0f / (float)chunksPerSegment; - const Transform splineTransform = _spline->GetTransform(); + const Transform splineTransform = GetTransform(); _instances.Resize(segments, false); - BoundingBox localModelBounds; + BoundingBox localModelBounds(Vector3::Maximum, Vector3::Minimum); { auto& meshes = Model->LODs[0].Meshes; - Vector3 tmp, min = Vector3::Maximum, max = Vector3::Minimum; Vector3 corners[8]; for (int32 j = 0; j < meshes.Count(); j++) { @@ -122,12 +134,9 @@ void SplineModel::OnSplineUpdated() for (int32 i = 0; i < 8; i++) { - //Vector3::Transform(corners[i], localTransform, tmp); - //_localTransform.LocalToWorld(corners[i], tmp); - - // Transform mesh corner using local spline model transformation but use double-precision to prevent issues when rotating model - tmp = corners[i] * _localTransform.Scale; - double rotation[4] = { (double)_localTransform.Orientation.X, (double)_localTransform.Orientation.Y, (double)_localTransform.Orientation.Z, (double)_localTransform.Orientation.W }; + // Transform mesh corner using pre-transform but use double-precision to prevent issues when rotating model + Vector3 tmp = corners[i]; + double rotation[4] = { (double)_preRotation.X, (double)_preRotation.Y, (double)_preRotation.Z, (double)_preRotation.W }; const double length = sqrt(rotation[0] * rotation[0] + rotation[1] * rotation[1] + rotation[2] * rotation[2] + rotation[3] * rotation[3]); const double inv = 1.0 / length; rotation[0] *= inv; @@ -151,13 +160,11 @@ void SplineModel::OnSplineUpdated() (float)(pos[0] * (1.0 - yy - zz) + pos[1] * (xy - wz) + pos[2] * (xz + wy)), (float)(pos[0] * (xy + wz) + pos[1] * (1.0 - xx - zz) + pos[2] * (yz - wx)), (float)(pos[0] * (xz - wy) + pos[1] * (yz + wx) + pos[2] * (1.0 - xx - yy))); - Vector3::Add(tmp, _localTransform.Translation, tmp); - min = Vector3::Min(min, tmp); - max = Vector3::Max(max, tmp); + localModelBounds.Minimum = Vector3::Min(localModelBounds.Minimum, tmp); + localModelBounds.Maximum = Vector3::Max(localModelBounds.Maximum, tmp); } } - localModelBounds = BoundingBox(min, max); } _meshMinZ = localModelBounds.Minimum.Z; _meshMaxZ = localModelBounds.Maximum.Z; @@ -185,9 +192,7 @@ void SplineModel::OnSplineUpdated() segmentPoints.Add(chunkWorld.Translation); maxScale = Math::Max(maxScale, chunkWorld.Scale.GetAbsolute().MaxValue()); } - maxScale = Math::Max(maxScale, _localTransform.Scale.GetAbsolute().MaxValue()); BoundingSphere::FromPoints(segmentPoints.Get(), segmentPoints.Count(), instance.Sphere); - instance.Sphere.Center += _localTransform.Translation; instance.Sphere.Radius *= maxScale * _boundsScale; } @@ -356,8 +361,8 @@ void SplineModel::Draw(RenderContext& renderContext) drawCall.Deformable.MeshMaxZ = _meshMaxZ; drawCall.Deformable.GeometrySize = _box.GetSize(); drawCall.PerInstanceRandom = GetPerInstanceRandom(); - _localTransform.GetWorld(drawCall.Deformable.LocalMatrix); - const Transform splineTransform = _spline->GetTransform(); + Matrix::RotationQuaternion(_preRotation, drawCall.Deformable.LocalMatrix); + const Transform splineTransform = GetTransform(); splineTransform.GetWorld(drawCall.World); drawCall.ObjectPosition = drawCall.World.GetTranslation() + drawCall.Deformable.LocalMatrix.GetTranslation(); const float worldDeterminantSign = drawCall.World.RotDeterminant() * drawCall.Deformable.LocalMatrix.RotDeterminant(); diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h index a7d53af49..9a2d5420b 100644 --- a/Source/Engine/Level/Actors/SplineModel.h +++ b/Source/Engine/Level/Actors/SplineModel.h @@ -26,6 +26,7 @@ private: char _forcedLod = -1; bool _deformationDirty = false; Array _instances; + Quaternion _preRotation = Quaternion::Identity; Spline* _spline = nullptr; GPUBuffer* _deformationBuffer = nullptr; void* _deformationBufferData = nullptr; @@ -41,6 +42,17 @@ public: API_FIELD(Attributes="EditorOrder(20), DefaultValue(null), EditorDisplay(\"Model\")") AssetReference Model; + /// + /// Gets the rotation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY(Attributes="EditorOrder(21), EditorDisplay(\"Model\")") + Quaternion GetPreRotation() const; + + /// + /// Sets the rotation applied to the model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY() void SetPreRotation(const Quaternion& value); + /// /// The draw passes to use for rendering this object. /// diff --git a/Source/Engine/Physics/Colliders/SplineCollider.cpp b/Source/Engine/Physics/Colliders/SplineCollider.cpp index a529baf7d..ff670fcd5 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.cpp +++ b/Source/Engine/Physics/Colliders/SplineCollider.cpp @@ -22,6 +22,19 @@ SplineCollider::SplineCollider(const SpawnParams& params) CollisionData.Loaded.Bind(this); } +Quaternion SplineCollider::GetPreRotation() const +{ + return _preRotation; +} + +void SplineCollider::SetPreRotation(const Quaternion& value) +{ + if (_preRotation == value) + return; + _preRotation = value; + UpdateGeometry(); +} + void SplineCollider::OnCollisionDataChanged() { // This should not be called during physics simulation, if it happened use write lock on physx scene @@ -180,11 +193,10 @@ void SplineCollider::GetGeometry(PxGeometryHolder& geometry) } // Apply local mesh transformation - const Transform localTransform = _localTransform; - if (!localTransform.IsIdentity()) + if (!_preRotation.IsIdentity()) { for (int32 i = 0; i < collisionVertices.Count(); i++) - collisionVertices[i] = localTransform.LocalToWorld(collisionVertices[i]); + collisionVertices[i] = Vector3::Transform(collisionVertices[i], _preRotation); } // Find collision geometry local bounds diff --git a/Source/Engine/Physics/Colliders/SplineCollider.h b/Source/Engine/Physics/Colliders/SplineCollider.h index b5c0cf318..e7ca5a831 100644 --- a/Source/Engine/Physics/Colliders/SplineCollider.h +++ b/Source/Engine/Physics/Colliders/SplineCollider.h @@ -21,6 +21,7 @@ private: PxTriangleMesh* _triangleMesh = nullptr; Array _vertexBuffer; Array _indexBuffer; + Quaternion _preRotation = Quaternion::Identity; public: @@ -30,6 +31,17 @@ public: API_FIELD(Attributes="EditorOrder(100), DefaultValue(null), EditorDisplay(\"Collider\")") AssetReference CollisionData; + /// + /// Gets the rotation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY(Attributes="EditorOrder(101), EditorDisplay(\"Collider\")") + Quaternion GetPreRotation() const; + + /// + /// Sets the rotation applied to the collision data model geometry before placing it over the spline. Can be used to change the way model goes over the spline. + /// + API_PROPERTY() void SetPreRotation(const Quaternion& value); + private: void OnCollisionDataChanged();