mysimulation/server/tso.files/RC/DGRP3DGeometry.cs

316 lines
11 KiB
C#
Raw Permalink Normal View History

using FSO.Files.Formats.IFF;
using FSO.Files.Formats.IFF.Chunks;
using FSO.Files.Utils;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
namespace FSO.Files.RC
{
public class DGRP3DGeometry
{
public bool Rendered = false;
public Texture2D Pixel;
public ushort PixelSPR;
public ushort PixelDir;
public ushort CustomTexture;
public static Func<string, Texture2D> ReplTextureProvider;
public List<DGRP3DVert> SVerts; //simplified vertices
public List<int> SIndices; //simplified indices
public VertexBuffer Verts;
public IndexBuffer Indices;
public int PrimCount;
public void SComplete(GraphicsDevice gd)
{
Rendered = true;
Verts?.Dispose();
Indices?.Dispose();
PrimCount = SIndices.Count / 3;
if (PrimCount > 0)
{
Verts = new VertexBuffer(gd, typeof(DGRP3DVert), SVerts.Count, BufferUsage.None);
Verts.SetData(SVerts.ToArray());
Indices = new IndexBuffer(gd, IndexElementSize.ThirtyTwoBits, SIndices.Count, BufferUsage.None);
Indices.SetData(SIndices.ToArray());
}
if (!IffFile.RETAIN_CHUNK_DATA)
{
SVerts = null;
SIndices = null;
}
}
public DGRP3DGeometry() { }
public DGRP3DGeometry(IoBuffer io, DGRP source, GraphicsDevice gd, int Version)
{
PixelSPR = io.ReadUInt16();
PixelDir = io.ReadUInt16();
if (PixelDir == 65535)
{
CustomTexture = 1;
if (source == null)
{
//temporary system for models without DGRP
Pixel = ReplTextureProvider("FSO_TEX_" + PixelSPR + ".png");
}
else
{
var name = source.ChunkParent.Filename.Replace('.', '_').Replace("spf", "iff");
name += "_TEX_" + PixelSPR + ".png";
Pixel = ReplTextureProvider(name);
if (Pixel == null)
{
Pixel = source.ChunkParent.Get<MTEX>(PixelSPR)?.GetTexture(gd);
}
}
}
else
{
Pixel = source.GetImage(1, 3, PixelDir).Sprites[PixelSPR].GetTexture(gd);
}
var vertCount = io.ReadInt32();
SVerts = new List<DGRP3DVert>();
if (Version > 1)
{
var bytes = io.ReadBytes(vertCount * Marshal.SizeOf(typeof(DGRP3DVert)));
var readVerts = new DGRP3DVert[vertCount];
var pinnedHandle = GCHandle.Alloc(readVerts, GCHandleType.Pinned);
Marshal.Copy(bytes, 0, pinnedHandle.AddrOfPinnedObject(), bytes.Length);
pinnedHandle.Free();
SVerts = readVerts.ToList();
}
else
{
for (int i = 0; i < vertCount; i++)
{
var x = io.ReadFloat();
var y = io.ReadFloat();
var z = io.ReadFloat();
var u = io.ReadFloat();
var v = io.ReadFloat();
var normal = new Vector3();
SVerts.Add(new DGRP3DVert(new Vector3(x, y, z), normal, new Vector2(u, v)));
}
}
var indexCount = io.ReadInt32();
SIndices = ToTArray<int>(io.ReadBytes(indexCount * 4)).ToList();
// bottom up triangle ordering. useful for trees.
/*
var triBase = new int[SIndices.Count / 3][];
for (int i = 0; i < triBase.Length; i++) triBase[i] = new int[] { SIndices[i * 3], SIndices[i*3 + 1], SIndices[i * 3 + 2] };
var ordered = triBase.OrderBy(x => SVerts[x[0]].Position.Y);
SIndices.Clear();
foreach (var item in ordered) SIndices.AddRange(item);
*/
if (Version < 2) GenerateNormals(false);
SComplete(gd);
}
public DGRP3DGeometry(string[] splitName, OBJ obj, List<int[]> indices, DGRP source, GraphicsDevice gd)
{
if (splitName[1] == "SPR")
{
PixelSPR = ushort.Parse(splitName[3]);
PixelDir = ushort.Parse(splitName[2].Substring(3));
Pixel = source.GetImage(1, 3, PixelDir).Sprites[PixelSPR].GetTexture(gd);
}
else if (splitName[1] != "MASK")
{
PixelSPR = ushort.Parse(splitName[2]);
CustomTexture = 1;
PixelDir = 65535;
var name = source.ChunkParent.Filename.Replace('.', '_').Replace("spf", "iff");
name += "_TEX_" + PixelSPR + ".png";
Pixel = ReplTextureProvider(name);
if (Pixel == null)
{
Pixel = source.ChunkParent.Get<MTEX>(PixelSPR)?.GetTexture(gd);
}
}
SVerts = new List<DGRP3DVert>();
SIndices = new List<int>();
var dict = new Dictionary<Tuple<int, int, int>, int>();
var hasNormals = false;
foreach (var ind in indices)
{
var tup = new Tuple<int, int, int>(ind[0], ind[1], (ind.Length > 2) ? ind[2] : -1);
int targ;
if (!dict.TryGetValue(tup, out targ))
{
//add a vertex
targ = SVerts.Count;
Vector3 normal = Vector3.Zero;
if (tup.Item3 > -1)
{
normal = obj.Normals[tup.Item3 - 1];
hasNormals = true;
}
var vert = new DGRP3DVert(obj.Vertices[ind[0] - 1], normal, obj.TextureCoords[ind[1] - 1]);
vert.TextureCoordinate.Y = 1 - vert.TextureCoordinate.Y;
SVerts.Add(vert);
dict[tup] = targ;
}
SIndices.Add(targ);
}
if (!hasNormals) GenerateNormals(false);
/*
var triBase = new int[SIndices.Count / 3][];
for (int i = 0; i < triBase.Length; i++) triBase[i] = new int[] { SIndices[i * 3], SIndices[i * 3 + 1], SIndices[i * 3 + 2] };
var ordered = triBase.OrderBy(x => SVerts[x[0]].Position.Y + SVerts[x[1]].Position.Y + SVerts[x[2]].Position.Y);
SIndices.Clear();
foreach (var item in ordered) SIndices.AddRange(item);
*/
SComplete(gd);
}
public void GenerateNormals(bool invert)
{
DGRP3DVert.GenerateNormals(invert, SVerts, SIndices);
}
public void Save(IoWriter io)
{
io.WriteUInt16(PixelSPR);
io.WriteUInt16(PixelDir);
io.WriteInt32(SVerts.Count);
foreach (var vert in SVerts)
{
io.WriteFloat(vert.Position.X);
io.WriteFloat(vert.Position.Y);
io.WriteFloat(vert.Position.Z);
io.WriteFloat(vert.TextureCoordinate.X);
io.WriteFloat(vert.TextureCoordinate.Y);
io.WriteFloat(vert.Normal.X);
io.WriteFloat(vert.Normal.Y);
io.WriteFloat(vert.Normal.Z);
}
io.WriteInt32(SIndices.Count);
io.WriteBytes(ToByteArray(SIndices.ToArray()));
}
private string GetOName(int dyn)
{
if (CustomTexture == 0)
{
return dyn + "_SPR_rot" + PixelDir + "_" + PixelSPR;
}
else
{
return dyn + "_TEX_" + PixelSPR;
}
}
public void SaveOBJ(StreamWriter io, int dyn, ref int baseInd)
{
string o_name = GetOName(dyn);
io.WriteLine("usemtl " + o_name);
io.WriteLine("o " + o_name);
foreach (var vert in SVerts)
{
io.WriteLine("v " + vert.Position.X.ToString(CultureInfo.InvariantCulture) + " " + vert.Position.Y.ToString(CultureInfo.InvariantCulture) + " " + vert.Position.Z.ToString(CultureInfo.InvariantCulture));
}
foreach (var vert in SVerts)
{
io.WriteLine("vt " + vert.TextureCoordinate.X.ToString(CultureInfo.InvariantCulture) + " " + (1 - vert.TextureCoordinate.Y).ToString(CultureInfo.InvariantCulture));
}
foreach (var vert in SVerts)
{
io.WriteLine("vn " + vert.Normal.X.ToString(CultureInfo.InvariantCulture) + " " + vert.Normal.Y.ToString(CultureInfo.InvariantCulture) + " " + vert.Normal.Z.ToString(CultureInfo.InvariantCulture));
}
io.Write("f ");
var ticker = 0;
var j = 0;
foreach (var ind in SIndices)
{
var i = ind + baseInd;
io.Write(i + "/" + i + "/" + i + " ");
if (++ticker == 3)
{
io.WriteLine("");
if (j < SIndices.Count - 1) io.Write("f ");
ticker = 0;
}
j++;
}
baseInd += SVerts.Count;
}
public void SaveMTL(StreamWriter io, int dyn, string path)
{
var oname = GetOName(dyn);
if (Pixel != null)
{
Common.Utils.GameThread.NextUpdate(x =>
{
try
{
using (var io2 = File.Open(Path.Combine(path, oname + ".png"), FileMode.Create))
Pixel.SaveAsPng(io2, Pixel.Width, Pixel.Height);
}
catch (Exception e)
{
}
});
}
io.WriteLine("newmtl " + oname);
io.WriteLine("Ka 1.000 1.000 1.000");
io.WriteLine("Kd 1.000 1.000 1.000");
io.WriteLine("Ks 0.000 0.000 0.000");
io.WriteLine("Ns 10.0000");
io.WriteLine("illum 2");
io.WriteLine("map_Kd " + oname + ".png");
io.WriteLine("map_d " + oname + ".png");
}
public void Dispose()
{
Verts?.Dispose();
Indices?.Dispose();
}
private static T[] ToTArray<T>(byte[] input)
{
var result = new T[input.Length / Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(input, 0, result, 0, input.Length);
return result;
}
private static byte[] ToByteArray<T>(T[] input)
{
var result = new byte[input.Length * Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(input, 0, result, 0, result.Length);
return result;
}
}
}