Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified Courses/Range/Terrain/terrain3d-01-01.res
Binary file not shown.
Binary file modified Courses/Range/Terrain/terrain3d-01_00.res
Binary file not shown.
Binary file modified Courses/Range/Terrain/terrain3d_00-01.res
Binary file not shown.
Binary file modified Courses/Range/Terrain/terrain3d_00_00.res
Binary file not shown.
Binary file modified Courses/Range/Terrain/terrain3d_01-01.res
Binary file not shown.
Binary file modified Courses/Range/Terrain/terrain3d_01_00.res
Binary file not shown.
225 changes: 225 additions & 0 deletions Courses/Range/arc_line.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
@tool
extends MeshInstance3D
var label_left: Node3D
var label_right: Node3D

@export var distance := 100.0 :
set(value):
distance = value
_update_arc()

@export var arc_angle := 180.0 :
set(value):
arc_angle = value
_update_arc()

@export var thickness := 0.5 :
set(value):
thickness = value
_update_arc()

@export var segments := 64 :
set(value):
segments = max(4, value)
_update_arc()

@export var color := Color.WHITE :
set(value):
color = value
_update_material()
_update_labels()

@export_range(0.1, 1.0, 0.05)
var label_arc_ratio := 0.5 :
set(value):
label_arc_ratio = value
_update_arc()

@export var label_text := "100" :
set(value):
label_text = value
_update_arc()

@export var label_char_spacing := 0.1 :
set(value):
label_char_spacing = value
_update_arc()

@export var label_offset := 3.0 :
set(value):
label_offset = value
_update_arc()

@export var label_size := 500 :
set(value):
label_size = value
_update_arc()

@export_range(-180.0, 180.0, 1.0)
var label_letter_angle_offset := 30 :
set(value):
label_letter_angle_offset = value
_update_arc()


func _enter_tree():
_setup_labels()

func _ready():
_update_material()
_update_arc()

func _update_arc():
if not is_inside_tree():
return

mesh = _generate_arc_mesh()
position.y = 0.05 # lift above grass
_update_labels()


func _generate_arc_mesh() -> ArrayMesh:
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)

var half_angle = deg_to_rad(arc_angle) * 0.5

for i in range(segments):
var t1 = float(i) / segments
var t2 = float(i + 1) / segments

var a1 = lerp(-half_angle, half_angle, t1)
var a2 = lerp(-half_angle, half_angle, t2)

var r_inner = distance - thickness
var r_outer = distance + thickness

var inner1 = Vector3(cos(a1) * r_inner, 0, sin(a1) * r_inner)
var outer1 = Vector3(cos(a1) * r_outer, 0, sin(a1) * r_outer)
var inner2 = Vector3(cos(a2) * r_inner, 0, sin(a2) * r_inner)
var outer2 = Vector3(cos(a2) * r_outer, 0, sin(a2) * r_outer)

st.add_vertex(inner1)
st.add_vertex(outer1)
st.add_vertex(outer2)

st.add_vertex(inner1)
st.add_vertex(outer2)
st.add_vertex(inner2)

st.generate_normals()
return st.commit()

func _update_material():
var mat := StandardMaterial3D.new()
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
mat.albedo_color = color
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
mat.depth_draw_mode = BaseMaterial3D.DEPTH_DRAW_OPAQUE_ONLY
material_override = mat

func _update_labels():
_setup_labels()
if label_left == null or label_right == null:
return

var half_angle := deg_to_rad(arc_angle) * 0.5
var label_angle := half_angle * label_arc_ratio

_build_curved_label(label_left, -label_angle, -1.0, true)
_build_curved_label(label_right, label_angle, 1, false)


func _place_label(label: Label3D, angle: float):
if label == null:
return

# Position on arc
label.position = Vector3(
cos(angle) * distance,
0.02,
sin(angle) * distance
)

# Lay flat + rotate tangentially to arc
label.rotation = Vector3(
-PI / 2, # flat on ground
angle + PI / 2 + PI, # tangent direction
0
)

func _build_curved_label(container: Node3D, base_angle: float, side_sign: float, flip_180: bool = false):
if container == null:
return

for c in container.get_children():
c.queue_free()

var char_count := label_text.length()
if char_count == 0:
return

var half_span := (char_count - 1) * label_char_spacing * 0.5
var label_radius := distance + label_offset
var angle_offset := deg_to_rad(label_letter_angle_offset)

for i in range(char_count):
var label_char := label_text[i]

var offset := (i * label_char_spacing) - half_span
var char_angle := base_angle + offset

var lbl := Label3D.new()
lbl.text = label_char
lbl.font_size = label_size
lbl.modulate = color
lbl.outline_modulate = color
lbl.pixel_size = 0.01
lbl.billboard = BaseMaterial3D.BILLBOARD_DISABLED

lbl.position = Vector3(
cos(char_angle) * label_radius,
0.02,
sin(char_angle) * label_radius
)

# 🔑 MIRRORED rotation
lbl.rotation = Vector3(
-PI / 2,
char_angle + PI + angle_offset * side_sign,
0
)
if flip_180:
lbl.rotate_y(PI)


container.add_child(lbl)
if Engine.is_editor_hint() and is_inside_tree():
var tree := get_tree()
if tree:
lbl.owner = tree.edited_scene_root

func _setup_labels():
if label_left == null:
label_left = _ensure_label_node("LabelLeft")

if label_right == null:
label_right = _ensure_label_node("LabelRight")


func _ensure_label_node(name: String) -> Node3D:
var node := get_node_or_null(name)
if node:
return node

var container := Node3D.new()
container.name = name
container.set_unique_name_in_owner(true)
add_child(container)

if Engine.is_editor_hint() and is_inside_tree():
var tree := get_tree()
if tree:
container.owner = tree.edited_scene_root

return container
1 change: 1 addition & 0 deletions Courses/Range/arc_line.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://b0xkcntqrhd6f
782 changes: 748 additions & 34 deletions Courses/Range/range.tscn

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions UI/Flag/flag_pole.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
extends Node3D

@export var label_text: int = 150
@export var pole_height := 3.0

@export var flag_color := Color.GREEN_YELLOW
@export var pole_color := Color.WHITE

@onready var pole := $PoleMesh
@onready var flag := $FlagMesh


func _ready():
_apply_height()
_apply_color()

func _apply_height():
var mesh := pole.mesh as CylinderMesh
mesh.height = pole_height
pole.position.y = pole_height / 2.0


func _apply_color():
set_pole_color(pole_color)
set_flag_color(flag_color)

func set_pole_color(c: Color) -> void:
var mat := StandardMaterial3D.new()
mat.albedo_color = c
pole.material_override = mat

func set_flag_color(c: Color) -> void:
var mat : ShaderMaterial = flag.get_active_material(0)

if mat:
var unique_mat = mat.duplicate() as ShaderMaterial
flag.set_surface_override_material(0, unique_mat)

unique_mat.set_shader_parameter("flag_color", c)



func _process(_delta):
pass
1 change: 1 addition & 0 deletions UI/Flag/flag_pole.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://bkt0dnsrg7i0n
25 changes: 25 additions & 0 deletions UI/Flag/flag_pole.gdshader
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
shader_type spatial;
render_mode cull_disabled;
uniform sampler2D uv_offset_texture : hint_default_black;
uniform vec2 uv_offset_scale = vec2(-0.2,-0.1);
uniform vec2 time_scale = vec2(0.3, 0.0);
uniform float face_distortion = 0.5;

uniform vec4 flag_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);


void vertex(){
vec2 base_uv_offset = UV * uv_offset_scale;
base_uv_offset += TIME * time_scale;

float noise = texture(uv_offset_texture, base_uv_offset).r;
float texture_based_offset = noise * 2.0 - 1.0;
texture_based_offset *= UV.x;
VERTEX.y += noise;
VERTEX.z += texture_based_offset * face_distortion;
VERTEX.x += texture_based_offset * -face_distortion;
}

void fragment(){
ALBEDO = flag_color.rgb;
}
1 change: 1 addition & 0 deletions UI/Flag/flag_pole.gdshader.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://dhb4kyon4rp
43 changes: 43 additions & 0 deletions UI/Flag/flag_pole.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[gd_scene load_steps=8 format=3 uid="uid://bx67q2irdmviu"]

[ext_resource type="Script" uid="uid://bkt0dnsrg7i0n" path="res://UI/Flag/flag_pole.gd" id="1_p87f8"]
[ext_resource type="Shader" uid="uid://dhb4kyon4rp" path="res://UI/Flag/flag_pole.gdshader" id="2_45a7n"]

[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p87f8"]
cull_mode = 2

[sub_resource type="CylinderMesh" id="CylinderMesh_ojxsx"]
material = SubResource("StandardMaterial3D_p87f8")
top_radius = 0.05
bottom_radius = 0.05
height = 3.0

[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_78cgf"]
seamless = true

[sub_resource type="ShaderMaterial" id="ShaderMaterial_58edq"]
render_priority = 0
shader = ExtResource("2_45a7n")
shader_parameter/uv_offset_texture = SubResource("NoiseTexture2D_78cgf")
shader_parameter/uv_offset_scale = Vector2(-0.2, -0.1)
shader_parameter/time_scale = Vector2(0.22, 0)
shader_parameter/face_distortion = 0.1
shader_parameter/flag_color = Color(1, 1, 1, 1)

[sub_resource type="PlaneMesh" id="PlaneMesh_7jc6a"]
material = SubResource("ShaderMaterial_58edq")
size = Vector2(0.8, 1.3)
subdivide_width = 10
subdivide_depth = 4

[node name="FlagPole" type="Node3D"]
script = ExtResource("1_p87f8")
pole_color = Color(0.297765, 0.560518, 0.87938124, 1)

[node name="PoleMesh" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
mesh = SubResource("CylinderMesh_ojxsx")

[node name="FlagMesh" type="MeshInstance3D" parent="."]
transform = Transform3D(1.9106855e-15, 4.371139e-08, 1, 1, -4.371139e-08, 0, 4.371139e-08, 1, -4.371139e-08, 0.7, 2.5, -0.5)
mesh = SubResource("PlaneMesh_7jc6a")
Loading