diff --git a/src/Branch/PackageObjectLegacyVersion.cs b/src/Branch/PackageObjectLegacyVersion.cs index efc0055..39ced3d 100644 --- a/src/Branch/PackageObjectLegacyVersion.cs +++ b/src/Branch/PackageObjectLegacyVersion.cs @@ -8,7 +8,7 @@ public enum PackageObjectLegacyVersion /// This is one particular update with A LOT of general package changes. /// ReturnExpressionAddedToReturnToken = 62, - + SphereExtendsPlane = 62, LazyArrayAdded = 63, @@ -19,18 +19,22 @@ public enum PackageObjectLegacyVersion /// CastStringSizeTokenDeprecated = 70, + PanUVRemovedFromPoly = 78, + /// /// FIXME: Version, set 95 (Deus Ex: IW) /// PrimitiveCastTokenAdded = 95, - - + + LightMapScaleAddedToPoly = 106, + KerningAddedToUFont = 119, FontPagesDisplaced = 122, - - UE3 = 184, - RangeConstTokenDeprecated = UE3, + // The estimated version changes that came after the latest known UE2 build. + TextureDeprecatedFromPoly = 170, + MaterialAddedToPoly = 170, + /// /// Present in all released UE3 games (starting with RoboBlitz). /// @@ -38,9 +42,33 @@ public enum PackageObjectLegacyVersion /// IsLocalAddedToDelegateFunctionToken = 181, + UE3 = 184, + RangeConstTokenDeprecated = UE3, + + // 227 according to the GoW client + FixedVerticesToArrayFromPoly = 227, + + // FIXME: Not attested in the GoW client, must have been before v321 + LightMapScaleRemovedFromPoly = 300, + + // FIXME: Not attested in the GoW client, must have been before v321 + ShadowMapScaleAddedToPoly = 300, + + // 321 according to the GoW client + ElementOwnerAddedToUPolys = 321, + + // 417 according to the GoW client + LightingChannelsAddedToPoly = 417, + VerticalOffsetAddedToUFont = 506, CleanupFonts = 511, - + + LightmassAdded = 600, + UProcBuildingReferenceAddedToPoly = 606, + LightmassShadowIndirectOnlyOptionAdded = 652, + LightmassExplicitEmissiveLightRadiusAdded = 636, + PolyRulesetVariationTypeChangedToName = 670, + ProbeMaskReducedAndIgnoreMaskRemoved = 692, ForceScriptOrderAddedToUClass = 749, SuperReferenceMovedToUStruct = 756, diff --git a/src/Eliot.UELib.csproj b/src/Eliot.UELib.csproj index 675581a..d9f6f09 100644 --- a/src/Eliot.UELib.csproj +++ b/src/Eliot.UELib.csproj @@ -183,6 +183,9 @@ + + + @@ -194,6 +197,7 @@ + @@ -204,6 +208,7 @@ + diff --git a/src/Engine/Classes/UModel.cs b/src/Engine/Classes/UModel.cs index ab3b982..95631a3 100644 --- a/src/Engine/Classes/UModel.cs +++ b/src/Engine/Classes/UModel.cs @@ -1,14 +1,26 @@ +using UELib.Engine; +using UELib.ObjectModel.Annotations; + namespace UELib.Core { /// - /// Implements UModel/Engine.Model + /// Implements UModel/Engine.Model /// + [Output("Brush")] [UnrealRegisterClass] public class UModel : UObject { + [Output] + public UPolys Polys; + public UModel() { ShouldDeserializeOnDemand = true; } + + protected override void Deserialize() + { + base.Deserialize(); + } } } \ No newline at end of file diff --git a/src/Engine/Classes/UPolys.cs b/src/Engine/Classes/UPolys.cs new file mode 100644 index 0000000..804a882 --- /dev/null +++ b/src/Engine/Classes/UPolys.cs @@ -0,0 +1,62 @@ +using UELib.Annotations; +using UELib.Branch; +using UELib.Core; +using UELib.Flags; +using UELib.ObjectModel.Annotations; + +namespace UELib.Engine +{ + /// + /// Implements UPolys/Engine.Polys + /// + [Output("PolyList")] + [UnrealRegisterClass] + public class UPolys : UObject + { + [CanBeNull] public UObject ElementOwner; + + [Output] + public UArray Element; + + public UPolys() + { + ShouldDeserializeOnDemand = true; + } + + protected override void Deserialize() + { + base.Deserialize(); + + // Faster serialization for cooked packages, no don't have to check for the package's version here. + if (Package.Summary.PackageFlags.HasFlag(PackageFlags.Cooked)) + { + ElementOwner = _Buffer.ReadObject(); + Record(nameof(ElementOwner), ElementOwner); + + _Buffer.ReadArray(out Element); + Record(nameof(Element), Element); + return; + } + + int num, max; + + num = _Buffer.ReadInt32(); + Record(nameof(num), num); + max = _Buffer.ReadInt32(); + Record(nameof(max), max); + + if (_Buffer.Version >= (uint)PackageObjectLegacyVersion.ElementOwnerAddedToUPolys) + { + ElementOwner = _Buffer.ReadObject(); + Record(nameof(ElementOwner), ElementOwner); + } + + Element = new UArray(num); + if (num > 0) + { + _Buffer.ReadArray(out Element, num); + Record(nameof(Element), Element); + } + } + } +} \ No newline at end of file diff --git a/src/Engine/Classes/UProcBuildingRuleset.cs b/src/Engine/Classes/UProcBuildingRuleset.cs new file mode 100644 index 0000000..381d746 --- /dev/null +++ b/src/Engine/Classes/UProcBuildingRuleset.cs @@ -0,0 +1,13 @@ +using UELib.Core; + +namespace UELib.Engine +{ + /// + /// Implements UProcBuildingRuleset/Engine.ProcBuildingRuleset + /// + [UnrealRegisterClass] + [BuildGeneration(BuildGeneration.UE3)] + public class UProcBuildingRuleset : UObject + { + } +} \ No newline at end of file diff --git a/src/Engine/Types/LightingChannelContainer.cs b/src/Engine/Types/LightingChannelContainer.cs new file mode 100644 index 0000000..d189a9c --- /dev/null +++ b/src/Engine/Types/LightingChannelContainer.cs @@ -0,0 +1,16 @@ +using System.Runtime.InteropServices; + +namespace UELib.Engine +{ + /// + /// See LightingChannelContainer in Engine/Classes/LightComponent.uc + /// + [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 4)] + public struct LightingChannelContainer : IUnrealAtomicStruct + { + [MarshalAs(UnmanagedType.I1)] public bool Initialized; + [MarshalAs(UnmanagedType.I1)] public bool BSP; + [MarshalAs(UnmanagedType.I1)] public bool Static; + [MarshalAs(UnmanagedType.I1)] public bool Dynamic; + } +} \ No newline at end of file diff --git a/src/Engine/Types/LightmassPrimitiveSettings.cs b/src/Engine/Types/LightmassPrimitiveSettings.cs new file mode 100644 index 0000000..e38043d --- /dev/null +++ b/src/Engine/Types/LightmassPrimitiveSettings.cs @@ -0,0 +1,75 @@ +using System.ComponentModel; +using System.Runtime.InteropServices; +using UELib.Branch; + +namespace UELib.Engine +{ + /// + /// Implements FLightmassPrimitiveSettings + /// + [StructLayout(LayoutKind.Sequential)] + public struct LightmassPrimitiveSettings : IUnrealSerializableClass + { + [MarshalAs(UnmanagedType.I1)] public bool UseTwoSidedLighting; + [MarshalAs(UnmanagedType.I1)] public bool ShadowIndirectOnly; + [MarshalAs(UnmanagedType.I1)] public bool UseEmissiveForStaticLighting; + + public float EmissiveLightFalloffExponent; + public float EmissiveLightExplicitInfluenceRadius; + public float EmissiveBoost; + public float DiffuseBoost; + public float SpecularBoost; + + [DefaultValue(1.0f)] public float FullyOccludedSamplesFraction; + + public void Deserialize(IUnrealStream stream) + { + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightmassShadowIndirectOnlyOptionAdded) + { + stream.Read(out UseTwoSidedLighting); + stream.Read(out ShadowIndirectOnly); + stream.Read(out FullyOccludedSamplesFraction); + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightmassAdded) + { + stream.Read(out UseEmissiveForStaticLighting); + stream.Read(out EmissiveLightFalloffExponent); + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightmassExplicitEmissiveLightRadiusAdded) + { + stream.Read(out EmissiveLightExplicitInfluenceRadius); + } + + stream.Read(out EmissiveBoost); + stream.Read(out DiffuseBoost); + stream.Read(out SpecularBoost); + } + + public void Serialize(IUnrealStream stream) + { + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightmassShadowIndirectOnlyOptionAdded) + { + stream.Write(UseTwoSidedLighting); + stream.Write(ShadowIndirectOnly); + stream.Write(FullyOccludedSamplesFraction); + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightmassAdded) + { + stream.Write(UseEmissiveForStaticLighting); + stream.Write(EmissiveLightFalloffExponent); + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightmassExplicitEmissiveLightRadiusAdded) + { + stream.Write(EmissiveLightExplicitInfluenceRadius); + } + + stream.Write(EmissiveBoost); + stream.Write(DiffuseBoost); + stream.Write(SpecularBoost); + } + } +} \ No newline at end of file diff --git a/src/Engine/Types/Poly.cs b/src/Engine/Types/Poly.cs new file mode 100644 index 0000000..aa70cc1 --- /dev/null +++ b/src/Engine/Types/Poly.cs @@ -0,0 +1,173 @@ +using System; +using System.ComponentModel; +using System.Numerics; +using UELib.Annotations; +using UELib.Branch; +using UELib.Core; +using UELib.ObjectModel.Annotations; + +namespace UELib.Engine +{ + /// + /// Implements FPoly + /// + [Output("Polygon")] + public class Poly : IUnrealSerializableClass, IAcceptable + { + [CanBeNull] public UObject Actor; + + public int BrushPoly = -1; + + [DefaultValue(null)] [Output("Item", OutputSlot.Parameter)] + public UName ItemName; + + [DefaultValue(null)] [Output("Texture")] + public UObject Material; + + [DefaultValue(3584u)] [Output("Flags", OutputSlot.Parameter)] + public uint PolyFlags = 3584u; + + [DefaultValue(-1)] [Output(OutputSlot.Parameter)] + public int Link = -1; + + [DefaultValue(32.0f)] [Output(OutputSlot.Parameter)] + public float ShadowMapScale = 32.0f; + + //[Output(OutputSlot.Parameter)] + public LightingChannelContainer LightingChannels; + + [Output("Origin", OutputFlags.ShorthandProperty)] + public UVector Base; + + [Output(OutputFlags.ShorthandProperty)] + public UVector Normal; + + [DefaultValue(0)] [Output("U")] public short PanU; + [DefaultValue(0)] [Output("V")] public short PanV; + + [Output(OutputFlags.ShorthandProperty)] + public UVector TextureU; + + [Output(OutputFlags.ShorthandProperty)] + public UVector TextureV; + + [Output(OutputFlags.ShorthandProperty)] + public UArray Vertex; + + [DefaultValue(32.0f)] public float LightMapScale = 32.0f; // Not exported for some reason + + public LightmassPrimitiveSettings LightmassSettings; + + [DefaultValue(null)] public UProcBuildingRuleset Ruleset; + [DefaultValue(null)] public UName RulesetVariation; + + public void Accept(IVisitor visitor) + { + visitor.Visit(this); + } + + public TResult Accept(IVisitor visitor) + { + return visitor.Visit(this); + } + + public void Deserialize(IUnrealStream stream) + { + // Always 16 + int numVertices = stream.Version < (uint)PackageObjectLegacyVersion.FixedVerticesToArrayFromPoly + ? stream.ReadIndex() + : -1; + + stream.ReadStruct(out Base); + stream.ReadStruct(out Normal); + stream.ReadStruct(out TextureU); + stream.ReadStruct(out TextureV); + + if (stream.Version >= (uint)PackageObjectLegacyVersion.FixedVerticesToArrayFromPoly) + { + stream.Read(out Vertex); + } + else + { + stream.ReadArray(out Vertex, numVertices); + } + + PolyFlags = stream.ReadUInt32(); + if (stream.Version < 250) + { + PolyFlags |= 0xe00; + } + + Actor = stream.ReadObject(); + if (stream.Version < (uint)PackageObjectLegacyVersion.TextureDeprecatedFromPoly) + { + Material = stream.ReadObject(); + } + + ItemName = stream.ReadNameReference(); + // The fact this is serialized after ItemName indicates we may have had both (A texture and material) at one point. + if (stream.Version >= (uint)PackageObjectLegacyVersion.MaterialAddedToPoly) + { + Material = stream.ReadObject(); + } + + Link = stream.ReadIndex(); + BrushPoly = stream.ReadIndex(); + + if (stream.Version < (uint)PackageObjectLegacyVersion.PanUVRemovedFromPoly) + { + PanU = stream.ReadInt16(); + PanV = stream.ReadInt16(); + + // Fix UV + var newBase = (Vector3)Base; + newBase -= (Vector3)TextureU / ((Vector3)TextureU).LengthSquared() * PanU; + newBase -= (Vector3)TextureV / ((Vector3)TextureV).LengthSquared() * PanV; + Base = (UVector)newBase; + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightMapScaleAddedToPoly && + stream.Version < (uint)PackageObjectLegacyVersion.LightMapScaleRemovedFromPoly) + { + LightMapScale = stream.ReadFloat(); + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.ShadowMapScaleAddedToPoly) + { + ShadowMapScale = stream.ReadFloat(); + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightingChannelsAddedToPoly) + { + stream.ReadAtomicStruct(out LightingChannels); + } + else + { + LightingChannels.Initialized = true; + LightingChannels.BSP = true; + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.LightmassAdded) + { + stream.ReadStruct(out LightmassSettings); + } + + if (stream.Version >= (uint)PackageObjectLegacyVersion.UProcBuildingReferenceAddedToPoly) + { + if (stream.Version >= (uint)PackageObjectLegacyVersion.PolyRulesetVariationTypeChangedToName) + { + stream.Read(out RulesetVariation); + } + else + { + stream.Read(out Ruleset); + } + } + } + + public void Serialize(IUnrealStream stream) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file