mesh-v4 finished.

This commit is contained in:
CloneTrooper1019 2020-07-18 18:43:58 -05:00
parent c620938a29
commit 5ae02a9a66
2 changed files with 304 additions and 190 deletions

View File

@ -7,19 +7,19 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using RobloxFiles;
using RobloxFiles.DataTypes; using RobloxFiles.DataTypes;
namespace BevelGenerator namespace BevelGenerator
{ {
public class Bone public class Bone : Instance
{ {
public string Name;
public int NameIndex; public int NameIndex;
public short Id; public short Id;
public short Parent; public short ParentId;
public float Angle; public float Unknown;
public CFrame CFrame; public CFrame CFrame;
public override string ToString() public override string ToString()
@ -38,10 +38,16 @@ namespace BevelGenerator
public BoneWeights? Weights; public BoneWeights? Weights;
} }
public class ParentLink public class SkinData
{ {
public int[] UnknownA; public int FacesBegin;
public short[] UnknownB; public int FacesLength;
public int VertsBegin;
public int VertsLength;
public int NumBones;
public short[] BoneIndexTree;
} }
public struct BoneWeights public struct BoneWeights
@ -51,7 +57,10 @@ namespace BevelGenerator
public override string ToString() public override string ToString()
{ {
return $"[Bones: {{{string.Join(", ", Bones)}}} | Weights: {{{string.Join(", ", Weights)}}}]"; var bones = string.Join(", ", Bones);
var weights = string.Join(", ", Weights);
return $"{{Bones: [{bones}] | Weights: [{weights}]}}";
} }
} }
@ -72,12 +81,16 @@ namespace BevelGenerator
public int NumBones = 0; public int NumBones = 0;
public List<Bone> Bones; public List<Bone> Bones;
public int NumLinks = 0; public int NumSkinData = 0;
public List<ParentLink> Links; public List<SkinData> SkinData;
public int NameTableSize = 0; public int NameTableSize = 0;
public byte[] NameTable; public byte[] NameTable;
public bool HasLODs => (Version >= 3);
public bool HasSkinning => (Version >= 4);
public bool HasVertexColors { get; private set; }
private static Vector3 ReadVector3(BinaryReader reader) private static Vector3 ReadVector3(BinaryReader reader)
{ {
float x = reader.ReadSingle(), float x = reader.ReadSingle(),
@ -87,20 +100,10 @@ namespace BevelGenerator
return new Vector3(x, y, z); return new Vector3(x, y, z);
} }
private static Mesh CheckLOD(Mesh mesh)
{
if (mesh.NumLODs == 0)
{
mesh.NumLODs = 2;
mesh.LODs = new List<int> { 0, mesh.NumFaces };
}
return mesh;
}
private static void LoadGeometry_Ascii(StringReader reader, Mesh mesh) private static void LoadGeometry_Ascii(StringReader reader, Mesh mesh)
{ {
string header = reader.ReadLine(); string header = reader.ReadLine();
mesh.NumMeshes = 1;
if (!header.StartsWith("version 1")) if (!header.StartsWith("version 1"))
throw new Exception("Expected version 1 header, got: " + header); throw new Exception("Expected version 1 header, got: " + header);
@ -155,31 +158,55 @@ namespace BevelGenerator
} }
} }
} }
CheckLOD(mesh);
} }
private static void LoadGeometry_Binary(BinaryReader reader, Mesh mesh) private static void LoadGeometry_Binary(BinaryReader reader, Mesh mesh)
{ {
byte[] binVersion = reader.ReadBytes(13); // version x.xx\n byte[] binVersion = reader.ReadBytes(13); // version x.xx\n
var headerSize = reader.ReadUInt16(); var headerSize = reader.ReadUInt16();
mesh.NumMeshes = reader.ReadUInt16();
mesh.NumVerts = reader.ReadInt32(); if (mesh.HasSkinning)
mesh.NumFaces = reader.ReadInt32(); {
mesh.NumMeshes = reader.ReadUInt16();
mesh.NumLODs = reader.ReadUInt16(); mesh.NumVerts = reader.ReadInt32();
mesh.NumBones = reader.ReadUInt16(); mesh.NumFaces = reader.ReadInt32();
mesh.NameTableSize = reader.ReadInt32(); mesh.NumLODs = reader.ReadUInt16();
mesh.NumLinks = reader.ReadInt32(); mesh.NumBones = reader.ReadUInt16();
mesh.NameTableSize = reader.ReadInt32();
mesh.NumSkinData = reader.ReadInt32();
}
else
{
var sizeof_Vertex = reader.ReadByte();
mesh.HasVertexColors = (sizeof_Vertex > 36);
_ = reader.ReadByte();
if (mesh.HasLODs)
{
_ = reader.ReadUInt16();
mesh.NumLODs = reader.ReadUInt16();
}
if (mesh.NumLODs > 0)
mesh.NumMeshes = (ushort)(mesh.NumLODs - 1);
else
mesh.NumMeshes = 1;
mesh.NumVerts = reader.ReadInt32();
mesh.NumFaces = reader.ReadInt32();
mesh.NameTable = new byte[0];
}
mesh.LODs = new List<int>(); mesh.LODs = new List<int>();
mesh.Bones = new List<Bone>(); mesh.Bones = new List<Bone>();
mesh.Faces = new List<int[]>(); mesh.Faces = new List<int[]>();
mesh.Verts = new List<Vertex>(); mesh.Verts = new List<Vertex>();
mesh.Links = new List<ParentLink>(); mesh.SkinData = new List<SkinData>();
// Read Vertices // Read Vertices
for (int i = 0; i < mesh.NumVerts; i++) for (int i = 0; i < mesh.NumVerts; i++)
@ -191,28 +218,33 @@ namespace BevelGenerator
UV = ReadVector3(reader) UV = ReadVector3(reader)
}; };
int rgba = reader.ReadInt32(); Color? color = null;
int argb = (rgba << 24 | rgba >> 8);
vert.Color = Color.FromArgb(argb); if (mesh.HasVertexColors)
{
int rgba = reader.ReadInt32();
color = Color.FromArgb(rgba << 24 | rgba >> 8);
}
vert.Color = color;
mesh.Verts.Add(vert); mesh.Verts.Add(vert);
} }
// Read Bone Weights? if (mesh.HasSkinning)
var weightSets = new List<BoneWeights>();
for (int i = 0; i < mesh.NumVerts; i++)
{ {
var vert = mesh.Verts[i]; // Read Bone Weights?
for (int i = 0; i < mesh.NumVerts; i++)
var weights = new BoneWeights()
{ {
Bones = reader.ReadBytes(4), var vert = mesh.Verts[i];
Weights = reader.ReadBytes(4)
};
vert.Weights = weights; var weights = new BoneWeights()
weightSets.Add(weights); {
Bones = reader.ReadBytes(4),
Weights = reader.ReadBytes(4)
};
vert.Weights = weights;
}
} }
// Read Faces // Read Faces
@ -226,9 +258,9 @@ namespace BevelGenerator
mesh.Faces.Add(face); mesh.Faces.Add(face);
} }
// Read LOD ranges if (mesh.HasLODs && mesh.NumLODs > 0)
if (mesh.NumLODs > 0)
{ {
// Read LOD ranges
for (int i = 0; i < mesh.NumLODs; i++) for (int i = 0; i < mesh.NumLODs; i++)
{ {
int lod = reader.ReadInt32(); int lod = reader.ReadInt32();
@ -236,85 +268,88 @@ namespace BevelGenerator
} }
} }
// Validate LODs if (mesh.HasSkinning)
CheckLOD(mesh);
// Read Bones
for (int i = 0; i < mesh.NumBones; i++)
{ {
float[] cf = new float[12]; // Read Bones
for (int i = 0; i < mesh.NumBones; i++)
Bone bone = new Bone()
{ {
NameIndex = reader.ReadInt32(), float[] cf = new float[12];
Id = reader.ReadInt16(),
Parent = reader.ReadInt16(), Bone bone = new Bone()
Angle = reader.ReadSingle() {
}; NameIndex = reader.ReadInt32(),
Id = reader.ReadInt16(),
for (int m = 0; m < 12; m++) ParentId = reader.ReadInt16(),
{ Unknown = reader.ReadSingle()
int index = (m + 3) % 12; };
cf[index] = reader.ReadSingle();
for (int m = 0; m < 12; m++)
{
int index = (m + 3) % 12;
cf[index] = reader.ReadSingle();
}
bone.CFrame = new CFrame(cf);
mesh.Bones.Add(bone);
} }
bone.CFrame = new CFrame(cf); // Read Bone Names & Parents
mesh.Bones.Add(bone); var nameTable = reader.ReadBytes(mesh.NameTableSize);
} mesh.NameTable = nameTable;
// Read Bone Names foreach (Bone bone in mesh.Bones)
var nameTable = reader.ReadBytes(mesh.NameTableSize);
mesh.NameTable = nameTable;
foreach (Bone bone in mesh.Bones)
{
int index = bone.NameIndex;
var buffer = new List<byte>();
while (true)
{ {
byte next = nameTable[index]; int index = bone.NameIndex;
int parentId = bone.ParentId;
if (next > 0) var buffer = new List<byte>();
index++;
else
break;
buffer.Add(next); while (true)
{
byte next = nameTable[index];
if (next > 0)
index++;
else
break;
buffer.Add(next);
}
var result = buffer.ToArray();
bone.Name = Encoding.UTF8.GetString(result);
if (parentId >= 0)
{
var parent = mesh.Bones[parentId];
bone.Parent = parent;
}
} }
var result = buffer.ToArray(); // Read Skin Data?
bone.Name = Encoding.UTF8.GetString(result); for (int p = 0; p < mesh.NumSkinData; p++)
}
// Read Skinning Data?
for (int p = 0; p < mesh.NumLinks; p++)
{
var link = new ParentLink();
mesh.Links.Add(link);
byte[] data = reader.ReadBytes(72);
var stream = new MemoryStream(data);
using (var buffer = new BinaryReader(stream))
{ {
int[] unknownA = new int[4]; var skinData = new SkinData()
{
FacesBegin = reader.ReadInt32(),
FacesLength = reader.ReadInt32(),
for (int i = 0; i < 4; i++) VertsBegin = reader.ReadInt32(),
unknownA[i] = buffer.ReadInt32(); VertsLength = reader.ReadInt32(),
int numUnknownB = buffer.ReadInt32(); NumBones = reader.ReadInt32(),
short[] unknownB = new short[numUnknownB]; BoneIndexTree = new short[26]
};
for (int i = 0; i < numUnknownB; i++) for (int i = 0; i < 26; i++)
unknownB[i] = buffer.ReadInt16(); skinData.BoneIndexTree[i] = reader.ReadInt16();
link.UnknownA = unknownA; mesh.SkinData.Add(skinData);
link.UnknownB = unknownB;
} }
} }
Debugger.Break(); Debugger.Break();
} }
@ -342,11 +377,17 @@ namespace BevelGenerator
public void Save(Stream stream) public void Save(Stream stream)
{ {
const ushort HeaderSize = 16; const ushort HeaderSize = 16;
const byte VertSize = 40; const byte VertSize = 40;
const byte FaceSize = 12; const byte FaceSize = 12;
const ushort LOD_Size = 4; const ushort LOD_Size = 4;
byte[] VersionHeader = Encoding.ASCII.GetBytes("version 3.00\n"); byte[] VersionHeader = Encoding.UTF8.GetBytes("version 3.00\n");
if (NumLODs == 0)
{
NumLODs = 2;
LODs = new List<int> { 0, NumFaces };
}
using (BinaryWriter writer = new BinaryWriter(stream)) using (BinaryWriter writer = new BinaryWriter(stream))
{ {
@ -424,6 +465,7 @@ namespace BevelGenerator
Verts = new List<Vertex>() Verts = new List<Vertex>()
}; };
var uvTable = new List<Vector3>();
var posTable = new List<Vector3>(); var posTable = new List<Vector3>();
var normTable = new List<Vector3>(); var normTable = new List<Vector3>();
@ -439,59 +481,87 @@ namespace BevelGenerator
continue; continue;
string[] buffer = line.Split(' '); string[] buffer = line.Split(' ');
string cmd = buffer[0]; string action = buffer[0];
if (cmd == "v" || cmd == "vn") switch (action)
{ {
float[] input = buffer case "v":
.Skip(1) case "vn":
.Select(float.Parse) case "vt":
.ToArray();
var value = new Vector3(input);
var target = (cmd == "v" ? posTable : normTable);
target.Add(value);
}
else if (cmd == "f")
{
int[] face = new int[3];
for (int i = 0; i < 3; i++)
{ {
string faceDef = buffer[i + 1]; float[] input = buffer
string[] indices = faceDef.Split('/'); .Skip(1)
.Select(float.Parse)
.ToArray();
int posId = int.Parse(indices[0]) - 1; var value = new Vector3(input);
int normId = int.Parse(indices[2]) - 1; List<Vector3> target = null;
string key = $"{posId}/{normId}"; switch (action)
if (!vertexLookup.ContainsKey(key))
{ {
int faceId = mesh.NumVerts++; case "v":
vertexLookup.Add(key, faceId);
Vertex vert = new Vertex()
{ {
Position = posTable[posId], target = posTable;
Normal = normTable[normId], break;
UV = new Vector3() }
}; case "vn":
{
mesh.Verts.Add(vert); target = normTable;
break;
}
case "vt":
{
target = uvTable;
break;
}
} }
face[i] = vertexLookup[key]; target.Add(value);
break;
} }
case "f":
{
int[] face = new int[3];
mesh.Faces.Add(face); for (int i = 0; i < 3; i++)
mesh.NumFaces++; {
string faceDef = buffer[i + 1];
string[] indices = faceDef.Split('/');
int uvId = int.Parse(indices[1]) - 1;
int posId = int.Parse(indices[0]) - 1;
int normId = int.Parse(indices[2]) - 1;
string key = $"{posId}/{uvId}/{normId}";
if (!vertexLookup.ContainsKey(key))
{
int faceId = mesh.NumVerts++;
vertexLookup.Add(key, faceId);
Vertex vert = new Vertex()
{
Position = posTable[posId],
Normal = normTable[normId],
UV = uvTable[uvId]
};
mesh.Verts.Add(vert);
}
face[i] = vertexLookup[key];
}
mesh.Faces.Add(face);
mesh.NumFaces++;
break;
}
} }
} }
} }
return CheckLOD(mesh); return mesh;
} }
public static Mesh FromBuffer(byte[] data) public static Mesh FromBuffer(byte[] data)
@ -547,10 +617,12 @@ namespace BevelGenerator
public static Mesh FromFile(string path) public static Mesh FromFile(string path)
{ {
Mesh result;
using (FileStream meshStream = File.OpenRead(path)) using (FileStream meshStream = File.OpenRead(path))
{ result = FromStream(meshStream);
return FromStream(meshStream);
} return result;
} }
} }
} }

View File

@ -11,6 +11,7 @@ using RobloxFiles.Enums;
using RobloxFiles.DataTypes; using RobloxFiles.DataTypes;
using Microsoft.Win32; using Microsoft.Win32;
using System.Text.RegularExpressions;
namespace BevelGenerator namespace BevelGenerator
{ {
@ -20,7 +21,7 @@ namespace BevelGenerator
private static string bevelCacheDir; private static string bevelCacheDir;
private static string studioCookies = ""; private static string studioCookies = "";
private static string xCsrfToken = "Fetch"; private static string xCsrfToken = "FETCH";
private static Random rng = new Random(); private static Random rng = new Random();
@ -102,7 +103,7 @@ namespace BevelGenerator
// Update the X-CSRF-TOKEN. // Update the X-CSRF-TOKEN.
xCsrfToken = response.Headers.Get("X-CSRF-TOKEN"); xCsrfToken = response.Headers.Get("X-CSRF-TOKEN");
// Retry the request again. // Retry the upload.
return UploadMesh(mesh, name, desc); return UploadMesh(mesh, name, desc);
} }
else else
@ -240,39 +241,79 @@ namespace BevelGenerator
Console.Clear(); Console.Clear();
} }
static void ProcessObjFile(FileInfo info) static Mesh PortObjFile(FileInfo info)
{ {
Console.WriteLine("Reading obj file...");
string objPath = info.FullName; string objPath = info.FullName;
Mesh mesh = Mesh.FromObjFile(objPath); Mesh mesh = Mesh.FromObjFile(objPath);
string meshPath = objPath.Replace(info.Extension, ".mesh"); Console.WriteLine("Writing mesh file...");
FileStream file = File.OpenWrite(meshPath);
using (file) string extension = info.Extension;
string filePath = objPath.Replace(extension, ".mesh");
using (FileStream file = File.OpenWrite(filePath))
{ {
file.SetLength(0); file.SetLength(0);
mesh.Save(file); mesh.Save(file);
} }
Debugger.Break(); return mesh;
} }
static void ProcessFileArg(string filePath) static void ProcessFileArg(string filePath)
{ {
FileInfo info = new FileInfo(filePath); FileInfo info = new FileInfo(filePath);
if (info.Extension == ".obj") switch (info.Extension)
{ {
ProcessObjFile(info); case ".obj":
return; Mesh export = PortObjFile(info);
}
else if (info.Extension == ".bin" || info.Extension == ".mesh")
{
Mesh mesh = Mesh.FromFile(filePath);
Debugger.Break();
}
ProcessModelFile(filePath); Console.Write("Would you like to upload this mesh? (y/n): ");
string answer = Console.ReadLine();
if (answer.ToLower()[0] == 'y')
{
Console.Write("Enter a name for this mesh: ");
string name = Console.ReadLine();
Console.Write("Enter a description for this mesh: ");
string desc = Console.ReadLine();
Console.WriteLine("Uploading mesh...");
long result = 0;
try
{
result = UploadMesh(export, name, desc);
}
catch (Exception e)
{
Console.WriteLine($"An error occurred while uploading: {e.Message}");
}
if (result > 0)
{
Clipboard.SetText($"rbxassetid://{result}");
Console.WriteLine($"Result -> rbxassetid://{result} (copied to clipboard)");
}
Debugger.Break();
}
break;
case ".bin":
case ".mesh":
Mesh import = Mesh.FromFile(filePath);
Debugger.Break();
break;
default:
ProcessModelFile(filePath);
break;
}
} }
[STAThread] [STAThread]
@ -294,16 +335,17 @@ namespace BevelGenerator
foreach (string name in robloxCookies.GetValueNames()) foreach (string name in robloxCookies.GetValueNames())
{ {
string cookie = robloxCookies.GetString(name); string cookie = robloxCookies.GetString(name);
Match match = Regex.Match(cookie, "COOK::<([^>]*)>");
int startIndex = cookie.IndexOf("COOK::<") + 7; if (match.Groups.Count > 1)
int endIndex = cookie.IndexOf('>', startIndex); {
cookie = match.Groups[1].Value;
cookie = cookie.Substring(startIndex, endIndex - startIndex); if (studioCookies.Length > 0)
studioCookies += "; ";
if (studioCookies.Length > 0) studioCookies += $"{name}={cookie}";
studioCookies += "; "; }
studioCookies += $"{name}={cookie}";
} }
if (args.Length > 0) if (args.Length > 0)