immersive-home/app/lib/utils/mesh/construct_room_mesh.gd
2024-05-29 18:01:49 +02:00

423 lines
12 KiB
GDScript

static func generate_wall_mesh(corners, height):
if corners.size() < 3:
return null
var st = SurfaceTool.new()
var wall_up = Vector3.UP * height
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
for corner in corners:
var corner3D = Vector3(corner.x, 0, corner.y)
st.add_vertex(corner3D + wall_up)
st.add_vertex(corner3D)
var first_corner = Vector3(corners[0].x, 0, corners[0].y)
st.add_vertex(first_corner + wall_up)
st.add_vertex(first_corner)
st.index()
st.generate_normals()
st.generate_tangents()
var mesh = st.commit()
return mesh
## Generate a wall mesh with doors
## corners: Array of Vector2
## height: float
## doors: Array[Array[Vector3, Vector3]]
static func generate_wall_mesh_with_doors(corners, height, doors):
if corners.size() < 3:
return null
var mesh = ArrayMesh.new()
if Geometry2D.is_polygon_clockwise(PackedVector2Array(corners)) == false:
corners.reverse()
for i in range(0, corners.size()):
var corner = corners[i]
var next_corner = corners[(i + 1) % corners.size()]
var width = corner.distance_to(next_corner)
var forward = Vector3(next_corner.x - corner.x, 0, next_corner.y - corner.y).normalized()
var points := PackedVector2Array()
points.append(Vector2(0, 0))
points.append(Vector2(0, height))
points.append(Vector2(corner.distance_to(next_corner), height))
points.append(Vector2(corner.distance_to(next_corner), 0))
for door in doors:
var door_point1 = Vector2(door[0].x, door[0].z)
var door_point2 = Vector2(door[1].x, door[1].z)
var proj_point1 = Geometry2D.get_closest_point_to_segment_uncapped(door_point1, corner, next_corner)
var proj_point2 = Geometry2D.get_closest_point_to_segment_uncapped(door_point2, corner, next_corner)
if proj_point1.distance_to(door_point1) > 0.02&&proj_point2.distance_to(door_point2) > 0.02:
continue
if proj_point1.distance_to(proj_point2) < 0.02:
continue
var point1_distance = corner.distance_to(proj_point1)
var point2_distance = corner.distance_to(proj_point2)
var door_points := PackedVector2Array()
door_points.append(Vector2(point1_distance, -1))
door_points.append(Vector2(point1_distance, door[0].y))
door_points.append(Vector2(point2_distance, door[1].y))
door_points.append(Vector2(point2_distance, -1))
var clip = Geometry2D.clip_polygons(points, door_points)
if clip.size() == 0:
continue
assert(clip.size() != 2, "Door clip should not create holes")
points = clip[0]
var edges = PackedInt32Array()
for k in range(points.size()):
edges.append(k)
edges.append((k + 1) % points.size())
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
cdt.init(true, true, 0.1)
cdt.insert_vertices(points)
cdt.insert_edges(edges)
cdt.erase_outer_triangles()
points = cdt.get_all_vertices()
var triangles: PackedInt32Array = cdt.get_all_triangles()
var points_3d = PackedVector3Array()
var points_uv = PackedVector2Array()
for k in range(points.size()):
var point = Vector3(corner.x, 0, corner.y) + points[k].x * forward + Vector3(0, points[k].y, 0)
points_3d.append(point)
points_uv.append(Vector2(points[k].x / width, points[k].y / height))
mesh = _create_mesh_3d(points_3d, triangles, points_uv, width, height, mesh)
return mesh
static func generate_ceiling_mesh(corners):
var points: PackedVector2Array = PackedVector2Array()
var edges: PackedInt32Array = PackedInt32Array()
var triangles: PackedInt32Array
if corners.size() < 3:
return null
for i in range(corners.size()):
var corner = corners[i]
points.append(Vector2(corner.x, corner.y))
edges.append(i)
edges.append((i + 1) % corners.size())
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
cdt.init(true, true, 0.1)
cdt.insert_vertices(points)
cdt.insert_edges(edges)
cdt.erase_outer_triangles()
points = cdt.get_all_vertices()
triangles = cdt.get_all_triangles()
return _create_mesh_2d(points, triangles)
static func generate_wall_mesh_with_doors_grid(corners, height, doors, grid:=0.2):
if corners.size() < 3:
return null
var mesh = ArrayMesh.new()
for i in range(0, corners.size()):
var corner = corners[i]
var next_corner = corners[(i + 1) % corners.size()]
var width = corner.distance_to(next_corner)
var forward = Vector3(next_corner.x - corner.x, 0, next_corner.y - corner.y).normalized()
var points := PackedVector2Array()
points.append(Vector2(0, 0))
points.append(Vector2(0, height))
points.append(Vector2(corner.distance_to(next_corner), height))
points.append(Vector2(corner.distance_to(next_corner), 0))
for door in doors:
var door_point1 = Vector2(door[0].x, door[0].z)
var door_point2 = Vector2(door[1].x, door[1].z)
var proj_point1 = Geometry2D.get_closest_point_to_segment_uncapped(door_point1, corner, next_corner)
var proj_point2 = Geometry2D.get_closest_point_to_segment_uncapped(door_point2, corner, next_corner)
if proj_point1.distance_to(door_point1) > 0.02&&proj_point2.distance_to(door_point2) > 0.02:
continue
if proj_point1.distance_to(proj_point2) < 0.02:
continue
var point1_distance = corner.distance_to(proj_point1)
var point2_distance = corner.distance_to(proj_point2)
var door_points := PackedVector2Array()
door_points.append(Vector2(point1_distance, -1))
door_points.append(Vector2(point1_distance, door[0].y))
door_points.append(Vector2(point2_distance, door[1].y))
door_points.append(Vector2(point2_distance, -1))
var clip = Geometry2D.clip_polygons(points, door_points)
if clip.size() == 0:
continue
assert(clip.size() != 2, "Door clip should not create holes")
points = clip[0]
# Subdivide edge to grid
var new_points = PackedVector2Array()
for k in range(points.size()):
var point = points[k]
var next_point = points[(k + 1) % points.size()]
new_points.append(point)
var steps = floor(point.distance_to(next_point) / grid)
for x in range(1, steps):
new_points.append(point + (next_point - point).normalized() * grid * x)
points = new_points
var edges = PackedInt32Array()
for k in range(points.size()):
edges.append(k)
edges.append((k + 1) % points.size())
# Subdivide inner polygon to grid
var steps = ceil(Vector2(corner.distance_to(next_corner) / grid, height / grid))
for y in range(1, steps.y):
for x in range(1, steps.x):
var point = Vector2(x * grid, y * grid)
points.append(point)
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
cdt.init(true, true, 0.001)
cdt.insert_vertices(points)
cdt.insert_edges(edges)
cdt.erase_outer_triangles()
points = cdt.get_all_vertices()
var triangles: PackedInt32Array = cdt.get_all_triangles()
var points_3d = PackedVector3Array()
var points_uv = PackedVector2Array()
for k in range(points.size()):
points_3d.append(Vector3(corner.x, 0, corner.y) + points[k].x * forward + Vector3(0, points[k].y, 0))
points_uv.append(Vector2(points[k].x / width, points[k].y / height))
mesh = _create_mesh_3d(points_3d, triangles, points_uv, width, height, mesh)
return mesh
static func generate_wall_mesh_grid(corners, height, grid: Vector2=Vector2(0.2, 0.2)):
if corners.size() < 3:
return null
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
for corner_i in range(corners.size()):
var corner = Vector3(corners[corner_i].x, 0, corners[corner_i].y)
var next_index = (corner_i + 1) % corners.size()
var next_corner = Vector3(corners[next_index].x, 0, corners[next_index].y)
var steps = ceil(Vector2((next_corner - corner).length() / grid.x, height / grid.y))
var forward_dir = (next_corner - corner).normalized() * grid.x
var up_dir = Vector3.UP * grid.y
var close_distance = Vector2(1, 1)
for y in range(0, steps.y):
close_distance.x = 1
if y == steps.y - 1:
close_distance.y = fmod(height, grid.y) / grid.y
for x in range(0, steps.x):
var point = corner + forward_dir * x + Vector3.UP * grid.y * y
if x == steps.x - 1:
close_distance.x = fmod(corner.distance_to(next_corner), grid.x) / grid.x
st.add_vertex(point)
st.add_vertex(point + forward_dir * close_distance.x)
st.add_vertex(point + up_dir * close_distance.y)
st.add_vertex(point + forward_dir * close_distance.x)
st.add_vertex(point + forward_dir * close_distance.x + up_dir * close_distance.y)
st.add_vertex(point + up_dir * close_distance.y)
st.index()
st.generate_normals()
st.generate_tangents()
var mesh = st.commit()
return mesh
static func generate_ceiling_mesh_grid(corners, grid: Vector2=Vector2(0.2, 0.2)):
var points: PackedVector2Array = PackedVector2Array()
var edges: PackedInt32Array = PackedInt32Array()
var triangles: PackedInt32Array
if corners.size() < 3:
return null
var min_val = Vector2(corners[0])
var max_val = Vector2(corners[0])
for i in range(corners.size()):
var corner = corners[i]
min_val.x = min(min_val.x, corner.x)
min_val.y = min(min_val.y, corner.y)
max_val.x = max(max_val.x, corner.x)
max_val.y = max(max_val.y, corner.y)
points.append(Vector2(corner.x, corner.y))
edges.append(i)
edges.append((i + 1) % corners.size())
var size = max_val - min_val
# Subdivide edges to grid
for i in range(corners.size()):
var corner = corners[i]
var next_index = (i + 1) % corners.size()
var next_corner = corners[next_index]
var steps = ceil(Vector2((next_corner - corner).length() / grid.x, size.y / grid.y))
var forward_dir = (next_corner - corner).normalized() * grid.x
for x in range(1, steps.x):
var point = corner + forward_dir * x
points.append(Vector2(point.x, point.y))
## Fill points insde the polygon
for y in range(1, int(size.y / grid.y)):
var x_intersections: Array[float] = []
var y_start = Vector2(min_val.x, min_val.y + y * grid.y)
var y_end = Vector2(max_val.x, min_val.y + y * grid.y)
for i in range(corners.size()):
var a = corners[i]
var b = corners[(i + 1) % corners.size()]
var result = Geometry2D.segment_intersects_segment(a, b, y_start, y_end)
if result != null:
x_intersections.append(result.x)
var intersection_counter = 0
x_intersections.sort()
for x in range(1, int(size.x / grid.x)):
var point = min_val + Vector2(x * grid.x, y * grid.y)
for i in range(intersection_counter, x_intersections.size()):
if x_intersections[i] < point.x:
intersection_counter += 1
var color = Color(1, 1, 0)
if intersection_counter % 2 == 1:
color = Color(1, 0, 1)
points.append(point)
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
cdt.init(true, true, 0.1)
cdt.insert_vertices(points)
cdt.insert_edges(edges)
cdt.erase_outer_triangles()
points = cdt.get_all_vertices()
triangles = cdt.get_all_triangles()
return _create_mesh_2d(points, triangles)
static func _create_mesh_3d(points: PackedVector3Array, triangles: PackedInt32Array, points_uv, width: float, height: float, existing=null):
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
st.set_custom_format(0, SurfaceTool.CUSTOM_RG_FLOAT)
for i in range(points.size()):
st.set_uv(points_uv[i])
st.set_custom(0, Color(width, height, 0, 0))
st.add_vertex(Vector3(points[i].x, points[i].y, points[i].z))
for i in range(triangles.size()):
st.add_index(triangles[i])
st.index()
st.generate_normals()
st.generate_tangents()
if existing != null:
return st.commit(existing)
return st.commit()
static func _create_mesh_2d(points: PackedVector2Array, triangles: PackedInt32Array):
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
st.set_custom_format(0, SurfaceTool.CUSTOM_RG_FLOAT)
for i in range(points.size()):
st.set_uv(Vector2(0.5, 0.5))
st.set_custom(0, Color(1.0, 1.0, 0, 0))
st.add_vertex(Vector3(points[i].x, 0, points[i].y))
for i in range(triangles.size()):
st.add_index(triangles[i])
st.index()
st.generate_normals()
st.generate_tangents()
var mesh = st.commit()
return mesh