add possibility to configure height

This commit is contained in:
Nitwel 2023-12-18 23:15:48 +01:00
parent d25017192d
commit eab54b5850
11 changed files with 401 additions and 195 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Godot 4+ specific ignores # Godot 4+ specific ignores
.godot/ .godot/
android/build/ android/build/
builds/
# Godot-specific ignores # Godot-specific ignores
.import/ .import/

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:e54002ccb9b04b9b4fb4642cc3b5fd6512f910e65ecb52e41c117a2345d1d22b oid sha256:b117dc5a7537a60ff341010636802131c9aa3b80982130e63d5d94902c002d07
size 17810864 size 13505995

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:68e2b9d5e723a2009f341e45e2968893dd2a1dd5077fe1d9ba970f01d0d75e02 oid sha256:9738010a57d2157a53e4f537afbcfb50dd28e9de40bae510621d0ea8c94c5844
size 56989 size 51682

View File

@ -1,172 +1,21 @@
extends Node3D extends Node3D
const wall_corner_scene = preload("./wall_corner.tscn") @onready var wall_corners = $Ceiling/WallCorners
const wall_edge_scene = preload("./wall_edge.tscn") @onready var wall_edges = $Ceiling/WallEdges
@onready var wall_corners = $WallCorners
@onready var wall_edges = $WallEdges
@onready var wall_mesh = $WallMesh @onready var wall_mesh = $WallMesh
@onready var wall_collisions = $WallCollisions @onready var wall_collisions = $WallCollisions
@onready var ground = $Ground/Clickable @onready var room_floor = $Floor
@onready var room_ceiling = $Ceiling
var moving = null @onready var state_machine = $StateMachine
var editable := false:
var editable: bool = false:
set(value): set(value):
if value == editable:
return
editable = value
if value: if value:
_start_edit_mode() state_machine.change_to("Edit")
else: else:
_end_edit_mode() state_machine.change_to("View")
var ground_plane = Plane(Vector3.UP, Vector3.ZERO)
func _ready():
ground.on_click.connect(func(event):
if !editable:
return
add_corner(event.ray.get_collision_point())
)
func _start_edit_mode():
wall_corners.visible = true
wall_edges.visible = true
wall_mesh.visible = false
wall_mesh.mesh = null
for old_coll in wall_collisions.get_children():
old_coll.queue_free()
func _end_edit_mode():
wall_corners.visible = false
wall_edges.visible = false
wall_mesh.mesh = generate_mesh()
if wall_mesh.mesh == null:
return
var collisions = generate_collision(wall_mesh.mesh)
for collision in collisions:
var static_body = StaticBody3D.new()
static_body.set_collision_layer_value(4, true)
static_body.set_collision_layer_value(5, true)
static_body.collision_mask = 0
static_body.add_child(collision)
wall_collisions.add_child(static_body)
wall_mesh.visible = true
func generate_mesh():
var corner_count = wall_corners.get_child_count()
if corner_count < 3:
return
var st = SurfaceTool.new()
var wall_up = Vector3.UP * 3
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
for i in range(corner_count):
var corner = get_corner(i)
st.add_vertex(corner.position)
st.add_vertex(corner.position + wall_up)
var first_corner = get_corner(0)
st.add_vertex(first_corner.position)
st.add_vertex(first_corner.position + wall_up)
st.index()
st.generate_normals()
st.generate_tangents()
var mesh = st.commit()
return mesh
func generate_collision(mesh: ArrayMesh):
var corner_count = wall_corners.get_child_count()
if corner_count < 3:
return
var collision_shapes: Array[CollisionShape3D] = []
for i in range(corner_count):
var corner = get_corner(i)
var next_corner = get_corner(i + 1)
var shape = BoxShape3D.new()
shape.size = Vector3((next_corner.position - corner.position).length(), 3, 0.04)
var transform = Transform3D()
var back_vector = (corner.position - next_corner.position).cross(Vector3.UP).normalized() * shape.size.z / 2
transform.basis = Basis((next_corner.position - corner.position).normalized(), Vector3.UP, back_vector.normalized())
transform.origin = corner.position + (next_corner.position - corner.position) / 2 + back_vector + Vector3.UP * shape.size.y / 2
var collision_shape = CollisionShape3D.new()
collision_shape.shape = shape
collision_shape.transform = transform
collision_shapes.append(collision_shape)
return collision_shapes
func add_corner(position: Vector3):
var corner = wall_corner_scene.instantiate()
corner.position = position
corner.get_node("Clickable").on_grab_down.connect(func(event):
if !editable:
return
moving = event.target
)
corner.get_node("Clickable").on_grab_move.connect(func(event):
if moving == null:
return
var direction = -event.ray.global_transform.basis.z
var new_position = ground_plane.intersects_ray(event.ray.global_position, direction)
if new_position == null:
return
moving.position = new_position
var moving_index = moving.get_index()
get_edge(moving_index).transform = corners_to_edge_transform(new_position, get_corner(moving_index + 1).position)
get_edge(moving_index - 1).transform = corners_to_edge_transform(get_corner(moving_index - 1).position, new_position)
)
corner.get_node("Clickable").on_grab_up.connect(func(_event):
moving = null
)
wall_corners.add_child(corner)
var num_corners = wall_corners.get_child_count()
var edge
if num_corners > 1:
edge = add_edge(wall_corners.get_child(num_corners - 2).position, position)
if num_corners > 2:
if num_corners != wall_edges.get_child_count():
add_edge(position, wall_corners.get_child(0).position)
else:
wall_edges.move_child(edge, num_corners - 2)
get_edge(-1).transform = corners_to_edge_transform(position, get_corner(0).position)
func get_corner(index: int) -> MeshInstance3D: func get_corner(index: int) -> MeshInstance3D:
return wall_corners.get_child(index % wall_corners.get_child_count()) return wall_corners.get_child(index % wall_corners.get_child_count())
@ -175,22 +24,6 @@ func get_edge(index: int) -> MeshInstance3D:
return wall_edges.get_child(index % wall_edges.get_child_count()) return wall_edges.get_child(index % wall_edges.get_child_count())
func add_edge(from_pos: Vector3, to_pos: Vector3):
var edge: MeshInstance3D = wall_edge_scene.instantiate()
edge.transform = corners_to_edge_transform(from_pos, to_pos)
wall_edges.add_child(edge)
return edge
func corners_to_edge_transform(from_pos: Vector3, to_pos: Vector3) -> Transform3D:
var diff = to_pos - from_pos
var direction = diff.normalized()
var edge_position = from_pos + diff / 2
var edge_basis = Basis(Vector3.UP, diff, direction.cross(Vector3.UP))
var edge_transform = Transform3D(edge_basis, edge_position)
return edge_transform
func _save(): func _save():
return { return {
"corners": wall_corners.get_children().map(func(corner): return corner.position), "corners": wall_corners.get_children().map(func(corner): return corner.position),
@ -198,8 +31,11 @@ func _save():
func _load(data): func _load(data):
await ready await ready
return
state_machine.change_to("Edit")
for corner in data["corners"]: for corner in data["corners"]:
add_corner(corner) state_machine.current_state.add_corner(corner)
_end_edit_mode() state_machine.change_to("View")

View File

@ -1,42 +1,60 @@
[gd_scene load_steps=7 format=3 uid="uid://bswgmclohuqui"] [gd_scene load_steps=10 format=3 uid="uid://bswgmclohuqui"]
[ext_resource type="Script" path="res://content/system/room/room.gd" id="1_fccq0"] [ext_resource type="Script" path="res://content/system/room/room.gd" id="1_fccq0"]
[ext_resource type="Script" path="res://content/functions/clickable.gd" id="1_ugebq"] [ext_resource type="Script" path="res://content/functions/clickable.gd" id="1_ugebq"]
[ext_resource type="Material" uid="uid://bbx6fv7jq50tr" path="res://content/system/room/walls.tres" id="3_al1ev"] [ext_resource type="Material" uid="uid://bbx6fv7jq50tr" path="res://content/system/room/walls.tres" id="3_al1ev"]
[ext_resource type="Script" path="res://lib/utils/state_machine/state_machine.gd" id="4_nbbo6"]
[ext_resource type="Script" path="res://content/system/room/states/view.gd" id="6_g066t"]
[ext_resource type="Script" path="res://content/system/room/states/edit.gd" id="7_ap14h"]
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_08sv0"] [sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_08sv0"]
[sub_resource type="ArrayMesh" id="ArrayMesh_7dibq"] [sub_resource type="ArrayMesh" id="ArrayMesh_7dibq"]
[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_ap613"] [sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_ap613"]
plane = Plane(0, -1, 0, 0)
[node name="Room" type="Node3D"] [node name="Room" type="Node3D"]
script = ExtResource("1_fccq0") script = ExtResource("1_fccq0")
[node name="Ground" type="StaticBody3D" parent="."] [node name="Floor" type="StaticBody3D" parent="."]
collision_layer = 24 collision_layer = 24
collision_mask = 0 collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Floor"]
shape = SubResource("WorldBoundaryShape3D_08sv0") shape = SubResource("WorldBoundaryShape3D_08sv0")
[node name="Clickable" type="Node" parent="Ground"] [node name="Clickable" type="Node" parent="Floor"]
script = ExtResource("1_ugebq") script = ExtResource("1_ugebq")
[node name="WallCorners" type="Node3D" parent="."]
[node name="WallEdges" type="Node3D" parent="."]
[node name="WallMesh" type="MeshInstance3D" parent="."] [node name="WallMesh" type="MeshInstance3D" parent="."]
material_override = ExtResource("3_al1ev") material_override = ExtResource("3_al1ev")
mesh = SubResource("ArrayMesh_7dibq") mesh = SubResource("ArrayMesh_7dibq")
[node name="WallCollisions" type="Node3D" parent="."] [node name="WallCollisions" type="Node3D" parent="."]
[node name="Celing" type="StaticBody3D" parent="."] [node name="Ceiling" type="StaticBody3D" parent="."]
transform = Transform3D(-1, 8.74228e-08, 0, -8.74228e-08, -1, 0, 0, 0, 1, 0, 3.07, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
collision_layer = 0 collision_layer = 24
collision_mask = 0 collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Celing"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Ceiling"]
shape = SubResource("WorldBoundaryShape3D_ap613") shape = SubResource("WorldBoundaryShape3D_ap613")
disabled = true
[node name="Clickable" type="Node" parent="Ceiling"]
script = ExtResource("1_ugebq")
[node name="WallCorners" type="Node3D" parent="Ceiling"]
[node name="WallEdges" type="Node3D" parent="Ceiling"]
[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("current_state")]
script = ExtResource("4_nbbo6")
current_state = NodePath("View")
[node name="View" type="Node" parent="StateMachine"]
script = ExtResource("6_g066t")
[node name="Edit" type="Node" parent="StateMachine"]
script = ExtResource("7_ap14h")

View File

@ -0,0 +1,213 @@
extends RoomState
const wall_corner_scene = preload("../wall_corner.tscn")
const wall_edge_scene = preload("../wall_edge.tscn")
const RoomState = preload("./room_state.gd")
var moving = null
var floor_corner: StaticBody3D = null
var height_corner: StaticBody3D = null
var height_edge: MeshInstance3D = null
func _on_enter():
room.wall_corners.visible = true
room.wall_edges.visible = true
if floor_corner != null:
floor_corner.visible = true
height_corner.visible = true
height_edge.visible = true
room.room_ceiling.get_node("Clickable").on_click.connect(_on_click_ceiling)
room.room_floor.get_node("Clickable").on_click.connect(_on_click_floor)
func _on_leave():
room.wall_corners.visible = false
room.wall_edges.visible = false
if floor_corner != null:
floor_corner.visible = false
height_corner.visible = false
height_edge.visible = false
room.room_ceiling.get_node("Clickable").on_click.disconnect(_on_click_ceiling)
room.room_floor.get_node("Clickable").on_click.disconnect(_on_click_floor)
func _on_click_floor(event):
if floor_corner != null && height_corner != null:
return
add_floor_corner(event.ray.get_collision_point())
add_height_corner(event.ray.get_collision_point())
room.room_ceiling.get_node("CollisionShape3D").disabled = false
func _on_click_ceiling(event):
if floor_corner == null || height_corner == null:
return
var pos = event.ray.get_collision_point()
pos.y = 0
add_corner(pos)
func add_floor_corner(position: Vector3):
floor_corner = wall_corner_scene.instantiate()
floor_corner.position = position
height_edge = wall_edge_scene.instantiate()
height_edge.transform = corners_to_edge_transform(position, position + Vector3.UP * room.room_ceiling.global_position.y)
floor_corner.get_node("Clickable").on_grab_down.connect(func(event):
if !is_active():
return
moving = event.target
)
floor_corner.get_node("Clickable").on_grab_move.connect(func(event):
if moving == null:
return
var direction = -event.ray.global_transform.basis.z
var new_position = room.room_floor.get_node("CollisionShape3D").shape.plane.intersects_ray(event.ray.global_position, direction)
if new_position == null:
# mark for deletion
return
moving.position = new_position
var moving_index = height_corner.get_index()
height_edge.transform = corners_to_edge_transform(new_position, new_position + Vector3.UP * room.room_ceiling.global_position.y)
room.get_corner(moving_index).position.x = new_position.x
room.get_corner(moving_index).position.z = new_position.z
if room.wall_edges.get_child_count() == 0:
return
room.get_edge(moving_index).transform = corners_to_edge_transform(new_position, room.get_corner(moving_index + 1).position)
room.get_edge(moving_index - 1).transform = corners_to_edge_transform(room.get_corner(moving_index - 1).position, new_position)
)
floor_corner.get_node("Clickable").on_grab_up.connect(func(_event):
moving = null
)
room.add_child(floor_corner)
room.add_child(height_edge)
func add_height_corner(position: Vector3):
height_corner = wall_corner_scene.instantiate()
height_corner.position.x = position.x
height_corner.position.z = position.z
height_corner.get_node("Clickable").on_grab_down.connect(func(event):
if !is_active():
return
moving = event.target
)
height_corner.get_node("Clickable").on_grab_move.connect(func(event):
if moving == null:
return
var direction = -event.ray.global_transform.basis.z
var plane_direction = direction
plane_direction.y = 0
plane_direction = plane_direction.normalized() * -1
var plane = Plane(plane_direction, moving.position)
var new_position = plane.intersects_ray(event.ray.global_position, direction)
if new_position == null:
return
room.room_ceiling.position.y = new_position.y
height_edge.transform = corners_to_edge_transform(floor_corner.global_position, height_corner.global_position)
)
height_corner.get_node("Clickable").on_grab_up.connect(func(_event):
moving = null
)
room.wall_corners.add_child(height_corner)
func add_corner(position: Vector3):
var corner = wall_corner_scene.instantiate()
corner.position.x = position.x
corner.position.z = position.z
corner.get_node("Clickable").on_grab_down.connect(func(event):
if !is_active():
return
moving = event.target
)
corner.get_node("Clickable").on_grab_move.connect(func(event):
if moving == null:
return
var direction = -event.ray.global_transform.basis.z
var ceiling_plane = Plane(Vector3.DOWN, room.room_ceiling.global_position)
var new_position = ceiling_plane.intersects_ray(event.ray.global_position, direction)
if new_position == null:
return
new_position.y = 0
moving.position = new_position
var moving_index = moving.get_index()
if room.wall_edges.get_child_count() == 0:
return
room.get_edge(moving_index).transform = corners_to_edge_transform(new_position, room.get_corner(moving_index + 1).position)
room.get_edge(moving_index - 1).transform = corners_to_edge_transform(room.get_corner(moving_index - 1).position, new_position)
)
corner.get_node("Clickable").on_grab_up.connect(func(_event):
moving = null
)
room.wall_corners.add_child(corner)
var num_corners = room.wall_corners.get_child_count()
var edge
if num_corners > 1:
edge = add_edge(room.wall_corners.get_child(num_corners - 2).position, position)
if num_corners > 2:
if num_corners != room.wall_edges.get_child_count():
add_edge(position, room.wall_corners.get_child(0).position)
else:
room.wall_edges.move_child(edge, num_corners - 2)
room.get_edge(-1).transform = corners_to_edge_transform(position, room.get_corner(0).position)
func add_edge(from_pos: Vector3, to_pos: Vector3):
var edge: MeshInstance3D = wall_edge_scene.instantiate()
edge.transform = corners_to_edge_transform(from_pos, to_pos)
room.wall_edges.add_child(edge)
return edge
func corners_to_edge_transform(from_pos: Vector3, to_pos: Vector3) -> Transform3D:
var diff = to_pos - from_pos
var direction = diff.normalized()
var tangent = Vector3(direction.z, 0, -direction.x).normalized()
if tangent == Vector3.ZERO:
tangent = Vector3(1, 0, 0)
var edge_position = from_pos + diff / 2
var edge_basis = Basis(tangent, diff, tangent.cross(direction))
var edge_transform = Transform3D(edge_basis, edge_position)
return edge_transform

View File

@ -0,0 +1,7 @@
extends State
const Room = preload("res://content/system/room/room.gd")
var room: Room
func _ready():
room = get_parent().get_parent()

View File

@ -0,0 +1,86 @@
extends RoomState
const RoomState = preload("./room_state.gd")
var room_height = 3
var corner_count = 0
func _on_enter():
corner_count = room.wall_corners.get_child_count()
if corner_count < 3:
return
room_height = room.get_corner(0).global_position.y
room.wall_mesh.mesh = generate_mesh()
if room.wall_mesh.mesh == null:
return
var collisions = generate_collision()
for collision in collisions:
var static_body = StaticBody3D.new()
static_body.set_collision_layer_value(4, true)
static_body.set_collision_layer_value(5, true)
static_body.collision_mask = 0
static_body.add_child(collision)
room.wall_collisions.add_child(static_body)
room.wall_mesh.visible = true
func _on_leave():
room.wall_mesh.mesh = null
for collision in room.wall_collisions.get_children():
collision.queue_free()
func generate_mesh():
var st = SurfaceTool.new()
var wall_up = Vector3.UP * room_height
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
for i in range(corner_count):
var corner = room.get_corner(i)
st.add_vertex(corner.position)
st.add_vertex(corner.position + wall_up)
var first_corner = room.get_corner(0)
st.add_vertex(first_corner.position)
st.add_vertex(first_corner.position + wall_up)
st.index()
st.generate_normals()
st.generate_tangents()
var mesh = st.commit()
return mesh
func generate_collision():
var collision_shapes: Array[CollisionShape3D] = []
for i in range(corner_count):
var corner = room.get_corner(i)
var next_corner = room.get_corner(i + 1)
var shape = BoxShape3D.new()
shape.size = Vector3((next_corner.position - corner.position).length(), room_height, 0.04)
var transform = Transform3D()
var back_vector = (corner.position - next_corner.position).cross(Vector3.UP).normalized() * shape.size.z / 2
transform.basis = Basis((next_corner.position - corner.position).normalized(), Vector3.UP, back_vector.normalized())
transform.origin = corner.position + (next_corner.position - corner.position) / 2 + back_vector + Vector3.UP * shape.size.y / 2
var collision_shape = CollisionShape3D.new()
collision_shape.shape = shape
collision_shape.transform = transform
collision_shapes.append(collision_shape)
return collision_shapes

View File

@ -248,7 +248,7 @@ architectures/armeabi-v7a=false
architectures/arm64-v8a=true architectures/arm64-v8a=true
architectures/x86=false architectures/x86=false
architectures/x86_64=false architectures/x86_64=false
version/code=6 version/code=7
version/name="v0.2.0" version/name="v0.2.0"
package/unique_name="de.nitwel.$genname" package/unique_name="de.nitwel.$genname"
package/name="Immersive Home" package/name="Immersive Home"

View File

@ -0,0 +1,13 @@
extends Node
class_name State
var state_machine: StateMachine
func is_active():
return state_machine.current_state == self
func _on_enter():
pass
func _on_leave():
pass

View File

@ -0,0 +1,32 @@
class_name StateMachine
extends Node
signal changed(state_name: String, old_state: String)
@export var current_state: Node
var states: Dictionary = {}
func _ready() -> void:
for state in get_children():
states[state.get_name()] = state
state.state_machine = self
if state != current_state:
remove_child(state)
await get_parent().ready
current_state._on_enter()
func change_to(new_state: String) -> void:
if states.has(new_state) == false:
return
var old_state = current_state.get_name()
current_state._on_leave()
remove_child(current_state)
current_state = states[new_state]
add_child(current_state)
current_state._on_enter()
changed.emit(new_state, old_state)