Example #1
0
def build_module_rec(node,
                     resolution,
                     verts,
                     faces,
                     uvs,
                     weights,
                     bone_weights,
                     input_loop=[],
                     uv_height=0):
    is_origin = False  # true if thare are no input loop
    if len(node.children) == 0:
        if len(input_loop) > 0:
            faces.append(input_loop)
            n = len(input_loop)
            uvs.append([
                Vector((cos(i / (n - 1)), sin(i / (n - 1)))) for i in range(n)
            ])
        return

    rot_dir = Vector((0, 0, 1)).rotation_difference(
        node.direction)  # transformation from y_up space to directio_up space
    rot_dir_inv = rot_dir.inverted()  # inverse transformation
    module_verts = []  # verts created by the module
    n_verts = len(verts)  # number of vertices before adding module
    if len(input_loop) == 0:
        is_origin = True
        module_verts = make_circle(Vector((0, 0, 0)), Vector((0, 0, 1)),
                                   node.radius, resolution)
        input_loop = [i for i in range(resolution)]
        n_verts += resolution
    else:
        input_tangent = rot_dir_inv @ (verts[input_loop[0]] - node.position)
        input_angle_offset = int(
            input_tangent.xy.angle_signed(Vector(
                (1, 0))) / 2 / pi * resolution)
        if input_angle_offset != 0:
            input_loop = rotate(input_loop, input_angle_offset)
    output_loops = []  # input_loop for each child
    output_resolutions = [resolution]
    extremity = node.children[0]
    extremity_height = (extremity.position - node.position).magnitude
    extremity_position = rot_dir_inv @ (extremity.position - node.position)
    extremity_direction = rot_dir_inv @ extremity.direction
    extremity_verts = make_circle(extremity_position, extremity_direction,
                                  extremity.radius,
                                  resolution)  # verts of output loop
    extremity_loop = [
        i for i in range(n_verts, n_verts + len(extremity_verts))
    ]  # indexes of output_loops
    output_loops.append(extremity_loop)
    module_verts += extremity_verts
    n_verts += resolution
    filling_loop_indexes = [
        True
    ] * resolution  # False when a vert of the loop is replaces by a children loop
    loop_up = [-1
               ] * resolution  # loop for faces in the upper part of the module
    loop_down = [
        -1
    ] * resolution  # loop for faces in the lower part of the module

    for child in node.children[1:]:
        if is_origin:  # if true then this child is the roots origin
            break
        max_child_res = resolution // (len(node.children) - 1
                                       )  # max resolution of childs
        child_dir = rot_dir_inv @ child.direction  # child direction in y_up space
        child_dir.normalize()  #is this needed ?
        child_pos = rot_dir_inv @ (child.position - node.position)
        child_resolution = get_resolution(node.radius, child.radius,
                                          resolution, max_child_res)
        child_verts = make_circle_2(child_pos, child_dir, child.radius,
                                    Vector((0, 0, -1)), child_resolution)
        child_loop = [i for i in range(n_verts, n_verts + child_resolution)
                      ]  # input loop for child
        output_loops.append(child_loop)
        output_resolutions.append(child_resolution)
        wrap_circle(child_verts, child_dir, Vector((0, 0, 1)),
                    node.radius)  # wrap child verts around module cylinder

        angle_offset = int(
            ((-Vector(
                (1, 0)).angle_signed(child_dir.xy) / 2 / pi) % 1) * resolution
            + .5
        )  # offset in index necessary for the loops of child to be aligned with the base loop
        for i in range(-len(child_verts) // 4, len(child_verts) // 4 + 1):
            filling_loop_indexes[(i + angle_offset) % resolution] = False
            loop_down[(i + angle_offset) %
                      resolution] = n_verts + i % len(child_verts)
            loop_up[(i + angle_offset) % resolution] = n_verts + (
                -(i + len(child_verts) // 2)) % len(child_verts)
        module_verts.extend(child_verts)
        n_verts += len(child_verts)

    filling_loop = make_circle(extremity_position / 2, Vector(
        (0, 0, 1)), (node.radius + extremity.radius) / 2, resolution)
    index = 0
    for i in range(len(filling_loop)):
        if filling_loop_indexes[i]:
            loop_down[i] = n_verts + index
            loop_up[i] = n_verts + index
            index += 1

    filling_loop = [
        filling_loop[i] for i in range(len(filling_loop))
        if filling_loop_indexes[i]
    ]
    module_verts.extend(filling_loop)
    faces.extend(
        bridge(input_loop, loop_down) + bridge(loop_up, extremity_loop))
    if is_origin:
        #weights.append((input_loop, 0))
        weights.append(
            ([i for i in range(resolution,
                               len(module_verts) - resolution)], node.radius))
    else:
        weights.append(
            ([i for i in range(len(verts),
                               len(verts) + len(module_verts))], node.radius))
    if node.bone_name is not None:
        if node.bone_name in bone_weights:
            bone_weights[node.bone_name].extend(
                [i for i in range(len(verts),
                                  len(verts) + len(module_verts))])
        else:
            bone_weights[node.bone_name] = [
                i for i in range(len(verts),
                                 len(verts) + len(module_verts))
            ]

    verts.extend([rot_dir @ v + node.position for v in module_verts])
    uv_height = uv_loops(input_loop, loop_down, uv_height, uvs, verts,
                         node.radius,
                         len(node.children) == 1)
    uv_height = uv_loops(loop_up, extremity_loop, uv_height, uvs, verts,
                         extremity.radius,
                         len(node.children) == 1)
    for i, child in enumerate(
            node.children):  # recursively call function on all children
        if is_origin and i == 1:  # if true then this child is the roots origin
            build_module_rec(child, resolution, verts, faces,
                             uvs, weights, bone_weights,
                             list(reversed(input_loop)), uv_height)
        else:
            build_module_rec(child, output_resolutions[i], verts, faces, uvs,
                             weights, bone_weights, output_loops[i], uv_height)
Example #2
0
def OBB(vecs, r_indices=None, eps=1e-6):
    """Convex hull を用いたOBBを返す。
    Z->Y->Xの順で長さが最少となる軸を求める。
    :param vecs: list of Vector
    :type vecs: list | tuple
    :param r_indices: listを渡すとconvexhullの結果を格納する
    :type r_indices: None | list
    :param eps: 種々の計算の閾値
    :return:
        (matrix, obb_size)
        matrix:
            type: Matrx
            OBBの回転と中心を表す。vecsが二次元ベクトルの場合は3x3, 三次元なら4x4。
        obb_size:
            type: Vector
            OBBの各軸の長さ。vecsと同じ次元。
    :rtype: (Matrix, Vector)
    """

    if not vecs:
        return None, None

    # 2D ----------------------------------------------------------------------
    if len(vecs[0]) == 2:
        mat = Matrix.Identity(3)
        bb_size = Vector((0, 0))

        indices = convex_hull_2d(vecs, eps)
        if r_indices:
            r_indices[:] = indices

        if len(indices) == 1:
            mat.col[2][:2] = vecs[0]
        elif len(indices) == 2:
            v1 = vecs[indices[0]]
            v2 = vecs[indices[1]]
            xaxis = (v2 - v1).normalized()
            angle = math.atan2(xaxis[1], xaxis[0])
            mat2 = Matrix.Rotation(angle, 2)
            mat.col[0][:2] = mat2.col[0]
            mat.col[1][:2] = mat2.col[1]
            mat.col[2][:2] = (v1 + v2) / 2
            bb_size[0] = (v2 - v1).length
        else:
            yaxis = _closest_axis_on_plane(vecs, indices)
            angle = math.atan2(yaxis[1], yaxis[0]) - math.pi / 2  # X軸
            mat2 = Matrix.Rotation(angle, 2)
            imat2 = Matrix.Rotation(-angle, 2)
            rotvecs = [imat2 * v for v in vecs]
            loc = Vector((0, 0))
            for i in range(2):
                rotvecs.sort(key=lambda v: v[i])
                bb_size[i] = rotvecs[-1][i] - rotvecs[0][i]
                loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2
            mat.col[0][:2] = mat2.col[0]
            mat.col[1][:2] = mat2.col[1]
            mat.col[2][:2] = mat2 * loc
        return mat, bb_size

    # 3D ----------------------------------------------------------------------
    mat = Matrix.Identity(4)
    bb_size = Vector((0, 0, 0))

    indices = convex_hull(vecs, eps)

    if r_indices:
        r_indices[:] = indices

    if isinstance(indices[0], int):  # 2d
        if len(indices) == 1:
            mat.col[3][:3] = vecs[0]
            return mat, bb_size
        
        elif len(indices) == 2:
            # 同一線上
            v1 = vecs[indices[0]]
            v2 = vecs[indices[1]]
            xaxis = (v2 - v1).normalized()
            quat = Vector((1, 0, 0)).rotation_difference(xaxis)
            mat = quat.to_matrix().to_4x4()
            mat.col[3][:3] = (v1 + v2) / 2
            bb_size[0] = (v2 - v1).length
            return mat, bb_size

        else:
            # 同一平面上
            medium = reduce(lambda a, b: a + b, vecs) / len(vecs)
            v1 = max(vecs, key=lambda v: (v - medium).length)
            v2 = max(vecs, key=lambda v: (v - v1).length)
            line = v2 - v1
            v3 = max(vecs, key=lambda v: line.cross(v - v1).length)
            zaxis = geom.normal(v1, v2, v3)
            if zaxis[2] < 0.0:
                zaxis.negate()

            quat = zaxis.rotation_difference(Vector((0, 0, 1)))
            rotvecs = [quat * v for v in vecs]
            indices_2d = indices

    else:  # 3d
        indices_set = set(chain(*indices))
        zaxis = None
        dist = 0.0
        # 最も距離の近い面(平面)と頂点を求める
        for tri in indices:
            v1, v2, v3 = [vecs[i] for i in tri]
            normal = geom.normal(v1, v2, v3)
            d = 0.0
            for v4 in (vecs[i] for i in indices_set if i not in tri):
                f = abs(geom.distance_point_to_plane(v4, v1, normal))
                d = max(f, d)
            if zaxis is None or d < dist:
                zaxis = -normal
                dist = d

        quat = zaxis.rotation_difference(Vector((0, 0, 1)))
        rotvecs = [(quat * v).to_2d() for v in vecs]
        indices_2d = convex_hull_2d(rotvecs, eps)

    yaxis = _closest_axis_on_plane(rotvecs, indices_2d)
    yaxis = quat.inverted() * yaxis.to_3d()

    xaxis = yaxis.cross(zaxis)
    xaxis.normalize()  # 不要?

    mat.col[0][:3] = xaxis
    mat.col[1][:3] = yaxis
    mat.col[2][:3] = zaxis

    # OBBの大きさと中心を求める
    imat = mat.inverted()
    rotvecs = [imat * v for v in vecs]
    loc = Vector()
    for i in range(3):
        rotvecs.sort(key=lambda v: v[i])
        bb_size[i] = rotvecs[-1][i] - rotvecs[0][i]
        loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2
    mat.col[3][:3] = mat * loc
    return mat, bb_size
Example #3
0
def OBB(vecs, r_indices=None, eps=1e-6):
    """Convex hull を用いたOBBを返す。
    Z->Y->Xの順で長さが最少となる軸を求める。
    :param vecs: list of Vector
    :type vecs: list | tuple
    :param r_indices: listを渡すとconvexhullの結果を格納する
    :type r_indices: None | list
    :param eps: 種々の計算の閾値
    :return:
        (matrix, obb_size)
        matrix:
            type: Matrx
            OBBの回転と中心を表す。vecsが二次元ベクトルの場合は3x3, 三次元なら4x4。
        obb_size:
            type: Vector
            OBBの各軸の長さ。vecsと同じ次元。
    :rtype: (Matrix, Vector)
    """

    if not vecs:
        return None, None

    # 2D ----------------------------------------------------------------------
    if len(vecs[0]) == 2:
        mat = Matrix.Identity(3)
        bb_size = Vector((0, 0))

        indices = convex_hull_2d(vecs, eps)
        if r_indices:
            r_indices[:] = indices

        if len(indices) == 1:
            mat.col[2][:2] = vecs[0]
        elif len(indices) == 2:
            v1 = vecs[indices[0]]
            v2 = vecs[indices[1]]
            xaxis = (v2 - v1).normalized()
            angle = math.atan2(xaxis[1], xaxis[0])
            mat2 = Matrix.Rotation(angle, 2)
            mat.col[0][:2] = mat2.col[0]
            mat.col[1][:2] = mat2.col[1]
            mat.col[2][:2] = (v1 + v2) / 2
            bb_size[0] = (v2 - v1).length
        else:
            yaxis = _closest_axis_on_plane(vecs, indices)
            angle = math.atan2(yaxis[1], yaxis[0]) - math.pi / 2  # X軸
            mat2 = Matrix.Rotation(angle, 2)
            imat2 = Matrix.Rotation(-angle, 2)
            rotvecs = [imat2 * v for v in vecs]
            loc = Vector((0, 0))
            for i in range(2):
                rotvecs.sort(key=lambda v: v[i])
                bb_size[i] = rotvecs[-1][i] - rotvecs[0][i]
                loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2
            mat.col[0][:2] = mat2.col[0]
            mat.col[1][:2] = mat2.col[1]
            mat.col[2][:2] = mat2 * loc
        return mat, bb_size

    # 3D ----------------------------------------------------------------------
    mat = Matrix.Identity(4)
    bb_size = Vector((0, 0, 0))

    indices = convex_hull(vecs, eps)

    if r_indices:
        r_indices[:] = indices

    if isinstance(indices[0], int):  # 2d
        if len(indices) == 1:
            mat.col[3][:3] = vecs[0]
            return mat, bb_size

        elif len(indices) == 2:
            # 同一線上
            v1 = vecs[indices[0]]
            v2 = vecs[indices[1]]
            xaxis = (v2 - v1).normalized()
            quat = Vector((1, 0, 0)).rotation_difference(xaxis)
            mat = quat.to_matrix().to_4x4()
            mat.col[3][:3] = (v1 + v2) / 2
            bb_size[0] = (v2 - v1).length
            return mat, bb_size

        else:
            # 同一平面上
            medium = reduce(lambda a, b: a + b, vecs) / len(vecs)
            v1 = max(vecs, key=lambda v: (v - medium).length)
            v2 = max(vecs, key=lambda v: (v - v1).length)
            line = v2 - v1
            v3 = max(vecs, key=lambda v: line.cross(v - v1).length)
            zaxis = geom.normal(v1, v2, v3)
            if zaxis[2] < 0.0:
                zaxis.negate()

            quat = zaxis.rotation_difference(Vector((0, 0, 1)))
            rotvecs = [quat * v for v in vecs]
            indices_2d = indices

    else:  # 3d
        indices_set = set(chain(*indices))
        zaxis = None
        dist = 0.0
        # 最も距離の近い面(平面)と頂点を求める
        for tri in indices:
            v1, v2, v3 = [vecs[i] for i in tri]
            normal = geom.normal(v1, v2, v3)
            d = 0.0
            for v4 in (vecs[i] for i in indices_set if i not in tri):
                f = abs(geom.distance_point_to_plane(v4, v1, normal))
                d = max(f, d)
            if zaxis is None or d < dist:
                zaxis = -normal
                dist = d

        quat = zaxis.rotation_difference(Vector((0, 0, 1)))
        rotvecs = [(quat * v).to_2d() for v in vecs]
        indices_2d = convex_hull_2d(rotvecs, eps)

    yaxis = _closest_axis_on_plane(rotvecs, indices_2d)
    yaxis = quat.inverted() * yaxis.to_3d()

    xaxis = yaxis.cross(zaxis)
    xaxis.normalize()  # 不要?

    mat.col[0][:3] = xaxis
    mat.col[1][:3] = yaxis
    mat.col[2][:3] = zaxis

    # OBBの大きさと中心を求める
    imat = mat.inverted()
    rotvecs = [imat * v for v in vecs]
    loc = Vector()
    for i in range(3):
        rotvecs.sort(key=lambda v: v[i])
        bb_size[i] = rotvecs[-1][i] - rotvecs[0][i]
        loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2
    mat.col[3][:3] = mat * loc
    return mat, bb_size