diff --git a/Unreal/GameDatabase.cpp b/Unreal/GameDatabase.cpp index 4dbdeca1..95539979 100644 --- a/Unreal/GameDatabase.cpp +++ b/Unreal/GameDatabase.cpp @@ -30,6 +30,9 @@ const GameInfo GListOfGames[] = { #if UNDYING G("Undying", undying, GAME_Undying), #endif + #if HP2 + G("Harry Potter and the Chamber of Secrets", hp2, GAME_HarryPotter2), + #endif #endif // UNREAL1 // Unreal Engine 2 diff --git a/Unreal/GameDefines.h b/Unreal/GameDefines.h index 3c5fe091..c008d4a0 100644 --- a/Unreal/GameDefines.h +++ b/Unreal/GameDefines.h @@ -33,6 +33,7 @@ #define DEUS_EX 1 #define RUNE 1 #define UNDYING 1 +#define HP2 1 #endif // requires UNREAL25 diff --git a/Unreal/UnCore.h b/Unreal/UnCore.h index 22f46781..913aadfe 100644 --- a/Unreal/UnCore.h +++ b/Unreal/UnCore.h @@ -347,6 +347,7 @@ enum EGame GAME_UE1 = 0x0100000, GAME_Undying, + GAME_HarryPotter2, GAME_UE2 = 0x0200000, GAME_UT2, @@ -1118,6 +1119,17 @@ struct FVector { float X, Y, Z; + // Constructors. + FVector() {} + + FVector(float InX, float InY, float InZ) + : X(InX), Y(InY), Z(InZ) + {} + + explicit FVector(float In) + : X(In), Y(In), Z(In) + {} + void Set(float _X, float _Y, float _Z) { X = _X; Y = _Y; Z = _Z; @@ -1149,6 +1161,7 @@ struct FVector X *= value; Y *= value; Z *= value; } + friend FArchive& operator<<(FArchive &Ar, FVector &V) { Ar << V.X << V.Y << V.Z; diff --git a/Unreal/UnCoreSerialize.cpp b/Unreal/UnCoreSerialize.cpp index 2846ea10..85fe2107 100644 --- a/Unreal/UnCoreSerialize.cpp +++ b/Unreal/UnCoreSerialize.cpp @@ -219,6 +219,8 @@ FArchive& FArray::SerializeSimple(FArchive &Ar, int NumFields, int FieldSize) //?? be done using generic serializer, or SerializeSimple should be //?? extended for this + const bool bIsStatic = IsStatic(); + // serialize data count int Count = DataCount; if (GameUsesFCompactIndex(Ar)) @@ -234,6 +236,7 @@ FArchive& FArray::SerializeSimple(FArchive &Ar, int NumFields, int FieldSize) DataCount = Count; } if (!Count) return Ar; + if (bIsStatic) return Ar; // perform serialization itself Ar.Serialize(DataPtr, elementSize * Count); diff --git a/Unreal/UnrealMesh/UnAnim2.cpp b/Unreal/UnrealMesh/UnAnim2.cpp index 8faf72fa..821f45d8 100644 --- a/Unreal/UnrealMesh/UnAnim2.cpp +++ b/Unreal/UnrealMesh/UnAnim2.cpp @@ -18,7 +18,7 @@ UMeshAnimation::~UMeshAnimation() } -void UMeshAnimation::ConvertAnims() +void UMeshAnimation::ConvertAnims(bool bShouldAdjustTime /* = true */) { guard(UMeshAnimation::ConvertAnims); @@ -62,7 +62,7 @@ void UMeshAnimation::ConvertAnims() CopyArray(T->KeyTime, A.KeyTime); // usually MotionChunk.TrackTime is identical to NumFrames, but some packages exists where // time channel should be adjusted - if (M.TrackTime > 0) + if (bShouldAdjustTime && M.TrackTime > 0) { float TimeScale = Src.NumFrames / M.TrackTime; for (int k = 0; k < T->KeyTime.Num(); k++) diff --git a/Unreal/UnrealMesh/UnMesh1.cpp b/Unreal/UnrealMesh/UnMesh1.cpp index 8192d31c..ea911422 100644 --- a/Unreal/UnrealMesh/UnMesh1.cpp +++ b/Unreal/UnrealMesh/UnMesh1.cpp @@ -393,6 +393,27 @@ void USkeletalMesh::SerializeSkelMesh1(FArchive &Ar) } unguard; +#if HP2 + if (Ar.Game == GAME_HarryPotter2) + { + int i; + for (i = 0; i < Points.Num(); i++) + Points[i].Y *= -1; + for (i = 0; i < Triangles.Num(); i++) + Exchange(Triangles[i].WedgeIndex[0], Triangles[i].WedgeIndex[1]); + for (i = 0; i < RefSkeleton.Num(); i++) + { + FMeshBone &S = RefSkeleton[i]; + S.BonePos.Position.Y *= -1; + S.BonePos.Orientation.Y *= -1; + S.BonePos.Orientation.W *= -1; + } + + ConvertMesh(); + return; + } +#endif // HP2 + // mirror model: points, faces and skeleton int i; for (i = 0; i < Points.Num(); i++) @@ -412,4 +433,204 @@ void USkeletalMesh::SerializeSkelMesh1(FArchive &Ar) unguard; } +#if HP2 + +struct FAnimVec +{ + int16 X = 0, Y = 0, Z = 0; + + // Convert back to Vector, with additional scale. + FVector Vector( float Scale ) const + { + FVector OutVector; + Scale /= BASE_SCALE; + OutVector.Set(X * Scale, Y * Scale, Z * Scale); + return OutVector; + } + + enum { BASE_SCALE = (1<<15)-1 }; + + FQuat Quat() const + { + static float Scale = 1.57079633 / BASE_SCALE; + + const float _X = sin(X * Scale); + const float _Y = sin(Y * Scale); + const float _Z = sin(Z * Scale); + + const float W = sqrt(max(0.0f, 1.f - _X * _X - _Y * _Y - _Z * _Z)); + + FQuat OutQuat; + OutQuat.Set(_X, _Y, _Z, W); + return OutQuat; + } + + friend FArchive& operator<<(FArchive &Ar, FAnimVec &A) + { + return Ar << A.X << A.Y << A.Z; + } +}; + +SIMPLE_TYPE(FAnimVec, int16) + +struct AnalogTrack_HP2 +{ + unsigned Flags = 0; // reserved + TStaticArray KeyQuat; // Orientation key track (count = 1 or KeyTime.Count) + TStaticArray KeyPos; // Position key track (count = 1 or KeyTime.Count) + TStaticArray KeyDelta; // For each key, time when next key takes effect (measured from start of track) + float PosScale = 0.0f; // Scale for position track. + float TimeScale = 0.0f; // Scale for time track. + + inline FVector GetKeyPos(int i) const + { + return KeyPos[i].Vector( PosScale ); + } + + friend FArchive& operator<<(FArchive &Ar, AnalogTrack_HP2 &A) + { + guard(AnalogTrack_HP2<<); + return Ar << A.Flags << A.KeyQuat << A.KeyPos << A.KeyDelta << A.PosScale << A.TimeScale; + unguard; + } +}; + +struct MotionChunk_HP2 +{ + FVector RootSpeed3D; // Net 3d speed. + float TrackTime; // Total time (Same for each track.) + int StartBone; // If we're a partial-hierarchy-movement, this is the lowest bone. + unsigned Flags; // Reserved; equals to UMeshAnimation.Version in UE2.5 + + TArray BoneIndices; // Refbones number of Bone indices (-1 or valid one) to fast-find tracks for a particular bone. + // Frame-less, compressed animation tracks. NumBones times NumAnims tracks in total + TArray AnimTracks; // Compressed key tracks (one for each bone) + // AnalogTrack RootTrack; // May or may not be used; actual traverse-a-scene root tracks for use + // with cutscenes / special physics modes, in addition to the regular skeletal root track. + + friend FArchive& operator<<(FArchive &Ar, MotionChunk_HP2 &M) + { + guard(MotionChunk<<); + return Ar << M.RootSpeed3D << M.TrackTime << M.StartBone << M.Flags << M.BoneIndices << M.AnimTracks; + unguard; + } +}; + +struct FMasterTrack +{ + TArray KeyQuat; + TArray KeyPos; + TArray KeyDelta; + + friend FArchive& operator<<(FArchive &Ar, FMasterTrack &M) + { + guard(FMasterTrack<<); + return Ar << M.KeyQuat << M.KeyPos << M.KeyDelta; + unguard; + } +}; + +#include "Mesh/SkeletalMesh.h" + +void UMeshAnimation::SerializeHP2Moves(FArchive &Ar) +{ + guard(SerializeHP2Moves); + + TArray HPMoves; + FMasterTrack MasterTrack; + Ar << HPMoves << AnimSeqs << MasterTrack; + + int Q = 0, P = 0, T = 0; + + for (MotionChunk_HP2& Move : HPMoves) + { + for (AnalogTrack_HP2& Track : Move.AnimTracks) + { + const int CurrentKeyQuatNum = Track.KeyQuat.Num(); + const int CurrentKeyPosNum = Track.KeyPos.Num(); + const int CurrentKeyDeltaNum = Track.KeyDelta.Num(); + + for (int QuatIndex = 0; QuatIndex < CurrentKeyQuatNum; ++QuatIndex) + { + const int TargetIndex = (Q + QuatIndex); + Track.KeyQuat[QuatIndex] = MasterTrack.KeyQuat[TargetIndex]; + } + for (int PosIndex = 0; PosIndex < CurrentKeyPosNum; ++PosIndex) + { + const int TargetIndex = (P + PosIndex); + Track.KeyPos[PosIndex] = MasterTrack.KeyPos[TargetIndex]; + } + for (int DeltaIndex = 0; DeltaIndex < CurrentKeyDeltaNum; ++DeltaIndex) + { + const int TargetIndex = (T + DeltaIndex); + Track.KeyDelta[DeltaIndex] = MasterTrack.KeyDelta[TargetIndex]; + } + + Q += CurrentKeyQuatNum; + P += CurrentKeyPosNum; + T += CurrentKeyDeltaNum; + } + } + + Moves.SetNum(HPMoves.Num()); + + for (int MoveIndex = 0; MoveIndex < HPMoves.Num(); MoveIndex++) + { + FMeshAnimSeq& AnimSeq = AnimSeqs[MoveIndex]; + + MotionChunk_HP2& SourceMove = HPMoves[MoveIndex]; + MotionChunk& TargetMove = Moves[MoveIndex]; + + TargetMove.RootSpeed3D = SourceMove.RootSpeed3D; + TargetMove.TrackTime = SourceMove.TrackTime; + TargetMove.StartBone = SourceMove.StartBone; + TargetMove.Flags = SourceMove.Flags; + + CopyArray(TargetMove.BoneIndices, SourceMove.BoneIndices); + + // For every bone + TargetMove.AnimTracks.SetNum(SourceMove.AnimTracks.Num()); + + for (int BoneIndex = 0; BoneIndex < TargetMove.AnimTracks.Num(); BoneIndex++) + { + AnalogTrack& TargetAnalogTrack = TargetMove.AnimTracks[BoneIndex]; + AnalogTrack_HP2& SourceAnalogTrack = SourceMove.AnimTracks[BoneIndex]; + + TargetAnalogTrack.Flags = SourceAnalogTrack.Flags; + TargetAnalogTrack.KeyQuat.SetNum(SourceAnalogTrack.KeyQuat.Num()); + TargetAnalogTrack.KeyPos.SetNum(SourceAnalogTrack.KeyPos.Num()); + TargetAnalogTrack.KeyTime.SetNum(SourceAnalogTrack.KeyDelta.Num()); + + for (int KeyQuatIndex = 0; KeyQuatIndex < TargetAnalogTrack.KeyQuat.Num(); KeyQuatIndex++) + { + FQuat TargetQuat = SourceAnalogTrack.KeyQuat[KeyQuatIndex].Quat(); + TargetQuat.Y *= -1.0f; + + // "Due to historical idiosyncrasy, root orientation is negated. !" From UE1 + if (BoneIndex != 0) + { + TargetQuat.W *= -1.0f; + } + + TargetAnalogTrack.KeyQuat[KeyQuatIndex] = TargetQuat; + } + for (int KeyPosIndex = 0; KeyPosIndex < TargetAnalogTrack.KeyPos.Num(); KeyPosIndex++) + { + TargetAnalogTrack.KeyPos[KeyPosIndex] = SourceAnalogTrack.GetKeyPos(KeyPosIndex); + TargetAnalogTrack.KeyPos[KeyPosIndex].Y *= -1.0f; + } + + int CurrentTime = 0; + for (int KeyTimeIndex = 0; KeyTimeIndex < TargetAnalogTrack.KeyTime.Num(); KeyTimeIndex++) + { + TargetAnalogTrack.KeyTime[KeyTimeIndex] = CurrentTime + SourceAnalogTrack.KeyDelta[KeyTimeIndex]; + CurrentTime += SourceAnalogTrack.KeyDelta[KeyTimeIndex]; + } + } + } + + unguard; +} +#endif // HP2 + #endif // UNREAL1 diff --git a/Unreal/UnrealMesh/UnMesh2.h b/Unreal/UnrealMesh/UnMesh2.h index ea41c350..15ac4c5d 100644 --- a/Unreal/UnrealMesh/UnMesh2.h +++ b/Unreal/UnrealMesh/UnMesh2.h @@ -1393,6 +1393,9 @@ class UMeshAnimation : public UObject void SerializeSCell(FArchive &Ar); #endif #if UNREAL1 +#if HP2 + void SerializeHP2Moves(FArchive &Ar); +#endif void Upgrade(); #endif @@ -1422,6 +1425,16 @@ class UMeshAnimation : public UObject return; } #endif // SWRC +#if UNREAL1 +#if HP2 + if (Ar.Game == GAME_HarryPotter2) + { + SerializeHP2Moves(Ar); + ConvertAnims(false); + return; + } +#endif // HP2 +#endif // UNREAL1 #if UC2 if (Ar.Engine() == GAME_UE2X) { @@ -1454,7 +1467,7 @@ class UMeshAnimation : public UObject unguard; } - void ConvertAnims(); + void ConvertAnims(bool bShouldAdjustTime = true); };