public void BufferLandtable(bool optimize = true) { LandtableFormat format = Format; ConvertToFormat(LandtableFormat.Buffer, optimize, false); Format = format; }
/// <summary> /// Creates a new Landtable from existing collections /// </summary> /// <param name="geometry">Level geometry</param> /// <param name="geometryAnimations">Geometry animations</param> /// <param name="metaData">Various meta data</param> /// <param name="format">Landtable Format</param> public LandTable(List <LandEntry> geometry, List <LandEntryMotion> geometryAnimations, LandtableFormat format) { Geometry = geometry; GeometryAnimations = geometryAnimations; Format = format; MetaData = new MetaData(); string identifier = GenerateIdentifier(); Name = "landtable_" + identifier; GeoName = "collist_" + identifier; GeoAnimName = "animlist_" + identifier; }
/// <summary> /// Reads a landtable from a byte array /// </summary> /// <param name="source"></param> /// <param name="address"></param> /// <param name="imageBase"></param> /// <param name="format"></param> /// <param name="labels"></param> /// <returns></returns> public static LandTable Read(byte[] source, uint address, uint imageBase, LandtableFormat format, Dictionary <uint, string> labels) { string name = labels.ContainsKey(address) ? labels[address] : "landtable_" + address.ToString("X8"); float radius; LandtableAttributes attribs = 0; string identifier = GenerateIdentifier(); List <LandEntry> geometry = new(); string geomName; List <LandEntryMotion> anim = new(); string animName; Dictionary <uint, Attach> attaches = new(); string texName = ""; uint texListPtr; uint tmpaddr; ushort geoCount = source.ToUInt16(address); switch (format) { case LandtableFormat.SA1: case LandtableFormat.SADX: short anicnt = source.ToInt16(address + 2); attribs = (LandtableAttributes)source.ToUInt32(address + 4); radius = source.ToSingle(address + 8); tmpaddr = source.ToUInt32(address + 0xC); if (tmpaddr != 0) { tmpaddr -= imageBase; geomName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "collist_" + tmpaddr.ToString("X8"); for (int i = 0; i < geoCount; i++) { geometry.Add(LandEntry.Read(source, tmpaddr, imageBase, AttachFormat.BASIC, format, labels, attaches)); tmpaddr += 0x24; } } else { geomName = "collist_" + identifier; } tmpaddr = source.ToUInt32(address + 0x10); if (tmpaddr != 0) { tmpaddr -= imageBase; animName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "animlist_" + tmpaddr.ToString("X8"); for (int i = 0; i < anicnt; i++) { anim.Add(LandEntryMotion.Read(source, tmpaddr, imageBase, AttachFormat.BASIC, format == LandtableFormat.SADX, labels, attaches)); tmpaddr += LandEntryMotion.Size; } } else { animName = "animlist_" + identifier; } tmpaddr = source.ToUInt32(address + 0x14); if (tmpaddr != 0) { tmpaddr -= imageBase; texName = source.GetCString(tmpaddr, Encoding.ASCII); } texListPtr = source.ToUInt32(address + 0x18); break; case LandtableFormat.SA2: case LandtableFormat.SA2B: AttachFormat atcFmt = format == LandtableFormat.SA2 ? AttachFormat.CHUNK : AttachFormat.GC; ushort visualCount = source.ToUInt16(address + 2); radius = source.ToSingle(address + 0xC); tmpaddr = source.ToUInt32(address + 0x10); if (tmpaddr != 0) { tmpaddr -= imageBase; geomName = labels.ContainsKey(tmpaddr) ? labels[tmpaddr] : "collist_" + tmpaddr.ToString("X8"); for (int i = 0; i < geoCount; i++) { geometry.Add(LandEntry.Read(source, tmpaddr, imageBase, i >= visualCount ? AttachFormat.BASIC : atcFmt, format, labels, attaches)); tmpaddr += 0x20; } } else { geomName = "collist_" + identifier; } animName = "animlist_" + identifier; tmpaddr = source.ToUInt32(address + 0x18); if (tmpaddr != 0) { tmpaddr -= imageBase; texName = source.GetCString(tmpaddr, Encoding.ASCII); } texListPtr = source.ToUInt32(address + 0x1C); break; default: throw new InvalidDataException("Landtable format not valid"); } return(new(geometry, anim, format) { Name = name, DrawDistance = radius, Attributes = attribs, GeoName = geomName, GeoAnimName = animName, TexListPtr = texListPtr, TextureFileName = texName }); }
public void ConvertToFormat(LandtableFormat newFormat, bool optimize, bool forceUpdate) { if (newFormat == Format && !forceUpdate) { return; } NJObject dummyModel = new(); void convertAttaches(AttachFormat format, HashSet <Attach> attaches, Dictionary <Attach, Attach> attachMap, HashSet <LandEntry> landentries) { foreach (Attach atc in attaches) { dummyModel.Attach = atc; dummyModel.ConvertAttachFormat(format, optimize, false, forceUpdate); attachMap.Add(atc, dummyModel.Attach); } if (format == AttachFormat.Buffer) { return; } foreach (LandEntry le in landentries) { le.Attach = attachMap[le.Attach]; } } var newAtcFormat = newFormat switch { LandtableFormat.SA1 or LandtableFormat.SADX => AttachFormat.BASIC, LandtableFormat.SA2 => AttachFormat.CHUNK, LandtableFormat.SA2B => AttachFormat.GC, _ => AttachFormat.Buffer, }; if (newFormat <= LandtableFormat.SADX || newFormat == LandtableFormat.Buffer) { HashSet <Attach> attaches = Geometry.Select(x => x.Attach).ToHashSet(); convertAttaches(newAtcFormat, attaches, new(), new(Geometry)); } else { if (Format <= LandtableFormat.SADX || Format == LandtableFormat.Buffer) { // attaches that are used for rendering HashSet <Attach> visualAttaches = new(); // Attaches that are used for collision HashSet <Attach> collisionAttaches = new(); HashSet <LandEntry> visualLandEntries = new(); HashSet <LandEntry> collisionLandEntries = new(); // For sa1/dx, of which a landentry can be used for both, collision and rendering HashSet <LandEntry> hybridLandEntries = new(); foreach (LandEntry le in Geometry) { bool isCollision = le.SurfaceAttributes.IsCollision(); bool isVisual = !isCollision || le.SurfaceAttributes.HasFlag(SurfaceAttributes.Visible); // if its neither, we'll just keep it as an invisible visual model. just in case if (isCollision) { collisionAttaches.Add(le.Attach); collisionLandEntries.Add(le); } if (isVisual) { visualAttaches.Add(le.Attach); visualLandEntries.Add(le); } if (isVisual && isCollision) { hybridLandEntries.Add(le); } } visualLandEntries.RemoveWhere(x => hybridLandEntries.Contains(x)); collisionLandEntries.RemoveWhere(x => hybridLandEntries.Contains(x)); Dictionary <Attach, Attach> visualAttachMap = new(); convertAttaches(newAtcFormat, visualAttaches, visualAttachMap, visualLandEntries); Dictionary <Attach, Attach> collisionAttachMap = new(); convertAttaches(AttachFormat.BASIC, collisionAttaches, collisionAttachMap, collisionLandEntries); foreach (LandEntry le in hybridLandEntries) { // the copy will act as collision LandEntry copy = le.ShallowCopy(); copy.Attach = collisionAttachMap[le.Attach]; Geometry.Add(copy); le.Attach = visualAttachMap[le.Attach]; } } else // when converting between sa2 formats { // the collision format for sa2 and sa2b is the same, no conversion needed HashSet <LandEntry> visualGeometry = Geometry.Where(x => x.Attach.Format != AttachFormat.BASIC).ToHashSet(); HashSet <Attach> attaches = visualGeometry.Select(x => x.Attach).ToHashSet(); convertAttaches(newAtcFormat, attaches, new(), new(visualGeometry)); } } Format = newFormat; }
/// <summary> /// Creates an empty landtable /// </summary> /// <param name="format"></param> public LandTable(LandtableFormat format) : this(new List <LandEntry>(), new List <LandEntryMotion>(), format) { }
/// <summary> /// Creates a new Landtable from existing collections /// </summary> /// <param name="geometry">Level geometry</param> /// <param name="metaData">Various meta data</param> /// <param name="format">Landtable Format</param> public LandTable(List <LandEntry> geometry, LandtableFormat format) : this(geometry, new List <LandEntryMotion>(), format) { }