예제 #1
0
        private void SerializeCompressedData(FAssetArchive Ar)
        {
            // These fields were serialized as properties in pre-UE4.12 engine version
            KeyEncodingFormat            = Ar.Read <AnimationKeyFormat>();
            TranslationCompressionFormat = Ar.Read <AnimationCompressionFormat>();
            RotationCompressionFormat    = Ar.Read <AnimationCompressionFormat>();
            ScaleCompressionFormat       = Ar.Read <AnimationCompressionFormat>();

            CompressedTrackOffsets = Ar.ReadArray <int>();
            CompressedScaleOffsets = new FCompressedOffsetData(Ar);

            if (Ar.Game >= EGame.GAME_UE4_21)
            {
                // UE4.21+ - added compressed segments; disappeared in 4.23
                CompressedSegments = Ar.ReadArray <FCompressedSegment>();
                if (CompressedSegments.Length > 0)
                {
                    Log.Information("animation has CompressedSegments!");
                }
            }

            CompressedTrackToSkeletonMapTable = Ar.ReadArray <FTrackToSkeletonMap>();

            if (Ar.Game < EGame.GAME_UE4_22)
            {
                CompressedCurveData = new FStructFallback(Ar, "RawCurveTracks");
            }
            else
            {
                var compressedCurveNames = Ar.ReadArray(() => new FSmartName(Ar));
            }

            if (Ar.Game >= EGame.GAME_UE4_17)
            {
                // UE4.17+
                var compressedRawDataSize = Ar.Read <int>();
            }

            if (Ar.Game >= EGame.GAME_UE4_22)
            {
                var compressedNumFrames = Ar.Read <int>();
            }

            // compressed data
            var numBytes = Ar.Read <int>();

            CompressedByteStream = Ar.ReadBytes(numBytes);

            if (Ar.Game >= EGame.GAME_UE4_22)
            {
                var curveCodecPath            = Ar.ReadFString();
                var compressedCurveByteStream = Ar.ReadArray <byte>();
            }

            // Fix layout of "byte swapped" data (workaround for UE4 bug)
            if (KeyEncodingFormat == AnimationKeyFormat.AKF_PerTrackCompression && CompressedScaleOffsets.OffsetData.Length > 0 && Ar.Game < EGame.GAME_UE4_23)
            {
                throw new NotImplementedException();
            }
        }
예제 #2
0
        // UE4.23-4.24 has changed compressed data layout for streaming, so it's worth making a separate
        // serializer function for it.
        private void SerializeCompressedData2(FAssetArchive Ar)
        {
            var compressedRawDataSize = Ar.Read <int>();

            CompressedTrackToSkeletonMapTable = Ar.ReadArray <FTrackToSkeletonMap>();
            var compressedCurveNames = Ar.ReadArray(() => new FSmartName(Ar));

            // Since 4.23, this is FUECompressedAnimData::SerializeCompressedData
            KeyEncodingFormat            = Ar.Read <AnimationKeyFormat>();
            TranslationCompressionFormat = Ar.Read <AnimationCompressionFormat>();
            RotationCompressionFormat    = Ar.Read <AnimationCompressionFormat>();
            ScaleCompressionFormat       = Ar.Read <AnimationCompressionFormat>();

            var compressedNumFrames = Ar.Read <int>();

            // SerializeView() just serializes array size
            var compressedTrackOffsetsNum = Ar.Read <int>();
            var compressedScaleOffsetsNum = Ar.Read <int>();

            CompressedScaleOffsets = new FCompressedOffsetData(Ar.Read <int>());
            var compressedByteStreamNum = Ar.Read <int>();
            // ... end of FUECompressedAnimData::SerializeCompressedData

            var numBytes            = Ar.Read <int>();
            var bUseBulkDataForLoad = Ar.ReadBoolean();

            // In UE4.23 CompressedByteStream field exists in FUECompressedAnimData (as TArrayView) and in
            // FCompressedAnimSequence (as byte array). Serialization is done in FCompressedAnimSequence,
            // either as TArray or as bulk, and then array is separated onto multiple "views" for
            // FUECompressedAnimData. We'll use a different name for "joined" serialized array here to
            // avoid confuse.
            byte[] serializedByteStream;

            if (bUseBulkDataForLoad)
            {
                throw new NotImplementedException("Anim: bUseBulkDataForLoad not implemented");
                //todo: read from bulk to serializedByteStream
            }
            else
            {
                serializedByteStream = Ar.ReadBytes(numBytes);
            }

            // Setup all array views from single array. In UE4 this is done in FUECompressedAnimData::InitViewsFromBuffer.
            // We'll simply copy array data away from SerializedByteStream, and then SerializedByteStream
            // will be released from memory as it is a local variable here.
            // Note: copying is not byte-order wise, so if there will be any problems in the future,
            // should use byte swap functions.
            using (var tempAr = new FByteArchive("SerializedByteStream", serializedByteStream, Ar.Versions))
            {
                CompressedTrackOffsets            = tempAr.ReadArray <int>(compressedTrackOffsetsNum);
                CompressedScaleOffsets.OffsetData = tempAr.ReadArray <int>(compressedScaleOffsetsNum);
                CompressedByteStream = tempAr.ReadBytes(compressedByteStreamNum);
            }

            var curveCodecPath            = Ar.ReadFString();
            var compressedCurveByteStream = Ar.ReadArray <byte>();
        }
예제 #3
0
        public FAnimKeyHeader(BinaryReader reader)
        {
            var packed = reader.ReadUInt32();

            key_format      = (AnimationCompressionFormat)(packed >> 28);
            component_mask  = (packed >> 24) & 0xF;
            num_keys        = packed & 0xFFFFFF;
            has_time_tracks = (component_mask & 8) != 0;
        }
예제 #4
0
 public void UpdateProps(PropertyCollection props, MEGame newGame, AnimationCompressionFormat newRotationCompression = AnimationCompressionFormat.ACF_Float96NoW)
 {
     if (compressedDataSource == MEGame.Unknown || (newGame != compressedDataSource && !(newGame <= MEGame.ME3 && compressedDataSource <= MEGame.ME3)))
     {
         CompressAnimationData(newGame, newRotationCompression);
         props.RemoveNamedProperty("KeyEncodingFormat");
         props.RemoveNamedProperty("TranslationCompressionFormat");
         props.AddOrReplaceProp(new EnumProperty(rotCompression.ToString(), nameof(AnimationCompressionFormat), newGame, "RotationCompressionFormat"));
         props.AddOrReplaceProp(new ArrayProperty <IntProperty>(TrackOffsets.Select(i => new IntProperty(i)), "CompressedTrackOffsets"));
         props.AddOrReplaceProp(new IntProperty(NumFrames, "NumFrames"));
         props.AddOrReplaceProp(new FloatProperty(RateScale, "RateScale"));
         props.AddOrReplaceProp(new FloatProperty(SequenceLength, "SequenceLength"));
         props.AddOrReplaceProp(new NameProperty(Name, "SequenceName"));
     }
 }
예제 #5
0
        public void DecompressAnimationData()
        {
            var ms = new MemoryStream(CompressedAnimationData);

            RawAnimationData = new List <AnimTrack>();

            for (int i = 0; i < Bones.Count; i++)
            {
                int posOff     = TrackOffsets[i * 4];
                int numPosKeys = TrackOffsets[i * 4 + 1];
                int rotOff     = TrackOffsets[i * 4 + 2];
                int numRotKeys = TrackOffsets[i * 4 + 3];

                var track = new AnimTrack
                {
                    Positions = new List <Vector3>(numPosKeys),
                    Rotations = new List <Quaternion>(numRotKeys)
                };

                if (numPosKeys > 0)
                {
                    ms.JumpTo(posOff);

                    AnimationCompressionFormat compressionFormat = posCompression;

                    if (numPosKeys == 1)
                    {
                        compressionFormat = AnimationCompressionFormat.ACF_None;
                    }

                    for (int j = 0; j < numPosKeys; j++)
                    {
                        switch (compressionFormat)
                        {
                        case AnimationCompressionFormat.ACF_None:
                        case AnimationCompressionFormat.ACF_Float96NoW:
                            track.Positions.Add(new Vector3(ms.ReadFloat(), ms.ReadFloat(), ms.ReadFloat()));
                            break;

                        case AnimationCompressionFormat.ACF_IntervalFixed32NoW:
                        case AnimationCompressionFormat.ACF_Fixed48NoW:
                        case AnimationCompressionFormat.ACF_Fixed32NoW:
                        case AnimationCompressionFormat.ACF_Float32NoW:
                        case AnimationCompressionFormat.ACF_BioFixed48:
                            throw new NotImplementedException($"Translation keys in format {compressionFormat} cannot be read yet!");
                        }
                    }

                    if (keyEncoding == AnimationKeyFormat.AKF_VariableKeyLerp && numPosKeys > 1)
                    {
                        ms.JumpTo(ms.Position.Align(4));

                        var keyTimes = new List <int>(numPosKeys);
                        for (int j = 0; j < numPosKeys; j++)
                        {
                            keyTimes.Add(NumFrames > 0xFF ? ms.ReadUInt16() : ms.ReadByte());
                        }
                        //RawAnimationData should have either 1 key, or the same number of keys as frames.
                        //Lerp any missing keys
                        List <Vector3> tempPositions = track.Positions;
                        track.Positions = new List <Vector3>(NumFrames)
                        {
                            tempPositions[0]
                        };
                        for (int frameIdx = 1, keyIdx = 1; frameIdx < NumFrames; keyIdx++, frameIdx++)
                        {
                            if (keyIdx >= keyTimes.Count)
                            {
                                track.Positions.Add(track.Positions[frameIdx - 1]);
                            }
                            else if (keyTimes[keyIdx] == frameIdx)
                            {
                                track.Positions.Add(tempPositions[keyIdx]);
                            }
                            else
                            {
                                int nextFrame = keyTimes[keyIdx];
                                int prevFrame = frameIdx - 1;
                                for (int j = frameIdx; j < nextFrame; j++)
                                {
                                    float amount = (float)(j - prevFrame) / (nextFrame - prevFrame);
                                    track.Positions.Add(Vector3.Lerp(track.Positions[prevFrame], tempPositions[keyIdx], amount));
                                }
                                track.Positions.Add(tempPositions[keyIdx]);
                                frameIdx = nextFrame;
                            }
                        }
                    }
                }

                if (numRotKeys > 0)
                {
                    ms.JumpTo(rotOff);

                    AnimationCompressionFormat compressionFormat = rotCompression;

                    if (numRotKeys == 1)
                    {
                        compressionFormat = AnimationCompressionFormat.ACF_Float96NoW;
                    }
                    else if (compressedDataSource != MEGame.UDK)
                    {
                        ms.Skip(12 * 2); //skip mins and ranges
                    }

                    for (int j = 0; j < numRotKeys; j++)
                    {
                        switch (compressionFormat)
                        {
                        case AnimationCompressionFormat.ACF_None:
                            track.Rotations.Add(new Quaternion(ms.ReadFloat(), ms.ReadFloat(), ms.ReadFloat(), ms.ReadFloat()));
                            break;

                        case AnimationCompressionFormat.ACF_Float96NoW:
                        {
                            float x = ms.ReadFloat();
                            float y = ms.ReadFloat();
                            float z = ms.ReadFloat();
                            track.Rotations.Add(new Quaternion(x, y, z, getW(x, y, z)));
                            break;
                        }

                        case AnimationCompressionFormat.ACF_BioFixed48:
                        {
                            const float shift         = 0.70710678118f;
                            const float scale         = 1.41421356237f;
                            const float precisionMult = 32767.0f;
                            ushort      a             = ms.ReadUInt16();
                            ushort      b             = ms.ReadUInt16();
                            ushort      c             = ms.ReadUInt16();
                            float       x             = (a & 0x7FFF) / precisionMult * scale - shift;
                            float       y             = (b & 0x7FFF) / precisionMult * scale - shift;
                            float       z             = (c & 0x7FFF) / precisionMult * scale - shift;
                            float       w             = getW(x, y, z);
                            int         wPos          = ((a >> 14) & 2) | ((b >> 15) & 1);
                            track.Rotations.Add(wPos switch
                                {
                                    0 => new Quaternion(w, x, y, z),
                                    1 => new Quaternion(x, w, y, z),
                                    2 => new Quaternion(x, y, w, z),
                                    _ => new Quaternion(x, y, z, w)
                                });

                            break;
                        }