Example #1
1
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
Example #2
0
    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
Example #3
0
    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'}
Example #4
0
	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])
Example #6
0
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
Example #7
0
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'}
Example #9
0
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()
Example #10
0
    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
Example #11
0
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
Example #12
0
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
Example #14
0
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
Example #15
0
 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
Example #19
0
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
Example #20
0
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
Example #21
0
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
Example #23
0
           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],
Example #24
0
    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'}
Example #25
0
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
Example #28
0
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
Example #29
0
    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'])
Example #31
0
    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'}
Example #32
0
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()
Example #33
0
    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'}
Example #34
0
 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'
Example #35
0
    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))