From f380fa3b84287bd030a039d9bc83cd6e5604080e Mon Sep 17 00:00:00 2001
From: Christian Semmler <mail@csemmler.com>
Date: Sat, 25 May 2024 11:18:24 -0400
Subject: [PATCH] Implement/match LegoAnimationManager::FUN_100648f0 (#954)

---
 .../legoomni/include/legoanimationmanager.h   |  2 +-
 LEGO1/lego/legoomni/src/actors/helicopter.cpp |  6 +-
 .../src/common/legoanimationmanager.cpp       | 40 +++++++++++--
 LEGO1/lego/sources/anim/legoanim.cpp          |  6 +-
 LEGO1/mxgeometry/mxgeometry3d.h               | 56 +++++++++++++++++--
 5 files changed, 92 insertions(+), 18 deletions(-)

diff --git a/LEGO1/lego/legoomni/include/legoanimationmanager.h b/LEGO1/lego/legoomni/include/legoanimationmanager.h
index 087e3f86..b25b2510 100644
--- a/LEGO1/lego/legoomni/include/legoanimationmanager.h
+++ b/LEGO1/lego/legoomni/include/legoanimationmanager.h
@@ -170,7 +170,7 @@ private:
 		MxS32 p_unk0x10,
 		float p_speed
 	);
-	void FUN_100648f0(LegoTranInfo*, MxLong);
+	void FUN_100648f0(LegoTranInfo* p_tranInfo, MxLong p_unk0x404);
 	void FUN_10064b50(MxLong p_time);
 
 	MxS32 m_scriptIndex;               // 0x08
diff --git a/LEGO1/lego/legoomni/src/actors/helicopter.cpp b/LEGO1/lego/legoomni/src/actors/helicopter.cpp
index 06eba4e0..d3b3ef51 100644
--- a/LEGO1/lego/legoomni/src/actors/helicopter.cpp
+++ b/LEGO1/lego/legoomni/src/actors/helicopter.cpp
@@ -378,11 +378,7 @@ void Helicopter::VTable0x70(float p_float)
 			Vector3 v2(m_unk0x1a8[3]);
 			float* loc = m_unk0x1a8[3];
 			mat.SetIdentity();
-			float fa[4];
-			Vector4 v3(fa);
-			if (m_unk0x1f4.FUN_100040a0(v3, f2) == SUCCESS) {
-				mat.FromQuaternion(v3);
-			}
+			m_unk0x1f4.Unknown6(mat, f2);
 			v2.SetVector(loc);
 			v2.Sub(&v);
 			v2.Mul(f2);
diff --git a/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp b/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp
index c3a939fe..2d780d0a 100644
--- a/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp
+++ b/LEGO1/lego/legoomni/src/common/legoanimationmanager.cpp
@@ -25,6 +25,7 @@
 #include "mxticklemanager.h"
 #include "mxtimer.h"
 #include "mxutilities.h"
+#include "realtime/realtime.h"
 #include "viewmanager/viewmanager.h"
 
 #include <io.h>
@@ -2480,11 +2481,42 @@ MxResult LegoAnimationManager::FUN_10064880(const char* p_name, MxS32 p_unk0x0c,
 	return FAILURE;
 }
 
-// STUB: LEGO1 0x100648f0
+// FUNCTION: LEGO1 0x100648f0
 // FUNCTION: BETA10 0x10045daf
-void LegoAnimationManager::FUN_100648f0(LegoTranInfo*, MxLong)
+void LegoAnimationManager::FUN_100648f0(LegoTranInfo* p_tranInfo, MxLong p_unk0x404)
 {
-	// TODO
+	if (m_unk0x402 && p_tranInfo->m_unk0x14) {
+		p_tranInfo->m_flags |= LegoTranInfo::c_bit1;
+		m_unk0x430 = TRUE;
+		m_unk0x42c = p_tranInfo;
+		m_unk0x434 = p_unk0x404;
+		m_unk0x438 = p_unk0x404 + 1000;
+
+		ViewROI* viewROI = VideoManager()->GetViewROI();
+		m_unk0x43c = viewROI->GetLocal2World();
+		p_tranInfo->m_unk0x2c = m_unk0x43c;
+
+		LegoPathActor* actor = CurrentActor();
+		if (actor != NULL) {
+			actor->SetState(4);
+			actor->SetWorldSpeed(0.0f);
+		}
+
+		LegoLocation* location = NavController()->GetLocation(p_tranInfo->m_location);
+		if (location != NULL) {
+			CalcLocalTransform(location->m_position, location->m_direction, location->m_up, m_unk0x484);
+			m_unk0x4cc.Unknown1(m_unk0x43c, m_unk0x484);
+			m_unk0x4cc.Unknown7();
+		}
+		else {
+			p_tranInfo->m_flags &= ~LegoTranInfo::c_bit1;
+			m_unk0x430 = FALSE;
+		}
+
+		Mx3DPointFloat vec;
+		vec.Clear();
+		viewROI->FUN_100a5a30(vec);
+	}
 }
 
 // FUNCTION: LEGO1 0x10064b50
@@ -2508,7 +2540,7 @@ void LegoAnimationManager::FUN_10064b50(MxLong p_time)
 			sub[1] = (m_unk0x484[3][1] - m_unk0x43c[3][1]) * und;
 			sub[2] = (m_unk0x484[3][2] - m_unk0x43c[3][2]) * und;
 
-			m_unk0x4cc.Unknown_100040a0(mat, (float) (p_time - m_unk0x434) / 1000.0f);
+			m_unk0x4cc.Unknown6(mat, (float) (p_time - m_unk0x434) / 1000.0f);
 
 			VPV3(mat[3], m_unk0x43c[3], sub);
 			mat[3][4] = 1.0f;
diff --git a/LEGO1/lego/sources/anim/legoanim.cpp b/LEGO1/lego/sources/anim/legoanim.cpp
index 92de53ae..4be46ae4 100644
--- a/LEGO1/lego/sources/anim/legoanim.cpp
+++ b/LEGO1/lego/sources/anim/legoanim.cpp
@@ -572,9 +572,9 @@ inline void LegoAnimNodeData::GetTranslation(
 				c[3] = p_rotationKeys[i + 1].GetAngle();
 			}
 
-			b.Unknown1(a);
-			b.Unknown2(c);
-			b.Unknown_100040a0(
+			b.Unknown4(a);
+			b.Unknown5(c);
+			b.Unknown6(
 				p_matrix,
 				(p_time - p_rotationKeys[i].GetTime()) / (p_rotationKeys[i + 1].GetTime() - p_rotationKeys[i].GetTime())
 			);
diff --git a/LEGO1/mxgeometry/mxgeometry3d.h b/LEGO1/mxgeometry/mxgeometry3d.h
index bd4cc24a..970695bb 100644
--- a/LEGO1/mxgeometry/mxgeometry3d.h
+++ b/LEGO1/mxgeometry/mxgeometry3d.h
@@ -65,6 +65,9 @@ public:
 	inline float& operator[](int idx) { return m_data[idx]; }
 	inline const float& operator[](int idx) const { return m_data[idx]; }
 
+	// SYNTHETIC: LEGO1 0x10064b20
+	// Mx4DPointFloat::operator=
+
 private:
 	float m_elements[4]; // 0x08
 };
@@ -79,29 +82,54 @@ public:
 
 	UnknownMx4DPointFloat() : m_unk0x30(0) {}
 
-	inline void Unknown1(Vector4& p_v)
+	// FUNCTION: BETA10 0x1004a9b0
+	inline void Unknown1(Matrix4& p_m1, Matrix4& p_m2)
+	{
+		Unknown2(p_m1);
+		Unknown3(p_m2);
+	}
+
+	// FUNCTION: BETA10 0x1004a9f0
+	inline void Unknown2(Matrix4& p_m)
+	{
+		p_m.ToQuaternion(m_unk0x00);
+		m_unk0x30 |= c_bit1;
+	}
+
+	// FUNCTION: BETA10 0x1004aa30
+	inline void Unknown3(Matrix4& p_m)
+	{
+		p_m.ToQuaternion(m_unk0x18);
+		m_unk0x30 |= c_bit2;
+	}
+
+	// FUNCTION: BETA10 0x10180b80
+	inline void Unknown4(Vector4& p_v)
 	{
 		m_unk0x00 = p_v;
 		m_unk0x30 |= c_bit1;
 	}
 
-	inline void Unknown2(Vector4& p_v)
+	// FUNCTION: BETA10 0x10180bc0
+	inline void Unknown5(Vector4& p_v)
 	{
 		m_unk0x18 = p_v;
 		m_unk0x30 |= c_bit2;
 	}
 
-	inline int Unknown_100040a0(Matrix4& p_matrix, float p_f);
-	inline int FUN_100040a0(Vector4& p_v, float p_f);
+	inline int Unknown6(Matrix4& p_matrix, float p_f);
+	inline void Unknown7();
 
 private:
+	inline int FUN_100040a0(Vector4& p_v, float p_f);
+
 	Mx4DPointFloat m_unk0x00; // 0x00
 	Mx4DPointFloat m_unk0x18; // 0x18
 	undefined4 m_unk0x30;     // 0x30
 };
 
 // FUNCTION: BETA10 0x1004aaa0
-int UnknownMx4DPointFloat::Unknown_100040a0(Matrix4& p_matrix, float p_f)
+int UnknownMx4DPointFloat::Unknown6(Matrix4& p_matrix, float p_f)
 {
 	float data[4];
 	Vector4 v(data);
@@ -114,6 +142,24 @@ int UnknownMx4DPointFloat::Unknown_100040a0(Matrix4& p_matrix, float p_f)
 	}
 }
 
+inline void UnknownMx4DPointFloat::Unknown7()
+{
+	if (m_unk0x30) {
+		Mx4DPointFloat v1;
+		Mx4DPointFloat v2;
+
+		v1 = m_unk0x00;
+		((Vector4&) v1).Add(&m_unk0x18);
+
+		v2 = m_unk0x00;
+		((Vector4&) v2).Sub(&m_unk0x18);
+
+		if (v1.Dot(&v1, &v1) < v2.Dot(&v2, &v2)) {
+			((Vector4&) m_unk0x18).Mul(-1.0f);
+		}
+	}
+}
+
 // FUNCTION: LEGO1 0x100040a0
 // FUNCTION: BETA10 0x1004ab10
 inline int UnknownMx4DPointFloat::FUN_100040a0(Vector4& p_v, float p_f)