Isometric test

This commit is contained in:
Marco 2025-06-09 18:57:53 +02:00
commit 1434f7aa6d
806 changed files with 18966 additions and 15 deletions

View file

@ -0,0 +1,7 @@
@tool
## Special inheritance class for [FuncGodotFGDSolidClass] and [FuncGodotFGDPointClass] entity definitions. Useful for adding shared or common properties and descriptions.
class_name FuncGodotFGDBaseClass
extends FuncGodotFGDEntityClass
func _init() -> void:
prefix = "@BaseClass"

View file

@ -0,0 +1 @@
uid://6o4wbl0iau0v

View file

@ -0,0 +1,217 @@
@icon("res://addons/func_godot/icons/icon_godot_ranger.svg")
## Base entity definition class. Not to be used directly, use [FuncGodotFGDBaseClass], [FuncGodotFGDSolidClass], or [FuncGodotFGDPointClass] instead.
class_name FuncGodotFGDEntityClass
extends Resource
var prefix: String = ""
@export_group("Entity Definition")
## Entity classname. This is a required field in all entity types as it is parsed by both the map editor and by FuncGodot on map build.
@export var classname : String = ""
## Entity description that appears in the map editor. Not required.
@export_multiline var description : String = ""
## Entity does not get written to the exported FGD. Entity is only used for [FuncGodotMap] build process.
@export var func_godot_internal : bool = false
## FuncGodotFGDBaseClass resources to inherit [member class_properties] and [member class_descriptions] from.
@export var base_classes: Array[Resource] = []
## Key value pair properties that will appear in the map editor. After building the FuncGodotMap in Godot, these properties will be added to a Dictionary that gets applied to the generated Node, as long as that Node is a tool script with an exported `func_godot_properties` Dictionary.
@export var class_properties : Dictionary = {}
## Descriptions for previously defined key value pair properties.
@export var class_property_descriptions : Dictionary = {}
## Automatically applies entity class properties to matching properties in the generated node. When using this feature, class properties need to be the correct type or you may run into errors on map build.
@export var auto_apply_to_matching_node_properties : bool = false
## Appearance properties for the map editor. See the [**Valve FGD**](https://developer.valvesoftware.com/wiki/FGD#Entity_Description) and [**TrenchBroom**](https://trenchbroom.github.io/manual/latest/#display-models-for-entities) documentation for more information.
@export var meta_properties : Dictionary = {
"size": AABB(Vector3(-8, -8, -8), Vector3(8, 8, 8)),
"color": Color(0.8, 0.8, 0.8)
}
@export_group("Node Generation")
## Node to generate on map build. This can be a built-in Godot class or a GDExtension class. For Point Class entities that use Scene File instantiation leave this blank.
@export var node_class := ""
## Class property to use in naming the generated node. Overrides `name_property` in [FuncGodotMapSettings].
## Naming occurs before adding to the [SceneTree] and applying properties.
## Nodes will be named `"entity_" + name_property`. An entity's name should be unique, otherwise you may run into unexpected behavior.
@export var name_property := ""
func build_def_text(target_editor: FuncGodotFGDFile.FuncGodotTargetMapEditors = FuncGodotFGDFile.FuncGodotTargetMapEditors.TRENCHBROOM) -> String:
# Class prefix
var res : String = prefix
# Meta properties
var base_str = ""
var meta_props = meta_properties.duplicate()
for base_class in base_classes:
if not 'classname' in base_class:
continue
base_str += base_class.classname
if base_class != base_classes.back():
base_str += ", "
if base_str != "":
meta_props['base'] = base_str
for prop in meta_props:
if prefix == '@SolidClass':
if prop == "size" or prop == "model":
continue
if prop == 'model' and target_editor != FuncGodotFGDFile.FuncGodotTargetMapEditors.TRENCHBROOM:
continue
var value = meta_props[prop]
res += " " + prop + "("
if value is AABB:
res += "%s %s %s, %s %s %s" % [
value.position.x,
value.position.y,
value.position.z,
value.size.x,
value.size.y,
value.size.z
]
elif value is Color:
res += "%s %s %s" % [
value.r8,
value.g8,
value.b8
]
elif value is String:
res += value
res += ")"
res += " = " + classname
if prefix != "@BaseClass": # having a description in BaseClasses crashes some editors
var normalized_description = description.replace("\"", "\'")
if normalized_description != "":
res += " : \"%s\" " % [normalized_description]
else: # Having no description crashes some editors
res += " : \"" + classname + "\" "
if class_properties.size() > 0:
res += FuncGodotUtil.newline() + "[" + FuncGodotUtil.newline()
else:
res += "["
# Class properties
for prop in class_properties:
var value = class_properties[prop]
var prop_val = null
var prop_type := ""
var prop_description: String
if prop in class_property_descriptions:
# Optional default value for Choices can be set up as [String, int]
if value is Dictionary and class_property_descriptions[prop] is Array:
var prop_arr: Array = class_property_descriptions[prop]
if prop_arr.size() > 1 and (prop_arr[1] is int or prop_arr[1] is String):
prop_description = "\"" + prop_arr[0] + "\" : " + str(prop_arr[1])
else:
prop_description = "\"\" : 0"
printerr(str(prop) + " has incorrect description format. Should be [String description, int / String default value].")
else:
prop_description = "\"" + class_property_descriptions[prop] + "\""
else:
prop_description = "\"\""
match typeof(value):
TYPE_INT:
prop_type = "integer"
prop_val = str(value)
TYPE_FLOAT:
prop_type = "float"
prop_val = "\"" + str(value) + "\""
TYPE_STRING:
prop_type = "string"
prop_val = "\"" + value + "\""
TYPE_BOOL:
prop_type = "choices"
prop_val = FuncGodotUtil.newline() + "\t[" + FuncGodotUtil.newline()
prop_val += "\t\t" + str(0) + " : \"No\"" + FuncGodotUtil.newline()
prop_val += "\t\t" + str(1) + " : \"Yes\"" + FuncGodotUtil.newline()
prop_val += "\t]"
TYPE_VECTOR2, TYPE_VECTOR2I:
prop_type = "string"
prop_val = "\"%s %s\"" % [value.x, value.y]
TYPE_VECTOR3, TYPE_VECTOR3I:
prop_type = "string"
prop_val = "\"%s %s %s\"" % [value.x, value.y, value.z]
TYPE_VECTOR4, TYPE_VECTOR4I:
prop_type = "string"
prop_val = "\"%s %s %s %s\"" % [value[0], value[1], value[2], value[3]]
TYPE_COLOR:
prop_type = "color255"
prop_val = "\"%s %s %s\"" % [value.r8, value.g8, value.b8]
TYPE_DICTIONARY:
prop_type = "choices"
prop_val = FuncGodotUtil.newline() + "\t[" + FuncGodotUtil.newline()
for choice in value:
var choice_val = value[choice]
if typeof(choice_val) == TYPE_STRING:
if not (choice_val as String).begins_with("\""):
choice_val = "\"" + choice_val + "\""
prop_val += "\t\t" + str(choice_val) + " : \"" + choice + "\"" + FuncGodotUtil.newline()
prop_val += "\t]"
TYPE_ARRAY:
prop_type = "flags"
prop_val = FuncGodotUtil.newline() + "\t[" + FuncGodotUtil.newline()
for arr_val in value:
prop_val += "\t\t" + str(arr_val[1]) + " : \"" + str(arr_val[0]) + "\" : " + ("1" if arr_val[2] else "0") + FuncGodotUtil.newline()
prop_val += "\t]"
TYPE_NODE_PATH:
prop_type = "target_destination"
prop_val = "\"\""
TYPE_OBJECT:
if value is Resource:
prop_val = value.resource_path
if value is Material:
if target_editor != FuncGodotFGDFile.FuncGodotTargetMapEditors.JACK:
prop_type = "material"
else:
prop_type = "shader"
elif value is Texture2D:
prop_type = "decal"
elif value is AudioStream:
prop_type = "sound"
else:
prop_type = "target_source"
prop_val = "\"\""
if prop_val:
res += "\t"
res += prop
res += "("
res += prop_type
res += ")"
if not value is Array:
if not value is Dictionary or prop_description != "":
res += " : "
res += prop_description
if value is bool or value is Dictionary or value is Array:
res += " = "
else:
res += " : "
res += prop_val
res += FuncGodotUtil.newline()
res += "]" + FuncGodotUtil.newline()
return res

View file

@ -0,0 +1 @@
uid://blhmvtghs553u

View file

@ -0,0 +1,166 @@
@tool
@icon("res://addons/func_godot/icons/icon_godot_ranger.svg")
## [Resource] file used to express a set of [FuncGodotFGDEntity] definitions. Can be exported as an FGD file for use with a Quake map editor. Used in conjunction with a [FuncGodotMapSetting] resource to generate nodes in a [FuncGodotMap] node.
class_name FuncGodotFGDFile
extends Resource
## Supported map editors enum, used in conjunction with [member target_map_editor].
enum FuncGodotTargetMapEditors {
OTHER,
TRENCHBROOM,
JACK,
NET_RADIANT_CUSTOM,
}
## Builds and exports the FGD file.
@export var export_file: bool:
get:
return export_file # TODO Converter40 Non existent get function
set(new_export_file):
if new_export_file != export_file:
do_export_file(target_map_editor)
func do_export_file(target_editor: FuncGodotTargetMapEditors = FuncGodotTargetMapEditors.TRENCHBROOM, fgd_output_folder: String = "") -> void:
if not Engine.is_editor_hint():
return
if fgd_output_folder.is_empty():
fgd_output_folder = FuncGodotLocalConfig.get_setting(FuncGodotLocalConfig.PROPERTY.FGD_OUTPUT_FOLDER) as String
if fgd_output_folder.is_empty():
print("Skipping export: No game config folder")
return
if fgd_name == "":
print("Skipping export: Empty FGD name")
var fgd_file = fgd_output_folder + "/" + fgd_name + ".fgd"
print("Exporting FGD to ", fgd_file)
var file_obj := FileAccess.open(fgd_file, FileAccess.WRITE)
file_obj.store_string(build_class_text(target_editor))
file_obj.close()
@export_group("Map Editor")
## Some map editors do not support the features found in others
## (ex: TrenchBroom supports the "model" key word while others require "studio",
## J.A.C.K. uses the "shader" key word while others use "material", etc...).
## If you get errors in your map editor, try changing this setting and re-exporting.
## This setting is overridden when the FGD is built via the Game Config resource.
@export var target_map_editor: FuncGodotTargetMapEditors = FuncGodotTargetMapEditors.TRENCHBROOM
# Some map editors do not support the "model" key word and require the "studio" key word instead.
# If you get errors in your map editor, try changing this setting.
# This setting is overridden when the FGD is built via the Game Config resource.
#@export var model_key_word_supported: bool = true
@export_group("FGD")
## FGD output filename without the extension.
@export var fgd_name: String = "FuncGodot"
## Array of [FuncGodotFGDFile] resources to include in FGD file output. All of the entities included with these FuncGodotFGDFile resources will be prepended to the outputted FGD file.
@export var base_fgd_files: Array[Resource] = []
## Array of resources that inherit from [FuncGodotFGDEntityClass]. This array defines the entities that will be added to the exported FGD file and the nodes that will be generated in a [FuncGodotMap].
@export var entity_definitions: Array[Resource] = []
func build_class_text(target_editor: FuncGodotTargetMapEditors = FuncGodotTargetMapEditors.TRENCHBROOM) -> String:
var res : String = ""
for base_fgd in base_fgd_files:
if base_fgd is FuncGodotFGDFile:
res += base_fgd.build_class_text(target_editor)
else:
printerr("Base Fgd Files contains incorrect resource type! Should only be type FuncGodotFGDFile.")
var entities = get_fgd_classes()
for ent in entities:
if not ent is FuncGodotFGDEntityClass:
continue
if ent.func_godot_internal:
continue
var ent_text = ent.build_def_text(target_editor)
res += ent_text
if ent != entities[-1]:
res += "\n"
return res
## This getter does a little bit of validation. Providing only an array of non-null uniquely-named entity definitions
func get_fgd_classes() -> Array:
var res : Array = []
for cur_ent_def_ind in range(entity_definitions.size()):
var cur_ent_def = entity_definitions[cur_ent_def_ind]
if cur_ent_def == null:
continue
elif not (cur_ent_def is FuncGodotFGDEntityClass):
printerr("Bad value in entity definition set at position %s! Not an entity defintion." % cur_ent_def_ind)
continue
res.append(cur_ent_def)
return res
func get_entity_definitions() -> Dictionary:
var res : Dictionary = {}
for base_fgd in base_fgd_files:
var fgd_res = base_fgd.get_entity_definitions()
for key in fgd_res:
res[key] = fgd_res[key]
for ent in get_fgd_classes():
# Skip entities without classnames
if ent.classname.replace(" ","") == "":
printerr("Skipping " + ent.get_path() + ": Empty classname")
continue
if ent is FuncGodotFGDPointClass or ent is FuncGodotFGDSolidClass:
var entity_def = ent.duplicate()
var meta_properties := {}
var class_properties := {}
var class_property_descriptions := {}
for base_class in _generate_base_class_list(entity_def):
for meta_property in base_class.meta_properties:
meta_properties[meta_property] = base_class.meta_properties[meta_property]
for class_property in base_class.class_properties:
class_properties[class_property] = base_class.class_properties[class_property]
for class_property_desc in base_class.class_property_descriptions:
class_property_descriptions[class_property_desc] = base_class.class_property_descriptions[class_property_desc]
for meta_property in entity_def.meta_properties:
meta_properties[meta_property] = entity_def.meta_properties[meta_property]
for class_property in entity_def.class_properties:
class_properties[class_property] = entity_def.class_properties[class_property]
for class_property_desc in entity_def.class_property_descriptions:
class_property_descriptions[class_property_desc] = entity_def.class_property_descriptions[class_property_desc]
entity_def.meta_properties = meta_properties
entity_def.class_properties = class_properties
entity_def.class_property_descriptions = class_property_descriptions
res[ent.classname] = entity_def
return res
func _generate_base_class_list(entity_def : Resource, visited_base_classes = []) -> Array:
var base_classes : Array = []
visited_base_classes.append(entity_def.classname)
# End recursive search if no more base_classes
if len(entity_def.base_classes) == 0:
return base_classes
# Traverse up to the next level of hierarchy, if not already visited
for base_class in entity_def.base_classes:
if not base_class.classname in visited_base_classes:
base_classes.append(base_class)
base_classes += _generate_base_class_list(base_class, visited_base_classes)
else:
printerr(str("Entity '", entity_def.classname,"' contains cycle/duplicate to Entity '", base_class.classname, "'"))
return base_classes

View file

@ -0,0 +1 @@
uid://cknmd0lgmorx2

View file

@ -0,0 +1,166 @@
@tool
## A special type of [FuncGodotFGDPointClass] entity that can automatically generate a special simplified GLB model file for the map editor display.
## Only supported in map editors that support GLTF or GLB.
class_name FuncGodotFGDModelPointClass
extends FuncGodotFGDPointClass
enum TargetMapEditor {
GENERIC,
TRENCHBROOM
}
## Determines how model interprets [member scale_expression].
@export var target_map_editor: TargetMapEditor = TargetMapEditor.GENERIC
## Display model export folder relative to the model folder set by [FuncGodotLocalConfig].
@export var models_sub_folder : String = ""
## Scale expression applied to model. See the [TrenchBroom Documentation](https://trenchbroom.github.io/manual/latest/#display-models-for-entities) for more information.
@export var scale_expression : String = ""
## Model Point Class can override the 'size' meta property by auto-generating a value from the meshes' [AABB]. Proper generation requires 'scale_expression' set to a float or [Vector3]. **WARNING:** Generated size property unlikely to align cleanly to grid!
@export var generate_size_property : bool = false
## Creates a .gdignore file in the model export folder to prevent Godot importing the display models. Only needs to be generated once.
@export var generate_gd_ignore_file : bool = false :
get:
return generate_gd_ignore_file
set(ignore):
if (ignore != generate_gd_ignore_file):
if Engine.is_editor_hint():
var path: String = _get_game_path().path_join(_get_model_folder())
var error: Error = DirAccess.make_dir_recursive_absolute(path)
if error != Error.OK:
printerr("Failed creating dir for GDIgnore file", error)
return
path = path.path_join('.gdignore')
if FileAccess.file_exists(path):
return
var file: FileAccess = FileAccess.open(path, FileAccess.WRITE)
file.store_string('')
file.close()
func build_def_text(target_editor: FuncGodotFGDFile.FuncGodotTargetMapEditors = FuncGodotFGDFile.FuncGodotTargetMapEditors.TRENCHBROOM) -> String:
_generate_model()
return super()
func _generate_model() -> void:
if not scene_file:
return
var gltf_state := GLTFState.new()
var path = _get_export_dir()
var node = _get_node()
if node == null: return
if not _create_gltf_file(gltf_state, path, node):
printerr("could not create gltf file")
return
node.queue_free()
if target_map_editor == TargetMapEditor.TRENCHBROOM:
const model_key: String = "model"
if scale_expression.is_empty():
meta_properties[model_key] = '"%s"' % _get_local_path()
else:
meta_properties[model_key] = '{"path": "%s", "scale": %s }' % [
_get_local_path(),
scale_expression
]
else:
meta_properties["studio"] = '"%s"' % _get_local_path()
if generate_size_property:
meta_properties["size"] = _generate_size_from_aabb(gltf_state.meshes)
func _get_node() -> Node3D:
var node := scene_file.instantiate()
if node is Node3D:
return node as Node3D
node.queue_free()
printerr("Scene is not of type 'Node3D'")
return null
func _get_export_dir() -> String:
var work_dir: String = _get_game_path()
var model_dir: String = _get_model_folder()
return work_dir.path_join(model_dir).path_join('%s.glb' % classname)
func _get_local_path() -> String:
return _get_model_folder().path_join('%s.glb' % classname)
func _get_model_folder() -> String:
var model_dir: String = FuncGodotLocalConfig.get_setting(FuncGodotLocalConfig.PROPERTY.GAME_PATH_MODELS_FOLDER) as String
if not models_sub_folder.is_empty():
model_dir = model_dir.path_join(models_sub_folder)
return model_dir
func _get_game_path() -> String:
return FuncGodotLocalConfig.get_setting(FuncGodotLocalConfig.PROPERTY.MAP_EDITOR_GAME_PATH) as String
func _create_gltf_file(gltf_state: GLTFState, path: String, node: Node3D) -> bool:
var global_export_path = path
var gltf_document := GLTFDocument.new()
gltf_state.create_animations = false
node.rotate_y(deg_to_rad(-90))
# With TrenchBroom we can specify a scale expression, but for other editors we need to scale our models manually.
if target_map_editor != TargetMapEditor.TRENCHBROOM:
var scale_factor: Vector3 = Vector3.ONE
if scale_expression.is_empty():
scale_factor *= FuncGodotLocalConfig.get_setting(FuncGodotLocalConfig.PROPERTY.DEFAULT_INVERSE_SCALE) as float
else:
if scale_expression.begins_with('\''):
var scale_arr := scale_expression.split_floats(' ', false)
if scale_arr.size() == 3:
scale_factor *= Vector3(scale_arr[0], scale_arr[1], scale_arr[2])
elif scale_expression.to_float() > 0:
scale_factor *= scale_expression.to_float()
if scale_factor.length() == 0:
scale_factor = Vector3.ONE # Don't let the node scale into oblivion!
node.scale *= scale_factor
var error: Error = gltf_document.append_from_scene(node, gltf_state)
if error != Error.OK:
printerr("Failed appending to gltf document", error)
return false
call_deferred("_save_to_file_system", gltf_document, gltf_state, global_export_path)
return true
func _save_to_file_system(gltf_document: GLTFDocument, gltf_state: GLTFState, path: String) -> void:
var error: Error = DirAccess.make_dir_recursive_absolute(path.get_base_dir())
if error != Error.OK:
printerr("Failed creating dir", error)
return
error = gltf_document.write_to_filesystem(gltf_state, path)
if error != OK:
printerr("Failed writing to file system", error)
return
print('Exported model to ', path)
func _generate_size_from_aabb(meshes: Array[GLTFMesh]) -> AABB:
var aabb := AABB()
for mesh in meshes:
aabb = aabb.merge(mesh.mesh.get_mesh().get_aabb())
# Reorient the AABB so it matches TrenchBroom's coordinate system
var size_prop := AABB()
size_prop.position = Vector3(aabb.position.z, aabb.position.x, aabb.position.y)
size_prop.size = Vector3(aabb.size.z, aabb.size.x, aabb.size.y)
# Scale the size bounds to our scale factor
# Scale factor will need to be set if we decide to auto-generate our bounds
var scale_factor: Vector3 = Vector3.ONE
if target_map_editor == TargetMapEditor.TRENCHBROOM:
if scale_expression.begins_with('\''):
var scale_arr := scale_expression.split_floats(' ', false)
if scale_arr.size() == 3:
scale_factor *= Vector3(scale_arr[0], scale_arr[1], scale_arr[2])
elif scale_expression.to_float() > 0:
scale_factor *= scale_expression.to_float()
size_prop.position *= scale_factor
size_prop.size *= scale_factor
size_prop.size += size_prop.position
# Round the size so it can stay on grid level 1 at least
for i in 3:
size_prop.position[i] = round(size_prop.position[i])
size_prop.size[i] = round(size_prop.size[i])
return size_prop

View file

@ -0,0 +1 @@
uid://dkmyelig23ub5

View file

@ -0,0 +1,23 @@
@tool
## FGD PointClass entity definition, used to define point entities.
## PointClass entities can use either the `node_class` or the `scene_file` property to tell [FuncGodotMap] what to generate on map build.
class_name FuncGodotFGDPointClass
extends FuncGodotFGDEntityClass
func _init() -> void:
prefix = "@PointClass"
@export_group ("Scene")
## An optional scene file to instantiate on map build. Overrides `node_class` and `script_class`.
@export var scene_file: PackedScene
## An optional script file to attach to the node generated on map build. Ignored if `scene_file` is specified.
@export_group ("Scripting")
@export var script_class: Script
@export_group("Build")
## Toggles whether entity will use `angles`, `mangle`, or `angle` to determine rotations on [FuncGodotMap] build, prioritizing the key value pairs in that order. Set to `false` if you would like to define how the generated node is rotated yourself.
@export var apply_rotation_on_map_build : bool = true
## Toggles whether entity will use `scale` to determine the generated node or scene's scale. This is performed on the top level node. The property can be a [float], [Vector3], or [Vector2].
@export var apply_scale_on_map_build: bool = true

View file

@ -0,0 +1 @@
uid://c83r7t467hm4m

View file

@ -0,0 +1,88 @@
@tool
## FGD SolidClass entity definition, used to define brush entities.
## A [MeshInstance3D] will be generated by FuncGodotMap according to this definition's Visual Build settings. If FuncGodotFGDSolidClass [member node_class] inherits [CollisionObject3D] then one or more [CollisionShape3D] nodes will be generated according to Collision Build settings.
class_name FuncGodotFGDSolidClass
extends FuncGodotFGDEntityClass
enum SpawnType {
WORLDSPAWN = 0, ## Is worldspawn
MERGE_WORLDSPAWN = 1, ## Should be combined with worldspawn
ENTITY = 2, ## Is its own separate entity
}
enum OriginType {
AVERAGED = 0, ## Use averaged brush vertices for center position. This is the old Qodot behavior.
ABSOLUTE = 1, ## Use `origin` class property in global coordinates as the center position.
RELATIVE = 2, ## Calculate center position using `origin` class property as an offset to the entity's bounding box center.
BRUSH = 3, ## Calculate center position based on the bounding box center of all brushes using the 'origin' texture specified in the [FuncGodotMapSettings].
BOUNDS_CENTER = 4, ## Use the center of the entity's bounding box for center position. This is the default option and recommended for most entities.
BOUNDS_MINS = 5, ## Use the lowest bounding box coordinates for center position. This is standard Quake and Half-Life brush entity behavior.
BOUNDS_MAXS = 6, ## Use the highest bounding box coordinates for center position.
}
enum CollisionShapeType {
NONE, ## No collision shape is built. Useful for decorative geometry like vines, hanging wires, grass, etc...
CONVEX, ## Will build a Convex CollisionShape3D for each brush used to make this Solid Class. Required for non-[StaticBody3D] nodes like [Area3D].
CONCAVE ## Should have a concave collision shape
}
## Controls whether this Solid Class is the worldspawn, is combined with the worldspawn, or is spawned as its own free-standing entity.
@export var spawn_type: SpawnType = SpawnType.ENTITY
## Controls how this Solid Class determines its center position. Only valid if [member spawn_type] is set to ENTITY.
@export var origin_type: OriginType = OriginType.BOUNDS_CENTER
@export_group("Visual Build")
## Controls whether a [MeshInstance3D] is built for this Solid Class.
@export var build_visuals : bool = true
## Sets generated [MeshInstance3D] to be available for UV2 unwrapping after [FuncGodotMap] build. Utilized in baked lightmapping.
@export var use_in_baked_light : bool = true
## Shadow casting setting allows for further lightmapping customization.
@export var shadow_casting_setting : GeometryInstance3D.ShadowCastingSetting = GeometryInstance3D.SHADOW_CASTING_SETTING_ON
## Automatically build [OccluderInstance3D] for this entity.
@export var build_occlusion : bool = false
## This Solid Class' [MeshInstance3D] will only be visible for [Camera3D]s whose cull mask includes any of these render layers.
@export_flags_3d_render var render_layers: int = 1
@export_group("Collision Build")
## Controls how collisions are built for this Solid Class.
@export var collision_shape_type: CollisionShapeType = CollisionShapeType.CONVEX
## The physics layers this Solid Class can be detected in.
@export_flags_3d_physics var collision_layer: int = 1
## The physics layers this Solid Class scans.
@export_flags_3d_physics var collision_mask: int = 1
## The priority used to solve colliding when penetration occurs. The higher the priority is, the lower the penetration into the Solid Class will be. This can for example be used to prevent the player from breaking through the boundaries of a level.
@export var collision_priority: float = 1.0
## 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
func _init():
prefix = "@SolidClass"

View file

@ -0,0 +1 @@
uid://msq50x6rk4po