├── .gitignore ├── Documentation ├── .gitignore ├── toc.yml ├── filterConfig.yml ├── DocSource │ ├── toc.yml │ └── AdvancedSamples.md ├── docfx.json └── index.md ├── UCamera ├── UCamera │ ├── .gitignore │ ├── src │ │ └── main │ │ │ ├── cpp │ │ │ ├── .gitignore │ │ │ ├── CMakeLists.txt │ │ │ ├── JNIExtensions.h │ │ │ ├── Renderer.h │ │ │ └── JNIExtensions.cpp │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── com │ │ │ └── uralstech │ │ │ └── ucamera │ │ │ ├── RepeatingCaptureSessionWrapper.kt │ │ │ ├── Camera2Wrapper.kt │ │ │ ├── CameraCharacteristicsWrapper.kt │ │ │ ├── OnDemandCaptureSessionWrapper.kt │ │ │ └── CameraDeviceWrapper.kt │ ├── proguard-rules.pro │ └── build.gradle.kts ├── gradle │ ├── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ └── libs.versions.toml ├── .gitignore ├── .idea │ ├── compiler.xml │ ├── vcs.xml │ ├── AndroidProjectSystem.xml │ ├── migrations.xml │ ├── misc.xml │ ├── gradle.xml │ └── runConfigurations.xml ├── build.gradle.kts ├── settings.gradle.kts ├── README.md ├── gradle.properties ├── gradlew.bat └── gradlew ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── upm-subtree-split.yml │ └── pages-upload-action.yml ├── UXR.QuestCamera ├── ProjectSettings │ ├── ProjectVersion.txt │ ├── Packages │ │ ├── com.unity.testtools.codecoverage │ │ │ └── Settings.json │ │ └── com.unity.dedicated-server │ │ │ └── MultiplayerRolesSettings.asset │ ├── ClusterInputManager.asset │ ├── PresetManager.asset │ ├── MultiplayerManager.asset │ ├── XRSettings.asset │ ├── VersionControlSettings.asset │ ├── TimeManager.asset │ ├── VFXManager.asset │ ├── AudioManager.asset │ ├── TagManager.asset │ ├── EditorBuildSettings.asset │ ├── UnityConnectSettings.asset │ ├── AndroidResolverDependencies.xml │ ├── DynamicsManager.asset │ ├── MemorySettings.asset │ ├── PackageManagerSettings.asset │ ├── GvhProjectSettings.xml │ ├── EditorSettings.asset │ ├── NavMeshAreas.asset │ ├── Physics2DSettings.asset │ ├── GraphicsSettings.asset │ ├── SceneTemplateSettings.json │ ├── InputManager.asset │ └── QualitySettings.asset ├── .vsconfig ├── Packages │ ├── com.uralstech.uxr.questcamera │ │ ├── Runtime │ │ │ ├── Scripts │ │ │ │ ├── CameraInfo.cs.meta │ │ │ │ ├── CameraDevice.cs.meta │ │ │ │ ├── NativeWrapperState.cs.meta │ │ │ │ ├── YUVToRGBAConverter.cs.meta │ │ │ │ ├── Extensions │ │ │ │ │ ├── JNIExtensions.cs.meta │ │ │ │ │ ├── TaskExtensions.cs.meta │ │ │ │ │ ├── TaskExtensions.cs │ │ │ │ │ └── JNIExtensions.cs │ │ │ │ ├── External │ │ │ │ │ ├── CameraSupport.cs.meta │ │ │ │ │ └── CameraSupport.cs │ │ │ │ ├── CaptureSession │ │ │ │ │ ├── CapturePipeline.cs.meta │ │ │ │ │ ├── CaptureTemplate.cs.meta │ │ │ │ │ ├── ContinuousCaptureSession.cs.meta │ │ │ │ │ ├── OnDemandCaptureSession.cs.meta │ │ │ │ │ ├── SurfaceTextureCapture │ │ │ │ │ │ ├── STCaptureSessionNative.cs.meta │ │ │ │ │ │ ├── SurfaceTextureCaptureSession.cs.meta │ │ │ │ │ │ ├── OnDemandSurfaceTextureCaptureSession.cs.meta │ │ │ │ │ │ ├── OnDemandSurfaceTextureCaptureSession.cs │ │ │ │ │ │ └── STCaptureSessionNative.cs │ │ │ │ │ ├── SurfaceTextureCapture.meta │ │ │ │ │ ├── CaptureTemplate.cs │ │ │ │ │ ├── CapturePipeline.cs │ │ │ │ │ └── OnDemandCaptureSession.cs │ │ │ │ ├── External.meta │ │ │ │ ├── Managers.meta │ │ │ │ ├── Extensions.meta │ │ │ │ ├── CaptureSession.meta │ │ │ │ ├── Managers │ │ │ │ │ ├── UCameraManager.cs.meta │ │ │ │ │ └── UCameraManager.cs │ │ │ │ ├── NativeWrapperState.cs │ │ │ │ └── CameraInfo.cs │ │ │ ├── Plugins │ │ │ │ ├── Android │ │ │ │ │ ├── com.uralstech.ucamera-release.aar.meta │ │ │ │ │ └── com.uralstech.ucamera-release.aar │ │ │ │ └── Android.meta │ │ │ ├── Plugins.meta │ │ │ ├── Prefabs.meta │ │ │ ├── Prefabs │ │ │ │ ├── QuestCameraManager.prefab.meta │ │ │ │ └── QuestCameraManager.prefab │ │ │ ├── Scripts.meta │ │ │ ├── Shaders.meta │ │ │ ├── Shaders │ │ │ │ ├── YUVConverter.compute.meta │ │ │ │ └── YUVConverter.compute │ │ │ ├── UXR.QuestCamera.asmdef.meta │ │ │ └── UXR.QuestCamera.asmdef │ │ ├── Documentation~ │ │ │ ├── Documentation.pdf │ │ │ └── APIReferenceManual.pdf │ │ ├── Editor │ │ │ ├── Dependencies │ │ │ │ ├── Dependencies.xml │ │ │ │ └── Dependencies.xml.meta │ │ │ └── Dependencies.meta │ │ ├── package.json.meta │ │ ├── Editor.meta │ │ ├── Runtime.meta │ │ ├── Samples~ │ │ │ └── DigitRecognitionSample │ │ │ │ ├── Models │ │ │ │ ├── mnist-12.sentis │ │ │ │ └── mnist-12.sentis.meta │ │ │ │ ├── README.md.meta │ │ │ │ ├── Scenes │ │ │ │ └── Main.unity.meta │ │ │ │ ├── Models.meta │ │ │ │ ├── Scenes.meta │ │ │ │ ├── Scripts.meta │ │ │ │ ├── Shaders.meta │ │ │ │ ├── Shaders │ │ │ │ ├── YUVToLuminanceConverter.compute.meta │ │ │ │ └── YUVToLuminanceConverter.compute │ │ │ │ ├── UXR.QuestCamera.DigitRecognitionSample.asmdef.meta │ │ │ │ ├── README.md │ │ │ │ ├── Scripts │ │ │ │ └── DigitRecognition.cs.meta │ │ │ │ └── UXR.QuestCamera.DigitRecognitionSample.asmdef │ │ └── package.json │ ├── manifest.json │ └── packages-lock.json ├── UXR.QuestCamera.slnx ├── .gitignore └── .gitattributes ├── THIRD-PARTY-LICENSES.md ├── README.md └── CODE_OF_CONDUCT.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | -------------------------------------------------------------------------------- /Documentation/.gitignore: -------------------------------------------------------------------------------- 1 | api/ 2 | _site/ -------------------------------------------------------------------------------- /UCamera/UCamera/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /libs -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: udayshankar -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/cpp/.gitignore: -------------------------------------------------------------------------------- 1 | /UnityInterfaces -------------------------------------------------------------------------------- /Documentation/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Documentation 2 | href: DocSource/ 3 | - name: API Reference 4 | href: api/ -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /UCamera/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Uralstech/UXR.QuestCamera/HEAD/UCamera/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 6000.3.1f1 2 | m_EditorVersionWithRevision: 6000.3.1f1 (37142da19a94) 3 | -------------------------------------------------------------------------------- /UXR.QuestCamera/.vsconfig: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "components": [ 4 | "Microsoft.VisualStudio.Workload.ManagedGame" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CameraInfo.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8fde1d85bdd092e41bc26c0fafeb288f -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CameraDevice.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 285934e2c84af4f459356ca2f064defd -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/NativeWrapperState.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 65693e6cc00f90c47a6e8cd07d71301b -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/YUVToRGBAConverter.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 74b2186b02472214a9014902697800ff -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Extensions/JNIExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c57b7c58470ca458d833bdade1fb57f6 -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Extensions/TaskExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 270f7c0d4f02b481c800918a399eb1be -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/External/CameraSupport.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9e4894eb9bf8f5e499e6174e615fb97e -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "m_Dictionary": { 3 | "m_DictionaryValues": [] 4 | } 5 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/CapturePipeline.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eec447ba6d2f4644e8cf8f263c449837 -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/CaptureTemplate.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 91509ff863046d04bb7b222e5c155a88 -------------------------------------------------------------------------------- /UCamera/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/ 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Plugins/Android/com.uralstech.ucamera-release.aar.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aadf76ff4b9586f42b87290d3bbcf8e3 -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/ContinuousCaptureSession.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d6fb5bc5032c0d5488d8c12a43e75019 -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/OnDemandCaptureSession.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 149de9cd67086b24383382889b0e01fa -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!236 &1 4 | ClusterInputManager: 5 | m_ObjectHideFlags: 0 6 | m_Inputs: [] 7 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/SurfaceTextureCapture/STCaptureSessionNative.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2213a275b566c4418a518d63c89d454b -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/SurfaceTextureCapture/SurfaceTextureCaptureSession.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d59bee6224a254cafae1536e485e2e5b -------------------------------------------------------------------------------- /UCamera/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/SurfaceTextureCapture/OnDemandSurfaceTextureCaptureSession.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 47bd88a4dd63a470992e71a465541721 -------------------------------------------------------------------------------- /UCamera/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1386491679 &1 4 | PresetManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_DefaultPresets: {} 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Documentation~/Documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Uralstech/UXR.QuestCamera/HEAD/UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Documentation~/Documentation.pdf -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/MultiplayerManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!655991488 &1 4 | MultiplayerManager: 5 | m_ObjectHideFlags: 0 6 | m_EnableMultiplayerRoles: 0 7 | m_StrippingTypes: {} 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Documentation~/APIReferenceManual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Uralstech/UXR.QuestCamera/HEAD/UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Documentation~/APIReferenceManual.pdf -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Editor/Dependencies/Dependencies.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /UCamera/build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | alias(libs.plugins.android.library) apply false 4 | alias(libs.plugins.kotlin.android) apply false 5 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f6e953ec952e85498350251f4681a8c 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Documentation/filterConfig.yml: -------------------------------------------------------------------------------- 1 | ### YamlMime:ManagedReference 2 | apiRules: 3 | - include: # The namespaces to generate 4 | uidRegex: ^Uralstech 5 | type: Namespace 6 | - exclude: 7 | uidRegex: .* # Every other namespaces are ignored 8 | type: Namespace -------------------------------------------------------------------------------- /UCamera/.idea/AndroidProjectSystem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e778fb10e6b58914494a9ba041085814 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8c06455408824464793d067bbf55403f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Editor/Dependencies.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9127736cb61866542b4b12cbcc146a5e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Editor/Dependencies/Dependencies.xml.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6b1e7357a03966942af53e9f54817ef9 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 21977a5ed2454a94bb9e5d4dcbe256be 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Prefabs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d9478c5d540a50346925003760a12557 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Prefabs/QuestCameraManager.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a5aadf43c7e381a49a3d96f0618bc958 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1d562975c1af6df4eb3f2106fa0ab747 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e695e2512257c045b3ec4ece7419913 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Models/mnist-12.sentis: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Uralstech/UXR.QuestCamera/HEAD/UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Models/mnist-12.sentis -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/VersionControlSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!890905787 &1 4 | VersionControlSettings: 5 | m_ObjectHideFlags: 0 6 | m_Mode: Visible Meta Files 7 | m_CollabEditorSettings: 8 | inProgressEnabled: 1 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/UXR.QuestCamera.slnx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Plugins/Android.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f9ff1e3ecce3dc540adbf8be5be4089b 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Plugins/Android/com.uralstech.ucamera-release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Uralstech/UXR.QuestCamera/HEAD/UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Plugins/Android/com.uralstech.ucamera-release.aar -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/External.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 429706b0d4f9ec64eb10b75985e5637d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Managers.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 827386b5524fb93418d6d869b0a4b8c9 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Shaders/YUVConverter.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 33a2078621338e548bada15b083db82e 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/UXR.QuestCamera.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 799d6a9cc351a8b4ea6bb0fcbeb86427 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3e1f91d04da976048a3e8cfe6f436da7 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Extensions.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 167629483423eb2429dac0c90221d950 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Scenes/Main.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d21f9028511979e4bbe9142830c1ea80 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eae404ad2e6a4fe4990c2431a7ca2c09 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: 0.02 7 | Maximum Allowed Timestep: 0.33333334 8 | m_TimeScale: 1 9 | Maximum Particle Timestep: 0.03 10 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Models.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b18b63adfd6e43e41b22433855dd27b5 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8b49840e04f04a24b9eb82961a8d79f9 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f0a7afa3d52fe53469821095e95b0be8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b3104fc52432de944b75fdca000b4a2f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UCamera/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Mar 11 23:27:00 IST 2025 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /Documentation/DocSource/toc.yml: -------------------------------------------------------------------------------- 1 | pdfFileName: Documentation.pdf 2 | pdfTocPage: true 3 | pdf: true 4 | pdfPrintBackground: true 5 | 6 | items: 7 | - name: Quick Start 8 | href: QuickStart.md 9 | - name: Advanced Samples 10 | href: AdvancedSamples.md 11 | - name: V3 Migration 12 | href: V3Migration.md -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/SurfaceTextureCapture.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4e7818b6716684e1f8e6bd97ee9af025 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Shaders/YUVToLuminanceConverter.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1329fb68a9d50ab4b98c8e5d397cff7e 3 | ComputeShaderImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UCamera/.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/UXR.QuestCamera.DigitRecognitionSample.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6ca8206f8002e6f4da694588be1e3b27 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/README.md: -------------------------------------------------------------------------------- 1 | ### Package Dependencies 2 | 3 | This sample requires the Unity Inference Engine package (`com.unity.ai.inference`) and was built with version 2.3.0 of the package. 4 | This sample also uses the old input system. There is no code that references it, but you will have to change the 5 | UI input module in the scenes. -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!937362698 &1 4 | VFXManager: 5 | m_ObjectHideFlags: 0 6 | m_IndirectShader: {fileID: 0} 7 | m_CopyBufferShader: {fileID: 0} 8 | m_SortShader: {fileID: 0} 9 | m_StripUpdateShader: {fileID: 0} 10 | m_RenderPipeSettingsPath: 11 | m_FixedTimeStep: 0.016666668 12 | m_MaxDeltaTime: 0.05 13 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Models/mnist-12.sentis.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bea3df236e6caba4f87e4d9ee236498c 3 | ScriptedImporter: 4 | internalIDToNameTable: [] 5 | externalObjects: {} 6 | serializedVersion: 2 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | script: {fileID: 11500000, guid: d416c695138885743b59877b4a1c2f5a, type: 3} 11 | -------------------------------------------------------------------------------- /UCamera/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Ask a question about the project 4 | title: "(Summary of the question)" 5 | labels: question 6 | assignees: Uralstech 7 | 8 | --- 9 | 10 | **Question** 11 | What is your question? 12 | 13 | **Context** 14 | Provide any context or background information that might be relevant to your question. 15 | 16 | **Additional Information** 17 | Add any other details that might help in answering your question. 18 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Managers/UCameraManager.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cfe1b046d243a524aa171f4d1d110abc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: 7 | - YUVToRGBAComputeShader: {fileID: 7200000, guid: 33a2078621338e548bada15b083db82e, type: 3} 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | add_library( 4 | NativeTextureHelper 5 | SHARED 6 | 7 | JNIExtensions.h 8 | JNIExtensions.cpp 9 | Renderer.h 10 | Renderer.cpp 11 | STCaptureSessionHelper.cpp 12 | ) 13 | 14 | target_include_directories( 15 | NativeTextureHelper 16 | PRIVATE 17 | UnityInterfaces 18 | ) 19 | 20 | target_link_libraries(NativeTextureHelper android GLESv3 log) -------------------------------------------------------------------------------- /UCamera/gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.10.1" 3 | camera2 = "1.5.2" 4 | kotlin = "2.2.21" 5 | coreKtx = "1.17.0" 6 | 7 | [libraries] 8 | androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "camera2" } 9 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } 10 | 11 | [plugins] 12 | android-library = { id = "com.android.library", version.ref = "agp" } 13 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 14 | 15 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Volume: 1 8 | Rolloff Scale: 1 9 | Doppler Factor: 1 10 | Default Speaker Mode: 2 11 | m_SampleRate: 0 12 | m_DSPBufferSize: 1024 13 | m_VirtualVoiceCount: 512 14 | m_RealVoiceCount: 32 15 | m_SpatializerPlugin: 16 | m_AmbisonicDecoderPlugin: 17 | m_DisableAudio: 0 18 | m_VirtualizeEffects: 1 19 | m_RequestedDSPBufferSize: 1024 20 | -------------------------------------------------------------------------------- /.github/workflows/upm-subtree-split.yml: -------------------------------------------------------------------------------- 1 | # Taken from https://github.com/RageAgainstThePixel/upm-subtree-split 2 | 3 | name: upm-subtree-split 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | upm-subtree-split: 12 | permissions: 13 | contents: write 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | 20 | - uses: RageAgainstThePixel/upm-subtree-split@v1.1 21 | with: 22 | package-root: "**/Packages/com.uralstech.uxr.questcamera" 23 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | serializedVersion: 2 6 | tags: [] 7 | layers: 8 | - Default 9 | - TransparentFX 10 | - Ignore Raycast 11 | - 12 | - Water 13 | - UI 14 | - 15 | - 16 | - 17 | - 18 | - 19 | - 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | m_SortingLayers: 41 | - name: Default 42 | uniqueID: 0 43 | locked: 0 44 | -------------------------------------------------------------------------------- /UCamera/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google { 4 | content { 5 | includeGroupByRegex("com\\.android.*") 6 | includeGroupByRegex("com\\.google.*") 7 | includeGroupByRegex("androidx.*") 8 | } 9 | } 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | rootProject.name = "UCamera" 23 | include(":UCamera") 24 | -------------------------------------------------------------------------------- /UCamera/README.md: -------------------------------------------------------------------------------- 1 | ## Setup 2 | 3 | ### Java/Kotlin Dependencies 4 | - In the `UCamera` sub-directory, create a new folder named `libs`. 5 | - Copy `classes.jar` from `C:\Program Files\Unity\Hub\Editor\[Unity version]\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes` (or wherever Unity is installed on your computer) into `libs`. 6 | 7 | ### C++ Dependencies 8 | - In the `UCamera\src\main\cpp` sub-directory, create a new folder named `UnityInterfaces`. 9 | - Copy `IUnityGraphics.h` and `IUnityInterface.h` from `C:\Program Files\Unity\Hub\Editor\[Unity version]\Editor\Data\PluginAPI` (or wherever Unity is installed on your computer) into `UnityInterfaces`. -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Scripts/DigitRecognition.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5e04218d77daa1f449902a2ca79278b7 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: 7 | - _outputText: {instanceID: 0} 8 | - _cameraPreview: {instanceID: 0} 9 | - _modelInputPreview: {instanceID: 0} 10 | - _startButton: {instanceID: 0} 11 | - _stopButton: {instanceID: 0} 12 | - _modelAsset: {fileID: 5022602860645237092, guid: bea3df236e6caba4f87e4d9ee236498c, type: 3} 13 | executionOrder: 0 14 | icon: {instanceID: 0} 15 | userData: 16 | assetBundleName: 17 | assetBundleVariant: 18 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/Packages/com.unity.dedicated-server/MultiplayerRolesSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &1 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 53 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 15023, guid: 0000000000000000e000000000000000, type: 0} 13 | m_Name: 14 | m_EditorClassIdentifier: UnityEditor.MultiplayerModule.dll::UnityEditor.Multiplayer.Internal.MultiplayerRolesSettings 15 | m_MultiplayerRoleForClassicProfile: 16 | m_Keys: [] 17 | m_Values: 18 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.google.external-dependency-manager": "1.2.186", 4 | "com.unity.ai.inference": "2.3.0", 5 | "com.unity.ide.visualstudio": "2.0.25", 6 | "com.unity.mobile.android-logcat": "1.4.6", 7 | "com.unity.ugui": "2.0.0", 8 | "com.unity.modules.unitywebrequest": "1.0.0" 9 | }, 10 | "scopedRegistries": [ 11 | { 12 | "name": "OpenUPM", 13 | "url": "https://package.openupm.com", 14 | "scopes": [ 15 | "com.google.external-dependency-manager", 16 | "com.uralstech.utils.singleton", 17 | "com.utilities.async" 18 | ], 19 | "overrideBuiltIns": false 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/UXR.QuestCamera.DigitRecognitionSample.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UXR.QuestCamera.Samples.DigitRecognition", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:63a57c8b658089e49a173b0f0c4870a7", 6 | "GUID:799d6a9cc351a8b4ea6bb0fcbeb86427", 7 | "GUID:2281fe9ed9abb88469162fc481fcc7a1" 8 | ], 9 | "includePlatforms": [], 10 | "excludePlatforms": [], 11 | "allowUnsafeCode": false, 12 | "overrideReferences": false, 13 | "precompiledReferences": [], 14 | "autoReferenced": true, 15 | "defineConstraints": [], 16 | "versionDefines": [], 17 | "noEngineReferences": false 18 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "(Summary of the feature)" 5 | labels: enhancement 6 | assignees: Uralstech 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context about the feature request here. 21 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: 8 | - enabled: 1 9 | path: Assets/test.unity 10 | guid: b5a0a63e0e2a441d9bece1500fac12ab 11 | - enabled: 0 12 | path: Assets/Samples/UXR.QuestCamera/3.1.2/Digit Recognition with Unity Inference 13 | Engine/Scenes/Main.unity 14 | guid: d21f9028511979e4bbe9142830c1ea80 15 | m_configObjects: 16 | com.unity.dt.app-ui: {fileID: 11400000, guid: 1b1c20d82303e4b5781c3ef50ac1449f, type: 2} 17 | com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 289c1b55c9541489481df5cc06664110, type: 3} 18 | m_UseUCBPForAssetBundles: 0 19 | -------------------------------------------------------------------------------- /UCamera/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help the project improve 4 | title: "(Summary of the bug)" 5 | labels: bug 6 | assignees: Uralstech 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Development environment:** 24 | - Unity version: [e.g. 2022.3, 2020.2] 25 | - Build target: [e.g. Android, Editor] 26 | - Package version: [e.g. 1.0.0] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/UXR.QuestCamera.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "UXR.QuestCamera", 3 | "rootNamespace": "", 4 | "references": [ 5 | "GUID:2281fe9ed9abb88469162fc481fcc7a1", 6 | "GUID:a6609af893242c7438d701ddd4cce46a", 7 | "GUID:f64c9ebcd7899c3448a08dc9f9ddbe30" 8 | ], 9 | "includePlatforms": [], 10 | "excludePlatforms": [], 11 | "allowUnsafeCode": true, 12 | "overrideReferences": true, 13 | "precompiledReferences": [], 14 | "autoReferenced": true, 15 | "defineConstraints": [], 16 | "versionDefines": [ 17 | { 18 | "name": "com.meta.xr.sdk.core", 19 | "expression": "1.0.0", 20 | "define": "META_XR_SDK_CORE" 21 | } 22 | ], 23 | "noEngineReferences": false 24 | } -------------------------------------------------------------------------------- /UCamera/UCamera/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/cpp/JNIExtensions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef UCAMERA_JNIEXTENSIONS_H 16 | #define UCAMERA_JNIEXTENSIONS_H 17 | 18 | #include 19 | 20 | bool HasJNIException(JNIEnv* env); 21 | 22 | JNIEnv* AttachEnv(JavaVM* javaVm, bool* shouldDetach); 23 | void DetachJNIEnv(JavaVM* javaVm); 24 | 25 | #endif //UCAMERA_JNIEXTENSIONS_H 26 | -------------------------------------------------------------------------------- /UCamera/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!310 &1 4 | UnityConnectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 1 7 | m_Enabled: 0 8 | m_TestMode: 0 9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events 10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events 11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com 12 | m_DashboardUrl: https://dashboard.unity3d.com 13 | m_TestInitMode: 0 14 | CrashReportingSettings: 15 | m_EventUrl: https://perf-events.cloud.unity3d.com 16 | m_Enabled: 0 17 | m_LogBufferSize: 10 18 | m_CaptureEditorExceptions: 1 19 | UnityPurchasingSettings: 20 | m_Enabled: 0 21 | m_TestMode: 0 22 | UnityAnalyticsSettings: 23 | m_Enabled: 0 24 | m_TestMode: 0 25 | m_InitializeOnStartup: 1 26 | m_PackageRequiringCoreStatsPresent: 0 27 | UnityAdsSettings: 28 | m_Enabled: 0 29 | m_InitializeOnStartup: 1 30 | m_TestMode: 0 31 | m_IosGameId: 32 | m_AndroidGameId: 33 | m_GameIds: {} 34 | m_GameId: 35 | PerformanceReportingSettings: 36 | m_Enabled: 0 37 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/AndroidResolverDependencies.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | androidx.camera:camera-camera2:1.5.2 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /.github/workflows/pages-upload-action.yml: -------------------------------------------------------------------------------- 1 | # Trigger the action on push to master 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 8 | permissions: 9 | actions: read 10 | pages: write 11 | id-token: write 12 | 13 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 14 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: false 18 | 19 | jobs: 20 | publish-docs: 21 | environment: 22 | name: github-pages 23 | url: ${{ steps.deployment.outputs.page_url }} 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v4 28 | - name: Dotnet Setup 29 | uses: actions/setup-dotnet@v4 30 | with: 31 | dotnet-version: 8.x 32 | 33 | - run: dotnet tool update -g docfx 34 | - run: docfx Documentation/docfx.json 35 | 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | # Upload entire repository 40 | path: 'Documentation/_site' 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@v4 44 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/NativeWrapperState.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #nullable enable 16 | namespace Uralstech.UXR.QuestCamera 17 | { 18 | /// 19 | /// The current assumed state of a native wrapper. 20 | /// 21 | public enum NativeWrapperState 22 | { 23 | /// The native wrapper is still initializing. 24 | Initializing, 25 | 26 | /// The native wrapper is open and ready. 27 | Opened, 28 | 29 | /// The native wrapper failed with an error, was disconnected or is being/was closed normally. 30 | Closed, 31 | } 32 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.uralstech.uxr.questcamera", 3 | "displayName": "UXR.QuestCamera", 4 | "description": "A Unity package to use the new Meta Quest Passthrough Camera API.", 5 | "keywords": [], 6 | "version": "3.1.3", 7 | "unity": "2022.3", 8 | "hideInEditor": false, 9 | "documentationUrl": "https://uralstech.github.io/UXR.QuestCamera/", 10 | "changelogUrl": "https://github.com/Uralstech/UXR.QuestCamera/releases", 11 | "licensesUrl": "https://github.com/Uralstech/UXR.QuestCamera/blob/master/LICENSE", 12 | "license": "Apache-2.0", 13 | "repository": { 14 | "type": "git", 15 | "repository": "https://github.com/Uralstech/UXR.QuestCamera.git" 16 | }, 17 | "samples": [ 18 | { 19 | "displayName": "Digit Recognition with Unity Inference Engine", 20 | "description": "A sample showcasing a Unity Inference Engine (formerly known as Unity Sentis) digit recognition model being used with the Meta Quest Passthrough Camera API.", 21 | "path": "Samples~/DigitRecognitionSample" 22 | } 23 | ], 24 | "author": { 25 | "name": "Udayshankar Ravikumar", 26 | "url": "https://github.com/Uralstech" 27 | }, 28 | "dependencies": { 29 | "com.uralstech.utils.singleton": "1.2.1", 30 | "com.unity.modules.androidjni": "1.0.0" 31 | } 32 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 11 7 | m_Gravity: {x: 0, y: -9.81, z: 0} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_BounceThreshold: 2 10 | m_SleepThreshold: 0.005 11 | m_DefaultContactOffset: 0.01 12 | m_DefaultSolverIterations: 6 13 | m_DefaultSolverVelocityIterations: 1 14 | m_QueriesHitBackfaces: 0 15 | m_QueriesHitTriggers: 1 16 | m_EnableAdaptiveForce: 0 17 | m_ClothInterCollisionDistance: 0 18 | m_ClothInterCollisionStiffness: 0 19 | m_ContactsGeneration: 1 20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 | m_AutoSimulation: 1 22 | m_AutoSyncTransforms: 0 23 | m_ReuseCollisionCallbacks: 1 24 | m_ClothInterCollisionSettingsToggle: 0 25 | m_ContactPairsMode: 0 26 | m_BroadphaseType: 0 27 | m_WorldBounds: 28 | m_Center: {x: 0, y: 0, z: 0} 29 | m_Extent: {x: 250, y: 250, z: 250} 30 | m_WorldSubdivisions: 8 31 | m_FrictionType: 0 32 | m_EnableEnhancedDeterminism: 0 33 | m_EnableUnifiedHeightmaps: 1 34 | m_DefaultMaxAngluarSpeed: 7 35 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/MemorySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!387306366 &1 4 | MemorySettings: 5 | m_ObjectHideFlags: 0 6 | m_EditorMemorySettings: 7 | m_MainAllocatorBlockSize: -1 8 | m_ThreadAllocatorBlockSize: -1 9 | m_MainGfxBlockSize: -1 10 | m_ThreadGfxBlockSize: -1 11 | m_CacheBlockSize: -1 12 | m_TypetreeBlockSize: -1 13 | m_ProfilerBlockSize: -1 14 | m_ProfilerEditorBlockSize: -1 15 | m_BucketAllocatorGranularity: -1 16 | m_BucketAllocatorBucketsCount: -1 17 | m_BucketAllocatorBlockSize: -1 18 | m_BucketAllocatorBlockCount: -1 19 | m_ProfilerBucketAllocatorGranularity: -1 20 | m_ProfilerBucketAllocatorBucketsCount: -1 21 | m_ProfilerBucketAllocatorBlockSize: -1 22 | m_ProfilerBucketAllocatorBlockCount: -1 23 | m_TempAllocatorSizeMain: -1 24 | m_JobTempAllocatorBlockSize: -1 25 | m_BackgroundJobTempAllocatorBlockSize: -1 26 | m_JobTempAllocatorReducedBlockSize: -1 27 | m_TempAllocatorSizeGIBakingWorker: -1 28 | m_TempAllocatorSizeNavMeshWorker: -1 29 | m_TempAllocatorSizeAudioWorker: -1 30 | m_TempAllocatorSizeCloudWorker: -1 31 | m_TempAllocatorSizeGfx: -1 32 | m_TempAllocatorSizeJobWorker: -1 33 | m_TempAllocatorSizeBackgroundWorker: -1 34 | m_TempAllocatorSizePreloadManager: -1 35 | m_PlatformMemorySettings: {} 36 | -------------------------------------------------------------------------------- /UCamera/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. For more details, visit 12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/PackageManagerSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &1 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 61 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} 13 | m_Name: 14 | m_EditorClassIdentifier: 15 | m_EnablePreReleasePackages: 0 16 | m_AdvancedSettingsExpanded: 1 17 | m_ScopedRegistriesSettingsExpanded: 1 18 | m_SeeAllPackageVersions: 0 19 | m_DismissPreviewPackagesInUse: 0 20 | oneTimeWarningShown: 0 21 | oneTimeDeprecatedPopUpShown: 0 22 | m_Registries: 23 | - m_Id: main 24 | m_Name: 25 | m_Url: https://packages.unity.com 26 | m_Scopes: [] 27 | m_IsDefault: 1 28 | m_Capabilities: 7 29 | m_ConfigSource: 0 30 | - m_Id: scoped:project:OpenUPM 31 | m_Name: OpenUPM 32 | m_Url: https://package.openupm.com 33 | m_Scopes: 34 | - com.google.external-dependency-manager 35 | - com.uralstech.utils.singleton 36 | - com.utilities.async 37 | m_IsDefault: 0 38 | m_Capabilities: 0 39 | m_ConfigSource: 4 40 | m_UserSelectedRegistryName: OpenUPM 41 | m_UserAddingNewScopedRegistry: 0 42 | m_RegistryInfoDraft: 43 | m_Modified: 0 44 | m_ErrorMessage: 45 | m_UserModificationsInstanceId: -868 46 | m_OriginalInstanceId: -870 47 | m_LoadAssets: 0 48 | -------------------------------------------------------------------------------- /UCamera/UCamera/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | 3 | plugins { 4 | alias(libs.plugins.android.library) 5 | alias(libs.plugins.kotlin.android) 6 | } 7 | 8 | android { 9 | namespace = "com.uralstech.ucamera" 10 | compileSdk = 36 11 | 12 | defaultConfig { 13 | minSdk = 29 14 | 15 | setProperty("archivesBaseName", "$namespace") 16 | 17 | ndk { 18 | //noinspection ChromeOsAbiSupport 19 | abiFilters += listOf("arm64-v8a") 20 | } 21 | } 22 | 23 | buildTypes { 24 | release { 25 | isMinifyEnabled = false 26 | proguardFiles( 27 | getDefaultProguardFile("proguard-android-optimize.txt"), 28 | "proguard-rules.pro" 29 | ) 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility = JavaVersion.VERSION_11 34 | targetCompatibility = JavaVersion.VERSION_11 35 | } 36 | 37 | kotlin { 38 | compilerOptions { 39 | jvmTarget.set(JvmTarget.JVM_11) 40 | } 41 | } 42 | 43 | externalNativeBuild { 44 | cmake { 45 | path = file("src/main/cpp/CMakeLists.txt") 46 | } 47 | } 48 | 49 | packaging { 50 | resources { 51 | excludes.add("com/unity3d/**") 52 | } 53 | } 54 | } 55 | 56 | dependencies { 57 | compileOnly(files("libs/classes.jar")) 58 | implementation(libs.androidx.core.ktx) 59 | implementation(libs.androidx.camera.camera2) 60 | } -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/java/com/uralstech/ucamera/RepeatingCaptureSessionWrapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.uralstech.ucamera 16 | 17 | import android.hardware.camera2.CameraCaptureSession 18 | import android.hardware.camera2.CameraDevice 19 | import android.hardware.camera2.params.OutputConfiguration 20 | 21 | /** 22 | * Wrapper class for [CameraCaptureSession] with a repeating capture request. 23 | */ 24 | class RepeatingCaptureSessionWrapper( 25 | cameraDevice: CameraDevice, captureTemplate: Int, 26 | callbacks: Callbacks, width: Int, height: Int) : CaptureSessionWrapper(cameraDevice, captureTemplate, callbacks, width, height) { 27 | 28 | /** 29 | * Creates a new capture session and sets the repeating capture request. 30 | */ 31 | override fun startCaptureSession(camera: CameraDevice, captureTemplate: Int) { 32 | super.startRepeatingCaptureSession(camera, captureTemplate, 33 | listOf(OutputConfiguration(imageReader.surface)), imageReader.surface) 34 | } 35 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore 4 | # 5 | /[Aa]ssets/ 6 | /[Ll]ibrary/ 7 | /[Tt]emp/ 8 | /[Oo]bj/ 9 | /[Bb]uild/ 10 | /[Bb]uilds/ 11 | /[Ll]ogs/ 12 | /[Uu]ser[Ss]ettings/ 13 | 14 | # MemoryCaptures can get excessive in size. 15 | # They also could contain extremely sensitive data 16 | /[Mm]emoryCaptures/ 17 | 18 | # Recordings can get excessive in size 19 | /[Rr]ecordings/ 20 | 21 | # Uncomment this line if you wish to ignore the asset store tools plugin 22 | # /[Aa]ssets/AssetStoreTools* 23 | 24 | # Autogenerated Jetbrains Rider plugin 25 | /[Aa]ssets/Plugins/Editor/JetBrains* 26 | 27 | # Visual Studio cache directory 28 | .vs/ 29 | 30 | # Gradle cache directory 31 | .gradle/ 32 | 33 | # Autogenerated VS/MD/Consulo solution and project files 34 | ExportedObj/ 35 | .consulo/ 36 | *.csproj 37 | *.unityproj 38 | *.sln 39 | *.suo 40 | *.tmp 41 | *.user 42 | *.userprefs 43 | *.pidb 44 | *.booproj 45 | *.svd 46 | *.pdb 47 | *.mdb 48 | *.opendb 49 | *.VC.db 50 | 51 | # Unity3D generated meta files 52 | *.pidb.meta 53 | *.pdb.meta 54 | *.mdb.meta 55 | 56 | # Unity3D generated file on crash reports 57 | sysinfo.txt 58 | 59 | # Builds 60 | *.apk 61 | *.aab 62 | *.unitypackage 63 | *.app 64 | 65 | # Crashlytics generated file 66 | crashlytics-build.properties 67 | 68 | # Packed Addressables 69 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 70 | 71 | # Temporary auto-generated Android Assets 72 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 73 | /[Aa]ssets/[Ss]treamingAssets/aa/* 74 | 75 | /[Aa]ssets 76 | /.utmp 77 | /.vscode -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Prefabs/QuestCameraManager.prefab: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1 &439309611987774447 4 | GameObject: 5 | m_ObjectHideFlags: 0 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | serializedVersion: 6 10 | m_Component: 11 | - component: {fileID: 1528171967605105492} 12 | - component: {fileID: 1727881059514603240} 13 | m_Layer: 0 14 | m_Name: QuestCameraManager 15 | m_TagString: Untagged 16 | m_Icon: {fileID: 0} 17 | m_NavMeshLayer: 0 18 | m_StaticEditorFlags: 0 19 | m_IsActive: 1 20 | --- !u!4 &1528171967605105492 21 | Transform: 22 | m_ObjectHideFlags: 0 23 | m_CorrespondingSourceObject: {fileID: 0} 24 | m_PrefabInstance: {fileID: 0} 25 | m_PrefabAsset: {fileID: 0} 26 | m_GameObject: {fileID: 439309611987774447} 27 | serializedVersion: 2 28 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 29 | m_LocalPosition: {x: 0, y: 0, z: 0} 30 | m_LocalScale: {x: 1, y: 1, z: 1} 31 | m_ConstrainProportionsScale: 0 32 | m_Children: [] 33 | m_Father: {fileID: 0} 34 | m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 35 | --- !u!114 &1727881059514603240 36 | MonoBehaviour: 37 | m_ObjectHideFlags: 0 38 | m_CorrespondingSourceObject: {fileID: 0} 39 | m_PrefabInstance: {fileID: 0} 40 | m_PrefabAsset: {fileID: 0} 41 | m_GameObject: {fileID: 439309611987774447} 42 | m_Enabled: 1 43 | m_EditorHideFlags: 0 44 | m_Script: {fileID: 11500000, guid: cfe1b046d243a524aa171f4d1d110abc, type: 3} 45 | m_Name: 46 | m_EditorClassIdentifier: 47 | YUVToRGBAComputeShader: {fileID: 7200000, guid: 33a2078621338e548bada15b083db82e, type: 3} 48 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/GvhProjectSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 13 7 | m_SerializationMode: 2 8 | m_LineEndingsForNewScripts: 0 9 | m_DefaultBehaviorMode: 0 10 | m_PrefabRegularEnvironment: {fileID: 0} 11 | m_PrefabUIEnvironment: {fileID: 0} 12 | m_SpritePackerMode: 0 13 | m_SpritePackerCacheSize: 10 14 | m_SpritePackerPaddingPower: 1 15 | m_Bc7TextureCompressor: 0 16 | m_EtcTextureCompressorBehavior: 1 17 | m_EtcTextureFastCompressor: 1 18 | m_EtcTextureNormalCompressor: 2 19 | m_EtcTextureBestCompressor: 4 20 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref 21 | m_ProjectGenerationRootNamespace: 22 | m_EnableTextureStreamingInEditMode: 1 23 | m_EnableTextureStreamingInPlayMode: 1 24 | m_EnableEditorAsyncCPUTextureLoading: 0 25 | m_AsyncShaderCompilation: 1 26 | m_PrefabModeAllowAutoSave: 1 27 | m_EnterPlayModeOptionsEnabled: 1 28 | m_EnterPlayModeOptions: 0 29 | m_GameObjectNamingDigits: 1 30 | m_GameObjectNamingScheme: 0 31 | m_AssetNamingUsesSpace: 1 32 | m_InspectorUseIMGUIDefaultInspector: 0 33 | m_UseLegacyProbeSampleCount: 0 34 | m_SerializeInlineMappingsOnOneLine: 1 35 | m_DisableCookiesInLightmapper: 0 36 | m_AssetPipelineMode: 1 37 | m_RefreshImportMode: 0 38 | m_CacheServerMode: 0 39 | m_CacheServerEndpoint: 40 | m_CacheServerNamespacePrefix: default 41 | m_CacheServerEnableDownload: 1 42 | m_CacheServerEnableUpload: 1 43 | m_CacheServerEnableAuth: 0 44 | m_CacheServerEnableTls: 0 45 | m_CacheServerValidationMode: 2 46 | m_CacheServerDownloadBatchSize: 128 47 | m_EnableEnlightenBakedGI: 0 48 | m_ReferencedClipsExactNaming: 1 49 | -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshProjectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | areas: 8 | - name: Walkable 9 | cost: 1 10 | - name: Not Walkable 11 | cost: 1 12 | - name: Jump 13 | cost: 2 14 | - name: 15 | cost: 1 16 | - name: 17 | cost: 1 18 | - name: 19 | cost: 1 20 | - name: 21 | cost: 1 22 | - name: 23 | cost: 1 24 | - name: 25 | cost: 1 26 | - name: 27 | cost: 1 28 | - name: 29 | cost: 1 30 | - name: 31 | cost: 1 32 | - name: 33 | cost: 1 34 | - name: 35 | cost: 1 36 | - name: 37 | cost: 1 38 | - name: 39 | cost: 1 40 | - name: 41 | cost: 1 42 | - name: 43 | cost: 1 44 | - name: 45 | cost: 1 46 | - name: 47 | cost: 1 48 | - name: 49 | cost: 1 50 | - name: 51 | cost: 1 52 | - name: 53 | cost: 1 54 | - name: 55 | cost: 1 56 | - name: 57 | cost: 1 58 | - name: 59 | cost: 1 60 | - name: 61 | cost: 1 62 | - name: 63 | cost: 1 64 | - name: 65 | cost: 1 66 | - name: 67 | cost: 1 68 | - name: 69 | cost: 1 70 | - name: 71 | cost: 1 72 | m_LastAgentTypeID: -887442657 73 | m_Settings: 74 | - serializedVersion: 2 75 | agentTypeID: 0 76 | agentRadius: 0.5 77 | agentHeight: 2 78 | agentSlope: 45 79 | agentClimb: 0.75 80 | ledgeDropHeight: 0 81 | maxJumpAcrossDistance: 0 82 | minRegionArea: 2 83 | manualCellSize: 0 84 | cellSize: 0.16666667 85 | manualTileSize: 0 86 | tileSize: 256 87 | accuratePlacement: 0 88 | debug: 89 | m_Flags: 0 90 | m_SettingNames: 91 | - Humanoid 92 | -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/cpp/Renderer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #ifndef UCAMERA_RENDERER_H 16 | #define UCAMERA_RENDERER_H 17 | 18 | #include 19 | #include 20 | 21 | class Renderer { 22 | public: 23 | Renderer(GLuint unityTexture, GLint width, GLint height); 24 | bool initialize(GLuint* texture); 25 | bool render(ASurfaceTexture* surfaceTexture) const; 26 | void dispose(); 27 | 28 | private: 29 | GLuint _unityTexture; 30 | GLuint _sourceTexture; 31 | GLuint _frameBufferObject; 32 | 33 | GLint _width; GLint _height; 34 | bool _disposed; 35 | 36 | static uint8_t _staticReferenceHolders; 37 | 38 | static GLuint _shaderProgram; 39 | static GLint _transformMatrixHandle; 40 | static GLint _textureSamplerHandle; 41 | 42 | static GLuint _vertexBufferObject; 43 | static GLuint _vertexArrayObject; 44 | 45 | static bool hasGlErrors(const char* methodName); 46 | static bool linkShaderProgram(GLuint vertexShader, GLuint fragmentShader); 47 | static bool compileShader(GLenum type, const char* source, GLuint* shader); 48 | static bool setupGeometry(); 49 | }; 50 | 51 | 52 | #endif //UCAMERA_RENDERER_H 53 | -------------------------------------------------------------------------------- /Documentation/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "src": "../UXR.QuestCamera", 7 | "files": [ 8 | "Packages/com.uralstech.uxr.questcamera/Runtime/**/*.cs" 9 | ] 10 | } 11 | ], 12 | "properties": { 13 | "DefineConstants": "UNITY_6000_0_OR_NEWER;META_XR_SDK_CORE" 14 | }, 15 | "dest": "api", 16 | "filter": "filterConfig.yml", 17 | "includePrivateMembers": false, 18 | "allowCompilationErrors": true 19 | } 20 | ], 21 | "build": { 22 | "content": [ 23 | { 24 | "files": [ 25 | "**/*.{md,yml}" 26 | ], 27 | "exclude": [ 28 | "_site/**" 29 | ] 30 | } 31 | ], 32 | "resource": [ 33 | { 34 | "files": [ 35 | "images/**" 36 | ] 37 | } 38 | ], 39 | "output": "_site", 40 | "template": [ 41 | "default", 42 | "modern" 43 | ], 44 | "globalMetadata": { 45 | "_appName": "UXR.QuestCamera", 46 | "_appTitle": "UXR.QuestCamera", 47 | "_appFooter": "(C) 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED, licensed under the Apache License, Version 2.0.", 48 | "_enableSearch": true 49 | }, 50 | "fileMetadata": { 51 | "pdf": { 52 | "api/toc.yml": true 53 | }, 54 | "pdfPrintBackground": { 55 | "api/toc.yml": true 56 | }, 57 | "pdfTocPage": { 58 | "api/toc.yml": true 59 | }, 60 | "pdfFileName": { 61 | "api/toc.yml": "APIReferenceManual.pdf" 62 | } 63 | }, 64 | "sitemap": { 65 | "baseUrl": "https://uralstech.github.io/UXR.QuestCamera", 66 | "priority": 1.0, 67 | "changefreq": "monthly" 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /THIRD-PARTY-LICENSES.md: -------------------------------------------------------------------------------- 1 | THIRD-PARTY SOFTWARE NOTICES AND INFORMATION 2 | ============================================== 3 | 4 | This project (UXR.QuestCamera) incorporates or utilizes portions of the following third-party software, which is subject to the terms and conditions of its respective license. 5 | 6 | -------------------------------------------------- 7 | 8 | **Component:** Unity-PassthroughCameraApiSamples (portions) 9 | 10 | **Source:** https://github.com/oculus-samples/Unity-PassthroughCameraApiSamples 11 | 12 | **Copyright:** Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. 13 | 14 | **License:** Oculus SDK License Agreement 15 | 16 | **License Text:** 17 | 18 | Copyright (c) Meta Platforms, Inc. and affiliates. 19 | All rights reserved. 20 | 21 | Licensed under the Oculus SDK License Agreement (the "License"); 22 | you may not use the Oculus SDK except in compliance with the License, 23 | which is provided at the time of installation or download, or which 24 | otherwise accompanies this software in either electronic or hard copy form. 25 | 26 | You may obtain a copy of the License at 27 | 28 | https://developer.oculus.com/licenses/oculussdk/ 29 | 30 | Unless required by applicable law or agreed to in writing, the Oculus SDK 31 | distributed under the License is distributed on an "AS IS" BASIS, 32 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 33 | See the License for the specific language governing permissions and 34 | limitations under the License. 35 | 36 | -------------------------------------------------- 37 | 38 | **Note**: This file provides a summary of the third-party licenses applicable to *this* project (UXR.QuestCamera). The full Oculus SDK License Agreement (linked above) governs the use of the incorporated portions from Meta's sample code. Please review the full license agreement for complete details. This file does *not* include licenses for *your* code, only for the external dependencies you've indicated. You should separately license your own original contributions. -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/CaptureTemplate.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #nullable enable 16 | namespace Uralstech.UXR.QuestCamera 17 | { 18 | /// 19 | /// Capture template to use when recording. 20 | /// 21 | public enum CaptureTemplate 22 | { 23 | /// Default value, do not use. 24 | Default = 0, 25 | 26 | /// Creates a request suitable for a camera preview window. 27 | /// 28 | Preview = 1, 29 | 30 | /// Creates a request suitable for still image capture. 31 | /// 32 | StillCapture = 2, 33 | 34 | /// Creates a request suitable for video recording. 35 | /// 36 | Record = 3, 37 | 38 | /// Creates a request suitable for still image capture while recording video. 39 | /// 40 | VideoSnapshot = 4, 41 | } 42 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/CapturePipeline.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Threading.Tasks; 17 | 18 | #nullable enable 19 | namespace Uralstech.UXR.QuestCamera 20 | { 21 | /// 22 | /// Simple class for grouping a capture session and its texture converter. 23 | /// 24 | public class CapturePipeline : IAsyncDisposable 25 | where T : ContinuousCaptureSession 26 | { 27 | /// 28 | /// The capture session wrapper. 29 | /// 30 | public readonly T CaptureSession; 31 | 32 | /// 33 | /// The YUV to RGBA texture converter. 34 | /// 35 | public readonly YUVToRGBAConverter TextureConverter; 36 | 37 | public CapturePipeline(T captureSession, YUVToRGBAConverter textureConverter) 38 | { 39 | CaptureSession = captureSession; 40 | TextureConverter = textureConverter; 41 | } 42 | 43 | private bool _disposed = false; 44 | 45 | /// 46 | /// Closes and disposes the capture session and texture converter. 47 | /// 48 | public async ValueTask DisposeAsync() 49 | { 50 | if (_disposed) 51 | return; 52 | 53 | _disposed = true; 54 | await CaptureSession.DisposeAsync(); 55 | TextureConverter.Dispose(); 56 | GC.SuppressFinalize(this); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 4 7 | m_Gravity: {x: 0, y: -9.81} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_VelocityIterations: 8 10 | m_PositionIterations: 3 11 | m_VelocityThreshold: 1 12 | m_MaxLinearCorrection: 0.2 13 | m_MaxAngularCorrection: 8 14 | m_MaxTranslationSpeed: 100 15 | m_MaxRotationSpeed: 360 16 | m_BaumgarteScale: 0.2 17 | m_BaumgarteTimeOfImpactScale: 0.75 18 | m_TimeToSleep: 0.5 19 | m_LinearSleepTolerance: 0.01 20 | m_AngularSleepTolerance: 2 21 | m_DefaultContactOffset: 0.01 22 | m_JobOptions: 23 | serializedVersion: 2 24 | useMultithreading: 0 25 | useConsistencySorting: 0 26 | m_InterpolationPosesPerJob: 100 27 | m_NewContactsPerJob: 30 28 | m_CollideContactsPerJob: 100 29 | m_ClearFlagsPerJob: 200 30 | m_ClearBodyForcesPerJob: 200 31 | m_SyncDiscreteFixturesPerJob: 50 32 | m_SyncContinuousFixturesPerJob: 50 33 | m_FindNearestContactsPerJob: 100 34 | m_UpdateTriggerContactsPerJob: 100 35 | m_IslandSolverCostThreshold: 100 36 | m_IslandSolverBodyCostScale: 1 37 | m_IslandSolverContactCostScale: 10 38 | m_IslandSolverJointCostScale: 10 39 | m_IslandSolverBodiesPerJob: 50 40 | m_IslandSolverContactsPerJob: 50 41 | m_AutoSimulation: 1 42 | m_QueriesHitTriggers: 1 43 | m_QueriesStartInColliders: 1 44 | m_CallbacksOnDisable: 1 45 | m_ReuseCollisionCallbacks: 1 46 | m_AutoSyncTransforms: 0 47 | m_AlwaysShowColliders: 0 48 | m_ShowColliderSleep: 1 49 | m_ShowColliderContacts: 0 50 | m_ShowColliderAABB: 0 51 | m_ContactArrowScale: 0.2 52 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} 53 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} 54 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} 55 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} 56 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 57 | -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/cpp/JNIExtensions.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "JNIExtensions.h" 17 | 18 | #define LOG_TAG "UCameraJNIExt" 19 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 20 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 21 | 22 | bool HasJNIException(JNIEnv* env) { 23 | if (env->ExceptionCheck()) { 24 | env->ExceptionDescribe(); 25 | env->ExceptionClear(); 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | JNIEnv* AttachEnv(JavaVM* javaVm, bool* shouldDetach) { 33 | if (javaVm == nullptr) { 34 | LOGE("javaVM is a nullptr, can't get JNIEnv."); 35 | return nullptr; 36 | } 37 | 38 | *shouldDetach = false; 39 | 40 | JNIEnv* env; 41 | jint result = javaVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); 42 | 43 | if (result == JNI_EDETACHED) { 44 | LOGI("Attaching to JNI thread."); 45 | 46 | result = javaVm->AttachCurrentThread(&env, nullptr); 47 | if (result != JNI_OK) { 48 | LOGE("Failed to attach to JNI thread, result: %i", result); 49 | return nullptr; 50 | } 51 | 52 | *shouldDetach = true; 53 | } else if (result != JNI_OK) { 54 | LOGE("Failed to get JNIEnv, result: %i", result); 55 | return nullptr; 56 | } 57 | 58 | LOGI("Got JNIEnv."); 59 | return env; 60 | } 61 | 62 | 63 | void DetachJNIEnv(JavaVM* javaVm) { 64 | if (javaVm == nullptr) { 65 | LOGE("javaVM is a nullptr, can't detach JNI thread."); 66 | return; 67 | } 68 | 69 | jint result = javaVm->DetachCurrentThread(); 70 | if (result != JNI_OK) { 71 | LOGE("Failed to detach from JNI thread, result: %i", result); 72 | } 73 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 16 7 | m_Deferred: 8 | m_Mode: 1 9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} 10 | m_DeferredReflections: 11 | m_Mode: 1 12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} 13 | m_ScreenSpaceShadows: 14 | m_Mode: 1 15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} 16 | m_DepthNormals: 17 | m_Mode: 1 18 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} 19 | m_MotionVectors: 20 | m_Mode: 1 21 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} 22 | m_LightHalo: 23 | m_Mode: 1 24 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} 25 | m_LensFlare: 26 | m_Mode: 1 27 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} 28 | m_VideoShadersIncludeMode: 2 29 | m_AlwaysIncludedShaders: 30 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} 31 | - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} 32 | - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} 33 | - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} 34 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} 35 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 36 | m_PreloadedShaders: [] 37 | m_PreloadShadersBatchTimeLimit: -1 38 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} 39 | m_CustomRenderPipeline: {fileID: 0} 40 | m_TransparencySortMode: 0 41 | m_TransparencySortAxis: {x: 0, y: 0, z: 1} 42 | m_DefaultRenderingPath: 1 43 | m_DefaultMobileRenderingPath: 1 44 | m_TierSettings: [] 45 | m_LightmapStripping: 0 46 | m_FogStripping: 0 47 | m_InstancingStripping: 0 48 | m_BrgStripping: 0 49 | m_LightmapKeepPlain: 1 50 | m_LightmapKeepDirCombined: 1 51 | m_LightmapKeepDynamicPlain: 1 52 | m_LightmapKeepDynamicDirCombined: 1 53 | m_LightmapKeepShadowMask: 1 54 | m_LightmapKeepSubtractive: 1 55 | m_FogKeepLinear: 1 56 | m_FogKeepExp: 1 57 | m_FogKeepExp2: 1 58 | m_AlbedoSwatchInfos: [] 59 | m_RenderPipelineGlobalSettingsMap: {} 60 | m_LightsUseLinearIntensity: 0 61 | m_LightsUseColorTemperature: 0 62 | m_LogWhenShaderIsCompiled: 0 63 | m_LightProbeOutsideHullStrategy: 0 64 | m_CameraRelativeLightCulling: 0 65 | m_CameraRelativeShadowCulling: 0 66 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Samples~/DigitRecognitionSample/Shaders/YUVToLuminanceConverter.compute: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma kernel CSMain 16 | 17 | // Input buffers (read-only) 18 | ByteAddressBuffer YBuffer; 19 | ByteAddressBuffer UBuffer; 20 | ByteAddressBuffer VBuffer; 21 | 22 | // Row strides 23 | uint YRowStride; 24 | uint UVRowStride; 25 | 26 | // Pixel strides 27 | uint UVPixelStride; 28 | 29 | // Image dimensions 30 | uint TargetWidth; 31 | uint TargetHeight; 32 | 33 | // Output texture (read-write) 34 | RWTexture2D OutputTexture; 35 | 36 | // Helper function to get a byte from a ByteAddressBuffer. 37 | // buffer: The ByteAddressBuffer. 38 | // byteIndex: The *byte* index (offset) into the buffer. 39 | uint GetByteFromBuffer(ByteAddressBuffer buffer, uint byteIndex) 40 | { 41 | // Calculate the 32-bit word offset (each word is 4 bytes). 42 | uint wordOffset = byteIndex / 4; 43 | 44 | // Load the 32-bit word containing the byte. 45 | uint word = buffer.Load(wordOffset * 4); // MUST multiply by 4 for ByteAddressBuffer.Load() 46 | 47 | // Calculate the byte position *within* the word (0, 1, 2, or 3). 48 | uint byteInWord = byteIndex % 4; 49 | 50 | // Extract the correct byte using bit shifts and masking. 51 | return (word >> (byteInWord * 8)) & 0xFF; 52 | } 53 | 54 | [numthreads(8, 8, 1)] 55 | void CSMain(uint3 id : SV_DispatchThreadID) 56 | { 57 | if (id.x >= TargetWidth || id.y >= TargetHeight) 58 | return; 59 | 60 | // The YUV stream is flipped, so we have to un-flip it. 61 | uint flippedY = TargetHeight - 1 - id.y; 62 | 63 | // Index of Y value in buffer. 64 | uint yIndex = flippedY * YRowStride + id.x; 65 | 66 | // Get the Y (luminance) value. 67 | uint yValue = GetByteFromBuffer(YBuffer, yIndex); 68 | 69 | // Convert the values from 0-255 to 0-1 and set the output texture. 70 | float3 luminance = float3(yValue, yValue, yValue) / 255.0; 71 | OutputTexture[id.xy] = float4(luminance.rgb, 1.0); 72 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/OnDemandCaptureSession.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | 17 | #nullable enable 18 | namespace Uralstech.UXR.QuestCamera 19 | { 20 | /// 21 | /// A wrapper for a native Camera2 CaptureSession and ImageReader. 22 | /// 23 | /// 24 | /// This is different from as it only returns a frame 25 | /// from the native plugin when required. This is recommended for single-image 26 | /// capturing or on-demand capturing where you don't need a continuous stream 27 | /// of images. 28 | ///

29 | /// Why does inherit from ? 30 | /// Because under the hood, both do the same thing - a repeating capture session. A true on-demand 31 | /// capture results in a black image, so runs a repeating capture 32 | /// request running on an dummy texture natively, and reads the actual image through an ImageReader only 33 | /// when requested to do so. This means that while the processes 34 | /// each and every frame sent to it, converting it to RGBA, only 35 | /// does it when required. 36 | ///
37 | public class OnDemandCaptureSession : ContinuousCaptureSession 38 | { 39 | /// 40 | /// Requests a new capture from the session. 41 | /// 42 | /// The capture template to use for the capture 43 | /// If the capture request was set successfully, , otherwise, . 44 | public bool RequestCapture(CaptureTemplate captureTemplate = CaptureTemplate.StillCapture) 45 | { 46 | return _captureSession?.Call("setSingleCaptureRequest", (int)captureTemplate) ?? throw new ObjectDisposedException(nameof(OnDemandCaptureSession)); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UXR.QuestCamera 2 | 3 | A Unity package to use the new Meta Quest Passthrough Camera API. 4 | 5 | [![openupm](https://img.shields.io/npm/v/com.uralstech.uxr.questcamera?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.uralstech.uxr.questcamera/) 6 | [![openupm](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=downloads&query=%24.downloads&suffix=%2Fmonth&url=https%3A%2F%2Fpackage.openupm.com%2Fdownloads%2Fpoint%2Flast-month%2Fcom.uralstech.uxr.questcamera)](https://openupm.com/packages/com.uralstech.uxr.questcamera/) 7 | 8 | ## Installation 9 | 10 | This *should* work on any reasonably modern Unity version. Built and tested in Unity 6.3. 11 | Versions older than Unity 6.0 **REQUIRE** [com.utilities.async](https://github.com/RageAgainstThePixel/com.utilities.async/). 12 | 13 | Since this package contains a native AAR plugin, it depends on the [External Dependency Manager for Unity (EDM4U)](https://github.com/googlesamples/unity-jar-resolver) to resolve native dependencies. 14 | You may already have EDM4U if you use Google or Firebase SDKs in your Unity project. If you do not, the installation steps are available 15 | here: [EDM4U - Getting Started](https://github.com/googlesamples/unity-jar-resolver?tab=readme-ov-file#getting-started) 16 | 17 | ### OpenUPM 18 | 19 | 1. Open project settings 20 | 2. Select `Package Manager` 21 | 3. Add the OpenUPM package registry: 22 | - Name: `OpenUPM` 23 | - URL: `https://package.openupm.com` 24 | - Scope(s) 25 | - `com.uralstech` 26 | 4. Open the Unity Package Manager window (`Window` -> `Package Manager`) 27 | 5. Change the registry from `Unity` to `My Registries` 28 | 6. Add the `UXR.QuestCamera` package 29 | 30 | ### Unity Package Manager 31 | 32 | 1. Open the Unity Package Manager window (`Window` -> `Package Manager`) 33 | 2. Select the `+` icon and `Add package from git URL...` 34 | 3. Paste the UPM branch URL and press enter: 35 | - `https://github.com/Uralstech/UXR.QuestCamera.git#upm` 36 | 4. Check the instructions for [`Utils.Singleton`](https://uralstech.github.io/Utils.Singleton) to install the dependency 37 | 38 | ### GitHub Clone 39 | 40 | 1. Clone or download the repository from the desired branch (master, preview/unstable) 41 | 2. Drag the package folder `UXR.QuestCamera/UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera` into your Unity project's `Packages` folder 42 | 3. Check the instructions for [`Utils.Singleton`](https://uralstech.github.io/Utils.Singleton) to install the dependency 43 | 44 | ## Preview Versions 45 | 46 | Do not use preview versions (i.e. versions that end with "-preview") for production use as they are unstable and untested. 47 | 48 | ## Documentation 49 | 50 | See or `APIReferenceManual.pdf` and `Documentation.pdf` in the package documentation for the reference manual and tutorial. 51 | -------------------------------------------------------------------------------- /Documentation/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | _layout: landing 3 | --- 4 | 5 | # UXR.QuestCamera 6 | 7 | A Unity package to use the new Meta Quest Passthrough Camera API. 8 | 9 | [![openupm](https://img.shields.io/npm/v/com.uralstech.uxr.questcamera?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.uralstech.uxr.questcamera/) 10 | [![openupm](https://img.shields.io/badge/dynamic/json?color=brightgreen&label=downloads&query=%24.downloads&suffix=%2Fmonth&url=https%3A%2F%2Fpackage.openupm.com%2Fdownloads%2Fpoint%2Flast-month%2Fcom.uralstech.uxr.questcamera)](https://openupm.com/packages/com.uralstech.uxr.questcamera/) 11 | 12 | ## Installation 13 | 14 | This *should* work on any reasonably modern Unity version. Built and tested in Unity 6.3. 15 | Versions older than Unity 6.0 **REQUIRE** [com.utilities.async](https://github.com/RageAgainstThePixel/com.utilities.async/). 16 | 17 | Since this package contains a native AAR plugin, it depends on the [External Dependency Manager for Unity (EDM4U)](https://github.com/googlesamples/unity-jar-resolver) to resolve native dependencies. 18 | You may already have EDM4U if you use Google or Firebase SDKs in your Unity project. If you do not, the installation steps are available 19 | here: [EDM4U - Getting Started](https://github.com/googlesamples/unity-jar-resolver?tab=readme-ov-file#getting-started) 20 | 21 | # [OpenUPM](#tab/openupm) 22 | 23 | 1. Open project settings 24 | 2. Select `Package Manager` 25 | 3. Add the OpenUPM package registry: 26 | - Name: `OpenUPM` 27 | - URL: `https://package.openupm.com` 28 | - Scope(s) 29 | - `com.uralstech` 30 | 4. Open the Unity Package Manager window (`Window` -> `Package Manager`) 31 | 5. Change the registry from `Unity` to `My Registries` 32 | 6. Add the `UXR.QuestCamera` package 33 | 34 | # [Unity Package Manager](#tab/upm) 35 | 36 | 1. Open the Unity Package Manager window (`Window` -> `Package Manager`) 37 | 2. Select the `+` icon and `Add package from git URL...` 38 | 3. Paste the UPM branch URL and press enter: 39 | - `https://github.com/Uralstech/UXR.QuestCamera.git#upm` 40 | 4. Check the instructions for [`Utils.Singleton`](https://uralstech.github.io/Utils.Singleton) to install the dependency 41 | 42 | # [GitHub Clone](#tab/github) 43 | 44 | 1. Clone or download the repository from the desired branch (master, preview/unstable) 45 | 2. Drag the package folder `UXR.QuestCamera/UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera` into your Unity project's `Packages` folder 46 | 3. Check the instructions for [`Utils.Singleton`](https://uralstech.github.io/Utils.Singleton) to install the dependency 47 | 48 | --- 49 | 50 | ## Preview Versions 51 | 52 | Do not use preview versions (i.e. versions that end with "-preview") for production use as they are unstable and untested. 53 | 54 | ## Documentation 55 | 56 | See or `APIReferenceManual.pdf` and `Documentation.pdf` in the package documentation for the reference manual and tutorial. 57 | -------------------------------------------------------------------------------- /UCamera/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Shaders/YUVConverter.compute: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #pragma kernel CSMain 16 | 17 | // Input buffers (read-only) 18 | ByteAddressBuffer YBuffer; 19 | ByteAddressBuffer UBuffer; 20 | ByteAddressBuffer VBuffer; 21 | 22 | // Row strides 23 | uint YRowStride; 24 | uint UVRowStride; 25 | 26 | // Pixel strides 27 | uint UVPixelStride; 28 | 29 | // Image dimensions 30 | uint TargetWidth; 31 | uint TargetHeight; 32 | 33 | // Output texture (read-write) 34 | RWTexture2D OutputTexture; 35 | 36 | // Helper function to get a byte from a ByteAddressBuffer. 37 | // buffer: The ByteAddressBuffer. 38 | // byteIndex: The *byte* index (offset) into the buffer. 39 | uint GetByteFromBuffer(ByteAddressBuffer buffer, uint byteIndex) 40 | { 41 | // Calculate the 32-bit word offset (each word is 4 bytes). 42 | uint wordOffset = byteIndex / 4; 43 | 44 | // Load the 32-bit word containing the byte. 45 | uint word = buffer.Load(wordOffset * 4); // MUST multiply by 4 for ByteAddressBuffer.Load() 46 | 47 | // Calculate the byte position *within* the word (0, 1, 2, or 3). 48 | uint byteInWord = byteIndex % 4; 49 | 50 | // Extract the correct byte using bit shifts and masking. 51 | return (word >> (byteInWord * 8)) & 0xFF; 52 | } 53 | 54 | // Helper function to convert YUV to RGB. 55 | float3 YUVtoRGB(uint y, uint u, uint v) 56 | { 57 | // ITU-R BT.601 standard convertion 58 | 59 | float yf = float(y) + 16.0; 60 | float uf = float(u) - 128.0; 61 | float vf = float(v) - 128.0; 62 | 63 | float3 rgb = float3( 64 | yf + 1.402 * vf, 65 | yf - 0.344136 * uf - 0.714136 * vf, 66 | yf + 1.772 * uf 67 | ); 68 | 69 | return saturate(rgb / 255.0); 70 | } 71 | 72 | [numthreads(8, 8, 1)] 73 | void CSMain(uint3 id : SV_DispatchThreadID) 74 | { 75 | if (id.x >= TargetWidth || id.y >= TargetHeight) 76 | return; 77 | 78 | // The YUV stream is flipped, so we have to un-flip it. 79 | uint flippedY = TargetHeight - 1 - id.y; 80 | 81 | // Index of Y value in buffer. 82 | uint yIndex = flippedY * YRowStride + id.x; 83 | 84 | // Index of the U and V values in the buffer. They are the same for YUV_420_888: 85 | // https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888 86 | uint uvIndex = (flippedY / 2) * UVRowStride + (id.x / 2) * UVPixelStride; 87 | 88 | // Get Y, U, and V values. 89 | uint yValue = GetByteFromBuffer(YBuffer, yIndex); 90 | uint uValue = GetByteFromBuffer(UBuffer, uvIndex); 91 | uint vValue = GetByteFromBuffer(VBuffer, uvIndex); 92 | 93 | // Convert them and set the output texture. 94 | float3 converted = YUVtoRGB(yValue, uValue, vValue); 95 | OutputTexture[id.xy] = float4(converted.rgb, 1.0); 96 | } -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/java/com/uralstech/ucamera/Camera2Wrapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.uralstech.ucamera 16 | 17 | import android.Manifest 18 | import android.content.Context 19 | import android.hardware.camera2.CameraAccessException 20 | import android.hardware.camera2.CameraManager 21 | import android.util.Log 22 | import androidx.annotation.RequiresPermission 23 | import com.unity3d.player.UnityPlayer 24 | 25 | /** 26 | * Script to manage Camera2 resources. 27 | */ 28 | class Camera2Wrapper { 29 | 30 | companion object { 31 | /** Logcat tag. */ 32 | private const val TAG = "Camera2Wrapper" 33 | 34 | @JvmStatic 35 | val instance: Camera2Wrapper by lazy { 36 | Camera2Wrapper() 37 | } 38 | } 39 | 40 | /** The application context. */ 41 | private val appContext = UnityPlayer.currentContext.applicationContext 42 | 43 | /** Detects, characterizes, and connects to a CameraDevice (used for all camera operations). */ 44 | private val cameraManager: CameraManager by lazy { 45 | appContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager 46 | } 47 | 48 | /** 49 | * Gets all available camera devices and their characteristics. 50 | */ 51 | fun getCameraDevices(): Array? { 52 | return try { 53 | Log.i(TAG, "Getting camera intrinsics.") 54 | 55 | val cameraIdList = cameraManager.cameraIdList 56 | val wrappers = mutableListOf() 57 | 58 | for (cameraId in cameraIdList) { 59 | try { 60 | val characteristics = cameraManager.getCameraCharacteristics(cameraId) 61 | wrappers.add(CameraCharacteristicsWrapper(cameraId, characteristics)) 62 | } catch (_: CameraAccessException) { } 63 | } 64 | 65 | wrappers.toTypedArray() 66 | } catch (exp: CameraAccessException) { 67 | Log.e(TAG, "Could not get camera characteristics due to a camera access exception.", exp) 68 | null 69 | } catch (exp: SecurityException) { 70 | Log.e(TAG, "Could not get camera characteristics due to a security exception.", exp) 71 | null 72 | } 73 | } 74 | 75 | /** 76 | * Creates a wrapper for a camera device, and tries to open it. 77 | * The returned wrapper may or may not have an open device, the 78 | * exact status of the device will be sent through the wrapper's 79 | * callbacks. 80 | */ 81 | @RequiresPermission(Manifest.permission.CAMERA) 82 | fun openCameraDevice(camera: String, callbacks: CameraDeviceWrapper.Callbacks): CameraDeviceWrapper { 83 | Log.i(TAG, "Opening camera device with ID \"$camera\"") 84 | return CameraDeviceWrapper(camera, callbacks, cameraManager) 85 | } 86 | } -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/java/com/uralstech/ucamera/CameraCharacteristicsWrapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.uralstech.ucamera 16 | 17 | import android.graphics.ImageFormat 18 | import android.hardware.camera2.CameraCharacteristics 19 | 20 | /** 21 | * Wrapper for [CameraCharacteristics]. 22 | */ 23 | class CameraCharacteristicsWrapper(val cameraId: String, val characteristics: CameraCharacteristics) { 24 | companion object { 25 | private const val META_CAMERA_SOURCE_METADATA = "com.meta.extra_metadata.camera_source" 26 | private const val META_CAMERA_POSITION_METADATA = "com.meta.extra_metadata.position" 27 | 28 | private val metaCameraSourceMetadata = CameraCharacteristics.Key(META_CAMERA_SOURCE_METADATA, Int::class.java) 29 | private val metaCameraPositionMetadata = CameraCharacteristics.Key(META_CAMERA_POSITION_METADATA, Int::class.java) 30 | } 31 | 32 | /** 33 | * (Meta Quest) The source of the camera feed. 34 | */ 35 | val metaQuestCameraSource = characteristics.get(metaCameraSourceMetadata) 36 | 37 | /** 38 | * (Meta Quest) The eye which the camera is closest to. 39 | */ 40 | val metaQuestCameraEye = characteristics.get(metaCameraPositionMetadata) 41 | 42 | /** 43 | * The position of the camera device's lens optical center. 44 | */ 45 | val lensPoseTranslation = characteristics.get(CameraCharacteristics.LENS_POSE_TRANSLATION) 46 | 47 | /** 48 | * The orientation of the camera relative to the sensor coordinate system. 49 | */ 50 | val lensPoseRotation = characteristics.get(CameraCharacteristics.LENS_POSE_ROTATION) 51 | 52 | /** 53 | * The resolutions supported by this device. 54 | */ 55 | val supportedResolutions = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)?.getOutputSizes(ImageFormat.YUV_420_888) ?: emptyArray() 56 | 57 | /** 58 | * The area of the image sensor which corresponds to active pixels prior to the application of any geometric distortion correction. 59 | */ 60 | val intrinsicsResolution = characteristics.get(CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE)?.let { sensorSize -> 61 | intArrayOf(sensorSize.right, sensorSize.bottom) 62 | } 63 | 64 | /** 65 | * The horizontal and vertical focal lengths, in pixels. 66 | */ 67 | val intrinsicsFocalLength: FloatArray? 68 | 69 | /** 70 | * Principal point in pixels from the image's top-left corner. 71 | */ 72 | val intrinsicsPrincipalPoint: FloatArray? 73 | 74 | /** 75 | * Skew coefficient for axis misalignment. 76 | */ 77 | val intrinsicsSkew: Float? 78 | 79 | init { 80 | val lensCalibration = characteristics.get(CameraCharacteristics.LENS_INTRINSIC_CALIBRATION) 81 | intrinsicsFocalLength = lensCalibration?.let { floatArrayOf(it[0], it[1]) } 82 | intrinsicsPrincipalPoint = lensCalibration?.let { floatArrayOf(it[2], it[3]) } 83 | intrinsicsSkew = lensCalibration?.get(4) 84 | } 85 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/External/CameraSupport.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) Meta Platforms, Inc. and affiliates. 2 | // All rights reserved. 3 | // 4 | // Licensed under the Oculus SDK License Agreement (the "License"); 5 | // you may not use the Oculus SDK except in compliance with the License, 6 | // which is provided at the time of installation or download, or which 7 | // otherwise accompanies this software in either electronic or hard copy form. 8 | // 9 | // You may obtain a copy of the License at 10 | // 11 | // https://developer.oculus.com/licenses/oculussdk/ 12 | // 13 | // Unless required by applicable law or agreed to in writing, the Oculus SDK 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #if META_XR_SDK_CORE 20 | using UnityEngine; 21 | 22 | #nullable enable 23 | namespace Uralstech.UXR.QuestCamera 24 | { 25 | /// 26 | /// Utility to check if the current Meta Quest device supports the Passthrough Camera API. 27 | /// 28 | /// 29 | /// Requires the Meta XR Core SDK. 30 | /// 31 | public static class CameraSupport 32 | { 33 | 34 | // The Horizon OS starts supporting PCA with v74. 35 | public const int MINSUPPORTOSVERSION = 74; 36 | 37 | private static bool? s_isSupported; 38 | private static int? s_horizonOsVersion; 39 | 40 | /// 41 | /// Get the Horizon OS version number on the headset 42 | /// 43 | /// 44 | /// Requires the Meta XR Core SDK. 45 | /// 46 | public static int? HorizonOSVersion 47 | { 48 | get 49 | { 50 | if (!s_horizonOsVersion.HasValue) 51 | { 52 | AndroidJavaClass vrosClass = new("vros.os.VrosBuild"); 53 | s_horizonOsVersion = vrosClass.CallStatic("getSdkVersion"); 54 | #if OVR_INTERNAL_CODE 55 | // 10000 means that the build doesn't have a proper release version, and it is still in Mainline, 56 | // not in a release branch. 57 | #endif // OVR_INTERNAL_CODE 58 | if (s_horizonOsVersion == 10000) 59 | { 60 | s_horizonOsVersion = -1; 61 | } 62 | } 63 | 64 | return s_horizonOsVersion.Value != -1 ? s_horizonOsVersion.Value : null; 65 | } 66 | } 67 | 68 | /// 69 | /// Returns true if the current headset supports Passthrough Camera API 70 | /// 71 | /// 72 | /// Requires the Meta XR Core SDK. 73 | /// 74 | public static bool IsSupported 75 | { 76 | get 77 | { 78 | if (!s_isSupported.HasValue) 79 | { 80 | OVRPlugin.SystemHeadset headset = OVRPlugin.GetSystemHeadsetType(); 81 | bool isSupported = (headset == OVRPlugin.SystemHeadset.Meta_Quest_3 || 82 | headset == OVRPlugin.SystemHeadset.Meta_Quest_3S) && 83 | (!HorizonOSVersion.HasValue || HorizonOSVersion >= MINSUPPORTOSVERSION); 84 | 85 | s_isSupported = isSupported; 86 | } 87 | 88 | return s_isSupported.Value; 89 | } 90 | } 91 | } 92 | } 93 | 94 | #endif -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/SceneTemplateSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "templatePinStates": [], 3 | "dependencyTypeInfos": [ 4 | { 5 | "userAdded": false, 6 | "type": "UnityEngine.AnimationClip", 7 | "defaultInstantiationMode": 0 8 | }, 9 | { 10 | "userAdded": false, 11 | "type": "UnityEditor.Animations.AnimatorController", 12 | "defaultInstantiationMode": 0 13 | }, 14 | { 15 | "userAdded": false, 16 | "type": "UnityEngine.AnimatorOverrideController", 17 | "defaultInstantiationMode": 0 18 | }, 19 | { 20 | "userAdded": false, 21 | "type": "UnityEditor.Audio.AudioMixerController", 22 | "defaultInstantiationMode": 0 23 | }, 24 | { 25 | "userAdded": false, 26 | "type": "UnityEngine.ComputeShader", 27 | "defaultInstantiationMode": 1 28 | }, 29 | { 30 | "userAdded": false, 31 | "type": "UnityEngine.Cubemap", 32 | "defaultInstantiationMode": 0 33 | }, 34 | { 35 | "userAdded": false, 36 | "type": "UnityEngine.GameObject", 37 | "defaultInstantiationMode": 0 38 | }, 39 | { 40 | "userAdded": false, 41 | "type": "UnityEditor.LightingDataAsset", 42 | "defaultInstantiationMode": 0 43 | }, 44 | { 45 | "userAdded": false, 46 | "type": "UnityEngine.LightingSettings", 47 | "defaultInstantiationMode": 0 48 | }, 49 | { 50 | "userAdded": false, 51 | "type": "UnityEngine.Material", 52 | "defaultInstantiationMode": 0 53 | }, 54 | { 55 | "userAdded": false, 56 | "type": "UnityEditor.MonoScript", 57 | "defaultInstantiationMode": 1 58 | }, 59 | { 60 | "userAdded": false, 61 | "type": "UnityEngine.PhysicsMaterial", 62 | "defaultInstantiationMode": 0 63 | }, 64 | { 65 | "userAdded": false, 66 | "type": "UnityEngine.PhysicsMaterial2D", 67 | "defaultInstantiationMode": 0 68 | }, 69 | { 70 | "userAdded": false, 71 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", 72 | "defaultInstantiationMode": 0 73 | }, 74 | { 75 | "userAdded": false, 76 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", 77 | "defaultInstantiationMode": 0 78 | }, 79 | { 80 | "userAdded": false, 81 | "type": "UnityEngine.Rendering.VolumeProfile", 82 | "defaultInstantiationMode": 0 83 | }, 84 | { 85 | "userAdded": false, 86 | "type": "UnityEditor.SceneAsset", 87 | "defaultInstantiationMode": 1 88 | }, 89 | { 90 | "userAdded": false, 91 | "type": "UnityEngine.Shader", 92 | "defaultInstantiationMode": 1 93 | }, 94 | { 95 | "userAdded": false, 96 | "type": "UnityEngine.ShaderVariantCollection", 97 | "defaultInstantiationMode": 1 98 | }, 99 | { 100 | "userAdded": false, 101 | "type": "UnityEngine.Texture", 102 | "defaultInstantiationMode": 0 103 | }, 104 | { 105 | "userAdded": false, 106 | "type": "UnityEngine.Texture2D", 107 | "defaultInstantiationMode": 0 108 | }, 109 | { 110 | "userAdded": false, 111 | "type": "UnityEngine.Timeline.TimelineAsset", 112 | "defaultInstantiationMode": 0 113 | } 114 | ], 115 | "defaultDependencyTypeInfo": { 116 | "userAdded": false, 117 | "type": "", 118 | "defaultInstantiationMode": 1 119 | }, 120 | "newSceneOverride": 0 121 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Extensions/TaskExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Threading.Tasks; 17 | using UnityEngine; 18 | 19 | #if !UNITY_6000_0_OR_NEWER 20 | using Utilities.Async; 21 | #endif 22 | 23 | #nullable enable 24 | namespace Uralstech.UXR.QuestCamera 25 | { 26 | /// 27 | /// Extensions for . 28 | /// 29 | public static class TaskExtensions 30 | { 31 | /// 32 | /// Adds a continuation to a task to log exceptions. 33 | /// 34 | public static void HandleAnyException(this Task current) 35 | { 36 | current.ContinueWith(static t => 37 | { 38 | foreach (Exception ex in t.Exception.Flatten().InnerExceptions) 39 | Debug.LogException(ex); 40 | }, TaskContinuationOptions.OnlyOnFaulted); 41 | } 42 | 43 | /// 44 | /// Invokes the current action on the main thread. 45 | /// 46 | public static async Task InvokeOnMainThread(this Action? current) 47 | { 48 | #if UNITY_6000_0_OR_NEWER 49 | await Awaitable.MainThreadAsync(); 50 | #else 51 | await Awaiters.UnityMainThread; 52 | #endif 53 | 54 | current?.Invoke(); 55 | } 56 | 57 | /// 58 | /// Invokes the current action on the main thread. 59 | /// 60 | public static async Task InvokeOnMainThread(this Action? current, T arg0) 61 | { 62 | #if UNITY_6000_0_OR_NEWER 63 | await Awaitable.MainThreadAsync(); 64 | #else 65 | await Awaiters.UnityMainThread; 66 | #endif 67 | 68 | current?.Invoke(arg0); 69 | } 70 | 71 | /// 72 | /// Invokes the current action on the main thread. 73 | /// 74 | public static async Task InvokeOnMainThread(this Action? current, T0 arg0, T1 arg1) 75 | { 76 | #if UNITY_6000_0_OR_NEWER 77 | await Awaitable.MainThreadAsync(); 78 | #else 79 | await Awaiters.UnityMainThread; 80 | #endif 81 | 82 | current?.Invoke(arg0, arg1); 83 | } 84 | 85 | /// 86 | /// Allows for "yielding" a using a object. 87 | /// 88 | public static WaitUntil Yield(this ValueTask current) => new(() => current.IsCompleted); 89 | 90 | /// 91 | /// Allows for "yielding" a using a object. 92 | /// 93 | public static WaitUntil Yield(this Task current) => new(() => current.IsCompleted); 94 | 95 | #if UNITY_6000_0_OR_NEWER 96 | /// 97 | /// 98 | public static WaitUntil Yield(this ValueTask current, TimeSpan timeout, Action onTimeout, WaitTimeoutMode timeoutMode = WaitTimeoutMode.Realtime) => 99 | new(() => current.IsCompleted, timeout, onTimeout, timeoutMode); 100 | 101 | /// 102 | /// 103 | public static WaitUntil Yield(this Task current, TimeSpan timeout, Action onTimeout, WaitTimeoutMode timeoutMode = WaitTimeoutMode.Realtime) => 104 | new(() => current.IsCompleted, timeout, onTimeout, timeoutMode); 105 | #endif 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/java/com/uralstech/ucamera/OnDemandCaptureSessionWrapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.uralstech.ucamera 16 | 17 | import android.graphics.SurfaceTexture 18 | import android.hardware.camera2.CameraAccessException 19 | import android.hardware.camera2.CameraCaptureSession 20 | import android.hardware.camera2.CameraDevice 21 | import android.hardware.camera2.params.OutputConfiguration 22 | import android.util.Log 23 | import android.view.Surface 24 | 25 | /** 26 | * Wrapper class for [CameraCaptureSession] meant for taking on-demand captures. 27 | * 28 | * It still runs a repeating preview stream in the background, so that when 29 | * a capture is requested, it is not pitch black due to it being the first 30 | * frame being captured. 31 | */ 32 | class OnDemandCaptureSessionWrapper( 33 | cameraDevice: CameraDevice, captureTemplate: Int, 34 | callbacks: Callbacks, width: Int, height: Int) : CaptureSessionWrapper(cameraDevice, captureTemplate, callbacks, width, height) { 35 | companion object { 36 | const val TAG = "ODCaptureSessionWrapper" 37 | } 38 | 39 | /** Dummy surface texture for preview capture request. */ 40 | private var dummySurfaceTexture: SurfaceTexture? = null 41 | 42 | /** Dummy surface for preview capture request. */ 43 | private var dummySurface: Surface? = null 44 | 45 | /** 46 | * Creates a new capture session and sets the repeating capture request. 47 | */ 48 | override fun startCaptureSession(camera: CameraDevice, captureTemplate: Int) { 49 | Log.i(TAG, "Setting up capture session for single-capture request.") 50 | 51 | val dummySurfaceTexture = SurfaceTexture(0) 52 | val dummySurface = Surface(dummySurfaceTexture) 53 | this.dummySurfaceTexture = dummySurfaceTexture 54 | this.dummySurface = dummySurface 55 | 56 | try { 57 | super.startRepeatingCaptureSession(camera, captureTemplate, 58 | listOf(OutputConfiguration(dummySurface), OutputConfiguration(imageReader.surface)), dummySurface) 59 | } catch (exp: Exception) { 60 | dummySurface.release() 61 | this.dummySurface = null 62 | 63 | dummySurfaceTexture.release() 64 | this.dummySurfaceTexture = null 65 | 66 | Log.e(TAG, "Failed to start repeating capture session, cleaned up dummy surfaces.", exp) 67 | throw exp 68 | } 69 | } 70 | 71 | /** 72 | * Sets a new non-repeating capture request. 73 | */ 74 | fun setSingleCaptureRequest(captureTemplate: Int): Boolean { 75 | val captureSession = this.captureSession 76 | if (isDisposed || captureSession == null) { 77 | Log.e(TAG, "Tried to set non-repeating capture request for unusable capture session.") 78 | return false 79 | } 80 | 81 | Log.i(TAG, "Setting non-repeating capture request.") 82 | 83 | try { 84 | val captureRequest = captureSession.device.createCaptureRequest(captureTemplate).apply { 85 | addTarget(imageReader.surface) 86 | }.build() 87 | 88 | captureSession.captureSingleRequest(captureRequest, captureSessionExecutor, object : CameraCaptureSession.CaptureCallback() { }) 89 | 90 | Log.i(TAG, "Non-repeating capture request set for camera session of camera with ID \"${captureSession.device.id}\".") 91 | return true 92 | } catch (exp: CameraAccessException) { 93 | Log.e(TAG, "Camera device with ID \"${captureSession.device.id}\" erred out with a camera access exception.", exp) 94 | return false 95 | } catch (exp: SecurityException) { 96 | Log.e(TAG, "Camera device with ID \"${captureSession.device.id}\" erred out with a security exception.", exp) 97 | return false 98 | } 99 | } 100 | 101 | /** 102 | * Same as [CaptureSessionWrapper.closeWork], but also releases [dummySurfaceTexture]. 103 | */ 104 | override fun closeWork() { 105 | super.closeWork() 106 | 107 | dummySurface?.release() 108 | dummySurface = null 109 | 110 | dummySurfaceTexture?.release() 111 | dummySurfaceTexture = null 112 | 113 | Log.i(TAG, "Dummy texture released.") 114 | } 115 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/SurfaceTextureCapture/OnDemandSurfaceTextureCaptureSession.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using System.Threading; 17 | using System.Threading.Tasks; 18 | using UnityEngine; 19 | using static Uralstech.UXR.QuestCamera.SurfaceTextureCapture.STCaptureSessionNative; 20 | 21 | #if !UNITY_6000_0_OR_NEWER 22 | using Utilities.Async; 23 | #endif 24 | 25 | #nullable enable 26 | namespace Uralstech.UXR.QuestCamera.SurfaceTextureCapture 27 | { 28 | /// 29 | /// On-demand version of . 30 | /// 31 | /// 32 | /// This experimental capture session uses a native OpenGL texture to capture images for better performance and 33 | /// requires OpenGL ES 3.0 as the project's graphics API. Works with single and multi-threaded rendering. 34 | /// 35 | public class OnDemandSurfaceTextureCaptureSession : SurfaceTextureCaptureSession 36 | { 37 | public OnDemandSurfaceTextureCaptureSession(Resolution resolution) : base(resolution) { } 38 | 39 | /// 40 | public override IntPtr Invoke(string methodName, IntPtr javaArgs) 41 | { 42 | if (methodName == "onCaptureCompleted") 43 | { 44 | CaptureTimestamp = JNIExtensions.UnboxLongElement(javaArgs, 0); 45 | return IntPtr.Zero; 46 | } 47 | 48 | return base.Invoke(methodName, javaArgs); 49 | } 50 | 51 | /// 52 | /// Has the capture session received its first frame? 53 | /// 54 | public bool HasFrame => _nativeTextureId != null && CaptureTimestamp != 0; 55 | 56 | /// 57 | /// Updates the unity texture with the latest capture from the camera. 58 | /// 59 | /// Called when the capture has been rendered in unity, with its timestamp. 60 | /// if the renderer was invoked, otherwise. 61 | public bool RequestCapture(Action onDone) 62 | { 63 | ThrowIfDisposed(); 64 | if (!HasFrame) 65 | return false; 66 | 67 | SendNativeUpdate(NativeEventId.RenderTextures, (_, result, timestamp) => 68 | { 69 | if (result) 70 | { 71 | GL.InvalidateState(); 72 | OnFrameReadyInvk(Texture, timestamp); 73 | onDone.InvokeOnMainThread(Texture, timestamp).HandleAnyException(); 74 | } 75 | }, CaptureTimestamp).HandleAnyException(); 76 | return true; 77 | } 78 | 79 | /// 80 | /// Updates the unity texture with the latest capture from the camera. 81 | /// 82 | /// Returns a WaitUntil operation if the renderer was invoked, otherwise. 83 | public WaitUntil? RequestCapture() 84 | { 85 | ThrowIfDisposed(); 86 | 87 | bool isDone = false; 88 | return RequestCapture((_, _) => isDone = true) 89 | ? new WaitUntil(() => isDone) : null; 90 | } 91 | 92 | #if UNITY_6000_0_OR_NEWER 93 | /// 94 | /// 95 | public WaitUntil? RequestCapture(TimeSpan timeout, Action onTimeout, WaitTimeoutMode timeoutMode = WaitTimeoutMode.Realtime) 96 | { 97 | ThrowIfDisposed(); 98 | 99 | bool isDone = false; 100 | return RequestCapture((_, _) => isDone = true) 101 | ? new WaitUntil(() => isDone, timeout, onTimeout, timeoutMode) : null; 102 | } 103 | #endif 104 | 105 | /// 106 | /// Updates the unity texture with the latest capture from the camera. 107 | /// 108 | /// The rendered texture and timestamp, or default values if the renderer could not be invoked. 109 | public async Task<(Texture2D?, long)> RequestCaptureAsync(CancellationToken token = default) 110 | { 111 | ThrowIfDisposed(); 112 | 113 | TaskCompletionSource<(Texture2D?, long)> tcs = new(); 114 | using (token.Register(tcs.SetCanceled)) 115 | { 116 | return RequestCapture((texture, timestamp) => tcs.SetResult((texture, timestamp))) 117 | ? await tcs.Task : (null, 0); 118 | } 119 | } 120 | 121 | private void ThrowIfDisposed() 122 | { 123 | if (Disposed) 124 | throw new ObjectDisposedException(nameof(OnDemandSurfaceTextureCaptureSession)); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Extensions/JNIExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using UnityEngine; 17 | 18 | #nullable enable 19 | namespace Uralstech.UXR.QuestCamera 20 | { 21 | /// 22 | /// QOL extensions for the JNI. 23 | /// 24 | public static class JNIExtensions 25 | { 26 | /// 27 | /// Unboxes and creates a global ref of a native ByteBuffer from a native Object array, and returns its direct buffer address. 28 | /// 29 | /// The native array to take the buffer from. 30 | /// The index of the buffer object in the native array. 31 | /// The global reference and the direct buffer address. 32 | public static unsafe (IntPtr obj, IntPtr ptr) UnboxAndCreateGlobalRefForByteBufferElement(IntPtr args, int index) 33 | { 34 | IntPtr localRef = AndroidJNI.GetObjectArrayElement(args, index); 35 | IntPtr globalRef = AndroidJNI.NewGlobalRef(localRef); 36 | AndroidJNI.DeleteLocalRef(localRef); 37 | 38 | return (globalRef, (IntPtr)AndroidJNI.GetDirectBufferAddress(globalRef)); 39 | } 40 | 41 | /// 42 | /// Unboxes an integer from a native Object array. 43 | /// 44 | /// The native array to take the integer from. 45 | /// The index of the integer object in the native array. 46 | /// The unboxed integer. 47 | public static int UnboxIntElement(IntPtr args, int index) 48 | { 49 | IntPtr ptr = AndroidJNI.GetObjectArrayElement(args, index); 50 | AndroidJNIHelper.Unbox(ptr, out int value); 51 | 52 | AndroidJNI.DeleteLocalRef(ptr); 53 | return value; 54 | } 55 | 56 | /// 57 | /// Unboxes a long from a native Object array. 58 | /// 59 | /// The native array to take the long from. 60 | /// The index of the long object in the native array. 61 | /// The unboxed long. 62 | public static long UnboxLongElement(IntPtr args, int index) 63 | { 64 | IntPtr ptr = AndroidJNI.GetObjectArrayElement(args, index); 65 | AndroidJNIHelper.Unbox(ptr, out long value); 66 | 67 | AndroidJNI.DeleteLocalRef(ptr); 68 | return value; 69 | } 70 | 71 | /// 72 | /// Unboxes a string from a native Object array. 73 | /// 74 | /// The native array to take the string from. 75 | /// The index of the string object in the native array. 76 | /// The unboxed string. 77 | public static string? UnboxStringElement(IntPtr args, int index) 78 | { 79 | IntPtr ptr = AndroidJNI.GetObjectArrayElement(args, index); 80 | string? value = AndroidJNI.GetStringUTFChars(ptr); 81 | 82 | AndroidJNI.DeleteLocalRef(ptr); 83 | return value; 84 | } 85 | 86 | /// 87 | /// Unboxes a boolean from a native Object array. 88 | /// 89 | /// The native array to take the boolean from. 90 | /// The index of the boolean object in the native array. 91 | /// The unboxed boolean. 92 | public static bool UnboxBoolElement(IntPtr args, int index) 93 | { 94 | IntPtr ptr = AndroidJNI.GetObjectArrayElement(args, index); 95 | AndroidJNIHelper.Unbox(ptr, out bool value); 96 | 97 | AndroidJNI.DeleteLocalRef(ptr); 98 | return value; 99 | } 100 | 101 | /// 102 | /// Unboxes a native nullable integer field into an int?. 103 | /// 104 | /// The field to unbox. 105 | /// The unboxed value. 106 | public static int? GetNullableInt(this AndroidJavaObject current, string fieldName) 107 | { 108 | using AndroidJavaObject? nullable = current.Get(fieldName); 109 | return nullable?.Call("intValue"); 110 | } 111 | 112 | /// 113 | /// Unboxes a native nullable float field into an float?. 114 | /// 115 | /// 116 | public static float? GetNullableFloat(this AndroidJavaObject current, string fieldName) 117 | { 118 | using AndroidJavaObject? nullable = current.Get(fieldName); 119 | return nullable?.Call("floatValue"); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | uralstech@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Documentation/DocSource/AdvancedSamples.md: -------------------------------------------------------------------------------- 1 | # Advanced Samples 2 | 3 | This page contains some samples for advanced use-cases, like custom texture converters or multi-camera streaming. 4 | 5 | ## Custom Texture Converters 6 | 7 | The texture converter in `CapturePipeline.TextureConverter` allows you to easily change the conversion compute shader to custom 8 | ones. All you have to do is set `CapturePipeline.TextureConverter.Shader` to your shader. You can also change the compute shader 9 | for all new capture sessions by changing `UCameraManager.YUVToRGBAComputeShader`. 10 | 11 | For example, the following compute shader ignores the U and V values of the YUV stream to provide a Luminance-only image: 12 | 13 | ```hlsl 14 | #pragma kernel CSMain 15 | 16 | // Input buffers (read-only) 17 | ByteAddressBuffer YBuffer; 18 | ByteAddressBuffer UBuffer; 19 | ByteAddressBuffer VBuffer; 20 | 21 | // Row strides 22 | uint YRowStride; 23 | uint UVRowStride; 24 | 25 | // Pixel strides 26 | uint UVPixelStride; 27 | 28 | // Image dimensions 29 | uint TargetWidth; 30 | uint TargetHeight; 31 | 32 | // Output texture (read-write) 33 | RWTexture2D OutputTexture; 34 | 35 | // Helper function to get a byte from a ByteAddressBuffer. 36 | // buffer: The ByteAddressBuffer. 37 | // byteIndex: The *byte* index (offset) into the buffer. 38 | uint GetByteFromBuffer(ByteAddressBuffer buffer, uint byteIndex) 39 | { 40 | // Calculate the 32-bit word offset (each word is 4 bytes). 41 | uint wordOffset = byteIndex / 4; 42 | 43 | // Load the 32-bit word containing the byte. 44 | uint word = buffer.Load(wordOffset * 4); // MUST multiply by 4 for ByteAddressBuffer.Load() 45 | 46 | // Calculate the byte position *within* the word (0, 1, 2, or 3). 47 | uint byteInWord = byteIndex % 4; 48 | 49 | // Extract the correct byte using bit shifts and masking. 50 | return (word >> (byteInWord * 8)) & 0xFF; 51 | } 52 | 53 | [numthreads(8, 8, 1)] 54 | void CSMain(uint3 id : SV_DispatchThreadID) 55 | { 56 | if (id.x >= TargetWidth || id.y >= TargetHeight) 57 | return; 58 | 59 | // The YUV stream is flipped, so we have to un-flip it. 60 | uint flippedY = TargetHeight - 1 - id.y; 61 | 62 | // Index of Y value in buffer. 63 | uint yIndex = flippedY * YRowStride + id.x; 64 | uint yValue = GetByteFromBuffer(YBuffer, yIndex); 65 | 66 | float3 luminance = float3(yValue, yValue, yValue) / 255.0; 67 | OutputTexture[id.xy] = float4(luminance.rgb, 1.0); 68 | } 69 | ``` 70 | 71 | ## Multiple Streams From One Camera 72 | 73 | By adding multiple texture converters to the same request, you can emulate the effect of having more than one image stream from a 74 | single camera. For example, you can have one converter stream the camera image as-is, and another streaming with a simple Sepia 75 | post-processing effect: 76 | 77 | ```csharp 78 | // Create a capture session with the camera, at the chosen resolution. 79 | CapturePipeline capturePipeline = camera.CreateContinuousCaptureSession(highestResolution); 80 | if (capturePipeline == null... 81 | 82 | yield return capturePipeline.CaptureSession.WaitForInitialization(); 83 | 84 | // Check if it opened successfully. 85 | if (capturePipeline.CaptureSession.CurrentState... 86 | 87 | // Set the image texture. 88 | _rawImage.texture = capturePipeline.TextureConverter.FrameRenderTexture; 89 | 90 | // Create a new YUVToRGBAConverter. 91 | YUVToRGBAConverter secondary = new YUVToRGBAConverter(highestResolution); 92 | 93 | // Assign it a different shader. 94 | secondary.Shader = _postProcessShader; 95 | 96 | // Link the capture session and the converter. 97 | capturePipeline.CaptureSession.OnFrameReady += secondary.OnFrameReady; 98 | 99 | // Set the second image to the post processed RenderTexture. 100 | _rawImagePostProcessed.texture = secondary.FrameRenderTexture; 101 | ``` 102 | 103 | ### YUV To RGBA Converter With Sepia Effect 104 | 105 | ```hlsl 106 | #pragma kernel CSMain 107 | 108 | // Input buffers (read-only) 109 | ByteAddressBuffer YBuffer; 110 | ByteAddressBuffer UBuffer; 111 | ByteAddressBuffer VBuffer; 112 | 113 | // Row strides 114 | uint YRowStride; 115 | uint UVRowStride; 116 | 117 | // Pixel strides 118 | uint UVPixelStride; 119 | 120 | // Image dimensions 121 | uint TargetWidth; 122 | uint TargetHeight; 123 | 124 | // Output texture (read-write) 125 | RWTexture2D OutputTexture; 126 | 127 | // Helper function to get a byte from a ByteAddressBuffer. 128 | // buffer: The ByteAddressBuffer. 129 | // byteIndex: The *byte* index (offset) into the buffer. 130 | uint GetByteFromBuffer(ByteAddressBuffer buffer, uint byteIndex) 131 | { 132 | // Calculate the 32-bit word offset (each word is 4 bytes). 133 | uint wordOffset = byteIndex / 4; 134 | 135 | // Load the 32-bit word containing the byte. 136 | uint word = buffer.Load(wordOffset * 4); // MUST multiply by 4 for ByteAddressBuffer.Load() 137 | 138 | // Calculate the byte position *within* the word (0, 1, 2, or 3). 139 | uint byteInWord = byteIndex % 4; 140 | 141 | // Extract the correct byte using bit shifts and masking. 142 | return (word >> (byteInWord * 8)) & 0xFF; 143 | } 144 | 145 | [numthreads(8, 8, 1)] 146 | void CSMain(uint3 id : SV_DispatchThreadID) 147 | { 148 | if (id.x >= TargetWidth || id.y >= TargetHeight) 149 | return; 150 | 151 | // The YUV stream is flipped, so we have to un-flip it. 152 | uint flippedY = TargetHeight - 1 - id.y; 153 | 154 | // Index of Y value in buffer. 155 | uint yIndex = flippedY * YRowStride + id.x; 156 | uint yValue = GetByteFromBuffer(YBuffer, yIndex); 157 | 158 | float3 luminance = float3(yValue, yValue, yValue) / 255.0; 159 | 160 | // --- Post-processing (Sepia Tone) --- 161 | float4 color = float4(luminance.rgb, 1.0); 162 | 163 | //Simple Sepia. Could also do a vignette, bloom, etc. here. 164 | float4 sepiaColor; 165 | sepiaColor.r = dot(color.rgb, float3(0.393, 0.769, 0.189)); 166 | sepiaColor.g = dot(color.rgb, float3(0.349, 0.686, 0.168)); 167 | sepiaColor.b = dot(color.rgb, float3(0.272, 0.534, 0.131)); 168 | sepiaColor.a = 1.0; 169 | 170 | OutputTexture[id.xy] = sepiaColor; 171 | } 172 | ``` 173 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/packages-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.google.external-dependency-manager": { 4 | "version": "1.2.186", 5 | "depth": 0, 6 | "source": "registry", 7 | "dependencies": {}, 8 | "url": "https://package.openupm.com" 9 | }, 10 | "com.unity.ai.inference": { 11 | "version": "2.3.0", 12 | "depth": 0, 13 | "source": "registry", 14 | "dependencies": { 15 | "com.unity.burst": "1.8.17", 16 | "com.unity.dt.app-ui": "1.3.1", 17 | "com.unity.collections": "2.4.3", 18 | "com.unity.modules.imageconversion": "1.0.0" 19 | }, 20 | "url": "https://packages.unity.com" 21 | }, 22 | "com.unity.burst": { 23 | "version": "1.8.26", 24 | "depth": 1, 25 | "source": "registry", 26 | "dependencies": { 27 | "com.unity.mathematics": "1.2.1", 28 | "com.unity.modules.jsonserialize": "1.0.0" 29 | }, 30 | "url": "https://packages.unity.com" 31 | }, 32 | "com.unity.collections": { 33 | "version": "2.6.2", 34 | "depth": 1, 35 | "source": "registry", 36 | "dependencies": { 37 | "com.unity.burst": "1.8.23", 38 | "com.unity.mathematics": "1.3.2", 39 | "com.unity.test-framework": "1.4.6", 40 | "com.unity.nuget.mono-cecil": "1.11.5", 41 | "com.unity.test-framework.performance": "3.0.3" 42 | }, 43 | "url": "https://packages.unity.com" 44 | }, 45 | "com.unity.dt.app-ui": { 46 | "version": "2.1.1", 47 | "depth": 1, 48 | "source": "registry", 49 | "dependencies": { 50 | "com.unity.modules.physics": "1.0.0", 51 | "com.unity.modules.androidjni": "1.0.0", 52 | "com.unity.modules.uielements": "1.0.0", 53 | "com.unity.modules.screencapture": "1.0.0" 54 | }, 55 | "url": "https://packages.unity.com" 56 | }, 57 | "com.unity.ext.nunit": { 58 | "version": "2.0.5", 59 | "depth": 2, 60 | "source": "builtin", 61 | "dependencies": {} 62 | }, 63 | "com.unity.ide.visualstudio": { 64 | "version": "2.0.25", 65 | "depth": 0, 66 | "source": "registry", 67 | "dependencies": { 68 | "com.unity.test-framework": "1.1.31" 69 | }, 70 | "url": "https://packages.unity.com" 71 | }, 72 | "com.unity.mathematics": { 73 | "version": "1.3.3", 74 | "depth": 2, 75 | "source": "registry", 76 | "dependencies": {}, 77 | "url": "https://packages.unity.com" 78 | }, 79 | "com.unity.mobile.android-logcat": { 80 | "version": "1.4.6", 81 | "depth": 0, 82 | "source": "registry", 83 | "dependencies": {}, 84 | "url": "https://packages.unity.com" 85 | }, 86 | "com.unity.nuget.mono-cecil": { 87 | "version": "1.11.6", 88 | "depth": 2, 89 | "source": "registry", 90 | "dependencies": {}, 91 | "url": "https://packages.unity.com" 92 | }, 93 | "com.unity.test-framework": { 94 | "version": "1.6.0", 95 | "depth": 1, 96 | "source": "builtin", 97 | "dependencies": { 98 | "com.unity.ext.nunit": "2.0.3", 99 | "com.unity.modules.imgui": "1.0.0", 100 | "com.unity.modules.jsonserialize": "1.0.0" 101 | } 102 | }, 103 | "com.unity.test-framework.performance": { 104 | "version": "3.2.0", 105 | "depth": 2, 106 | "source": "registry", 107 | "dependencies": { 108 | "com.unity.test-framework": "1.1.33", 109 | "com.unity.modules.jsonserialize": "1.0.0" 110 | }, 111 | "url": "https://packages.unity.com" 112 | }, 113 | "com.unity.ugui": { 114 | "version": "2.0.0", 115 | "depth": 0, 116 | "source": "builtin", 117 | "dependencies": { 118 | "com.unity.modules.ui": "1.0.0", 119 | "com.unity.modules.imgui": "1.0.0" 120 | } 121 | }, 122 | "com.uralstech.utils.singleton": { 123 | "version": "1.2.1", 124 | "depth": 1, 125 | "source": "registry", 126 | "dependencies": {}, 127 | "url": "https://package.openupm.com" 128 | }, 129 | "com.uralstech.uxr.questcamera": { 130 | "version": "file:com.uralstech.uxr.questcamera", 131 | "depth": 0, 132 | "source": "embedded", 133 | "dependencies": { 134 | "com.uralstech.utils.singleton": "1.2.1", 135 | "com.unity.modules.androidjni": "1.0.0" 136 | } 137 | }, 138 | "com.unity.modules.androidjni": { 139 | "version": "1.0.0", 140 | "depth": 1, 141 | "source": "builtin", 142 | "dependencies": {} 143 | }, 144 | "com.unity.modules.hierarchycore": { 145 | "version": "1.0.0", 146 | "depth": 3, 147 | "source": "builtin", 148 | "dependencies": {} 149 | }, 150 | "com.unity.modules.imageconversion": { 151 | "version": "1.0.0", 152 | "depth": 1, 153 | "source": "builtin", 154 | "dependencies": {} 155 | }, 156 | "com.unity.modules.imgui": { 157 | "version": "1.0.0", 158 | "depth": 1, 159 | "source": "builtin", 160 | "dependencies": {} 161 | }, 162 | "com.unity.modules.jsonserialize": { 163 | "version": "1.0.0", 164 | "depth": 2, 165 | "source": "builtin", 166 | "dependencies": {} 167 | }, 168 | "com.unity.modules.physics": { 169 | "version": "1.0.0", 170 | "depth": 2, 171 | "source": "builtin", 172 | "dependencies": {} 173 | }, 174 | "com.unity.modules.screencapture": { 175 | "version": "1.0.0", 176 | "depth": 2, 177 | "source": "builtin", 178 | "dependencies": { 179 | "com.unity.modules.imageconversion": "1.0.0" 180 | } 181 | }, 182 | "com.unity.modules.ui": { 183 | "version": "1.0.0", 184 | "depth": 1, 185 | "source": "builtin", 186 | "dependencies": {} 187 | }, 188 | "com.unity.modules.uielements": { 189 | "version": "1.0.0", 190 | "depth": 2, 191 | "source": "builtin", 192 | "dependencies": { 193 | "com.unity.modules.ui": "1.0.0", 194 | "com.unity.modules.imgui": "1.0.0", 195 | "com.unity.modules.jsonserialize": "1.0.0", 196 | "com.unity.modules.hierarchycore": "1.0.0", 197 | "com.unity.modules.physics": "1.0.0" 198 | } 199 | }, 200 | "com.unity.modules.unitywebrequest": { 201 | "version": "1.0.0", 202 | "depth": 0, 203 | "source": "builtin", 204 | "dependencies": {} 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /UCamera/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CameraInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using System; 16 | using UnityEngine; 17 | 18 | #nullable enable 19 | namespace Uralstech.UXR.QuestCamera 20 | { 21 | /// 22 | /// Wrapper for Camera2's CameraCharacteristics. 23 | /// 24 | public record CameraInfo : IDisposable 25 | { 26 | #region Enums 27 | /// 28 | /// The camera eye. 29 | /// 30 | public enum CameraEye 31 | { 32 | /// Unknown. 33 | Unknown = -1, 34 | 35 | /// The leftmost camera. 36 | Left = 0, 37 | 38 | /// The rightmost camera. 39 | Right = 1, 40 | } 41 | 42 | /// 43 | /// The source of the camera feed. 44 | /// 45 | public enum CameraSource 46 | { 47 | /// Unknown. 48 | Unknown = -1, 49 | 50 | /// Meta Quest Passthrough RGB cameras. 51 | PassthroughRGB = 0, 52 | } 53 | #endregion 54 | 55 | /// 56 | /// Defines the camera's intrinsic properties. All values are in pixels. 57 | /// 58 | public record CameraIntrinsics 59 | { 60 | /// Resolution in pixels. 61 | public readonly Vector2 Resolution; 62 | 63 | /// Focal length in pixels. 64 | public readonly Vector2 FocalLength; 65 | 66 | /// Principal point in pixels from the image's top-left corner. 67 | public readonly Vector2 PrincipalPoint; 68 | 69 | /// Skew coefficient for axis misalignment. 70 | public readonly float Skew; 71 | 72 | public CameraIntrinsics(Vector2 resolution, Vector2 focalLength, Vector2 principalPoint, float skew) 73 | { 74 | Resolution = resolution; 75 | FocalLength = focalLength; 76 | PrincipalPoint = principalPoint; 77 | Skew = skew; 78 | } 79 | } 80 | 81 | /// 82 | /// The actual device ID of this camera. 83 | /// 84 | public readonly string CameraId; 85 | 86 | /// 87 | /// (Meta Quest) The source of the camera feed. 88 | /// 89 | public readonly CameraSource Source; 90 | 91 | /// 92 | /// (Meta Quest) The eye which the camera is closest to. 93 | /// 94 | public readonly CameraEye Eye; 95 | 96 | /// 97 | /// The position of the camera's optical center. 98 | /// 99 | public readonly Vector3? LensPoseTranslation; 100 | 101 | /// 102 | /// The orientation of the camera relative to the sensor coordinate system. 103 | /// 104 | public readonly Quaternion? LensPoseRotation; 105 | 106 | /// 107 | /// The resolutions supported by this camera. 108 | /// 109 | public readonly Resolution[] SupportedResolutions; 110 | 111 | /// 112 | /// The intrinsic data for this camera. 113 | /// 114 | public readonly CameraIntrinsics? Intrinsics; 115 | 116 | /// 117 | /// The native CameraCharacteristics object. 118 | /// 119 | public readonly AndroidJavaObject NativeCameraCharacteristics; 120 | 121 | public CameraInfo(AndroidJavaObject cameraInfo) 122 | { 123 | CameraId = cameraInfo.Get("cameraId"); 124 | Source = cameraInfo.GetNullableInt("metaQuestCameraSource") is int source ? (CameraSource)source : CameraSource.Unknown; 125 | Eye = cameraInfo.GetNullableInt("metaQuestCameraEye") is int eye ? (CameraEye)eye : CameraEye.Unknown; 126 | LensPoseTranslation = cameraInfo.Get("lensPoseTranslation") is float[] translation 127 | ? new Vector3(translation[0], translation[1], -translation[2]) : null; 128 | LensPoseRotation = cameraInfo.Get("lensPoseRotation") is float[] rotation 129 | ? new Quaternion(-rotation[0], -rotation[1], rotation[2], rotation[3]) : null; 130 | SupportedResolutions = Array.ConvertAll(cameraInfo.Get("supportedResolutions"), size => 131 | { 132 | int width = size.Call("getWidth"); 133 | int height = size.Call("getHeight"); 134 | size.Dispose(); 135 | 136 | return new Resolution() 137 | { 138 | width = width, 139 | height = height 140 | }; 141 | }); 142 | 143 | if (cameraInfo.Call("getIntrinsicsResolution") is int[] intrinsicsResolution 144 | && cameraInfo.Get("intrinsicsFocalLength") is float[] intrinsicsFocalLength 145 | && cameraInfo.Get("intrinsicsPrincipalPoint") is float[] intrinsicsPrincipalPoint 146 | && cameraInfo.GetNullableFloat("intrinsicsSkew") is float intrinsicsSkew) 147 | { 148 | Intrinsics = new CameraIntrinsics( 149 | new Vector2(intrinsicsResolution[0], intrinsicsResolution[1]), 150 | new Vector2(intrinsicsFocalLength[0], intrinsicsFocalLength[1]), 151 | new Vector2(intrinsicsPrincipalPoint[0], intrinsicsPrincipalPoint[1]), 152 | intrinsicsSkew 153 | ); 154 | } 155 | 156 | NativeCameraCharacteristics = cameraInfo.Get("characteristics"); 157 | } 158 | 159 | public static implicit operator string(CameraInfo camera) => camera.CameraId; 160 | 161 | private bool _disposed = false; 162 | 163 | /// 164 | /// Releases native plugin resources. 165 | /// 166 | public void Dispose() 167 | { 168 | if (_disposed) 169 | return; 170 | 171 | _disposed = true; 172 | NativeCameraCharacteristics.Dispose(); 173 | GC.SuppressFinalize(this); 174 | } 175 | } 176 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!13 &1 4 | InputManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Axes: 8 | - serializedVersion: 3 9 | m_Name: Horizontal 10 | descriptiveName: 11 | descriptiveNegativeName: 12 | negativeButton: left 13 | positiveButton: right 14 | altNegativeButton: a 15 | altPositiveButton: d 16 | gravity: 3 17 | dead: 0.001 18 | sensitivity: 3 19 | snap: 1 20 | invert: 0 21 | type: 0 22 | axis: 0 23 | joyNum: 0 24 | - serializedVersion: 3 25 | m_Name: Vertical 26 | descriptiveName: 27 | descriptiveNegativeName: 28 | negativeButton: down 29 | positiveButton: up 30 | altNegativeButton: s 31 | altPositiveButton: w 32 | gravity: 3 33 | dead: 0.001 34 | sensitivity: 3 35 | snap: 1 36 | invert: 0 37 | type: 0 38 | axis: 0 39 | joyNum: 0 40 | - serializedVersion: 3 41 | m_Name: Fire1 42 | descriptiveName: 43 | descriptiveNegativeName: 44 | negativeButton: 45 | positiveButton: left ctrl 46 | altNegativeButton: 47 | altPositiveButton: mouse 0 48 | gravity: 1000 49 | dead: 0.001 50 | sensitivity: 1000 51 | snap: 0 52 | invert: 0 53 | type: 0 54 | axis: 0 55 | joyNum: 0 56 | - serializedVersion: 3 57 | m_Name: Fire2 58 | descriptiveName: 59 | descriptiveNegativeName: 60 | negativeButton: 61 | positiveButton: left alt 62 | altNegativeButton: 63 | altPositiveButton: mouse 1 64 | gravity: 1000 65 | dead: 0.001 66 | sensitivity: 1000 67 | snap: 0 68 | invert: 0 69 | type: 0 70 | axis: 0 71 | joyNum: 0 72 | - serializedVersion: 3 73 | m_Name: Fire3 74 | descriptiveName: 75 | descriptiveNegativeName: 76 | negativeButton: 77 | positiveButton: left shift 78 | altNegativeButton: 79 | altPositiveButton: mouse 2 80 | gravity: 1000 81 | dead: 0.001 82 | sensitivity: 1000 83 | snap: 0 84 | invert: 0 85 | type: 0 86 | axis: 0 87 | joyNum: 0 88 | - serializedVersion: 3 89 | m_Name: Jump 90 | descriptiveName: 91 | descriptiveNegativeName: 92 | negativeButton: 93 | positiveButton: space 94 | altNegativeButton: 95 | altPositiveButton: 96 | gravity: 1000 97 | dead: 0.001 98 | sensitivity: 1000 99 | snap: 0 100 | invert: 0 101 | type: 0 102 | axis: 0 103 | joyNum: 0 104 | - serializedVersion: 3 105 | m_Name: Mouse X 106 | descriptiveName: 107 | descriptiveNegativeName: 108 | negativeButton: 109 | positiveButton: 110 | altNegativeButton: 111 | altPositiveButton: 112 | gravity: 0 113 | dead: 0 114 | sensitivity: 0.1 115 | snap: 0 116 | invert: 0 117 | type: 1 118 | axis: 0 119 | joyNum: 0 120 | - serializedVersion: 3 121 | m_Name: Mouse Y 122 | descriptiveName: 123 | descriptiveNegativeName: 124 | negativeButton: 125 | positiveButton: 126 | altNegativeButton: 127 | altPositiveButton: 128 | gravity: 0 129 | dead: 0 130 | sensitivity: 0.1 131 | snap: 0 132 | invert: 0 133 | type: 1 134 | axis: 1 135 | joyNum: 0 136 | - serializedVersion: 3 137 | m_Name: Mouse ScrollWheel 138 | descriptiveName: 139 | descriptiveNegativeName: 140 | negativeButton: 141 | positiveButton: 142 | altNegativeButton: 143 | altPositiveButton: 144 | gravity: 0 145 | dead: 0 146 | sensitivity: 0.1 147 | snap: 0 148 | invert: 0 149 | type: 1 150 | axis: 2 151 | joyNum: 0 152 | - serializedVersion: 3 153 | m_Name: Horizontal 154 | descriptiveName: 155 | descriptiveNegativeName: 156 | negativeButton: 157 | positiveButton: 158 | altNegativeButton: 159 | altPositiveButton: 160 | gravity: 0 161 | dead: 0.19 162 | sensitivity: 1 163 | snap: 0 164 | invert: 0 165 | type: 2 166 | axis: 0 167 | joyNum: 0 168 | - serializedVersion: 3 169 | m_Name: Vertical 170 | descriptiveName: 171 | descriptiveNegativeName: 172 | negativeButton: 173 | positiveButton: 174 | altNegativeButton: 175 | altPositiveButton: 176 | gravity: 0 177 | dead: 0.19 178 | sensitivity: 1 179 | snap: 0 180 | invert: 1 181 | type: 2 182 | axis: 1 183 | joyNum: 0 184 | - serializedVersion: 3 185 | m_Name: Fire1 186 | descriptiveName: 187 | descriptiveNegativeName: 188 | negativeButton: 189 | positiveButton: joystick button 0 190 | altNegativeButton: 191 | altPositiveButton: 192 | gravity: 1000 193 | dead: 0.001 194 | sensitivity: 1000 195 | snap: 0 196 | invert: 0 197 | type: 0 198 | axis: 0 199 | joyNum: 0 200 | - serializedVersion: 3 201 | m_Name: Fire2 202 | descriptiveName: 203 | descriptiveNegativeName: 204 | negativeButton: 205 | positiveButton: joystick button 1 206 | altNegativeButton: 207 | altPositiveButton: 208 | gravity: 1000 209 | dead: 0.001 210 | sensitivity: 1000 211 | snap: 0 212 | invert: 0 213 | type: 0 214 | axis: 0 215 | joyNum: 0 216 | - serializedVersion: 3 217 | m_Name: Fire3 218 | descriptiveName: 219 | descriptiveNegativeName: 220 | negativeButton: 221 | positiveButton: joystick button 2 222 | altNegativeButton: 223 | altPositiveButton: 224 | gravity: 1000 225 | dead: 0.001 226 | sensitivity: 1000 227 | snap: 0 228 | invert: 0 229 | type: 0 230 | axis: 0 231 | joyNum: 0 232 | - serializedVersion: 3 233 | m_Name: Jump 234 | descriptiveName: 235 | descriptiveNegativeName: 236 | negativeButton: 237 | positiveButton: joystick button 3 238 | altNegativeButton: 239 | altPositiveButton: 240 | gravity: 1000 241 | dead: 0.001 242 | sensitivity: 1000 243 | snap: 0 244 | invert: 0 245 | type: 0 246 | axis: 0 247 | joyNum: 0 248 | - serializedVersion: 3 249 | m_Name: Submit 250 | descriptiveName: 251 | descriptiveNegativeName: 252 | negativeButton: 253 | positiveButton: return 254 | altNegativeButton: 255 | altPositiveButton: joystick button 0 256 | gravity: 1000 257 | dead: 0.001 258 | sensitivity: 1000 259 | snap: 0 260 | invert: 0 261 | type: 0 262 | axis: 0 263 | joyNum: 0 264 | - serializedVersion: 3 265 | m_Name: Submit 266 | descriptiveName: 267 | descriptiveNegativeName: 268 | negativeButton: 269 | positiveButton: enter 270 | altNegativeButton: 271 | altPositiveButton: space 272 | gravity: 1000 273 | dead: 0.001 274 | sensitivity: 1000 275 | snap: 0 276 | invert: 0 277 | type: 0 278 | axis: 0 279 | joyNum: 0 280 | - serializedVersion: 3 281 | m_Name: Cancel 282 | descriptiveName: 283 | descriptiveNegativeName: 284 | negativeButton: 285 | positiveButton: escape 286 | altNegativeButton: 287 | altPositiveButton: joystick button 1 288 | gravity: 1000 289 | dead: 0.001 290 | sensitivity: 1000 291 | snap: 0 292 | invert: 0 293 | type: 0 294 | axis: 0 295 | joyNum: 0 296 | -------------------------------------------------------------------------------- /UCamera/UCamera/src/main/java/com/uralstech/ucamera/CameraDeviceWrapper.kt: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.uralstech.ucamera 16 | 17 | import android.Manifest 18 | import android.hardware.camera2.CameraAccessException 19 | import android.hardware.camera2.CameraDevice 20 | import android.hardware.camera2.CameraManager 21 | import android.util.Log 22 | import androidx.annotation.RequiresPermission 23 | import java.util.concurrent.Executors 24 | 25 | /** 26 | * Wrapper class for [CameraDevice]. 27 | */ 28 | class CameraDeviceWrapper private constructor(private val id: String, private val callbacks: Callbacks) { 29 | interface Callbacks { 30 | fun onDeviceOpened(id: String) 31 | fun onDeviceClosed(id: String?) 32 | fun onDeviceErred(id: String?, errorCode: Int) 33 | fun onDeviceDisconnected(id: String) 34 | } 35 | 36 | companion object { 37 | /** Logcat tag. */ 38 | private const val TAG = "CameraDeviceWrapper" 39 | 40 | private const val ERROR_CODE_CAMERA_ACCESS_EXCEPTION = 1000 41 | private const val ERROR_CODE_SECURITY_EXCEPTION = 1001 42 | } 43 | 44 | /** Is this object active and usable? */ 45 | @Volatile 46 | private var isDisposed = false 47 | 48 | /** [java.util.concurrent.ExecutorService] for [cameraDevice]. */ 49 | private val cameraExecutor = Executors.newSingleThreadExecutor() 50 | 51 | /** The camera device being wrapped by this object. */ 52 | private var cameraDevice: CameraDevice? = null 53 | 54 | @RequiresPermission(Manifest.permission.CAMERA) 55 | constructor(id: String, callbacks: Callbacks, cameraManager: CameraManager) : this(id, callbacks) { 56 | cameraExecutor.submit { 57 | try { 58 | cameraManager.openCamera(id, cameraExecutor, object : CameraDevice.StateCallback() { 59 | override fun onOpened(camera: CameraDevice) { 60 | Log.i(TAG, "Camera device with ID \"$id\" opened.") 61 | cameraDevice = camera 62 | 63 | callbacks.onDeviceOpened(camera.id) 64 | } 65 | 66 | override fun onClosed(camera: CameraDevice) { 67 | Log.i(TAG, "Camera device with ID \"$id\" closed.") 68 | callbacks.onDeviceClosed(camera.id) 69 | } 70 | 71 | override fun onDisconnected(camera: CameraDevice) { 72 | Log.i(TAG, "Camera device with ID \"$id\" disconnected.") 73 | close() 74 | 75 | callbacks.onDeviceDisconnected(camera.id) 76 | } 77 | 78 | override fun onError(camera: CameraDevice, error: Int) { 79 | Log.i(TAG, "Camera device with ID \"$id\" erred out, code: $error") 80 | close() 81 | 82 | callbacks.onDeviceErred(camera.id, error) 83 | } 84 | }) 85 | } catch (exp: CameraAccessException) { 86 | Log.e(TAG, "Camera could not be opened due to a camera access exception.", exp) 87 | close() 88 | 89 | callbacks.onDeviceErred(null, ERROR_CODE_CAMERA_ACCESS_EXCEPTION) 90 | } catch (exp: SecurityException) { 91 | Log.e(TAG, "Camera could not be opened due to a security exception.", exp) 92 | close() 93 | 94 | callbacks.onDeviceErred(null, ERROR_CODE_SECURITY_EXCEPTION) 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * Gets the [CameraDevice] this object is wrapping, or logs an error and returns null if it was not found. 101 | */ 102 | private fun getActiveDevice(): CameraDevice? { 103 | if (isDisposed || cameraDevice == null) { 104 | Log.e(TAG, "Tried to use an unusable CameraDeviceWrapper for camera ID \"$id\"!") 105 | return null 106 | } 107 | return cameraDevice 108 | } 109 | 110 | /** 111 | * Creates a new capture session with a repeating capture request and a wrapper for it. 112 | */ 113 | fun createContinuousCaptureSession( 114 | callbacks: CaptureSessionWrapper.Callbacks, 115 | width: Int, height: Int, 116 | captureTemplate: Int): RepeatingCaptureSessionWrapper? { 117 | 118 | val cameraDevice = getActiveDevice() ?: return null 119 | Log.i(TAG, "Creating new repeating camera session for camera with ID \"$id\".") 120 | return RepeatingCaptureSessionWrapper(cameraDevice, captureTemplate, callbacks, width, height) 121 | } 122 | 123 | /** 124 | * Creates a new on-demand capture session and a wrapper for it. 125 | */ 126 | fun createOnDemandCaptureSession( 127 | callbacks: CaptureSessionWrapper.Callbacks, 128 | width: Int, height: Int): OnDemandCaptureSessionWrapper? { 129 | 130 | val cameraDevice = getActiveDevice() ?: return null 131 | Log.i(TAG, "Creating new on-demand camera session for camera with ID \"$id\".") 132 | return OnDemandCaptureSessionWrapper(cameraDevice, CameraDevice.TEMPLATE_PREVIEW, callbacks, width, height) 133 | } 134 | 135 | /** 136 | * Creates a new SurfaceTexture-based capture session and a wrapper for it. 137 | */ 138 | fun createSurfaceTextureCaptureSession( 139 | timeStamp: Long, callbacks: STCaptureSessionWrapper.Callbacks, 140 | width: Int, height: Int, 141 | captureTemplate: Int): STCaptureSessionWrapper? { 142 | 143 | val cameraDevice = getActiveDevice() ?: return null 144 | Log.i(TAG, "Creating new SurfaceTexture-based camera session for camera with ID \"$id\".") 145 | 146 | val session = STCaptureSessionWrapper(timeStamp, callbacks, cameraDevice, width, height, captureTemplate) 147 | if (!session.tryRegister()) { 148 | session.close() 149 | return null 150 | } 151 | 152 | return session 153 | } 154 | 155 | /** 156 | * Releases associated resources and closes the camera device. 157 | */ 158 | fun close(): Boolean { 159 | if (isDisposed) { 160 | return false 161 | } 162 | 163 | Log.i(TAG, "Closing camera device wrapper for camera with ID \"$id\".") 164 | isDisposed = true 165 | 166 | cameraExecutor.submit { 167 | try { 168 | if (cameraDevice != null) { 169 | cameraDevice?.close() 170 | cameraDevice = null 171 | } else { 172 | callbacks.onDeviceClosed(null) 173 | } 174 | 175 | Log.i(TAG, "Camera device closed.") 176 | } finally { 177 | cameraExecutor.shutdown() 178 | } 179 | } 180 | 181 | return true 182 | } 183 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/Managers/UCameraManager.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using UnityEngine; 16 | using Uralstech.Utils.Singleton; 17 | 18 | #nullable enable 19 | namespace Uralstech.UXR.QuestCamera 20 | { 21 | /// 22 | /// Class for interfacing with the native Camera2 API on Android. 23 | /// 24 | [AddComponentMenu("Uralstech/UXR/Quest Camera/Quest Camera Manager")] 25 | public class UCameraManager : DontCreateNewSingleton 26 | { 27 | /// 28 | /// The permission required to access the Meta Quest's Passthrough cameras. 29 | /// 30 | public const string HeadsetCameraPermission = "horizonos.permission.HEADSET_CAMERA"; 31 | 32 | /// 33 | /// The permission required to access the Meta Quest's avatar camera. 34 | /// 35 | public const string AvatarCameraPermission = "android.permission.CAMERA"; 36 | 37 | /// 38 | /// The compute shader to use to convert the camera's YUV 4:2:0 images to RGBA. 39 | /// 40 | [Tooltip("The compute shader to use to convert the camera's YUV 4:2:0 images to RGBA.")] 41 | public ComputeShader YUVToRGBAComputeShader; 42 | 43 | /// 44 | /// Gets a cached array of the available cameras and their characteristics. 45 | /// 46 | /// 47 | /// The disposal of the objects generated by this property 48 | /// are managed by the instance. If you require control 49 | /// of the objects, use instead. 50 | /// 51 | public CameraInfo[]? Cameras => _cameraInfosCached is not null ? _cameraInfosCached : (_cameraInfosCached = GetCameraInfos()); 52 | 53 | private CameraInfo[]? _cameraInfosCached = null; 54 | private AndroidJavaObject? _camera2Wrapper; 55 | 56 | protected override void Awake() 57 | { 58 | base.Awake(); 59 | DontDestroyOnLoad(gameObject); 60 | 61 | using AndroidJavaClass camera2WrapperClass = new("com.uralstech.ucamera.Camera2Wrapper"); 62 | _camera2Wrapper = camera2WrapperClass.CallStatic("getInstance"); 63 | } 64 | 65 | protected void OnDestroy() 66 | { 67 | ClearAndDisposeCameraInfos(); 68 | _camera2Wrapper?.Dispose(); 69 | _camera2Wrapper = null; 70 | } 71 | 72 | /// 73 | /// Refreshes the cached array of camera devices. 74 | /// 75 | /// if the refresh was successful; otherwise. 76 | public bool RefreshCachedCameraInfos() 77 | { 78 | ClearAndDisposeCameraInfos(); 79 | return (_cameraInfosCached = GetCameraInfos()) != null; 80 | } 81 | 82 | private void ClearAndDisposeCameraInfos() 83 | { 84 | if (_cameraInfosCached is not null) 85 | { 86 | int cameraInfoCount = _cameraInfosCached.Length; 87 | for (int i = 0; i < cameraInfoCount; i++) 88 | _cameraInfosCached[i].Dispose(); 89 | 90 | _cameraInfosCached = null; 91 | } 92 | } 93 | 94 | /// 95 | /// Gets all available cameras and their characteristics. This is not cached. 96 | /// 97 | /// 98 | /// You will have to manage the disposal of the objects 99 | /// returned by this method. Use if you don't want to handle 100 | /// the objects yourself. 101 | /// 102 | /// An array of objects or if any errors occurred. 103 | public CameraInfo[]? GetCameraInfos() 104 | { 105 | AndroidJavaObject[]? nativeObjects = _camera2Wrapper?.Call("getCameraDevices"); 106 | if (nativeObjects is null) 107 | { 108 | Debug.LogError("Could not get camera device information."); 109 | return null; 110 | } 111 | 112 | int count = nativeObjects.Length; 113 | CameraInfo[] wrappers = new CameraInfo[count]; 114 | 115 | for (int i = 0; i < count; i++) 116 | { 117 | AndroidJavaObject nativeObj = nativeObjects[i]; 118 | wrappers[i] = new CameraInfo(nativeObj); 119 | nativeObj.Dispose(); 120 | } 121 | 122 | return wrappers; 123 | } 124 | 125 | /// 126 | /// Gets a camera device by the eye it is closest to. 127 | /// 128 | /// 129 | /// The object returned by this method is managed by the 130 | /// instance, so do not dispose it manually. 131 | /// 132 | /// The eye. 133 | /// A object or if none were found. 134 | public CameraInfo? GetCamera(CameraInfo.CameraEye eye) 135 | { 136 | if (Cameras is CameraInfo[] cameras) 137 | { 138 | foreach (CameraInfo cameraInfo in cameras) 139 | { 140 | if (cameraInfo.Eye == eye) 141 | return cameraInfo; 142 | } 143 | } 144 | 145 | return null; 146 | } 147 | 148 | /// 149 | /// Opens a camera device for use. 150 | /// 151 | /// 152 | /// Once you have finished using the camera, close and dispose of it using . 153 | /// 154 | /// The ID of the camera to open; accepts objects through an implicit cast. 155 | /// A object or if any errors occurred. 156 | public CameraDevice? OpenCamera(string camera) 157 | { 158 | CameraDevice cameraDevice = new(camera); 159 | AndroidJavaObject? nativeObject = _camera2Wrapper?.Call("openCameraDevice", camera, cameraDevice); 160 | if (nativeObject is null) 161 | { 162 | StartCoroutine(cameraDevice.DisposeAsync().Yield()); 163 | return null; 164 | } 165 | 166 | cameraDevice._cameraDevice = nativeObject; 167 | return cameraDevice; 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/Packages/com.uralstech.uxr.questcamera/Runtime/Scripts/CaptureSession/SurfaceTextureCapture/STCaptureSessionNative.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 URAV ADVANCED LEARNING SYSTEMS PRIVATE LIMITED 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | using AOT; 16 | using System; 17 | using System.Collections.Concurrent; 18 | using System.Runtime.InteropServices; 19 | 20 | #nullable enable 21 | namespace Uralstech.UXR.QuestCamera.SurfaceTextureCapture 22 | { 23 | /// 24 | /// Class to interact with the native graphics plugin for SurfaceTexture rendering. 25 | /// 26 | public static class STCaptureSessionNative 27 | { 28 | /// 29 | /// Data for setting up a native renderer. 30 | /// 31 | [StructLayout(LayoutKind.Sequential)] 32 | public struct NativeSetupData 33 | { 34 | /// 35 | /// The unity texture to render to. 36 | /// 37 | public uint UnityTexture; 38 | 39 | /// 40 | /// The width of the texture; 41 | /// 42 | public int Width; 43 | 44 | /// 45 | /// The height of the texture. 46 | /// 47 | public int Height; 48 | 49 | /// 50 | /// Timestamp associated with the STCaptureSessionWrapper which will be the source for rendering. 51 | /// 52 | public long Timestamp; 53 | 54 | /// 55 | /// Callback for when the operation is done, type: . 56 | /// 57 | public IntPtr OnDoneCallback; 58 | } 59 | 60 | /// 61 | /// Data for updating a native renderer. 62 | /// 63 | [StructLayout(LayoutKind.Sequential)] 64 | public struct NativeUpdateData 65 | { 66 | /// 67 | /// The native texture to update. 68 | /// 69 | public uint NativeTexture; 70 | 71 | /// 72 | /// Callback for when the operation is done, type: . 73 | /// 74 | public IntPtr OnDoneCallback; 75 | } 76 | 77 | /// 78 | /// Gets the pointer to the native render event handler. 79 | /// 80 | [DllImport("NativeTextureHelper")] 81 | public static extern IntPtr GetRenderEventFunction(); 82 | 83 | /// 84 | /// Event ID for native rendering events. 85 | /// 86 | public enum NativeEventId 87 | { 88 | /// 89 | /// Sets up a native renderer. 90 | /// 91 | SetupNativeTexture = 1, 92 | 93 | /// 94 | /// Disposes a native renderer. 95 | /// 96 | CleanupNativeTexture = 2, 97 | 98 | /// 99 | /// Renders the textures. 100 | /// 101 | RenderTextures = 3, 102 | } 103 | 104 | /// 105 | /// Callback type for and events. 106 | /// 107 | /// The ID of the native texture which was updated. 108 | /// If the operation was successful. 109 | public delegate void NativeUpdateCallbackType(uint textureId, bool success); 110 | 111 | /// 112 | /// Same as , but can include a timestamp tracked from C#. 113 | /// 114 | /// The timestamp tracked from C#. 115 | /// 116 | public delegate void NativeUpdateCallbackWithTimestampType(uint textureId, bool success, long timestamp); 117 | 118 | /// 119 | /// Additional data tracked in C# related to a native renderer update event. 120 | /// 121 | public readonly struct AdditionalUpdateCallbackData 122 | { 123 | /// 124 | /// Optional callback that should be called after processing for the current native callback is done. 125 | /// 126 | public readonly NativeUpdateCallbackWithTimestampType? NextCall; 127 | 128 | /// 129 | /// Native data that should be disposed as part of this callback. 130 | /// 131 | public readonly IntPtr NativeData; 132 | 133 | /// 134 | /// Timestamp value which will be provided in . 135 | /// 136 | public readonly long Timestamp; 137 | 138 | public AdditionalUpdateCallbackData(NativeUpdateCallbackWithTimestampType? nextCall, IntPtr nativeData, long timestamp) 139 | { 140 | NextCall = nextCall; 141 | NativeData = nativeData; 142 | Timestamp = timestamp; 143 | } 144 | } 145 | 146 | /// 147 | /// Event queues for update events, mapped to the IDs of the native textures they are for. 148 | /// 149 | public static readonly ConcurrentDictionary> NativeUpdateCallbacksQueue = new(); 150 | 151 | /// 152 | /// The actual callback for native rendering updates. 153 | /// 154 | /// 155 | /// This will dequeue from and process the callback data. 156 | /// 157 | /// 158 | [MonoPInvokeCallback(typeof(NativeUpdateCallbackType))] 159 | public static void NativeUpdateCallback(uint textureId, bool success) 160 | { 161 | if (NativeUpdateCallbacksQueue.TryGetValue(textureId, out ConcurrentQueue queue) 162 | && queue.TryDequeue(out AdditionalUpdateCallbackData data)) 163 | { 164 | if (data.NativeData != IntPtr.Zero) 165 | Marshal.FreeHGlobal(data.NativeData); 166 | 167 | data.NextCall?.Invoke(textureId, success, data.Timestamp); 168 | } 169 | } 170 | 171 | /// 172 | /// Deregisters a native update queue for a texture and disposes allocated data. 173 | /// 174 | /// The ID of the native texture to deregister updates of. 175 | public static void DeregisterNativeUpdateCallbacks(uint textureId) 176 | { 177 | if (NativeUpdateCallbacksQueue.TryRemove(textureId, out ConcurrentQueue queue)) 178 | { 179 | while (queue.TryDequeue(out AdditionalUpdateCallbackData data)) 180 | { 181 | if (data.NativeData != IntPtr.Zero) 182 | Marshal.FreeHGlobal(data.NativeData); 183 | } 184 | } 185 | } 186 | 187 | /// 188 | /// Callback type for events. 189 | /// 190 | /// Was the GL context successfully cleaned up in this call? 191 | /// Was the call to start the capture session sent to the Kotlin class? 192 | /// The unity texture associated with the event. 193 | /// The native texture created by the call, may be invalid. 194 | /// Is a valid texture? 195 | public delegate void NativeSetupCallbackType(bool glIsClean, bool sessionCallSent, uint unityTextureId, uint textureId, bool idIsValid); 196 | 197 | /// 198 | /// Event queues for renderer setup events, mapped to the IDs of the unity textures they are for. 199 | /// 200 | public static readonly ConcurrentDictionary NativeSetupCallbacksQueue = new(); 201 | 202 | /// 203 | /// The actual callback for native renderer setup events. 204 | /// 205 | /// 206 | /// This will dequeue from and process the callbacks. 207 | /// 208 | /// 209 | [MonoPInvokeCallback(typeof(NativeSetupCallbackType))] 210 | public static void NativeSetupCallback(bool glIsClean, bool sessionCallSent, uint unityTextureId, uint textureId, bool idIsValid) 211 | { 212 | if (NativeSetupCallbacksQueue.TryRemove(unityTextureId, out NativeSetupCallbackType callback)) 213 | callback.Invoke(glIsClean, sessionCallSent, unityTextureId, textureId, idIsValid); 214 | } 215 | } 216 | } -------------------------------------------------------------------------------- /UXR.QuestCamera/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!47 &1 4 | QualitySettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 5 7 | m_CurrentQuality: 2 8 | m_QualitySettings: 9 | - serializedVersion: 5 10 | name: Very Low 11 | pixelLightCount: 0 12 | shadows: 0 13 | shadowResolution: 0 14 | shadowProjection: 1 15 | shadowCascades: 1 16 | shadowDistance: 15 17 | shadowNearPlaneOffset: 3 18 | shadowCascade2Split: 0.33333334 19 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 20 | shadowmaskMode: 0 21 | skinWeights: 1 22 | globalTextureMipmapLimit: 1 23 | textureMipmapLimitSettings: [] 24 | anisotropicTextures: 0 25 | antiAliasing: 0 26 | softParticles: 0 27 | softVegetation: 0 28 | realtimeReflectionProbes: 0 29 | billboardsFaceCameraPosition: 0 30 | useLegacyDetailDistribution: 1 31 | adaptiveVsync: 0 32 | vSyncCount: 0 33 | realtimeGICPUUsage: 25 34 | adaptiveVsyncExtraA: 0 35 | adaptiveVsyncExtraB: 0 36 | lodBias: 0.3 37 | meshLodThreshold: 1 38 | maximumLODLevel: 0 39 | enableLODCrossFade: 1 40 | streamingMipmapsActive: 0 41 | streamingMipmapsAddAllCameras: 1 42 | streamingMipmapsMemoryBudget: 512 43 | streamingMipmapsRenderersPerFrame: 512 44 | streamingMipmapsMaxLevelReduction: 2 45 | streamingMipmapsMaxFileIORequests: 1024 46 | particleRaycastBudget: 4 47 | asyncUploadTimeSlice: 2 48 | asyncUploadBufferSize: 16 49 | asyncUploadPersistentBuffer: 1 50 | resolutionScalingFixedDPIFactor: 1 51 | customRenderPipeline: {fileID: 0} 52 | terrainQualityOverrides: 0 53 | terrainPixelError: 1 54 | terrainDetailDensityScale: 1 55 | terrainBasemapDistance: 1000 56 | terrainDetailDistance: 80 57 | terrainTreeDistance: 5000 58 | terrainBillboardStart: 50 59 | terrainFadeLength: 5 60 | terrainMaxTrees: 50 61 | excludedTargetPlatforms: [] 62 | - serializedVersion: 5 63 | name: Low 64 | pixelLightCount: 0 65 | shadows: 0 66 | shadowResolution: 0 67 | shadowProjection: 1 68 | shadowCascades: 1 69 | shadowDistance: 20 70 | shadowNearPlaneOffset: 3 71 | shadowCascade2Split: 0.33333334 72 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 73 | shadowmaskMode: 0 74 | skinWeights: 2 75 | globalTextureMipmapLimit: 0 76 | textureMipmapLimitSettings: [] 77 | anisotropicTextures: 0 78 | antiAliasing: 0 79 | softParticles: 0 80 | softVegetation: 0 81 | realtimeReflectionProbes: 0 82 | billboardsFaceCameraPosition: 0 83 | useLegacyDetailDistribution: 1 84 | adaptiveVsync: 0 85 | vSyncCount: 0 86 | realtimeGICPUUsage: 25 87 | adaptiveVsyncExtraA: 0 88 | adaptiveVsyncExtraB: 0 89 | lodBias: 0.4 90 | meshLodThreshold: 1 91 | maximumLODLevel: 0 92 | enableLODCrossFade: 1 93 | streamingMipmapsActive: 0 94 | streamingMipmapsAddAllCameras: 1 95 | streamingMipmapsMemoryBudget: 512 96 | streamingMipmapsRenderersPerFrame: 512 97 | streamingMipmapsMaxLevelReduction: 2 98 | streamingMipmapsMaxFileIORequests: 1024 99 | particleRaycastBudget: 16 100 | asyncUploadTimeSlice: 2 101 | asyncUploadBufferSize: 16 102 | asyncUploadPersistentBuffer: 1 103 | resolutionScalingFixedDPIFactor: 1 104 | customRenderPipeline: {fileID: 0} 105 | terrainQualityOverrides: 0 106 | terrainPixelError: 1 107 | terrainDetailDensityScale: 1 108 | terrainBasemapDistance: 1000 109 | terrainDetailDistance: 80 110 | terrainTreeDistance: 5000 111 | terrainBillboardStart: 50 112 | terrainFadeLength: 5 113 | terrainMaxTrees: 50 114 | excludedTargetPlatforms: [] 115 | - serializedVersion: 5 116 | name: Medium 117 | pixelLightCount: 1 118 | shadows: 1 119 | shadowResolution: 0 120 | shadowProjection: 1 121 | shadowCascades: 1 122 | shadowDistance: 20 123 | shadowNearPlaneOffset: 3 124 | shadowCascade2Split: 0.33333334 125 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 126 | shadowmaskMode: 0 127 | skinWeights: 2 128 | globalTextureMipmapLimit: 0 129 | textureMipmapLimitSettings: [] 130 | anisotropicTextures: 1 131 | antiAliasing: 0 132 | softParticles: 0 133 | softVegetation: 0 134 | realtimeReflectionProbes: 0 135 | billboardsFaceCameraPosition: 0 136 | useLegacyDetailDistribution: 1 137 | adaptiveVsync: 0 138 | vSyncCount: 0 139 | realtimeGICPUUsage: 25 140 | adaptiveVsyncExtraA: 0 141 | adaptiveVsyncExtraB: 0 142 | lodBias: 0.7 143 | meshLodThreshold: 1 144 | maximumLODLevel: 0 145 | enableLODCrossFade: 1 146 | streamingMipmapsActive: 0 147 | streamingMipmapsAddAllCameras: 1 148 | streamingMipmapsMemoryBudget: 512 149 | streamingMipmapsRenderersPerFrame: 512 150 | streamingMipmapsMaxLevelReduction: 2 151 | streamingMipmapsMaxFileIORequests: 1024 152 | particleRaycastBudget: 64 153 | asyncUploadTimeSlice: 2 154 | asyncUploadBufferSize: 16 155 | asyncUploadPersistentBuffer: 1 156 | resolutionScalingFixedDPIFactor: 1 157 | customRenderPipeline: {fileID: 0} 158 | terrainQualityOverrides: 0 159 | terrainPixelError: 1 160 | terrainDetailDensityScale: 1 161 | terrainBasemapDistance: 1000 162 | terrainDetailDistance: 80 163 | terrainTreeDistance: 5000 164 | terrainBillboardStart: 50 165 | terrainFadeLength: 5 166 | terrainMaxTrees: 50 167 | excludedTargetPlatforms: [] 168 | - serializedVersion: 5 169 | name: High 170 | pixelLightCount: 2 171 | shadows: 2 172 | shadowResolution: 1 173 | shadowProjection: 1 174 | shadowCascades: 2 175 | shadowDistance: 40 176 | shadowNearPlaneOffset: 3 177 | shadowCascade2Split: 0.33333334 178 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 179 | shadowmaskMode: 1 180 | skinWeights: 2 181 | globalTextureMipmapLimit: 0 182 | textureMipmapLimitSettings: [] 183 | anisotropicTextures: 1 184 | antiAliasing: 0 185 | softParticles: 0 186 | softVegetation: 1 187 | realtimeReflectionProbes: 1 188 | billboardsFaceCameraPosition: 1 189 | useLegacyDetailDistribution: 1 190 | adaptiveVsync: 0 191 | vSyncCount: 1 192 | realtimeGICPUUsage: 50 193 | adaptiveVsyncExtraA: 0 194 | adaptiveVsyncExtraB: 0 195 | lodBias: 1 196 | meshLodThreshold: 1 197 | maximumLODLevel: 0 198 | enableLODCrossFade: 1 199 | streamingMipmapsActive: 0 200 | streamingMipmapsAddAllCameras: 1 201 | streamingMipmapsMemoryBudget: 512 202 | streamingMipmapsRenderersPerFrame: 512 203 | streamingMipmapsMaxLevelReduction: 2 204 | streamingMipmapsMaxFileIORequests: 1024 205 | particleRaycastBudget: 256 206 | asyncUploadTimeSlice: 2 207 | asyncUploadBufferSize: 16 208 | asyncUploadPersistentBuffer: 1 209 | resolutionScalingFixedDPIFactor: 1 210 | customRenderPipeline: {fileID: 0} 211 | terrainQualityOverrides: 0 212 | terrainPixelError: 1 213 | terrainDetailDensityScale: 1 214 | terrainBasemapDistance: 1000 215 | terrainDetailDistance: 80 216 | terrainTreeDistance: 5000 217 | terrainBillboardStart: 50 218 | terrainFadeLength: 5 219 | terrainMaxTrees: 50 220 | excludedTargetPlatforms: [] 221 | - serializedVersion: 5 222 | name: Very High 223 | pixelLightCount: 3 224 | shadows: 2 225 | shadowResolution: 2 226 | shadowProjection: 1 227 | shadowCascades: 2 228 | shadowDistance: 70 229 | shadowNearPlaneOffset: 3 230 | shadowCascade2Split: 0.33333334 231 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 232 | shadowmaskMode: 1 233 | skinWeights: 4 234 | globalTextureMipmapLimit: 0 235 | textureMipmapLimitSettings: [] 236 | anisotropicTextures: 2 237 | antiAliasing: 2 238 | softParticles: 1 239 | softVegetation: 1 240 | realtimeReflectionProbes: 1 241 | billboardsFaceCameraPosition: 1 242 | useLegacyDetailDistribution: 1 243 | adaptiveVsync: 0 244 | vSyncCount: 1 245 | realtimeGICPUUsage: 50 246 | adaptiveVsyncExtraA: 0 247 | adaptiveVsyncExtraB: 0 248 | lodBias: 1.5 249 | meshLodThreshold: 1 250 | maximumLODLevel: 0 251 | enableLODCrossFade: 1 252 | streamingMipmapsActive: 0 253 | streamingMipmapsAddAllCameras: 1 254 | streamingMipmapsMemoryBudget: 512 255 | streamingMipmapsRenderersPerFrame: 512 256 | streamingMipmapsMaxLevelReduction: 2 257 | streamingMipmapsMaxFileIORequests: 1024 258 | particleRaycastBudget: 1024 259 | asyncUploadTimeSlice: 2 260 | asyncUploadBufferSize: 16 261 | asyncUploadPersistentBuffer: 1 262 | resolutionScalingFixedDPIFactor: 1 263 | customRenderPipeline: {fileID: 0} 264 | terrainQualityOverrides: 0 265 | terrainPixelError: 1 266 | terrainDetailDensityScale: 1 267 | terrainBasemapDistance: 1000 268 | terrainDetailDistance: 80 269 | terrainTreeDistance: 5000 270 | terrainBillboardStart: 50 271 | terrainFadeLength: 5 272 | terrainMaxTrees: 50 273 | excludedTargetPlatforms: [] 274 | - serializedVersion: 5 275 | name: Ultra 276 | pixelLightCount: 4 277 | shadows: 2 278 | shadowResolution: 2 279 | shadowProjection: 1 280 | shadowCascades: 4 281 | shadowDistance: 150 282 | shadowNearPlaneOffset: 3 283 | shadowCascade2Split: 0.33333334 284 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 285 | shadowmaskMode: 1 286 | skinWeights: 4 287 | globalTextureMipmapLimit: 0 288 | textureMipmapLimitSettings: [] 289 | anisotropicTextures: 2 290 | antiAliasing: 2 291 | softParticles: 1 292 | softVegetation: 1 293 | realtimeReflectionProbes: 1 294 | billboardsFaceCameraPosition: 1 295 | useLegacyDetailDistribution: 1 296 | adaptiveVsync: 0 297 | vSyncCount: 0 298 | realtimeGICPUUsage: 100 299 | adaptiveVsyncExtraA: 0 300 | adaptiveVsyncExtraB: 0 301 | lodBias: 2 302 | meshLodThreshold: 1 303 | maximumLODLevel: 0 304 | enableLODCrossFade: 1 305 | streamingMipmapsActive: 0 306 | streamingMipmapsAddAllCameras: 1 307 | streamingMipmapsMemoryBudget: 512 308 | streamingMipmapsRenderersPerFrame: 512 309 | streamingMipmapsMaxLevelReduction: 2 310 | streamingMipmapsMaxFileIORequests: 1024 311 | particleRaycastBudget: 4096 312 | asyncUploadTimeSlice: 2 313 | asyncUploadBufferSize: 16 314 | asyncUploadPersistentBuffer: 1 315 | resolutionScalingFixedDPIFactor: 1 316 | customRenderPipeline: {fileID: 0} 317 | terrainQualityOverrides: 0 318 | terrainPixelError: 1 319 | terrainDetailDensityScale: 1 320 | terrainBasemapDistance: 1000 321 | terrainDetailDistance: 80 322 | terrainTreeDistance: 5000 323 | terrainBillboardStart: 50 324 | terrainFadeLength: 5 325 | terrainMaxTrees: 50 326 | excludedTargetPlatforms: [] 327 | m_TextureMipmapLimitGroupNames: [] 328 | m_PerPlatformDefaultQuality: 329 | Android: 2 330 | GameCoreScarlett: 5 331 | GameCoreXboxOne: 5 332 | Lumin: 5 333 | Nintendo 3DS: 5 334 | Nintendo Switch: 5 335 | PS4: 5 336 | PS5: 5 337 | Stadia: 5 338 | Standalone: 5 339 | WebGL: 3 340 | Windows Store Apps: 5 341 | XboxOne: 5 342 | iPhone: 2 343 | tvOS: 2 344 | --------------------------------------------------------------------------------