public Vertex sub(Vertex other) { return new Vertex(this.x.theValue - other.x.theValue, this.y.theValue - other.y.theValue, this.z.theValue - other.z.theValue); }
//For dealing with type Vertex public int add(Vertex v) { int min_ix = (int)((v.x.theValue - this.threshold) / this.cell_width); int min_iy = (int)((v.y.theValue - this.threshold) / this.cell_width); int min_iz = (int)((v.z.theValue - this.threshold) / this.cell_width); int max_ix = (int)((v.x.theValue + this.threshold) / this.cell_width); int max_iy = (int)((v.y.theValue + this.threshold) / this.cell_width); int max_iz = (int)((v.z.theValue + this.threshold) / this.cell_width); for (int ix = min_ix; ix < max_ix + 1; ix++) { for (int iy = min_iy; iy < max_iy + 1; iy++) { for (int iz = min_iz; iz < max_iz + 1; iz++) { foreach (int index in this.buckets[(int)this.vertex_hash(ix, iy, iz)]) { if (v.sub(this.vertices[index]).norm_sq() < this.threshold * this.threshold) return index; } } } } this.vertices.Add(v); this.buckets[(int)this.vertex_hash((int)(v.x.theValue / this.cell_width), (int)(v.y.theValue / this.cell_width), (int)(v.z.theValue / this.cell_width))].Add(this.vertices.Count - 1); return (this.vertices.Count - 1); }
public Vertex iadd(Vertex other) { this.x.theValue += other.x.theValue; this.y.theValue += other.y.theValue; this.z.theValue += other.z.theValue; return this; }
public Vertex isub(Vertex other) { this.x.theValue -= other.x.theValue; this.y.theValue -= other.y.theValue; this.z.theValue -= other.z.theValue; return this; }
public Triangle(Vertex u, Vertex v, Vertex w, int group) { this.u = u; this.v = v; this.w = w; this.n = KCLWriter.unit(KCLWriter.cross(v.sub(u), w.sub(u))); this.group = group; }
public Vertex add(Vertex other) { return new Vertex(this.x.theValue + other.x.theValue, this.y.theValue + other.y.theValue, this.z.theValue + other.z.theValue); }
//Base octree constructor public Octree(List<Triangle> triangles, int max_triangles, float min_width) { this.triangles = triangles; this.max_triangles = max_triangles; this.min_width = min_width; float min_x = 0, min_y = 0, min_z = 0, max_x = 0, max_y = 0, max_z = 0; foreach (Triangle t in triangles) { float min_x0 = KCLWriter.min(KCLWriter.min(t.u.x.theValue, t.v.x.theValue, t.w.x.theValue)); if (min_x0 < min_x) min_x = min_x0; float min_y0 = KCLWriter.min(KCLWriter.min(t.u.y.theValue, t.v.y.theValue, t.w.y.theValue)); if (min_y0 < min_y) min_y = min_y0; float min_z0 = KCLWriter.min(KCLWriter.min(t.u.z.theValue, t.v.z.theValue, t.w.z.theValue)); if (min_z0 < min_z) min_z = min_z0; float max_x0 = KCLWriter.max(KCLWriter.max(t.u.x.theValue, t.v.x.theValue, t.w.x.theValue)); if (max_x0 > max_x) max_x = max_x0; float max_y0 = KCLWriter.max(KCLWriter.max(t.u.y.theValue, t.v.y.theValue, t.w.y.theValue)); if (max_y0 > max_y) max_y = max_y0; float max_z0 = KCLWriter.max(KCLWriter.max(t.u.z.theValue, t.v.z.theValue, t.w.z.theValue)); if (max_z0 > max_z) max_z = max_z0; } // If model only uses two axes, eg. flat square, the base width will get set to min_width (1) which can // create an octree with 100's of thousands of tiny empty or almost empty nodes is very computationally expensive if (max_x == 0) max_x = KCLWriter.max(max_y, max_z); if (max_y == 0) max_y = KCLWriter.max(max_x, max_z); if (max_z == 0) max_z = KCLWriter.max(max_x, max_y); this.width_x = (float)Math.Pow(2, (int)(Math.Ceiling(Math.Log(KCLWriter.max(max_x - min_x, min_width), 2)))); this.width_y = (float)Math.Pow(2, (int)(Math.Ceiling(Math.Log(KCLWriter.max(max_y - min_y, min_width), 2)))); this.width_z = (float)Math.Pow(2, (int)(Math.Ceiling(Math.Log(KCLWriter.max(max_z - min_z, min_width), 2)))); this.base_width = KCLWriter.min(width_x, width_y, width_z); this.bas = new Vertex(min_x, min_y, min_z); this.nx = (int)Math.Floor(this.width_x / this.base_width); this.ny = (int)Math.Floor(this.width_y / this.base_width); this.nz = (int)Math.Floor(this.width_z / this.base_width); List<int> ind = new List<int>(); for (int i = 0; i < triangles.Count; i++) ind.Add(i); for (int k = 0; k < this.nz; k++) { for (int j = 0; j < this.ny; j++) { for (int i = 0; i < this.nx; i++) { this.children.Add(new Octree(this.bas.add((new Vertex(i, j, k)).mul(this.base_width)), this.base_width, ind, this.triangles, this.max_triangles, this.min_width)); } } } }
//Constructor for creating nodes in the octree public Octree(Vertex bas, float width, List<int> indices, List<Triangle> triangles, int max_triangles, float min_width) { Vertex centre = bas.add(new Vertex(width, width, width).truediv(2f)); this.triangles = triangles; this.max_triangles = max_triangles; this.min_width = min_width; this.width_x /= 2f; this.width_y /= 2f; this.width_z /= 2f; this.base_width = KCLWriter.min(width_x, width_y, width_z); this.bas = new Vertex(width / 2f, width / 2f, width / 2f); foreach (int i in indices) { if (KCLWriter.tribox_overlap(this.triangles[i], centre, width / 2f)) { this.indices.Add(i); } } this.is_leaf = true; //Console.WriteLine("node hw: " + width / 2f + " num of indices: " + this.indices.Count); if (this.indices.Count > this.max_triangles && width >= (2 * this.min_width)) { for (int k = 0; k < 2; k++) { for (int j = 0; j < 2; j++) { for (int i = 0; i < 2; i++) { this.children.Add(new Octree(bas.add(new Vertex((float)i, (float)j, (float)k).truediv(2f).mul(width)), width / 2, this.indices, this.triangles, this.max_triangles, this.min_width)); } } } this.indices.Clear(); this.is_leaf = false; } }
private static List<Triangle> readLoadedModel(ModelBase model, float faceSizeThreshold, Dictionary<string, int> matColTypes) { List<Triangle> triangles = new List<Triangle>(); foreach (ModelBase.BoneDef bone in model.m_BoneTree) { foreach (ModelBase.GeometryDef geometry in bone.m_Geometries.Values) { foreach (ModelBase.PolyListDef polyList in geometry.m_PolyLists.Values) { string material = polyList.m_MaterialName; foreach (ModelBase.FaceListDef faceList in polyList.m_FaceLists) { foreach (ModelBase.FaceDef face in faceList.m_Faces) { if (face.m_NumVertices == 3) { Vertex u = new Vertex( face.m_Vertices[0].m_Position.X, face.m_Vertices[0].m_Position.Y, face.m_Vertices[0].m_Position.Z); Vertex v = new Vertex( face.m_Vertices[1].m_Position.X, face.m_Vertices[1].m_Position.Y, face.m_Vertices[1].m_Position.Z); Vertex w = new Vertex( face.m_Vertices[2].m_Position.X, face.m_Vertices[2].m_Position.Y, face.m_Vertices[2].m_Position.Z); //Below line gets rid of faces that are too small, original 0.001 if (cross(v.sub(u), w.sub(u)).norm_sq() < faceSizeThreshold) { continue; } //#TODO: find a better solution triangles.Add(new Triangle(u, v, w, matColTypes[material])); } else if (face.m_NumVertices == 4) { Vertex u1 = new Vertex( face.m_Vertices[0].m_Position.X, face.m_Vertices[0].m_Position.Y, face.m_Vertices[0].m_Position.Z); Vertex v1 = new Vertex( face.m_Vertices[1].m_Position.X, face.m_Vertices[1].m_Position.Y, face.m_Vertices[1].m_Position.Z); Vertex w1 = new Vertex( face.m_Vertices[3].m_Position.X, face.m_Vertices[3].m_Position.Y, face.m_Vertices[3].m_Position.Z); //Below line gets rid of faces that are too small, original 0.001 if (cross(v1.sub(u1), w1.sub(u1)).norm_sq() < faceSizeThreshold) { continue; } //#TODO: find a better solution triangles.Add(new Triangle(u1, v1, w1, matColTypes[material])); Vertex u2 = new Vertex( face.m_Vertices[1].m_Position.X, face.m_Vertices[1].m_Position.Y, face.m_Vertices[1].m_Position.Z); Vertex v2 = new Vertex( face.m_Vertices[2].m_Position.X, face.m_Vertices[2].m_Position.Y, face.m_Vertices[2].m_Position.Z); Vertex w2 = new Vertex( face.m_Vertices[3].m_Position.X, face.m_Vertices[3].m_Position.Y, face.m_Vertices[3].m_Position.Z); //Below line gets rid of faces that are too small, original 0.001 if (cross(v2.sub(u2), w2.sub(u2)).norm_sq() < faceSizeThreshold) { continue; } //#TODO: find a better solution triangles.Add(new Triangle(u2, v2, w2, matColTypes[material])); } } } } } } return triangles; }
public static bool tribox_overlap(Triangle triangle, Vertex centre, float half_width) { /*Intersection test for triangle and axis-aligned cube. Test if the triangle intersects the axis-aligned cube given by the center and half width. This algorithm is an adapted version of the algorithm presented here: http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/tribox3.txt */ Vertex u = triangle.u.sub(centre); Vertex v = triangle.v.sub(centre); Vertex w = triangle.w.sub(centre); Vector n = triangle.n; // Test for separation along the axes normal to the faces of the cube if (max(u.x.theValue, v.x.theValue, w.x.theValue) < -half_width || min(u.x.theValue, v.x.theValue, w.x.theValue) > half_width) return false; if (max(u.y.theValue, v.y.theValue, w.y.theValue) < -half_width || min(u.y.theValue, v.y.theValue, w.y.theValue) > half_width) return false; if (max(u.z.theValue, v.z.theValue, w.z.theValue) < -half_width || min(u.z.theValue, v.z.theValue, w.z.theValue) > half_width) return false; // Test for separation along the axis normal to the face of the triangle float d = dot(n, u); float r = half_width * (Math.Abs(n.x.theValue) + Math.Abs(n.y.theValue) + Math.Abs(n.z.theValue)); if (d < -r || d > r) return false; // Test for separation along the axes parallel to the cross products of the // edges of the triangle and the edges of the cube if (edge_test(u, v, w, half_width)) return false; if (edge_test(v, w, u, half_width)) return false; if (edge_test(w, u, v, half_width)) return false; // Triangle and cube intersects return true; }
public static bool edge_test(Vertex v0, Vertex v1, Vertex v2, float hw) { Vertex e = v1.sub(v0); if (edge_axis_test(e.z.theValue, -e.y.theValue, v0.y.theValue, v0.z.theValue, v2.y.theValue, v2.z.theValue, hw)) return true; if (edge_axis_test(-e.z.theValue, e.x.theValue, v0.x.theValue, v0.z.theValue, v2.x.theValue, v2.z.theValue, hw)) return true; if (edge_axis_test(e.y.theValue, -e.x.theValue, v0.x.theValue, v0.y.theValue, v2.x.theValue, v2.y.theValue, hw)) return true; return false; }
public static float dot(Vertex a, Vector b) { return a.x.theValue * b.x.theValue + a.y.theValue * b.y.theValue + a.z.theValue * b.z.theValue; }
//Vertex, Vector > Vector public static Vector cross(Vertex a, Vector b) { return new Vector(a.y.theValue * b.z.theValue - a.z.theValue * b.y.theValue, a.z.theValue * b.x.theValue - a.x.theValue * b.z.theValue, a.x.theValue * b.y.theValue - a.y.theValue * b.x.theValue); }