//========= Copyright 2016-2020, HTC Corporation. All rights reserved. =========== #pragma warning disable 0649 using HTC.UnityPlugin.Utility; using HTC.UnityPlugin.VRModuleManagement; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine; #if VIU_OCULUSVR_AVATAR using Oculus.Avatar; #endif namespace HTC.UnityPlugin.Vive.OculusVRExtension { // Only works in playing mode public class VIUOculusVRRenderModel : MonoBehaviour { private struct ChildTransforms { public Transform root; public Transform attach; } // Name of the sub-object which represents the "local" coordinate space for each component. public const string LOCAL_TRANSFORM_NAME = "attach"; public const string MODEL_OVERRIDE_WARNNING = "Model override is really only meant to be used in " + "the scene view for lining things up. Use tracked device " + "index instead to ensure the correct model is displayed for all users."; private const uint LEFT_INDEX = 1; private const uint RIGHT_INDEX = 2; [Tooltip(MODEL_OVERRIDE_WARNNING)] [SerializeField] private string m_modelOverride; [Tooltip("Shader to apply to model.")] [SerializeField] private Shader m_shaderOverride; [Tooltip("Update transforms of components at runtime to reflect user action.")] [SerializeField] private bool m_updateDynamically = true; private uint m_deviceIndex = VRModule.INVALID_DEVICE_INDEX; private MeshFilter m_meshFilter; private MeshRenderer m_meshRenderer; private IndexedTable m_chilTransforms = new IndexedTable(); private IndexedTable m_materials = new IndexedTable(); private HashSet m_loadingRenderModels = new HashSet(); private bool m_isAppQuit; #if (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER || VIU_OCULUSVR_1_37_0_OR_NEWER) && VIU_OCULUSVR_AVATAR private IntPtr sdkAvatar = IntPtr.Zero; private HashSet assetLoadingIds = new HashSet(); private Dictionary trackedComponents = new Dictionary(); private OvrAvatarTouchController ovrController; private int renderPartCount = 0; private Shader SurfaceShader; private Shader SurfaceShaderSelfOccluding; private List RenderParts = new List(); private ovrAvatarHandInputState inputStateLeft; private ovrAvatarHandInputState inputStateRight; private bool firstSkinnedUpdate = true; private bool assetsFinishedLoading = false; private bool isMaterialInitilized = false; private bool isMeshInitilized = false; private ovrAvatarControllerType m_controllerType = ovrAvatarControllerType.Quest; #endif private string preferedModelName { get { if (!string.IsNullOrEmpty(m_modelOverride)) { return m_modelOverride; } #if UNITY_EDITOR if (!Application.isPlaying) { return string.Empty; } else #endif { return VRModule.GetCurrentDeviceState(m_deviceIndex).renderModelName; } } } private Shader preferedShader { get { return m_shaderOverride == null ? Shader.Find("Standard") : m_shaderOverride; } } public bool updateDynamically { get { return m_updateDynamically; } set { m_updateDynamically = value; } } public bool isLoadingModel { get { return m_loadingRenderModels.Count > 0; } } public string loadedModelName { get; private set; } public bool isModelLoaded { get { return !string.IsNullOrEmpty(loadedModelName); } } public Shader loadedShader { get; private set; } public string modelOverride { get { return m_modelOverride; } set { m_modelOverride = value; LoadPreferedModel(); } } public Shader shaderOverride { get { return m_shaderOverride; } set { m_shaderOverride = value; SetPreferedShader(); } } #if UNITY_EDITOR private void OnValidate() { if (Application.isPlaying) { UnityEditor.EditorApplication.delayCall += () => { if (!m_isAppQuit && this != null && isActiveAndEnabled) { LoadPreferedModel(); SetPreferedShader(); } }; } } #endif private void Update() { if (m_updateDynamically) { #if (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER || VIU_OCULUSVR_1_37_0_OR_NEWER) && VIU_OCULUSVR_AVATAR if (sdkAvatar == IntPtr.Zero) { return; } if (m_deviceIndex == LEFT_INDEX) { inputStateLeft.transform.position = transform.position; inputStateLeft.transform.orientation = transform.rotation; inputStateLeft.transform.scale = transform.localScale; inputStateLeft.buttonMask = 0; inputStateLeft.touchMask = ovrAvatarTouch.Pointing | ovrAvatarTouch.ThumbUp; if (ViveInput.GetPress(HandRole.LeftHand, ControllerButton.AKey)) { inputStateLeft.buttonMask |= ovrAvatarButton.One; } if (ViveInput.GetPress(HandRole.LeftHand, ControllerButton.BKey)) { inputStateLeft.buttonMask |= ovrAvatarButton.Two; } if (ViveInput.GetPress(HandRole.LeftHand, ControllerButton.System)) { inputStateLeft.buttonMask |= ovrAvatarButton.Three; } if (ViveInput.GetPress(HandRole.LeftHand, ControllerButton.Pad)) { inputStateLeft.buttonMask |= ovrAvatarButton.Joystick; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.AKeyTouch)) { inputStateLeft.touchMask &= ~ovrAvatarTouch.ThumbUp; inputStateLeft.touchMask |= ovrAvatarTouch.One; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.BkeyTouch)) { inputStateLeft.touchMask &= ~ovrAvatarTouch.ThumbUp; inputStateLeft.touchMask |= ovrAvatarTouch.Two; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.PadTouch)) { inputStateLeft.touchMask &= ~ovrAvatarTouch.ThumbUp; inputStateLeft.touchMask |= ovrAvatarTouch.Joystick; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.TriggerTouch)) { inputStateLeft.touchMask &= ~ovrAvatarTouch.Pointing; inputStateLeft.touchMask |= ovrAvatarTouch.Index; } inputStateLeft.joystickX = ViveInput.GetAxis(HandRole.LeftHand, ControllerAxis.JoystickX); inputStateLeft.joystickY = ViveInput.GetAxis(HandRole.LeftHand, ControllerAxis.JoystickY); inputStateLeft.indexTrigger = ViveInput.GetAxis(HandRole.LeftHand, ControllerAxis.Trigger); inputStateLeft.handTrigger = ViveInput.GetAxis(HandRole.LeftHand, ControllerAxis.CapSenseGrip); inputStateLeft.isActive = true; } else if (m_deviceIndex == RIGHT_INDEX) { inputStateRight.transform.position = transform.position; inputStateRight.transform.orientation = transform.rotation; inputStateRight.transform.scale = transform.localScale; inputStateRight.buttonMask = 0; inputStateRight.touchMask = ovrAvatarTouch.Pointing | ovrAvatarTouch.ThumbUp; if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.AKey)) { inputStateRight.buttonMask |= ovrAvatarButton.One; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.BKey)) { inputStateRight.buttonMask |= ovrAvatarButton.Two; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.Pad)) { inputStateRight.buttonMask |= ovrAvatarButton.Joystick; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.AKeyTouch)) { inputStateRight.touchMask &= ~ovrAvatarTouch.ThumbUp; inputStateRight.touchMask |= ovrAvatarTouch.One; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.BkeyTouch)) { inputStateRight.touchMask &= ~ovrAvatarTouch.ThumbUp; inputStateRight.touchMask |= ovrAvatarTouch.Two; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.PadTouch)) { inputStateRight.touchMask &= ~ovrAvatarTouch.ThumbUp; inputStateRight.touchMask |= ovrAvatarTouch.Joystick; } if (ViveInput.GetPress(HandRole.RightHand, ControllerButton.TriggerTouch)) { inputStateRight.touchMask &= ~ovrAvatarTouch.Pointing; inputStateRight.touchMask |= ovrAvatarTouch.Index; } inputStateRight.joystickX = ViveInput.GetAxis(HandRole.RightHand, ControllerAxis.JoystickX); inputStateRight.joystickY = ViveInput.GetAxis(HandRole.RightHand, ControllerAxis.JoystickY); inputStateRight.indexTrigger = ViveInput.GetAxis(HandRole.RightHand, ControllerAxis.Trigger); inputStateRight.handTrigger = ViveInput.GetAxis(HandRole.RightHand, ControllerAxis.CapSenseGrip); inputStateRight.isActive = true; } CAPI.ovrAvatarPose_UpdateHandsWithType(sdkAvatar, inputStateLeft, inputStateRight, m_controllerType); CAPI.ovrAvatarPose_Finalize(sdkAvatar, Time.deltaTime); #endif UpdateComponents(); #if VIU_OCULUSVR_1_37_0_OR_NEWER && VIU_OCULUSVR_AVATAR if (m_deviceIndex == LEFT_INDEX) { ovrAvatarControllerComponent component = new ovrAvatarControllerComponent(); if (CAPI.ovrAvatarPose_GetLeftControllerComponent(sdkAvatar, ref component)) { UpdateAvatarComponent(component.renderComponent); } } else if (m_deviceIndex == RIGHT_INDEX) { ovrAvatarControllerComponent component = new ovrAvatarControllerComponent(); if (CAPI.ovrAvatarPose_GetRightControllerComponent(sdkAvatar, ref component)) { UpdateAvatarComponent(component.renderComponent); } } #endif } } private void OnEnable() { LoadPreferedModel(); } private void OnDestroy() { ClearModel(); } private void OnApplicationQuit() { m_isAppQuit = true; } public void ClearModel() { if (!isModelLoaded) { return; } if (m_meshRenderer != null) { Destroy(m_meshRenderer); } if (m_meshFilter != null) { Destroy(m_meshFilter); } for (int i = 0, imax = m_chilTransforms.Count; i < imax; ++i) { var c = m_chilTransforms.GetValueByIndex(i); if (c.root == null) { continue; } Destroy(c.root.gameObject); } m_chilTransforms.Clear(); m_materials.Clear(); loadedModelName = string.Empty; loadedShader = null; } private void SetPreferedShader() { SetShader(preferedShader); } private void SetShader(Shader newShader) { if (loadedShader == newShader) { return; } loadedShader = newShader; if (m_materials == null) { return; } for (int i = 0, imax = m_materials.Count; i < imax; ++i) { var mat = m_materials.GetValueByIndex(i); if (mat != null) { mat.shader = newShader; } } } private void LoadPreferedModel() { LoadModel(preferedModelName); } private void LoadModel(string renderModelName) { //Debug.Log(transform.parent.parent.name + " Try LoadModel " + renderModelName); #if UNITY_EDITOR if (!Application.isPlaying) { Debug.LogWarning("LoadModel failed! This function only works in playing mode"); return; } #endif if (string.IsNullOrEmpty(loadedModelName) && string.IsNullOrEmpty(renderModelName)) { return; } if (loadedModelName == renderModelName) { return; } if (m_loadingRenderModels.Contains(renderModelName)) { return; } ClearModel(); if (!m_isAppQuit && !string.IsNullOrEmpty(renderModelName)) { //Debug.Log(transform.parent.parent.name + " LoadModel " + renderModelName); m_loadingRenderModels.Add(renderModelName); #if VIU_OCULUSVR_1_37_0_OR_NEWER && VIU_OCULUSVR_AVATAR OvrAvatarSDKManager.AvatarSpecRequestParams avatarSpecRequest = new OvrAvatarSDKManager.AvatarSpecRequestParams( 0, this.AvatarSpecificationCallback, false, ovrAvatarAssetLevelOfDetail.Highest, false, ovrAvatarLookAndFeelVersion.Two, ovrAvatarLookAndFeelVersion.One, false); OvrAvatarSDKManager.Instance.RequestAvatarSpecification(avatarSpecRequest); #elif VIU_OCULUSVR_1_36_0_OR_NEWER && VIU_OCULUSVR_AVATAR OvrAvatarSDKManager.Instance.RequestAvatarSpecification( 0, this.AvatarSpecificationCallback, false, ovrAvatarAssetLevelOfDetail.Highest, false, ovrAvatarLookAndFeelVersion.Two, ovrAvatarLookAndFeelVersion.One, false); #elif VIU_OCULUSVR_1_32_0_OR_NEWER && VIU_OCULUSVR_AVATAR OvrAvatarSDKManager.Instance.RequestAvatarSpecification( 0, this.AvatarSpecificationCallback, false, ovrAvatarAssetLevelOfDetail.Highest, false); #endif } } private void AvatarSpecificationCallback(IntPtr avatarSpecification) { #if (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER || VIU_OCULUSVR_1_37_0_OR_NEWER) && VIU_OCULUSVR_AVATAR sdkAvatar = CAPI.ovrAvatar_Create(avatarSpecification, ovrAvatarCapabilities.All); CAPI.ovrAvatar_SetLeftControllerVisibility(sdkAvatar, true); CAPI.ovrAvatar_SetRightControllerVisibility(sdkAvatar, true); //Fetch all the assets that this avatar uses. UInt32 assetCount = CAPI.ovrAvatar_GetReferencedAssetCount(sdkAvatar); for (UInt32 i = 0; i < assetCount; ++i) { UInt64 id = CAPI.ovrAvatar_GetReferencedAsset(sdkAvatar, i); if (OvrAvatarSDKManager.Instance.GetAsset(id) == null) { OvrAvatarSDKManager.Instance.BeginLoadingAsset( id, ovrAvatarAssetLevelOfDetail.Highest, AssetLoadedCallback); assetLoadingIds.Add(id); } } #endif } #if (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER || VIU_OCULUSVR_1_37_0_OR_NEWER) && VIU_OCULUSVR_AVATAR private void AssetLoadedCallback(OvrAvatarAsset asset) { assetLoadingIds.Remove(asset.assetID); } #endif private void OnLoadModelComplete(string renderModelName) { m_loadingRenderModels.Remove(renderModelName); if (loadedModelName == renderModelName) { return; } if (preferedModelName != renderModelName) { return; } if (!isActiveAndEnabled) { return; } //Debug.Log(transform.parent.parent.name + " OnLoadModelComplete " + renderModelName); ClearModel(); loadedModelName = renderModelName; } private void UpdateComponents() { #if (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER || VIU_OCULUSVR_1_37_0_OR_NEWER) && VIU_OCULUSVR_AVATAR if (assetLoadingIds.Count == 0) { if (!assetsFinishedLoading) { UpdateSDKAvatarUnityState(); assetsFinishedLoading = true; } } #endif } #if (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER || VIU_OCULUSVR_1_37_0_OR_NEWER) && VIU_OCULUSVR_AVATAR private void UpdateSDKAvatarUnityState() { #if VIU_OCULUSVR_1_37_0_OR_NEWER && VIU_OCULUSVR_AVATAR ovrAvatarControllerComponent controllerComponent = new ovrAvatarControllerComponent(); ovrAvatarComponent dummyComponent = new ovrAvatarComponent(); OvrAvatarTouchController controller = null; if (m_deviceIndex == LEFT_INDEX) { if (CAPI.ovrAvatarPose_GetLeftControllerComponent(sdkAvatar, ref controllerComponent)) { CAPI.ovrAvatarComponent_Get(controllerComponent.renderComponent, true, ref dummyComponent); AddAvatarComponent(ref controller, dummyComponent); controller.isLeftHand = true; } } else if (m_deviceIndex == RIGHT_INDEX) { if (CAPI.ovrAvatarPose_GetRightControllerComponent(sdkAvatar, ref controllerComponent)) { CAPI.ovrAvatarComponent_Get(controllerComponent.renderComponent, true, ref dummyComponent); AddAvatarComponent(ref controller, dummyComponent); controller.isLeftHand = false; } } #elif (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER) && VIU_OCULUSVR_AVATAR //Iterate through all the render components UInt32 componentCount = CAPI.ovrAvatarComponent_Count(sdkAvatar); for (UInt32 i = 0; i < componentCount; i++) { IntPtr ptr = CAPI.ovrAvatarComponent_Get_Native(sdkAvatar, i); ovrAvatarComponent component = (ovrAvatarComponent)Marshal.PtrToStructure(ptr, typeof(ovrAvatarComponent)); if (!trackedComponents.ContainsKey(component.name)) { GameObject componentObject = null; Type specificType = null; if (specificType == null && (ovrAvatarCapabilities.All & ovrAvatarCapabilities.Hands) != 0) { if (m_deviceIndex == LEFT_INDEX) { ovrAvatarControllerComponent? controllerComponent = CAPI.ovrAvatarPose_GetLeftControllerComponent(sdkAvatar); if (specificType == null && controllerComponent.HasValue && ptr == controllerComponent.Value.renderComponent) { specificType = typeof(OvrAvatarTouchController); if (ovrController != null) { componentObject = ovrController.gameObject; } } } else if (m_deviceIndex == RIGHT_INDEX) { ovrAvatarControllerComponent? controllerComponent = CAPI.ovrAvatarPose_GetRightControllerComponent(sdkAvatar); if (specificType == null && controllerComponent.HasValue && ptr == controllerComponent.Value.renderComponent) { specificType = typeof(OvrAvatarTouchController); if (ovrController != null) { componentObject = ovrController.gameObject; } } } } if (componentObject != null) { AddAvatarComponent(componentObject, component); } } UpdateAvatarComponent(component); } #endif } #if VIU_OCULUSVR_1_37_0_OR_NEWER && VIU_OCULUSVR_AVATAR private void AddAvatarComponent(ref T root, ovrAvatarComponent nativeComponent) where T : OvrAvatarComponent { GameObject componentObject = new GameObject(); componentObject.name = nativeComponent.name; componentObject.transform.SetParent(transform); root = componentObject.AddComponent(); AddRenderParts(root, nativeComponent, componentObject.transform); } #elif VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER private void AddAvatarComponent(GameObject componentObject, ovrAvatarComponent component) { OvrAvatarComponent ovrComponent = componentObject.AddComponent(); trackedComponents.Add(component.name, ovrComponent); AddRenderParts(ovrComponent, component, componentObject.transform); } #endif private void AddRenderParts( OvrAvatarComponent ovrComponent, ovrAvatarComponent component, Transform parent) { for (UInt32 renderPartIndex = 0; renderPartIndex < component.renderPartCount; renderPartIndex++) { GameObject renderPartObject = new GameObject(); renderPartObject.name = GetRenderPartName(component, renderPartIndex); renderPartObject.transform.SetParent(parent); renderPartObject.transform.localPosition = Vector2.zero; renderPartObject.transform.localRotation = Quaternion.identity; IntPtr renderPart = GetRenderPart(component, renderPartIndex); ovrAvatarRenderPartType type = CAPI.ovrAvatarRenderPart_GetType(renderPart); OvrAvatarRenderComponent ovrRenderPart = null; switch (type) { case ovrAvatarRenderPartType.SkinnedMeshRenderPBS: ovrRenderPart = AddSkinnedMeshRenderPBSComponent(renderPartObject, CAPI.ovrAvatarRenderPart_GetSkinnedMeshRenderPBS(renderPart)); break; case ovrAvatarRenderPartType.SkinnedMeshRenderPBS_V2: ovrRenderPart = AddSkinnedMeshRenderPBSV2Component(renderPartObject, CAPI.ovrAvatarRenderPart_GetSkinnedMeshRenderPBSV2(renderPart)); break; default: break;// throw new NotImplementedException(string.Format("Unsupported render part type: {0}", type.ToString())); } if (ovrRenderPart != null) { ovrComponent.RenderParts.Add(ovrRenderPart); RenderParts.Add(ovrRenderPart); } } } private OvrAvatarSkinnedMeshRenderPBSComponent AddSkinnedMeshRenderPBSComponent(GameObject gameObject, ovrAvatarRenderPart_SkinnedMeshRenderPBS skinnedMeshRenderPBS) { OvrAvatarSkinnedMeshRenderPBSComponent skinnedMeshRenderer = gameObject.AddComponent(); OvrAvatarAssetMesh meshAsset = (OvrAvatarAssetMesh)OvrAvatarSDKManager.Instance.GetAsset(skinnedMeshRenderPBS.meshAssetID); SkinnedMeshRenderer renderer = meshAsset.CreateSkinnedMeshRendererOnObject(gameObject); #if UNITY_ANDROID renderer.quality = SkinQuality.Bone2; #else renderer.quality = SkinQuality.Bone4; #endif renderer.updateWhenOffscreen = true; skinnedMeshRenderer.mesh = renderer; transform.GetChild(0).localPosition = Vector2.zero; transform.GetChild(0).localRotation = Quaternion.identity; transform.GetChild(0).GetComponentInChildren().gameObject.SetActive(false); var shader = Shader.Find("OvrAvatar/AvatarSurfaceShaderPBS"); renderer.sharedMaterial = CreateAvatarMaterial(gameObject.name + "_material", shader); SetMaterialOpaque(renderer.sharedMaterial); skinnedMeshRenderer.bones = renderer.bones; return skinnedMeshRenderer; } private OvrAvatarSkinnedMeshPBSV2RenderComponent AddSkinnedMeshRenderPBSV2Component(GameObject gameObject, ovrAvatarRenderPart_SkinnedMeshRenderPBS_V2 skinnedMeshRenderPBSV2) { OvrAvatarSkinnedMeshPBSV2RenderComponent skinnedMeshRenderer = gameObject.AddComponent(); OvrAvatarAssetMesh meshAsset = (OvrAvatarAssetMesh)OvrAvatarSDKManager.Instance.GetAsset(skinnedMeshRenderPBSV2.meshAssetID); SkinnedMeshRenderer renderer = meshAsset.CreateSkinnedMeshRendererOnObject(gameObject); #if UNITY_ANDROID renderer.quality = SkinQuality.Bone2; #else renderer.quality = SkinQuality.Bone4; #endif renderer.updateWhenOffscreen = true; skinnedMeshRenderer.mesh = renderer; transform.GetChild(0).localPosition = Vector2.zero; transform.GetChild(0).localRotation = Quaternion.identity; transform.GetChild(0).GetComponentInChildren().gameObject.SetActive(false); var shader = Shader.Find("OvrAvatar/AvatarPBRV2Simple"); renderer.sharedMaterial = CreateAvatarMaterial(gameObject.name + "_material", shader); SetMaterialOpaque(renderer.sharedMaterial); skinnedMeshRenderer.bones = renderer.bones; return skinnedMeshRenderer; } private Material CreateAvatarMaterial(string name, Shader shader) { if (shader == null) { throw new Exception("No shader provided for avatar material."); } Material mat = new Material(shader); mat.name = name; return mat; } private void SetMaterialOpaque(Material mat) { // Initialize shader to use geometry render queue with no blending mat.SetOverrideTag("Queue", "Geometry"); mat.SetOverrideTag("RenderType", "Opaque"); mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); mat.DisableKeyword("_ALPHATEST_ON"); mat.DisableKeyword("_ALPHABLEND_ON"); mat.DisableKeyword("_ALPHAPREMULTIPLY_ON"); mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry; } private static string GetRenderPartName(ovrAvatarComponent component, uint renderPartIndex) { return component.name + "_renderPart_" + (int)renderPartIndex; } static public IntPtr GetRenderPart(ovrAvatarComponent component, UInt32 renderPartIndex) { long offset = Marshal.SizeOf(typeof(IntPtr)) * renderPartIndex; IntPtr marshalPtr = new IntPtr(component.renderParts.ToInt64() + offset); return (IntPtr)Marshal.PtrToStructure(marshalPtr, typeof(IntPtr)); } #if VIU_OCULUSVR_1_37_0_OR_NEWER private void UpdateAvatarComponent(IntPtr nativeComponent) { ovrAvatarComponent nativeAvatarComponent = new ovrAvatarComponent(); CAPI.ovrAvatarComponent_Get(nativeComponent, false, ref nativeAvatarComponent); ConvertTransform(nativeAvatarComponent.transform, transform); for (UInt32 renderPartIndex = 0; renderPartIndex < nativeAvatarComponent.renderPartCount; renderPartIndex++) { if (RenderParts.Count <= renderPartIndex) { break; } OvrAvatarRenderComponent renderComponent = RenderParts[(int)renderPartIndex]; IntPtr renderPart = OvrAvatar.GetRenderPart(nativeAvatarComponent, renderPartIndex); ovrAvatarRenderPartType type = CAPI.ovrAvatarRenderPart_GetType(renderPart); ovrAvatarTransform localTransform; ovrAvatarVisibilityFlags visibilityMask; var mesh = renderComponent.mesh; var bones = renderComponent.bones; switch (type) { case ovrAvatarRenderPartType.SkinnedMeshRenderPBS: visibilityMask = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetVisibilityMask(renderPart); localTransform = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetTransform(renderPart); if ((visibilityMask & ovrAvatarVisibilityFlags.FirstPerson) != 0) { renderComponent.gameObject.SetActive(true); mesh.enabled = true; } UpdateSkinnedMesh(localTransform, renderPart, bones); UInt64 albedoTextureID = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetAlbedoTextureAssetID(renderPart); UInt64 surfaceTextureID = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetSurfaceTextureAssetID(renderPart); mesh.sharedMaterial.SetTexture("_Albedo", OvrAvatarComponent.GetLoadedTexture(albedoTextureID)); mesh.sharedMaterial.SetTexture("_Surface", OvrAvatarComponent.GetLoadedTexture(surfaceTextureID)); break; case ovrAvatarRenderPartType.SkinnedMeshRenderPBS_V2: visibilityMask = CAPI.ovrAvatarSkinnedMeshRenderPBSV2_GetVisibilityMask(renderPart); localTransform = CAPI.ovrAvatarSkinnedMeshRenderPBSV2_GetTransform(renderPart); if ((visibilityMask & ovrAvatarVisibilityFlags.FirstPerson) != 0) { renderComponent.gameObject.SetActive(true); mesh.enabled = true; } UpdateSkinnedMesh(localTransform, renderPart, bones); ovrAvatarPBSMaterialState materialState = CAPI.ovrAvatarSkinnedMeshRenderPBSV2_GetPBSMaterialState(renderPart); Texture2D diffuseTexture = OvrAvatarComponent.GetLoadedTexture(materialState.albedoTextureID); Texture2D normalTexture = OvrAvatarComponent.GetLoadedTexture(materialState.normalTextureID); Texture2D metallicTexture = OvrAvatarComponent.GetLoadedTexture(materialState.metallicnessTextureID); mesh.materials[0].SetTexture("_MainTex", diffuseTexture); mesh.materials[0].SetTexture("_NormalMap", normalTexture); mesh.materials[0].SetTexture("_RoughnessMap", metallicTexture); break; default: break; } } } #elif VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER private void UpdateAvatarComponent(ovrAvatarComponent component) { for (UInt32 renderPartIndex = 0; renderPartIndex < component.renderPartCount; renderPartIndex++) { if (RenderParts.Count <= renderPartIndex) { break; } OvrAvatarRenderComponent renderComponent = RenderParts[(int)renderPartIndex]; IntPtr renderPart = OvrAvatar.GetRenderPart(component, renderPartIndex); ovrAvatarRenderPartType type = CAPI.ovrAvatarRenderPart_GetType(renderPart); switch (type) { case ovrAvatarRenderPartType.SkinnedMeshRenderPBS: var mat = renderComponent.mesh.sharedMaterial; var bones = renderComponent.bones; ovrAvatarVisibilityFlags visibilityMask = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetVisibilityMask(renderPart); ovrAvatarTransform localTransform = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetTransform(renderPart); UpdateSkinnedMesh(localTransform, renderPart, bones); UInt64 albedoTextureID = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetAlbedoTextureAssetID(renderPart); UInt64 surfaceTextureID = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetSurfaceTextureAssetID(renderPart); mat.SetTexture("_Albedo", OvrAvatarComponent.GetLoadedTexture(albedoTextureID)); mat.SetTexture("_Surface", OvrAvatarComponent.GetLoadedTexture(surfaceTextureID)); break; default: break; } } } #endif private void UpdateSkinnedMesh(ovrAvatarTransform localTransform, IntPtr renderPart, Transform[] bones) { ConvertTransform(localTransform, transform); ovrAvatarRenderPartType type = CAPI.ovrAvatarRenderPart_GetType(renderPart); UInt64 dirtyJoints; switch (type) { case ovrAvatarRenderPartType.SkinnedMeshRender: dirtyJoints = CAPI.ovrAvatarSkinnedMeshRender_GetDirtyJoints(renderPart); break; case ovrAvatarRenderPartType.SkinnedMeshRenderPBS: dirtyJoints = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetDirtyJoints(renderPart); break; case ovrAvatarRenderPartType.SkinnedMeshRenderPBS_V2: dirtyJoints = CAPI.ovrAvatarSkinnedMeshRenderPBSV2_GetDirtyJoints(renderPart); break; default: throw new Exception("Unhandled render part type: " + type); } for (UInt32 i = 0; i < 64; i++) { UInt64 dirtyMask = (ulong)1 << (int)i; // We need to make sure that we fully update the initial position of // Skinned mesh renderers, then, thereafter, we can only update dirty joints if ((firstSkinnedUpdate && i < bones.Length) || (dirtyMask & dirtyJoints) != 0) { //This joint is dirty and needs to be updated Transform targetBone = bones[i]; ovrAvatarTransform transform; switch (type) { case ovrAvatarRenderPartType.SkinnedMeshRenderPBS: transform = CAPI.ovrAvatarSkinnedMeshRenderPBS_GetJointTransform(renderPart, i); break; case ovrAvatarRenderPartType.SkinnedMeshRenderPBS_V2: transform = CAPI.ovrAvatarSkinnedMeshRenderPBSV2_GetJointTransform(renderPart, i); break; default: throw new Exception("Unhandled render part type: " + type); } ConvertTransform(transform, targetBone); } } firstSkinnedUpdate = false; } private void ConvertTransform(ovrAvatarTransform transform, Transform target) { Vector3 position = transform.position; position.z = -position.z; Quaternion orientation = transform.orientation; orientation.x = -orientation.x; orientation.y = -orientation.y; target.localPosition = position; target.localRotation = orientation; target.localScale = transform.scale; } #endif public void SetDeviceIndex(uint index) { //Debug.Log(transform.parent.parent.name + " SetDeviceIndex " + index); m_deviceIndex = index; #if (VIU_OCULUSVR_1_32_0_OR_NEWER || VIU_OCULUSVR_1_36_0_OR_NEWER) && VIU_OCULUSVR_AVATAR ovrController = this.GetComponent(); #endif #if VIU_OCULUSVR && VIU_OCULUSVR_AVATAR var headsetType = OVRPlugin.GetSystemHeadsetType(); switch (headsetType) { #if !VIU_OCULUSVR_19_0_OR_NEWER case OVRPlugin.SystemHeadset.GearVR_R320: case OVRPlugin.SystemHeadset.GearVR_R321: case OVRPlugin.SystemHeadset.GearVR_R322: case OVRPlugin.SystemHeadset.GearVR_R323: case OVRPlugin.SystemHeadset.GearVR_R324: case OVRPlugin.SystemHeadset.GearVR_R325: m_controllerType = ovrAvatarControllerType.Malibu; break; case OVRPlugin.SystemHeadset.Oculus_Go: m_controllerType = ovrAvatarControllerType.Go; break; #endif #if VIU_OCULUSVR_16_0_OR_NEWER case OVRPlugin.SystemHeadset.Oculus_Link_Quest: #endif case OVRPlugin.SystemHeadset.Oculus_Quest: #if VIU_OCULUSVR_1_37_0_OR_NEWER case OVRPlugin.SystemHeadset.Rift_S: m_controllerType = ovrAvatarControllerType.Quest; break; #endif case OVRPlugin.SystemHeadset.Rift_DK1: case OVRPlugin.SystemHeadset.Rift_DK2: case OVRPlugin.SystemHeadset.Rift_CV1: default: m_controllerType = ovrAvatarControllerType.Touch; break; } #endif LoadPreferedModel(); } } }