CylinderLine

extends Node3D

# 控制
@export var enable: bool = true: 
	set(value):
		var was_enabled = enable
		enable = value
		if ready_called and enable != was_enabled:
			if enable:
				engage()
			else:
				disengage()

# 导出变量
@export var mainline: CharacterBody3D
@export var camera: Camera3D
@export var cylinder_tail_parent: Node3D

@export_group("Cylinder Settings")
@export var cylinder_reference_node: Node3D
@export var auto_update_cylinder: bool = true
@export var cylinder_radius: float = 14.5
@export var cylinder_center_offset: Vector3 = Vector3.ZERO
@export var cylinder_axis_direction: Vector3 = Vector3(1, 0, -1)

@export_group("Transform Offsets")
@export var position_offset: Vector3 = Vector3.ZERO
@export var rotation_offset: Vector3 = Vector3.ZERO
@export var initial_angle_degrees: float = 0.0

@export_group("Update Settings")
@export var update_per_frame: bool = true

@export_group("Spiral Motion Settings")
@export_enum("Fixed Angular Speed", "Fixed Pitch") var spiral_mode: int = 1
@export var spiral_angular_speed: float = 1.0
@export var spiral_pitch: float = 15.0
@export var spiral_enabled: bool = true
@export var use_independent_speed: bool = false
@export var forward_speed: float = 5.0

@export_group("Trail Settings")
@export var map_trails: bool = true
@export var trail_color: Color = Color.WHITE
@export var trail_emission: bool = false
@export var trail_emission_color: Color = Color.WHITE
@export var trail_emission_energy: float = 1.0
@export var min_segment_length: float = 0.01

# 内部变量
var cylinder_axis_point: Vector3
var cylinder_axis: Vector3
var last_mainline_position: Vector3
var current_angle: float = 0.0
var current_axial_progress: float = 0.0
var target_transform: Transform3D
var reference_direction: Vector3
var perpendicular_direction: Vector3
var current_cylinder_line: MeshInstance3D = null
var line_start_position: Vector3
var is_drawing: bool = false
var mainline_tail_node: Node3D = null
var last_mapped_position: Vector3
var has_last_mapped_position: bool = false
var cylinder_bend_shader: Shader
var cylinder_bend_material: ShaderMaterial

# 偏移变换
var rotation_offset_basis: Basis

# 螺旋运动相关
var spiral_direction: float = 1.0

# 状态标记
var ready_called: bool = false
var engage_flag: bool = false
var resources_initialized: bool = false

var last_cylinder_reference_position: Vector3

# 缓存的计算结果
var _cached_local_rotation: Basis
var _cached_two_pi_div_pitch: float
var _movement_threshold_squared: float = 0.0001  # 0.01^2

# 优化:缓存信号连接状态
var _signals_connected: bool = false

func _ready():
	ready_called = true
	# 预计算常用值
	_cached_local_rotation = Basis.from_euler(Vector3(deg_to_rad(90), deg_to_rad(90), 0))
	if spiral_pitch > 0.001:
		_cached_two_pi_div_pitch = TAU / spiral_pitch

func _init_resources():
	if resources_initialized:
		return
	
	if not mainline:
		return
	
	cylinder_bend_shader = load("res://cylinder_bend.gdshader")
	if not cylinder_bend_shader:
		return
	
	if not camera:
		camera = get_viewport().get_camera_3d()
	
	if not cylinder_tail_parent:
		cylinder_tail_parent = Node3D.new()
		cylinder_tail_parent.name = "CylinderTail"
		get_tree().current_scene.add_child(cylinder_tail_parent)
	
	_update_offset_transform()
	setup_cylinder()
	last_cylinder_reference_position = cylinder_axis_point
	create_base_material()
	
	var scene_root = get_tree().current_scene
	if scene_root and scene_root.has_node("tail"):
		mainline_tail_node = scene_root.get_node("tail")
	
	resources_initialized = true

func _update_offset_transform():
	rotation_offset_basis = Basis.from_euler(Vector3(
		deg_to_rad(rotation_offset.x),
		deg_to_rad(rotation_offset.y),
		deg_to_rad(rotation_offset.z)
	))

func _enter_tree():
	if mainline and engage_flag and not _signals_connected:
		_connect_signals()

func _exit_tree():
	if _signals_connected:
		_disconnect_signals()

func _connect_signals():
	if not mainline or _signals_connected:
		return
	
	if mainline.has_signal("onturn"):
		mainline.onturn.connect(_on_mainline_turn)
	
	if mainline.has_signal("new_line1"):
		mainline.new_line1.connect(_on_mainline_new_line)
	
	if mainline.has_signal("on_sky"):
		mainline.on_sky.connect(_on_mainline_on_sky)
	
	_signals_connected = true

func _disconnect_signals():
	if not mainline or not _signals_connected:
		return
	
	if mainline.has_signal("onturn") and mainline.onturn.is_connected(_on_mainline_turn):
		mainline.onturn.disconnect(_on_mainline_turn)
	
	if mainline.has_signal("new_line1") and mainline.new_line1.is_connected(_on_mainline_new_line):
		mainline.new_line1.disconnect(_on_mainline_new_line)
	
	if mainline.has_signal("on_sky") and mainline.on_sky.is_connected(_on_mainline_on_sky):
		mainline.on_sky.disconnect(_on_mainline_on_sky)
	
	_signals_connected = false

func engage():
	if not ready_called:
		return
	
	if not resources_initialized:
		_init_resources()
	
	if not resources_initialized:
		return
	
	engage_flag = true
	
	if not _signals_connected:
		_connect_signals()
	
	current_angle = deg_to_rad(initial_angle_degrees)
	initialize_mapping()
	
	if map_trails and has_last_mapped_position:
		is_drawing = true
		create_new_cylinder_line()

func disengage():
	if not engage_flag:
		return
	
	engage_flag = false
	_disconnect_signals()
	stop_drawing_trails()

func stop_drawing_trails():
	is_drawing = false
	current_cylinder_line = null

func manual_clear_trails():
	clear_cylinder_trails()

func setup_cylinder():
	var reference_position: Vector3
	if cylinder_reference_node and is_instance_valid(cylinder_reference_node):
		reference_position = cylinder_reference_node.global_position
	elif camera:
		reference_position = camera.global_position
	else:
		return
	
	cylinder_axis_point = reference_position + cylinder_center_offset
	cylinder_axis = cylinder_axis_direction.normalized()
	reference_direction = Vector3.UP.cross(cylinder_axis)
	if reference_direction.length_squared() < 0.000001:  # 使用 length_squared 避免开方
		reference_direction = Vector3.RIGHT.cross(cylinder_axis)
	reference_direction = reference_direction.normalized()
	perpendicular_direction = cylinder_axis.cross(reference_direction).normalized()

func create_base_material():
	cylinder_bend_material = ShaderMaterial.new()
	cylinder_bend_material.shader = cylinder_bend_shader
	cylinder_bend_material.set_shader_parameter("cylinder_axis_point", cylinder_axis_point)
	cylinder_bend_material.set_shader_parameter("cylinder_axis", cylinder_axis)
	cylinder_bend_material.set_shader_parameter("cylinder_radius", cylinder_radius)
	cylinder_bend_material.set_shader_parameter("albedo_color", trail_color)
	cylinder_bend_material.set_shader_parameter("use_emission", trail_emission)
	cylinder_bend_material.set_shader_parameter("emission_color", trail_emission_color)
	cylinder_bend_material.set_shader_parameter("emission_energy", trail_emission_energy)

func initialize_mapping():
	if not mainline or not engage_flag:
		return
	
	last_mainline_position = mainline.global_position
	var to_mainline = mainline.global_position - cylinder_axis_point
	current_axial_progress = to_mainline.dot(cylinder_axis)
	var virtual_position = cylinder_axis_point + cylinder_axis * current_axial_progress
	map_position_to_cylinder(virtual_position, -mainline.transform.basis.z)

func _on_mainline_turn():
	if not engage_flag:
		return
	spiral_direction = -spiral_direction  # 优化:直接取反

func _on_mainline_new_line():
	if not engage_flag:
		return
	if target_transform:
		last_mapped_position = target_transform.origin
		has_last_mapped_position = true
	if map_trails:
		is_drawing = true
		create_new_cylinder_line()

func _on_mainline_on_sky():
	if not engage_flag:
		return
	if target_transform:
		last_mapped_position = target_transform.origin
		has_last_mapped_position = true
	is_drawing = false
	current_cylinder_line = null

func _process(delta):
	if not engage_flag or not mainline:
		return
	
	if auto_update_cylinder:
		_auto_update_cylinder_position()
	
	if update_per_frame:
		var current_pos = mainline.global_position
		var forward = -mainline.transform.basis.z
		var axial_movement: float
		
		if use_independent_speed:
			axial_movement = forward_speed * delta
		else:
			var movement = current_pos - last_mainline_position
			axial_movement = movement.dot(cylinder_axis)
		
		if spiral_enabled:
			if spiral_mode == 0:
				update_spiral_motion_angular_speed(axial_movement, forward, delta)
			else:
				update_spiral_motion_fixed_pitch(axial_movement, forward)
		
		# 优化:使用 distance_squared_to 避免开方
		if current_pos.distance_squared_to(last_mainline_position) > _movement_threshold_squared:
			last_mainline_position = current_pos
		
		if map_trails and is_drawing and current_cylinder_line and is_instance_valid(current_cylinder_line):
			update_cylinder_trail()

func _auto_update_cylinder_position():
	var reference_position: Vector3
	if cylinder_reference_node and is_instance_valid(cylinder_reference_node):
		reference_position = cylinder_reference_node.global_position
	elif camera:
		reference_position = camera.global_position
	else:
		return
	
	var new_axis_point = reference_position + cylinder_center_offset
	# 优化:使用 distance_squared_to
	if new_axis_point.distance_squared_to(last_cylinder_reference_position) > 0.000001:
		cylinder_axis_point = new_axis_point
		last_cylinder_reference_position = new_axis_point
		if cylinder_bend_material:
			cylinder_bend_material.set_shader_parameter("cylinder_axis_point", cylinder_axis_point)
		_update_all_trails_cylinder_params()

func _update_all_trails_cylinder_params():
	if not cylinder_tail_parent:
		return
	
	# 优化:批量更新,减少函数调用
	var children = cylinder_tail_parent.get_children()
	for child in children:
		if child is MeshInstance3D:
			var line_material = child.get_surface_override_material(0)
			if line_material and line_material is ShaderMaterial:
				# 一次性设置多个参数
				line_material.set_shader_parameter("cylinder_axis_point", cylinder_axis_point)
				line_material.set_shader_parameter("cylinder_axis", cylinder_axis)
				line_material.set_shader_parameter("cylinder_radius", cylinder_radius)
				line_material.set_shader_parameter("reference_direction", reference_direction)
				line_material.set_shader_parameter("perpendicular_direction", perpendicular_direction)

func update_spiral_motion_angular_speed(axial_movement: float, forward_direction: Vector3, delta: float):
	current_axial_progress += axial_movement
	current_angle += spiral_angular_speed * spiral_direction * delta
	var virtual_position = cylinder_axis_point + cylinder_axis * current_axial_progress
	map_position_to_cylinder(virtual_position, forward_direction)

func update_spiral_motion_fixed_pitch(axial_movement: float, forward_direction: Vector3):
	current_axial_progress += axial_movement
	if spiral_pitch > 0.001:
		# 优化:使用预计算的值
		var angle_increment = axial_movement * _cached_two_pi_div_pitch
		current_angle += angle_increment * spiral_direction
	var virtual_position = cylinder_axis_point + cylinder_axis * current_axial_progress
	map_position_to_cylinder(virtual_position, forward_direction)

func map_position_to_cylinder(mainline_pos: Vector3, _forward_direction: Vector3):
	var to_mainline = mainline_pos - cylinder_axis_point
	var axial_distance = to_mainline.dot(cylinder_axis)
	var axial_position = cylinder_axis_point + cylinder_axis * axial_distance
	var radial_direction = get_radial_direction_from_angle(current_angle)
	var surface_position = axial_position + radial_direction * cylinder_radius
	
	# 应用位置偏移
	surface_position += position_offset
	
	var tangent_direction = cylinder_axis.cross(radial_direction).normalized()
	if spiral_direction < 0:
		tangent_direction = -tangent_direction
	
	var rotation_basis = Basis()
	rotation_basis.z = -tangent_direction
	rotation_basis.y = cylinder_axis
	rotation_basis.x = rotation_basis.y.cross(rotation_basis.z).normalized()
	rotation_basis = rotation_basis.orthonormalized()
	
	# 应用旋转偏移
	rotation_basis = rotation_offset_basis * rotation_basis
	
	# 优化:使用缓存的局部旋转
	target_transform = Transform3D()
	target_transform.origin = surface_position
	target_transform.basis = rotation_basis * _cached_local_rotation
	last_mapped_position = surface_position
	has_last_mapped_position = true

func create_new_cylinder_line():
	if not engage_flag or not cylinder_tail_parent:
		return
	if not has_last_mapped_position:
		return
	
	current_cylinder_line = MeshInstance3D.new()
	
	# 优化:复用 BoxMesh 实例(如果需要大量创建,可以使用对象池)
	var box_mesh = BoxMesh.new()
	box_mesh.size = Vector3(1, 1, 1)
	box_mesh.subdivide_width = 2
	box_mesh.subdivide_height = 2
	box_mesh.subdivide_depth = 20
	
	current_cylinder_line.mesh = box_mesh
	var line_material = cylinder_bend_material.duplicate()
	line_start_position = last_mapped_position
	
	# 批量设置 shader 参数
	line_material.set_shader_parameter("line_start_position", line_start_position)
	line_material.set_shader_parameter("line_end_position", line_start_position)
	line_material.set_shader_parameter("line_length", 0.01)
	line_material.set_shader_parameter("reference_direction", reference_direction)
	line_material.set_shader_parameter("perpendicular_direction", perpendicular_direction)
	
	current_cylinder_line.set_surface_override_material(0, line_material)
	current_cylinder_line.global_transform = Transform3D.IDENTITY
	current_cylinder_line.name = "CylinderLine"
	current_cylinder_line.extra_cull_margin = 16384.0
	current_cylinder_line.visibility_range_end_margin = 0.0
	cylinder_tail_parent.add_child(current_cylinder_line)

func update_cylinder_trail():
	if not engage_flag or not current_cylinder_line or not is_instance_valid(current_cylinder_line):
		return
	
	var current_pos = target_transform.origin
	var offset = current_pos - line_start_position
	var distance_squared = offset.length_squared()  # 优化:先用平方比较
	
	if distance_squared < min_segment_length * min_segment_length:
		return
	
	var line_material = current_cylinder_line.get_surface_override_material(0)
	if line_material and line_material is ShaderMaterial:
		var distance = sqrt(distance_squared)  # 只在需要时才开方
		line_material.set_shader_parameter("line_end_position", current_pos)
		line_material.set_shader_parameter("line_length", distance)
	last_mapped_position = current_pos

@warning_ignore("shadowed_variable")
func get_radial_direction_from_angle(angle: float) -> Vector3:
	# 优化:内联计算,减少临时变量
	var cos_a = cos(angle)
	var sin_a = sin(angle)
	return (reference_direction * cos_a + perpendicular_direction * sin_a).normalized()

func project_vector_to_cylinder_plane(vec: Vector3) -> Vector3:
	var projection_on_axis = vec.dot(cylinder_axis) * cylinder_axis
	var projected = vec - projection_on_axis
	return projected.normalized() if projected.length_squared() > 0.000001 else Vector3.ZERO

func project_point_to_cylinder_plane(point: Vector3) -> Vector3:
	var to_point = point - cylinder_axis_point
	var projection_on_axis = to_point.dot(cylinder_axis)
	return point - cylinder_axis * projection_on_axis

func get_mapping_info() -> Dictionary:
	return {
		"enabled": enable,
		"engaged": engage_flag,
		"spiral_mode": "固定角速度" if spiral_mode == 0 else "固定螺距",
		"speed_mode": "独立速度" if use_independent_speed else "跟随mainline",
		"forward_speed": forward_speed,
		"spiral_direction": "顺时针" if spiral_direction > 0 else "逆时针",
		"angle_degrees": rad_to_deg(current_angle),
		"angle_radians": current_angle,
		"axial_progress": current_axial_progress,
		"surface_position": target_transform.origin,
		"cylinder_axis_point": cylinder_axis_point,
		"cylinder_reference": "自定义节点" if cylinder_reference_node else "相机",
		"auto_update": auto_update_cylinder,
		"cylinder_radius": cylinder_radius,
		"spiral_pitch": spiral_pitch,
		"spiral_angular_speed": spiral_angular_speed,
		"position_offset": position_offset,
		"rotation_offset": rotation_offset,
		"initial_angle_degrees": initial_angle_degrees,
		"distance_to_axis": target_transform.origin.distance_to(
			cylinder_axis_point + cylinder_axis * current_axial_progress
		) if engage_flag else 0.0,
		"is_drawing": is_drawing,
		"active_lines": cylinder_tail_parent.get_child_count() if cylinder_tail_parent else 0,
		"current_line_length": (target_transform.origin - line_start_position).length() if is_drawing and has_last_mapped_position else 0.0
	}

func clear_cylinder_trails():
	if not cylinder_tail_parent:
		return
	
	# 优化:直接获取子节点并删除,避免创建临时数组
	var children = cylinder_tail_parent.get_children()
	for child in children:
		if is_instance_valid(child):
			child.queue_free()
	
	# 如果需要立即清理,可以等待一帧
	if children.size() > 0:
		await get_tree().process_frame

cylinderTP.gd

extends Node

@export var cylinder_mapper: Node3D

func _ready():
	if not cylinder_mapper:
		push_error("CylinderTP: 需要指定 cylinder_mapper 引用!")
		return

func flip_to_opposite_side():
	if not cylinder_mapper:
		push_warning("CylinderTP: cylinder_mapper 未设置")
		return
	
	if cylinder_mapper.is_drawing and cylinder_mapper.current_cylinder_line:
		cylinder_mapper.is_drawing = false
		cylinder_mapper.current_cylinder_line = null
	
	cylinder_mapper.current_angle += PI
	
	if cylinder_mapper.engage_flag and cylinder_mapper.mainline:
		var forward = -cylinder_mapper.mainline.transform.basis.z
		var virtual_position = cylinder_mapper.cylinder_axis_point + cylinder_mapper.cylinder_axis * cylinder_mapper.current_axial_progress
		cylinder_mapper.map_position_to_cylinder(virtual_position, forward)
		
		cylinder_mapper.last_mapped_position = cylinder_mapper.target_transform.origin
		cylinder_mapper.has_last_mapped_position = true
		
		if cylinder_mapper.map_trails:
			cylinder_mapper.is_drawing = true
			cylinder_mapper.create_new_cylinder_line()

cylinder_bend.gdshader

shader_type spatial;
render_mode unshaded;

// 圆柱体参数
uniform vec3 cylinder_axis_point = vec3(0.0, 0.0, 0.0);  // 圆柱体轴线上的点
uniform vec3 cylinder_axis = vec3(0.707, 0.0, -0.707);   // 圆柱体轴线方向(归一化)
uniform float cylinder_radius = 14.5;                     // 圆柱体半径

// 线段参数
uniform vec3 line_start_position;      // 线段起点(世界坐标)
uniform vec3 line_end_position;        // 线段终点(世界坐标)
uniform float line_length = 1.0;       // 线段长度

// 圆柱体坐标系
uniform vec3 reference_direction = vec3(0.0, 1.0, 0.0);      // 参考方向
uniform vec3 perpendicular_direction = vec3(1.0, 0.0, 0.0);  // 垂直方向

// 材质参数
uniform vec4 albedo_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable;
uniform float metallic : hint_range(0.0, 1.0) = 0.0;
uniform float roughness : hint_range(0.0, 1.0) = 1.0;
uniform bool use_emission = false;
uniform vec4 emission_color : source_color = vec4(1.0, 1.0, 1.0, 1.0);
uniform float emission_energy : hint_range(0.0, 16.0) = 1.0;

// 计算点到轴线的投影
vec3 project_to_axis(vec3 point, vec3 axis_point, vec3 axis_dir) {
    vec3 to_point = point - axis_point;
    float axial_distance = dot(to_point, axis_dir);
    return axis_point + axis_dir * axial_distance;
}

// 计算径向方向
vec3 get_radial_direction(vec3 point, vec3 axis_point, vec3 axis_dir) {
    vec3 projection = project_to_axis(point, axis_point, axis_dir);
    vec3 radial = point - projection;
    float len = length(radial);
    return len > 0.001 ? radial / len : vec3(0.0, 1.0, 0.0);
}

// 从角度获取径向方向(使用圆柱体坐标系)
vec3 get_radial_from_angle(float angle, vec3 ref_dir, vec3 perp_dir) {
    return normalize(ref_dir * cos(angle) + perp_dir * sin(angle));
}

// 计算点在圆柱体上的角度
float get_angle_on_cylinder(vec3 point, vec3 axis_point, vec3 axis_dir, vec3 ref_dir, vec3 perp_dir) {
    vec3 projection = project_to_axis(point, axis_point, axis_dir);
    vec3 radial = point - projection;
    float len = length(radial);
    
    if (len < 0.001) {
        return 0.0;
    }
    
    radial = radial / len;
    
    float cos_angle = dot(radial, ref_dir);
    float sin_angle = dot(radial, perp_dir);
    
    return atan(sin_angle, cos_angle);
}

// 将点映射到圆柱体表面(保持在圆柱体表面上)
vec3 map_to_cylinder_surface(vec3 point, vec3 axis_point, vec3 axis_dir, float radius, vec3 ref_dir, vec3 perp_dir) {
    // 1. 获取轴向投影
    vec3 axial_projection = project_to_axis(point, axis_point, axis_dir);
    
    // 2. 获取角度
    float angle = get_angle_on_cylinder(point, axis_point, axis_dir, ref_dir, perp_dir);
    
    // 3. 从角度计算径向方向
    vec3 radial_dir = get_radial_from_angle(angle, ref_dir, perp_dir);
    
    // 4. 映射到表面
    return axial_projection + radial_dir * radius;
}

// 计算圆柱体表面的法线
vec3 calculate_cylinder_normal(vec3 surface_point, vec3 axis_point, vec3 axis_dir) {
    vec3 axial_projection = project_to_axis(surface_point, axis_point, axis_dir);
    vec3 radial = surface_point - axial_projection;
    float len = length(radial);
    return len > 0.001 ? radial / len : vec3(0.0, 1.0, 0.0);
}

// 在圆柱体表面插值两点
vec3 interpolate_on_cylinder(vec3 start_pos, vec3 end_pos, float t, vec3 axis_point, vec3 axis_dir, float radius, vec3 ref_dir, vec3 perp_dir) {
    // 获取起点和终点的角度和轴向位置
    float start_angle = get_angle_on_cylinder(start_pos, axis_point, axis_dir, ref_dir, perp_dir);
    float end_angle = get_angle_on_cylinder(end_pos, axis_point, axis_dir, ref_dir, perp_dir);
    
    // 处理角度跨越 -π 到 π 的情况
    float angle_diff = end_angle - start_angle;
    if (angle_diff > 3.14159) {
        start_angle += 6.28318;
    } else if (angle_diff < -3.14159) {
        end_angle += 6.28318;
    }
    
    // 插值角度
    float interpolated_angle = mix(start_angle, end_angle, t);
    
    // 获取轴向位置
    vec3 start_axial = project_to_axis(start_pos, axis_point, axis_dir);
    vec3 end_axial = project_to_axis(end_pos, axis_point, axis_dir);
    
    // 插值轴向位置
    vec3 interpolated_axial = mix(start_axial, end_axial, t);
    
    // 从插值后的角度获取径向方向
    vec3 radial_dir = get_radial_from_angle(interpolated_angle, ref_dir, perp_dir);
    
    // 返回圆柱体表面上的点
    return interpolated_axial + radial_dir * radius;
}

void vertex() {
    // 获取顶点在模型空间的位置
    vec3 local_pos = VERTEX;
    
    // 计算沿线段的进度 (0.0 到 1.0)
    // 假设线段沿 Z 轴,范围从 -0.5 到 +0.5
    float t = local_pos.z + 0.5;
    t = clamp(t, 0.0, 1.0);
    
    // 在圆柱体表面上插值起点和终点
    vec3 curved_center = interpolate_on_cylinder(
        line_start_position,
        line_end_position,
        t,
        cylinder_axis_point,
        cylinder_axis,
        cylinder_radius,
        reference_direction,
        perpendicular_direction
    );
    
    // 计算该点的切线方向(用于横向偏移)
    float epsilon = 0.01;
    float t_before = clamp(t - epsilon, 0.0, 1.0);
    float t_after = clamp(t + epsilon, 0.0, 1.0);
    
    vec3 point_before = interpolate_on_cylinder(
        line_start_position,
        line_end_position,
        t_before,
        cylinder_axis_point,
        cylinder_axis,
        cylinder_radius,
        reference_direction,
        perpendicular_direction
    );
    
    vec3 point_after = interpolate_on_cylinder(
        line_start_position,
        line_end_position,
        t_after,
        cylinder_axis_point,
        cylinder_axis,
        cylinder_radius,
        reference_direction,
        perpendicular_direction
    );
    
    // 计算切线方向
    vec3 tangent_dir = normalize(point_after - point_before);
    
    // 计算该点的径向方向(法线方向)
    vec3 radial_dir = calculate_cylinder_normal(curved_center, cylinder_axis_point, cylinder_axis);
    
    // 计算横向方向(垂直于切线和径向)
    vec3 lateral_dir = normalize(cross(tangent_dir, radial_dir));
    
    // 应用偏移
    // local_pos.x: 横向偏移(沿着 lateral_dir)
    // local_pos.y: 径向偏移(沿着 radial_dir,用于线条厚度)
    vec3 final_position = curved_center + lateral_dir * local_pos.x + radial_dir * local_pos.y;
    
    // 更新顶点位置(转回局部空间)
    VERTEX = (inverse(MODEL_MATRIX) * vec4(final_position, 1.0)).xyz;
    
    // 计算法线
    // 对于线条,法线应该是径向方向
    vec3 final_normal = radial_dir;
    NORMAL = normalize((inverse(transpose(mat3(MODEL_MATRIX))) * final_normal));
    
    // 传递 UV 坐标
    UV = UV;
}

void fragment() {
    // 基础颜色
    vec4 tex_color = texture(texture_albedo, UV);
    ALBEDO = albedo_color.rgb * tex_color.rgb;
    ALPHA = albedo_color.a * tex_color.a;
    
    // 材质属性
    METALLIC = metallic;
    ROUGHNESS = roughness;
    
    // 自发光
    if (use_emission) {
        EMISSION = emission_color.rgb * emission_energy;
    }
}

posted @ 2026-02-01 10:19  meny  阅读(4)  评论(0)    收藏  举报