Exemplo n.º 1
0
        private static List <ScanResultLOD> GetCenterPointList(
            Transform transform,
            GameObjectShape shape)
        {
            // Single ScanResultLOD for lowest LOD.
            var result = ScanResultLOD.CreateList(1);

            Matrix4x4 matrix  = transform.worldToLocalMatrix;
            bool      flatten = shape.Flatten;

            foreach (Volume vol in s_Volumes)
            {
                Vector3 localPoint = matrix.MultiplyPoint3x4(vol.Bounds.center);
                if (flatten)
                {
                    localPoint.y = 0;
                }
                result[0].LocalPoints.Add(localPoint);
            }

            return(result);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Performs a scan over a set of colliders and returns
        /// lists of enclosed points for different levels of detail.
        /// </summary>
        /// <param name="scene"><see cref="PhysicsScene"/> for raycasts and overlap checks</param>
        /// <param name="transform"><see cref="DetectableGameObject"/> transform</param>
        /// <param name="colliders">List of colliders</param>
        /// <param name="shape"><see cref="GameObjectShape"/></param>
        /// <param name="result">List of <see cref="ScanResultLOD"/> instances (output)</param>
        /// <returns>The highest LOD for the given scan settings and object size</returns>
        public static int Scan(
            PhysicsScene scene,
            Transform transform,
            IList <Collider> colliders,
            GameObjectShape shape,
            out List <ScanResultLOD> result)
        {
            s_GlobalMaxLOD = 0;

            CheckPhysicsBufferSize(colliders);
            CreateVolumes(colliders, shape);

            result = shape.ScanLOD > 0
                ? GetLODPointLists(scene, transform, shape)
                : GetCenterPointList(transform, shape);

            // GlobalMaxLOD corresponds to the result list's
            // length unless the 'flatten' option is enabled:
            // GlobalMaxLOD = result.Length - 1
            //
            // If the points are flattened for 2D, the result
            // only contains a single point list, because dynamic
            // LODs are unnecessary for 2D detection.
            // However, GlobalMaxLOD still refers to the number
            // of LODs that *would* be used given the current
            // Scan LOD without flattening. The reason for this
            // is that we need the max LOD value for showing
            // the correct inspector settings, even if the scan
            // result contains only one point list.
            //
            // GlobalMaxLOD is also used for clamping the
            // Scan LOD setting, because we have a minimum
            // length limit when splitting volumes into cells.
            // Therefore it's not possible to apply high Scan
            // LODs to small colliders/volumes.

            return(s_GlobalMaxLOD);
        }
Exemplo n.º 3
0
        private static List <ScanResultLOD> GetLODPointLists(
            PhysicsScene scene,
            Transform transform,
            GameObjectShape shape)
        {
            SplitVolumesIntoCells(shape.ScanLOD);

            int  mask    = 1 << transform.gameObject.layer;
            bool flatten = shape.Flatten;

            var result = ScanResultLOD.CreateList(flatten ? 1 : s_GlobalMaxLOD + 1);

            foreach (Volume vol in s_Volumes)
            {
                ToggleActiveColliders(vol);

                bool       isConcave       = vol.IsConcave;
                Vector3    cellSize        = vol.CellSize;
                Vector3Int gridSize        = vol.GridSize;
                Vector3    offset          = vol.Bounds.min + cellSize * 0.5f;
                Vector3    boundsCenter    = vol.Bounds.center;
                float      boundsMagnitude = vol.Bounds.size.magnitude;
                int        nx = gridSize.x - 1;
                int        ny = gridSize.y - 1;
                int        nz = gridSize.z - 1;


                // Find cells occupied by colliders.

                Clear(gridSize);

                for (int x = 0; x <= nx; x++)
                {
                    for (int y = 0; y <= ny; y++)
                    {
                        for (int z = 0; z <= nz; z++)
                        {
                            Vector3 worldPoint = Vector3.Scale(
                                new Vector3(x, y, z), cellSize) + offset;

                            bool isInside = isConcave
                                ? IsInsideCollider(
                                scene, mask, worldPoint, vol.Bounds)
                                : IsInsideCollider(
                                scene, mask, worldPoint, vol.Colliders);

                            s_HollowGrid[x, y, z] = isInside;
                            s_FilledGrid[x, y, z] = isInside;
                        }
                    }
                }


                // Reduce cell count, hollow out filled grid.

                for (int x = 1; x < nx; x++)
                {
                    for (int y = 1; y < ny; y++)
                    {
                        for (int z = 1; z < nz; z++)
                        {
                            if (s_FilledGrid[x, y, z])
                            {
                                s_HollowGrid[x, y, z] &=
                                    !(s_FilledGrid[x - 1, y, z]
                                      & s_FilledGrid[x + 1, y, z]
                                      & s_FilledGrid[x, y - 1, z]
                                      & s_FilledGrid[x, y + 1, z]
                                      & s_FilledGrid[x, y, z - 1]
                                      & s_FilledGrid[x, y, z + 1]);
                            }
                        }
                    }
                }


                // Downsample cells for generating LODs
                // and store points in corresponding groups.

                int   level = flatten ? 0 : vol.MaxLOD;
                float yFlat = transform.position.y;

                for (int x = 0; x <= nx; x++)
                {
                    for (int y = 0; y <= ny; y++)
                    {
                        for (int z = 0; z <= nz; z++)
                        {
                            if (s_HollowGrid[x, y, z])
                            {
                                Cell    cell       = new Cell(level, x, y, z);
                                Vector3 worldPoint = cell.Scale(cellSize) + offset;

                                if (flatten)
                                {
                                    cell.y       = 0;
                                    worldPoint.y = yFlat;
                                }

                                // Highest LOD.
                                AddPoint(cell, worldPoint);

                                for (int i = level; i > 0; i--)
                                {
                                    cell = cell.Downsample();
                                    AddPoint(cell, worldPoint);
                                }
                            }
                        }
                    }
                }


                // Write centroids to result.

                Matrix4x4 matrix     = transform.worldToLocalMatrix;
                float     projection = shape.Projection;
                bool      project    = projection > 0;

                foreach (var kvp in s_PointGroupsByCell)
                {
                    // KeyValuePair, Key: Cell, Value: PointGroup.
                    int     i          = kvp.Key.level;
                    Vector3 worldPoint = kvp.Value.Centroid;

                    if (project && i > 0)
                    {
                        // Project outward from volume bounds center,
                        // unless lowest LOD or flattened points -> i == 0.
                        // NOTE Doesn't work well with compound or concave colliders.
                        Vector3 normal = (worldPoint - boundsCenter).normalized;
                        if (scene.Raycast(boundsCenter + normal * boundsMagnitude,
                                          -normal, out RaycastHit hit, boundsMagnitude, mask))
                        {
                            worldPoint = Vector3.Lerp(worldPoint, hit.point, projection);
                        }
                    }
                    // World -> local.
                    result[i].LocalPoints.Add(matrix.MultiplyPoint3x4(worldPoint));
                }
            }

            ToggleActiveColliders();

            return(result);
        }
Exemplo n.º 4
0
        private static void CreateVolumes(
            IList <Collider> colliders,
            GameObjectShape shape)
        {
            s_Volumes.Clear();

            if (shape.Merge)
            {
                // Merge all.

                var volume = new Volume()
                {
                    Colliders = new List <Collider>(colliders)
                };

                foreach (var collider in colliders)
                {
                    bool isConcave = collider is MeshCollider &&
                                     !((MeshCollider)collider).convex;

                    volume.IsConcave = volume.IsConcave || isConcave;
                    volume.Bounds.Encapsulate(collider.bounds);
                }
                s_Volumes.Add(volume);
            }
            else
            {
                var byColliderType = new List <Volume>[]
                {
                    new List <Volume>(), new List <Volume>()
                };

                foreach (var collider in colliders)
                {
                    bool isConcave = collider is MeshCollider &&
                                     !((MeshCollider)collider).convex;

                    byColliderType[isConcave ? 1 : 0].Add(new Volume()
                    {
                        Colliders = new List <Collider>()
                        {
                            collider
                        },
                        Bounds    = collider.bounds,
                        IsConcave = isConcave
                    });
                }

                // Merge connected by type, don't mix convex and concave.

                for (int i = 0; i < 2; i++)
                {
                    var volumes = byColliderType[i];

                    for (int j = volumes.Count - 1; j > 0; j--)
                    {
                        for (int k = j - 1; k > -1; k--)
                        {
                            // NOTE Intersects check yields true for colliders
                            // located side by side, e.g. two box colliders of
                            // size 1x1x1, sitting at 0/0/0 and 1/1/1 respectively.
                            if (volumes[j].Bounds.Intersects(volumes[k].Bounds))
                            {
                                volumes[j].Bounds.Encapsulate(volumes[k].Bounds);
                                volumes[j].Colliders.AddRange(volumes[k].Colliders);
                                volumes.RemoveAt(k);
                                j--;
                            }
                        }
                    }

                    s_Volumes.AddRange(volumes);
                }
            }
        }