public static bool CheckGroundedAABB(Vector3 position, AABB aabb, IWorld world, out BlockData groundBlock)
        {
            aabb += position;
            int y = Mathf.FloorToInt(aabb.Min.y - 0.5f);

            if (y >= 0 && y < ChunkHeight)
            {
                int startX = Mathf.FloorToInt(aabb.Min.x);
                int endX   = Mathf.FloorToInt(aabb.Max.x);
                int startZ = Mathf.FloorToInt(aabb.Min.z);
                int endZ   = Mathf.FloorToInt(aabb.Max.z);

                for (int x = startX; x <= endX; x++)
                {
                    for (int z = startZ; z <= endZ; z++)
                    {
                        groundBlock = world.RWAccessor.GetBlock(x, y, z);
                        AABB?blockAABB = groundBlock.GetBoundingBox(x, y, z, world, true);

                        if (blockAABB != null && aabb.Intersects(blockAABB.Value))
                        {
                            return(true);
                        }
                    }
                }
            }

            groundBlock = null;
            return(false);
        }
        private static bool CollideWithTerrainAABB(int axis, AABB aabb, IWorld world, PhysicMaterial material, float time, ref float velocity, out float movement)
        {
            movement = velocity * time;

            if (movement == 0)
            {
                return(false);
            }

            Vector3Int start = default;
            Vector3Int end   = default;
            Vector3Int step  = default;

            if (movement > 0)
            {
                for (int i = 0; i < 3; i++)
                {
                    if (axis == i)
                    {
                        start[i] = Mathf.FloorToInt(aabb.Max[i]);
                        end[i]   = Mathf.FloorToInt(aabb.Max[i] + movement);
                    }
                    else
                    {
                        start[i] = Mathf.FloorToInt(aabb.Min[i]);
                        end[i]   = Mathf.FloorToInt(aabb.Max[i]);
                    }

                    step[i] = 1;
                }
            }
            else
            {
                for (int i = 0; i < 3; i++)
                {
                    if (axis == i)
                    {
                        end[i]  = Mathf.FloorToInt(aabb.Min[i] + movement);
                        step[i] = -1;
                    }
                    else
                    {
                        end[i]  = Mathf.FloorToInt(aabb.Max[i]);
                        step[i] = 1;
                    }

                    start[i] = Mathf.FloorToInt(aabb.Min[i]);
                }
            }

            end += step;

            for (int x = start.x; x != end.x; x += step.x)
            {
                for (int z = start.z; z != end.z; z += step.z)
                {
                    for (int y = start.y; y != end.y; y += step.y)
                    {
                        BlockData block     = world.RWAccessor.GetBlock(x, y, z);
                        AABB?     blockAABB = block.GetBoundingBox(x, y, z, world, true);

                        if (blockAABB == null)
                        {
                            continue;
                        }

                        AABB blockBB = blockAABB.Value;
                        bool flag    = true;

                        for (int i = 0; i < 3; i++)
                        {
                            if (i != axis)
                            {
                                flag &= blockBB.Max[i] > aabb.Min[i] && blockBB.Min[i] < aabb.Max[i];
                            }
                        }

                        if (!flag)
                        {
                            continue;
                        }

                        if (movement > 0 && aabb.Max[axis] <= blockBB.Min[axis])
                        {
                            float maxMovement = blockBB.Min[axis] - aabb.Max[axis];

                            if (movement > maxMovement)
                            {
                                CalculateVelocityAfterCollisionWithTerrain(material, block, ref velocity);
                                movement = maxMovement;
                                return(true);
                            }
                        }
                        else if (movement < 0 && aabb.Min[axis] >= blockBB.Max[axis])
                        {
                            float maxMovement = blockBB.Max[axis] - aabb.Min[axis];

                            if (movement < maxMovement)
                            {
                                CalculateVelocityAfterCollisionWithTerrain(material, block, ref velocity);
                                movement = maxMovement;
                                return(true);
                            }
                        }
                    }
                }
            }

            return(false);
        }