Add basic object hierarchy generation and traversal

This commit is contained in:
Jarrod Doyle 2024-08-25 15:49:40 +01:00
parent 501f27a721
commit 2365f09931
Signed by: Jayrude
GPG Key ID: 38B57B16E7C0ADF7
2 changed files with 171 additions and 126 deletions

View File

@ -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<string, Property> properties;
public DarkObject(int id)
{
objectId = id;
parentId = 0;
properties = new Dictionary<string, Property>();
}
public T GetProperty<T>(string propName) where T : Property
{
if (properties.TryGetValue(propName, out var prop))
{
return (T)prop;
}
return null;
}
}
private Dictionary<int, DarkObject> _objects;
public ObjectHierarchy(DbFile db, DbFile gam = null)
{
_objects = new Dictionary<int, DarkObject>();
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<PropModelName>)modelNamesRaw;
var scales = (PropertyChunk<PropScale>)scalesRaw;
var renderTypes = (PropertyChunk<PropRenderType>)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<PropModelName>)gamModelNames).properties);
scales.properties.AddRange(((PropertyChunk<PropScale>)gamScales).properties);
renderTypes.properties.AddRange(((PropertyChunk<PropRenderType>)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<T>(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<T>(propName);
if (prop != null)
{
return prop;
}
parentId = obj.parentId;
}
return null;
}
}

View File

@ -101,54 +101,32 @@ public partial class Mission : Node3D
_file = new(FileName);
UseChunk<TxList>("TXLIST", LoadTextures);
UseChunk<WorldRep>("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)
)
{
var brList = (BrList)brListRaw;
var modelNames = (PropertyChunk<PropModelName>)modelNamesRaw;
var scales = (PropertyChunk<PropScale>)scalesRaw;
var renderTypes = (PropertyChunk<PropRenderType>)renderTypesRaw;
var metaPropLinks = (LinkChunk)metaPropLinksRaw;
var metaPropLinkData = (LinkDataMetaProp)metaPropLinkDataRaw;
// TODO: Do this somewhere else lol
ObjectHierarchy objHierarchy;
if (_file.Chunks.TryGetValue("GAM_FILE", out var gamFileChunk))
{
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))
objHierarchy = new ObjectHierarchy(_file, new DbFile(paths[0]));
}
else
{
GD.Print($"Pre-Merged chunks: {modelNames.properties.Count} {metaPropLinks.links.Count} {metaPropLinkData.linkData.Count}");
modelNames.properties.AddRange(((PropertyChunk<PropModelName>)gamChunk1).properties);
metaPropLinks.links.AddRange(((LinkChunk)gamChunk2).links);
metaPropLinkData.linkData.AddRange(((LinkDataMetaProp)gamChunk3).linkData);
scales.properties.AddRange(((PropertyChunk<PropScale>)gamChunk4).properties);
renderTypes.properties.AddRange(((PropertyChunk<PropRenderType>)gamChunk5).properties);
GD.Print($"Post-Merged chunks: {modelNames.properties.Count} {metaPropLinks.links.Count} {metaPropLinkData.linkData.Count}");
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<PropModelName> modelNames,
PropertyChunk<PropScale> scales,
PropertyChunk<PropRenderType> 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<PropModelName>(id, "P$ModelName");
var scaleProp = objHierarchy.GetProperty<PropScale>(id, "P$Scale");
var renderTypeProp = objHierarchy.GetProperty<PropRenderType>(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,