From e300175ee9f08dc71c83eed49d0ac936bddb476b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Papenfu=C3=9F?= Date: Tue, 28 Apr 2026 11:27:06 +0200 Subject: [PATCH 1/5] added webxr anchor feature --- .../webxr/Runtime/Plugins/WebGL/webxr.jslib | 25 ++ .../webxr/Runtime/Plugins/WebGL/webxr.jspre | 259 +++++++++++++++++- .../Runtime/Scripts/WebXRAnchorManager.cs | 61 +++++ .../Runtime/Scripts/WebXRControllerData.cs | 10 + .../Runtime/Scripts/WebXRManager.Anchors.cs | 40 +++ .../webxr/Runtime/Scripts/WebXRManager.cs | 4 +- .../webxr/Runtime/XRPlugin/WebXRSettings.cs | 3 +- .../webxr/Runtime/XRPlugin/WebXRSubsystem.cs | 153 +++++++++++ 8 files changed, 551 insertions(+), 4 deletions(-) create mode 100644 Packages/webxr/Runtime/Scripts/WebXRAnchorManager.cs create mode 100644 Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs diff --git a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib index 4a007e19..8d1d909f 100644 --- a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib +++ b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib @@ -29,6 +29,31 @@ var LibraryWebXR = { Module.HandsArrayOffset = byteOffset / 4; }, + InitAnchorsArray: function(byteOffset) { + Module.AnchorsArrayOffset = byteOffset / 4; + }, + + SetWebXRAnchorEvents: function(onAnchorCreatedPtr, onAnchorDeletedPtr) { + Module.WebXR.onAnchorCreatedPtr = onAnchorCreatedPtr; + Module.WebXR.onAnchorDeletedPtr = onAnchorDeletedPtr; + }, + + CreateAnchorFromViewerHitTest: function() { + Module.WebXR.createAnchorFromViewerHitTest(); + }, + + CreateAnchorFromPose: function(px, py, pz, qx, qy, qz, qw) { + Module.WebXR.createAnchorFromPose(px, py, pz, qx, qy, qz, qw); + }, + + DeleteAnchor: function(anchorId) { + Module.WebXR.deleteAnchor(anchorId); + }, + + DeleteAllAnchors: function() { + Module.WebXR.deleteAllAnchors(); + }, + InitViewerHitTestPoseArray: function(byteOffset) { Module.ViewerHitTestPoseArrayOffset = byteOffset / 4; }, diff --git a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre index be1453df..50ba4859 100644 --- a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre +++ b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre @@ -105,6 +105,11 @@ void main() this.touchIDs = []; this.touches = []; this.eventsNamesToIDs = {}; + this.maxAnchors = 32; + this.anchors = []; + for (var anchorIndex = 0; anchorIndex < this.maxAnchors; anchorIndex++) { + this.anchors.push(new XRAnchorData()); + } this.CreateTouch = function (pageElement, xPercentage, yPercentage) { var touchID = 0; while (this.touchIDs.includes(touchID)) @@ -264,6 +269,27 @@ void main() this.rotationIndices[3] = index; } } + + function XRAnchorData() { + this.frameIndex = 0; + this.idIndex = 0; + this.trackedIndex = 0; + this.positionIndices = [0, 0, 0]; + this.rotationIndices = [0, 0, 0, 0]; + + this.setIndices = function(index) { + this.frameIndex = index++; + this.idIndex = index++; + this.trackedIndex = index++; + this.positionIndices[0] = index++; + this.positionIndices[1] = index++; + this.positionIndices[2] = index++; + this.rotationIndices[0] = index++; + this.rotationIndices[1] = index++; + this.rotationIndices[2] = index++; + this.rotationIndices[3] = index; + } + } function lerp(start, end, percentage) { @@ -346,6 +372,11 @@ void main() this.onSessionVisibilityEvent = null; this.BrowserObject = null; this.JSEventsObject = null; + this.webXRAnchors = {}; + this.webXRAnchorIds = new Map(); + this.nextAnchorId = 1; + this.pendingAnchorRequests = []; + this.lastViewerHitTestResult = null; this.init(); } @@ -382,7 +413,6 @@ void main() }); } - XRManager.prototype.attachEventListeners = function () { var onToggleAr = this.toggleAr.bind(this); var onToggleVr = this.toggleVr.bind(this); @@ -395,6 +425,10 @@ void main() Module.WebXR.toggleVR = onToggleVr; Module.WebXR.toggleHitTest = onToggleHitTest; Module.WebXR.callHapticPulse = onCallHapticPulse; + Module.WebXR.createAnchorFromViewerHitTest = this.createAnchorFromViewerHitTest.bind(this); + Module.WebXR.createAnchorFromPose = this.createAnchorFromPose.bind(this); + Module.WebXR.deleteAnchor = this.deleteAnchor.bind(this); + Module.WebXR.deleteAllAnchors = this.deleteAllAnchors.bind(this); } XRManager.prototype.onRequestARSession = function () { @@ -482,6 +516,10 @@ void main() this.viewerHitTestSource.cancel(); this.viewerHitTestSource = null; } + + this.deleteAllAnchors(); + this.pendingAnchorRequests = []; + this.lastViewerHitTestResult = null; this.removeRemainingTouches(); @@ -1016,6 +1054,13 @@ void main() this.xrData.handLeft.setIndices(Module.HandsArrayOffset); this.xrData.handRight.setIndices(Module.HandsArrayOffset + 212); this.xrData.viewerHitTestPose.setIndices(Module.ViewerHitTestPoseArrayOffset); + if (Module.AnchorsArrayOffset !== undefined) { + for (var anchorIndex = 0; anchorIndex < this.xrData.maxAnchors; anchorIndex++) { + var anchorData = this.xrData.anchors[anchorIndex]; + anchorData.setIndices(Module.AnchorsArrayOffset + anchorIndex * 10); + this.clearAnchorSlot(anchorIndex); + } + } this.xrData.controllerA.updatedProfiles = 0; this.xrData.controllerB.updatedProfiles = 0; this.xrData.controllerA.profiles = []; @@ -1043,6 +1088,213 @@ void main() session.requestAnimationFrame(tempRaf); }); } + + XRManager.prototype.callAnchorCreated = function(anchorId) { + if (Module.WebXR.onAnchorCreatedPtr) { + Module.dynCall_vi(Module.WebXR.onAnchorCreatedPtr, anchorId); + } + } + + XRManager.prototype.callAnchorDeleted = function(anchorId) { + if (Module.WebXR.onAnchorDeletedPtr) { + Module.dynCall_vi(Module.WebXR.onAnchorDeletedPtr, anchorId); + } + } + + XRManager.prototype.clearAnchorSlot = function(slot) { + if (Module.AnchorsArrayOffset === undefined || !this.xrData.anchors[slot]) { + return; + } + + var anchorData = this.xrData.anchors[slot]; + Module.HEAPF32[anchorData.frameIndex] = this.xrData.frameNumber; + Module.HEAPF32[anchorData.idIndex] = -1; + Module.HEAPF32[anchorData.trackedIndex] = 0; + } + + XRManager.prototype.findFreeAnchorSlot = function() { + for (var i = 0; i < this.xrData.maxAnchors; i++) { + var used = false; + for (var anchorId in this.webXRAnchors) { + if (this.webXRAnchors[anchorId].slot == i) { + used = true; + break; + } + } + if (!used) { + return i; + } + } + return -1; + } + + XRManager.prototype.registerAnchor = function(anchor) { + if (!anchor || !anchor.anchorSpace) { + console.warn('WebXR anchor has no anchorSpace.'); + return; + } + + var slot = this.findFreeAnchorSlot(); + if (slot < 0) { + console.warn('No free WebXR anchor slot available.'); + if (anchor.delete) { + anchor.delete(); + } + return; + } + + var anchorId = this.nextAnchorId++; + var entry = { + id: anchorId, + slot: slot, + anchor: anchor + }; + + this.webXRAnchors[anchorId] = entry; + this.webXRAnchorIds.set(anchor, anchorId); + this.clearAnchorSlot(slot); + this.callAnchorCreated(anchorId); + } + + XRManager.prototype.createAnchorFromViewerHitTest = function() { + this.pendingAnchorRequests.push({ type: 'viewer-hit-test' }); + } + + XRManager.prototype.createAnchorFromPose = function(px, py, pz, qx, qy, qz, qw) { + this.pendingAnchorRequests.push({ + type: 'pose', + position: { x: px, y: py, z: pz }, + rotation: { x: qx, y: qy, z: qz, w: qw } + }); + } + + XRManager.prototype.deleteAnchor = function(anchorId) { + var entry = this.webXRAnchors[anchorId]; + if (!entry) { + return; + } + + if (entry.anchor && entry.anchor.delete) { + entry.anchor.delete(); + } + + this.webXRAnchorIds.delete(entry.anchor); + delete this.webXRAnchors[anchorId]; + this.clearAnchorSlot(entry.slot); + this.callAnchorDeleted(anchorId); + } + + XRManager.prototype.deleteAllAnchors = function() { + var ids = []; + for (var anchorId in this.webXRAnchors) { + ids.push(parseInt(anchorId)); + } + for (var i = 0; i < ids.length; i++) { + this.deleteAnchor(ids[i]); + } + } + + XRManager.prototype.processAnchorRequests = function(frame, session) { + if (!session || !session.isAR || this.pendingAnchorRequests.length == 0) { + return; + } + + if (!frame.trackedAnchors) { + console.warn('WebXR anchors are not available in this session. Add anchors to AR optional or required features.'); + this.pendingAnchorRequests = []; + return; + } + + var requests = this.pendingAnchorRequests; + this.pendingAnchorRequests = []; + + for (var i = 0; i < requests.length; i++) { + var request = requests[i]; + var thisXRMananger = this; + + if (request.type == 'viewer-hit-test') { + if (!this.lastViewerHitTestResult || !this.lastViewerHitTestResult.createAnchor) { + console.warn('Cannot create WebXR anchor: no viewer hit-test result available.'); + continue; + } + + this.lastViewerHitTestResult.createAnchor().then(function(anchor) { + thisXRMananger.registerAnchor(anchor); + }).catch(function(error) { + console.warn('Could not create WebXR anchor from hit-test result:', error); + }); + } else if (request.type == 'pose') { + if (!frame.createAnchor || !window.XRRigidTransform) { + console.warn('Cannot create WebXR anchor: XRFrame.createAnchor or XRRigidTransform is unavailable.'); + continue; + } + + var transform = new XRRigidTransform( + { + x: request.position.x, + y: request.position.y, + z: -request.position.z + }, + { + x: -request.rotation.x, + y: -request.rotation.y, + z: request.rotation.z, + w: request.rotation.w + } + ); + + frame.createAnchor(transform, session.refSpace).then(function(anchor) { + thisXRMananger.registerAnchor(anchor); + }).catch(function(error) { + console.warn('Could not create WebXR anchor from pose:', error); + }); + } + } + } + + XRManager.prototype.updateAnchors = function(frame, session) { + if (!session || !session.isAR || !frame.trackedAnchors || Module.AnchorsArrayOffset === undefined) { + return; + } + + for (var anchorId in this.webXRAnchors) { + var inactiveEntry = this.webXRAnchors[anchorId]; + var inactiveData = this.xrData.anchors[inactiveEntry.slot]; + Module.HEAPF32[inactiveData.frameIndex] = this.xrData.frameNumber; + Module.HEAPF32[inactiveData.idIndex] = inactiveEntry.id; + Module.HEAPF32[inactiveData.trackedIndex] = 0; + } + + var baseSpace = session.localRefSpace || session.refSpace; + for (var anchor of frame.trackedAnchors) { + var id = this.webXRAnchorIds.get(anchor); + if (!id) { + continue; + } + + var entry = this.webXRAnchors[id]; + if (!entry) { + continue; + } + + var pose = frame.getPose(anchor.anchorSpace, baseSpace); + if (!pose) { + continue; + } + + var data = this.xrData.anchors[entry.slot]; + Module.HEAPF32[data.frameIndex] = this.xrData.frameNumber; + Module.HEAPF32[data.idIndex] = entry.id; + Module.HEAPF32[data.trackedIndex] = 1; + Module.HEAPF32[data.positionIndices[0]] = pose.transform.position.x; + Module.HEAPF32[data.positionIndices[1]] = pose.transform.position.y; + Module.HEAPF32[data.positionIndices[2]] = -pose.transform.position.z; + Module.HEAPF32[data.rotationIndices[0]] = -pose.transform.orientation.x; + Module.HEAPF32[data.rotationIndices[1]] = -pose.transform.orientation.y; + Module.HEAPF32[data.rotationIndices[2]] = pose.transform.orientation.z; + Module.HEAPF32[data.rotationIndices[3]] = pose.transform.orientation.w; + } + } XRManager.prototype.animate = function (frame) { var session = frame.session; @@ -1112,9 +1364,11 @@ void main() this.getXRControllersData(frame, session.inputSources, session.refSpace, xrData); if (session.isAR && this.viewerHitTestSource) { + this.lastViewerHitTestResult = null; Module.HEAPF32[xrData.viewerHitTestPose.frameIndex] = xrData.frameNumber; // XRHitPoseData.frame var viewerHitTestResults = frame.getHitTestResults(this.viewerHitTestSource); if (viewerHitTestResults.length > 0) { + this.lastViewerHitTestResult = viewerHitTestResults[0]; var hitTestPose = viewerHitTestResults[0].getPose(session.localRefSpace); Module.HEAPF32[xrData.viewerHitTestPose.availableIndex] = 1; // XRHitPoseData.available Module.HEAPF32[xrData.viewerHitTestPose.positionIndices[0]] = hitTestPose.transform.position.x; // XRHitPoseData.position[0] @@ -1129,6 +1383,9 @@ void main() Module.HEAPF32[xrData.viewerHitTestPose.availableIndex] = 0; // XRHitPoseData.available } } + + this.processAnchorRequests(frame, session); + this.updateAnchors(frame, session); if (xrData.controllerA.updatedProfiles == 1 || xrData.controllerB.updatedProfiles == 1) { diff --git a/Packages/webxr/Runtime/Scripts/WebXRAnchorManager.cs b/Packages/webxr/Runtime/Scripts/WebXRAnchorManager.cs new file mode 100644 index 00000000..c75e0e2f --- /dev/null +++ b/Packages/webxr/Runtime/Scripts/WebXRAnchorManager.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using UnityEngine; +using WebXR; + +public class WebXRAnchorExample : MonoBehaviour +{ + [SerializeField] + private Transform anchoredPrefab; + + private readonly Dictionary instances = new Dictionary(); + + private void OnEnable() + { + WebXRManager.OnAnchorUpdate += OnAnchorUpdate; + WebXRManager.OnAnchorDeleted += OnAnchorDeleted; + } + + private void OnDisable() + { + WebXRManager.OnAnchorUpdate -= OnAnchorUpdate; + WebXRManager.OnAnchorDeleted -= OnAnchorDeleted; + } + + public void StartHitTest() + { + WebXRManager.Instance.StartViewerHitTest(); + } + + public void PlaceAnchorAtCurrentHitTest() + { + WebXRManager.Instance.CreateAnchorFromViewerHitTest(); + } + + public void PlaceAnchorAtTransform(Transform source) + { + WebXRManager.Instance.CreateAnchorFromPose(source.position, source.rotation); + } + + private void OnAnchorUpdate(WebXRAnchorData anchor) + { + if (!anchor.tracked) + return; + + if (!instances.TryGetValue(anchor.id, out Transform instance)) + { + instance = Instantiate(anchoredPrefab); + instances.Add(anchor.id, instance); + } + + instance.SetPositionAndRotation(anchor.position, anchor.rotation); + } + + private void OnAnchorDeleted(int anchorId) + { + if (instances.TryGetValue(anchorId, out Transform instance)) + { + Destroy(instance.gameObject); + instances.Remove(anchorId); + } + } +} \ No newline at end of file diff --git a/Packages/webxr/Runtime/Scripts/WebXRControllerData.cs b/Packages/webxr/Runtime/Scripts/WebXRControllerData.cs index d3b87bb4..d7e46541 100644 --- a/Packages/webxr/Runtime/Scripts/WebXRControllerData.cs +++ b/Packages/webxr/Runtime/Scripts/WebXRControllerData.cs @@ -97,6 +97,16 @@ public class WebXRHitPoseData public Quaternion rotation; } + [System.Serializable] + public class WebXRAnchorData + { + public int frame; + public int id; + public bool tracked; + public Vector3 position; + public Quaternion rotation; + } + public enum WebXRControllerHand { NONE = 0, diff --git a/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs new file mode 100644 index 00000000..5cef0590 --- /dev/null +++ b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs @@ -0,0 +1,40 @@ +public partial class WebXRManager +{ + public static event WebXRSubsystem.AnchorCreated OnAnchorCreated + { + add => WebXRSubsystem.OnAnchorCreated += value; + remove => WebXRSubsystem.OnAnchorCreated -= value; + } + + public static event WebXRSubsystem.AnchorDeleted OnAnchorDeleted + { + add => WebXRSubsystem.OnAnchorDeleted += value; + remove => WebXRSubsystem.OnAnchorDeleted -= value; + } + + public static event WebXRSubsystem.AnchorUpdate OnAnchorUpdate + { + add => WebXRSubsystem.OnAnchorUpdate += value; + remove => WebXRSubsystem.OnAnchorUpdate -= value; + } + + public void CreateAnchorFromViewerHitTest() + { + subsystem?.CreateAnchorFromViewerHitTest(); + } + + public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) + { + subsystem?.CreateAnchorFromPose(position, rotation); + } + + public void DeleteAnchor(int anchorId) + { + subsystem?.DeleteAnchor(anchorId); + } + + public void DeleteAllAnchors() + { + subsystem?.DeleteAllAnchors(); + } +} \ No newline at end of file diff --git a/Packages/webxr/Runtime/Scripts/WebXRManager.cs b/Packages/webxr/Runtime/Scripts/WebXRManager.cs index a7d19a57..684b16f5 100644 --- a/Packages/webxr/Runtime/Scripts/WebXRManager.cs +++ b/Packages/webxr/Runtime/Scripts/WebXRManager.cs @@ -12,9 +12,9 @@ public enum WebXRVisibilityState [DefaultExecutionOrder(-2020)] #if UNITY_XR_MANAGEMENT_4_3_1_OR_NEWER - public class WebXRManager : SubsystemLifecycleManager + public partial class WebXRManager : SubsystemLifecycleManager #else - public class WebXRManager : SubsystemLifecycleManager + public partial class WebXRManager : SubsystemLifecycleManager #endif { private static readonly Rect defaultRect = new Rect(0, 0, 1, 1); diff --git a/Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs b/Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs index 3108dc68..83e57dbb 100644 --- a/Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs +++ b/Packages/webxr/Runtime/XRPlugin/WebXRSettings.cs @@ -21,7 +21,8 @@ public enum ReferenceSpaceTypes public enum ExtraFeatureTypes { hit_test = 1, - hand_tracking = 2 + hand_tracking = 2, + anchors = 4 } [Header("VR Settings")] diff --git a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs index fefc6189..f4344e0d 100644 --- a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs +++ b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs @@ -213,6 +213,10 @@ internal void OnUpdate() OnViewerHitTestUpdate?.Invoke(viewerHitTestPose); } } + if (OnAnchorUpdate != null && this.xrState == WebXRState.AR) + { + UpdateAnchors(); + } } private void UpdateXRCameras() @@ -228,6 +232,63 @@ private void UpdateXRCameras() } } + private void UpdateAnchors() + { + for (int i = 0; i < MaxWebXRAnchors; i++) + { + if (anchors[i] == null) + { + anchors[i] = new WebXRAnchorData { frame = -1, id = -1 }; + } + + if (GetAnchorFromAnchorsArray(i, anchors[i])) + { + OnAnchorUpdate?.Invoke(anchors[i]); + } + } + } + + private bool GetAnchorFromAnchorsArray(int anchorIndex, WebXRAnchorData anchorData) + { + int arrayAnchorDataStartPosition = anchorIndex * WebXRAnchorArrayStride; + + int frameNumber = (int)anchorsDataArray[arrayAnchorDataStartPosition++]; + if (anchorData.frame == frameNumber) + { + return false; + } + + int anchorId = (int)anchorsDataArray[arrayAnchorDataStartPosition++]; + bool tracked = anchorsDataArray[arrayAnchorDataStartPosition++] != 0; + + anchorData.frame = frameNumber; + anchorData.id = anchorId; + anchorData.tracked = tracked; + + if (anchorId < 0) + { + return false; + } + + if (!tracked) + { + return true; + } + + anchorData.position = new Vector3( + anchorsDataArray[arrayAnchorDataStartPosition++], + anchorsDataArray[arrayAnchorDataStartPosition++], + anchorsDataArray[arrayAnchorDataStartPosition++]); + + anchorData.rotation = new Quaternion( + anchorsDataArray[arrayAnchorDataStartPosition++], + anchorsDataArray[arrayAnchorDataStartPosition++], + anchorsDataArray[arrayAnchorDataStartPosition++], + anchorsDataArray[arrayAnchorDataStartPosition++]); + + return true; + } + private float CheckViewsDistance() { if (viewsCount == 1) @@ -253,6 +314,8 @@ private void InternalStart() Native.InitControllersArray(controllersArray); Native.InitHandsArray(handsArray); Native.InitViewerHitTestPoseArray(viewerHitTestPoseArray); + Native.SetWebXRAnchorEvents(OnAnchorCreatedInternal, OnAnchorDeletedInternal); + Native.InitAnchorsArray(anchorsDataArray); Native.InitXRSharedArray(sharedArray); #endif } @@ -294,6 +357,28 @@ public static extern void SetWebXREvents(StartXREvent on_start_ar, EndXREvent on_end_xr, XRCapabilitiesEvent on_xr_capabilities, InputProfilesEvent on_input_profiles); + + [DllImport("__Internal")] + public static extern void InitAnchorsArray(float[] array); + + [DllImport("__Internal")] + public static extern void SetWebXRAnchorEvents( + AnchorCreatedEvent on_anchor_created, + AnchorDeletedEvent on_anchor_deleted); + + [DllImport("__Internal")] + public static extern void CreateAnchorFromViewerHitTest(); + + [DllImport("__Internal")] + public static extern void CreateAnchorFromPose( + float px, float py, float pz, + float qx, float qy, float qz, float qw); + + [DllImport("__Internal")] + public static extern void DeleteAnchor(int anchorId); + + [DllImport("__Internal")] + public static extern void DeleteAllAnchors(); } #endif @@ -340,6 +425,18 @@ public delegate void HeadsetUpdate( internal static event HitTestUpdate OnViewerHitTestUpdate; + public delegate void AnchorCreated(int anchorId); + internal static event AnchorCreated OnAnchorCreated; + + public delegate void AnchorDeleted(int anchorId); + internal static event AnchorDeleted OnAnchorDeleted; + + public delegate void AnchorUpdate(WebXRAnchorData anchorData); + internal static event AnchorUpdate OnAnchorUpdate; + + internal delegate void AnchorCreatedEvent(int anchorId); + internal delegate void AnchorDeletedEvent(int anchorId); + internal delegate void StartXREvent(int viewsCount, float left_x, float left_y, float left_w, float left_h, float right_x, float right_y, float right_w, float right_h); @@ -363,6 +460,14 @@ internal delegate void StartXREvent(int viewsCount, // 2 XRViewports, views count, is transparent, framebuffer width height, stored linearly. float[] sharedArray = new float[(2 * 16) + (2 * 7) + (2 * 4) + 1 + 1 + 2]; + private const int MaxWebXRAnchors = 32; + + private const int WebXRAnchorArrayStride = 10; + + float[] anchorsDataArray = new float[MaxWebXRAnchors * WebXRAnchorArrayStride]; + + private readonly WebXRAnchorData[] anchors = new WebXRAnchorData[MaxWebXRAnchors]; + // Shared array for controllers data float[] controllersArray = new float[2 * 34]; @@ -472,6 +577,18 @@ public static void OnEndXR() Instance.setXrState(WebXRState.NORMAL, 1, new Rect(), new Rect()); } + [MonoPInvokeCallback(typeof(AnchorCreatedEvent))] + public static void OnAnchorCreatedInternal(int anchorId) + { + OnAnchorCreated?.Invoke(anchorId); + } + + [MonoPInvokeCallback(typeof(AnchorDeletedEvent))] + public static void OnAnchorDeletedInternal(int anchorId) + { + OnAnchorDeleted?.Invoke(anchorId); + } + public void ToggleAR() { #if UNITY_WEBGL @@ -652,5 +769,41 @@ bool GetHitTestPoseFromViewerHitTestPoseArray(ref WebXRHitPoseData hitPoseData) viewerHitTestPoseArray[arrayPosition++], viewerHitTestPoseArray[arrayPosition++]); return true; } + + public void CreateAnchorFromViewerHitTest() + { + #if UNITY_WEBGL + if (xrState == WebXRState.AR) + { + Native.CreateAnchorFromViewerHitTest(); + } + #endif + } + + public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) + { + #if UNITY_WEBGL + if (xrState == WebXRState.AR) + { + Native.CreateAnchorFromPose( + position.x, position.y, position.z, + rotation.x, rotation.y, rotation.z, rotation.w); + } + #endif + } + + public void DeleteAnchor(int anchorId) + { + #if UNITY_WEBGL + Native.DeleteAnchor(anchorId); + #endif + } + + public void DeleteAllAnchors() + { + #if UNITY_WEBGL + Native.DeleteAllAnchors(); + #endif + } } } From 55f123e051e3a971119885a1d3f6547c753467ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Papenfu=C3=9F?= Date: Tue, 28 Apr 2026 13:37:30 +0200 Subject: [PATCH 2/5] webxr anchor refactor --- Packages/webxr/CHANGELOG.md | 4 + .../webxr/Runtime/Plugins/WebGL/webxr.jspre | 8 +- .../Runtime/Scripts/WebXRManager.Anchors.cs | 75 ++++++++++--------- .../Anchors/WebXRAnchorExample.cs} | 0 4 files changed, 50 insertions(+), 37 deletions(-) rename Packages/webxr/{Runtime/Scripts/WebXRAnchorManager.cs => Samples~/Anchors/WebXRAnchorExample.cs} (100%) diff --git a/Packages/webxr/CHANGELOG.md b/Packages/webxr/CHANGELOG.md index fedc848c..fb0bb703 100644 --- a/Packages/webxr/CHANGELOG.md +++ b/Packages/webxr/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Hands tracking update. +### Added +- WebXR Anchors: Session anchor support for immersive AR sessions, including creating anchors from viewer hit-test results or Unity poses and receiving per-frame anchor pose updates. +- WebXR Anchors: To use anchors, enable the `anchors` optional feature in WebXR AR settings and see samples for usage. + ## [0.22.1] - 2024-11-09 ### Added - Support for WebAssembly Table, to support newer Unity versions features. diff --git a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre index 50ba4859..d19bc8be 100644 --- a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre +++ b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre @@ -1175,7 +1175,11 @@ void main() } if (entry.anchor && entry.anchor.delete) { - entry.anchor.delete(); + try { + entry.anchor.delete(); + } catch (error) { + console.warn('Could not delete WebXR anchor:', error); + } } this.webXRAnchorIds.delete(entry.anchor); @@ -1297,6 +1301,7 @@ void main() } XRManager.prototype.animate = function (frame) { + this.lastViewerHitTestResult = null; var session = frame.session; if (!session) { return this.didNotifyUnity; @@ -1364,7 +1369,6 @@ void main() this.getXRControllersData(frame, session.inputSources, session.refSpace, xrData); if (session.isAR && this.viewerHitTestSource) { - this.lastViewerHitTestResult = null; Module.HEAPF32[xrData.viewerHitTestPose.frameIndex] = xrData.frameNumber; // XRHitPoseData.frame var viewerHitTestResults = frame.getHitTestResults(this.viewerHitTestSource); if (viewerHitTestResults.length > 0) { diff --git a/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs index 5cef0590..67bff2a1 100644 --- a/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs +++ b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs @@ -1,40 +1,45 @@ -public partial class WebXRManager +using UnityEngine; + +namespace WebXR { - public static event WebXRSubsystem.AnchorCreated OnAnchorCreated - { - add => WebXRSubsystem.OnAnchorCreated += value; - remove => WebXRSubsystem.OnAnchorCreated -= value; - } - - public static event WebXRSubsystem.AnchorDeleted OnAnchorDeleted - { - add => WebXRSubsystem.OnAnchorDeleted += value; - remove => WebXRSubsystem.OnAnchorDeleted -= value; - } - - public static event WebXRSubsystem.AnchorUpdate OnAnchorUpdate + public partial class WebXRManager { - add => WebXRSubsystem.OnAnchorUpdate += value; - remove => WebXRSubsystem.OnAnchorUpdate -= value; - } + public static event WebXRSubsystem.AnchorCreated OnAnchorCreated + { + add => WebXRSubsystem.OnAnchorCreated += value; + remove => WebXRSubsystem.OnAnchorCreated -= value; + } - public void CreateAnchorFromViewerHitTest() - { - subsystem?.CreateAnchorFromViewerHitTest(); - } - - public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) - { - subsystem?.CreateAnchorFromPose(position, rotation); - } - - public void DeleteAnchor(int anchorId) - { - subsystem?.DeleteAnchor(anchorId); - } - - public void DeleteAllAnchors() - { - subsystem?.DeleteAllAnchors(); + public static event WebXRSubsystem.AnchorDeleted OnAnchorDeleted + { + add => WebXRSubsystem.OnAnchorDeleted += value; + remove => WebXRSubsystem.OnAnchorDeleted -= value; + } + + public static event WebXRSubsystem.AnchorUpdate OnAnchorUpdate + { + add => WebXRSubsystem.OnAnchorUpdate += value; + remove => WebXRSubsystem.OnAnchorUpdate -= value; + } + + public void CreateAnchorFromViewerHitTest() + { + subsystem?.CreateAnchorFromViewerHitTest(); + } + + public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) + { + subsystem?.CreateAnchorFromPose(position, rotation); + } + + public void DeleteAnchor(int anchorId) + { + subsystem?.DeleteAnchor(anchorId); + } + + public void DeleteAllAnchors() + { + subsystem?.DeleteAllAnchors(); + } } } \ No newline at end of file diff --git a/Packages/webxr/Runtime/Scripts/WebXRAnchorManager.cs b/Packages/webxr/Samples~/Anchors/WebXRAnchorExample.cs similarity index 100% rename from Packages/webxr/Runtime/Scripts/WebXRAnchorManager.cs rename to Packages/webxr/Samples~/Anchors/WebXRAnchorExample.cs From 94db827336bf7a169cc0f72c938453d82e94c818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Papenfu=C3=9F?= Date: Tue, 28 Apr 2026 16:28:20 +0200 Subject: [PATCH 3/5] webxr added method to wait for hit test --- .../webxr/Runtime/Plugins/WebGL/webxr.jslib | 4 + .../webxr/Runtime/Plugins/WebGL/webxr.jspre | 29 +- .../Runtime/Scripts/WebXRManager.Anchors.cs | 5 + .../Scripts/WebXRManager.Anchors.cs.meta | 2 + .../webxr/Runtime/XRPlugin/WebXRSubsystem.cs | 13 + .../Samples~/Anchors/Prefabs/Anchor.prefab | 491 ++++++++++++++++++ .../Anchors/Prefabs/Anchor.prefab.meta | 7 + .../webxr/Samples~/Anchors/Prefabs/Blue.mat | 137 +++++ .../Samples~/Anchors/Prefabs/Blue.mat.meta | 8 + .../webxr/Samples~/Anchors/Prefabs/Green.mat | 137 +++++ .../Samples~/Anchors/Prefabs/Green.mat.meta | 8 + .../webxr/Samples~/Anchors/Prefabs/Red.mat | 137 +++++ .../Samples~/Anchors/Prefabs/Red.mat.meta | 8 + .../Samples~/Anchors/Prefabs/Transparent.mat | 141 +++++ .../Anchors/Prefabs/Transparent.mat.meta | 8 + .../Anchors/Prefabs/WebXRAnchor.prefab | 48 ++ .../Anchors/Prefabs/WebXRAnchor.prefab.meta | 7 + Packages/webxr/Samples~/Anchors/Scripts.meta | 8 + .../{ => Scripts}/WebXRAnchorExample.cs | 17 + .../Scripts/WebXRAnchorExample.cs.meta | 2 + .../Samples~/Anchors/WebXRAnchorSample.asmdef | 18 + .../Anchors/WebXRAnchorSample.asmdef.meta | 7 + Packages/webxr/package.json | 7 + 23 files changed, 1248 insertions(+), 1 deletion(-) create mode 100644 Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs.meta create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab.meta create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat.meta create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Green.mat create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Green.mat.meta create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Red.mat create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Red.mat.meta create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat.meta create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab create mode 100644 Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab.meta create mode 100644 Packages/webxr/Samples~/Anchors/Scripts.meta rename Packages/webxr/Samples~/Anchors/{ => Scripts}/WebXRAnchorExample.cs (56%) create mode 100644 Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs.meta create mode 100644 Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef create mode 100644 Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef.meta diff --git a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib index 8d1d909f..d7404f41 100644 --- a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib +++ b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jslib @@ -42,6 +42,10 @@ var LibraryWebXR = { Module.WebXR.createAnchorFromViewerHitTest(); }, + CreateAnchorFromWaitingForViewerHitTest: function() { + Module.WebXR.createAnchorFromWaitingForViewerHitTest(); + }, + CreateAnchorFromPose: function(px, py, pz, qx, qy, qz, qw) { Module.WebXR.createAnchorFromPose(px, py, pz, qx, qy, qz, qw); }, diff --git a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre index d19bc8be..358646f5 100644 --- a/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre +++ b/Packages/webxr/Runtime/Plugins/WebGL/webxr.jspre @@ -426,6 +426,7 @@ void main() Module.WebXR.toggleHitTest = onToggleHitTest; Module.WebXR.callHapticPulse = onCallHapticPulse; Module.WebXR.createAnchorFromViewerHitTest = this.createAnchorFromViewerHitTest.bind(this); + Module.WebXR.createAnchorFromWaitingForViewerHitTest = this.createAnchorFromWaitingForViewerHitTest.bind(this); Module.WebXR.createAnchorFromPose = this.createAnchorFromPose.bind(this); Module.WebXR.deleteAnchor = this.deleteAnchor.bind(this); Module.WebXR.deleteAllAnchors = this.deleteAllAnchors.bind(this); @@ -1160,6 +1161,10 @@ void main() this.pendingAnchorRequests.push({ type: 'viewer-hit-test' }); } + XRManager.prototype.createAnchorFromWaitingForViewerHitTest = function() { + this.pendingAnchorRequests.push({ type: 'wait-viewer-hit-test' }); + } + XRManager.prototype.createAnchorFromPose = function(px, py, pz, qx, qy, qz, qw) { this.pendingAnchorRequests.push({ type: 'pose', @@ -1217,10 +1222,32 @@ void main() var thisXRMananger = this; if (request.type == 'viewer-hit-test') { - if (!this.lastViewerHitTestResult || !this.lastViewerHitTestResult.createAnchor) { + if (!this.lastViewerHitTestResult) { console.warn('Cannot create WebXR anchor: no viewer hit-test result available.'); continue; } + + if (!this.lastViewerHitTestResult.createAnchor) { + console.warn('Cannot create WebXR anchor: XRHitTestResult.createAnchor is unavailable.'); + continue; + } + + this.lastViewerHitTestResult.createAnchor().then(function(anchor) { + thisXRMananger.registerAnchor(anchor); + }).catch(function(error) { + console.warn('Could not create WebXR anchor from hit-test result:', error); + }); + } else if (request.type == 'wait-viewer-hit-test') { + if (!this.lastViewerHitTestResult) { + console.info('Cannot create WebXR anchor now will try again.'); + this.pendingAnchorRequests.push(request); + continue; + } + + if (!this.lastViewerHitTestResult.createAnchor) { + console.warn('Cannot create WebXR anchor: XRHitTestResult.createAnchor is unavailable.'); + continue; + } this.lastViewerHitTestResult.createAnchor().then(function(anchor) { thisXRMananger.registerAnchor(anchor); diff --git a/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs index 67bff2a1..f2f1e60f 100644 --- a/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs +++ b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs @@ -27,6 +27,11 @@ public void CreateAnchorFromViewerHitTest() subsystem?.CreateAnchorFromViewerHitTest(); } + public void CreateAnchorFromWaitingForViewerHitTest() + { + subsystem?.CreateAnchorFromWaitingForViewerHitTest(); + } + public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) { subsystem?.CreateAnchorFromPose(position, rotation); diff --git a/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs.meta b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs.meta new file mode 100644 index 00000000..a3880efb --- /dev/null +++ b/Packages/webxr/Runtime/Scripts/WebXRManager.Anchors.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2229e54e1166e445d955314701f4f554 \ No newline at end of file diff --git a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs index f4344e0d..69f55bf7 100644 --- a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs +++ b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs @@ -369,6 +369,9 @@ public static extern void SetWebXRAnchorEvents( [DllImport("__Internal")] public static extern void CreateAnchorFromViewerHitTest(); + [DllImport("__Internal")] + public static extern void CreateAnchorFromWaitingForViewerHitTest(); + [DllImport("__Internal")] public static extern void CreateAnchorFromPose( float px, float py, float pz, @@ -780,6 +783,16 @@ public void CreateAnchorFromViewerHitTest() #endif } + public void CreateAnchorFromWaitingForViewerHitTest() + { + #if UNITY_WEBGL + if (xrState == WebXRState.AR) + { + Native.CreateAnchorFromWaitingForViewerHitTest(); + } + #endif + } + public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) { #if UNITY_WEBGL diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab b/Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab new file mode 100644 index 00000000..71932311 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab @@ -0,0 +1,491 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &186481346483102803 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8971202239850332909} + - component: {fileID: 7802920332531909973} + - component: {fileID: 1992713582359565122} + - component: {fileID: 6890298623029420764} + m_Layer: 0 + m_Name: AxisZ + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8971202239850332909 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 186481346483102803} + serializedVersion: 2 + m_LocalRotation: {x: 0.70710707, y: -0, z: -0, w: 0.7071066} + m_LocalPosition: {x: 0, y: 0, z: 0.75} + m_LocalScale: {x: 0.05, y: 0.75, z: 0.05} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4196391294288101103} + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!33 &7802920332531909973 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 186481346483102803} + m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1992713582359565122 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 186481346483102803} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 07bc01a61745c4456847de19a0a08da1, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_MaskInteraction: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!136 &6890298623029420764 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 186481346483102803} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5000001 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697} +--- !u!1 &2678738213269342726 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4196391294288101103} + - component: {fileID: 5884933614345139448} + - component: {fileID: 1671750000665188811} + - component: {fileID: 2636517920994574474} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4196391294288101103 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2678738213269342726} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.05, y: 0.05, z: 0.05} + m_ConstrainProportionsScale: 1 + m_Children: + - {fileID: 8971202239850332909} + - {fileID: 4120173779939497730} + - {fileID: 3396652751079388407} + m_Father: {fileID: 3345775371870950389} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5884933614345139448 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2678738213269342726} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1671750000665188811 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2678738213269342726} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 9e4f8d2e35ef5472a83b6077130cb51e, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_MaskInteraction: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!65 &2636517920994574474 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2678738213269342726} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &7346354743300970381 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3396652751079388407} + - component: {fileID: 3677326710678424955} + - component: {fileID: 5526121050161511356} + - component: {fileID: 8376316236685843477} + m_Layer: 0 + m_Name: AxisY + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3396652751079388407 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7346354743300970381} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.75, z: 0} + m_LocalScale: {x: 0.05, y: 0.75, z: 0.05} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4196391294288101103} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3677326710678424955 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7346354743300970381} + m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5526121050161511356 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7346354743300970381} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 83216b7817d6b4f5db75c534988ccb26, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_MaskInteraction: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!136 &8376316236685843477 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7346354743300970381} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5000001 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697} +--- !u!1 &7418316785324538391 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3345775371870950389} + m_Layer: 0 + m_Name: Anchor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3345775371870950389 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7418316785324538391} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4196391294288101103} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &8744828765488003652 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4120173779939497730} + - component: {fileID: 120220627702371309} + - component: {fileID: 8630198480999416374} + - component: {fileID: 7495224800854054248} + m_Layer: 0 + m_Name: AxisX + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4120173779939497730 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8744828765488003652} + serializedVersion: 2 + m_LocalRotation: {x: 0.5, y: 0.5, z: -0.5, w: 0.5} + m_LocalPosition: {x: 0.75, y: 0, z: 0} + m_LocalScale: {x: 0.05, y: 0.75, z: 0.05} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4196391294288101103} + m_LocalEulerAnglesHint: {x: 90, y: 90, z: 0} +--- !u!33 &120220627702371309 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8744828765488003652} + m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8630198480999416374 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8744828765488003652} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: cd5ab9304d6ab4f858d818b0cc3feffd, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_MaskInteraction: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!136 &7495224800854054248 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8744828765488003652} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5000001 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697} diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab.meta b/Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab.meta new file mode 100644 index 00000000..cbff7250 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Anchor.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b5a85fd60a6b14f28b4cb5f7adfa86b6 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat b/Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat new file mode 100644 index 00000000..80a8f75e --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat @@ -0,0 +1,137 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-1536827106776506223 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion + version: 10 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Blue + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _XRMotionVectorsPass: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0, g: 0, b: 1, a: 1} + - _Color: {r: 0, g: 0, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat.meta b/Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat.meta new file mode 100644 index 00000000..cf846321 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Blue.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 07bc01a61745c4456847de19a0a08da1 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Green.mat b/Packages/webxr/Samples~/Anchors/Prefabs/Green.mat new file mode 100644 index 00000000..fedd4d9d --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Green.mat @@ -0,0 +1,137 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-1536827106776506223 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion + version: 10 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Green + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _XRMotionVectorsPass: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0, g: 1, b: 0, a: 1} + - _Color: {r: 0, g: 1, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Green.mat.meta b/Packages/webxr/Samples~/Anchors/Prefabs/Green.mat.meta new file mode 100644 index 00000000..69e84cb3 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Green.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 83216b7817d6b4f5db75c534988ccb26 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Red.mat b/Packages/webxr/Samples~/Anchors/Prefabs/Red.mat new file mode 100644 index 00000000..e6d43ec0 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Red.mat @@ -0,0 +1,137 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-1536827106776506223 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion + version: 10 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Red + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _WorkflowMode: 1 + - _XRMotionVectorsPass: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 1, g: 0, b: 0, a: 1} + - _Color: {r: 1, g: 0, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Red.mat.meta b/Packages/webxr/Samples~/Anchors/Prefabs/Red.mat.meta new file mode 100644 index 00000000..a3164fcd --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Red.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cd5ab9304d6ab4f858d818b0cc3feffd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat b/Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat new file mode 100644 index 00000000..0c73d220 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat @@ -0,0 +1,141 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Transparent + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _ALPHAPREMULTIPLY_ON + - _SURFACE_TYPE_TRANSPARENT + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - MOTIONVECTORS + - DepthOnly + - SHADOWCASTER + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _DstBlendAlpha: 10 + - _EnvironmentReflections: 1 + - _GlossMapScale: 0 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.005 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 1 + - _WorkflowMode: 1 + - _XRMotionVectorsPass: 1 + - _ZWrite: 0 + m_Colors: + - _BaseColor: {r: 1, g: 1, b: 1, a: 0.5019608} + - _Color: {r: 1, g: 1, b: 1, a: 0.5019608} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.2, g: 0.2, b: 0.2, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &7883437871751797079 +MonoBehaviour: + m_ObjectHideFlags: 11 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion + version: 10 diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat.meta b/Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat.meta new file mode 100644 index 00000000..55ca9f23 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/Transparent.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9e4f8d2e35ef5472a83b6077130cb51e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab b/Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab new file mode 100644 index 00000000..e79cb59c --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab @@ -0,0 +1,48 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7080766884625336350 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1237885867817271649} + - component: {fileID: 2520011617434627279} + m_Layer: 0 + m_Name: WebXRAnchor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1237885867817271649 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7080766884625336350} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 270, y: 480, z: 0.025} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2520011617434627279 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7080766884625336350} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6185a19b483ff4f42a1ad0172c19eb0b, type: 3} + m_Name: + m_EditorClassIdentifier: WebXRAnchorSample::WebXRAnchorExample + anchoredPrefab: {fileID: 3345775371870950389, guid: b5a85fd60a6b14f28b4cb5f7adfa86b6, + type: 3} diff --git a/Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab.meta b/Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab.meta new file mode 100644 index 00000000..29b06943 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Prefabs/WebXRAnchor.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7ca39775fb9b5429897891dd3a1b2274 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/Samples~/Anchors/Scripts.meta b/Packages/webxr/Samples~/Anchors/Scripts.meta new file mode 100644 index 00000000..38986fa0 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 01c8a24bdbc0f46e989bc8621a7fa9cf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/Samples~/Anchors/WebXRAnchorExample.cs b/Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs similarity index 56% rename from Packages/webxr/Samples~/Anchors/WebXRAnchorExample.cs rename to Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs index c75e0e2f..1d8eb5bd 100644 --- a/Packages/webxr/Samples~/Anchors/WebXRAnchorExample.cs +++ b/Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs @@ -26,11 +26,28 @@ public void StartHitTest() WebXRManager.Instance.StartViewerHitTest(); } + /// + /// Creates an anchor at the current hit test result. The anchor will be created at the position and rotation of the current hit test result, which is updated every frame when a hit test result is available. If no hit test result is available, no anchor will be created.
+ /// This method does not work reliably because of timing.. better use + ///
public void PlaceAnchorAtCurrentHitTest() { WebXRManager.Instance.CreateAnchorFromViewerHitTest(); } + /// + /// Creates an anchor at the next hit test result. The anchor will be created at the position and rotation of the next hit test result, which is updated every frame when a hit test result is available. + /// + public void PlaceAnchorAtNextHitTest() + { + WebXRManager.Instance.CreateAnchorFromWaitingForViewerHitTest(); + } + + /// + /// Creates an anchor at the position and rotation of the given transform. The anchor will be created at the position and rotation of the given transform.
+ /// Anchors are more reliable when created from hit test results, so prefer the other methods if possible. + ///
+ /// Any Transform (this method will just read its position and rotation) public void PlaceAnchorAtTransform(Transform source) { WebXRManager.Instance.CreateAnchorFromPose(source.position, source.rotation); diff --git a/Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs.meta b/Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs.meta new file mode 100644 index 00000000..96c6dde3 --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/Scripts/WebXRAnchorExample.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6185a19b483ff4f42a1ad0172c19eb0b \ No newline at end of file diff --git a/Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef b/Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef new file mode 100644 index 00000000..c6e588ce --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef @@ -0,0 +1,18 @@ +{ + "name": "WebXRAnchorSample", + "rootNamespace": "", + "references": [ + "GUID:fd4abe4ffe74ef1448afe15c6cb36bb7" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [ + "" + ], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef.meta b/Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef.meta new file mode 100644 index 00000000..aa8ff63e --- /dev/null +++ b/Packages/webxr/Samples~/Anchors/WebXRAnchorSample.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 11ffbc57b92314805bf3dffb5d12b852 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/webxr/package.json b/Packages/webxr/package.json index 91613da7..42a177c8 100644 --- a/Packages/webxr/package.json +++ b/Packages/webxr/package.json @@ -17,6 +17,13 @@ "name": "De-Panther", "url": "https://github.com/De-Panther/unity-webxr-export" }, + "samples": [ + { + "displayName": "XR Anchors Sample", + "description": "Sample Class to work with XR Anchors in WebXR", + "path": "Samples~/Anchors" + } + ], "dependencies": { "com.unity.xr.management": "3.2.13" } From 0e3f4e1605c8e5ab370d110641ca098bbb84353b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Papenfu=C3=9F?= Date: Thu, 30 Apr 2026 06:05:48 +0200 Subject: [PATCH 4/5] feedback from coderabit: check for feature before using anchors --- .../webxr/Runtime/XRPlugin/WebXRSubsystem.cs | 86 ++++++++++++++----- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs index 69f55bf7..b10fca95 100644 --- a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs +++ b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs @@ -234,13 +234,13 @@ private void UpdateXRCameras() private void UpdateAnchors() { + WebXRSettings settings = WebXRSettings.GetSettings(); + if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + { + return; + } for (int i = 0; i < MaxWebXRAnchors; i++) { - if (anchors[i] == null) - { - anchors[i] = new WebXRAnchorData { frame = -1, id = -1 }; - } - if (GetAnchorFromAnchorsArray(i, anchors[i])) { OnAnchorUpdate?.Invoke(anchors[i]); @@ -309,6 +309,10 @@ private float CheckViewsDistance() private void InternalStart() { + for (int i = 0; i < MaxWebXRAnchors; i++) + { + anchors[i] = new WebXRAnchorData { frame = -1, id = -1 }; + } #if UNITY_WEBGL Native.SetWebXREvents(OnStartAR, OnStartVR, UpdateVisibilityState, OnEndXR, OnXRCapabilities, OnInputProfiles); Native.InitControllersArray(controllersArray); @@ -775,48 +779,84 @@ bool GetHitTestPoseFromViewerHitTestPoseArray(ref WebXRHitPoseData hitPoseData) public void CreateAnchorFromViewerHitTest() { - #if UNITY_WEBGL - if (xrState == WebXRState.AR) +#if UNITY_WEBGL + if (xrState != WebXRState.AR) + { + return; + } + WebXRSettings settings = WebXRSettings.GetSettings(); + if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) { - Native.CreateAnchorFromViewerHitTest(); + return; } - #endif + Native.CreateAnchorFromViewerHitTest(); +#endif } public void CreateAnchorFromWaitingForViewerHitTest() { - #if UNITY_WEBGL - if (xrState == WebXRState.AR) +#if UNITY_WEBGL + if (xrState != WebXRState.AR) { - Native.CreateAnchorFromWaitingForViewerHitTest(); + return; } - #endif + WebXRSettings settings = WebXRSettings.GetSettings(); + if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + { + return; + } + Native.CreateAnchorFromWaitingForViewerHitTest(); +#endif } public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) { - #if UNITY_WEBGL - if (xrState == WebXRState.AR) +#if UNITY_WEBGL + if (xrState != WebXRState.AR) { - Native.CreateAnchorFromPose( - position.x, position.y, position.z, - rotation.x, rotation.y, rotation.z, rotation.w); + return; + } + WebXRSettings settings = WebXRSettings.GetSettings(); + if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + { + return; } - #endif + Native.CreateAnchorFromPose( + position.x, position.y, position.z, + rotation.x, rotation.y, rotation.z, rotation.w); +#endif } public void DeleteAnchor(int anchorId) { - #if UNITY_WEBGL +#if UNITY_WEBGL + if (xrState != WebXRState.AR) + { + return; + } + WebXRSettings settings = WebXRSettings.GetSettings(); + if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + { + return; + } Native.DeleteAnchor(anchorId); - #endif +#endif } public void DeleteAllAnchors() { - #if UNITY_WEBGL +#if UNITY_WEBGL + if (xrState != WebXRState.AR) + { + return; + } + WebXRSettings settings = WebXRSettings.GetSettings(); + if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + { + return; + } Native.DeleteAllAnchors(); - #endif +#endif } } } From 58ea32873a9d86cefb7b136883ee87cfb009c722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Papenfu=C3=9F?= Date: Thu, 30 Apr 2026 07:18:26 +0200 Subject: [PATCH 5/5] extracted Feature check into seerate method --- .../webxr/Runtime/XRPlugin/WebXRSubsystem.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs index b10fca95..4469e4f5 100644 --- a/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs +++ b/Packages/webxr/Runtime/XRPlugin/WebXRSubsystem.cs @@ -232,10 +232,16 @@ private void UpdateXRCameras() } } - private void UpdateAnchors() + private static bool AnchorsFeatureEnabled() { WebXRSettings settings = WebXRSettings.GetSettings(); - if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + return settings != null + && settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors); + } + + private void UpdateAnchors() + { + if (!AnchorsFeatureEnabled()) { return; } @@ -784,8 +790,7 @@ public void CreateAnchorFromViewerHitTest() { return; } - WebXRSettings settings = WebXRSettings.GetSettings(); - if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + if (!AnchorsFeatureEnabled()) { return; } @@ -800,8 +805,7 @@ public void CreateAnchorFromWaitingForViewerHitTest() { return; } - WebXRSettings settings = WebXRSettings.GetSettings(); - if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + if (!AnchorsFeatureEnabled()) { return; } @@ -816,8 +820,7 @@ public void CreateAnchorFromPose(Vector3 position, Quaternion rotation) { return; } - WebXRSettings settings = WebXRSettings.GetSettings(); - if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + if (!AnchorsFeatureEnabled()) { return; } @@ -834,8 +837,7 @@ public void DeleteAnchor(int anchorId) { return; } - WebXRSettings settings = WebXRSettings.GetSettings(); - if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + if (!AnchorsFeatureEnabled()) { return; } @@ -850,8 +852,7 @@ public void DeleteAllAnchors() { return; } - WebXRSettings settings = WebXRSettings.GetSettings(); - if (settings != null && !settings.AROptionalFeatures.HasFlag(WebXRSettings.ExtraFeatureTypes.anchors)) + if (!AnchorsFeatureEnabled()) { return; }