├── .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 | ![image](https://user-images.githubusercontent.com/13400398/216873785-c92ece5b-fbfc-440e-9aaf-6c1e87fe0651.png) 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 | ![image](https://user-images.githubusercontent.com/13400398/216836079-c8306dba-3823-41b4-9ec1-1b1ef775b7a8.png) 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 | --------------------------------------------------------------------------------