// Called by ObjectSync when become ready async void OnSyncReady() { Debug.Log("OnSyncReady"); SyncObject obj = GetComponent <ObjectSync>().SyncObject; SyncNode node = GetComponent <ObjectSync>().Node; obj.BeforeSync += OnBeforeSync; obj.AfterSync += OnAfterSync; obj.RegisterFieldUpdateHandler("leftHand", OnLeftHandUpdated); obj.RegisterFieldUpdateHandler("rightHand", OnRightHandUpdated); obj.RegisterFieldUpdateHandler("mouseGrabber", OnMouseGrabberUpdated); OnLeftHandUpdated(); OnRightHandUpdated(); OnMouseGrabberUpdated(); Blob vrmBlob; if (GetComponent <ObjectSync>().IsOriginal) { byte[] vrmData = File.ReadAllBytes(Settings.Instance.AvatarPath); // Use MIME type for GLTF binary https://www.iana.org/assignments/media-types/model/gltf-binary vrmBlob = new Blob(vrmData, "model/gltf-binary"); BlobHandle vrmHandle = vrmBlob.GenerateHandle(); node.WriteBlob(vrmHandle, vrmBlob); obj.SetField("vrm", vrmHandle); } else { while (!obj.HasField("vrm") || !(obj.GetField("vrm") is BlobHandle)) { obj.WriteDebugLog("PlayerAvatar", "Field vrm not ready"); await UniTask.WaitForFixedUpdate(); } BlobHandle blobHandle = (BlobHandle)obj.GetField("vrm"); vrmBlob = await node.ReadBlob(blobHandle); } // Disable collision detection (and character control) during VRM load // When the avatar collides with something during creation, // it goes to wrong position (e.g. floating). // Note: CharacterController inherits Collider. // (See https://docs.unity3d.com/2019.3/Documentation/ScriptReference/CharacterController.html ) GetComponent <CharacterController>().enabled = false; // Load VRM from byte array // https://github.com/vrm-c/UniVRM/wiki/Runtime-import // https://qiita.com/sh_akira/items/8155e4b69107c2a7ede6 ctx = new VRMImporterContext(); ctx.Root = new GameObject(); // VRM is loaded as a separate object ctx.ParseGlb(vrmBlob.Data); var meta = ctx.ReadMeta(); obj.WriteLog("PlayerAvatar", $"Loading VRM {meta.Title} created by {meta.Author} ({meta.ContactInformation})"); obj.WriteLog("PlayerAvatar", $"AllowedUser={meta.AllowedUser}, ViolentUsage={meta.ViolentUssage}"); obj.WriteLog("PlayerAvatar", $"SexualUsage={meta.SexualUssage}, CommercialUsage={meta.CommercialUssage}"); obj.WriteLog("PlayerAvatar", $"OtherPermissionUrl={meta.OtherPermissionUrl}"); obj.WriteLog("PlayerAvatar", $"LicenseType={meta.LicenseType}"); obj.WriteLog("PlayerAvatar", $"OtherLicenseUrl={meta.OtherLicenseUrl}"); await ctx.LoadAsyncTask(); ctx.EnableUpdateWhenOffscreen(); ctx.ShowMeshes(); // Enable collision (and character controller) again (see the disabling line above) GetComponent <CharacterController>().enabled = true; // Move VRM avatar inside this gameObject ctx.Root.transform.SetParent(transform); ctx.Root.transform.localPosition = Vector3.zero; ctx.Root.transform.localRotation = Quaternion.identity; GetComponent <Animator>().avatar = ctx.Root.GetComponent <Animator>().avatar; obj.WriteLog("PlayerAvatar", $"VRM loaded"); if (GetComponent <ObjectSync>().IsOriginal) { // Set up first person view (do not display avatar of the player) // https://vrm.dev/en/univrm/components/univrm_firstperson/ // https://vrm.dev/en/dev/univrm-0.xx/programming/univrm_use_firstperson/ var fp = GetComponentInChildren <VRMFirstPerson>(); fp.Setup(); if (XRRig != null) { XRRig.transform.position = fp.FirstPersonBone.position + fp.FirstPersonBone.rotation * fp.FirstPersonOffset; XRRig.transform.rotation = transform.rotation; // face forward // Do not render layer "VRMThirdPersonOnly" on first person camera xrCamera.cullingMask &= ~LayerMask.GetMask("VRMThirdPersonOnly"); SetHeadShadow(); } if (ThirdPersonCamera != null) { // Do not render layer "VRMFirstPersonOnly" on third person camera ThirdPersonCamera.cullingMask &= ~LayerMask.GetMask("VRMFirstPersonOnly"); } // Left hand if (leftController.HasValue) { // If left hand device is present var leftId = await node.CreateObject(); leftHandObj = node.Objects[leftId]; leftHandObj.SetField("parent", obj.GetObjectRef()); leftHandObj.SetField("tags", new Sequence(new IValue[] { new Primitive <string>("constantVelocity"), new Primitive <string>("collider") })); obj.SetField("leftHand", leftHandObj.GetObjectRef()); } // Right hand if (rightController.HasValue) { // If right hand device is present var rightId = await node.CreateObject(); rightHandObj = node.Objects[rightId]; rightHandObj.SetField("parent", obj.GetObjectRef()); rightHandObj.SetField("tags", new Sequence(new IValue[] { new Primitive <string>("constantVelocity"), new Primitive <string>("collider") })); obj.SetField("rightHand", rightHandObj.GetObjectRef()); } // Mouse var mouseGrabberId = await node.CreateObject(); mouseGrabberObj = node.Objects[mouseGrabberId]; mouseGrabberObj.SetField("parent", obj.GetObjectRef()); mouseGrabberObj.SetField("tags", new Sequence(new IValue[] { new Primitive <string>("constantVelocity"), new Primitive <string>("collider") })); obj.SetField("mouseGrabber", mouseGrabberObj.GetObjectRef()); } }