Skip to content

Commit

Permalink
improve: move colliders generation to the content loading thread (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
leanmendoza authored Sep 25, 2023
1 parent 1475eac commit 99e028e
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 46 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ jobs:
# args: --manifest-path rust/decentraland-godot-lib/Cargo.toml --release --target=aarch64-apple-darwin

- name: cargo test
if: runner.os == 'windows'
working-directory: rust/decentraland-godot-lib
run: cargo test --release

- name: cargo test
if: runner.os != 'windows'
working-directory: rust/decentraland-godot-lib
run: cargo test

Expand Down
2 changes: 2 additions & 0 deletions godot/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ config_version=5
config/name="Decentraland Godot Rust"
config/description="Decentraland Godot Client - Protocol Squad"
run/main_scene="res://src/main.tscn"
run/disable_stdout=true
run/disable_stderr=true
config/features=PackedStringArray("4.1")
config/icon="res://decentraland_logo.png"

Expand Down
76 changes: 33 additions & 43 deletions godot/src/decentraland_components/gltf_container.gd
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ const GodotGltfState = {
Finished = 4,
}
var gltf_state: int = 0

var gltf_start_loading_time: int = 0
var gltf_instance_req_id: int = 0


func _ready():
Expand All @@ -35,8 +34,6 @@ func load_gltf():
gltf_state = GodotGltfState.NotFound
return

gltf_start_loading_time = Time.get_ticks_usec()

var fetching_resource = Global.content_manager.fetch_gltf(dcl_gltf_src, content_mapping)

# TODO: should we set a timeout?
Expand All @@ -62,30 +59,34 @@ func _on_gltf_loaded(resource_hash: String):
gltf_state = GodotGltfState.FinishedWithError
return

gltf_state = GodotGltfState.Finished
gltf_node = node.duplicate()
gltf_instance_req_id = Global.content_manager.instance_gltf_colliders(
node, dcl_visible_cmask, dcl_invisible_cmask, dcl_scene_id, dcl_entity_id
)
Global.content_manager.gltf_node_collider_finishes.connect(self._on_gltf_instanced)

# var colliders_timestamp = Time.get_ticks_usec()
create_and_set_mask_colliders(gltf_node)
add_child.call_deferred(gltf_node)

func _on_gltf_instanced(req_id: int, node: Node):
if req_id != gltf_instance_req_id:
return

#
# var now = Time.get_ticks_usec()
# var loading_time = now - gltf_start_loading_time
# var colliders_time = now - colliders_timestamp
# print("gltf ", dcl_gltf_src, " loaded in ", roundf(float(loading_time) / 1000.0), " ms", " collider in ", roundf(float(colliders_time) / 1000.0))
Global.content_manager.gltf_node_collider_finishes.disconnect(self._on_gltf_instanced)

gltf_node = node
gltf_state = GodotGltfState.Finished

add_child.call_deferred(gltf_node)


func get_collider(mesh_instance: MeshInstance3D):
func get_animatable_body_3d(mesh_instance: MeshInstance3D):
for maybe_static_body in mesh_instance.get_children():
if maybe_static_body is StaticBody3D:
if maybe_static_body is AnimatableBody3D:
return maybe_static_body

return null


func create_and_set_mask_colliders(node_to_inspect: Node):
func update_mask_colliders(node_to_inspect: Node):
print("updating mask colliders")
for node in node_to_inspect.get_children():
if node is MeshInstance3D:
var mask: int = 0
Expand All @@ -94,31 +95,17 @@ func create_and_set_mask_colliders(node_to_inspect: Node):
else:
mask = dcl_invisible_cmask

var static_body_3d: StaticBody3D = get_collider(node)
if static_body_3d == null and mask > 0:
node.create_trimesh_collision()
static_body_3d = get_collider(node)

if static_body_3d != null:
var parent = static_body_3d.get_parent()
var new_animatable = AnimatableBody3D.new()
parent.add_child(new_animatable)
parent.remove_child(static_body_3d)

for child in static_body_3d.get_children(true):
static_body_3d.remove_child(child)
new_animatable.add_child(child)
if child is CollisionShape3D and child.shape is ConcavePolygonShape3D:
# TODO: workaround, the face's normals probably need to be inverted in some meshes
child.shape.backface_collision = true

new_animatable.collision_layer = mask
new_animatable.sync_to_physics = false
new_animatable.set_meta("dcl_scene_id", dcl_scene_id)
new_animatable.set_meta("dcl_entity_id", dcl_entity_id)
var animatable_body_3d = get_animatable_body_3d(node)
if animatable_body_3d != null:
if mask == 0:
animatable_body_3d.process_mode = Node.PROCESS_MODE_DISABLED
animatable_body_3d.mask = 0
else:
animatable_body_3d.process_mode = Node.PROCESS_MODE_INHERIT
animatable_body_3d.mask = mask

if node is Node:
create_and_set_mask_colliders(node)
update_mask_colliders(node)


func change_gltf(new_gltf, visible_meshes_collision_mask, invisible_meshes_collision_mask):
Expand All @@ -135,9 +122,12 @@ func change_gltf(new_gltf, visible_meshes_collision_mask, invisible_meshes_colli
self.load_gltf.call_deferred()
else:
if (
visible_meshes_collision_mask != dcl_visible_cmask
or invisible_meshes_collision_mask != dcl_invisible_cmask
(
visible_meshes_collision_mask != dcl_visible_cmask
or invisible_meshes_collision_mask != dcl_invisible_cmask
)
and gltf_node != null
):
dcl_visible_cmask = visible_meshes_collision_mask
dcl_invisible_cmask = invisible_meshes_collision_mask
create_and_set_mask_colliders(gltf_node)
update_mask_colliders(gltf_node)
156 changes: 153 additions & 3 deletions godot/src/logic/content_manager.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@ class_name ContentManager
signal content_loading_finished(hash: String)
signal wearable_data_loaded(id: String)
signal meshes_material_finished(id: int)
signal gltf_node_collider_finishes(id: int, gltf_node: Node)

enum ContentType { CT_GLTF_GLB = 1, CT_TEXTURE = 2, CT_WEARABLE_EMOTE = 3, CT_MESHES_MATERIAL = 4 }
enum ContentType {
CT_GLTF_GLB = 1,
CT_TEXTURE = 2,
CT_WEARABLE_EMOTE = 3,
CT_MESHES_MATERIAL = 4,
CT_INSTACE_GLTF = 5
}

var loading_content: Array[Dictionary] = []
var pending_content: Array[Dictionary] = []
Expand Down Expand Up @@ -95,6 +102,31 @@ func duplicate_materials(target_meshes: Array[Dictionary]) -> int:
return id


func instance_gltf_colliders(
gltf_node: Node,
dcl_visible_cmask: int,
dcl_invisible_cmask: int,
dcl_scene_id: int,
dcl_entity_id: int
) -> int:
var id = request_monotonic_counter + 1
request_monotonic_counter = id

pending_content.push_back(
{
"id": id,
"content_type": ContentType.CT_INSTACE_GLTF,
"gltf_node": gltf_node,
"dcl_visible_cmask": dcl_visible_cmask,
"dcl_invisible_cmask": dcl_invisible_cmask,
"dcl_scene_id": dcl_scene_id,
"dcl_entity_id": dcl_entity_id
}
)

return id


# Public function
# @returns true if the resource was added to queue to fetch, false if it had already been fetched
func fetch_gltf(file_path: String, content_mapping: Dictionary):
Expand Down Expand Up @@ -187,6 +219,10 @@ func _th_poll():
if not _process_meshes_material(content):
_th_to_delete.push_back(content)

ContentType.CT_INSTACE_GLTF:
if not _process_instance_gltf(content):
_th_to_delete.push_back(content)

_:
printerr("Fetching invalid content type ", _th_content_type)

Expand Down Expand Up @@ -407,8 +443,7 @@ func _process_loading_gltf(content: Dictionary, finished_downloads: Array[Reques
var node = new_gltf.generate_scene(new_gltf_state)
if node != null:
node.rotate_y(PI)
_hide_colliders(node)
split_animations(node)
create_colliders(node)
if err != OK:
push_warning("resource with errors ", file_path, " : ", err)
else:
Expand Down Expand Up @@ -520,3 +555,118 @@ func _hide_colliders(gltf_node):

if maybe_collider is Node:
_hide_colliders(maybe_collider)


func create_colliders(node_to_inspect: Node):
for node in node_to_inspect.get_children():
if node is MeshInstance3D:
var invisible_mesh = node.name.find("_collider") != -1
var static_body_3d: StaticBody3D = get_collider(node)
if static_body_3d == null:
node.create_trimesh_collision()
static_body_3d = get_collider(node)
static_body_3d.name = node.name + "_colgen"

if static_body_3d != null:
var parent = static_body_3d.get_parent()
var new_animatable = AnimatableBody3D.new()
parent.add_child(new_animatable)
parent.remove_child(static_body_3d)

for child in static_body_3d.get_children(true):
static_body_3d.remove_child(child)
new_animatable.add_child(child)
if child is CollisionShape3D and child.shape is ConcavePolygonShape3D:
# TODO: workaround, the face's normals probably need to be inverted in some meshes
child.shape.backface_collision = true

new_animatable.sync_to_physics = false
new_animatable.process_mode = Node.PROCESS_MODE_DISABLED
new_animatable.collision_layer = 0

new_animatable.set_meta("invisible_mesh", invisible_mesh)

if invisible_mesh:
node.visible = false

if node is Node:
create_colliders(node)


func _process_instance_gltf(content: Dictionary):
var gltf_node: Node = content.get("gltf_node")
var dcl_visible_cmask: int = content.get("dcl_visible_cmask")
var dcl_invisible_cmask: int = content.get("dcl_invisible_cmask")
var dcl_scene_id: int = content.get("dcl_scene_id")
var dcl_entity_id: int = content.get("dcl_entity_id")

gltf_node = gltf_node.duplicate()

var to_remove_nodes = []
update_set_mask_colliders(
gltf_node,
dcl_visible_cmask,
dcl_invisible_cmask,
dcl_scene_id,
dcl_entity_id,
to_remove_nodes
)

for node in to_remove_nodes:
node.get_parent().remove_child(node)

self.emit_signal.call_deferred("gltf_node_collider_finishes", content["id"], gltf_node)
return false


func get_collider(mesh_instance: MeshInstance3D):
for maybe_static_body in mesh_instance.get_children():
if maybe_static_body is StaticBody3D:
return maybe_static_body
return null


func update_set_mask_colliders(
node_to_inspect: Node,
dcl_visible_cmask: int,
dcl_invisible_cmask: int,
dcl_scene_id: int,
dcl_entity_id: int,
to_remove_nodes: Array
):
for node in node_to_inspect.get_children():
if node is AnimatableBody3D:
var mask: int = 0
var invisible_mesh = node.has_meta("invisible_mesh") and node.get_meta("invisible_mesh")
if invisible_mesh:
mask = dcl_invisible_cmask
else:
mask = dcl_visible_cmask

var resolved_node = node
if not node.has_meta("dcl_scene_id"):
var parent = node.get_parent()
resolved_node = node.duplicate()
resolved_node.name = node.name + "_instanced"
resolved_node.set_meta("dcl_scene_id", dcl_scene_id)
resolved_node.set_meta("dcl_entity_id", dcl_entity_id)

parent.add_child(resolved_node)
to_remove_nodes.push_back(node)

if mask == 0:
resolved_node.process_mode = Node.PROCESS_MODE_DISABLED
resolved_node.collision_layer = 0
else:
resolved_node.process_mode = Node.PROCESS_MODE_INHERIT
resolved_node.collision_layer = mask

if node is Node:
update_set_mask_colliders(
node,
dcl_visible_cmask,
dcl_invisible_cmask,
dcl_scene_id,
dcl_entity_id,
to_remove_nodes
)

0 comments on commit 99e028e

Please sign in to comment.