Exemplo n.º 1
0
        /// <summary>
        /// Generates triangles for an infinite plane that will completely intersect the given bounding box
        /// </summary>
        private NavigationMeshInputBuilder BuildPlaneGeometry(Plane plane, BoundingBox boundingBox)
        {
            Vector3 maxSize     = boundingBox.Maximum - boundingBox.Minimum;
            float   maxDiagonal = Math.Max(maxSize.X, Math.Max(maxSize.Y, maxSize.Z));

            // Generate source plane triangles
            Vector3[] planePoints;
            int[]     planeInds;
            NavigationMeshBuildUtils.BuildPlanePoints(ref plane, maxDiagonal, out planePoints, out planeInds);

            Vector3 tangent, bitangent;

            NavigationMeshBuildUtils.GenerateTangentBinormal(plane.Normal, out tangent, out bitangent);
            // Calculate plane offset so that the plane always covers the whole range of the bounding box
            Vector3 planeOffset = Vector3.Dot(boundingBox.Center, tangent) * tangent;

            planeOffset += Vector3.Dot(boundingBox.Center, bitangent) * bitangent;

            VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[planePoints.Length];
            for (int i = 0; i < planePoints.Length; i++)
            {
                vertices[i] = new VertexPositionNormalTexture(planePoints[i] + planeOffset, Vector3.UnitY, Vector2.Zero);
            }

            GeometricMeshData <VertexPositionNormalTexture> meshData = new GeometricMeshData <VertexPositionNormalTexture>(vertices, planeInds, true);

            NavigationMeshInputBuilder inputBuilder = new NavigationMeshInputBuilder();

            inputBuilder.AppendMeshData(meshData, Matrix.Identity);
            return(inputBuilder);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Rebuilds outdated triangle data for colliders and recalculates hashes storing everything in StaticColliderData
        /// </summary>
        private void BuildInput(StaticColliderData[] collidersLocal, CollisionFilterGroupFlags includedCollisionGroups)
        {
            NavigationMeshCache lastCache = oldNavigationMesh?.Cache;

            bool clearCache = false;

            Dispatcher.ForEach(collidersLocal, colliderData =>
            {
                var entity = colliderData.Component.Entity;
                TransformComponent entityTransform = entity.Transform;
                Matrix entityWorldMatrix           = entityTransform.WorldMatrix;

                NavigationMeshInputBuilder entityNavigationMeshInputBuilder = colliderData.InputBuilder = new NavigationMeshInputBuilder();

                // Compute hash of collider and compare it with the previous build if there is one
                colliderData.ParameterHash = NavigationMeshBuildUtils.HashEntityCollider(colliderData.Component, includedCollisionGroups);
                colliderData.Previous      = null;
                if (lastCache?.Objects.TryGetValue(colliderData.Component.Id, out colliderData.Previous) ?? false)
                {
                    if (colliderData.Previous.ParameterHash == colliderData.ParameterHash)
                    {
                        // In this case, we don't need to recalculate the geometry for this shape, since it wasn't changed
                        // here we take the triangle mesh from the previous build as the current
                        colliderData.InputBuilder = colliderData.Previous.InputBuilder;
                        colliderData.Planes.Clear();
                        colliderData.Planes.AddRange(colliderData.Previous.Planes);
                        colliderData.Processed = false;
                        return;
                    }
                }

                // Clear cache on removal of infinite planes
                if (colliderData.Planes.Count > 0)
                {
                    clearCache = true;
                }

                // Clear planes
                colliderData.Planes.Clear();

                // Return empty data for disabled colliders, filtered out colliders or trigger colliders
                if (!colliderData.Component.Enabled || colliderData.Component.IsTrigger ||
                    !NavigationMeshBuildUtils.CheckColliderFilter(colliderData.Component, includedCollisionGroups))
                {
                    colliderData.Processed = true;
                    return;
                }

                // Make sure shape is up to date
                colliderData.Component.ComposeShape();

                // Interate through all the colliders shapes while queueing all shapes in compound shapes to process those as well
                Queue <ColliderShape> shapesToProcess = new Queue <ColliderShape>();
                if (colliderData.Component.ColliderShape != null)
                {
                    shapesToProcess.Enqueue(colliderData.Component.ColliderShape);
                    while (shapesToProcess.Count > 0)
                    {
                        var shape     = shapesToProcess.Dequeue();
                        var shapeType = shape.GetType();
                        if (shapeType == typeof(BoxColliderShape))
                        {
                            var box          = (BoxColliderShape)shape;
                            var boxDesc      = GetColliderShapeDesc <BoxColliderShapeDesc>(box.Description);
                            Matrix transform = box.PositiveCenterMatrix * entityWorldMatrix;

                            var meshData = GeometricPrimitive.Cube.New(boxDesc.Size, toLeftHanded: true);
                            entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform);
                        }
                        else if (shapeType == typeof(SphereColliderShape))
                        {
                            var sphere       = (SphereColliderShape)shape;
                            var sphereDesc   = GetColliderShapeDesc <SphereColliderShapeDesc>(sphere.Description);
                            Matrix transform = sphere.PositiveCenterMatrix * entityWorldMatrix;

                            var meshData = GeometricPrimitive.Sphere.New(sphereDesc.Radius, toLeftHanded: true);
                            entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform);
                        }
                        else if (shapeType == typeof(CylinderColliderShape))
                        {
                            var cylinder     = (CylinderColliderShape)shape;
                            var cylinderDesc = GetColliderShapeDesc <CylinderColliderShapeDesc>(cylinder.Description);
                            Matrix transform = cylinder.PositiveCenterMatrix * entityWorldMatrix;

                            var meshData = GeometricPrimitive.Cylinder.New(cylinderDesc.Height, cylinderDesc.Radius, toLeftHanded: true);
                            entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform);
                        }
                        else if (shapeType == typeof(CapsuleColliderShape))
                        {
                            var capsule      = (CapsuleColliderShape)shape;
                            var capsuleDesc  = GetColliderShapeDesc <CapsuleColliderShapeDesc>(capsule.Description);
                            Matrix transform = capsule.PositiveCenterMatrix * entityWorldMatrix;

                            var meshData = GeometricPrimitive.Capsule.New(capsuleDesc.Length, capsuleDesc.Radius, toLeftHanded: true);
                            entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform);
                        }
                        else if (shapeType == typeof(ConeColliderShape))
                        {
                            var cone         = (ConeColliderShape)shape;
                            var coneDesc     = GetColliderShapeDesc <ConeColliderShapeDesc>(cone.Description);
                            Matrix transform = cone.PositiveCenterMatrix * entityWorldMatrix;

                            var meshData = GeometricPrimitive.Cone.New(coneDesc.Radius, coneDesc.Height, toLeftHanded: true);
                            entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform);
                        }
                        else if (shapeType == typeof(StaticPlaneColliderShape))
                        {
                            var planeShape   = (StaticPlaneColliderShape)shape;
                            var planeDesc    = GetColliderShapeDesc <StaticPlaneColliderShapeDesc>(planeShape.Description);
                            Matrix transform = entityWorldMatrix;

                            Plane plane = new Plane(planeDesc.Normal, planeDesc.Offset);

                            // Pre-Transform plane parameters
                            plane.Normal = Vector3.TransformNormal(planeDesc.Normal, transform);
                            plane.Normal.Normalize();
                            plane.D += Vector3.Dot(transform.TranslationVector, plane.Normal);

                            colliderData.Planes.Add(plane);
                        }
                        else if (shapeType == typeof(ConvexHullColliderShape))
                        {
                            var hull         = (ConvexHullColliderShape)shape;
                            Matrix transform = hull.PositiveCenterMatrix * entityWorldMatrix;

                            // Convert hull indices to int
                            int[] indices = new int[hull.Indices.Count];
                            if (hull.Indices.Count % 3 != 0)
                            {
                                throw new InvalidOperationException("Physics hull does not consist of triangles");
                            }
                            for (int i = 0; i < hull.Indices.Count; i += 3)
                            {
                                indices[i]     = (int)hull.Indices[i];
                                indices[i + 2] = (int)hull.Indices[i + 1]; // NOTE: Reversed winding to create left handed input
                                indices[i + 1] = (int)hull.Indices[i + 2];
                            }

                            entityNavigationMeshInputBuilder.AppendArrays(hull.Points.ToArray(), indices, transform);
                        }
                        else if (shapeType == typeof(CompoundColliderShape))
                        {
                            // Unroll compound collider shapes
                            var compound = (CompoundColliderShape)shape;
                            for (int i = 0; i < compound.Count; i++)
                            {
                                shapesToProcess.Enqueue(compound[i]);
                            }
                        }
                    }
                }

                // Clear cache on addition of infinite planes
                if (colliderData.Planes.Count > 0)
                {
                    clearCache = true;
                }

                // Mark collider as processed
                colliderData.Processed = true;
            });

            if (clearCache && oldNavigationMesh != null)
            {
                oldNavigationMesh = null;
            }
        }