927 lines
32 KiB
GDScript
927 lines
32 KiB
GDScript
extends Node
|
|
|
|
const DEFAULT_LIQUID_DEPTH: float = 32.0
|
|
|
|
var _convex_decomposition_settings: MeshConvexDecompositionSettings
|
|
|
|
func _ready() -> void:
|
|
_convex_decomposition_settings = MeshConvexDecompositionSettings.new()
|
|
_convex_decomposition_settings.max_concavity = 0.0
|
|
_convex_decomposition_settings.max_convex_hulls = 32
|
|
_convex_decomposition_settings.resolution = 100000
|
|
|
|
func setup(scene: Node) -> Object:
|
|
#print(scene.get_meta(&'extras'))
|
|
if scene:
|
|
var meshes: Array[Mesh] = []
|
|
var mats: Array[Material] = []
|
|
var nodes_by_unique_name := {}
|
|
print("===Map preinit for %s===" % scene)
|
|
print("---Setting up nodes---")
|
|
_setup_nodes(scene, scene, meshes, mats, nodes_by_unique_name)
|
|
print("---Applying shaders---")
|
|
_apply_shaders(meshes, mats)
|
|
print("---Applying properties---")
|
|
_apply_properties(scene, scene, nodes_by_unique_name)
|
|
print("===Map preinit for %s complete===" % scene)
|
|
#scene.print_tree_pretty()
|
|
return scene
|
|
|
|
func _setup_nodes(
|
|
what: Node,
|
|
root: Node,
|
|
meshes: Array[Mesh],
|
|
mats: Array[Material],
|
|
nodes_by_unique_name: Dictionary
|
|
) -> void:
|
|
for child in what.get_children():
|
|
_setup_nodes(child, root, meshes, mats, nodes_by_unique_name)
|
|
_setup_node(what, root, meshes, mats, nodes_by_unique_name)
|
|
|
|
func _record_mesh_and_mats(
|
|
what: Node, root: Node,
|
|
mesh: Mesh, meshes: Array[Mesh], mats: Array[Material]
|
|
) -> void:
|
|
if mesh and not (mesh in meshes):
|
|
#print((
|
|
#"Recording mesh %s and its materials " +
|
|
#"for use during applying shaders"
|
|
#) % mesh)
|
|
meshes.push_back(mesh)
|
|
for i in mesh.get_surface_count():
|
|
var mat := mesh.surface_get_material(i)
|
|
if mat and not (mat in mats):
|
|
#print("Recording material: %s" % mat)
|
|
mats.push_back(mat)
|
|
var meta := mat.get_meta(&'extras', {}) as Dictionary
|
|
var known_surfaces := root.get_meta(&'extras', {}).get(
|
|
root.get_path_to(what), {}
|
|
).get(&'surfaces', []) as Array
|
|
if known_surfaces.size() > i:
|
|
meta.merge(known_surfaces[i], true)
|
|
mat.set_meta(&'extras', meta)
|
|
|
|
func _setup_node(
|
|
what: Node,
|
|
root: Node,
|
|
meshes: Array[Mesh],
|
|
mats: Array[Material],
|
|
nodes_by_unique_name: Dictionary
|
|
) -> void:
|
|
#print("Setting up node %s" % what)
|
|
var unique_name := what.name
|
|
var replacement: Node = null
|
|
var discard := false
|
|
var recursive_discard := false
|
|
var extras := {}
|
|
var all_extras := root.get_meta(&'extras', {}) as Dictionary
|
|
var path := root.get_path_to(what)
|
|
var meta := all_extras.get(path, {}) as Dictionary
|
|
extras.merge(meta.get(&'mesh', {}), true)
|
|
extras.merge(meta.get(&'node', {}), true)
|
|
what.set_meta(&'extras', extras)
|
|
var node_type := &''
|
|
var path_prefix := ""
|
|
#print("Assessing node category...")
|
|
if extras.has(&'SpawnGroup'):
|
|
match extras.SpawnGroup:
|
|
&'conditional': node_type = &'ConditionalSpawnGroup'
|
|
&'deferred': node_type = &'DeferredSpawnGroup'
|
|
&'spawner': node_type = &'Spawner'
|
|
elif extras.has(&'NPC'):
|
|
#print("Node is npc: %s" % extras.NPC)
|
|
node_type = &'NPC'
|
|
path_prefix = "res://characters/npcs/%s/%s" % [extras.NPC, extras.NPC]
|
|
elif extras.has(&'Enemy'):
|
|
#print("Node is enemy: %s" % extras.Enemy)
|
|
node_type = &'Enemy'
|
|
path_prefix = "res://characters/enemies/%s/%s" % [
|
|
extras.Enemy, extras.Enemy
|
|
]
|
|
elif extras.has(&'Liquid'):
|
|
#print("Node is liquid: %s" % extras.Liquid)
|
|
node_type = &'Liquid'
|
|
path_prefix = "res://zones/Liquid/descriptors/%s" % extras.Liquid
|
|
elif extras.has(&'Object'):
|
|
#print("Node is game-object: %s" % extras.Object)
|
|
node_type = &'Object'
|
|
path_prefix = "res://objects/%s/%s" % [extras.Object, extras.Object]
|
|
elif extras.has(&'Path'):
|
|
#print("Node is path: %s" % extras.Path)
|
|
node_type = &'Path'
|
|
elif extras.has(&'VisualLiquid'):
|
|
#print("Node is visual liquid: %s" % extras.VisualLiquid)
|
|
node_type = &'VisualLiquid'
|
|
path_prefix = "res://zones/Liquid/descriptors/%s" % extras.VisualLiquid
|
|
elif extras.has(&'Zone'):
|
|
#print("Node is zone: %s" % extras.Zone)
|
|
node_type = &'Zone'
|
|
path_prefix = "res://zones/%s/%s" % [extras.Zone, extras.Zone]
|
|
elif extras.has(&'Intangible') and not extras.Intangible and (
|
|
what is Node3D and not (what is MeshInstance3D)
|
|
):
|
|
#print("Node is invisible wall")
|
|
node_type = &'InvisibleWall'
|
|
else:
|
|
#print("Node is uncategorized; no special setup needed")
|
|
pass
|
|
if not node_type.is_empty():
|
|
print("Setting up node %s as %s%s" % [
|
|
root.get_path_to(what), node_type,
|
|
(":%s" % extras.get(node_type)) if extras.has(node_type) else ""
|
|
])
|
|
var script_path: String = path_prefix + ".gd"
|
|
var scene_path: String = path_prefix + ".tscn"
|
|
var resource_path: String = path_prefix + ".tres"
|
|
var instantiated_from_script := false
|
|
#print("Doing node-category-specific setup:")
|
|
match node_type:
|
|
&'':
|
|
#print("None needed; to reiterate, node is uncategorized")
|
|
pass
|
|
&'ConditionalSpawnGroup':
|
|
#print("Instantiating ConditionalSpawnGroup")
|
|
replacement = ConditionalSpawnGroup.new()
|
|
#print("Instantiated: %s" % replacement)
|
|
&'DeferredSpawnGroup':
|
|
replacement = DeferredSpawnGroup.new()
|
|
&'InvisibleWall':
|
|
#print("Instantiating StaticBody3D")
|
|
replacement = StaticBody3D.new()
|
|
#print("Instantiated: %s" % replacement)
|
|
&'Liquid':
|
|
#print("Instantiating Liquid node")
|
|
replacement = load("res://zones/Liquid/Liquid.tscn").instantiate()
|
|
#print("Instantiated: %s" % replacement)
|
|
#print(
|
|
#"Trying to load LiquidDescriptor from %s" %
|
|
#resource_path
|
|
#)
|
|
if ResourceLoader.exists(resource_path):
|
|
replacement.descriptor = load(resource_path)
|
|
#print(
|
|
#"Loaded LiquidDescriptor: %s" %
|
|
#replacement.descriptor
|
|
#)
|
|
else:
|
|
#print("!!!LiquidDescriptor does not exist!!!")
|
|
push_error(
|
|
"LiquidDescriptor @ %s not found" % resource_path
|
|
)
|
|
&'Path':
|
|
if what is MeshInstance3D:
|
|
#print("Node stands in for a Curve3D, " +
|
|
#"which is a Resource; " +
|
|
#"actual node will be discarded")
|
|
discard = true
|
|
#print("Instantiating Curve3D")
|
|
var curve := Curve3D.new()
|
|
# Might try to switch to Hobby algorithm later, not sure
|
|
#print("Building curve from vertices of surface 0")
|
|
for vertex in (
|
|
what.mesh.surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
|
|
):
|
|
#print("Adding vertex %s" % vertex)
|
|
curve.add_point(vertex)
|
|
#print("Storing curve to extras under &'%s'" % extras.Path)
|
|
extras[extras.Path] = curve
|
|
else:
|
|
#print("!!!Node is path, but is not MeshInstance3D!!!")
|
|
push_error((
|
|
"Node marked to become a path " +
|
|
"is not a MeshInstance3D: %s"
|
|
) % what)
|
|
&'Spawner':
|
|
replacement = Spawner.new()
|
|
&'VisualLiquid':
|
|
#print("Instantiating VisualLiquid node")
|
|
replacement = load("res://zones/Liquid/VisualLiquid.gd").new()
|
|
#print(
|
|
#"Trying to load LiquidDescriptor from %s" %
|
|
#resource_path
|
|
#)
|
|
if ResourceLoader.exists(resource_path):
|
|
replacement.descriptor = load(resource_path)
|
|
#print(
|
|
#"Loaded LiquidDescriptor: %s" %
|
|
#replacement.descriptor
|
|
#)
|
|
else:
|
|
#print("!!!LiquidDescriptor does not exist!!!")
|
|
push_error(
|
|
"LiquidDescriptor @ %s not found" % resource_path
|
|
)
|
|
assert(what is MeshInstance3D)
|
|
#print("Grabbing mesh from original")
|
|
replacement.mesh = what.mesh
|
|
#print("Attaching liquid shader as override")
|
|
replacement.set_surface_override_material(0, preload(
|
|
"res://zones/Liquid/LiquidMaterial.tres"
|
|
))
|
|
_:
|
|
if ResourceLoader.exists(scene_path, &'PackedScene'):
|
|
#print("Instantiating %s node from scene %s" % [
|
|
#node_type, scene_path
|
|
#])
|
|
replacement = load(scene_path).instantiate()
|
|
#print("Result: %s" % replacement)
|
|
elif ResourceLoader.exists(script_path, &'GDScript'):
|
|
#print("Instantiating %s node from script %s" % [
|
|
#node_type, script_path
|
|
#])
|
|
replacement = load(script_path).new()
|
|
#print("Result: %s" % replacement)
|
|
instantiated_from_script = true
|
|
else:
|
|
#print("!!!Node type does not exist: %s %s!!!" % [
|
|
#node_type, extras[node_type]
|
|
#])
|
|
push_error("No such %s: %s (what: %s)" % [
|
|
node_type, extras[node_type], what
|
|
])
|
|
#print("Node-category-specific setup done")
|
|
if discard:
|
|
#print("Node will be discarded")
|
|
var parent := what.get_parent()
|
|
if not recursive_discard:
|
|
#print("Propagating children up to parent")
|
|
for child in what.get_children():
|
|
#print("Removing child %s" % child)
|
|
what.remove_child(child)
|
|
#print("Adding to parent")
|
|
what.get_parent().add_child(child)
|
|
#print("Removing discarded node from tree")
|
|
parent.remove_child(what)
|
|
#print("Queuing discarded node to be freed")
|
|
what.queue_free()
|
|
else:
|
|
if replacement:
|
|
#print("Adding instantiated node %s to tree" % replacement)
|
|
if (
|
|
node_type == &'ConditionalSpawnGroup' or
|
|
node_type == &'DeferredSpawnGroup' or
|
|
node_type == &'Spawner'
|
|
):
|
|
replacement.root = what
|
|
var parent := what.get_parent()
|
|
parent.remove_child(what)
|
|
parent.add_child(replacement)
|
|
elif node_type == &'Zone' and what is MeshInstance3D:
|
|
#print((
|
|
#"Node is a zone, and original was a MeshInstance3D " +
|
|
#"(%s); therefore, the instantiated zone belongs " +
|
|
#"under the original MeshInstance3D as a child, " +
|
|
#"rather than in its place as a replacement; " +
|
|
#"adding as child"
|
|
#) % what)
|
|
replacement.name = extras.Zone
|
|
what.add_child(replacement)
|
|
elif node_type == &'Liquid' and what is MeshInstance3D:
|
|
#print((
|
|
#"Node is a liquid, " +
|
|
#"and original was a MeshInstance3D " +
|
|
#"(%s); therefore, the instantiated liquid belongs " +
|
|
#"under the original MeshInstance3D as a child, " +
|
|
#"rather than in its place as a replacement; " +
|
|
#"adding as child"
|
|
#) % what)
|
|
replacement.name = &'Liquid'
|
|
what.add_child(replacement)
|
|
#print("Generating collision meshes")
|
|
for collider in PlaneConvexDecompose.from(
|
|
what.mesh, extras.get(&'Depth', DEFAULT_LIQUID_DEPTH)
|
|
):
|
|
replacement.add_child(collider)
|
|
collider.owner = root
|
|
#print("Generating collision meshes " +
|
|
#"from the original MeshInstance3D " +
|
|
#"for use for the instantiated liquid")
|
|
#var colliders := _get_multiple_convex_collisions(
|
|
#what, _convex_decomposition_settings
|
|
#)
|
|
#print("Adding collision meshes as children")
|
|
#for collider in colliders:
|
|
#print("Adding collision mesh %s" % collider)
|
|
#replacement.add_child(collider)
|
|
#print("Generating collision trimesh")
|
|
#var collider := _get_trimesh(what)
|
|
#replacement.add_child(collider)
|
|
elif (
|
|
node_type == &'Object' and instantiated_from_script and
|
|
(what is MeshInstance3D)
|
|
):
|
|
#print((
|
|
#"Node is a game-object instantiated from a script, " +
|
|
#"not a scene, and original was a MeshInstance3D " +
|
|
#"(%s); therefore, the original MeshInstance3D " +
|
|
#"should be preserved under its replacement " +
|
|
#"as a child, and also used to generate " +
|
|
#"collision meshes"
|
|
#) % what)
|
|
#print("Recording original's mesh and materials")
|
|
_record_mesh_and_mats(what, root, what.mesh, meshes, mats)
|
|
#print("Replacing original with replacement")
|
|
replacement.name = what.name
|
|
_replace_node_maybe_with_scene_instance(
|
|
what, replacement
|
|
)
|
|
#print("Adding original as child to replacement")
|
|
what.name = &'MeshInstance3D'
|
|
replacement.add_child(what)
|
|
#print("Generating collision meshes")
|
|
var colliders := _get_multiple_convex_collisions(
|
|
what, _convex_decomposition_settings
|
|
)
|
|
#print("Adding collision meshes as children")
|
|
for collider in colliders:
|
|
#print("Adding collision mesh %s" % collider)
|
|
replacement.add_child(collider)
|
|
else:
|
|
#print((
|
|
#"Replacing original node (%s) " +
|
|
#"with instantiated replacement (%s)"
|
|
#) % [what, replacement])
|
|
replacement.name = what.name
|
|
_replace_node_maybe_with_scene_instance(
|
|
what, replacement
|
|
)
|
|
#print("Queuing original to be freed")
|
|
what.queue_free()
|
|
if replacement is Node3D and what is Node3D and not (
|
|
what.get_parent() == replacement
|
|
) and not (
|
|
replacement.get_parent() == what
|
|
):
|
|
#print("Both instantiated node and original are Node3D; " +
|
|
#"ergo, copying transform")
|
|
replacement.transform = what.transform
|
|
elif replacement is Node2D and what is Node2D and not (
|
|
what.get_parent() == replacement
|
|
) and not (
|
|
replacement.get_parent() == what
|
|
):
|
|
#print("Both instantiated node and original are Node2D; " +
|
|
#"ergo, copying transform (%s)" % what.transform)
|
|
replacement.transform = what.transform
|
|
if node_type == &'Zone' or ((
|
|
node_type == &'Liquid' or
|
|
node_type == &'InvisibleWall'
|
|
) and not (what is MeshInstance3D)):
|
|
#print("Instantiated node still needs a collision mesh, " +
|
|
#"but building one from the original node " +
|
|
#"is either not appropriate (if instantiated node " +
|
|
#"is a zone) or not possible (if original node " +
|
|
#"is not a MeshInstance3D); will use a BoxShape3D")
|
|
#print("Instantiating CollisionShape3D")
|
|
var collision_shape := CollisionShape3D.new()
|
|
collision_shape.name = &'CollisionShape3D'
|
|
#print("Instantiating BoxShape3D resource")
|
|
collision_shape.shape = BoxShape3D.new()
|
|
#print("Setting box size from node scale (2.0*%s)" %
|
|
#what.scale)
|
|
collision_shape.shape.size = 2.0*what.scale
|
|
#print("Setting node scale to identity")
|
|
replacement.scale = Vector3.ONE
|
|
#print("Adding collision mesh as child")
|
|
replacement.add_child(collision_shape)
|
|
#print("Setting collision mesh's transform to identity")
|
|
collision_shape.transform = Transform3D.IDENTITY
|
|
#print("Copying extras to replacement")
|
|
replacement.set_meta(&'extras', extras)
|
|
what = replacement
|
|
if what is MeshInstance3D:
|
|
#print("Node is MeshInstance3D; recording mesh and materials")
|
|
_record_mesh_and_mats(what, root, what.mesh, meshes, mats)
|
|
if (
|
|
node_type != &'VisualLiquid' and
|
|
not extras.get(&'Intangible', false)
|
|
):
|
|
#print("Node is not intangible; creating collision mesh")
|
|
what.create_trimesh_collision()
|
|
#print("Recording node under its unique name")
|
|
nodes_by_unique_name[unique_name] = what
|
|
|
|
func _get_multiple_convex_collisions(
|
|
what: MeshInstance3D,
|
|
params: MeshConvexDecompositionSettings = null
|
|
) -> Array[CollisionShape3D]:
|
|
#print("Creating collision meshes from node %s")
|
|
var children_before := what.get_children()
|
|
what.create_multiple_convex_collisions(params)
|
|
var children_after := what.get_children()
|
|
#print("Searching children to find result of operation")
|
|
var staticbody: StaticBody3D
|
|
for child in children_after:
|
|
if not (child in children_before):
|
|
#print("Result found: %s" % child)
|
|
assert(child is StaticBody3D and not staticbody)
|
|
staticbody = child
|
|
#print("Removing resulting staticbody from tree")
|
|
what.remove_child(staticbody)
|
|
#print("Collecting collision meshes from staticbody")
|
|
var colliders: Array[CollisionShape3D] = []
|
|
for collider in staticbody.get_children():
|
|
assert(collider is CollisionShape3D)
|
|
#print("Removing collision mesh %s from staticbody" % collider)
|
|
staticbody.remove_child(collider)
|
|
#print("Queuing up collision mesh")
|
|
colliders.push_back(collider)
|
|
#print("Queuing staticbody to be freed")
|
|
staticbody.queue_free()
|
|
return colliders
|
|
|
|
func _get_trimesh(what: MeshInstance3D) -> CollisionShape3D:
|
|
#print("Creating collision mesh from node %s")
|
|
var children_before := what.get_children()
|
|
what.create_trimesh_collision()
|
|
var children_after := what.get_children()
|
|
#print("Searching children to find result of operation")
|
|
var staticbody: StaticBody3D
|
|
for child in children_after:
|
|
if not (child in children_before):
|
|
#print("Result found: %s" % child)
|
|
assert(child is StaticBody3D and not staticbody)
|
|
staticbody = child
|
|
#print("Removing resulting staticbody from tree")
|
|
what.remove_child(staticbody)
|
|
#print("Collecting collision meshes from staticbody")
|
|
var colliders: Array[CollisionShape3D] = []
|
|
for collider in staticbody.get_children():
|
|
assert(collider is CollisionShape3D)
|
|
#print("Removing collision mesh %s from staticbody" % collider)
|
|
staticbody.remove_child(collider)
|
|
#print("Queuing up collision mesh")
|
|
colliders.push_back(collider)
|
|
#print("Queuing staticbody to be freed")
|
|
staticbody.queue_free()
|
|
assert(colliders.size() == 1)
|
|
return colliders[0]
|
|
|
|
func _apply_shaders(meshes: Array[Mesh], mats: Array[Material]) -> void:
|
|
#print("Building array of ShaderMaterial replacements " +
|
|
#"for original materials")
|
|
var shadmats: Array[ShaderMaterial] = []
|
|
var shaders := {}
|
|
for mat in mats:
|
|
#print("Building ShaderMaterial to replace %s" % mat)
|
|
var extras := {}
|
|
if mat.has_meta(&'extras'):
|
|
extras = mat.get_meta(&'extras')
|
|
var shadmat := ShaderMaterial.new()
|
|
shadmats.push_back(shadmat)
|
|
if extras.has(&'Shader'):
|
|
#print("Material says to use shader &'%s'; " +
|
|
# "loading" % extras.Shader)
|
|
shadmat.shader = load("res://vfx/%s.gdshader" % extras.Shader)
|
|
else:
|
|
#print("Material does not specify a shader; will use &'basic'")
|
|
shadmat.shader = preload("res://vfx/basic.gdshader")
|
|
shaders[
|
|
StringName(extras.Shader) if extras.has(&'Shader') else &'basic'
|
|
] = true
|
|
assert(mat is BaseMaterial3D)
|
|
#print((
|
|
#"Setting shader parameter &'image' " +
|
|
#"to original material's texture (%s)"
|
|
#) % mat.albedo_texture)
|
|
shadmat.set_shader_parameter(&'image', mat.albedo_texture)
|
|
for k in extras:
|
|
if k == k.to_lower():
|
|
#print((
|
|
#"Setting shader parameter &'%s' " +
|
|
#"to value specified in original material's extras: %s"
|
|
#) % [k, extras[k]])
|
|
shadmat.set_shader_parameter(k, extras[k])
|
|
print("Loaded shaders: %s" % (", ".join(shaders.keys())))
|
|
#print("Replacing meshes' original materials with ShaderMaterials")
|
|
for mesh in meshes:
|
|
#print("Replacing materials in mesh %s" % mesh)
|
|
for i in mesh.get_surface_count():
|
|
#print("Replacing material for surface %d" % i)
|
|
var j: int = mats.find(mesh.surface_get_material(i))
|
|
if j >= 0:
|
|
mesh.surface_set_material(i, shadmats[j])
|
|
|
|
func _apply_properties(
|
|
what: Node, root: Node,
|
|
nodes_by_unique_name: Dictionary
|
|
) -> void:
|
|
for child in what.get_children():
|
|
_apply_properties(child, root, nodes_by_unique_name)
|
|
if (
|
|
(what is ConditionalSpawnGroup) or
|
|
(what is DeferredSpawnGroup) or
|
|
(what is Spawner)
|
|
):
|
|
_apply_properties(what.root, what.root, nodes_by_unique_name)
|
|
_apply_properties_to_node(what, root, nodes_by_unique_name)
|
|
|
|
func _apply_properties_to_node(
|
|
what: Node, root: Node,
|
|
nodes_by_unique_name: Dictionary
|
|
) -> void:
|
|
#print("Setting properties for node %s from its extras" % what)
|
|
var extras := what.get_meta(&'extras', {}) as Dictionary
|
|
for key in extras:
|
|
for descriptor in what.get_property_list():
|
|
if descriptor.name.capitalize() == key.capitalize():
|
|
#print("Interpreting value of extra &'%s'" % key)
|
|
var value: Variant = _interpret_property_value(
|
|
descriptor, extras[key], what, root,
|
|
nodes_by_unique_name
|
|
)
|
|
#print("Value interpreted as %s" % value)
|
|
print("Setting property: $'%s'.%s = %s" % [
|
|
root.get_path_to(what), key, value
|
|
])
|
|
what.set(key, value)
|
|
break
|
|
|
|
func _interpret_property_value(
|
|
descriptor: Dictionary,
|
|
value: Variant,
|
|
what: Node,
|
|
root: Node,
|
|
nodes_by_unique_name: Dictionary
|
|
) -> Variant:
|
|
#print("First, parsing property hint string '%s'" % descriptor.hint_string)
|
|
var hint_args: PackedStringArray = descriptor.hint_string.split(',')
|
|
var hint_arg_args: Array[PackedStringArray] = []
|
|
for i in hint_args.size():
|
|
var arg_args := hint_args[i].split(':')
|
|
hint_args[i] = arg_args[0]
|
|
arg_args.remove_at(0)
|
|
hint_arg_args.push_back(arg_args)
|
|
#print("Checking property type")
|
|
match descriptor.type:
|
|
TYPE_NIL:
|
|
#print("Nil property can only be null")
|
|
return null
|
|
TYPE_BOOL:
|
|
#print("Property is bool; double-negating extra")
|
|
return not not value
|
|
TYPE_INT: match descriptor.hint:
|
|
PROPERTY_HINT_ENUM, PROPERTY_HINT_ENUM_SUGGESTION:
|
|
#print("Property is enum")
|
|
if value is String:
|
|
#print("Extra is string; searching enum")
|
|
var i = hint_args.find(value.capitalize())
|
|
if i >= 0 and hint_arg_args[i].size() > 0:
|
|
#print(
|
|
#"Found enum value corresponding to given string"
|
|
#)
|
|
return hint_arg_args[i][0]
|
|
else:
|
|
#print("Could not find enum value " +
|
|
#"corresponding to given string; defaulting to -1")
|
|
return -1
|
|
else:
|
|
#print("Extra is not string; skipping enum search " +
|
|
#"and using directly")
|
|
return int(value)
|
|
_:
|
|
#print("Property is int")
|
|
return int(value)
|
|
TYPE_FLOAT:
|
|
#print("Property is float")
|
|
return float(value)
|
|
TYPE_STRING:
|
|
#print("Property is string")
|
|
return String(value)
|
|
TYPE_VECTOR2:
|
|
#print("Property is Vector2; ergo, " +
|
|
#"assuming extra is 2-element float array")
|
|
return Vector2(value[0], value[1])
|
|
TYPE_VECTOR2I:
|
|
#print("Property is Vector2i; ergo, " +
|
|
#"assuming extra is 2-element int array")
|
|
return Vector2i(value[0], value[1])
|
|
TYPE_RECT2:
|
|
#print("Property is Rect2; ergo, " +
|
|
#"assuming extra is 2-element float array")
|
|
return Rect2(value[0], value[1])
|
|
TYPE_RECT2I:
|
|
#print("Property is Rect2i; ergo, " +
|
|
#"assuming extra is 2-element int array")
|
|
return Rect2i(value[0], value[1])
|
|
TYPE_VECTOR3:
|
|
#print("Property is Vector3; ergo, " +
|
|
#"assuming extra is 3-element float array")
|
|
return Vector3(value[0], value[1], value[2])
|
|
TYPE_VECTOR3I:
|
|
#print("Property is Vector3i; ergo, " +
|
|
#"assuming extra is 3-element int array")
|
|
return Vector3i(value[0], value[1], value[2])
|
|
TYPE_TRANSFORM2D:
|
|
#print("Property is Transform2D; ergo, " +
|
|
#"assuming extra is 6-element float array")
|
|
return Transform2D(
|
|
value[0], Vector2(value[1], value[2]),
|
|
value[3], Vector2(value[4], value[5])
|
|
)
|
|
TYPE_VECTOR4:
|
|
#print("Property is Vector4; ergo, " +
|
|
#"assuming extra is 4-element float array")
|
|
return Vector4(value[0], value[1], value[2], value[3])
|
|
TYPE_VECTOR4I:
|
|
#print("Property is Vector4i; ergo, " +
|
|
#"assuming extra is 4-element int array")
|
|
return Vector4i(value[0], value[1], value[2], value[3])
|
|
TYPE_PLANE:
|
|
#print("Property is Plane; ergo, " +
|
|
#"assuming extra is float array with 3, 4, 6, or 9 elements")
|
|
match value.size():
|
|
3:
|
|
#print("3 elements; ergo, using Plane(Vector3)")
|
|
return Plane(Vector3(value[0], value[1], value[2]))
|
|
4:
|
|
#print("4 elements; ergo, using Plane(Vector3, float)")
|
|
return Plane(
|
|
Vector3(value[0], value[1], value[2]),
|
|
value[3]
|
|
)
|
|
6:
|
|
#print("6 elements; ergo, using Plane(Vector3, Vector3)")
|
|
return Plane(
|
|
Vector3(value[0], value[1], value[2]),
|
|
Vector3(value[3], value[4], value[5])
|
|
)
|
|
9:
|
|
#print("9 elements; ergo, " +
|
|
#"using Plane(Vector3, Vector3, Vector3)")
|
|
return Plane(
|
|
Vector3(value[0], value[1], value[2]),
|
|
Vector3(value[3], value[4], value[5]),
|
|
Vector3(value[6], value[7], value[8])
|
|
)
|
|
_:
|
|
#print((
|
|
#"!!!Wrong number of elements (%d); " +
|
|
#"ergo, using Plane()!!!"
|
|
#) % value.size())
|
|
push_error((
|
|
"Arguments %s are invalid " +
|
|
"for constructing Plane"
|
|
) % value)
|
|
return Plane()
|
|
TYPE_QUATERNION:
|
|
#print("Property is Quaternion; ergo, " +
|
|
#"assuming extra is 4-element float array")
|
|
return Quaternion(
|
|
value[0], value[1], value[2], value[3]
|
|
)
|
|
TYPE_AABB:
|
|
#print("Property is AABB; ergo, " +
|
|
#"assuming extra is 6-element float array")
|
|
return AABB(
|
|
Vector3(value[0], value[1], value[2]),
|
|
Vector3(value[3], value[4], value[5])
|
|
)
|
|
TYPE_BASIS:
|
|
#print("Property is Basis; ergo, " +
|
|
#"assuming extra is 9-element float array")
|
|
return Basis(
|
|
Vector3(value[0], value[1], value[2]),
|
|
Vector3(value[3], value[4], value[5]),
|
|
Vector3(value[6], value[7], value[8])
|
|
)
|
|
TYPE_TRANSFORM3D:
|
|
#print("Property is Transform3D; ergo, " +
|
|
#"assuming extra is 12-element float array")
|
|
return Transform3D(
|
|
Basis(
|
|
Vector3(value[0], value[1], value[2]),
|
|
Vector3(value[3], value[4], value[5]),
|
|
Vector3(value[6], value[7], value[8])
|
|
),
|
|
Vector3(value[9], value[10], value[11])
|
|
)
|
|
TYPE_PROJECTION:
|
|
#print("Property is Projection; ergo, " +
|
|
#"assuming extra is float array with 12 or 16 elements")
|
|
match value.size():
|
|
12:
|
|
#print("12 elements; ergo, " +
|
|
#"using Projection(Transform3D)")
|
|
return Projection(Transform3D(
|
|
Basis(
|
|
Vector3(value[0], value[1], value[2]),
|
|
Vector3(value[3], value[4], value[5]),
|
|
Vector3(value[6], value[7], value[8])
|
|
),
|
|
Vector3(value[9], value[10], value[11])
|
|
))
|
|
16:
|
|
#print("16 elements; ergo, " +
|
|
#"using Projection(" +
|
|
#"Vector4, Vector4, Vector4, Vector4" +
|
|
#")")
|
|
return Projection(
|
|
Vector4(value[0], value[1], value[2], value[3]),
|
|
Vector4(value[4], value[5], value[6], value[7]),
|
|
Vector4(value[8], value[9], value[10], value[11]),
|
|
Vector4(value[12], value[13], value[14], value[15])
|
|
)
|
|
_:
|
|
#print((
|
|
#"!!!Wrong number of elements (%d); " +
|
|
#"ergo, using Projection()!!!"
|
|
#) % value.size())
|
|
push_error((
|
|
"Arguments %s are invalid " +
|
|
"for constructing Projection"
|
|
) % value)
|
|
return Projection()
|
|
TYPE_COLOR:
|
|
#print("Property is Color")
|
|
if value is String:
|
|
#print("Extra is String; parsing as Color")
|
|
return Color(value)
|
|
else:
|
|
#print("Extra is not String; ergo, " +
|
|
#"assuming float array with 3 or 4 elements")
|
|
match value.size():
|
|
3:
|
|
#print("3 elements; ergo, RGB")
|
|
return Color(value[0], value[1], value[2])
|
|
4:
|
|
#print("4 elements; ergo, RGBA")
|
|
return Color(value[0], value[1], value[2], value[3])
|
|
_:
|
|
#print((
|
|
#"!!!Wrong number of elements (%d); " +
|
|
#"ergo, using Color.WHITE!!!"
|
|
#) % value.size())
|
|
push_error((
|
|
"Arguments %s are invalid " +
|
|
"for constructing Color"
|
|
) % value)
|
|
return Color.WHITE
|
|
TYPE_STRING_NAME:
|
|
#print("Property is StringName; interning extra")
|
|
return StringName(String(value))
|
|
TYPE_NODE_PATH:
|
|
#print("Property is NodePath; parsing extra")
|
|
return NodePath(String(value))
|
|
TYPE_RID:
|
|
#print("Property is RID; " +
|
|
#"interpreting extra as resource path (without res://)")
|
|
return load("res://" + String(value)).get_rid()
|
|
TYPE_OBJECT: match descriptor.hint:
|
|
PROPERTY_HINT_RESOURCE_TYPE:
|
|
#print("Property is Resource")
|
|
# special case for assigning resources from discarded nodes
|
|
if value is Resource:
|
|
#print("Extra is Resource, likely built " +
|
|
#"from discarded node during node setup phase; " +
|
|
#"using unmodified")
|
|
return value
|
|
else:
|
|
#print("Interpreting extra as resource path " +
|
|
#"(without res://)")
|
|
return load("res://" + String(value))
|
|
PROPERTY_HINT_NODE_TYPE:
|
|
#print("Property is Node")
|
|
if nodes_by_unique_name.has(value):
|
|
#print("Extra refers to a node's unique name " +
|
|
#"as defined in the glTF data; " +
|
|
#"using that node")
|
|
return nodes_by_unique_name.get(value)
|
|
elif String(value).begins_with('/'):
|
|
#print("Extra begins with /; " +
|
|
#"interpreting as absolute node path")
|
|
return root.get_node(value)
|
|
else:
|
|
#print("Interpreting extra as node path")
|
|
return what.get_node(value)
|
|
_:
|
|
#print("Property is Object; interpreting extra " +
|
|
#"as name usable with ClassDB")
|
|
return ClassDB.instantiate(StringName(String(value)))
|
|
TYPE_CALLABLE:
|
|
#print("Property is callable; " +
|
|
#"interpreting extra as method reference (:-style)")
|
|
#print("Parsing method reference for receiver and method name")
|
|
var parts := String(value).split(':')
|
|
#print("Parse result: %s" % parts)
|
|
if parts.size() == 1:
|
|
#print("No receiver; assuming implicit self")
|
|
return Callable(what, parts[0])
|
|
elif nodes_by_unique_name.has(parts[0]):
|
|
#print("Receiver refers to a node's unique name " +
|
|
#"as defined in the glTF data; " +
|
|
#"using that node")
|
|
return Callable(nodes_by_unique_name.get(parts[0]), parts[1])
|
|
elif String(parts[0]).begins_with('/'):
|
|
#print("Receiver begins with /; " +
|
|
#"interpreting as absolute node path")
|
|
return Callable(root.get_node(parts[0]), parts[1])
|
|
else:
|
|
#print("Interpreting receiver as node path")
|
|
return Callable(what.get_node(parts[0]), parts[1])
|
|
TYPE_SIGNAL:
|
|
#print("Property is signal; " +
|
|
#"interpreting extra as signal reference (:-style)")
|
|
#print("Parsing signal reference for emitter and signal name")
|
|
var parts := String(value).split(':')
|
|
#print("Parse result: %s" % parts)
|
|
if parts.size() == 1:
|
|
#print("No emitter; assuming implicit self")
|
|
return what.get(parts[0])
|
|
elif nodes_by_unique_name.has(parts[0]):
|
|
#print("Emitter refers to a node's unique name " +
|
|
#"as defined in the glTF data; " +
|
|
#"using that node")
|
|
return nodes_by_unique_name.get(parts[0]).get(parts[1])
|
|
elif String(parts[0]).begins_with('/'):
|
|
#print("Emitter begins with /; " +
|
|
#"interpreting as absolute node path")
|
|
return root.get_node(parts[0]).get(parts[1])
|
|
else:
|
|
#print("Interpreting emitter as node path")
|
|
return what.get_node(parts[0]).get(parts[1])
|
|
TYPE_DICTIONARY:
|
|
#print("Property is Dictionary; interpreting extra as JSON")
|
|
return JSON.parse_string(value)
|
|
TYPE_ARRAY:
|
|
#print("Property is array; assuming extra is array")
|
|
return value
|
|
TYPE_PACKED_BYTE_ARRAY:
|
|
#print("Property is PackedByteArray; assuming extra is int array")
|
|
return PackedByteArray(value)
|
|
TYPE_PACKED_INT32_ARRAY:
|
|
#print("Property is PackedInt32Array; assuming extra is int array")
|
|
return PackedInt32Array(value)
|
|
TYPE_PACKED_INT64_ARRAY:
|
|
#print("Property is PackedInt64Array; assuming extra is int array")
|
|
return PackedInt64Array(value)
|
|
TYPE_PACKED_FLOAT32_ARRAY:
|
|
#print("Property is PackedFloat32Array; " +
|
|
#"assuming extra is float array")
|
|
return PackedFloat32Array(value)
|
|
TYPE_PACKED_FLOAT64_ARRAY:
|
|
#print("Property is PackedFloat64Array; " +
|
|
#"assuming extra is float array")
|
|
return PackedFloat64Array(value)
|
|
TYPE_PACKED_STRING_ARRAY:
|
|
#print("Property is PackedStringArray; " +
|
|
#"treating extra as String containing comma-separated tokens")
|
|
return PackedStringArray(String(value).split(','))
|
|
TYPE_PACKED_VECTOR2_ARRAY:
|
|
#print("Property is PackedVector2Array; ergo, " +
|
|
#"assuming extra is float array of size divisible by 2")
|
|
var vecs: Array[Vector2] = []
|
|
var current := Vector2.ZERO
|
|
var current_i: int = 0
|
|
for num in value:
|
|
current[current_i] = value
|
|
current_i += 1
|
|
if current_i >= 2:
|
|
current_i = 0
|
|
#print("Assembled %s" % current)
|
|
vecs.push_back(current)
|
|
return PackedVector2Array(vecs)
|
|
TYPE_PACKED_VECTOR3_ARRAY:
|
|
#print("Property is PackedVector3Array; ergo, " +
|
|
#"assuming extra is float array of size divisible by 3")
|
|
var vecs: Array[Vector2] = []
|
|
var current := Vector3.ZERO
|
|
var current_i: int = 0
|
|
for num in value:
|
|
current[current_i] = value
|
|
current_i += 1
|
|
if current_i >= 3:
|
|
current_i = 0
|
|
#print("Assembled %s" % current)
|
|
vecs.push_back(current)
|
|
return PackedVector3Array(vecs)
|
|
TYPE_PACKED_COLOR_ARRAY:
|
|
#print("Property is PackedColorArray")
|
|
var colors: Array[Color] = []
|
|
if value is String:
|
|
#print("Extra is string; parsing as comma-separated tokens")
|
|
for token in value.split(','):
|
|
#print("Parsing token '%s' as a Color" % token)
|
|
colors.push_back(Color(token))
|
|
else:
|
|
#print("Assuming extra is float array of size divisible by 4")
|
|
var current := Color.WHITE
|
|
var current_i: int = 0
|
|
for num in value:
|
|
current[current_i] = value
|
|
current_i += 1
|
|
if current_i >= 4:
|
|
current_i = 0
|
|
#print("Assembled %s" % current)
|
|
colors.push_back(current)
|
|
return PackedColorArray(colors)
|
|
_:
|
|
#print("Property is of unknown or unspecified type; " +
|
|
#"returning extra unmodified")
|
|
return value
|
|
|
|
func _replace_node_maybe_with_scene_instance(x: Node, y: Node) -> void:
|
|
var scene_path := y.scene_file_path
|
|
x.replace_by(y, true)
|
|
y.scene_file_path = scene_path
|