/// <summary>
 /// Registers a new processed object that is build into the navigation mesh
 /// </summary>
 /// <param name="entity">The entity that was processed</param>
 /// <param name="data">The collider vertex data that is generated for this entity</param>
 public void Add(Entity entity, NavigationMeshInputBuilder data)
 {
     StaticColliderComponent collider = entity.Get<StaticColliderComponent>();
     if (collider != null)
     {
         int hash = NavigationMeshBuildUtils.HashEntityCollider(collider);
         Objects.Add(entity.Id, new NavigationMeshCachedBuildObject()
         {
             Guid = entity.Id,
             ParameterHash = hash,
             Data = data
         });
     }
 }
        /// <summary>
        /// Registers a new processed object that is build into the navigation mesh
        /// </summary>
        /// <param name="entity">The entity that was processed</param>
        /// <param name="data">The collider vertex data that is generated for this entity</param>
        public void Add(Entity entity, NavigationMeshInputBuilder data)
        {
            StaticColliderComponent collider = entity.Get <StaticColliderComponent>();

            if (collider != null)
            {
                int hash = NavigationMeshBuildUtils.HashEntityCollider(collider);
                Objects.Add(entity.Id, new NavigationMeshCachedBuildObject()
                {
                    Guid          = entity.Id,
                    ParameterHash = hash,
                    Data          = data
                });
            }
        }
        /// <summary>
        /// Appends another vertex data builder
        /// </summary>
        /// <param name="other"></param>
        public void AppendOther(NavigationMeshInputBuilder other)
        {
            // Copy vertices
            int vbase = Points.Count;
            for (int i = 0; i < other.Points.Count; i++)
            {
                Vector3 point = other.Points[i];
                Points.Add(point);
                BoundingBox.Merge(ref BoundingBox, ref point, out BoundingBox);
            }

            // Copy indices with offset applied
            foreach (int index in other.Indices)
                Indices.Add(index + vbase);
        }
Example #4
0
        /// <summary>
        /// Appends another vertex data builder
        /// </summary>
        /// <param name="other"></param>
        public void AppendOther(NavigationMeshInputBuilder other)
        {
            // Copy vertices
            int vbase = Points.Count;

            for (int i = 0; i < other.Points.Count; i++)
            {
                Vector3 point = other.Points[i];
                Points.Add(point);
                BoundingBox.Merge(ref BoundingBox, ref point, out BoundingBox);
            }

            // Copy indices with offset applied
            foreach (int index in other.Indices)
            {
                Indices.Add(index + vbase);
            }
        }
            private void CollectInputGeometry()
            {
                // Reset state
                fullRebuild = false;
                updatedAreas.Clear();
                fullRebuild = oldBuild == null;

                sceneNavigationMeshInputBuilder = new NavigationMeshInputBuilder();

                foreach (var entity in sceneEntities)
                {
                    StaticColliderComponent collider = entity.Get <StaticColliderComponent>();

                    bool colliderEnabled = collider != null && ((CollisionFilterGroupFlags)collider.CollisionGroup & asset.IncludedCollisionGroups) != 0 && collider.Enabled;
                    if (colliderEnabled)
                    {
                        // Compose collider shape, mark as disabled if it failed so the respective area gets updated
                        collider.ComposeShape();
                        if (collider.ColliderShape == null)
                        {
                            colliderEnabled = false;
                        }
                    }

                    if (!colliderEnabled) // Removed or disabled
                    {
                        // Check for old object
                        NavigationMeshCachedBuildObject oldObject = null;
                        if (oldBuild?.Objects.TryGetValue(entity.Id, out oldObject) ?? false)
                        {
                            // This object has been disabled, update the area because it was removed and continue to the next object
                            updatedAreas.Add(oldObject.Data.BoundingBox);
                        }
                    }
                    else if (oldBuild?.IsUpdatedOrNew(entity) ?? true) // Is the entity updated?
                    {
                        TransformComponent entityTransform   = entity.Transform;
                        Matrix             entityWorldMatrix = entityTransform.WorldMatrix;

                        NavigationMeshInputBuilder entityNavigationMeshInputBuilder = new NavigationMeshInputBuilder();

                        bool isDeferred = false;

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

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

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

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

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

                                var meshData = GeometricPrimitive.Cone.New(coneDesc.Radius, coneDesc.Height);
                                entityNavigationMeshInputBuilder.AppendMeshData(meshData, transform);
                            }
                            else if (shapeType == typeof(StaticPlaneColliderShape))
                            {
                                var    plane     = (StaticPlaneColliderShape)shape;
                                var    planeDesc = (StaticPlaneColliderShapeDesc)plane.Description;
                                Matrix transform = plane.PositiveCenterMatrix * entityWorldMatrix;

                                // Defer infinite planes because their size is not defined yet
                                deferredShapes.Add(new DeferredShape
                                {
                                    Description = planeDesc,
                                    Transform   = transform,
                                    Entity      = entity,
                                    NavigationMeshInputBuilder = entityNavigationMeshInputBuilder
                                });
                                isDeferred = true;
                            }
                            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 + 1] = (int)hull.Indices[i + 1];
                                    indices[i + 2] = (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]);
                                }
                            }
                        }

                        if (!isDeferred)
                        {
                            // Store current entity in build cache
                            currentBuild.Add(entity, entityNavigationMeshInputBuilder);

                            // Add (?old) and new bounding box to modified areas
                            sceneNavigationMeshInputBuilder.AppendOther(entityNavigationMeshInputBuilder);
                            NavigationMeshCachedBuildObject oldObject = null;
                            if (oldBuild?.Objects.TryGetValue(entity.Id, out oldObject) ?? false)
                            {
                                updatedAreas.Add(oldObject.Data.BoundingBox);
                            }
                            updatedAreas.Add(entityNavigationMeshInputBuilder.BoundingBox);
                        }
                    }
                    else // Not updated
                    {
                        // Copy old data into vertex buffer
                        NavigationMeshCachedBuildObject oldObject = oldBuild.Objects[entity.Id];
                        sceneNavigationMeshInputBuilder.AppendOther(oldObject.Data);
                        currentBuild.Add(entity, oldObject.Data);
                    }
                }

                // Unload loaded collider shapes
                foreach (var pair in loadedColliderShapes)
                {
                    contentManager.Unload(pair.Key);
                }

                // Store calculated bounding box
                if (generateBoundingBox)
                {
                    globalBoundingBox = sceneNavigationMeshInputBuilder.BoundingBox;
                }

                // Process deferred shapes
                Vector3 maxSize     = globalBoundingBox.Maximum - globalBoundingBox.Minimum;
                float   maxDiagonal = Math.Max(maxSize.X, Math.Max(maxSize.Y, maxSize.Z));

                foreach (DeferredShape shape in deferredShapes)
                {
                    StaticPlaneColliderShapeDesc planeDesc = (StaticPlaneColliderShapeDesc)shape.Description;
                    Plane plane = new Plane(planeDesc.Normal, planeDesc.Offset);

                    // Pre-Transform plane parameters
                    plane.Normal = Vector3.TransformNormal(plane.Normal, shape.Transform);
                    float offset = Vector3.Dot(shape.Transform.TranslationVector, plane.Normal);
                    plane.D += offset;

                    // 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(globalBoundingBox.Center, tangent) * tangent;
                    planeOffset += Vector3.Dot(globalBoundingBox.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, false);
                    shape.NavigationMeshInputBuilder.AppendMeshData(meshData, Matrix.Identity);
                    sceneNavigationMeshInputBuilder.AppendMeshData(meshData, Matrix.Identity);
                    // Update bounding box after plane generation
                    if (generateBoundingBox)
                    {
                        globalBoundingBox = sceneNavigationMeshInputBuilder.BoundingBox;
                    }

                    // Store deferred shape in build cahce just like normal onesdddd
                    currentBuild.Add(shape.Entity, shape.NavigationMeshInputBuilder);

                    // NOTE: Force a full rebuild when moving unbound shapes such as ininite planes
                    // the alternative is to intersect the old and new plane with the tiles to see which ones changed
                    // although in most cases it will be a horizontal plane and all tiles will be affected anyways
                    fullRebuild = true;
                }
            }