Compare commits

...

5 Commits

8 changed files with 142 additions and 113 deletions

View File

@ -66,3 +66,8 @@ public class GenericChunk : IChunk
writer.Write(Data);
}
}
public interface IMergable
{
public abstract void Merge(IMergable other);
}

View File

@ -34,7 +34,7 @@ public record LinkId
}
}
public class LinkChunk : IChunk
public class LinkChunk : IChunk, IMergable
{
public record Link
{
@ -68,9 +68,15 @@ public class LinkChunk : IChunk
{
throw new System.NotImplementedException();
}
public void Merge(IMergable other)
{
links.AddRange(((LinkChunk)other).links);
}
}
public class LinkDataMetaProp : IChunk
// TODO: This should be generic like Property
public class LinkDataMetaProp : IChunk, IMergable
{
public record LinkData
{
@ -102,4 +108,9 @@ public class LinkDataMetaProp : IChunk
{
throw new System.NotImplementedException();
}
public void Merge(IMergable other)
{
linkData.AddRange(((LinkDataMetaProp)other).linkData);
}
}

View File

@ -18,7 +18,7 @@ public class Property
}
}
public class PropertyChunk<T> : IChunk where T : Property, new()
public class PropertyChunk<T> : IChunk, IMergable where T : Property, new()
{
public ChunkHeader Header { get; set; }
public List<T> properties;
@ -38,6 +38,11 @@ public class PropertyChunk<T> : IChunk where T : Property, new()
{
throw new System.NotImplementedException();
}
public void Merge(IMergable other)
{
properties.AddRange(((PropertyChunk<T>)other).properties);
}
}
public class PropGeneric : Property
@ -111,3 +116,14 @@ public class PropString : Property
// value = tmpName[..Math.Min(length - 1, tmpName.Length)];
}
}
public class PropFloat : Property
{
public float value;
public override void Read(BinaryReader reader)
{
base.Read(reader);
value = reader.ReadSingle();
}
}

View File

@ -109,6 +109,7 @@ public class DbFile
"P$OTxtRepr1" => new PropertyChunk<PropString>(),
"P$OTxtRepr2" => new PropertyChunk<PropString>(),
"P$OTxtRepr3" => new PropertyChunk<PropString>(),
"P$RenderAlp" => new PropertyChunk<PropFloat>(),
"LD$MetaProp" => new LinkDataMetaProp(),
_ when entryName.StartsWith("L$") => new LinkChunk(),
_ when entryName.StartsWith("P$") => new PropertyChunk<PropGeneric>(),

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using KeepersCompound.LGS.Database.Chunks;
@ -35,50 +36,25 @@ public class ObjectHierarchy
{
_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("P$OTxtRepr0", out var txtRepl0Raw) &&
db.Chunks.TryGetValue("P$OTxtRepr1", out var txtRepl1Raw) &&
db.Chunks.TryGetValue("P$OTxtRepr2", out var txtRepl2Raw) &&
db.Chunks.TryGetValue("P$OTxtRepr3", out var txtRepl3Raw) &&
db.Chunks.TryGetValue("L$MetaProp", out var metaPropLinksRaw) &&
db.Chunks.TryGetValue("LD$MetaProp", out var metaPropLinkDataRaw))
T GetMergedChunk<T>(string name) where T : IMergable
{
var modelNames = (PropertyChunk<PropModelName>)modelNamesRaw;
var scales = (PropertyChunk<PropScale>)scalesRaw;
var renderTypes = (PropertyChunk<PropRenderType>)renderTypesRaw;
var txtRepl0s = (PropertyChunk<PropString>)txtRepl0Raw;
var txtRepl1s = (PropertyChunk<PropString>)txtRepl1Raw;
var txtRepl2s = (PropertyChunk<PropString>)txtRepl2Raw;
var txtRepl3s = (PropertyChunk<PropString>)txtRepl3Raw;
var metaPropLinks = (LinkChunk)metaPropLinksRaw;
var metaPropLinkData = (LinkDataMetaProp)metaPropLinkDataRaw;
if (db.Chunks.TryGetValue(name, out var rawChunk))
{
var chunk = (T)rawChunk;
if (gam != null && gam.Chunks.TryGetValue(name, out var rawGamChunk))
{
var gamChunk = (T)rawGamChunk;
chunk.Merge(gamChunk);
}
return chunk;
}
// 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("P$OTxtRepr0", out var gamTxtRepl0s) &&
gam.Chunks.TryGetValue("P$OTxtRepr1", out var gamTxtRepl1s) &&
gam.Chunks.TryGetValue("P$OTxtRepr2", out var gamTxtRepl2s) &&
gam.Chunks.TryGetValue("P$OTxtRepr3", out var gamTxtRepl3s) &&
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);
txtRepl0s.properties.AddRange(((PropertyChunk<PropString>)gamTxtRepl0s).properties);
txtRepl1s.properties.AddRange(((PropertyChunk<PropString>)gamTxtRepl1s).properties);
txtRepl2s.properties.AddRange(((PropertyChunk<PropString>)gamTxtRepl2s).properties);
txtRepl3s.properties.AddRange(((PropertyChunk<PropString>)gamTxtRepl3s).properties);
metaPropLinks.links.AddRange(((LinkChunk)gamLinks).links);
metaPropLinkData.linkData.AddRange(((LinkDataMetaProp)gamLinkData).linkData);
throw new ArgumentException($"No chunk with name ({name}) found", nameof(name));
}
// Add parentages
var metaPropLinks = GetMergedChunk<LinkChunk>("L$MetaProp");
var metaPropLinkData = GetMergedChunk<LinkDataMetaProp>("LD$MetaProp");
var length = metaPropLinks.links.Count;
for (var i = 0; i < length; i++)
{
@ -101,46 +77,28 @@ public class ObjectHierarchy
}
}
void TryAddProp(string propName, Property prop)
void AddProp<T>(string name) where T : Property, new()
{
var chunk = GetMergedChunk<PropertyChunk<T>>(name);
foreach (var prop in chunk.properties)
{
var id = prop.objectId;
if (!_objects.ContainsKey(id))
{
_objects.Add(id, new DarkObject(id));
}
_objects[id].properties.TryAdd(propName, prop);
_objects[id].properties.TryAdd(name, 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);
}
foreach (var prop in txtRepl0s.properties)
{
TryAddProp("P$OTxtRepr0", prop);
}
foreach (var prop in txtRepl1s.properties)
{
TryAddProp("P$OTxtRepr1", prop);
}
foreach (var prop in txtRepl2s.properties)
{
TryAddProp("P$OTxtRepr2", prop);
}
foreach (var prop in txtRepl3s.properties)
{
TryAddProp("P$OTxtRepr3", prop);
}
}
AddProp<PropModelName>("P$ModelName");
AddProp<PropScale>("P$Scale");
AddProp<PropRenderType>("P$RenderTyp");
AddProp<PropString>("P$OTxtRepr0");
AddProp<PropString>("P$OTxtRepr1");
AddProp<PropString>("P$OTxtRepr2");
AddProp<PropString>("P$OTxtRepr3");
AddProp<PropFloat>("P$RenderAlp");
}
public T GetProperty<T>(int objectId, string propName) where T : Property

View File

@ -62,6 +62,11 @@ public class ResourcePathManager
_extractionPath = extractionPath;
}
public static string ConvertSeparator(string path)
{
return path.Replace('\\', '/');
}
public bool Init(string installPath)
{
// TODO: This can be done less awkwardly with the resource paths
@ -156,6 +161,7 @@ public class ResourcePathManager
throw new ArgumentException("No campaign found with given name", nameof(campaignName));
}
// This expects resourceName to already have it's separator converted
public (string, string) GetResourcePath(
ResourceType type,
string campaignName,
@ -206,11 +212,12 @@ public class ResourcePathManager
{
foreach (var path in Directory.EnumerateFiles(dir, "*", texOptions))
{
var ext = Path.GetExtension(path);
var convertedPath = ConvertSeparator(path);
var ext = Path.GetExtension(convertedPath);
if (validExtensions.Contains(ext.ToLower()))
{
var key = Path.GetFileNameWithoutExtension(path).ToLower();
pathMap.TryAdd(key, path);
var key = Path.GetFileNameWithoutExtension(convertedPath).ToLower();
pathMap.TryAdd(key, convertedPath);
}
}
}
@ -234,11 +241,12 @@ public class ResourcePathManager
{
foreach (var path in Directory.EnumerateFiles(dir, "*", textureOptions))
{
var ext = Path.GetExtension(path);
var convertedPath = ConvertSeparator(path);
var ext = Path.GetExtension(convertedPath);
if (validExtensions.Contains(ext.ToLower()))
{
var key = Path.GetRelativePath(root, path)[..^ext.Length].ToLower();
pathMap.TryAdd(key, path);
var key = Path.GetRelativePath(root, convertedPath)[..^ext.Length].ToLower();
pathMap.TryAdd(key, convertedPath);
}
}
}
@ -260,8 +268,9 @@ public class ResourcePathManager
{
foreach (var path in Directory.EnumerateFiles(dir, "*.bin", binOptions))
{
var key = Path.GetRelativePath(dir, path).ToLower();
pathMap.TryAdd(key, path);
var convertedPath = ConvertSeparator(path);
var key = Path.GetRelativePath(dir, convertedPath).ToLower();
pathMap.TryAdd(key, convertedPath);
}
}
@ -277,8 +286,8 @@ public class ResourcePathManager
{
if (line.StartsWith("load_path"))
{
// TODO: Do we only need to replace on systems with a different path separator?
var path = line.Split(" ")[1].Replace("\\", "/");
// TODO: This can have multiple paths I think
var path = ConvertSeparator(line.Split(" ")[1]);
omsPath = Path.GetFullPath(root + path);
break;
}
@ -294,8 +303,9 @@ public class ResourcePathManager
};
foreach (var path in Directory.GetFiles(omsPath, "*.mis", searchOptions))
{
var baseName = Path.GetFileName(path).ToLower();
map.Add(baseName, path);
var convertedPath = ConvertSeparator(path);
var baseName = Path.GetFileName(convertedPath).ToLower();
map.Add(baseName, convertedPath);
}
return true;
@ -329,7 +339,7 @@ public class ResourcePathManager
if (line.StartsWith("fm_path"))
{
// TODO: I think this can technically contain multiple paths
var path = line.Split(" ")[1].Replace("\\", "/");
var path = ConvertSeparator(line.Split(" ")[1]);
fmsPath = Path.GetFullPath(root + path);
break;
}
@ -344,10 +354,11 @@ public class ResourcePathManager
var campaignMap = new Dictionary<string, string>();
foreach (var path in Directory.GetFiles(dir))
{
if (extensions.Contains(Path.GetExtension(path).ToLower()))
var convertedPath = ConvertSeparator(path);
if (extensions.Contains(Path.GetExtension(convertedPath).ToLower()))
{
var baseName = Path.GetFileName(path).ToLower();
campaignMap.Add(baseName, path);
var baseName = Path.GetFileName(convertedPath).ToLower();
campaignMap.Add(baseName, ConvertSeparator(path));
}
}

View File

@ -29,6 +29,8 @@ public partial class Mission : Node3D
}
}
private const string OBJECT_MODELS_GROUP = "ObjectModels";
[Export(PropertyHint.GlobalFile, "*.mis")]
public string FileName { get; set; }
[Export]
@ -94,10 +96,14 @@ public partial class Mission : Node3D
{
Build = true;
}
if (keyEvent.Keycode == Key.O)
if (keyEvent.Keycode == Key.L)
{
ToggleLightmap();
}
if (keyEvent.Keycode == Key.O)
{
ToggleObjectRendering();
}
}
}
@ -127,6 +133,17 @@ public partial class Mission : Node3D
}
}
public void ToggleObjectRendering()
{
// It might be "better" to use a parent node and just toggle that, but
// the performance of this is completely fine so we'll stick with it
foreach (var node in GetTree().GetNodesInGroup(OBJECT_MODELS_GROUP))
{
var node3d = node as Node3D;
node3d.Visible = !node3d.Visible;
}
}
public void ToggleLightmap()
{
if (_lmLayerMask == Vector2I.Zero)
@ -236,6 +253,7 @@ public partial class Mission : Node3D
var txtRepl1 = objHierarchy.GetProperty<PropString>(id, "P$OTxtRepr1");
var txtRepl2 = objHierarchy.GetProperty<PropString>(id, "P$OTxtRepr2");
var txtRepl3 = objHierarchy.GetProperty<PropString>(id, "P$OTxtRepr3");
var renderAlpha = objHierarchy.GetProperty<PropFloat>(id, "P$RenderAlp");
var renderMode = renderTypeProp == null ? PropRenderType.Mode.Normal : renderTypeProp.mode;
if (modelNameProp == null || renderMode == PropRenderType.Mode.NotRendered)
@ -278,7 +296,8 @@ public partial class Mission : Node3D
else
{
var resType = ResourceType.ObjectTexture;
var resName = Path.GetFileNameWithoutExtension(prop.value);
var convertedValue = ResourcePathManager.ConvertSeparator(prop.value);
var resName = Path.GetFileNameWithoutExtension(convertedValue);
path = pathManager.GetResourcePath(resType, _campaignName, resName).Item2;
}
@ -308,6 +327,13 @@ public partial class Mission : Node3D
}
}
if (renderAlpha != null)
{
model.Transparency = 1.0f - renderAlpha.value;
}
model.AddToGroup(OBJECT_MODELS_GROUP);
AddChild(model);
}
}

View File

@ -52,7 +52,8 @@ public class ModelLoader
{
if (material.Type == 0)
{
var resName = Path.GetFileNameWithoutExtension(material.Name);
var convertedName = ResourcePathManager.ConvertSeparator(material.Name);
var resName = Path.GetFileNameWithoutExtension(convertedName);
var (_, path) = pathManager.GetResourcePath(ResourceType.ObjectTexture, campaignName, resName);
if (path == null)
{