diff --git a/project/code/LGS/Database/ObjectHierarchy.cs b/project/code/LGS/Database/ObjectHierarchy.cs new file mode 100644 index 0000000..cde0a3d --- /dev/null +++ b/project/code/LGS/Database/ObjectHierarchy.cs @@ -0,0 +1,139 @@ + +using System.Collections.Generic; +using KeepersCompound.LGS.Database.Chunks; + +namespace KeepersCompound.LGS.Database; + +public class ObjectHierarchy +{ + public class DarkObject + { + public int objectId; + public int parentId; + public Dictionary properties; + + public DarkObject(int id) + { + objectId = id; + parentId = 0; + properties = new Dictionary(); + } + + public T GetProperty(string propName) where T : Property + { + if (properties.TryGetValue(propName, out var prop)) + { + return (T)prop; + } + return null; + } + } + + private Dictionary _objects; + + public ObjectHierarchy(DbFile db, DbFile gam = null) + { + _objects = new Dictionary(); + + if (db.Chunks.TryGetValue("P$ModelName", out var modelNamesRaw) && + db.Chunks.TryGetValue("P$Scale", out var scalesRaw) && + db.Chunks.TryGetValue("P$RenderTyp", out var renderTypesRaw) && + db.Chunks.TryGetValue("L$MetaProp", out var metaPropLinksRaw) && + db.Chunks.TryGetValue("LD$MetaProp", out var metaPropLinkDataRaw)) + { + var modelNames = (PropertyChunk)modelNamesRaw; + var scales = (PropertyChunk)scalesRaw; + var renderTypes = (PropertyChunk)renderTypesRaw; + var metaPropLinks = (LinkChunk)metaPropLinksRaw; + var metaPropLinkData = (LinkDataMetaProp)metaPropLinkDataRaw; + + // Merge gam chunks + if (gam != null && + gam.Chunks.TryGetValue("P$ModelName", out var gamModelNames) && + gam.Chunks.TryGetValue("P$Scale", out var gamScales) && + gam.Chunks.TryGetValue("P$RenderTyp", out var gamRenderTypes) && + gam.Chunks.TryGetValue("L$MetaProp", out var gamLinks) && + gam.Chunks.TryGetValue("LD$MetaProp", out var gamLinkData)) + { + modelNames.properties.AddRange(((PropertyChunk)gamModelNames).properties); + scales.properties.AddRange(((PropertyChunk)gamScales).properties); + renderTypes.properties.AddRange(((PropertyChunk)gamRenderTypes).properties); + metaPropLinks.links.AddRange(((LinkChunk)gamLinks).links); + metaPropLinkData.linkData.AddRange(((LinkDataMetaProp)gamLinkData).linkData); + } + + // Add parentages + var length = metaPropLinks.links.Count; + for (var i = 0; i < length; i++) + { + var link = metaPropLinks.links[i]; + var linkData = metaPropLinkData.linkData[i]; + var childId = link.source; + var parentId = link.destination; + if (!_objects.ContainsKey(childId)) + { + _objects.Add(childId, new DarkObject(childId)); + } + if (!_objects.ContainsKey(parentId)) + { + _objects.Add(parentId, new DarkObject(parentId)); + } + + if (linkData.priority == 0) + { + _objects[childId].parentId = parentId; + } + } + + void TryAddProp(string propName, Property prop) + { + var id = prop.objectId; + if (!_objects.ContainsKey(id)) + { + _objects.Add(id, new DarkObject(id)); + } + _objects[id].properties.TryAdd(propName, prop); + } + + // TODO: Find a way to do this better + foreach (var prop in modelNames.properties) + { + TryAddProp("P$ModelName", prop); + } + foreach (var prop in scales.properties) + { + TryAddProp("P$Scale", prop); + } + foreach (var prop in renderTypes.properties) + { + TryAddProp("P$RenderTyp", prop); + } + } + } + + public T GetProperty(int objectId, string propName) where T : Property + { + if (!_objects.ContainsKey(objectId)) + { + return null; + } + + var parentId = objectId; + while (parentId != 0) + { + if (!_objects.TryGetValue(parentId, out var obj)) + { + return null; + } + + var prop = obj.GetProperty(propName); + if (prop != null) + { + return prop; + } + parentId = obj.parentId; + } + + return null; + } +} \ No newline at end of file diff --git a/project/code/TMV/Mission.cs b/project/code/TMV/Mission.cs index cd83eb4..4b174eb 100644 --- a/project/code/TMV/Mission.cs +++ b/project/code/TMV/Mission.cs @@ -101,54 +101,32 @@ public partial class Mission : Node3D _file = new(FileName); UseChunk("TXLIST", LoadTextures); UseChunk("WREXT", BuildWrMeshes); - if ( - _file.Chunks.TryGetValue("BRLIST", out var brListRaw) && - _file.Chunks.TryGetValue("P$ModelName", out var modelNamesRaw) && - _file.Chunks.TryGetValue("P$Scale", out var scalesRaw) && - _file.Chunks.TryGetValue("P$RenderTyp", out var renderTypesRaw) && - _file.Chunks.TryGetValue("L$MetaProp", out var metaPropLinksRaw) && - _file.Chunks.TryGetValue("LD$MetaProp", out var metaPropLinkDataRaw) - ) + + ObjectHierarchy objHierarchy; + if (_file.Chunks.TryGetValue("GAM_FILE", out var gamFileChunk)) { - var brList = (BrList)brListRaw; - var modelNames = (PropertyChunk)modelNamesRaw; - var scales = (PropertyChunk)scalesRaw; - var renderTypes = (PropertyChunk)renderTypesRaw; - var metaPropLinks = (LinkChunk)metaPropLinksRaw; - var metaPropLinkData = (LinkDataMetaProp)metaPropLinkDataRaw; - // TODO: Do this somewhere else lol - if (_file.Chunks.TryGetValue("GAM_FILE", out var gamFileChunk)) + var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive }; + var name = ((GamFile)gamFileChunk).fileName; + var paths = Directory.GetFiles(FileName.GetBaseDir(), name, options); + if (!paths.IsEmpty()) { - GD.Print("GAM_FILE detected"); - - var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive }; - var name = ((GamFile)gamFileChunk).fileName; - GD.Print($"Searching for GAM: {FileName.GetBaseDir()}/{name}"); - var paths = Directory.GetFiles(FileName.GetBaseDir(), name, options); - GD.Print($"Found paths: {paths.Length}"); - if (!paths.IsEmpty()) - { - GD.Print($"Attempting to load GAM at: {paths[0]}"); - var gamFile = new DbFile(paths[0]); - if (gamFile.Chunks.TryGetValue("P$ModelName", out var gamChunk1) && - gamFile.Chunks.TryGetValue("L$MetaProp", out var gamChunk2) && - gamFile.Chunks.TryGetValue("LD$MetaProp", out var gamChunk3) && - gamFile.Chunks.TryGetValue("P$Scale", out var gamChunk4) && - gamFile.Chunks.TryGetValue("P$RenderTyp", out var gamChunk5)) - { - GD.Print($"Pre-Merged chunks: {modelNames.properties.Count} {metaPropLinks.links.Count} {metaPropLinkData.linkData.Count}"); - modelNames.properties.AddRange(((PropertyChunk)gamChunk1).properties); - metaPropLinks.links.AddRange(((LinkChunk)gamChunk2).links); - metaPropLinkData.linkData.AddRange(((LinkDataMetaProp)gamChunk3).linkData); - scales.properties.AddRange(((PropertyChunk)gamChunk4).properties); - renderTypes.properties.AddRange(((PropertyChunk)gamChunk5).properties); - GD.Print($"Post-Merged chunks: {modelNames.properties.Count} {metaPropLinks.links.Count} {metaPropLinkData.linkData.Count}"); - } - } + objHierarchy = new ObjectHierarchy(_file, new DbFile(paths[0])); } + else + { + objHierarchy = new ObjectHierarchy(_file); + } + } + else + { + objHierarchy = new ObjectHierarchy(_file); + } - PlaceObjects(brList, modelNames, scales, renderTypes, metaPropLinks, metaPropLinkData); + if ( + _file.Chunks.TryGetValue("BRLIST", out var brList)) + { + PlaceObjects((BrList)brList, objHierarchy); } } @@ -164,13 +142,7 @@ public partial class Mission : Node3D } } - private void PlaceObjects( - BrList brList, - PropertyChunk modelNames, - PropertyChunk scales, - PropertyChunk renderTypes, - LinkChunk metapropLink, - LinkDataMetaProp metaPropLinkData) + private void PlaceObjects(BrList brList, ObjectHierarchy objHierarchy) { foreach (var brush in brList.Brushes) { @@ -179,93 +151,27 @@ public partial class Mission : Node3D continue; } - // TODO: Build an actual hierarchy and such :) - // TODO: We need to load the gamesys :) - // Determine if we have a model name :)) var id = (int)brush.brushInfo; - var modelName = ""; - var scale = Vector3.One; - var scaleFound = false; - var renderType = PropRenderType.Mode.Normal; - var renderTypeFound = false; - while (true) - { - // See if there's a modelname property - if (modelName == "") - { - foreach (var prop in modelNames.properties) - { - if (prop.objectId == id) - { - modelName = prop.modelName; - break; - } - } - } + var modelNameProp = objHierarchy.GetProperty(id, "P$ModelName"); + var scaleProp = objHierarchy.GetProperty(id, "P$Scale"); + var renderTypeProp = objHierarchy.GetProperty(id, "P$RenderTyp"); + var renderMode = renderTypeProp == null ? PropRenderType.Mode.Normal : renderTypeProp.mode; - if (!scaleFound) - { - foreach (var prop in scales.properties) - { - if (prop.objectId == id) - { - scale = prop.scale.ToGodotVec3(false); - scaleFound = true; - break; - } - } - } - - if (!renderTypeFound) - { - foreach (var prop in renderTypes.properties) - { - if (prop.objectId == id) - { - renderType = prop.mode; - renderTypeFound = true; - break; - } - } - } - - if (modelName != "" && scaleFound && renderTypeFound) - { - break; - } - - // Check for a parent - var length = metapropLink.links.Count; - var prevId = id; - for (var i = 0; i < length; i++) - { - var link = metapropLink.links[i]; - var linkData = metaPropLinkData.linkData[i]; - if (link.source == id && linkData.priority == 0) - { - id = link.destination; - break; - } - } - // No parent found - if (id == prevId) - { - break; - } - } - if (modelName == "" || renderType == PropRenderType.Mode.NotRendered) + if (modelNameProp == null || renderMode == PropRenderType.Mode.NotRendered) { continue; } // Let's try and place an object :) - var fmName = FileName.GetBaseDir().GetFile(); - var objPath = _installPaths.GetObjectPath(fmName, modelName + ".bin"); - objPath ??= _installPaths.GetObjectPath(modelName + ".bin"); + var modelName = modelNameProp.modelName + ".bin"; + var fmName = FileName.GetBaseDir().GetFile(); // TODO: Doesn't work for OMs + var objPath = _installPaths.GetObjectPath(fmName, modelName); + objPath ??= _installPaths.GetObjectPath(modelName); var pos = brush.position.ToGodotVec3(); var rawRot = brush.angle; var rot = new Vector3(rawRot.Y, rawRot.Z, rawRot.X) * 360 / ushort.MaxValue; + var scale = scaleProp == null ? Vector3.One : scaleProp.scale.ToGodotVec3(false); var model = new Model { Position = pos,