/// <summary> /// Corte con plano Y /// </summary> private void doSectorKdTreeY(KdTreeNode parent, TGCVector3 pMin, TGCVector3 pMax, int step, List <TgcMesh> meshes, int childIndex, float xCutValue) { //Crear listas para realizar corte var possitiveList = new List <TgcMesh>(); var negativeList = new List <TgcMesh>(); //Y-cut float cutValue = 0; var yCutPlane = getCutPlane(meshes, TGCVector3.Up, 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 TGCVector3(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 TGCVector3(pMax.X, cutValue, pMax.Z), step, negativeList, childIndex + 2, xCutValue, cutValue); }
/// <summary> /// Corte con plano X /// </summary> private void doSectorKdTreeX(KdTreeNode parent, TGCVector3 pMin, TGCVector3 pMax, int step, List <TgcMesh> meshes) { //Crear listas para realizar corte var possitiveList = new List <TgcMesh>(); var negativeList = new List <TgcMesh>(); //X-cut float cutValue = 0; var xCutPlane = getCutPlane(meshes, new TGCVector3(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 TGCVector3(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 TGCVector3(cutValue, pMax.Y, pMax.Z), step, negativeList, 4, cutValue); }
/// <summary> /// Impresion recursiva /// </summary> private void doPrintDebugKdTree(KdTreeNode node, int index, StringBuilder sb) { var lineas = ""; for (var 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 (var i = 0; i < node.children.Length; i++) { doPrintDebugKdTree(node.children[i], index, sb); } } }
/// <summary> /// Hacer visibles todas las meshes de un nodo /// </summary> private void selectLeafMeshes(KdTreeNode node) { var models = node.models; foreach (var m in models) { m.Enabled = true; } }
/// <summary> /// Imprime por consola la generacion del KdTree /// </summary> private void printDebugKdTree(KdTreeNode rootNode) { Console.WriteLine("########## KdTree DEBUG ##########"); var 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 <TgcBoxDebug> createDebugKdTreeMeshes(KdTreeNode rootNode, TgcBoundingAxisAlignBox sceneBounds) { var pMax = sceneBounds.PMax; var pMin = sceneBounds.PMin; var debugBoxes = new List <TgcBoxDebug>(); doCreateKdTreeDebugBox(rootNode, debugBoxes, pMin.X, pMin.Y, pMin.Z, pMax.X, pMax.Y, pMax.Z, 0); return(debugBoxes); }
/// <summary> /// Imprime estadisticas del KdTree /// </summary> private void printEstadisticasKdTree(KdTreeNode rootNode) { Console.WriteLine("*********** KdTree Statics ***********"); var minModels = int.MaxValue; var 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 ************"); }
/// <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, TgcBoundingAxisAlignBox sceneBounds) { this.modelos = modelos; this.sceneBounds = sceneBounds; //Crear KdTree kdtreeRootNode = builder.crearKdTree(modelos, sceneBounds); //Deshabilitar todos los mesh inicialmente foreach (var 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) { var children = node.children; for (var i = 0; i < children.Length; i++) { var childNode = children[i]; if (childNode.children != null || childNode.models.Length > 0) { return(false); } } return(true); }
private void doCreateKdTreeDebugBox(KdTreeNode node, List <TgcBoxDebug> debugBoxes, float boxLowerX, float boxLowerY, float boxLowerZ, float boxUpperX, float boxUpperY, float boxUpperZ, int step) { var children = node.children; //Crear caja debug var box = createDebugBox(boxLowerX, boxLowerY, boxLowerZ, boxUpperX, boxUpperY, boxUpperZ, step); debugBoxes.Add(box); //es hoja, dibujar caja if (children == null) { } //recursividad sobre hijos else { step++; var xCut = node.xCut; var yCut = node.yCut; var 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, TgcBoundingAxisAlignBox sceneBounds) { var 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) { var children = node.children; //es hoja, cargar todos los meshes if (children == null) { selectLeafMeshes(node); //pedir hojas a hijos } else { for (var i = 0; i < children.Length; i++) { addAllLeafMeshes(children[i]); } } }
/// <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 var caja = new TgcBoundingAxisAlignBox( new TGCVector3(boxLowerX, boxLowerY, boxLowerZ), new TGCVector3(boxUpperX, boxUpperY, boxUpperZ)); var 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> /// 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) { var children = node.children; //es hoja, cargar todos los meshes if (children == null) { selectLeafMeshes(node); } //recursividad sobre hijos else { var xCut = node.xCut; var yCut = node.yCut; var 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); } }
private void obtenerEstadisticas(KdTreeNode node, ref int minModels, ref int maxModels) { if (node.isLeaf()) { var n = node.models.Length; if (n < minModels) { minModels = n; } if (n > maxModels) { maxModels = n; } } else { for (var i = 0; i < node.children.Length; i++) { obtenerEstadisticas(node.children[i], ref minModels, ref maxModels); } } }
/// <summary> /// Corte de plano Z /// </summary> private void doSectorKdTreeZ(KdTreeNode parent, TGCVector3 pMin, TGCVector3 pMax, int step, List <TgcMesh> meshes, int childIndex, float xCutValue, float yCutValue) { //Crear listas para realizar corte var possitiveList = new List <TgcMesh>(); var negativeList = new List <TgcMesh>(); //Z-cut float cutValue = 0; var zCutPlane = getCutPlane(meshes, new TGCVector3(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 var posNode = new KdTreeNode(); parent.children[childIndex] = posNode; //cargar nodo negativo en parent, segun childIndex var negNode = new KdTreeNode(); parent.children[childIndex + 1] = negNode; //cargar cortes en parent parent.xCut = xCutValue; parent.yCut = yCutValue; parent.zCut = cutValue; //nuevos limites var v1 = new TGCVector3(pMax.X - pMin.X, pMax.Y - pMin.Y, pMax.Z - cutValue); var v2 = new TGCVector3(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 TGCVector3(pMin.X, pMin.Y, cutValue), pMax, step, possitiveList); //recursividad de negativos con plano Y, usando resultados negativos doSectorKdTreeX(negNode, pMin, new TGCVector3(pMax.X, pMax.Y, cutValue), step, negativeList); } }