diff --git a/project/blocky_terrain/avatar_interaction.gd b/project/blocky_terrain/avatar_interaction.gd index 2d8606d..16ace50 100644 --- a/project/blocky_terrain/avatar_interaction.gd +++ b/project/blocky_terrain/avatar_interaction.gd @@ -1,177 +1,129 @@ -extends Node - -const COLLISION_LAYER_AVATAR = 2 - -export(NodePath) var terrain_path = null -export(Material) var cursor_material = null - -onready var _light = get_node("../../DirectionalLight") # For debug shadow toggle -onready var _head = get_parent().get_node("Camera") - -var _terrain = null -var _terrain_tool = null -var _cursor = null -var _action_place = false -var _action_remove = false - -var _inventory = [1, 2] -var _inventory_index = 0 - - -func _ready(): - if terrain_path == null: - _terrain = get_parent().get_node(get_parent().terrain) - terrain_path = _terrain.get_path() # For correctness - else: - _terrain = get_node(terrain_path) - _cursor = _make_cursor() - _terrain.add_child(_cursor) - _terrain_tool = _terrain.get_voxel_tool() - - -func get_pointed_voxel(): - var origin = _head.get_global_transform().origin - var forward = -_head.get_transform().basis.z.normalized() - var hit = _terrain_tool.raycast(origin, forward, 10) - return hit - - -func _physics_process(delta): - if _terrain == null: - return - - var hit = get_pointed_voxel() - if hit != null: - _cursor.show() - _cursor.set_translation(hit.position + Vector3(1,1,1)*0.5) - get_parent().get_node("debug_label").text = str(hit.position) - else: - _cursor.hide() - get_parent().get_node("debug_label").text = "---" - - # These inputs have to be in _fixed_process because they rely on collision queries - if hit != null: - var has_cube = _terrain_tool.get_voxel(hit.position) != 0 - - if _action_place and has_cube: - var pos = hit.position - do_sphere(pos, 5, 0) - - elif _action_remove: - var pos = hit.previous_position - if has_cube == false: - pos = hit.position - if can_place_voxel_at(pos): - do_sphere(pos, 4, _inventory[_inventory_index]) - print("Place voxel at ", pos) - else: - print("Can't place here!") - - _action_place = false - _action_remove = false - - -func _input(event): - if event is InputEventMouseButton: - if event.pressed: - match event.button_index: - BUTTON_LEFT: - _action_place = true - BUTTON_RIGHT: - _action_remove = true - - elif event is InputEventKey: - if event.pressed: - match event.scancode: - KEY_1: - select_inventory(0) - KEY_2: - select_inventory(1) - KEY_L: - _light.shadow_enabled = not _light.shadow_enabled - - -func select_inventory(i): - if i < 0 or i >= len(_inventory): - return - _inventory_index = i - var vi = _inventory[i] - print("Inventory select ", _terrain.voxel_library.get_voxel(vi).voxel_name, " (", vi, ")") - - -func can_place_voxel_at(pos): - var space_state = get_viewport().get_world().get_direct_space_state() - var params = PhysicsShapeQueryParameters.new() - params.collision_mask = COLLISION_LAYER_AVATAR - params.transform = Transform(Basis(), pos + Vector3(1,1,1)*0.5) - var shape = BoxShape.new() - var ex = 0.5 - shape.extents = Vector3(ex, ex, ex) - params.set_shape(shape) - var hits = space_state.intersect_shape(params) - return hits.size() == 0 - - -# Makes a 3D wireframe cube cursor -func _make_cursor(): - var st = SurfaceTool.new() - st.begin(Mesh.PRIMITIVE_LINES) - _add_wireframe_cube(st, -Vector3(1,1,1)*0.5, 1, Color(0,0,0)) - var mesh = st.commit() - var mesh_instance = MeshInstance.new() - mesh_instance.mesh = mesh - if cursor_material != null: - mesh_instance.material_override = cursor_material - mesh_instance.set_scale(Vector3(1,1,1)*1.01) - return mesh_instance - - -func do_sphere(center, r, type): - _terrain_tool.channel = VoxelBuffer.CHANNEL_TYPE - _terrain_tool.value = type - #_terrain_tool.do_sphere(center, r) - _terrain_tool.do_point(center) - - -static func _add_wireframe_cube(st, pos, step, color): - - st.add_color(color) - - st.add_vertex(pos) - st.add_vertex(pos + Vector3(step, 0, 0)) - - st.add_vertex(pos + Vector3(step, 0, 0)) - st.add_vertex(pos + Vector3(step, 0, step)) - - st.add_vertex(pos + Vector3(step, 0, step)) - st.add_vertex(pos + Vector3(0, 0, step)) - - st.add_vertex(pos + Vector3(0, 0, step)) - st.add_vertex(pos) - - - st.add_vertex(pos + Vector3(0, step, 0)) - st.add_vertex(pos + Vector3(step, step, 0)) - - st.add_vertex(pos + Vector3(step, step, 0)) - st.add_vertex(pos + Vector3(step, step, step)) - - st.add_vertex(pos + Vector3(step, step, step)) - st.add_vertex(pos + Vector3(0, step, step)) - - st.add_vertex(pos + Vector3(0, step, step)) - st.add_vertex(pos + Vector3(0, step, 0)) - - - st.add_vertex(pos) - st.add_vertex(pos + Vector3(0, step, 0)) - - st.add_vertex(pos + Vector3(step, 0, 0)) - st.add_vertex(pos + Vector3(step, step, 0)) - - st.add_vertex(pos + Vector3(step, 0, step)) - st.add_vertex(pos + Vector3(step, step, step)) - - st.add_vertex(pos + Vector3(0, 0, step)) - st.add_vertex(pos + Vector3(0, step, step)) - +extends Node + +const Util = preload("res://common/util.gd") + +const COLLISION_LAYER_AVATAR = 2 + +export(NodePath) var terrain_path = null +export(Material) var cursor_material = null + +onready var _light = get_node("../../DirectionalLight") # For debug shadow toggle +onready var _head = get_parent().get_node("Camera") + +var _terrain = null +var _terrain_tool = null +var _cursor = null +var _action_place = false +var _action_remove = false + +var _inventory = [1, 2] +var _inventory_index = 0 + + +func _ready(): + if terrain_path == null: + _terrain = get_parent().get_node(get_parent().terrain) + terrain_path = _terrain.get_path() # For correctness + else: + _terrain = get_node(terrain_path) + + var mesh = Util.create_wirecube_mesh(Color(0,0,0)) + var mesh_instance = MeshInstance.new() + mesh_instance.mesh = mesh + if cursor_material != null: + mesh_instance.material_override = cursor_material + mesh_instance.set_scale(Vector3(1,1,1)*1.01) + _cursor = mesh_instance + + _terrain.add_child(_cursor) + _terrain_tool = _terrain.get_voxel_tool() + + +func get_pointed_voxel(): + var origin = _head.get_global_transform().origin + var forward = -_head.get_transform().basis.z.normalized() + var hit = _terrain_tool.raycast(origin, forward, 10) + return hit + + +func _physics_process(delta): + if _terrain == null: + return + + var hit = get_pointed_voxel() + if hit != null: + _cursor.show() + _cursor.set_translation(hit.position) + get_parent().get_node("debug_label").text = str(hit.position) + else: + _cursor.hide() + get_parent().get_node("debug_label").text = "---" + + # These inputs have to be in _fixed_process because they rely on collision queries + if hit != null: + var has_cube = _terrain_tool.get_voxel(hit.position) != 0 + + if _action_place and has_cube: + var pos = hit.position + do_sphere(pos, 5, 0) + + elif _action_remove: + var pos = hit.previous_position + if has_cube == false: + pos = hit.position + if can_place_voxel_at(pos): + do_sphere(pos, 4, _inventory[_inventory_index]) + print("Place voxel at ", pos) + else: + print("Can't place here!") + + _action_place = false + _action_remove = false + + +func _input(event): + if event is InputEventMouseButton: + if event.pressed: + match event.button_index: + BUTTON_LEFT: + _action_place = true + BUTTON_RIGHT: + _action_remove = true + + elif event is InputEventKey: + if event.pressed: + match event.scancode: + KEY_1: + select_inventory(0) + KEY_2: + select_inventory(1) + KEY_L: + _light.shadow_enabled = not _light.shadow_enabled + + +func select_inventory(i): + if i < 0 or i >= len(_inventory): + return + _inventory_index = i + var vi = _inventory[i] + print("Inventory select ", _terrain.voxel_library.get_voxel(vi).voxel_name, " (", vi, ")") + + +func can_place_voxel_at(pos): + var space_state = get_viewport().get_world().get_direct_space_state() + var params = PhysicsShapeQueryParameters.new() + params.collision_mask = COLLISION_LAYER_AVATAR + params.transform = Transform(Basis(), pos + Vector3(1,1,1)*0.5) + var shape = BoxShape.new() + var ex = 0.5 + shape.extents = Vector3(ex, ex, ex) + params.set_shape(shape) + var hits = space_state.intersect_shape(params) + return hits.size() == 0 + + +func do_sphere(center, r, type): + _terrain_tool.channel = VoxelBuffer.CHANNEL_TYPE + _terrain_tool.value = type + _terrain_tool.do_point(center) + diff --git a/project/blocky_terrain/character_controller.gd b/project/blocky_terrain/character_controller.gd index aac0514..bae84dd 100644 --- a/project/blocky_terrain/character_controller.gd +++ b/project/blocky_terrain/character_controller.gd @@ -1,80 +1,61 @@ -extends Spatial - -export var speed = 5.0 -export var gravity = 9.8 -export var jump_force = 5.0 -export(NodePath) var head = null - -# Not used in this script, but might be useful for child nodes because -# this controller will most likely be on the root -export(NodePath) var terrain = null - -var _velocity = Vector3() -var _grounded = false -var _head = null -var _box_mover = VoxelBoxMover.new() - - -func _ready(): - _head = get_node(head) - - # FIX - #set_shape_transform(0, Transform().rotated(Vector3(1,0,0), PI/2.0)) - - -func _physics_process(delta): - - var forward = _head.get_transform().basis.z.normalized() - forward = Plane(Vector3(0, 1, 0), 0).project(forward) - var right = _head.get_transform().basis.x.normalized() - var motor = Vector3() - - if Input.is_key_pressed(KEY_UP) or Input.is_key_pressed(KEY_Z) or Input.is_key_pressed(KEY_W): - motor -= forward - if Input.is_key_pressed(KEY_DOWN) or Input.is_key_pressed(KEY_S): - motor += forward - if Input.is_key_pressed(KEY_LEFT) or Input.is_key_pressed(KEY_Q) or Input.is_key_pressed(KEY_A): - motor -= right - if Input.is_key_pressed(KEY_RIGHT) or Input.is_key_pressed(KEY_D): - motor += right - - motor = motor.normalized() * speed - - _velocity.x = motor.x - _velocity.z = motor.z - _velocity.y -= gravity * delta - - #if _grounded and Input.is_key_pressed(KEY_SPACE): - if Input.is_key_pressed(KEY_SPACE): - _velocity.y = jump_force - #_grounded = false - - var motion = _velocity * delta - - if has_node(terrain): - var aabb = AABB(Vector3(-0.4, -0.9, -0.4), Vector3(0.8, 1.8, 0.8)) - var terrain_node = get_node(terrain) - motion = _box_mover.get_motion(get_translation(), motion, aabb, terrain_node) - global_translate(motion) - - assert(delta > 0) - _velocity = motion / delta - - #var rem = move(motion) - - # TODO Fix it, obsolete code - # if is_colliding(): - # var n = get_collision_normal() - # var k = 1.0#clamp(n.y, 0, 1) - # rem = rem.slide(n)*k - # _velocity = _velocity.slide(n)*k - # #rem = n.slide(rem)*k - # #_velocity = n.slide(_velocity)*k - # _grounded = true - # move(rem) - # else: - # _grounded = false - #get_node("debug").set_text("Grounded=" + str(_grounded)) - - - +extends Spatial + +export var speed = 5.0 +export var gravity = 9.8 +export var jump_force = 5.0 +export(NodePath) var head = null + +# Not used in this script, but might be useful for child nodes because +# this controller will most likely be on the root +export(NodePath) var terrain = null + +var _velocity = Vector3() +var _grounded = false +var _head = null +var _box_mover = VoxelBoxMover.new() + + +func _ready(): + _head = get_node(head) + + +func _physics_process(delta): + + var forward = _head.get_transform().basis.z.normalized() + forward = Plane(Vector3(0, 1, 0), 0).project(forward) + var right = _head.get_transform().basis.x.normalized() + var motor = Vector3() + + if Input.is_key_pressed(KEY_UP) or Input.is_key_pressed(KEY_Z) or Input.is_key_pressed(KEY_W): + motor -= forward + if Input.is_key_pressed(KEY_DOWN) or Input.is_key_pressed(KEY_S): + motor += forward + if Input.is_key_pressed(KEY_LEFT) or Input.is_key_pressed(KEY_Q) or Input.is_key_pressed(KEY_A): + motor -= right + if Input.is_key_pressed(KEY_RIGHT) or Input.is_key_pressed(KEY_D): + motor += right + + motor = motor.normalized() * speed + + _velocity.x = motor.x + _velocity.z = motor.z + _velocity.y -= gravity * delta + + #if _grounded and Input.is_key_pressed(KEY_SPACE): + if Input.is_key_pressed(KEY_SPACE): + _velocity.y = jump_force + #_grounded = false + + var motion = _velocity * delta + + if has_node(terrain): + var aabb = AABB(Vector3(-0.4, -0.9, -0.4), Vector3(0.8, 1.8, 0.8)) + var terrain_node = get_node(terrain) + motion = _box_mover.get_motion(get_translation(), motion, aabb, terrain_node) + global_translate(motion) + + assert(delta > 0) + _velocity = motion / delta + + + diff --git a/project/blocky_terrain/main.tscn b/project/blocky_terrain/main.tscn index 07ca25d..62f1fee 100644 --- a/project/blocky_terrain/main.tscn +++ b/project/blocky_terrain/main.tscn @@ -9,8 +9,6 @@ [ext_resource path="res://blocky_terrain/profiling_gui.gd" type="Script" id=8] [ext_resource path="res://blocky_terrain/debug3d.gd" type="Script" id=9] - - [sub_resource type="ProceduralSky" id=1] sky_top_color = Color( 0.268204, 0.522478, 0.847656, 1 ) sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 ) @@ -86,7 +84,6 @@ stream = ExtResource( 1 ) voxel_library = SubResource( 6 ) view_distance = 256 viewer_path = NodePath("../CharacterAvatar") -generate_collisions = true material/0 = ExtResource( 2 ) material/1 = ExtResource( 3 ) diff --git a/project/common/util.gd b/project/common/util.gd new file mode 100644 index 0000000..0c10f05 --- /dev/null +++ b/project/common/util.gd @@ -0,0 +1,41 @@ + +static func create_wirecube_mesh(color = Color(1,1,1)): + var positions = PoolVector3Array([ + Vector3(0, 0, 0), + Vector3(1, 0, 0), + Vector3(1, 0, 1), + Vector3(0, 0, 1), + Vector3(0, 1, 0), + Vector3(1, 1, 0), + Vector3(1, 1, 1), + Vector3(0, 1, 1), + ]) + var colors = PoolColorArray([ + color, color, color, color, + color, color, color, color, + ]) + var indices = PoolIntArray([ + 0, 1, + 1, 2, + 2, 3, + 3, 0, + + 4, 5, + 5, 6, + 6, 7, + 7, 4, + + 0, 4, + 1, 5, + 2, 6, + 3, 7 + ]) + var arrays = [] + arrays.resize(Mesh.ARRAY_MAX) + arrays[Mesh.ARRAY_VERTEX] = positions + arrays[Mesh.ARRAY_COLOR] = colors + arrays[Mesh.ARRAY_INDEX] = indices + var mesh = ArrayMesh.new() + mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, arrays) + return mesh +