def discard_outside_of_view(self, view): 
        # Assume that the mesh object's bounding box is fully contained in the
        # view projection, and test this assumption.
        bounding_box = mesh_object.bound_box
        bounding_box_contained_in_projection = True

        # Transform the coordinates of each vertex of the bounding box from
        # object space to clip space.  As soon as any single vertex is
        # found to be outside of the view projection, duly indicate, and exit
        # the loop.
        projection_matrix = view.projection_matrix
        for vertex in bounding_box:
            co = Vector(vertex)
            co.resize(4)
            co.w = 1
            co.xyzw = projection_matrix * co
            w = co.w

            # Determine if the coordinates are within the view projection.
            if abs(co.x) > w or\
               abs(co.y) > w or\
               abs(co.z) > w:
                bounding_box_contained_in_projection = False
                break

        # If the bounding box is not entirely contained within the view
        # projection then some vertices may exist outside of the view
        # projection.
        if not bounding_box_contained_in_projection: 
            clip_space_map = self.coordinate_map

            # Retain each clip space vertex that is inside of the view
            # projection.
            indices = self.indices
            self.indices = [
                index
                for index in indices
                if abs(clip_space_map[index].x) < clip_space_map[index].w and\
                   abs(clip_space_map[index].y) < clip_space_map[index].w and\
                   abs(clip_space_map[index].z) < clip_space_map[index].w
            ] 
Example #2
0
    def discard_outside_of_view(self, view):
        # Assume that the mesh object's bounding box is fully contained in the
        # view projection, and test this assumption.
        bounding_box = mesh_object.bound_box
        bounding_box_contained_in_projection = True

        # Transform the coordinates of each vertex of the bounding box from
        # object space to clip space.  As soon as any single vertex is
        # found to be outside of the view projection, duly indicate, and exit
        # the loop.
        projection_matrix = view.projection_matrix
        for vertex in bounding_box:
            co = Vector(vertex)
            co.resize(4)
            co.w = 1
            co.xyzw = projection_matrix * co
            w = co.w

            # Determine if the coordinates are within the view projection.
            if abs(co.x) > w or\
               abs(co.y) > w or\
               abs(co.z) > w:
                bounding_box_contained_in_projection = False
                break

        # If the bounding box is not entirely contained within the view
        # projection then some vertices may exist outside of the view
        # projection.
        if not bounding_box_contained_in_projection:
            clip_space_map = self.coordinate_map

            # Retain each clip space vertex that is inside of the view
            # projection.
            indices = self.indices
            self.indices = [
                index
                for index in indices
                if abs(clip_space_map[index].x) < clip_space_map[index].w and\
                   abs(clip_space_map[index].y) < clip_space_map[index].w and\
                   abs(clip_space_map[index].z) < clip_space_map[index].w
            ]
Example #3
0
    def resize_primary_brush(self, radius):
        context = bpy.context
        primary_brush = self.primary_brush
        region_height = context.region.height
        region_width = context.region.width

        # Determine the world space radius of the primary brush necessary to
        # project a circle onto the view plane with the specified region space
        # radius.

        # Determine the z-depth of the primary brush's center in normalized
        # device coordinates.
        projection_matrix = context.region_data.perspective_matrix
        co = primary_brush.center.copy()
        co.resize(4)
        co.w = 1
        co.xyzw = projection_matrix * co
        w = co.w
        co.xyz /= w
        NDC_z_depth = co.z

        # Determine the region space coordinates of the primary brush's center.
        region_x = (co.x + 1) * region_width / 2
        region_y = (co.y + 1) * region_height / 2

        # Determine the NDC coordinates of a point on the edge of the
        # circle that should result from projecting the brush onto the view
        # plane.
        co = Vector((region_x, region_y)) + Vector((radius, 0))

        co.x = co.x * 2 / region_width - 1
        co.y = co.y * 2 / region_height - 1
        co.resize(3)
        co.z = NDC_z_depth

        # Calculate the world space radius of the primary brush.
        co.resize(4)
        co.w = 1
        co.xyzw = projection_matrix.inverted() * co
        w = co.w
        co.resize(3)
        co.xyz /= w
        primary_brush.radius = (co - primary_brush.center).length
    def stroke(self, region_x, region_y):
        props = self.props
        brushes = props.brushes
        derived_brushes = brushes.derived_brushes
        primary_brush = brushes.primary_brush

        active_object = bpy.context.active_object
        indices_affected_by_stroke = self.indices_affected_by_stroke
        vertices = active_object.data.vertices

        # The primary brush must be on the mesh in order to execute a stroke.
        if not primary_brush.is_on_mesh:
            return

        # Only proceed if at least one vertex is affected by the stroke.
        if not indices_affected_by_stroke:
            return

        # Determine the terminal position of the stroke in world space
        # coordinates.
        inverted_perspective_matrix =\
            bpy.context.region_data.perspective_matrix.inverted()
        co = Vector((region_x, region_y))
        co.x = co.x * 2 / bpy.context.region.width - 1
        co.y = co.y * 2 / bpy.context.region.height - 1
        co.resize(4)
        co.z = self.stroke_z_depth
        co.w = 1
        co = inverted_perspective_matrix @ co
        w = co.w
        co.resize(3)
        co /= w
        stroke_terminal_co = co

        # Apply the stroke to each brush.
        inverted_model_matrix = active_object.matrix_world.inverted()
        for brush in [primary_brush] + brushes.derived_brushes:
            # Determine the displaced position of the brush caused by the
            # stroke.
            displaced_brush_center =\
                brush.transformation_matrix @ stroke_terminal_co

            # Calculate an object space displacement vector between the brush's
            # original position and its displaced position.
            object_space_displacement = (
                inverted_model_matrix @  displaced_brush_center -
                inverted_model_matrix @ brush.center
            )

            # Move the brush to its displaced position.
            brush.center = displaced_brush_center

            # Add the displacement vector to the coordinates of the brush's
            # affected vertices, taking into account brush falloff.
            falloff_map = brush.falloff_map
            for index in brush.indices:
                vertices[index].co +=\
                    falloff_map[index] * object_space_displacement

        # Constrain the vertices to the specified target, if necessary.
        if self.target:
            surface_constraint_props = self.surface_constraint_props
            apply_shrinkwrap(
                offset = self.offset, target = self.target,
                wrap_method = surface_constraint_props.wrap_method_map[
                    surface_constraint_props.direction
                ],
                affected_indices = list(indices_affected_by_stroke)
            )

        # Update the octree's coordinate map.
        model_matrix = active_object.matrix_world
        world_space_submap = {
            index : model_matrix @ vertices[index].co
            for index in indices_affected_by_stroke
        }
        self.props.octree.coordinate_map.update(world_space_submap)
Example #5
0
    def stroke(self, region_x, region_y):
        props = self.props
        brushes = props.brushes
        derived_brushes = brushes.derived_brushes
        primary_brush = brushes.primary_brush

        active_object = bpy.context.active_object
        indices_affected_by_stroke = self.indices_affected_by_stroke
        vertices = active_object.data.vertices

        # The primary brush must be on the mesh in order to execute a stroke.
        if not primary_brush.is_on_mesh:
            return

        # Only proceed if at least one vertex is affected by the stroke.
        if not indices_affected_by_stroke:
            return

        # Determine the terminal position of the stroke in world space
        # coordinates.
        inverted_perspective_matrix =\
            bpy.context.region_data.perspective_matrix.inverted()
        co = Vector((region_x, region_y))
        co.x = co.x * 2 / bpy.context.region.width - 1
        co.y = co.y * 2 / bpy.context.region.height - 1
        co.resize(4)
        co.z = self.stroke_z_depth
        co.w = 1
        co = inverted_perspective_matrix * co
        w = co.w
        co.resize(3)
        co /= w
        stroke_terminal_co = co

        # Apply the stroke to each brush.
        inverted_model_matrix = active_object.matrix_world.inverted()
        for brush in [primary_brush] + brushes.derived_brushes:
            # Determine the displaced position of the brush caused by the
            # stroke.
            displaced_brush_center =\
                brush.transformation_matrix * stroke_terminal_co

            # Calculate an object space displacement vector between the brush's
            # original position and its displaced position.
            object_space_displacement = (
                inverted_model_matrix *  displaced_brush_center -
                inverted_model_matrix * brush.center
            )

            # Move the brush to its displaced position.
            brush.center = displaced_brush_center

            # Add the displacement vector to the coordinates of the brush's
            # affected vertices, taking into account brush falloff.
            falloff_map = brush.falloff_map
            for index in brush.indices:
                vertices[index].co +=\
                    falloff_map[index] * object_space_displacement

        # Constrain the vertices to the specified target, if necessary.
        if self.target:
            surface_constraint_props = self.surface_constraint_props
            apply_shrinkwrap(
                offset = self.offset, target = self.target,
                wrap_method = surface_constraint_props.wrap_method_map[
                    surface_constraint_props.direction
                ],
                affected_indices = list(indices_affected_by_stroke)
            )

        # Update the octree's coordinate map.
        model_matrix = active_object.matrix_world
        world_space_submap = {
            index : model_matrix * vertices[index].co
            for index in indices_affected_by_stroke
        }
        self.props.octree.coordinate_map.update(world_space_submap)