Skip to content

Commit

Permalink
Merge pull request #66 from distractedmosfet/master
Browse files Browse the repository at this point in the history
Initial implementation of FGD export with point entity support.
  • Loading branch information
Shfty authored Feb 2, 2020
2 parents 6886063 + 05a1f61 commit aca910d
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 8 deletions.
22 changes: 18 additions & 4 deletions addons/qodot/src/build/step/build_nodes/build_entity_spawns.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ func _run(context) -> Dictionary:
var entity_idx = context['entity_idx']
var entity_properties = context['entity_properties']

var is_child_scene = false
var node = null

if('classname' in entity_properties):
var classname = entity_properties['classname']
if classname.substr(0, 5) == 'func_':
Expand Down Expand Up @@ -86,8 +86,14 @@ func _run(context) -> Dictionary:
'trigger':
node = null
_:
node = QodotEntity.new()
node.properties = entity_properties
var entity_defintion_set = context['entity_definition_set']
if entity_defintion_set.has(classname):
var entity_def_scene = load(entity_defintion_set[classname])
node = entity_def_scene.instance(PackedScene.GEN_EDIT_STATE_INSTANCE)
is_child_scene = true
else:
node = QodotEntity.new()
node.properties = entity_properties
if 'angle' in entity_properties:
node.rotation.y = deg2rad(180 + entity_properties['angle'])

Expand All @@ -103,7 +109,15 @@ func _run(context) -> Dictionary:
return {
'nodes': {
'entity_spawns_node': {
get_entity_key(entity_idx): node
get_entity_key(entity_idx): InstancedScene.new(node) if is_child_scene else node
}
}
}

class InstancedScene:
var wrapped_node
func _init(node : Node):
wrapped_node = node

func get_build_params() -> Array:
return ['entity_definition_set']
31 changes: 27 additions & 4 deletions addons/qodot/src/nodes/qodot_map.gd
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export(String) var materials = CATEGORY_STRING
export(String) var material_extension = '.tres'
export (SpatialMaterial) var default_material

# Entity defintions
export(String) var entities = CATEGORY_STRING
export(Resource) var entity_definitions

# Threads
export(String) var threading = CATEGORY_STRING
export(int) var max_build_threads = 4
Expand Down Expand Up @@ -117,7 +121,7 @@ func set_texture_wads(new_texture_wads):
if(texture_wads != tw):
texture_wads = tw
print(texture_wads)

func print_log(msg):
if(print_to_log):
QodotPrinter.print_typed(msg)
Expand Down Expand Up @@ -166,6 +170,17 @@ func build_map(map_file: String) -> void:
"inverse_scale_factor": inverse_scale_factor
}

# Loading entity defintions
var entity_set = {}
print_log("\nLoading entity definition set...")
if entity_definitions != null:
entity_set = entity_definitions.get_point_entity_scene_map()
for key in entity_set.keys():
if entity_set[key] == "":
print("WARNING: No scene file set for entity classname: %s, Position3Ds will be used instead" % key)
entity_set.erase(key) #erasing it to avoid errors further down the line
context["entity_definition_set"] = entity_set

# Initialize thread pool
print_log("\nInitializing Thread Pool...")
var thread_init_profiler = QodotProfiler.new()
Expand Down Expand Up @@ -193,6 +208,7 @@ func build_map(map_file: String) -> void:
var job_profiler = QodotProfiler.new()
thread_pool.start_thread_jobs()
var results = yield(thread_pool, "jobs_complete")

add_context_results(context, results)
var job_duration = job_profiler.finish()
print_log("Done in " + String(job_duration * 0.001) + " seconds.\n")
Expand Down Expand Up @@ -354,7 +370,11 @@ func add_context_nodes_recursive(context: Dictionary, context_key: String, nodes
context[context_key] = {
'children': {}
}

var is_instanced_scene = false
if node is QodotBuildEntitySpawns.InstancedScene:
node = node.wrapped_node
is_instanced_scene = true

context[context_key]['children'][node_key] = {
'node': node,
'children': {}
Expand All @@ -364,8 +384,11 @@ func add_context_nodes_recursive(context: Dictionary, context_key: String, nodes
context[context_key]['node'].add_child(node)
else:
add_child(node)

recursive_set_owner(node, get_tree().get_edited_scene_root())

if is_instanced_scene:
node.owner = get_tree().get_edited_scene_root()
else:
recursive_set_owner(node, get_tree().get_edited_scene_root())


# Queues a build step for execution
Expand Down
78 changes: 78 additions & 0 deletions addons/qodot/src/resources/qodot_entity_definition_set.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
class_name QodotEntityDefinitionSet
extends Resource
tool

## A node used to to express a set of entity definitions that can be exproted

const base_text = """
@SolidClass = worldspawn : \"World Entity\" []
@SolidClass = trigger []
@baseclass color(255 255 40) = Light [
light(integer) : \"Brightness\" : 300
wait(integer) : \"Fade distance multiplier\" : 1
delay(choices) : \"Attenuation\" =
[
0 : \"Linear falloff (Default)\"
1 : \"Inverse distance falloff\"
2 : \"Inverse distance squared\"
3 : \"No falloff\"
4 : \"Local minlight\"
5 : \"Inverse distance squared B\"
]
mangle(string) : \"Spotlight angle\"
]
@PointClass size(-8 -8 -8, 8 8 8) base(Light) =
light : \"Invisible light source\" []
"""

#psuedo-button to export
export(bool) var export_file setget set_export_file
export(String, FILE, GLOBAL, "*.fgd") var target_file
export(Array, Resource) var entity_defintions

func set_export_file(new_export_file = true):
if new_export_file != export_file:
if Engine.is_editor_hint() and get_entities().size() > 0:
if not target_file:
print("Skipping export: No target file")
var file_obj = File.new()
file_obj.open(target_file, File.WRITE)
file_obj.store_string(build_def_text())
file_obj.close()

func build_def_text() -> String:
var res = base_text.strip_edges()
for ent in get_entities():
var ent_text = ent.build_def_text()
res += "\n\n\n" + ent_text
return res

#This getter does a little bit of validation. Providing only an array of non-null uniquely-named entity definitions
func get_entities() -> Array:
var res = []
#Remember indices so our errors can be a little more helpful
var used_names = {"Light": -1, "light": -1, "trigger": -1, "worldspawn" : -1}
for cur_ent_def_ind in range(entity_defintions.size()):
var cur_ent_def = entity_defintions[cur_ent_def_ind]
if cur_ent_def == null:
continue
elif not (cur_ent_def is QodotPointEntityDefinition):
printerr("Bad value in entity definition set at position %s! Not an entity defintion." % cur_ent_def_ind)
continue
var ent_name = cur_ent_def.classname
if used_names.has(ent_name):
printerr("Entity defintion class name collision with name %s, at positions %s and %s please rename!" % [ent_name, used_names[ent_name], cur_ent_def_ind])
continue
used_names[ent_name] = cur_ent_def_ind
res.append(cur_ent_def)
return res

func get_point_entity_scene_map() -> Dictionary:
var res = {}
for ent in get_entities():
if ent is QodotPointEntityDefinition:
res[ent.classname] = ent.scene_file
return res
36 changes: 36 additions & 0 deletions addons/qodot/src/resources/qodot_point_entity_definition.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class_name QodotPointEntityDefinition
extends Resource
tool

## A node used to define an entity in a QodotEntityDefinitionSet
export(String) var classname

# description for the entity def
export var description = ""

# the colour of the entity def
export var color = Color(0.8, 0.8, 0.8)

# the size of the entity def, it gets expressed as two extent points
export var size_point_1 = Vector3(-8, -8, -8)
export var size_point_2 = Vector3(8, 8, 8)

# the scene file you want to associate with this entity definition
# on building the map, an instanced node of this scene will be added
# to the map
export(String, FILE, '*.tscn,*.scn') var scene_file


func build_def_text() -> String:
var res = "@PointClass size(%s %s %s, %s %s %s) color(%s %s %s) = %s" % [int(size_point_1.x), int(size_point_1.y), int(size_point_1.z), int(size_point_2.x), int(size_point_2.y), int(size_point_2.z), color.r8, color.g8, color.b8, classname]
#I have a weird feeling that Godot uses "\n" new lines internally, so just using \n here is right? Uncertain.
var normalized_description = description.replace("\n", " ").strip_edges()
if normalized_description != "":
res += " : \"%s\"%s" % [normalized_description, QodotUtil.newline()]
res += "[" + QodotUtil.newline()
res += "\tangle(float) : \"0.0\"" + QodotUtil.newline()
#eventually custom properties would go here!
res += "]" + QodotUtil.newline()

return res

6 changes: 6 additions & 0 deletions addons/qodot/src/util/qodot_util.gd
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ const DEBUG = false
static func debug_print(msg):
if(DEBUG):
print(msg)

static func newline():
if OS.get_name() == "Windows":
return "\r\n"
else:
return "\n"

0 comments on commit aca910d

Please sign in to comment.