built more assets and started playing with foliage painting
This commit is contained in:
534
addons/dreadpon.spatial_gardener/gardener/painter.gd
Normal file
534
addons/dreadpon.spatial_gardener/gardener/painter.gd
Normal file
@@ -0,0 +1,534 @@
|
||||
@tool
|
||||
extends RefCounted
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Handles keeping track of brush strokes, brush position and some of the brush settings
|
||||
# Also notifies others of painting lifecycle updates
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
const FunLib = preload("../utility/fun_lib.gd")
|
||||
const DponDebugDraw = preload("../utility/debug_draw.gd")
|
||||
const Toolshed_Brush = preload("../toolshed/toolshed_brush.gd")
|
||||
const Globals = preload("../utility/globals.gd")
|
||||
|
||||
|
||||
enum ModifierKeyboardKey {KEY_SHIFT, KEY_CTRL, KEY_ALT, KEY_TAB}
|
||||
enum BrushPrimaryKeyboardKey {MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_XBUTTON1, MOUSE_BUTTON_XBUTTON2}
|
||||
enum BrushPropEditFlag {MODIFIER, NONE, SIZE, STRENGTH}
|
||||
|
||||
|
||||
var owned_spatial:Node3D = null
|
||||
# Used for immediate updates when changes happen to the brush
|
||||
# This should NOT be used in update() or each frame in general
|
||||
var _cached_camera: Camera3D = null
|
||||
|
||||
const sphere_brush_material = preload("../shaders/shm_sphere_brush.tres")
|
||||
const circle_brush_material = preload("../shaders/shm_circle_brush.tres")
|
||||
var paint_brush_node:MeshInstance3D = null
|
||||
var detached_paint_brush_container:Node = null
|
||||
|
||||
# Temporary variables to store current quick prop edit state
|
||||
var brush_prop_edit_flag = BrushPropEditFlag.NONE
|
||||
const brush_prop_edit_max_dist:float = 500.0
|
||||
var brush_prop_edit_max_val:float = 0.0
|
||||
var brush_prop_edit_cur_val:float = 0.0
|
||||
var brush_prop_edit_start_pos:Vector2 = Vector2.ZERO
|
||||
var brush_prop_edit_offset:float = 0.0
|
||||
|
||||
var can_draw:bool = false
|
||||
var is_drawing:bool = false
|
||||
var pending_movement_update:bool = false
|
||||
var brush_collision_mask:int : set = set_brush_collision_mask
|
||||
|
||||
# Used to pass during stroke-state signals sent to Gardener/Arborist
|
||||
# Meant to avoid retrieving transform from an actual 3D node
|
||||
# And more importantly to cache a raycast normal at every given point in time
|
||||
var active_brush_data:Dictionary = {'brush_pos': Vector3.ZERO, 'brush_normal': Vector3.UP, 'brush_basis': Basis()}
|
||||
|
||||
# Variables to sync quick brush property edit with UI and vice-versa
|
||||
# And also for keeping brush state up-to-date without needing a reference to actual active brush
|
||||
var active_brush_overlap_mode: int = Toolshed_Brush.OverlapMode.VOLUME
|
||||
var active_brush_size:float : set = set_active_brush_size
|
||||
var active_brush_strength:float : set = set_active_brush_strength
|
||||
var active_brush_max_size:float : set = set_active_brush_max_size
|
||||
var active_brush_max_strength:float : set = set_active_brush_max_strength
|
||||
|
||||
# A queue of methods to be called once _cached_camera becomes available
|
||||
var when_camera_queue: Array = []
|
||||
|
||||
# Ooooh boy
|
||||
# Go to finish_brush_prop_edit() for explanation
|
||||
var mouse_move_call_delay: int = 0
|
||||
|
||||
|
||||
signal changed_active_brush_prop(prop, val, final)
|
||||
|
||||
signal stroke_started
|
||||
signal stroke_finished
|
||||
signal stroke_updated
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Lifecycle
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# When working with this object, we assume it does not exist outside the editor
|
||||
func _init(_owned_spatial):
|
||||
set_meta("class", "Painter")
|
||||
|
||||
owned_spatial = _owned_spatial
|
||||
#
|
||||
paint_brush_node = MeshInstance3D.new()
|
||||
paint_brush_node.name = "active_brush"
|
||||
set_brush_mesh()
|
||||
|
||||
#owned_spatial.add_child(paint_brush_node)
|
||||
detached_paint_brush_container = Node.new()
|
||||
owned_spatial.add_child(detached_paint_brush_container)
|
||||
detached_paint_brush_container.add_child(paint_brush_node)
|
||||
set_can_draw(false)
|
||||
|
||||
|
||||
func update(delta):
|
||||
if _cached_camera:
|
||||
# Handle queue of methods that need a _cached_camera
|
||||
for queue_item in when_camera_queue.duplicate():
|
||||
callv(queue_item.method_name, queue_item.args)
|
||||
when_camera_queue.erase(queue_item)
|
||||
consume_brush_drawing_update(delta)
|
||||
|
||||
|
||||
func set_brush_mesh(is_sphere: bool = false):
|
||||
if is_sphere:
|
||||
paint_brush_node.mesh = SphereMesh.new()
|
||||
paint_brush_node.mesh.radial_segments = 32
|
||||
paint_brush_node.mesh.rings = 16
|
||||
paint_brush_node.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
paint_brush_node.material_override = sphere_brush_material.duplicate()
|
||||
else:
|
||||
paint_brush_node.mesh = QuadMesh.new()
|
||||
paint_brush_node.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
paint_brush_node.material_override = circle_brush_material.duplicate()
|
||||
|
||||
|
||||
# Queue a call to method that needs a _cached_camera to be set
|
||||
func queue_call_when_camera(method_name: String, args: Array = []):
|
||||
when_camera_queue.append({'method_name': method_name, 'args': args})
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Editing lifecycle
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func start_editing():
|
||||
set_can_draw(true)
|
||||
|
||||
|
||||
func stop_editing():
|
||||
stop_brush_stroke()
|
||||
set_can_draw(false)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Input
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func forwarded_input(camera:Camera3D, event):
|
||||
if !can_draw: return
|
||||
|
||||
_cached_camera = camera
|
||||
var handled = false
|
||||
|
||||
# If inactive property edit
|
||||
# And event == mouseMotion
|
||||
# -> move the brush
|
||||
if brush_prop_edit_flag <= BrushPropEditFlag.NONE:
|
||||
if (is_instance_of(event, InputEventMouseMotion)
|
||||
|| (is_instance_of(event, InputEventMouseButton) && event.button_index == MOUSE_BUTTON_WHEEL_UP)
|
||||
|| (is_instance_of(event, InputEventMouseButton) && event.button_index == MOUSE_BUTTON_WHEEL_DOWN)):
|
||||
|
||||
if mouse_move_call_delay > 0:
|
||||
mouse_move_call_delay -= 1
|
||||
else:
|
||||
move_brush()
|
||||
pending_movement_update = true
|
||||
# Don't handle input - moving a brush is not destructive
|
||||
|
||||
# If inactive property edit
|
||||
# And event == overlap mode key
|
||||
# -> cycle overlap modes
|
||||
if brush_prop_edit_flag <= BrushPropEditFlag.NONE && is_instance_of(event, InputEventKey) && event.keycode == get_overlap_mode_key():
|
||||
if event.pressed && !event.is_echo():
|
||||
cycle_overlap_modes()
|
||||
handled = true
|
||||
|
||||
# If inactive property edit/modifier key pressed
|
||||
# And event == modifier key pressed
|
||||
# -> remember/forget the modifier
|
||||
if brush_prop_edit_flag <= BrushPropEditFlag.NONE && is_instance_of(event, InputEventKey) && event.keycode == get_property_edit_modifier():
|
||||
if event.pressed:
|
||||
brush_prop_edit_flag = BrushPropEditFlag.MODIFIER
|
||||
if !event.pressed:
|
||||
brush_prop_edit_flag = BrushPropEditFlag.NONE
|
||||
handled = true
|
||||
|
||||
# If inactive property edit or modifier key pressed
|
||||
# And event == property edit trigger pressed
|
||||
# -> start property edit
|
||||
if brush_prop_edit_flag <= BrushPropEditFlag.NONE && is_instance_of(event, InputEventMouseButton) && event.button_index == get_property_edit_button():
|
||||
if event.pressed:
|
||||
brush_prop_edit_flag = BrushPropEditFlag.SIZE if brush_prop_edit_flag != BrushPropEditFlag.MODIFIER else BrushPropEditFlag.STRENGTH
|
||||
start_brush_prop_edit(event.global_position)
|
||||
handled = true
|
||||
|
||||
# If editing property
|
||||
# And event == property edit trigger released
|
||||
# -> stop property edit
|
||||
if brush_prop_edit_flag > BrushPropEditFlag.NONE && is_instance_of(event, InputEventMouseButton) && event.button_index == get_property_edit_button():
|
||||
if !event.pressed:
|
||||
finish_brush_prop_edit(camera)
|
||||
brush_prop_edit_flag = BrushPropEditFlag.NONE
|
||||
handled = true
|
||||
|
||||
# If editing property
|
||||
# And event == mouseMotion
|
||||
# -> update property value
|
||||
if brush_prop_edit_flag > BrushPropEditFlag.NONE && is_instance_of(event, InputEventMouseMotion):
|
||||
brush_prop_edit_calc_val(event.global_position)
|
||||
handled = true
|
||||
|
||||
# If editing property
|
||||
# And event == paint trigger pressed/releasedq
|
||||
# -> start/stop the brush stroke
|
||||
if brush_prop_edit_flag == BrushPropEditFlag.NONE && is_instance_of(event, InputEventMouseButton) && event.button_index == MOUSE_BUTTON_LEFT:
|
||||
if event.pressed:
|
||||
move_brush()
|
||||
start_brush_stroke()
|
||||
else:
|
||||
stop_brush_stroke()
|
||||
handled = true
|
||||
|
||||
return handled
|
||||
|
||||
|
||||
func get_property_edit_modifier():
|
||||
# This convolution exists because a project setting with default value is not saved for some reason and load as "null"
|
||||
# See https://github.com/godotengine/godot/issues/56598
|
||||
var key = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/brush_prop_edit_modifier", Globals.KeyboardKey.KEY_SHIFT)
|
||||
return Globals.index_to_enum(key, Globals.KeyboardKey)
|
||||
|
||||
|
||||
func get_property_edit_button():
|
||||
var key = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/brush_prop_edit_button", Globals.MouseButton.MOUSE_BUTTON_XBUTTON1)
|
||||
return Globals.index_to_enum(key, Globals.MouseButton)
|
||||
|
||||
|
||||
func get_overlap_mode_key():
|
||||
var key = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/brush_overlap_mode_button", Globals.KeyboardKey.KEY_QUOTELEFT)
|
||||
return Globals.index_to_enum(key, Globals.KeyboardKey)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Painting lifecycle
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func set_can_draw(state):
|
||||
can_draw = state
|
||||
if state:
|
||||
paint_brush_node.visible = true
|
||||
else:
|
||||
paint_brush_node.visible = false
|
||||
|
||||
|
||||
func start_brush_stroke():
|
||||
if is_drawing: return
|
||||
is_drawing = true
|
||||
stroke_started.emit(active_brush_data)
|
||||
|
||||
|
||||
func stop_brush_stroke():
|
||||
if !is_drawing: return
|
||||
is_drawing = false
|
||||
active_brush_data = {'brush_pos': Vector3.ZERO, 'brush_normal': Vector3.UP, 'brush_basis': Basis()}
|
||||
stroke_finished.emit(active_brush_data)
|
||||
|
||||
|
||||
# Actually update the stroke only if it was preceeded by the input event
|
||||
func consume_brush_drawing_update(delta):
|
||||
if !can_draw: return
|
||||
if !is_drawing: return
|
||||
if !pending_movement_update: return
|
||||
|
||||
pending_movement_update = false
|
||||
stroke_updated.emit(active_brush_data)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Brush movement
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func move_brush():
|
||||
if !_cached_camera: return
|
||||
update_active_brush_data()
|
||||
refresh_brush_transform()
|
||||
|
||||
|
||||
# Update brush data that is passed through signals to Gardener/Arborist
|
||||
# Raycast overrides exist for compatability with gardener tests
|
||||
func update_active_brush_data(raycast_overrides: Dictionary = {}):
|
||||
var space_state = paint_brush_node.get_world_3d().direct_space_state
|
||||
var start = project_mouse_near() if !raycast_overrides.has('start') else raycast_overrides.start
|
||||
var end = project_mouse_far() if !raycast_overrides.has('end') else raycast_overrides.end
|
||||
|
||||
var params = PhysicsRayQueryParameters3D.create(start, end, brush_collision_mask, [])
|
||||
var ray_result:Dictionary = space_state.intersect_ray(params)
|
||||
|
||||
if !ray_result.is_empty():
|
||||
active_brush_data.brush_pos = ray_result.position
|
||||
active_brush_data.brush_normal = ray_result.normal
|
||||
else:
|
||||
# If raycast failed - align to camera plane, retaining current distance to camera
|
||||
var camera_normal = -_cached_camera.global_transform.basis.z
|
||||
var planar_dist_to_camera = (active_brush_data.brush_pos - _cached_camera.global_transform.origin).dot(camera_normal)
|
||||
var brush_pos:Vector3 = project_mouse(planar_dist_to_camera)
|
||||
active_brush_data.brush_pos = brush_pos
|
||||
|
||||
# It's possible we don't have _cached_camera defined here since
|
||||
# Gardener tests might call update_active_brush_data() without setting it
|
||||
if _cached_camera:
|
||||
# Cache to use with Projection brush
|
||||
active_brush_data.brush_basis = _cached_camera.global_transform.basis
|
||||
|
||||
|
||||
# Update transform of a paint brush 3D node
|
||||
func refresh_brush_transform():
|
||||
if active_brush_data.is_empty(): return
|
||||
|
||||
match active_brush_overlap_mode:
|
||||
Toolshed_Brush.OverlapMode.VOLUME:
|
||||
paint_brush_node.global_transform.origin = active_brush_data.brush_pos
|
||||
paint_brush_node.global_transform.basis = Basis()
|
||||
Toolshed_Brush.OverlapMode.PROJECTION:
|
||||
paint_brush_node.global_transform.origin = active_brush_data.brush_pos
|
||||
paint_brush_node.global_transform.basis = active_brush_data.brush_basis
|
||||
# Projection brush size is in viewport-space, but it will move forward and backward
|
||||
# Thus appearing smaller or bigger
|
||||
# So we need to update it's size to keep it consistent
|
||||
set_brush_diameter(active_brush_size)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Brush quick property edit lifecycle
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Quickly edit a brush property without using the UI (aka like in Blender)
|
||||
# The flow here is as follows:
|
||||
# 1. Respond to mouse events, calculate property value, emit a signal
|
||||
# 2. Signal is received in the Gardener, passed to an active Toolshed_Brush
|
||||
# 3. Active brush updates it's values
|
||||
# 4. Toolshed notifies Painter of a value change
|
||||
# 5. Painter updates it's helper variables and visual representation
|
||||
|
||||
# Switching between Volume/Projection brush is here too, but it's not connected to the whole Blender-like process
|
||||
# It's just a hotkey handling
|
||||
|
||||
# Set the initial value of edited property and mouse offset
|
||||
func start_brush_prop_edit(mouse_pos):
|
||||
match brush_prop_edit_flag:
|
||||
BrushPropEditFlag.SIZE:
|
||||
brush_prop_edit_cur_val = active_brush_size
|
||||
brush_prop_edit_max_val = active_brush_max_size
|
||||
BrushPropEditFlag.STRENGTH:
|
||||
brush_prop_edit_cur_val = active_brush_strength
|
||||
brush_prop_edit_max_val = active_brush_max_strength
|
||||
|
||||
brush_prop_edit_start_pos = mouse_pos
|
||||
brush_prop_edit_offset = brush_prop_edit_cur_val / brush_prop_edit_max_val * brush_prop_edit_max_dist
|
||||
|
||||
|
||||
# Calculate edited property value based on mouse offset
|
||||
func brush_prop_edit_calc_val(mouse_pos):
|
||||
brush_prop_edit_cur_val = clamp((mouse_pos.x - brush_prop_edit_start_pos.x + brush_prop_edit_offset) / brush_prop_edit_max_dist, 0.0, 1.0) * brush_prop_edit_max_val
|
||||
|
||||
match active_brush_overlap_mode:
|
||||
Toolshed_Brush.OverlapMode.VOLUME:
|
||||
match brush_prop_edit_flag:
|
||||
BrushPropEditFlag.SIZE:
|
||||
changed_active_brush_prop.emit("shape/shape_volume_size", brush_prop_edit_cur_val, false)
|
||||
BrushPropEditFlag.STRENGTH:
|
||||
changed_active_brush_prop.emit("behavior/behavior_strength", brush_prop_edit_cur_val, false)
|
||||
Toolshed_Brush.OverlapMode.PROJECTION:
|
||||
match brush_prop_edit_flag:
|
||||
BrushPropEditFlag.SIZE:
|
||||
changed_active_brush_prop.emit("shape/shape_projection_size", brush_prop_edit_cur_val, false)
|
||||
|
||||
|
||||
# Stop editing brush property and reset helper variables and mouse position
|
||||
func finish_brush_prop_edit(camera:Camera3D):
|
||||
match active_brush_overlap_mode:
|
||||
Toolshed_Brush.OverlapMode.VOLUME:
|
||||
match brush_prop_edit_flag:
|
||||
BrushPropEditFlag.SIZE:
|
||||
changed_active_brush_prop.emit("shape/shape_volume_size", brush_prop_edit_cur_val, true)
|
||||
BrushPropEditFlag.STRENGTH:
|
||||
changed_active_brush_prop.emit("behavior/behavior_strength", brush_prop_edit_cur_val, true)
|
||||
Toolshed_Brush.OverlapMode.PROJECTION:
|
||||
match brush_prop_edit_flag:
|
||||
BrushPropEditFlag.SIZE:
|
||||
changed_active_brush_prop.emit("shape/shape_projection_size", brush_prop_edit_cur_val, true)
|
||||
|
||||
Input.warp_mouse(brush_prop_edit_start_pos)
|
||||
|
||||
brush_prop_edit_flag = BrushPropEditFlag.NONE
|
||||
brush_prop_edit_start_pos = Vector2.ZERO
|
||||
brush_prop_edit_max_val = 0.0
|
||||
brush_prop_edit_cur_val = 0.0
|
||||
|
||||
# Apparently warp_mouse() sometimes takes a few mouse motion events to actually take place
|
||||
# Sometimes it's instant, sometimes it takes 1, and sometimes 2 events (at least on my machine)
|
||||
# This leads to brush jumping to position used in prop edit and then back. Like it's on a string
|
||||
# As an workaround, we delay processing motion input for 2 events (which should be enough for 99% of cases?)
|
||||
mouse_move_call_delay = 2
|
||||
|
||||
|
||||
# Cycle between brush overlap modes on a button press
|
||||
func cycle_overlap_modes():
|
||||
active_brush_overlap_mode += 1
|
||||
if active_brush_overlap_mode > Toolshed_Brush.OverlapMode.PROJECTION:
|
||||
active_brush_overlap_mode = Toolshed_Brush.OverlapMode.VOLUME
|
||||
changed_active_brush_prop.emit("behavior/behavior_overlap_mode", active_brush_overlap_mode, true)
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Setters for brush parameters meant to be accessed from outside
|
||||
# In response to UI inputs
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func update_all_props_to_active_brush(brush: Toolshed_Brush):
|
||||
var max_size = 1.0
|
||||
var max_strength = 1.0
|
||||
var curr_size = 1.0
|
||||
var curr_strength = brush.behavior_strength
|
||||
|
||||
match brush.behavior_overlap_mode:
|
||||
Toolshed_Brush.OverlapMode.VOLUME:
|
||||
max_size = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/brush_volume_size_slider_max_value", 100.0)
|
||||
curr_size = brush.shape_volume_size
|
||||
Toolshed_Brush.OverlapMode.PROJECTION:
|
||||
max_size = FunLib.get_setting_safe("dreadpons_spatial_gardener/input_and_ui/brush_projection_size_slider_max_value", 1000.0)
|
||||
curr_size = brush.shape_projection_size
|
||||
|
||||
set_active_brush_overlap_mode(brush.behavior_overlap_mode)
|
||||
set_active_brush_max_size(max_size)
|
||||
set_active_brush_max_strength(max_strength)
|
||||
set_active_brush_size(curr_size)
|
||||
set_active_brush_strength(curr_strength)
|
||||
|
||||
|
||||
# Update helper variables and visuals
|
||||
func set_active_brush_size(val):
|
||||
active_brush_size = val
|
||||
paint_brush_node.material_override.set_shader_parameter("proximity_multiplier", active_brush_size * 0.5)
|
||||
queue_call_when_camera('set_brush_diameter', [active_brush_size])
|
||||
|
||||
|
||||
# Update helper variables and visuals
|
||||
func set_active_brush_max_size(val):
|
||||
active_brush_max_size = val
|
||||
queue_call_when_camera('set_brush_diameter', [active_brush_size])
|
||||
|
||||
|
||||
# Update helper variables
|
||||
func set_active_brush_strength(val):
|
||||
active_brush_strength = val
|
||||
|
||||
|
||||
# Update helper variables
|
||||
func set_active_brush_max_strength(val):
|
||||
active_brush_max_strength = val
|
||||
|
||||
|
||||
# Update visuals
|
||||
func set_brush_diameter(diameter: float):
|
||||
match active_brush_overlap_mode:
|
||||
|
||||
Toolshed_Brush.OverlapMode.VOLUME:
|
||||
paint_brush_node.mesh.radius = diameter * 0.5
|
||||
paint_brush_node.mesh.height = diameter
|
||||
|
||||
Toolshed_Brush.OverlapMode.PROJECTION:
|
||||
var camera_normal = -_cached_camera.global_transform.basis.z
|
||||
var planar_dist_to_camera = (active_brush_data.brush_pos - _cached_camera.global_transform.origin).dot(camera_normal)
|
||||
var circle_center:Vector3 = active_brush_data.brush_pos
|
||||
var circle_edge:Vector3
|
||||
# If we're editing props (or just finished it as indicated by 'mouse_move_call_delay')
|
||||
# Then to prevent size doubling/overflow use out brush position as mouse position
|
||||
# (Since out mouse WILL be offset due to us dragging it to the side)
|
||||
if brush_prop_edit_flag > BrushPropEditFlag.NONE || mouse_move_call_delay > 0:
|
||||
var screen_space_brush_pos = _cached_camera.unproject_position(active_brush_data.brush_pos)
|
||||
circle_edge = _cached_camera.project_position(screen_space_brush_pos + Vector2(diameter * 0.5, 0), planar_dist_to_camera)
|
||||
else:
|
||||
circle_edge = project_mouse(planar_dist_to_camera, Vector2(diameter * 0.5, 0))
|
||||
var size = (circle_edge - circle_center).length()
|
||||
paint_brush_node.mesh.size = Vector2(size, size) * 2.0
|
||||
|
||||
|
||||
func set_brush_collision_mask(val):
|
||||
brush_collision_mask = val
|
||||
|
||||
|
||||
# Update helper variables and visuals
|
||||
func set_active_brush_overlap_mode(val):
|
||||
active_brush_overlap_mode = val
|
||||
|
||||
match active_brush_overlap_mode:
|
||||
Toolshed_Brush.OverlapMode.VOLUME:
|
||||
set_brush_mesh(true)
|
||||
Toolshed_Brush.OverlapMode.PROJECTION:
|
||||
set_brush_mesh(false)
|
||||
|
||||
# Since we are rebuilding the mesh here
|
||||
# It means that we need to move it in a proper position as well
|
||||
move_brush()
|
||||
|
||||
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Camera3D/raycasting methods
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
func project_mouse_near() -> Vector3:
|
||||
return project_mouse(_cached_camera.near)
|
||||
|
||||
|
||||
func project_mouse_far() -> Vector3:
|
||||
return project_mouse(_cached_camera.far - 0.1)
|
||||
|
||||
|
||||
func project_mouse(distance: float, offset: Vector2 = Vector2.ZERO) -> Vector3:
|
||||
return _cached_camera.project_position(_cached_camera.get_viewport().get_mouse_position() + offset, distance)
|
||||
Reference in New Issue
Block a user