Created
June 23, 2025 05:35
-
-
Save Lightnet/d842c786094979f10aa550390bbc1021 to your computer and use it in GitHub Desktop.
circle uv test godot 4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@tool | |
extends MeshInstance3D | |
# this is for ImmediateMesh | |
# works uv align but scale does not work. | |
# Rope properties | |
@export var rope_width: float = 0.1 # Radius of the rope | |
@export var resoulution: int = 8 # Number of vertices around the rope's circumference | |
@export var uv_scale: Vector2 = Vector2(1.0, 1.0) # UV scaling factor | |
@export var texture_height_to_width: float = 1.0 # Texture aspect ratio (height/width) | |
@export var show_debug_cubes: bool = true # Toggle to show/hide debug cubes | |
@export var use_face_colors: bool = true # Toggle to enable/disable per-face colors | |
@export var disable_face_logging: bool = false # Toggle to disable per-face logging | |
# Mesh data arrays | |
var vertex_array: Array = [] | |
var uv1_array: Array = [] | |
var normal_array: Array = [] | |
var tangent_array: Array = [] | |
var index_array: Array = [] | |
var color_array: Array = [] # For per-vertex colors | |
# Rope points | |
var points: Array = [] | |
@export var point_count: int = 0 | |
var player_position: Vector3 = Vector3.ZERO | |
var grapple_hook_position: Vector3 = Vector3(0, 0, 1) # Short segment for testing | |
# Debug cubes | |
var debug_cubes: Array = [] # Store references to debug cube nodes | |
var cube_mesh: BoxMesh = BoxMesh.new() # Placeholder cube mesh | |
func _ready(): | |
# Initialize test points for a single segment | |
initialize_test_points() | |
# Setup cube mesh | |
setup_debug_cubes() | |
func _process(delta: float) -> void: | |
generate_mesh() | |
func setup_debug_cubes(): | |
# Configure cube mesh | |
cube_mesh.size = Vector3(0.05, 0.05, 0.05) # Small cubes for visibility | |
func clear_debug_cubes(): | |
# Remove all existing debug cubes | |
for cube in debug_cubes: | |
if is_instance_valid(cube): | |
cube.queue_free() | |
debug_cubes.clear() | |
func create_debug_cube(position: Vector3, uv: Vector2, face_id: int, face_color: Color) -> MeshInstance3D: | |
# Create a new cube instance | |
var cube = MeshInstance3D.new() | |
cube.mesh = cube_mesh | |
var material = StandardMaterial3D.new() | |
material.albedo_color = face_color | |
material.flags_unshaded = true | |
cube.material_override = material | |
cube.global_position = position | |
# Add to scene tree | |
get_tree().root.add_child(cube) | |
# Store UV and face ID as metadata | |
cube.set_meta("uv", uv) | |
cube.set_meta("face_id", face_id) | |
# Add to debug_cubes array | |
debug_cubes.append(cube) | |
return cube | |
func initialize_test_points(): | |
# Create a single rope segment (two points) | |
points.clear() | |
points.append(player_position) # Start at (0, 0, 0) | |
points.append(grapple_hook_position) # End at (0, 0, 1) | |
point_count = points.size() | |
func generate_mesh(): | |
# Safeguard against invalid setup | |
if points.is_empty() or point_count < 2 or resoulution < 3: | |
print("GenerateMesh: Invalid setup (empty points, point_count < 2, or resoulution < 3)") | |
return | |
vertex_array.clear() | |
uv1_array.clear() | |
normal_array.clear() | |
tangent_array.clear() | |
index_array.clear() | |
color_array.clear() | |
# Clear existing debug cubes | |
if show_debug_cubes: | |
clear_debug_cubes() | |
# Calculate normals and tangents | |
calculate_normals() | |
# Clear existing mesh | |
mesh.clear_surfaces() | |
# Begin mesh construction | |
mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLES) | |
# Total rope length for UV scaling | |
var total_rope_length = (grapple_hook_position - player_position).length() | |
if total_rope_length == 0: | |
total_rope_length = 0.001 # Prevent division by zero | |
# Circumference for UV aspect ratio | |
var circumference = 2.0 * PI * rope_width | |
var uv_u_scale = circumference * texture_height_to_width # ≈0.6283 | |
# Generate vertices, UVs, colors, and indices | |
for p in range(point_count): | |
var center: Vector3 = points[p] | |
var forward = tangent_array[p] | |
var norm = normal_array[p] | |
var bitangent = norm.cross(forward).normalized() | |
# UV V-coordinate: normalized distance along rope | |
var distance_from_start = (center - player_position).length() | |
var uv1_v = distance_from_start / total_rope_length | |
# Generate vertices for the segment | |
for c in range(resoulution): | |
var angle = (float(c) / resoulution) * 2.0 * PI | |
var xVal = sin(angle) * rope_width | |
var yVal = cos(angle) * rope_width | |
var point = (norm * xVal) + (bitangent * yVal) + center | |
vertex_array.append(point) | |
# UV U-coordinate: normalize to [0, uv_u_scale] | |
var uv1_u = (float(c) / resoulution) * uv_u_scale | |
var uv = Vector2(uv1_u, uv1_v) * uv_scale | |
uv1_array.append(uv) | |
# Placeholder color | |
color_array.append(Color.WHITE) | |
# Generate triangle indices | |
if p < point_count - 1: | |
var start_index = resoulution * p | |
var i1 = start_index + c | |
var i2 = start_index + c + resoulution | |
var i3 = start_index + (c + 1) % resoulution | |
index_array.append(i1) | |
index_array.append(i2) | |
index_array.append(i3) | |
var i4 = start_index + (c + 1) % resoulution | |
var i5 = start_index + c + resoulution | |
var i6 = start_index + ((c + 1) % resoulution) + resoulution | |
index_array.append(i4) | |
index_array.append(i5) | |
index_array.append(i6) | |
# Add triangles to ImmediateMesh with per-face colors and debug cubes | |
var face_id: int = 0 | |
var max_faces = index_array.size() / 3 | |
for i in range(max_faces): | |
var i1 = index_array[3 * i] | |
var i2 = index_array[3 * i + 1] | |
var i3 = index_array[3 * i + 2] | |
var p1 = vertex_array[i1] | |
var p2 = vertex_array[i2] | |
var p3 = vertex_array[i3] | |
var uv1 = uv1_array[i1] | |
var uv2 = uv1_array[i2] | |
var uv3 = uv1_array[i3] | |
# Apply UV fix for faces 0, 1, 2, 14, 15 | |
if face_id == max_faces - 1: # Face 15 | |
uv1.x = uv_u_scale | |
uv3.x = uv_u_scale | |
elif face_id == max_faces - 2: # Face 14 | |
uv3.x = uv_u_scale | |
elif face_id == 1: # Face 1 | |
uv1.x = uv_u_scale / resoulution # ≈0.0785 | |
uv3.x = uv_u_scale / resoulution # ≈0.0785 | |
elif face_id == 0: # Face 0 | |
uv3.x = uv_u_scale / resoulution # ≈0.0785 | |
elif face_id == 2: # Face 2 | |
uv3.x = 2 * uv_u_scale / resoulution # ≈0.157 | |
#uv3.x = 1 * uv_u_scale / resoulution # ≈0.157 | |
pass | |
# Calculate triangle normal | |
var edge1 = p2 - p1 | |
var edge2 = p3 - p1 | |
var normal = edge1.cross(edge2).normalized() | |
# Check for degenerate triangle | |
var area = edge1.cross(edge2).length() / 2.0 | |
var is_degenerate = area < 0.0001 | |
# Assign unique color to this face | |
var face_color = Color.from_hsv(float(face_id) / max_faces, 0.8, 0.8) if use_face_colors else Color.WHITE | |
# Update vertex colors for this face | |
color_array[i1] = face_color | |
color_array[i2] = face_color | |
color_array[i3] = face_color | |
# Log face information for all faces to verify | |
if not disable_face_logging: | |
print("Face ", face_id, ":") | |
print(" Indices: ", [i1, i2, i3]) | |
print(" Vertex 1: ", p1, ", UV: ", uv1) | |
print(" Vertex 2: ", p2, ", UV: ", uv2) | |
print(" Vertex 3: ", p3, ", UV: ", uv3) | |
print(" UV u differences: ", [abs(uv2.x - uv1.x), abs(uv3.x - uv1.x), abs(uv3.x - uv2.x)]) | |
print(" Triangle area: ", area, ", Degenerate: ", is_degenerate) | |
print(" Color: ", face_color) | |
if show_debug_cubes: | |
var cube1 = create_debug_cube(p1, uv1, face_id, face_color) | |
var cube2 = create_debug_cube(p2, uv2, face_id, face_color) | |
var cube3 = create_debug_cube(p3, uv3, face_id, face_color) | |
print(" Cube 1 ID: ", cube1.get_instance_id(), ", Position: ", p1) | |
print(" Cube 2 ID: ", cube2.get_instance_id(), ", Position: ", p2) | |
print(" Cube 3 ID: ", cube3.get_instance_id(), ", Position: ", p3) | |
else: | |
create_debug_cube(p1, uv1, face_id, face_color) | |
create_debug_cube(p2, uv2, face_id, face_color) | |
create_debug_cube(p3, uv3, face_id, face_color) | |
else: | |
if show_debug_cubes: | |
create_debug_cube(p1, uv1, face_id, face_color) | |
create_debug_cube(p2, uv2, face_id, face_color) | |
create_debug_cube(p3, uv3, face_id, face_color) | |
# Add vertices with normals, UVs, and colors | |
mesh.surface_set_normal(normal) | |
mesh.surface_set_uv(uv1) | |
mesh.surface_set_color(face_color) | |
mesh.surface_add_vertex(p1) | |
mesh.surface_set_normal(normal) | |
mesh.surface_set_uv(uv2) | |
mesh.surface_set_color(face_color) | |
mesh.surface_add_vertex(p2) | |
mesh.surface_set_normal(normal) | |
mesh.surface_set_uv(uv3) | |
mesh.surface_set_color(face_color) | |
mesh.surface_add_vertex(p3) | |
face_id += 1 | |
# End mesh | |
mesh.surface_end() | |
func calculate_normals(): | |
normal_array.clear() | |
tangent_array.clear() | |
for i in range(point_count): | |
var tangent: Vector3 = Vector3.ZERO | |
var normal: Vector3 = Vector3.ZERO | |
var temp_helper_vector: Vector3 = Vector3.ZERO | |
# First point | |
if i == 0: | |
tangent = (points[i + 1] - points[i]).normalized() | |
# Last point | |
elif i == point_count - 1: | |
tangent = (points[i] - points[i - 1]).normalized() | |
# Between point | |
else: | |
tangent = ((points[i + 1] - points[i]).normalized() + | |
(points[i] - points[i - 1]).normalized()).normalized() | |
if i == 0: | |
temp_helper_vector = Vector3.UP if tangent.dot(Vector3.UP) < 0.5 else -Vector3.FORWARD | |
normal = temp_helper_vector.cross(tangent).normalized() | |
else: | |
var tangent_prev = tangent_array[i - 1] | |
var normal_prev = normal_array[i - 1] | |
var bitangent = tangent_prev.cross(tangent) | |
if bitangent.length() == 0: | |
normal = normal_prev | |
else: | |
var bitangent_dir = bitangent.normalized() | |
var theta = acos(tangent_prev.dot(tangent)) | |
var rotate_matrix = Basis(bitangent_dir, theta) | |
normal = (rotate_matrix * normal_prev).normalized() | |
tangent_array.append(tangent) | |
normal_array.append(normal) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment