diff --git a/Scripts/Space/SpaceRegion.gd b/Scripts/Space/SpaceRegion.gd index caf573d..64c56a0 100644 --- a/Scripts/Space/SpaceRegion.gd +++ b/Scripts/Space/SpaceRegion.gd @@ -6,23 +6,30 @@ extends SpaceEntity class_name SpaceRegion +# TODO: Make evelation and height be against the parent. @export var elevation: float = 0.0 @export var height: float = 100.0 @export var wall_texture: Texture2D @export var floor_texture: Texture2D @export var ceiling_texture: Texture2D +@export var top_texture: Texture2D + +var _is_convex_cache: bool func generate_geometry(space: Space) -> Node3D: - return _generate_geometry(space, true) + return _generate_geometry(space, true, null) func _process(delta): if Engine.is_editor_hint(): - self.texture = floor_texture + self.texture = top_texture if top_texture else floor_texture -func _generate_geometry(space: Space, looked_from_inside: bool) -> Node3D: +func _generate_geometry(space: Space, + looked_from_inside: bool, + parent_region: SpaceRegion +) -> Node3D: var geometry := MeshInstance3D.new() geometry.name = name geometry.position = Vector3( @@ -32,29 +39,43 @@ func _generate_geometry(space: Space, looked_from_inside: bool) -> Node3D: ) var mesh := ArrayMesh.new() - mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, _generate_wall_arrays(space, looked_from_inside)) + var surface_count: int = 0 + mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, _generate_wall_arrays(space, looked_from_inside)) + surface_count += 1 var wall_material := StandardMaterial3D.new() wall_material.albedo_texture = wall_texture - mesh.surface_set_material(0, wall_material) + mesh.surface_set_material(surface_count - 1, wall_material) if (looked_from_inside): mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, _generate_floor_arrays(space)) + surface_count += 1 var floor_material := StandardMaterial3D.new() floor_material.albedo_texture = floor_texture - mesh.surface_set_material(1, floor_material) + mesh.surface_set_material(surface_count - 1, floor_material) mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, _generate_ceiling_arrays(space)) + surface_count += 1 var ceiling_material := StandardMaterial3D.new() ceiling_material.albedo_texture = ceiling_texture ceiling_material.cull_mode = BaseMaterial3D.CULL_FRONT # todo: Don't require state change - mesh.surface_set_material(2, ceiling_material) + mesh.surface_set_material(surface_count - 1, ceiling_material) + + if parent_region and not parent_region._is_convex_cache: + if parent_region.height > height: + mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, _generate_ceiling_arrays(space)) + surface_count += 1 + var top_material := StandardMaterial3D.new() + top_material.albedo_texture = top_texture + mesh.surface_set_material(surface_count - 1, top_material) geometry.mesh = mesh + _is_convex_cache = is_convex() + for child in get_children(): if child is SpaceRegion: - geometry.add_child(child._generate_geometry(space, not looked_from_inside)) + geometry.add_child(child._generate_geometry(space, not looked_from_inside, self)) geometry.create_trimesh_collision() return geometry @@ -156,3 +177,94 @@ func _generate_ceiling_arrays(space: Space) -> Array: arrays[Mesh.ARRAY_TEX_UV] = uvs return arrays + + +## Check whether region polygon forms a convex geometry. +## +## Based on: https://math.stackexchange.com/a/1745427 +func is_convex() -> bool: + if self.polygon.size() < 3: + return false + + var wSign: float = 0 # First nonzero orientation (positive or negative) + + var xSign: float = 0 + var xFirstSign: float = 0 # Sign of first nonzero edge vector x + var xFlips: float = 0 # Number of sign changes in x + + var ySign: float = 0 + var yFirstSign: float = 0 # Sign of first nonzero edge vector y + var yFlips: float = 0 # Number of sign changes in y + + var curr: Vector2 = self.polygon[-2] # Second-to-last vertex + var next: Vector2 = self.polygon[-1] # Last vertex + + for v in self.polygon: # Each vertex, in order + var prev: Vector2 = curr # Previous vertex + curr = next # Current vertex + next = v # Next vertex + + # Previous edge vector ("before"): + var bx: float = curr.x - prev.x + var by: float = curr.y - prev.y + + # Next edge vector ("after"): + var ax: float = next.x - curr.x + var ay: float = next.y - curr.y + + # Calculate sign flips using the next edge vector ("after"), + # recording the first sign. + if ax > 0: + if xSign == 0: + xFirstSign = +1 + elif xSign < 0: + xFlips = xFlips + 1 + xSign = +1 + elif ax < 0: + if xSign == 0: + xFirstSign = -1 + elif xSign > 0: + xFlips = xFlips + 1 + xSign = -1 + + if xFlips > 2: + return false + + if ay > 0: + if ySign == 0: + yFirstSign = +1 + elif ySign < 0: + yFlips = yFlips + 1 + ySign = +1 + elif ay < 0: + if ySign == 0: + yFirstSign = -1 + elif ySign > 0: + yFlips = yFlips + 1 + ySign = -1 + + if yFlips > 2: + return false + + # Find out the orientation of this pair of edges, + # and ensure it does not differ from previous ones. + var w := bx * ay - ax * by + if (wSign == 0) and (w != 0): + wSign = w + elif (wSign > 0) and (w < 0): + return false + elif (wSign < 0) and (w > 0): + return false + + # Final/wraparound sign flips: + if (xSign != 0) and (xFirstSign != 0) and (xSign != xFirstSign): + xFlips = xFlips + 1 + if (ySign != 0) and (yFirstSign != 0) and (ySign != yFirstSign): + yFlips = yFlips + 1 + + # Concave polygons have two sign flips along each axis. + if (xFlips != 2) or (yFlips != 2): + return false + + # This is a convex polygon. + return true diff --git a/Spaces/Dungeon.tscn b/Spaces/Dungeon.tscn index 4957421..4241164 100644 --- a/Spaces/Dungeon.tscn +++ b/Spaces/Dungeon.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=3 uid="uid://deff7lt34nj0h"] +[gd_scene load_steps=8 format=3 uid="uid://deff7lt34nj0h"] [ext_resource type="Script" path="res://Scripts/Space/Space.gd" id="1_ckp7u"] [ext_resource type="Script" path="res://Scripts/Space/SpaceRegion.gd" id="2_s3h7s"] @@ -6,6 +6,7 @@ [ext_resource type="Texture2D" uid="uid://icmmuqquxhq3" path="res://Art/Stone_06-128x128.png" id="3_ondo6"] [ext_resource type="Texture2D" uid="uid://eempmja460l5" path="res://Art/Stone_11-128x128.png" id="4_mnswr"] [ext_resource type="Script" path="res://Scripts/Space/SpaceSpawn.gd" id="4_rhl23"] +[ext_resource type="Texture2D" uid="uid://buoupwtftxjse" path="res://Art/Stone_05-128x128.png" id="5_evp07"] [node name="Dungeon" type="Node2D"] script = ExtResource("1_ckp7u") @@ -24,10 +25,22 @@ ceiling_texture = ExtResource("4_mnswr") [node name="Column" type="Polygon2D" parent="Room"] texture_repeat = 2 -polygon = PackedVector2Array(8, -40, 48, -64, 88, -24, 64, 0, 24, -8) +texture = ExtResource("3_ondo6") +polygon = PackedVector2Array(8, -40, 48, -64, 88, -40, 72, -8, 72, -32, 48, -48, 24, -32, 24, -8) uv = PackedVector2Array(1.88, 5.28, 0.6, 1.44, 5.24, 0.64, 5.24, 0.64, 9.94, 3.84, 5.72, 6.56, 5.72, 6.56, 1.88, 5.28, 5.24, 0.64) script = ExtResource("2_s3h7s") wall_texture = ExtResource("3_ondo6") +floor_texture = ExtResource("3_ondo6") + +[node name="Floor" type="Polygon2D" parent="Room/Column"] +texture_repeat = 2 +texture = ExtResource("5_evp07") +polygon = PackedVector2Array(72, -8, 72, -32, 48, -48, 24, -32, 24, -8) +uv = PackedVector2Array(1.88, 5.28, 0.6, 1.44, 5.24, 0.64, 5.24, 0.64, 9.94, 3.84, 5.72, 6.56, 5.72, 6.56, 1.88, 5.28, 5.24, 0.64) +script = ExtResource("2_s3h7s") +height = 20.0 +wall_texture = ExtResource("3_ondo6") +top_texture = ExtResource("5_evp07") [node name="Spawn" type="Sprite2D" parent="Room"] position = Vector2(-48, 32)