from bpy_extras import bpy,bmesh from mathutils import Vector, Matrix from pathlib import Path def setViewport(space, ctx, position): rv3d = space.region_3d rv3d.view_matrix = position bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='SELECT') bpy.ops.view3d.view_selected(ctx) bpy.ops.object.select_all(action='DESELECT') frontier = r"G:\Frontier" for filepath in list(Path(frontier).rglob("*.fmod")): filepath = filepath.resolve().as_posix() bpy.ops.custom_import.import_mhf_fmod(filepath = filepath) space, area = next(((space, area) for area in bpy.context.screen.areas if area.type == 'VIEW_3D' for space in area.spaces if space.type == 'VIEW_3D')) ctx = bpy.context.copy() ctx['area'] = area ctx['region'] = area.regions[-1] space.viewport_shade = 'TEXTURED' for ix,position in enumerate([Matrix.Rotation(i,4,'Z')*Matrix.Rotation(j,4,'Y')*Matrix.Rotation(k,4,'X') for i in range(-45,46,45) for j in range(-45,46,45) for k in range(-45,46,45)]): #Y is axis of weapons and armour setViewport(space, ctx, position) bpy.context.scene.render.image_settings.file_format='PNG' bpy.context.scene.render.alpha_mode = "TRANSPARENT" bpy.context.scene.render.resolution_percentage = 100 bpy.context.scene.render.filepath = filepath[:-4]+"-Angle %d"%ix+".JPEG" bpy.ops.render.opengl(write_still=True)
def makeObject(self): print("Creating mesh", end='') # Create the mesh mesh = bpy.data.meshes.new(self.name) # Prepare vertices and faces mesh.vertices.add(self.numVerts) mesh.tessfaces.add(self.numTris) print('.', end='') # Verts mesh.vertices.foreach_set("co", unpack_list(self.frames[0])) mesh.transform(Matrix.Rotation(-pi / 2, 4, 'Z')) print('.', end='') # Tris mesh.tessfaces.foreach_set("vertices_raw", unpack_face_list([face[0] for face in self.tris])) print('.', end='') # Skins mesh.tessface_uv_textures.new() if self.numSkins > 0: material = bpy.data.materials.new(self.name) for skin in self.skins: skinImg = Util.loadImage(skin, self.filePath) if skinImg == None: skinImg = bpy.data.images.new(os.path.join(self.filePath, skin), self.skinWidth, self.skinHeight) skinImg.mapping = 'UV' skinImg.name = skin skinTex = bpy.data.textures.new(self.name + skin, type='IMAGE') skinTex.image = skinImg matTex = material.texture_slots.add() matTex.texture = skinTex matTex.texture_coords = 'UV' matTex.use_map_color_diffuse = True matTex.use_map_alpha = True matTex.uv_layer = mesh.tessface_uv_textures[0].name mesh.materials.append(material) print('.', end='') # UV mesh.tessface_uv_textures[0].data.foreach_set("uv_raw", unpack_list([self.uvs[i] for i in unpack_face_list([face[1] for face in self.tris])])) if self.numSkins > 0: image = mesh.materials[0].texture_slots[0].texture.image if image != None: for uv in mesh.tessface_uv_textures[0].data: uv.image = image print('.', end='') mesh.validate() mesh.update() obj = bpy.data.objects.new(mesh.name, mesh) base = bpy.context.scene.objects.link(obj) bpy.context.scene.objects.active = obj base.select = True print("Done") # Animate if self.options.fImportAnimation and self.numFrames > 1: for i, frame in enumerate(self.frames): progressStatus = i / self.numFrames * 100 #bpy.context.scene.frame_set(i + 1) obj.shape_key_add(from_mix=False) mesh.vertices.foreach_set("co", unpack_list(frame)) mesh.transform(Matrix.Rotation(-pi / 2, 4, 'Z')) mesh.shape_keys.key_blocks[i].value = 1.0 mesh.shape_keys.key_blocks[i].keyframe_insert("value", frame=i + 1) if i < len(self.frames) - 1: mesh.shape_keys.key_blocks[i].value = 0.0 mesh.shape_keys.key_blocks[i].keyframe_insert("value", frame=i + 2) if i > 0: mesh.shape_keys.key_blocks[i].value = 0.0 mesh.shape_keys.key_blocks[i].keyframe_insert("value", frame=i) print("Animating - progress: %3i%%\r" % int(progressStatus), end='') print("Animating - progress: 100%.") bpy.context.scene.update() print("Model imported")
def make_ik_spin_control_widget(self): if self.pivot_type == 'ANKLE_TOE': obj = create_ballsocket_widget(self.obj, self.bones.ctrl.ik_spin, size=0.75) rotfix = Matrix.Rotation(math.pi/2, 4, self.main_axis.upper()) adjust_widget_transform_mesh(obj, rotfix, local=True)
def _main(self, obj, group, DENSITY=1.0, SCALE=0.6, RAND_LOC=0.8, RAND_ALIGN=0.75, ): from math import radians, pi # OFS = 0.2 SEEK = 2.0 # distance for ray to seek BAD_NORMAL = Vector((0.0, 0.0, -1.0)) WALL_LIMIT = radians(45.0) mats = (Matrix.Rotation(radians(-45), 3, 'X'), Matrix.Rotation(radians(+45), 3, 'X'), Matrix.Rotation(radians(-45), 3, 'Y'), Matrix.Rotation(radians(+45), 3, 'Y'), Matrix.Rotation(radians(-45), 3, 'Z'), Matrix.Rotation(radians(+45), 3, 'Z'), ) Z_UP = Vector((0.0, 0.0, 1.0)) Y_UP = Vector((0.0, 1.0, 0.0)) if not group: self.report({'WARNING'}, "Group '%s' not found" % obj.name) return def debug_edge(v1, v2): mesh = bpy.data.meshes.new("Retopo") mesh.from_pydata([v1, v2], [(0.0, 1.0)], []) scene = bpy.context.scene mesh.update() obj_new = bpy.data.objects.new("Torus", mesh) scene.objects.link(obj_new) ray = obj.ray_cast closest_point_on_mesh = obj.closest_point_on_mesh obj_mat = obj.matrix_world.copy() obj_mat_inv = obj_mat.inverted() # obj_quat = obj_mat.to_quaternion() # obj_quat_inv = obj_mat_inv.to_quaternion() DEBUG = False def fix_point(p): ok, hit, no, ind = closest_point_on_mesh(obj_mat_inv * p) if ok: if DEBUG: return [p, no, None] else: # print("good", hit, no) return [hit, no, None] # worry! print("bad!", p, BAD_NORMAL) return [p, BAD_NORMAL, None] def get_points(stroke): return [fix_point(point.co) for point in stroke.points] def get_splines(gp): if gp.layers.active: frame = gp.layers.active.active_frame return [get_points(stroke) for stroke in frame.strokes] else: return [] def main(): scene = bpy.context.scene obj = bpy.context.object gp = None if obj: gp = obj.grease_pencil if not gp: gp = scene.grease_pencil if not gp: self.report({'WARNING'}, "No grease pencil layer found") return splines = get_splines(gp) for s in splines: for pt in s: p = pt[0] n = pt[1] # print(p, n) if n is BAD_NORMAL: continue # # dont self intersect best_nor = None #best_hit = None best_dist = 10000000.0 pofs = p + n * 0.01 n_seek = n * SEEK m_alt_1 = Matrix.Rotation(radians(22.5), 3, n) m_alt_2 = Matrix.Rotation(radians(-22.5), 3, n) for _m in mats: for m in (_m, m_alt_1 * _m, m_alt_2 * _m): pdir = m * n_seek ok, hit, nor, ind = ray(pofs, pdir, best_dist) if ok: best_dist = (pofs - hit).length best_nor = nor # best_hit = hit if best_nor: pt[1].length = best_dist best_nor.negate() pt[2] = best_nor #scene.cursor_location[:] = best_hitnyway # bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', # iterations=1) # debug_edge(p, best_hit) # p[:] = best_hit # Now we need to do scattering. # first corners hits = [] nors = [] oris = [] for s in splines: # point, normal, n_other the closest hit normal for p, n, n_other in s: if n is BAD_NORMAL: continue if n_other: # cast vectors twice as long as the distance # needed just in case. n_down = (n * -SEEK) l = n_down.length n_other.length = l vantage = p + n if DEBUG: p[:] = vantage # We should cast rays between n_down and n_other #for f in (0.0, 0.2, 0.4, 0.6, 0.8, 1.0): TOT = int(10 * DENSITY) #for i in list(range(TOT)): for i in list(range(TOT))[int(TOT / 1.5):]: # second half f = i / (TOT - 1) # focus on the center ''' f -= 0.5 f = f*f f += 0.5 ''' ntmp = f * n_down + (1.0 - f) * n_other # randomize ntmp.x += uniform(-l, l) * RAND_LOC ntmp.y += uniform(-l, l) * RAND_LOC ntmp.z += uniform(-l, l) * RAND_LOC ok, hit, hit_no, ind = ray(vantage, ntmp, ntmp.length) # print(hit, hit_no) if ok: if hit_no.angle(Z_UP) < WALL_LIMIT: hits.append(hit) nors.append(hit_no) oris.append(n_other.cross(hit_no)) #oris.append(n_other) if 0: mesh = bpy.data.meshes.new("ScatterDupliFace") mesh.from_pydata(hits, [], []) scene = bpy.context.scene mesh.update() obj_new = bpy.data.objects.new("ScatterPar", mesh) scene.objects.link(obj_new) obj_new.layers[:] = obj.layers # Now setup dupli-faces obj_new.dupli_type = 'VERTS' ob_child = bpy.data.objects["trash"] ob_child.location = obj_new.location ob_child.parent = obj_new else: def apply_faces(triples): # first randomize the faces shuffle(triples) obs = group.objects[:] tot = len(obs) tot_div = int(len(triples) / tot) for inst_ob in obs: triple_sub = triples[0:tot_div] triples[0:tot_div] = [] vv = [tuple(v) for f in triple_sub for v in f] mesh = bpy.data.meshes.new("ScatterDupliFace") mesh.from_pydata(vv, [], [(i * 3, i * 3 + 1, i * 3 + 2) for i in range(len(triple_sub))]) scene = bpy.context.scene mesh.update() obj_new = bpy.data.objects.new("ScatterPar", mesh) scene.objects.link(obj_new) obj_new.layers[:] = obj.layers # Now setup dupli-faces obj_new.dupli_type = 'FACES' obj_new.use_dupli_faces_scale = True obj_new.dupli_faces_scale = 100.0 inst_ob.location = 0.0, 0.0, 0.0 inst_ob.parent = obj_new # align the object with worldspace obj_new.matrix_world = obj_mat # BGE settings for testing ''' inst_ob.game.physics_type = 'RIGID_BODY' inst_ob.game.use_collision_bounds = True inst_ob.game.collision_bounds = 'TRIANGLE_MESH' inst_ob.game.collision_margin = 0.1 obj_new.select = True ''' # build faces from vert/normals tri = (Vector((0.0, 0.0, 0.01)), Vector((0.0, 0.0, 0.0)), Vector((0.0, 0.01, 0.01))) coords = [] # face_ind = [] for i in range(len(hits)): co = hits[i] no = nors[i] ori = oris[i] quat = no.to_track_quat('X', 'Z') # make 2 angles and blend angle = uniform(-pi, pi) angle_aligned = -(ori.angle(quat * Y_UP, pi)) quat = Quaternion(no, (angle * (1.0 - RAND_ALIGN)) + (angle_aligned * RAND_ALIGN) ).cross(quat) f = uniform(0.1, 1.2) * SCALE coords.append([co + (quat * (tri[0] * f)), co + (quat * (tri[1] * f)), co + (quat * (tri[2] * f)), ]) apply_faces(coords) main()
def rotate_uvs(uv_points, angle): if angle != 0.0: mat = Matrix.Rotation(angle, 2) for uv in uv_points: uv[:] = mat * uv
FBX_DEFORMER_SHAPE_VERSION = 100 FBX_DEFORMER_SHAPECHANNEL_VERSION = 100 FBX_POSE_BIND_VERSION = 100 FBX_DEFORMER_SKIN_VERSION = 101 FBX_DEFORMER_CLUSTER_VERSION = 100 FBX_MATERIAL_VERSION = 102 FBX_TEXTURE_VERSION = 202 FBX_ANIM_KEY_VERSION = 4008 FBX_NAME_CLASS_SEP = b"\x00\x01" FBX_ANIM_PROPSGROUP_NAME = "d" FBX_KTIME = 46186158000 # This is the number of "ktimes" in one second (yep, precision over the nanosecond...) MAT_CONVERT_LIGHT = Matrix.Rotation(math.pi / 2.0, 4, 'X') # Blender is -Z, FBX is -Y. MAT_CONVERT_CAMERA = Matrix.Rotation(math.pi / 2.0, 4, 'Y') # Blender is -Z, FBX is +X. # XXX I can't get this working :( # MAT_CONVERT_BONE = Matrix.Rotation(math.pi / 2.0, 4, 'Z') # Blender is +Y, FBX is -X. MAT_CONVERT_BONE = Matrix() BLENDER_OTHER_OBJECT_TYPES = {'CURVE', 'SURFACE', 'FONT', 'META'} BLENDER_OBJECT_TYPES_MESHLIKE = {'MESH'} | BLENDER_OTHER_OBJECT_TYPES # Lamps. FBX_LIGHT_TYPES = { 'POINT': 0, # Point. 'SUN': 1, # Directional. 'SPOT': 2, # Spot.
def make_armature(self, context): bpy.ops.object.add(type="ARMATURE", enter_editmode=True, location=(0, 0, 0)) armature = context.object armature.name = "skeleton" armature.show_in_front = True bone_dic = {} def bone_add(name, head_pos, tail_pos, parent_bone=None, radius=0.1, roll=0): added_bone = armature.data.edit_bones.new(name) added_bone.head = head_pos added_bone.tail = tail_pos added_bone.head_radius = radius added_bone.tail_radius = radius added_bone.envelope_distance = 0.01 added_bone.roll = radians(roll) if parent_bone is not None: added_bone.parent = parent_bone bone_dic.update({name: added_bone}) return added_bone # bone_type = "leg" or "arm" for roll setting def x_mirror_bones_add( base_name, right_head_pos, right_tail_pos, parent_bones, radius=0.1, bone_type="other", ): right_roll = 0 left_roll = 0 if bone_type == "arm": right_roll = 180 elif bone_type == "leg": right_roll = 90 left_roll = 90 left_bone = bone_add( base_name + "_L", right_head_pos, right_tail_pos, parent_bones[0], radius=radius, roll=left_roll, ) right_bone = bone_add( base_name + "_R", [pos * axis for pos, axis in zip(right_head_pos, (-1, 1, 1))], [pos * axis for pos, axis in zip(right_tail_pos, (-1, 1, 1))], parent_bones[1], radius=radius, roll=right_roll, ) return left_bone, right_bone def x_add(pos_a, add_x): pos = [p_a + _add for p_a, _add in zip(pos_a, [add_x, 0, 0])] return pos def y_add(pos_a, add_y): pos = [p_a + _add for p_a, _add in zip(pos_a, [0, add_y, 0])] return pos def z_add(pos_a, add_z): pos = [p_a + _add for p_a, _add in zip(pos_a, [0, 0, add_z])] return pos root = bone_add("root", (0, 0, 0), (0, 0, 0.3)) head_size = self.tall / self.head_ratio # down side (前は8頭身の時の股上/股下の股下側割合、後ろは4頭身のときの〃を年齢具合で線形補完)(股上高めにすると破綻する) eight_upside_ratio, four_upside_ratio = ( 1 - self.leg_length_ratio, (2.5 / 4) * (1 - self.aging_ratio) + (1 - self.leg_length_ratio) * self.aging_ratio, ) hip_up_down_ratio = ( eight_upside_ratio * (1 - (8 - self.head_ratio) / 4) + four_upside_ratio * (8 - self.head_ratio) / 4 ) # 体幹 # 股間 body_separate = self.tall * (1 - hip_up_down_ratio) # 首の長さ neck_len = head_size * 2 / 3 # 仙骨(骨盤脊柱基部) hips_tall = body_separate + head_size * 3 / 4 # 胸椎・spineの全長 #首の1/3は顎の後ろに隠れてる backbone_len = self.tall - hips_tall - head_size - neck_len / 2 # FIXME 胸椎と脊椎の割合の確認 //脊椎の基部に位置する主となる屈曲点と、胸郭基部に位置するもうひとつの屈曲点byHumanoid Doc chest_len = backbone_len * 12 / 17 # noqa: F841 mesh生成で使ってる spine_len = backbone_len * 5 / 17 # 仙骨基部 hips = bone_add("Hips", (0, 0, body_separate), (0, 0, hips_tall), root, roll=90) # 骨盤基部->胸郭基部 spine = bone_add( "Spine", hips.tail, z_add(hips.tail, spine_len), hips, roll=-90 ) # 胸郭基部->首元 chest = bone_add( "Chest", spine.tail, z_add(hips.tail, backbone_len), spine, roll=-90 ) neck = bone_add( "Neck", (0, 0, self.tall - head_size - neck_len / 2), (0, 0, self.tall - head_size + neck_len / 2), chest, roll=-90, ) # 首の1/2は顎の後ろに隠れてる head = bone_add( "Head", (0, 0, self.tall - head_size + neck_len / 2), (0, 0, self.tall), neck, roll=-90, ) # 目 eye_depth = self.eye_depth eyes = x_mirror_bones_add( "eye", (head_size * self.head_width_ratio / 5, 0, self.tall - head_size / 2), ( head_size * self.head_width_ratio / 5, eye_depth, self.tall - head_size / 2, ), (head, head), ) # 足 leg_width = head_size / 4 * self.leg_width_ratio leg_size = self.leg_size leg_bone_length = (body_separate + head_size * 3 / 8 - self.tall * 0.05) / 2 upside_legs = x_mirror_bones_add( "Upper_Leg", x_add((0, 0, body_separate + head_size * 3 / 8), leg_width), x_add( z_add((0, 0, body_separate + head_size * 3 / 8), -leg_bone_length), leg_width, ), (hips, hips), radius=leg_width * 0.9, bone_type="leg", ) lower_legs = x_mirror_bones_add( "Lower_Leg", upside_legs[0].tail, (leg_width, 0, self.tall * 0.05), upside_legs, radius=leg_width * 0.9, bone_type="leg", ) foots = x_mirror_bones_add( "Foot", lower_legs[0].tail, (leg_width, -leg_size * (2 / 3), 0), lower_legs, radius=leg_width * 0.9, bone_type="leg", ) toes = x_mirror_bones_add( "Toes", foots[0].tail, (leg_width, -leg_size, 0), foots, radius=leg_width * 0.5, bone_type="leg", ) # 肩~指 self.hand_size = head_size * 0.75 * self.hand_ratio shoulder_in_pos = self.shoulder_in_width / 2 shoulder_parent = chest shoulders = x_mirror_bones_add( "shoulder", x_add(shoulder_parent.tail, shoulder_in_pos), x_add(shoulder_parent.tail, shoulder_in_pos + self.shoulder_width), (shoulder_parent, shoulder_parent), radius=self.hand_size * 0.4, bone_type="arm", ) arm_length = ( head_size * (1 * (1 - (self.head_ratio - 6) / 2) + 1.5 * ((self.head_ratio - 6) / 2)) * self.arm_length_ratio ) arms = x_mirror_bones_add( "Arm", shoulders[0].tail, x_add(shoulders[0].tail, arm_length), shoulders, radius=self.hand_size * 0.4, bone_type="arm", ) # グーにするとパーの半分くらいになる、グーのとき手を含む下腕の長さと上腕の長さが概ね一緒、けど手がでかすぎると破綻する forearm_length = max(arm_length - self.hand_size / 2, arm_length * 0.8) forearms = x_mirror_bones_add( "forearm", arms[0].tail, x_add(arms[0].tail, forearm_length), arms, radius=self.hand_size * 0.4, bone_type="arm", ) hands = x_mirror_bones_add( "hand", forearms[0].tail, x_add(forearms[0].tail, self.hand_size / 2), forearms, radius=self.hand_size / 4, bone_type="arm", ) def fingers(finger_name, proximal_pos, finger_len_sum): finger_normalize = 1 / ( self.finger_1_2_ratio * self.finger_2_3_ratio + self.finger_1_2_ratio + 1 ) proximal_finger_len = finger_len_sum * finger_normalize intermediate_finger_len = ( finger_len_sum * finger_normalize * self.finger_1_2_ratio ) distal_finger_len = ( finger_len_sum * finger_normalize * self.finger_1_2_ratio * self.finger_2_3_ratio ) proximal_bones = x_mirror_bones_add( f"{finger_name}_proximal", proximal_pos, x_add(proximal_pos, proximal_finger_len), hands, self.hand_size / 18, bone_type="arm", ) intermediate_bones = x_mirror_bones_add( f"{finger_name}_intermediate", proximal_bones[0].tail, x_add(proximal_bones[0].tail, intermediate_finger_len), proximal_bones, self.hand_size / 18, bone_type="arm", ) distal_bones = x_mirror_bones_add( f"{finger_name}_distal", intermediate_bones[0].tail, x_add(intermediate_bones[0].tail, distal_finger_len), intermediate_bones, self.hand_size / 18, bone_type="arm", ) if self.nail_bone: x_mirror_bones_add( f"{finger_name}_nail", distal_bones[0].tail, x_add(distal_bones[0].tail, distal_finger_len), distal_bones, self.hand_size / 20, bone_type="arm", ) return proximal_bones, intermediate_bones, distal_bones finger_y_offset = -self.hand_size / 16 thumbs = fingers( "finger_thumbs", y_add(hands[0].head, finger_y_offset * 3), self.hand_size / 2, ) mats = [thumbs[0][i].matrix.translation for i in [0, 1]] mats = [Matrix.Translation(mat) for mat in mats] for j in range(3): for n, angle in enumerate([-45, 45]): thumbs[j][n].transform(mats[n].inverted()) thumbs[j][n].transform(Matrix.Rotation(radians(angle), 4, "Z")) thumbs[j][n].transform(mats[n]) thumbs[j][n].roll = [0, radians(180)][n] index_fingers = fingers( "finger_index", y_add(hands[0].tail, finger_y_offset * 3), (self.hand_size / 2) - (1 / 2.3125) * (self.hand_size / 2) / 3, ) middle_fingers = fingers( "finger_middle", y_add(hands[0].tail, finger_y_offset), self.hand_size / 2 ) ring_fingers = fingers( "finger_ring", y_add(hands[0].tail, -finger_y_offset), (self.hand_size / 2) - (1 / 2.3125) * (self.hand_size / 2) / 3, ) little_fingers = fingers( "finger_little", y_add(hands[0].tail, -finger_y_offset * 3), ((self.hand_size / 2) - (1 / 2.3125) * (self.hand_size / 2) / 3) * ((1 / 2.3125) + (1 / 2.3125) * 0.75), ) body_dict = { "hips": hips.name, "spine": spine.name, "chest": chest.name, "neck": neck.name, "head": head.name, } left_right_body_dict = { f"{left_right}{bone_name}": bones[lr].name for bone_name, bones in { "Eye": eyes, "UpperLeg": upside_legs, "LowerLeg": lower_legs, "Foot": foots, "Toes": toes, "Shoulder": shoulders, "UpperArm": arms, "LowerArm": forearms, "Hand": hands, }.items() for lr, left_right in enumerate(["left", "right"]) } # VRM finger like name key fingers_dict = { f"{left_right}{finger_name}{position}": finger[i][lr].name for finger_name, finger in zip( ["Thumb", "Index", "Middle", "Ring", "Little"], [thumbs, index_fingers, middle_fingers, ring_fingers, little_fingers], ) for i, position in enumerate(["Proximal", "Intermediate", "Distal"]) for lr, left_right in enumerate(["left", "right"]) } # VRM bone name : blender bone name bone_name_all_dict = {} bone_name_all_dict.update(body_dict) bone_name_all_dict.update(left_right_body_dict) bone_name_all_dict.update(fingers_dict) context.scene.view_layers.update() bpy.ops.object.mode_set(mode="OBJECT") context.scene.view_layers.update() return armature, bone_name_all_dict
def execute(self, context): scene = context.scene ofst = scene.cursor_location if self.use_cursor else (0.0, 0.0, 0.0) mat_z = Matrix.Rotation(self.rot_z, 4, 'Z') mat_y_180 = Matrix.Rotation(radians(180.0), 4, 'Y') mat_z_180 = Matrix.Rotation(radians(180.0), 4, 'Z') for ob in context.selected_objects: ob_copy = ob.copy() scene.objects.link(ob_copy) ob_copy.parent = None ob.select = False if ob_copy.constraints: for con in ob_copy.constraints: ob_copy.constraints.remove(con) ob_copy.matrix_world = ob.matrix_world # Mirror axes # --------------------------- if self.x: ob_copy.rotation_euler[1] = -ob_copy.rotation_euler[1] ob_copy.rotation_euler[2] = -ob_copy.rotation_euler[2] ob_copy.location[0] = ob_copy.location[0] - ( ob_copy.location[0] - ofst[0]) * 2 if self.y: ob_copy.rotation_euler[0] = -ob_copy.rotation_euler[0] ob_copy.rotation_euler[2] = -ob_copy.rotation_euler[2] ob_copy.location[1] = ob_copy.location[1] - ( ob_copy.location[1] - ofst[1]) * 2 ob_copy.matrix_basis *= mat_z_180 if self.z: ob_copy.rotation_euler[0] = -ob_copy.rotation_euler[0] ob_copy.rotation_euler[1] = -ob_copy.rotation_euler[1] ob_copy.location[2] = ob_copy.location[2] - ( ob_copy.location[2] - ofst[2]) * 2 ob_copy.matrix_basis *= mat_y_180 # Adjust orientation for mirrored objects # --------------------------------------------- if self.rot_z: ob_copy.matrix_basis *= mat_z scene.objects.active = ob_copy return {'FINISHED'}
def execute(self, context): if self.reset_values is True: self.reset_all_values() active_obj = context.active_object bm = bmesh.from_edit_mesh(active_obj.data) bm.verts.ensure_lookup_table() #verts = [v for v in bm.verts if v.select] # get loops loops = loop_t.get_connected_input(bm) loops = loop_t.check_loops(loops, bm) if not loops: self.report({'WARNING'}, "No Loops!") return {'CANCELLED'} first_indexes = [] if isinstance(bm.select_history[0], bmesh.types.BMVert): for element in bm.select_history: first_indexes.append(element.index) elif isinstance(bm.select_history[0], bmesh.types.BMEdge): for element in bm.select_history: el_verts = element.verts first_indexes.append(el_verts[0].index) first_indexes.append(el_verts[1].index) for loop in loops: if loop[1] is True: continue loop_verts = [] for ind in loop[0]: loop_verts.append(bm.verts[ind]) # for the case if we need to reverse it if loop[0][-1] in first_indexes: loop_verts = list(reversed(loop_verts)) # reverse again for the direction if self.reverse_direction is True: loop_verts = list(reversed(loop_verts)) # positions first_vert_pos = active_obj.matrix_world @ loop_verts[0].co last_vert_pos = active_obj.matrix_world @ loop_verts[-1].co loop_centr_orig = first_vert_pos.lerp(last_vert_pos, 0.5) relative_dist = (first_vert_pos - loop_centr_orig).length sidevec = (first_vert_pos - last_vert_pos).normalized() obj_matrix = active_obj.matrix_world obj_matrix_inv = obj_matrix.inverted() if self.direction_vector == 'Custom': rot_dir = Vector((self.rotate_axis[0], self.rotate_axis[1], self.rotate_axis[2])).normalized() elif self.direction_vector == 'MiddleCrossed': middle_nor = loop_verts[int(len(loop_verts) / 2)].normal.copy().normalized() middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix, obj_matrix_inv) rot_dir = middle_nor.cross(sidevec).normalized() # fix only for MiddleCrossed if not self.reverse_direction: rot_dir.negate() elif self.direction_vector == 'Middle': middle_nor = loop_verts[int(len(loop_verts) / 2)].normal.copy().normalized() middle_nor = ut_base.get_normal_world(middle_nor, obj_matrix, obj_matrix_inv) middle_nor = middle_nor.cross(sidevec).normalized() rot_dir = middle_nor.cross(sidevec).normalized() rot_dir.negate() # Auto Direction else: auto_loop_vec = (loop_verts[0].co - loop_verts[1].co).normalized() test_auto_vec = (loop_verts[0].co - loop_verts[-1].co).normalized() auto_loop_vec_angle = auto_loop_vec.angle(test_auto_vec) for loop in loop_verts: if loop != loop_verts[1] and loop != loop_verts[0] and loop != loop_verts[-1]: auto_loop_vec_2 = (loop_verts[0].co - loop.co).normalized() auto_loop_vec_angle_2 = auto_loop_vec_2.angle(test_auto_vec) if auto_loop_vec_angle < auto_loop_vec_angle_2: auto_loop_vec = auto_loop_vec_2 auto_loop_vec_angle = auto_loop_vec_angle_2 rot_dir = (auto_loop_vec).cross(test_auto_vec).normalized() # If Reverse Direction is True if self.reverse_direction: rot_dir.negate() upvec = rot_dir.cross(sidevec).normalized() loop_centr = (self.upvec_offset * upvec * relative_dist) + loop_centr_orig loop_angle = (first_vert_pos - loop_centr).normalized().angle((last_vert_pos - loop_centr).normalized()) if self.upvec_offset > 0: loop_angle = math.radians((360 - math.degrees(loop_angle))) # even spread line_data = None if self.spread_mode == 'Even': world_verts = [active_obj.matrix_world @ vert.co for vert in loop_verts] line_data = [] line_length = 0.0 for i, vec in enumerate(world_verts): if i == 0: line_data.append(0) else: line_length += (vec - world_verts[i-1]).length line_data.append(line_length) # make arc! for i, vert in enumerate(loop_verts): if i != 0 and i != len(loop_verts)-1: if self.spread_mode == 'Normal': rot_angle = loop_angle * (i / (len(loop_verts) - 1)) else: rot_angle = loop_angle * (line_data[i] / line_data[len(loop_verts)-1]) rot_mat = Matrix.Rotation(rot_angle, 3, rot_dir) vert_pos = (rot_mat @ (first_vert_pos - loop_centr)) + loop_centr if self.scale_arc != 0: vert_rel_dist = mathu.geometry.distance_point_to_plane(vert_pos, loop_centr_orig, upvec) vert_rel_dist_max = self.upvec_offset + relative_dist if vert_rel_dist != 0 and vert_rel_dist_max != 0: vert_pos_offset = vert_rel_dist / vert_rel_dist_max vert_pos += (self.scale_arc * upvec * vert_pos_offset * vert_rel_dist_max) # rotate arc if self.rotate_arc_axis != 0: rot_mat_2 = Matrix.Rotation(math.radians(self.rotate_arc_axis), 3, sidevec) vert_pos = (rot_mat_2 @ (vert_pos - loop_centr_orig)) + loop_centr_orig vert.co = active_obj.matrix_world.inverted() @ vert_pos bm.normal_update() bmesh.update_edit_mesh(active_obj.data) return {'FINISHED'}
def get_uv_pos_size(data, image_size, target_grid, origin_xy, size_x, size_y, up_vector, right_vector, verts, vtx_center): pixel_uv_x = 1.0 / image_size[0] pixel_uv_y = 1.0 / image_size[1] uv_unit_x = pixel_uv_x * size_x uv_unit_y = pixel_uv_y * size_y world_units = data.world_pixels world_convert = Vector((size_x / world_units, size_y / world_units)) # Build the translation matrix offset_matrix = Matrix.Translation((target_grid.offset[0] * pixel_uv_x, target_grid.offset[1] * pixel_uv_y, 0)) rotate_matrix = Matrix.Rotation(target_grid.rotate, 4, 'Z') origin_x = target_grid.grid[0] + ( target_grid.padding[0] * 2) + target_grid.margin[1] + target_grid.margin[3] origin_x *= origin_xy[0] origin_x += target_grid.padding[0] origin_x = pixel_uv_x * origin_x origin_y = target_grid.grid[1] + ( target_grid.padding[1] * 2) + target_grid.margin[0] + target_grid.margin[2] origin_y *= origin_xy[1] origin_y += target_grid.padding[1] origin_y = pixel_uv_y * origin_y origin_matrix = Matrix.Translation((origin_x, origin_y, 0)) uv_matrix = offset_matrix @ rotate_matrix @ origin_matrix flip_x = -1 if data.uv_flip_x else 1 flip_y = -1 if data.uv_flip_y else 1 flip_matrix = Matrix.Scale(flip_x, 4, right_vector) @ Matrix.Scale( flip_y, 4, up_vector) pad_offset = target_grid.auto_pad_offset if target_grid.auto_pad is False: pad_offset = 0 pad_scale = Vector( ((size_x - pad_offset) / size_x, (size_y - pad_offset) / size_y)) pad_matrix = Matrix.Scale(pad_scale.x, 4, right_vector) @ Matrix.Scale( pad_scale.y, 4, up_vector) uv_min = Vector((float('inf'), float('inf'))) uv_max = Vector((float('-inf'), float('-inf'))) uv_verts = [] for vert in verts: # Around center vert_pos = vert - vtx_center # Apply flip scaling vert_pos = flip_matrix @ vert_pos # Apply padding vert_pos = pad_matrix @ vert_pos # Get x/y values by using the right/up vectors vert_xy = (right_vector.dot(vert_pos), up_vector.dot(vert_pos), 0) vert_xy = Vector(vert_xy) # Convert to -0.5 to 0.5 space vert_xy.x /= world_convert.x vert_xy.y /= world_convert.y # Offset by half, to move coordinates back into 0-1 range vert_xy += Vector((0.5, 0.5, 0)) # Multiply by the uv unit sizes to get actual UV space vert_xy.x *= uv_unit_x vert_xy.y *= uv_unit_y # Then offset the actual UV space by the translation matrix vert_xy = uv_matrix @ vert_xy # Record min/max for tile alignment step uv_min.x = min(uv_min.x, vert_xy.x) uv_min.y = min(uv_min.y, vert_xy.y) uv_max.x = max(uv_max.x, vert_xy.x) uv_max.y = max(uv_max.y, vert_xy.y) # Save to uv verts uv_verts.append(vert_xy) # In paint mode, do alignment and stretching steps if data.paint_mode == 'PAINT': # Convert vert origin to UV space, for use with paint modify uv_center = Vector((0.5, 0.5, 0.0)) uv_center.x *= uv_unit_x uv_center.y *= uv_unit_y uv_center = uv_matrix @ uv_center uv_verts = get_uv_paint_modify(data, uv_verts, uv_matrix, pad_scale, uv_unit_x, uv_unit_y, uv_min, uv_max, uv_center, Vector((pixel_uv_x, pixel_uv_y))) # One final loop to snap the UVs to the pixel grid # Always snap if not in paint mode, paint mode does # UV snapping in UV map paint modify function do_snap = data.paint_mode != 'PAINT' if do_snap and pixel_uv_x > 0 and pixel_uv_y > 0: for uv_vert in uv_verts: p_x = uv_vert.x / pixel_uv_x p_y = uv_vert.y / pixel_uv_y if math.isnan(p_x) or math.isnan(p_y): return None uv_pixel_x = int(round(p_x)) uv_pixel_y = int(round(p_y)) uv_vert.x = min(uv_max.x, max(uv_pixel_x * pixel_uv_x, uv_min.x)) uv_vert.y = min(uv_max.y, max(uv_pixel_y * pixel_uv_y, uv_min.y)) return uv_verts
def direction_set_cb(value): op = SelectSideOfPlaneGizmoGroup.my_target_operator(context) matrix_rotate = Matrix.Rotation(-value, 3, self.rotate_axis) no = matrix_rotate @ self.widget_dial.matrix_basis.col[1].xyz op.plane_no = no op.execute(context)
def rotatescreen_main(self): if self.rfcontext.actions.pressed('confirm'): return 'main' if self.rfcontext.actions.pressed('cancel'): self.rfcontext.undo_cancel() return 'main' if self.rfcontext.actions.pressed('rotate plane'): self.rfcontext.undo_cancel() return 'rotate plane' # only update cut on timer events and when mouse has moved if not self.rfcontext.actions.timer: return if self.move_prevmouse == self.rfcontext.actions.mouse: return self.move_prevmouse = self.rfcontext.actions.mouse delta = Vec2D(self.rfcontext.actions.mouse - self.rotate_about) rotate = (math.atan2(delta.y, delta.x) - self.rotate_start + math.pi) % (math.pi * 2) raycast,project = self.rfcontext.raycast_sources_Point2D,self.rfcontext.Point_to_Point2D for i_cloop in range(len(self.move_cloops)): cloop = self.move_cloops[i_cloop] verts = self.move_verts[i_cloop] pts = self.move_pts[i_cloop] dists = self.move_dists[i_cloop] origin = self.move_origins[i_cloop] proj_dists = self.move_proj_dists[i_cloop] circumference = self.move_circumferences[i_cloop] origin2D = self.rfcontext.Point_to_Point2D(origin) ray = self.rfcontext.Point_to_Ray(origin) rmat = Matrix.Rotation(rotate, 4, -ray.d) normal = matrix_vector_mult(rmat, cloop.plane.n) plane = Plane(cloop.plane.o, normal) ray = self.rfcontext.Point2D_to_Ray(origin2D) crawl = self.rfcontext.plane_intersection_crawl(ray, plane, walk_to_plane=True) if not crawl: continue crawl_pts = [c for _,c,_ in crawl] connected = crawl[0][0] is not None crawl_pts,connected = self.rfcontext.clip_pointloop(crawl_pts, connected) if not crawl_pts or connected != cloop.connected: continue cl_cut = Contours_Loop(crawl_pts, connected) cl_cut.align_to(cloop) lc = cl_cut.circumference ndists = [cl_cut.offset] + [0.999 * lc * (d/circumference) for d in dists] i,dist = 0,ndists[0] l = len(ndists)-1 if cloop.connected else len(ndists) for c0,c1 in cl_cut.iter_pts(repeat=True): d = (c1-c0).length d = max(0.00000001, d) while dist - d <= 0: # create new vert between c0 and c1 p = c0 + (c1 - c0) * (dist / d) + (cloop.plane.n * proj_dists[i]) p,_,_,_ = self.rfcontext.nearest_sources_Point(p) verts[i].co = p i += 1 if i == l: break dist += ndists[i] dist -= d if i == l: break self.rfcontext.update_verts_faces(verts)
def rotate(co): """ Set the human orientation in reference to the camera orientation. """ ow = co.owner # get the suffix of the human to reference the right objects suffix = ow.name[-4:] if ow.name[-4] == "." else "" keyboard = co.sensors['All_Keys'] scene = blenderapi.scene() human_pos = co.owner pos = scene.objects['POS_EMPTY' + suffix] active_camera = scene.active_camera # if the human is external, do nothing if human_pos.get( 'External_Robot_Tag') or human_pos['disable_keyboard_control']: return if human_pos['move_cameraFP'] and active_camera.name != ('Human_Camera' + suffix): return keylist = keyboard.events k = [] #initiate a list with all currently pressed keys for key in keylist: if key[1] == blenderapi.input_active(): k.append(key[0]) # add all pressed keys to a list - as ASCII CODES pos.worldPosition = ow.worldPosition # Get active camera scene = blenderapi.scene() active_camera = scene.active_camera if ow['Manipulate']: ow.worldOrientation = pos.worldOrientation # lock camera to head in Manipulation Mode else: if FORWARDS in k and not (LEFT in k or RIGHT in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate(pos.worldOrientation, ow) else: applyrotate(human_pos.worldOrientation, ow) elif LEFT in k and not (FORWARDS in k or BACKWARDS in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi / 2, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi / 2, 3, 'Z'), ow) # turn around 90 deg elif RIGHT in k and not (FORWARDS in k or BACKWARDS in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 3 / 2, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi * 3 / 2, 3, 'Z'), ow) # turn around 270 deg elif LEFT in k and FORWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi / 4, 3, 'Z'), ow) # turn around 45 deg elif RIGHT in k and FORWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 7 / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi * 7 / 4, 3, 'Z'), ow) # turn around 315 deg elif BACKWARDS in k and not (LEFT in k or RIGHT in k): if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi, 3, 'Z'), ow) # turn around 180 deg if in game-mode elif LEFT in k and BACKWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 3 / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi / 4, 3, 'Z'), ow) # turn around 135 deg if in game-mode, else turn 45 deg elif RIGHT in k and BACKWARDS in k: if active_camera.name == ("Human_Camera" + suffix): applyrotate( pos.worldOrientation * Matrix.Rotation(math.pi * 5 / 4, 3, 'Z'), ow) else: applyrotate( human_pos.worldOrientation * Matrix.Rotation(math.pi * 7 / 4, 3, 'Z'), ow)
def draw_prepare(self, context): preference = addon.preference() abc = active_tool() if abc.idname != 'BoxCutter': bpy.context.window_manager.gizmo_group_type_unlink_delayed(BC_WGT_GizmoGroup.bl_idname) mpr_z = self.mpr_z dial_z = self.dial_z mpr_x = self.mpr_x dial_x = self.dial_x mpr_y = self.mpr_y dial_y = self.dial_y mid = self.mid if bpy.context.window_manager.bc.running: mpr_z.hide = True dial_z.hide = True mpr_x.hide = True dial_x.hide = True mpr_y.hide = True dial_y.hide = True mid.hide = True else: if preference.surface == 'CURSOR': mpr_z.hide = False dial_z.hide = False mpr_x.hide = False dial_x.hide = False mpr_y.hide = False dial_y.hide = False mid.hide = False else: mpr_z.hide = True dial_z.hide = True mpr_x.hide = True dial_x.hide = True mpr_y.hide = True dial_y.hide = True mid.hide = True orig_loc = context.scene.cursor.location orig_rot = context.scene.cursor.rotation_euler x_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation(radians(90), 4, 'Y') x_dial_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation(radians(-90), 4, 'Y') y_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation(radians(-90), 4, 'X') z_rot_mat = orig_rot.to_matrix().to_4x4() @ Matrix.Rotation(radians(90), 4, 'Z') orig_loc_mat = Matrix.Translation(orig_loc) orig_scale_mat = Matrix.Scale(1, 4, (1, 0, 0)) @ Matrix.Scale(1, 4, (0, 1, 0)) @ Matrix.Scale(1, 4, (0, 0, 1)) z_matrix_world = orig_loc_mat @ z_rot_mat @ orig_scale_mat x_matrix_world = orig_loc_mat @ x_rot_mat @ orig_scale_mat x_dial_matrix_world = orig_loc_mat @ x_dial_rot_mat @ orig_scale_mat y_matrix_world = orig_loc_mat @ y_rot_mat @ orig_scale_mat mid_matrix_world = orig_loc_mat @ orig_rot.to_matrix().to_4x4() @ orig_scale_mat mpr_z.matrix_basis = z_matrix_world.normalized() mpr_x.matrix_basis = x_matrix_world.normalized() mpr_y.matrix_basis = y_matrix_world.normalized() dial_x.matrix_basis = x_dial_matrix_world.normalized() dial_y.matrix_basis = y_matrix_world.normalized() dial_z.matrix_basis = z_matrix_world.normalized() mid.matrix_basis = mid_matrix_world.normalized()
def IsAdmited(self): global faces_to_unfold, mesh_to_unfold, island_verts, island_faces if (self.parentface_relationships == None): root = True print("La face ", self.srcfaceindex, " est la racine de son îlot.") else: root = False print("La face ", self.srcfaceindex, "est l'enfant de la face", self.parentface_relationships.srcfaceindex) fa = mesh_to_unfold.polygons[self.srcfaceindex] if root: def minz(vertice): """ trouve parmi une liste d'indices de vertices, l'indice du vertex dont la coordonée z est la plus basse """ #global mesh_to_unfold lowestve_meshindice = vertice[ 0] #indice par rapport au mesh du vertice le plus bas (ie dont la coordonnée z est la plus petite) lowestve_faindice = 0 #indice par rapport au poly de l'indice par rapport au mesh du vertice le plus bas for i, ve in enumerate(vertice): if mesh_to_unfold.vertices[ve].co[ 2] <= mesh_to_unfold.vertices[ lowestve_meshindice].co[2]: lowestve_faindice = i lowestve_meshindice = ve return (lowestve_meshindice, lowestve_faindice) #choix du premier vertex de référence #celui qui va définir la translation qu'on va appliquer à la face #pour que ce premier vertex de référence se retrouve dans le plan z=0 vertref1_meshindice = minz(fa.vertices)[0] vertref1 = mesh_to_unfold.vertices[ vertref1_meshindice] #de la classe MeshVertex #calcul des vecteurs de départ et d'arrivée de la transtation v1 = Vector(vertref1.co) v2 = Vector((vertref1.co.x, vertref1.co.y, 0)) #v2 projection de v1 sur le plan z=0 vertref1_faindice = minz(fa.vertices)[1] else: #si la face courante a déjà une face parente dans l'îlot, #on recherche l'arête commune entre la face courante et la face parente. parentfa = mesh_to_unfold.polygons[ self.parentface_relationships.srcfaceindex] commonedgevertice_meshindexes = [ ] #les indices absolus des 2 vertices de l'arête commune commonedgevertice_faindexes = [ ] #les indices des 2 vertices de l'arête commune dans la face courante commonedgevertice_parentfaindexes = [ ] #les indices des 2 vertices de l'arête commune dans la face parente for i, vi in enumerate(fa.vertices): for j, vj in enumerate(parentfa.vertices): if (vi == vj): commonedgevertice_meshindexes.append(vi) commonedgevertice_faindexes.append(i) commonedgevertice_parentfaindexes.append(j) vertref1_meshindice = commonedgevertice_meshindexes[ 0] #index absolu du premier vertex de l'arête commune print("index absolu du premier vertex de l'arête commune : ", vertref1_meshindice) v1 = Vector(mesh_to_unfold.vertices[vertref1_meshindice].co) vertref1_parentfaindice = commonedgevertice_parentfaindexes[ 0] #index relatif à la face parente, du premier vertex de l'arête commune v2 = Vector( island_verts[self.parentface_relationships. destfacevertsindexes[vertref1_parentfaindice]][1]) vertref1_faindice = commonedgevertice_faindexes[ 0] #index relatif à la face courante, du premier vertex de l'arête commune #calcul du vecteur de translation vt = v2 - v1 #calcul des vertices translatés de la face courante vectors = [ Vector(mesh_to_unfold.vertices[ve].co) + vt for ve in fa.vertices ] print("vectors après translation: ", vectors) if root: #choix du deuxième vertex de référence, #celui qui va définir la 1ère rotation qu'on va appliquer aux vertice de la face #cette rotation appliquée au deuxième vertex de référence va le ramener dans le plan z=0 possiblechoices = [i for i in range(len(vectors))] possiblechoices.remove( vertref1_faindice ) # on écarte le vertex qui vient d'être translaté dans le plan z=0 for i in possiblechoices: # on écarte également un éventuel vertex qui serait à la verticale du 1er vertex de référence if (vectors[i].x == v2.x) and (vectors[i].y == v2.y): possiblechoices.remove(i) vertref2_faindice = possiblechoices[ 0] # sachant qu'on ne traite que des faces triangulaires, il reste 1 ou 2 choix possible, on prend arbitrairement le premier for i in possiblechoices: # et on le compare au 2eme (s'il y en a un pour choisir celui qui est le plus près du plan z=0 if abs(vectors[i].z) <= abs(vectors[vertref2_faindice].z): vertref2_faindice = i #calcul des vecteurs définissant la 1ere rotation v1 = vectors[vertref2_faindice] - vectors[vertref1_faindice] pvertref2 = Vector( (vectors[vertref2_faindice].x, vectors[vertref2_faindice].y, 0)) #projection de vertref2 sur le plan z=0 v2 = pvertref2 - vectors[vertref1_faindice] else: vertref2_faindice = commonedgevertice_faindexes[ 1] #index relatif à la face courante du deuxième vertex de l'arête commune v1 = vectors[vertref2_faindice] - vectors[vertref1_faindice] print("v1 = ", v1) v2 = Vector(island_verts[ self.parentface_relationships.destfacevertsindexes[ commonedgevertice_parentfaindexes[1]]] [1]) - vectors[vertref1_faindice] print("v2 = ", v2) # transformé du 2eme vertex de l'arête commune #calcul de la matrice de rotation angle12 = v1.angle(v2) print("angle de rotation : ", angle12) #calcul de l'axe de rotation v12 = v1.cross(v2) print("axe de rotation : ", v12) #calcul de la matrice de rotation rot1mat = Matrix.Rotation(angle12, 3, v12) print("matrice de rotation : ", rot1mat) def TransformVectors(vecs, vt, rotmat): """ applique successivement une translation vt, une rotation rotmat, et la translation inverse à tous les vecteurs de la liste vecs """ vecs1 = [v + vt for v in vecs] vecs2 = [rotmat * v for v in vecs1] vecs3 = [v - vt for v in vecs2] return vecs3 #applications de la transformation en 3 étapes #translation vers l'origine de sorte que le 1er vertex de référence soit inchangé par la rotation #rotation qui amène le 2eme vertex de référence dans le plan z=0, puis translation inverse vectors = TransformVectors(vectors, -vectors[vertref1_faindice], rot1mat) print("vectors après rotation 1 == ", vectors) #choix du 3eme vertex de reférence possiblechoices = [i for i in range(len(vectors))] possiblechoices.remove(vertref1_faindice) possiblechoices.remove(vertref2_faindice) vertref3_faindice = possiblechoices[0] print("ordre des indices = ", vertref1_faindice, ", ", vertref2_faindice, ", ", vertref3_faindice) #on veut poser la face sur le plan z=0 de sorte que sa normale pointe vers le haut #or on connait la normale de la face avant transformation #mais pas après les transformations qui viennent d'etre faite #on va donc comparer la normale avec le produit vectoriel des vecteurs v12(vertref1, vertref2) et v13(vertref1, vertref3) #et en fonction du résultat de la comparaison, #c'est soit le produit vectoriel des vecteurs transformés #soit le vecteur inverse qui servira au calcul de l'angle de rotation vertref1_meindice = fa.vertices[ vertref1_faindice] #on retrouve l'indice absolu du vertref1 à partir de son indice par rapport à la face courante vertref2_meindice = fa.vertices[vertref2_faindice] vertref3_meindice = fa.vertices[vertref3_faindice] print("indice absolu de vertref1 : ", vertref1_meindice, "vérification : ", vertref1_meshindice) print("indice absolu de vertref2 : ", vertref2_meindice) print("indice absolu de vertref3 : ", vertref3_meindice) vi1 = Vector( mesh_to_unfold.vertices[vertref1_meindice].co ) #vecteur initial 1 représentant le vertref1 avant transformation vi2 = Vector(mesh_to_unfold.vertices[vertref2_meindice].co) vi3 = Vector(mesh_to_unfold.vertices[vertref3_meindice].co) print("vi1 : ", vi1) print("vi2 : ", vi2) print("vi3 : ", vi3) vi12 = vi2 - vi1 print("vi12 : ", vi12) vi13 = vi3 - vi1 print("vi13 : ", vi13) vi12vi13 = vi12.cross(vi13) print("vi12vi13 : ", vi12vi13) angle_vi12vi13_no = vi12vi13.angle(fa.normal) if (angle_vi12vi13_no < pi): coef = 1 else: coef = -1 #coeficient qui déterminera l'orientation du vecteur #on calcule maintenant le produit vectoriel de ces meme deux vecteurs dans leur position courante après transformation vc12 = vectors[vertref2_faindice] - vectors[vertref1_faindice] print("vc12 : ", vc12) vc13 = vectors[vertref3_faindice] - vectors[vertref1_faindice] print("vc13 : ", vc13) vc12vc13 = vc12.cross(vc13) print("vc12vc13 : ", vc12vc13) vo = vc12vc13 * coef #vecteur "origine" de la rotation print("vo : ", vo) vd = Vector((0, 0, 1)) #vecteur "destination" de la rotation print("vd : ", vd) ar = vo.angle(vd) #angle de la rotation print("angle de la rotation vo vd : ", ar) if (-0.001 < ar < 0.001) or (pi - 0.001 < ar < pi + 0.001): va = vc12 # dans ce cas particulier d'une rotation de 0 ou de 180p else: vod = vo.cross( vd ) #pour déterminer l'orientation du vecteur axe de la rotation angle_vod_vc12 = vod.angle(vc12) if (angle_vod_vc12 < pi): va = vc12 else: va = -vc12 print("axe de la rotation = ", va) print("angle de la rotation = ", ar) rot2mat = Matrix.Rotation(ar, 3, va) vectors = TransformVectors(vectors, -vectors[vertref1_faindice], rot2mat) print("vectors après rotation 2: ", vectors) if root: #puisque on commence un ilot, les 3 vertices de la face en cours sont nouveaux dans l'ilot #pour chaque vertex on renseigne son index dans le mesh source et ses coordonnées dans l'ilot en création. self.newverts = [[ fa.vertices[i], [vectors[i].x, vectors[i].y, vectors[i].z] ] for i in range(len(fa.vertices))] #définition de la 1ere face de l'ilot self.destfacevertsindexes = [i for i in range(len(fa.vertices))] else: for i, vi in enumerate([ve for ve in fa.vertices]): if vi in commonedgevertice_meshindexes: # si le vertice appartiend à l'arête commune, on ne l'ajoute pas à newverts parce qu'il y est déjà suite au traitement de la face parente refindex = commonedgevertice_meshindexes.index( vi ) # index de ce vertice dans la liste des vertices de l'arête commune (0 ou 1) parentfavertindex = commonedgevertice_parentfaindexes[ refindex] #index de ce même vertice dans la liste des vertice de la face parente self.destfacevertsindexes.append( self.parentface_relationships. destfacevertsindexes[parentfavertindex] ) # l'index du vertice correspondant dans l'ilot else: self.destfacevertsindexes.append( len(island_verts) ) # creation d'un nouvel index pour ajouter ce vertex à l'ilot en cours self.newverts.append( [vi, [vectors[i].x, vectors[i].y, vectors[i].z]] ) #on ajoute à la liste des nouveaux vertice, les vertices qui n'appartiennent pas à l'arête commune return True # pour l'instant on admet toutes les faces sans vérifier le chevauchement.
def _export(self, filepath, objs): _rotate_axis = lambda angle, axis: Matrix.Rotation( math.radians(angle), 4, axis) _debug_print_quaternion = lambda q: [(math.degrees(x) if (x <= -1.0e-05) or (x >= 1.0e-05) else 0.0) for x in q.to_euler()] _debug_print_rot_matrix = lambda m: _debug_print_quaternion( m.decompose()[1]) def _get_axis_root_matrix(obj): return _rotate_axis(-90.0, 'X').inverted() def _get_axis_matrix(obj, rot_mat=Matrix()): r1 = _rotate_axis(-90.0, 'X') r2 = _rotate_axis(180.0, 'Y') return ((r2 * (r1 * ((obj.matrix_local * rot_mat) * r2)))) def _rotate_camera(obj, is_root): _rotate_object(obj, is_root, _rotate_axis(90.0, 'Y').inverted()) def _rotate_empty(obj, is_root): _rotate_object(obj, is_root, _rotate_axis(90.0, 'X')) def _scale_mesh(obj, is_root): _, _, scale = obj.matrix_local.decompose() mesh = obj.data new_bmesh = bmesh.new() new_bmesh.from_mesh(mesh) unique_verts = sorted(list( set([vert for face in new_bmesh.faces for vert in face.verts])), key=lambda vert: vert.index) bmesh.ops.scale(new_bmesh, vec=scale, verts=unique_verts) new_bmesh.to_mesh(mesh) mesh.update() new_bmesh.free() obj.scale = (1.0, 1.0, 1.0) bpy.context.scene.update() def _rotate_mesh(obj, is_root, apply_rotation): _, rotation, _ = obj.matrix_local.decompose() mesh_rot_matrix = (_rotate_axis(180.0, 'Z') * _rotate_axis(90.0, 'X') * (rotation.to_matrix().to_4x4() if apply_rotation else Matrix())) mesh = obj.data new_bmesh = bmesh.new() new_bmesh.from_mesh(mesh) unique_verts = sorted(list( set([vert for face in new_bmesh.faces for vert in face.verts])), key=lambda vert: vert.index) bmesh.ops.rotate(new_bmesh, cent=(0.0, 0.0, 0.0), matrix=mesh_rot_matrix, verts=unique_verts) new_bmesh.to_mesh(mesh) mesh.update() new_bmesh.free() bpy.context.scene.update() if apply_rotation: _rotate_root_object(obj, is_root) else: _rotate_object(obj, is_root, _rotate_axis(90.0, 'X')) def _rotate_root_object(obj, is_root): matrix = _get_axis_root_matrix(obj) if is_root else Matrix() _, rotation, _ = matrix.decompose() obj.rotation_euler = rotation.to_euler() def _rotate_object(obj, is_root, rot_mat=Matrix()): matrix = ((_get_axis_root_matrix(obj) if is_root else Matrix()) * _get_axis_matrix(obj, rot_mat)) _, rotation, _ = matrix.decompose() obj.rotation_euler = rotation.to_euler() def _translate_object(obj, is_root): local_loc = obj.matrix_local.to_translation() world_loc = obj.matrix_world.to_translation() diff_loc = world_loc - local_loc if is_root: obj.location = -diff_loc + (_rotate_axis(180.0, 'Z') * (diff_loc + local_loc)) else: obj.location = (_rotate_axis(180.0, 'Z') * _rotate_axis(90.0, 'X')) * local_loc def _parent_in_list(obj, objs): """Return True if obj has a parent in objs. False otherwise.""" def _parent_in_list(obj, first_obj, objs): if obj is None: return [] return [obj if (obj in objs) and (obj != first_obj) else None ] + _parent_in_list(obj.parent, first_obj, objs) return any(_parent_in_list(obj, obj, objs)) with helper_utils.DuplicateScene() as override_context: objs = [obj for obj in bpy.context.selected_objects] for obj in objs: is_root = not _parent_in_list(obj, objs) if obj.type == 'MESH': _translate_object(obj, is_root) _scale_mesh(obj, is_root) _rotate_mesh(obj, is_root, obj.apply_rotation) elif obj.type == 'CAMERA': _translate_object(obj, is_root) _rotate_camera(obj, is_root) elif obj.type == 'EMPTY': _translate_object(obj, is_root) _rotate_empty(obj, is_root) # Update override context override_context['selected_objects'] = objs # Finally, export bpy.ops.export_scene.fbx( override_context, filepath=self.filepath, check_existing=False, axis_forward='Y', axis_up='Z', use_selection=True, global_scale=1.0, apply_unit_scale=False, bake_anim=False, object_types={'EMPTY', 'CAMERA', 'LAMP', 'MESH'}, use_custom_props=True)
class ReconstructionBundle(ReconstructionBase): """The actual SfM reconstruction imported form a Bundle file. https://www.cs.cornell.edu/~snavely/bundler/ File format: https://www.cs.cornell.edu/~snavely/bundler/bundler-v0.4-manual.html#S6 """ SUPPORTED_EXTENSION = ".rd.out" # coordinates conversion matrix, from canonical right handed (Y-up) to blender's (Z-up) BUNDLE_TO_BLENDER = Matrix.Rotation(-pi / 2, 4, 'X') LIST_FILE_REGEX = re.compile(r'(.*?[0-9]+\.[a-zA-Z]+)(\s|$)') ################################################################################################ # Constructor and destructor # # ============================================================================================== def __init__(self, name: str, bundle_file_path: str): """Create a reconstruction object and load the reconstruction from the Bundle file. Arguments: name {str} -- reconstruction name file_path {str} -- path to the bundle.rd.out reconstruction file """ super().__init__(name) # self.file_path = os.path.abspath(bundle_file_path) self.file = open(self.file_path, "r") self.file.seek(0, 2) self.file_end = self.file.tell() # get char count self.file.seek(0) # self._load_reconstruction() # ============================================================================================== def __del__(self): """Ensure file release on object deletion.""" if hasattr(self, "file"): self.close() super().__del__() # ============================================================================================== def close(self) -> None: """Close the Bundle file if necessary. This is called by '__del__' to enure proper resource release. """ if self.file: self.file.close() ################################################################################################ # Helpers # # ============================================================================================== def _load_reconstruction(self) -> None: """Read the content of an Bundle reconstruction file and populate data structures. File format: https://www.cs.cornell.edu/~snavely/bundler/bundler-v0.4-manual.html Raises: ValueError: if something goes wrong reading the Bundle file (unexpected header, wrong cameras or points). Other errors are raised if there are errors in the Bundle file structure or file access. """ cam_name_list = self._load_camera_list() # read file header header = self.file.readline().strip() if header != "# Bundle file v0.3": msg = "Unexpected header '{}' (expected '# Bundle file v0.3')".format( header) logger.error(msg) raise ValueError(msg) # # get quantity of available cameras and points in file l = self._read_line() camera_count = int(l[0]) point_count = int(l[1]) assert camera_count == len( cam_name_list) # the amount of cameras must match across files # model = ReconModel(self.name + '_0') model.number_of_cameras = camera_count # # load reconstructed camera poses for i in range(0, camera_count): # # <f> <k1> <k2> [focal length follwed by 2 radial distortion coefficients] f_k1_k2 = self._read_line() # # <R> [3x3 matrix, camera rotation] r = Matrix.Identity(3) for row in range(3): r[row] = list(map(float, self._read_line())) # # <t> [XYZ vector, camera translation] t = Vector(list(map(float, self._read_line()))) # # build world matrix w2c = r.to_4x4() w2c[0][3] = t.x w2c[1][3] = t.y w2c[2][3] = t.z c2w = w2c.inverted() c2w = ReconstructionBundle.BUNDLE_TO_BLENDER @ c2w # model.add_camera( ReconCamera(cam_name_list[i], float(f_k1_k2[0]), c2w, list(map(float, f_k1_k2[1:2])))) # # load reconstructed point cloud pc = PointCloud(point_count) for i in range(0, point_count): # # <position> [a 3-vector describing the 3D position of the point] p = ReconstructionBundle.BUNDLE_TO_BLENDER @ Vector( list(map(float, self._read_line()))) # # <color> [a 3-vector describing the RGB color of the point] c = Color(list(map(int, self._read_line()))) / 255. # # <view list> [a list of views the point is visible in] self._read_line() # currently unused # pc.add_point(p, c) # model.point_cloud = pc self.add_model(model) # # close, everything has been read self.close() # ============================================================================================== def _load_camera_list(self) -> List[str]: """Load the camera list file names in the same order of appearance in the bundle.rd.out file. Raises: FileNotFoundError: if bundle.rd.out.list.txt OR list.txt are missing Returns: List[str] -- list of file names """ files_dir = os.path.dirname(self.file_path) f_names = [] # # handle VisualSfM special case, images are renamed during export for PMVS. # in this case original images names (frame numbers) can be found in # "cameras_v2.txt" instead of "bundle.rd.out.list.txt" camera_list_filename = os.path.join(files_dir, "cameras_v2.txt") if os.path.isfile( camera_list_filename ): # if file exists assume is a VisualSfM reconstruction with open(camera_list_filename, "r") as f: # # check header header = f.readline().strip() if not header == "# Camera parameter file.": raise ValueError("Wrong file format (" + header + ") for VisualSfM \"cameras_v2.txt\"") logger.info("Found VisualSfM cameras v2 file") for _ in range(15): f.readline() # discard header # # get camera quantity cam_count = int(f.readline().strip()) # # load frame numbers form file for _ in range(cam_count): while f.readline() != "\n": # discard unused data pass f.readline() # discard first line (new filename) # f_names.append(f.readline()) # get original image path # # # back to normal case else: cameraListFileName = os.path.join(files_dir, "bundle.rd.out.list.txt") if not os.path.isfile(cameraListFileName): cameraListFileName = os.path.join(files_dir, "list.txt") if not os.path.isfile(cameraListFileName): raise FileNotFoundError( "Could not find bundle.rd.out.list.txt OR list.txt") # with open(cameraListFileName, "r") as f: for _, line in enumerate(iter(f.readline, '')): filename = ReconstructionBundle.LIST_FILE_REGEX.match(line) filename = filename.group(1) f_names.append(filename) # return f_names # ============================================================================================== def _read_line(self) -> List[str]: """Read a line from the bundle file. Automatically discard empty lines and comments. Returns: List[str] -- the list of string tokens in the line Raises: EOFError: if reading lines after end of file """ while True: line = self.file.readline().strip() if line == '' and self.file.tell() >= self.file_end: raise EOFError("Reading after file end!") elif line != '' and not line.startswith( '#'): # skip empty lines and comments return line.split()
def update_mesh_to_curve(self, bm, deform_type, obj): lw_tool_vec = self.lw_tool.end_point.position - self.lw_tool.start_point.position lw_tool_dir = (self.lw_tool.end_point.position - self.lw_tool.start_point.position).normalized() if deform_type == 'Deform': # DEFORM TYPE ONLY deform_lines, points_indexes = get_bezier_area_data(self.curve_tool) line_len = deform_lines[-1][1] zero_vec = Vector((0.0, 0.0, 0.0)) # get bezier dirs b_dirs = [] for b_point_data in deform_lines: # bezier point direction b_point_dir = None index_point = deform_lines.index(b_point_data) if index_point < len(deform_lines) - 1: b_point_dir = deform_lines[index_point - 1][3] else: b_point_dir = b_point_data[3] #check_angle = b_point_dir.angle(self.tool_up_vec) ### upVec approach by me ## calculate using cross vec #b_point_up = b_point_dir.cross(self.tool_up_vec).normalized() # upVec approach by mano-wii version 1 pzv = self.tool_up_vec.project( b_point_dir) # here we project the direction to get upVec b_point_up = (self.tool_up_vec - pzv).normalized() ## upVec approach by mano-wii version 2 #dot = self.tool_up_vec.dot(b_point_dir) #pzv = dot * b_point_dir # here we dot the direction to get upVec #b_point_up = (self.tool_up_vec - pzv).normalized() # invert direction feature if self.invert_deform_upvec is True: b_point_up.negate() # fix directions according to previous upvec if b_dirs: if b_point_up.length == 0.0: b_point_up = b_dirs[-1][1].copy() elif b_dirs[-1][1].angle(b_point_up) > math.radians(90.0): # here we invert upVec if it was incorrect according to previous one b_point_up.negate() b_point_side = b_point_dir.cross(b_point_up).normalized() b_dirs.append([b_point_dir, b_point_up, b_point_side]) # find the best point for every vert for vert_id in self.work_verts.keys(): vert = bm.verts[vert_id] vert_data = self.work_verts[vert_id] for i, point in enumerate(self.curve_tool.curve_points): if i > 0: point_len = deform_lines[points_indexes.get( point.point_id)][1] / line_len vert_len = vert_data[1] / lw_tool_vec.length if point_len >= vert_len: # max is for the first point first_index = 0 if i > 0: first_index = points_indexes.get( self.curve_tool.curve_points[ self.curve_tool.curve_points.index(point) - 1].point_id) # get the best point #b_point_up = None #b_point_side = None best_pos = None for b_point_data in deform_lines[first_index:]: j = deform_lines.index(b_point_data) #print(j, jj) if j > 0: b_point_len = b_point_data[1] / line_len if b_point_len >= vert_len: b_point_dirs = b_dirs[deform_lines.index( b_point_data)] # best position if b_point_len == vert_len: best_pos = b_point_data[0] else: previous_pos_len = deform_lines[ j - 1][1] / line_len interp_pos = ( vert_len - previous_pos_len) * line_len # fix for interpolation between lines if j > 1 and j < len(deform_lines) - 1: prev_b_point_dirs = b_dirs[ deform_lines.index( b_point_data)] b_point_dirs_temp = b_dirs[ deform_lines.index( b_point_data) + 1] b_point_dirs = b_point_dirs.copy() new_side_vec = prev_b_point_dirs[ 1].lerp( b_point_dirs_temp[1], interp_pos).normalized() b_point_dirs[1] = new_side_vec new_side_vec = prev_b_point_dirs[ 2].lerp( b_point_dirs_temp[2], interp_pos).normalized() b_point_dirs[2] = new_side_vec best_pos = deform_lines[j - 1][0] + ( (interp_pos) * b_point_dirs[0]) break vert.co = obj.matrix_world.inverted() * ( best_pos + (b_point_dirs[1] * vert_data[3]) - (b_point_dirs[2] * vert_data[2])) break else: # ALL OTHER TYPES # get points dists points_dists = [] for point in self.curve_tool.curve_points: bezier_dists = [] p_dist = mathu.geometry.distance_point_to_plane( point.position, self.lw_tool.start_point.position, lw_tool_dir) # add bezer dists if self.curve_tool.curve_points.index(point) > 0: for b_point in self.curve_tool.display_bezier[point.point_id]: b_p_dist = mathu.geometry.distance_point_to_plane( b_point, self.lw_tool.start_point.position, lw_tool_dir) b_p_side_dist = mathu.geometry.distance_point_to_plane( b_point, self.lw_tool.start_point.position, self.tool_side_vec) bezier_dists.append((b_p_dist, b_p_side_dist, b_point)) points_dists.append((p_dist, bezier_dists)) # find the best point for every vert for vert_id in self.work_verts.keys(): vert = bm.verts[vert_id] vert_data = self.work_verts[vert_id] deform_dir = None if deform_type == 'Scale': deform_dir = (vert_data[0] - (self.lw_tool.start_point.position + (lw_tool_dir * vert_data[1]))).normalized() else: deform_dir = self.tool_side_vec for i, point_data in enumerate(points_dists): if point_data[0] >= vert_data[1]: best_bezier_len = None vert_front_pos = self.lw_tool.start_point.position + ( lw_tool_dir * vert_data[1]) # loop bezier points according to vert for j, b_point in enumerate(point_data[1]): if not best_bezier_len: best_bezier_len = b_point[1] elif b_point[0] >= vert_data[1]: bp_nor = (b_point[2] - point_data[1][j - 1][2]).normalized() bp_nor = bp_nor.cross( self.tool_up_vec).normalized() final_pos = mathu.geometry.intersect_line_plane( vert_front_pos - (self.tool_side_vec * 1000.0), vert_front_pos + (self.tool_side_vec * 1000.0), b_point[2], bp_nor) best_bezier_len = ( final_pos - vert_front_pos).length # the length! if deform_type in {'Shear', 'Twist'}: if (final_pos - vert_front_pos).normalized( ).angle(self.tool_side_vec) > math.radians(90): best_bezier_len = -best_bezier_len break #final_dist = best_bezier_len # multiplier for the vert dir_multilpier = None if deform_type == 'Stretch': dir_multilpier = ( vert_data[2] * (best_bezier_len / self.tool_side_vec_len)) - vert_data[2] elif deform_type in {'Shear', 'Twist'}: dir_multilpier = best_bezier_len - self.tool_side_vec_len else: vert_dist_scale = (vert_data[0] - vert_front_pos).length dir_multilpier = abs( vert_dist_scale * (best_bezier_len / self.tool_side_vec_len)) - vert_dist_scale # modify vert position if deform_type == 'Twist': twist_angle = dir_multilpier * math.radians(90) rot_mat = Matrix.Rotation(twist_angle, 3, lw_tool_dir) vert.co = obj.matrix_world.inverted() * ( rot_mat * (vert_data[0] - self.lw_tool.start_point.position) + self.lw_tool.start_point.position) else: vert.co = obj.matrix_world.inverted() * ( vert_data[0] + (deform_dir * dir_multilpier)) break
def fillets(list_0, startv, vertlist, face, adj, n, out, flip, radius): try: dict_0 = get_adj_v_(list_0) list_1 = [[dict_0[i][0], i, dict_0[i][1]] for i in dict_0 if (len(dict_0[i]) == 2)][0] list_3 = [] for elem in list_1: list_3.append(bm.verts[elem]) list_2 = [] p_ = list_3[1] p = (list_3[1].co).copy() p1 = (list_3[0].co).copy() p2 = (list_3[2].co).copy() vec1 = p - p1 vec2 = p - p2 ang = vec1.angle(vec2, any) check_angle = round(degrees(ang)) if check_angle == 180 or check_angle == 0.0: return False else: f_buf.check = True opp = adj if radius is False: h = adj * (1 / cos(ang * 0.5)) adj_ = adj elif radius is True: h = opp / sin(ang * 0.5) adj_ = opp / tan(ang * 0.5) p3 = p - (vec1.normalized() * adj_) p4 = p - (vec2.normalized() * adj_) rp = p - ((p - ((p3 + p4) * 0.5)).normalized() * h) vec3 = rp - p3 vec4 = rp - p4 axis = vec1.cross(vec2) if out is False: if flip is False: rot_ang = vec3.angle(vec4) elif flip is True: rot_ang = vec1.angle(vec2) elif out is True: rot_ang = (2 * pi) - vec1.angle(vec2) for j in range(n + 1): new_angle = rot_ang * j / n mtrx = Matrix.Rotation(new_angle, 3, axis) if out is False: if flip is False: tmp = p4 - rp tmp1 = mtrx @ tmp tmp2 = tmp1 + rp elif flip is True: p3 = p - (vec1.normalized() * opp) tmp = p3 - p tmp1 = mtrx @ tmp tmp2 = tmp1 + p elif out is True: p4 = p - (vec2.normalized() * opp) tmp = p4 - p tmp1 = mtrx @ tmp tmp2 = tmp1 + p v = bm.verts.new(tmp2) list_2.append(v) if flip is True: list_3[1:2] = list_2 else: list_2.reverse() list_3[1:2] = list_2 list_clear_(list_2) n1 = len(list_3) for t in range(n1 - 1): bm.edges.new([list_3[t], list_3[(t + 1) % n1]]) v = bm.verts.new(p) bm.edges.new([v, p_]) bm.edges.ensure_lookup_table() if face is not None: for l in face.loops: if l.vert == list_3[0]: startl = l break vertlist2 = [] if startl.link_loop_next.vert == startv: l = startl.link_loop_prev while len(vertlist) > 0: vertlist2.insert(0, l.vert) vertlist.pop(vertlist.index(l.vert)) l = l.link_loop_prev else: l = startl.link_loop_next while len(vertlist) > 0: vertlist2.insert(0, l.vert) vertlist.pop(vertlist.index(l.vert)) l = l.link_loop_next for v in list_3: vertlist2.append(v) bm.faces.new(vertlist2) if startv.is_valid: bm.verts.remove(startv) else: print("\n[Function fillets Error]\n" "Starting vertex (startv var) couldn't be removed\n") return False bm.verts.ensure_lookup_table() bm.edges.ensure_lookup_table() bm.faces.ensure_lookup_table() list_3[1].select = 1 list_3[-2].select = 1 bm.edges.get([list_3[0], list_3[1]]).select = 1 bm.edges.get([list_3[-1], list_3[-2]]).select = 1 bm.verts.index_update() bm.edges.index_update() bm.faces.index_update() me.update(calc_edges=True, calc_loop_triangles=True) bmesh.ops.recalc_face_normals(bm, faces=bm.faces) except Exception as e: print("\n[Function fillets Error]\n{}\n".format(e)) return False
def execute(self, context): obj = bpy.context.edit_object bm = bmesh.from_edit_mesh(obj.data) uv = bm.loops.layers.uv[int(self.p_dest_uv)] grid_scale = context.space_data.overlay.grid_scale selected_faces = [face for face in bm.faces if face.select and not face.hide] active_face = bm.select_history.active size = bpy.data.images[int(self.p_checker)].size; size = Vector((1.0 / size[0], 1.0 / size[1])) negate_first = Vector((-1, 1)) # auto pick axis axis = self.p_axis if axis == 'CLOSEST': if type(active_face) is not BMFace: self.report({'WARNING'}, "Operation cancelled: No active face") return {'CANCELLED'} axis = self.closest_axis(active_face.normal) # projection if axis == 'XPOS': for face in selected_faces: for loop in face.loops: loop[uv].uv.xy = loop.vert.co.yz * size / grid_scale elif axis == 'XNEG': for face in selected_faces: for loop in face.loops: loop[uv].uv.xy = loop.vert.co.yz * negate_first * size / grid_scale elif axis == 'YPOS': for face in selected_faces: for loop in face.loops: loop[uv].uv.xy = loop.vert.co.xz * negate_first * size / grid_scale elif axis == 'YNEG': for face in selected_faces: for loop in face.loops: loop[uv].uv.xy = loop.vert.co.xz * size / grid_scale elif axis == 'ZPOS': for face in selected_faces: for loop in face.loops: loop[uv].uv.xy = loop.vert.co.xy * size / grid_scale elif axis == 'ZNEG': for face in selected_faces: for loop in face.loops: loop[uv].uv.xy = loop.vert.co.xy * negate_first * size / grid_scale elif axis == 'ACTIVE': # a very different case if type(active_face) is not BMFace: self.report({'WARNING'}, "Operation cancelled: No active face") return {'CANCELLED'} # grab some edge as a temporary axis, then rotate the basis the same amount in 3d it has to be rotated in the uv # to align with pixel edges edge:bmesh.types.BMEdge = None for e in active_face.edges: if e in active_face.edges and e.calc_length() > 0: edge = e break assert edge is not None ends = [l for l in active_face.loops if edge in l.vert.link_edges] dir_uv = Vector((ends[1][uv].uv.x - ends[0][uv].uv.x, ends[1][uv].uv.y - ends[0][uv].uv.y)) to_x = dir_uv.angle((1.0, 0.0)) z_3d = active_face.normal dir_3d = Vector((ends[1].vert.co.x - ends[0].vert.co.x, ends[1].vert.co.y - ends[0].vert.co.y, ends[1].vert.co.z - ends[0].vert.co.z)) dir_3d.normalize() x_3d = Matrix.Rotation(to_x, 4, z_3d) @ dir_3d y_3d = z_3d.cross(x_3d) print(x_3d, y_3d, z_3d) # basis = Matrix(x_3d, y_3d, z_3d).to_4x4() # pin if asked if self.p_pin: for face in selected_faces: for loop in face.loops: loop[uv].pin_uv = True # apply changes bmesh.update_edit_mesh(obj.data) return {'FINISHED'}
def getNeckData(self): bones = bpy.evaAnimationManager.deformObj.pose.bones rneck = bones['DEF-neck'].matrix * Matrix.Rotation(-pi / 2, 4, 'X') q = rneck.to_quaternion() return {'x': q.x, 'y': q.y, 'z': q.z, 'w': q.w}
def process(self): inputs = self.inputs outputs = self.outputs if not (inputs['Vertices'].is_linked and inputs['Polygons'].is_linked): return if not any(socket.is_linked for socket in outputs): return vector_in = self.scale_socket_type vertices_s = inputs['Vertices'].sv_get() edges_s = inputs['Edges'].sv_get(default=[[]]) faces_s = inputs['Polygons'].sv_get(default=[[]]) masks_s = inputs['Mask'].sv_get(default=[[1]]) heights_s = inputs['Height'].sv_get() scales_s = inputs['Scale'].sv_get() linked_extruded_polygons = outputs['ExtrudedPolys'].is_linked linked_other_polygons = outputs['OtherPolys'].is_linked result_vertices = [] result_edges = [] result_faces = [] result_extruded_faces = [] result_other_faces = [] meshes = match_long_repeat( [vertices_s, edges_s, faces_s, masks_s, heights_s, scales_s]) for vertices, edges, faces, masks, heights, scales in zip(*meshes): new_extruded_faces = [] new_extruded_faces_append = new_extruded_faces.append fullList(heights, len(faces)) fullList(scales, len(faces)) fullList(masks, len(faces)) bm = bmesh_from_pydata(vertices, edges, faces) extruded_faces = bmesh.ops.extrude_discrete_faces( bm, faces=bm.faces)['faces'] for face, mask, height, scale in zip(extruded_faces, masks, heights, scales): if not mask: continue vec = scale if vector_in else (scale, scale, scale) # preparing matrix normal = face.normal if normal[0] == 0 and normal[1] == 0: m_r = Matrix() if normal[2] >= 0 else Matrix.Rotation( pi, 4, 'X') else: z_axis = normal x_axis = (Vector( (z_axis[1] * -1, z_axis[0], 0))).normalized() y_axis = (z_axis.cross(x_axis)).normalized() m_r = Matrix(list([*zip(x_axis[:], y_axis[:], z_axis[:]) ])).to_4x4() dr = face.normal * height center = face.calc_center_median() translation = Matrix.Translation(center) m = (translation * m_r).inverted() # inset, scale and push operations bmesh.ops.scale(bm, vec=vec, space=m, verts=face.verts) bmesh.ops.translate(bm, verts=face.verts, vec=dr) if linked_extruded_polygons or linked_other_polygons: new_extruded_faces_append([v.index for v in face.verts]) new_vertices, new_edges, new_faces = pydata_from_bmesh(bm) bm.free() new_other_faces = [ f for f in new_faces if f not in new_extruded_faces ] if linked_other_polygons else [] result_vertices.append(new_vertices) result_edges.append(new_edges) result_faces.append(new_faces) result_extruded_faces.append(new_extruded_faces) result_other_faces.append(new_other_faces) outputs['Vertices'].sv_set(result_vertices) outputs['Edges'].sv_set(result_edges) outputs['Polygons'].sv_set(result_faces) outputs['ExtrudedPolys'].sv_set(result_extruded_faces) outputs['OtherPolys'].sv_set(result_other_faces)
flip_x_z = { "L": Matrix(((1, 0, 0, 0), (0, 0, 0, 1), (0, 0, -1, 0), (0, -1, 0, 0))), "R": Matrix(((1, 0, 0, 0), (0, 0, 0, -1), (0, 0, 1, 0), (0, 1, 0, 0))), } def qrotation(mat): def rot(v): return (v[3], v[0], v[1], v[2]) return Matrix((rot(mat[3]), rot(mat[0]), rot(mat[1]), rot(mat[2]))) shoulder_angle = 1.3960005939006805 shoulder_rot = { "L": qrotation(Matrix.Rotation(shoulder_angle, 4, (0, 1, 0))), "R": qrotation(Matrix.Rotation(-shoulder_angle, 4, (0, 1, 0))), } bone_map = { "root": ("root", m2), "pelvis": ("torso", qrotation(Matrix.Rotation(1.4466689567595232, 4, (1, 0, 0)))), "spine01": ("spine_fk.001", m1), "spine02": ("spine_fk.002", m1), "spine03": ("spine_fk.003", m1), "neck": ("neck", m1), "head": ("head", m1), } for side in ["L", "R"]:
def deform_obj(obj, context, self): offset_rotation = 0.2 offset_axis = 5.0 bend_scale = 0.7 # get vertices verts = None if obj.mode == 'EDIT': # this works only in edit mode, bm = bmesh.from_edit_mesh(obj.data) verts = [v for v in bm.verts if v.select] if len(verts) == 0: verts = [v for v in bm.verts if v.hide is False] else: # this works only in object mode, verts = [v for v in obj.data.vertices if v.select] if len(verts) == 0: verts = [v for v in obj.data.vertices if v.hide is False] # TODO Move it into utilities method. As Extrude class has the same # min/max. if verts: if obj.mode == 'EDIT': bm.verts.ensure_lookup_table() x_min = verts[0].co.x x_max = verts[0].co.x y_min = verts[0].co.y y_max = verts[0].co.y z_min = verts[0].co.z z_max = verts[0].co.z for vert in verts: if vert.co.x > x_max: x_max = vert.co.x if vert.co.x < x_min: x_min = vert.co.x if vert.co.y > y_max: y_max = vert.co.y if vert.co.y < y_min: y_min = vert.co.y if vert.co.z > z_max: z_max = vert.co.z if vert.co.z < z_min: z_min = vert.co.z x_orig = ((x_max - x_min) / 2.0) + x_min y_orig = ((y_max - y_min) / 2.0) + y_min z_orig = z_min if self.deform_axis == 'Z': y_orig = y_min z_orig = ((z_max - z_min) / 2.0) + z_min rot_origin = Vector((x_orig, y_orig, z_orig)) visual_max = z_max - z_min if self.deform_axis == 'Z': visual_max = y_max - y_min for vert in verts: print(vert.hide) vec = vert.co.copy() visual_up_pos = None if self.deform_axis != 'Z': visual_up_pos = vec.z - z_min else: visual_up_pos = vec.y - y_min # TAPER CODE # scale the vert if self.taper_value != 0: taper_value = ((self.taper_value) * (visual_up_pos / visual_max)) if self.deform_axis != 'Z': vert.co.xy -= (vert.co.xy - rot_origin.xy) * taper_value else: vert.co.xz -= (vert.co.xz - rot_origin.xz) * taper_value # TWIST CODE # rotate the vert if self.twist_angle != 0: twist_angle = self.twist_angle * (visual_up_pos / visual_max) # if self.deform_axis == 'X': # rot_angle = -rot_angle rot_mat = None if self.deform_axis != 'Z': rot_mat = Matrix.Rotation(twist_angle, 3, 'Z') else: rot_mat = Matrix.Rotation(twist_angle, 3, 'Y') vert.co = rot_mat * (vert.co - rot_origin) + rot_origin # BEND CODE beta = math.radians(self.bend_angle * (visual_up_pos / visual_max)) if beta != 0: final_offset = visual_up_pos * self.offset_rotation if beta < 0: final_offset = -final_offset move_to_rotate = ( (visual_up_pos / beta) + final_offset) * self.bend_scale if self.deform_axis == 'X': vert.co.y -= move_to_rotate elif self.deform_axis == 'Y' or self.deform_axis == 'Z': vert.co.x -= move_to_rotate if self.deform_axis != 'Z': vert.co.z = rot_origin.z else: vert.co.y = rot_origin.y # rotate the vert rot_angle = beta if self.deform_axis == 'X' or self.deform_axis == 'Z': rot_angle = -rot_angle rot_mat = Matrix.Rotation(rot_angle, 3, self.deform_axis) vert.co = rot_mat * (vert.co - rot_origin) + rot_origin # back the rotation offset back_offset = (visual_up_pos / (beta)) * self.bend_scale if self.deform_axis == 'X': vert.co.y += back_offset elif self.deform_axis == 'Y' or self.deform_axis == 'Z': vert.co.x += back_offset # offset axys move_offset = self.offset_axis * (visual_up_pos / visual_max) if self.deform_axis == 'X': vert.co.x += move_offset elif self.deform_axis == 'Y': vert.co.y += move_offset elif self.deform_axis == 'Z': vert.co.z += move_offset # obj.data.update() #bpy.ops.mesh.normals_make_consistent() # recalculate normals #bpy.ops.object.editmode_toggle() #bpy.ops.object.editmode_toggle() bm.normal_update() bmesh.update_edit_mesh(obj.data)
def calc_pose_mats(iqmodel, iqpose, bone_axis): loc_pose_mat = [None] * len(iqmodel.bones) abs_pose_mat = [None] * len(iqmodel.bones) recalc = False # convert pose to local matrix and compute absolute matrix for n in range(len(iqmodel.bones)): iqbone = iqmodel.bones[n] pose_pos = iqpose[n].translate pose_rot = iqpose[n].rotate pose_scale = iqpose[n].scale local_pos = Vector(pose_pos) local_rot = Quaternion( (pose_rot[3], pose_rot[0], pose_rot[1], pose_rot[2])) local_scale = Vector(pose_scale) mat_pos = Matrix.Translation(local_pos) mat_rot = local_rot.to_matrix().to_4x4() mat_scale = Matrix.Scale(local_scale.x, 3).to_4x4() loc_pose_mat[n] = matmul(matmul(mat_pos, mat_rot), mat_scale) if iqbone.parent >= 0: abs_pose_mat[n] = matmul(abs_pose_mat[iqbone.parent], loc_pose_mat[n]) else: abs_pose_mat[n] = loc_pose_mat[n] # Remove negative scaling from bones. # Due to numerical instabilities in blender's matrix <-> head/tail/roll math # this isn't always stable when the bones are in the X axis. If the bones # end up rotated 90 degrees from what they should be, that's the reason. for n in range(len(iqmodel.bones)): if abs_pose_mat[n].is_negative: if not hasattr(iqmodel, 'abs_bind_mat'): print("warning: removing negative scale in bone", iqmodel.bones[n].name) abs_pose_mat[n] = matmul(abs_pose_mat[n], Matrix.Scale(-1, 4)) recalc = True # flip bone axis (and recompute local matrix if needed) if bone_axis == 'X': axis_flip = Matrix.Rotation(math.radians(-90), 4, 'Z') abs_pose_mat = [matmul(m, axis_flip) for m in abs_pose_mat] recalc = True if bone_axis == 'Z': axis_flip = Matrix.Rotation(math.radians(-90), 4, 'X') abs_pose_mat = [matmul(m, axis_flip) for m in abs_pose_mat] recalc = True if recalc: inv_pose_mat = [m.inverted() for m in abs_pose_mat] for n in range(len(iqmodel.bones)): iqbone = iqmodel.bones[n] if iqbone.parent >= 0: loc_pose_mat[n] = matmul(inv_pose_mat[iqbone.parent], abs_pose_mat[n]) else: loc_pose_mat[n] = abs_pose_mat[n] return loc_pose_mat, abs_pose_mat
def execute(self, context): #if self.reset_values is True: #self.reset_all_values() active_obj = context.active_object bm = bmesh.from_edit_mesh(active_obj.data) bm.verts.ensure_lookup_table() #verts = [v for v in bm.verts if v.select] # get loops loops = loop_t.get_connected_input(bm) loops = loop_t.check_loops(loops, bm) if not loops: self.report({'WARNING'}, "No Loops!") return {'CANCELLED'} #obj_matrix = active_obj.matrix_world #obj_matrix_inv = obj_matrix.inverted() if self.unbevel_value != 1: for loop in loops: if loop[1] is True: continue loop_verts = [] for ind in loop[0]: loop_verts.append(bm.verts[ind]) v1 = (loop_verts[1].co - loop_verts[0].co).normalized() v2 = (loop_verts[2].co - loop_verts[1].co).normalized() angle_1 = v1.angle(v2) / 2 v3 = (loop_verts[-2].co - loop_verts[-1].co).normalized() v4 = (loop_verts[-3].co - loop_verts[-2].co).normalized() angle_2 = v1.angle(v2) / 2 degree_90 = 1.5708 rot_dir = v1.cross(v2).normalized() rot_mat = Matrix.Rotation(-angle_1, 3, rot_dir) rot_mat_2 = Matrix.Rotation((angle_2 - degree_90), 3, rot_dir) v1_nor = ( (rot_mat @ v1).normalized() * 10000) + loop_verts[0].co v3_nor = (rot_mat_2 @ v3).normalized() scale_pos = mathu.geometry.intersect_line_plane( loop_verts[0].co, v1_nor, loop_verts[-1].co, v3_nor) for vert in loop_verts: vert.co = scale_pos.lerp(vert.co, self.unbevel_value) if self.unbevel_value == 0: bpy.ops.mesh.merge(type='COLLAPSE') bm.normal_update() bmesh.update_edit_mesh(active_obj.data) return {'FINISHED'}
def outFrame(self, file, frameName = "frame"): mesh = self.object.to_mesh(bpy.context.scene, True, 'PREVIEW') mesh.transform(self.object.matrix_world) mesh.transform(Matrix.Rotation(pi / 2, 4, 'Z')) if not self.options.fUseSharedBoundingBox: ###### compute the bounding box ############### min = [mesh.vertices[0].co[0], mesh.vertices[0].co[1], mesh.vertices[0].co[2]] max = [mesh.vertices[0].co[0], mesh.vertices[0].co[1], mesh.vertices[0].co[2]] for vert in mesh.vertices: for i in range(3): if vert.co[i] < min[i]: min[i] = vert.co[i] if vert.co[i] > max[i]: max[i] = vert.co[i] ######################################## else: min = self.bbox_min max = self.bbox_max # BL: some caching to speed it up: # -> sd_ gets the vertices between [0 and 255] # which is our important quantization. dx = max[0] - min[0] if max[0] - min[0] != 0.0 else 1.0 dy = max[1] - min[1] if max[1] - min[1] != 0.0 else 1.0 dz = max[2] - min[2] if max[2] - min[2] != 0.0 else 1.0 sdx = dx / 255.0 sdy = dy / 255.0 sdz = dz / 255.0 isdx = 255.0 / dx isdy = 255.0 / dy isdz = 255.0 / dz # note about the scale: self.object.scale is already applied via matrix_world data = struct.pack("<6f16s", # writing the scale of the model sdx, sdy, sdz, ## now the initial offset (= min of bounding box) min[0], min[1], min[2], # and finally the name. bytes(frameName, encoding="utf8")) file.write(data) # frame header for vert in mesh.vertices: # find the closest normal for every vertex for iN in range(162): dot = vert.normal[1] * MD2_NORMALS[iN][0] + \ -vert.normal[0] * MD2_NORMALS[iN][1] + \ vert.normal[2] * MD2_NORMALS[iN][2] if iN == 0 or dot > maxDot: maxDot = dot bestNormalIndex = iN # and now write the normal. data = struct.pack("<4B", int((vert.co[0] - min[0]) * isdx), int((vert.co[1] - min[1]) * isdy), int((vert.co[2] - min[2]) * isdz), bestNormalIndex) file.write(data) # write vertex and normal
def get_dim_coords(context, myobj, DimGen, dim, mat, offset_pos=True): dimProps = dim if dim.uses_style: for alignedDimStyle in context.scene.StyleGenerator.alignedDimensions: if alignedDimStyle.name == dim.style: dimProps = alignedDimStyle # get points positions from indicies aMatrix = dim.dimObjectA.matrix_world bMatrix = dim.dimObjectB.matrix_world offset = dim.dimOffset geoOffset = dim.dimLeaderOffset # get points positions from indicies p1Local = None p2Local = None try: p1Local = get_mesh_vertex(dim.dimObjectA, dim.dimPointA, dimProps.evalMods) except IndexError: print('p1 excepted for ' + dim.name + ' on ' + myobj.name) try: p2Local = get_mesh_vertex(dim.dimObjectB, dim.dimPointB, dimProps.evalMods) except IndexError: print('p2 excepted for ' + dim.name + ' on ' + myobj.name) p1 = get_point(p1Local, dim.dimObjectA, aMatrix) p2 = get_point(p2Local, dim.dimObjectB, bMatrix) # check dominant Axis sortedPoints = sortPoints(p1, p2) p1 = sortedPoints[0] p2 = sortedPoints[1] distVector = Vector(p1) - Vector(p2) dist = distVector.length midpoint = interpolate3d(p1, p2, fabs(dist / 2)) normDistVector = distVector.normalized() # Compute offset vector from face normal and user input rotationMatrix = Matrix.Rotation(dim.dimRotation, 4, normDistVector) selectedNormal = Vector( select_normal(myobj, dim, normDistVector, midpoint, dimProps)) userOffsetVector = rotationMatrix @ selectedNormal offsetDistance = userOffsetVector * offset geoOffsetDistance = offsetDistance.normalized() * geoOffset if offsetDistance < geoOffsetDistance: offsetDistance = geoOffsetDistance dimLineStart = Vector(p1) + offsetDistance dimLineEnd = Vector(p2) + offsetDistance if offset_pos: return [dimLineStart, dimLineEnd] else: return [p1, p2]
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
def create_prongs(self): # Prong # --------------------------- prong_rad = self.diameter / 2 taper = self.taper + 1 if self.bump_scale: curve_resolution = int(self.detalization / 4) + 1 angle = (pi / 2) / (curve_resolution - 1) v_cos = [( 0.0, sin(i * angle) * prong_rad, cos(i * angle) * prong_rad * self.bump_scale + self.z_top, ) for i in range(curve_resolution)] v_cos.append((0.0, prong_rad * taper, -self.z_btm)) else: v_cos = ( (0.0, 0.0, self.z_top), (0.0, prong_rad, self.z_top), (0.0, prong_rad * taper, -self.z_btm), ) bm = bmesh.new() v_profile = [bm.verts.new(v) for v in v_cos] for vs in zip(v_profile, v_profile[1:]): bm.edges.new(vs) bmesh.ops.spin(bm, geom=bm.edges, angle=tau, steps=self.detalization, axis=(0.0, 0.0, 1.0), cent=(0.0, 0.0, 0.0)) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.00001) v_boundary = [x for x in bm.verts if x.is_boundary] bm.faces.new(reversed(v_boundary)) # Transforms # --------------------------- pos_offset = (self.gem_l / 2 + prong_rad) - (self.diameter * (self.intersection / 100)) spin_steps = self.number - 1 if self.alignment: bmesh.ops.rotate(bm, verts=bm.verts, cent=(0.0, 0.0, 0.0), matrix=Matrix.Rotation(-self.alignment, 4, "X")) bmesh.ops.translate(bm, verts=bm.verts, vec=(0.0, pos_offset, 0.0)) if spin_steps: spin_angle = tau - tau / self.number bmesh.ops.spin(bm, geom=bm.faces, angle=spin_angle, steps=spin_steps, axis=(0.0, 0.0, 1.0), cent=(0.0, 0.0, 0.0), use_duplicate=True) bmesh.ops.rotate(bm, verts=bm.verts, cent=(0.0, 0.0, 0.0), matrix=Matrix.Rotation(-self.position, 4, "Z")) if self.use_symmetry: bmesh.ops.mirror(bm, geom=bm.faces, merge_dist=0, axis="Y") bmesh.ops.recalc_face_normals(bm, faces=bm.faces) bmesh.ops.rotate(bm, verts=bm.verts, cent=(0.0, 0.0, 0.0), matrix=Matrix.Rotation(-self.symmetry_pivot, 4, "Z")) return bm