Exemplo n.º 1
0
        /// <summary>
        /// Renderizar escenario BSP utilizando matriz PVS para descartar clusters no visibles
        /// </summary>
        /// <param name="camPos">Posición actual de la camara</param>
        public void render(Vector3 camPos)
        {
            Device device = GuiController.Instance.D3dDevice;

            float elapsedTime = GuiController.Instance.ElapsedTime;

            time += elapsedTime;

            //Obtener leaf actual
            int actualLeaf = FindLeaf(camPos);

            //Obtener clusters visibles segun PVS
            int actualCluster = data.leafs[actualLeaf].cluster;

            if (actualCluster != antCluster)
            {
                antCluster = actualCluster;
                clusterVis.Clear();
                for (int i = 0; i < data.leafs.Length; i++)
                {
                    if (isClusterVisible(actualCluster, data.leafs[i].cluster))
                    {
                        clusterVis.Add(i);
                    }
                }
            }

            //Actualizar volumen del Frustum con nuevos valores de camara
            TgcFrustum frustum = GuiController.Instance.Frustum;

            frustum.updateVolume(device.Transform.View, device.Transform.Projection);

            foreach (int nleaf in clusterVis)
            {
                //Frustum Culling con el AABB del cluster
                TgcCollisionUtils.FrustumResult result = TgcCollisionUtils.classifyFrustumAABB(frustum, data.leafs[nleaf].boundingBox);
                if (result == TgcCollisionUtils.FrustumResult.OUTSIDE)
                {
                    continue;
                }

                //Habilitar meshes de este leaf
                QLeaf currentLeaf = data.leafs[nleaf];
                for (int i = currentLeaf.firstLeafSurface; i < currentLeaf.firstLeafSurface + currentLeaf.numLeafSurfaces; i++)
                {
                    int     iMesh = data.leafSurfaces[i];
                    TgcMesh mesh  = meshes[iMesh];
                    if (mesh != null)
                    {
                        meshes[iMesh].Enabled = true;
                    }
                }
            }

            //Renderizar meshes visibles
            mViewProj = device.Transform.View * device.Transform.Projection;
            for (int i = 0; i < meshes.Count; i++)
            {
                //Ignonar si no está habilitada
                TgcMesh mesh = meshes[i];
                if (mesh == null || !mesh.Enabled)
                {
                    continue;
                }


                QShaderData shader = data.shaderXSurface[i];

                //Renderizado con shaders
                //TODO: Mejorar el renderizado de shaders. Por ahora esta deshabilitado
                if (shader != null && shader.Stages.Count > 0)
                {
                    renderShaderMesh(mesh, shader);
                }
                //Renderizado normal
                else
                {
                    //render
                    mesh.render();

                    //deshabilitar para la proxima vuelta
                    mesh.Enabled = false;
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Traverses the BSP to find the brushes closest to our position
        /// </summary>
        private void CheckNode(int nodeIndex, float startRatio, float endRatio, Vector3 vStart, Vector3 vEnd)
        {
            // Remember, the nodeIndices are stored as negative numbers when we get to a leaf, so we
            // check if the current node is a leaf, which holds brushes.  If the nodeIndex is negative,
            // the next index is a leaf (note the: nodeIndex + 1)
            if (nodeIndex < 0)
            {
                // If this node in the BSP is a leaf, we need to negate and add 1 to offset
                // the real node index into the m_pLeafs[] array.  You could also do [~nodeIndex].
                QLeaf pLeaf = bspMap.Data.leafs[~nodeIndex];

                // We have a leaf, so let's go through all of the brushes for that leaf
                for (int i = 0; i < pLeaf.numLeafBrushes; i++)
                {
                    // Get the current brush that we going to check
                    QBrush pBrush = bspMap.Data.brushes[bspMap.Data.leafbrushes[pLeaf.firstLeafBrush + i]];

                    // This is kind of an important line.  First, we check if there is actually
                    // and brush sides (which store indices to the normal and plane data for the brush).
                    // If not, then go to the next one.  Otherwise, we also check to see if the brush
                    // is something that we want to collide with.  For instance, there are brushes for
                    // water, lava, bushes, misc. sprites, etc...  We don't want to collide with water
                    // and other things like bushes, so we check the texture type to see if it's a solid.
                    // If the textureType can be binary-anded (&) and still be 1, then it's solid,
                    // otherwise it's something that can be walked through.  That's how Quake chose to
                    // do it.

                    // Check if we have brush sides and the current brush is solid and collidable
                    if ((pBrush.numSides > 0) && (bspMap.Data.shaders[pBrush.shaderNum].contentFlags & 1) > 0)
                    {
                        // Now we delve into the dark depths of the real calculations for collision.
                        // We can now check the movement vector against our brush planes.
                        CheckBrush(pBrush, vStart, vEnd);
                    }
                }

                // Since we found the brushes, we can go back up and stop recursing at this level
                return;
            }

            // If we haven't found a leaf in the node, then we need to keep doing some dirty work
            // until we find the leafs which store the brush information for collision detection.

            // Grad the next node to work with and grab this node's plane data
            QNode  pNode  = bspMap.Data.nodes[nodeIndex];
            QPlane pPlane = bspMap.Data.planes[pNode.planeNum];

            // Now we do some quick tests to see which side we fall on of the node in the BSP

            // Here we use the plane equation to find out where our initial start position is
            // according the the node that we are checking.  We then grab the same info for the end pos.
            float startDistance = Vector3.Dot(vStart, pPlane.normal) - pPlane.dist;
            float endDistance   = Vector3.Dot(vEnd, pPlane.normal) - pPlane.dist;
            float offset        = 0.0f;

            // If we are doing any type of collision detection besides a ray, we need to change
            // the offset for which we are testing collision against the brushes.  If we are testing
            // a sphere against the brushes, we need to add the sphere's offset when we do the plane
            // equation for testing our movement vector (start and end position).  * More Info * For
            // more info on sphere collision, check out our tutorials on this subject.

            // If we are doing sphere collision, include an offset for our collision tests below
            if (traceType == TYPE_SPHERE)
            {
                offset = traceRadius;
            }
            else if (traceType == TYPE_BOX)
            {
                // This equation does a dot product to see how far our
                // AABB is away from the current plane we are checking.
                // Since this is a distance, we need to make it an absolute
                // value, which calls for the fabs() function (abs() for floats).

                // Get the distance our AABB is from the current splitter plane
                offset = Math.Abs(vExtents.X * pPlane.normal.X) +
                         Math.Abs(vExtents.Y * pPlane.normal.Y) +
                         Math.Abs(vExtents.Z * pPlane.normal.Z);
            }

            // Below we just do a basic traversal down the BSP tree.  If the points are in
            // front of the current splitter plane, then only check the nodes in front of
            // that splitter plane.  Otherwise, if both are behind, check the nodes that are
            // behind the current splitter plane.  The next case is that the movement vector
            // is on both sides of the splitter plane, which makes it a bit more tricky because we now
            // need to check both the front and the back and split up the movement vector for both sides.

            // Here we check to see if the start and end point are both in front of the current node.
            // If so, we want to check all of the nodes in front of this current splitter plane.
            if (startDistance >= offset && endDistance >= offset)
            {
                // Traverse the BSP tree on all the nodes in front of this current splitter plane
                CheckNode(pNode.children[0], startDistance, endDistance, vStart, vEnd);
            }
            // If both points are behind the current splitter plane, traverse down the back nodes
            else if (startDistance < -offset && endDistance < -offset)
            {
                // Traverse the BSP tree on all the nodes in back of this current splitter plane
                CheckNode(pNode.children[1], startDistance, endDistance, vStart, vEnd);
            }
            else
            {
                // If we get here, then our ray needs to be split in half to check the nodes
                // on both sides of the current splitter plane.  Thus we create 2 ratios.
                float   Ratio1 = 1.0f, Ratio2 = 0.0f, middleRatio = 0.0f;
                Vector3 vMiddle;        // This stores the middle point for our split ray

                // Start of the side as the front side to check
                int side = pNode.children[0];

                // Here we check to see if the start point is in back of the plane (negative)
                if (startDistance < endDistance)
                {
                    // Since the start position is in back, let's check the back nodes
                    side = pNode.children[1];

                    // Here we create 2 ratios that hold a distance from the start to the
                    // extent closest to the start (take into account a sphere and epsilon).
                    // We use epsilon like Quake does to compensate for float errors.  The second
                    // ratio holds a distance from the other size of the extents on the other side
                    // of the plane.  This essential splits the ray for both sides of the splitter plane.
                    float inverseDistance = 1.0f / (startDistance - endDistance);
                    Ratio1 = (startDistance - offset - kEpsilon) * inverseDistance;
                    Ratio2 = (startDistance + offset + kEpsilon) * inverseDistance;
                }
                // Check if the starting point is greater than the end point (positive)
                else if (startDistance > endDistance)
                {
                    // This means that we are going to recurse down the front nodes first.
                    // We do the same thing as above and get 2 ratios for split ray.
                    // Ratio 1 and 2 are switched in contrast to the last if statement.
                    // This is because the start is starting in the front of the splitter plane.
                    float inverseDistance = 1.0f / (startDistance - endDistance);
                    Ratio1 = (startDistance + offset + kEpsilon) * inverseDistance;
                    Ratio2 = (startDistance - offset - kEpsilon) * inverseDistance;
                }

                // Make sure that we have valid numbers and not some weird float problems.
                // This ensures that we have a value from 0 to 1 as a good ratio should be :)
                if (Ratio1 < 0.0f)
                {
                    Ratio1 = 0.0f;
                }
                else if (Ratio1 > 1.0f)
                {
                    Ratio1 = 1.0f;
                }

                if (Ratio2 < 0.0f)
                {
                    Ratio2 = 0.0f;
                }
                else if (Ratio2 > 1.0f)
                {
                    Ratio2 = 1.0f;
                }

                // Just like we do in the Trace() function, we find the desired middle
                // point on the ray, but instead of a point we get a middleRatio percentage.
                // This isn't the true middle point since we are using offset's and the epsilon value.
                // We also grab the middle point to go with the ratio.
                middleRatio = startRatio + ((endRatio - startRatio) * Ratio1);
                vMiddle     = vStart + ((vEnd - vStart) * Ratio1);

                // Now we recurse on the current side with only the first half of the ray
                CheckNode(side, startRatio, middleRatio, vStart, vMiddle);

                // Now we need to make a middle point and ratio for the other side of the node
                middleRatio = startRatio + ((endRatio - startRatio) * Ratio2);
                vMiddle     = vStart + ((vEnd - vStart) * Ratio2);

                // Depending on which side should go last, traverse the bsp with the
                // other side of the split ray (movement vector).
                if (side == pNode.children[1])
                {
                    CheckNode(pNode.children[0], middleRatio, endRatio, vMiddle, vEnd);
                }
                else
                {
                    CheckNode(pNode.children[1], middleRatio, endRatio, vMiddle, vEnd);
                }
            }
        }