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)
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