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);
        }
        public static CollisionFlags CollideWithTerrainAABB(Vector3 position, AABB aabb, IWorld world, PhysicMaterial material, float time, ref Vector3 velocity, out Vector3 movement)
        {
            aabb += position;

            for (int i = 0; i < 3; i++)
            {
                if (Mathf.Abs(velocity[i]) <= Mathf.Epsilon)
                {
                    velocity[i] = 0;
                }
            }

            CollisionFlags flags     = CollisionFlags.None;
            bool           negativeY = velocity.y < 0;

            movement = default;

            if (CollideWithTerrainAABB(1, aabb, world, material, time, ref velocity.y, out movement.y))
            {
                flags |= negativeY ? CollisionFlags.Below : CollisionFlags.Above;
            }

            aabb += new Vector3(0, movement.y, 0);

            if (CollideWithTerrainAABB(0, aabb, world, material, time, ref velocity.x, out movement.x))
            {
                flags |= CollisionFlags.Sides;
            }

            aabb += new Vector3(movement.x, 0, 0);

            if (CollideWithTerrainAABB(2, aabb, world, material, time, ref velocity.z, out movement.z))
            {
                flags |= CollisionFlags.Sides;
            }

            return(flags);
        }