/// <summary> /// Get an ordered list of the faces to render based on the camera position. /// </summary> /// <param name="node"></param> /// <param name="meshToViewTransform"></param> /// <param name="invMeshToViewTransform"></param> /// <param name="faceRenderOrder"></param> public static IEnumerable<int> GetFacesInVisibiltyOrder(Mesh mesh, BspNode root, Matrix4X4 meshToViewTransform, Matrix4X4 invMeshToViewTransform) { var renderOrder = new Stack<BspNode>(new BspNode[] { root.RenderOrder(mesh, meshToViewTransform, invMeshToViewTransform) }); do { var lastBack = renderOrder.Peek().BackNode; while (lastBack != null && lastBack.Index != -1) { renderOrder.Peek().BackNode = null; renderOrder.Push(lastBack.RenderOrder(mesh, meshToViewTransform, invMeshToViewTransform)); lastBack = renderOrder.Peek().BackNode; } var node = renderOrder.Pop(); if (node.Index != -1) { yield return node.Index; } var lastFront = node.FrontNode; if (lastFront != null && lastFront.Index != -1) { renderOrder.Push(lastFront.RenderOrder(mesh, meshToViewTransform, invMeshToViewTransform)); } } while (renderOrder.Any()); }
public static BspNode RenderOrder(this BspNode node, Mesh mesh, Matrix4X4 meshToViewTransform, Matrix4X4 invMeshToViewTransform) { var faceNormalInViewSpace = mesh.Faces[node.Index].normal.TransformNormalInverse(invMeshToViewTransform); var pointOnFaceInViewSpace = mesh.Vertices[mesh.Faces[node.Index].v0].Transform(meshToViewTransform); var infrontOfFace = faceNormalInViewSpace.Dot(pointOnFaceInViewSpace) < 0; if (infrontOfFace) { return new BspNode() { Index = node.Index, BackNode = node.BackNode, FrontNode = node.FrontNode }; } else { return new BspNode() { Index = node.Index, BackNode = node.FrontNode, FrontNode = node.BackNode }; } }
public static BspNode RenderOrder(this BspNode node, List <Face> meshFaces, Matrix4X4 meshToViewTransform, Matrix4X4 invMeshToViewTransform) { var faceNormalInViewSpace = Vector3.TransformNormalInverse(meshFaces[node.Index].Normal, invMeshToViewTransform); var pointOnFaceInViewSpace = Vector3.Transform(meshFaces[node.Index].firstFaceEdge.FirstVertex.Position, meshToViewTransform); var infrontOfFace = Vector3.Dot(faceNormalInViewSpace, pointOnFaceInViewSpace) < 0; if (infrontOfFace) { return(new BspNode() { Index = node.Index, BackNode = node.BackNode, FrontNode = node.FrontNode }); } else { return(new BspNode() { Index = node.Index, BackNode = node.FrontNode, FrontNode = node.BackNode }); } }
/// <summary> /// This function will search for the first face that produces no polygon cuts /// and split the tree on it. If it can't find a non-cutting face, /// it will split on the face that minimizes the area that it divides. /// </summary> /// <param name="mesh"></param> /// <returns></returns> public static BspNode Create(Mesh mesh, int maxFacesToTest = 10, bool tryToBalanceTree = false) { BspNode root = new BspNode(); var faces = new List <Face>(mesh.Faces); CreateNoSplitingFast(mesh.Faces, root, faces, maxFacesToTest, tryToBalanceTree); return(root); }
/// <summary> /// This function will search for the first face that produces no polygon cuts /// and split the tree on it. If it can't find a non-cutting face, /// it will split on the face that minimizes the area that it divides. /// </summary> /// <param name="mesh"></param> /// <returns></returns> public static BspNode Create(Mesh mesh, int maxFacesToTest = 10, bool tryToBalanceTree = false) { BspNode root = new BspNode(); var sourceFaces = Enumerable.Range(0, mesh.Faces.Count).ToList(); var faces = Enumerable.Range(0, mesh.Faces.Count).ToList(); CreateNoSplittingFast(mesh, sourceFaces, root, faces, maxFacesToTest, tryToBalanceTree); return root; }
private static void CreateNoSplittingFast(Mesh mesh, List <int> sourceFaces, BspNode node, List <int> faces, int maxFacesToTest, bool tryToBalanceTree) { if (faces.Count == 0) { return; } int bestFaceIndex = -1; double smallestCrossingArrea = double.MaxValue; int bestBalance = int.MaxValue; // find the first face that does not split anything int step = Math.Max(1, faces.Count / maxFacesToTest); for (int i = 0; i < faces.Count; i += step) { // calculate how much of polygons cross this face (double crossingArrea, int balance) = CalculateCrossingArea(mesh, i, faces, smallestCrossingArrea); // keep track of the best face so far if (crossingArrea < smallestCrossingArrea) { smallestCrossingArrea = crossingArrea; bestBalance = balance; bestFaceIndex = i; if (crossingArrea == 0 && !tryToBalanceTree) { break; } } else if (crossingArrea == smallestCrossingArrea && balance < bestBalance) { // the crossing area is the same but the tree balance is better bestBalance = balance; bestFaceIndex = i; } } node.Index = sourceFaces.IndexOf(faces[bestFaceIndex]); // put the behind stuff in a list var backFaces = new List <int>(); var frontFaces = new List <int>(); CreateBackAndFrontFaceLists(mesh, bestFaceIndex, faces, backFaces, frontFaces); CreateNoSplittingFast(mesh, sourceFaces, node.BackNode = new BspNode(), backFaces, maxFacesToTest, tryToBalanceTree); CreateNoSplittingFast(mesh, sourceFaces, node.FrontNode = new BspNode(), frontFaces, maxFacesToTest, tryToBalanceTree); }