307 lines
10 KiB
GDScript
307 lines
10 KiB
GDScript
extends RefCounted
|
|
class_name VizSimpleLeg
|
|
|
|
var _error : String = ""
|
|
var _begin : VisualizationSpot
|
|
var _end : VisualizationSpot
|
|
var _width : float
|
|
var _bend_lip : float
|
|
var _bend : VizBend
|
|
|
|
const MIN_SEGMENT_LENGTH=0.01
|
|
const EPSILON=0.00001
|
|
|
|
func _init(p_begin : VisualizationSpot, p_end : VisualizationSpot, p_width : float, p_bend_lip : float):
|
|
_begin = p_begin
|
|
_end = p_end
|
|
_width = p_width
|
|
_bend_lip = p_bend_lip
|
|
if not _has_valid_normal():
|
|
return
|
|
if _is_straight():
|
|
return
|
|
_bend = VizBend.new(_begin, _end, p_width, p_bend_lip)
|
|
if not _bend.is_invalid():
|
|
_check_offsets(_begin, _end)
|
|
|
|
# ---------------------------------------------
|
|
# Public Methods
|
|
|
|
func get_error() -> String:
|
|
if _error != "":
|
|
return _error
|
|
if _bend != null:
|
|
return _bend.get_error()
|
|
return ""
|
|
|
|
func is_invalid() -> bool:
|
|
if _error != "":
|
|
return true
|
|
if _bend != null:
|
|
return _bend.is_invalid()
|
|
return false
|
|
|
|
func extend_mesh(mesh_node : Node3D, u : float, left : Vector3, right : Vector3, segs : int, sharpness : float, mat : Material) -> float:
|
|
if _bend == null:
|
|
u = _extend_straight_leg(mesh_node, u, left, right, _begin.point, _end.point, _begin.normal, mat)
|
|
else:
|
|
u = _extend_straight_leg(mesh_node, u, left, right, _begin.point, _bend.get_begin(), _begin.normal, mat)
|
|
if _bend.is_angled():
|
|
u = _create_begin_triangle(mesh_node, u, _begin.normal, _bend.get_begin_triangle(), mat)
|
|
u = _create_bend(mesh_node, u, segs, sharpness, mat)
|
|
if _bend.is_angled():
|
|
u = _create_end_triangle(mesh_node, u, _end.normal, _bend.get_end_triangle(), mat)
|
|
u = _create_straight_leg(mesh_node, u, _bend.get_end(), _end.point, _end.normal, mat)
|
|
return u
|
|
|
|
func update_mesh(mesh_node : Node3D, u : float, segs : int, sharpness : float, mat : Material) -> float:
|
|
if _bend == null:
|
|
u = _create_straight_leg(mesh_node, u, _begin.point, _end.point, _begin.normal, mat)
|
|
else:
|
|
u = _create_straight_leg(mesh_node, u, _begin.point, _bend.get_begin(), _begin.normal, mat)
|
|
if _bend.is_angled():
|
|
u = _create_begin_triangle(mesh_node, u, _begin.normal, _bend.get_begin_triangle(), mat)
|
|
u = _create_bend(mesh_node, u, segs, sharpness, mat)
|
|
if _bend.is_angled():
|
|
u = _create_end_triangle(mesh_node, u, _end.normal, _bend.get_end_triangle(), mat)
|
|
u = _create_straight_leg(mesh_node, u, _bend.get_end(), _end.point, _end.normal, mat)
|
|
return u
|
|
|
|
func get_begin() -> VisualizationSpot:
|
|
return _begin
|
|
|
|
func get_leg_bend_begin() -> Vector3:
|
|
if _bend == null:
|
|
return (_end.point - _begin.point)/2.0
|
|
return _bend.get_begin_triangle().get_leg_mid()
|
|
|
|
func get_begin_ray() -> Vector3:
|
|
if _bend == null:
|
|
return (_end.point - _begin.point).normalized()
|
|
return (_bend.get_begin() - _begin.point).normalized()
|
|
|
|
func adjust_begin(distance : float):
|
|
var new_begin := VisualizationSpot.new()
|
|
new_begin.point = _begin.point + distance * get_begin_ray()
|
|
new_begin.normal = _begin.normal
|
|
if _check_offsets(new_begin, _end):
|
|
_begin = new_begin
|
|
|
|
func get_end() -> VisualizationSpot:
|
|
return _end
|
|
|
|
func get_leg_bend_end() -> Vector3:
|
|
if _bend == null:
|
|
return (_end.point - _begin.point)/2.0
|
|
return _bend.get_end_triangle().get_leg_mid()
|
|
|
|
func get_end_left() -> Vector3:
|
|
var segment := _end.point - get_leg_bend_end()
|
|
var binormal := segment.cross(_end.normal).normalized()
|
|
var half_width = binormal*_width/2.0
|
|
return _end.point - half_width
|
|
|
|
func get_end_right() -> Vector3:
|
|
var segment := _end.point - get_leg_bend_end()
|
|
var binormal := segment.cross(_end.normal).normalized()
|
|
var half_width = binormal*_width/2.0
|
|
return _end.point + half_width
|
|
|
|
func get_end_ray() -> Vector3:
|
|
if _bend == null:
|
|
return (_end.point - _begin.point).normalized()
|
|
return (_end.point - _bend.get_end()).normalized()
|
|
|
|
func adjust_end(distance : float):
|
|
var new_end := VisualizationSpot.new()
|
|
new_end.point = _end.point - distance * get_end_ray()
|
|
new_end.normal = _end.normal
|
|
if _check_offsets(_begin, new_end):
|
|
_end = new_end
|
|
|
|
# ---------------------------------------------
|
|
# Local Methods
|
|
|
|
func _check_offsets(p_begin : VisualizationSpot, p_end : VisualizationSpot) -> bool:
|
|
var dir := (_end.point - _begin.point).normalized()
|
|
var min_len := MIN_SEGMENT_LENGTH + EPSILON
|
|
var max_begin := _end.point - dir * min_len
|
|
var max_end := _begin.point + dir * min_len
|
|
if _bend != null:
|
|
max_begin = _bend.get_begin() - dir * min_len
|
|
max_end = _bend.get_end() + dir * min_len
|
|
var new_begin_dir := (max_begin - p_begin.point).normalized()
|
|
var new_end_dir := (p_end.point - max_end).normalized()
|
|
if new_begin_dir.dot(dir) <= 0.0:
|
|
_error = "bend start %s is too near %s to be bent with this width and lip" % [ _begin.point, _end.point ]
|
|
return false
|
|
if new_end_dir.dot(dir) <= 0.0:
|
|
_error = "bend end %s is too near %s to be bent with this width and lip" % [ _end.point, _begin.point ]
|
|
return false
|
|
return true
|
|
|
|
func _has_valid_normal() -> bool:
|
|
var segment = _end.point - _begin.point
|
|
var binormal = segment.cross(_begin.normal).normalized()
|
|
if not binormal.is_normalized():
|
|
_error = "%s to %s is parallel to normal %s" % [ _begin.point, _end.point, _begin.normal ]
|
|
return false
|
|
return true
|
|
|
|
func _is_straight() -> bool:
|
|
if _begin.normal.is_equal_approx(_end.normal):
|
|
var segment = _end.point - _begin.point
|
|
if is_equal_approx(segment.dot(_begin.normal), 0.0):
|
|
return true
|
|
_error = "%s to %s have same normal but are not in same plane" % [ _begin.point, _end.point ]
|
|
return false
|
|
|
|
func _extend_straight_leg(mesh_node : Node3D, u : float, left : Vector3, right : Vector3, begin : Vector3, end : Vector3, normal : Vector3, mat : Material):
|
|
var segment := end - begin
|
|
var binormal := segment.cross(normal).normalized()
|
|
var half_width = binormal*_width/2.0
|
|
var seg := begin - end
|
|
var seg_len := seg.length()
|
|
|
|
var st := SurfaceTool.new()
|
|
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
|
st.set_color(Color(1, 0, 0))
|
|
st.set_normal(normal)
|
|
|
|
st.set_uv(Vector2(u, 1))
|
|
st.add_vertex(right)
|
|
|
|
st.set_uv(Vector2(u, 0))
|
|
st.add_vertex(left)
|
|
|
|
st.set_uv(Vector2(u+seg_len, 1))
|
|
st.add_vertex(end+half_width)
|
|
|
|
st.set_uv(Vector2(u+seg_len, 0))
|
|
st.add_vertex(end-half_width)
|
|
|
|
st.set_material(mat)
|
|
mesh_node.mesh = st.commit(mesh_node.mesh)
|
|
|
|
return u + seg_len
|
|
|
|
func _create_straight_leg(mesh_node : Node3D, u : float, begin : Vector3, end : Vector3, normal : Vector3, mat : Material):
|
|
var segment := end - begin
|
|
var binormal := segment.cross(normal).normalized()
|
|
var half_width = binormal*_width/2.0
|
|
var seg := begin - end
|
|
var seg_len := seg.length()
|
|
|
|
var st := SurfaceTool.new()
|
|
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
|
st.set_color(Color(1, 0, 0))
|
|
st.set_normal(normal)
|
|
|
|
st.set_uv(Vector2(u, 1))
|
|
st.add_vertex(begin+half_width)
|
|
|
|
st.set_uv(Vector2(u, 0))
|
|
st.add_vertex(begin-half_width)
|
|
|
|
st.set_uv(Vector2(u+seg_len, 1))
|
|
st.add_vertex(end+half_width)
|
|
|
|
st.set_uv(Vector2(u+seg_len, 0))
|
|
st.add_vertex(end-half_width)
|
|
|
|
st.set_material(mat)
|
|
mesh_node.mesh = st.commit(mesh_node.mesh)
|
|
|
|
return u + seg_len
|
|
|
|
func _create_begin_triangle(mesh_node : Node3D, u : float, normal : Vector3, triangle : VizTriangle, mat : Material):
|
|
var st := SurfaceTool.new()
|
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
|
st.set_color(Color(1, 0, 0))
|
|
st.set_normal(normal)
|
|
|
|
st.set_uv(Vector2(u, 0))
|
|
st.add_vertex(triangle.leg_left)
|
|
st.set_uv(Vector2(u + triangle.distance, triangle.get_fwd_side()))
|
|
st.add_vertex(triangle.leg_fwd)
|
|
st.set_uv(Vector2(u, 1))
|
|
st.add_vertex(triangle.leg_right)
|
|
st.set_material(mat)
|
|
mesh_node.mesh = st.commit(mesh_node.mesh)
|
|
return u + triangle.distance
|
|
|
|
func _create_end_triangle(mesh_node : Node3D, u : float, normal : Vector3, triangle : VizTriangle, mat : Material):
|
|
var st := SurfaceTool.new()
|
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
|
st.set_color(Color(1, 0, 0))
|
|
st.set_normal(normal)
|
|
|
|
st.set_uv(Vector2(u + triangle.distance, 0))
|
|
st.add_vertex(triangle.leg_left)
|
|
st.set_uv(Vector2(u + triangle.distance, 1))
|
|
st.add_vertex(triangle.leg_right)
|
|
st.set_uv(Vector2(u, triangle.get_fwd_side()))
|
|
st.add_vertex(triangle.leg_fwd)
|
|
st.set_material(mat)
|
|
mesh_node.mesh = st.commit(mesh_node.mesh)
|
|
return u + triangle.distance
|
|
|
|
func _create_bend(mesh_node : Node3D, u : float, segs : int, sharpness : float, mat : Material):
|
|
var right_u := u - _bend.get_begin_triangle().distance
|
|
var left_u := u
|
|
if _bend.is_angled_against_curve():
|
|
right_u = u
|
|
left_u = u - _bend.get_begin_triangle().distance
|
|
var arc_len := 0.0
|
|
|
|
var segment := _bend.get_begin() - _begin.point
|
|
var binormal := segment.cross(_begin.normal).normalized()
|
|
var half_width = _bend.get_half_bend_width()
|
|
var half_width_normal := half_width.normalized()
|
|
var st := SurfaceTool.new()
|
|
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
|
st.set_normal(_begin.normal)
|
|
st.set_uv(Vector2(right_u, 1))
|
|
st.add_vertex(_bend.get_begin_triangle().bend_right)
|
|
st.set_uv(Vector2(left_u, 0))
|
|
st.add_vertex(_bend.get_begin_triangle().bend_left)
|
|
var shift_width := _bend.get_shift_width()
|
|
var last_point := _bend.get_arc_begin_point()
|
|
var total_len := 0.0
|
|
for i in range(0, segs):
|
|
var weight := float(i) / float(segs-1)
|
|
var cur_point := _bend.get_arc_point(weight, sharpness)
|
|
total_len += cur_point.distance_to(last_point)
|
|
last_point = cur_point
|
|
total_len += _bend.get_arc_end_point().distance_to(last_point)
|
|
last_point = _bend.get_arc_begin_point()
|
|
for i in range(1, segs-1):
|
|
var weight := float(i) / float(segs-1)
|
|
var cur_point := _bend.get_arc_point(weight, sharpness)
|
|
arc_len += cur_point.distance_to(last_point)
|
|
var shift := shift_width * (arc_len / total_len)
|
|
var pos := cur_point + shift - _bend.get_shift_offset()
|
|
var cur_normal := Vector3(cur_point - last_point).normalized().cross(half_width_normal)
|
|
last_point = cur_point
|
|
st.set_normal(cur_normal)
|
|
st.set_uv(Vector2(right_u + arc_len, 1))
|
|
st.add_vertex(pos-half_width)
|
|
st.set_uv(Vector2(left_u + arc_len, 0))
|
|
st.add_vertex(pos+half_width)
|
|
st.set_normal(_end.normal)
|
|
st.set_uv(Vector2(right_u + total_len, 1))
|
|
st.add_vertex(_bend.get_end_triangle().bend_right)
|
|
st.set_uv(Vector2(left_u + total_len, 0))
|
|
st.add_vertex(_bend.get_end_triangle().bend_left)
|
|
st.set_material(mat)
|
|
mesh_node.mesh = st.commit(mesh_node.mesh)
|
|
u = right_u + total_len
|
|
if _bend.is_angled_against_curve():
|
|
u = left_u + total_len
|
|
return u
|
|
|
|
func _to_string():
|
|
if _bend != null:
|
|
return "SLEG(%s->%s through %s)" % [ _begin, _end, _bend ]
|
|
return "SLEG(%s->%s)" % [ _begin, _end ]
|
|
|