Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve: move colliders generation to the content loading thread #51

Merged
merged 1 commit into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
)
Loading