netfishing/drawing/surface_drawing_placement.gd

92 lines
2.8 KiB
GDScript

class_name SurfaceDrawingPlacement
extends RefCounted
const SNAP_DISTANCE: float = 0.55
const SNAP_PLANE_DISTANCE: float = 0.18
const SNAP_NORMAL_DOT: float = 0.94
static func resolve(
origin: Vector3,
normal: Vector3,
fallback_tangent: Vector3,
canvas_states: Array[Dictionary],
requested_grid_size: int = SurfaceDrawingProtocol.DEFAULT_GRID_SIZE,
) -> Dictionary:
var surface_normal: Vector3 = normal.normalized()
var surface_tangent: Vector3 = _projected_tangent(
fallback_tangent, surface_normal
)
var best: Dictionary = {
"origin": origin,
"normal": surface_normal,
"tangent": surface_tangent,
"snapped": false,
}
var nearest_distance: float = SNAP_DISTANCE
for state: Dictionary in canvas_states:
if not SurfaceDrawingProtocol.validate_canvas_state(state):
continue
var anchor_normal: Vector3 = SurfaceDrawingProtocol.array_to_vector(
state["normal"]
).normalized()
if anchor_normal.dot(surface_normal) < SNAP_NORMAL_DOT:
continue
var anchor_origin: Vector3 = SurfaceDrawingProtocol.array_to_vector(
state["origin"]
)
var relative: Vector3 = origin - anchor_origin
if absf(relative.dot(anchor_normal)) > SNAP_PLANE_DISTANCE:
continue
var anchor_tangent: Vector3 = _projected_tangent(
SurfaceDrawingProtocol.array_to_vector(state["tangent"]),
anchor_normal,
)
var anchor_bitangent: Vector3 = anchor_normal.cross(
anchor_tangent
).normalized()
var width: float = float(state["width"]) * float(state["cell_size"])
var height: float = float(state["height"]) * float(state["cell_size"])
var requested_extent: float = (
float(requested_grid_size) * SurfaceDrawingProtocol.CELL_SIZE
)
if width <= 0.0 or height <= 0.0:
continue
var horizontal_spacing: float = (width + requested_extent) * 0.5
var vertical_spacing: float = (height + requested_extent) * 0.5
var horizontal_step: int = roundi(
relative.dot(anchor_tangent) / horizontal_spacing
)
var vertical_step: int = roundi(
relative.dot(anchor_bitangent) / vertical_spacing
)
if horizontal_step == 0 and vertical_step == 0:
continue
var candidate: Vector3 = (
anchor_origin
+ anchor_tangent * float(horizontal_step) * horizontal_spacing
+ anchor_bitangent * float(vertical_step) * vertical_spacing
)
var distance: float = candidate.distance_to(origin)
if distance > nearest_distance:
continue
nearest_distance = distance
best = {
"origin": candidate,
"normal": anchor_normal,
"tangent": anchor_tangent,
"snapped": true,
}
return best
static func _projected_tangent(
value: Vector3,
normal: Vector3,
) -> Vector3:
var tangent: Vector3 = (value - normal * value.dot(normal)).normalized()
if tangent.is_zero_approx():
tangent = Vector3.UP.cross(normal).normalized()
if tangent.is_zero_approx():
tangent = Vector3.RIGHT
return tangent