Skip to content

Instantly share code, notes, and snippets.

@partybusiness
Last active February 16, 2025 17:57
Show Gist options
  • Save partybusiness/9d457778698ea29ce388802a157cf995 to your computer and use it in GitHub Desktop.
Save partybusiness/9d457778698ea29ce388802a157cf995 to your computer and use it in GitHub Desktop.
#Based on Procedural Toolkit by Syomus (https://github.com/Syomus/ProceduralToolkit)
@tool
extends PrimitiveMesh
class_name SuperSphere
# generates a supersphere
## size of the supersphere
@export var radius : float = 1.0:
get:
return radius
set(p_value):
radius = p_value
request_update()
## curve of supersphere 2.0 == sphere, higher becomes more cubish
@export var n : float = 3.0:
get:
return n
set(p_value):
n = p_value
request_update()
## number of segments to divide each face horizontally and vertically
@export var segments : int = 16:
get:
return segments
set(p_value):
segments = p_value
request_update()
## whether to apply cubemap UVs or mape each face individually
@export var cube_map_uvs : bool = false:
get:
return cube_map_uvs
set(p_value):
cube_map_uvs = p_value
request_update()
# gets point on surface of supersphere along this vector
func get_point(dir:Vector3) -> Vector3:
dir = dir.normalized()
var dist:float = pow(abs(dir.x), n) + pow(abs(dir.y), n) + pow(abs(dir.z), n)
dist = abs(pow(dist, 1.0 / n))
return dir * radius / dist
# generates a vector along x and y converted to angles
func generate_vector(x:float, y:float) -> Vector3:
var rx:float = (x - 0.5) * PI / 2.0
var ry:float = (y - 0.5) * PI / 2.0
var result:Vector3 = Vector3(-sin(rx) * cos(ry), - cos(rx) * sin(ry), cos(rx) * cos(ry))
return result.normalized()
# generates normals
func generate_normal(x:float, y:float) -> Vector3:
# just does it the brute force way rather than doing a derivative
var offset:float = 0.02 / float(segments)
var x1: = get_point(generate_vector(x - offset, y))
var x2: = get_point(generate_vector(x + offset, y))
var y1: = get_point(generate_vector(x, y + offset))
var y2: = get_point(generate_vector(x, y - offset))
var tangent:Vector3 = (x2 - x1).normalized()
var binormal:Vector3 = (y1 - y2).normalized()
return tangent.cross(binormal)
# generates a single row of vertices
func generate_row_of_vertices(y:float, rotation:Quaternion, p_vert_array : PackedVector3Array, p_normal_array : PackedVector3Array, p_uv_array : PackedVector2Array, uv_offset:Vector2) -> void:
for xx in range(segments + 1):
var x:float = float(xx) / float(segments)
var vec:Vector3 = rotation * get_point(generate_vector(x, y))
p_vert_array.append(vec)
p_normal_array.append(rotation * generate_normal(x, y))
if cube_map_uvs:
p_uv_array.append(Vector2(x / 3.0, y / 2.0) + uv_offset)
else:
p_uv_array.append(Vector2(x, y))
# add to list of vertices
# adds the indices that will connect one row of vertices to the row above it
func add_row_of_indices(last_index:int, p_index_array:PackedInt32Array) -> void:
for xx in range(0, segments):
# two triangles
var s:int = xx + last_index
p_index_array.append_array([s, s + 1, s - segments])
p_index_array.append_array([s, s - segments, s - segments - 1])
# generates one of six faces of a supersphere
func generate_face(rotation:Quaternion, p_vert_array : PackedVector3Array, p_normal_array : PackedVector3Array, p_uv_array : PackedVector2Array, p_index_array : PackedInt32Array, uv_offset:Vector2) -> void:
generate_row_of_vertices(0, rotation, p_vert_array, p_normal_array, p_uv_array, uv_offset) # generate initial row
for yy in range(1, segments + 1):
generate_row_of_vertices(float(yy) / float(segments), rotation, p_vert_array, p_normal_array, p_uv_array, uv_offset)
# add indices for triangles
add_row_of_indices(p_vert_array.size() - 1 - segments, p_index_array)
# add normals?
func _create_mesh_array() -> Array:
var verts := PackedVector3Array()
var uvs := PackedVector2Array()
var normals := PackedVector3Array()
var indices := PackedInt32Array()
# generate six faces of supersphere
var face_rotation:Quaternion = Quaternion.IDENTITY
generate_face(face_rotation, verts, normals, uvs, indices, Vector2(1.0 / 3.0, 0.0))
face_rotation = Quaternion.from_euler(Vector3(0, PI/2.0, 0))
generate_face(face_rotation, verts, normals, uvs, indices, Vector2(2.0 / 3.0, 0.5))
face_rotation = Quaternion.from_euler(Vector3(0, PI, 0))
generate_face(face_rotation, verts, normals, uvs, indices, Vector2(0.0, 0.0))
face_rotation = Quaternion.from_euler(Vector3(0, -PI/2.0, 0))
generate_face(face_rotation, verts, normals, uvs, indices, Vector2(1.0 / 3.0, 0.5))
face_rotation = Quaternion.from_euler(Vector3(PI/2.0, -PI/2.0, 0))
generate_face(face_rotation, verts, normals, uvs, indices, Vector2(0.0, 0.5))
face_rotation = Quaternion.from_euler(Vector3(-PI/2.0, -PI/2.0, 0))
generate_face(face_rotation, verts, normals, uvs, indices, Vector2(2.0 / 3.0, 0.0))
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = verts
arrays[Mesh.ARRAY_TEX_UV] = uvs
arrays[Mesh.ARRAY_NORMAL] = normals
#arrays[Mesh.ARRAY_TEX_UV2] = uvs
arrays[Mesh.ARRAY_INDEX] = indices
return arrays
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment