/// <summary> /// Renderizar en forma optimizado utilizando el Quadtree para hacer FrustumCulling /// </summary> public void render(TgcFrustum frustum, bool debugEnabled) { Vector3 pMax = sceneBounds.PMax; Vector3 pMin = sceneBounds.PMin; findVisibleMeshes(frustum, quadtreeRootNode, pMin.X, pMin.Y, pMin.Z, pMax.X, pMax.Y, pMax.Z); //Renderizar foreach (TgcMesh mesh in modelos) { if (mesh.Enabled) { mesh.render(); mesh.Enabled = false; } } if (debugEnabled) { foreach (TgcDebugBox debugBox in debugQuadtreeBoxes) { debugBox.render(); } } }
public AdaptativeHeightmap(float initialScaleXZ, float initialScaleY, float initialThreshold) { this.Enabled = true; this.AlphaBlendEnable = false; this.frustum = new TgcFrustum(); this.Threshold = initialThreshold; this.ScaleY = initialScaleY; this.ScaleXZ = initialScaleXZ; }
/// <summary> /// Indica si un Punto colisiona con el Frustum /// </summary> /// <param name="frustum">Frustum</param> /// <param name="p">Punto</param> /// <returns>True si el Punto está adentro del Frustum</returns> public static bool testPointFrustum(TgcFrustum frustum, Vector3 p) { bool result = true; Plane[] frustumPlanes = frustum.FrustumPlanes; for (int i = 0; i < 6; i++) { if (distPointPlane(p, frustumPlanes[i]) < 0) { return(false); } } return(result); }
/// <summary> /// Clasifica un BoundingBox respecto del Frustum /// </summary> /// <param name="frustum">Frustum</param> /// <param name="aabb">BoundingBox</param> /// <returns>Resultado de la clasificación</returns> public static FrustumResult classifyFrustumAABB(TgcFrustum frustum, TgcBoundingBox aabb) { int totalIn = 0; Plane[] frustumPlanes = frustum.FrustumPlanes; // get the corners of the box into the vCorner array Vector3[] aabbCorners = aabb.computeCorners(); // test all 8 corners against the 6 sides // if all points are behind 1 specific plane, we are out // if we are in with all points, then we are fully in for (int p = 0; p < 6; ++p) { int inCount = 8; int ptIn = 1; for (int i = 0; i < 8; ++i) { // test this point against the planes if (classifyPointPlane(aabbCorners[i], frustumPlanes[p]) == PointPlaneResult.BEHIND) { ptIn = 0; --inCount; } } // were all the points outside of plane p? if (inCount == 0) { return(FrustumResult.OUTSIDE); } // check if they were all on the right side of the plane totalIn += ptIn; } // so if iTotalIn is 6, then all are inside the view if (totalIn == 6) { return(FrustumResult.INSIDE); } // we must be partly in then otherwise return(FrustumResult.INTERSECT); }
/// <summary> /// Indica si un BoundingSphere colisiona con el Frustum /// </summary> /// <param name="frustum">Frustum</param> /// <param name="sphere">BoundingSphere</param> /// <returns>Resultado de la colisión</returns> public static FrustumResult classifyFrustumSphere(TgcFrustum frustum, TgcBoundingSphere sphere) { float distance; FrustumResult result = FrustumResult.INSIDE; Plane[] frustumPlanes = frustum.FrustumPlanes; for (int i = 0; i < 6; i++) { distance = distPointPlane(sphere.Center, frustumPlanes[i]); if (distance < -sphere.Radius) { return(FrustumResult.OUTSIDE); } else if (distance < sphere.Radius) { result = FrustumResult.INTERSECT; } } return(result); }
/// <summary> /// Recorrer recursivamente el Quadtree para encontrar los nodos visibles /// </summary> private void findVisibleMeshes(TgcFrustum frustum, QuadtreeNode node, float boxLowerX, float boxLowerY, float boxLowerZ, float boxUpperX, float boxUpperY, float boxUpperZ) { QuadtreeNode[] children = node.children; //es hoja, cargar todos los meshes if (children == null) { selectLeafMeshes(node); } //recursividad sobre hijos else { float midX = FastMath.Abs((boxUpperX - boxLowerX) / 2); float midZ = FastMath.Abs((boxUpperZ - boxLowerZ) / 2); //00 testChildVisibility(frustum, children[0], boxLowerX + midX, boxLowerY, boxLowerZ + midZ, boxUpperX, boxUpperY, boxUpperZ); //01 testChildVisibility(frustum, children[1], boxLowerX + midX, boxLowerY, boxLowerZ, boxUpperX, boxUpperY, boxUpperZ - midZ); //10 testChildVisibility(frustum, children[2], boxLowerX, boxLowerY, boxLowerZ + midZ, boxUpperX - midX, boxUpperY, boxUpperZ); //11 testChildVisibility(frustum, children[3], boxLowerX, boxLowerY, boxLowerZ, boxUpperX - midX, boxUpperY, boxUpperZ - midZ); } }
/// <summary> /// Indica si un BoundingSphere colisiona con el Frustum /// </summary> /// <param name="frustum">Frustum</param> /// <param name="sphere">BoundingSphere</param> /// <returns>Resultado de la colisión</returns> public static FrustumResult classifyFrustumSphere(TgcFrustum frustum, TgcBoundingSphere sphere) { float distance; FrustumResult result = FrustumResult.INSIDE; Plane[] frustumPlanes = frustum.FrustumPlanes; for (int i = 0; i < 6; i++) { distance = distPointPlane(sphere.Center, frustumPlanes[i]); if (distance < - sphere.Radius) { return FrustumResult.OUTSIDE; } else if (distance < sphere.Radius) { result = FrustumResult.INTERSECT; } } return result; }
/// <summary> /// Crea todos los modulos necesarios de la aplicacion /// </summary> internal void initGraphics(MainForm mainForm, Control panel3d) { this.mainForm = mainForm; this.panel3d = panel3d; this.fullScreenPanel = new FullScreenPanel(); panel3d.Focus(); //Iniciar graficos this.tgcD3dDevice = new TgcD3dDevice(panel3d); this.texturesManager = new TgcTexture.Manager(); this.tgcD3dDevice.OnResetDevice(tgcD3dDevice.D3dDevice, null); //Iniciar otras herramientas this.texturesPool = new TgcTexture.Pool(); this.logger = new Logger(mainForm.LogConsole); this.text3d = new TgcDrawText(tgcD3dDevice.D3dDevice); this.tgcD3dInput = new TgcD3dInput(mainForm, panel3d); this.fpsCamera = new TgcFpsCamera(); this.rotCamera = new TgcRotationalCamera(); this.thirdPersonCamera = new TgcThirdPersonCamera(); this.axisLines = new TgcAxisLines(tgcD3dDevice.D3dDevice); this.userVars = new TgcUserVars(mainForm.getDataGridUserVars()); this.modifiers = new TgcModifiers(mainForm.getModifiersPanel()); this.elapsedTime = -1; this.frustum = new TgcFrustum(); this.mp3Player = new TgcMp3Player(); this.directSound = new TgcDirectSound(); this.fog = new TgcFog(); this.currentCamera = this.rotCamera; this.customRenderEnabled = false; this.drawer2D = new TgcDrawer2D(); this.shaders = new TgcShaders(); //toogles this.rotCamera.Enable = true; this.fpsCamera.Enable = false; this.thirdPersonCamera.Enable = false; this.fpsCounterEnable = true; this.axisLines.Enable = true; //Cargar algoritmos exampleLoader = new ExampleLoader(); examplesDir = System.Environment.CurrentDirectory + "\\" + ExampleLoader.EXAMPLES_DIR + "\\"; examplesMediaDir = examplesDir + "Media" + "\\"; alumnoEjemplosDir = System.Environment.CurrentDirectory + "\\" + "AlumnoEjemplos" + "\\"; alumnoEjemplosMediaDir = alumnoEjemplosDir + "AlumnoMedia" + "\\"; exampleLoader.loadExamplesInGui(mainForm.TreeViewExamples, new string[] { examplesDir, alumnoEjemplosDir }); //Cargar shaders del framework this.shaders.loadCommonShaders(); //Cargar ejemplo default TgcExample defaultExample = exampleLoader.getExampleByName(mainForm.Config.defaultExampleName, mainForm.Config.defaultExampleCategory); executeExample(defaultExample); }
/// <summary> /// Clasifica un BoundingBox respecto del Frustum /// </summary> /// <param name="frustum">Frustum</param> /// <param name="aabb">BoundingBox</param> /// <returns>Resultado de la clasificación</returns> public static FrustumResult classifyFrustumAABB(TgcFrustum frustum, TgcBoundingBox aabb) { int totalIn = 0; Plane[] frustumPlanes = frustum.FrustumPlanes; // get the corners of the box into the vCorner array Vector3[] aabbCorners = aabb.computeCorners(); // test all 8 corners against the 6 sides // if all points are behind 1 specific plane, we are out // if we are in with all points, then we are fully in for(int p = 0; p < 6; ++p) { int inCount = 8; int ptIn = 1; for(int i = 0; i < 8; ++i) { // test this point against the planes if (classifyPointPlane(aabbCorners[i], frustumPlanes[p]) == PointPlaneResult.BEHIND) { ptIn = 0; --inCount; } } // were all the points outside of plane p? if (inCount == 0) { return FrustumResult.OUTSIDE; } // check if they were all on the right side of the plane totalIn += ptIn; } // so if iTotalIn is 6, then all are inside the view if (totalIn == 6) { return FrustumResult.INSIDE; } // we must be partly in then otherwise return FrustumResult.INTERSECT; }
public bool shouldSplit(ref Matrix wvp, ref TgcFrustum bf) { //[!]Cambiar para que los cambios no sean tan bruscos /* * Determina si un triángulo debe dividirse o no, midiendo la distancia *(screen distance) entre su left point y su center point. Cerca del frustum, esta *distancia es más grande. Si supera un threshold se considera suficiente para dividirlo. */ bool shouldSplit = false; if (TgcCollisionUtils.testPointFrustum(bf, centerPos) //si colisiona con center, left, o right... || TgcCollisionUtils.testPointFrustum(bf, lPos) || TgcCollisionUtils.testPointFrustum(bf, rPos)) { Vector4 lScreenPos = Vector4.Transform(new Vector4(lPos.X, lPos.Y, lPos.Z, 1), wvp); Vector4 aScreenPos = Vector4.Transform(new Vector4(centerPos.X, centerPos.Y, centerPos.Z, 1), wvp); lScreenPos = lScreenPos * (1 / lScreenPos.W); aScreenPos = aScreenPos * (1 / aScreenPos.W); Vector4 difference = lScreenPos - aScreenPos; Vector2 screenDifference = new Vector2(difference.X, difference.Y); //(menos tolerancia => más calidad) if (screenDifference.Length() > map.Threshold) shouldSplit = true; } return shouldSplit; }
public bool collidesWithFrustum(TgcFrustum frustum) { TgcCollisionUtils.FrustumResult result = TgcCollisionUtils.classifyFrustumAABB(frustum, this.tp.BoundingBox); if (result != TgcCollisionUtils.FrustumResult.OUTSIDE) return true; return false; }
public void createSplitList(ref List<Triangle> splitList, ref List<Triangle> remainderList, ref Matrix wvp, ref TgcFrustum bf) { this.addedToMergeList = false; //este flag se resetea acá ya que se llama por cada render bool hasSplit = false; if ((lChild != null) && (!splitted)) { //si todavía no se dividió if (shouldSplit(ref wvp, ref bf)) { //y se puede propageSplit(ref splitList); //dale!!! hasSplit = true; } } if (!hasSplit) remainderList.Add(this); //sino va a remainderList }
public void processMergeList(ref List<Triangle> toDrawList, ref Matrix wvp, ref TgcFrustum bf) { //Los triángulos que se unen simplemente avisan que no están divididos y se agregan a la lista this.splitted = false; toDrawList.Add(this); }
public void createMergeList(ref List<Triangle> mergeList, ref List<Triangle> leftoverList, ref Matrix wvp, ref TgcFrustum bf) { //Crea la mergeList, los restantes van a leftoverList bool cannotMerge = true; if (parent != null) cannotMerge = !parent.checkMerge(ref mergeList, ref wvp, ref bf); if (cannotMerge) leftoverList.Add(this); }
public bool checkMerge(ref List<Triangle> mergeList, ref Matrix wvp, ref TgcFrustum bf) { //Determina si debe unirse o no, y en caso de poderse se agrega a la mergeList bool shouldMerge = false; if (!addedToMergeList) { if (canMerge()) { if (!shouldSplit(ref wvp, ref bf)) { shouldMerge = true; if (bNeigh != null) if (bNeigh.shouldSplit(ref wvp, ref bf)) //él y su vecino deben ponerse de acuerdo para mergear shouldMerge = false; } } } if (shouldMerge) { addedToMergeList = true; mergeList.Add(this); if (bNeigh != null) { bNeigh.addedToMergeList = true; mergeList.Add(bNeigh); } } return addedToMergeList; }
/// <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, QuadtreeNode 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> /// Indica si un Punto colisiona con el Frustum /// </summary> /// <param name="frustum">Frustum</param> /// <param name="p">Punto</param> /// <returns>True si el Punto está adentro del Frustum</returns> public static bool testPointFrustum(TgcFrustum frustum, Vector3 p) { bool result = true; Plane[] frustumPlanes = frustum.FrustumPlanes; for(int i=0; i < 6; i++) { if (distPointPlane(p, frustumPlanes[i]) < 0) { return false; } } return result; }
/// <summary> /// Renderizar en forma optimizado utilizando el Quadtree para hacer FrustumCulling /// </summary> public void render(TgcFrustum frustum, bool debugEnabled, string name) { Vector3 pMax = sceneBounds.PMax; Vector3 pMin = sceneBounds.PMin; findVisibleMeshes(frustum, quadtreeRootNode, pMin.X, pMin.Y, pMin.Z, pMax.X, pMax.Y, pMax.Z); //Renderizar foreach (TgcMesh mesh in modelos) { if (mesh.Enabled && mesh.Name.Contains(name)) { mesh.render(); mesh.Enabled = false; GameManager.Instance.vegetacionVisible++; if (debugEnabled) { mesh.BoundingBox.render(); } } } if (debugEnabled) { foreach (TgcDebugBox debugBox in debugQuadtreeBoxes) { debugBox.render(); } } }
/// <summary> /// Activar modelos dentro de celdas visibles /// </summary> private void findVisibleMeshes(TgcFrustum frustum) { for (int x = 0; x < grid.GetUpperBound(0); x++) { for (int y = 0; y < grid.GetUpperBound(1); y++) { for (int z = 0; z < grid.GetUpperBound(2); z++) { GrillaRegularNode node = grid[x, y, z]; TgcCollisionUtils.FrustumResult r = TgcCollisionUtils.classifyFrustumAABB(frustum, node.BoundingBox); if (r != TgcCollisionUtils.FrustumResult.OUTSIDE) { node.activateCellMeshes(); } } } } }