[source=c#]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using System.IO;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Drawing;
namespace MKDS_Course_Modifier.Converters.Obj2Kcl
{
class Obj2Kcl
{
public static void ConvertToKcl(string infile, string outfile)
{
object[] o = read_obj(infile);
write_kcl(outfile, o[0] as List, (Vector3)o[1], (Vector3)o[2],23, 1, o[3] as string[], o[4] as int[]);
}
private static object[] read_obj(string filename)
{
Vector3 bb_min = new Vector3();
Vector3 bb_max = new Vector3();
List vertices = new List();
List triangles = new List();
List Normals = new List();
List Materialu = new List();
List names = new List();
Stream fs = File.OpenRead(filename);
StreamReader sr = new StreamReader(fs);
string curmaterial = "";
CultureInfo usahax = new CultureInfo("en-US");
string curline;
while ((curline = sr.ReadLine()) != null)
{
curline = curline.Trim();
// skip empty lines and comments
if (curline.Length < 1) continue;
if (curline[0] == '#') continue;
string[] parts = curline.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 1) continue;
int face = 0;
switch (parts[0])
{
case "mtllib": // material lib file
{
}
break;
case "usemtl": // material name
if (parts.Length < 2) continue;
curmaterial = parts[1];
if (!names.Contains(curmaterial))
{
names.Add(curmaterial);
}
break;
case "v": // vertex
{
if (parts.Length < 4) continue;
float x = float.Parse(parts[1], usahax);
float y = float.Parse(parts[2], usahax);
float z = float.Parse(parts[3], usahax);
if (x < bb_min.X || bb_min.X == 0) { bb_min.X = x; }
if (y < bb_min.Y || bb_min.Y == 0) { bb_min.Y = y; }
if (z < bb_min.Z || bb_min.Z == 0) { bb_min.Z = z; }
if (x > bb_max.X || bb_max.X == 0) { bb_max.X = x; }
if (y > bb_max.Y || bb_max.Y == 0) { bb_max.Y = y; }
if (z > bb_max.Z || bb_max.Z == 0) { bb_max.Z = z; }
vertices.Add(new Vector3(x, y, z));
}
break;
case "vt": // texcoord
{
}
break;
case "vn": // normal
{
if (parts.Length < 4) continue;
float x = float.Parse(parts[1], usahax);
float y = float.Parse(parts[2], usahax);
float z = float.Parse(parts[3], usahax);
Normals.Add(new Vector3(x, y, z));
//Vector3 vec = new Vector3(x, y, z);
//vec.Normalize();
//m_Normals.Add(vec);
}
break;
case "f": // face
{
if (parts.Length < 4) continue;
Vector3 u = vertices[int.Parse(parts[1].Split('/')[0]) - 1];
Vector3 v = vertices[int.Parse(parts[2].Split('/')[0]) - 1];
Vector3 w = vertices[int.Parse(parts[3].Split('/')[0]) - 1];
if (parts[1].Split('/').Length == 2)
{
Vector3 n = Normals[int.Parse(parts[1].Split('/')[2]) - 1];
if (norm_sq(Vector3.Cross(v - u, w - u)) < 0.001) { continue; } //#TODO: find a better solution
triangles.Add(new Triangle(u, v, w, n));
Materialu.Add(names.IndexOf(curmaterial));
}
else
{
if (norm_sq(Vector3.Cross(v - u, w - u)) < 0.001) { continue; } //#TODO: find a better solution
triangles.Add(new Triangle(u, v, w));
Materialu.Add(names.IndexOf(curmaterial));
}
}
break;
}
}
sr.Close();
return new object[] { triangles, bb_min, bb_max, names.ToArray(), Materialu.ToArray() };
}
private static float maxcubesize = -1;
private static void write_kcl(string filename, List triangles, Vector3 bb_min, Vector3 bb_max, int max_triangles, int min_width, string[] names, int[] materialu)
{
bb_min -= new Vector3(25f, 25f, 25f);
bb_max += new Vector3(25f, 25f, 25f);
kclType ty = new kclType(names);
ty.DialogResult = System.Windows.Forms.DialogResult.None;
ty.ShowDialog();
while (ty.DialogResult != System.Windows.Forms.DialogResult.OK) ;
Dictionary Mapping;
Dictionary Colli;
Mapping = ty.Mapping;
Colli = ty.Colli;
int n_min = next_exponent(min_width, 2);
int n_x = Math.Max(next_exponent(bb_max.X - bb_min.X, 2), n_min);
int n_y = Math.Max(next_exponent(bb_max.Y - bb_min.Y, 2), n_min);
int n_z = Math.Max(next_exponent(bb_max.Z - bb_min.Z, 2), n_min);
n_x = (int)max(n_x, n_z);
n_z = n_x;
int n = Math.Max((int)min(n_x, n_y, n_z), n_min);
float divs_x = (float)Math.Pow(2, (n_x - n));
float divs_y = (float)Math.Pow(2, (n_y - n));
float divs_z = (float)Math.Pow(2, (n_z - n));
float width = (float)Math.Pow(2, n);
maxcubesize = width;
List octree = new List();
List indices = new List();
int l = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[l]]])
{
indices.Add(l);
}
l++;
}
for (int k = 0; k < divs_z; k++)
{
for (int j = 0; j < divs_y; j++)
{
for (int i = 0; i < divs_x; i++)
{
bool st;
octree.Add(new OctreeNode(bb_min + width * new Vector3(i, j, k), width, triangles, indices, max_triangles, min_width, out st));
}
}
}
//OctreeNode octree = [OctreeNode(bb_min + width*Vector(i,j,k),width,triangles,xrange(len(triangles)),max_triangles,min_width)
// for k in xrange(divs_z) for j in xrange(divs_y) for i in xrange(divs_x)]
EndianBinaryWriter f = new EndianBinaryWriter(File.Create(filename), Endianness.LittleEndian);
//# header
f.Write((UInt32)0); //# vertex offset placeholder
f.Write((UInt32)0); //# vector offset placeholder
f.Write((UInt32)0); //# triangle offset placeholder
f.Write((UInt32)0); //# octree offset placeholder
f.Write((UInt32)122880); //# unknown
f.Write((Int32)(bb_min.X * 4096)); //# x min
f.Write((Int32)(bb_min.Y * 4096)); //# y min
f.Write((Int32)(bb_min.Z * 4096)); //# z min
f.Write((UInt32)((0xFFFFFFFF << n_x) & 0xFFFFFFFF)); //# x mask
f.Write((UInt32)((0xFFFFFFFF << n_y) & 0xFFFFFFFF)); //# y mask
f.Write((UInt32)((0xFFFFFFFF << n_z) & 0xFFFFFFFF)); //# z mask
f.Write((UInt32)n); //# coordinate shift
f.Write((UInt32)(n_x - n)); //# y shift
f.Write((UInt32)(n_x - n + n_y - n)); //# z shift
f.Write((UInt32)102400); //# unknown
//# vertex section
int pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 0;
f.Write((Int32)pos); //# vertex offset
f.BaseStream.Position = pos;
List Vt = new List();
List Videx = new List();
int ii = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[ii]]])
{
int d = containsVector3(t.u, Vt);
if (d == -1)
{
Vt.Add(t.u);
Videx.Add(Vt.Count - 1);
}
else
{
Videx.Add(d);
}
}
ii++;
}
foreach (Vector3 c in Vt)
{
f.Write((Int32)(c.X * 4096));
f.Write((Int32)(c.Y * 4096));
f.Write((Int32)(c.Z * 4096));
}
//# vector section
pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 4;
f.Write((Int32)pos); //# vector offset
f.BaseStream.Position = pos;
List Norm = new List();
List Nidex = new List();
ii = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[ii]]])
{
Vector3 a = -unit(Vector3.Cross(t.w - t.u, t.n));
Vector3 b = unit(Vector3.Cross(t.v - t.u, t.n));
Vector3 c = unit(Vector3.Cross(t.w - t.v, t.n));
int d = containsVector3(t.n, Norm);
if (d == -1)
{
Norm.Add(t.n);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
d = containsVector3(a, Norm);
if (d == -1)
{
Norm.Add(a);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
d = containsVector3(b, Norm);
if (d == -1)
{
Norm.Add(b);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
d = containsVector3(c, Norm);
if (d == -1)
{
Norm.Add(c);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
}
ii++;
}
foreach (Vector3 c in Norm)
{
f.Write((Int16)(c.X * 4096));
f.Write((Int16)(c.Y * 4096));
f.Write((Int16)(c.Z * 4096));
}
while (f.BaseStream.Position % 4 != 0) { f.Write((Byte)0); }
//# triangle section
pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 8;
f.Write((Int32)(pos - 16)); //# triangle offset
f.BaseStream.Position = pos;
int m = 0;
ii = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[ii]]])
{
Vector3 c = unit(Vector3.Cross(t.w - t.v, t.n));
f.Write((Int32)(Vector3.Dot(t.w - t.u, c) * 4096)); //# length
f.Write((UInt16)(Videx[m])); //# vertex index
f.Write((UInt16)(Nidex[4 * m])); //# normal index
f.Write((UInt16)(Nidex[4 * m + 1])); //# a index
f.Write((UInt16)(Nidex[4 * m + 2])); //# b index
f.Write((UInt16)(Nidex[4 * m + 3])); //# c index
f.Write(Bytes.StringToByte(string.Format("{0:X4}", Mapping[names[materialu[ii]]])), 0, 2); //# surface type
m++;
}
ii++;
}
//# octree section
pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 12;
f.Write((Int32)pos); //# octree offset
f.BaseStream.Position = pos;
long bas = f.BaseStream.Position;
Queue nodes;
Queue nodeStarts;
OctreeNode current;
Collection dataSection;
int progress, totalNodes;
long freeAddress, startAddress, dataStart;
startAddress = freeAddress = f.BaseStream.Position;
nodes = new Queue();
dataSection = new Collection();
nodeStarts = new Queue();
for (int i = 0; i < octree.Count; i++)
{
nodeStarts.Enqueue(freeAddress);
nodes.Enqueue(octree);
}
freeAddress += octree.Count * 4;
progress = 0;
totalNodes = octree.Count;
while (nodes.Count > 0)
{
current = nodes.Dequeue();
if (current.is_leaf)
{
f.Write(0);
nodeStarts.Dequeue();
}
else
{
f.Write((int)(freeAddress - nodeStarts.Dequeue()));
for (int i = 0; i < current.branches.Count; i++)
{
nodes.Enqueue(current.branches);
nodeStarts.Enqueue(freeAddress);
totalNodes++;
}
freeAddress += 0x20;
}
progress++;
//progressHandler.Invoke(10 + ((45 * progress) / totalNodes), Program.GetString("ProgressWritingOctree"));
}
dataStart = f.BaseStream.Position;
freeAddress = startAddress;
f.BaseStream.Seek(startAddress, SeekOrigin.Begin);
for (int i = 0; i < octree.Count; i++)
{
nodeStarts.Enqueue(freeAddress);
nodes.Enqueue(octree);
}
freeAddress += octree.Count * 4;
progress = 0;
while (nodes.Count > 0)
{
startAddress = f.BaseStream.Position;
current = nodes.Dequeue();
if (current.is_leaf)
{
f.Write((int)(int.MinValue | (f.BaseStream.Length - nodeStarts.Dequeue() - 2)));
f.BaseStream.Seek(0, SeekOrigin.End);
for (int i = 0; i < current.indices.Count; i++)
f.Write((ushort)(current.indices + 1));
f.Write((ushort)0);
for (int i = 0; i < current.indices.Count; i++)
dataSection.Add((ushort)(current.indices + 1));
dataSection.Add(0);
f.BaseStream.Seek(startAddress + 4, SeekOrigin.Begin);
}
else
{
nodeStarts.Dequeue();
f.BaseStream.Seek(4, SeekOrigin.Current);
for (int i = 0; i < current.branches.Count; i++)
{
nodes.Enqueue(current.branches);
nodeStarts.Enqueue(freeAddress);
}
freeAddress += 0x20;
}
progress++;
//progressHandler.Invoke(55 + ((45 * progress) / totalNodes), Program.GetString("ProgressWritingOctree"));
}
f.Close();
}
private static int containsVector3(Vector3 a, List b)
{
for (int i = 0; i < b.Count; i++)
{
if (b.X == a.X && b.Y == a.Y && b.Z == a.Z)
{
return i;
}
}
return -1;
}
private static int next_exponent(float value, int bas)
{
if (value <= 1) { return 0; }
return (int)(Math.Ceiling(Math.Log(value, bas)));
}
public static float norm_sq(Vector3 a)
{
return a.X * a.X + a.Y * a.Y + a.Z * a.Z;
}
public static float norm(Vector3 a)
{
return (float)Math.Sqrt(norm_sq(a));
}
public static Vector3 unit(Vector3 a)
{
Vector3 r = a;
r = Vector3.Divide(r, norm(r));
return r;
}
private class Triangle
{
public Vector3 u;
public Vector3 v;
public Vector3 w;
public Vector3 n;
public Triangle(Vector3 u, Vector3 v, Vector3 w)
{
this.u = u;
this.v = v;
this.w = w;
this.n = unit(Vector3.Cross(v - u, w - u));
}
public Triangle(Vector3 u, Vector3 v, Vector3 w, Vector3 n)
{
this.u = u;
this.v = v;
this.w = w;
this.n = n;
}
}
private class OctreeNode : Cube
{
public bool is_leaf;
public List indices = new List();
public List branches = new List();
public OctreeNode(Vector3 bas, float width, List triangles, List indices, int max_triangles, int min_width, out bool stayd)
{
stayd = false;
//Cube.__init__(self,base,width)
this.hw = width / 2.0f;
this.c = bas + new Vector3(this.hw, this.hw, this.hw);
this.is_leaf = true;
bool stay = false;
bool go = false;
foreach (int i in indices)
{
bool stay2;
bool go2;
if (tricube_overlap(triangles, this, out stay2, out go2)) { this.indices.Add(i); }
if (stay == false && stay2)
{
stay = stay2;
}
if (go == false && go2)
{
go = go2;
}
}
if (stay && this.indices.Count < 64)
{
stayd = true;
return;
}
else if (this.indices.Count >= 64)
{
go = true;
}
if (this.indices.Count > max_triangles && this.hw > min_width/* || this.hw > 32 && this.indices.Count > 0*/ || go && this.hw > min_width)
{
this.is_leaf = false;
for (int k = 0; k < 2; k++)
{
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < 2; i++)
{
bool st;
this.branches.Add(new OctreeNode(bas + this.hw * new Vector3(i, j, k), this.hw, triangles, this.indices, max_triangles, min_width, out st));
if (st)
{
this.is_leaf = true;
foreach (int l in indices)
{
bool stay2;
bool go2;
if (tricube_overlap(triangles, this, out stay2, out go2)) { this.indices.Add(l); }
}
this.branches.Clear();
return;
}
}
}
}
this.indices.Clear();
}
}
}
private class Cube
{
public float hw;
public Vector3 c;
//def __init__(self,base,width):
// self.hw = width/2.0
// self.c = base + Vector(self.hw,self.hw,self.hw)
public bool axis_test(float a1, float a2, float b1, float b2, float c1, float c2, Cube c)
{
float p = a1 * b1 + a2 * b2;
float q = a1 * c1 + a2 * c2;
float r = /*(c.hw + c.hw / 4f)*/c.hw * (Math.Abs(a1) + Math.Abs(a2));
return Math.Min(p, q) > r || Math.Max(p, q) < -r;
}
public bool tricube_overlap(Triangle t, Cube c, out bool stay, out bool go)
{
/*float oldhw = c.hw;
//c.hw += 1;
//c.hw += 10;
stay = false;
go = false;
Vector3 v0 = t.u - c.c;
Vector3 v1 = t.v - c.c;
Vector3 v2 = t.w - c.c;
float hw = c.hw;
c.hw += hw/4f;
if (min(v0.X, v1.X, v2.X) > c.hw || max(v0.X, v1.X, v2.X) < -/*(c.hw + c.hw / 4f)/c.hw) { goto endfalse; }
if (min(v0.Y, v1.Y, v2.Y) > c.hw || max(v0.Y, v1.Y, v2.Y) < -/*(c.hw + c.hw / 4f)/c.hw) { goto endfalse; }
if (min(v0.Z, v1.Z, v2.Z) > c.hw || max(v0.Z, v1.Z, v2.Z) < -/*(c.hw + c.hw / 4f)/c.hw) { goto endfalse; }
/*float d = Vector3.Dot(t.n, v0);
float r = /*(c.hw + c.hw / 4f)/c.hw * (Math.Abs(t.n.X) + Math.Abs(t.n.Y) + Math.Abs(t.n.Z));
if (d > r || d < -r) { goto endfalse; }
Vector3 e = v1 - v0;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { goto endfalse; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { goto endfalse; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { goto endfalse; }
e = v2 - v1;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { goto endfalse; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { goto endfalse; }
if (axis_test(e.Y, -e.X, v0.X, v0.Y, v1.X, v1.Y, c)) { goto endfalse; }
e = v0 - v2;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v1.Y, v1.Z, c)) { goto endfalse; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v1.X, v1.Z, c)) { goto endfalse; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { goto endfalse; }/
c.hw = hw;
if (c.hw < 64)
{
if (v0.Y > (c.hw / 2f) + (c.hw / 4f) || v1.Y > (c.hw / 2f) + (c.hw / 4f) || v2.Y > (c.hw / 2f) + (c.hw / 4f))// || v0.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v1.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v2.Y < -(c.hw / 2.0f) - (c.hw / 4f))
{
stay = true;
//go = true;
}
}
return true;
endfalse:
c.hw = hw;
return false;
}*/
/*stay = false;
System.Windows.Media.Media3D.Rect3D r = new System.Windows.Media.Media3D.Rect3D(c.c.X - c.hw, c.c.Y - c.hw, c.c.Z - c.hw, c.hw, c.hw, c.hw);
if (r.Contains(new System.Windows.Media.Media3D.Point3D(t.u.X, t.u.Y, t.u.Z)) || r.Contains(new System.Windows.Media.Media3D.Point3D(t.v.X, t.v.Y, t.v.Z)) || r.Contains(new System.Windows.Media.Media3D.Point3D(t.w.X, t.w.Y, t.w.Z)))
{
return true;
}
return false;*/
//c.hw += 1;
//c.hw += 10;
stay = false;
go = false;
Vector3 v0 = t.u - c.c;
Vector3 v1 = t.v - c.c;
Vector3 v2 = t.w - c.c;
if (min(v0.X, v1.X, v2.X) > c.hw || max(v0.X, v1.X, v2.X) < -(c.hw + c.hw / 4f)) { return false; }
if (min(v0.Y, v1.Y, v2.Y) > c.hw || max(v0.Y, v1.Y, v2.Y) < -(c.hw + c.hw / 4f)) { return false; }
if (min(v0.Z, v1.Z, v2.Z) > c.hw || max(v0.Z, v1.Z, v2.Z) < -(c.hw + c.hw / 4f)) { return false; }
float d = Vector3.Dot(t.n, v0);
float r = (c.hw + c.hw / 4f) * (Math.Abs(t.n.X) + Math.Abs(t.n.Y) + Math.Abs(t.n.Z));
if (d > r || d < -r) { return false; }
Vector3 e = v1 - v0;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { return false; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { return false; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { return false; }
e = v2 - v1;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { return false; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { return false; }
if (axis_test(e.Y, -e.X, v0.X, v0.Y, v1.X, v1.Y, c)) { return false; }
e = v0 - v2;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v1.Y, v1.Z, c)) { return false; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v1.X, v1.Z, c)) { return false; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { return false; }
if (c.hw < 64)
{
stay = true;
//if (v0.Y > (c.hw / 2f) + (c.hw / 4f) || v1.Y > (c.hw / 2f) + (c.hw / 4f) || v2.Y > (c.hw / 2f) + (c.hw / 4f))// || v0.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v1.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v2.Y < -(c.hw / 2.0f) - (c.hw / 4f))
//{
// stay = true;
// //go = true;
//}
}
return true;
}
}
private static float min(params float[] v)
{
float min = float.MaxValue;
foreach (float f in v)
{
if (f < min)
{
min = f;
}
}
return min;
}
private static float max(params float[] v)
{
float max = float.MinValue;
foreach (float f in v)
{
if (f > max)
{
max = f;
}
}
return max;
}
private static Vector3 MinMax(Vector3 min, Vector3 max, Vector3 input)
{
Vector3 a = input;
if (min.X > a.X)
min.X = a.X;
if (min.Y > a.Y)
min.Y = a.Y;
if (min.Z > a.Z)
min.Z = a.Z;
if (max.X < a.X)
max.X = a.X;
if (max.Y < a.Y)
max.Y = a.Y;
if (max.Z < a.Z)
max.Z = a.Z;
return a;
}
}
}
[/source]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using System.IO;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Drawing;
namespace MKDS_Course_Modifier.Converters.Obj2Kcl
{
class Obj2Kcl
{
public static void ConvertToKcl(string infile, string outfile)
{
object[] o = read_obj(infile);
write_kcl(outfile, o[0] as List
}
private static object[] read_obj(string filename)
{
Vector3 bb_min = new Vector3();
Vector3 bb_max = new Vector3();
List
List
List
List
List
Stream fs = File.OpenRead(filename);
StreamReader sr = new StreamReader(fs);
string curmaterial = "";
CultureInfo usahax = new CultureInfo("en-US");
string curline;
while ((curline = sr.ReadLine()) != null)
{
curline = curline.Trim();
// skip empty lines and comments
if (curline.Length < 1) continue;
if (curline[0] == '#') continue;
string[] parts = curline.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 1) continue;
int face = 0;
switch (parts[0])
{
case "mtllib": // material lib file
{
}
break;
case "usemtl": // material name
if (parts.Length < 2) continue;
curmaterial = parts[1];
if (!names.Contains(curmaterial))
{
names.Add(curmaterial);
}
break;
case "v": // vertex
{
if (parts.Length < 4) continue;
float x = float.Parse(parts[1], usahax);
float y = float.Parse(parts[2], usahax);
float z = float.Parse(parts[3], usahax);
if (x < bb_min.X || bb_min.X == 0) { bb_min.X = x; }
if (y < bb_min.Y || bb_min.Y == 0) { bb_min.Y = y; }
if (z < bb_min.Z || bb_min.Z == 0) { bb_min.Z = z; }
if (x > bb_max.X || bb_max.X == 0) { bb_max.X = x; }
if (y > bb_max.Y || bb_max.Y == 0) { bb_max.Y = y; }
if (z > bb_max.Z || bb_max.Z == 0) { bb_max.Z = z; }
vertices.Add(new Vector3(x, y, z));
}
break;
case "vt": // texcoord
{
}
break;
case "vn": // normal
{
if (parts.Length < 4) continue;
float x = float.Parse(parts[1], usahax);
float y = float.Parse(parts[2], usahax);
float z = float.Parse(parts[3], usahax);
Normals.Add(new Vector3(x, y, z));
//Vector3 vec = new Vector3(x, y, z);
//vec.Normalize();
//m_Normals.Add(vec);
}
break;
case "f": // face
{
if (parts.Length < 4) continue;
Vector3 u = vertices[int.Parse(parts[1].Split('/')[0]) - 1];
Vector3 v = vertices[int.Parse(parts[2].Split('/')[0]) - 1];
Vector3 w = vertices[int.Parse(parts[3].Split('/')[0]) - 1];
if (parts[1].Split('/').Length == 2)
{
Vector3 n = Normals[int.Parse(parts[1].Split('/')[2]) - 1];
if (norm_sq(Vector3.Cross(v - u, w - u)) < 0.001) { continue; } //#TODO: find a better solution
triangles.Add(new Triangle(u, v, w, n));
Materialu.Add(names.IndexOf(curmaterial));
}
else
{
if (norm_sq(Vector3.Cross(v - u, w - u)) < 0.001) { continue; } //#TODO: find a better solution
triangles.Add(new Triangle(u, v, w));
Materialu.Add(names.IndexOf(curmaterial));
}
}
break;
}
}
sr.Close();
return new object[] { triangles, bb_min, bb_max, names.ToArray(), Materialu.ToArray() };
}
private static float maxcubesize = -1;
private static void write_kcl(string filename, List
{
bb_min -= new Vector3(25f, 25f, 25f);
bb_max += new Vector3(25f, 25f, 25f);
kclType ty = new kclType(names);
ty.DialogResult = System.Windows.Forms.DialogResult.None;
ty.ShowDialog();
while (ty.DialogResult != System.Windows.Forms.DialogResult.OK) ;
Dictionary
Dictionary
Mapping = ty.Mapping;
Colli = ty.Colli;
int n_min = next_exponent(min_width, 2);
int n_x = Math.Max(next_exponent(bb_max.X - bb_min.X, 2), n_min);
int n_y = Math.Max(next_exponent(bb_max.Y - bb_min.Y, 2), n_min);
int n_z = Math.Max(next_exponent(bb_max.Z - bb_min.Z, 2), n_min);
n_x = (int)max(n_x, n_z);
n_z = n_x;
int n = Math.Max((int)min(n_x, n_y, n_z), n_min);
float divs_x = (float)Math.Pow(2, (n_x - n));
float divs_y = (float)Math.Pow(2, (n_y - n));
float divs_z = (float)Math.Pow(2, (n_z - n));
float width = (float)Math.Pow(2, n);
maxcubesize = width;
List
List
int l = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[l]]])
{
indices.Add(l);
}
l++;
}
for (int k = 0; k < divs_z; k++)
{
for (int j = 0; j < divs_y; j++)
{
for (int i = 0; i < divs_x; i++)
{
bool st;
octree.Add(new OctreeNode(bb_min + width * new Vector3(i, j, k), width, triangles, indices, max_triangles, min_width, out st));
}
}
}
//OctreeNode octree = [OctreeNode(bb_min + width*Vector(i,j,k),width,triangles,xrange(len(triangles)),max_triangles,min_width)
// for k in xrange(divs_z) for j in xrange(divs_y) for i in xrange(divs_x)]
EndianBinaryWriter f = new EndianBinaryWriter(File.Create(filename), Endianness.LittleEndian);
//# header
f.Write((UInt32)0); //# vertex offset placeholder
f.Write((UInt32)0); //# vector offset placeholder
f.Write((UInt32)0); //# triangle offset placeholder
f.Write((UInt32)0); //# octree offset placeholder
f.Write((UInt32)122880); //# unknown
f.Write((Int32)(bb_min.X * 4096)); //# x min
f.Write((Int32)(bb_min.Y * 4096)); //# y min
f.Write((Int32)(bb_min.Z * 4096)); //# z min
f.Write((UInt32)((0xFFFFFFFF << n_x) & 0xFFFFFFFF)); //# x mask
f.Write((UInt32)((0xFFFFFFFF << n_y) & 0xFFFFFFFF)); //# y mask
f.Write((UInt32)((0xFFFFFFFF << n_z) & 0xFFFFFFFF)); //# z mask
f.Write((UInt32)n); //# coordinate shift
f.Write((UInt32)(n_x - n)); //# y shift
f.Write((UInt32)(n_x - n + n_y - n)); //# z shift
f.Write((UInt32)102400); //# unknown
//# vertex section
int pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 0;
f.Write((Int32)pos); //# vertex offset
f.BaseStream.Position = pos;
List
List
int ii = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[ii]]])
{
int d = containsVector3(t.u, Vt);
if (d == -1)
{
Vt.Add(t.u);
Videx.Add(Vt.Count - 1);
}
else
{
Videx.Add(d);
}
}
ii++;
}
foreach (Vector3 c in Vt)
{
f.Write((Int32)(c.X * 4096));
f.Write((Int32)(c.Y * 4096));
f.Write((Int32)(c.Z * 4096));
}
//# vector section
pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 4;
f.Write((Int32)pos); //# vector offset
f.BaseStream.Position = pos;
List
List
ii = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[ii]]])
{
Vector3 a = -unit(Vector3.Cross(t.w - t.u, t.n));
Vector3 b = unit(Vector3.Cross(t.v - t.u, t.n));
Vector3 c = unit(Vector3.Cross(t.w - t.v, t.n));
int d = containsVector3(t.n, Norm);
if (d == -1)
{
Norm.Add(t.n);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
d = containsVector3(a, Norm);
if (d == -1)
{
Norm.Add(a);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
d = containsVector3(b, Norm);
if (d == -1)
{
Norm.Add(b);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
d = containsVector3(c, Norm);
if (d == -1)
{
Norm.Add(c);
Nidex.Add(Norm.Count - 1);
}
else
{
Nidex.Add(d);
}
}
ii++;
}
foreach (Vector3 c in Norm)
{
f.Write((Int16)(c.X * 4096));
f.Write((Int16)(c.Y * 4096));
f.Write((Int16)(c.Z * 4096));
}
while (f.BaseStream.Position % 4 != 0) { f.Write((Byte)0); }
//# triangle section
pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 8;
f.Write((Int32)(pos - 16)); //# triangle offset
f.BaseStream.Position = pos;
int m = 0;
ii = 0;
foreach (Triangle t in triangles)
{
if (Colli[names[materialu[ii]]])
{
Vector3 c = unit(Vector3.Cross(t.w - t.v, t.n));
f.Write((Int32)(Vector3.Dot(t.w - t.u, c) * 4096)); //# length
f.Write((UInt16)(Videx[m])); //# vertex index
f.Write((UInt16)(Nidex[4 * m])); //# normal index
f.Write((UInt16)(Nidex[4 * m + 1])); //# a index
f.Write((UInt16)(Nidex[4 * m + 2])); //# b index
f.Write((UInt16)(Nidex[4 * m + 3])); //# c index
f.Write(Bytes.StringToByte(string.Format("{0:X4}", Mapping[names[materialu[ii]]])), 0, 2); //# surface type
m++;
}
ii++;
}
//# octree section
pos = (int)f.BaseStream.Position;
f.BaseStream.Position = 12;
f.Write((Int32)pos); //# octree offset
f.BaseStream.Position = pos;
long bas = f.BaseStream.Position;
Queue
Queue
OctreeNode current;
Collection
int progress, totalNodes;
long freeAddress, startAddress, dataStart;
startAddress = freeAddress = f.BaseStream.Position;
nodes = new Queue
dataSection = new Collection
nodeStarts = new Queue
for (int i = 0; i < octree.Count; i++)
{
nodeStarts.Enqueue(freeAddress);
nodes.Enqueue(octree);
}
freeAddress += octree.Count * 4;
progress = 0;
totalNodes = octree.Count;
while (nodes.Count > 0)
{
current = nodes.Dequeue();
if (current.is_leaf)
{
f.Write(0);
nodeStarts.Dequeue();
}
else
{
f.Write((int)(freeAddress - nodeStarts.Dequeue()));
for (int i = 0; i < current.branches.Count; i++)
{
nodes.Enqueue(current.branches);
nodeStarts.Enqueue(freeAddress);
totalNodes++;
}
freeAddress += 0x20;
}
progress++;
//progressHandler.Invoke(10 + ((45 * progress) / totalNodes), Program.GetString("ProgressWritingOctree"));
}
dataStart = f.BaseStream.Position;
freeAddress = startAddress;
f.BaseStream.Seek(startAddress, SeekOrigin.Begin);
for (int i = 0; i < octree.Count; i++)
{
nodeStarts.Enqueue(freeAddress);
nodes.Enqueue(octree);
}
freeAddress += octree.Count * 4;
progress = 0;
while (nodes.Count > 0)
{
startAddress = f.BaseStream.Position;
current = nodes.Dequeue();
if (current.is_leaf)
{
f.Write((int)(int.MinValue | (f.BaseStream.Length - nodeStarts.Dequeue() - 2)));
f.BaseStream.Seek(0, SeekOrigin.End);
for (int i = 0; i < current.indices.Count; i++)
f.Write((ushort)(current.indices + 1));
f.Write((ushort)0);
for (int i = 0; i < current.indices.Count; i++)
dataSection.Add((ushort)(current.indices + 1));
dataSection.Add(0);
f.BaseStream.Seek(startAddress + 4, SeekOrigin.Begin);
}
else
{
nodeStarts.Dequeue();
f.BaseStream.Seek(4, SeekOrigin.Current);
for (int i = 0; i < current.branches.Count; i++)
{
nodes.Enqueue(current.branches);
nodeStarts.Enqueue(freeAddress);
}
freeAddress += 0x20;
}
progress++;
//progressHandler.Invoke(55 + ((45 * progress) / totalNodes), Program.GetString("ProgressWritingOctree"));
}
f.Close();
}
private static int containsVector3(Vector3 a, List
{
for (int i = 0; i < b.Count; i++)
{
if (b.X == a.X && b.Y == a.Y && b.Z == a.Z)
{
return i;
}
}
return -1;
}
private static int next_exponent(float value, int bas)
{
if (value <= 1) { return 0; }
return (int)(Math.Ceiling(Math.Log(value, bas)));
}
public static float norm_sq(Vector3 a)
{
return a.X * a.X + a.Y * a.Y + a.Z * a.Z;
}
public static float norm(Vector3 a)
{
return (float)Math.Sqrt(norm_sq(a));
}
public static Vector3 unit(Vector3 a)
{
Vector3 r = a;
r = Vector3.Divide(r, norm(r));
return r;
}
private class Triangle
{
public Vector3 u;
public Vector3 v;
public Vector3 w;
public Vector3 n;
public Triangle(Vector3 u, Vector3 v, Vector3 w)
{
this.u = u;
this.v = v;
this.w = w;
this.n = unit(Vector3.Cross(v - u, w - u));
}
public Triangle(Vector3 u, Vector3 v, Vector3 w, Vector3 n)
{
this.u = u;
this.v = v;
this.w = w;
this.n = n;
}
}
private class OctreeNode : Cube
{
public bool is_leaf;
public List
public List
public OctreeNode(Vector3 bas, float width, List
{
stayd = false;
//Cube.__init__(self,base,width)
this.hw = width / 2.0f;
this.c = bas + new Vector3(this.hw, this.hw, this.hw);
this.is_leaf = true;
bool stay = false;
bool go = false;
foreach (int i in indices)
{
bool stay2;
bool go2;
if (tricube_overlap(triangles, this, out stay2, out go2)) { this.indices.Add(i); }
if (stay == false && stay2)
{
stay = stay2;
}
if (go == false && go2)
{
go = go2;
}
}
if (stay && this.indices.Count < 64)
{
stayd = true;
return;
}
else if (this.indices.Count >= 64)
{
go = true;
}
if (this.indices.Count > max_triangles && this.hw > min_width/* || this.hw > 32 && this.indices.Count > 0*/ || go && this.hw > min_width)
{
this.is_leaf = false;
for (int k = 0; k < 2; k++)
{
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < 2; i++)
{
bool st;
this.branches.Add(new OctreeNode(bas + this.hw * new Vector3(i, j, k), this.hw, triangles, this.indices, max_triangles, min_width, out st));
if (st)
{
this.is_leaf = true;
foreach (int l in indices)
{
bool stay2;
bool go2;
if (tricube_overlap(triangles, this, out stay2, out go2)) { this.indices.Add(l); }
}
this.branches.Clear();
return;
}
}
}
}
this.indices.Clear();
}
}
}
private class Cube
{
public float hw;
public Vector3 c;
//def __init__(self,base,width):
// self.hw = width/2.0
// self.c = base + Vector(self.hw,self.hw,self.hw)
public bool axis_test(float a1, float a2, float b1, float b2, float c1, float c2, Cube c)
{
float p = a1 * b1 + a2 * b2;
float q = a1 * c1 + a2 * c2;
float r = /*(c.hw + c.hw / 4f)*/c.hw * (Math.Abs(a1) + Math.Abs(a2));
return Math.Min(p, q) > r || Math.Max(p, q) < -r;
}
public bool tricube_overlap(Triangle t, Cube c, out bool stay, out bool go)
{
/*float oldhw = c.hw;
//c.hw += 1;
//c.hw += 10;
stay = false;
go = false;
Vector3 v0 = t.u - c.c;
Vector3 v1 = t.v - c.c;
Vector3 v2 = t.w - c.c;
float hw = c.hw;
c.hw += hw/4f;
if (min(v0.X, v1.X, v2.X) > c.hw || max(v0.X, v1.X, v2.X) < -/*(c.hw + c.hw / 4f)/c.hw) { goto endfalse; }
if (min(v0.Y, v1.Y, v2.Y) > c.hw || max(v0.Y, v1.Y, v2.Y) < -/*(c.hw + c.hw / 4f)/c.hw) { goto endfalse; }
if (min(v0.Z, v1.Z, v2.Z) > c.hw || max(v0.Z, v1.Z, v2.Z) < -/*(c.hw + c.hw / 4f)/c.hw) { goto endfalse; }
/*float d = Vector3.Dot(t.n, v0);
float r = /*(c.hw + c.hw / 4f)/c.hw * (Math.Abs(t.n.X) + Math.Abs(t.n.Y) + Math.Abs(t.n.Z));
if (d > r || d < -r) { goto endfalse; }
Vector3 e = v1 - v0;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { goto endfalse; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { goto endfalse; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { goto endfalse; }
e = v2 - v1;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { goto endfalse; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { goto endfalse; }
if (axis_test(e.Y, -e.X, v0.X, v0.Y, v1.X, v1.Y, c)) { goto endfalse; }
e = v0 - v2;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v1.Y, v1.Z, c)) { goto endfalse; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v1.X, v1.Z, c)) { goto endfalse; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { goto endfalse; }/
c.hw = hw;
if (c.hw < 64)
{
if (v0.Y > (c.hw / 2f) + (c.hw / 4f) || v1.Y > (c.hw / 2f) + (c.hw / 4f) || v2.Y > (c.hw / 2f) + (c.hw / 4f))// || v0.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v1.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v2.Y < -(c.hw / 2.0f) - (c.hw / 4f))
{
stay = true;
//go = true;
}
}
return true;
endfalse:
c.hw = hw;
return false;
}*/
/*stay = false;
System.Windows.Media.Media3D.Rect3D r = new System.Windows.Media.Media3D.Rect3D(c.c.X - c.hw, c.c.Y - c.hw, c.c.Z - c.hw, c.hw, c.hw, c.hw);
if (r.Contains(new System.Windows.Media.Media3D.Point3D(t.u.X, t.u.Y, t.u.Z)) || r.Contains(new System.Windows.Media.Media3D.Point3D(t.v.X, t.v.Y, t.v.Z)) || r.Contains(new System.Windows.Media.Media3D.Point3D(t.w.X, t.w.Y, t.w.Z)))
{
return true;
}
return false;*/
//c.hw += 1;
//c.hw += 10;
stay = false;
go = false;
Vector3 v0 = t.u - c.c;
Vector3 v1 = t.v - c.c;
Vector3 v2 = t.w - c.c;
if (min(v0.X, v1.X, v2.X) > c.hw || max(v0.X, v1.X, v2.X) < -(c.hw + c.hw / 4f)) { return false; }
if (min(v0.Y, v1.Y, v2.Y) > c.hw || max(v0.Y, v1.Y, v2.Y) < -(c.hw + c.hw / 4f)) { return false; }
if (min(v0.Z, v1.Z, v2.Z) > c.hw || max(v0.Z, v1.Z, v2.Z) < -(c.hw + c.hw / 4f)) { return false; }
float d = Vector3.Dot(t.n, v0);
float r = (c.hw + c.hw / 4f) * (Math.Abs(t.n.X) + Math.Abs(t.n.Y) + Math.Abs(t.n.Z));
if (d > r || d < -r) { return false; }
Vector3 e = v1 - v0;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { return false; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { return false; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { return false; }
e = v2 - v1;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v2.Y, v2.Z, c)) { return false; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v2.X, v2.Z, c)) { return false; }
if (axis_test(e.Y, -e.X, v0.X, v0.Y, v1.X, v1.Y, c)) { return false; }
e = v0 - v2;
if (axis_test(e.Z, -e.Y, v0.Y, v0.Z, v1.Y, v1.Z, c)) { return false; }
if (axis_test(-e.Z, e.X, v0.X, v0.Z, v1.X, v1.Z, c)) { return false; }
if (axis_test(e.Y, -e.X, v1.X, v1.Y, v2.X, v2.Y, c)) { return false; }
if (c.hw < 64)
{
stay = true;
//if (v0.Y > (c.hw / 2f) + (c.hw / 4f) || v1.Y > (c.hw / 2f) + (c.hw / 4f) || v2.Y > (c.hw / 2f) + (c.hw / 4f))// || v0.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v1.Y < -(c.hw / 2.0f) - (c.hw / 4f) || v2.Y < -(c.hw / 2.0f) - (c.hw / 4f))
//{
// stay = true;
// //go = true;
//}
}
return true;
}
}
private static float min(params float[] v)
{
float min = float.MaxValue;
foreach (float f in v)
{
if (f < min)
{
min = f;
}
}
return min;
}
private static float max(params float[] v)
{
float max = float.MinValue;
foreach (float f in v)
{
if (f > max)
{
max = f;
}
}
return max;
}
private static Vector3 MinMax(Vector3 min, Vector3 max, Vector3 input)
{
Vector3 a = input;
if (min.X > a.X)
min.X = a.X;
if (min.Y > a.Y)
min.Y = a.Y;
if (min.Z > a.Z)
min.Z = a.Z;
if (max.X < a.X)
max.X = a.X;
if (max.Y < a.Y)
max.Y = a.Y;
if (max.Z < a.Z)
max.Z = a.Z;
return a;
}
}
}
[/source]