From d7bde0cc618dceb3e8ff1742f229cbfb79744694 Mon Sep 17 00:00:00 2001 From: RhapsodyInGeek <44485952+RhapsodyInGeek@users.noreply.github.com> Date: Tue, 26 Nov 2024 23:10:04 -0500 Subject: [PATCH 1/5] Revert "Revert "Add options for building mesh/collision data into metadata of generated nodes"" --- addons/func_godot/src/core/func_godot.gd | 24 +++- .../src/core/func_godot_map_data.gd | 14 ++- .../src/core/func_godot_surface_gatherer.gd | 93 +++++++++++++++- .../src/fgd/func_godot_fgd_solid_class.gd | 27 +++++ addons/func_godot/src/map/func_godot_map.gd | 103 ++++++++++++++++-- 5 files changed, 245 insertions(+), 16 deletions(-) diff --git a/addons/func_godot/src/core/func_godot.gd b/addons/func_godot/src/core/func_godot.gd index 1f4b77e..eb3096a 100644 --- a/addons/func_godot/src/core/func_godot.gd +++ b/addons/func_godot/src/core/func_godot.gd @@ -3,8 +3,12 @@ class_name FuncGodot extends RefCounted var map_data:= FuncGodotMapData.new() var map_parser:= FuncGodotMapParser.new(map_data) var geo_generator = preload("res://addons/func_godot/src/core/func_godot_geo_generator.gd").new(map_data) -var surface_gatherer:= FuncGodotSurfaceGatherer.new(map_data) -var map_settings: FuncGodotMapSettings = null +var map_settings: FuncGodotMapSettings = null: + set(new): + if not new or new == map_settings: return + surface_gatherer.map_settings = new + map_settings = new +var surface_gatherer:= FuncGodotSurfaceGatherer.new(map_data, map_settings) func load_map(filename: String, keep_tb_groups: bool) -> void: map_parser.load_map(filename, keep_tb_groups) @@ -24,7 +28,8 @@ func set_entity_definitions(entity_defs: Dictionary) -> void: var classname: String = entity_defs.keys()[i] var spawn_type: int = entity_defs.values()[i].get("spawn_type", FuncGodotMapData.FuncGodotEntitySpawnType.ENTITY) var origin_type: int = entity_defs.values()[i].get("origin_type", FuncGodotMapData.FuncGodotEntityOriginType.BOUNDS_CENTER) - map_data.set_entity_types_by_classname(classname, spawn_type, origin_type) + var metadata_inclusion_flags: int = entity_defs.values()[i].get("metadata_inclusion_flags", FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags.NONE) + map_data.set_entity_types_by_classname(classname, spawn_type, origin_type, metadata_inclusion_flags) func get_texture_info(texture_name: String) -> FuncGodotMapData.FuncGodotTextureType: if texture_name == map_settings.origin_texture: @@ -58,16 +63,21 @@ func get_entity_dicts() -> Array: return ent_dicts -func gather_texture_surfaces(texture_name: String) -> Array: - var sg: FuncGodotSurfaceGatherer = FuncGodotSurfaceGatherer.new(map_data) +func gather_texture_surfaces(texture_name: String) -> Dictionary: + var sg: FuncGodotSurfaceGatherer = FuncGodotSurfaceGatherer.new(map_data, map_settings) sg.reset_params() sg.split_type = FuncGodotSurfaceGatherer.SurfaceSplitType.ENTITY + const MFlags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags + sg.metadata_skip_flags = MFlags.TEXTURES | MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP sg.set_texture_filter(texture_name) sg.set_clip_filter_texture(map_settings.clip_texture) sg.set_skip_filter_texture(map_settings.skip_texture) sg.set_origin_filter_texture(map_settings.origin_texture) sg.run() - return fetch_surfaces(sg) + return { + surfaces = fetch_surfaces(sg), + metadata = sg.out_metadata, + } func gather_entity_convex_collision_surfaces(entity_idx: int) -> void: surface_gatherer.reset_params() @@ -80,6 +90,8 @@ func gather_entity_concave_collision_surfaces(entity_idx: int) -> void: surface_gatherer.reset_params() surface_gatherer.split_type = FuncGodotSurfaceGatherer.SurfaceSplitType.NONE surface_gatherer.entity_filter_idx = entity_idx + const MFlags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags + surface_gatherer.metadata_skip_flags |= MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP surface_gatherer.set_skip_filter_texture(map_settings.skip_texture) surface_gatherer.set_origin_filter_texture(map_settings.origin_texture) surface_gatherer.run() diff --git a/addons/func_godot/src/core/func_godot_map_data.gd b/addons/func_godot/src/core/func_godot_map_data.gd index 009f7af..1b6137f 100644 --- a/addons/func_godot/src/core/func_godot_map_data.gd +++ b/addons/func_godot/src/core/func_godot_map_data.gd @@ -26,9 +26,10 @@ func find_texture(texture_name: String) -> int: return i return -1 -func set_entity_types_by_classname(classname: String, spawn_type: int, origin_type: int) -> void: +func set_entity_types_by_classname(classname: String, spawn_type: int, origin_type: int, meta_flags: int) -> void: for entity in entities: if entity.properties.has("classname") and entity.properties["classname"] == classname: + entity.metadata_inclusion_flags = meta_flags as FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags entity.spawn_type = spawn_type as FuncGodotMapData.FuncGodotEntitySpawnType if entity.spawn_type == FuncGodotMapData.FuncGodotEntitySpawnType.ENTITY: entity.origin_type = origin_type as FuncGodotMapData.FuncGodotEntityOriginType @@ -59,6 +60,16 @@ enum FuncGodotEntityOriginType { BOUNDS_MAXS = 6, } +enum FuncGodotEntityMetadataInclusionFlags { + NONE = 0, + ENTITY_INDEX_RANGES = 1, + TEXTURES = 2, + VERTEX = 4, + FACE_POSITION = 8, + FACE_NORMAL = 16, + COLLISION_SHAPE_TO_FACE_RANGE_MAP = 32, +} + enum FuncGodotTextureType { NORMAL = 0, ORIGIN = 1 @@ -111,6 +122,7 @@ class FuncGodotEntity: var center: Vector3 var spawn_type: FuncGodotEntitySpawnType var origin_type: FuncGodotEntityOriginType + var metadata_inclusion_flags: FuncGodotEntityMetadataInclusionFlags class FuncGodotFaceVertex: var vertex: Vector3 diff --git a/addons/func_godot/src/core/func_godot_surface_gatherer.gd b/addons/func_godot/src/core/func_godot_surface_gatherer.gd index 843b14b..451b4a5 100644 --- a/addons/func_godot/src/core/func_godot_surface_gatherer.gd +++ b/addons/func_godot/src/core/func_godot_surface_gatherer.gd @@ -1,17 +1,21 @@ class_name FuncGodotSurfaceGatherer extends RefCounted var map_data: FuncGodotMapData +var map_settings: FuncGodotMapSettings var split_type: SurfaceSplitType = SurfaceSplitType.NONE var entity_filter_idx: int = -1 var texture_filter_idx: int = -1 var clip_filter_texture_idx: int var skip_filter_texture_idx: int var origin_filter_texture_idx: int +var metadata_skip_flags: int var out_surfaces: Array[FuncGodotMapData.FuncGodotFaceGeometry] +var out_metadata: Dictionary -func _init(in_map_data: FuncGodotMapData) -> void: +func _init(in_map_data: FuncGodotMapData, in_map_settings: FuncGodotMapSettings) -> void: map_data = in_map_data + map_settings = in_map_settings func set_texture_filter(texture_name: String) -> void: texture_filter_idx = map_data.find_texture(texture_name) @@ -56,8 +60,18 @@ func filter_face(entity_idx: int, brush_idx: int, face_idx: int) -> bool: func run() -> void: out_surfaces.clear() + var texture_names: Array[StringName] = [] + var textures: PackedInt32Array = [] + var vertices: PackedVector3Array = [] + var positions: PackedVector3Array = [] + var normals: PackedVector3Array = [] + var shape_index_ranges: Array[Vector2i] = [] + var entity_index_ranges: Array[Vector2i] = [] var index_offset: int = 0 + var entity_face_range: Vector2i = Vector2i.ZERO + const MFlags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags + var build_entity_index_ranges: bool = not metadata_skip_flags & MFlags.ENTITY_INDEX_RANGES var surf: FuncGodotMapData.FuncGodotFaceGeometry if split_type == SurfaceSplitType.NONE: @@ -67,6 +81,13 @@ func run() -> void: for e in range(map_data.entities.size()): var entity:= map_data.entities[e] var entity_geo:= map_data.entity_geo[e] + var shape_face_range := Vector2i.ZERO + var total_entity_tris := 0 + var include_normals_metadata: bool = not metadata_skip_flags & MFlags.FACE_NORMAL and entity.metadata_inclusion_flags & MFlags.FACE_NORMAL + var include_vertices_metadata: bool = not metadata_skip_flags & MFlags.VERTEX and entity.metadata_inclusion_flags & MFlags.VERTEX + var include_textures_metadata: bool = not metadata_skip_flags & MFlags.TEXTURES and entity.metadata_inclusion_flags & MFlags.TEXTURES + var include_positions_metadata: bool = not metadata_skip_flags & MFlags.FACE_POSITION and entity.metadata_inclusion_flags & MFlags.FACE_POSITION + var include_shape_range_metadata: bool = not metadata_skip_flags & MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP and entity.metadata_inclusion_flags & MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP if filter_entity(e): continue @@ -83,6 +104,7 @@ func run() -> void: for b in range(entity.brushes.size()): var brush:= entity.brushes[b] var brush_geo:= entity_geo.brushes[b] + var total_brush_tris:= 0 if split_type == SurfaceSplitType.BRUSH: index_offset = 0 @@ -90,6 +112,8 @@ func run() -> void: for f in range(brush.faces.size()): var face_geo: FuncGodotMapData.FuncGodotFaceGeometry = brush_geo.faces[f] + var face: FuncGodotMapData.FuncGodotFace = brush.faces[f] + var num_tris = face_geo.vertices.size() - 2 if filter_face(e, b, f): continue @@ -102,10 +126,74 @@ func run() -> void: surf.vertices.append(vert) - for i in range((face_geo.vertices.size() - 2) * 3): + if include_normals_metadata: + var normal := Vector3(face.plane_normal.y, face.plane_normal.z, face.plane_normal.x) + for i in num_tris: + normals.append(normal) + if include_shape_range_metadata or build_entity_index_ranges: + total_brush_tris += num_tris + if include_textures_metadata: + var texname := StringName(map_data.textures[face.texture_idx].name) + var index: int + if texture_names.is_empty(): + texture_names.append(texname) + index = 0 + elif texture_names.back() == texname: + # common case, faces with textures are next to each other + index = texture_names.size() - 1 + else: + var texture_name_index: int = texture_names.find(texname) + if texture_name_index == -1: + index = texture_names.size() + texture_names.append(texname) + else: + index = texture_name_index + # metadata addresses triangles, so we have to duplicate the info for each tri + for i in num_tris: + textures.append(index) + + var avg_vertex_pos := Vector3.ZERO + var avg_vertex_pos_ct: int = 0 + for i in range(num_tris * 3): surf.indicies.append(face_geo.indicies[i] + index_offset) + var vertex: Vector3 = surf.vertices[surf.indicies.back()].vertex + vertex = Vector3(vertex.y, vertex.z, vertex.x) * map_settings.scale_factor + if include_vertices_metadata: + vertices.append(vertex) + if include_positions_metadata: + avg_vertex_pos_ct += 1 + avg_vertex_pos += vertex + if avg_vertex_pos_ct == 3: + avg_vertex_pos /= 3 + positions.append(avg_vertex_pos) + avg_vertex_pos = Vector3.ZERO + avg_vertex_pos_ct = 0 index_offset += face_geo.vertices.size() + + if include_shape_range_metadata: + shape_face_range.x = shape_face_range.y + shape_face_range.y = shape_face_range.x + total_brush_tris + shape_index_ranges.append(shape_face_range) + + if build_entity_index_ranges: + total_entity_tris += total_brush_tris + + if build_entity_index_ranges: + entity_face_range.x = entity_face_range.y + entity_face_range.y = entity_face_range.x + total_entity_tris + entity_index_ranges.append(entity_face_range) + + out_metadata = { + textures = textures, + texture_names = texture_names, + normals = normals, + vertices = vertices, + positions = positions, + shape_index_ranges = shape_index_ranges, + } + if build_entity_index_ranges: + out_metadata["entity_index_ranges"] = entity_index_ranges func add_surface() -> FuncGodotMapData.FuncGodotFaceGeometry: var surf:= FuncGodotMapData.FuncGodotFaceGeometry.new() @@ -118,6 +206,7 @@ func reset_params() -> void: texture_filter_idx = -1 clip_filter_texture_idx = -1 skip_filter_texture_idx = -1 + metadata_skip_flags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags.ENTITY_INDEX_RANGES # nested enum SurfaceSplitType{ diff --git a/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd b/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd index 5e74973..ebc1c3c 100644 --- a/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd +++ b/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd @@ -55,6 +55,33 @@ enum CollisionShapeType { ## The collision margin for the Solid Class' collision shapes. Not used in Godot Physics. See [Shape3D] for details. @export var collision_shape_margin: float = 0.04 +## The following properties tell FuncGodot to add data to the metadata of the generated node upon build. +## This data is parallelized, so that each element of the array is ordered to reference the same face in the mesh. +## All of the data is stored in a dictionary in the node generated for the entity, in metadata entry +## [code]"func_godot_mesh_data"[/code] +@export_group("Mesh Metadata") +## Add a texture lookup table to the generated node's metadata on build. +## The data is split between an [Array] of [StringName] called `"texture_names"` containing all currently used texture materials +## and a [PackedInt32Array] called `"textures"` where each element is an index corresponding to the `"texture_names"` entries. +@export var add_textures_metadata: bool = false +## Add a [PackedVector3Array] called `"vertices"` in the generated node's metadata on build. +## This is a list of every vertex in the generated node's [MeshInstance3D]. Every 3 vertices represent a single face. +@export var add_vertex_metadata: bool = false +## Add a [PackedVector3Array] called `"positions"` in the generated node's metadata on build. +## This is a list of positions for each face, local to the generated node, calculated by averaging the vertices to find the face's center. +@export var add_face_position_metadata = false +## Add a [PackedVector3Array] called `"normals"` in the generated node's metadata on build. +## Contains a list of each face's normal. +@export var add_face_normal_metadata = false +## Add a [Dictionary] called `"collision_shape_to_face_range_map"` in the generated node's metadata on build. +## Contains keys of strings, which are the names of child [CollisionShape3D] nodes, and values of +## Vector2i, where the x represents the starting index of that child's faces and the y represents the +## ending index. +## For example: [code]{ "entity_1_brush_0_collision_shape" : Vector2i(0, 15) }[/code] shows that this +## solid class has been generated with one child collision shape named entity_1_brush_0_collision_shape +## which handles the first 15 faces of the parts of the mesh with collision. +@export var add_collision_shape_face_range_metadata = false + @export_group("Scripting") ## An optional script file to attach to the node generated on map build. @export var script_class: Script diff --git a/addons/func_godot/src/map/func_godot_map.gd b/addons/func_godot/src/map/func_godot_map.gd index 14e87db..42f9ec8 100644 --- a/addons/func_godot/src/map/func_godot_map.gd +++ b/addons/func_godot/src/map/func_godot_map.gd @@ -317,9 +317,19 @@ func set_core_entity_definitions() -> void: var core_ent_defs: Dictionary = {} for classname in entity_definitions: core_ent_defs[classname] = {} - if entity_definitions[classname] is FuncGodotFGDSolidClass: - core_ent_defs[classname]['spawn_type'] = entity_definitions[classname].spawn_type - core_ent_defs[classname]['origin_type'] = entity_definitions[classname].origin_type + var entity_definition: FuncGodotFGDEntityClass = entity_definitions[classname] + if entity_definition is FuncGodotFGDSolidClass: + core_ent_defs[classname]['spawn_type'] = entity_definition.spawn_type + core_ent_defs[classname]['origin_type'] = entity_definition.origin_type + + const MFlags = FuncGodotMapData.FuncGodotEntityMetadataInclusionFlags + var flags := MFlags.NONE + if entity_definition.add_textures_metadata: flags |= MFlags.TEXTURES + if entity_definition.add_vertex_metadata: flags |= MFlags.VERTEX + if entity_definition.add_face_normal_metadata: flags |= MFlags.FACE_NORMAL + if entity_definition.add_face_position_metadata: flags |= MFlags.FACE_POSITION + if entity_definition.add_collision_shape_face_range_metadata: flags |= MFlags.COLLISION_SHAPE_TO_FACE_RANGE_MAP + core_ent_defs[classname]['metadata_inclusion_flags'] = flags func_godot.set_entity_definitions(core_ent_defs) ## Generate geometry from map file @@ -549,11 +559,12 @@ func build_entity_collision_shapes() -> void: var entity_collision_shape: Array = entity_collision_shapes[entity_idx] var concave: bool = false var shape_margin: float = 0.04 + var entity_definition: FuncGodotFGDSolidClass if 'classname' in properties: var classname: String = properties['classname'] if classname in entity_definitions: - var entity_definition: FuncGodotFGDSolidClass = entity_definitions[classname] as FuncGodotFGDSolidClass + entity_definition = entity_definitions[classname] as FuncGodotFGDSolidClass if entity_definition: match(entity_definition.collision_shape_type): FuncGodotFGDSolidClass.CollisionShapeType.NONE: @@ -572,6 +583,9 @@ func build_entity_collision_shapes() -> void: else: func_godot.gather_entity_convex_collision_surfaces(entity_idx) var entity_surfaces: Array = func_godot.fetch_surfaces(func_godot.surface_gatherer) + var metadata: Dictionary = func_godot.surface_gatherer.out_metadata + var collision_shape_to_face_range_map: Dictionary = {} + var face_shape_indices: Array[Vector2i] = metadata["shape_index_ranges"] var entity_verts: PackedVector3Array = PackedVector3Array() @@ -599,6 +613,10 @@ func build_entity_collision_shapes() -> void: var collision_shape: CollisionShape3D = entity_collision_shape[surface_idx] collision_shape.set_shape(shape) + # for face shape range metadata, we need to add info about child node names + if entity_definition and entity_definition.add_collision_shape_face_range_metadata: + collision_shape_to_face_range_map[collision_shape.name] = face_shape_indices[surface_idx] + if concave: if entity_verts.size() == 0: continue @@ -609,18 +627,43 @@ func build_entity_collision_shapes() -> void: var collision_shape: CollisionShape3D = entity_collision_shapes[entity_idx][0] collision_shape.set_shape(shape) + + if entity_definition and entity_definition.add_collision_shape_face_range_metadata: + collision_shape_to_face_range_map[collision_shape.name] = Vector2i(0, entity_verts.size() / 3) + + if entity_definition: + if not entity_definition.add_face_normal_metadata: + metadata.erase("normals") + if not entity_definition.add_face_position_metadata: + metadata.erase("positions") + if not entity_definition.add_textures_metadata: + metadata.erase("textures") + metadata.erase("texture_names") + if not entity_definition.add_vertex_metadata: + metadata.erase("vertices") + + metadata.erase("shape_index_ranges") # cleanup intermediate / buffer + if entity_definition.add_collision_shape_face_range_metadata: + metadata["collision_shape_to_face_range_map"] = collision_shape_to_face_range_map + + if not metadata.is_empty(): + entity_nodes[entity_idx].set_meta("func_godot_mesh_data", metadata) ## Build Dictionary from entity indices to [ArrayMesh] instances func build_entity_mesh_dict() -> Dictionary: var meshes: Dictionary = {} var texture_surf_map: Dictionary + var texture_to_metadata_map: Dictionary for texture in texture_dict: texture_surf_map[texture] = Array() + texture_to_metadata_map[texture] = {} var gather_task = func(i): var texture: String = texture_dict.keys()[i] - texture_surf_map[texture] = func_godot.gather_texture_surfaces(texture) + var fetch_result = func_godot.gather_texture_surfaces(texture) + texture_surf_map[texture] = fetch_result["surfaces"] + texture_to_metadata_map[texture] = fetch_result["metadata"] var task_id: int = WorkerThreadPool.add_group_task(gather_task, texture_dict.keys().size(), 4, true) WorkerThreadPool.wait_for_group_task_completion(task_id) @@ -633,11 +676,12 @@ func build_entity_mesh_dict() -> Dictionary: var properties: Dictionary = entity_dict['properties'] var entity_surface = texture_surfaces[entity_idx] + var entity_definition: FuncGodotFGDSolidClass if 'classname' in properties: var classname: String = properties['classname'] if classname in entity_definitions: - var entity_definition: FuncGodotFGDSolidClass = entity_definitions[classname] as FuncGodotFGDSolidClass + entity_definition = entity_definitions[classname] as FuncGodotFGDSolidClass if entity_definition: if entity_definition.spawn_type == FuncGodotFGDSolidClass.SpawnType.MERGE_WORLDSPAWN: entity_surface = null @@ -655,7 +699,47 @@ func build_entity_mesh_dict() -> Dictionary: mesh.add_surface_from_arrays(ArrayMesh.PRIMITIVE_TRIANGLES, entity_surface) mesh.surface_set_name(mesh.get_surface_count() - 1, texture) mesh.surface_set_material(mesh.get_surface_count() - 1, material_dict[texture]) - + + # build metadata, only if the node is set to not build collision. otherwise we are already + # building it in build_entity_collision_shapes + if entity_definition and entity_definition.collision_shape_type == FuncGodotFGDSolidClass.CollisionShapeType.NONE: + if not mesh.has_meta("func_godot_mesh_data"): + mesh.set_meta("func_godot_mesh_data", Dictionary()) + var this_textures_metadata: Dictionary = texture_to_metadata_map[texture] + var entity_metadata: Dictionary = mesh.get_meta("func_godot_mesh_data") + var entity_index_ranges: Array[Vector2i] = this_textures_metadata["entity_index_ranges"] + var range: Vector2i = entity_index_ranges[entity_idx] + + if entity_definition.add_vertex_metadata: + var vertices: PackedVector3Array = entity_metadata.get("vertices", PackedVector3Array()) + vertices.append_array((this_textures_metadata["vertices"] as PackedVector3Array).slice(range.x * 3, range.y * 3)) + entity_metadata["vertices"] = vertices + + if entity_definition.add_face_normal_metadata: + var normals: PackedVector3Array = entity_metadata.get("normals", PackedVector3Array()) + normals.append_array((this_textures_metadata["normals"] as PackedVector3Array).slice(range.x, range.y)) + entity_metadata["normals"] = normals + + if entity_definition.add_face_position_metadata: + var positions: PackedVector3Array = entity_metadata.get("positions", PackedVector3Array()) + positions.append_array((this_textures_metadata["positions"] as PackedVector3Array).slice(range.x, range.y)) + entity_metadata["positions"] = positions + + if entity_definition.add_textures_metadata: + # different (if null: add empty) logic for texture_names due to not being able make a static typed + # Array[StringName] inline in the get() function + if not entity_metadata.has("texture_names"): + var new: Array[StringName] = [] + entity_metadata["texture_names"] = new + var texture_names: Array[StringName] = entity_metadata["texture_names"] + var textures: PackedInt32Array = entity_metadata.get("textures", PackedInt32Array()) + var texture_block: PackedInt32Array = [] + texture_block.resize(range.y - range.x) + texture_block.fill(texture_names.size()) + texture_names.append(StringName(texture)) + textures.append_array(texture_block) + entity_metadata["textures"] = textures + return meshes ## Build [MeshInstance3D]s from brush entities and add them to the add child queue @@ -725,10 +809,15 @@ func apply_entity_meshes() -> void: var mesh: Mesh = entity_mesh_dict[entity_idx] as Mesh var mesh_instance: MeshInstance3D = entity_mesh_instances[entity_idx] as MeshInstance3D if not mesh or not mesh_instance: + if mesh.has_meta("func_godot_mesh_data"): + mesh.remove_meta("func_godot_mesh_data") continue mesh_instance.set_mesh(mesh) queue_add_child(entity_nodes[entity_idx], mesh_instance) + if mesh.has_meta("func_godot_mesh_data"): + entity_nodes[entity_idx].set_meta("func_godot_mesh_data", mesh.get_meta("func_godot_mesh_data")) + mesh.remove_meta("func_godot_mesh_data") func apply_entity_occluders() -> void: for entity_idx in entity_mesh_dict: From 4826925f22bfca44f0b9ea5e69c8ec498baaf29d Mon Sep 17 00:00:00 2001 From: RhapsodyInGeek <44485952+RhapsodyInGeek@users.noreply.github.com> Date: Tue, 26 Nov 2024 23:16:57 -0500 Subject: [PATCH 2/5] Fix auto-apply matching properties Previously was placed out of scope from `entity_definition` and left it out entirely. --- addons/func_godot/src/map/func_godot_map.gd | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/func_godot/src/map/func_godot_map.gd b/addons/func_godot/src/map/func_godot_map.gd index 42f9ec8..50231ec 100644 --- a/addons/func_godot/src/map/func_godot_map.gd +++ b/addons/func_godot/src/map/func_godot_map.gd @@ -1093,17 +1093,17 @@ func apply_properties_and_finish() -> void: # Everything else else: properties[property] = prop_default + + if entity_definition.auto_apply_to_matching_node_properties: + for property in properties: + if property in entity_node: + if typeof(entity_node.get(property)) == typeof(properties[property]): + entity_node.set(property, properties[property]) + else: + push_error("Entity %s property \'%s\' type mismatch with matching generated node property." % [entity_node.name, property]) if 'func_godot_properties' in entity_node: entity_node.func_godot_properties = properties - - if auto_apply_to_matching_node_properties: - for property in properties: - if property in entity_node: - if typeof(entity_node.get(property)) == typeof(properties[property]): - entity_node.set(property, properties[property]) - else: - push_error("Entity %s property \'%s\' type mismatch with matching generated node property." % [entity_node.name, property]) if entity_node.has_method("_func_godot_apply_properties"): entity_node.call("_func_godot_apply_properties", properties) From d67404e64c99d0563112455b2ffad6a228b28787 Mon Sep 17 00:00:00 2001 From: RhapsodyInGeek <44485952+RhapsodyInGeek@users.noreply.github.com> Date: Tue, 26 Nov 2024 23:34:42 -0500 Subject: [PATCH 3/5] Update SolidClass Mesh Metadata Property comments Comments previously used unsupported Markdown tags. Fixed and add some more formatting for readability. --- .../src/fgd/func_godot_fgd_solid_class.gd | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd b/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd index ebc1c3c..1836a3a 100644 --- a/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd +++ b/addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd @@ -55,31 +55,29 @@ enum CollisionShapeType { ## The collision margin for the Solid Class' collision shapes. Not used in Godot Physics. See [Shape3D] for details. @export var collision_shape_margin: float = 0.04 -## The following properties tell FuncGodot to add data to the metadata of the generated node upon build. -## This data is parallelized, so that each element of the array is ordered to reference the same face in the mesh. -## All of the data is stored in a dictionary in the node generated for the entity, in metadata entry -## [code]"func_godot_mesh_data"[/code] +## The following properties tell FuncGodot to add a [i]"func_godot_mesh_data"[/i] Dictionary to the metadata of the generated node upon build. +## This data is parallelized, so that each element of the array is ordered to reference the same face in the mesh. @export_group("Mesh Metadata") -## Add a texture lookup table to the generated node's metadata on build. -## The data is split between an [Array] of [StringName] called `"texture_names"` containing all currently used texture materials -## and a [PackedInt32Array] called `"textures"` where each element is an index corresponding to the `"texture_names"` entries. +## Add a texture lookup table to the generated node's metadata on build.[br][br] +## The data is split between an [Array] of [StringName] called [i]"texture_names"[/i] containing all currently used texture materials +## and a [PackedInt32Array] called [i]"textures"[/i] where each element is an index corresponding to the [i]"texture_names"[/i] entries. @export var add_textures_metadata: bool = false -## Add a [PackedVector3Array] called `"vertices"` in the generated node's metadata on build. +## Add a [PackedVector3Array] called [i]"vertices"[/i] to the generated node's metadata on build.[br][br] ## This is a list of every vertex in the generated node's [MeshInstance3D]. Every 3 vertices represent a single face. @export var add_vertex_metadata: bool = false -## Add a [PackedVector3Array] called `"positions"` in the generated node's metadata on build. +## Add a [PackedVector3Array] called [i]"positions"[/i] to the generated node's metadata on build.[br][br] ## This is a list of positions for each face, local to the generated node, calculated by averaging the vertices to find the face's center. @export var add_face_position_metadata = false -## Add a [PackedVector3Array] called `"normals"` in the generated node's metadata on build. +## Add a [PackedVector3Array] called [i]"normals"[/i] to the generated node's metadata on build.[br][br] ## Contains a list of each face's normal. @export var add_face_normal_metadata = false -## Add a [Dictionary] called `"collision_shape_to_face_range_map"` in the generated node's metadata on build. +## Add a [Dictionary] called [i]"collision_shape_to_face_range_map"[/i] in the generated node's metadata on build.[br][br] ## Contains keys of strings, which are the names of child [CollisionShape3D] nodes, and values of -## Vector2i, where the x represents the starting index of that child's faces and the y represents the -## ending index. -## For example: [code]{ "entity_1_brush_0_collision_shape" : Vector2i(0, 15) }[/code] shows that this -## solid class has been generated with one child collision shape named entity_1_brush_0_collision_shape -## which handles the first 15 faces of the parts of the mesh with collision. +## [Vector2i], where [i]X[/i] represents the starting index of that child's faces and [i]Y[/i] represents the +## ending index.[br][br] +## For example, an element of [br][br][code]{ "entity_1_brush_0_collision_shape" : Vector2i(0, 15) }[/code][br][br] +## shows that this solid class has been generated with one child collision shape named +## [i]entity_1_brush_0_collision_shape[/i] which handles the first 15 faces of the parts of the mesh with collision. @export var add_collision_shape_face_range_metadata = false @export_group("Scripting") From 39edc3d610f2c6041acd14a5a0737ab34b2a9be0 Mon Sep 17 00:00:00 2001 From: RhapsodyInGeek <44485952+RhapsodyInGeek@users.noreply.github.com> Date: Tue, 26 Nov 2024 23:44:51 -0500 Subject: [PATCH 4/5] Minor comment cleanup func_godot_map.gd --- addons/func_godot/src/map/func_godot_map.gd | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/func_godot/src/map/func_godot_map.gd b/addons/func_godot/src/map/func_godot_map.gd index 50231ec..61ec174 100644 --- a/addons/func_godot/src/map/func_godot_map.gd +++ b/addons/func_godot/src/map/func_godot_map.gd @@ -613,7 +613,7 @@ func build_entity_collision_shapes() -> void: var collision_shape: CollisionShape3D = entity_collision_shape[surface_idx] collision_shape.set_shape(shape) - # for face shape range metadata, we need to add info about child node names + # For face shape range metadata, we need to add info about child node names if entity_definition and entity_definition.add_collision_shape_face_range_metadata: collision_shape_to_face_range_map[collision_shape.name] = face_shape_indices[surface_idx] @@ -700,8 +700,7 @@ func build_entity_mesh_dict() -> Dictionary: mesh.surface_set_name(mesh.get_surface_count() - 1, texture) mesh.surface_set_material(mesh.get_surface_count() - 1, material_dict[texture]) - # build metadata, only if the node is set to not build collision. otherwise we are already - # building it in build_entity_collision_shapes + # Build metadata only if the node is set to not build collision. Otherwise we are already building it in build_entity_collision_shapes. if entity_definition and entity_definition.collision_shape_type == FuncGodotFGDSolidClass.CollisionShapeType.NONE: if not mesh.has_meta("func_godot_mesh_data"): mesh.set_meta("func_godot_mesh_data", Dictionary()) From bafc144d96b8c119c82253512aae7b49c4979788 Mon Sep 17 00:00:00 2001 From: RhapsodyInGeek <44485952+RhapsodyInGeek@users.noreply.github.com> Date: Tue, 26 Nov 2024 23:49:10 -0500 Subject: [PATCH 5/5] Comment cleanup in func_godot_surface_gatherer.gd --- .../src/core/func_godot_surface_gatherer.gd | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/addons/func_godot/src/core/func_godot_surface_gatherer.gd b/addons/func_godot/src/core/func_godot_surface_gatherer.gd index 451b4a5..f072434 100644 --- a/addons/func_godot/src/core/func_godot_surface_gatherer.gd +++ b/addons/func_godot/src/core/func_godot_surface_gatherer.gd @@ -40,19 +40,20 @@ func filter_face(entity_idx: int, brush_idx: int, face_idx: int) -> bool: if face_geo.vertices.size() < 3: return true - + + # Omit faces textured with Clip if clip_filter_texture_idx != -1 and face.texture_idx == clip_filter_texture_idx: return true - # omit faces textured with skip + # Omit faces textured with Skip if skip_filter_texture_idx != -1 and face.texture_idx == skip_filter_texture_idx: return true - # omit faces textured with origin + # Omit faces textured with Origin if origin_filter_texture_idx != -1 and face.texture_idx == origin_filter_texture_idx: return true - # omit filtered texture indices + # Omit filtered texture indices if texture_filter_idx != -1 and face.texture_idx != texture_filter_idx: return true @@ -139,7 +140,7 @@ func run() -> void: texture_names.append(texname) index = 0 elif texture_names.back() == texname: - # common case, faces with textures are next to each other + # Common case, faces with textures are next to each other index = texture_names.size() - 1 else: var texture_name_index: int = texture_names.find(texname) @@ -148,7 +149,7 @@ func run() -> void: texture_names.append(texname) else: index = texture_name_index - # metadata addresses triangles, so we have to duplicate the info for each tri + # Metadata addresses triangles, so we have to duplicate the info for each tri for i in num_tris: textures.append(index) @@ -191,7 +192,7 @@ func run() -> void: vertices = vertices, positions = positions, shape_index_ranges = shape_index_ranges, - } + } if build_entity_index_ranges: out_metadata["entity_index_ranges"] = entity_index_ranges