def point_camera(obj_camera, point): loc_camera = obj_camera.location direction = Vector(point) - loc_camera old_rotation = obj_camera.rotation_euler[:] # point the cameras '-Z' and use its 'Y' as up rot_quat = direction.to_track_quat('-Z', 'Y') # assume we're using euler rotation new_rotation = rot_quat.to_euler() # bunch of code to keep rotations nice regardless of quadrant # treat all angles on the interval [-pi, pi] before adding them for ii, (old, new) in enumerate(zip(old_rotation, new_rotation)): oldang = old % (2 * math.pi) newang = new % (2 * math.pi) if oldang > math.pi: oldang = oldang - 2 * math.pi if newang > math.pi: newang = newang - 2 * math.pi diff = newang - oldang if abs(diff) > math.pi: sgn = 1 if diff > 0 else -1 diff = sgn * (abs(diff) - math.pi) # Use the smaller angle to rotate the camera new = old + diff obj_camera.rotation_euler[ii] = new
def _cam2world_matrix_from_cam_extrinsics(self, config): """ Determines camera extrinsics by using the given config and returns them in form of a cam to world frame transformation matrix. :param config: The configuration object. :return: The cam to world transformation matrix. """ if not config.has_param("cam2world_matrix"): position = Utility.transform_point_to_blender_coord_frame(config.get_vector3d("location", [0, 0, 0]), self.source_frame) # Rotation rotation_format = config.get_string("rotation/format", "euler") value = config.get_vector3d("rotation/value", [0, 0, 0]) if rotation_format == "euler": # Rotation, specified as euler angles rotation_euler = Utility.transform_point_to_blender_coord_frame(value, self.source_frame) elif rotation_format == "forward_vec": # Rotation, specified as forward vector forward_vec = Vector(Utility.transform_point_to_blender_coord_frame(value, self.source_frame)) # Convert forward vector to euler angle (Assume Up = Z) rotation_euler = forward_vec.to_track_quat('-Z', 'Y').to_euler() elif rotation_format == "look_at": # Compute forward vector forward_vec = value - position forward_vec.normalize() # Convert forward vector to euler angle (Assume Up = Z) rotation_euler = forward_vec.to_track_quat('-Z', 'Y').to_euler() else: raise Exception("No such rotation format:" + str(rotation_format)) cam2world_matrix = Matrix.Translation(Vector(position)) @ Euler(rotation_euler, 'XYZ').to_matrix().to_4x4() else: cam2world_matrix = Matrix(np.array(config.get_list("cam2world_matrix")).reshape(4, 4).astype(np.float32)) return cam2world_matrix
def execute(self, context): bpy.ops.object.mode_set(mode='EDIT') obj = bpy.context.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) cornerverts, sharp = corner_vertices(bm) #print("corner verts:", cornerverts) #print ("corner sharp:",sharp) # ------------------------------------------------ # Get angle of edge corners bpy.ops.object.mode_set(mode='OBJECT') o = bpy.context.object mat = o.matrix_world sel = bpy.context.selected_objects #bpy.context.Scene.offset = .1 if len(sel) == 2: sharpcnt = 0 for v in range(0, len(cornerverts), 3): v1 = mat * o.data.vertices[cornerverts[v]].co v2 = mat * o.data.vertices[cornerverts[v + 1]].co v3 = mat * o.data.vertices[cornerverts[v + 2]].co vec_result = get_angle(v1, v2, v3) ob = create_object(sel[0], v1) ob.rotation_mode = 'QUATERNION' if sharp[sharpcnt] == True: vec_result = -vec_result sharpcnt += 1 ob.rotation_quaternion = Vector.to_track_quat( -vec_result, '-Y', 'Z') bpy.context.scene.update() # offset vec = Vector((0, -bpy.context.scene.offset, 0)) inv = ob.matrix_world.copy() inv.invert() # vec aligned to local axis vec_rot = vec * inv ob.location += vec_rot # rename if bpy.context.scene.angle: ob.name = rename( ob.name, Vector.to_track_quat(-vec_result, '-Y', 'Z')) return {'FINISHED'}
def execute(self, context): bpy.ops.object.mode_set(mode='EDIT') obj = bpy.context.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) cornerverts, sharp = corner_vertices(bm) #print("corner verts:", cornerverts) #print ("corner sharp:",sharp) # ------------------------------------------------ # Get angle of edge corners bpy.ops.object.mode_set(mode='OBJECT') o = bpy.context.object mat = o.matrix_world sel = bpy.context.selected_objects #bpy.context.Scene.offset = .1 if len(sel) == 2: sharpcnt = 0 for v in range(0, len(cornerverts), 3): v1 = mat * o.data.vertices[cornerverts[v]].co v2 = mat * o.data.vertices[cornerverts[v + 1]].co v3 = mat * o.data.vertices[cornerverts[v + 2]].co vec_result = get_angle (v1,v2,v3) ob = create_object(sel[0], v1) ob.rotation_mode = 'QUATERNION' if sharp[sharpcnt] == True: vec_result = - vec_result sharpcnt += 1 ob.rotation_quaternion = Vector.to_track_quat(-vec_result, '-Y', 'Z') bpy.context.scene.update() # offset vec = Vector((0, -bpy.context.scene.offset, 0)) inv = ob.matrix_world.copy() inv.invert() # vec aligned to local axis vec_rot = vec * inv ob.location += vec_rot # rename if bpy.context.scene.angle: ob.name = rename(ob.name,Vector.to_track_quat(-vec_result, '-Y', 'Z')) return {'FINISHED'}
def simbone_bake(ob, pose_bone, edit_bone, scene): frames, frame_step = get_scene_frames(scene) matrices = get_matrices(ob, pose_bone, edit_bone, frames) end_mats = get_matrices_single_bone(ob, pose_bone, edit_bone, frames) # Main lists. positions = [ mat.decompose()[0] * scene.simboneworld.spaceScale for mat in matrices ] orientations = [(matrices[i] @ end_mats[i].inverted()).decompose()[1] for i in range(len(end_mats))] velocities = [(positions[i] - positions[max(0, i - 1)]) / frame_step for i in range(len(positions))] accelerations = [(velocities[i] - velocities[max(0, i - 1)]) / frame_step for i in range(len(velocities))] gravity = get_gravity(scene) custom_force = pose_bone.simbone.custom_force.copy() up_quat = Euler((pose_bone.simbone.up_offset - 90, 0, 0)).to_quaternion() start_dir = bone_quat_to_dir(matrices[0].decompose()[1]) # Setup pendulum values. pen = Pendulum() pen.l = edit_bone.length * scene.simboneworld.spaceScale pen.m = pose_bone.simbone.m pen.set_coords(start_dir) pen.c_a = pose_bone.simbone.c_a pen.c_d = pose_bone.simbone.c_d datapath = 'pose.bones["' + pose_bone.name + '"].rotation_quaternion' pose_bone.rotation_mode = 'QUATERNION' pose_bone.rotation_quaternion = end_mats[0].decompose()[1] pose_bone.keyframe_insert(data_path='rotation_quaternion', frame=frames[0]) fcurves = [ ob.animation_data.action.fcurves.find(datapath, index=i) for i in range(4) ] for i in range(1, len(matrices)): matrix = matrices[i] frame = frames[i] g = gravity.copy() f = custom_force.copy() f.rotate(orientations[i]) pen.w = scene.simboneworld.wind - velocities[i] pen.f = g + f - accelerations[i] pen.do_steps(frame_step * scene.simboneworld.timeScale, 1) direction = Vector(pen.get_coords()) direction.rotate(orientations[i].inverted()) direction.rotate(up_quat.inverted()) orient = direction.to_track_quat('Y', 'Z') orient.rotate(up_quat) for i in range(4): fcurves[i].keyframe_points.insert(frame, orient[i])
def ctx_camera_setup(context, location=(0.0, 0.0, 0.0), lookat=(0.0, 0.0, 0.0), # most likely the following vars can be left as defaults up=(0.0, 0.0, 1.0), lookat_axis='-Z', up_axis='Y', ): camera = bpy.data.cameras.new(whoami()) obj = bpy.data.objects.new(whoami(), camera) scene = context.scene scene.objects.link(obj) scene.camera = obj from mathutils import Vector, Matrix # setup transform view_vec = Vector(lookat) - Vector(location) rot_mat = view_vec.to_track_quat(lookat_axis, up_axis).to_matrix().to_4x4() tra_mat = Matrix.Translation(location) obj.matrix_world = tra_mat * rot_mat ctx_viewport_camera(context) return obj
def execute(self, context): curr_scene = context.scene file_path = curr_scene.srti_props.light_file_path if not os.path.isfile( file_path) or os.path.splitext(file_path)[1] != ".lp": self.report({'ERROR'}, 'No valid file selected on ' + file_path) return {'CANCELLED'} bpy.ops.srti.delete_lamps() #delete exsisting lamps #create the main parent create_main(curr_scene) main_parent = curr_scene.srti_props.main_parent file = open(file_path) rows = file.readlines() #copy all lines in memory file.close() n_lights = int( rows[0].split() [0]) #in the first row there is the total count of lights print("- Creating %i lamps ---" % n_lights) ##Use standars light, TODO add a differente object lamp_data = bpy.data.lamps.new( name="Project_light", type='SUN' ) # Create new lamp datablock. It s going to be created outside for lamp in range(1, n_lights + 1): #step trought all lamps valori_riga = rows[lamp].split() #split values lmp_x = float(valori_riga[1]) lmp_y = float(valori_riga[2]) lmp_z = float(valori_riga[3]) direction = Vector((lmp_x, lmp_y, lmp_z)) print("-- ", lamp, 'x=', lmp_x, 'y=', lmp_y, 'z=', lmp_z) #print all values lamp_object = bpy.data.objects.new( name="Lamp_{0}".format(format_index(lamp, n_lights)), object_data=lamp_data ) # Create new object with our lamp datablock curr_scene.objects.link( lamp_object ) # Link lamp object to the scene so it'll appear in this scene lamp_object.parent = main_parent lamp_object.location = (lmp_x, lmp_y, lmp_z ) # Place lamp to a specified location lamp_object.rotation_mode = 'QUATERNION' lamp_object.rotation_quaternion = direction.to_track_quat('Z', 'Y') ##change the name lamp = curr_scene.srti_props.list_lights.add() lamp.light = lamp_object self.report({'INFO'}, "Created %i lamps." % n_lights) return {'FINISHED'}
def look_at(obj_camera, point): loc_camera = obj_camera.matrix_world.to_translation() direction = Vector(point) - loc_camera # point the cameras '-Z' and use its 'Y' as up rot_quat = direction.to_track_quat('-Z', 'Y') # assume we're using euler rotation obj_camera.rotation_euler = rot_quat.to_euler()
def look_at(self, context, target, axis, flip): objs = bpy.context.selected_objects self.gpu_verts = [] for o in objs: # Reset matrix q = o.matrix_world.to_quaternion() m = q.to_matrix() m = m.to_4x4() o.matrix_world @= m.inverted() self.gpu_verts.append(o.location) self.gpu_verts.append(target.location) v = Vector(o.location - target.location) if flip: rot_mx = v.to_track_quat("-" + axis[0], axis[1]).to_matrix().to_4x4() else: rot_mx = v.to_track_quat(axis[0], axis[1]).to_matrix().to_4x4() o.matrix_world @= rot_mx
def floor_raycast(context: bpy.types.Context, mx: float, my: float) -> tuple: """Perform a raycast from the floor. Parameters: context: Blender context mx: mouse x-coordinate my: mouse y-coordinate Returns: tuple: Tuple containing: if a object was hit; location; normal; rotation; face index; object; matrix; """ region = context.region rv3d = context.region_data coord = mx, my # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) # various intersection plane normals are needed for corner cases # that might actually happen quite often - in front and side view. # default plane normal is scene floor. tolerance = 1e-4 plane_normal = (0, 0, 1) if math.isclose(view_vector.z, 0, abs_tol=tolerance): if math.isclose(view_vector.x, 0, abs_tol=tolerance): plane_normal = (0, 1, 0) else: plane_normal = (1, 0, 0) origin = (0, 0, 0) snapped_location = mathutils.geometry.intersect_line_plane( ray_origin, ray_target, origin, plane_normal, ) if snapped_location is not None: has_hit = True snapped_normal = Vector((0, 0, 1)) face_index = None obj_hit = None matrix = None snapped_rotation = snapped_normal.to_track_quat('Z', 'Y').to_euler() props = getattr(bpy.context.window_manager, HANA3D_MODELS) randoffset = props.offset_rotation_amount + math.pi snapped_rotation.rotate_axis('Z', randoffset) return has_hit, snapped_location, snapped_normal, snapped_rotation, face_index, obj_hit, matrix
def add_rectangular_plane( center_loc=(0, 0, 0), point_to=(0, 0, 1), size=(2, 2), name=None): """ Add a rectangular plane specified by its center location, dimensions, and where its +z points to Args: center_loc: Plane center location in world coordinates Array_like containing three floats Optional; defaults to world origin point_to: Direction to which plane's +z points to in world coordinates Array_like containing three floats Optional; defaults to world +z size: Sizes in x and y directions (0 in z) Array_like containing two floats Optional; defaults to a square with side length 2 name: Plane name String Optional; defaults to Blender defaults Returns: plane_obj: Handle of added plane bpy_types.Object """ center_loc = np.array(center_loc) point_to = np.array(point_to) size = np.append(np.array(size), 0) bpy.ops.mesh.primitive_plane_add(location=center_loc) plane_obj = bpy.context.object if name is not None: plane_obj.name = name plane_obj.dimensions = size # Point it to target direction = Vector(point_to) - plane_obj.location # Find quaternion that rotates plane's 'Z' so that it aligns with 'direction' # This rotation is not unique because the rotated plane can still rotate about direction vector # Specifying 'Y' gives the rotation quaternion with plane's 'Y' pointing up rot_quat = direction.to_track_quat('Z', 'Y') plane_obj.rotation_euler = rot_quat.to_euler() # Scene update necessary, as matrix_world is updated lazily bpy.context.scene.update() return plane_obj
def Point_at(obj, direction): if (type(direction) != Vector): direction = Vector(direction) if (type(obj) == str): obj = bpy.data.collections[0].objects[obj] # print('Norm of ' + str(direction) + ' is :' + str(Norm(direction))) obj.scale.x = Norm(direction) * 0.4 obj.scale.y = Norm(direction) * 0.2 obj.scale.z = Norm(direction) * 0.3 # point the obj 'Y' and use its 'Z' as up rot_quat = direction.to_track_quat('Y', 'Z') # assume we're using euler rotation obj.rotation_euler = rot_quat.to_euler() return
def floor_raycast(context, mx, my): ''' This casts a ray into the 3D view and returns information based on what is under the mouse ARGS context (bpy.context) = current blender context mx (float) = 2D mouse x location my (float) = 2D mouse y location RETURNS tuple has_hit (boolean) - determines if an object is under the mouse snapped_location (tuple) - x,y,z location of location under mouse snapped_normal (tuple) - normal direction snapped_rotation (tuple) - rotation face_index (int) - face index under mouse object (bpy.types.Object) - Blender Object under mouse martix (float multi-dimensional array of 4 * 4 items in [-inf, inf]) - matrix of placement under mouse ''' r = context.region rv3d = context.region_data coord = mx, my # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(r, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(r, rv3d, coord) # ray_target = ray_origin + (view_vector * 1000000000) ray_target = ray_origin + view_vector snapped_location = mathutils.geometry.intersect_line_plane(ray_origin, ray_target, (0, 0, 0), (0, 0, 1), False) if snapped_location != None: has_hit = True snapped_normal = Vector((0, 0, 1)) face_index = None object = None matrix = None snapped_rotation = snapped_normal.to_track_quat('Z', 'Y').to_euler() offset_rotation_amount = 0 randomize_rotation_amount = 0 randomize_rotation = False if randomize_rotation: randoffset = offset_rotation_amount + math.pi + ( random.random() - 0.5) * randomize_rotation_amount else: randoffset = offset_rotation_amount + math.pi snapped_rotation.rotate_axis('Z', randoffset) return has_hit, snapped_location, snapped_normal, snapped_rotation, face_index, object, matrix
def create_lights(self, lights_metadata_path): with open(lights_metadata_path) as f: metadata = json.load(f) light_ids = metadata.keys() success = False for m in bpy.data.meshes: if "Model#" in m.name and m.name.split("#")[1] in light_ids: success = True light_metadata = metadata[m.name.split("#")[1]][0] light_type = light_metadata['type'] model_position = utils.find_model_center( bpy.data.objects[m.name]) if light_type == "PointLight" or light_type == "LineLight": bpy.ops.object.lamp_add(location=model_position) obj = bpy.context.object obj.data.node_tree.nodes[ "Emission"].inputs[1].default_value = random.uniform( 2.0, 4.0) * light_metadata["power"] light_offset = light_metadata['position'] obj.location += Vector( (float(light_offset[0]), -float(light_offset[2]), float(light_offset[1]))) obj.data.shadow_soft_size = 1.5 elif light_type == "SpotLight": success = True light_direction = light_metadata['direction'] direction = Vector(model_position) + Vector( (float(light_direction[0]), -float(light_direction[2]), float(light_direction[1]))) rot_quat = direction.to_track_quat( 'X', 'Z') # point the cameras '-Z' and use its 'Y' as up bpy.ops.object.lamp_add(type='SPOT', location=model_position,\ rotation=rot_quat.to_euler()) obj = bpy.context.object obj.data.node_tree.nodes[ "Emission"].inputs[1].default_value = random.uniform( 5.0, 15.0) * light_metadata["power"] light_offset = light_metadata['position'] obj.location += Vector( (float(light_offset[0]), -float(light_offset[2]), float(light_offset[1]))) cutoffAngle = float(light_metadata['cutoffAngle']) obj.data.spot_size = random.uniform(0.5, 1.0) * cutoffAngle obj.data.shadow_soft_size = 1.0 return success
def _cam2world_matrix_from_cam_extrinsics_look_at(self, location, look_at): """ Determines camera extrinsics by using the location and the look_at vector. :param look_at: The look_at vector. :return: The cam to world transformation matrix. """ position = Vector( Utility.transform_point_to_blender_coord_frame( location, self.source_frame)) forward_vec = Vector(look_at) - position forward_vec.normalize() # Convert forward vector to euler angle (Assume Up = Z) rotation_euler = forward_vec.to_track_quat('-Z', 'Y').to_euler() cam2world_matrix = Matrix.Translation(position) @ Euler( rotation_euler, 'XYZ').to_matrix().to_4x4() return cam2world_matrix
def _cam2world_matrix_from_cam_extrinsics_forward(self, location, forward_vec): """ Determines camera extrinsics by using the location and the forward vector. :param forward_vec: The forward vector. :return: The cam to world transformation matrix. """ # Rotation, specified as forward vector forward_vec = Vector( Utility.transform_point_to_blender_coord_frame( forward_vec, self.source_frame)) # Convert forward vector to euler angle (Assume Up = Z) rotation_euler = forward_vec.to_track_quat('-Z', 'Y').to_euler() position = Utility.transform_point_to_blender_coord_frame( location, self.source_frame) cam2world_matrix = Matrix.Translation(Vector(position)) @ Euler( rotation_euler, 'XYZ').to_matrix().to_4x4() return cam2world_matrix
def set_camera(): global CAMERA_NAME # get camera or create one cam = cam_ob = None if CAMERA_NAME in bpy.data.cameras: cam = bpy.data.cameras[CAMERA_NAME] else: cam = bpy.data.cameras.new(CAMERA_NAME) if CAMERA_NAME in bpy.data.objects: cam_ob = bpy.data.objects[CAMERA_NAME] else: cam_ob = bpy.data.objects.new(CAMERA_NAME, cam) bpy.context.scene.objects.link(cam_ob) # position ob = bpy.data.objects[OBJECT_NAME] cam_ob.location = (0, 0, 0) max_dim = max(ob.dimensions.z, max(ob.dimensions.y, ob.dimensions.x)) dim_ratio = 1 try: dim_ratio = ob.dimensions.y / ob.dimensions.x if ob.dimensions.y > ob.dimensions.x else ob.dimensions.x / ob.dimensions.y except ZeroDivisionError: pass cam_ob.location.y += max_dim * 1.1 cam_ob.location.x += max_dim * 1.1 cam_ob.location.z += max_dim / dim_ratio # rotation dir = Vector((0, 0, 0)) - cam_ob.location cam_ob.rotation_euler = dir.to_track_quat('-Z', 'Y').to_euler() # other settings cam.clip_start = 0.002 cam.clip_end = 3 * max(abs(cam_ob.location.z), max(abs(cam_ob.location.x), abs(cam_ob.location.y))) cam.show_limits = True # set camera as the active one bpy.context.scene.camera = cam_ob
def floor_raycast(context, mx, my): r = context.region rv3d = context.region_data coord = mx, my # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(r, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(r, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) # various intersection plane normals are needed for corner cases that might actually happen quite often - in front and side view. # default plane normal is scene floor. plane_normal = (0, 0, 1) if math.isclose(view_vector.x, 0, abs_tol=1e-4) and math.isclose( view_vector.z, 0, abs_tol=1e-4): plane_normal = (0, 1, 0) elif math.isclose(view_vector.z, 0, abs_tol=1e-4): plane_normal = (1, 0, 0) snapped_location = mathutils.geometry.intersect_line_plane( ray_origin, ray_target, (0, 0, 0), plane_normal, False) if snapped_location != None: has_hit = True snapped_normal = Vector((0, 0, 1)) face_index = None object = None matrix = None snapped_rotation = snapped_normal.to_track_quat('Z', 'Y').to_euler() props = bpy.context.scene.luxcoreOL.model if props.randomize_rotation: randoffset = props.offset_rotation_amount + math.pi + ( random.random() - 0.5) * props.randomize_rotation_amount else: randoffset = props.offset_rotation_amount + math.pi snapped_rotation.rotate_axis('Z', randoffset) return has_hit, snapped_location, snapped_normal, snapped_rotation, face_index, object, matrix
def add_rectangular_plane( center_loc=(0, 0, 0), point_to=(0, 0, 1), size=(2, 2), name=None): """Adds a rectangular plane specified by its center location, dimensions, and where its +z points to. Args: center_loc (array_like, optional): Plane center location in world coordinates. point_to (array_like, optional): Point in world coordinates to which plane's +z points. size (array_like, optional): Sizes in x and y directions (0 in z). name (str, optional): Plane name. Returns: bpy_types.Object: Plane added. """ center_loc = np.array(center_loc) point_to = np.array(point_to) size = np.append(np.array(size), 0) bpy.ops.mesh.primitive_plane_add(location=center_loc) plane_obj = bpy.context.object if name is not None: plane_obj.name = name plane_obj.dimensions = size # Point it to target direction = Vector(point_to) - plane_obj.location # Find quaternion that rotates plane's 'Z' so that it aligns with `direction` # This rotation is not unique because the rotated plane can still rotate about direction vector # Specifying 'Y' gives the rotation quaternion with plane's 'Y' pointing up rot_quat = direction.to_track_quat('Z', 'Y') plane_obj.rotation_euler = rot_quat.to_euler() # Scene update necessary, as matrix_world is updated lazily bpy.context.scene.update() return plane_obj
def add_camera_translation(cam, radius): scene = bpy.context.scene x = cam.location[0] y = cam.location[1] z = cam.location[2] bpy.ops.curve.primitive_nurbs_path_add(radius=radius, location=(x, y, z)) # Pose it path = bpy.context.object direction = Vector((0, 0, z)) - Vector((x, y, z)) rot_quat = direction.to_track_quat('-Z', 'Y') path.rotation_euler = rot_quat.to_euler() deselect_all_objects() cam.select = True path.select = True scene.objects.active = path bpy.ops.object.parent_set(type='FOLLOW') path.data.path_duration = scene.frame_end # Hide the curve from the scene path.hide = True
def set_camera(): global CAMERA_NAME # get camera or create one cam = cam_ob = None if CAMERA_NAME in bpy.data.cameras: cam = bpy.data.cameras[CAMERA_NAME] else: cam = bpy.data.cameras.new(CAMERA_NAME) if CAMERA_NAME in bpy.data.objects: cam_ob = bpy.data.objects[CAMERA_NAME] else: cam_ob = bpy.data.objects.new(CAMERA_NAME, cam) bpy.context.scene.objects.link(cam_ob) # position ob = bpy.data.objects[OBJECT_NAME] cam_ob.location = (0,0,0) max_dim = max(ob.dimensions.z, max(ob.dimensions.y, ob.dimensions.x)) dim_ratio = 1 try: dim_ratio = ob.dimensions.y/ob.dimensions.x if ob.dimensions.y>ob.dimensions.x else ob.dimensions.x/ob.dimensions.y except ZeroDivisionError: pass cam_ob.location.y += max_dim * 1.1 cam_ob.location.x += max_dim * 1.1 cam_ob.location.z += max_dim / dim_ratio # rotation dir = Vector((0,0,0)) - cam_ob.location cam_ob.rotation_euler = dir.to_track_quat('-Z', 'Y').to_euler() # other settings cam.clip_start = 0.002 cam.clip_end = 3*max(abs(cam_ob.location.z), max(abs(cam_ob.location.x), abs(cam_ob.location.y))) cam.show_limits = True # set camera as the active one bpy.context.scene.camera = cam_ob
itertools.permutations(range(3)))) verts = [] for sign in signs: for order in orders: for v in vector: arr = [float(a * b) for (a, b) in zip([v[i] for i in order], sign)] #print(', '.join(['{0:+.5}']*3).format(*arr)) verts.append(arr) for i, vert in enumerate(verts): bpy.ops.object.text_add(location=vert) ob = bpy.context.object v = Vector(vert) v.normalize() t = v.to_track_quat('Z', 'Y').to_euler() ob.rotation_euler = Euler(tuple(t), 'XYZ') ob.data.body = '{}'.format(i) ob.data.extrude = 0.2 ob.data.align_x = ob.data.align_y = 'CENTER' starts = [0, 15, 30, 45] faces = [ [0, 1, 3, 4, 2], [1, 0, 8], [1, 8, 6], [1, 6, 11], [1, 11, 3], #[ 5, 6, 8, 9, 7], #[ 10, 11, 13, 14, 12],
def execute(self, context): turtle = bpy.context.scene.cursor if bpy.context.object.get('pendownp') is None: # pen state bpy.context.object['pendownp'] = True if bpy.context.object['pendownp']: world = bpy.context.object world_name = world.name bpy.ops.curve.primitive_bezier_curve_add(radius=1, enter_editmode=True) bpy.ops.curve.select_all(action='DESELECT') # set location of first spline point and control point bpy.context.active_object.data.splines[0].bezier_points[0].co = (0, 0, 0) bpy.context.active_object.data.splines[0].bezier_points[ 0].handle_right = self.cp # set location of second control point. bpy.context.active_object.data.splines[0].bezier_points[ 1].co = self.ep bpy.context.active_object.data.splines[0].bezier_points[ 1].handle_right = self.ep # set turtle location turtle.location = turtle.location + Vector(self.ep) # set turtle rotation direction_vec = Vector(self.ep) - Vector(self.cp) rot_quat = direction_vec.to_track_quat('Y', 'Z') turtle.rotation_mode = 'QUATERNION' turtle.rotation_quaternion = rot_quat turtle.rotation_mode = 'XYZ' bpy.ops.object.editmode_toggle() # convert curve to mesh and join to turtle_world object bpy.ops.object.convert(target='MESH') bpy.data.objects[world.name].select_set(True) bpy.ops.object.join() bpy.context.object.name = world_name # merge vertices bpy.ops.object.editmode_toggle() bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.remove_doubles() bpy.ops.mesh.select_all(action='DESELECT') # select last vert of converted curve lbound = turtle.location ubound = turtle.location select_by_loc(lbound, ubound, select_mode='VERT', coords='GLOBAL', buffer=0.001) else: # set turtle location without drawing anything turtle.location = self.ep # set turtle rotation direction_vec = Vector(self.ep) - Vector(self.cp) rot_quat = direction_vec.to_track_quat('Y', 'Z') turtle.rotation_mode = 'QUATERNION' turtle.rotation_quaternion = rot_quat turtle.rotation_mode = 'XYZ' return {'FINISHED'}
def cc(cp1, cp2, ep): """moves the turtle on a path described by a cubic Bezier curve. Keyword Arguments: cp1 -- coordinates of control point1 cp2 -- coordinates of control point2 ep -- coordinate of end point """ if S.pendownp: canvas = bpy.context.object canvas_name = canvas.name bpy.ops.curve.primitive_bezier_curve_add(radius=1, enter_editmode=True) bpy.ops.curve.select_all(action='DESELECT') p0 = bpy.context.active_object.data.splines[0].bezier_points[0] p1 = bpy.context.active_object.data.splines[0].bezier_points[1] #set location of first spline point and control point p0.co = (0, 0, 0) bpy.ops.curve.select_all(action='DESELECT') p0.select_right_handle = True p0.handle_right = cp1 #set location of second spline point and control point p1.co = ep bpy.ops.curve.select_all(action='DESELECT') p1.select_left_handle = True p1.handle_left = cp2 #set turtle location turtle.location = turtle.location + Vector(ep) #set turtle rotation direction_vec = Vector(ep) - Vector(cp2) rot_quat = direction_vec.to_track_quat('Y', 'Z') turtle.rotation_mode = 'QUATERNION' turtle.rotation_quaternion = rot_quat turtle.rotation_mode = 'XYZ' mode('OBJECT') #convert curve to mesh and join to canvas bpy.ops.object.convert(target='MESH') bpy.data.objects[canvas.name].select_set(True) bpy.ops.object.join() bpy.context.object.name = canvas_name #merge vertices mode('EDIT') select_all() bpy.ops.mesh.remove_doubles() deselect_all() #select last vert of converted curve lbound = turtle.location ubound = turtle.location select_by_loc(lbound, ubound, select_mode='VERT', coords='GLOBAL', buffer=0.001) else: #set turtle location without drawing anything turtle.location = ep #set turtle rotation direction_vec = Vector(ep) - Vector(cp2) rot_quat = direction_vec.to_track_quat('Y', 'Z') turtle.rotation_mode = 'QUATERNION' turtle.rotation_quaternion = rot_quat turtle.rotation_mode = 'XYZ'
def look_at(point, camera): direction = Vector(point) - camera.location rot_quat = direction.to_track_quat('-Z', 'Y') camera.rotation_euler = rot_quat.to_euler()
def _add_cam_pose(self, config, H_cam2world=None, cam_K=None): """ Adds new cam pose + intrinsics according to the given configuration. :param config: A configuration object which contains all parameters relevant for the new cam pose. :param H_cam2world: Optionally, 4x4 numpy array defining homogenous trafo from camera to world coordinates. :param cam_K: Optionally, 3x3 numpy array containing the camera matrix cam_K. """ self._add_cam_intrinsics(config, cam_K) # Collect camera object cam_ob = bpy.context.scene.camera cam = cam_ob.data cam_ob.location = Utility.transform_point_to_blender_coord_frame( config.get_list("location", [0, 0, 0]), self.source_frame) # Rotation rotation_format = config.get_string("rotation/format", "euler") value = config.get_vector3d("rotation/value", [0, 0, 0]) if rotation_format == "euler": # Rotation, specified as euler angles cam_ob.rotation_euler = Utility.transform_point_to_blender_coord_frame( value, self.source_frame) elif rotation_format == "forward_vec": # Rotation, specified as forward vector forward_vec = Vector( Utility.transform_point_to_blender_coord_frame( value, self.source_frame)) # Convert forward vector to euler angle (Assume Up = Z) cam_ob.rotation_euler = forward_vec.to_track_quat('-Z', 'Y').to_euler() elif rotation_format == "look_at": # Compute forward vector forward_vec = value - cam_ob.location forward_vec.normalize() # Convert forward vector to euler angle (Assume Up = Z) cam_ob.rotation_euler = forward_vec.to_track_quat('-Z', 'Y').to_euler() else: raise Exception("No such rotation format:" + str(rotation_format)) if H_cam2world is not None: # Set homogeneous camera pose from input parameter H_cam2world cam_ob.matrix_world = H_cam2world # transform from OpenCV to blender coords cam_ob.matrix_world @= Matrix.Rotation(math.radians(180), 4, "X") # How the two cameras converge (e.g. Off-Axis where both cameras are shifted inwards to converge in the # convergence plane, or parallel where they do not converge and are parallel) cam.stereo.convergence_mode = config.get_string( "stereo_convergence_mode", "OFFAXIS") # The convergence point for the stereo cameras (i.e. distance from the projector to the projection screen) (Default value is the same as the default blender value) cam.stereo.convergence_distance = config.get_float( "convergence_distance", 1.95) # Distance between the camera pair (Default value is the same as the default blender value) cam.stereo.interocular_distance = config.get_float( "interocular_distance", 0.065) # Store new cam pose as next frame frame_id = bpy.context.scene.frame_end self._insert_key_frames(cam, cam_ob, frame_id) bpy.context.scene.frame_end = frame_id + 1
class arrow(object): """ Arrow class that adds a 3D 'vector' arrow """ def __init__(self): self.root_point = (0,0,0) # The point where the base of the arrow will lie self.direction = (0,0,0) # the direction vector self.length = 1 self.color = None self.mesh_data = None self.mesh_material = None self.mesh_object = None def place(self): import os import bpy import numpy as np from mathutils import Vector import matplotlib.cm as cm #deselect all objects bpy.ops.object.select_all(action='DESELECT') if not isinstance(self.root_point, tuple) and not isinstance(self.root_point, np.ndarray): print("Error: root_point not a tuple of numbers or ndarray.") return -1 if not self.mesh_object is None: bpy.ops.object.select_all(action='DESELECT') # don't you always need to deselect? self.mesh_object.select_set(state = True) bpy.ops.object.delete() self.mesh_object = None # Delete existing materials. if not self.mesh_material is None: bpy.data.materials.remove(self.mesh_material) if bpy.data.objects.get("arrow_Mesh") is None: #load the invisible arrow print('reading arrow from file') #diagnostic, REMOVE arrowpath = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'arrow.obj') # find arrow.obj in same folder as this file. bpy.ops.import_scene.obj(filepath=arrowpath) bpy.context.scene.collection.objects.unlink(bpy.data.objects['arrow_Mesh']) #unlink so this arrow is invisible self.mesh_data = bpy.data.objects['arrow_Mesh'].data.copy() self.mesh_object = bpy.data.objects.new("vector", self.mesh_data ) bpy.context.scene.collection.objects.link(self.mesh_object)#must link before you select! self.mesh_object.select_set(state = True) # Assign a material to the surface. self.mesh_material = bpy.data.materials.new('MaterialMesh') self.mesh_data.materials.append(self.mesh_material) # inherits previous material, changing this probably changes all? # Make root_point and direction mathutil.Vector type vectors self.root_point = Vector(self.root_point) self.direction = Vector(self.direction) self.direction.normalize() if self.thin is not None: #print('thinning...') bpy.ops.transform.resize(value=(self.thin, 1, 1)) bpy.ops.transform.resize(value=(1/self.thin, 1/self.thin, 1/self.thin)) # rotate using Quaternions! Why Quaternions? Because awesome! self.mesh_object.location = self.root_point self.mesh_object.rotation_mode = 'QUATERNION' self.mesh_object.rotation_quaternion = self.direction.to_track_quat('X','Z') color_rgba = self.color if isinstance(self.color, str): if self.color == 'random': from numpy.random import rand color_rgba=(rand(), rand(), rand(), 1) else: from . import colors color_rgba = colors.string_to_rgba(self.color) #cleanup so old code with 3-tuples works if len(color_rgba) == 3: #print('Deprecation warning: from blender 2.80 you should RGBA not RGB') if isinstance(color_rgba, tuple): color_rgba = np.array(color_rgba + (1,)) if isinstance(color_rgba, list): color_rgba = np.array(color_rgba + [1]) if isinstance(color_rgba, np.ndarray): color_rgba = np.ones(4) color_rgba[0:3] = self.color self.mesh_material.diffuse_color = color_rgba self.mesh_object.active_material = self.mesh_material bpy.ops.transform.resize(value=(self.length, self.length, self.length)) def change_loc(self, newroot): """ updates the location of an existing vector. *root_point*: The location where the vector is 'rooted' """ import numpy as np from mathutils import Vector if not isinstance(newroot, tuple) and not isinstance(newroot, np.ndarray): print("Error: root_point not a tuple of numbers or ndarray.") return -1 self.root_point = Vector(newroot) self.mesh_object.location = self.root_point
def execute(self, context): object = context.object object.update_from_editmode() object_bm = bmesh.from_edit_mesh(object.data) object_bm.verts.ensure_lookup_table() object_bm.edges.ensure_lookup_table() object_bm.faces.ensure_lookup_table() selected_faces = [f for f in object_bm.faces if f.select] selected_edges = [e for e in object_bm.edges if e.select] selected_verts = [v for v in object_bm.verts if v.select] selection_center = Vector() if len(selected_edges) == 0: self.report({'WARNING'}, "Please select edges.") return {'CANCELLED'} try: cache_loops = get_cache(self.as_pointer(), "loops") loops = [] for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in cache_loops: loops.append((([object_bm.verts[v] for v in loop_verts], [object_bm.edges[e] for e in loop_edges], [object_bm.faces[f] for f in loop_faces]), is_loop_cyclic, is_loop_boundary)) except CacheException: loops = get_loops(selected_edges, selected_faces) if loops: cache_loops = [] for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in loops: cache_loops.append((([v.index for v in loop_verts], [e.index for e in loop_edges], [f.index for f in loop_faces]), is_loop_cyclic, is_loop_boundary)) set_cache(self.as_pointer(), "loops", cache_loops) if loops is None: self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).") return {'CANCELLED'} for vert in selected_verts: selection_center += vert.co selection_center /= len(selected_verts) object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False) refresh_icons() shape_bm = bmesh.new() for loop_idx, ((loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary) in enumerate(loops): if len(loop_edges) < 3: continue loop_verts_len = len(loop_verts) shape_verts = None shape_edges = None try: cache_verts = get_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx)) tmp_vert = None for i, cache_vert in enumerate(cache_verts): new_vert = shape_bm.verts.new(cache_vert) if i > 0: shape_bm.edges.new((tmp_vert, new_vert)) tmp_vert = new_vert shape_verts = shape_bm.verts[:] shape_bm.edges.new((shape_verts[-1], shape_verts[0])) shape_edges = shape_bm.edges[:] except CacheException: if self.shape == "CIRCLE": a = sum([e.calc_length() for e in loop_edges]) / loop_verts_len diameter = a / (2 * math.sin(math.pi / loop_verts_len)) shape_segments = loop_verts_len + self.span shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments, diameter=diameter) shape_verts = shape_verts["verts"] shape_edges = shape_bm.edges[:] elif self.shape == "RECTANGLE": if loop_verts_len % 2 > 0: self.report({'WARNING'}, "An odd number of edges.") del shape_bm return {'FINISHED'} size = sum([e.calc_length() for e in loop_edges]) size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b) if seg_a % 1 > 0: self.report({'WARNING'}, "Incorrect sides ratio.") seg_a += 1 seg_b += 2 seg_a = int(seg_a) if self.is_square: size_a = (size_a + size_b) / 2 size_b = size_a seg_len_a = size_a / seg_a seg_len_b = size_b / seg_b for i in range(seg_a): shape_bm.verts.new(Vector((size_b / 2 * -1, seg_len_a * i - (size_a / 2), 0))) for i in range(seg_b): shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2, 0))) for i in range(seg_a, 0, -1): shape_bm.verts.new(Vector((size_b / 2, seg_len_a * i - (size_a / 2), 0))) for i in range(seg_b, 0, -1): shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2 * -1, 0))) shape_verts = shape_bm.verts[:] for i in range(len(shape_verts)): shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)])) shape_edges = shape_bm.edges[:] elif self.shape == "PATTERN": pattern_idx = context.scene.perfect_shape.active_pattern pattern = context.scene.perfect_shape.patterns[int(pattern_idx)] if len(pattern.verts) == 0: self.report({'WARNING'}, "Empty Pattern Data.") del shape_bm return {'FINISHED'} if len(pattern.verts) != len(loop_verts): self.report({'WARNING'}, "Pattern and loop vertices count must be the same.") del shape_bm return {'FINISHED'} for pattern_vert in pattern.verts: shape_bm.verts.new(Vector(pattern_vert.co)) shape_verts = shape_bm.verts[:] for i in range(len(shape_verts)): shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)])) shape_edges = shape_bm.edges[:] elif self.shape == "OBJECT": if self.target in bpy.data.objects: shape_object = bpy.data.objects[self.target] shape_bm.from_object(shape_object, context.scene) loops = get_loops(shape_bm.edges[:]) if not loops or len(loops) > 1: self.report({'WARNING'}, "Wrong mesh data.") del shape_bm return {'FINISHED'} if len(loops[0][0][0]) > len(loop_verts): self.report({'WARNING'}, "Shape and loop vertices count must be the same.") del shape_bm return {'FINISHED'} shape_verts = loops[0][0][0] shape_edges = loops[0][0][1] if shape_verts: set_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx), [v.co.copy() for v in shape_verts]) if shape_verts is not None and len(shape_verts) > 0: if context.space_data.pivot_point == "CURSOR": center = object.matrix_world.copy() * context.scene.cursor_location.copy() else: temp_bm = bmesh.new() for loop_vert in loop_verts: temp_bm.verts.new(loop_vert.co.copy()) temp_verts = temp_bm.verts[:] for i in range(len(temp_verts)): temp_bm.edges.new((temp_verts[i], temp_verts[(i + 1) % len(temp_verts)])) temp_bm.faces.new(temp_bm.verts) temp_bm.faces.ensure_lookup_table() if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER': center = temp_bm.faces[0].calc_center_bounds() else: center = temp_bm.faces[0].calc_center_median() del temp_bm context.scene.perfect_shape.preview_verts_count = loop_verts_len + self.span if self.projection == "NORMAL": forward = calculate_normal([v.co.copy() for v in loop_verts]) normal_forward = reduce( lambda v1, v2: v1.normal.copy() + v2.normal.copy() if isinstance(v1, bmesh.types.BMVert) else v1.copy() + v2.normal.copy(), loop_verts).normalized() if normal_forward.angle(forward) - math.pi / 2 > 1e-6: forward.negate() else: forward = Vector([v == self.projection for v in ["X", "Y", "Z"]]) if self.invert_projection: forward.negate() matrix_rotation = forward.to_track_quat('Z', 'Y').to_matrix().to_4x4() matrix_translation = Matrix.Translation(center) bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1)) * (1 + self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation * matrix_rotation) if not is_clockwise(forward, center, loop_verts): loop_verts.reverse() loop_edges.reverse() if not is_clockwise(forward, center, shape_verts): shape_verts.reverse() correct_angle_m = 1 shift_m = 1 if context.space_data.pivot_point != "INDIVIDUAL_ORIGINS": if selection_center.dot(center.cross(forward)) >= 0: # if (center.cross(forward)).angle(selection_center) - math.pi/2 <= 1e-6: correct_angle_m = -1 shift_m = -1 loop_verts_co_2d, shape_verts_co_2d = None, None if loop_verts_co_2d is None: loop_verts_co_2d = [(matrix_rotation.transposed() * v.co).to_2d() for v in loop_verts] shape_verts_co_2d = [(matrix_rotation.transposed() * v.co).to_2d() for v in shape_verts] loop_angle = box_fit_2d(loop_verts_co_2d) shape_angle = box_fit_2d(shape_verts_co_2d) # if round(loop_angle, 4) == round(math.pi, 4): # loop_angle = 0 correct_angle = 0 if self.loop_rotation: correct_angle = loop_angle * correct_angle_m if round(loop_angle, 3) == round(shape_angle, 3): correct_angle -= shape_angle if self.shape_rotation: correct_angle -= shape_angle if correct_angle != 0: bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center, matrix=Matrix.Rotation(-correct_angle * correct_angle_m, 3, forward)) kd_tree = mathutils.kdtree.KDTree(len(loop_verts)) for idx, loop_vert in enumerate(loop_verts): kd_tree.insert(loop_vert.co, idx) kd_tree.balance() shape_first_idx = kd_tree.find(shape_verts[0].co)[1] shift = shape_first_idx + self.shift * shift_m if shift != 0: loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)] if self.rotation != 0: bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center, matrix=Matrix.Rotation(-self.rotation * correct_angle_m, 3, forward)) bmesh.ops.translate(shape_bm, vec=self.shape_translation, verts=shape_bm.verts) center = Matrix.Translation(self.shape_translation) * center if not is_loop_boundary and self.use_ray_cast: for shape_vert in shape_verts: co = shape_vert.co ray_cast_data = object_bvh.ray_cast(co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(co, -forward) if ray_cast_data[0] is not None: shape_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): vert.co = vert.co.lerp(shape_verts[idx].co, self.factor / 100) if not is_loop_boundary and is_loop_cyclic and loop_faces: if self.fill_type != "ORIGINAL": smooth = loop_faces[0].smooth bmesh.ops.delete(object_bm, geom=loop_faces, context=5) loop_faces = [] center_vert = object_bm.verts.new(center) if self.use_ray_cast: ray_cast_data = object_bvh.ray_cast(center_vert.co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward) if ray_cast_data[0] is not None: center_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx + 1) % loop_verts_len])) new_face.smooth = smooth loop_faces.append(new_face) bmesh.ops.recalc_face_normals(object_bm, faces=loop_faces) if self.outset > 0.0: outset_region_faces = bmesh.ops.inset_region(object_bm, faces=loop_faces, thickness=self.outset, use_even_offset=True, use_interpolate=True, use_outset=True) if self.extrude == 0: verts = loop_verts[:] for face in loop_faces: for vert in face.verts: if vert not in verts: verts.append(vert) if self.fill_flatten: matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=loop_verts) bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) if self.inset > 0.0: bmesh.ops.inset_region(object_bm, faces=loop_faces, thickness=self.inset, use_even_offset=True, use_interpolate=True) if self.fill_type == "HOLE": bmesh.ops.delete(object_bm, geom=loop_faces, context=5) elif self.fill_type == "NGON": bmesh.utils.face_join(loop_faces) else: verts = [] edges = [] faces = [] side_faces = [] side_edges = [] extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=loop_faces, use_keep_orig=True) bmesh.ops.delete(object_bm, geom=loop_faces, context=5) for geom in extrude_geom["geom"]: if isinstance(geom, bmesh.types.BMVert): verts.append(geom) elif isinstance(geom, bmesh.types.BMFace): faces.append(geom) elif isinstance(geom, bmesh.types.BMEdge): edges.append(geom) for edge in loop_edges: for face in edge.link_faces: if any((e for e in face.edges if e in edges)): side_faces.append(face) for edge in face.edges: if edge not in side_edges and edge not in edges and edge not in loop_edges: side_edges.append(edge) if self.fill_flatten: matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts) bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) bmesh.ops.translate(object_bm, verts=verts, vec=forward * self.extrude) cuts = max(self.cuts, self.cuts_rings) if cuts > 0: sub = bmesh.ops.subdivide_edges(object_bm, edges=side_edges, cuts=cuts) loop_verts = [] first_verts = loop_edges[0].verts[:] for edge in loop_edges: if edge == loop_edges[0]: continue if edge == loop_edges[1]: if first_verts[0] == edge.verts[0]: first_verts.reverse() loop_verts.extend(first_verts) for vert in edge.verts: if vert not in loop_verts: loop_verts.append(vert) split_edges = [] for geom in sub["geom_split"]: if isinstance(geom, bmesh.types.BMEdge): split_edges.append(geom) skip_edges = [] for vert in loop_verts: for edge in vert.link_edges: if edge not in skip_edges and edge not in split_edges: skip_edges.append(edge) start = self.cuts_shift % loop_verts_len stop = self.cuts_shift % loop_verts_len verts_list = loop_verts[start:] + loop_verts[:stop] for i in range(self.cuts): new_split_edges = [] for idx, vert in enumerate(verts_list): if idx < self.cuts_len + i or idx >= loop_verts_len - i: continue for edge in vert.link_edges: if edge in split_edges: other_vert = edge.other_vert(vert) bmesh.ops.weld_verts(object_bm, targetmap={other_vert: vert}) for edge in vert.link_edges: if edge not in new_split_edges and edge not in skip_edges: new_split_edges.append(edge) split_edges = new_split_edges cut_edges = [] dissolve_edges = [] cut_skip = [] prev_edge = None first_join = True for i in range(self.cuts_rings): if i >= self.cuts: break split_edges = [] for idx, vert in enumerate(verts_list): if idx < self.cuts_len + i or idx >= loop_verts_len - i: for edge in vert.link_edges: if edge not in skip_edges and edge not in cut_skip: if prev_edge is not None and idx not in range(self.cuts_len): if not any((v for v in edge.verts if v in prev_edge.verts)): if edge not in dissolve_edges: dissolve_edges.append(edge) if edge not in dissolve_edges and edge not in split_edges: split_edges.append(edge) cut_skip.append(edge) # edge.select_set(True) prev_edge = edge if first_join and len(cut_edges) == 1: cut_edges[0].extend(split_edges) first_join = False else: cut_edges.append(split_edges) # if dissolve_edges: # bmesh.ops.dissolve_edges(object_bm, edges=dissolve_edges) # inner_verts = [] # for i, split_edges in enumerate(cut_edges): # for edge in split_edges: # sub = bmesh.ops.subdivide_edges(object_bm, edges=[edge], cuts=self.cuts-i) # sub_verts = [v for v in sub["geom_inner"] if isinstance(v, bmesh.types.BMVert)] # inner_verts.append(sub_verts) if self.side_inset > 0.0: inset_region = bmesh.ops.inset_region(object_bm, faces=side_faces, thickness=self.side_inset, use_even_offset=True, use_interpolate=True) if self.inset > 0.0: inset_region_faces = bmesh.ops.inset_region(object_bm, faces=faces, thickness=self.inset, use_even_offset=True, use_interpolate=True) if self.fill_type == "HOLE": bmesh.ops.delete(object_bm, geom=faces, context=5) elif self.fill_type == "NGON": bmesh.utils.face_join(faces) shape_bm.clear() del object_bvh object_bm.normal_update() bmesh.update_edit_mesh(object.data) return {'FINISHED'}
def SetupScene(): #Add Camera pi = math.pi camera_loc = [ float(l) for l in d_camera["camera"]["camera_location"].split(",") ] camera_rot = [ float(l) for l in d_camera["camera"]["camera_rotation"].split(",") ] bpy.ops.object.camera_add( enter_editmode=False, align='VIEW', location=camera_loc, rotation=camera_rot) #((90*pi)/180, 0, (270*pi)/180) currentCameraObj = bpy.data.objects[bpy.context.active_object.name] scene.camera = currentCameraObj if d_camera["camera"]["camera_lensType"] == "Orthographic": currentCameraObj.data.type = 'ORTHO' #data.type currentCameraObj.data.ortho_scale = float( d_camera["camera"]["camera_lensLength"]) else: #scene_camera.data.sensor_fit = "HORIZONTAL" #scene_camera.data.sensor_width = 35 #scene_camera.data.lens = float(d_camera["camera"]["camera_lensLength"]) currentCameraObj.data.lens_unit = 'FOV' currentCameraObj.data.angle = float( d_camera["camera"]["camera_lensLength"]) #Align View to Main Camera area = next(area for area in bpy.context.screen.areas if area.type == 'VIEW_3D') area.spaces[0].region_3d.view_perspective = 'CAMERA' #Exposure scene.view_settings.exposure = float( d_settings["camera"]["camera_exposure"]) #Transparency if bool(d_settings["camera"]["camera_transparent"]): scene.render.film_transparent = True #Clipping clippingNear = float(d_camera["camera"]["camera_clippingNear"]) clippingFar = float(d_camera["camera"]["camera_clippingFar"]) currentCameraObj.data.clip_start = clippingNear currentCameraObj.data.clip_end = clippingFar #Add Sun if bool(d_camera["world"]["sun_enabled"]): light_data = bpy.data.lights.new(name="Sun", type='SUN') light_data.energy = 2.5 light_data.angle = math.radians(2.5) light_object = bpy.data.objects.new(name="Sun", object_data=light_data) bpy.context.collection.objects.link(light_object) bpy.context.view_layer.objects.active = light_object sun_direction = [ float(l) for l in d_camera["world"]["sun_vector"].split(",") ] sun_direction = Vector(sun_direction) light_object.rotation_mode = 'QUATERNION' light_object.rotation_quaternion = sun_direction.to_track_quat( 'Z', 'Y') #Skylight if bool(d_camera["world"]["ambientocclusion_enabled"]): bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.1 bpy.context.scene.world.light_settings.distance = 1000 #Add Ground Plane if bool(d_camera["world"]["groundplane_enabled"]): bpy.ops.mesh.primitive_plane_add() plane = bpy.context.selected_objects[0] plane.location = (0.0, 0.0, float(d_camera["world"]["groundplane_height"])) plane.scale = (100000, 100000, 100000) #Sky if d_settings["world"]["world_HDRI"] != "Colour": hdri_filepath = filepath + "HDRI\\" + d_settings["world"]["world_HDRI"] scene.world.use_nodes = True world = bpy.data.worlds["World"] nodes = world.node_tree.nodes links = world.node_tree.links world_HDRIPower = float(d_settings["world"]["world_HDRIPower"]) bg_node = world.node_tree.nodes['Background'] bg_node.inputs[1].default_value = world_HDRIPower env_node = world.node_tree.nodes.new('ShaderNodeTexEnvironment') env_node.image = bpy.data.images.load(hdri_filepath) env_node.location = (bg_node.location.x - 300, bg_node.location.y) links.new(env_node.outputs['Color'], bg_node.inputs['Color']) world_rotation = float(d_settings["world"]["world_HDRIRotation"]) map_node = world.node_tree.nodes.new('ShaderNodeMapping') map_node.inputs[2].default_value[2] = math.radians(world_rotation) map_node.location = (env_node.location.x - 200, env_node.location.y) links.new(map_node.outputs['Vector'], env_node.inputs['Vector']) world_HDRIBlur = float(d_settings["world"]["world_HDRIBlur"]) add_node = world.node_tree.nodes.new('ShaderNodeMixRGB') add_node.blend_type = "ADD" add_node.inputs[0].default_value = world_HDRIBlur add_node.location = (map_node.location.x - 200, map_node.location.y) links.new(add_node.outputs['Color'], map_node.inputs['Vector']) subtract_node = world.node_tree.nodes.new('ShaderNodeMixRGB') subtract_node.blend_type = "SUBTRACT" subtract_node.inputs[0].default_value = 1.0 subtract_node.location = (add_node.location.x - 200, add_node.location.y - 200) links.new(subtract_node.outputs['Color'], add_node.inputs['Color2']) noise_node = world.node_tree.nodes.new('ShaderNodeTexNoise') noise_node.inputs[2].default_value = 10000 noise_node.location = (subtract_node.location.x - 200, subtract_node.location.y) links.new(noise_node.outputs['Color'], subtract_node.inputs['Color2']) text_node = world.node_tree.nodes.new('ShaderNodeTexCoord') text_node.location = (noise_node.location.x - 200, noise_node.location.y + 200) links.new(text_node.outputs['Generated'], add_node.inputs['Color1']) links.new(text_node.outputs['Generated'], noise_node.inputs['Vector'])
def execute(self, context): object = context.object object.update_from_editmode() self.pivot_point = context.space_data.pivot_point self.transform_orientation = context.space_data.transform_orientation object_bm = bmesh.from_edit_mesh(object.data) object_bm.verts.ensure_lookup_table() object_bm.edges.ensure_lookup_table() object_bm.faces.ensure_lookup_table() selected_faces = [f for f in object_bm.faces if f.select] selected_edges = [e for e in object_bm.edges if e.select] selected_verts = [v for v in object_bm.verts if v.select] if len(selected_edges) == 0: self.report({'WARNING'}, "Please select edges.") return {'CANCELLED'} try: cache_loops = get_cache(self.as_pointer(), "loops") loops = [] for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in cache_loops: loops.append((([object_bm.verts[v] for v in loop_verts], [object_bm.edges[e] for e in loop_edges], [object_bm.faces[f] for f in loop_faces]), is_loop_cyclic, is_loop_boundary)) except CacheException: loops = get_loops(selected_edges, selected_faces) if loops: cache_loops = [] for (loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary in loops: cache_loops.append((([v.index for v in loop_verts], [e.index for e in loop_edges], [f.index for f in loop_faces]), is_loop_cyclic, is_loop_boundary)) set_cache(self.as_pointer(), "loops", cache_loops) if loops is None: self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).") return {'CANCELLED'} selection_center = Vector() for vert in selected_verts: selection_center += vert.co selection_center /= len(selected_verts) object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False) refresh_icons() shape_bm = bmesh.new() for loop_idx, ((loop_verts, loop_edges, loop_faces), is_loop_cyclic, is_loop_boundary) in enumerate(loops): if len(loop_edges) < 3: continue loop_verts_len = len(loop_verts) shape_verts = None shape_edges = None try: cache_verts = get_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx)) tmp_vert = None for i, cache_vert in enumerate(cache_verts): new_vert = shape_bm.verts.new(cache_vert) if i > 0: shape_bm.edges.new((tmp_vert, new_vert)) tmp_vert = new_vert shape_verts = shape_bm.verts[:] shape_bm.edges.new((shape_verts[-1], shape_verts[0])) shape_edges = shape_bm.edges[:] except CacheException: if self.shape == "CIRCLE": a = sum([e.calc_length() for e in loop_edges]) / loop_verts_len diameter = a / (2 * math.sin(math.pi / loop_verts_len)) shape_segments = loop_verts_len + self.span shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments, radius=diameter/2) shape_verts = shape_verts["verts"] shape_edges = shape_bm.edges[:] elif self.shape == "RECTANGLE": if loop_verts_len % 2 > 0: self.report({'WARNING'}, "An odd number of edges.") del shape_bm return {'FINISHED'} size = sum([e.calc_length() for e in loop_edges]) size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b) if seg_a % 1 > 0: self.report({'WARNING'}, "Incorrect sides ratio.") seg_a += 1 seg_b += 2 seg_a = int(seg_a) if self.is_square: size_a = (size_a + size_b) / 2 size_b = size_a seg_len_a = size_a / seg_a seg_len_b = size_b / seg_b for i in range(seg_a): shape_bm.verts.new(Vector((size_b / 2 * -1, seg_len_a * i - (size_a / 2), 0))) for i in range(seg_b): shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2, 0))) for i in range(seg_a, 0, -1): shape_bm.verts.new(Vector((size_b / 2, seg_len_a * i - (size_a / 2), 0))) for i in range(seg_b, 0, -1): shape_bm.verts.new(Vector((seg_len_b * i - (size_b / 2), size_a / 2 * -1, 0))) shape_verts = shape_bm.verts[:] for i in range(len(shape_verts)): shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)])) shape_edges = shape_bm.edges[:] elif self.shape == "PATTERN": pattern_idx = context.scene.perfect_shape.active_pattern pattern = context.scene.perfect_shape.patterns[int(pattern_idx)] if len(pattern.verts) == 0: self.report({'WARNING'}, "Empty Pattern Data.") del shape_bm return {'FINISHED'} if len(pattern.verts) != len(loop_verts): self.report({'WARNING'}, "Pattern and loop vertices count must be the same.") del shape_bm return {'FINISHED'} for pattern_vert in pattern.verts: shape_bm.verts.new(Vector(pattern_vert.co)) shape_verts = shape_bm.verts[:] for i in range(len(shape_verts)): shape_bm.edges.new((shape_verts[i], shape_verts[(i + 1) % len(shape_verts)])) shape_edges = shape_bm.edges[:] elif self.shape == "OBJECT": if self.target in bpy.data.objects: shape_object = bpy.data.objects[self.target] shape_bm.from_object(shape_object, context.scene) loops = get_loops(shape_bm.edges[:]) if not loops or len(loops) > 1: self.report({'WARNING'}, "Wrong mesh data.") del shape_bm return {'FINISHED'} if len(loops[0][0][0]) > len(loop_verts): self.report({'WARNING'}, "Shape and loop vertices count must be the same.") del shape_bm return {'FINISHED'} shape_verts = loops[0][0][0] shape_edges = loops[0][0][1] if shape_verts: set_cache(self.as_pointer(), "shape_verts_{}".format(loop_idx), [v.co.copy() for v in shape_verts]) if shape_verts: context.scene.perfect_shape.preview_verts_count = loop_verts_len + self.span try: center = get_cache(self.as_pointer(), "P_{}_{}".format(self.pivot_point, loop_idx)) except CacheException: if self.pivot_point == "CURSOR": center = object.matrix_world.copy() * context.scene.cursor_location.copy() else: temp_bm = bmesh.new() for loop_vert in loop_verts: temp_bm.verts.new(loop_vert.co.copy()) temp_verts = temp_bm.verts[:] for i in range(len(temp_verts)): temp_bm.edges.new((temp_verts[i], temp_verts[(i + 1) % len(temp_verts)])) temp_bm.faces.new(temp_bm.verts) temp_bm.faces.ensure_lookup_table() if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER': center = temp_bm.faces[0].calc_center_bounds() else: center = temp_bm.faces[0].calc_center_median() del temp_bm set_cache(self.as_pointer(), "P_{}_{}".format(self.pivot_point, loop_idx), center) if self.projection == "NORMAL": forward = calculate_normal([v.co.copy() for v in loop_verts]) normal_forward = reduce( lambda v1, v2: v1.normal.copy() + v2.normal.copy() if isinstance(v1, bmesh.types.BMVert) else v1.copy() + v2.normal.copy(), loop_verts).normalized() if forward.angle(normal_forward) - math.pi / 2 >= 1e-6: forward.negate() else: forward = Vector([v == self.projection for v in ["X", "Y", "Z"]]) if self.invert_projection: forward.negate() rotation_m = 1 if context.space_data.pivot_point != "INDIVIDUAL_ORIGINS": if (center+selection_center).dot(forward) > 0: rotation_m = -1 # if center.cross(forward).angle(selection_center) >= math.pi / 2: # forward.negate() # if cross.dot(center) < 0: # forward.negate() # if(center + selection_center).dot(forward) < 0: # forward.negate() # matrix_rotation = forward.to_track_quat('Z', 'Y').to_matrix().to_4x4() # if (matrix_rotation * center).dot(matrix_rotation * (center + selection_center)) > 0: # forward.negate() # if loop_faces: # rotation_m = - 1 if not is_clockwise(forward, center, loop_verts): loop_verts.reverse() loop_edges.reverse() matrix_rotation = forward.to_track_quat('Z', 'Y').to_matrix().to_4x4() matrix_translation = Matrix.Translation(center) bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1)) * (1 + self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation * matrix_rotation) loop_verts_co_2d = [(v.co * matrix_rotation).to_2d() for v in loop_verts] shape_verts_co_2d = [(v.co * matrix_rotation).to_2d() for v in shape_verts] loop_angle = box_fit_2d(loop_verts_co_2d) shape_angle = box_fit_2d(shape_verts_co_2d) correct_angle = 0 if self.loop_rotation: correct_angle = loop_angle if self.shape_rotation: correct_angle += shape_angle if correct_angle != 0: bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center, matrix=Matrix.Rotation(-correct_angle, 3, forward)) kd_tree = mathutils.kdtree.KDTree(len(loop_verts)) for idx, loop_vert in enumerate(loop_verts): kd_tree.insert(loop_vert.co, idx) kd_tree.balance() shape_first_idx = kd_tree.find(shape_verts[0].co)[1] shift = shape_first_idx + self.shift if shift != 0: loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)] if self.rotation != 0: bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center, matrix=Matrix.Rotation(-self.rotation*rotation_m, 3, forward)) bmesh.ops.translate(shape_bm, vec=self.shape_translation, verts=shape_bm.verts) center = Matrix.Translation(self.shape_translation) * center if not is_loop_boundary and self.use_ray_cast: for shape_vert in shape_verts: co = shape_vert.co ray_cast_data = object_bvh.ray_cast(co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(co, -forward) if ray_cast_data[0] is not None: shape_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): vert.co = vert.co.lerp(shape_verts[idx].co, self.factor / 100) if not is_loop_boundary and is_loop_cyclic and loop_faces: if self.fill_type != "ORIGINAL": smooth = loop_faces[0].smooth bmesh.ops.delete(object_bm, geom=loop_faces, context=5) loop_faces = [] center_vert = object_bm.verts.new(center) if self.use_ray_cast: ray_cast_data = object_bvh.ray_cast(center_vert.co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward) if ray_cast_data[0] is not None: center_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx + 1) % loop_verts_len])) new_face.smooth = smooth loop_faces.append(new_face) bmesh.ops.recalc_face_normals(object_bm, faces=loop_faces) if self.outset > 0.0: outset_region_faces = bmesh.ops.inset_region(object_bm, faces=loop_faces, thickness=self.outset, use_even_offset=True, use_interpolate=True, use_outset=True) if self.extrude == 0: verts = loop_verts[:] for face in loop_faces: for vert in face.verts: if vert not in verts: verts.append(vert) if self.fill_flatten: matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=loop_verts) bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) if self.inset > 0.0: bmesh.ops.inset_region(object_bm, faces=loop_faces, thickness=self.inset, use_even_offset=True, use_interpolate=True) if self.fill_type == "HOLE": bmesh.ops.delete(object_bm, geom=loop_faces, context=5) elif self.fill_type == "NGON": bmesh.utils.face_join(loop_faces) else: verts = [] edges = [] faces = [] side_faces = [] side_edges = [] extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=loop_faces, use_keep_orig=True) bmesh.ops.delete(object_bm, geom=loop_faces, context=5) for geom in extrude_geom["geom"]: if isinstance(geom, bmesh.types.BMVert): verts.append(geom) elif isinstance(geom, bmesh.types.BMFace): faces.append(geom) elif isinstance(geom, bmesh.types.BMEdge): edges.append(geom) for edge in loop_edges: for face in edge.link_faces: if any((e for e in face.edges if e in edges)): side_faces.append(face) for edge in face.edges: if edge not in side_edges and edge not in edges and edge not in loop_edges: side_edges.append(edge) if self.fill_flatten: matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts) bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) bmesh.ops.translate(object_bm, verts=verts, vec=forward * self.extrude) cuts = max(self.cuts, self.cuts_rings) if cuts > 0: sub = bmesh.ops.subdivide_edges(object_bm, edges=side_edges, cuts=cuts) loop_verts = [] first_verts = loop_edges[0].verts[:] for edge in loop_edges: if edge == loop_edges[0]: continue if edge == loop_edges[1]: if first_verts[0] == edge.verts[0]: first_verts.reverse() loop_verts.extend(first_verts) for vert in edge.verts: if vert not in loop_verts: loop_verts.append(vert) split_edges = [] for geom in sub["geom_split"]: if isinstance(geom, bmesh.types.BMEdge): split_edges.append(geom) skip_edges = [] for vert in loop_verts: for edge in vert.link_edges: if edge not in skip_edges and edge not in split_edges: skip_edges.append(edge) start = self.cuts_shift % loop_verts_len stop = self.cuts_shift % loop_verts_len verts_list = loop_verts[start:] + loop_verts[:stop] for i in range(self.cuts): new_split_edges = [] for idx, vert in enumerate(verts_list): if idx < self.cuts_len+i or idx >= loop_verts_len-i: continue for edge in vert.link_edges: if edge in split_edges: other_vert = edge.other_vert(vert) bmesh.ops.weld_verts(object_bm, targetmap={other_vert: vert}) for edge in vert.link_edges: if edge not in new_split_edges and edge not in skip_edges: new_split_edges.append(edge) split_edges = new_split_edges cut_edges = [] dissolve_edges = [] cut_skip = [] prev_edge = None first_join = True for i in range(self.cuts_rings): if i >= self.cuts: break split_edges = [] for idx, vert in enumerate(verts_list): if idx < self.cuts_len+i or idx >= loop_verts_len-i: for edge in vert.link_edges: if edge not in skip_edges and edge not in cut_skip: if prev_edge is not None and idx not in range(self.cuts_len): if not any((v for v in edge.verts if v in prev_edge.verts)): if edge not in dissolve_edges: dissolve_edges.append(edge) if edge not in dissolve_edges and edge not in split_edges: split_edges.append(edge) cut_skip.append(edge) #edge.select_set(True) prev_edge = edge if first_join and len(cut_edges) == 1: cut_edges[0].extend(split_edges) first_join = False else: cut_edges.append(split_edges) # if dissolve_edges: # bmesh.ops.dissolve_edges(object_bm, edges=dissolve_edges) # inner_verts = [] # for i, split_edges in enumerate(cut_edges): # for edge in split_edges: # sub = bmesh.ops.subdivide_edges(object_bm, edges=[edge], cuts=self.cuts-i) # sub_verts = [v for v in sub["geom_inner"] if isinstance(v, bmesh.types.BMVert)] # inner_verts.append(sub_verts) if self.side_inset > 0.0: inset_region = bmesh.ops.inset_region(object_bm, faces=side_faces, thickness=self.side_inset, use_even_offset=True, use_interpolate=True) if self.inset > 0.0: inset_region_faces = bmesh.ops.inset_region(object_bm, faces=faces, thickness=self.inset, use_even_offset=True, use_interpolate=True) if self.fill_type == "HOLE": bmesh.ops.delete(object_bm, geom=faces, context=5) elif self.fill_type == "NGON": bmesh.utils.face_join(faces) if not selected_faces and self.extrude != 0: self.report({'WARNING'}, "Please select faces to extrude.") shape_bm.clear() del object_bvh object_bm.normal_update() bmesh.update_edit_mesh(object.data) return {'FINISHED'}
def look_at(obj, point): direction = Vector(point) - obj.location rot_quat = direction.to_track_quat('-Z', 'Y') obj.rotation_euler = rot_quat.to_euler()
def execute(self, context): object = context.object object.update_from_editmode() object_bm = bmesh.from_edit_mesh(object.data) selected_edges = [e for e in object_bm.edges if e.select] selected_verts = [v for v in object_bm.verts if v.select] selected_verts_fin = [] if len(selected_edges) == 0: self.report({'WARNING'}, "Please select edges.") return {'CANCELLED'} loops = prepare_loops(selected_edges[:]) if loops is None: self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).") return {'CANCELLED'} object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False) for (loop_verts, loop_edges), is_loop_cyclic, is_loop_boundary in loops: if len(loop_edges) < 3: continue loop_verts_len = len(loop_verts) if self.projection == "NORMAL": if is_loop_boundary: forward = calculate_normal([v.co for v in loop_verts]) else: forward = reduce( lambda v1, v2: v1.normal.copy() if isinstance(v1, bmesh.types.BMVert) else v1.copy() + v2.normal.copy(), loop_verts).normalized() else: forward = Vector([v == self.projection for v in ["X", "Y", "Z"]]) if self.invert_projection: forward.negate() shape_bm = bmesh.new() if context.space_data.pivot_point == "CURSOR": center = object.matrix_world.copy() * context.scene.cursor_location.copy() else: for loop_vert in loop_verts: shape_bm.verts.new(loop_vert.co.copy()) shape_bm.faces.new(shape_bm.verts) shape_bm.faces.ensure_lookup_table() if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER': center = shape_bm.faces[0].calc_center_bounds() else: center = shape_bm.faces[0].calc_center_median() shape_bm.clear() matrix_rotation = forward.to_track_quat('Z', 'X').to_matrix().to_4x4() matrix_translation = Matrix.Translation(center) shape_bm = bmesh.new() shape_verts = None if self.shape == "CIRCLE": diameter = sum([e.calc_length() for e in loop_edges]) / (2*math.pi) diameter += self.offset shape_segments = loop_verts_len + self.span shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments, diameter=diameter, matrix=matrix_translation*matrix_rotation) shape_verts = shape_verts["verts"] elif self.shape == "RECTANGLE": if loop_verts_len % 2 > 0: self.report({'WARNING'}, "An odd number of edges.") del shape_bm return {'FINISHED'} size = sum([e.calc_length() for e in loop_edges]) size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b) if seg_a % 1 > 0: self.report({'WARNING'}, "Incorrect sides ratio.") seg_a += 1 seg_b += 2 seg_a = int(seg_a) if self.is_square: size_a = (size_a + size_b) / 2 size_b = size_a seg_len_a = size_a / seg_a seg_len_b = size_b / seg_b for i in range(seg_a): shape_bm.verts.new(Vector((size_b/2*-1, seg_len_a*i-(size_a/2), 0))) for i in range(seg_b): shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2, 0))) for i in range(seg_a, 0, -1): shape_bm.verts.new(Vector((size_b/2, seg_len_a*i-(size_a/2), 0))) for i in range(seg_b, 0, -1): shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2*-1, 0))) shape_verts = shape_bm.verts[:] bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation) elif self.shape == "PATTERN": if len(object.perfect_pattern) == 0: self.report({'WARNING'}, "Empty Pattern Data.") del shape_bm return {'FINISHED'} if len(object.perfect_pattern) != len(loop_verts): self.report({'WARNING'}, "Pattern and loop vertices count must be the same.") del shape_bm return {'FINISHED'} for pattern_vert in object.perfect_pattern: shape_bm.verts.new(Vector(pattern_vert.co)) shape_verts = shape_bm.verts[:] bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation) elif self.shape == "OBJECT": if self.target in bpy.data.objects: shape_object = bpy.data.objects[self.target] shape_bm.from_object(shape_object, context.scene) loops = prepare_loops(shape_bm.edges[:]) if loops is None or len(loops) > 1: self.report({'WARNING'}, "Wrong mesh data.") del shape_bm return {'FINISHED'} shape_verts = shape_bm.verts[:] bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation) if shape_verts is not None and len(shape_verts) > 0: if not is_clockwise(forward, center, loop_verts): loop_verts.reverse() loop_edges.reverse() if not is_clockwise(forward, center, shape_verts): shape_verts.reverse() loop_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in loop_verts]) shape_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in shape_verts]) if abs(abs(loop_angle) - abs(shape_angle)) <= 0.01: loop_angle = 0 correct_angle = loop_angle + self.rotation if self.shape_rotation: correct_angle -= shape_angle if correct_angle != 0 and not self.active_as_first: bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center, matrix=Matrix.Rotation(-correct_angle, 3, forward)) active = object_bm.select_history.active if self.active_as_first and isinstance(active, bmesh.types.BMVert) and active in loop_verts: shift = loop_verts.index(active) else: kd_tree = mathutils.kdtree.KDTree(len(loop_verts)) for idx, loop_vert in enumerate(loop_verts): kd_tree.insert(loop_vert.co, idx) kd_tree.balance() shape_first_idx = kd_tree.find(shape_verts[0].co)[1] shift = shape_first_idx + self.shift if shift != 0: loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)] if not is_loop_boundary and self.use_ray_cast: for shape_vert in shape_verts: co = shape_vert.co ray_cast_data = object_bvh.ray_cast(co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(co, -forward) if ray_cast_data[0] is not None: shape_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): vert.co = vert.co.lerp(shape_verts[idx].co, self.factor) if not is_loop_boundary and is_loop_cyclic: object_bm.select_flush_mode() select_only(object_bm, loop_edges, {"EDGE"}) bpy.ops.mesh.loop_to_region() # Ugly. inset_faces = [f for f in object_bm.faces[:] if f.select] if self.fill_type != "ORIGINAL": smooth = inset_faces[0].smooth bmesh.ops.delete(object_bm, geom=inset_faces, context=5) inset_faces = [] center_vert = object_bm.verts.new(center) if self.use_ray_cast: ray_cast_data = object_bvh.ray_cast(center_vert.co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward) if ray_cast_data[0] is not None: center_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx+1) % loop_verts_len])) new_face.smooth = smooth inset_faces.append(new_face) bmesh.ops.recalc_face_normals(object_bm, faces=inset_faces) selected_co = [] for vert in selected_verts: if vert.is_valid: selected_co.append(vert.co.copy()) outset_region_faces = [] if self.outset > 0.0: outset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces, thickness=self.outset, use_even_offset=True, use_interpolate=True, use_outset=True) outset_region_faces = outset_region_faces["faces"] inset_region_faces = [] if self.inset > 0.0: inset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces, thickness=self.inset, use_even_offset=True, use_interpolate=True) inset_region_faces = inset_region_faces["faces"] new_selected_verts = [] for face in set(inset_region_faces+inset_faces): for vert in face.verts: if vert.co in selected_co: new_selected_verts.append(vert) selected_co.remove(vert.co) selected_verts_fin.append(new_selected_verts) select_only(object_bm, new_selected_verts, {"EDGE"}) if self.fill_type == "HOLE": bmesh.ops.delete(object_bm, geom=inset_faces, context=5) inset_faces = [] elif self.fill_type == "NGON": inset_faces = [bmesh.utils.face_join(inset_faces)] if self.fill_flatten and self.extrude == 0: verts = list(set(reduce(lambda v1, v2: list(v1) + list(v2), [v.verts for v in inset_region_faces + inset_faces]))) matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts) bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) bmesh.ops.recalc_face_normals(object_bm, faces=outset_region_faces+inset_region_faces+inset_faces) if self.extrude != 0: extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=inset_region_faces+inset_faces) verts = [v for v in extrude_geom['geom'] if isinstance(v, bmesh.types.BMVert)] if self.fill_flatten: matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts) bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) bmesh.ops.delete(object_bm, geom=inset_region_faces+inset_faces, context=5) bmesh.ops.translate(object_bm, verts=verts, vec=forward * self.extrude) del shape_bm if selected_verts_fin: select_only(object_bm, reduce(lambda x, y: x + y, selected_verts_fin), {"EDGE"}) object_bm.select_flush(True) del object_bvh bmesh.update_edit_mesh(object.data) return {'FINISHED'}
def look_at_model(self, target): self.camera.rotation_mode = 'QUATERNION' looking_direction = Vector(self.camera.location) - Vector(target.location) - Vector([0, 0, parameters.HEIGHT/2]) self.camera.rotation_quaternion = looking_direction.to_track_quat('Z', 'Y') self.camera.rotation_mode = 'XYZ'
def execute(self, context): object = context.object object.update_from_editmode() object_bm = bmesh.from_edit_mesh(object.data) selected_edges = [e for e in object_bm.edges if e.select] selected_verts = [v for v in object_bm.verts if v.select] selected_verts_fin = [] if len(selected_edges) == 0: self.report({'WARNING'}, "Please select edges.") return {'CANCELLED'} loops = prepare_loops(selected_edges[:]) if loops is None: self.report({'WARNING'}, "Please select boundary loop(s) of selected area(s).") return {'CANCELLED'} object_bvh = mathutils.bvhtree.BVHTree.FromObject(object, context.scene, deform=False) refresh_icons() for (loop_verts, loop_edges), is_loop_cyclic, is_loop_boundary in loops: if len(loop_edges) < 3: continue loop_verts_len = len(loop_verts) context.window_manager.perfect_shape.preview_verts_count = loop_verts_len if self.projection == "NORMAL": if is_loop_boundary: forward = calculate_normal([v.co for v in loop_verts]) else: forward = reduce( lambda v1, v2: v1.normal.copy() + v2.normal.copy() if isinstance(v1, bmesh.types.BMVert) else v1.copy() + v2.normal.copy(), loop_verts).normalized() else: forward = Vector([v == self.projection for v in ["X", "Y", "Z"]]) if self.invert_projection: forward.negate() shape_bm = bmesh.new() if context.space_data.pivot_point == "CURSOR": center = object.matrix_world.copy() * context.scene.cursor_location.copy() else: for loop_vert in loop_verts: shape_bm.verts.new(loop_vert.co.copy()) shape_bm.faces.new(shape_bm.verts) shape_bm.faces.ensure_lookup_table() if context.space_data.pivot_point == 'BOUNDING_BOX_CENTER': center = shape_bm.faces[0].calc_center_bounds() else: center = shape_bm.faces[0].calc_center_median() shape_bm.clear() matrix_rotation = forward.to_track_quat('Z', 'X').to_matrix().to_4x4() matrix_translation = Matrix.Translation(center) shape_bm = bmesh.new() shape_verts = None if self.shape == "CIRCLE": diameter = sum([e.calc_length() for e in loop_edges]) / (2*math.pi) diameter += self.offset shape_segments = loop_verts_len + self.span shape_verts = bmesh.ops.create_circle(shape_bm, segments=shape_segments, diameter=diameter, matrix=matrix_translation*matrix_rotation) shape_verts = shape_verts["verts"] elif self.shape == "RECTANGLE": if loop_verts_len % 2 > 0: self.report({'WARNING'}, "An odd number of edges.") del shape_bm return {'FINISHED'} size = sum([e.calc_length() for e in loop_edges]) size_a = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a size_b = (size / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b seg_a = (loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_a seg_b = int((loop_verts_len / 2) / (self.ratio_a + self.ratio_b) * self.ratio_b) if seg_a % 1 > 0: self.report({'WARNING'}, "Incorrect sides ratio.") seg_a += 1 seg_b += 2 seg_a = int(seg_a) if self.is_square: size_a = (size_a + size_b) / 2 size_b = size_a seg_len_a = size_a / seg_a seg_len_b = size_b / seg_b for i in range(seg_a): shape_bm.verts.new(Vector((size_b/2*-1, seg_len_a*i-(size_a/2), 0))) for i in range(seg_b): shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2, 0))) for i in range(seg_a, 0, -1): shape_bm.verts.new(Vector((size_b/2, seg_len_a*i-(size_a/2), 0))) for i in range(seg_b, 0, -1): shape_bm.verts.new(Vector((seg_len_b*i-(size_b/2), size_a/2*-1, 0))) shape_verts = shape_bm.verts[:] bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation) elif self.shape == "PATTERN": if len(object.perfect_pattern.vertices) == 0: self.report({'WARNING'}, "Empty Pattern Data.") del shape_bm return {'FINISHED'} if len(object.perfect_pattern.vertices) != len(loop_verts): self.report({'WARNING'}, "Pattern and loop vertices count must be the same.") del shape_bm return {'FINISHED'} for pattern_vert in object.perfect_pattern.vertices: shape_bm.verts.new(Vector(pattern_vert.co)) shape_verts = shape_bm.verts[:] bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation) elif self.shape == "OBJECT": if self.target in bpy.data.objects: shape_object = bpy.data.objects[self.target] shape_bm.from_object(shape_object, context.scene) loops = prepare_loops(shape_bm.edges[:]) if not loops or len(loops) > 1: self.report({'WARNING'}, "Wrong mesh data.") del shape_bm return {'FINISHED'} if len(loops[0][0][0]) != len(loop_verts): self.report({'WARNING'}, "Shape and loop vertices count must be the same.") del shape_bm return {'FINISHED'} shape_verts = shape_bm.verts[:] bmesh.ops.scale(shape_bm, vec=Vector((1, 1, 1))*(1+self.offset), verts=shape_verts) bmesh.ops.transform(shape_bm, verts=shape_verts, matrix=matrix_translation*matrix_rotation) if shape_verts is not None and len(shape_verts) > 0: if not is_clockwise(forward, center, loop_verts): loop_verts.reverse() loop_edges.reverse() if not is_clockwise(forward, center, shape_verts): shape_verts.reverse() loop_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in loop_verts]) shape_angle = box_fit_2d([(matrix_rotation.transposed() * v.co).to_2d() for v in shape_verts]) if abs(abs(loop_angle) - abs(shape_angle)) <= 0.01: loop_angle = 0 correct_angle = loop_angle + self.rotation if self.shape_rotation: correct_angle -= shape_angle if correct_angle != 0 and not self.active_as_first: bmesh.ops.rotate(shape_bm, verts=shape_verts, cent=center, matrix=Matrix.Rotation(-correct_angle, 3, forward)) active = object_bm.select_history.active if self.active_as_first and isinstance(active, bmesh.types.BMVert) and active in loop_verts: shift = loop_verts.index(active) else: kd_tree = mathutils.kdtree.KDTree(len(loop_verts)) for idx, loop_vert in enumerate(loop_verts): kd_tree.insert(loop_vert.co, idx) kd_tree.balance() shape_first_idx = kd_tree.find(shape_verts[0].co)[1] shift = shape_first_idx + self.shift if shift != 0: loop_verts = loop_verts[shift % len(loop_verts):] + loop_verts[:shift % len(loop_verts)] if not is_loop_boundary and self.use_ray_cast: for shape_vert in shape_verts: co = shape_vert.co ray_cast_data = object_bvh.ray_cast(co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(co, -forward) if ray_cast_data[0] is not None: shape_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): vert.co = vert.co.lerp(shape_verts[idx].co, self.factor/100) if not is_loop_boundary and is_loop_cyclic: object_bm.select_flush_mode() select_only(object_bm, loop_edges, {"EDGE"}) bpy.ops.mesh.loop_to_region() # Ugly. inset_faces = [f for f in object_bm.faces[:] if f.select] if self.fill_type != "ORIGINAL": smooth = inset_faces[0].smooth bmesh.ops.delete(object_bm, geom=inset_faces, context=5) inset_faces = [] center_vert = object_bm.verts.new(center) if self.use_ray_cast: ray_cast_data = object_bvh.ray_cast(center_vert.co, forward) if ray_cast_data[0] is None: ray_cast_data = object_bvh.ray_cast(center_vert.co, -forward) if ray_cast_data[0] is not None: center_vert.co = ray_cast_data[0] for idx, vert in enumerate(loop_verts): new_face = object_bm.faces.new((center_vert, vert, loop_verts[(idx+1) % loop_verts_len])) new_face.smooth = smooth inset_faces.append(new_face) bmesh.ops.recalc_face_normals(object_bm, faces=inset_faces) selected_co = [] for vert in selected_verts: if vert.is_valid: selected_co.append(vert.co.copy()) outset_region_faces = [] if self.outset > 0.0: outset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces, thickness=self.outset, use_even_offset=True, use_interpolate=True, use_outset=True) outset_region_faces = outset_region_faces["faces"] inset_region_faces = [] if self.inset > 0.0: inset_region_faces = bmesh.ops.inset_region(object_bm, faces=inset_faces, thickness=self.inset, use_even_offset=True, use_interpolate=True) inset_region_faces = inset_region_faces["faces"] new_selected_verts = [] for face in set(inset_region_faces+inset_faces): for vert in face.verts: if vert.co in selected_co: new_selected_verts.append(vert) selected_co.remove(vert.co) selected_verts_fin.append(new_selected_verts) select_only(object_bm, new_selected_verts, {"EDGE"}) if self.fill_type == "HOLE": bmesh.ops.delete(object_bm, geom=inset_faces, context=5) inset_faces = [] elif self.fill_type == "NGON": inset_faces = [bmesh.utils.face_join(inset_faces)] if self.fill_flatten and self.extrude == 0: if len(inset_region_faces + inset_faces) > 0: verts = list(set(reduce(lambda v1, v2: list(v1) + list(v2), [v.verts for v in inset_region_faces + inset_faces]))) matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts) bmesh.ops.scale(object_bm, vec=Vector((1, 1, +0)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) bmesh.ops.recalc_face_normals(object_bm, faces=outset_region_faces+inset_region_faces+inset_faces) if self.extrude != 0: extrude_geom = bmesh.ops.extrude_face_region(object_bm, geom=inset_region_faces+inset_faces) verts = [v for v in extrude_geom['geom'] if isinstance(v, bmesh.types.BMVert)] if self.fill_flatten: matrix = Matrix.Translation(-center) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation.transposed(), verts=verts) bmesh.ops.scale(object_bm, vec=Vector((1.0, 1.0, 0.001)), space=matrix, verts=verts) bmesh.ops.rotate(object_bm, cent=center, matrix=matrix_rotation, verts=verts) bmesh.ops.delete(object_bm, geom=inset_region_faces+inset_faces, context=5) bmesh.ops.translate(object_bm, verts=verts, vec=forward * self.extrude) del shape_bm if selected_verts_fin: select_only(object_bm, reduce(lambda x, y: x + y, selected_verts_fin), {"EDGE"}) object_bm.select_flush(True) del object_bvh bmesh.update_edit_mesh(object.data) return {'FINISHED'}
cam.rotation_mode = 'QUATERNION' if True: cam.location = (0.78856, 0.63725, 0.68383) cam.rotation_quaternion = (0.417, 0.241, 0.439, 0.758) cam.data.angle = numpy.radians(34.) if False: cx, cy = lbf.convert_3d_to_2d_coords(Vector(V.xyz) + rho * c, normalize=True) print(cx, cy) cam.data.shift_x -= (0.5 - cx) cam.data.shift_y -= (0.5 - cy) print( lbf.convert_3d_to_2d_coords(Vector(V.xyz) + rho * c, normalize=True)) else: cam.rotation_quaternion = c.to_track_quat('Z', 'Y') bpy.ops.object.select_all(action='DESELECT') for obj in bpy.data.objects: if obj.name[0:9] == 'spherical': obj.select = True bpy.ops.view3d.camera_to_view_selected() cam.data.angle += numpy.radians(10.) ################################################################ ################################################################ # EXPORT VERTEX IMAGE COORDS bpy.ops.object.empty_add(location=V.xyz) vx, vy = lbf.convert_3d_to_2d_coords(V.xyz, normalize=True) print('(vx, vy) = (%s, %s)' % (vx, vy))