diff --git a/CodeWalker.Core/GameFiles/FileTypes/WatermapFile.cs b/CodeWalker.Core/GameFiles/FileTypes/WatermapFile.cs new file mode 100644 index 0000000..e98e8d4 --- /dev/null +++ b/CodeWalker.Core/GameFiles/FileTypes/WatermapFile.cs @@ -0,0 +1,550 @@ +using System; +using System.Collections.Generic; +using System.Text; +using TC = System.ComponentModel.TypeConverterAttribute; +using EXP = System.ComponentModel.ExpandableObjectConverter; +using System.IO; +using System.Xml; +using SharpDX; + +namespace CodeWalker.GameFiles +{ + [TC(typeof(EXP))] + public class WatermapFile : GameFile, PackedFile + { + public byte[] RawFileData { get; set; } + + public uint Magic { get; set; } = 0x574D4150; //'WMAP' + public uint Version { get; set; } = 100; + public uint DataLength { get; set; } //59360 - data length + public float CornerX { get; set; } //-4050.0f - topleft X + public float CornerY { get; set; } //8400.0f - topleft Y + public float TileX { get; set; } //50.0f - tile size X + public float TileY { get; set; } //50.0f - tile size Y (step negative?) + public ushort Width { get; set; } //183 - image Width + public ushort Height { get; set; } //249 - image Height + public uint WatermapIndsCount { get; set; } //10668 + public uint WatermapRefsCount { get; set; } //11796 + public ushort RiverVecsCount { get; set; } //99 + public ushort RiverCount { get; set; } //13 + public ushort LakeVecsCount { get; set; } //28 + public ushort LakeCount { get; set; } //15 + public ushort PoolCount { get; set; } //314 + public ushort ColoursOffset { get; set; } //13316 + public byte[] Unks1 { get; set; }//2,2,16,48,16,48,32,0 ..? + + public CompHeader[] CompHeaders { get; set; } + public short[] CompWatermapInds { get; set; }//indices into CompWatermapRefs + public WaterItemRef[] CompWatermapRefs { get; set; }//contains multibit, type, index1, [index2](optional) + public byte[] Zeros1 { get; set; }//x12 + public Vector4[] RiverVecs { get; set; } + public WaterFlow[] Rivers { get; set; } + public Vector4[] LakeVecs { get; set; } + public WaterFlow[] Lakes { get; set; } + public WaterPool[] Pools { get; set; } + public Color[] Colours { get; set; }//x342 + public uint ColourCount { get; set; }//342 (RiverCount + LakeCount + PoolCount) + + + public short[] GridWatermapInds { get; set; } //expanded from CompWatermapInds. + public WaterItemRef[][] GridWatermapRefs { get; set; } //expanded from CompWatermapHeaders. ends up max 7 items + + + public WatermapFile() : base(null, GameFileType.Watermap) + { + } + public WatermapFile(RpfFileEntry entry) : base(entry, GameFileType.Watermap) + { + RpfFileEntry = entry; + } + + public void Load(byte[] data, RpfFileEntry entry) + { + RawFileData = data; + if (entry != null) + { + RpfFileEntry = entry; + Name = entry.Name; + } + + using (MemoryStream ms = new MemoryStream(data)) + { + DataReader r = new DataReader(ms, Endianess.BigEndian); + + Read(r); + } + } + + public byte[] Save() + { + MemoryStream s = new MemoryStream(); + DataWriter w = new DataWriter(s, Endianess.BigEndian); + + Write(w); + + var buf = new byte[s.Length]; + s.Position = 0; + s.Read(buf, 0, buf.Length); + return buf; + } + + + private void Read(DataReader r) + { + Magic = r.ReadUInt32();//'WMAP' + Version = r.ReadUInt32();//100 - version? + DataLength = r.ReadUInt32();//59360 - data length (excluding last flags array!) + CornerX = r.ReadSingle();//-4050.0f - min XY? + CornerY = r.ReadSingle();//8400.0f - max XY? + TileX = r.ReadSingle();//50.0f - tile size X + TileY = r.ReadSingle();//50.0f - tile size Y + Width = r.ReadUInt16();//183 - image Width + Height = r.ReadUInt16();//249 - image Height + WatermapIndsCount = r.ReadUInt32();//10668 + WatermapRefsCount = r.ReadUInt32();//11796 + RiverVecsCount = r.ReadUInt16();//99 + RiverCount = r.ReadUInt16();//13 + LakeVecsCount = r.ReadUInt16();//28 + LakeCount = r.ReadUInt16();//15 + PoolCount = r.ReadUInt16();//314 + ColoursOffset = r.ReadUInt16();//13316 + Unks1 = r.ReadBytes(8);//2,2,16,48,16,48,32,0 flags..? + + + var shortslen = (int)((WatermapIndsCount + WatermapRefsCount) * 2) + (Height * 4);//offset from here to Zeros1 + var padcount = (16 - (shortslen % 16)) % 16;//12 .. is this right? all are zeroes. + var strucslen = ((RiverVecsCount + LakeVecsCount) * 16) + ((RiverCount + LakeCount) * 48) + (PoolCount * 32); + var datalen = shortslen + padcount + strucslen; //DataLength calculation + var extoffs = padcount + strucslen - 60 - 60;//ExtraFlagsOffset calculation + + + CompHeaders = new CompHeader[Height];//249 - image height + for (int i = 0; i < Height; i++) CompHeaders[i].Read(r); + + CompWatermapInds = new short[WatermapIndsCount];//10668 + for (int i = 0; i < WatermapIndsCount; i++) CompWatermapInds[i] = r.ReadInt16(); + + CompWatermapRefs = new WaterItemRef[WatermapRefsCount];//11796 + for (int i = 0; i < WatermapRefsCount; i++) CompWatermapRefs[i] = new WaterItemRef(r.ReadUInt16()); + + Zeros1 = r.ReadBytes(padcount);//align to 16 bytes (position:45984) + + RiverVecs = new Vector4[RiverVecsCount];//99 + for (int i = 0; i < RiverVecsCount; i++) RiverVecs[i] = r.ReadVector4(); + + Rivers = new WaterFlow[RiverCount];//13 + for (int i = 0; i < RiverCount; i++) Rivers[i] = new WaterFlow(WaterItemType.River, r, RiverVecs); + + LakeVecs = new Vector4[LakeVecsCount];//28 + for (int i = 0; i < LakeVecsCount; i++) LakeVecs[i] = r.ReadVector4(); + + Lakes = new WaterFlow[LakeCount];//15 + for (int i = 0; i < LakeCount; i++) Lakes[i] = new WaterFlow(WaterItemType.Lake, r, LakeVecs); + + Pools = new WaterPool[PoolCount];//314 + for (int i = 0; i < PoolCount; i++) Pools[i] = new WaterPool(r); + + ColourCount = (uint)(RiverCount + LakeCount + PoolCount); //342 + Colours = new Color[ColourCount]; //342 + for (int i = 0; i < 342; i++) Colours[i] = Color.FromAbgr(r.ReadUInt32()); + + + var flagoff = 0; //assign extra colours out of the main array + for (int i = 0; i < Rivers.Length; i++) + { + var river = Rivers[i]; + river.Colour = Colours[flagoff++]; + } + for (int i = 0; i < Lakes.Length; i++) + { + var lake = Lakes[i]; + lake.Colour = Colours[flagoff++]; + } + for (int i = 0; i < Pools.Length; i++) + { + var pool = Pools[i]; + pool.Colour = Colours[flagoff++]; + } + + + + for (int i = 0; i < CompWatermapRefs.Length; i++) //assign items to CompWatermapRefs + { + var ir = CompWatermapRefs[i]; + switch (ir.Type) + { + case WaterItemType.River: CompWatermapRefs[i].Item = Rivers[ir.ItemIndex]; break; + case WaterItemType.Lake: CompWatermapRefs[i].Item = Lakes[ir.ItemIndex]; break; + case WaterItemType.Pool: CompWatermapRefs[i].Item = Pools[ir.ItemIndex]; break; + } + } + + + + //decompress main data into grid form + GridWatermapInds = new short[Width * Height]; + GridWatermapRefs = new WaterItemRef[Width * Height][]; + var reflist = new List(); + for (int y = 0; y < Height; y++) + { + var ch = CompHeaders[y]; + for (int i = 0; i < ch.Count; i++) + { + var x = ch.Start + i; + var n = CompWatermapInds[ch.Offset + i]; + var o = y * Width + x; + + reflist.Clear(); + WaterItemRef[] refarr = null; + if (n >= 0) + { + var h = CompWatermapRefs[n]; + reflist.Add(h); + var cn = n; + while (h.EndOfList == false) + { + cn++; + h = CompWatermapRefs[cn]; + reflist.Add(h); + } + + refarr = reflist.ToArray(); + } + + GridWatermapInds[o] = n; + GridWatermapRefs[o] = refarr; + } + } + + + + + + + + //var pgm = GetPGM(); + + + var rem = r.Length - r.Position;//60788 + if (rem != 0) + { } + + + + + + + + //var sb = new StringBuilder(); + //for (int y = Height - 1; y >= 0; y--) + //{ + // for (int x = 0; x < Width; x++) + // { + // var v = GridWatermapVals[y * Width + x]; + // sb.Append(Convert.ToString(v, 16).ToUpperInvariant().PadLeft(4, '0')); + // sb.Append(" "); + // } + // sb.AppendLine(); + //} + //var hstr = sb.ToString(); + + + + + } + private void Write(DataWriter w) + { + + + w.Write(Magic); + + } + + + public void WriteXml(StringBuilder sb, int indent) + { + //HmapXml.ValueTag(sb, indent, "Width", Width.ToString()); + //HmapXml.ValueTag(sb, indent, "Height", Height.ToString()); + //HmapXml.SelfClosingTag(sb, indent, "BBMin " + FloatUtil.GetVector3XmlString(BBMin)); + //HmapXml.SelfClosingTag(sb, indent, "BBMax " + FloatUtil.GetVector3XmlString(BBMax)); + //HmapXml.WriteRawArray(sb, InvertImage(MaxHeights, Width, Height), indent, "MaxHeights", "", HmapXml.FormatHexByte, Width); + //HmapXml.WriteRawArray(sb, InvertImage(MinHeights, Width, Height), indent, "MinHeights", "", HmapXml.FormatHexByte, Width); + } + public void ReadXml(XmlNode node) + { + //Width = (ushort)Xml.GetChildUIntAttribute(node, "Width"); + //Height = (ushort)Xml.GetChildUIntAttribute(node, "Height"); + //BBMin = Xml.GetChildVector3Attributes(node, "BBMin"); + //BBMax = Xml.GetChildVector3Attributes(node, "BBMax"); + //MaxHeights = InvertImage(Xml.GetChildRawByteArray(node, "MaxHeights"), Width, Height); + //MinHeights = InvertImage(Xml.GetChildRawByteArray(node, "MinHeights"), Width, Height); + } + + + + public struct CompHeader + { + public byte Start { get; set; } + public byte Count { get; set; } + public ushort Offset { get; set; } + + public void Read(DataReader r) + { + Start = r.ReadByte(); + Count = r.ReadByte(); + Offset = r.ReadUInt16(); + } + + public override string ToString() + { + return string.Format("{0}, {1}, {2}", + Start, Count, Offset); + } + } + + + public struct WaterItemRef + { + public ushort RawValue { get; set; } + + public bool EndOfList { get { return ((RawValue >> 15) & 0x1) == 1; } } //highest bit indicates if it's at the end of the list + public WaterItemType Type { get { return (WaterItemType)((RawValue >> 13) & 0x3); } } //next 2 bits are the item type + public ushort ItemIndex + { + get + { + switch (Type) + { + case WaterItemType.River: + case WaterItemType.Lake: + return (ushort)((RawValue >> 7) & 0x3F); + case WaterItemType.Pool: + default: + return (ushort)(RawValue & 0x7FF); + } + } + } + public ushort VectorIndex + { + get + { + switch (Type) + { + case WaterItemType.River: + case WaterItemType.Lake: + return (ushort)(RawValue & 0x7F); + case WaterItemType.Pool: + default: + return 0; + } + } + } + + public WaterItem Item { get; set; } //lookup reference + public Vector4 Vector + { + get + { + if (Item?.Vectors == null) return Vector4.Zero; + if (VectorIndex >= Item.Vectors.Length) return Vector4.Zero; + return Item.Vectors[VectorIndex]; + } + } + + public WaterItemRef(ushort rawval) { RawValue = rawval; Item = null; } + + public override string ToString() + { + if (Item != null) return Item.ToString() + ": " + Vector.ToString(); + return Type.ToString() + ": " + ItemIndex.ToString() + ": " + VectorIndex.ToString(); + } + } + public enum WaterItemType + { + None = 0, + River = 1, + Lake = 2, + Pool = 3, + } + public abstract class WaterItem + { + //length:32 + public Vector3 Position { get; set; } + public uint Unk04 { get; set; }//0 + public Vector3 Size { get; set; } + public uint Unk09 { get; set; }//0 + + public WaterItemType Type { get; private set; } + + public Vector4[] Vectors { get; set; }//built from packed data + public Color Colour { get; set; } //from the end of the file + + public WaterItem(WaterItemType type) + { + Type = type; + } + + public virtual void Read(DataReader r) + { + Position = r.ReadVector3(); + Unk04 = r.ReadUInt32(); + Size = r.ReadVector3(); + Unk09 = r.ReadUInt32(); + + if (Unk04 != 0) + { } + if (Unk09 != 0) + { } + } + + public override string ToString() + { + return string.Format("{0} - Size: {1}, Pos: {2}", Type, Size, Position); + } + } + public class WaterFlow : WaterItem + { + //length:48 (including base) + public byte VectorCount { get; set; } + public byte Unk11 { get; set; }//0 + public ushort VectorOffset { get; set; } + public uint Unk13 { get; set; }//0 + public uint Unk14 { get; set; }//0 + public uint Unk15 { get; set; }//0 + + public WaterFlow(WaterItemType type) : base(type) { } + public WaterFlow(WaterItemType type, DataReader r, Vector4[] vecs) : base(type) + { + Read(r); + + if (VectorCount > 0) + { + Vectors = new Vector4[VectorCount]; + for (int i = 0; i < VectorCount; i++) + { + Vectors[i] = vecs[VectorOffset + i]; + } + } + } + + public override void Read(DataReader r) + { + base.Read(r); + VectorCount = r.ReadByte(); + Unk11 = r.ReadByte(); + VectorOffset = r.ReadUInt16(); + Unk13 = r.ReadUInt32(); + Unk14 = r.ReadUInt32(); + Unk15 = r.ReadUInt32(); + + //if (Unk11 != 0) + //{ } + //if (Unk13 != 0) + //{ } + //if (Unk14 != 0) + //{ } + //if (Unk15 != 0) + //{ } + + } + + public override string ToString() + { + return base.ToString() + " : " + VectorCount.ToString(); + } + } + public class WaterPool : WaterItem + { + //length:32 (from base) + + public WaterPool() : base(WaterItemType.Pool) { } + public WaterPool(DataReader r) : base(WaterItemType.Pool) { Read(r); } + + public override void Read(DataReader r) + { + base.Read(r); + } + + public override string ToString() + { + return base.ToString(); + } + } + + + + + public string GetPGM() + { + if (GridWatermapInds == null) return string.Empty; + + var sb = new StringBuilder(); + sb.AppendFormat("P2\n{0} {1}\n65535\n", Width, Height); + //sb.AppendFormat("P2\n{0} {1}\n255\n", Width, Height); + + for (int y = 0; y < Height; y++) + { + for (int x = 0; x < Width; x++) + { + var h = GridWatermapInds[y * Width + x]; + sb.Append(h.ToString()); + sb.Append(" "); + } + sb.Append("\n"); + } + + return sb.ToString(); + } + + + + } + + + public class WatermapXml : MetaXmlBase + { + + public static string GetXml(WatermapFile wmf) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine(XmlHeader); + + if ((wmf != null)) + { + var name = "Watermap"; + + OpenTag(sb, 0, name); + + wmf.WriteXml(sb, 1); + + CloseTag(sb, 0, name); + } + + return sb.ToString(); + } + + + } + + + public class XmlWatermap + { + + public static WatermapFile GetWatermap(string xml) + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + return GetWatermap(doc); + } + + public static WatermapFile GetWatermap(XmlDocument doc) + { + WatermapFile wmf = new WatermapFile(); + wmf.ReadXml(doc.DocumentElement); + return wmf; + } + + + } + +} diff --git a/CodeWalker.Core/GameFiles/GameFile.cs b/CodeWalker.Core/GameFiles/GameFile.cs index 6953d32..a436c2f 100644 --- a/CodeWalker.Core/GameFiles/GameFile.cs +++ b/CodeWalker.Core/GameFiles/GameFile.cs @@ -81,6 +81,7 @@ namespace CodeWalker.GameFiles Yld = 25, Yfd = 26, Heightmap = 27, + Watermap = 28, } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index b7fbbfd..eecd23f 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -200,6 +200,7 @@ namespace CodeWalker.GameFiles //TestPlacements(); //TestDrawables(); //TestHeightmaps(); + //TestWatermaps(); //GetShadersXml(); //GetArchetypeTimesList(); //string typestr = PsoTypes.GetTypesString(); @@ -3953,6 +3954,8 @@ namespace CodeWalker.GameFiles { bool savetest = false; var errorfiles = new List(); + var sb = new StringBuilder(); + int[] flagcheck = new int[16]; foreach (RpfFile file in AllRpfs) { foreach (RpfEntry entry in file.AllEntries) @@ -3993,6 +3996,25 @@ namespace CodeWalker.GameFiles { continue; } } + var groups = yft?.Fragment?.PhysicsLODGroup?.PhysicsLOD1?.Groups?.data_items; + if (groups != null) + { + foreach (var g in groups) + { + ushort f = (ushort)(g.UnkByte52 + (g.UnkByte53 << 8)); + for (int i = 0; i < 16; i++) + { + if (flagcheck[i]>=3) continue; + var t = 1 << i; + if ((f & t) > 0) + { + sb.AppendLine(entry.Path + ": " + g.Name.ToString() + ", UnkByte52:" + g.UnkByte52.ToString() + ", UnkByte53:" + g.UnkByte53.ToString() + " zflag:" + (i+1).ToString()); + flagcheck[i]++; + } + } + + } + } } } //catch (Exception ex) @@ -4001,6 +4023,8 @@ namespace CodeWalker.GameFiles //} } } + var teststr = sb.ToString(); + if (errorfiles.Count > 0) { } } @@ -4538,6 +4562,41 @@ namespace CodeWalker.GameFiles if (errorfiles.Count > 0) { } } + public void TestWatermaps() + { + var errorfiles = new List(); + foreach (RpfFile file in AllRpfs) + { + foreach (RpfEntry entry in file.AllEntries) + { + if (entry.NameLower.EndsWith(".dat") && entry.NameLower.StartsWith("waterheight")) + { + UpdateStatus(string.Format(entry.Path)); + WatermapFile wmf = null; + wmf = RpfMan.GetFile(entry); + //var d1 = wmf.RawFileData; + //var d2 = wmf.Save(); + //var xml = WatermapXml.GetXml(wmf); + //var wmf2 = XmlWatermap.GetWatermap(xml); + //var d2 = wmf2.Save(); + + //if (d1.Length == d2.Length) + //{ + // for (int i = 0; i < d1.Length; i++) + // { + // if (d1[i] != d2[i]) + // { } + // } + //} + //else + //{ } + + } + } + } + if (errorfiles.Count > 0) + { } + } public void GetShadersXml() { bool doydr = true; diff --git a/CodeWalker.Core/World/Heightmaps.cs b/CodeWalker.Core/World/Heightmaps.cs new file mode 100644 index 0000000..e5f614d --- /dev/null +++ b/CodeWalker.Core/World/Heightmaps.cs @@ -0,0 +1,170 @@ +using CodeWalker.GameFiles; +using SharpDX; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodeWalker.World +{ + public class Heightmaps : BasePathData + { + public volatile bool Inited = false; + public GameFileCache GameFileCache; + + public List HeightmapFiles = new List(); + + + public Vector4[] GetNodePositions() + { + return NodePositions; + } + public EditorVertex[] GetPathVertices() + { + return null; + } + public EditorVertex[] GetTriangleVertices() + { + return TriangleVerts; + } + + public Vector4[] NodePositions; + public EditorVertex[] TriangleVerts; + + + public void Init(GameFileCache gameFileCache, Action updateStatus) + { + Inited = false; + + GameFileCache = gameFileCache; + + + HeightmapFiles.Clear(); + + + if (gameFileCache.EnableDlc) + { + LoadHeightmap("update\\update.rpf\\common\\data\\levels\\gta5\\heightmap.dat"); + LoadHeightmap("update\\update.rpf\\common\\data\\levels\\gta5\\heightmapheistisland.dat"); + } + else + { + LoadHeightmap("common.rpf\\data\\levels\\gta5\\heightmap.dat"); + } + + + BuildVertices(); + + Inited = true; + } + + private void LoadHeightmap(string filename) + { + var hmf = GameFileCache.RpfMan.GetFile(filename); + HeightmapFiles.Add(hmf); + } + + + + public void BuildVertices() + { + + var vlist = new List(); + var nlist = new List(); + + foreach (var hmf in HeightmapFiles) + { + BuildHeightmapVertices(hmf, vlist, nlist); + } + + if (vlist.Count > 0) + { + TriangleVerts = vlist.ToArray(); + } + else + { + TriangleVerts = null; + } + if (nlist.Count > 0) + { + NodePositions = nlist.ToArray(); + } + else + { + NodePositions = null; + } + + } + private void BuildHeightmapVertices(HeightmapFile hmf, List vl, List nl) + { + var v1 = new EditorVertex(); + var v2 = new EditorVertex(); + var v3 = new EditorVertex(); + var v4 = new EditorVertex(); + + uint cgrn = (uint)new Color(0, 128, 0, 60).ToRgba(); + uint cyel = (uint)new Color(128, 128, 0, 200).ToRgba(); + + var w = hmf.Width; + var h = hmf.Height; + var hmin = hmf.MinHeights; + var hmax = hmf.MaxHeights; + var min = hmf.BBMin; + var max = hmf.BBMax; + var siz = max - min; + var step = siz / new Vector3(w - 1, h - 1, 255); + + v1.Colour = v2.Colour = v3.Colour = v4.Colour = cyel; + for (int yi = 1; yi < h; yi++) + { + var yo = yi - 1; + for (int xi = 1; xi < w; xi++) + { + var xo = xi - 1; + var o1 = yo * w + xo; + var o2 = yo * w + xi; + var o3 = yi * w + xo; + var o4 = yi * w + xi; + v1.Position = min + step * new Vector3(xo, yo, hmin[o1]); + v2.Position = min + step * new Vector3(xi, yo, hmin[o2]); + v3.Position = min + step * new Vector3(xo, yi, hmin[o3]); + v4.Position = min + step * new Vector3(xi, yi, hmin[o4]); + vl.Add(v1); vl.Add(v2); vl.Add(v3); + vl.Add(v3); vl.Add(v2); vl.Add(v4); + } + } + v1.Colour = v2.Colour = v3.Colour = v4.Colour = cgrn; + for (int yi = 1; yi < h; yi++) + { + var yo = yi - 1; + for (int xi = 1; xi < w; xi++) + { + var xo = xi - 1; + var o1 = yo * w + xo; + var o2 = yo * w + xi; + var o3 = yi * w + xo; + var o4 = yi * w + xi; + v1.Position = min + step * new Vector3(xo, yo, hmax[o1]); + v2.Position = min + step * new Vector3(xi, yo, hmax[o2]); + v3.Position = min + step * new Vector3(xo, yi, hmax[o3]); + v4.Position = min + step * new Vector3(xi, yi, hmax[o4]); + vl.Add(v1); vl.Add(v2); vl.Add(v3); + vl.Add(v3); vl.Add(v2); vl.Add(v4); + } + } + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + var o = y * w + x; + nl.Add(new Vector4(min + step * new Vector3(x, y, hmin[o]), 10)); + nl.Add(new Vector4(min + step * new Vector3(x, y, hmax[o]), 10)); + } + } + + + } + + + } +} diff --git a/CodeWalker.Core/World/Watermaps.cs b/CodeWalker.Core/World/Watermaps.cs new file mode 100644 index 0000000..f715ae4 --- /dev/null +++ b/CodeWalker.Core/World/Watermaps.cs @@ -0,0 +1,290 @@ +using CodeWalker.GameFiles; +using SharpDX; +using System; +using System.Collections.Generic; +using System.Text; + +namespace CodeWalker.World +{ + public class Watermaps : BasePathData + { + public volatile bool Inited = false; + public GameFileCache GameFileCache; + + public List WatermapFiles = new List(); + + + public Vector4[] GetNodePositions() + { + return NodePositions; + } + public EditorVertex[] GetPathVertices() + { + return null; + } + public EditorVertex[] GetTriangleVertices() + { + return TriangleVerts; + } + + public Vector4[] NodePositions; + public EditorVertex[] TriangleVerts; + + + public void Init(GameFileCache gameFileCache, Action updateStatus) + { + Inited = false; + + GameFileCache = gameFileCache; + + + WatermapFiles.Clear(); + + + LoadWatermap("common.rpf\\data\\levels\\gta5\\waterheight.dat"); + + + + BuildVertices(); + + Inited = true; + } + + private void LoadWatermap(string filename) + { + var wmf = GameFileCache.RpfMan.GetFile(filename); + WatermapFiles.Add(wmf); + } + + + + public void BuildVertices() + { + + var vlist = new List(); + var nlist = new List(); + + foreach (var wmf in WatermapFiles) + { + BuildWatermapVertices(wmf, vlist, nlist); + } + + if (vlist.Count > 0) + { + TriangleVerts = vlist.ToArray(); + } + else + { + TriangleVerts = null; + } + if (nlist.Count > 0) + { + NodePositions = nlist.ToArray(); + } + else + { + NodePositions = null; + } + + } + private void BuildWatermapVertices(WatermapFile wmf, List vl, List nl) + { + var v1 = new EditorVertex(); + var v2 = new EditorVertex(); + var v3 = new EditorVertex(); + var v4 = new EditorVertex(); + + uint cblu = (uint)new Color(0, 0, 128, 60).ToRgba(); + + + float getHeight(int o) + { + var harr = wmf.GridWatermapRefs[o]; + if (harr == null) return 0; + if (harr.Length == 0) return 0; + var h0 = harr[0]; + var i0 = h0.Item; + if (h0.Type == WatermapFile.WaterItemType.River) + { + return h0.Vector.Z; + } + if (h0.Type == WatermapFile.WaterItemType.Lake) + { + if (i0 != null) return i0.Position.Z; + } + if (h0.Type == WatermapFile.WaterItemType.Pool) + { + if (i0 != null) return i0.Position.Z; + } + return h0.Vector.Z; + } + uint getColour(int o) + { + var harr = wmf.GridWatermapRefs[o]; + if (harr == null) return cblu; + if (harr.Length == 0) return cblu; + var i0 = harr[0].Item; + if (i0 == null) return cblu; + var c = i0.Colour; + c.A = 128; + return (uint)c.ToRgba(); + } + var w = wmf.Width; + var h = wmf.Height; + var min = new Vector3(wmf.CornerX, wmf.CornerY, 0.0f); + var step = new Vector3(wmf.TileX, -wmf.TileY, 1.0f); + //var siz = new Vector3(w, h, 1) * step; + for (int yi = 1; yi < h; yi++) + { + var yo = yi - 1; + for (int xi = 1; xi < w; xi++) + { + var xo = xi - 1; + var o1 = yi * w + xo; + var o2 = yi * w + xi; + var o3 = yo * w + xo; + var o4 = yo * w + xi; + v1.Position = min + step * new Vector3(xo, yi, getHeight(o1)); + v2.Position = min + step * new Vector3(xi, yi, getHeight(o2)); + v3.Position = min + step * new Vector3(xo, yo, getHeight(o3)); + v4.Position = min + step * new Vector3(xi, yo, getHeight(o4)); + v1.Colour = getColour(o1); + v2.Colour = getColour(o2); + v3.Colour = getColour(o3); + v4.Colour = getColour(o4); + //vl.Add(v1); vl.Add(v2); vl.Add(v3); + //vl.Add(v3); vl.Add(v2); vl.Add(v4); + } + } + //for (int y = 0; y < h; y++) + //{ + // for (int x = 0; x < w; x++) + // { + // var o = y * w + x; + // nl.Add(new Vector4(min + step * new Vector3(x, y, getHeight(o)), 10)); + // } + //} + + + void addQuad(Quad q) + { + v1.Position = q.P1; + v2.Position = q.P2; + v3.Position = q.P3; + v4.Position = q.P4; + vl.Add(v1); vl.Add(v2); vl.Add(v3); + vl.Add(v3); vl.Add(v2); vl.Add(v4); + } + void addRivEnd(Vector3 p, Vector3 s, Vector3 d, float r) + { + v1.Position = p; + v2.Position = p + s * r; + v3.Position = p + d * r; + v4.Position = p - s * r; + vl.Add(v1); vl.Add(v2); vl.Add(v3); + vl.Add(v1); vl.Add(v3); vl.Add(v4); + } + var rivers = wmf.Rivers; + if (rivers != null) + { + foreach (var river in rivers) + { + if ((river.Vectors == null) || (river.VectorCount <= 1)) + { continue; } + + var rwid = 20.0f; + var rc = river.Colour; + rc.A = 128; + v1.Colour = v2.Colour = v3.Colour = v4.Colour = (uint)rc.ToRgba(); + var quads = new Quad[river.Vectors.Length - 1]; + var li = river.Vectors.Length - 1; + for (int i = 1; i < river.Vectors.Length; i++) + { + var o = i - 1; + var vo = river.Vectors[o]; + var vi = river.Vectors[i]; + var dif = vi.XYZ() - vo.XYZ(); + var dir = Vector3.Normalize(dif); + var sid = Vector3.Normalize(Vector3.Cross(dir, Vector3.UnitZ)); + if (Math.Abs(dir.Z) > 0.95f) + { + dir = Vector3.UnitY; + sid = Vector3.UnitX; + } + quads[o].P1 = vo.XYZ() - sid*rwid; + quads[o].P2 = vo.XYZ() + sid*rwid; + quads[o].P3 = vi.XYZ() - sid*rwid; + quads[o].P4 = vi.XYZ() + sid*rwid; + if (i == 1) addRivEnd(vo.XYZ(), -sid, -dir, rwid); + if (i == li) addRivEnd(vi.XYZ(), sid, dir, rwid); + } + for (int i = 1; i < quads.Length; i++) + { + var o = i - 1; + quads[o].P3 = quads[i].P1 = (quads[o].P3 + quads[i].P1) * 0.5f; + quads[o].P4 = quads[i].P2 = (quads[o].P4 + quads[i].P2) * 0.5f; + } + for (int i = 0; i < quads.Length; i++) + { + addQuad(quads[i]); + } + } + } + var lakes = wmf.Lakes; + if (lakes != null) + { + foreach (var lake in lakes) + { + if ((lake.Vectors == null) || (lake.VectorCount == 0)) + { continue; } + + var lp = lake.Position; + var lc = lake.Colour; + lc.A = 128; + v1.Colour = v2.Colour = v3.Colour = v4.Colour = (uint)lc.ToRgba(); + for (int i = 0; i < lake.Vectors.Length; i++) + { + var vi = lake.Vectors[i]; + var vp = new Vector3(vi.X, vi.Y, lp.Z); + var q = new Quad(); + q.P1 = vp + new Vector3(vi.Z, -vi.W, 0); + q.P2 = vp + new Vector3(vi.Z, vi.W, 0); + q.P3 = vp + new Vector3(-vi.Z, -vi.W, 0); + q.P4 = vp + new Vector3(-vi.Z, vi.W, 0); + addQuad(q); + } + } + } + var pools = wmf.Pools; + if (pools != null) + { + foreach (var pool in pools) + { + var pp = pool.Position; + var ps = pool.Size; + var pc = pool.Colour; + pc.A = 128; + v1.Colour = v2.Colour = v3.Colour = v4.Colour = (uint)pc.ToRgba(); + var q = new Quad(); + q.P1 = pp + new Vector3(ps.X, -ps.Y, 0); + q.P2 = pp + new Vector3(ps.X, ps.Y, 0); + q.P3 = pp + new Vector3(-ps.X, -ps.Y, 0); + q.P4 = pp + new Vector3(-ps.X, ps.Y, 0); + addQuad(q); + } + } + + + } + + + + struct Quad + { + public Vector3 P1; + public Vector3 P2; + public Vector3 P3; + public Vector3 P4; + } + } +} diff --git a/CodeWalker/World/MapSelection.cs b/CodeWalker/World/MapSelection.cs index 46dc2ba..cad95af 100644 --- a/CodeWalker/World/MapSelection.cs +++ b/CodeWalker/World/MapSelection.cs @@ -32,8 +32,10 @@ namespace CodeWalker MloInstance = 13, Scenario = 14, PopZone = 15, - Audio = 16, - Occlusion = 17, + Heightmap = 16, + Watermap = 17, + Audio = 18, + Occlusion = 19, } diff --git a/CodeWalker/WorldForm.cs b/CodeWalker/WorldForm.cs index 5ef0602..988902e 100644 --- a/CodeWalker/WorldForm.cs +++ b/CodeWalker/WorldForm.cs @@ -40,6 +40,8 @@ namespace CodeWalker Trains trains = new Trains(); Scenarios scenarios = new Scenarios(); PopZones popzones = new PopZones(); + Heightmaps heightmaps = new Heightmaps(); + Watermaps watermaps = new Watermaps(); AudioZones audiozones = new AudioZones(); public Space Space { get { return space; } } @@ -138,6 +140,8 @@ namespace CodeWalker List renderscenariolist = new List(); bool renderpopzones = false; + bool renderheightmaps = false; + bool renderwatermaps = false; bool renderaudiozones = false; bool renderaudioouterbounds = true; @@ -729,6 +733,14 @@ namespace CodeWalker { RenderWorldPopZones(); } + if (renderheightmaps || (SelectionMode == MapSelectionMode.Heightmap)) + { + RenderWorldHeightmaps(); + } + if (renderwatermaps || (SelectionMode == MapSelectionMode.Watermap)) + { + RenderWorldWatermaps(); + } if (renderaudiozones || (SelectionMode == MapSelectionMode.Audio)) { RenderWorldAudioZones(); @@ -889,6 +901,36 @@ namespace CodeWalker Renderer.RenderPopZones(popzones); } + private void RenderWorldHeightmaps() + { + if (!heightmaps.Inited) return; + + //renderheightmaplist.Clear(); + //renderheightmaplist.AddRange(heightmaps.Heightmaps); + + if (ProjectForm != null) + { + //ProjectForm.GetVisibleHeightmaps(camera, renderheightmaplist); + } + + Renderer.RenderBasePath(heightmaps); + } + + private void RenderWorldWatermaps() + { + if (!watermaps.Inited) return; + + //renderwatermaplist.Clear(); + //renderwatermaplist.AddRange(watermaps.Watermaps); + + if (ProjectForm != null) + { + //ProjectForm.GetVisibleWatermaps(camera, renderwatermaplist); + } + + Renderer.RenderBasePath(watermaps); + } + private void RenderWorldAudioZones() { if (!audiozones.Inited) return; @@ -4010,6 +4052,12 @@ namespace CodeWalker UpdateStatus("Loading popzones..."); popzones.Init(gameFileCache, UpdateStatus); + UpdateStatus("Loading heightmaps..."); + heightmaps.Init(gameFileCache, UpdateStatus); + + UpdateStatus("Loading watermaps..."); + watermaps.Init(gameFileCache, UpdateStatus); + UpdateStatus("Loading audio zones..."); audiozones.Init(gameFileCache, UpdateStatus);