├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── addons
└── qmapbsp
│ ├── class
│ ├── __unknown__.gd
│ ├── func_blocklight.gd
│ ├── func_brush.gd
│ ├── func_navmesh.gd
│ ├── func_occluder.gd
│ └── worldspawn.gd
│ ├── icon
│ ├── icon_node.svg
│ ├── icon_node.svg.import
│ ├── icon_node3d.svg
│ ├── icon_node3d.svg.import
│ ├── icon_node3d_error.svg
│ └── icon_node3d_error.svg.import
│ ├── importer
│ ├── base_loader.gd
│ ├── base_parser.gd
│ ├── bsp_parser.gd
│ ├── cmplwf
│ │ └── quake1.gd
│ ├── compilation_workflow.gd
│ ├── impext
│ │ ├── custom_textures.gd
│ │ ├── scene.gd
│ │ └── trenchbroom.gd
│ ├── map_parser.gd
│ └── world_importer.gd
│ ├── inspector
│ ├── map_inspector.gd
│ └── sub
│ │ └── map_quake_inspector_control.tscn
│ ├── plugin.cfg
│ ├── qmapbsp.gd
│ ├── resource
│ ├── map.gd
│ ├── map_format.gd
│ ├── map_import_error.gd
│ ├── map_import_error.tscn
│ ├── quake1_style_shader.gd
│ └── user_config.gd
│ ├── texture
│ ├── missing.png
│ ├── missing.png.import
│ └── missing.tres
│ ├── trenchbroom
│ ├── game_config_inspector.gd
│ ├── game_config_maker.gd
│ ├── game_config_resource.gd
│ ├── map_config.gd
│ ├── map_importer.gd
│ └── sub
│ │ ├── game_config_inspector.gd
│ │ └── game_config_inspector.tscn
│ └── util
│ ├── clipper.gd
│ ├── imagepacker.gd
│ └── type_prop.gd
├── icon.svg
├── icon.svg.import
├── project.godot
├── quake1_example
├── README.md
├── background.png
├── background.png.import
├── class
│ ├── _ambient.gd
│ ├── _func.gd
│ ├── _trigger.gd
│ ├── ambient_comp_hum.gd
│ ├── ambient_drip.gd
│ ├── ambient_drone.gd
│ ├── ambient_flouro_buzz.gd
│ ├── ambient_light_buzz.gd
│ ├── ambient_suck_wind.gd
│ ├── ambient_swamp1.gd
│ ├── ambient_swamp2.gd
│ ├── ambient_thunder.gd
│ ├── func_bossgate.gd
│ ├── func_button.gd
│ ├── func_door.gd
│ ├── func_door_secret.gd
│ ├── func_episodegate.gd
│ ├── func_illusionary.gd
│ ├── func_plat.gd
│ ├── func_train.gd
│ ├── func_wall.gd
│ ├── info_player_start.gd
│ ├── info_teleport_destination.gd
│ ├── light.gd
│ ├── light_flame_large_yellow.gd
│ ├── light_fluorospark.gd
│ ├── light_torch_small_walltorch.gd
│ ├── path_corner.gd
│ ├── trigger_changelevel.gd
│ ├── trigger_counter.gd
│ ├── trigger_hurt.gd
│ ├── trigger_monsterjump.gd
│ ├── trigger_multiple.gd
│ ├── trigger_once.gd
│ ├── trigger_onlyregistered.gd
│ ├── trigger_push.gd
│ ├── trigger_relay.gd
│ ├── trigger_secret.gd
│ ├── trigger_setskill.gd
│ ├── trigger_teleport.gd
│ ├── worldspawn.gd
│ └── worldspawn.tscn
├── clip_proxy_animated.gd
├── clip_proxy_area.gd
├── clip_proxy_static.gd
├── console.gd
├── console.tscn
├── fluid.gd
├── fluid_lava.gd
├── fluid_slime.gd
├── hub.tscn
├── hud.gd
├── leaf_volume.gd
├── lmp.gd
├── material
│ ├── fluid.gdshader
│ ├── mdl_animated.gdshader
│ ├── sky.gdshader
│ ├── sky.tres
│ └── sky_sky.gdshader
├── mdl.gd
├── mdl_instance.gd
├── menu.gd
├── message.gd
├── pak_file.gd
├── quake1_hub.gd
├── quake1_style.gd
├── quake_draw.gd
├── raw_file.gd
├── scene
│ ├── player.gd
│ └── player.tscn
├── sky_sky.tres
├── viewer.gd
├── viewer.tscn
├── viewer_fade.gdshader
├── wad.gd
├── wav.gd
├── wim.gd
└── world.gd
└── trenchbroom_example
├── README.md
├── classes
├── light.gd
├── light_static.gd
├── test_sphere.gd
└── test_sphere.tscn
├── icon.png
├── icon.png.import
├── inherited
└── map4_lightmap.tscn
├── maps
├── 1_geometry.map
├── 1_geometry.map.import
├── 2_group.map
├── 2_group.map.import
├── 3_special.map
├── 3_special.map.import
├── 4_lightmap.map
├── 4_lightmap.map.import
├── 5_interior.map
├── 5_interior.map.import
├── 6_occluder.map
├── 6_occluder.map.import
├── 7_occluder_shrink.map
├── 7_occluder_shrink.map.import
├── 7_occluder_shrink.tscn
├── 8_smoothing.map
├── 8_smoothing.map.import
├── 9_node_exterior.map
├── 9_node_exterior.map.import
├── 9_node_interior.map
├── 9_node_interior.map.import
├── occluder_config.tres
└── occluder_shrink_test_config.tres
├── my_game.tres
├── playable
├── interior_test.occ
└── interior_test.tscn
├── scenes
├── player.gd
└── player.tscn
├── textures
├── area.png
├── area.png.import
├── block_light.png
├── block_light.png.import
├── hint.png
├── hint.png.import
├── hintskip.png
├── hintskip.png.import
├── navmesh.png
├── navmesh.png.import
├── occluder.png
├── occluder.png.import
├── prototype
│ ├── prototype1.png
│ └── prototype1.png.import
├── simple1.png
├── simple1.png.import
├── simple2.png
├── simple2.png.import
├── simple3.png
├── simple3.png.import
├── simple4.png
├── simple4.png.import
├── simple5.png
├── simple5.png.import
├── skip.png
├── skip.png.import
├── subfolder
│ ├── fence1.png
│ ├── fence1.png.import
│ ├── fence1.tres
│ ├── glass1.png
│ ├── glass1.png.import
│ ├── glass1.tres
│ ├── simple.png
│ └── simple.png.import
├── trigger.png
└── trigger.png.import
└── usercfg.tres
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Normalize EOL for all files that Git considers text files.
2 | * text=auto eol=lf
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Godot 4+ specific ignores
2 | .godot/
3 |
4 | _test/
5 | trenchbroom_example/autosave/
6 | .DS_Store
7 |
8 | # test content
9 | ID1/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Kongfa Waroros
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Qmapbsp
2 | An interactive Quake's MAP/BSP loader for Godot 4. It loads MAP and BSP together into the Godot 4 scene.
3 |
4 | > [!WARNING]
5 | > This plugin is still a work in progress and is expected to be updated periodically.
6 |
7 | ## Supported MAP/BSP formats
8 | ### BSP
9 | - Quake 1 BSP/BSP2
10 | ### MAP
11 | - Quake 1 MAP
12 | - Valve 220 Format MAP
13 |
14 | ## Installation
15 | Copy the `addons` folder to your project folder. Then go to `Project Settings → Plugins` and check the `Enable` checkbox.
16 |
17 | ## Usage
18 | For loading MAP files with Trenchbroom configuration, [See this example folder](https://github.com/gongpha/gdQmapbsp/tree/master/trenchbroom_example)
19 |
20 | For trying out Quake maps, [See this example folder](https://github.com/gongpha/gdQmapbsp/tree/master/quake1_example)
21 |
--------------------------------------------------------------------------------
/addons/qmapbsp/class/__unknown__.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Node3D
3 | class_name QmapbspUnknownClassname
4 |
5 | ## An entity that doesn't have any script file
6 |
7 | @export var props : Dictionary
8 |
--------------------------------------------------------------------------------
/addons/qmapbsp/class/func_blocklight.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Node3D
3 | class_name QmapbspBlocklight
4 |
5 | func _qmapbsp_is_brush_solid() -> bool : return false
6 |
7 | func _init() -> void : set_script.call_deferred(null) # remove itself
8 |
--------------------------------------------------------------------------------
/addons/qmapbsp/class/func_brush.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends AnimatableBody3D
3 | class_name QmapbspBrush
4 |
5 | func _qmapbsp_get_gi_mode() -> int :
6 | return GeometryInstance3D.GI_MODE_STATIC
7 |
8 | func _init() -> void : set_script.call_deferred(null) # remove itself
9 |
--------------------------------------------------------------------------------
/addons/qmapbsp/class/func_navmesh.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends NavigationRegion3D
3 | class_name QmapbspFuncNavmesh
4 |
5 | func _qmapbsp_is_brush_solid() -> bool : return false
6 |
7 | func _init() -> void : set_script.call_deferred(null) # remove itself
8 |
--------------------------------------------------------------------------------
/addons/qmapbsp/class/func_occluder.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Node3D
3 | class_name QmapbspOccluder
4 |
5 | func _qmapbsp_is_brush_visible() -> bool : return false
6 | func _qmapbsp_is_brush_solid() -> bool : return false
7 |
8 | func _init() -> void : set_script.call_deferred(null) # remove itself
9 |
--------------------------------------------------------------------------------
/addons/qmapbsp/class/worldspawn.gd:
--------------------------------------------------------------------------------
1 | extends StaticBody3D
2 | class_name QmapbspWorldspawn
3 |
4 | func _init() -> void : set_script.call_deferred(null) # remove itself
5 |
--------------------------------------------------------------------------------
/addons/qmapbsp/icon/icon_node.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
104 |
--------------------------------------------------------------------------------
/addons/qmapbsp/icon/icon_node.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bjcxe1pm4fqjx"
6 | path="res://.godot/imported/icon_node.svg-5ef2a7affe7e079e6a86aa2913d1e895.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/qmapbsp/icon/icon_node.svg"
14 | dest_files=["res://.godot/imported/icon_node.svg-5ef2a7affe7e079e6a86aa2913d1e895.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/qmapbsp/icon/icon_node3d.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
104 |
--------------------------------------------------------------------------------
/addons/qmapbsp/icon/icon_node3d.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cd0v0fom8bjfo"
6 | path="res://.godot/imported/icon_node3d.svg-1704f8e1cb2b4a8b3544beb17db9a02c.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/qmapbsp/icon/icon_node3d.svg"
14 | dest_files=["res://.godot/imported/icon_node3d.svg-1704f8e1cb2b4a8b3544beb17db9a02c.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/qmapbsp/icon/icon_node3d_error.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
112 |
--------------------------------------------------------------------------------
/addons/qmapbsp/icon/icon_node3d_error.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cbkmer0i3bdwk"
6 | path="res://.godot/imported/icon_node3d_error.svg-ae76da4d442b6ad66e8f05dda25953e2.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://addons/qmapbsp/icon/icon_node3d_error.svg"
14 | dest_files=["res://.godot/imported/icon_node3d_error.svg-ae76da4d442b6ad66e8f05dda25953e2.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/addons/qmapbsp/importer/base_loader.gd:
--------------------------------------------------------------------------------
1 | extends RefCounted
2 | class_name QmapbspBaseLoader
3 |
4 | var load_section : int
5 | var section_keys : Array
6 | var section_ratios : PackedFloat32Array
7 | var section_ratio_totals : PackedFloat32Array
8 | var load_index : int
9 |
10 | var curr_section : Array
11 | var curr_section_call : Callable
12 | var local_progress : float
13 |
14 | func __end_section__() -> int : return __sections__().size()
15 | var __end := __end_section__()
16 | func __sections__() -> Dictionary : return {}
17 | var __sections := __sections__()
18 | var __error : StringName
19 | var __ret : Array
20 |
21 | func _init() :
22 | section_ratios.resize(__sections.size())
23 | section_ratio_totals.resize(__sections.size())
24 | var V : Array = __sections.values()
25 | var total : float = 0.0
26 | for i in V.size() :
27 | var v = V[i]
28 | var r : float
29 | if v is Array :
30 | r = v[1]
31 |
32 | else :
33 | r = 1.0
34 | total += r
35 | section_ratios[i] = r
36 | for i in V.size() :
37 | section_ratios[i] /= total
38 |
39 | for i in section_ratio_totals.size() - 1 :
40 | var t : float = section_ratios[i]
41 | if i > 0 :
42 | t += section_ratio_totals[i]
43 | section_ratio_totals[i + 1] = t
44 |
45 | section_keys = __sections.keys()
46 | _update_load_section()
47 |
48 | func poll(known_delta : float = INF) -> StringName :
49 | local_progress = curr_section_call.call()
50 | if __error != StringName() :
51 | return __error
52 | if local_progress >= 1.0 :
53 | load_section += 1
54 | local_progress = 0.0
55 | if load_section == __end :
56 | _end()
57 | return &'END'
58 | _update_load_section()
59 | load_index = 0
60 | return StringName()
61 |
62 | func _update_load_section() :
63 | var that = __sections[section_keys[load_section]]
64 | if that is Array :
65 | curr_section_call = that[0]
66 | return
67 | curr_section_call = that
68 |
69 | func get_progress() -> float :
70 | if load_section >= __end : return 1.0
71 | #var stp := 1.0 / __end
72 | var sec := section_ratio_totals[load_section]
73 | var scp : float = local_progress * section_ratios[load_section]
74 | return scp + sec
75 |
76 | func get_local_progress() -> float :
77 | return local_progress
78 |
79 | func _end() : pass
80 |
--------------------------------------------------------------------------------
/addons/qmapbsp/importer/base_parser.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspBaseLoader
2 | class_name QmapbspBaseParser
3 |
4 | var file : FileAccess
5 | var file_begin : int
6 | var error : StringName
7 |
8 | # in
9 | var wim : QmapbspWorldImporter
10 | var unit_scale : float = 1.0 / 32
11 |
12 | signal tell_entity_props(id : int, props : Dictionary)
13 |
14 | # entities
15 | var mapf : QmapbspMapFormat
16 |
17 | func begin_file(f : FileAccess, begin := 0) -> StringName :
18 | file = f
19 | file_begin = begin
20 | return StringName()
21 |
22 | func _GatheringAllEntities() -> float :
23 | var err : int = mapf.poll(__ret)
24 | var end := _mapf_after_poll(err)
25 |
26 | if end :
27 | entity_dict = {}
28 | return 1.0
29 | return minf(_mapf_prog(), 0.99)
30 |
31 | func _ImportingData() -> float :
32 | return 1.0
33 |
34 | func _ConstructingData() -> float :
35 | return 1.0
36 |
37 | func _BuildingData() -> float :
38 | return 1.0
39 |
40 | func _BuildingDataCustom() -> float :
41 | return 1.0
42 |
43 | const WORLDSPAWN_BRUSH_ENTITIES := [
44 | # Trenchbroom
45 | &"func_group",
46 | # ericw-tools
47 | &"func_detail",
48 | &"func_detail_illusionary",
49 | &"func_detail_wall",
50 | &"func_detail_fence",
51 | ]
52 |
53 | var entity_idx := 0
54 | var entity_dict : Dictionary
55 |
56 | var entity_first_brush : bool = false
57 | var entity_is_illusionary : bool = false
58 |
59 | # !!! a bsp entity count can probably not be the same as a map entity !!!
60 | var worldspawn_entity_count : int = 0
61 |
62 | var brushes : Array
63 | func _mapf_after_poll(pollr : int) -> bool :
64 | match pollr :
65 | QmapbspMapFormat.PollResult.ERR :
66 | __error = &'MAP_PARSE_ERROR'
67 | QmapbspMapFormat.PollResult.BEGIN_ENTITY :
68 | entity_dict = {}
69 | entity_first_brush = true
70 | QmapbspMapFormat.PollResult.END_ENTITY :
71 | if entity_dict.get('classname') in WORLDSPAWN_BRUSH_ENTITIES :
72 | # ignore these entities
73 | _end_entity(0) # treat as Worldspawn
74 | worldspawn_entity_count += 1
75 | else :
76 | _end_entity(
77 | entity_idx - worldspawn_entity_count
78 | )
79 | entity_idx += 1
80 |
81 | QmapbspMapFormat.PollResult.FOUND_KEYVALUE :
82 | var k : StringName = mapf.out[0]
83 | var v : StringName = mapf.out[1]
84 | entity_dict[k] = v
85 | if k == &'mapversion' and v == &'220' :
86 | mapf.tell_valve_format = true
87 | QmapbspMapFormat.PollResult.FOUND_BRUSH :
88 | entity_dict['__qmapbsp_has_brush'] = true
89 | if entity_first_brush :
90 | entity_is_illusionary = entity_dict.get('classname') == &'func_detail_illusionary'
91 | entity_first_brush = false
92 |
93 | _brush_found()
94 | QmapbspMapFormat.PollResult.END :
95 | return true
96 | return false
97 |
98 | func _brush_found() -> void : return
99 | func _end_entity(idx : int) -> void : return
100 |
101 | func _mapf_prog() -> float :
102 | return float(mapf.i) / mapf.src.length()
103 |
104 | ##################################################
105 |
106 | func __sections__() -> Dictionary :
107 | return {
108 | &'GATHERING_ALL_ENTITIES' : _GatheringAllEntities,
109 | &'IMPORTING_DATA' : _ImportingData,
110 | &'CONSTRUCTING_DATA' : _ConstructingData,
111 | &'BUILDING_DATA' : _BuildingData,
112 | &'BUILDING_DATA_CUSTOM' : _BuildingDataCustom,
113 | }
114 |
115 | static func _qnor_to_vec3(q : Vector3) -> Vector3 :
116 | return Vector3(-q.y, q.z, -q.x)
117 |
118 | static func _read_vec3(f : FileAccess) -> Vector3 :
119 | return Vector3(
120 | f.get_float(),
121 | f.get_float(),
122 | f.get_float()
123 | )
124 |
125 | static func _read_vec3_16(f : FileAccess) -> Vector3 :
126 | return Vector3(
127 | f.get_16(),
128 | f.get_16(),
129 | f.get_16()
130 | )
131 |
132 | func _qpos_to_vec3(q : Vector3) -> Vector3 :
133 | return _qnor_to_vec3(q) * unit_scale
134 |
135 | static func _qnor_to_vec3_read(f : FileAccess) -> Vector3 :
136 | return _qnor_to_vec3(_read_vec3(f))
137 |
138 | func _qpos_to_vec3_read(f : FileAccess) -> Vector3 :
139 | return _qnor_to_vec3(_read_vec3(f)) * unit_scale
140 |
141 | static func _qnor_to_vec3_read_16(f : FileAccess) -> Vector3 :
142 | return _qnor_to_vec3(_read_vec3_16(f))
143 |
144 | func _qpos_to_vec3_read_16(f : FileAccess) -> Vector3 :
145 | return _qnor_to_vec3(_read_vec3_16(f)) * unit_scale
146 |
147 | const _1s15 = 1 << 15
148 | const _1s16 = 1 << 16
149 | static func u16to16(u : int) -> int :
150 | return (u + _1s15) % _1s16 - _1s15
151 |
152 | static func _read_qvec3_16(f : FileAccess) -> Vector3i :
153 | return Vector3i(
154 | u16to16(f.get_16()),
155 | u16to16(f.get_16()),
156 | u16to16(f.get_16())
157 | )
158 |
159 | func _read_bbshort(f : FileAccess) -> AABB :
160 | var min := _read_qvec3_16(f) * unit_scale
161 | var max := _read_qvec3_16(f) * unit_scale
162 | max -= min
163 | #return AABB(min, max)
164 | min.x += max.x
165 | return AABB(
166 | _qnor_to_vec3(min),
167 | Vector3(max.x, max.z, max.y)
168 | )
169 |
170 | func _read_bbfloat(f : FileAccess) -> AABB :
171 | var min := _read_vec3(f) * unit_scale
172 | var max := _read_vec3(f) * unit_scale
173 | max -= min
174 | #return AABB(min, max)
175 | min.x += max.x
176 | return AABB(
177 | _qnor_to_vec3(min),
178 | Vector3(max.x, max.z, max.y)
179 | )
180 |
181 | func _get16as32(f : FileAccess) -> int :
182 | return u16to16(f.get_16())
183 |
--------------------------------------------------------------------------------
/addons/qmapbsp/importer/cmplwf/quake1.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends QmapbspCompilationWorkflow
3 | class_name QmapbspCompilationWorkflowQuake1
4 |
5 | @export_global_file var qbsp_path : String
6 | #@export_global_file var vis_path : String
7 | #@export_global_file var light_path : String
8 |
9 | func _get_working_dir() -> String : return "res://.godot/qmapbsp/artifact"
10 |
11 | func _compile(map_filepath : String) -> String :
12 | if qbsp_path.is_empty() :
13 | printerr("No QBSP path specified")
14 | return ""
15 | var o : Array
16 |
17 | DirAccess.make_dir_recursive_absolute(_get_working_dir())
18 | var out := ProjectSettings.globalize_path(_get_working_dir().path_join(
19 | map_filepath.get_file()
20 | ))
21 | out = out.get_basename() + ".bsp"
22 | OS.execute(qbsp_path, [
23 | '-wrbrushesonly',
24 | '-notex',
25 | '-nopercent',
26 | '-maxNodeSize 0',
27 | '-subdivide 0',
28 | '-bsp2',
29 | ProjectSettings.globalize_path(map_filepath),
30 | out
31 | ], o)
32 | return out
33 |
--------------------------------------------------------------------------------
/addons/qmapbsp/importer/compilation_workflow.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Resource
3 | class_name QmapbspCompilationWorkflow
4 |
5 | func _compile(map_filepath : String) -> String : return String()
6 | func _get_working_dir() -> String : return String()
7 |
--------------------------------------------------------------------------------
/addons/qmapbsp/importer/impext/custom_textures.gd:
--------------------------------------------------------------------------------
1 | ## A simple extension for loading textures from FileSystem
2 | extends QmapbspWorldImporterScene
3 | class_name QmapbspWorldImporterCustomTextures
4 |
5 | # VVV - override your own - VVV #
6 |
7 | ## Returns all supported file extensions for finding a texture
8 | func _get_extensions() -> PackedStringArray :
9 | return PackedStringArray([
10 | 'tres', 'material',
11 | 'png', 'jpeg', 'jpg'
12 | ])
13 |
14 | ## Returns the texture directory path that contains texture resources
15 | func _texture_get_dir() -> String :
16 | return "res://textures/"
17 |
18 | ## Constructs new material from textures that were recognized as Texture2D
19 | func _construct_new_material(texture : Texture2D) -> Material :
20 | var mat := StandardMaterial3D.new()
21 | mat.albedo_texture = texture
22 | return mat
23 |
24 | #################################################
25 | var _created_textures : Dictionary #
26 |
27 | func _texture_include_bsp_textures() -> bool : return true
28 |
29 | func _rename_texture(name : String) -> String : return name
30 |
31 | func _build_paths(
32 | name : String
33 | ) -> PackedStringArray :
34 | var exts := _get_extensions()
35 | var texdir := _texture_get_dir()
36 | var paths : PackedStringArray
37 | for e in exts :
38 | paths.append(texdir.path_join('%s.%s' % [_rename_texture(name), e]))
39 | return paths
40 |
41 | func _texture_get_material(
42 | index : int, texture_name : String, texture_size : Vector2i
43 | ) -> Array :
44 | var existed : Array = _created_textures.get(texture_name, [])
45 | if !existed.is_empty() : return existed
46 |
47 | var paths := _build_paths(texture_name)
48 | var rsc : Array
49 |
50 | # finding the best resource file for this texture
51 | for p in paths :
52 | if !ResourceLoader.exists(p) : continue
53 | var mat_or_tex2d = load(p)
54 | if mat_or_tex2d is Texture2D :
55 | rsc = [_construct_new_material(mat_or_tex2d), mat_or_tex2d.get_size()]
56 | _created_textures[texture_name] = rsc
57 | return rsc
58 | elif mat_or_tex2d is BaseMaterial3D :
59 | if mat_or_tex2d.albedo_texture :
60 | rsc = [mat_or_tex2d, mat_or_tex2d.albedo_texture.get_size()]
61 | _created_textures[texture_name] = [rsc]
62 | return rsc
63 | return super(index, texture_name, texture_size)
64 |
--------------------------------------------------------------------------------
/addons/qmapbsp/importer/impext/trenchbroom.gd:
--------------------------------------------------------------------------------
1 | ## A world importer with Trenchbroom extension
2 | extends QmapbspWorldImporterCustomTextures
3 | class_name QmapbspWorldImporterTrenchbroom
4 |
5 | var game_config : QmapbspTrenchbroomGameConfigResource
6 | var map_config : QmapbspTrenchbroomMapConfig
7 |
8 | var point_file_points : PackedVector3Array
9 |
10 | func _get_unit_scale_f() -> float :
11 | return map_config.inverse_scale_factor
12 |
13 | func _entity_region_size(ent_id : int) -> float :
14 | return map_config.mesh_splitting_size
15 |
16 | func _texture_get_missing_texture() -> Array :
17 | var no := map_config.default_material
18 | if no : return [no, map_config.default_material_texture_size]
19 | return super()
20 |
21 | func _entity_unwrap_uv2(
22 | id : int, brush_id : int, mesh : ArrayMesh
23 | ) -> float :
24 | return map_config.lightmap_texel if !map_config.use_bsp_lightmap else -1.0
25 |
26 | func _build_point_file_lines() -> bool :
27 | return map_config.load_point_file
28 |
29 | func _compile_bsp(mappath : String) -> String :
30 | var usercfg := game_config.usercfg
31 | var cmplwf := usercfg.compilation_workflow
32 | var result_path := cmplwf._compile(mappath)
33 |
34 | # Check for a point file
35 | if map_config.load_point_file :
36 | var pts := FileAccess.open(result_path.get_basename() + ".pts", FileAccess.READ)
37 | if pts :
38 | var l := pts.get_line()
39 | var points : PackedVector3Array
40 | var isf := _get_unit_scale_f()
41 | while true :
42 | if l.is_empty() : break
43 |
44 | var psa := l.split(' ')
45 | if psa.size() != 3 :
46 | # cannot load
47 | return result_path
48 | points.append(QmapbspBaseParser._qnor_to_vec3(Vector3(
49 | float(psa[0]), float(psa[1]), float(psa[2])
50 | ) / isf))
51 | l = pts.get_line()
52 | point_file_points = points
53 | return result_path
54 |
55 | func _get_point_file_points() -> PackedVector3Array :
56 | return point_file_points
57 |
58 | func _point_files_simplify_angle() -> float :
59 | return super() if map_config.simplify_point_files else 0.0
60 |
61 | func _texture_get_dir() -> String :
62 | return game_config.textures_directory
63 |
64 | func _entity_node_directory_paths() -> PackedStringArray :
65 | return PackedStringArray(
66 | [game_config.ent_entity_script_directory]
67 | ) + super()
68 |
69 | func _entity_your_cooked_properties(id : int, entity : Dictionary) -> void :
70 | var epd := game_config._entity_properties_def
71 | var props : Dictionary = epd.get(entity.get("classname", &""), {})
72 | for k in props :
73 | if !entity.has(k) : continue
74 | var v = entity[k]
75 | var dv = props[k]
76 | if not v is StringName : continue
77 | entity[k] = QmapbspTypeProp.prop_to_var(v, typeof(dv))
78 | entity.merge(props)
79 |
80 | super(id, entity)
81 |
82 | func _get_entity_node(id : int) -> Node :
83 | var node : Node = entity_nodes.get(id, null)
84 | if !node : node = super(id)
85 | if !node : return null
86 |
87 | var dict : Dictionary = entity_props.get(id, {})
88 | if node is Node3D :
89 | node.visible = dict.get("visible", true)
90 |
91 | return node
92 |
93 | func _entity_prefers_occluder(ent_id : int) -> bool :
94 | return super(ent_id) or (ent_id == 0 and map_config.occ_bake_occluders)
95 |
96 | func _get_occluder_shrink_amount() -> float :
97 | return map_config.occ_shrink_amount
98 |
99 | func _entity_get_collision_shape_method(ent_id : int) -> int :
100 | return map_config.collsion_constructing_method
101 |
102 | func _get_navmesh_template() -> NavigationMesh :
103 | return map_config.navmesh_template
104 |
105 | func _entity_auto_smooth_degree() -> float :
106 | return map_config.auto_smooth_max_angle
107 |
--------------------------------------------------------------------------------
/addons/qmapbsp/importer/map_parser.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspBaseParser
2 | class_name QmapbspMAPParser
3 |
4 | #var enable_collision_shapes : bool = false
5 | signal tell_collision_shapes(
6 | entity_curr_idx : int, entity_curr_brush_idx : int, shape : Shape3D, origin : Vector3
7 | )
8 |
9 | var known_textures : PackedStringArray
10 |
11 | var generated_box_shapes : Array[BoxShape3D]
12 | var generated_box_shapes_size : PackedVector3Array
13 |
14 | var parsed_shapes : Array[Array] # [shape, origin, textures]
15 |
16 | func begin_file(f : FileAccess, begin := 0) -> StringName :
17 | super(f, begin)
18 | mapf = QmapbspMapFormat.begin_from_text(f.get_as_text(true))
19 | return StringName()
20 |
21 | func _brush_found() -> void :
22 | if wim._entity_get_collision_shape_method(entity_idx) == 0 :
23 | _parse_shape()
24 |
25 | for t in mapf.brush_textures :
26 | if known_textures.has(t) : continue
27 | known_textures.append(t)
28 |
29 | func _make_convex() -> void :
30 | pass
31 |
32 | func _parse_shape() -> void :
33 | var shape : Shape3D
34 | var V := Vector3()
35 | var aabb : AABB
36 | if !entity_is_illusionary :
37 | var planes : Array[Plane] = mapf.brush_planes
38 | var is_box := true
39 | if planes.size() == 6 :
40 | # if it was a box. create a box shape instead of a convex shape.
41 | var x := Vector2(INF, -INF) # min, max
42 | var y := x
43 | var z := x
44 | for i in 6 :
45 | var plane := planes[i]
46 | var d := plane.d
47 | match plane.normal :
48 | Vector3(1, 0, 0) :
49 | x = Vector2(
50 | minf(plane.d, x.x),
51 | maxf(plane.d, x.y),
52 | )
53 | Vector3(-1, 0, 0) :
54 | x = Vector2(
55 | minf(-plane.d, x.x),
56 | maxf(-plane.d, x.y),
57 | )
58 | Vector3(0, 1, 0) :
59 | y = Vector2(
60 | minf(plane.d, y.x),
61 | maxf(plane.d, y.y),
62 | )
63 | Vector3(0, -1, 0) :
64 | y = Vector2(
65 | minf(-plane.d, y.x),
66 | maxf(-plane.d, y.y),
67 | )
68 | Vector3(0, 0, 1) :
69 | z = Vector2(
70 | minf(plane.d, z.x),
71 | maxf(plane.d, z.y),
72 | )
73 | Vector3(0, 0, -1) :
74 | z = Vector2(
75 | minf(-plane.d, z.x),
76 | maxf(-plane.d, z.y),
77 | )
78 | _ :
79 | is_box = false
80 | break
81 | aabb.position = _qpos_to_vec3(Vector3(x.x, y.x, z.x))
82 |
83 | aabb = aabb.expand(_qpos_to_vec3(Vector3(x.y, y.y, z.y)))
84 | else : is_box = false
85 |
86 | if is_box :
87 | var s := aabb.size
88 | var i := generated_box_shapes_size.find(s)
89 | if i == -1 :
90 | shape = BoxShape3D.new()
91 | (shape as BoxShape3D).size = s
92 | generated_box_shapes.append(shape)
93 | generated_box_shapes_size.append(s)
94 | else :
95 | shape = generated_box_shapes[i]
96 | V = aabb.get_center()
97 | else :
98 | var vertices := planes_intersect(planes)
99 | var vs := vertices.size()
100 | aabb.size = Vector3.ZERO
101 |
102 | for i in vs :
103 | var v := vertices[i]
104 | v = _qpos_to_vec3(v * -1)
105 | vertices[i] = v
106 | if i == 0 :
107 | aabb.position = v
108 | else :
109 | aabb = aabb.expand(v)
110 | V = aabb.get_center()
111 | for i in vs :
112 | vertices[i] -= V
113 |
114 | shape = ConvexPolygonShape3D.new()
115 | shape.points = vertices
116 |
117 | parsed_shapes.append([shape, V, mapf.brush_textures.duplicate()])
118 |
119 | func _end_entity(idx : int) :
120 | for i in parsed_shapes.size() :
121 | var arr : Array = parsed_shapes[i]
122 | tell_collision_shapes.emit(
123 | idx, -1, i, arr[0], arr[1], {
124 | 'hull' : -2,
125 | 'known_texture_names' : arr[2]
126 | }
127 | )
128 | parsed_shapes.clear()
129 |
130 | const EPS := 0.000001
131 | # https://math.stackexchange.com/a/1884181
132 | static func planes_intersect(planes : Array[Plane]) -> PackedVector3Array :
133 | var vv := PackedVector3Array()
134 |
135 | for i in planes.size() - 2 :
136 | for j in range(i + 1, planes.size() - 1) :
137 | for k in range(j + 1, planes.size()) :
138 | var n0 := planes[i].normal
139 | var n1 := planes[j].normal
140 | var n2 := planes[k].normal
141 | var d0 := planes[i].d * -1.0
142 | var d1 := planes[j].d * -1.0
143 | var d2 := planes[k].d * -1.0
144 | var t : float = (
145 | n0.x * (n1.y * n2.z - n1.z * n2.y) +
146 | n0.y * (n1.z * n2.x - n1.x * n2.z) +
147 | n0.z * (n1.x * n2.y - n1.y * n2.x)
148 | )
149 | if absf(t) < EPS : continue
150 | var v := Vector3(
151 | (d0 * (n1.z * n2.y - n1.y * n2.z) + d1 * (n0.y * n2.z - n0.z * n2.y) + d2 * (n0.z * n1.y - n0.y * n1.z)) / -t,
152 | (d0 * (n1.x * n2.z - n1.z * n2.x) + d1 * (n0.z * n2.x - n0.x * n2.z) + d2 * (n0.x * n1.z - n0.z * n1.x)) / -t,
153 | (d0 * (n1.y * n2.x - n1.x * n2.y) + d1 * (n0.x * n2.y - n0.y * n2.x) + d2 * (n0.y * n1.x - n0.x * n1.y)) / -t
154 | )
155 | var yes := true
156 | for l in planes.size() :
157 | var lp := planes[l]
158 | if l != i and l != j and l != k and v.dot(lp.normal) < (lp.d * -1) + EPS :
159 | yes = false
160 | break
161 | if yes :
162 | vv.append(v)
163 | return vv
164 |
--------------------------------------------------------------------------------
/addons/qmapbsp/inspector/map_inspector.gd:
--------------------------------------------------------------------------------
1 | extends EditorInspectorPlugin
2 | class_name QmapbspMapInspectorPlugin
3 |
4 | func _can_handle(o) -> bool :
5 | return o is QmapbspMap
6 |
7 | func _parse_begin(o) :
8 | var pscene := preload("sub/map_quake_inspector_control.tscn")
9 | add_custom_control(pscene.instantiate())
10 |
--------------------------------------------------------------------------------
/addons/qmapbsp/inspector/sub/map_quake_inspector_control.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene format=3 uid="uid://dlonoadgxlppd"]
2 |
3 | [node name="insp" type="VBoxContainer"]
4 | anchors_preset = 15
5 | anchor_right = 1.0
6 | anchor_bottom = 1.0
7 | grow_horizontal = 2
8 | grow_vertical = 2
9 |
10 | [node name="compile" type="Button" parent="."]
11 | layout_mode = 2
12 | text = "Compile to BSP"
13 |
--------------------------------------------------------------------------------
/addons/qmapbsp/plugin.cfg:
--------------------------------------------------------------------------------
1 | [plugin]
2 |
3 | name="Qmapbsp"
4 | description=""
5 | author="gongpha"
6 | version="0.1"
7 | script="qmapbsp.gd"
8 |
--------------------------------------------------------------------------------
/addons/qmapbsp/qmapbsp.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorPlugin
3 |
4 | const PATH := "res://addons/qmapbsp/"
5 | const PLUGIN_PATHS := [
6 | #"inspector/map_inspector.gd",
7 | "trenchbroom/game_config_inspector.gd",
8 | "trenchbroom/map_importer.gd",
9 | ]
10 |
11 | var plugins : Array[RefCounted] # []
12 |
13 | func _enter_tree() :
14 | for p in PLUGIN_PATHS :
15 | var plugin : RefCounted = load(PATH + p).new()
16 | plugins.append(plugin)
17 | if plugin is EditorImportPlugin :
18 | add_import_plugin(plugin)
19 | elif plugin is EditorInspectorPlugin :
20 | add_inspector_plugin(plugin)
21 |
22 | if plugin.has_method(&'_editor_plugin') :
23 | plugin.call(&'_editor_plugin', self)
24 |
25 | func _exit_tree() :
26 | for i in plugins :
27 | if i is EditorImportPlugin :
28 | remove_import_plugin(i)
29 | elif i is EditorInspectorPlugin :
30 | remove_inspector_plugin(i)
31 | plugins.clear()
32 |
--------------------------------------------------------------------------------
/addons/qmapbsp/resource/map.gd:
--------------------------------------------------------------------------------
1 | extends Resource
2 | class_name QmapbspMap
3 |
--------------------------------------------------------------------------------
/addons/qmapbsp/resource/map_format.gd:
--------------------------------------------------------------------------------
1 | extends Resource
2 | class_name QmapbspMapFormat
3 |
4 | ############################
5 | var src : String
6 | var i : int = -1
7 | var level : int = 0
8 |
9 | enum PollResult {
10 | BEGIN_ENTITY, # (no return results)
11 | FOUND_KEYVALUE, # [key, value]
12 | FOUND_BRUSH, #
13 | END_ENTITY,
14 | END, # (no return results)
15 |
16 | ERR, # [StringName]
17 | }
18 |
19 | # AFTER BEGIN_ENTITY ONLY
20 | var tell_skip_entity_entries : bool = false
21 | var tell_skip_entity_brushes : bool = false
22 |
23 | var tell_valve_format : bool = false
24 |
25 | # outputs
26 | var out : Array
27 | var brush_planes : Array[Plane]
28 | var brush_textures : PackedStringArray
29 | var brush_offsets : PackedVector2Array
30 | var brush_offsets1_valve : PackedColorArray
31 | var brush_offsets2_valve : PackedColorArray
32 | var brush_rotations : PackedFloat32Array
33 | var brush_scales : PackedVector2Array
34 |
35 | func poll(result : Array) -> int :
36 | var parsing : int = 0
37 | var str_begin : int
38 | var pushed : StringName
39 |
40 | while i < src.length() :
41 | var c := src.unicode_at(i)
42 |
43 | match parsing :
44 | 1 : # literal string
45 | match c :
46 | 0x5c : # \
47 | parsing = 2
48 | 0x22 : # "
49 | # end of the literal string
50 | var v := StringName(
51 | src.substr(str_begin, i - str_begin).c_unescape()
52 | )
53 | i += 1
54 | parsing = 0
55 | if pushed == StringName() :
56 | pushed = v
57 | else :
58 | # new entry
59 | out = [pushed, v]
60 | pushed = StringName()
61 | return PollResult.FOUND_KEYVALUE
62 | i += 1
63 | 2 : # escaping
64 | parsing = 1
65 | 3 : # comment
66 | if c == 0x10 : # newline
67 | parsing = 0
68 | i += 1
69 | 0 : # other
70 | match c :
71 | 0x7b : # {
72 | level += 1
73 | i += 1
74 | if level == 1 :
75 | return PollResult.BEGIN_ENTITY
76 | elif level == 2 :
77 | # begin brush
78 | brush_planes = []
79 | brush_textures = PackedStringArray()
80 | brush_offsets = PackedVector2Array()
81 | brush_offsets1_valve = PackedColorArray()
82 | brush_offsets2_valve = PackedColorArray()
83 | brush_rotations = PackedFloat32Array()
84 | brush_scales = PackedVector2Array()
85 | else :
86 | result.append(&'UNEXPECTED_LEVEL')
87 | return PollResult.ERR
88 |
89 | 0x7d : # }
90 | level -= 1
91 | i += 1
92 | if level < 0 :
93 | result.append(&'UNMATCHED_BRACES')
94 | return PollResult.ERR
95 | elif level == 0 :
96 | return PollResult.END_ENTITY
97 | elif level == 1 :
98 | return PollResult.FOUND_BRUSH
99 | 0x22 : # "
100 | if !tell_skip_entity_entries :
101 | str_begin = i + 1
102 | parsing = 1
103 | i += 1
104 | 0x27 : # /
105 | if src.length() - i - 1 == 0 :
106 | result.append(&'FOUND_SLASH_AT_EOF')
107 | return PollResult.ERR
108 | i += 1
109 | if src.unicode_at(i + 1) == 0x27 :
110 | # //
111 | parsing = 3
112 | 0x28 : # (
113 | if tell_skip_entity_brushes :
114 | i += 1
115 | continue
116 |
117 | var bs := src.substr(
118 | i, src.find('\n', i) - i
119 | )
120 | i += bs.length()
121 |
122 | var comment : int = bs.find('//', bs.length() - 1)
123 | if comment != -1 :
124 | bs = bs.substr(0, comment)
125 |
126 | var S := bs.split(' ')
127 |
128 | if tell_valve_format :
129 | if S.size() != 31 :
130 | result.append(&'INVALID_VALVE_BRUSH_PLANE')
131 | return PollResult.ERR
132 |
133 | brush_planes.append(Plane(
134 | Vector3(float(S[1]), float(S[2]), float(S[3])),
135 | Vector3(float(S[6]), float(S[7]), float(S[8])),
136 | Vector3(float(S[11]), float(S[12]), float(S[13])),
137 | ))
138 | brush_textures.append(S[15])
139 | brush_offsets1_valve.append(Color(
140 | float(S[17]), float(S[18]), float(S[19]), float(S[20])
141 | ))
142 | brush_offsets2_valve.append(Color(
143 | float(S[23]), float(S[24]), float(S[25]), float(S[26])
144 | ))
145 | brush_rotations.append(float(S[28]))
146 | brush_scales.append(Vector2(float(S[29]), float(S[30])))
147 | else :
148 | if S.size() != 21 :
149 | result.append(&'INVALID_BRUSH_PLANE')
150 | return PollResult.ERR
151 |
152 | brush_planes.append(Plane(
153 | Vector3(float(S[1]), float(S[2]), float(S[3])),
154 | Vector3(float(S[6]), float(S[7]), float(S[8])),
155 | Vector3(float(S[11]), float(S[12]), float(S[13])),
156 | ))
157 | brush_textures.append(S[15])
158 | brush_offsets.append(Vector2(float(S[16]), float(S[17])))
159 | brush_rotations.append(float(S[18]))
160 | brush_scales.append(Vector2(float(S[19]), float(S[20])))
161 |
162 | _ :
163 | i += 1
164 | continue
165 | if level != 0 :
166 | result.append(&"UNMATCHED_BRACES")
167 | return PollResult.ERR
168 | if pushed != StringName() :
169 | result.append(&"INCOMPLETE_KEY")
170 | return PollResult.ERR
171 | return PollResult.END
172 |
173 | static func begin_from_text(text : String) -> QmapbspMapFormat :
174 | var kv := QmapbspMapFormat.new()
175 | kv.i = 0
176 | kv.src = text
177 | return kv
178 |
179 | static func expect_vec3(v : StringName) -> Vector3 :
180 | var s := v.split(' ') if !v.is_empty() else PackedStringArray()
181 | if s.size() < 3 : return Vector3()
182 | return Vector3(float(s[0]), float(s[1]), float(s[2]))
183 |
184 | static func expect_int(v : StringName) -> int :
185 | return v.to_int()
186 |
187 | static func expect_float(v : StringName) -> int :
188 | return v.to_float()
189 |
--------------------------------------------------------------------------------
/addons/qmapbsp/resource/map_import_error.gd:
--------------------------------------------------------------------------------
1 | @icon("res://addons/qmapbsp/icon/icon_node3d_error.svg")
2 | extends Node3D
3 | class_name QmapbspMapImportError
4 |
5 | ## Created instead of an actual result when the importing progress was failed
6 |
7 | @export var error : String
8 | @export_multiline var error_data : String
9 |
--------------------------------------------------------------------------------
/addons/qmapbsp/resource/map_import_error.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://by31r16mgbeh7"]
2 |
3 | [ext_resource type="Script" path="res://addons/qmapbsp/resource/map_import_error.gd" id="1_kgybe"]
4 |
5 | [node name="!!! ERROR !!!" type="Node3D"]
6 | script = ExtResource("1_kgybe")
7 |
--------------------------------------------------------------------------------
/addons/qmapbsp/resource/quake1_style_shader.gd:
--------------------------------------------------------------------------------
1 | extends Shader
2 | class_name QmapbspQuake1StyleShader
3 |
4 | # After modifying these variables,
5 | # "rebuild_shader" must be called after it
6 | # VVV - VVV
7 | var texture_filter := BaseMaterial3D.TextureFilter.TEXTURE_FILTER_LINEAR
8 |
9 | enum TextureMode { NORMAL, UNSHADED, LIGHTMAP, NORMALMAP }
10 | var texture_mode := TextureMode.NORMAL
11 |
12 | ## use "true" if you don't desire to have any lights on your scene.
13 | ## It's surely faster than the "false" option
14 | ## but it will have literally NO lights except the lightmaps.
15 | ## That means you can't have gunfire flash effects like
16 | ## the Quake's original has.
17 | ## (This project used "true" because
18 | ##it's intended to be a demonstration of lightmap rendering)
19 | var fully_no_lights := true
20 |
21 | # ^^^ - ^^^
22 |
23 | func rebuild_shader() -> void :
24 | var albedo : String
25 | var texture_albedo_hint : String
26 |
27 | match texture_filter :
28 | BaseMaterial3D.TextureFilter.TEXTURE_FILTER_NEAREST :
29 | texture_albedo_hint = ", filter_nearest"
30 |
31 | albedo = get_albedo()
32 |
33 | ######################################################
34 |
35 | code = get_base_code().format({
36 | 'render_mode' : make_render_mode(),
37 | 'texture_albedo_hint' : texture_albedo_hint,
38 | 'albedo' : albedo,
39 | })
40 | func make_render_mode() -> String :
41 | if fully_no_lights :
42 | return "render_mode unshaded, specular_disabled;"
43 | return "render_mode specular_disabled;"
44 |
45 | func get_albedo() -> String :
46 | match texture_mode :
47 | TextureMode.NORMAL :
48 | if fully_no_lights :
49 | return """
50 | ALBEDO = color * mix(
51 | lightmap(UV2),
52 | 4.0f,
53 | texture(texf[frame], UV).r
54 | ) * lmboost;
55 | """
56 | return """
57 | ALBEDO = color;
58 | AO = mix(
59 | lightmap(UV2),
60 | 4.0f,
61 | texture(texf[frame], UV).r
62 | ) * lmboost;
63 | """
64 | TextureMode.UNSHADED :
65 | return "ALBEDO = color;"
66 |
67 | TextureMode.LIGHTMAP :
68 | return "ALBEDO = vec3(lightmap(UV2));"
69 |
70 |
71 | TextureMode.NORMALMAP :
72 | return "ALBEDO = NORMAL;"
73 | return ""
74 |
75 | func get_base_code() -> String : return """
76 | shader_type spatial;
77 | {render_mode}
78 |
79 | uniform sampler2D tex[20] : source_color{texture_albedo_hint};
80 | uniform sampler2D texf[20];
81 | uniform int frame_count = 1;
82 | uniform int frame_count2 = 1;
83 |
84 | global uniform sampler2D lightstyle_tex : filter_nearest, source_color; // 64x1
85 | global uniform float lmboost = 1.0f;
86 | global uniform sampler2D lightmap_tex;
87 |
88 | instance uniform bool use_alternate = false; // use +a +b +c ... instead of +0 +1 +2 ...
89 |
90 | varying flat int lstyles;
91 | varying float lwidth;
92 | varying float lights[4];
93 | varying float lx2pix;
94 | varying flat int frame;
95 | varying flat int frame_plus;
96 |
97 | void vertex() {
98 | lights = {
99 | texture(lightstyle_tex, vec2(CUSTOM1.x, 0.0f)).r,
100 | texture(lightstyle_tex, vec2(CUSTOM1.y, 0.0f)).r,
101 | texture(lightstyle_tex, vec2(CUSTOM1.z, 0.0f)).r,
102 | texture(lightstyle_tex, vec2(CUSTOM1.w, 0.0f)).r
103 | };
104 | lstyles = int(CUSTOM0.y);
105 | lwidth = CUSTOM0.z;
106 | lx2pix = CUSTOM0.w;
107 | frame = int(TIME * 5.0f) % (use_alternate ? frame_count2 : frame_count);
108 | frame_plus = use_alternate ? frame_count : 0;
109 | }
110 |
111 | float lightmap(in vec2 uv2) {
112 | float lighttotal = 0.0;
113 | float lcursor = 0.0;
114 | for (int l = 0; l < 4; l++) {
115 | if ((lstyles & (1 << l)) == 0) continue;
116 | lighttotal += texture(lightmap_tex, uv2 + vec2(lcursor, 0.0)).x * lights[l];
117 | lcursor += lwidth + lx2pix;
118 | }
119 | return lighttotal;
120 | }
121 |
122 | void fragment() {
123 | vec3 color = texture(tex[frame + frame_plus], UV).xyz;
124 | ROUGHNESS = 1.0f;
125 | METALLIC = 0.0f;
126 |
127 | {albedo}
128 | }
129 |
130 | """
131 |
--------------------------------------------------------------------------------
/addons/qmapbsp/resource/user_config.gd:
--------------------------------------------------------------------------------
1 | extends Resource
2 | class_name QmapbspUserConfig
3 |
4 | ## An individual user configuration.
5 | ## When worked on the team. This should be ignored via [code].gitignore[/code]
6 |
7 | @export var compilation_workflow : QmapbspCompilationWorkflow
8 | @export_group("Trenchbroom", "tb_")
9 | @export_global_dir var tb_path : String
10 |
--------------------------------------------------------------------------------
/addons/qmapbsp/texture/missing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/addons/qmapbsp/texture/missing.png
--------------------------------------------------------------------------------
/addons/qmapbsp/texture/missing.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://2d52247jrmj6"
6 | path.s3tc="res://.godot/imported/missing.png-2d9086912efaa308b0888b66ada0bf3b.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://addons/qmapbsp/texture/missing.png"
15 | dest_files=["res://.godot/imported/missing.png-2d9086912efaa308b0888b66ada0bf3b.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/addons/qmapbsp/texture/missing.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://ykmjbk4bx4vg"]
2 |
3 | [ext_resource type="Texture2D" uid="uid://2d52247jrmj6" path="res://addons/qmapbsp/texture/missing.png" id="1_ld8ep"]
4 |
5 | [resource]
6 | albedo_texture = ExtResource("1_ld8ep")
7 | texture_filter = 2
8 |
--------------------------------------------------------------------------------
/addons/qmapbsp/trenchbroom/game_config_inspector.gd:
--------------------------------------------------------------------------------
1 | extends EditorInspectorPlugin
2 | class_name QmapbspTrenchbroomGameConfigResourceInspectorPlugin
3 |
4 | func _can_handle(o) -> bool :
5 | return o is QmapbspTrenchbroomGameConfigResource
6 |
7 | func _parse_begin(o) :
8 | var pscene := preload("sub/game_config_inspector.tscn")
9 | var insp : QmapbspTrenchbroomGameConfigResourceInspector = (
10 | pscene.instantiate()
11 | )
12 | insp.game_config = o
13 | add_custom_control(insp)
14 |
--------------------------------------------------------------------------------
/addons/qmapbsp/trenchbroom/game_config_maker.gd:
--------------------------------------------------------------------------------
1 | extends RefCounted
2 | class_name QmapbspTrenchbroomGameConfigMaker
3 |
--------------------------------------------------------------------------------
/addons/qmapbsp/trenchbroom/map_config.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends Resource
3 | class_name QmapbspTrenchbroomMapConfig
4 |
5 | ## If [code]false[/code], the importer will omit internal lightmap loading.
6 | ## and unwraps UV2.
7 | @export var use_bsp_lightmap : bool = false
8 | ## The texel size used for baking UV2.
9 | ## A lower value gives high-resolution lightmaps.
10 | ## But also leads to bigger lightmap size and more baking time.
11 | @export var lightmap_texel : float = 1.0
12 | ## The ratio of Quake 1 unit per Godot unit.
13 | @export var inverse_scale_factor : float = 32.0
14 | ## The threshold for splitting meshes apart.
15 | ## When using occlusion culling, A lower value is much better.
16 | @export var mesh_splitting_size : float = 32.0 # godot unit
17 | ## The placeholder material. This applies to faces that can't load any textures.
18 | @export var default_material : Material
19 | ## The placeholder material texture size.
20 | @export var default_material_texture_size : Vector2i = Vector2i(64, 64)
21 | ## Bakes navigation meshes based on this [NavigationMesh].
22 | ## The operation will skip if it's empty.
23 | @export var navmesh_template : NavigationMesh
24 | ## The method for constructing collision shapes from a whole map. Useful when importing individual models. Leave "Optimal" if importing for level maps
25 | @export_enum("Optimal", "Convex Hull", "Convex Hull Simple", "Trimesh", "Disabled") var collsion_constructing_method : int = 0
26 | ## Loads point file as [Path3D] if it exists.
27 | ## Useful for interior maps. This can help you to locate leaks in your map. And gives an extra face clipping.
28 | @export var load_point_file : bool = false
29 | ## Removes unnecessary points in the point file.
30 | ## But still can lead you to the leak properly.
31 | @export var simplify_point_files : bool = true
32 | ## The maximum angle (in degrees) between faces for grouping smooth groups.
33 | ## Enter [code]-1[/code] to disable this feature.
34 | @export var auto_smooth_max_angle : float = 30
35 |
36 | @export_group("Occluders", "occ_")
37 | ## Bakes occluders from the worldspawn entity.
38 | @export var occ_bake_occluders : bool = false
39 | ## The amount for shrinking occluders.
40 | ## This method will reduce an over-occlusion that could make an inaccurate rasterizing.
41 | ## Must not be lesser than the grid size (like in Trenchbroom) divided with the inverse scale factor.
42 | ## Otherwise, it will shrink through to the opposite side and make it grow rather than shrinking.
43 | @export var occ_shrink_amount : float = 0.5 # grid 16
44 |
--------------------------------------------------------------------------------
/addons/qmapbsp/trenchbroom/map_importer.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends EditorImportPlugin
3 | class_name QmapbspTrenchbroomMapImporterPlugin
4 |
5 | var editor_plugin : EditorPlugin
6 |
7 | func _editor_plugin(p : EditorPlugin) -> void :
8 | editor_plugin = p
9 |
10 | func _get_importer_name() -> String :
11 | return "qmapbsp.trenchbroom_map"
12 |
13 | func _get_visible_name() -> String :
14 | return "Qmapbsp Trenchbroom"
15 |
16 | func _get_recognized_extensions() -> PackedStringArray :
17 | return PackedStringArray(["map"])
18 |
19 | func _get_save_extension() -> String :
20 | return "scn"
21 |
22 | func _get_resource_type() -> String :
23 | return "PackedScene"
24 |
25 | func _get_priority() -> float :
26 | return 1.0
27 |
28 | func _get_import_order() -> int :
29 | return 0
30 |
31 | func _can_import_threaded() -> bool :
32 | return false
33 |
34 | ###############################
35 | # Presets
36 |
37 | func _get_preset_count() -> int :
38 | return 1
39 |
40 | func _get_preset_name(i : int) -> String :
41 | return "Default"
42 |
43 | ###############################
44 |
45 | func _get_import_options(p : String, i : int) -> Array[Dictionary] :
46 | return [
47 | {
48 | "name" : "game_config_path",
49 | "default_value" : "",
50 | "property_hint" : PROPERTY_HINT_FILE,
51 | "hint_string" : "*.tres,*.res"
52 | },
53 | {
54 | "name" : "map_config_path",
55 | "default_value" : "",
56 | "property_hint" : PROPERTY_HINT_FILE,
57 | "hint_string" : "*.tres,*.res"
58 | }
59 | ]
60 |
61 | func _get_option_visibility(path: String, option_name: StringName, options: Dictionary) -> bool :
62 | return true
63 |
64 | const MIE := "res://addons/qmapbsp/resource/map_import_error.tscn"
65 | func _save_error(save_path : String, error : StringName, error_ret : Array) :
66 | var mie := preload(MIE).instantiate()
67 | mie.error = error
68 | var strs : PackedStringArray
69 | strs.resize(error_ret.size())
70 | for i in strs.size() :
71 | strs[i] = str(error_ret[i])
72 | mie.error_data = '\n'.join(strs)
73 |
74 | var pscene := PackedScene.new()
75 | pscene.pack(mie)
76 | var filename := save_path + "." + _get_save_extension()
77 | return ResourceSaver.save(pscene, filename)
78 |
79 |
80 | func _import(
81 | source_file : String,
82 | save_path : String,
83 | options : Dictionary,
84 | platform_variants : Array[String],
85 | gen_files : Array[String]
86 | ) -> Error :
87 | var file := FileAccess.open(source_file, FileAccess.READ)
88 | if !file :
89 | return FileAccess.get_open_error()
90 |
91 | var wis : QmapbspWorldImporterTrenchbroom
92 | var bsp_path : String
93 | var t_path : String = options.get("game_config_path", "")
94 | if t_path.is_empty() :
95 | printerr("No Trenchbroom game config file")
96 | return _save_error(save_path, &"NO_TRENCHBROOM_GAMECFG_FILE", [])
97 |
98 | var gamecfg : QmapbspTrenchbroomGameConfigResource = load(t_path)
99 | if !gamecfg :
100 | printerr("Cannot load Trenchbroom game config file")
101 | return _save_error(save_path, &"CANNOT_LOAD_TRENCHBROOM_GAMECFG_FILE", [])
102 | t_path = options.get("map_config_path", "")
103 | var mapcfg : QmapbspTrenchbroomMapConfig
104 | if !t_path.is_empty() :
105 | mapcfg = load(t_path)
106 |
107 |
108 | if gamecfg.custom_trenchbroom_world_importer :
109 | wis = gamecfg.custom_trenchbroom_world_importer.new()
110 | else :
111 | wis = QmapbspWorldImporterTrenchbroom.new()
112 |
113 | if mapcfg :
114 | wis.map_config = mapcfg
115 | else :
116 | wis.map_config = gamecfg.global_map_config
117 | if !wis.map_config :
118 | wis.map_config = QmapbspTrenchbroomMapConfig.new()
119 | wis.game_config = gamecfg
120 | bsp_path = wis._compile_bsp(source_file)
121 |
122 | var editor_itf := editor_plugin.get_editor_interface()
123 | var node := Node3D.new()
124 | node.name = &'map'
125 | wis.root = node
126 | wis.owner = node
127 | node.hide()
128 | editor_itf.get_base_control().add_child(node)
129 |
130 | if bsp_path.is_empty() :
131 | printerr("No BSP file")
132 | node.free()
133 | return _save_error(save_path, &"NO_BSP_FILE_COMPILED", [])
134 |
135 | var ret : Array
136 |
137 | var err := wis.begin_load_absolute(
138 | bsp_path, source_file, ret
139 | )
140 |
141 | if err != StringName() :
142 | node.free()
143 | return _save_error(save_path, err, ret)
144 |
145 | err = StringName()
146 | while true :
147 | var reti := wis.poll()
148 | if reti == &'END' :
149 | break
150 | elif reti != StringName() :
151 | err = reti
152 | break
153 |
154 | node = wis.root
155 |
156 | if err != StringName() :
157 | printerr(err)
158 | node.free()
159 | return _save_error(save_path, err, [])
160 |
161 | #######################################################
162 |
163 | var pscene := PackedScene.new()
164 | node.show()
165 | if pscene.pack(node) :
166 | node.free()
167 | return _save_error(save_path, &'CANNOT_SAVE_SCENE', [])
168 |
169 | node.free()
170 | var filename := save_path + "." + _get_save_extension()
171 | return ResourceSaver.save(pscene, filename)
172 |
--------------------------------------------------------------------------------
/addons/qmapbsp/trenchbroom/sub/game_config_inspector.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends VBoxContainer
3 | class_name QmapbspTrenchbroomGameConfigResourceInspector
4 |
5 | var game_config : QmapbspTrenchbroomGameConfigResource
6 |
7 | var done_streak : int = 0
8 |
9 | func _on_export_pressed() :
10 | game_config.export_cfg()
11 | done_streak += 1
12 | if done_streak == 1 :
13 | $export.text = "Done !"
14 | else :
15 | $export.text = "Done (%d)" % done_streak
16 |
--------------------------------------------------------------------------------
/addons/qmapbsp/trenchbroom/sub/game_config_inspector.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://bxnhoo3bpbpnx"]
2 |
3 | [ext_resource type="Script" path="res://addons/qmapbsp/trenchbroom/sub/game_config_inspector.gd" id="1_4asoy"]
4 |
5 | [node name="insp" type="VBoxContainer"]
6 | anchors_preset = 15
7 | anchor_right = 1.0
8 | anchor_bottom = 1.0
9 | grow_horizontal = 2
10 | grow_vertical = 2
11 | script = ExtResource("1_4asoy")
12 |
13 | [node name="export" type="Button" parent="."]
14 | layout_mode = 2
15 | text = "Export configs to Trenchbroom"
16 |
17 | [connection signal="pressed" from="export" to="." method="_on_export_pressed"]
18 |
--------------------------------------------------------------------------------
/addons/qmapbsp/util/clipper.gd:
--------------------------------------------------------------------------------
1 | extends RefCounted
2 | class_name QmapbspClipper
3 |
4 | # from wootguy's bspguy
5 | # 670fca408b7d376b28da97daa323aade2ea649d7 src/editor/Clipper.cpp
6 |
7 | const TESTMAX := 5555
8 | const EPS := 0.000001
9 |
10 | var vertices : PackedVector3Array
11 | var edges : Array[Vector4i] # [v0, v1, f0, f1]...
12 | var faces : Array[Array] # [edge_ids : PIA32, normal : Vector3]...
13 |
14 | var verts_v : PackedByteArray
15 | var edges_v : PackedByteArray
16 | var faces_v : PackedByteArray
17 |
18 | var verts_o : PackedInt32Array
19 |
20 | func clip_plane(p : Plane) -> bool :
21 | p.normal *= -1
22 | p.d *= -1
23 | var res := clip_vertices(p)
24 | if res == -1 : return true
25 | if res == 1 : return false
26 | clip_edges(p)
27 | clip_faces(p)
28 | return false
29 |
30 | func clip_planes(planes : Array[Plane]) -> void :
31 | for p in planes :
32 | if clip_plane(p) : break
33 | filter_and_clean()
34 |
35 | func filter_and_clean() -> void :
36 | var vvv : PackedVector3Array
37 | for i in faces.size() :
38 | if !faces_v[i] : continue
39 | for j in faces[i][0] :
40 | var edge : Vector4i = edges[j]
41 | if verts_v[edge.x] : vvv.append(vertices[edge.x])
42 | if verts_v[edge.y] : vvv.append(vertices[edge.y])
43 |
44 | vertices = vvv
45 |
46 | verts_v.clear()
47 | edges_v.clear()
48 | faces_v.clear()
49 | verts_o.clear()
50 |
51 | func clip_vertices(plane : Plane) -> int :
52 | var pos := 0
53 | var neg := 0
54 | for i in vertices.size() :
55 | if verts_v[i] == 0 : continue
56 |
57 | var dist := plane.distance_to(vertices[i])
58 |
59 | if dist >= EPS : pos += 1
60 | elif dist < EPS :
61 | neg += 1
62 | verts_v[i] = 0
63 | if neg == 0 : return 1
64 | if pos == 0 : return -1
65 | return 0
66 |
67 | func clip_edges(plane : Plane) -> void :
68 | for i in edges.size() :
69 | var e : Vector4i = edges[i]
70 | var v0 := e[0]
71 | var v1 := e[1]
72 |
73 | if edges_v[i] :
74 | var d0 := plane.distance_to(vertices[v0])
75 | var d1 := plane.distance_to(vertices[v1])
76 |
77 | if d0 <= 0 and d1 <= 0 :
78 | for k in 2 :
79 | var face : Array = faces[e[2 + k]]
80 | var f : int = face[0].find(i)
81 | if f != -1 :
82 | face[0].remove_at(f)
83 | if face[0].is_empty() :
84 | faces_v[e[2 + k]] = 0
85 | edges_v[i] = 0
86 | continue
87 | if d0 >= 0 and d1 >= 0 :
88 | continue
89 | var t := d0 / (d0 - d1)
90 | var v := vertices[v0].lerp(vertices[v1], t)
91 | vertices.append(v)
92 | verts_v.append(1)
93 | verts_o.append(0)
94 | if d0 > 0 :
95 | e[1] = vertices.size() - 1
96 | else :
97 | e[0] = vertices.size() - 1
98 | edges[i] = e
99 |
100 | func clip_faces(plane : Plane) -> void :
101 | var closef := [PackedInt32Array(), plane.normal * -1]
102 | var fsize := faces.size()
103 | for i in fsize :
104 | if faces_v[i] == 1 :
105 | var face : Array = faces[i]
106 | for j in face[0].size() :
107 | var edge : Vector4i = edges[face[0][j]]
108 | verts_o[edge[0]] = 0
109 | verts_o[edge[1]] = 0
110 | var ref := PackedInt32Array([-1, -1])
111 |
112 | if get_open_polyline(face, ref) :
113 | var nedge := Vector4i(ref[0], ref[1], i, fsize)
114 | edges.append(nedge)
115 | edges_v.append(1)
116 | face[0].append(edges.size() - 1)
117 | closef[0].append(edges.size() - 1)
118 | faces.append(closef)
119 | faces_v.append(1)
120 |
121 | func get_open_polyline(face : Array, ref : PackedInt32Array) -> bool :
122 | for i in face[0].size() :
123 | var edge : Vector4i = edges[face[0][i]]
124 | verts_o[edge[0]] += 1
125 | verts_o[edge[1]] += 1
126 | for i in face[0].size() :
127 | var edge : Vector4i = edges[face[0][i]]
128 | var v0 := edge[0]
129 | var v1 := edge[1]
130 | if verts_o[v0] == 1 :
131 | if ref[0] == -1 :
132 | ref[0] = v0
133 | elif ref[1] == -1 :
134 | ref[1] = v0
135 | if verts_o[v1] == 1 :
136 | if ref[0] == -1 :
137 | ref[0] = v1
138 | elif ref[1] == -1 :
139 | ref[1] = v1
140 | return ref[0] != -1 and ref[1] != -1
141 |
142 | func begin(bound := AABB(
143 | -Vector3(TESTMAX, TESTMAX, TESTMAX),
144 | Vector3(TESTMAX, TESTMAX, TESTMAX) * 2.0,
145 | )) -> void :
146 | vertices = [
147 | bound.position,
148 | Vector3(bound.end.x, bound.position.y, bound.position.z),
149 | Vector3(bound.end.x, bound.end.y, bound.position.z),
150 | Vector3(bound.position.x, bound.end.y, bound.position.z),
151 |
152 | Vector3(bound.position.x, bound.position.y, bound.end.z),
153 | Vector3(bound.end.x, bound.position.y, bound.end.z),
154 | bound.end,
155 | Vector3(bound.position.x, bound.end.y, bound.end.z),
156 | ]
157 | verts_v.resize(8)
158 | verts_v.fill(1)
159 | verts_o.resize(8)
160 | verts_o.fill(0.0)
161 |
162 | edges = [
163 | Vector4i(0, 1, 0, 5),
164 | Vector4i(0, 4, 0, 2),
165 | Vector4i(4, 5, 0, 4),
166 | Vector4i(5, 1, 0, 3),
167 |
168 | Vector4i(3, 2, 1, 5),
169 | Vector4i(3, 7, 1, 2),
170 | Vector4i(6, 7, 1, 4),
171 | Vector4i(2, 6, 1, 3),
172 |
173 | Vector4i(0, 3, 2, 5),
174 | Vector4i(4, 7, 2, 4),
175 | Vector4i(1, 2, 3, 5),
176 | Vector4i(5, 6, 3, 4)
177 | ]
178 | edges_v.resize(12)
179 | edges_v.fill(1)
180 | faces = [
181 | [PackedInt32Array([0, 1, 2, 3]), Vector3( 0, -1, 0)],
182 | [PackedInt32Array([4, 5, 6, 7]), Vector3( 0, 1, 0)],
183 | [PackedInt32Array([1, 5, 8, 9]), Vector3(-1, 0, 0)],
184 | [PackedInt32Array([3, 7, 10, 11]), Vector3( 1, 0, 0)],
185 | [PackedInt32Array([2, 6, 9, 11]), Vector3( 0, 0, 1)],
186 | [PackedInt32Array([0, 4, 8, 10]), Vector3( 0, 0, -1)]
187 | ]
188 | faces_v.resize(6)
189 | faces_v.fill(1)
190 |
--------------------------------------------------------------------------------
/addons/qmapbsp/util/imagepacker.gd:
--------------------------------------------------------------------------------
1 | extends RefCounted
2 | class_name QmapbspImagePacker
3 |
4 | var stack_size : PackedVector2Array
5 | var stack_data : Array[Image]
6 |
7 | func add(size : Vector2i, im : Image) -> int :
8 | stack_size.append(size)
9 | stack_data.append(im)
10 | return stack_size.size() - 1
11 |
12 | func commit(format : int, pos_list : PackedVector2Array, po2 : bool = true) -> Image :
13 | if stack_size.is_empty() : return null
14 | var dict := Geometry2D.make_atlas(stack_size)
15 | var points : PackedVector2Array = dict['points']
16 | var size : Vector2i = dict['size']
17 | if po2 :
18 | size = Vector2i(nearest_po2(size.x), nearest_po2(size.y))
19 | var im := Image.create(size.x, size.y, false, format)
20 | for i in points.size() :
21 | im.blit_rect(stack_data[i], Rect2i(Vector2(), stack_size[i]), points[i])
22 | pos_list.append_array(points)
23 | return im
24 |
--------------------------------------------------------------------------------
/addons/qmapbsp/util/type_prop.gd:
--------------------------------------------------------------------------------
1 | extends Object
2 | class_name QmapbspTypeProp
3 |
4 | static func var_to_prop(v) -> Array :
5 | var vv : String
6 | var typev : String = "string"
7 | match typeof(v) :
8 | TYPE_OBJECT, TYPE_MAX :
9 | printerr("Cannot export objects")
10 | return ["", "string"]
11 | TYPE_INT : typev = "integer"
12 | TYPE_FLOAT : typev = "float"
13 | TYPE_STRING, TYPE_STRING_NAME : typev = "string"
14 | TYPE_BOOL : typev = "boolean"
15 | TYPE_NODE_PATH : typev = "string"
16 | TYPE_COLOR : typev = "string"
17 |
18 | match typeof(v) :
19 | TYPE_COLOR :
20 | vv = var_to_str(String("%d %d %d" % [v.r * 255, v.g * 255, v.b * 255]))
21 | TYPE_STRING_NAME, TYPE_NODE_PATH :
22 | vv = var_to_str(String(vv))
23 | _ :
24 | vv = var_to_str(v)
25 | return [vv, typev]
26 |
27 | static func prop_to_var(p : StringName, known_type : int = -1) :
28 | match known_type :
29 | TYPE_COLOR :
30 | var S := p.split(' ')
31 | if S.size() < 3 : return Color()
32 | return Color(
33 | int(S[0]) / 255.0,
34 | int(S[1]) / 255.0,
35 | int(S[2]) / 255.0,
36 | )
37 | TYPE_STRING :
38 | return String(p)
39 | TYPE_STRING_NAME :
40 | return p
41 | TYPE_NODE_PATH :
42 | return NodePath(p)
43 | return str_to_var(p)
44 |
45 | #TYPE_AABB, TYPE_ARRAY, TYPE_BASIS,
46 | #TYPE_CALLABLE, TYPE_COLOR, TYPE_DICTIONARY,
47 | #TYPE_NIL, TYPE_NODE_PATH,
48 | #TYPE_PACKED_BYTE_ARRAY,
49 | #TYPE_PACKED_COLOR_ARRAY,
50 | #TYPE_PACKED_FLOAT32_ARRAY,
51 | #TYPE_PACKED_FLOAT64_ARRAY,
52 | #TYPE_PACKED_INT32_ARRAY,
53 | #TYPE_PACKED_INT64_ARRAY,
54 | #TYPE_PACKED_STRING_ARRAY,
55 | #TYPE_PACKED_VECTOR2_ARRAY,
56 | #TYPE_PACKED_VECTOR3_ARRAY,
57 | #TYPE_PLANE,
58 | #TYPE_PROJECTION,
59 | #TYPE_QUATERNION,
60 | #TYPE_RECT2,
61 | #TYPE_RECT2I,
62 | #TYPE_RID,
63 | #TYPE_SIGNAL,
64 | #TYPE_STRING,
65 | #TYPE_STRING_NAME,
66 | #TYPE_TRANSFORM2D,
67 | #TYPE_TRANSFORM3D,
68 | #TYPE_VECTOR2,
69 | #TYPE_VECTOR2I,
70 | #TYPE_VECTOR3,
71 | #TYPE_VECTOR3I,
72 | #TYPE_VECTOR4,
73 | #TYPE_VECTOR4I,
74 |
--------------------------------------------------------------------------------
/icon.svg.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://ddgo0qldm1jtp"
6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://icon.svg"
14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=true
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=0
35 | svg/scale=1.0
36 | editor/scale_with_editor_scale=false
37 | editor/convert_colors_with_editor_theme=false
38 |
--------------------------------------------------------------------------------
/project.godot:
--------------------------------------------------------------------------------
1 | ; Engine configuration file.
2 | ; It's best edited using the editor UI and not directly,
3 | ; since the parameters that go here are not all obvious.
4 | ;
5 | ; Format:
6 | ; [section] ; section goes between []
7 | ; param=value ; assign values to parameters
8 |
9 | config_version=5
10 |
11 | [application]
12 |
13 | config/name="gdQmapbsp"
14 | run/main_scene="res://quake1_example/hub.tscn"
15 | config/features=PackedStringArray("4.3", "Mobile")
16 | config/icon="res://icon.svg"
17 |
18 | [debug]
19 |
20 | gdscript/warnings/enable=false
21 | shapes/collision/shape_color=Color(4, 0, 0, 1)
22 | shapes/collision/contact_color=Color(1, 0.2, 0.101961, 0.8)
23 |
24 | [display]
25 |
26 | window/size/viewport_width=1280
27 | window/size/viewport_height=720
28 | window/stretch/mode="canvas_items"
29 | window/stretch/aspect="expand"
30 |
31 | [editor_plugins]
32 |
33 | enabled=PackedStringArray("res://addons/qmapbsp/plugin.cfg")
34 |
35 | [input]
36 |
37 | q1_move_forward={
38 | "deadzone": 0.5,
39 | "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)
40 | ]
41 | }
42 | q1_move_left={
43 | "deadzone": 0.5,
44 | "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)
45 | ]
46 | }
47 | q1_move_right={
48 | "deadzone": 0.5,
49 | "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)
50 | ]
51 | }
52 | q1_move_back={
53 | "deadzone": 0.5,
54 | "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)
55 | ]
56 | }
57 | q1_jump={
58 | "deadzone": 0.5,
59 | "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)
60 | ]
61 | }
62 | q1_toggle_noclip={
63 | "deadzone": 0.5,
64 | "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":86,"key_label":0,"unicode":118,"location":0,"echo":false,"script":null)
65 | ]
66 | }
67 | trenchbroom_test_move_forward={
68 | "deadzone": 0.5,
69 | "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)
70 | ]
71 | }
72 | trenchbroom_test_move_left={
73 | "deadzone": 0.5,
74 | "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)
75 | ]
76 | }
77 | trenchbroom_test_move_right={
78 | "deadzone": 0.5,
79 | "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)
80 | ]
81 | }
82 | trenchbroom_test_move_back={
83 | "deadzone": 0.5,
84 | "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)
85 | ]
86 | }
87 | trenchbroom_test_jump={
88 | "deadzone": 0.5,
89 | "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)
90 | ]
91 | }
92 | trenchbroom_test_toggle_noclip={
93 | "deadzone": 0.5,
94 | "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":86,"key_label":0,"unicode":118,"location":0,"echo":false,"script":null)
95 | ]
96 | }
97 |
98 | [layer_names]
99 |
100 | 3d_physics/layer_1="bsp"
101 | 3d_physics/layer_2="bsp_water"
102 | 3d_physics/layer_3="clip"
103 | 3d_physics/layer_4="player"
104 | 3d_physics/layer_29="LEAVES"
105 |
106 | [physics]
107 |
108 | common/physics_ticks_per_second=120
109 |
110 | [rendering]
111 |
112 | global_illumination/sdfgi/probe_ray_count=0
113 | global_illumination/sdfgi/frames_to_converge=3
114 | occlusion_culling/use_occlusion_culling=true
115 |
116 | [shader_globals]
117 |
118 | lmboost={
119 | "type": "float",
120 | "value": 4.0
121 | }
122 | lightstyle_tex={
123 | "type": "sampler2D",
124 | "value": ""
125 | }
126 | lightmap_tex={
127 | "type": "sampler2D",
128 | "value": ""
129 | }
130 |
--------------------------------------------------------------------------------
/quake1_example/README.md:
--------------------------------------------------------------------------------
1 | # Qmapbsp Quake1 Example
2 |
3 | This folder contains a simple Quake1 BSP viewer, PAK/WAD explorers, a basic Quake movement character, and some entity implementations like `trigger_*` and `func_*`
4 |
5 | This viewer requires PAK files and **MAP files of maps** inside the PAK to load.
6 |
7 | 
8 |
9 | Tested on some ID Software's Quake (Shareware) maps. Not all entities are implemented yet. And remember that you have to run this viewer on the full project. Because it contains bound keys for the player character.
10 |
11 | ## Usage
12 | Run `hub.tscn` then enter your Mod and MAP paths. And hit `Load`. The tree will display a file structure inside the PAK files. You can click on some resources to play (\*.bsp, \*.wav) or view (\*.wad, \*.lmp).
13 |
--------------------------------------------------------------------------------
/quake1_example/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/quake1_example/background.png
--------------------------------------------------------------------------------
/quake1_example/background.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dwa5a34uawid4"
6 | path="res://.godot/imported/background.png-992fda8eb28af844f048b57ee84f4962.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://quake1_example/background.png"
14 | dest_files=["res://.godot/imported/background.png-992fda8eb28af844f048b57ee84f4962.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/quake1_example/class/_ambient.gd:
--------------------------------------------------------------------------------
1 | extends AudioStreamPlayer3D
2 | class_name QmapbspQuakeAmbient
3 |
4 | var viewer : QmapbspQuakeViewer
5 | var props : Dictionary
6 | func _qmapbsp_is_brush_visible() -> bool : return false
7 | func _get_properties(dict : Dictionary) : props = dict
8 |
9 | signal emit_message_state(m : String, show : bool)
10 |
11 | func _show_message_start(msg : String) : return
12 | func _show_message_end() : return
13 |
14 | func _ready() :
15 | viewer = get_meta(&'viewer')
16 |
17 | func _map_ready() :
18 | unit_size = 1.0
19 | stream = viewer.hub.load_audio(_audiopath())
20 | finished.connect(func() : play())
21 | play()
22 |
23 | func _audiopath() -> String :
24 | return "misc/null.wav"
25 |
--------------------------------------------------------------------------------
/quake1_example/class/_func.gd:
--------------------------------------------------------------------------------
1 | extends AnimatableBody3D
2 | class_name QmapbspQuakeFunctionBrush
3 |
4 | var props : Dictionary
5 | func _get_properties(dict : Dictionary) : props = dict
6 |
7 | var aabb : AABB
8 |
9 | func _gen_aabb() :
10 | #aabb.position = global_position
11 | for m in get_children() :
12 | if m is GeometryInstance3D :
13 | aabb = aabb.merge(m.get_aabb())
14 | aabb.position += global_position
15 |
--------------------------------------------------------------------------------
/quake1_example/class/_trigger.gd:
--------------------------------------------------------------------------------
1 | extends Area3D
2 | class_name QmapbspQuakeTrigger
3 | var props : Dictionary
4 | var v : QmapbspQuakeViewer
5 | func _qmapbsp_is_brush_visible() -> bool : return false
6 | func _get_properties(dict : Dictionary) : props = dict
7 |
8 | signal emit_message_state(m : String, show : bool)
9 |
10 | func _show_message_start(msg : String) :
11 | emit_message_state.emit(msg, true)
12 |
13 | func _show_message_end() :
14 | emit_message_state.emit('', false)
15 |
16 | func _init() -> void :
17 | monitorable = false
18 | collision_layer = 0b1000
19 | collision_mask = 0b1000
20 |
21 | func _ready() :
22 | v = get_meta(&'viewer')
23 | body_entered.connect(_bo_en)
24 | body_exited.connect(_bo_ex)
25 |
26 | func _bo_en(b : Node3D) :
27 | _message()
28 | _trigger(b)
29 |
30 | func _bo_ex(b : Node3D) :
31 | _show_message_end()
32 |
33 | func _message() :
34 | var message = props.get('message', null)
35 | if message is StringName :
36 | _show_message_start(message)
37 |
38 | func _trigger(b : Node3D) :
39 | var delay : float = props.get('delay', '0').to_float()
40 | if delay > 0 :
41 | get_tree().create_timer(delay, false).timeout.connect(
42 | _trigger_now.bind(b)
43 | )
44 | else :
45 | _trigger_now(b)
46 |
47 | func _trigger_now(b : Node3D) :
48 | var target : String = props.get("target", '')
49 | if !target.is_empty() :
50 | v.trigger_targets(target, b)
51 | var killtarget : String = props.get("killtarget", '')
52 | if !killtarget.is_empty() :
53 | v.killtarget(killtarget)
54 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_comp_hum.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientCompHum
3 |
4 | func _audiopath() -> String :
5 | return "ambience/comp1.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_drip.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientDrip
3 |
4 | func _audiopath() -> String :
5 | return "ambience/drip1.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_drone.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientDrone
3 |
4 | func _audiopath() -> String :
5 | return "ambience/drone6.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_flouro_buzz.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientFlouroBuzz
3 |
4 | func _audiopath() -> String :
5 | return "ambience/fl_hum1.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_light_buzz.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientLightBuzz
3 |
4 | func _audiopath() -> String :
5 | return "ambience/buzz1.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_suck_wind.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientSuckWind
3 |
4 | func _audiopath() -> String :
5 | return "ambience/wind2.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_swamp1.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientSwamp1
3 |
4 | func _audiopath() -> String :
5 | return "ambience/swamp1.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_swamp2.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientSwamp2
3 |
4 | func _audiopath() -> String :
5 | return "ambience/swamp2.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/ambient_thunder.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeAmbient
2 | class_name QmapbspQuakeAmbientThunder
3 |
4 | func _audiopath() -> String :
5 | return "ambience/thunder1.wav"
6 |
--------------------------------------------------------------------------------
/quake1_example/class/func_bossgate.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionBrush
2 | class_name QmapbspQuakeFunctionBossgate
3 |
--------------------------------------------------------------------------------
/quake1_example/class/func_button.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionDoor
2 | class_name QmapbspQuakeFunctionButton
3 |
4 | func _motion_f(destroy_tween : bool = false) :
5 | super(destroy_tween)
6 | for t in get_tree().get_nodes_in_group(
7 | 'T_' + props.get('target')
8 | ) :
9 | if !t : continue
10 | if !t.has_method(&'_trigger') : continue
11 | t._trigger(self)
12 |
13 | if open :
14 | # change to alternate texture
15 | meshin.set_instance_shader_parameter(&'use_alternate', true)
16 |
17 | func _move_return() -> void :
18 | _move()
19 | # change to normal texture
20 | meshin.set_instance_shader_parameter(&'use_alternate', false)
21 |
22 | func _def_lip() -> String : return '4'
23 | func _def_wait() -> String : return '1'
24 | func _no_linking() -> bool : return true
25 |
26 |
27 | func _play_snd(idx : int) :
28 | if open : super(idx)
29 |
30 | func _get_sounds(sounds : int) :
31 | if sounds == 0 :
32 | streams = [
33 | 'buttons/airbut1.wav', ''
34 | ]
35 | else :
36 | super(sounds)
37 |
38 | func _player_touch(p : QmapbspQuakePlayer, pos : Vector3, nor : Vector3) :
39 | _trigger(p)
40 |
--------------------------------------------------------------------------------
/quake1_example/class/func_door.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionBrush
2 | class_name QmapbspQuakeFunctionDoor
3 |
4 | # this is written from scratch. No QuakeC code was applied here
5 | # maybe not accurate to the original approach
6 |
7 | var tween : Tween
8 | var add : Vector3
9 | var add_reveal : Vector3
10 | var dura : float
11 | var wait : int
12 | var calc_ : bool = false
13 | var streams : Array
14 | var viewer : QmapbspQuakeViewer
15 | var open : bool = false
16 |
17 | var player : AudioStreamPlayer3D
18 | var player_end : bool = false
19 | var links : Array[QmapbspQuakeFunctionDoor]
20 |
21 | signal emit_message_once(m : String)
22 |
23 | # according to the QC file
24 | const audio_paths := [
25 | ['', ''],
26 | ['doors/drclos4.wav', 'doors/doormv1.wav'],
27 | ['doors/hydro1.wav', 'doors/hydro2.wav'],
28 | ['doors/stndr1.wav', 'doors/stndr2.wav'],
29 | ['doors/ddoor1.wav', 'doors/ddoor2.wav'],
30 | ['', ''],
31 | ]
32 |
33 | var func_door := [
34 | ['doors/medtry.wav', 'doors/meduse.wav'],
35 | ['doors/runetry.wav', 'doors/runeuse.wav'],
36 | ['doors/basetry.wav', 'doors/baseuse.wav'],
37 | ]
38 |
39 | # first found mesh instance
40 | var meshin : MeshInstance3D
41 |
42 | func _map_ready() :
43 | add_to_group(&'doors')
44 | _calc_add()
45 | _starts_open()
46 |
47 | for c in get_children() :
48 | if c is MeshInstance3D :
49 | meshin = c
50 | break
51 |
52 | func _starts_open() :
53 | if props.get('spawnflags', 0) & 0b01 :
54 | _open_direct()
55 |
56 | func _add_link(n : QmapbspQuakeFunctionDoor) :
57 | if n == self : return
58 | if links.has(n) : return
59 | links.append(n)
60 |
61 | func _def_lip() -> String : return '8'
62 | func _def_wait() -> String : return '-1'
63 | func _def_speed() -> String : return '100'
64 |
65 | func _no_linking() -> bool :
66 | return props.get('spawnflags', 0) & 0b100
67 |
68 | func _get_angle() -> int :
69 | return props.get('angle', 0)
70 |
71 | func _calc_add() :
72 | if !calc_ :
73 | calc_ = true
74 |
75 | _gen_aabb()
76 |
77 | if !(props.get('spawnflags', 0) & 0b100) :
78 | for n in get_tree().get_nodes_in_group(&'doors') :
79 | #if n.calc_ : continue
80 | n._calc_add()
81 | if n._no_linking() :
82 | continue
83 |
84 | if (
85 | aabb.size.x >= 0 and aabb.size.y >= 0 and
86 | n.aabb.size.x >= 0 and n.aabb.size.y >= 0
87 | ) :
88 | if aabb.grow(0.01).intersects(n.aabb) :
89 | _add_link(n)
90 | n._add_link(self)
91 |
92 | viewer = get_meta(&'viewer')
93 | var angle : int = _get_angle()
94 | var s : float = get_meta(&'scale', 32.0)
95 | wait = props.get('wait', _def_wait()).to_int()
96 | var lip : float = props.get('lip', _def_lip()).to_int() / s
97 |
98 | if angle == -1 :
99 | add = Vector3(0.0, aabb.size.y - lip, 0.0)
100 | elif angle == -2 :
101 | add = Vector3(0.0, -aabb.size.y + lip, 0.0)
102 | else :
103 | var rot := (angle / 180.0) * PI
104 | var dir := -(Vector3(
105 | aabb.size.x, 0.0, aabb.size.z
106 | )) + Vector3(lip, 0.0, lip)
107 | add = Vector3.BACK.rotated(Vector3.UP, rot) * dir
108 | add_reveal = Vector3.LEFT.rotated(Vector3.UP, -rot) * -(Vector3(
109 | aabb.size.x, 0.0, aabb.size.z
110 | ))
111 | dura = add.length() / (props.get('speed', _def_speed()).to_int() / s)
112 |
113 | var sounds : int = clampi(props.get('sounds', '0').to_int(), 0, 5)
114 | _get_sounds(sounds)
115 |
116 | func _get_sounds(sounds : int) :
117 | if sounds == 5 :
118 | streams = func_door[
119 | viewer.worldspawn.props.get("worldtype", ['', '']) % 3
120 | ]
121 | else :
122 | streams = audio_paths[sounds]
123 |
124 | func _trigger(b : Node3D) :
125 | if tween : return
126 | _move()
127 | for l in links :
128 | l._trigger(b)
129 |
130 | func _make_player() :
131 | if !player :
132 | player = AudioStreamPlayer3D.new()
133 | player.finished.connect(_audf)
134 | add_child(player)
135 |
136 | func _get_sound_index_loop() -> int : return 0
137 | func _get_sound_index_motion_end() -> int : return 1
138 |
139 | func _motion_f(destroy_tween : bool = false) :
140 | player_end = true
141 | _play_snd(_get_sound_index_motion_end())
142 | if destroy_tween :
143 | tween.kill()
144 | tween = null
145 |
146 | func _play_snd(idx : int) :
147 | _make_player()
148 | var s : String = streams[idx]
149 | if s.is_empty() : return
150 | player.stream = viewer.hub.load_audio(s)
151 | player.play()
152 |
153 | func _audf() :
154 | if player_end :
155 | player.queue_free()
156 | player = null
157 | else :
158 | _make_player()
159 | player.play()
160 |
161 | func _move_pre(tween : Tween) -> Vector3 : return position
162 | func _move_return() -> void :
163 | _move()
164 |
165 | func _move() :
166 | tween = create_tween()
167 |
168 | var basepos := _move_pre(tween)
169 |
170 | if open :
171 | tween.tween_property(self, ^'position',
172 | basepos - add, dura
173 | ).finished.connect(_motion_f.bind(true))
174 | else :
175 | tween.tween_property(self, ^'position',
176 | basepos + add, dura
177 | ).finished.connect(_motion_f)
178 | open = !open
179 | _play_snd(_get_sound_index_loop())
180 | player_end = false
181 |
182 | if wait != -1 :
183 | tween.tween_interval(wait)
184 | tween.finished.connect(_move_return)
185 |
186 |
187 |
188 | func _open_direct() :
189 | player_end = false
190 | position += add
191 | open = true
192 |
193 | func _player_touch(p : QmapbspQuakePlayer, pos : Vector3, nor : Vector3) :
194 | if props.has("targetname") :
195 | if props.has("message") :
196 | emit_message_once.emit(props["message"])
197 | return
198 | for l in links :
199 | if l.props.has("targetname") :
200 | if l.props.has("message") :
201 | l.emit_message_once.emit(l.props["message"])
202 | return
203 | _trigger(p)
204 |
--------------------------------------------------------------------------------
/quake1_example/class/func_door_secret.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionDoor
2 | class_name QmapbspQuakeFunctionDoorSecret
3 |
4 | func _starts_open() :
5 | return
6 |
7 | func _map_ready() :
8 | super()
9 | if props.get('spawnflags', 0) & 0b01 :
10 | wait = -1.0
11 |
12 | const audio_paths_secret := [
13 | ['doors/basesec2.wav', 'doors/basesec1.wav', 'doors/basesec2.wav'],
14 | ['doors/latch2.wav', 'doors/winch2.wav', 'doors/drclos4.wav'],
15 | ['doors/airdoor2.wav', 'doors/airdoor1.wav', 'doors/airdoor2.wav'],
16 | ['doors/basesec2.wav', 'doors/basesec1.wav', 'doors/basesec2.wav'],
17 | ]
18 |
19 | func _reveal() -> bool : return true
20 |
21 | func _get_sounds(sounds : int) :
22 | streams = audio_paths_secret[sounds]
23 |
24 | func _move_pre(tween : Tween) -> Vector3 :
25 | _play_snd(0)
26 | tween.tween_property(self, ^'position',
27 | position + add_reveal, 0.5 # approx
28 | )
29 | tween.tween_interval(1) # approx
30 | return position + add_reveal
31 |
32 | func _no_linking() -> bool :
33 | return true
34 |
35 | func _get_sound_index_loop() -> int : return 1
36 | func _get_sound_index_motion_end() -> int : return 2
37 |
--------------------------------------------------------------------------------
/quake1_example/class/func_episodegate.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionBrush
2 | class_name QmapbspQuakeFunctionEpisodegate
3 |
4 | func _ready() :
5 | queue_free()
6 |
--------------------------------------------------------------------------------
/quake1_example/class/func_illusionary.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionBrush
2 | class_name QmapbspQuakeFunctionDoorIllusionary
3 |
--------------------------------------------------------------------------------
/quake1_example/class/func_plat.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionDoor
2 | class_name QmapbspQuakeFunctionPlat
3 |
4 | func _gen_aabb() :
5 | var height : int = props.get('height', '0').to_int()
6 | if height == 0 :
7 | super()
8 | else :
9 | aabb.size.y = height
10 | aabb.size.y *= -1
11 |
12 | func _def_lip() -> String : return '-8' # >O_O<
13 | func _no_linking() -> bool : return false
14 | func _player_touch(p : QmapbspQuakePlayer, pos : Vector3, nor : Vector3) :
15 | if open and nor.y > 0.9 :
16 | _trigger(p)
17 |
18 | func _motion_f(destroy_tween : bool = false) :
19 | super(true)
20 |
21 | func _starts_open() :
22 | _open_direct()
23 |
24 | func _get_angle() -> int :
25 | return -1
26 |
27 | func _def_speed() -> String : return '150'
28 |
--------------------------------------------------------------------------------
/quake1_example/class/func_train.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionBrush
2 | class_name QmapbspQuakeFunctionTrain
3 |
4 | var tween : Tween
5 | var curve : Curve3D
6 | var corner : Vector3
7 |
8 | var player : AudioStreamPlayer3D
9 | var player_end := false
10 |
11 | func _make_player() :
12 | if !player :
13 | player = AudioStreamPlayer3D.new()
14 | player.finished.connect(_audf)
15 | add_child(player)
16 |
17 | func _audf() :
18 | if player_end :
19 | player.queue_free()
20 | player = null
21 | else :
22 | _make_player()
23 | player.play()
24 |
25 | func _play_snd(path : String) :
26 | _make_player()
27 | player.stream = get_meta(&'viewer').hub.load_audio(path)
28 | player.play()
29 |
30 | func _map_ready() :
31 | _after_map_ready.call_deferred()
32 |
33 | func _after_map_ready() :
34 | var path : Node = get_tree().get_first_node_in_group(
35 | 'T_' + props.get('target')
36 | )
37 |
38 | if path is QmapbspQuakePathCorner :
39 | curve = path.curve
40 | _gen_aabb()
41 | if curve and curve.point_count > 0 :
42 | corner = aabb.size / 2.0
43 | corner.y -= aabb.size.y
44 | position = curve.get_point_position(0) - corner
45 |
46 | func _trigger(b : Node3D) :
47 | if curve == null : return
48 | _start(curve)
49 |
50 | func _start(c : Curve3D) :
51 | if tween : return
52 | var s : float = get_meta(&'scale', 32.0)
53 | tween = create_tween()
54 | tween.finished.connect(_f)
55 | var poscursor := position
56 | for i in c.point_count :
57 | if i == 0 : continue
58 |
59 | var nextpos := c.get_point_position(i)
60 | var dura : float = (
61 | poscursor.distance_to(nextpos)
62 | ) / (props.get('speed', '64').to_int() / s)
63 | poscursor = nextpos
64 | tween.tween_property(self, ^'position',
65 | nextpos - corner,
66 | dura
67 | )
68 | player_end = false
69 | _play_snd('plats/train1.wav')
70 |
71 | func _f() :
72 | player_end = true
73 | _play_snd('plats/train2.wav')
74 |
--------------------------------------------------------------------------------
/quake1_example/class/func_wall.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFunctionBrush
2 | class_name QmapbspQuakeFunctionWall
3 |
--------------------------------------------------------------------------------
/quake1_example/class/info_player_start.gd:
--------------------------------------------------------------------------------
1 | extends Marker3D
2 | class_name QmapbspQuakeInfoPlayerSpawn
3 |
4 | func _ready() :
5 | add_to_group(&'player_spawn')
6 |
--------------------------------------------------------------------------------
/quake1_example/class/info_teleport_destination.gd:
--------------------------------------------------------------------------------
1 | extends Marker3D
2 | class_name QmapbspQuakeInfoTeleportDestination
3 |
--------------------------------------------------------------------------------
/quake1_example/class/light.gd:
--------------------------------------------------------------------------------
1 | extends OmniLight3D
2 | class_name QmapbspQuakeLight
3 |
4 | var props : Dictionary
5 | func _get_properties(dict : Dictionary) : props = dict
6 |
7 | var style : int
8 |
9 | func _init() :
10 | #shadow_enabled = true
11 | light_bake_mode = Light3D.BAKE_STATIC
12 |
13 | var light : int = props.get("light", 300)
14 | omni_range = light / 32.0
15 | omni_attenuation = 0.00001
16 | light_indirect_energy = light / 50.0
17 | distance_fade_enabled = true
18 | distance_fade_length = 5.0
19 |
20 | func _map_ready() :
21 | # misc.qc line 59
22 | if !props.has("targetname") :
23 | # inert light
24 | queue_free()
25 | return
26 |
27 | style = props.get("style", &'0').to_int()
28 |
29 | if style >= 32 :
30 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer')
31 |
32 | var spawnflags : int = props.get("spawnflags")
33 | if spawnflags & 0b1 :
34 | hide()
35 | viewer.qc_lightstyle(style, 'a')
36 | else :
37 | viewer.qc_lightstyle(style, 'm')
38 |
39 | func _trigger(b) :
40 | # misc.qc line 67
41 | var spawnflags : int = props.get("spawnflags")
42 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer')
43 | if !visible :
44 | viewer.qc_lightstyle(style, 'm')
45 | else :
46 | viewer.qc_lightstyle(style, 'a')
47 |
48 | visible = !visible
49 |
50 |
--------------------------------------------------------------------------------
/quake1_example/class/light_flame_large_yellow.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeLight
2 | class_name QmapbspQuakeLightFlameLargeYellow
3 |
4 | func _map_ready() -> void :
5 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer')
6 | var mdl : QmapbspMDLFile = viewer.hub.load_model("progs/flame2.mdl")
7 | var mdli := QmapbspMDLInstance.new()
8 | mdli.mdl = mdl
9 | mdli.name = &"MODEL"
10 | add_child(mdli)
11 |
12 | var aud := AudioStreamPlayer3D.new()
13 | aud.stream = viewer.hub.load_audio("ambience/fire1.wav")
14 | aud.max_distance = 10.0
15 | aud.finished.connect(func() :
16 | aud.play()
17 | )
18 | add_child(aud)
19 | aud.play()
20 |
--------------------------------------------------------------------------------
/quake1_example/class/light_fluorospark.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeLight
2 | class_name QmapbspQuakeLightFluorospark
3 |
4 | func _map_ready() :
5 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer')
6 | var aud := AudioStreamPlayer3D.new()
7 | aud.stream = viewer.hub.load_audio("ambience/buzz1.wav")
8 | aud.max_distance = 10.0
9 | aud.finished.connect(func() :
10 | aud.play()
11 | )
12 | add_child(aud)
13 | aud.play()
14 |
--------------------------------------------------------------------------------
/quake1_example/class/light_torch_small_walltorch.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeLight
2 | class_name QmapbspQuakeLightTorchSmallWalltorch
3 |
4 | func _map_ready() -> void :
5 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer')
6 | var mdl : QmapbspMDLFile = viewer.hub.load_model("progs/flame.mdl")
7 | var mdli := QmapbspMDLInstance.new()
8 | mdli.mdl = mdl
9 | mdli.name = &"MODEL"
10 | add_child(mdli)
11 |
12 | var aud := AudioStreamPlayer3D.new()
13 | aud.stream = viewer.hub.load_audio("ambience/fire1.wav")
14 | aud.max_distance = 10.0
15 | aud.finished.connect(func() :
16 | aud.play()
17 | )
18 | add_child(aud)
19 | aud.play()
20 |
--------------------------------------------------------------------------------
/quake1_example/class/path_corner.gd:
--------------------------------------------------------------------------------
1 | extends Node3D
2 | class_name QmapbspQuakePathCorner
3 |
4 | var props : Dictionary
5 | func _get_properties(dict : Dictionary) : props = dict
6 |
7 | var curve : Curve3D
8 |
9 | func _map_ready() :
10 | # gen path
11 | curve = Curve3D.new()
12 | curve.add_point(position)
13 |
14 | var t : String = props.get('target', '')
15 | var added : PackedStringArray
16 | while !t.is_empty() and !added.has(t) :
17 | added.append(t)
18 | t = 'T_' + t
19 | var node := get_tree().get_first_node_in_group(t)
20 | if node is QmapbspQuakePathCorner :
21 | curve.add_point(node.position)
22 | t = node.props.get('target', '')
23 | else :
24 | break
25 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_changelevel.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerChangeLevel
3 |
4 | func _trigger(b : Node3D) :
5 | v.change_level(props.get('map', ''))
6 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_counter.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerCounter
3 |
4 | var counter : int = 0
5 | var counter_max : int = 0
6 |
7 | func _bo_en(b : Node3D) : return
8 | func _bo_ex(b : Node3D) : return
9 |
10 | func _map_ready() :
11 | counter_max = props.get('count', '0').to_int()
12 |
13 | func _trigger(b : Node3D) :
14 | counter += 1
15 | if counter == counter_max :
16 | super(b)
17 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_hurt.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerHurt
3 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_monsterjump.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerMonsterJump
3 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_multiple.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerMultiple
3 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_once.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerOnce
3 |
4 | func _trigger_now(b : Node3D) :
5 | super(b)
6 | queue_free()
7 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_onlyregistered.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerOnlyregistered
3 |
4 | func _bo_en(b : Node3D) :
5 | if (get_meta(&'viewer') as QmapbspQuakeViewer).registered :
6 | _trigger(b)
7 | return
8 | _message()
9 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_push.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerPush
3 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_relay.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerRelay
3 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_secret.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTriggerOnce
2 | class_name QmapbspQuakeTriggerSecret
3 |
4 | func _trigger_now(b : Node3D) :
5 | super(b)
6 | get_meta(&'viewer').found_secret()
7 |
8 | func _show_message_end() : return
9 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_setskill.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerSetskill
3 |
4 | var skill : int = -1
5 | func _message() : return
6 |
7 | func _bo_en(b : Node3D) :
8 | super(b)
9 | v.set_skill(
10 | int(String(props.get('message')))
11 | )
12 |
--------------------------------------------------------------------------------
/quake1_example/class/trigger_teleport.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeTrigger
2 | class_name QmapbspQuakeTriggerTeleport
3 |
4 | func _trigger(b : Node3D) :
5 | for n in get_overlapping_bodies() :
6 | _teleport(b)
7 |
8 | func _bo_en(b : Node3D) :
9 | if props.has("targetname") : return
10 | _teleport(b)
11 |
12 | func _teleport(b : Node3D) :
13 | var dest : Node3D = get_tree().get_first_node_in_group(
14 | 'T_' + props.get('target')
15 | )
16 | if !dest : return
17 | if b is QmapbspQuakePlayer :
18 | b.teleport_to(dest, true)
19 |
--------------------------------------------------------------------------------
/quake1_example/class/worldspawn.gd:
--------------------------------------------------------------------------------
1 | extends StaticBody3D
2 | class_name QmapbspQuakeWorldspawn
3 |
4 | @onready var music := $music
5 | @onready var wenv : WorldEnvironment = $wenv
6 |
7 | var props : Dictionary
8 | func _get_properties(dict : Dictionary) : props = dict
9 |
10 | var world_shader : QmapbspQuake1StyleShader
11 |
12 | func _init() -> void :
13 | lightstyles.resize(MAX_LIGHTSTYLE)
14 | #lightstyles_f.resize(MAX_LIGHTSTYLE)
15 | lightstyles.fill(PackedColorArray([
16 | Color(DEFAULT_LIGHT_M, 0.0, 0.0, 0.0)
17 | ])) # normal light
18 |
19 | # stock lightstyles (world.qc)
20 | set_lightstyle(1, 'mmnmmommommnonmmonqnmmo')
21 | set_lightstyle(2, 'abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba')
22 | set_lightstyle(3, 'mmmmmaaaaammmmmaaaaaabcdefgabcdefg')
23 | set_lightstyle(4, 'mamamamamama')
24 | set_lightstyle(5, 'jklmnopqrstuvwxyzyxwvutsrqponmlkj')
25 | set_lightstyle(6, 'nmonqnmomnmomomno')
26 | set_lightstyle(7, 'mmmaaaabcdefgmmmmaaaammmaamm')
27 | set_lightstyle(8, 'mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa')
28 | set_lightstyle(9, 'aaaaaaaazzzzzzzz')
29 | set_lightstyle(10, 'mmamammmmammamamaaamammma')
30 | set_lightstyle(11, 'abcdefghijklmnopqrrqponmlkjihgfedcba')
31 |
32 | func _map_ready() :
33 | lightstyles_i = Image.create(64, 1, false, Image.FORMAT_RF)
34 | lightstyles_t = ImageTexture.create_from_image(lightstyles_i)
35 |
36 | RenderingServer.global_shader_parameter_set(
37 | &'lightstyle_tex',
38 | lightstyles_t
39 | )
40 |
41 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer', null)
42 | if viewer :
43 | world_shader = viewer.world_shader
44 |
45 | music.stream = viewer.get_music(props.get('sounds', 0))
46 | music.play()
47 |
48 | water.stream = viewer.hub.load_audio("ambience/water1.wav", true)
49 | sky.stream = viewer.hub.load_audio("ambience/wind2.wav", true)
50 | # the other 2 amb sounds are unknown
51 |
52 | ambplayers.append(water)
53 | ambplayers.append(sky)
54 | ambplayers.append(slime)
55 | ambplayers.append(lava)
56 |
57 | var env : Environment = wenv.environment
58 | var rendering : int = viewer.rendering
59 | if rendering != 0 :
60 | # var skysky : Sky = load("res://quake1_example/sky_sky.tres")
61 | # var skymat : ShaderMaterial = skysky.sky_material
62 | # skymat.set_shader_parameter(&'skytex', viewer.skytex)
63 | # env.sky = skysky
64 | # env.background_mode = Environment.BG_SKY
65 | # env.ambient_light_source = Environment.AMBIENT_SOURCE_SKY
66 | # env.ambient_light_energy = 16.0
67 | if rendering == 1 :
68 | env.sdfgi_enabled = true
69 | env.sdfgi_max_distance = 512.0
70 | env.sdfgi_cascades = 2
71 | env.sdfgi_read_sky_light = false
72 | env.sdfgi_energy = 4.0
73 | else :
74 | var aabb : AABB = props.get("__qmapbsp_aabb", AABB())
75 | if aabb.get_volume() > 0.0 :
76 | var voxelgi := VoxelGI.new()
77 | voxelgi.size = aabb.size
78 | voxelgi.position = aabb.get_center()
79 | voxelgi.name = "VOXELGI"
80 | add_child(voxelgi)
81 | voxelgi.bake()
82 | pass
83 |
84 | else :
85 | env.ambient_light_source = Environment.AMBIENT_SOURCE_DISABLED
86 |
87 | #####################################################################
88 | # lightstyles
89 | var lightstyles : Array[PackedColorArray]
90 | #var lightstyles_f : PackedFloat32Array
91 | var lightstyles_i : Image
92 | var lightstyles_t : ImageTexture
93 |
94 | const ZA : float = 0x7a - 0x61
95 | const MAX_LIGHTSTYLE := 64
96 | const SWITCHABLE_LIGHT_BEGIN := 32 # to 62
97 |
98 | const DEFAULT_LIGHT_M := (0x6D - 0x61) / ZA # M light (0.48)
99 |
100 | var update_tex_delay : int = 0
101 | var update_ls_delay : int = 0
102 | var ls_frame : int = 0
103 |
104 | #####################################################################
105 | # ambient sounds
106 | @onready var water : AudioStreamPlayer = $water
107 | @onready var sky : AudioStreamPlayer = $sky
108 | @onready var slime : AudioStreamPlayer = $slime
109 | @onready var lava : AudioStreamPlayer = $lava
110 | var amb : Vector4
111 | var ambtarget : Vector4
112 | var ambplayers : Array[AudioStreamPlayer]
113 |
114 | func _process(delta : float) :
115 | #if !surface : return # ?
116 |
117 | if update_ls_delay <= 0 :
118 | _update_ls()
119 | update_ls_delay = 10
120 | else :
121 | update_ls_delay -= 1
122 |
123 | for i in 4 :
124 | if amb[i] < ambtarget[i] :
125 | amb[i] = min(amb[i] + delta, ambtarget[i])
126 | else :
127 | amb[i] = max(amb[i] - delta, ambtarget[i])
128 | var ambp := ambplayers[i]
129 | if amb[i] <= 0 :
130 | ambp.stop()
131 | continue
132 | elif !ambp.playing :
133 | ambp.play()
134 | ambplayers[i].volume_db = linear_to_db(amb[i])
135 |
136 | func _update_ls() -> void :
137 | #var frame := Engine.get_frames_drawn() / 10
138 | for i in MAX_LIGHTSTYLE :
139 | var pf32a := lightstyles[i]
140 | #print(pf32a[ls_frame % pf32a.size()])
141 | lightstyles_i.set_pixel(i, 0, pf32a[ls_frame % pf32a.size()])
142 | lightstyles_t.update(lightstyles_i)
143 | ls_frame += 1
144 |
145 | func set_lightstyle(style : int, light : String) -> void :
146 | var lightraw : PackedColorArray
147 | lightraw.resize(light.length())
148 | for i in light.length() :
149 | lightraw[i] = Color(
150 | (light.unicode_at(i) - 0x61) / ZA,
151 | 0.0, 0.0, 0.0
152 | )
153 | lightstyles[style] = lightraw
154 |
155 | # ambient sounds
156 | # 0 = water
157 | # 1 = sky
158 | # 2 = slime
159 | # 3 = lava
160 | var amb_activator : Object
161 | func set_ambsnds(activator : Object, amb : Vector4) -> void :
162 | if amb.x < 0.0 :
163 | if amb_activator == activator :
164 | ambtarget = Vector4()
165 | return
166 |
167 | ambtarget = amb * 0.125 # too loud when used 1.0
168 | amb_activator = activator
169 |
170 | func set_filter_mode(filter : int) -> void :
171 | world_shader.texture_filter = filter
172 | world_shader.rebuild_shader()
173 |
174 | func set_rendering_mode(ren_mode : int) -> void :
175 | world_shader.texture_mode = ren_mode
176 | world_shader.rebuild_shader()
177 |
--------------------------------------------------------------------------------
/quake1_example/class/worldspawn.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=3 format=3 uid="uid://dh5c5ghexuj8i"]
2 |
3 | [ext_resource type="Script" path="res://quake1_example/class/worldspawn.gd" id="1_3n35g"]
4 |
5 | [sub_resource type="Environment" id="Environment_dlsko"]
6 | background_mode = 1
7 | ambient_light_source = 1
8 | ambient_light_color = Color(0, 1, 1, 1)
9 | ambient_light_energy = 16.0
10 | reflected_light_source = 1
11 | sdfgi_use_occlusion = true
12 |
13 | [node name="worldspawn" type="StaticBody3D"]
14 | script = ExtResource("1_3n35g")
15 |
16 | [node name="wenv" type="WorldEnvironment" parent="."]
17 | environment = SubResource("Environment_dlsko")
18 |
19 | [node name="music" type="AudioStreamPlayer" parent="."]
20 | process_mode = 3
21 |
22 | [node name="water" type="AudioStreamPlayer" parent="."]
23 | process_mode = 3
24 |
25 | [node name="sky" type="AudioStreamPlayer" parent="."]
26 | process_mode = 3
27 |
28 | [node name="slime" type="AudioStreamPlayer" parent="."]
29 | process_mode = 3
30 |
31 | [node name="lava" type="AudioStreamPlayer" parent="."]
32 | process_mode = 3
33 |
--------------------------------------------------------------------------------
/quake1_example/clip_proxy_animated.gd:
--------------------------------------------------------------------------------
1 | extends AnimatableBody3D
2 | class_name QmapbspQuakeClipProxyAnimated
3 |
--------------------------------------------------------------------------------
/quake1_example/clip_proxy_area.gd:
--------------------------------------------------------------------------------
1 | extends Area3D
2 | class_name QmapbspQuakeClipProxyArea
3 |
4 | func set_area(a : Area3D) -> void :
5 | area_entered.connect(func(b) : a.area_entered.emit(b))
6 | area_exited.connect(func(b) : a.area_exited.emit(b))
7 | area_shape_entered.connect(func(b,c,d,e) : a.area_shape_entered.emit(b,c,d,e))
8 | area_shape_exited.connect(func(b,c,d,e) : a.area_shape_exited.emit(b,c,d,e))
9 |
10 | body_entered.connect(func(b) : a.body_entered.emit(b))
11 | body_exited.connect(func(b) : a.body_exited.emit(b))
12 | body_shape_entered.connect(func(b,c,d,e) : a.body_shape_entered.emit(b,c,d,e))
13 | body_shape_exited.connect(func(b,c,d,e) : a.body_shape_exited.emit(b,c,d,e))
14 |
--------------------------------------------------------------------------------
/quake1_example/clip_proxy_static.gd:
--------------------------------------------------------------------------------
1 | extends StaticBody3D
2 | class_name QmapbspQuakeClipProxyStatic
3 |
--------------------------------------------------------------------------------
/quake1_example/console.gd:
--------------------------------------------------------------------------------
1 | extends Control
2 | class_name QmapbspConsole
3 |
4 | ## it used to function like an actual console. but I removed a lot of this work due to the complexity T^T
5 |
6 | var hub : QmapbspQuake1Hub
7 |
8 | var enginetext : String = "Qmapbsp Quake1 Example @ Godot 4"
9 |
10 | var cmds : Dictionary
11 | var control : QmapbspQuakeDraw
12 | var regex : RegEx
13 | var tween : Tween
14 | var showing : bool = true
15 |
16 | var text_visible : bool = true
17 | var text : PackedStringArray
18 | var cvars : Dictionary
19 |
20 | func setup(hub_ : QmapbspQuake1Hub) :
21 | hub = hub_
22 | control.hub = hub
23 | $disc.texture = hub.load_as_texture("gfx.wad:DISC")
24 |
25 | func var_set(cvar : String, v) -> void :
26 | cvars[cvar] = v
27 |
28 | func var_get(cvar : String) :
29 | return cvars.get(cvar, 0)
30 |
31 | func exec(cmd : StringName, args : Array) :
32 | var call = cmds.get(cmd)
33 | if call is Callable :
34 | last_call_v = args
35 | call.call()
36 |
37 | func exec_line(line : String) :
38 | var r := regex.search_all(line)
39 | pass
40 |
41 | func add_command(n : StringName, call : Callable) :
42 | cmds[n] = call
43 |
44 | func down(d : float = 0.33) :
45 | showing = true
46 | if tween :
47 | tween.kill()
48 | tween = create_tween()
49 | tween.set_parallel()
50 | anchor_top = -1.0
51 | anchor_bottom = 0.0
52 | show()
53 | tween.tween_property(self, ^'anchor_bottom', 1.0, d)
54 | tween.tween_property(self, ^'anchor_top', 0.0, d)
55 | control.queue_redraw()
56 |
57 | func toggle() :
58 | showing = !showing
59 | tween = create_tween()
60 | tween.set_parallel()
61 | if showing :
62 | anchor_top = -1.0
63 | anchor_bottom = 0.0
64 | show()
65 | tween.tween_property(self, ^'anchor_bottom', 1.0, 1.0)
66 | tween.tween_property(self, ^'anchor_top', 0.0, 1.0)
67 | else :
68 | anchor_top = 0.0
69 | anchor_bottom = 1.0
70 | tween.tween_property(self, ^'anchor_top', -1.0, 1.0)
71 | tween.tween_property(self, ^'anchor_bottom', 0.0, 1.0)
72 | tween.finished.connect(_tf)
73 | control.queue_redraw()
74 |
75 | func _tf() :
76 | if !showing : hide()
77 |
78 | func make_visible(yes : bool) :
79 | control.visible = yes
80 |
81 | func printv(s : String) :
82 | text.push_back(s)
83 | while (text.size() + 2) * 8 * 3 > size.y :
84 | text.remove_at(0)
85 | control.queue_redraw()
86 |
87 | func _ready() :
88 | control = QmapbspQuakeDraw.new()
89 | control.name = &'CONSOLECONTROL'
90 | control.draw.connect(_console_draw)
91 | control.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
92 | control.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
93 | add_child(control)
94 | $disc.move_to_front()
95 |
96 | regex = RegEx.new()
97 | regex.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'")
98 |
99 | var begin_line := 0
100 |
101 | func _console_draw() :
102 | if !hub : return
103 | var texture := hub.load_as_texture("gfx/conback.lmp")
104 | control.draw_texture_rect(texture, Rect2(
105 | Vector2(), size
106 | ), false)
107 |
108 | if text_visible :
109 | var y : float = 8.0 * 3
110 | for i in text.size() :
111 | var t := text[i]
112 | control.draw_quake_text(Vector2(
113 | 8 * 3, y + (begin_line * 8 * 3)
114 | ), t, 0, Vector2(3, 3))
115 | y += 8 * 3
116 |
117 | control.draw_quake_text(
118 | Vector2(size.x - (enginetext.length() * 8 * 3), size.y - 8 * 3),
119 | enginetext, 0, Vector2(3, 3)
120 | )
121 |
122 | func scroll(add : int) :
123 | begin_line += add
124 | control.queue_redraw()
125 |
126 | #func _unhandled_input(event : InputEvent) :
127 | # if !text_visible :
128 | # Menu.unhandled_input(event)
129 | # return
130 | # if event is InputEventMouseButton :
131 | # if !event.pressed : return
132 | # if event.button_index == MOUSE_BUTTON_WHEEL_UP :
133 | # scroll(1)
134 | # elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN :
135 | # scroll(-1)
136 | # elif event.is_action_pressed(&"ui_cancel") :
137 | # text_visible = false
138 | # Menu.show()
139 | # control.queue_redraw()
140 |
141 | ########################
142 | var last_call_v : Array
143 | var last_call_cmd : StringName
144 |
145 | func argv() -> Array :
146 | return [last_call_cmd] + last_call_v
147 |
--------------------------------------------------------------------------------
/quake1_example/console.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=5 format=3 uid="uid://dvlurm3ntglha"]
2 |
3 | [ext_resource type="Script" path="res://quake1_example/console.gd" id="1_tic73"]
4 |
5 | [sub_resource type="Animation" id="Animation_vukfu"]
6 | length = 0.001
7 | tracks/0/type = "value"
8 | tracks/0/imported = false
9 | tracks/0/enabled = true
10 | tracks/0/path = NodePath(".:self_modulate")
11 | tracks/0/interp = 1
12 | tracks/0/loop_wrap = true
13 | tracks/0/keys = {
14 | "times": PackedFloat32Array(0),
15 | "transitions": PackedFloat32Array(1),
16 | "update": 0,
17 | "values": [Color(1, 1, 1, 1)]
18 | }
19 |
20 | [sub_resource type="Animation" id="Animation_sgl1c"]
21 | resource_name = "aniloop"
22 | length = 0.2
23 | loop_mode = 2
24 | tracks/0/type = "value"
25 | tracks/0/imported = false
26 | tracks/0/enabled = true
27 | tracks/0/path = NodePath(".:self_modulate")
28 | tracks/0/interp = 1
29 | tracks/0/loop_wrap = true
30 | tracks/0/keys = {
31 | "times": PackedFloat32Array(0, 0.2),
32 | "transitions": PackedFloat32Array(1, 1),
33 | "update": 0,
34 | "values": [Color(1, 1, 1, 1), Color(4, 4, 0, 1)]
35 | }
36 |
37 | [sub_resource type="AnimationLibrary" id="AnimationLibrary_a4dt3"]
38 | _data = {
39 | "RESET": SubResource("Animation_vukfu"),
40 | "aniloop": SubResource("Animation_sgl1c")
41 | }
42 |
43 | [node name="console" type="Control"]
44 | layout_mode = 3
45 | anchors_preset = 15
46 | anchor_right = 1.0
47 | anchor_bottom = 1.0
48 | grow_horizontal = 2
49 | grow_vertical = 2
50 | script = ExtResource("1_tic73")
51 |
52 | [node name="disc" type="TextureRect" parent="."]
53 | layout_mode = 1
54 | anchors_preset = -1
55 | anchor_left = 1.0
56 | anchor_top = 1.0
57 | anchor_right = 1.0
58 | anchor_bottom = 1.0
59 | offset_bottom = -72.0
60 | grow_horizontal = 0
61 | grow_vertical = 0
62 | scale = Vector2(3, 3)
63 | pivot_offset = Vector2(24, 24)
64 |
65 | [node name="ani" type="AnimationPlayer" parent="disc"]
66 | autoplay = "aniloop"
67 | libraries = {
68 | "": SubResource("AnimationLibrary_a4dt3")
69 | }
70 |
--------------------------------------------------------------------------------
/quake1_example/fluid.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeLeafVolume
2 | class_name QmapbspQuakeFluidVolume
3 |
4 | func _damage() -> int : return 0
5 | func _liquid_type() -> StringName : return &'water'
6 | func _decay_time() -> float : return 0 # in seconds
7 |
8 | #func _ready() :
9 | # body_entered.connect(_bo_en)
10 | # body_exited.connect(_bo_ex)
11 | #
12 | #func _bo_en(b : Node3D) :
13 | # if b.has_method(&'_fluid_enter') :
14 | # b._fluid_enter(self)
15 | #
16 | #func _bo_ex(b : Node3D) :
17 | # if b.has_method(&'_fluid_exit') :
18 | # b._fluid_exit(self)
19 |
--------------------------------------------------------------------------------
/quake1_example/fluid_lava.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFluidVolume
2 | class_name QmapbspQuakeLavaVolume
3 |
4 | func _damage() -> int : return 10
5 | func _liquid_type() -> StringName : return &'lava'
6 |
--------------------------------------------------------------------------------
/quake1_example/fluid_slime.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeFluidVolume
2 | class_name QmapbspQuakeSlimeVolume
3 |
4 | func _damage() -> int : return 2
5 | func _liquid_type() -> StringName : return &'slime'
6 | func _decay_time() -> float : return 5 # in seconds
7 |
--------------------------------------------------------------------------------
/quake1_example/hud.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeDraw
2 | class_name QmapbspQuakeHUD
3 |
4 | var nums : Array[ImageTexture]
5 | var anums : Array[ImageTexture]
6 | var faces : Array[ImageTexture]
7 |
8 | var player : QmapbspQuakePlayer
9 |
10 | const MARGIN : float = 32.0
11 | const SCALE := Vector2(3, 3)
12 |
13 | var health := 100
14 |
15 | func _process(delta : float) :
16 | queue_redraw()
17 |
18 | func _draw() :
19 | draw_quake_text(
20 | Vector2(16, 16),
21 | "fps : %s\ndraw calls : %s\nprimitives : %s\nvelocity : %d" % [
22 | Performance.get_monitor(Performance.TIME_FPS),
23 | Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME),
24 | Performance.get_monitor(Performance.RENDER_TOTAL_PRIMITIVES_IN_FRAME),
25 | player.velocity.length() * 32.0 if is_instance_valid(player) else "N/A"
26 | ], 0, Vector2(3, 3)
27 | )
28 | draw_line(Vector2(16, 128), Vector2(16 + (player.velocity.length() / 15.0 * 128.0), 128), Color.TOMATO, 4.0)
29 |
30 |
31 |
32 |
33 | return
34 | var t : ImageTexture
35 | var ts : Vector2
36 | t = faces[0]
37 | ts = t.get_size() * SCALE
38 | draw_texture_rect(t, Rect2(
39 | Vector2(MARGIN, size.y - ts.y - MARGIN),
40 | ts
41 | ), false)
42 |
43 | # HEALTH
44 | var hs := str(health)
45 | if hs.length() >= 3 :
46 | t = nums[int(hs[0])]
47 | ts = t.get_size() * SCALE
48 | draw_texture_rect(t, Rect2(
49 | Vector2(MARGIN + ts.x + 16.0,
50 | size.y - ts.y - MARGIN),
51 | ts
52 | ), false)
53 |
54 | if hs.length() >= 2 :
55 | t = nums[int(hs[hs.length() - 2])]
56 | ts = t.get_size() * SCALE
57 | draw_texture_rect(t, Rect2(
58 | Vector2(MARGIN + ts.x + 16.0 + (24.0 * 3),
59 | size.y - ts.y - MARGIN),
60 | ts
61 | ), false)
62 |
63 | t = nums[int(hs[hs.length() - 1])]
64 | ts = t.get_size() * SCALE
65 | draw_texture_rect(t, Rect2(
66 | Vector2(MARGIN + ts.x + 16.0 + (24.0 * 2 * 3),
67 | size.y - ts.y - MARGIN),
68 | ts
69 | ), false)
70 |
71 | func setup(viewer : QmapbspQuakeViewer) :
72 | var C := viewer.hub.load_as_texture
73 | hub = viewer.hub
74 | nums = [
75 | C.call("gfx.wad:NUM_0"),
76 | C.call("gfx.wad:NUM_1"),
77 | C.call("gfx.wad:NUM_2"),
78 | C.call("gfx.wad:NUM_3"),
79 | C.call("gfx.wad:NUM_4"),
80 | C.call("gfx.wad:NUM_5"),
81 | C.call("gfx.wad:NUM_6"),
82 | C.call("gfx.wad:NUM_7"),
83 | C.call("gfx.wad:NUM_8"),
84 | C.call("gfx.wad:NUM_9")
85 | ]
86 | anums = [
87 | C.call("gfx.wad:ANUM_0"),
88 | C.call("gfx.wad:ANUM_1"),
89 | C.call("gfx.wad:ANUM_2"),
90 | C.call("gfx.wad:ANUM_3"),
91 | C.call("gfx.wad:ANUM_4"),
92 | C.call("gfx.wad:ANUM_5"),
93 | C.call("gfx.wad:ANUM_6"),
94 | C.call("gfx.wad:ANUM_7"),
95 | C.call("gfx.wad:ANUM_8"),
96 | C.call("gfx.wad:ANUM_9")
97 | ]
98 | faces = [
99 | C.call("gfx.wad:FACE1"),
100 | C.call("gfx.wad:FACE2"),
101 | C.call("gfx.wad:FACE3"),
102 | C.call("gfx.wad:FACE4"),
103 | C.call("gfx.wad:FACE5")
104 | ]
105 |
--------------------------------------------------------------------------------
/quake1_example/leaf_volume.gd:
--------------------------------------------------------------------------------
1 | extends Area3D
2 | class_name QmapbspQuakeLeafVolume
3 |
4 | func _ready() :
5 | body_shape_entered.connect(_bo_en)
6 | body_shape_exited.connect(_bo_ex)
7 |
8 | func _bo_en(
9 | rid : RID, b : Node3D,
10 | body_shape_index : int, local_shape_index : int
11 | ) :
12 |
13 | var shape_node : CollisionShape3D = shape_owner_get_owner(shape_find_owner(local_shape_index))
14 | if b is QmapbspQuakePlayer :
15 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer', null)
16 | viewer.set_ambsnds(shape_node, shape_node.get_meta(&'ambsnds'))
17 | #prints("EN", shape_node.get_meta(&'ambsnds'))
18 |
19 | func _bo_ex(
20 | rid : RID, b : Node3D,
21 | body_shape_index : int, local_shape_index : int
22 | ) :
23 |
24 | var shape_node : CollisionShape3D = shape_owner_get_owner(shape_find_owner(local_shape_index))
25 | if b is QmapbspQuakePlayer :
26 | var viewer : QmapbspQuakeViewer = get_meta(&'viewer', null)
27 | viewer.set_ambsnds(shape_node, Vector4(-1, -1, -1, -1))
28 | #prints("EX", shape_node.get_meta(&'ambsnds'))
29 |
--------------------------------------------------------------------------------
/quake1_example/lmp.gd:
--------------------------------------------------------------------------------
1 | extends RefCounted
2 | class_name QmapbspLmpFile
3 |
4 | static func load_from_file(
5 | path : String, f : FileAccess, res : Array
6 | ) -> StringName :
7 | # returns PackedColorArray if it's a palette lump file
8 | # returns [PackedColorArray...] if it's a colormap lump file
9 | # returns [size, data] if it's a picture lump file
10 |
11 | if path.ends_with('palette.lmp') :
12 | res.append(_pal(f))
13 | return &'pal'
14 | elif path.ends_with('colormap.lmp') :
15 | var arr : Array[PackedColorArray]
16 | arr.resize(32)
17 | for i in 32 :
18 | arr[i] = _pal(f)
19 | res.append(arr)
20 | return &'map'
21 | elif path.ends_with('pop.lmp') :
22 | # proof-of-purchase graphics
23 | var psize := Vector2i(16, 16)
24 | res.append([
25 | psize,
26 | f.get_buffer(psize.x * psize.y)
27 | ])
28 | return &'pic'
29 |
30 |
31 | var psize := Vector2i(f.get_32(), f.get_32())
32 | res.append([
33 | psize,
34 | f.get_buffer(psize.x * psize.y)
35 | ])
36 | return &'pic'
37 |
38 |
39 | static func _pal(f : FileAccess) -> PackedColorArray :
40 | var pal : PackedColorArray
41 | pal.resize(256)
42 | for i in 256 : pal[i] = Color(
43 | f.get_8() / 255.0,
44 | f.get_8() / 255.0,
45 | f.get_8() / 255.0
46 | )
47 | return pal
48 |
--------------------------------------------------------------------------------
/quake1_example/material/fluid.gdshader:
--------------------------------------------------------------------------------
1 | shader_type spatial;
2 | render_mode unshaded, shadows_disabled, depth_prepass_alpha, specular_disabled;
3 |
4 | uniform sampler2D tex : source_color;
5 | uniform float alpha = 0.8;
6 |
7 | const float AMP = 0.1;
8 |
9 | float map(float value, float from, float to) {
10 | return (value - from) / (to - from);
11 | }
12 |
13 | const float WAVES = 5.0;
14 | const float SPEED = 3.0;
15 |
16 | void fragment() {
17 | vec2 uv = UV;
18 | uv.x -= map(sin(uv.y * WAVES - (TIME * SPEED)), -WAVES, WAVES) - 0.5;
19 | uv.y += map(sin(uv.x * WAVES - (TIME * SPEED)), -WAVES, WAVES) - 0.5;
20 | ALBEDO = texture(tex, uv).rgb;
21 | ALPHA = alpha; // ?
22 | }
--------------------------------------------------------------------------------
/quake1_example/material/mdl_animated.gdshader:
--------------------------------------------------------------------------------
1 | shader_type spatial;
2 | render_mode specular_disabled;
3 |
4 | uniform sampler2D skin : source_color;
5 |
6 | uniform sampler2D animation;
7 | uniform vec3 scale;
8 | uniform vec3 origin;
9 | uniform float seek;
10 |
11 | void vertex() {
12 | vec2 pixhalf = 1.0 / vec2(textureSize(animation, 0)) * 0.5f;
13 | vec3 v = vec3(
14 | 255.0f * texture(animation, vec2(CUSTOM0.x, seek) + pixhalf).xyz
15 | ) * scale + origin;
16 | VERTEX = vec3(-v.x, v.z, v.y);
17 | }
18 |
19 | void fragment() {
20 | ROUGHNESS = 1.0f;
21 | METALLIC = 0.0f;
22 | ALBEDO.rgb = texture(skin, UV).rgb;
23 | }
24 |
--------------------------------------------------------------------------------
/quake1_example/material/sky.gdshader:
--------------------------------------------------------------------------------
1 | shader_type spatial;
2 | render_mode unshaded, shadows_disabled;
3 |
4 | uniform sampler2D skytex_fg : source_color;
5 | uniform sampler2D skytex_bg : source_color; // has alpha
6 |
7 | void fragment()
8 | {
9 | vec3 view = -(vec4(VIEW, 1.0f) * VIEW_MATRIX).xyz;
10 | vec2 uv = ((view * (256.0f / view.y)).xz + (TIME * vec2(-8.0f, 8.0f))) / 512.0f;
11 | vec3 t2 = texture(skytex_bg, uv * 2.0f).xyz;
12 | uv += (TIME * 0.05f * vec2(-1.0f, 1.0f));
13 | vec4 t1 = texture(skytex_fg, uv * 2.0f);
14 | ALBEDO = mix(t2, t1.xyz, t1.a);
15 | }
16 |
--------------------------------------------------------------------------------
/quake1_example/material/sky.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://uv38265jaguo"]
2 |
3 | [ext_resource type="Shader" path="res://quake1_example/material/sky.gdshader" id="1_k4c4m"]
4 |
5 | [resource]
6 | render_priority = 0
7 | shader = ExtResource("1_k4c4m")
8 | shader_parameter/threshold = 0.01
9 |
--------------------------------------------------------------------------------
/quake1_example/material/sky_sky.gdshader:
--------------------------------------------------------------------------------
1 | shader_type sky;
2 |
3 | uniform sampler2D skytex : source_color;
4 | uniform float threshold = 0.1;
5 |
6 | void sky()
7 | {
8 | vec3 view = EYEDIR;
9 | vec2 uv = ((view * (256.0 / view.y)).xz + (TIME * vec2(-8.0, 8.0))) / 512.0;
10 | vec3 t2 = texture(skytex, vec2(
11 | 0.5 + mod(uv.x, 0.5),
12 | uv.y
13 | )).xyz;
14 | uv += (TIME * 0.05 * vec2(-1.0, 1.0));
15 | vec3 t1 = texture(skytex, vec2(
16 | mod(uv.x, 0.5),
17 | uv.y * 2.0
18 | )).xyz;
19 |
20 | vec3 tf = t1;
21 | if ((t1.x + t1.y + t1.z) < threshold) {
22 | tf = t2;
23 | }
24 | COLOR = tf;
25 | }
26 |
--------------------------------------------------------------------------------
/quake1_example/mdl_instance.gd:
--------------------------------------------------------------------------------
1 | extends MeshInstance3D
2 | class_name QmapbspMDLInstance
3 |
4 | var mat : ShaderMaterial
5 |
6 | @export var mdl : QmapbspMDLFile :
7 | set(v) :
8 | if mdl == v : return
9 | if mdl :
10 | mesh.surface_set_material(0, null)
11 | mdl = v
12 | mesh = v.base_mesh
13 |
14 | if !mat :
15 | mat = ShaderMaterial.new()
16 | mat.shader = preload("res://quake1_example/material/mdl_animated.gdshader")
17 | mesh.surface_set_material(0, mat)
18 |
19 | mat.set_shader_parameter(&'skin', v.skin)
20 | mat.set_shader_parameter(&'scale', v.quake_scale)
21 | mat.set_shader_parameter(&'origin', v.quake_origin)
22 | mat.set_shader_parameter(&'animation', v.animation)
23 | mat.set_shader_parameter(&'skin', v.skin)
24 |
25 | mat.set_shader_parameter(&'seek', seek)
26 |
27 | @export var seek : float :
28 | set(v) :
29 | if mat :
30 | mat.set_shader_parameter(&'seek', v)
31 | seek = v
32 |
33 | func _init() -> void :
34 | pass
35 |
--------------------------------------------------------------------------------
/quake1_example/message.gd:
--------------------------------------------------------------------------------
1 | extends QmapbspQuakeDraw
2 | class_name QmapbspQuakeViewerMessage
3 |
4 | var current_emitter : Node
5 | var current_message : String
6 | @onready var talk : AudioStreamPlayer = $talk
7 | @onready var life : Timer = $life
8 |
9 | func _ready() :
10 | life.timeout.connect(_on_life_timeout)
11 |
12 | func clear() :
13 | talk.stream = null
14 | current_message = ''
15 | current_emitter = null
16 | queue_redraw()
17 |
18 | func set_talk_sound(audio : AudioStream) :
19 | if talk.stream == audio : return
20 | talk.stream = audio
21 |
22 | func set_emitter(msg : String, show : bool, from : Node) :
23 | if msg == current_message : return
24 | if from == null : current_emitter = null
25 | if show :
26 | current_emitter = from
27 | _show(msg)
28 | talk.play()
29 | elif current_emitter == from : current_emitter = null
30 |
31 |
32 | func _show(m : String) :
33 | current_message = m
34 | life.start()
35 | queue_redraw()
36 |
37 | func _draw() :
38 | draw_quake_text(
39 | Vector2(), current_message, 0, Vector2(3, 3), true
40 | )
41 |
42 |
43 | func _on_life_timeout() :
44 | clear()
45 |
--------------------------------------------------------------------------------
/quake1_example/pak_file.gd:
--------------------------------------------------------------------------------
1 | extends RefCounted
2 | class_name QmapbspPakFile
3 |
4 | const ENTRY_SIZE = 64
5 | var filename : String # pak0.pak
6 | var loadrsc_f : FileAccess
7 | var loadrsc_pathlist : PackedStringArray
8 | var loadrsc_entries : PackedInt32Array # [offset, size]
9 | var loadrsc_index : int = -1
10 | var loadrsc_status : int
11 | var loaded_entries : Array[Resource]
12 |
13 | var global_pal : PackedColorArray
14 | var global_map : Array[PackedColorArray]
15 | var convert_indexes : PackedInt32Array
16 | var convert_entries : Array
17 |
18 | static func begin(p : String, ret : Array = []) -> QmapbspPakFile :
19 | var f := FileAccess.open(p, FileAccess.READ)
20 | if !f :
21 | ret.append_array([
22 | &"OPEN_FILE_ERROR"
23 | ])
24 | return null
25 |
26 | # HEADER
27 | var header : int = f.get_32()
28 | if header != 1262698832 :
29 | ret.append(&"INVALID_MAGIC_NUMBER")
30 | return null
31 |
32 | var pak := QmapbspPakFile.new()
33 | pak.filename = p.get_file()
34 | pak.loadrsc_f = f
35 |
36 | var diroffset : int = f.get_32()
37 | var dirsize : int = f.get_32()
38 |
39 | var L1 := pak.loadrsc_pathlist
40 | var L2 := pak.loadrsc_entries
41 |
42 | L1.resize(dirsize / ENTRY_SIZE)
43 | L2.resize(L1.size() * 2)
44 | pak.loaded_entries.resize(L1.size())
45 |
46 | for i in L1.size() :
47 | f.seek(diroffset + ENTRY_SIZE * i)
48 | L1[i] = f.get_buffer(0x38).get_string_from_ascii()
49 | L2[i * 2 + 0] = f.get_32()
50 | L2[i * 2 + 1] = f.get_32()
51 | pak.loadrsc_index = 0
52 | pak.loadrsc_status = LoadingStatus.READ_FILES
53 | return pak
54 |
55 | enum LoadingStatus {
56 | READ_FILES, CONVERTING
57 | }
58 |
59 | func get_progress() -> float :
60 | if loadrsc_index == -1 : return 1.0
61 | match loadrsc_status :
62 | LoadingStatus.READ_FILES :
63 | return 0.0 + (float(loadrsc_index) / loaded_entries.size()) * 0.5
64 | LoadingStatus.CONVERTING :
65 | return 0.5 + (float(loadrsc_index) / convert_entries.size()) * 0.5
66 | return 0.0
67 |
68 | func poll() -> StringName :
69 | if loadrsc_index == -1 : return &'UNINITIALIZED'
70 | match loadrsc_status :
71 | LoadingStatus.READ_FILES :
72 | return _poll_read_file()
73 | LoadingStatus.CONVERTING :
74 | return _poll_converting()
75 | return &''
76 |
77 | func _poll_read_file() -> StringName :
78 | var rsc : Resource
79 | var path := loadrsc_pathlist[loadrsc_index]
80 |
81 | loadrsc_f.seek(loadrsc_entries[loadrsc_index * 2 + 0])
82 | var datasize := loadrsc_entries[loadrsc_index * 2 + 1]
83 |
84 |
85 | match path.get_extension() :
86 | 'wav' :
87 | var what = QmapbspWAVLoader.load_from_file(loadrsc_f)
88 | if what is StringName : return what
89 | rsc = what
90 | #what.save_to_wav("_c/" + path.get_file())
91 | 'wad' :
92 | var wad = QmapbspWadFile.load_from_file(loadrsc_f)
93 | if wad is StringName : return wad
94 | wad.pal = global_pal
95 | rsc = wad
96 | # 'mdl' :
97 | # var mdl = QmapbspMDLFile.load_from_file(loadrsc_f, global_pal)
98 | # if mdl is StringName : return mdl
99 | # rsc = mdl
100 | 'lmp' :
101 | var res : Array
102 | var ret := QmapbspLmpFile.load_from_file(path, loadrsc_f, res)
103 | match ret :
104 | &'pal' :
105 | if global_pal.is_empty() :
106 | global_pal.append_array(res[0]) # DO NOT SET IT INSTANTLY
107 | &'map' :
108 | if global_pal.is_empty() :
109 | global_map.append_array(res[0]) # DO NOT SET IT INSTANTLY
110 | &'pic' :
111 | convert_indexes.append(loadrsc_index)
112 | convert_entries.append([&'lmp_pic', res[0]])
113 | _ :
114 | rsc = QmapbspRawFile.new()
115 | var mpath := "user://packcache/".path_join(path)
116 | DirAccess.make_dir_absolute(mpath.get_base_dir())
117 | var f := FileAccess.open(mpath, FileAccess.WRITE)
118 | if f :
119 | f.store_buffer(loadrsc_f.get_buffer(datasize))
120 | rsc.raw_path = mpath
121 |
122 | if rsc :
123 | _save_entry(loadrsc_index, rsc)
124 | loadrsc_index += 1
125 | if loadrsc_index >= loaded_entries.size() :
126 | loadrsc_status = LoadingStatus.CONVERTING
127 | loadrsc_index = 0
128 | return StringName()
129 |
130 | static func _make_image(
131 | entrydata : Array,
132 | pal : PackedColorArray,
133 | transparent : int = 255
134 | ) -> Image :
135 | var size : Vector2i = entrydata[0]
136 | if size.x == 0 or size.y == 0 :
137 | return null
138 | var data : PackedByteArray = entrydata[1]
139 | var im := Image.create(size.x, size.y, false, Image.FORMAT_RGBA8)
140 | var TRANSPARENT := Color(Color(), 0.0)
141 | for i in data.size() :
142 | var pali := data[i]
143 | im.set_pixelv(Vector2i(
144 | i % size.x, i / size.x
145 | ), TRANSPARENT if pali == transparent else pal[pali])
146 | return im
147 |
148 | func _poll_converting() -> StringName :
149 | if !convert_indexes.is_empty() :
150 | var idx := convert_indexes[loadrsc_index]
151 | var entry : Array = convert_entries[loadrsc_index]
152 | var entrydata : Array = entry[1]
153 | match entry[0] :
154 | &'lmp_pic' :
155 | _save_entry(idx, _make_image(entrydata, global_pal))
156 | loadrsc_index += 1
157 |
158 | if loadrsc_index >= convert_entries.size() :
159 | #_save_to_pck()
160 | #var d := DirAccess.open("user://pak_imported_cache")
161 | #d.remove("")
162 |
163 | return &'DONE'
164 | return StringName()
165 |
166 | func _save_entry(entry_index : int, resource : Resource) :
167 | # var path := loadrsc_pathlist[entry_index]
168 | # match path.get_extension() :
169 | # 'wad' :
170 | # pass
171 | # 'lmp' : # usually for pictures
172 | # path += ".png"
173 | # path = "user://pak_imported_cache/" + path
174 | # resource.set_meta(&'pakpath', path)
175 | loaded_entries[entry_index] = resource
176 |
177 | #func _save_to_pck() :
178 | # var pck := PCKPacker.new()
179 | # pck.pck_start("user://pak_imported/%s.pck" % filename)
180 | #
181 | # for i in loadrsc_pathlist.size() :
182 | # var rsc : Resource = loaded_entries[i]
183 | # if rsc.has_meta(&'pakpath') : continue
184 | #
185 | #
186 | #
187 | # pck.add_file("res://_QUAKEPCK/%s" % (
188 | #
189 | # ), _get_path_from_entry_index(i))
190 | # pck.flush()
191 |
--------------------------------------------------------------------------------
/quake1_example/quake_draw.gd:
--------------------------------------------------------------------------------
1 | extends Control
2 | class_name QmapbspQuakeDraw
3 |
4 | var hub : QmapbspQuake1Hub
5 |
6 | func draw_quake_text(where : Vector2, text : String,
7 | add : int = 0, scale_ := Vector2.ONE, center : bool = false
8 | ) :
9 | if text.contains('\n') :
10 | var nls := text.split('\n')
11 | for i in nls.size() :
12 | draw_quake_text(where + Vector2(
13 | 0.0, i * 8 * scale_.y
14 | ), nls[i], add, scale_, center)
15 | return
16 |
17 | if center :
18 | where -= Vector2(8 * text.length() * 0.5, 0.0) * scale_.x
19 | var scale_c : float = 8 * scale_.x
20 | for i in text.length() :
21 | draw_quake_character(where, text.unicode_at(i) + add, scale_)
22 | where.x += scale_c
23 |
24 | const SLIDER_RANGE := 10
25 | func draw_slider(where : Vector2, call : Callable,
26 | scale_ := Vector2.ONE
27 | ) :
28 | var v : float = call.call()
29 | v = clampf(v, 0, 1)
30 | draw_quake_character(where + Vector2(-8, 0), 128, scale_)
31 | for i in SLIDER_RANGE :
32 | draw_quake_character(where + Vector2(i * 8.0, 0), 129, scale_)
33 | draw_quake_character(where + Vector2(SLIDER_RANGE * 8.0, 0), 130, scale_)
34 | draw_quake_character(where + Vector2((SLIDER_RANGE - 1) * 8.0 * v, 0), 131, scale_)
35 |
36 | #const CHARSIZE := 0.0625
37 | const CHARSIZE_VEC := Vector2(8, 8)
38 | func draw_quake_character(where : Vector2, num : int, scale_ := Vector2.ONE) :
39 | var pos := Vector2(num & 15, num >> 4) * 8
40 | if !hub : return
41 | draw_texture_rect_region(
42 | hub.load_as_texture("gfx.wad:CONCHARS"),
43 | Rect2(where, CHARSIZE_VEC * scale_),
44 | Rect2(pos, CHARSIZE_VEC)
45 | )
46 |
--------------------------------------------------------------------------------
/quake1_example/raw_file.gd:
--------------------------------------------------------------------------------
1 | extends Resource
2 | class_name QmapbspRawFile
3 | var raw_path : String
4 |
--------------------------------------------------------------------------------
/quake1_example/scene/player.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=3 format=3 uid="uid://bjrvl643qpvsm"]
2 |
3 | [ext_resource type="Script" path="res://quake1_example/scene/player.gd" id="1_ssijj"]
4 |
5 | [sub_resource type="BoxShape3D" id="BoxShape3D_c8hfp"]
6 | size = Vector3(1, 1.5, 1)
7 |
8 | [node name="player" type="CharacterBody3D"]
9 | collision_layer = 15
10 | floor_constant_speed = true
11 | platform_on_leave = 2
12 | script = ExtResource("1_ssijj")
13 |
14 | [node name="col" type="CollisionShape3D" parent="."]
15 | shape = SubResource("BoxShape3D_c8hfp")
16 |
17 | [node name="around" type="Node3D" parent="."]
18 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.688, 0)
19 |
20 | [node name="head" type="Node3D" parent="around"]
21 |
22 | [node name="cam" type="Camera3D" parent="around/head"]
23 | current = true
24 |
25 | [node name="jump" type="AudioStreamPlayer3D" parent="."]
26 |
27 | [node name="origin" type="Marker3D" parent="."]
28 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.668, 0)
29 |
--------------------------------------------------------------------------------
/quake1_example/sky_sky.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Sky" load_steps=3 format=3 uid="uid://ckv8olny8srk3"]
2 |
3 | [ext_resource type="Shader" path="res://quake1_example/material/sky_sky.gdshader" id="1_k5whv"]
4 |
5 | [sub_resource type="ShaderMaterial" id="ShaderMaterial_bo0rt"]
6 | shader = ExtResource("1_k5whv")
7 | shader_parameter/threshold = 0.01
8 |
9 | [resource]
10 | sky_material = SubResource("ShaderMaterial_bo0rt")
11 |
--------------------------------------------------------------------------------
/quake1_example/viewer.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=8 format=3 uid="uid://bfrpdkolpjr3g"]
2 |
3 | [ext_resource type="Script" path="res://quake1_example/viewer.gd" id="1_vs1gv"]
4 | [ext_resource type="PackedScene" uid="uid://dvlurm3ntglha" path="res://quake1_example/console.tscn" id="2_qwu1n"]
5 | [ext_resource type="Script" path="res://quake1_example/hud.gd" id="2_tj87b"]
6 | [ext_resource type="Script" path="res://quake1_example/menu.gd" id="3_044qv"]
7 | [ext_resource type="Script" path="res://quake1_example/message.gd" id="5_6jblb"]
8 | [ext_resource type="Shader" path="res://quake1_example/viewer_fade.gdshader" id="5_upbmq"]
9 |
10 | [sub_resource type="ShaderMaterial" id="ShaderMaterial_spi4i"]
11 | shader = ExtResource("5_upbmq")
12 |
13 | [node name="viewer" type="Control"]
14 | process_mode = 3
15 | texture_filter = 1
16 | layout_mode = 3
17 | anchors_preset = 15
18 | anchor_right = 1.0
19 | anchor_bottom = 1.0
20 | grow_horizontal = 2
21 | grow_vertical = 2
22 | script = ExtResource("1_vs1gv")
23 |
24 | [node name="loading" type="TextureRect" parent="."]
25 | self_modulate = Color(0.5, 0.5, 0.5, 1)
26 | layout_mode = 1
27 | anchors_preset = 15
28 | anchor_right = 1.0
29 | anchor_bottom = 1.0
30 | grow_horizontal = 2
31 | grow_vertical = 2
32 |
33 | [node name="loading" type="TextureRect" parent="loading"]
34 | layout_mode = 1
35 | anchors_preset = 8
36 | anchor_left = 0.5
37 | anchor_top = 0.5
38 | anchor_right = 0.5
39 | anchor_bottom = 0.5
40 | grow_horizontal = 2
41 | grow_vertical = 2
42 | scale = Vector2(3, 3)
43 |
44 | [node name="hud" type="Control" parent="."]
45 | layout_mode = 1
46 | anchors_preset = 15
47 | anchor_right = 1.0
48 | anchor_bottom = 1.0
49 | grow_horizontal = 2
50 | grow_vertical = 2
51 | script = ExtResource("2_tj87b")
52 |
53 | [node name="console" parent="." instance=ExtResource("2_qwu1n")]
54 | layout_mode = 1
55 |
56 | [node name="menu" type="Control" parent="."]
57 | layout_mode = 1
58 | anchors_preset = 15
59 | anchor_right = 1.0
60 | anchor_bottom = 1.0
61 | grow_horizontal = 2
62 | grow_vertical = 2
63 | pivot_offset = Vector2(160, 0)
64 | script = ExtResource("3_044qv")
65 |
66 | [node name="fade" type="ColorRect" parent="menu"]
67 | material = SubResource("ShaderMaterial_spi4i")
68 | layout_mode = 1
69 | anchors_preset = 15
70 | anchor_right = 1.0
71 | anchor_bottom = 1.0
72 | grow_horizontal = 2
73 | grow_vertical = 2
74 |
75 | [node name="message" type="Control" parent="."]
76 | process_mode = 1
77 | layout_mode = 1
78 | anchor_left = 0.5
79 | anchor_top = 0.4
80 | anchor_right = 0.5
81 | anchor_bottom = 0.4
82 | grow_horizontal = 2
83 | grow_vertical = 2
84 | script = ExtResource("5_6jblb")
85 |
86 | [node name="talk" type="AudioStreamPlayer" parent="message"]
87 |
88 | [node name="life" type="Timer" parent="message"]
89 | wait_time = 2.0
90 |
--------------------------------------------------------------------------------
/quake1_example/viewer_fade.gdshader:
--------------------------------------------------------------------------------
1 | shader_type canvas_item;
2 | uniform sampler2D SCREEN_TEXTURE : hint_screen_texture;
3 | void fragment() {
4 | vec3 stex = texture(SCREEN_TEXTURE, SCREEN_UV).xyz;
5 | float g = dot(stex, vec3(0.5, 0.5, 0.5));
6 | COLOR.xyz = vec3(0.5, 0.33, 0.2) * g;
7 | }
8 |
--------------------------------------------------------------------------------
/quake1_example/wad.gd:
--------------------------------------------------------------------------------
1 | extends Resource
2 | class_name QmapbspWadFile
3 |
4 | var pics : Dictionary #
5 |
6 | var cache : Dictionary #
7 |
8 | var pal : PackedColorArray
9 |
10 | func load_pic(name : String) -> ImageTexture :
11 | var itex : ImageTexture = cache.get(name)
12 | if itex : return itex
13 | if pal.size() != 256 : return null
14 |
15 | # load image
16 | var im := QmapbspPakFile._make_image(pics.get(name, [Vector2i(), PackedByteArray()]), pal,
17 | 0 if name == "CONCHARS" else 255
18 | )
19 | itex = ImageTexture.create_from_image(im)
20 | cache[name] = itex
21 | return itex
22 |
23 | static func load_from_file(f : FileAccess) :
24 | # HEADER
25 | var begin := f.get_position()
26 | if f.get_32() != 0x32444157 : return &"WAD_INVALID_MAGIC"
27 | var numentries := f.get_32()
28 | var diroffset := f.get_32()
29 |
30 | var wad := QmapbspWadFile.new()
31 | var pics := wad.pics
32 |
33 | for i in numentries :
34 | f.seek(begin + diroffset + (32 * i)) # 32 is the size of wadentry_t
35 | var offset := f.get_32()
36 | var dsize := f.get_32()
37 | var size := f.get_32()
38 | var type := f.get_8()
39 | var cmprs := f.get_8()
40 | f.get_16()
41 | var name := f.get_buffer(16).get_string_from_ascii()
42 |
43 | f.seek(begin + offset)
44 |
45 | match type :
46 | 0x40 : # @
47 | #Es.append(f.get_buffer(256))
48 | pass
49 | 0x42 : # B
50 | var psize := Vector2i(f.get_32(), f.get_32())
51 | pics[name] = [
52 | psize,
53 | f.get_buffer(psize.x * psize.y)
54 | ]
55 | 0x43 : # C
56 | pass
57 | 0x44 : # D
58 | var psize := Vector2i(128, 128)
59 | pics[name] = [
60 | psize,
61 | f.get_buffer(psize.x * psize.y)
62 | ]
63 | 0x45 : # E
64 | # Es.append([
65 | # f.get_buffer(200 * 320),
66 | # f.get_buffer(128 * 128)
67 | # ])
68 | pass
69 | pass
70 |
71 | return wad
72 |
--------------------------------------------------------------------------------
/quake1_example/wav.gd:
--------------------------------------------------------------------------------
1 | extends Object
2 | class_name QmapbspWAVLoader
3 | # Ah yes . . . Yet Another WAV Loader in Godot
4 | static func load_from_file(f : FileAccess) :
5 | # headers
6 | if f.get_32() != 0x46464952 : return &'WAV_INVALID_HEADER_RIFF'
7 | f.get_32() # who cares lmao
8 | if f.get_32() != 0x45564157 : return &'WAV_INVALID_HEADER_WAVE'
9 | if f.get_32() != 0x20746d66 : return &'WAV_INVALID_HEADER_FORMAT'
10 | var chunks := f.get_32()
11 | var fmttype := f.get_16()
12 | var channel := f.get_16()
13 | var samrate := f.get_32()
14 | f.get_32()
15 | f.get_16()
16 | var bitPsam := f.get_16()
17 | if bitPsam != 8 and bitPsam != 16 : return &'WAV_UNSUPPORTED_BIT_PER_SAMPLE'
18 | if chunks > 16 : f.seek(f.get_position() + chunks - 16)
19 | if f.get_32() != 0x61746164 : return &'WAV_INVALID_HEADER_DATA'
20 | var datsize := f.get_32()
21 | var a := AudioStreamWAV.new()
22 | var data := f.get_buffer(datsize)
23 | for i in data.size() : data[i] = data[i] - 128
24 | a.data = data
25 | a.format = 0 # force 8-bit (OO)???
26 | a.mix_rate = samrate
27 | a.stereo = channel == 2
28 |
29 | # metadata
30 | a.set_meta(&'length', datsize)
31 |
32 | return a
33 |
--------------------------------------------------------------------------------
/quake1_example/world.gd:
--------------------------------------------------------------------------------
1 | extends Node3D
2 | class_name QmapbspQuakeWorld
3 |
4 | func _init() :
5 | process_mode = Node.PROCESS_MODE_PAUSABLE
6 |
--------------------------------------------------------------------------------
/trenchbroom_example/README.md:
--------------------------------------------------------------------------------
1 | # Qmapbsp Trenchbroom Importing
2 |
3 | ## Running the example
4 | To make the importer works. First, Get the Quake tools (qbsp, vis, light, ...). If you have no idea where these are, You can obtain `ericw-tools` [here](https://github.com/ericwa/ericw-tools/releases/).
5 |
6 | 1. Open the `usercfg.tres` resource
7 | 2. Click on `QmapbspCompilationWorkflow` resource in `Compilation Workflow` field inside the `usercfg.tres` to expand its values
8 | 3. Enter your absolute `qbsp.exe` (or `qbsp` on Linux) in `Qbsp path`
9 | 4. Click on `map1.map` file in the FileSystem and open the `Import` tab. Then click `Reimport`
10 |
11 | 
12 |
13 | ## Using my own game configurations
14 | Create a new `QmapbspTrenchbroomGameConfigResource` resource then put your configurations here. And don't forget to insert `QmapbspUserConfig` with the information.
15 |
16 | Then hit `Export configs to Trenchbroom` button to export configurations to Trenchbroom.
17 |
18 | To make MAP files know your game configuration. Go to `Project Settings → Import Defaults → Qmapbsp Trenchbroom → Game Config Path` then put your game configuration resource file path here.
19 |
20 | ## Customing entities
21 | Qmapbsp supports custom entities by scanning all entity scripts inside the chosen folder (`ent_entity_script_directory`) when clicking the `Export configs to Trenchbroom` button. It will read the info by calling the `_qmapbsp_get_fgd_info` method on each script file that has to return a `Dictionary` value with these keyvalues:
22 | ```gdscript
23 | {
24 | property_name1 : [property_description, property_default_value],
25 | property_name2 : [property_description, property_default_value],
26 | property_name3 : [property_description, property_default_value],
27 | . . .
28 | }
29 | ```
30 | ```gdscript
31 | func _qmapbsp_get_fgd_info() -> Dictionary :
32 | return {
33 | "my_prop1" : ["My integer property", 0],
34 | "my_prop2" : ["My string property", "Hello"]
35 | }
36 | ```
37 | Once all entities have been scanned. The game configuration resource will convert to an FGD class format and export to an FGD file.
38 |
39 | To import properties, Create a new method in a script named below with the first argument being `Dictionary`
40 |
41 | - `_qmapbsp_ent_props_pre` : Calls **before** an entity node enters the root node.
42 | - `_qmapbsp_ent_props_post` : Calls **after** an entity node enters the root node. Use this method if you would like to access the parent node
43 |
44 | ```gdscript
45 | func _qmapbsp_ent_props_post(props : Dictionary) -> void :
46 | var my_prop1 = props.get('my_prop1', 0) # returns int
47 | var my_prop2 = props.get('my_prop2', "Bye") # returns string
48 | # do something
49 | ```
50 |
51 | ## Dealing with transparent textures
52 | Since the BSP compiler cannot know transparent textures like glasses or grilles, their faces will be clipped away because the compiler would think they were opaque textures. You could fix this issue by converting these brushes into a brush entity `func_detail_fence`. (You can read more about the entity class [here](http://ericwa.github.io/ericw-tools/doc/qbsp.html#DETAIL%20VARIANTS))
53 |
54 | ## Where's the compiled BSP file ?
55 | It was stored at `res://.godot/qmapbsp/artifact`. So the BSP file shouldn't include in the version control like Git.
56 |
--------------------------------------------------------------------------------
/trenchbroom_example/classes/light.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends OmniLight3D
3 |
4 | # { . . . }
5 | func _qmapbsp_get_fgd_info() -> Dictionary :
6 | return {
7 | "range" : ["The light's radius", omni_range],
8 | "color" : ["The light's color", light_color],
9 | "attenuation" : ["The light's attenuation curve", omni_attenuation],
10 | "energy" : ["The light's strength multiplier", light_energy],
11 | "specular" : ["The intensity of the specular blob in objects affected by the light", light_specular],
12 | "shadow" : ["If true, the light will cast real-time shadows", shadow_enabled],
13 | }
14 |
15 | func _qmapbsp_ent_props_pre(d : Dictionary) :
16 | omni_range = d.get("range", omni_range)
17 | light_color = d.get("color", light_color)
18 | omni_attenuation = d.get("attenuation", omni_attenuation)
19 | light_energy = d.get("energy", light_energy)
20 | light_specular = d.get("specular", light_specular)
21 | shadow_enabled = d.get("shadow", shadow_enabled)
22 |
--------------------------------------------------------------------------------
/trenchbroom_example/classes/light_static.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends "res://trenchbroom_example/classes/light.gd"
3 |
4 | func _init() :
5 | super()
6 | shadow_enabled = true
7 |
8 | func _qmapbsp_ent_props_pre(d : Dictionary) :
9 | super(d)
10 | light_bake_mode = Light3D.BAKE_STATIC
11 |
--------------------------------------------------------------------------------
/trenchbroom_example/classes/test_sphere.gd:
--------------------------------------------------------------------------------
1 | @tool
2 | extends RigidBody3D
3 |
4 | func _qmapbsp_ent_props_post(d : Dictionary) :
5 | var radius : float = d.get('radius', 0.5)
6 | if radius != 0.5 :
7 | # clone
8 | var ncol : CollisionShape3D = $col
9 | var nmesh : MeshInstance3D = $mesh
10 | get_parent().set_editable_instance(self, true)
11 | var col : SphereShape3D = ncol.shape.duplicate()
12 | var mesh : SphereMesh = nmesh.mesh.duplicate()
13 | col.radius = radius
14 | mesh.radius = radius
15 | mesh.height = radius * 2.0
16 | ncol.shape = col
17 | nmesh.mesh = mesh
18 |
19 | func _qmapbsp_get_fgd_info() -> Dictionary :
20 | return {
21 | "radius" : ["The sphere's radius", 0.5],
22 | }
23 |
--------------------------------------------------------------------------------
/trenchbroom_example/classes/test_sphere.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=4 format=3 uid="uid://b240v1psxwvew"]
2 |
3 | [ext_resource type="Script" path="res://trenchbroom_example/classes/test_sphere.gd" id="1_s3wf2"]
4 |
5 | [sub_resource type="SphereShape3D" id="SphereShape3D_p31m1"]
6 |
7 | [sub_resource type="SphereMesh" id="SphereMesh_bae44"]
8 |
9 | [node name="test_sphere" type="RigidBody3D"]
10 | script = ExtResource("1_s3wf2")
11 |
12 | [node name="col" type="CollisionShape3D" parent="."]
13 | shape = SubResource("SphereShape3D_p31m1")
14 |
15 | [node name="mesh" type="MeshInstance3D" parent="."]
16 | mesh = SubResource("SphereMesh_bae44")
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/icon.png
--------------------------------------------------------------------------------
/trenchbroom_example/icon.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bd03r4qixned2"
6 | path="res://.godot/imported/icon.png-e775f820cb235ab9f5b46b22416d3191.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/icon.png"
14 | dest_files=["res://.godot/imported/icon.png-e775f820cb235ab9f5b46b22416d3191.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/inherited/map4_lightmap.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://b3r3tyb0nv1gj"]
2 |
3 | [ext_resource type="PackedScene" uid="uid://c2sspd5hoamrc" path="res://trenchbroom_example/maps/4_lightmap.map" id="1_h6uyr"]
4 |
5 | [node name="map" instance=ExtResource("1_h6uyr")]
6 |
7 | [node name="light_static0001" parent="." index="1"]
8 | transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -0.125, 2.84656, -2.125)
9 |
10 | [node name="lightmap" type="LightmapGI" parent="." index="2"]
11 | quality = 3
12 | interior = true
13 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/1_geometry.map:
--------------------------------------------------------------------------------
1 | // Game: Qmapbsp Test Game
2 | // Format: Standard
3 | // entity 0
4 | {
5 | "classname" "worldspawn"
6 | "_tb_textures" "trenchbroom_example/textures"
7 | // brush 0
8 | {
9 | ( -128 -64 -32 ) ( -128 -63 -32 ) ( -128 -64 -31 ) simple1 0 0 0 1 1
10 | ( -64 -128 -32 ) ( -64 -128 -31 ) ( -63 -128 -32 ) simple1 0 0 0 1 1
11 | ( -64 -64 -64 ) ( -63 -64 -64 ) ( -64 -63 -64 ) simple1 0 0 0 1 1
12 | ( 64 64 0 ) ( 64 65 0 ) ( 65 64 0 ) simple5 0 0 0 1 1
13 | ( 64 128 0 ) ( 65 128 0 ) ( 64 128 1 ) simple1 0 0 0 1 1
14 | ( 128 64 0 ) ( 128 64 1 ) ( 128 65 0 ) simple1 0 0 0 1 1
15 | }
16 | // brush 1
17 | {
18 | ( -128 128 -16 ) ( -128 129 -16 ) ( -128 128 -15 ) simple1 0 0 0 1 1
19 | ( -144 128 -16 ) ( -144 128 -15 ) ( -143 128 -16 ) simple2 -64 0 0 2 2
20 | ( -144 128 -64 ) ( -143 128 -64 ) ( -144 129 -64 ) simple1 0 0 0 1 1
21 | ( 128 192 256 ) ( 128 193 256 ) ( 129 192 256 ) simple1 0 0 0 1 1
22 | ( 128 192 16 ) ( 129 192 16 ) ( 128 192 17 ) simple1 0 0 0 1 1
23 | ( 128 192 16 ) ( 128 192 17 ) ( 128 193 16 ) simple1 0 0 0 1 1
24 | }
25 | // brush 2
26 | {
27 | ( -64 -32 96 ) ( -64 32 0 ) ( -64 32 96 ) simple2 0 0 0 128 128
28 | ( -32 -64 96 ) ( -64 -32 0 ) ( -64 -32 96 ) simple2 0 0 0 128 128
29 | ( -32 64 96 ) ( -64 32 0 ) ( -32 64 0 ) simple2 0 0 0 128 128
30 | ( 32 -64 96 ) ( -32 -64 0 ) ( -32 -64 96 ) simple2 0 0 0 128 128
31 | ( 64 32 0 ) ( 32 -64 0 ) ( 64 -32 0 ) simple1 0 0 0 1 1
32 | ( 64 32 16 ) ( -32 64 16 ) ( 32 64 16 ) simple4 64 64 0 1 1
33 | ( 32 64 96 ) ( -32 64 0 ) ( 32 64 0 ) simple2 0 0 0 128 128
34 | ( 64 -32 96 ) ( 32 -64 0 ) ( 32 -64 96 ) simple2 0 0 0 128 128
35 | ( 64 32 96 ) ( 32 64 0 ) ( 64 32 0 ) simple2 0 0 0 128 128
36 | ( 64 32 96 ) ( 64 -32 0 ) ( 64 -32 96 ) simple2 0 0 0 128 128
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/1_geometry.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://b1d4ejl7k8ce8"
6 | path="res://.godot/imported/1_geometry.map-5c6e893c559db4735e7e9eddb011fd2d.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/1_geometry.map"
11 | dest_files=["res://.godot/imported/1_geometry.map-5c6e893c559db4735e7e9eddb011fd2d.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/2_group.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://di611258dgqd0"
6 | path="res://.godot/imported/2_group.map-4aa24d9d875bbffe0f2cbaaef475517c.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/2_group.map"
11 | dest_files=["res://.godot/imported/2_group.map-4aa24d9d875bbffe0f2cbaaef475517c.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/3_special.map:
--------------------------------------------------------------------------------
1 | // Game: Qmapbsp Test Game
2 | // Format: Standard
3 | // entity 0
4 | {
5 | "classname" "worldspawn"
6 | "_tb_textures" "trenchbroom_example/textures;trenchbroom_example/textures/subfolder"
7 | // brush 0
8 | {
9 | ( -320 128 -16 ) ( -320 129 -16 ) ( -320 128 -15 ) subfolder/simple -64 0 0 1 1
10 | ( -256 -320 -16 ) ( -256 -320 -15 ) ( -255 -320 -16 ) subfolder/simple -64 0 0 1 1
11 | ( -256 128 -128 ) ( -255 128 -128 ) ( -256 129 -128 ) subfolder/simple -64 64 0 1 1
12 | ( -128 256 0 ) ( -128 257 0 ) ( -127 256 0 ) subfolder/simple -64 64 0 1 1
13 | ( -128 320 16 ) ( -127 320 16 ) ( -128 320 17 ) subfolder/simple -64 0 0 1 1
14 | ( 320 256 16 ) ( 320 256 17 ) ( 320 257 16 ) subfolder/simple -64 0 0 1 1
15 | }
16 | // brush 1
17 | {
18 | ( -32 256 0 ) ( -32 257 0 ) ( -32 256 1 ) subfolder/simple -64 0 0 1 1
19 | ( -32 256 0 ) ( -32 256 1 ) ( -31 256 0 ) subfolder/simple -64 0 0 1 1
20 | ( -32 256 0 ) ( -31 256 0 ) ( -32 257 0 ) subfolder/simple -64 64 0 1 1
21 | ( 32 320 128 ) ( 32 321 128 ) ( 33 320 128 ) subfolder/simple -64 64 0 1 1
22 | ( 32 320 128 ) ( 33 320 128 ) ( 32 320 129 ) subfolder/simple -64 0 0 1 1
23 | ( 32 320 128 ) ( 32 320 129 ) ( 32 321 128 ) subfolder/simple -64 0 0 1 1
24 | }
25 | // brush 2
26 | {
27 | ( 160 256 0 ) ( 160 257 0 ) ( 160 256 1 ) subfolder/simple -64 0 0 1 1
28 | ( 160 256 0 ) ( 160 256 1 ) ( 161 256 0 ) subfolder/simple 0 0 0 1 1
29 | ( 160 256 0 ) ( 161 256 0 ) ( 160 257 0 ) subfolder/simple 0 64 0 1 1
30 | ( 224 320 128 ) ( 224 321 128 ) ( 225 320 128 ) subfolder/simple 0 64 0 1 1
31 | ( 224 320 128 ) ( 225 320 128 ) ( 224 320 129 ) subfolder/simple 0 0 0 1 1
32 | ( 224 320 128 ) ( 224 320 129 ) ( 224 321 128 ) subfolder/simple -64 0 0 1 1
33 | }
34 | // brush 3
35 | {
36 | ( -224 256 0 ) ( -224 257 0 ) ( -224 256 1 ) subfolder/simple -64 0 0 1 1
37 | ( -224 256 0 ) ( -224 256 1 ) ( -223 256 0 ) subfolder/simple 0 0 0 1 1
38 | ( -224 256 0 ) ( -223 256 0 ) ( -224 257 0 ) subfolder/simple 0 64 0 1 1
39 | ( -160 320 128 ) ( -160 321 128 ) ( -159 320 128 ) subfolder/simple 0 64 0 1 1
40 | ( -160 320 128 ) ( -159 320 128 ) ( -160 320 129 ) subfolder/simple 0 0 0 1 1
41 | ( -160 320 128 ) ( -160 320 129 ) ( -160 321 128 ) subfolder/simple -64 0 0 1 1
42 | }
43 | // brush 4
44 | {
45 | ( -256 -128 -128 ) ( -256 -127 -128 ) ( -256 -128 -127 ) skip 0 0 0 1 1
46 | ( -272 -64 -128 ) ( -272 -64 -127 ) ( -271 -64 -128 ) skip 0 0 0 1 1
47 | ( -272 -128 0 ) ( -271 -128 0 ) ( -272 -127 0 ) skip 0 0 0 1 1
48 | ( -128 0 64 ) ( -128 1 64 ) ( -127 0 64 ) skip 0 0 0 1 1
49 | ( -128 0 0 ) ( -127 0 0 ) ( -128 0 1 ) skip 0 0 0 1 1
50 | ( -192 0 0 ) ( -192 0 1 ) ( -192 1 0 ) skip 0 0 0 1 1
51 | }
52 | // brush 5
53 | {
54 | ( -128 -128 -128 ) ( -128 -127 -128 ) ( -128 -128 -127 ) skip 0 0 0 1 1
55 | ( -144 -64 -128 ) ( -144 -64 -127 ) ( -143 -64 -128 ) skip 0 0 0 1 1
56 | ( -144 -128 0 ) ( -143 -128 0 ) ( -144 -127 0 ) skip 0 0 0 1 1
57 | ( 0 0 64 ) ( 0 1 64 ) ( 1 0 64 ) simple2 0 0 0 1 1
58 | ( 0 0 0 ) ( 1 0 0 ) ( 0 0 1 ) skip 0 0 0 1 1
59 | ( -64 0 0 ) ( -64 0 1 ) ( -64 1 0 ) skip 0 0 0 1 1
60 | }
61 | }
62 | // entity 1
63 | {
64 | "classname" "func_detail_fence"
65 | // brush 0
66 | {
67 | ( -256 128 -128 ) ( -256 129 -128 ) ( -256 128 -127 ) subfolder/glass1 -64 0 0 1 1
68 | ( -256 128 -128 ) ( -256 128 -127 ) ( -255 128 -128 ) subfolder/glass1 -64 0 0 1 1
69 | ( -256 128 0 ) ( -255 128 0 ) ( -256 129 0 ) subfolder/glass1 -64 64 0 1 1
70 | ( -128 256 128 ) ( -128 257 128 ) ( -127 256 128 ) subfolder/glass1 -64 64 0 1 1
71 | ( -128 256 0 ) ( -127 256 0 ) ( -128 256 1 ) subfolder/glass1 -64 0 0 1 1
72 | ( -128 256 0 ) ( -128 256 1 ) ( -128 257 0 ) subfolder/glass1 -64 0 0 1 1
73 | }
74 | }
75 | // entity 2
76 | {
77 | "classname" "func_detail_wall"
78 | // brush 0
79 | {
80 | ( -64 128 -128 ) ( -64 129 -128 ) ( -64 128 -127 ) simple4 0 0 0 1 1
81 | ( -64 128 -128 ) ( -64 128 -127 ) ( -63 128 -128 ) simple4 -64 0 0 1 1
82 | ( -64 128 0 ) ( -63 128 0 ) ( -64 129 0 ) simple4 -64 64 0 1 1
83 | ( 64 256 128 ) ( 64 257 128 ) ( 65 256 128 ) simple4 -64 0 0 1 1
84 | ( 64 256 0 ) ( 65 256 0 ) ( 64 256 1 ) simple4 -64 0 0 1 1
85 | ( 64 256 0 ) ( 64 256 1 ) ( 64 257 0 ) simple4 0 0 0 1 1
86 | }
87 | }
88 | // entity 3
89 | {
90 | "classname" "func_detail_illusionary"
91 | // brush 0
92 | {
93 | ( 128 128 -128 ) ( 128 129 -128 ) ( 128 128 -127 ) simple3 0 0 0 1 1
94 | ( 128 128 -128 ) ( 128 128 -127 ) ( 129 128 -128 ) simple3 0 0 0 1 1
95 | ( 128 128 0 ) ( 129 128 0 ) ( 128 129 0 ) simple3 0 64 0 1 1
96 | ( 256 256 128 ) ( 256 257 128 ) ( 257 256 128 ) simple3 0 0 0 1 1
97 | ( 256 256 0 ) ( 257 256 0 ) ( 256 256 1 ) simple3 0 0 0 1 1
98 | ( 256 256 0 ) ( 256 256 1 ) ( 256 257 0 ) simple3 0 0 0 1 1
99 | }
100 | }
101 | // entity 4
102 | {
103 | "classname" "func_detail_fence"
104 | // brush 0
105 | {
106 | ( 128 -64 -128 ) ( 128 -63 -128 ) ( 128 -64 -127 ) subfolder/fence1 64 0 0 1 1
107 | ( 128 -64 -128 ) ( 128 -64 -127 ) ( 129 -64 -128 ) subfolder/fence1 0 0 0 1 1
108 | ( 128 -64 0 ) ( 129 -64 0 ) ( 128 -63 0 ) subfolder/fence1 0 0 0 1 1
109 | ( 256 64 128 ) ( 256 65 128 ) ( 257 64 128 ) subfolder/fence1 0 64 0 1 1
110 | ( 256 64 0 ) ( 257 64 0 ) ( 256 64 1 ) subfolder/fence1 0 0 0 1 1
111 | ( 256 64 0 ) ( 256 64 1 ) ( 256 65 0 ) subfolder/fence1 64 0 0 1 1
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/3_special.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://b1cnk32jw81u2"
6 | path="res://.godot/imported/3_special.map-174d2570424ae77c8039194e075a19ee.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/3_special.map"
11 | dest_files=["res://.godot/imported/3_special.map-174d2570424ae77c8039194e075a19ee.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/4_lightmap.map:
--------------------------------------------------------------------------------
1 | // Game: Qmapbsp Test Game
2 | // Format: Standard
3 | // entity 0
4 | {
5 | "classname" "worldspawn"
6 | "_tb_textures" "trenchbroom_example/textures;trenchbroom_example/textures/subfolder"
7 | // brush 0
8 | {
9 | ( -128 -128 -128 ) ( -128 -127 -128 ) ( -128 -128 -127 ) simple2 0 0 0 1 1
10 | ( -64 -128 -128 ) ( -64 -128 -127 ) ( -63 -128 -128 ) simple2 0 0 0 1 1
11 | ( -64 -128 -64 ) ( -63 -128 -64 ) ( -64 -127 -64 ) simple2 0 0 0 1 1
12 | ( 128 144 0 ) ( 128 145 0 ) ( 129 144 0 ) simple2 0 0 0 1 1
13 | ( 128 128 128 ) ( 129 128 128 ) ( 128 128 129 ) simple2 0 0 0 1 1
14 | ( 128 144 128 ) ( 128 144 129 ) ( 128 145 128 ) simple2 0 0 0 1 1
15 | }
16 | // brush 1
17 | {
18 | ( -128 128 -64 ) ( -128 129 -64 ) ( -128 128 -63 ) simple2 0 0 0 1 1
19 | ( -128 128 -64 ) ( -128 128 -63 ) ( -127 128 -64 ) simple2 0 0 0 1 1
20 | ( -128 128 0 ) ( -127 128 0 ) ( -128 129 0 ) simple2 0 0 0 1 1
21 | ( 128 192 256 ) ( 128 193 256 ) ( 129 192 256 ) simple2 0 0 0 1 1
22 | ( 128 192 0 ) ( 129 192 0 ) ( 128 192 1 ) simple2 0 0 0 1 1
23 | ( 128 192 0 ) ( 128 192 1 ) ( 128 193 0 ) simple2 0 0 0 1 1
24 | }
25 | // brush 2
26 | {
27 | ( 128 -128 0 ) ( 128 -127 0 ) ( 128 -128 1 ) simple2 0 0 0 1 1
28 | ( 128 -128 0 ) ( 128 -128 1 ) ( 129 -128 0 ) simple2 0 0 0 1 1
29 | ( 128 -128 0 ) ( 129 -128 0 ) ( 128 -127 0 ) simple2 0 0 0 1 1
30 | ( 192 128 256 ) ( 192 129 256 ) ( 193 128 256 ) simple2 0 0 0 1 1
31 | ( 192 128 256 ) ( 193 128 256 ) ( 192 128 257 ) simple2 0 0 0 1 1
32 | ( 192 128 256 ) ( 192 128 257 ) ( 192 129 256 ) simple2 0 0 0 1 1
33 | }
34 | // brush 3
35 | {
36 | ( -192 -128 0 ) ( -192 -127 0 ) ( -192 -128 1 ) simple2 0 0 0 1 1
37 | ( -192 -128 0 ) ( -192 -128 1 ) ( -191 -128 0 ) simple2 0 0 0 1 1
38 | ( -192 -128 0 ) ( -191 -128 0 ) ( -192 -127 0 ) simple2 0 0 0 1 1
39 | ( -128 128 256 ) ( -128 129 256 ) ( -127 128 256 ) simple2 0 0 0 1 1
40 | ( -128 128 256 ) ( -127 128 256 ) ( -128 128 257 ) simple2 0 0 0 1 1
41 | ( -128 128 256 ) ( -128 128 257 ) ( -128 129 256 ) simple2 0 0 0 1 1
42 | }
43 | // brush 4
44 | {
45 | ( -128 -192 0 ) ( -128 -191 0 ) ( -128 -192 1 ) simple2 0 0 0 1 1
46 | ( -128 -192 0 ) ( -128 -192 1 ) ( -127 -192 0 ) simple2 0 0 0 1 1
47 | ( -128 -192 0 ) ( -127 -192 0 ) ( -128 -191 0 ) simple2 0 0 0 1 1
48 | ( 128 -128 256 ) ( 128 -127 256 ) ( 129 -128 256 ) simple2 0 0 0 1 1
49 | ( 128 -128 256 ) ( 129 -128 256 ) ( 128 -128 257 ) simple2 0 0 0 1 1
50 | ( 128 -128 256 ) ( 128 -128 257 ) ( 128 -127 256 ) simple2 0 0 0 1 1
51 | }
52 | // brush 5
53 | {
54 | ( -128 -192 256 ) ( -128 -191 256 ) ( -128 -192 257 ) simple2 0 0 0 1 1
55 | ( -128 -128 256 ) ( -128 -128 257 ) ( -127 -128 256 ) simple2 0 0 0 1 1
56 | ( -128 -192 256 ) ( -127 -192 256 ) ( -128 -191 256 ) simple2 0 0 0 1 1
57 | ( 128 -128 320 ) ( 128 -127 320 ) ( 129 -128 320 ) simple2 0 0 0 1 1
58 | ( 128 128 384 ) ( 129 128 384 ) ( 128 128 385 ) simple2 0 0 0 1 1
59 | ( 128 -128 384 ) ( 128 -128 385 ) ( 128 -127 384 ) simple2 0 0 0 1 1
60 | }
61 | // brush 6
62 | {
63 | ( -11.712812921102053 20.28718707889803 64 ) ( 20.28718707889802 75.71281292110206 0 ) ( 20.28718707889802 75.71281292110206 64 ) simple4 40.57437 0 0 0.8660254 1
64 | ( 43.71281292110197 -11.71281292110205 64 ) ( -11.712812921102053 20.28718707889803 0 ) ( -11.712812921102053 20.28718707889803 64 ) simple4 13.524792 0 0 0.8660254 1
65 | ( 75.71281292110206 43.71281292110197 0 ) ( -11.712812921102053 20.28718707889803 0 ) ( 43.71281292110197 -11.71281292110205 0 ) simple4 20.287186 -52.287186 330 1 1
66 | ( 75.71281292110206 43.71281292110197 64 ) ( -11.712812921102053 20.28718707889803 64 ) ( 20.28718707889802 75.71281292110206 64 ) simple4 20.287186 -52.287186 330 1 1
67 | ( 75.71281292110206 43.71281292110197 64 ) ( 20.28718707889802 75.71281292110206 0 ) ( 75.71281292110206 43.71281292110197 0 ) simple4 -23.425629 0 0 0.8660254 1
68 | ( 75.71281292110206 43.71281292110197 64 ) ( 43.71281292110197 -11.71281292110205 0 ) ( 43.71281292110197 -11.71281292110205 64 ) simple4 77.524796 0 0 0.8660254 1
69 | }
70 | // brush 7
71 | {
72 | ( 25.372583002030417 -7.191835884530821 128 ) ( 8.808164115469175 54.62741699796959 64 ) ( 8.808164115469175 54.62741699796959 128 ) simple4 71.44553 64 0 0.9659258 1
73 | ( 70.62741699796958 71.19183588453083 128 ) ( 8.808164115469175 54.62741699796959 64 ) ( 70.62741699796958 71.19183588453083 64 ) simple4 -9.118881 64 0 0.9659258 1
74 | ( 70.62741699796958 71.19183588453083 64 ) ( 25.372583002030417 -7.191835884530821 64 ) ( 87.19183588453082 9.372583002030424 64 ) simple4 -22.646648 -77.51369 15.000003 1 1
75 | ( 70.62741699796958 71.19183588453083 128 ) ( 25.372583002030417 -7.191835884530821 128 ) ( 8.808164115469175 54.62741699796959 128 ) simple4 -22.646648 -77.51369 15.000003 1 1
76 | ( 87.19183588453082 9.372583002030424 128 ) ( 25.372583002030417 -7.191835884530821 64 ) ( 25.372583002030417 -7.191835884530821 128 ) simple4 -26.267632 64 0 0.9659258 1
77 | ( 70.62741699796958 71.19183588453083 128 ) ( 87.19183588453082 9.372583002030424 64 ) ( 87.19183588453082 9.372583002030424 128 ) simple4 54.296787 64 0 0.9659258 1
78 | }
79 | // brush 8
80 | {
81 | ( -75.71281292110204 -43.71281292110203 0 ) ( -76.21281292110204 -42.84678751731758 0 ) ( -75.71281292110204 -43.71281292110203 1 ) simple5 -13.524792 0 0 0.8660254 1
82 | ( -52.28718707889796 43.71281292110203 64 ) ( -51.421161675113524 44.21281292110203 64 ) ( -52.28718707889796 43.71281292110203 65 ) simple5 60.376045 0 0 0.8660254 1
83 | ( -75.71281292110204 -43.71281292110203 0 ) ( -74.84678751731761 -43.21281292110203 0 ) ( -76.21281292110204 -42.84678751731758 0 ) simple5 23.425625 64 30 1 1
84 | ( -60.28718707889796 57.569219381653056 64 ) ( -60.78718707889796 58.43524478543749 64 ) ( -59.42116167511352 58.069219381653056 64 ) simple5 23.425625 64 30 1 1
85 | ( -75.71281292110204 -43.71281292110203 0 ) ( -75.71281292110204 -43.71281292110203 1 ) ( -74.84678751731761 -43.21281292110203 0 ) simple5 23.425629 0 0 0.8660254 1
86 | ( -60.28718707889796 57.569219381653056 64 ) ( -60.28718707889796 57.569219381653056 65 ) ( -60.78718707889796 58.43524478543749 64 ) simple5 -50.47521 0 0 0.8660254 1
87 | }
88 | }
89 | // entity 1
90 | {
91 | "classname" "light_static"
92 | "origin" "-12 -68 116"
93 | "energy" "10"
94 | "range" "20"
95 | }
96 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/4_lightmap.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://c2sspd5hoamrc"
6 | path="res://.godot/imported/4_lightmap.map-cd5673bfa8c154b203fc18bbe9c76552.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/4_lightmap.map"
11 | dest_files=["res://.godot/imported/4_lightmap.map-cd5673bfa8c154b203fc18bbe9c76552.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/5_interior.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://c7skj88wu4yv"
6 | path="res://.godot/imported/5_interior.map-89bb4613bf0badff1e521864e0f76f46.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/5_interior.map"
11 | dest_files=["res://.godot/imported/5_interior.map-89bb4613bf0badff1e521864e0f76f46.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path="res://trenchbroom_example/maps/occluder_config.tres"
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/6_occluder.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://dfof01qg4l1ah"
6 | path="res://.godot/imported/6_occluder.map-226ac17319dc991f40a61300c7e27cd9.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/6_occluder.map"
11 | dest_files=["res://.godot/imported/6_occluder.map-226ac17319dc991f40a61300c7e27cd9.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/7_occluder_shrink.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://c82c7eoln3shj"
6 | path="res://.godot/imported/7_occluder_shrink.map-6257e6ca181031340d80df3b5f7e2eed.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/7_occluder_shrink.map"
11 | dest_files=["res://.godot/imported/7_occluder_shrink.map-6257e6ca181031340d80df3b5f7e2eed.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path="res://trenchbroom_example/maps/occluder_shrink_test_config.tres"
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/7_occluder_shrink.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=2 format=3 uid="uid://c1smett155dgj"]
2 |
3 | [ext_resource type="PackedScene" uid="uid://c82c7eoln3shj" path="res://trenchbroom_example/maps/7_occluder_shrink.map" id="1_yluif"]
4 |
5 | [node name="map" instance=ExtResource("1_yluif")]
6 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/8_smoothing.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://c4cikt4m763lr"
6 | path="res://.godot/imported/8_smoothing.map-9769608c8ff41c067f23720246be486c.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/8_smoothing.map"
11 | dest_files=["res://.godot/imported/8_smoothing.map-9769608c8ff41c067f23720246be486c.scn"]
12 |
13 | [params]
14 |
15 | game_config_path="res://trenchbroom_example/my_game.tres"
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/9_node_exterior.map:
--------------------------------------------------------------------------------
1 | // Game: Qmapbsp Test Game
2 | // Format: Standard
3 | // entity 0
4 | {
5 | "classname" "worldspawn"
6 | "_tb_textures" "trenchbroom_example/textures"
7 | // brush 0
8 | {
9 | ( -64 -64 32 ) ( -64 -63 32 ) ( -64 -64 33 ) simple1 0 0 0 1 1
10 | ( -64 -64 32 ) ( -64 -64 33 ) ( -63 -64 32 ) simple1 0 0 0 1 1
11 | ( -64 -64 -64 ) ( -63 -64 -64 ) ( -64 -63 -64 ) simple1 0 0 0 1 1
12 | ( 64 64 64 ) ( 64 65 64 ) ( 65 64 64 ) simple1 0 0 0 1 1
13 | ( 64 64 64 ) ( 65 64 64 ) ( 64 64 65 ) simple1 0 0 0 1 1
14 | ( 64 64 64 ) ( 64 64 65 ) ( 64 65 64 ) simple1 0 0 0 1 1
15 | }
16 | }
17 | // entity 1
18 | {
19 | "classname" "light"
20 | "origin" "4 -4 -60"
21 | }
22 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/9_node_exterior.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://tbv2gbb631xu"
6 | path="res://.godot/imported/9_node_exterior.map-ce6766964b3719d512eefd139f2ea422.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/9_node_exterior.map"
11 | dest_files=["res://.godot/imported/9_node_exterior.map-ce6766964b3719d512eefd139f2ea422.scn"]
12 |
13 | [params]
14 |
15 | game_config_path=""
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/9_node_interior.map:
--------------------------------------------------------------------------------
1 | // Game: Qmapbsp Test Game
2 | // Format: Standard
3 | // entity 0
4 | {
5 | "classname" "worldspawn"
6 | "_tb_textures" "trenchbroom_example/textures"
7 | // brush 0
8 | {
9 | ( -64 -64 32 ) ( -64 -63 32 ) ( -64 -64 33 ) simple1 0 0 0 1 1
10 | ( -64 -64 32 ) ( -64 -64 33 ) ( -63 -64 32 ) simple1 0 0 0 1 1
11 | ( -64 -64 -128 ) ( -63 -64 -128 ) ( -64 -63 -128 ) simple1 0 0 0 1 1
12 | ( 64 64 -64 ) ( 64 65 -64 ) ( 65 64 -64 ) simple1 0 0 0 1 1
13 | ( 64 64 64 ) ( 65 64 64 ) ( 64 64 65 ) simple1 0 0 0 1 1
14 | ( 64 64 64 ) ( 64 64 65 ) ( 64 65 64 ) simple1 0 0 0 1 1
15 | }
16 | // brush 1
17 | {
18 | ( 64 -64 96 ) ( 64 -63 96 ) ( 64 -64 97 ) simple1 0 0 0 1 1
19 | ( 64 -64 96 ) ( 64 -64 97 ) ( 65 -64 96 ) simple1 0 0 0 1 1
20 | ( 64 -64 -64 ) ( 65 -64 -64 ) ( 64 -63 -64 ) simple1 0 0 0 1 1
21 | ( 192 64 64 ) ( 192 65 64 ) ( 193 64 64 ) simple1 0 0 0 1 1
22 | ( 192 64 128 ) ( 193 64 128 ) ( 192 64 129 ) simple1 0 0 0 1 1
23 | ( 128 64 128 ) ( 128 64 129 ) ( 128 65 128 ) simple1 0 0 0 1 1
24 | }
25 | // brush 2
26 | {
27 | ( -128 -64 96 ) ( -128 -63 96 ) ( -128 -64 97 ) simple1 0 0 0 1 1
28 | ( -128 -64 96 ) ( -128 -64 97 ) ( -127 -64 96 ) simple1 0 0 0 1 1
29 | ( -128 -64 -64 ) ( -127 -64 -64 ) ( -128 -63 -64 ) simple1 0 0 0 1 1
30 | ( 0 64 64 ) ( 0 65 64 ) ( 1 64 64 ) simple1 0 0 0 1 1
31 | ( 0 64 128 ) ( 1 64 128 ) ( 0 64 129 ) simple1 0 0 0 1 1
32 | ( -64 64 128 ) ( -64 64 129 ) ( -64 65 128 ) simple1 0 0 0 1 1
33 | }
34 | // brush 3
35 | {
36 | ( -64 -64 224 ) ( -64 -63 224 ) ( -64 -64 225 ) simple1 0 0 0 1 1
37 | ( -64 -64 224 ) ( -64 -64 225 ) ( -63 -64 224 ) simple1 0 0 0 1 1
38 | ( -64 -64 64 ) ( -63 -64 64 ) ( -64 -63 64 ) simple1 0 0 0 1 1
39 | ( 64 64 128 ) ( 64 65 128 ) ( 65 64 128 ) simple1 0 0 0 1 1
40 | ( 64 64 256 ) ( 65 64 256 ) ( 64 64 257 ) simple1 0 0 0 1 1
41 | ( 64 64 256 ) ( 64 64 257 ) ( 64 65 256 ) simple1 0 0 0 1 1
42 | }
43 | // brush 4
44 | {
45 | ( -64 -192 96 ) ( -64 -191 96 ) ( -64 -192 97 ) simple1 0 0 0 1 1
46 | ( -64 -128 96 ) ( -64 -128 97 ) ( -63 -128 96 ) simple1 0 0 0 1 1
47 | ( -64 -192 -64 ) ( -63 -192 -64 ) ( -64 -191 -64 ) simple1 0 0 0 1 1
48 | ( 64 -64 64 ) ( 64 -63 64 ) ( 65 -64 64 ) simple1 0 0 0 1 1
49 | ( 64 -64 128 ) ( 65 -64 128 ) ( 64 -64 129 ) simple1 0 0 0 1 1
50 | ( 64 -64 128 ) ( 64 -64 129 ) ( 64 -63 128 ) simple1 0 0 0 1 1
51 | }
52 | // brush 5
53 | {
54 | ( -64 0 96 ) ( -64 1 96 ) ( -64 0 97 ) simple1 0 0 0 1 1
55 | ( -64 64 96 ) ( -64 64 97 ) ( -63 64 96 ) simple1 0 0 0 1 1
56 | ( -64 0 -64 ) ( -63 0 -64 ) ( -64 1 -64 ) simple1 0 0 0 1 1
57 | ( 64 128 64 ) ( 64 129 64 ) ( 65 128 64 ) simple1 0 0 0 1 1
58 | ( 64 128 128 ) ( 65 128 128 ) ( 64 128 129 ) simple1 0 0 0 1 1
59 | ( 64 128 128 ) ( 64 128 129 ) ( 64 129 128 ) simple1 0 0 0 1 1
60 | }
61 | }
62 | // entity 1
63 | {
64 | "classname" "light"
65 | "origin" "4 -4 -60"
66 | }
67 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/9_node_interior.map.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="qmapbsp.trenchbroom_map"
4 | type="PackedScene"
5 | uid="uid://d2i7g0i7bstp2"
6 | path="res://.godot/imported/9_node_interior.map-df3f5b606d2a3ad853d8bdbd3ef3702f.scn"
7 |
8 | [deps]
9 |
10 | source_file="res://trenchbroom_example/maps/9_node_interior.map"
11 | dest_files=["res://.godot/imported/9_node_interior.map-df3f5b606d2a3ad853d8bdbd3ef3702f.scn"]
12 |
13 | [params]
14 |
15 | game_config_path=""
16 | map_config_path=""
17 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/occluder_config.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Resource" script_class="QmapbspTrenchbroomMapConfig" load_steps=2 format=3 uid="uid://d2jat17pe468k"]
2 |
3 | [ext_resource type="Script" path="res://addons/qmapbsp/trenchbroom/map_config.gd" id="1_r6noe"]
4 |
5 | [resource]
6 | script = ExtResource("1_r6noe")
7 | use_bsp_lightmap = false
8 | lightmap_texel = 1.0
9 | inverse_scale_factor = 32.0
10 | mesh_splitting_size = 32.0
11 | bake_occluders = true
12 |
--------------------------------------------------------------------------------
/trenchbroom_example/maps/occluder_shrink_test_config.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Resource" script_class="QmapbspTrenchbroomMapConfig" load_steps=2 format=3 uid="uid://c6fawqvu8glhw"]
2 |
3 | [ext_resource type="Script" path="res://addons/qmapbsp/trenchbroom/map_config.gd" id="1_k64gb"]
4 |
5 | [resource]
6 | script = ExtResource("1_k64gb")
7 | use_bsp_lightmap = false
8 | lightmap_texel = 1.0
9 | inverse_scale_factor = 32.0
10 | mesh_splitting_size = 0.1
11 | ignore_collision = true
12 | bake_occluders = true
13 | occluder_shrink_amount = 0.5
14 | load_point_file = true
15 |
--------------------------------------------------------------------------------
/trenchbroom_example/my_game.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Resource" script_class="QmapbspTrenchbroomGameConfigResource" load_steps=3 format=3 uid="uid://c78vigdmlrdpg"]
2 |
3 | [ext_resource type="Script" path="res://addons/qmapbsp/trenchbroom/game_config_resource.gd" id="1_pj0cu"]
4 | [ext_resource type="Resource" uid="uid://d3djx5mad2mq1" path="res://trenchbroom_example/usercfg.tres" id="2_mulf6"]
5 |
6 | [resource]
7 | script = ExtResource("1_pj0cu")
8 | usercfg = ExtResource("2_mulf6")
9 | name = "Qmapbsp Test Game"
10 | icon = "res://trenchbroom_example/icon.png"
11 | textures_directory = "res://trenchbroom_example/textures"
12 | ent_entity_script_directory = "res://trenchbroom_example/classes"
13 | ent_export_to_fgd_file = true
14 | def_face_offset = Vector2(0, 0)
15 | def_face_scale = Vector2(1, 1)
16 | additional_fgd = ""
17 | _entity_properties_def = {
18 | "light": {
19 | "attenuation": 1.0,
20 | "color": Color(1, 1, 1, 1),
21 | "energy": 1.0,
22 | "range": 5.0,
23 | "specular": 0.5
24 | },
25 | "light_static": {
26 | "attenuation": 1.0,
27 | "color": Color(1, 1, 1, 1),
28 | "energy": 1.0,
29 | "range": 5.0,
30 | "specular": 0.5
31 | },
32 | "test_sphere": {
33 | "radius": 0.5
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/playable/interior_test.occ:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/playable/interior_test.occ
--------------------------------------------------------------------------------
/trenchbroom_example/playable/interior_test.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=5 format=3 uid="uid://brgdlntp1i0so"]
2 |
3 | [ext_resource type="PackedScene" uid="uid://c7skj88wu4yv" path="res://trenchbroom_example/maps/5_interior.map" id="1_fqt2g"]
4 | [ext_resource type="PackedScene" uid="uid://bjrvl643qpvsm" path="res://trenchbroom_example/scenes/player.tscn" id="2_3adn1"]
5 |
6 | [sub_resource type="BoxShape3D" id="BoxShape3D_rl4fl"]
7 | resource_local_to_scene = true
8 | size = Vector3(1, 0.175, 1)
9 |
10 | [sub_resource type="SphereMesh" id="SphereMesh_gt63i"]
11 | radial_segments = 256
12 | rings = 128
13 |
14 | [node name="visleaves_test" type="Node3D"]
15 |
16 | [node name="map" parent="." instance=ExtResource("1_fqt2g")]
17 |
18 | [node name="player" parent="." instance=ExtResource("2_3adn1")]
19 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.760663, -4.06355)
20 |
21 | [node name="around" parent="player" index="1"]
22 | transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0.688, 0)
23 |
24 | [node name="staircast" parent="player" index="2"]
25 | shape = SubResource("BoxShape3D_rl4fl")
26 |
27 | [node name="mesh" type="Node3D" parent="."]
28 |
29 | [node name="m1" type="MeshInstance3D" parent="mesh"]
30 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.67734, 0.851365, -3.92581)
31 | mesh = SubResource("SphereMesh_gt63i")
32 | skeleton = NodePath("../..")
33 |
34 | [editable path="player"]
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/scenes/player.gd:
--------------------------------------------------------------------------------
1 | extends CharacterBody3D
2 | class_name QmapbspTrenchbroomTestPlayer
3 |
4 | @export var max_speed : float = 10
5 | @export var max_air_speed : float = 0.5
6 | @export var accel : float = 100
7 | @export var fric : float = 8
8 | @export var sensitivity : float = 0.0025
9 | @export var stairstep := 0.6
10 | @export var gravity : float = 20
11 | @export var jump_up : float = 7.6
12 |
13 | var noclip : bool = false
14 |
15 | @onready var around : Node3D = $around
16 | @onready var head : Node3D = $around/head
17 | @onready var camera : Camera3D = $around/head/cam
18 | @onready var staircast : ShapeCast3D = $staircast
19 |
20 | var wishdir : Vector3
21 | var wish_jump : bool = false
22 | var auto_jump : bool = true
23 | var smooth_y : float
24 |
25 | func _ready() -> void :
26 | Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
27 |
28 | func accelerate(in_speed : float, delta : float) -> void :
29 | velocity += wishdir * (
30 | clampf(in_speed - velocity.dot(wishdir), 0, accel * delta)
31 | )
32 |
33 | func friction(delta : float) -> void :
34 | var speed : float = velocity.length()
35 | var svec : Vector3
36 | if speed > 0 :
37 | svec = velocity * maxf(speed - (
38 | fric * speed * delta
39 | ), 0) / speed
40 | if speed < 0.1 :
41 | svec = Vector3()
42 | velocity = svec
43 |
44 | func move_ground(delta : float) -> void :
45 | friction(delta)
46 | accelerate(max_speed, delta)
47 |
48 | _stairs(delta)
49 | # test ceiling
50 | staircast.target_position.y = 0.66 + stairstep
51 | staircast.force_shapecast_update()
52 | if staircast.get_collision_count() == 0 :
53 | staircast.target_position.y = -stairstep - 0.1 # (?)
54 | staircast.force_shapecast_update()
55 | if staircast.get_collision_count() > 0 and staircast.get_collision_normal(0).y >= 0.8 :
56 | var height := staircast.get_collision_point(0).y - (global_position.y - 0.75)
57 | if height < stairstep :
58 | position.y += height * 1.25 # additional bonus
59 | smooth_y = -height
60 | around.position.y = -height + 0.688
61 | # 0.688 is an initial value of around.y
62 |
63 | move_and_slide()
64 | _coltest()
65 |
66 | func _stairs(delta : float) :
67 | var w := (velocity / max_speed) * Vector3(2.0, 0.0, 2.0) * delta
68 | var ws := w * max_speed
69 |
70 | # stair stuffs
71 | var shape : BoxShape3D = staircast.shape
72 | shape.size = Vector3(
73 | 1.0 + ws.length(), shape.size.y, 1.0 + ws.length()
74 | )
75 |
76 | staircast.position = Vector3(
77 | ws.x, 0.175 + stairstep - 0.75, ws.z
78 | )
79 |
80 | func move_air(delta : float) -> void :
81 | accelerate(max_air_speed, delta)
82 | _stairs(delta)
83 | move_and_slide()
84 | _coltest()
85 |
86 | func move_noclip(delta : float) -> void :
87 | friction(delta)
88 | accelerate(max_speed, delta)
89 | translate(velocity * delta)
90 |
91 | func _physics_process(delta : float) -> void :
92 | if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED :
93 | return
94 |
95 | wishdir = (head if noclip else around).global_transform.basis * Vector3((
96 | Input.get_axis(&"trenchbroom_test_move_left", &"trenchbroom_test_move_right")
97 | ), 0, (
98 | Input.get_axis(&"trenchbroom_test_move_forward", &"trenchbroom_test_move_back")
99 | )).normalized()
100 |
101 | if noclip :
102 | move_noclip(delta)
103 | return
104 |
105 | if auto_jump :
106 | wish_jump = Input.is_action_pressed(&"trenchbroom_test_jump")
107 | else :
108 | if !wish_jump and Input.is_action_just_pressed(&"trenchbroom_test_jump") :
109 | wish_jump = true
110 | if Input.is_action_just_released(&"trenchbroom_test_jump") :
111 | wish_jump = false
112 |
113 |
114 | if is_on_floor() :
115 | if wish_jump :
116 | velocity.y = jump_up
117 | move_air(delta)
118 | wish_jump = false
119 | else :
120 | velocity.y = 0
121 | move_ground(delta)
122 | else :
123 | velocity.y -= gravity * delta
124 | move_air(delta)
125 |
126 | if is_zero_approx(smooth_y) :
127 | smooth_y = 0.0
128 | else :
129 | smooth_y /= 1.5
130 | around.position.y = smooth_y + 0.688
131 |
132 | func _coltest() :
133 | for i in get_slide_collision_count() :
134 | var k := get_slide_collision(i)
135 | for j in k.get_collision_count() :
136 | var obj := k.get_collider(j)
137 | if obj.has_method(&'_player_touch') :
138 | obj._player_touch(self, k.get_position(j), k.get_normal(j))
139 | return
140 |
141 |
142 | func _input(event : InputEvent) -> void :
143 | if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED :
144 | return
145 |
146 | if Input.is_action_just_pressed(&'trenchbroom_test_toggle_noclip') :
147 | toggle_noclip()
148 |
149 | if event is InputEventMouseMotion :
150 | var r : Vector2 = event.relative * -1
151 | head.rotate_x(r.y * sensitivity)
152 | around.rotate_y(r.x * sensitivity)
153 |
154 | var hrot = head.rotation
155 | hrot.x = clampf(hrot.x, -PI/2, PI/2)
156 | head.rotation = hrot
157 |
158 | #########################################
159 |
160 | func toggle_noclip() :
161 | noclip = !noclip
162 |
--------------------------------------------------------------------------------
/trenchbroom_example/scenes/player.tscn:
--------------------------------------------------------------------------------
1 | [gd_scene load_steps=5 format=3 uid="uid://bjrvl643qpvsm"]
2 |
3 | [ext_resource type="Script" path="res://trenchbroom_example/scenes/player.gd" id="1_xiyui"]
4 |
5 | [sub_resource type="BoxShape3D" id="BoxShape3D_c8hfp"]
6 | size = Vector3(1, 1.5, 1)
7 |
8 | [sub_resource type="BoxShape3D" id="BoxShape3D_qf3qb"]
9 | resource_local_to_scene = true
10 | size = Vector3(1, 0.175, 1)
11 |
12 | [sub_resource type="Resource" id="Resource_5aetx"]
13 | metadata/__load_path__ = "res://addons/qmapbsp/node/leaf_agent.gd"
14 |
15 | [node name="player" type="CharacterBody3D"]
16 | collision_layer = 3
17 | floor_constant_speed = true
18 | floor_snap_length = 0.4
19 | script = ExtResource("1_xiyui")
20 |
21 | [node name="col" type="CollisionShape3D" parent="."]
22 | shape = SubResource("BoxShape3D_c8hfp")
23 |
24 | [node name="around" type="Node3D" parent="."]
25 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.688, 0)
26 |
27 | [node name="head" type="Node3D" parent="around"]
28 |
29 | [node name="cam" type="Camera3D" parent="around/head"]
30 | transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.202, 0)
31 | current = true
32 |
33 | [node name="staircast" type="ShapeCast3D" parent="."]
34 | shape = SubResource("BoxShape3D_qf3qb")
35 | target_position = Vector3(0, -0.688, 0)
36 | max_results = 4
37 |
38 | [node name="leaf_agent" type="Node3D" parent="."]
39 | script = SubResource("Resource_5aetx")
40 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/area.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/area.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/area.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://c7jqbdo3wp3l1"
6 | path="res://.godot/imported/area.png-0084ab903543cb6d5c680410921c80c5.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/textures/area.png"
14 | dest_files=["res://.godot/imported/area.png-0084ab903543cb6d5c680410921c80c5.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/block_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/block_light.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/block_light.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dmbp7dei21m6n"
6 | path="res://.godot/imported/block_light.png-bae7702ed5485126cbd025a2e2577b9e.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/textures/block_light.png"
14 | dest_files=["res://.godot/imported/block_light.png-bae7702ed5485126cbd025a2e2577b9e.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/hint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/hint.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/hint.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cuycu6hlp1vql"
6 | path="res://.godot/imported/hint.png-3d2e0f1cffeb72abd352a757b55f31cc.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/textures/hint.png"
14 | dest_files=["res://.godot/imported/hint.png-3d2e0f1cffeb72abd352a757b55f31cc.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/hintskip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/hintskip.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/hintskip.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://q1objfnq4njr"
6 | path="res://.godot/imported/hintskip.png-dafbc9ecf396bc0d29cca48af1615992.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/textures/hintskip.png"
14 | dest_files=["res://.godot/imported/hintskip.png-dafbc9ecf396bc0d29cca48af1615992.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/navmesh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/navmesh.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/navmesh.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://uorfswm4usqg"
6 | path="res://.godot/imported/navmesh.png-fa8675098c63a7d92630282ea2725045.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/textures/navmesh.png"
14 | dest_files=["res://.godot/imported/navmesh.png-fa8675098c63a7d92630282ea2725045.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/occluder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/occluder.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/occluder.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cux1gvnrikh3s"
6 | path.s3tc="res://.godot/imported/occluder.png-7845f44a8871a5944b4e00207babb79f.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/occluder.png"
15 | dest_files=["res://.godot/imported/occluder.png-7845f44a8871a5944b4e00207babb79f.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/prototype/prototype1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/prototype/prototype1.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/prototype/prototype1.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cbeq6t7brv67n"
6 | path.s3tc="res://.godot/imported/prototype1.png-ef145f40486e2ad771791b958f8f5f44.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/prototype/prototype1.png"
15 | dest_files=["res://.godot/imported/prototype1.png-ef145f40486e2ad771791b958f8f5f44.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/simple1.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple1.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bb4kr8fhml0r7"
6 | path.s3tc="res://.godot/imported/simple1.png-7b49841bfeae96ad5ec17eb2c8c3e778.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/simple1.png"
15 | dest_files=["res://.godot/imported/simple1.png-7b49841bfeae96ad5ec17eb2c8c3e778.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/simple2.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple2.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bwwv8t2my60ly"
6 | path.s3tc="res://.godot/imported/simple2.png-935be993fdddccb4ac44122102558fdc.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/simple2.png"
15 | dest_files=["res://.godot/imported/simple2.png-935be993fdddccb4ac44122102558fdc.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/simple3.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple3.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bdflddgnbfthh"
6 | path.s3tc="res://.godot/imported/simple3.png-90371b4a1d698d0e096c206fc0f53a51.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/simple3.png"
15 | dest_files=["res://.godot/imported/simple3.png-90371b4a1d698d0e096c206fc0f53a51.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/simple4.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple4.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://sfu7ux56lg2k"
6 | path.s3tc="res://.godot/imported/simple4.png-5c91f27ca3d41521ccf2378b3eac9429.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/simple4.png"
15 | dest_files=["res://.godot/imported/simple4.png-5c91f27ca3d41521ccf2378b3eac9429.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/simple5.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/simple5.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dc2dklq0yxxfd"
6 | path.s3tc="res://.godot/imported/simple5.png-e41c1d24287c083270cbb218f9d08bc4.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/simple5.png"
15 | dest_files=["res://.godot/imported/simple5.png-e41c1d24287c083270cbb218f9d08bc4.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/skip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/skip.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/skip.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://gjhyvtaqd7lx"
6 | path="res://.godot/imported/skip.png-2dbf2df0c0e21d1ac04ec693825a69f7.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/textures/skip.png"
14 | dest_files=["res://.godot/imported/skip.png-2dbf2df0c0e21d1ac04ec693825a69f7.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/fence1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/subfolder/fence1.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/fence1.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://c01t14gva4vnf"
6 | path.s3tc="res://.godot/imported/fence1.png-0649031e0b5e6a75b090cdc2dd7adaff.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/subfolder/fence1.png"
15 | dest_files=["res://.godot/imported/fence1.png-0649031e0b5e6a75b090cdc2dd7adaff.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/fence1.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="StandardMaterial3D" load_steps=2 format=3 uid="uid://dcjdwq2ynhbsw"]
2 |
3 | [ext_resource type="Texture2D" uid="uid://c01t14gva4vnf" path="res://trenchbroom_example/textures/subfolder/fence1.png" id="1_jkpvw"]
4 |
5 | [resource]
6 | transparency = 1
7 | albedo_texture = ExtResource("1_jkpvw")
8 | metadata/size = Vector2i(128, 128)
9 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/glass1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/subfolder/glass1.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/glass1.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://dqyyely4p60y8"
6 | path.s3tc="res://.godot/imported/glass1.png-5f50f70582c5ed1f18aec35d41227908.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/subfolder/glass1.png"
15 | dest_files=["res://.godot/imported/glass1.png-5f50f70582c5ed1f18aec35d41227908.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/glass1.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="StandardMaterial3D" format=3 uid="uid://b6ywi1t3jkas8"]
2 |
3 | [resource]
4 | transparency = 1
5 | albedo_color = Color(1, 1, 1, 0.109804)
6 | metadata/size = Vector2i(0, 0)
7 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/simple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/subfolder/simple.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/subfolder/simple.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://cl8aohp8wrfif"
6 | path.s3tc="res://.godot/imported/simple.png-968df4e5897230b962dd310db5c11fa3.s3tc.ctex"
7 | metadata={
8 | "imported_formats": ["s3tc_bptc"],
9 | "vram_texture": true
10 | }
11 |
12 | [deps]
13 |
14 | source_file="res://trenchbroom_example/textures/subfolder/simple.png"
15 | dest_files=["res://.godot/imported/simple.png-968df4e5897230b962dd310db5c11fa3.s3tc.ctex"]
16 |
17 | [params]
18 |
19 | compress/mode=2
20 | compress/high_quality=false
21 | compress/lossy_quality=0.7
22 | compress/hdr_compression=1
23 | compress/normal_map=0
24 | compress/channel_pack=0
25 | mipmaps/generate=true
26 | mipmaps/limit=-1
27 | roughness/mode=0
28 | roughness/src_normal=""
29 | process/fix_alpha_border=true
30 | process/premult_alpha=false
31 | process/normal_map_invert_y=false
32 | process/hdr_as_srgb=false
33 | process/hdr_clamp_exposure=false
34 | process/size_limit=0
35 | detect_3d/compress_to=0
36 |
--------------------------------------------------------------------------------
/trenchbroom_example/textures/trigger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gongpha/gdQmapbsp/aa04de373b2b9a91706ae90e6d067ff16a0d9736/trenchbroom_example/textures/trigger.png
--------------------------------------------------------------------------------
/trenchbroom_example/textures/trigger.png.import:
--------------------------------------------------------------------------------
1 | [remap]
2 |
3 | importer="texture"
4 | type="CompressedTexture2D"
5 | uid="uid://bxp7d7cs7htdf"
6 | path="res://.godot/imported/trigger.png-d9eeb7179055193f0027bc3e3e4dc14d.ctex"
7 | metadata={
8 | "vram_texture": false
9 | }
10 |
11 | [deps]
12 |
13 | source_file="res://trenchbroom_example/textures/trigger.png"
14 | dest_files=["res://.godot/imported/trigger.png-d9eeb7179055193f0027bc3e3e4dc14d.ctex"]
15 |
16 | [params]
17 |
18 | compress/mode=0
19 | compress/high_quality=false
20 | compress/lossy_quality=0.7
21 | compress/hdr_compression=1
22 | compress/normal_map=0
23 | compress/channel_pack=0
24 | mipmaps/generate=false
25 | mipmaps/limit=-1
26 | roughness/mode=0
27 | roughness/src_normal=""
28 | process/fix_alpha_border=true
29 | process/premult_alpha=false
30 | process/normal_map_invert_y=false
31 | process/hdr_as_srgb=false
32 | process/hdr_clamp_exposure=false
33 | process/size_limit=0
34 | detect_3d/compress_to=1
35 |
--------------------------------------------------------------------------------
/trenchbroom_example/usercfg.tres:
--------------------------------------------------------------------------------
1 | [gd_resource type="Resource" script_class="QmapbspUserConfig" load_steps=4 format=3 uid="uid://d3djx5mad2mq1"]
2 |
3 | [ext_resource type="Script" path="res://addons/qmapbsp/resource/user_config.gd" id="1_4544i"]
4 | [ext_resource type="Script" path="res://addons/qmapbsp/importer/cmplwf/quake1.gd" id="1_aloy5"]
5 |
6 | [sub_resource type="Resource" id="Resource_1lml6"]
7 | script = ExtResource("1_aloy5")
8 | qbsp_path = ""
9 |
10 | [resource]
11 | resource_name = "USERCONFIG"
12 | script = ExtResource("1_4544i")
13 | compilation_workflow = SubResource("Resource_1lml6")
14 | tb_path = ""
15 |
--------------------------------------------------------------------------------