/// <summary> /// Impresion recursiva /// </summary> private void doPrintDebugKdTree(KdTreeNode node, int index, StringBuilder sb) { String lineas = ""; for (int i = 0; i < index; i++) { lineas += "-"; } if (node.isLeaf()) { if (node.models.Length > 0) { sb.Append(lineas + "Models [" + node.models.Length + "]" + "\n"); } else { sb.Append(lineas + "[0]" + "\n"); } } else { sb.Append(lineas + "\n"); index++; for (int i = 0; i < node.children.Length; i++) { doPrintDebugKdTree(node.children[i], index, sb); } } }
/// <summary> /// Corte con plano Y /// </summary> private void doSectorKdTreeY(KdTreeNode parent, Vector3 pMin, Vector3 pMax, int step, List <TgcMesh> meshes, int childIndex, float xCutValue) { //Crear listas para realizar corte List <TgcMesh> possitiveList = new List <TgcMesh>(); List <TgcMesh> negativeList = new List <TgcMesh>(); //Y-cut float cutValue = 0; Plane yCutPlane = getCutPlane(meshes, new Vector3(0, 1, 0), pMin.Y, pMax.Y, ref cutValue); splitByPlane(yCutPlane, meshes, possitiveList, negativeList); //recursividad de positivos con plano Z, usando resultados positivos y childIndex 0 doSectorKdTreeZ(parent, new Vector3(pMin.X, cutValue, pMin.Z), pMax, step, possitiveList, childIndex + 0, xCutValue, cutValue); //recursividad de negativos con plano Z, usando plano X negativo y childIndex 2 doSectorKdTreeZ(parent, pMin, new Vector3(pMax.X, cutValue, pMax.Z), step, negativeList, childIndex + 2, xCutValue, cutValue); }
/// <summary> /// Corte con plano X /// </summary> private void doSectorKdTreeX(KdTreeNode parent, Vector3 pMin, Vector3 pMax, int step, List <TgcMesh> meshes) { //Crear listas para realizar corte List <TgcMesh> possitiveList = new List <TgcMesh>(); List <TgcMesh> negativeList = new List <TgcMesh>(); //X-cut float cutValue = 0; Plane xCutPlane = getCutPlane(meshes, new Vector3(1, 0, 0), pMin.X, pMax.X, ref cutValue); splitByPlane(xCutPlane, meshes, possitiveList, negativeList); //recursividad de positivos con plano Y, usando resultados positivos y childIndex 0 doSectorKdTreeY(parent, new Vector3(cutValue, pMin.Y, pMin.Z), pMax, step, possitiveList, 0, cutValue); //recursividad de negativos con plano Y, usando resultados negativos y childIndex 4 doSectorKdTreeY(parent, pMin, new Vector3(cutValue, pMax.Y, pMax.Z), step, negativeList, 4, cutValue); }
/// <summary> /// Hacer visibles todas las meshes de un nodo /// </summary> private void selectLeafMeshes(KdTreeNode node) { TgcMesh[] models = node.models; foreach (TgcMesh m in models) { m.Enabled = true; } }
/// <summary> /// Imprime por consola la generacion del KdTree /// </summary> private void printDebugKdTree(KdTreeNode rootNode) { Console.WriteLine("########## KdTree DEBUG ##########"); StringBuilder sb = new StringBuilder(); doPrintDebugKdTree(rootNode, 0, sb); Console.WriteLine(sb.ToString()); Console.WriteLine("########## FIN KdTree DEBUG ##########"); }
/// <summary> /// Dibujar meshes que representan los sectores del KdTree /// </summary> public List<TgcDebugBox> createDebugKdTreeMeshes(KdTreeNode rootNode, TgcBoundingBox sceneBounds) { Vector3 pMax = sceneBounds.PMax; Vector3 pMin = sceneBounds.PMin; List<TgcDebugBox> debugBoxes = new List<TgcDebugBox>(); doCreateKdTreeDebugBox(rootNode, debugBoxes, pMin.X, pMin.Y, pMin.Z, pMax.X, pMax.Y, pMax.Z, 0); return debugBoxes; }
/// <summary> /// Dibujar meshes que representan los sectores del KdTree /// </summary> public List <TgcDebugBox> createDebugKdTreeMeshes(KdTreeNode rootNode, TgcBoundingBox sceneBounds) { Vector3 pMax = sceneBounds.PMax; Vector3 pMin = sceneBounds.PMin; List <TgcDebugBox> debugBoxes = new List <TgcDebugBox>(); doCreateKdTreeDebugBox(rootNode, debugBoxes, pMin.X, pMin.Y, pMin.Z, pMax.X, pMax.Y, pMax.Z, 0); return(debugBoxes); }
/// <summary> /// Crear nuevo KdTree /// </summary> /// <param name="modelos">Modelos a optimizar</param> /// <param name="sceneBounds">Límites del escenario</param> public void create(List<TgcMesh> modelos, TgcBoundingBox sceneBounds) { this.modelos = modelos; this.sceneBounds = sceneBounds; //Crear KdTree this.kdtreeRootNode = builder.crearKdTree(modelos, sceneBounds); //Deshabilitar todos los mesh inicialmente foreach (TgcMesh mesh in modelos) { mesh.Enabled = false; } }
/// <summary> /// Se fija si los hijos de un nodo no tienen mas hijos y no tienen ningun triangulo /// </summary> private bool hasEmptyChilds(KdTreeNode node) { KdTreeNode[] children = node.children; for (int i = 0; i < children.Length; i++) { KdTreeNode childNode = children[i]; if (childNode.children != null || childNode.models.Length > 0) { return(false); } } return(true); }
/// <summary> /// Crear nuevo KdTree /// </summary> /// <param name="modelos">Modelos a optimizar</param> /// <param name="sceneBounds">Límites del escenario</param> public void create(List <TgcMesh> modelos, TgcBoundingBox sceneBounds) { this.modelos = modelos; this.sceneBounds = sceneBounds; //Crear KdTree this.kdtreeRootNode = builder.crearKdTree(modelos, sceneBounds); //Deshabilitar todos los mesh inicialmente foreach (TgcMesh mesh in modelos) { mesh.Enabled = false; } }
/// <summary> /// Imprime estadisticas del KdTree /// </summary> private void printEstadisticasKdTree(KdTreeNode rootNode) { Console.WriteLine("*********** KdTree Statics ***********"); int minModels = int.MaxValue; int maxModels = int.MinValue; obtenerEstadisticas(rootNode, ref minModels, ref maxModels); Console.WriteLine("Minima cantidad de modelos en hoja: " + minModels); Console.WriteLine("Maxima cantidad de modelos en hoja: " + maxModels); Console.WriteLine("*********** FIN Octree Statics ************"); }
private void doCreateKdTreeDebugBox(KdTreeNode node, List <TgcDebugBox> debugBoxes, float boxLowerX, float boxLowerY, float boxLowerZ, float boxUpperX, float boxUpperY, float boxUpperZ, int step) { KdTreeNode[] children = node.children; //Crear caja debug TgcDebugBox box = createDebugBox(boxLowerX, boxLowerY, boxLowerZ, boxUpperX, boxUpperY, boxUpperZ, step); debugBoxes.Add(box); //es hoja, dibujar caja if (children == null) { } //recursividad sobre hijos else { step++; float xCut = node.xCut; float yCut = node.yCut; float zCut = node.zCut; //000 doCreateKdTreeDebugBox(children[0], debugBoxes, xCut, yCut, zCut, boxUpperX, boxUpperY, boxUpperZ, step); //001 doCreateKdTreeDebugBox(children[1], debugBoxes, xCut, yCut, boxLowerZ, boxUpperX, boxUpperY, zCut, step); //010 doCreateKdTreeDebugBox(children[2], debugBoxes, xCut, boxLowerY, zCut, boxUpperX, yCut, boxUpperZ, step); //011 doCreateKdTreeDebugBox(children[3], debugBoxes, xCut, boxLowerY, boxLowerZ, boxUpperX, yCut, zCut, step); //100 doCreateKdTreeDebugBox(children[4], debugBoxes, boxLowerX, yCut, zCut, xCut, boxUpperY, boxUpperZ, step); //101 doCreateKdTreeDebugBox(children[5], debugBoxes, boxLowerX, yCut, boxLowerZ, xCut, boxUpperY, zCut, step); //110 doCreateKdTreeDebugBox(children[6], debugBoxes, boxLowerX, boxLowerY, zCut, xCut, yCut, boxUpperZ, step); //111 doCreateKdTreeDebugBox(children[7], debugBoxes, boxLowerX, boxLowerY, boxLowerZ, xCut, yCut, zCut, step); } }
public KdTreeNode crearKdTree(List <TgcMesh> modelos, TgcBoundingBox sceneBounds) { KdTreeNode rootNode = new KdTreeNode(); //iniciar generacion recursiva de KdTree doSectorKdTreeX(rootNode, sceneBounds.PMin, sceneBounds.PMax, 0, modelos); //podar nodos innecesarios optimizeSectorKdTree(rootNode.children); //imprimir por consola el KdTree //printDebugKdTree(rootNode); //imprimir estadisticas de debug //printEstadisticasKdTree(rootNode); return(rootNode); }
public KdTreeNode crearKdTree(List<TgcMesh> modelos, TgcBoundingBox sceneBounds) { KdTreeNode rootNode = new KdTreeNode(); //iniciar generacion recursiva de KdTree doSectorKdTreeX(rootNode, sceneBounds.PMin, sceneBounds.PMax, 0, modelos); //podar nodos innecesarios optimizeSectorKdTree(rootNode.children); //imprimir por consola el KdTree //printDebugKdTree(rootNode); //imprimir estadisticas de debug //printEstadisticasKdTree(rootNode); return rootNode; }
/// <summary> /// Hacer visibles todas las meshes de un nodo, buscando recursivamente sus hojas /// </summary> private void addAllLeafMeshes(KdTreeNode node) { KdTreeNode[] children = node.children; //es hoja, cargar todos los meshes if (children == null) { selectLeafMeshes(node); //pedir hojas a hijos } else { for (int i = 0; i < children.Length; i++) { addAllLeafMeshes(children[i]); } } }
/// <summary> /// Recorrer recursivamente el KdTree para encontrar los nodos visibles /// </summary> private void findVisibleMeshes(TgcFrustum frustum, KdTreeNode node, float boxLowerX, float boxLowerY, float boxLowerZ, float boxUpperX, float boxUpperY, float boxUpperZ) { KdTreeNode[] children = node.children; //es hoja, cargar todos los meshes if (children == null) { selectLeafMeshes(node); } //recursividad sobre hijos else { float xCut = node.xCut; float yCut = node.yCut; float zCut = node.zCut; //000 testChildVisibility(frustum, children[0], xCut, yCut, zCut, boxUpperX, boxUpperY, boxUpperZ); //001 testChildVisibility(frustum, children[1], xCut, yCut, boxLowerZ, boxUpperX, boxUpperY, zCut); //010 testChildVisibility(frustum, children[2], xCut, boxLowerY, zCut, boxUpperX, yCut, boxUpperZ); //011 testChildVisibility(frustum, children[3], xCut, boxLowerY, boxLowerZ, boxUpperX, yCut, zCut); //100 testChildVisibility(frustum, children[4], boxLowerX, yCut, zCut, xCut, boxUpperY, boxUpperZ); //101 testChildVisibility(frustum, children[5], boxLowerX, yCut, boxLowerZ, xCut, boxUpperY, zCut); //110 testChildVisibility(frustum, children[6], boxLowerX, boxLowerY, zCut, xCut, yCut, boxUpperZ); //111 testChildVisibility(frustum, children[7], boxLowerX, boxLowerY, boxLowerZ, xCut, yCut, zCut); } }
/// <summary> /// Hacer visible las meshes de un nodo si es visible por el Frustum /// </summary> private void testChildVisibility(TgcFrustum frustum, KdTreeNode childNode, float boxLowerX, float boxLowerY, float boxLowerZ, float boxUpperX, float boxUpperY, float boxUpperZ) { //test frustum-box intersection TgcBoundingBox caja = new TgcBoundingBox( new Vector3(boxLowerX, boxLowerY, boxLowerZ), new Vector3(boxUpperX, boxUpperY, boxUpperZ)); TgcCollisionUtils.FrustumResult c = TgcCollisionUtils.classifyFrustumAABB(frustum, caja); //complementamente adentro: cargar todos los hijos directamente, sin testeos if (c == TgcCollisionUtils.FrustumResult.INSIDE) { addAllLeafMeshes(childNode); } //parte adentro: seguir haciendo testeos con hijos else if (c == TgcCollisionUtils.FrustumResult.INTERSECT) { findVisibleMeshes(frustum, childNode, boxLowerX, boxLowerY, boxLowerZ, boxUpperX, boxUpperY, boxUpperZ); } }
/// <summary> /// Se quitan padres cuyos nodos no tengan ningun triangulo /// </summary> private void optimizeSectorKdTree(KdTreeNode[] children) { if (children == null) { return; } for (int i = 0; i < children.Length; i++) { KdTreeNode childNode = children[i]; KdTreeNode[] childNodeChildren = childNode.children; if (childNodeChildren != null && hasEmptyChilds(childNode)) { childNode.children = null; childNode.models = new TgcMesh[0]; } else { optimizeSectorKdTree(childNodeChildren); } } }
private void obtenerEstadisticas(KdTreeNode node, ref int minModels, ref int maxModels) { if (node.isLeaf()) { int n = node.models.Length; if (n < minModels) { minModels = n; } if (n > maxModels) { maxModels = n; } } else { for (int i = 0; i < node.children.Length; i++) { obtenerEstadisticas(node.children[i], ref minModels, ref maxModels); } } }
/// <summary> /// Corte con plano X /// </summary> private void doSectorKdTreeX(KdTreeNode parent, Vector3 pMin, Vector3 pMax, int step, List<TgcMesh> meshes) { //Crear listas para realizar corte List<TgcMesh> possitiveList = new List<TgcMesh>(); List<TgcMesh> negativeList = new List<TgcMesh>(); //X-cut float cutValue = 0; Plane xCutPlane = getCutPlane(meshes, new Vector3(1, 0, 0), pMin.X, pMax.X, ref cutValue); splitByPlane(xCutPlane, meshes, possitiveList, negativeList); //recursividad de positivos con plano Y, usando resultados positivos y childIndex 0 doSectorKdTreeY(parent, new Vector3(cutValue, pMin.Y, pMin.Z), pMax, step, possitiveList, 0, cutValue); //recursividad de negativos con plano Y, usando resultados negativos y childIndex 4 doSectorKdTreeY(parent, pMin, new Vector3(cutValue, pMax.Y, pMax.Z), step, negativeList, 4, cutValue); }
/// <summary> /// Corte con plano Y /// </summary> private void doSectorKdTreeY(KdTreeNode parent, Vector3 pMin, Vector3 pMax, int step, List<TgcMesh> meshes, int childIndex, float xCutValue) { //Crear listas para realizar corte List<TgcMesh> possitiveList = new List<TgcMesh>(); List<TgcMesh> negativeList = new List<TgcMesh>(); //Y-cut float cutValue = 0; Plane yCutPlane = getCutPlane(meshes, new Vector3(0, 1, 0), pMin.Y, pMax.Y, ref cutValue); splitByPlane(yCutPlane, meshes, possitiveList, negativeList); //recursividad de positivos con plano Z, usando resultados positivos y childIndex 0 doSectorKdTreeZ(parent, new Vector3(pMin.X, cutValue, pMin.Z), pMax, step, possitiveList, childIndex + 0, xCutValue, cutValue); //recursividad de negativos con plano Z, usando plano X negativo y childIndex 2 doSectorKdTreeZ(parent, pMin, new Vector3(pMax.X, cutValue, pMax.Z), step, negativeList, childIndex + 2, xCutValue, cutValue); }
private void doCreateKdTreeDebugBox(KdTreeNode node, List<TgcDebugBox> debugBoxes, float boxLowerX, float boxLowerY, float boxLowerZ, float boxUpperX, float boxUpperY, float boxUpperZ, int step) { KdTreeNode[] children = node.children; //Crear caja debug TgcDebugBox box = createDebugBox(boxLowerX, boxLowerY, boxLowerZ, boxUpperX, boxUpperY, boxUpperZ, step); debugBoxes.Add(box); //es hoja, dibujar caja if (children == null) { } //recursividad sobre hijos else { step++; float xCut = node.xCut; float yCut = node.yCut; float zCut = node.zCut; //000 doCreateKdTreeDebugBox(children[0], debugBoxes, xCut, yCut, zCut, boxUpperX, boxUpperY, boxUpperZ, step); //001 doCreateKdTreeDebugBox(children[1], debugBoxes, xCut, yCut, boxLowerZ, boxUpperX, boxUpperY, zCut, step); //010 doCreateKdTreeDebugBox(children[2], debugBoxes, xCut, boxLowerY, zCut, boxUpperX, yCut, boxUpperZ, step); //011 doCreateKdTreeDebugBox(children[3], debugBoxes, xCut, boxLowerY, boxLowerZ, boxUpperX, yCut, zCut, step); //100 doCreateKdTreeDebugBox(children[4], debugBoxes, boxLowerX, yCut, zCut, xCut, boxUpperY, boxUpperZ, step); //101 doCreateKdTreeDebugBox(children[5], debugBoxes, boxLowerX, yCut, boxLowerZ, xCut, boxUpperY, zCut, step); //110 doCreateKdTreeDebugBox(children[6], debugBoxes, boxLowerX, boxLowerY, zCut, xCut, yCut, boxUpperZ, step); //111 doCreateKdTreeDebugBox(children[7], debugBoxes, boxLowerX, boxLowerY, boxLowerZ, xCut, yCut, zCut, step); } }
/// <summary> /// Corte de plano Z /// </summary> private void doSectorKdTreeZ(KdTreeNode parent, Vector3 pMin, Vector3 pMax, int step, List<TgcMesh> meshes, int childIndex, float xCutValue, float yCutValue) { //Crear listas para realizar corte List<TgcMesh> possitiveList = new List<TgcMesh>(); List<TgcMesh> negativeList = new List<TgcMesh>(); //Z-cut float cutValue = 0; Plane zCutPlane = getCutPlane(meshes, new Vector3(0, 0, 1), pMin.Z, pMax.Z, ref cutValue); splitByPlane(zCutPlane, meshes, possitiveList, negativeList); //obtener lista de children del parent, con iniciacion lazy if (parent.children == null) { parent.children = new KdTreeNode[8]; } //crear nodo positivo en parent, segun childIndex KdTreeNode posNode = new KdTreeNode(); parent.children[childIndex] = posNode; //cargar nodo negativo en parent, segun childIndex KdTreeNode negNode = new KdTreeNode(); parent.children[childIndex + 1] = negNode; //cargar cortes en parent parent.xCut = xCutValue; parent.yCut = yCutValue; parent.zCut = cutValue; //nuevos limites Vector3 v1 = new Vector3(pMax.X - pMin.X, pMax.Y - pMin.Y, pMax.Z - cutValue); Vector3 v2 = new Vector3(pMax.X - pMin.X, pMax.Y - pMin.Y, cutValue - pMin.Z); //condicion de corte if (step >= MAX_SECTOR_KDTREE_RECURSION || meshes.Count <= MIN_MESH_PER_LEAVE_THRESHOLD || v1.X < MIN_VOL || v1.Y < MIN_VOL || v1.Z < MIN_VOL || v2.X < MIN_VOL || v2.Y < MIN_VOL || v2.Z < MIN_VOL ) { //cargar hijos de nodo positivo posNode.models = possitiveList.ToArray(); //cargar hijos de nodo negativo negNode.models = negativeList.ToArray(); } //seguir recursividad else { step++; //recursividad de positivos con plano X, usando resultados positivos doSectorKdTreeX(posNode, new Vector3(pMin.X, pMin.Y, cutValue), pMax, step, possitiveList); //recursividad de negativos con plano Y, usando resultados negativos doSectorKdTreeX(negNode, pMin, new Vector3(pMax.X, pMax.Y, cutValue), step, negativeList); } }
/// <summary> /// Se fija si los hijos de un nodo no tienen mas hijos y no tienen ningun triangulo /// </summary> private bool hasEmptyChilds(KdTreeNode node) { KdTreeNode[] children = node.children; for (int i = 0; i < children.Length; i++) { KdTreeNode childNode = children[i]; if (childNode.children != null || childNode.models.Length > 0) { return false; } } return true; }
/// <summary> /// Corte de plano Z /// </summary> private void doSectorKdTreeZ(KdTreeNode parent, Vector3 pMin, Vector3 pMax, int step, List <TgcMesh> meshes, int childIndex, float xCutValue, float yCutValue) { //Crear listas para realizar corte List <TgcMesh> possitiveList = new List <TgcMesh>(); List <TgcMesh> negativeList = new List <TgcMesh>(); //Z-cut float cutValue = 0; Plane zCutPlane = getCutPlane(meshes, new Vector3(0, 0, 1), pMin.Z, pMax.Z, ref cutValue); splitByPlane(zCutPlane, meshes, possitiveList, negativeList); //obtener lista de children del parent, con iniciacion lazy if (parent.children == null) { parent.children = new KdTreeNode[8]; } //crear nodo positivo en parent, segun childIndex KdTreeNode posNode = new KdTreeNode(); parent.children[childIndex] = posNode; //cargar nodo negativo en parent, segun childIndex KdTreeNode negNode = new KdTreeNode(); parent.children[childIndex + 1] = negNode; //cargar cortes en parent parent.xCut = xCutValue; parent.yCut = yCutValue; parent.zCut = cutValue; //nuevos limites Vector3 v1 = new Vector3(pMax.X - pMin.X, pMax.Y - pMin.Y, pMax.Z - cutValue); Vector3 v2 = new Vector3(pMax.X - pMin.X, pMax.Y - pMin.Y, cutValue - pMin.Z); //condicion de corte if (step >= MAX_SECTOR_KDTREE_RECURSION || meshes.Count <= MIN_MESH_PER_LEAVE_THRESHOLD || v1.X < MIN_VOL || v1.Y < MIN_VOL || v1.Z < MIN_VOL || v2.X < MIN_VOL || v2.Y < MIN_VOL || v2.Z < MIN_VOL ) { //cargar hijos de nodo positivo posNode.models = possitiveList.ToArray(); //cargar hijos de nodo negativo negNode.models = negativeList.ToArray(); } //seguir recursividad else { step++; //recursividad de positivos con plano X, usando resultados positivos doSectorKdTreeX(posNode, new Vector3(pMin.X, pMin.Y, cutValue), pMax, step, possitiveList); //recursividad de negativos con plano Y, usando resultados negativos doSectorKdTreeX(negNode, pMin, new Vector3(pMax.X, pMax.Y, cutValue), step, negativeList); } }
private void obtenerEstadisticas(KdTreeNode node, ref int minModels, ref int maxModels) { if (node.isLeaf()) { int n = node.models.Length; if (n < minModels) minModels = n; if (n > maxModels) maxModels = n; } else { for (int i = 0; i < node.children.Length; i++) { obtenerEstadisticas(node.children[i], ref minModels, ref maxModels); } } }