diff --git a/document.tscn b/document.tscn index c8bd599..ca26ea3 100644 --- a/document.tscn +++ b/document.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=8 format=3 uid="uid://qs3uqv4jnev3"] +[gd_scene load_steps=10 format=3 uid="uid://qs3uqv4jnev3"] [ext_resource type="Script" path="res://scripts/physics_drag.gd" id="1_i3q73"] [ext_resource type="Script" path="res://scripts/document.gd" id="1_t74iv"] [ext_resource type="PackedScene" uid="uid://hoo8ttycwy87" path="res://explosion.tscn" id="2_no6u6"] [ext_resource type="Script" path="res://scripts/toggle_gravity.gd" id="3_loy23"] +[ext_resource type="Script" path="res://scripts/walking_mode_button.gd" id="5_rjbc2"] +[ext_resource type="PackedScene" uid="uid://lgje7ym77d0" path="res://document3d.tscn" id="6_yrkbv"] [sub_resource type="WorldBoundaryShape2D" id="WorldBoundaryShape2D_ok5r2"] @@ -70,7 +72,7 @@ [node name="Label" type="Label" parent="Control/PanelContainer/VBoxContainer"] layout_mode = 2 theme_override_font_sizes/font_size = 24 -text = "Godot Browser" +text = "The Physical Web" horizontal_alignment = 1 [node name="HSeparator" type="HSeparator" parent="Control/PanelContainer/VBoxContainer"] @@ -126,6 +128,17 @@ disabled = true text = "Back" +[node name="HSeparator4" type="HSeparator" parent="Control/PanelContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 15.185) +layout_mode = 2 + +[node name="Button" type="Button" parent="Control/PanelContainer/VBoxContainer" node_paths=PackedStringArray("address_box")] +layout_mode = 2 +text = "Walking Mode" +script = ExtResource("5_rjbc2") +walking_scene = ExtResource("6_yrkbv") +address_box = NodePath("../TextEdit") + [node name="HTTPRequest" type="HTTPRequest" parent="."] [connection signal="pressed" from="Control/PanelContainer/VBoxContainer/HBoxContainer/Button" to="Document" method="on_refresh"] diff --git a/document3d.tscn b/document3d.tscn new file mode 100644 index 0000000..3661025 --- /dev/null +++ b/document3d.tscn @@ -0,0 +1,58 @@ +[gd_scene load_steps=11 format=3 uid="uid://lgje7ym77d0"] + +[ext_resource type="Script" path="res://scripts/document3d.gd" id="1_djivk"] +[ext_resource type="PackedScene" uid="uid://jpxr4fmd3lp6" path="res://player.tscn" id="2_xlarm"] +[ext_resource type="Texture2D" uid="uid://dyfs4oi60qhi6" path="res://textures/Concrete_019_BaseColor.jpg" id="3_4im1o"] +[ext_resource type="Texture2D" uid="uid://cf7eapkaadrgp" path="res://textures/Concrete_019_Normal.jpg" id="4_5herb"] + +[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_qupos"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_6hkal"] +albedo_texture = ExtResource("3_4im1o") +normal_enabled = true +normal_scale = 0.8 +normal_texture = ExtResource("4_5herb") +uv1_scale = Vector3(100, 100, 100) + +[sub_resource type="PlaneMesh" id="PlaneMesh_sudig"] +material = SubResource("StandardMaterial3D_6hkal") +size = Vector2(1000, 1000) + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_1iiyu"] + +[sub_resource type="Sky" id="Sky_kohlj"] +sky_material = SubResource("ProceduralSkyMaterial_1iiyu") + +[sub_resource type="Environment" id="Environment_2f7s3"] +background_mode = 2 +background_color = Color(0.241156, 0.241156, 0.241156, 1) +sky = SubResource("Sky_kohlj") + +[node name="Root" type="Node3D"] + +[node name="Document" type="Node3D" parent="." node_paths=PackedStringArray("http_request", "player")] +script = ExtResource("1_djivk") +http_request = NodePath("../HTTPRequest") +player = NodePath("../Player") + +[node name="HTTPRequest" type="HTTPRequest" parent="."] + +[node name="Player" parent="." instance=ExtResource("2_xlarm")] +transform = Transform3D(0.627678, 0, 0.778473, 0, 1, 0, -0.778473, 0, 0.627678, -5.64398, 1.739, 3.59849) +speed = 300.0 +jump_height = 1500.0 + +[node name="Floor" type="StaticBody3D" parent="."] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Floor"] +shape = SubResource("WorldBoundaryShape3D_qupos") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Floor"] +mesh = SubResource("PlaneMesh_sudig") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(0.928824, -0.194409, -0.315424, 0.370512, 0.493739, 0.786729, 0.00279003, -0.847601, 0.530627, 0, 18.6614, 0) +shadow_enabled = true + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_2f7s3") diff --git a/player.tscn b/player.tscn new file mode 100644 index 0000000..aec6364 --- /dev/null +++ b/player.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=3 format=3 uid="uid://jpxr4fmd3lp6"] + +[ext_resource type="Script" path="res://scripts/player.gd" id="1"] + +[sub_resource type="CapsuleShape3D" id="1"] +radius = 0.8 +height = 3.48668 + +[node name="Player" type="CharacterBody3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.739, 0) +script = ExtResource("1") + +[node name="Collider" type="CollisionShape3D" parent="."] +shape = SubResource("1") + +[node name="Camera3D" type="Camera3D" parent="."] +transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 1.07958, 0) +cull_mask = 1048571 +current = true + +[node name="HUD" type="CenterContainer" parent="Camera3D"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="CrossHeir" type="TextureRect" parent="Camera3D/HUD"] +modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(8, 8) +layout_mode = 2 +expand_mode = 1 diff --git a/project.godot b/project.godot index 0216467..2ef00e0 100644 --- a/project.godot +++ b/project.godot @@ -15,12 +15,54 @@ config/features=PackedStringArray("4.3", "Forward Plus") config/icon="res://icon.svg" +[autoload] + +Globals="*res://scripts/globals.gd" + [display] window/size/viewport_width=1920 window/size/viewport_height=1080 window/stretch/mode="canvas_items" +[input] + +Forward={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) +] +} +Back={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +] +} +Left={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null) +] +} +Right={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null) +] +} +Jump={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +] +} +Exit={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} +Run={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +] +} + [physics] 2d/default_gravity_vector=Vector2(0, 0) diff --git a/scripts/document.gd b/scripts/document.gd index d8ebfee..ce7842c 100644 --- a/scripts/document.gd +++ b/scripts/document.gd @@ -64,7 +64,6 @@ var parser = Parser.new(source) var dom_tree = DOM.build_dom_tree(parser) - dom_tree.debug_print() var layout_tree = Layout.build_layout_tree(dom_tree) layout_tree.layout(1000) _create_block(self, layout_tree) @@ -100,6 +99,7 @@ _load_page(address) func _ready(): + Input.mouse_mode = Input.MOUSE_MODE_VISIBLE current_page = address_box.text http_request.request_completed.connect(_on_request_completed) back_button.pressed.connect(_on_back_pressed) diff --git a/scripts/document3d.gd b/scripts/document3d.gd new file mode 100644 index 0000000..7b50342 --- /dev/null +++ b/scripts/document3d.gd @@ -0,0 +1,118 @@ +extends Node3D + +@export var http_request: HTTPRequest +@export var player: Player + +const SCALE = 0.2 +const TEXT_BIAS = 0.01 + +var current_address: String + +func _create_block_node(block: Layout.BlockNode) -> Node3D: + var box = Node3D.new() + box.name = block.dom_node.tag_name + "-" + str(randi_range(0, 1000)) + box.position = Vector3(block.rect.position.x * SCALE, 0, block.rect.position.y * SCALE) + return box + +func _apply_text(text: Label3D, fragment: Layout.TextFragment): + text.text = fragment.text + text.font_size = fragment.dom_node.style.font_size * SCALE * 200 + text.modulate = fragment.dom_node.style.text_color + text.shaded = true + text.double_sided = false + +func _create_text_fragment_mesh(parent: Node, fragment: Layout.TextFragment): + var box = MeshInstance3D.new() + box.name = 'Mesh' + box.mesh = BoxMesh.new() + box.mesh.size = Vector3(fragment.rect.size.x * SCALE, fragment.rect.size.y * SCALE, fragment.rect.size.y * SCALE) + parent.add_child(box) + + var text_front = Label3D.new() + _apply_text(text_front, fragment) + text_front.position.z = fragment.rect.size.y * SCALE / 2 + TEXT_BIAS + box.add_child(text_front) + + var text_back = Label3D.new() + _apply_text(text_back, fragment) + text_back.position.z = -fragment.rect.size.y * SCALE / 2 - TEXT_BIAS + text_back.rotate(Vector3(0, 1, 0), deg_to_rad(180)) + box.add_child(text_back) + + var text_top = Label3D.new() + _apply_text(text_top, fragment) + text_top.position.y = fragment.rect.size.y * SCALE / 2 + TEXT_BIAS + text_top.rotate(Vector3(1, 0, 0), deg_to_rad(-90)) + box.add_child(text_top) + + var text_bottom = Label3D.new() + _apply_text(text_bottom, fragment) + text_bottom.position.y = -fragment.rect.size.y * SCALE / 2 - TEXT_BIAS + text_bottom.rotate(Vector3(1, 0, 0), deg_to_rad(90)) + box.add_child(text_bottom) + +func _create_text_fragment(parent: Node, fragment: Layout.TextFragment): + var size = Vector3(fragment.rect.size.x * SCALE, fragment.rect.size.y * SCALE, fragment.rect.size.y * SCALE) + + var rigid_body = RigidBody3D.new() + rigid_body.position = Vector3(fragment.rect.position.x * SCALE, 0, fragment.rect.position.y * SCALE) + size / 2 + rigid_body.mass = 0.1 + parent.add_child(rigid_body) + + var shape = CollisionShape3D.new() + shape.name = 'Shape' + shape.shape = BoxShape3D.new() + shape.shape.size = size + rigid_body.add_child(shape) + + if fragment.dom_node.tag_name == 'a' and 'href' in fragment.dom_node.attributes: + var link = Link.new() + link.name = 'Link' + link.href = fragment.dom_node.attributes['href'] + rigid_body.add_child(link) + + _create_text_fragment_mesh(rigid_body, fragment) + +func _create_block(parent: Node, block: Layout.BlockNode) -> Node3D: + var box = _create_block_node(block) + parent.add_child(box, true) + + for block_child in block.block_children: + _create_block(box, block_child) + for text_fragment in block.text_fragments: + _create_text_fragment(box, text_fragment) + return box + +func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray): + var source = body.get_string_from_utf8() + + var parser = Parser.new(source) + var dom_tree = DOM.build_dom_tree(parser) + var layout_tree = Layout.build_layout_tree(dom_tree) + layout_tree.layout(1000) + _create_block(self, layout_tree) + +func _load_page(address: String): + for child in get_children(): + remove_child(child) + + print('Load ', address) + http_request.cancel_request() + http_request.request(address) + current_address = address + +func on_refresh() -> void: + _load_page(Globals.address) + +func _on_link_collided(link: Link): + var href = link.href + if not href.begins_with('http'): + var address = current_address.strip_edges().trim_prefix('/') + var base = '/'.join(address.split('/').slice(0, -1)) + href = base + '/' + href + _load_page(href) + +func _ready() -> void: + http_request.request_completed.connect(_on_request_completed) + player.on_link_collided.connect(_on_link_collided) + on_refresh() diff --git a/scripts/globals.gd b/scripts/globals.gd new file mode 100644 index 0000000..dbed700 --- /dev/null +++ b/scripts/globals.gd @@ -0,0 +1,3 @@ +extends Node + +var address: String = '' diff --git a/scripts/player.gd b/scripts/player.gd new file mode 100644 index 0000000..174a42e --- /dev/null +++ b/scripts/player.gd @@ -0,0 +1,94 @@ +class_name Player +extends CharacterBody3D + +signal on_link_collided(link: Link) + +const GRAVITY_MULTIPLYER = 5.0 + +const BOB_HEIGHT = 0.02 +const BOB_SIDE = 0.04 +const BOB_SPEED = 4.0 + +@export var speed = 180.0 +@export var gravity = -9.8 +@export var friction = Vector3(0.4, 1, 0.4) +@export var jump_height = 0.0 + +@export var mouse_sensitivity = 0.007 + +@export var step_sounds: Array[AudioStream] +@export var min_step_sound_speed = 0.1 +@export var step_sound_time = 0.5 +var step_timer: float = 0 + +@onready var camera = $Camera3D +@onready var camera_origin = camera.transform.origin +var bob_time: float = 0.0 +var bob_damper: float = 0.0 + +var in_link_grace: bool = false +var time_of_last_link_collision: int = 0 + +func _ready(): + set_up_direction(Vector3.UP) + set_floor_stop_on_slope_enabled(false) + Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + +func _on_link_collided(link: Link): + var time_since = Time.get_ticks_msec() - time_of_last_link_collision + if time_since > 3000: + on_link_collided.emit(link) + + time_of_last_link_collision = Time.get_ticks_msec() + +func _physics_process(delta): + var forward_axis = Input.get_axis('Back', 'Forward') + var side_axis = Input.get_axis('Right', 'Left') + + var speed_multiplier = 1 + if Input.is_action_pressed("Run"): + speed_multiplier = 10 + + var force = Vector3(0, gravity * GRAVITY_MULTIPLYER, 0) + force += transform.basis.z * forward_axis * speed * speed_multiplier + force += transform.basis.x * side_axis * speed * speed_multiplier + + if jump_height > 0 and is_on_floor() and Input.is_action_just_pressed('Jump'): + force += Vector3(0, jump_height, 0) + + velocity += force * delta + move_and_slide() + velocity *= friction + + for index in get_slide_collision_count(): + var collider = get_slide_collision(index).get_collider() + if collider is RigidBody3D: + var rigid_body = collider as RigidBody3D + if rigid_body.has_node('Link'): + _on_link_collided(rigid_body.get_node('Link') as Link) + + if is_on_floor(): + _bob_camera(delta) + +func _input(event): + if event is InputEventMouseMotion: + var motion = event.relative * -mouse_sensitivity + camera.rotation_degrees.x = clamp(camera.rotation_degrees.x + rad_to_deg(motion.y), -90, 90) + rotate_y(motion.x) + + if event.is_action('Exit'): + get_tree().change_scene_to_file('res://document.tscn') + +func _bob_camera(delta: float): + var movement_speed = Vector3(velocity.x, 0, velocity.z).length() + if bob_damper > movement_speed: + bob_damper -= delta * 20.0 + bob_damper = max(bob_damper, movement_speed) + elif bob_damper < movement_speed: + bob_damper += delta * 5.0 + bob_damper = min(bob_damper, movement_speed) + + var bob_offset = Vector3(sin(bob_time * 0.5) * BOB_SIDE, sin(bob_time) * BOB_HEIGHT, 0) + camera.transform.origin = camera_origin + bob_offset * bob_damper + camera.rotation_degrees.z = sin(bob_time * 0.5) * 0.1 * bob_damper + bob_time += delta * bob_damper * BOB_SPEED diff --git a/scripts/walking_mode_button.gd b/scripts/walking_mode_button.gd new file mode 100644 index 0000000..63597b1 --- /dev/null +++ b/scripts/walking_mode_button.gd @@ -0,0 +1,8 @@ +extends Button + +@export var walking_scene: PackedScene +@export var address_box: TextEdit + +func _pressed() -> void: + Globals.address = address_box.text + get_tree().change_scene_to_packed(walking_scene) diff --git a/textures/Concrete_019_BaseColor.jpg b/textures/Concrete_019_BaseColor.jpg new file mode 100644 index 0000000..ae88c55 --- /dev/null +++ b/textures/Concrete_019_BaseColor.jpg Binary files differ diff --git a/textures/Concrete_019_BaseColor.jpg.import b/textures/Concrete_019_BaseColor.jpg.import new file mode 100644 index 0000000..6ad78e5 --- /dev/null +++ b/textures/Concrete_019_BaseColor.jpg.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dyfs4oi60qhi6" +path.s3tc="res://.godot/imported/Concrete_019_BaseColor.jpg-acea8d5dec75537a30260b45d42196be.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://textures/Concrete_019_BaseColor.jpg" +dest_files=["res://.godot/imported/Concrete_019_BaseColor.jpg-acea8d5dec75537a30260b45d42196be.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/textures/Concrete_019_Normal.jpg b/textures/Concrete_019_Normal.jpg new file mode 100644 index 0000000..5b49c1b --- /dev/null +++ b/textures/Concrete_019_Normal.jpg Binary files differ diff --git a/textures/Concrete_019_Normal.jpg.import b/textures/Concrete_019_Normal.jpg.import new file mode 100644 index 0000000..4a2a33b --- /dev/null +++ b/textures/Concrete_019_Normal.jpg.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cf7eapkaadrgp" +path.s3tc="res://.godot/imported/Concrete_019_Normal.jpg-527f08cfabe81596111e425f15a43428.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://textures/Concrete_019_Normal.jpg" +dest_files=["res://.godot/imported/Concrete_019_Normal.jpg-527f08cfabe81596111e425f15a43428.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=1 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=1 +roughness/src_normal="res://textures/Concrete_019_Normal.jpg" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0