SnekStudio module for multiplayer / multiuser support
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

81 lines
3.0 KiB

class_name SyncController
extends Node
var module: copyMultiplayer
var model_controller: ModelController
var peer_id: int
var nickname: String
var model_name: String
var model: Node
var skeleton: Skeleton3D
# Allows us to use the "apply_animations" function to apply blendshapes to a model.
static var _functions_blendshapes: Script = load("res://Mods/MediaPipe/MediaPipeController_BlendShapes.gd")
func _ready() -> void:
module = get_parent().get_parent()
model_controller = get_parent()
peer_id = model_controller.name.to_int()
func get_display_name() -> String:
if nickname: return "Player '%s' (%d)" % [ nickname, peer_id ]
else: return "Player (%d)" % peer_id
func change_nickname(new_nickname: String) -> void:
new_nickname = new_nickname.strip_edges()
if new_nickname == "": return # Ignore empty nicknames.
module.print_log("%s is now known as '%s'" % [ get_display_name(), new_nickname ])
nickname = new_nickname
## Attempts to change the model of this player.
func change_model(filename: String) -> void:
if not filename.is_valid_filename():
module.print_log("ERROR: '%s' is not a valid file name!" % filename)
return
var full_path := module.cache.path_join(filename)
if not FileAccess.file_exists(full_path):
module.print_log("%s wanted to switch to '%s', but it doesn't exist, skipping" % [ get_display_name(), filename ])
return
if not model_controller.load_vrm(full_path):
module.print_log("ERROR: Model '%s' could not be loaded!" % filename)
return
module.print_log("%s switched to '%s'" % [ get_display_name(), filename ])
model_name = filename
model = model_controller.get_node_or_null("Model")
skeleton = model_controller._get_model_skeleton()
func sync_model_animation(
model_transform: Transform3D,
shape_dict: Dictionary, # Dictionary[String, float]
bone_poses: Dictionary, # Dictionary[String, Transform3D]
) -> void:
if (not model) or (not skeleton): return
model.transform = model_transform
_functions_blendshapes.apply_animations(model, shape_dict)
for bone_name in bone_poses:
var pose: Transform3D = bone_poses[bone_name]
var idx := skeleton.find_bone(bone_name)
if idx != -1: skeleton.set_bone_pose(idx, pose)
@warning_ignore("shadowed_variable")
static func send_model_animation(module: copyMultiplayer) -> void:
# Check if there's other players we're connected to.
if module.multiplayer.get_peers().size() == 0: return
var model := module.get_model()
var skeleton := module.get_skeleton()
var media_pipe = module.get_node("../MediaPipeController")
if (not model) or (not skeleton) or (not media_pipe): return
var shape_dict = media_pipe.blend_shape_last_values
var bone_poses = {}
for bone_name in module.tracked_bones:
var bone_idx = skeleton.find_bone(bone_name)
if bone_idx == -1: continue
var bone_pose = skeleton.get_bone_pose(bone_idx)
bone_poses[bone_name] = bone_pose
# FIXME: This sends way more information than necessary, but works as a proof-of-concept!
module.sync_model_animation.rpc(model.transform, shape_dict, bone_poses)