Example #1
0
def advanceToChunk(ray, dimension, maxDistance):
    point, vector = ray
    inBounds = point in dimension.bounds

    for pos, face in _cast(point, vector, maxDistance, 16):

        x, y, z = pos
        x >>= 4
        y >>= 4
        z >>= 4
        if inBounds and pos not in dimension.bounds:
            raise RayBoundsError("Ray exited dimension bounds.")
        inBounds = pos in dimension.bounds

        if dimension.containsChunk(x, z):
            chunk = dimension.getChunk(x, z)
            section = chunk.getSection(y)
            if section is not None:
                # if (section.Blocks == 0).all():
                #     log.warn("Empty section found!!")
                #     continue
                box = SectionBox(x, y, z)
                if point in box:
                    return point
                ixs = rayIntersectsBox(box, ray)
                if ixs:
                    hitPoint = ixs[0][0]
                    return hitPoint

    raise RayBoundsError("Ray exited dimension bounds.")
Example #2
0
def advanceToChunk(ray, dimension, maxDistance):
    point, vector = ray
    inBounds = point in dimension.bounds

    for pos, face in _cast(point, vector, maxDistance, 16):

        x, y, z = pos
        x >>= 4
        y >>= 4
        z >>= 4
        if inBounds and pos not in dimension.bounds:
            raise RayBoundsError("Ray exited dimension bounds.")
        inBounds = pos in dimension.bounds

        if dimension.containsChunk(x, z):
            chunk = dimension.getChunk(x, z)
            section = chunk.getSection(y)
            if section is not None:
                # if (section.Blocks == 0).all():
                #     log.warn("Empty section found!!")
                #     continue
                box = SectionBox(x, y, z)
                if point in box:
                    return point
                ixs = rayIntersectsBox(box, ray)
                if ixs:
                    hitPoint = ixs[0][0]
                    return hitPoint

    raise RayBoundsError("Ray exited dimension bounds.")
Example #3
0
def rayCast(ray, dimension, maxDistance=100):
    """
    Borrowed from https://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game

    Updates a factor t along each axis to compute the distance from the vector origin (in units of the vector
    magnitude) to the next integer value (i.e. block edge) along that axis.

    Return the block position and face of the block touched.

    Raises MaxDistanceError if the ray exceeded the max distance without hitting any blocks, or if the ray exits or
    doesn't enter the dimension's bounds.

    Bypasses non-air blocks until the first air block is found, and only returns when a non-air block is found after
    the first air block.

    :param ray:
    :type ray: Ray
    :param maxDistance:
    :type maxDistance: int
    :param dimension:
    :type dimension: mceditlib.worldeditor.WorldEditorDimension
    :return: (point, face)
    :rtype:
    """
    point, vector = ray
    if all(v == 0 for v in vector):
        raise ValueError("Cannot cast with zero direction ray.")

    bounds = dimension.bounds
    if point not in bounds:
        intersects = rayIntersectsBox(bounds, ray)
        if not intersects:
            raise RayBoundsError("Ray does not enter dimension bounds.")

        point = intersects[0][0]

    point = advanceToChunk(Ray(point, vector), dimension, maxDistance * 4)
    currentCX, currentCY, currentCZ = point.intfloor()
    currentChunk = None

    foundAir = False

    for pos, face in _cast(point, vector, maxDistance, 1):
        cx = pos[0] >> 4
        cz = pos[2] >> 4
        if cx != currentCX or cz != currentCZ:
            currentCX = cx
            currentCZ = cz
            if dimension.containsChunk(cx, cz):
                currentChunk = dimension.getChunk(cx, cz)  # xxxx WorldEditor.recentlyLoadedChunks
        ID = dimension.getBlockID(*pos)

        if ID == 0:
            foundAir = True
        if ID and foundAir:
            return Vector(*pos), faces.Face.fromVector(face)

    raise MaxDistanceError("Ray exceeded max distance.")
Example #4
0
def rayCastInBounds(ray, dimension, maxDistance=100, hitAir=False):
    try:
        position, face = rayCast(ray, dimension, maxDistance, hitAir)
    except RayBoundsError:
        ixs = rayIntersectsBox(dimension.bounds, ray)
        if ixs:
            position, face = ixs[0]
            position = position.intfloor()
        else:
            position, face = None, None
    return position, face
Example #5
0
def rayCastInBounds(ray, dimension, maxDistance=100):
    try:
        position, face = rayCast(ray, dimension, maxDistance)
    except RayBoundsError:
        ixs = rayIntersectsBox(dimension.bounds, ray)
        if ixs:
            position, face = ixs[0]
            position = position.intfloor()
        else:
            position, face = None, None
    return position, face
Example #6
0
def rayCast(ray, dimension, maxDistance=100, hitAir=False):
    """
    Borrowed from https://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game

    Updates a factor t along each axis to compute the distance from the vector origin (in units of the vector
    magnitude) to the next integer value (i.e. block edge) along that axis.

    Return the block position and face of the block touched.

    Raises MaxDistanceError if the ray exceeded the max distance without hitting any blocks, or if the ray exits or
    doesn't enter the dimension's bounds.

    :param ray:
    :type ray: Ray
    :param maxDistance:
    :type maxDistance: int
    :param dimension:
    :type dimension: mceditlib.worldeditor.WorldEditorDimension
    :return: (point, face)
    :rtype:
    """
    point, vector = ray
    if all(v == 0 for v in vector):
        raise ValueError("Cannot cast with zero direction ray.")

    bounds = dimension.bounds
    if point not in bounds:
        intersects = rayIntersectsBox(bounds, ray)
        if not intersects:
            raise RayBoundsError("Ray does not enter dimension bounds.")

        point = intersects[0][0]

    point = advanceToChunk(Ray(point, vector), dimension, maxDistance * 4)
    currentCX, currentCY, currentCZ = point.intfloor()
    currentChunk = None

    for pos, face in _cast(point, vector, maxDistance, 1):
        cx = pos[0] >> 4
        cz = pos[2] >> 4
        if cx != currentCX or cz != currentCZ:
            currentCX = cx
            currentCZ = cz
            if dimension.containsChunk(cx, cz):
                currentChunk = dimension.getChunk(
                    cx, cz)  # xxxx WorldEditor.recentlyLoadedChunks
        ID = dimension.getBlockID(*pos)
        if ID or hitAir:
            return Vector(*pos), faces.Face.fromVector(face)

    raise MaxDistanceError("Ray exceeded max distance.")
Example #7
0
def rayCast(ray, dimension, maxDistance=100):
    """
    Borrowed from https://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game

    Updates a factor t along each axis to compute the distance from the vector origin (in units of the vector
    magnitude) to the next integer value (i.e. block edge) along that axis.

    Return the block position and face of the block touched.

    Raises MaxDistanceError if the ray exceeded the max distance without hitting any blocks, or if the ray exits or
    doesn't enter the dimension's bounds.

    Bypasses non-air blocks until the first air block is found, and only returns when a non-air block is found after
    the first air block.

    :param ray:
    :type ray: Ray
    :param maxDistance:
    :type maxDistance: int
    :param dimension:
    :type dimension: mceditlib.worldeditor.WorldEditorDimension
    :return: (point, face)
    :rtype:
    """
    point, vector = ray
    if all(v == 0 for v in vector):
        raise ValueError("Cannot cast with zero direction ray.")

    bounds = dimension.bounds
    if point not in bounds:
        intersects = rayIntersectsBox(bounds, ray)
        if not intersects:
            raise RayBoundsError("Ray does not enter dimension bounds.")

        point = intersects[0][0]

    point = advanceToChunk(Ray(point, vector), dimension, maxDistance * 4)

    foundAir = False

    for pos, face in _cast(point, vector, maxDistance, 1):
        ID = dimension.getBlockID(*pos)

        if ID == 0:  # xxx configurable air blocks?
            foundAir = True
        if ID and foundAir:
            return Vector(*pos), faces.Face.fromVector(face)
        if pos not in bounds:
            raise RayBoundsError("Ray exited dimension bounds")

    raise MaxDistanceError("Ray exceeded max distance.")
Example #8
0
def boxFaceUnderCursor(box, mouseRay):
    """
    Find the nearest face of the given bounding box that intersects with the given mouse ray
    and return (point, face)
    """
    nearPoint, mouseVector = mouseRay

    intersections = rayIntersectsBox(box, mouseRay)
    if intersections is None:
        return None, None

    point, face = intersections[0]

    # if the point is near the edge of the face, and the edge is facing away,
    # return the away-facing face

    dim = face.dimension

    dim1 = (dim + 1) % 3
    dim2 = (dim + 2) % 3

    # determine if a click was within self.edge_factor of the edge of a selection box side. if so, click through
    # to the opposite side
    edge_factor = 0.1

    for d in dim1, dim2:
        if not isinstance(d, int):
            assert False
        edge_width = box.size[d] * edge_factor
        faceNormal = [0, 0, 0]
        cameraBehind = False

        if point[d] - box.origin[d] < edge_width:
            faceNormal[d] = -1
            cameraBehind = nearPoint[d] - box.origin[d] > 0
        if point[d] - box.maximum[d] > -edge_width:
            faceNormal[d] = 1
            cameraBehind = nearPoint[d] - box.maximum[d] < 0

        if numpy.dot(faceNormal, mouseVector) > 0 or cameraBehind:
            # the face adjacent to the clicked edge faces away from the cam
            # xxxx this is where to allow iso views in face-on angles to grab edges
            # xxxx also change face highlight node to highlight this area
            return intersections[1] if len(
                intersections) > 1 else intersections[0]

    return point, face
Example #9
0
def boxFaceUnderCursor(box, mouseRay):
    """
    Find the nearest face of the given bounding box that intersects with the given mouse ray
    and return (point, face)
    """
    nearPoint, mouseVector = mouseRay

    intersections = rayIntersectsBox(box, mouseRay)
    if intersections is None:
        return None, None

    point, face = intersections[0]

    # if the point is near the edge of the face, and the edge is facing away,
    # return the away-facing face

    dim = face.dimension

    dim1 = (dim + 1) % 3
    dim2 = (dim + 2) % 3

    # determine if a click was within self.edge_factor of the edge of a selection box side. if so, click through
    # to the opposite side
    edge_factor = 0.1

    for d in dim1, dim2:
        if not isinstance(d, int):
            assert False
        edge_width = box.size[d] * edge_factor
        faceNormal = [0, 0, 0]
        cameraBehind = False

        if point[d] - box.origin[d] < edge_width:
            faceNormal[d] = -1
            cameraBehind = nearPoint[d] - box.origin[d] > 0
        if point[d] - box.maximum[d] > -edge_width:
            faceNormal[d] = 1
            cameraBehind = nearPoint[d] - box.maximum[d] < 0

        if numpy.dot(faceNormal, mouseVector) > 0 or cameraBehind:
            # the face adjacent to the clicked edge faces away from the cam
            # xxxx this is where to allow iso views in face-on angles to grab edges
            # xxxx also change face highlight node to highlight this area
            return intersections[1] if len(intersections) > 1 else intersections[0]

    return point, face