mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-07-26 17:24:57 -04:00
Added FSO.Files for use with the API server
Don't ask me. FreeSO is the prime example of dependency hell.
This commit is contained in:
parent
4b5e584eeb
commit
8fec258215
104 changed files with 14653 additions and 163 deletions
315
server/tso.files/RC/DGRP3DGeometry.cs
Executable file
315
server/tso.files/RC/DGRP3DGeometry.cs
Executable file
|
@ -0,0 +1,315 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
736
server/tso.files/RC/DGRP3DMesh.cs
Executable file
736
server/tso.files/RC/DGRP3DMesh.cs
Executable file
|
@ -0,0 +1,736 @@
|
|||
using FSO.Common.MeshSimplify;
|
||||
using FSO.Common.Rendering;
|
||||
using FSO.Common.Utils;
|
||||
using FSO.Files.Formats.IFF.Chunks;
|
||||
using FSO.Files.RC.Utils;
|
||||
using FSO.Files.Utils;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace FSO.Files.RC
|
||||
{
|
||||
public class DGRP3DMesh
|
||||
{
|
||||
//1: initial 3d format
|
||||
//2: normals
|
||||
//3: depth mask (for sinks, fireplaces)
|
||||
public static int CURRENT_VERSION = 3;
|
||||
public static int CURRENT_RECONSTRUCT = 2;
|
||||
|
||||
public static DGRPRCParams DefaultParams = new DGRPRCParams();
|
||||
public static Dictionary<string, DGRPRCParams> ParamsByIff = new Dictionary<string, DGRPRCParams>()
|
||||
{
|
||||
{"windows2.iff", new DGRPRCParams() { Rotations = new bool[] {true, true, false, false } } },
|
||||
{"windows.iff", new DGRPRCParams() { Rotations = new bool[] {true, true, false, false } } },
|
||||
{"windows5.iff", new DGRPRCParams() { DoorFix = true } },
|
||||
|
||||
{"windowslodge.iff", new DGRPRCParams() { DoorFix = true } },
|
||||
{"doors.iff", new DGRPRCParams() { DoorFix = true } },
|
||||
{"doors5.iff", new DGRPRCParams() { DoorFix = true } },
|
||||
{"doorsmagic.iff", new DGRPRCParams() { DoorFix = true } },
|
||||
|
||||
{"phones.iff", new DGRPRCParams() { Rotations = new bool[] {true, true, false, false }, StartDGRP = 200, EndDGRP = 207 } },
|
||||
|
||||
{"countercasino.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"counters.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"counters2.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"counters3.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"counters4.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"counters5.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"counters6.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"counterwall.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"oj-rest-counters.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"oj-rest-pickup-counters.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"dishwashers.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"trashcompactor.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"3tileclock.iff", new DGRPRCParams() { BlenderTweak = true } },
|
||||
|
||||
{"fencessuperstar.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"fencesnowbank.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"fencesunleashed.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"fencelodgestone.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"fenceparty.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"fencecarnival.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"fencesspellbound.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"columnarchmagic.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
|
||||
{"awnings.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"awnings3.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"awnings4.iff", new DGRPRCParams() { CounterFix = true } },
|
||||
{"awningthatch.iff", new DGRPRCParams() { CounterFix = true } }
|
||||
};
|
||||
|
||||
//STATIC: multithreading for
|
||||
|
||||
public static Queue<Action> QueuedRC = new Queue<Action>();
|
||||
public static AutoResetEvent NewRecon = new AutoResetEvent(false);
|
||||
|
||||
public static bool Sync;
|
||||
public static void InitRCWorkers()
|
||||
{
|
||||
var cores = Math.Max(1, Environment.ProcessorCount-1); //maybe detect hyperthreading somehow
|
||||
for (int i=0; i<cores; i++)
|
||||
{
|
||||
var thread = new Thread(RCWorkerLoop);
|
||||
thread.Priority = ThreadPriority.BelowNormal;
|
||||
//todo: priority below normal, so we dont disrupt the game?
|
||||
thread.Start();
|
||||
}
|
||||
}
|
||||
|
||||
public static void QueueWork(Action work)
|
||||
{
|
||||
if (Sync) work();
|
||||
else
|
||||
{
|
||||
lock (QueuedRC) QueuedRC.Enqueue(work);
|
||||
NewRecon.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetWorkCount()
|
||||
{
|
||||
lock (QueuedRC) return QueuedRC.Count;
|
||||
}
|
||||
|
||||
public static void RCWorkerLoop()
|
||||
{
|
||||
while (!GameThread.Killed)
|
||||
{
|
||||
Action item = null;
|
||||
while (true)
|
||||
{
|
||||
lock (QueuedRC)
|
||||
{
|
||||
if (QueuedRC.Count > 0) item = QueuedRC.Dequeue();
|
||||
else break;
|
||||
}
|
||||
item?.Invoke();
|
||||
}
|
||||
WaitHandle.WaitAny(new WaitHandle[] { NewRecon, GameThread.OnKilled });
|
||||
}
|
||||
}
|
||||
|
||||
//END STATIC
|
||||
|
||||
public int Version = CURRENT_VERSION;
|
||||
public int ReconstructVersion;
|
||||
public string Name;
|
||||
public List<Dictionary<Texture2D, DGRP3DGeometry>> Geoms;
|
||||
public DGRP3DMaskType MaskType = DGRP3DMaskType.None;
|
||||
public DGRP3DGeometry DepthMask;
|
||||
public BoundingBox? Bounds;
|
||||
|
||||
|
||||
//for internal use
|
||||
private int TotalSprites;
|
||||
private int CompletedCount;
|
||||
private float MaxAllowedSq = 0.065f * 0.065f;
|
||||
public List<Vector3> BoundPts = new List<Vector3>();
|
||||
|
||||
|
||||
public DGRP3DMesh(DGRP dgrp, Stream source, GraphicsDevice gd)
|
||||
{
|
||||
using (var cstream = new GZipStream(source, CompressionMode.Decompress))
|
||||
{
|
||||
using (var io = IoBuffer.FromStream(cstream, ByteOrder.LITTLE_ENDIAN))
|
||||
{
|
||||
var fsom = io.ReadCString(4);
|
||||
Version = io.ReadInt32();
|
||||
ReconstructVersion = io.ReadInt32();
|
||||
if (ReconstructVersion != 0 && ReconstructVersion < CURRENT_RECONSTRUCT)
|
||||
throw new Exception("Reconstruction outdated, must be rerun!");
|
||||
Name = io.ReadPascalString();
|
||||
|
||||
var geomCount = io.ReadInt32();
|
||||
Geoms = new List<Dictionary<Texture2D, DGRP3DGeometry>>();
|
||||
for (int i = 0; i < geomCount; i++)
|
||||
{
|
||||
var d = new Dictionary<Texture2D, DGRP3DGeometry>();
|
||||
var subCount = io.ReadInt32();
|
||||
for (int j = 0; j < subCount; j++)
|
||||
{
|
||||
var geom = new DGRP3DGeometry(io, dgrp, gd, Version);
|
||||
if (geom.Pixel == null && geom.PrimCount > 0) throw new Exception("Invalid Mesh! (old format)");
|
||||
d.Add(geom.Pixel, geom);
|
||||
}
|
||||
Geoms.Add(d);
|
||||
}
|
||||
|
||||
if (Version > 2)
|
||||
{
|
||||
MaskType = (DGRP3DMaskType)io.ReadInt32();
|
||||
if (MaskType > DGRP3DMaskType.None)
|
||||
DepthMask = new DGRP3DGeometry(io, dgrp, gd, Version);
|
||||
}
|
||||
|
||||
var x = io.ReadFloat();
|
||||
var y = io.ReadFloat();
|
||||
var z = io.ReadFloat();
|
||||
var x2 = io.ReadFloat();
|
||||
var y2 = io.ReadFloat();
|
||||
var z2 = io.ReadFloat();
|
||||
Bounds = new BoundingBox(new Vector3(x, y, z), new Vector3(x2, y2, z2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string SaveDirectory;
|
||||
|
||||
public DGRP3DMesh(DGRP dgrp, OBJD obj, GraphicsDevice gd, string saveDirectory)
|
||||
{
|
||||
ReconstructVersion = CURRENT_RECONSTRUCT;
|
||||
SaveDirectory = saveDirectory;
|
||||
Geoms = new List<Dictionary<Texture2D, DGRP3DGeometry>>();
|
||||
if (dgrp == null) return;
|
||||
Name = obj.ChunkParent.Filename.Replace('.', '_') + "_" + dgrp.ChunkID;
|
||||
var lower = obj.ChunkParent.Filename.ToLowerInvariant();
|
||||
var config = obj.ChunkParent.List<FSOR>()?.FirstOrDefault()?.Params;
|
||||
if (config == null)
|
||||
{
|
||||
if (!ParamsByIff.TryGetValue(lower, out config)) config = DefaultParams;
|
||||
}
|
||||
if (!config.InRange(dgrp.ChunkID)) config = DefaultParams;
|
||||
|
||||
int totalSpr = 0;
|
||||
for (uint rotation = 0; rotation < 4; rotation++)
|
||||
{
|
||||
if (config.DoorFix)
|
||||
{
|
||||
if ((obj.SubIndex & 0xFF) == 1)
|
||||
{
|
||||
if ((rotation+1)%4 > 1) continue;
|
||||
} else
|
||||
{
|
||||
if ((rotation + 1) % 4 < 2) continue;
|
||||
}
|
||||
}
|
||||
else if (!config.Rotations[rotation]) continue;
|
||||
var img = dgrp.GetImage(1, 3, rotation);
|
||||
|
||||
var zOff = (config.BlenderTweak) ? -57.5f : -55f;
|
||||
|
||||
var mat = Matrix.CreateTranslation(new Vector3(-72, -344, zOff));
|
||||
mat *= Matrix.CreateScale((1f / (128)) * 1.43f);//1.4142135623730f);
|
||||
mat *= Matrix.CreateScale(1, -1, 1);
|
||||
|
||||
mat *= Matrix.CreateRotationX((float)Math.PI / -6);
|
||||
mat *= Matrix.CreateRotationY(((float)Math.PI / 4) * (1+rotation*2));
|
||||
|
||||
var factor = (config.BlenderTweak) ? 0.40f : 0.39f;
|
||||
|
||||
int curSpr = 0;
|
||||
foreach (var sprite in img.Sprites)
|
||||
{
|
||||
var sprMat = mat * Matrix.CreateTranslation(new Vector3(sprite.ObjectOffset.X, sprite.ObjectOffset.Z, sprite.ObjectOffset.Y) * new Vector3(1f / 16f, 1f / 5f, 1f / 16f));
|
||||
var inv = Matrix.Invert(sprMat);
|
||||
var tex = sprite.GetTexture(gd);
|
||||
|
||||
if (tex == null)
|
||||
{
|
||||
curSpr++;
|
||||
continue;
|
||||
}
|
||||
var isDynamic = sprite.SpriteID >= obj.DynamicSpriteBaseId && sprite.SpriteID < (obj.DynamicSpriteBaseId + obj.NumDynamicSprites);
|
||||
var dynid = (isDynamic) ? (int)(1 + sprite.SpriteID - obj.DynamicSpriteBaseId) : 0;
|
||||
|
||||
while (Geoms.Count <= dynid) Geoms.Add(new Dictionary<Texture2D, DGRP3DGeometry>());
|
||||
|
||||
DGRP3DGeometry geom = null;
|
||||
if (!Geoms[dynid].TryGetValue(tex, out geom))
|
||||
{
|
||||
geom = new DGRP3DGeometry() { Pixel = tex };
|
||||
Geoms[dynid][geom.Pixel] = geom;
|
||||
}
|
||||
geom.PixelDir = (ushort)rotation;
|
||||
geom.PixelSPR = (ushort)(curSpr++);
|
||||
totalSpr++;
|
||||
|
||||
var depthB = sprite.GetDepth();
|
||||
|
||||
var useDequantize = false;
|
||||
float[] depth = null;
|
||||
int iterations = 125;
|
||||
int triDivisor = 100;
|
||||
float aggressiveness = 3.5f;
|
||||
if (useDequantize)
|
||||
{
|
||||
var dtex = new Texture2D(gd, ((TextureInfo)tex.Tag).Size.X, ((TextureInfo)tex.Tag).Size.Y, false, SurfaceFormat.Color);
|
||||
dtex.SetData(depthB.Select(x => new Color(x, x, x, x)).ToArray());
|
||||
depth = DepthTreatment.DequantizeDepth(gd, dtex);
|
||||
dtex.Dispose();
|
||||
|
||||
iterations = 500;
|
||||
aggressiveness = 2.5f;
|
||||
MaxAllowedSq = 0.05f * 0.05f;
|
||||
}
|
||||
else if (depthB != null)
|
||||
{
|
||||
depth = depthB.Select(x => x / 255f).ToArray();
|
||||
iterations = 125;
|
||||
aggressiveness = 3.5f;
|
||||
}
|
||||
|
||||
if (depth == null) continue;
|
||||
|
||||
QueueWork(() =>
|
||||
{
|
||||
var boundPts = new List<Vector3>();
|
||||
//begin async part
|
||||
var w = ((TextureInfo)tex.Tag).Size.X;
|
||||
var h = ((TextureInfo)tex.Tag).Size.Y;
|
||||
|
||||
var pos = sprite.SpriteOffset + new Vector2(72, 348 - h);
|
||||
var tl = Vector3.Transform(new Vector3(pos, 0), sprMat);
|
||||
var tr = Vector3.Transform(new Vector3(pos + new Vector2(w, 0), 0), sprMat);
|
||||
var bl = Vector3.Transform(new Vector3(pos + new Vector2(0, h), 0), sprMat);
|
||||
var tlFront = Vector3.Transform(new Vector3(pos, 110.851251f), sprMat);
|
||||
|
||||
var xInc = (tr - tl) / w;
|
||||
var yInc = (bl - tl) / h;
|
||||
var dFactor = (tlFront - tl) / (factor);
|
||||
|
||||
if (sprite.Flip)
|
||||
{
|
||||
tl = tr;
|
||||
xInc *= -1;
|
||||
}
|
||||
|
||||
var dict = new Dictionary<int, int>();
|
||||
var verts = new List<VertexPositionTexture>();
|
||||
var indices = new List<int>();
|
||||
|
||||
var lastPt = new Vector3();
|
||||
var i = 0;
|
||||
var verti = 0;
|
||||
for (int y = 0; y < h; y++)
|
||||
{
|
||||
if (y > 0) boundPts.Add(lastPt);
|
||||
bool first = true;
|
||||
var vpos = tl;
|
||||
for (int x = 0; x < w; x++)
|
||||
{
|
||||
var d = depth[i++];
|
||||
if (d < 0.999f)
|
||||
{
|
||||
lastPt = vpos + (1f - d) * dFactor;
|
||||
if (first) { boundPts.Add(lastPt); first = false; }
|
||||
var vert = new VertexPositionTexture(lastPt, new Vector2((float)x / w, (float)y / h));
|
||||
verts.Add(vert);
|
||||
dict.Add(y * w + x, verti++);
|
||||
}
|
||||
vpos += xInc;
|
||||
}
|
||||
tl += yInc;
|
||||
}
|
||||
|
||||
for (int y = 0; y < h - 1; y++)
|
||||
{
|
||||
for (int x = 0; x < w - 1; x++)
|
||||
{
|
||||
//try make a triangle or two
|
||||
var quad = new int?[] {
|
||||
QuickTryGet(dict, x+y*w),
|
||||
QuickTryGet(dict, x+1+y*w),
|
||||
QuickTryGet(dict, x+1+(y+1)*w),
|
||||
QuickTryGet(dict, x+(y+1)*w)
|
||||
};
|
||||
var total = quad.Sum(v => (v == null) ? 0 : 1);
|
||||
if (total == 4)
|
||||
{
|
||||
var d1 = Vector3.DistanceSquared(verts[quad[0].Value].Position, verts[quad[2].Value].Position);
|
||||
var d2 = Vector3.DistanceSquared(verts[quad[1].Value].Position, verts[quad[3].Value].Position);
|
||||
|
||||
if (d1 > MaxAllowedSq || d2 > MaxAllowedSq) continue;
|
||||
|
||||
indices.Add(quad[0].Value);
|
||||
indices.Add(quad[1].Value);
|
||||
indices.Add(quad[2].Value);
|
||||
|
||||
indices.Add(quad[0].Value);
|
||||
indices.Add(quad[2].Value);
|
||||
indices.Add(quad[3].Value);
|
||||
}
|
||||
else if (total == 3)
|
||||
{
|
||||
//clockwise anyways. we can only make one
|
||||
int? last = null;
|
||||
int? first = null;
|
||||
bool exit = false;
|
||||
foreach (var v in quad)
|
||||
{
|
||||
if (v != null)
|
||||
{
|
||||
if (last != null && Vector3.DistanceSquared(verts[last.Value].Position, verts[v.Value].Position) > MaxAllowedSq)
|
||||
{
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
last = v.Value;
|
||||
if (first == null) first = last;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exit && Vector3.DistanceSquared(verts[last.Value].Position, verts[first.Value].Position) > MaxAllowedSq) exit = true;
|
||||
if (exit) continue;
|
||||
|
||||
foreach (var v in quad)
|
||||
{
|
||||
if (v != null) indices.Add(v.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.CounterFix)
|
||||
{
|
||||
//x axis extrapolation
|
||||
//clip: -0.4 to 0.4
|
||||
|
||||
//identify vertices very close to clipping range(border)
|
||||
//! for each vertex outwith clipping range
|
||||
//- idendify closest border pixel bp in image space
|
||||
//- result.zy = bp.zy
|
||||
//- result.x = (resultIMAGE.x - bpIMAGE.x) / 64;
|
||||
//- clip x to -0.5, 0.5f.
|
||||
|
||||
var clip = 0.4;
|
||||
var bWidth = 0.02;
|
||||
var border1 = new List<Tuple<Vector2, Vector3>>();
|
||||
var invalid1 = new List<KeyValuePair<int, int>>();
|
||||
var border2 = new List<Tuple<Vector2, Vector3>>();
|
||||
var invalid2 = new List<KeyValuePair<int, int>>();
|
||||
foreach (var vert in dict) {
|
||||
var vpos = verts[vert.Value].Position;
|
||||
var dist = Math.Abs(vpos.X);
|
||||
if (dist > clip)
|
||||
{
|
||||
if (vpos.X > 0)
|
||||
invalid1.Add(vert);
|
||||
else
|
||||
invalid2.Add(vert);
|
||||
} else if (dist > (clip - bWidth))
|
||||
{
|
||||
if (vpos.X > 0)
|
||||
border1.Add(new Tuple<Vector2, Vector3>(new Vector2(vert.Key % w, vert.Key / w), vpos));
|
||||
else
|
||||
border2.Add(new Tuple<Vector2, Vector3>(new Vector2(vert.Key % w, vert.Key / w), vpos));
|
||||
}
|
||||
}
|
||||
|
||||
var edge = 0.498f + 0.001f * (rotation % 2);
|
||||
|
||||
if (border1.Count > 0)
|
||||
{
|
||||
foreach (var vert in invalid1)
|
||||
{
|
||||
var vstr = verts[vert.Value];
|
||||
var pos2d = new Vector2(vert.Key % w, vert.Key / w);
|
||||
var vpos = vstr.Position;
|
||||
var closest = border1.OrderBy(x => Vector2.DistanceSquared(x.Item1, pos2d)).First();
|
||||
|
||||
vpos.X = closest.Item2.X + Vector2.Distance(closest.Item1, pos2d) / 71.55f;
|
||||
if (vpos.X > 0.5f)
|
||||
{
|
||||
vpos.X = edge;
|
||||
}
|
||||
else
|
||||
{
|
||||
vpos.Y = closest.Item2.Y;
|
||||
vpos.Z = closest.Item2.Z;
|
||||
}
|
||||
|
||||
vstr.Position = vpos;
|
||||
verts[vert.Value] = vstr;
|
||||
}
|
||||
}
|
||||
|
||||
if (border2.Count > 0)
|
||||
{
|
||||
foreach (var vert in invalid2)
|
||||
{
|
||||
var vstr = verts[vert.Value];
|
||||
var pos2d = new Vector2(vert.Key % w, vert.Key / w);
|
||||
var vpos = vstr.Position;
|
||||
var closest = border2.OrderBy(x => Vector2.DistanceSquared(x.Item1, pos2d)).First();
|
||||
|
||||
vpos.X = closest.Item2.X - Vector2.Distance(closest.Item1, pos2d) / 71.55f;
|
||||
if (vpos.X < -0.5f)
|
||||
{
|
||||
vpos.X = -edge;
|
||||
}
|
||||
else
|
||||
{
|
||||
vpos.Y = closest.Item2.Y;
|
||||
vpos.Z = closest.Item2.Z;
|
||||
}
|
||||
|
||||
vstr.Position = vpos;
|
||||
verts[vert.Value] = vstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lock (BoundPts) BoundPts.AddRange(boundPts);
|
||||
var useSimplification = config.Simplify;
|
||||
|
||||
if (useSimplification)
|
||||
{
|
||||
var simple = new Simplify();
|
||||
simple.vertices = verts.Select(x => new MSVertex() { p = x.Position, t = x.TextureCoordinate }).ToList();
|
||||
for (int t = 0; t < indices.Count; t += 3)
|
||||
{
|
||||
simple.triangles.Add(new MSTriangle()
|
||||
{
|
||||
v = new int[] { indices[t], indices[t + 1], indices[t + 2] }
|
||||
});
|
||||
}
|
||||
simple.simplify_mesh(simple.triangles.Count / triDivisor, agressiveness: aggressiveness, iterations: iterations);
|
||||
|
||||
verts = simple.vertices.Select(x =>
|
||||
{
|
||||
var iv = Vector3.Transform(x.p, inv);
|
||||
//DGRP3DVert
|
||||
return new VertexPositionTexture(x.p,
|
||||
new Vector2(
|
||||
(sprite.Flip) ? (1 - ((iv.X - pos.X + 0.5f) / w)) : ((iv.X - pos.X + 0.5f) / w),
|
||||
(iv.Y - pos.Y + 0.5f) / h));
|
||||
}
|
||||
).ToList();
|
||||
indices.Clear();
|
||||
foreach (var t in simple.triangles)
|
||||
{
|
||||
indices.Add(t.v[0]);
|
||||
indices.Add(t.v[1]);
|
||||
indices.Add(t.v[2]);
|
||||
}
|
||||
|
||||
GameThread.NextUpdate(x =>
|
||||
{
|
||||
if (geom.SVerts == null)
|
||||
{
|
||||
geom.SVerts = new List<DGRP3DVert>();
|
||||
geom.SIndices = new List<int>();
|
||||
}
|
||||
|
||||
var bID = geom.SVerts.Count;
|
||||
foreach (var id in indices) geom.SIndices.Add(id + bID);
|
||||
var verts2 = verts.Select(v => new DGRP3DVert(v.Position, Vector3.Zero, v.TextureCoordinate)).ToList();
|
||||
DGRP3DVert.GenerateNormals(!sprite.Flip, verts2, indices);
|
||||
geom.SVerts.AddRange(verts2);
|
||||
|
||||
lock (this)
|
||||
{
|
||||
if (++CompletedCount == TotalSprites) Complete(gd);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
GameThread.NextUpdate(x =>
|
||||
{
|
||||
if (geom.SVerts == null)
|
||||
{
|
||||
geom.SVerts = new List<DGRP3DVert>();
|
||||
geom.SIndices = new List<int>();
|
||||
}
|
||||
|
||||
var baseID = geom.SVerts.Count;
|
||||
foreach (var id in indices) geom.SIndices.Add(id + baseID);
|
||||
var verts2 = verts.Select(v => new DGRP3DVert(v.Position, Vector3.Zero, v.TextureCoordinate)).ToList();
|
||||
DGRP3DVert.GenerateNormals(!sprite.Flip, verts2, indices);
|
||||
geom.SVerts.AddRange(verts2);
|
||||
lock (this)
|
||||
{
|
||||
if (++CompletedCount == TotalSprites) Complete(gd);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
TotalSprites = totalSpr;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a DGRPMesh from a .OBJ file.
|
||||
/// </summary>
|
||||
public DGRP3DMesh(DGRP dgrp, OBJ source, GraphicsDevice gd)
|
||||
{
|
||||
Bounds = source.Vertices.Count>0?BoundingBox.CreateFromPoints(source.Vertices):new BoundingBox();
|
||||
Geoms = new List<Dictionary<Texture2D, DGRP3DGeometry>>();
|
||||
if (dgrp == null) return;
|
||||
Name = dgrp.ChunkParent.Filename.Replace('.', '_').Replace("spf", "iff") + "_" + dgrp.ChunkID;
|
||||
|
||||
foreach (var obj in source.FacesByObjgroup.OrderBy(x => x.Key))
|
||||
{
|
||||
if (obj.Key == "_default") continue;
|
||||
var split = obj.Key.Split('_');
|
||||
if (split[0] == "DEPTH")
|
||||
{
|
||||
DepthMask = new DGRP3DGeometry(split, source, obj.Value, dgrp, gd);
|
||||
if (split.Length > 2 && split[2] == "PORTAL")
|
||||
{
|
||||
MaskType = DGRP3DMaskType.Portal;
|
||||
|
||||
var verts = new List<Vector3>();
|
||||
var objs = source.FacesByObjgroup.Where(x => !x.Key.StartsWith("DEPTH_MASK_PORTAL")).Select(x => x.Value);
|
||||
foreach (var obj2 in objs)
|
||||
{
|
||||
foreach (var tri in obj2)
|
||||
{
|
||||
verts.Add(source.Vertices[tri[0] - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
Bounds = BoundingBox.CreateFromPoints(verts);
|
||||
}
|
||||
else
|
||||
MaskType = DGRP3DMaskType.Normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
//0: dynsprite id, 1: SPR or custom, 2: rotation, 3: index
|
||||
var id = int.Parse(split[0]);
|
||||
while (Geoms.Count <= id) Geoms.Add(new Dictionary<Texture2D, DGRP3DGeometry>());
|
||||
var dict = Geoms[id];
|
||||
var geom = new DGRP3DGeometry(split, source, obj.Value, dgrp, gd);
|
||||
dict[geom.Pixel] = geom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Complete(GraphicsDevice gd)
|
||||
{
|
||||
Bounds = (BoundPts.Count == 0) ? new BoundingBox() : BoundingBox.CreateFromPoints(BoundPts);
|
||||
BoundPts = null;
|
||||
Save();
|
||||
foreach (var g in Geoms)
|
||||
foreach (var e in g)
|
||||
{
|
||||
e.Value.SComplete(gd);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
if (SaveDirectory == null) return;
|
||||
var dir = Path.Combine(SaveDirectory, Name + ".fsom");
|
||||
Directory.CreateDirectory(SaveDirectory);
|
||||
using (var stream = File.Open(dir, FileMode.Create))
|
||||
{
|
||||
using (var cstream = new GZipStream(stream, CompressionMode.Compress))
|
||||
Save(cstream);
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(Stream stream)
|
||||
{
|
||||
using (var io = IoWriter.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
|
||||
{
|
||||
io.WriteCString("FSOm", 4);
|
||||
io.WriteInt32(CURRENT_VERSION);
|
||||
io.WriteInt32(ReconstructVersion);
|
||||
io.WritePascalString(Name);
|
||||
|
||||
io.WriteInt32(Geoms.Count);
|
||||
foreach (var g in Geoms)
|
||||
{
|
||||
io.WriteInt32(g.Count);
|
||||
foreach (var m in g.Values)
|
||||
{
|
||||
m.Save(io);
|
||||
}
|
||||
}
|
||||
|
||||
io.WriteInt32((int)MaskType);
|
||||
if (DepthMask != null)
|
||||
{
|
||||
DepthMask.Save(io);
|
||||
}
|
||||
|
||||
var b = Bounds.Value;
|
||||
io.WriteFloat(b.Min.X);
|
||||
io.WriteFloat(b.Min.Y);
|
||||
io.WriteFloat(b.Min.Z);
|
||||
io.WriteFloat(b.Max.X);
|
||||
io.WriteFloat(b.Max.Y);
|
||||
io.WriteFloat(b.Max.Z);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveOBJ(Stream stream, string filename)
|
||||
{
|
||||
using (var io = new StreamWriter(stream))
|
||||
{
|
||||
io.WriteLine("# Generated by the FreeSO FSOm Exporter tool.");
|
||||
io.WriteLine("# Meshes can be cleaned up then re-imported via Volcanic.");
|
||||
io.WriteLine("# One material per object... Note that material names must follow this format:");
|
||||
io.WriteLine("# - '$_SPR_rot#_#': uses the texture from the SPR this DGRP would normally use, ");
|
||||
io.WriteLine("# at the given rotation and index.");
|
||||
io.WriteLine("# - '$_TEX_#': import a custom PNG texture at the given chunk ID. Textures can be ");
|
||||
io.WriteLine("# shared across multiple DGRPs by using the same ID.");
|
||||
io.WriteLine("# Replace $ with the dynamic sprite index. 0 means base. (untoggleable)");
|
||||
io.WriteLine("# Textures are assumed to have a filename equivalent to their material name, plus png.");
|
||||
|
||||
|
||||
io.WriteLine("mtllib "+filename+".mtl");
|
||||
io.WriteLine("s 1");
|
||||
|
||||
int dyn = 0;
|
||||
int indCount = 1;
|
||||
foreach (var g in Geoms)
|
||||
{
|
||||
foreach (var m in g.Values)
|
||||
{
|
||||
m.SaveOBJ(io, dyn, ref indCount);
|
||||
}
|
||||
dyn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveMTL(Stream stream, string path)
|
||||
{
|
||||
using (var io = new StreamWriter(stream))
|
||||
{
|
||||
io.WriteLine("# Generated by the FreeSO FSOm Exporter tool.");
|
||||
io.WriteLine("# Contains material information for exported objects.");
|
||||
io.WriteLine("# See the associated .obj file for more information.");
|
||||
|
||||
int dyn = 0;
|
||||
foreach (var g in Geoms)
|
||||
{
|
||||
foreach (var m in g.Values)
|
||||
{
|
||||
m.SaveMTL(io, dyn, path);
|
||||
}
|
||||
dyn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int? QuickTryGet(Dictionary<int, int> dict, int pt)
|
||||
{
|
||||
int result;
|
||||
if (dict.TryGetValue(pt, out result)) return result;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DGRP3DMaskType
|
||||
{
|
||||
None = 0,
|
||||
Normal = 1,
|
||||
Portal = 2
|
||||
}
|
||||
}
|
111
server/tso.files/RC/DGRP3DVert.cs
Executable file
111
server/tso.files/RC/DGRP3DVert.cs
Executable file
|
@ -0,0 +1,111 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Graphics
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct DGRP3DVert : IVertexType
|
||||
{
|
||||
public Vector3 Position;
|
||||
public Vector2 TextureCoordinate;
|
||||
public Vector3 Normal;
|
||||
public static readonly VertexDeclaration VertexDeclaration;
|
||||
public DGRP3DVert(Vector3 position, Vector3 normal, Vector2 textureCoordinate)
|
||||
{
|
||||
this.Position = position;
|
||||
this.Normal = normal;
|
||||
this.TextureCoordinate = textureCoordinate;
|
||||
}
|
||||
|
||||
VertexDeclaration IVertexType.VertexDeclaration
|
||||
{
|
||||
get
|
||||
{
|
||||
return VertexDeclaration;
|
||||
}
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// TODO: FIc gethashcode
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{{Position:" + this.Position + " Normal:" + this.Normal + " TextureCoordinate:" + this.TextureCoordinate + "}}";
|
||||
}
|
||||
|
||||
public static bool operator ==(DGRP3DVert left, DGRP3DVert right)
|
||||
{
|
||||
return (((left.Position == right.Position) && (left.Normal == right.Normal)) && (left.TextureCoordinate == right.TextureCoordinate));
|
||||
}
|
||||
|
||||
public static bool operator !=(DGRP3DVert left, DGRP3DVert right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (obj.GetType() != base.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (this == ((DGRP3DVert)obj));
|
||||
}
|
||||
|
||||
static DGRP3DVert()
|
||||
{
|
||||
VertexElement[] elements = new VertexElement[] { new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0), new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0), new VertexElement(20, VertexElementFormat.Vector3, VertexElementUsage.TextureCoordinate, 1) };
|
||||
VertexDeclaration declaration = new VertexDeclaration(elements);
|
||||
VertexDeclaration = declaration;
|
||||
}
|
||||
|
||||
public static void GenerateNormals(bool invert, List<DGRP3DVert> verts, IList<int> indices)
|
||||
{
|
||||
for (int i = 0; i < indices.Count; i += 3)
|
||||
{
|
||||
var v1 = verts[indices[i + 1]].Position - verts[indices[i]].Position;
|
||||
var v2 = verts[indices[i + 2]].Position - verts[indices[i + 1]].Position;
|
||||
var cross = invert ? Vector3.Cross(v2, v1) : Vector3.Cross(v1, v2);
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
var id = indices[i + j];
|
||||
var v = verts[id];
|
||||
v.Normal += cross;
|
||||
verts[id] = v;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < verts.Count; i++)
|
||||
{
|
||||
var v = verts[i];
|
||||
v.Normal.Normalize();
|
||||
verts[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<int> StripToTri(List<int> ind)
|
||||
{
|
||||
var result = new List<int>();
|
||||
for (int i = 0; i < ind.Count; i += 2)
|
||||
{
|
||||
if (i>0)
|
||||
{
|
||||
result.Add(ind[i - 2]);
|
||||
result.Add(ind[i - 1]);
|
||||
result.Add(ind[i]);
|
||||
|
||||
result.Add(ind[i - 1]);
|
||||
result.Add(ind[i + 1]);
|
||||
result.Add(ind[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
45
server/tso.files/RC/DGRPRCParams.cs
Executable file
45
server/tso.files/RC/DGRPRCParams.cs
Executable file
|
@ -0,0 +1,45 @@
|
|||
using FSO.Files.Utils;
|
||||
|
||||
namespace FSO.Files.RC
|
||||
{
|
||||
public class DGRPRCParams
|
||||
{
|
||||
public bool[] Rotations = new bool[] { true, true, true, true };
|
||||
public bool DoorFix; //depending on subtile, disable certain rotations to fix door.
|
||||
public bool CounterFix; //extrapolate z on sides of counter to the edge of the tile.
|
||||
|
||||
public int StartDGRP;
|
||||
public int EndDGRP;
|
||||
public bool BlenderTweak;
|
||||
public bool Simplify = true;
|
||||
|
||||
public bool InRange(int dgrp)
|
||||
{
|
||||
return ((StartDGRP == EndDGRP && EndDGRP == 0) || (dgrp >= StartDGRP && dgrp <= EndDGRP));
|
||||
}
|
||||
|
||||
public DGRPRCParams() { }
|
||||
public DGRPRCParams(IoBuffer io, int version)
|
||||
{
|
||||
Rotations = new bool[4];
|
||||
for (int i = 0; i < 4; i++) Rotations[i] = io.ReadByte() > 0;
|
||||
DoorFix = io.ReadByte() > 0;
|
||||
CounterFix = io.ReadByte() > 0;
|
||||
StartDGRP = io.ReadInt32();
|
||||
EndDGRP = io.ReadInt32();
|
||||
BlenderTweak = io.ReadByte() > 0;
|
||||
Simplify = io.ReadByte() > 0;
|
||||
}
|
||||
|
||||
public void Save(IoWriter io)
|
||||
{
|
||||
foreach (var rotation in Rotations) io.WriteByte((byte)(rotation ? 1 : 0));
|
||||
io.WriteByte((byte)(DoorFix ? 1 : 0));
|
||||
io.WriteByte((byte)(CounterFix ? 1 : 0));
|
||||
io.WriteInt32(StartDGRP);
|
||||
io.WriteInt32(EndDGRP);
|
||||
io.WriteByte((byte)(BlenderTweak ? 1 : 0));
|
||||
io.WriteByte((byte)(Simplify ? 1 : 0));
|
||||
}
|
||||
}
|
||||
}
|
267
server/tso.files/RC/FSOF.cs
Executable file
267
server/tso.files/RC/FSOF.cs
Executable file
|
@ -0,0 +1,267 @@
|
|||
using FSO.Common;
|
||||
using FSO.Common.Utils;
|
||||
using FSO.Files.Utils;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FSO.Files.RC
|
||||
{
|
||||
public class FSOF
|
||||
{
|
||||
public int TexCompressionType; //RGBA8, DXT5
|
||||
|
||||
public int FloorWidth;
|
||||
public int FloorHeight;
|
||||
public int WallWidth;
|
||||
public int WallHeight;
|
||||
|
||||
public Color NightLightColor;
|
||||
|
||||
public byte[] FloorTextureData;
|
||||
public byte[] WallTextureData;
|
||||
|
||||
public byte[] NightFloorTextureData;
|
||||
public byte[] NightWallTextureData;
|
||||
|
||||
public int[] FloorIndices;
|
||||
public DGRP3DVert[] FloorVertices;
|
||||
|
||||
public int[] WallIndices;
|
||||
public DGRP3DVert[] WallVertices;
|
||||
|
||||
//loaded data
|
||||
public Texture2D FloorTexture;
|
||||
public Texture2D WallTexture;
|
||||
public Texture2D NightFloorTexture;
|
||||
public Texture2D NightWallTexture;
|
||||
|
||||
public VertexBuffer FloorVGPU;
|
||||
public IndexBuffer FloorIGPU;
|
||||
public int FloorPrims;
|
||||
|
||||
public VertexBuffer WallVGPU;
|
||||
public IndexBuffer WallIGPU;
|
||||
public int WallPrims;
|
||||
|
||||
public static int CURRENT_VERSION = 1;
|
||||
public int Version = CURRENT_VERSION;
|
||||
public bool Compressed = true;
|
||||
|
||||
public void Save(Stream stream)
|
||||
{
|
||||
var io = IoWriter.FromStream(stream, ByteOrder.LITTLE_ENDIAN);
|
||||
io.WriteCString("FSOf", 4);
|
||||
io.WriteInt32(CURRENT_VERSION);
|
||||
|
||||
io.WriteByte((byte)(Compressed ? 1 : 0));
|
||||
|
||||
MemoryStream target = null;
|
||||
GZipStream compressed = null;
|
||||
var cio = io;
|
||||
if (Compressed)
|
||||
{
|
||||
//target = new MemoryStream();
|
||||
compressed = new GZipStream(stream, CompressionMode.Compress);
|
||||
cio = IoWriter.FromStream(compressed, ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
cio.WriteInt32(TexCompressionType);
|
||||
cio.WriteInt32(FloorWidth);
|
||||
cio.WriteInt32(FloorHeight);
|
||||
cio.WriteInt32(WallWidth);
|
||||
cio.WriteInt32(WallHeight);
|
||||
cio.WriteByte((byte)((NightFloorTextureData == null)?0:1)); //has night tex?
|
||||
|
||||
cio.WriteInt32(FloorTextureData.Length);
|
||||
cio.WriteBytes(FloorTextureData);
|
||||
cio.WriteInt32(WallTextureData.Length);
|
||||
cio.WriteBytes(WallTextureData);
|
||||
|
||||
if (NightFloorTextureData != null)
|
||||
{
|
||||
cio.WriteInt32(NightFloorTextureData.Length);
|
||||
cio.WriteBytes(NightFloorTextureData);
|
||||
cio.WriteInt32(NightWallTextureData.Length);
|
||||
cio.WriteBytes(NightWallTextureData);
|
||||
cio.WriteUInt32(NightLightColor.PackedValue);
|
||||
}
|
||||
|
||||
WriteVerts(FloorVertices, FloorIndices, cio);
|
||||
WriteVerts(WallVertices, WallIndices, cio);
|
||||
|
||||
if (Compressed)
|
||||
{
|
||||
compressed.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public void Read(Stream stream)
|
||||
{
|
||||
using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
|
||||
{
|
||||
var fsof = io.ReadCString(4);
|
||||
if (fsof != "FSOf") throw new Exception("Invalid FSOf!");
|
||||
Version = io.ReadInt32();
|
||||
Compressed = io.ReadByte() > 0;
|
||||
|
||||
GZipStream compressed = null;
|
||||
var cio = io;
|
||||
if (Compressed)
|
||||
{
|
||||
compressed = new GZipStream(stream, CompressionMode.Decompress);
|
||||
cio = IoBuffer.FromStream(compressed, ByteOrder.LITTLE_ENDIAN);
|
||||
}
|
||||
|
||||
TexCompressionType = cio.ReadInt32();
|
||||
FloorWidth = cio.ReadInt32();
|
||||
FloorHeight = cio.ReadInt32();
|
||||
WallWidth = cio.ReadInt32();
|
||||
WallHeight = cio.ReadInt32();
|
||||
var hasNight = cio.ReadByte() > 0;
|
||||
|
||||
var floorTSize = cio.ReadInt32();
|
||||
FloorTextureData = cio.ReadBytes(floorTSize);
|
||||
var wallTSize = cio.ReadInt32();
|
||||
WallTextureData = cio.ReadBytes(wallTSize);
|
||||
|
||||
if (hasNight)
|
||||
{
|
||||
floorTSize = cio.ReadInt32();
|
||||
NightFloorTextureData = cio.ReadBytes(floorTSize);
|
||||
wallTSize = cio.ReadInt32();
|
||||
NightWallTextureData = cio.ReadBytes(wallTSize);
|
||||
NightLightColor = new Color(cio.ReadUInt32());
|
||||
}
|
||||
|
||||
var floor = ReadVerts(cio);
|
||||
FloorVertices = floor.Item1;
|
||||
FloorIndices = floor.Item2;
|
||||
var wall = ReadVerts(cio);
|
||||
WallVertices = wall.Item1;
|
||||
WallIndices = wall.Item2;
|
||||
}
|
||||
}
|
||||
|
||||
private SurfaceFormat GetTexFormat()
|
||||
{
|
||||
return (TexCompressionType == 1) ? SurfaceFormat.Dxt5 : SurfaceFormat.Color;
|
||||
}
|
||||
|
||||
public void LoadGPU(GraphicsDevice gd)
|
||||
{
|
||||
var format = GetTexFormat();
|
||||
if (format == SurfaceFormat.Dxt5 && !FSOEnvironment.TexCompressSupport)
|
||||
{
|
||||
//todo: software decode DXT5
|
||||
FloorTexture = new Texture2D(gd, FloorWidth, FloorHeight, false, SurfaceFormat.Color);
|
||||
FloorTexture.SetData(TextureUtils.DXT5Decompress(FloorTextureData, FloorWidth, FloorHeight));
|
||||
WallTexture = new Texture2D(gd, WallWidth, WallHeight, false, SurfaceFormat.Color);
|
||||
WallTexture.SetData(TextureUtils.DXT5Decompress(WallTextureData, WallWidth, WallHeight));
|
||||
if (NightFloorTextureData != null)
|
||||
{
|
||||
NightFloorTexture = new Texture2D(gd, FloorWidth, FloorHeight, false, SurfaceFormat.Color);
|
||||
NightFloorTexture.SetData(TextureUtils.DXT5Decompress(NightFloorTextureData, FloorWidth, FloorHeight));
|
||||
NightWallTexture = new Texture2D(gd, WallWidth, WallHeight, false, SurfaceFormat.Color);
|
||||
NightWallTexture.SetData(TextureUtils.DXT5Decompress(NightWallTextureData, WallWidth, WallHeight));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FloorTexture = new Texture2D(gd, FloorWidth, FloorHeight, false, format);
|
||||
FloorTexture.SetData(FloorTextureData);
|
||||
WallTexture = new Texture2D(gd, WallWidth, WallHeight, false, format);
|
||||
WallTexture.SetData(WallTextureData);
|
||||
if (NightFloorTextureData != null)
|
||||
{
|
||||
NightFloorTexture = new Texture2D(gd, FloorWidth, FloorHeight, false, format);
|
||||
NightFloorTexture.SetData(NightFloorTextureData);
|
||||
NightWallTexture = new Texture2D(gd, WallWidth, WallHeight, false, format);
|
||||
NightWallTexture.SetData(NightWallTextureData);
|
||||
}
|
||||
}
|
||||
|
||||
if (FloorVertices.Length > 0)
|
||||
{
|
||||
FloorVGPU = new VertexBuffer(gd, typeof(DGRP3DVert), FloorVertices.Length, BufferUsage.None);
|
||||
FloorVGPU.SetData(FloorVertices);
|
||||
FloorIGPU = new IndexBuffer(gd, IndexElementSize.ThirtyTwoBits, FloorIndices.Length, BufferUsage.None);
|
||||
FloorIGPU.SetData(FloorIndices);
|
||||
FloorPrims = FloorIndices.Length / 3;
|
||||
}
|
||||
|
||||
if (WallVertices.Length > 0)
|
||||
{
|
||||
WallVGPU = new VertexBuffer(gd, typeof(DGRP3DVert), WallVertices.Length, BufferUsage.None);
|
||||
WallVGPU.SetData(WallVertices);
|
||||
WallIGPU = new IndexBuffer(gd, IndexElementSize.ThirtyTwoBits, WallIndices.Length, BufferUsage.None);
|
||||
WallIGPU.SetData(WallIndices);
|
||||
WallPrims = WallIndices.Length / 3;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FloorTexture?.Dispose();
|
||||
WallTexture?.Dispose();
|
||||
NightFloorTexture?.Dispose();
|
||||
NightWallTexture?.Dispose();
|
||||
FloorVGPU?.Dispose();
|
||||
FloorIGPU?.Dispose();
|
||||
WallVGPU?.Dispose();
|
||||
WallIGPU?.Dispose();
|
||||
}
|
||||
|
||||
private Tuple<DGRP3DVert[], int[]> ReadVerts(IoBuffer io)
|
||||
{
|
||||
var vertCount = io.ReadInt32();
|
||||
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();
|
||||
|
||||
var indCount = io.ReadInt32();
|
||||
var indices = ToTArray<int>(io.ReadBytes(indCount * 4));
|
||||
|
||||
return new Tuple<DGRP3DVert[], int[]>(readVerts, indices);
|
||||
}
|
||||
|
||||
private void WriteVerts(DGRP3DVert[] verts, int[] indices, IoWriter io)
|
||||
{
|
||||
io.WriteInt32(verts.Length);
|
||||
foreach (var vert in verts)
|
||||
{
|
||||
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(indices.Length);
|
||||
io.WriteBytes(ToByteArray(indices.ToArray()));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
104
server/tso.files/RC/NBHm.cs
Executable file
104
server/tso.files/RC/NBHm.cs
Executable file
|
@ -0,0 +1,104 @@
|
|||
using FSO.Files.Utils;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace FSO.Files.RC
|
||||
{
|
||||
/// <summary>
|
||||
/// Neighbourhood model. Defines the 3D positions of all houses, and provides a model.
|
||||
/// There is a grass layer rendered with the grass shader, and various other layers drawn with textures.
|
||||
/// </summary>
|
||||
public class NBHm
|
||||
{
|
||||
public static int CURRENT_VERSION = 1;
|
||||
public int Version = CURRENT_VERSION;
|
||||
public Dictionary<short, NBHmHouse> Houses = new Dictionary<short, NBHmHouse>();
|
||||
|
||||
public NBHm(OBJ obj)
|
||||
{
|
||||
foreach (var group in obj.FacesByObjgroup)
|
||||
{
|
||||
if (group.Key[0] == 'p')
|
||||
{
|
||||
//lot group
|
||||
var split = group.Key.Split('_');
|
||||
if (split.Length > 1 && split[1] == "floor")
|
||||
{
|
||||
Vector2 minLocation = new Vector2(999999999, 999999999);
|
||||
var candidates = new List<Vector3>();
|
||||
foreach (var inds in group.Value)
|
||||
{
|
||||
var vtx = obj.Vertices[inds[0]-1];
|
||||
if (vtx.X < minLocation.X) { minLocation.X = vtx.X; candidates.Clear(); }
|
||||
if (vtx.Z < minLocation.Y) { minLocation.Y = vtx.Z; candidates.Clear(); }
|
||||
if ((int)vtx.Z == (int)minLocation.Y && (int)vtx.X == (int)minLocation.X) candidates.Add(vtx);
|
||||
}
|
||||
|
||||
var minLocation3 = new Vector3(minLocation.X, candidates.Min(x => x.Y), minLocation.Y);
|
||||
|
||||
//add this house
|
||||
var house = new NBHmHouse()
|
||||
{
|
||||
HouseNum = short.Parse(split[0].Substring(1)),
|
||||
Position = minLocation3
|
||||
};
|
||||
Houses[house.HouseNum] = house;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(Stream stream)
|
||||
{
|
||||
using (var io = IoWriter.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
|
||||
{
|
||||
io.WriteCString("NBHm", 4);
|
||||
io.WriteInt32(CURRENT_VERSION);
|
||||
io.WriteInt32(Houses.Count);
|
||||
foreach (var house in Houses)
|
||||
{
|
||||
io.WriteInt16(house.Key);
|
||||
io.WriteFloat(house.Value.Position.X);
|
||||
io.WriteFloat(house.Value.Position.Y);
|
||||
io.WriteFloat(house.Value.Position.Z);
|
||||
}
|
||||
io.WriteByte(0); //has model. right now there is no model.
|
||||
}
|
||||
}
|
||||
|
||||
public void Load(Stream stream)
|
||||
{
|
||||
using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN))
|
||||
{
|
||||
var magic = io.ReadCString(4);
|
||||
if (magic != "NBHm") throw new Exception("Not a valid neighbourhood model!");
|
||||
Version = io.ReadInt32();
|
||||
var houseC = io.ReadInt32();
|
||||
for (int i = 0; i < houseC; i++)
|
||||
{
|
||||
var house = new NBHmHouse()
|
||||
{
|
||||
HouseNum = io.ReadInt16(),
|
||||
Position = new Vector3()
|
||||
{
|
||||
X = io.ReadFloat(),
|
||||
Y = io.ReadFloat(),
|
||||
Z = io.ReadFloat()
|
||||
}
|
||||
};
|
||||
Houses[house.HouseNum] = house;
|
||||
}
|
||||
io.ReadByte(); //has model. right now there is no model.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NBHmHouse
|
||||
{
|
||||
public short HouseNum;
|
||||
public Vector3 Position;
|
||||
}
|
||||
}
|
70
server/tso.files/RC/OBJReader.cs
Executable file
70
server/tso.files/RC/OBJReader.cs
Executable file
|
@ -0,0 +1,70 @@
|
|||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace FSO.Files.RC
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple reader for .OBJ files. Only made to import from blender.
|
||||
/// </summary>
|
||||
public class OBJ
|
||||
{
|
||||
public List<Vector3> Vertices = new List<Vector3>();
|
||||
public List<Vector2> TextureCoords = new List<Vector2>();
|
||||
public List<Vector3> Normals = new List<Vector3>();
|
||||
public Dictionary<string, List<int[]>> FacesByObjgroup = new Dictionary<string, List<int[]>>();
|
||||
|
||||
public OBJ(Stream obj)
|
||||
{
|
||||
using (var reader = new StreamReader(obj))
|
||||
Read(reader);
|
||||
}
|
||||
|
||||
public void Read(StreamReader read)
|
||||
{
|
||||
string objGroup = "_default";
|
||||
string line = "";
|
||||
List<int[]> indices = new List<int[]>();
|
||||
FacesByObjgroup[objGroup] = indices;
|
||||
while ((line = read.ReadLine()) != null)
|
||||
{
|
||||
line = line.TrimStart();
|
||||
var comInd = line.IndexOf("#");
|
||||
if (comInd != -1) line = line.Substring(0, comInd);
|
||||
var split = line.Split(' ');
|
||||
if (split.Length == 0) continue;
|
||||
switch (split[0])
|
||||
{
|
||||
case "o": //set object group
|
||||
objGroup = split[1];
|
||||
if (!FacesByObjgroup.TryGetValue(objGroup, out indices))
|
||||
{
|
||||
indices = new List<int[]>();
|
||||
FacesByObjgroup[objGroup] = indices;
|
||||
}
|
||||
break;
|
||||
case "v":
|
||||
Vertices.Add(new Vector3(float.Parse(split[1], CultureInfo.InvariantCulture), float.Parse(split[2], CultureInfo.InvariantCulture), float.Parse(split[3], CultureInfo.InvariantCulture)));
|
||||
break;
|
||||
case "vt":
|
||||
TextureCoords.Add(new Vector2(float.Parse(split[1], CultureInfo.InvariantCulture), float.Parse(split[2], CultureInfo.InvariantCulture)));
|
||||
break;
|
||||
case "vn":
|
||||
Normals.Add(new Vector3(float.Parse(split[1], CultureInfo.InvariantCulture), float.Parse(split[2], CultureInfo.InvariantCulture), float.Parse(split[3], CultureInfo.InvariantCulture)));
|
||||
break;
|
||||
case "f":
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var split2 = split[i + 1].Split('/');
|
||||
if (split2.Length == 2)
|
||||
indices.Add(new int[] { int.Parse(split2[0]), int.Parse(split2[1]) });
|
||||
else if (split2.Length == 3)
|
||||
indices.Add(new int[] { int.Parse(split2[0]), int.Parse(split2[1]), int.Parse(split2[2]) });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
65
server/tso.files/RC/Utils/DepthTreatment.cs
Executable file
65
server/tso.files/RC/Utils/DepthTreatment.cs
Executable file
|
@ -0,0 +1,65 @@
|
|||
using FSO.Common.Utils;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Threading;
|
||||
|
||||
namespace FSO.Files.RC.Utils
|
||||
{
|
||||
public class DepthTreatment
|
||||
{
|
||||
public static Effect SpriteEffect;
|
||||
public static SpriteBatch Batch;
|
||||
|
||||
public DepthTreatment()
|
||||
{
|
||||
}
|
||||
|
||||
public static void EnsureBatch(GraphicsDevice gd)
|
||||
{
|
||||
if (Batch == null)
|
||||
{
|
||||
Batch = new SpriteBatch(gd);
|
||||
}
|
||||
}
|
||||
|
||||
public static float[] DequantizeDepth(GraphicsDevice gd, Texture2D depthIn)
|
||||
{
|
||||
var wait = new AutoResetEvent(false);
|
||||
float[] data = null;
|
||||
GameThread.InUpdate(() =>
|
||||
{
|
||||
var targetPrep = new RenderTarget2D(gd, depthIn.Width, depthIn.Height, false, SurfaceFormat.Vector4, DepthFormat.None);
|
||||
var target = new RenderTarget2D(gd, depthIn.Width, depthIn.Height, false, SurfaceFormat.Single, DepthFormat.None);
|
||||
gd.SetRenderTarget(targetPrep);
|
||||
|
||||
var effect = SpriteEffect;
|
||||
EnsureBatch(gd);
|
||||
|
||||
effect.CurrentTechnique = effect.Techniques["DerivativeDepth"];
|
||||
effect.Parameters["pixelSize"].SetValue(new Vector2(1f / depthIn.Width, 1f / depthIn.Height));
|
||||
|
||||
Batch.Begin(rasterizerState: RasterizerState.CullNone, effect: effect);
|
||||
Batch.Draw(depthIn, Vector2.Zero, Color.White);
|
||||
Batch.End();
|
||||
|
||||
gd.SetRenderTarget(target);
|
||||
|
||||
effect.CurrentTechnique = effect.Techniques["DequantizeDepth"];
|
||||
|
||||
Batch.Begin(rasterizerState: RasterizerState.CullNone, effect: effect);
|
||||
Batch.Draw(targetPrep, Vector2.Zero, Color.White);
|
||||
Batch.End();
|
||||
|
||||
gd.SetRenderTarget(null);
|
||||
|
||||
data = new float[depthIn.Width * depthIn.Height];
|
||||
target.GetData<float>(data);
|
||||
target.Dispose();
|
||||
targetPrep.Dispose();
|
||||
wait.Set();
|
||||
});
|
||||
wait.WaitOne();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue