Skip to content

Commit

Permalink
Merge pull request func-godot#64 from func-godot/node-mesh-metadata
Browse files Browse the repository at this point in the history
Solid Class  Mesh Metadata & Auto Apply Properties Fix
  • Loading branch information
RhapsodyInGeek authored Nov 27, 2024
2 parents 5bb4f51 + bafc144 commit 6b539c4
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 28 deletions.
24 changes: 18 additions & 6 deletions addons/func_godot/src/core/func_godot.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down
14 changes: 13 additions & 1 deletion addons/func_godot/src/core/func_godot_map_data.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
102 changes: 96 additions & 6 deletions addons/func_godot/src/core/func_godot_surface_gatherer.gd
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -36,28 +40,39 @@ 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

return false

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:
Expand All @@ -67,6 +82,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
Expand All @@ -83,13 +105,16 @@ 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
surf = add_surface()

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
Expand All @@ -102,10 +127,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()
Expand All @@ -118,6 +207,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{
Expand Down
25 changes: 25 additions & 0 deletions addons/func_godot/src/fgd/func_godot_fgd_solid_class.gd
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ 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 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.[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 [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 [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 [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 [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 [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")
## An optional script file to attach to the node generated on map build.
@export var script_class: Script
Expand Down
Loading

0 comments on commit 6b539c4

Please sign in to comment.