Skip to content

Instantly share code, notes, and snippets.

@slapin
Created May 7, 2020 23:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save slapin/9ab469a9b5482020fcf52ac0f97d6c31 to your computer and use it in GitHub Desktop.
Save slapin/9ab469a9b5482020fcf52ac0f97d6c31 to your computer and use it in GitHub Desktop.
extends Spatial
class GraphicsElement extends MeshInstance:
var mi: MeshInstance
var work_mesh: ArrayMesh
var vertices: = PoolVector3Array()
var indices: = PoolIntArray()
var uvdata: = PoolVector2Array()
var debug : ImmediateGeometry
func add_triangle(points:Array, uvs: Array):
var cur_index: int = indices.size()
for p in range(points.size()):
# print(p)
vertices.push_back(points[p])
uvdata.push_back(uvs[p])
indices.push_back(cur_index)
cur_index += 1
func add_quad(points:Array, uvs: Array):
var p1 = [points[0], points[1], points[2]]
var u1 = [uvs[0], uvs[1], uvs[2]]
var p2 = [points[0], points[2], points[3]]
var u2 = [uvs[0], uvs[2], uvs[3]]
add_triangle(p1, u1)
add_triangle(p2, u2)
func commit(material: Material):
var id = work_mesh.get_surface_count()
var array = []
array.resize(ArrayMesh.ARRAY_MAX)
array[ArrayMesh.ARRAY_VERTEX] = vertices
array[ArrayMesh.ARRAY_TEX_UV] = uvdata
array[ArrayMesh.ARRAY_INDEX] = indices
work_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)
work_mesh.surface_set_material(id, material)
hide()
mesh = work_mesh
show()
func clear():
work_mesh = ArrayMesh.new()
vertices = PoolVector3Array()
indices = PoolIntArray()
uvdata = PoolVector2Array()
func _init():
work_mesh = ArrayMesh.new()
debug = ImmediateGeometry.new()
add_child(debug)
var debug_mat = SpatialMaterial.new()
debug_mat.flags_no_depth_test = true
debug_mat.flags_unshaded = true
debug_mat.vertex_color_use_as_albedo = true
debug.material_override = debug_mat
class ExtrudeProfile extends GraphicsElement:
var profile: = PoolVector2Array()
func extrude(step: float, count: int):
var d = 0.0
var x1: = profile[0].x
var x2: = profile[profile.size() - 1].x
for e in profile:
if e.x > x2:
x2 = e.x
if abs(x2 - x1) < 0.0001:
return
var xd = abs(x2 - x1)
for i in range(count):
for j in range(profile.size()):
if i < count - 1 && j < profile.size() - 1:
var p0 = profile[j]
var p1 = profile[j + 1]
var pt0 = Vector3(p0.x, p0.y, d)
var uv0 = Vector2((p0.x - x1)/ xd, 0)
var pt1 = Vector3(p0.x, p0.y, d + step)
var uv1 = Vector2((p0.x - x1)/ xd, 1)
var pt2 = Vector3(p1.x, p1.y, d + step)
var uv2 = Vector2((p1.x - x1)/ xd, 1)
var pt3 = Vector3(p1.x, p1.y, d)
var uv3 = Vector2((p1.x - x1)/ xd, 0)
add_quad([pt3, pt2, pt1, pt0], [uv3, uv2, uv1, uv0])
d += step
class ExtrudeIntersection extends GraphicsElement:
var profile: = PoolVector2Array()
var lane_curves = []
var lane_geometry = []
var lane_basis = []
var center: Vector3
var polygon = []
var rect: Rect2 = Rect2()
var grid = PoolIntArray()
var size_x: int = 0
var size_y: int = 0
var mc_triangles = []
func mark_inflated(orig: Array, poly: Array):
var mark = []
var c: = Curve2D.new()
for k in orig:
c.add_point(k)
for p in poly:
var closest = -1
var closest_dst = INF
for r in range(orig.size()):
var op = orig[r]
var d = p.distance_to(op)
if d < closest_dst:
closest = r
closest_dst = d
assert(closest >= 0)
mark.push_back(closest)
return mark
func poly2topoly3(p: Array):
var ret = []
for j in range(p.size()):
ret.push_back(Vector3(p[j].x, 0, p[j].y))
return ret
func poly3topoly2(p: Array):
var ret = []
for j in range(p.size()):
ret.push_back(Vector2(p[j].x, p[j].z))
return ret
func recover_from_mark(p: Array, m: Array, poly: Array):
var ret = []
for s in range(m.size()):
var h = p[m[s]].y
var v = poly[s]
v.y = h
ret.push_back(v)
return ret
func isect_angle(left, modp, right, offset) -> Vector3:
var dir1 = (left - modp).normalized()
var dir2 = (right - modp).normalized()
var up = Vector3(0, 1, 0)
var dir1b = dir1.cross(up)
var dir2b = dir2.cross(up)
var isect: Vector3
if abs(dir1.dot(dir2) - 1.0) > 0.001:
var m1 = Vector2(modp.x + dir1b.x * offset, modp.z + dir1b.z * offset)
var m2 = Vector2(modp.x - dir2b.x * offset, modp.z - dir2b.z * offset)
var d1 = Vector2(dir1.x, dir1.z)
var d2 = Vector2(dir2.x, dir2.z)
var xisect = Geometry.line_intersects_line_2d(m1, d1, m2, d2)
assert(xisect != null)
isect = Vector3(xisect.x, modp.y, xisect.y)
else:
isect = modp + dir1b * offset
return isect
func create_lanes(points: PoolVector3Array, lanes: int, lx: float):
var modp = points[1]
var left = points[0]
var right = points[2]
var dir1 = (left - modp).normalized()
var dir2 = (right - modp).normalized()
var up = Vector3(0, 1, 0)
var dir1b = dir1.cross(up)
var dir2b = dir2.cross(up)
for k in range(lanes):
var offset = float(k) * lx
var moffset = offset + lx * 0.5
var isect = isect_angle(left, modp, right, moffset)
var lane_curve = Curve3D.new()
lane_curve.add_point(left + dir1b * moffset)
lane_curve.add_point(isect)
var inh = -dir2 * lx * 0.3 + dir1 * lx * 0.4
var outh = -dir1 * lx * 0.3 + dir2 * lx * 0.4
var depth = lx * 0.5
if !k in [lanes - 1]:
# inh = Vector3()
# outh = Vector3()
depth = lx * 0.7
inh = Vector3()
outh = Vector3()
lane_curve.set_point_in(1, inh)
lane_curve.set_point_out(1, outh)
lane_curve.add_point(right - dir2b * moffset)
lane_basis.push_back([left + dir1b * moffset, isect, right - dir2b * moffset])
lane_curves.push_back(lane_curve)
var lane_data = lane_curve.tessellate(5, 4)
var lane_data_2d = poly3topoly2(lane_data)
var poly = Geometry.offset_polyline_2d(lane_data_2d, depth, 0, 2)
var pdata = Geometry.merge_polygons_2d(polygon, poly[0])
polygon = pdata[0]
var mark = mark_inflated(lane_data_2d, poly[0])
var poly3d = poly2topoly3(poly[0])
poly3d = recover_from_mark(lane_data, mark, poly3d)
lane_geometry.push_back(poly3d)
func sort_neighbors(p1, p2):
var px1 = (p1 - center)
var px2 = (p2 - center)
var pvx1 = Vector2(px1.x, px1.z)
var pvx2 = Vector2(px2.x, px2.z)
return pvx1.angle() < pvx2.angle()
var edges = [
0x0, # 0000 Nothing intersects
0x9, # 0001 0
0x3, # 0010 1
0xA, # 0011 0 1
0x6, # 0100 2
0xF, # 0101 0 2
0x5, # 0110 1 2
0xC, # 0111 0 1 2
0xC, # 1000 3
0x5, # 1001 0 3
0xF, # 1010 1 3
0x6, # 1011 0 1 3
0xA, # 1100 2 3
0x3, # 1101 0 2 3
0x9, # 1110 1 2 3
0x0 # 1111 0 1 2 3
]
var triangles = [
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[7, 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ],
[3, 2, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[7, 2, 0, 7, 3, 2, -1, -1, -1, -1, -1, -1, -1],
[5, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[7, 1, 0, 3, 5, 4, -1, -1, -1, -1, -1, -1, -1],
[1, 4, 2, 1, 5, 4, -1, -1, -1, -1, -1, -1, -1],
[0, 7, 5, 0, 5, 4, 0, 4, 2, -1, -1, -1, -1],
[7, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[0, 5, 1, 0, 6, 5, -1, -1, -1, -1, -1, -1, -1],
[1, 3, 2, 7, 6, 5, -1, -1, -1, -1, -1, -1, -1],
[3, 6, 5, 3, 0, 6, 3, 2, 0, -1, -1, -1, -1],
[7, 6, 4, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1],
[3, 1, 0, 3, 0, 6, 3, 6, 4, -1, -1, -1, -1],
[2, 1, 7, 2, 7, 6, 2, 6, 4, -1, -1, -1, -1],
[6, 2, 0, 6, 4, 2, -1, -1, -1, -1, -1, -1, -1],
[1, 0, 7, 1, 7, 5, 1, 5, 4, 1, 4, 3, -1],
[1, 3, 2, 1, 5, 3, 1, 6, 5, 1, 7, 6, -1]
]
func interpolate(p0, p1, v0, v1):
if abs(v0) < 0.001:
return p0
if abs(v1) < 0.001:
return p1
if abs(v1 - v0) < 0.001:
return p0
var mu = -v0 / (v1 - v0)
return p0 + (p1 - p0) * mu
func ms_init():
for p in polygon:
rect = rect.expand(p)
size_x = int(rect.size.x + 2)
size_y = int(rect.size.y + 2)
var dist = []
grid.resize(size_x * size_y)
dist.resize(size_x * size_y)
for i in range(size_y):
for j in range(size_x):
var data = 0
var verts = [Vector2(j, i), Vector2(j + 1, i), Vector2(j + 1, i + 1), Vector2(j, i + 1)]
var distances = [INF, INF, INF, INF]
for p in range(4):
var v = verts[p] + Vector2(-1, -1) + rect.position
var min_dist
var pip = Geometry.is_point_in_polygon(v, polygon)
if pip:
data |= (1 << p)
for h in range(polygon.size()):
var p1 = polygon[h]
var p2 = polygon[(h + 1) % polygon.size()]
var sp = Geometry.get_closest_point_to_segment_2d(v, p1, p2)
var d = v.distance_to(sp)
if distances[p] > d:
if pip:
distances[p] = d
else:
distances[p] = -d
grid[i * size_x + j] = data
dist[i * size_x + j] = distances
var intersection_points = PoolVector2Array()
intersection_points.resize(8)
var pv = verts.duplicate()
for p in range(verts.size()):
pv[p] += Vector2(-1, -1) + rect.position
intersection_points[p * 2] = pv[p]
var edge: int = edges[data]
if edge & 1:
intersection_points[1] = interpolate(pv[0], pv[1], distances[0], distances[1])
if edge & 2:
intersection_points[3] = interpolate(pv[1], pv[2], distances[1], distances[2])
if edge & 4:
intersection_points[5] = interpolate(pv[2], pv[3], distances[2], distances[3])
if edge & 8:
intersection_points[7] = interpolate(pv[3], pv[0], distances[3], distances[0])
for t in range(0, triangles[data].size(), 3):
if triangles[data][t] < 0:
break
var p0 = intersection_points[triangles[data][t]]
var p1 = intersection_points[triangles[data][t + 1]]
var p2 = intersection_points[triangles[data][t + 2]]
mc_triangles.push_back([p0, p1, p2])
print(grid)
print(dist)
print(mc_triangles)
func extrude(step: float, var center: Vector3, neighbors: PoolVector3Array, lanes: int):
var lx = 3.0
self.center = center
var n = Array(neighbors)
assert(neighbors.size() > 2)
n.sort_custom(self, "sort_neighbors")
for p in range(n.size()):
var p1 = n[p]
var p2 = n[(p + 1) % n.size()]
var points = [p1, center, p2]
create_lanes(PoolVector3Array(points), lanes, lx)
var modp = points[1]
var left = points[0]
var right = points[2]
# var lane_curves = []
# var dir1 = (left - modp).normalized()
# var dir2 = (right - modp).normalized()
# var lane_geometry = []
# var lane_basis = []
# var up = Vector3(0, 1, 0)
# var dir1b = dir1.cross(up)
# var dir2b = dir2.cross(up)
# for k in range(lanes):
# var offset = float(k) * lx
# var moffset = offset + lx * 0.5
# var isect = isect_angle(left, modp, right, moffset)
# var lane_curve = Curve3D.new()
# lane_curve.add_point(left + dir1b * moffset)
# lane_curve.add_point(isect)
# var inh = -dir2 * lx * 0.3 + dir1 * lx * 0.4
# var outh = -dir1 * lx * 0.3 + dir2 * lx * 0.4
# var depth = lx * 0.5
# if !k in [lanes - 1]:
## inh = Vector3()
## outh = Vector3()
# depth = lx * 0.7
# inh = Vector3()
# outh = Vector3()
# lane_curve.set_point_in(1, inh)
# lane_curve.set_point_out(1, outh)
# lane_curve.add_point(right - dir2b * moffset)
# lane_basis.push_back([left + dir1b * moffset, isect, right - dir2b * moffset])
# lane_curves.push_back(lane_curve)
# var lane_data = lane_curve.tessellate(5, 4)
# var lane_data_2d = poly3topoly2(lane_data)
# var poly = Geometry.offset_polyline_2d(lane_data_2d, depth, 0, 2)
# var mark = mark_inflated(lane_data_2d, poly[0])
# var poly3d = poly2topoly3(poly[0])
# poly3d = recover_from_mark(lane_data, mark, poly3d)
# lane_geometry.push_back(poly3d)
debug.begin(Mesh.PRIMITIVE_LINES)
debug.set_color(Color(0, 1, 0, 1))
debug.add_vertex(modp)
debug.add_vertex(left)
debug.add_vertex(modp)
debug.add_vertex(right)
debug.end()
ms_init()
var p3 = poly2topoly3(polygon)
debug.begin(Mesh.PRIMITIVE_LINES)
debug.set_color(Color(0, 0, 1, 1))
for k in lane_curves:
var pts = k.tessellate(5, 4)
for r in range(pts.size() - 1):
var p1 = pts[r]
var p2 = pts[r + 1]
debug.add_vertex(p1)
debug.add_vertex(p2)
debug.end()
debug.begin(Mesh.PRIMITIVE_LINES)
debug.set_color(Color(1, 1, 0, 1))
for k in lane_geometry:
for r in range(k.size()):
var p1 = k[r]
var p2 = k[(r + 1) % k.size()]
debug.add_vertex(p1)
debug.add_vertex(p2)
debug.end()
var poly3 = poly2topoly3(polygon)
debug.begin(Mesh.PRIMITIVE_LINES)
debug.set_color(Color(1, 1, 1, 1))
for r in range(poly3.size()):
var p1 = poly3[r]
var p2 = poly3[(r + 1) % poly3.size()]
debug.add_vertex(p1)
debug.add_vertex(p2)
debug.end()
debug.begin(Mesh.PRIMITIVE_LINES)
debug.set_color(Color(1, 0.6, 0.6, 1))
for t in mc_triangles:
var tx = poly2topoly3(t)
print(t)
debug.add_vertex(tx[0])
debug.add_vertex(tx[1])
debug.add_vertex(tx[1])
debug.add_vertex(tx[2])
debug.add_vertex(tx[2])
debug.add_vertex(tx[0])
debug.end()
func _ready():
var mi1: = ExtrudeIntersection.new()
mi1.profile = PoolVector2Array([Vector2(-3.5, 0), Vector2(-3, 0.1), Vector2(0, 0), Vector2(3, 0.1)])
var pathpt = PoolVector3Array([Vector3(-23, 0, 25.5), Vector3(-23, 0, -25.5), Vector3(13, 0, 25.5)])
mi1.extrude(0.1, Vector3(0, 0, 0), pathpt, 6)
mi1.commit(SpatialMaterial.new())
add_child(mi1)
# var mi2: = ExtrudeIntersection.new()
# mi2.profile = PoolVector2Array([Vector2(-3.5, 0), Vector2(-3, 0.1), Vector2(0, 0), Vector2(3, 0.1)])
# var pathpt2 = PoolVector3Array([Vector3(10 + 3, 0, -6), Vector3(10 + 3, 0, 6)])
# mi2.extrude(0.1, Vector3(10 + 0, 0, 0), pathpt, 1)
# mi2.commit(SpatialMaterial.new())
# add_child(mi2)
var state = 0
func _process(delta):
match(state):
0:
# get_tree().get_root().debug_draw = Viewport.DEBUG_DRAW_OVERDRAW
state = 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment