Last active
February 16, 2025 17:57
-
-
Save partybusiness/9d457778698ea29ce388802a157cf995 to your computer and use it in GitHub Desktop.
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
#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