Compare commits

...

10 Commits

8 changed files with 140 additions and 36 deletions

View File

@ -8,5 +8,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="rectpacksharp" Version="1.2.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
</ItemGroup>
</Project>

View File

@ -129,19 +129,38 @@ public class ModelFile
UvIndices = new ushort[Type == 0x1B ? VertexCount : 0];
for (var i = 0; i < UvIndices.Length; i++)
{
LightIndices[i] = reader.ReadUInt16();
UvIndices[i] = reader.ReadUInt16();
}
Material = version == 4 ? reader.ReadByte() : (byte)0;
}
}
public struct Material
{
public string Name;
public byte Type;
public byte Slot;
public uint Handle;
public float Uv;
public Material(BinaryReader reader)
{
Name = Encoding.UTF8.GetString(reader.ReadBytes(16)).Replace("\0", string.Empty);
Type = reader.ReadByte();
Slot = reader.ReadByte();
Handle = reader.ReadUInt32();
Uv = reader.ReadSingle();
}
}
public BHeader BinHeader { get; set; }
public MHeader Header { get; set; }
public Vector3[] Vertices { get; }
public Vector2[] Uvs { get; }
public Vector3[] Normals { get; }
public Polygon[] Polygons { get; }
public Material[] Materials { get; }
public ModelFile(string filename)
{
@ -178,5 +197,11 @@ public class ModelFile
{
Polygons[i] = new Polygon(reader, BinHeader.Version);
}
stream.Seek(Header.MaterialOffset, SeekOrigin.Begin);
Materials = new Material[Header.MaterialCount];
for (var i = 0; i < Materials.Length; i++)
{
Materials[i] = new Material(reader);
}
}
}

View File

@ -12,19 +12,19 @@ public class MeshSurfaceData
private readonly List<Vector3> _vertices = new();
private readonly List<Vector3> _normals = new();
private readonly List<int> _indices = new();
private readonly List<Vector2> _textureUvs = new();
private readonly List<Vector2> _lightmapUvs = new();
private readonly List<Vector2> _uv1s = new();
private readonly List<Vector2> _uv2s = new();
public MeshSurfaceData()
{
Empty = true;
}
public void TransformLightmapUvs(int start, int end, Func<Vector2, Vector2> f)
public void TransformUv2s(int start, int end, Func<Vector2, Vector2> f)
{
for (var i = start; i < end; i++)
{
_lightmapUvs[i] = f(_lightmapUvs[i]);
_uv2s[i] = f(_uv2s[i]);
}
}
@ -32,8 +32,8 @@ public class MeshSurfaceData
public (int, int) AddPolygon(
List<Vector3> vertices,
Vector3 normal,
List<Vector2> textureUvs,
List<Vector2> lightmapUvs)
List<Vector2> uv1s,
List<Vector2> uv2s)
{
Empty = false;
@ -54,11 +54,11 @@ public class MeshSurfaceData
_indices.Add(indexOffset + j + 1);
}
_textureUvs.AddRange(textureUvs);
_lightmapUvs.AddRange(lightmapUvs);
_uv1s.AddRange(uv1s);
_uv2s.AddRange(uv2s);
var end = _lightmapUvs.Count;
var start = end - lightmapUvs.Count;
var end = _uv2s.Count;
var start = end - uv2s.Count;
return (start, end);
}
@ -69,8 +69,8 @@ public class MeshSurfaceData
array[(int)Mesh.ArrayType.Vertex] = _vertices.ToArray();
array[(int)Mesh.ArrayType.Normal] = _normals.ToArray();
array[(int)Mesh.ArrayType.Index] = _indices.ToArray();
array[(int)Mesh.ArrayType.TexUV] = _textureUvs.ToArray();
array[(int)Mesh.ArrayType.TexUV2] = _lightmapUvs.ToArray();
array[(int)Mesh.ArrayType.TexUV] = _uv1s.ToArray();
array[(int)Mesh.ArrayType.TexUV2] = _uv2s.ToArray();
return array;
}

View File

@ -229,7 +229,7 @@ public partial class Mission : Node3D
}
if (!surfaceDataMap.ContainsKey(info.textureId)) GD.Print("Invalid SurfaceDataMap key");
surfaceDataMap[info.textureId].TransformLightmapUvs(info.uvStart, info.uvEnd, (uv) =>
surfaceDataMap[info.textureId].TransformUv2s(info.uvStart, info.uvEnd, (uv) =>
{
var u = uv.X;
var v = uv.Y;

View File

@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.IO;
using Godot;
using Godot.Collections;
using KeepersCompound.LGS;
using KeepersCompound.TMV.UI;
@ -24,35 +24,81 @@ public partial class Model : Node3D
var modelFile = new ModelFile(modelPath);
if (modelFile == null)
{
GD.Print("UH OH");
GD.Print($"Failed to load model file: {modelPath}");
return;
}
var vertices = new List<Vector3>();
var indices = new List<int>();
foreach (var v in modelFile.Vertices)
// TODO: Remove this disgusting hack
var baseDir = ProjectSettings.GlobalizePath($"user://objects/tmp");
var options = new EnumerationOptions
{
vertices.Add(v.ToGodotVec3());
}
foreach (var poly in modelFile.Polygons)
MatchCasing = MatchCasing.CaseInsensitive,
RecurseSubdirectories = true,
};
var materials = new List<StandardMaterial3D>();
foreach (var material in modelFile.Materials)
{
for (var i = 1; i < poly.VertexCount - 1; i++)
if (material.Type == 0)
{
indices.Add(poly.VertexIndices[0]);
indices.Add(poly.VertexIndices[i]);
indices.Add(poly.VertexIndices[i + 1]);
}
}
var paths = Directory.GetFiles(baseDir, material.Name, options);
if (paths.IsEmpty()) continue;
var array = new Array();
array.Resize((int)Mesh.ArrayType.Max);
array[(int)Mesh.ArrayType.Vertex] = vertices.ToArray();
array[(int)Mesh.ArrayType.Index] = indices.ToArray();
materials.Add(new StandardMaterial3D
{
AlbedoTexture = TextureLoader.LoadTexture(paths[0])
});
}
else
{
var b = (material.Handle) & 0xff;
var g = (material.Handle >> 8) & 0xff;
var r = (material.Handle >> 16) & 0xff;
GD.Print($"Handle: {material.Handle}, R: {r}, G: {g}, B: {b}");
var colour = new Color(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f);
materials.Add(new StandardMaterial3D
{
AlbedoColor = colour
});
}
}
var mesh = new ArrayMesh();
foreach (var poly in modelFile.Polygons)
{
var vertices = new List<Vector3>();
var normal = modelFile.Normals[poly.Normal].ToGodotVec3();
var uvs = new List<Vector2>();
for (var i = 0; i < poly.VertexCount; i++)
{
var vertex = modelFile.Vertices[poly.VertexIndices[i]];
vertices.Add(vertex.ToGodotVec3());
if (i < poly.UvIndices.Length)
{
var uv = modelFile.Uvs[poly.UvIndices[i]];
uvs.Add(new Vector2(uv.X, uv.Y));
}
else
{
uvs.Add(Vector2.Zero);
}
}
var surfaceData = new MeshSurfaceData();
surfaceData.AddPolygon(vertices, normal, uvs, uvs);
var array = surfaceData.BuildSurfaceArray();
mesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, array);
for (var i = 0; i < materials.Count; i++)
{
var m = modelFile.Materials[i];
if (m.Slot == poly.Data)
{
mesh.SurfaceSetMaterial(poly.Index, materials[i]);
break;
}
}
}
var meshInstance = new MeshInstance3D { Mesh = mesh };
AddChild(meshInstance);
}

View File

@ -0,0 +1,29 @@
using Godot;
using SixLabors.ImageSharp.PixelFormats;
namespace KeepersCompound.TMV;
public partial class TextureLoader
{
// TODO: Replace this with my own implementation lol
// References:
// - https://www.w3.org/Graphics/GIF/spec-gif89a.txt
private static ImageTexture LoadGif(string path)
{
using var gifImage = SixLabors.ImageSharp.Image.Load<Rgba32>(path);
var width = gifImage.Width;
var height = gifImage.Height;
var image = Image.Create(width, height, false, Image.Format.Rgba8);
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var pixel = gifImage[x, y].ToVector4();
image.SetPixel(x, y, new Color(pixel.X, pixel.Y, pixel.Z, pixel.W));
}
}
return ImageTexture.CreateFromImage(image);
}
}

View File

@ -53,7 +53,7 @@ public partial class TextureLoader
private static void RegisterTexturePaths(string rootDir, Dictionary<string, string> map)
{
// TODO: Load DDS BMP GIF CEL
string[] validExtensions = { "png", "tga", "pcx" };
string[] validExtensions = { "png", "tga", "pcx", "gif" };
var famOptions = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
var textureOptions = new EnumerationOptions
@ -98,12 +98,13 @@ public partial class TextureLoader
return loaded;
}
private static ImageTexture LoadTexture(string path)
public static ImageTexture LoadTexture(string path)
{
var ext = path.GetExtension().ToLower();
var texture = ext switch
{
"pcx" => LoadPcx(path),
"gif" => LoadGif(path),
_ => ImageTexture.CreateFromImage(Image.LoadFromFile(path)),
};
return texture;

View File

@ -5,6 +5,8 @@
[ext_resource type="PackedScene" uid="uid://c366p056f8dlu" path="res://project/scenes/ui/model_selector.tscn" id="3_ovrmo"]
[sub_resource type="Environment" id="Environment_e4172"]
ambient_light_source = 2
ambient_light_color = Color(1, 1, 1, 1)
ssao_enabled = true
[node name="ModelViewer" type="Node3D"]