def cursor_snap(self, context, event): if self.tree is None or context.scene.sprytile_ui.use_mouse is True: return # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse ray_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) up_vector, right_vector, plane_normal = sprytile_utils.get_current_grid_vectors( scene) if event.type in self.is_keyboard_list and event.shift and event.value == 'PRESS': if scene.sprytile_data.cursor_snap == 'GRID': scene.sprytile_data.cursor_snap = 'VERTEX' else: scene.sprytile_data.cursor_snap = 'GRID' # Snap cursor, depending on setting if scene.sprytile_data.cursor_snap == 'GRID': location = intersect_line_plane(ray_origin, ray_origin + ray_vector, scene.cursor_location, plane_normal) if location is None: return world_pixels = scene.sprytile_data.world_pixels target_grid = sprytile_utils.get_grid( context, context.object.sprytile_gridid) grid_x = target_grid.grid[0] grid_y = target_grid.grid[1] grid_position, x_vector, y_vector = sprytile_utils.get_grid_pos( location, scene.cursor_location, right_vector.copy(), up_vector.copy(), world_pixels, grid_x, grid_y) scene.cursor_location = grid_position elif scene.sprytile_data.cursor_snap == 'VERTEX': location, normal, face_index, distance = self.raycast_object( context.object, ray_origin, ray_vector) if location is None: return # Location in world space, convert to object space matrix = context.object.matrix_world.copy() matrix_inv = matrix.inverted() location, normal, face_index, dist = self.tree.find_nearest( matrix_inv * location) if location is None: return # Found the nearest face, go to BMesh to find the nearest vertex if self.bmesh is None: self.refresh_mesh = True return if face_index >= len(self.bmesh.faces) or face_index < 0: return face = self.bmesh.faces[face_index] closest_vtx = -1 closest_dist = float('inf') # positions are in object space for vtx_idx, vertex in enumerate(face.verts): test_dist = (location - vertex.co).magnitude if test_dist < closest_dist: closest_vtx = vtx_idx closest_dist = test_dist # convert back to world space if closest_vtx != -1: scene.cursor_location = matrix * face.verts[closest_vtx].co
def cursor_snap(self, context, event): if self.tree is None or context.scene.sprytile_ui.use_mouse is True: return # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse ray_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) up_vector, right_vector, plane_normal = sprytile_utils.get_current_grid_vectors( scene) if event.type in self.is_keyboard_list and event.shift and event.value == 'PRESS': if scene.sprytile_data.cursor_snap == 'GRID': scene.sprytile_data.cursor_snap = 'VERTEX' else: scene.sprytile_data.cursor_snap = 'GRID' # Snap cursor, depending on setting if scene.sprytile_data.cursor_snap == 'GRID': location = intersect_line_plane(ray_origin, ray_origin + ray_vector, scene.cursor_location, plane_normal) if location is None: return world_pixels = scene.sprytile_data.world_pixels target_grid = sprytile_utils.get_grid( context, context.object.sprytile_gridid) grid_x = target_grid.grid[0] grid_y = target_grid.grid[1] grid_position, x_vector, y_vector = sprytile_utils.get_grid_pos( location, scene.cursor_location, right_vector.copy(), up_vector.copy(), world_pixels, grid_x, grid_y) scene.cursor_location = grid_position elif scene.sprytile_data.cursor_snap == 'VERTEX': # Get if user is holding down tile picker modifier check_modifier = False addon_prefs = context.user_preferences.addons[ __package__].preferences if addon_prefs.tile_picker_key == 'Alt': check_modifier = event.alt if addon_prefs.tile_picker_key == 'Ctrl': check_modifier = event.ctrl if addon_prefs.tile_picker_key == 'Shift': check_modifier = event.shift location, normal, face_index, distance = self.raycast_object( context.object, ray_origin, ray_vector) if location is None: if check_modifier: scene.sprytile_data.lock_normal = False return # Location in world space, convert to object space matrix = context.object.matrix_world.copy() matrix_inv = matrix.inverted() location, normal, face_index, dist = self.tree.find_nearest( matrix_inv * location) if location is None: return # Found the nearest face, go to BMesh to find the nearest vertex if self.bmesh is None: self.refresh_mesh = True return if face_index >= len(self.bmesh.faces) or face_index < 0: return face = self.bmesh.faces[face_index] closest_vtx = -1 closest_dist = float('inf') # positions are in object space for vtx_idx, vertex in enumerate(face.verts): test_dist = (location - vertex.co).magnitude if test_dist < closest_dist: closest_vtx = vtx_idx closest_dist = test_dist # convert back to world space if closest_vtx != -1: scene.cursor_location = matrix * face.verts[closest_vtx].co # If find face tile button pressed, set work plane normal too if check_modifier: sprytile_data = context.scene.sprytile_data # Check if mouse is hitting object target_normal = context.object.matrix_world.to_quaternion( ) * normal face_up_vector, face_right_vector = self.get_face_up_vector( context, face_index, 0.4) if face_up_vector is not None: sprytile_data.paint_normal_vector = target_normal sprytile_data.paint_up_vector = face_up_vector sprytile_data.lock_normal = True
def execute_fill(self, context, scene, ray_origin, ray_vector): up_vector, right_vector, plane_normal = sprytile_utils.get_current_grid_vectors(scene, with_rotation=False) # Intersect on the virtual plane plane_hit = intersect_line_plane(ray_origin, ray_origin + ray_vector, scene.cursor.location, plane_normal) # Didn't hit the plane exit if plane_hit is None: return grid = sprytile_utils.get_grid(context, context.object.sprytile_gridid) sprytile_data = scene.sprytile_data world_pixels = sprytile_data.world_pixels grid_x = grid.grid[0] grid_y = grid.grid[1] # Find the position of the plane hit, in terms of grid coordinates hit_coord, grid_right, grid_up = sprytile_utils.get_grid_pos( plane_hit, scene.cursor.location, right_vector.copy(), up_vector.copy(), world_pixels, grid_x, grid_y, as_coord=True ) # Check hit_coord is inside the work plane grid plane_size = sprytile_data.fill_plane_size grid_min, grid_max = sprytile_utils.get_workplane_area(plane_size[0], plane_size[1]) x_offset = 1 if plane_size[0] % 2 == 1: grid_min[0] += x_offset grid_max[0] += x_offset if hit_coord.x < grid_min[0] or hit_coord.x >= grid_max[0]: return if hit_coord.y < grid_min[1] or hit_coord.y >= grid_max[1]: return # Build the fill map sel_coords, sel_size, sel_ids = sprytile_utils.get_grid_selection_ids(context, grid) fill_map, face_idx_array = self.build_fill_map(context, grid_up, grid_right, plane_normal, plane_size, grid_min, grid_max, sel_ids) # Convert from grid coordinate to map coordinate hit_array_coord = [int(hit_coord.x) - grid_min[0], int(hit_coord.y) - grid_min[1]] # For getting paint settings later paint_setting_layer = self.modal.bmesh.faces.layers.int.get(UvDataLayers.PAINT_SETTINGS) # Get vectors again, to apply tile rotations in UV stage up_vector, right_vector, plane_normal = sprytile_utils.get_current_grid_vectors(scene) # Get the content in hit coordinate hit_coord_content = int(fill_map[hit_array_coord[1]][hit_array_coord[0]]) # Get the coordinates that would be flood filled fill_coords = self.flood_fill(fill_map, hit_array_coord, -2, hit_coord_content) # If lock transform on, cache the paint settings before doing any operations paint_setting_cache = None if sprytile_data.fill_lock_transform and paint_setting_layer is not None: paint_setting_cache = [len(fill_coords)] for idx, cell_coord in fill_coords: face_index = face_idx_array[cell_coord[1]][cell_coord[0]] if face_index > -1: face = self.modal.faces[face_index] paint_setting_cache[idx] = face[paint_setting_layer] # Get the work layer filter, based on layer settings work_layer_mask = sprytile_utils.get_work_layer_data(sprytile_data) require_base_layer = sprytile_data.work_layer != 'BASE' origin_xy = (grid.tile_selection[0], grid.tile_selection[1]) data = scene.sprytile_data # Loop through list of coords to be filled for idx, cell_coord in enumerate(fill_coords): # Fetch the paint settings from cache if paint_setting_cache is not None: paint_setting = paint_setting_cache[idx] sprytile_utils.from_paint_settings(data, paint_setting) # Convert map coord to grid coord grid_coord = [grid_min[0] + cell_coord[0], grid_min[1] + cell_coord[1]] sub_x = (grid_coord[0] - int(hit_coord.x)) % sel_size[0] sub_y = (grid_coord[1] - int(hit_coord.y)) % sel_size[1] sub_xy = sel_coords[(sub_y * sel_size[0]) + sub_x] self.modal.construct_face(context, grid_coord, [1,1], sub_xy, origin_xy, grid_up, grid_right, up_vector, right_vector, plane_normal, require_base_layer=require_base_layer, work_layer_mask=work_layer_mask)
def execute_fill(self, context, scene, ray_origin, ray_vector): up_vector, right_vector, plane_normal = sprytile_utils.get_current_grid_vectors( scene, with_rotation=False) # Intersect on the virtual plane plane_hit = intersect_line_plane(ray_origin, ray_origin + ray_vector, scene.cursor_location, plane_normal) # Didn't hit the plane exit if plane_hit is None: return grid = sprytile_utils.get_grid(context, context.object.sprytile_gridid) sprytile_data = scene.sprytile_data world_pixels = sprytile_data.world_pixels grid_x = grid.grid[0] grid_y = grid.grid[1] # Find the position of the plane hit, in terms of grid coordinates hit_coord, grid_right, grid_up = sprytile_utils.get_grid_pos( plane_hit, scene.cursor_location, right_vector.copy(), up_vector.copy(), world_pixels, grid_x, grid_y, as_coord=True) # Check hit_coord is inside the work plane grid plane_size = sprytile_data.axis_plane_size if hit_coord.x < -plane_size[0] or hit_coord.x >= plane_size[0]: return if hit_coord.y < -plane_size[1] or hit_coord.y >= plane_size[1]: return # Build the fill map fill_map, face_idx_array = self.build_fill_map(context, grid_up, grid_right, plane_normal, plane_size) # Convert from grid coordinate to map coordinate hit_array_coord = [ int(hit_coord.x) + plane_size[0], int((plane_size[1] * 2) - 1 - (hit_coord.y + plane_size[1])) ] # Calculate the tile index of currently selected tile tile_xy = (grid.tile_selection[0], grid.tile_selection[1]) # For getting paint settings later paint_setting_layer = self.modal.bmesh.faces.layers.int.get( 'paint_settings') # Pre calculate for auto merge shift_vec = plane_normal.normalized() * 0.01 threshold = (1 / context.scene.sprytile_data.world_pixels) * 2 # Get vectors again, to apply tile rotations in UV stage up_vector, right_vector, plane_normal = sprytile_utils.get_current_grid_vectors( scene) # Flood fill targets map cell coordinates hit_coord_content = int( fill_map[hit_array_coord[1]][hit_array_coord[0]]) fill_coords = self.flood_fill(fill_map, hit_array_coord, -1, hit_coord_content) for cell_coord in fill_coords: self.construct_fill(context, scene, sprytile_data, cell_coord, plane_size, face_idx_array, paint_setting_layer, tile_xy, ray_vector, shift_vec, threshold, up_vector, right_vector, plane_normal, grid_up, grid_right)