-
Notifications
You must be signed in to change notification settings - Fork 0
/
surface_net.gd
159 lines (134 loc) · 4.65 KB
/
surface_net.gd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
extends MeshInstance3D
var surface_tool := SurfaceTool.new();
func _ready():
surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES);
surface_tool.set_color(Colors.BLUE_D);
surface_tool.set_smooth_group(0)
create_surface_mesh();
mesh = surface_tool.commit();
const CENTER := Vector3.ZERO;
const RADIUS: float = 5.0;
func get_sample_value(index: Vector3i) -> float:
return CENTER.distance_to(index) - RADIUS;
## Creates a surface mesh by sampling and creating quads to divide the samples
## size: square radius to create the mesh in
func create_surface_mesh(size: int = 6):
for x in range(-size, size):
for y in range(-size, size):
for z in range(-size, size):
create_surface_mesh_quad(Vector3i(x,y,z));
## Create 0-3 quads based on the sample values of the given index and it's neighboring sample points
func create_surface_mesh_quad(index: Vector3i):
for axis_index in range(AXIS.size()):
var axis = AXIS[axis_index];
var sample_value1 = get_sample_value(index);
var sample_value2 = get_sample_value(index + axis);
if sample_value1 < 0 and sample_value2 >= 0:
add_quad(index, axis_index);
elif sample_value1 >= 0 and sample_value2 < 0:
add_reversed_quad(index, axis_index);
## Constant for the positive axis directions
const AXIS := [
Vector3i(1,0,0),
Vector3i(0,1,0),
Vector3i(0,0,1),
];
## Create a quad using Godot's Surface Tool
func add_quad(index: Vector3i, axis_index: int):
var points = get_quad_points(index, axis_index);
add_vertex(points[0])
add_vertex(points[1])
add_vertex(points[2])
add_vertex(points[0])
add_vertex(points[2])
add_vertex(points[3])
## Create a quad with reversed faces using Godot's Surface Tool
func add_reversed_quad(index: Vector3i, axis_index: int):
var points = get_quad_points(index, axis_index);
add_vertex(points[0])
add_vertex(points[2])
add_vertex(points[1])
add_vertex(points[0])
add_vertex(points[3])
add_vertex(points[2])
## Get the indexs of the 4 points for a quad facing the direction of AXIS[axis_index]
func get_quad_points(index: Vector3i, axis_index: int):
return [
index + QUAD_POINTS[axis_index][0],
index + QUAD_POINTS[axis_index][1],
index + QUAD_POINTS[axis_index][2],
index + QUAD_POINTS[axis_index][3],
];
# The 4 relative indexes of the corners of a Quad that is orthogonal to each axis
const QUAD_POINTS := [
# x axis
[
Vector3i(0,0,-1),
Vector3i(0,-1,-1),
Vector3i(0,-1,0),
Vector3i(0,0,0)
],
# y axis
[
Vector3i(0,0,-1),
Vector3i(0,0,0),
Vector3i(-1,0,0),
Vector3i(-1,0,-1)
],
# z axis
[
Vector3i(0,0,0),
Vector3i(0,-1,0),
Vector3i(-1,-1,0),
Vector3i(-1,0,0)
],
];
## Add a vertex with Godot's Surface Tool
func add_vertex(index: Vector3i):
var sample_value = get_sample_value(index);
var surface_position = get_surface_position(index);
var surface_gradient = get_surface_gradient(index, sample_value);
surface_tool.set_normal(surface_gradient);
surface_tool.add_vertex(surface_position);
## Calculate the surface position by finding the interpolated surface point for each edge
## Then average the interpolated points, and skip any edges that have the same sign
func get_surface_position(index: Vector3i):
var total := Vector3.ZERO;
var surface_edge_count = 0;
for edge_offsets in EDGE_OFFSETS:
var position_a = Vector3(index + edge_offsets[0]);
var sample_a = get_sample_value(position_a);
var position_b = Vector3(index + edge_offsets[1])
var sample_b = get_sample_value(position_b);
# if different signs
if sample_a * sample_b < 0:
surface_edge_count += 1;
total += position_a.lerp(position_b, abs(sample_a) / (abs(sample_a) + abs(sample_b)));
if surface_edge_count == 0:
return Vector3(index) + Vector3.ONE * 0.5;
return total / surface_edge_count;
## Calculate the surface normal by finding the slopes in the x,y,z directions of the surface
func get_surface_gradient(index: Vector3i, sample_value: float) -> Vector3:
return Vector3(
get_sample_value(index + AXIS[0]) - sample_value,
get_sample_value(index + AXIS[1]) - sample_value,
get_sample_value(index + AXIS[2]) - sample_value
).normalized();
## All the relative offsets for the two points of all 12 edges of a unit cube
const EDGE_OFFSETS := [
# Edges on min Z axis
[Vector3i(0,0,0),Vector3i(1,0,0)],
[Vector3i(1,0,0),Vector3i(1,1,0)],
[Vector3i(1,1,0),Vector3i(0,1,0)],
[Vector3i(0,1,0),Vector3i(0,0,0)],
# Edges on max Z axis
[Vector3i(0,0,1),Vector3i(1,0,1)],
[Vector3i(1,0,1),Vector3i(1,1,1)],
[Vector3i(1,1,1),Vector3i(0,1,1)],
[Vector3i(0,1,1),Vector3i(0,0,1)],
# Edges connecting min Z to max Z
[Vector3i(0,0,0),Vector3i(0,0,1)],
[Vector3i(1,0,0),Vector3i(1,0,1)],
[Vector3i(1,1,0),Vector3i(1,1,1)],
[Vector3i(0,1,0),Vector3i(0,1,1)],
]