### Install ViroReact with npm Source: https://github.com/reactvision/viro/blob/main/README.md Use this command to install the ViroReact library in your project. Ensure you have Node.js and npm installed. ```bash npm install @reactvision/react-viro ``` -------------------------------- ### Minimal React Viro Setup (No ARCore) Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md Use this configuration for apps that only require basic AR features without ARCore integration. ```json { "expo": { "name": "MyARApp", "plugins": [ ["@reactvision/react-viro", { "iosLinkage": "static" }] ] } } ``` -------------------------------- ### Install React Viro using NPM Source: https://github.com/reactvision/viro/blob/main/readmes/INSTALL.md Use this command to add the React Viro library to your project dependencies via NPM. ```bash npm install --save @reactvision/react-viro ``` -------------------------------- ### Install React Viro using Yarn Source: https://github.com/reactvision/viro/blob/main/readmes/INSTALL.md Use this command to add the React Viro library to your project dependencies via Yarn. ```bash yarn add @reactvision/react-viro ``` -------------------------------- ### Example of Weak Framework Flags in xcconfig Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md This is an example of the expected output when verifying weak linking flags for ARCore frameworks in your xcconfig file. ```text OTHER_LDFLAGS = ... -weak_framework "ARCoreBase" -weak_framework "ARCoreCloudAnchors" ... ``` -------------------------------- ### React Viro Setup for Full ARCore Features Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md This setup enables all ARCore capabilities, including geospatial anchoring and camera/location permissions. ```json { "expo": { "name": "MyARApp", "plugins": [ ["@reactvision/react-viro", { "ios": { "includeARCore": true, "cameraUsagePermission": "This app uses the camera for AR experiences", "locationUsagePermission": "This app uses your location for AR experiences" }, "cloudAnchorProvider": "arcore", "geospatialAnchorProvider": "arcore", "googleCloudApiKey": "AIza..." }] ] } } ``` -------------------------------- ### Fullscreen Camera Setup with ViroCameraTexture Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md This snippet demonstrates how to set up a fullscreen camera view using ViroScene and ViroCameraTexture. It configures a quad to fill the screen and displays the front camera feed. The `onCameraReady` callback is used to ensure the camera is ready before rendering the quad. ```tsx import React, { useState } from 'react'; import { Dimensions } from 'react-native'; import { ViroScene, ViroSceneNavigator, ViroQuad, ViroCameraTexture, ViroMaterials, } from '@reactvision/react-viro'; ViroMaterials.createMaterials({ mirror: { lightingModel: 'Constant' }, }); const DIST = 1; const WIDTH = 2 * DIST; // fills 90° horizontal FOV const { width: sw, height: sh } = Dimensions.get('window'); const HEIGHT = WIDTH * (sh / sw); // match screen aspect ratio function FullscreenSelfieScene() { const [ready, setReady] = useState(false); return ( setReady(true)} onError={(e) => console.error(e.nativeEvent.error)} /> ); } export default function App() { return ( ); } ``` -------------------------------- ### ViroGameLoop Example Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Example of using ViroGameLoop within a ViroARScene to manage fixed and regular updates for a ViroBox. ```tsx import { ViroARScene, ViroBox, ViroGameLoop } from "@reactvision/react-viro"; import { useRef } from "react"; const boxRef = useRef(null); { // deterministic physics step }} onUpdate={({ nativeEvent: { dt } }) => { // interpolate / render }} /> ; ``` -------------------------------- ### Basic ViroVirtualJoystick Usage Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Example of how to use ViroVirtualJoystick. Ensure explicit width and height are set in the style prop. The onStickChange callback updates module-level state. ```tsx import { ViroVirtualJoystick } from "@reactvision/react-viro"; const ctrl = { x: 0, y: 0 }; // module-level, shared with game loop { ctrl.x = e.nativeEvent.x; ctrl.y = e.nativeEvent.y; }} style={{ width: 120, height: 120 }} />; ``` -------------------------------- ### React Viro Setup for Cloud Anchors Only Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md Configure for shared AR experiences using cloud anchors, without enabling geospatial features. ```json { "expo": { "name": "MyARApp", "plugins": [ ["@reactvision/react-viro", { "cloudAnchorProvider": "arcore", "googleCloudApiKey": "AIza..." }] ] } } ``` -------------------------------- ### Defining a VR Scene with ViroScene Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md This example shows how to define a VR scene using ViroScene as the root component. It includes essential elements like lighting and a 360 image, suitable for VR environments on Quest. ```tsx import { ViroScene, ViroAmbientLight, ViroController, Viro360Image, } from "@reactvision/react-viro"; export function MyVRScene() { return ( {/* …your content… */} ); } ``` -------------------------------- ### Run Expo Prebuild Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md Execute 'npx expo prebuild --clean' to generate necessary Android files and manifest entries for VR support. This command is crucial after initial setup or upgrades. ```bash npx expo prebuild --clean ``` -------------------------------- ### ViroVirtualButton Component Usage Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Example of using the ViroVirtualButton component with custom properties like controllerId, button, size, tintColor, and event handlers. ```tsx console.log("A pressed")} onPressOut={() => console.log("A released")} style={{ width: 48, height: 48 }} /> ``` -------------------------------- ### Correct Call Order for StreamingAudioManager Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Demonstrates the essential sequence of operations for using StreamingAudioManager, emphasizing the critical need to push samples before starting playback to prevent underruns. ```typescript // 1. Create StreamingAudioManager.create("voice"); // 2. Configure stream format StreamingAudioManager.beginStreaming("voice", 22050, 1); // 3. Pre-fill buffer BEFORE play StreamingAudioManager.pushSamples("voice", firstChunkBase64); // 4. Start playback StreamingAudioManager.play("voice"); // 5. Continue pushing on each chunk StreamingAudioManager.pushSamples("voice", nextChunkBase64); // 6. Clean up StreamingAudioManager.destroy("voice"); ``` -------------------------------- ### Convert Binary Plists to XML for Pod Install UTF-8 Error Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md If you encounter 'invalid byte sequence in UTF-8' errors during `pod install`, convert binary Info.plist files to XML format within the ios/Pods directory. ```bash cd ios/Pods find . -name "Info.plist" -exec plutil -convert xml1 {} \; ``` -------------------------------- ### Implement Custom VR Root with Navigator Bridge Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md For custom roots bypassing `ViroQuestEntryPoint`, directly use `ViroVRSceneNavigator` and subscribe to `VRQuestNavigatorBridge` to handle navigation operations like push and pop. This setup is necessary if you need to manage intents and operations manually. ```tsx import { ViroVRSceneNavigator, VRQuestNavigatorBridge, } from "@reactvision/react-viro"; export default function VRQuestRoot() { // ViroQuestEntryPoint does all of this automatically — only needed for // custom roots that bypass it. const [intent, setIntent] = useState(() => VRQuestNavigatorBridge.getIntent()); const navRef = useRef(null); useEffect(() => VRQuestNavigatorBridge.onIntent(setIntent), []); useEffect(() => { if (!intent) return; return VRQuestNavigatorBridge.subscribeOps((op) => { if (op.type === "push") navRef.current?.push(op.scene); else if (op.type === "pop") navRef.current?.pop(); // …etc }); }, [intent?.intentKey]); if (!intent) return null; return ( ); } ``` -------------------------------- ### Record Video with ViroCameraTexture Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Starts and stops video recording of the camera feed to an MP4 file. Specify an outputPath for custom file locations. Ensure microphone permissions are granted for audio recording. ```tsx import React, { useRef, useState } from 'react'; import { Button } from 'react-native'; import { ViroARScene, ViroQuad, ViroCameraTexture, ViroMaterials } from '@reactvision/react-viro'; ViroMaterials.createMaterials({ cam: { lightingModel: 'Constant' } }); function Scene() { const cameraRef = useRef(null); const [recording, setRecording] = useState(false); const toggleRecording = async () => { if (!recording) { const result = await cameraRef.current?.startRecording(); if (result?.success) { setRecording(true); console.log('Recording started, will write to', result.url); } } else { const result = await cameraRef.current?.stopRecording(); setRecording(false); if (result?.success) { console.log('Video saved to', result.url); } } }; return ( ); } ``` -------------------------------- ### ViroGameLoop Component Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md The ViroGameLoop component provides a headless node that fires per-frame JavaScript callbacks from the native render loop. It can be mounted within a ViroARScene or ViroScene to start the loop and unmounted to stop it. It supports `onUpdate`, `onLateUpdate`, and `onFixedUpdate` callbacks, with an option to enable a fixed-step accumulator via `fixedHz`. ```APIDOC ## ViroGameLoop — F5 A headless (zero-size) node that fires per-frame JS callbacks from the native render loop. Mount it anywhere inside a `ViroARScene` or `ViroScene` to start the loop. Unmount it to stop. ### Import ```tsx import { ViroGameLoop } from "@reactvision/react-viro"; ``` ### Props | Prop | Type | Default | Description | | --------------- | -------------------------------------- | ------- | ------------------------------------------------------------------------------------------------ | | `onUpdate` | `(e: ViroGameLoopUpdateEvent) => void` | — | Fires every rendered frame. | | `onLateUpdate` | `(e: ViroGameLoopUpdateEvent) => void` | — | Fires after physics simulation. | | `fixedHz` | `number` | — | When set, enables the fixed-step accumulator. | | `onFixedUpdate` | `(e: ViroGameLoopFixedEvent) => void` | — | Fires at `fixedHz` regardless of render rate. Spiral-of-death protection: max 4 ticks per frame. | ```typescript type ViroGameLoopUpdateEvent = { nativeEvent: { dt: number; elapsed: number }; }; type ViroGameLoopFixedEvent = { nativeEvent: { dt: number }; }; ``` > **Note:** `dt` and `elapsed` arrive as strings on Android due to New Architecture Fabric constraints and are parsed to `number` by the component wrapper automatically. ### Hooks ```typescript import { useGameLoop, useLateUpdate, useFixedUpdate } from '@reactvision/react-viro'; useGameLoop((dt: number, elapsed: number) => void): void; useLateUpdate((dt: number, elapsed: number) => void): void; useFixedUpdate((dt: number) => void, fixedHz?: number): void; ``` ### Utility: bypass reconciler For high-frequency position/rotation updates, use these helpers to bypass the React reconciler: ```typescript import { ViroGameLoopUtils } from "@reactvision/react-viro"; ViroGameLoopUtils.setPosition(nodeRef, x, y, z); ViroGameLoopUtils.setRotation(nodeRef, x, y, z); ViroGameLoopUtils.setScale(nodeRef, x, y, z); ``` ### Example ```tsx import { ViroARScene, ViroBox, ViroGameLoop } from "@reactvision/react-viro"; import { useRef } from "react"; const boxRef = useRef(null); { // deterministic physics step }} onUpdate={({ nativeEvent: { dt } }) => { // interpolate / render }} /> ; ``` ``` -------------------------------- ### startRecording / stopRecording Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Records the camera feed to an MP4 file. `startRecording` initiates the session, and `stopRecording` finalizes the file and returns its path. ```APIDOC ## startRecording(options?) / stopRecording() ### Description Record the camera feed to an MP4. `startRecording` resolves once the recording session is open; `stopRecording` finalises the file and resolves with its path. ### Method Signatures ```typescript startRecording(options?: { outputPath?: string }): Promise stopRecording(): Promise ``` ### Parameters #### `startRecording` Options - `outputPath` (string) - Optional - Absolute path for the output file (e.g. from `react-native-fs`). Omit to use a default cache-directory path chosen by the native layer. ### Returns - `Promise` - A promise that resolves with a `ViroCaptureResult` object. ```ts type ViroCaptureResult = | { success: true; url: string } // absolute path of the written file | { success: false; error: string } // human-readable error ``` ### Example ```tsx import React, { useRef, useState } from 'react'; import { Button } from 'react-native'; import { ViroARScene, ViroQuad, ViroCameraTexture, ViroMaterials } from '@reactvision/react-viro'; ViroMaterials.createMaterials({ cam: { lightingModel: 'Constant' } }); function Scene() { const cameraRef = useRef(null); const [recording, setRecording] = useState(false); const toggleRecording = async () => { if (!recording) { const result = await cameraRef.current?.startRecording(); if (result?.success) { setRecording(true); console.log('Recording started, will write to', result.url); } } else { const result = await cameraRef.current?.stopRecording(); setRecording(false); if (result?.success) { console.log('Video saved to', result.url); } } }; return ( ); } ``` ``` -------------------------------- ### Using ViroXRSceneNavigator for Cross-Platform Entry Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md This snippet demonstrates the basic usage of ViroXRSceneNavigator, the primary entry point for cross-platform VR/AR experiences. It configures initial scenes for both AR and VR modes. ```tsx import { ViroXRSceneNavigator } from "@reactvision/react-viro"; export default function MyScreen() { return ( ); } ``` -------------------------------- ### Basic ViroCameraTexture Usage Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Register a material and attach the ViroCameraTexture to a ViroQuad. Ensure camera permissions are handled at runtime. ```tsx import { ViroMaterials, ViroQuad, ViroCameraTexture } from '@reactvision/react-viro'; // 1. Register the material — only lightingModel is required. // ViroCameraTexture sets the diffuse texture on it automatically. ViroMaterials.createMaterials({ selfieMat: { lightingModel: 'Constant' }, }); // 2. Place a surface and attach the camera texture to its material. function SelfieScene() { return ( console.log('Camera ready')} onError={(e) => console.error(e.nativeEvent.error)} /> ); } ``` -------------------------------- ### Import ViroVirtualButton Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Import the ViroVirtualButton component from the @reactvision/react-viro package. ```tsx import { ViroVirtualButton } from "@reactvision/react-viro"; ``` -------------------------------- ### Handle VR Exit with onExitViro Callback Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md Use the `onExitViro` prop on `ViroXRSceneNavigator` or `StudioSceneNavigator` to define behavior after the VR session ends, such as navigating back in the panel. ```tsx // ViroXRSceneNavigator navigation.goBack()} /> // StudioSceneNavigator navigation.goBack()} /> ``` -------------------------------- ### Camera Position Considerations with AR Scenes Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Explains the interaction between ViroCameraTexture's `cameraPosition` prop and ViroARScene, highlighting potential conflicts and recommended usage. ```APIDOC ## Camera position and AR scenes `ViroARScene` already uses the **back camera** via ARKit / ARCore for tracking and background rendering. `ViroCameraTexture` is independent of that feed — it opens its own camera session. | Scene type | `cameraPosition` | Result | |---|---|---| | `ViroARScene` | `"front"` | ✅ Two different physical cameras run concurrently — AR tracking on the back, selfie on the front | | `ViroARScene` | `"back"` | ❌ The OS will deny a second exclusive session on the same sensor already held by ARCore/ARKit | | `ViroScene` (non-AR) | `"front"` or `"back"` | ✅ No AR framework running, either camera works | The intended use-case inside an AR scene is always the **front camera** for a selfie overlay while the back camera drives AR tracking. ``` -------------------------------- ### Configure Static Framework Linkage for iOS Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md Use this configuration to set static framework linkage for iOS. This option provides a faster launch time and a single binary. ```json { "expo": { "plugins": [ ["@reactvision/react-viro", { "iosLinkage": "static" }] ] } } ``` -------------------------------- ### Import StreamingAudioManager Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Import the StreamingAudioManager class from the React-Viro library. ```typescript import { StreamingAudioManager } from "@reactvision/react-viro"; ``` -------------------------------- ### Switch Cameras at Runtime Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Update the `cameraPosition` prop to switch between 'front' and 'back' cameras. A brief black frame may appear during the transition. ```tsx const [position, setPosition] = React.useState<'front' | 'back'>('front'); setPosition(p => p === 'front' ? 'back' : 'front')} /> ``` -------------------------------- ### StreamingAudioManager API Overview Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Reference for the core methods of StreamingAudioManager, including creating players, managing streams, and controlling playback. Ensure correct call order for optimal performance. ```typescript // Create a named player StreamingAudioManager.create(playerId: string): void; // Begin streaming — must be called before pushSamples StreamingAudioManager.beginStreaming( playerId: string, sampleRate: number, // e.g. 22050, 44100, 48000 channels: number // 1 = mono, 2 = stereo ): void; // Playback control StreamingAudioManager.play(playerId: string): void; StreamingAudioManager.pause(playerId: string): void; StreamingAudioManager.setVolume(playerId: string, volume: number): void; // 0.0–1.0 StreamingAudioManager.setMuted(playerId: string, muted: boolean): void; // Push audio data — base64-encoded float32 PCM, interleaved, little-endian StreamingAudioManager.pushSamples(playerId: string, base64Samples: string): void; // Release resources StreamingAudioManager.destroy(playerId: string): void; ``` -------------------------------- ### Import ViroVirtualJoystick Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Import the ViroVirtualJoystick component from the @reactvision/react-viro package. ```tsx import { ViroVirtualJoystick } from "@reactvision/react-viro"; ``` -------------------------------- ### Capture Photo with ViroCameraTexture Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Saves the current camera frame as a JPEG. Specify an outputPath for custom file locations, or omit to use a default cache directory. The promise resolves with the file path upon successful save. ```tsx import React, { useRef } from 'react'; import { Button } from 'react-native'; import { ViroARScene, ViroQuad, ViroCameraTexture, ViroMaterials } from '@reactvision/react-viro'; ViroMaterials.createMaterials({ cam: { lightingModel: 'Constant' } }); function Scene() { const cameraRef = useRef(null); const takePhoto = async () => { const result = await cameraRef.current?.capturePhoto(); if (result?.success) { console.log('Photo saved to', result.url); } else { console.error('Capture failed:', result?.error); } }; return ( {/* Trigger takePhoto from your UI */} ); } ``` -------------------------------- ### World Mesh Configuration Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Props for enabling and configuring the real-world geometry mesh, including physics and debug drawing. ```APIDOC ## ViroARSceneNavigator - World Mesh ### Description Surfaces the real-world geometry as a subscribable, physics-ready mesh. ### Props - `worldMeshEnabled` (boolean) - Default: `false` - Enable real-world mesh generation. - `worldMeshConfig` (ViroWorldMeshConfig) - Fine-grained control over physics and debug draw. - `onWorldMeshUpdated` ((stats: ViroWorldMeshStats) => void) - Fired when the mesh is updated. ### ViroWorldMeshConfig Type ```typescript type ViroWorldMeshConfig = { // Physics mesh simplification physicsCellSize?: number; // Vertex clustering cell (default: 0.10 m). 0 = disabled. physicsMaxTriangles?: number; // Stride decimation limit. 0 = no limit. // Debug wireframe debugDrawEnabled?: boolean; // default: true debugDrawDepthTest?: boolean; // occluded by real surfaces (default: true) debugDrawMaxTriangles?: number; // default: 1000 debugDrawLineThickness?: number; // default: 0.001 m }; ``` ### ViroWorldMeshStats Type ```typescript type ViroWorldMeshStats = { vertexCount: number; triangleCount: number; source: "lidar" | "monocular" | "plane"; }; ``` ### Source Priority 1. `ARMeshAnchor` — iOS 13.4+ LiDAR, native accumulation across frames 2. Depth image — LiDAR or monocular depth, current frame only 3. `ARPlaneAnchor` — all platforms, session-persistent polygon fallback ### Performance Guidance - `physicsCellSize=0.10` clusters vertices within 10 cm → gap-free simplified mesh - `physicsMaxTriangles=200` limits per-update cost when LiDAR produces dense meshes - Async BVH construction — physics build runs on a background thread; render thread is never blocked ### Physics Usage For physics bodies to respond to the world mesh, add `physicsWorld` to your scene: ```tsx ``` The `VROARWorldMesh` physics body operates independently from JS `physicsBody` props — it uses Bullet directly without requiring `physicsWorld` on the scene. However, JS physics objects DO require it. ``` -------------------------------- ### Navigate VR Scenes via Ref Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md Access the `arSceneNavigator` ref from `ViroXRSceneNavigator` to control scene navigation programmatically. This works for both AR and VR on Quest. ```tsx const navRef = useRef(null); // Push a new scene navRef.current?.arSceneNavigator?.push({ scene: DetailScene }); // Pop back navRef.current?.arSceneNavigator?.pop(); ``` -------------------------------- ### Recenter Tracking and Toggle Passthrough Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md Utilize `VRModuleOpenXR.recenterTracking` and `setPassthroughEnabled` by obtaining the native view tag using the `useVRViewTag` hook. Ensure the view tag is available before calling these native functions. ```tsx import { VRModuleOpenXR, useVRViewTag } from "@reactvision/react-viro"; function MyVRScene() { const viewTag = useVRViewTag(); const recenter = () => { if (viewTag != null) VRModuleOpenXR?.recenterTracking?.(viewTag); }; return ( ... ); } ``` -------------------------------- ### ViroCameraTexture Props Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md This section details the available props for the ViroCameraTexture component, outlining their types, default values, and descriptions. ```APIDOC ## ViroCameraTexture Props | Prop | Type | Default | Description | |---|---|---|---| | `material` | `string` | **required** | Name of a material registered via `ViroMaterials.createMaterials`. The component sets this material's diffuse texture to the live camera feed. | | `cameraPosition` | `"front" | "back"` | `"front"` | Which physical camera to open. The front camera feed is automatically mirrored by the OS for a natural selfie effect. | | `paused` | `boolean` | `false` | When `true`, frame updates stop and the last captured frame is held. Drops CPU and battery usage to near zero. | | `onCameraReady` | `() => void` | — | Fires once when the first real camera frame is available and the texture is ready. Called from the native Camera2 / AVFoundation callback, not synchronously on mount. | | `onError` | `(event) => void` | — | Fires when the camera fails to initialise — permission denied, hardware error, or invalid material name. | ``` -------------------------------- ### Bind Live Camera Feed to Material Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Use ViroCameraTexture to display a live device camera feed on any geometry's material. This is suitable for mirror effects or picture-in-picture. ```tsx import { ViroMaterials, ViroQuad, ViroCameraTexture, } from "@reactvision/react-viro"; ViroMaterials.createMaterials({ mirrorMat: { lightingModel: "Constant" }, }); function MirrorScene() { const cameraRef = useRef(null); return ( console.log("Camera ready")} /> ); } ``` -------------------------------- ### Android Camera Permission Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Declare the camera permission in your AndroidManifest.xml file. This is required for ViroCameraTexture to function. ```xml ``` -------------------------------- ### Android Permissions for Audio Recording Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Declare the RECORD_AUDIO permission in AndroidManifest.xml to enable audio recording alongside video. ```xml ``` -------------------------------- ### Reading Controller State from C++ Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Accessing the state of a virtual controller, including joystick positions and button states, from C++ using VROVirtualControllerRegistry. ```cpp // virocore — safe from any thread auto state = VROVirtualControllerRegistry::instance().peek("p1"); if (state) { auto snap = state->snapshot(); float leftX = snap.stickLX; float leftY = snap.stickLY; bool aPressed = (snap.buttonBits >> VROButtonIndex_A) & 1; } ``` -------------------------------- ### React Viro Custom Permissions Configuration Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md Customize the permission messages for camera, microphone, photos, and location access within your AR application. ```json { "expo": { "plugins": [ ["@reactvision/react-viro", { "ios": { "cameraUsagePermission": "We need camera access to show AR content", "microphoneUsagePermission": "We need microphone access for voice commands", "photosPermission": "We need photo access to save AR screenshots", "savePhotosPermission": "We need permission to save AR screenshots", "locationUsagePermission": "We need location for geospatial AR features" } }] ] } } ``` -------------------------------- ### iOS Permissions for Audio Recording Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Add the NSMicrophoneUsageDescription key to Info.plist for iOS to request microphone access for audio recording. ```xml NSMicrophoneUsageDescription Used to record audio with video captures ``` -------------------------------- ### ViroVirtualButton Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md A native-rendered circular button that pairs with ViroVirtualJoystick via a shared controllerId. It allows for custom button actions and styling. ```APIDOC ## ViroVirtualButton ### Description A native-rendered circular button. Pairs with `ViroVirtualJoystick` via a shared `controllerId`. ### Import ```tsx import { ViroVirtualButton } from "@reactvision/react-viro"; ``` ### Props #### Required Props - **controllerId** (string) - Required - Shared controller ID. - **button** (ViroButtonName) - Required - Which button bit to set. #### Optional Props - **size** (number) - Optional - Circle diameter in dp/points. Default: `44`. - **tintColor** (string | number) - Optional - Button fill color. Default: `rgba(255,255,255,0.6)`. - **onPressIn** ((e) => void) - Optional - Fires on touch-down. - **onPressOut** ((e) => void) - Optional - Fires on touch-up or cancel. - **style** (StyleProp) - Optional - Must include explicit `width` and `height`. ### Type Definitions ```typescript type ViroButtonName = | "A" | "B" | "X" | "Y" | "Z" | "L1" | "R1" | "L2" | "R2" | "Start" | "Select"; ``` ### Example ```tsx console.log("A pressed")} onPressOut={() => console.log("A released")} style={{ width: 48, height: 48 }} /> ``` ``` -------------------------------- ### ViroVirtualJoystick Component Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md The ViroVirtualJoystick component is a native-rendered on-screen joystick that writes stick state to a VROInputState registry entry with single-digit ms latency, avoiding JS bridge round-trips for core functionality. It requires explicit width and height in its style prop. ```APIDOC ## ViroVirtualJoystick ### Description A native-rendered on-screen joystick that writes stick state to a `VROInputState` registry entry with single-digit ms latency (no JS bridge round-trip). ### Import ```tsx import { ViroVirtualJoystick } from "@reactvision/react-viro"; ``` ### Props | Prop | Type | Default | Description | | --------------- | ---------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `controllerId` | `string` | **required** | Identifies the shared controller state. Must match the `controllerId` of any `ViroVirtualButton` you want to share state with. | | `stickSide` | `"left" | "right"` | `"left"` | Which analog stick this joystick drives. | | `radius` | `number` | `60` | Outer ring radius in dp/points. | | `tintColor` | `string | number` | `rgba(255,255,255,0.6)` | Color of the ring and knob. | | `onStickChange` | `(e) => void` | — | Fires when stick value changes. `e.nativeEvent.x` and `.y` are `number` in `[-1, 1]`. | | `style` | `StyleProp` | — | Must include explicit `width` and `height` — native views have no intrinsic size. | > **Important:** Always set `style={{ width: N, height: N }}`. Without explicit size the view collapses. ### Example ```tsx import { ViroVirtualJoystick } from "@reactvision/react-viro"; const ctrl = { x: 0, y: 0 }; // module-level, shared with game loop { ctrl.x = e.nativeEvent.x; ctrl.y = e.nativeEvent.y; }} style={{ width: 120, height: 120 }} />; ``` ### Architecture note Touch events never cross the JS bridge. The native view (iOS `UIView`, Android `View`) computes normalized stick deflection and writes directly to a `VROInputState` via `VROVirtualControllerRegistry`. The `onStickChange` callback is a secondary, optional JS notification fired in the same tick. ``` -------------------------------- ### Push Scene from Inside VR Scene Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md Use the `sceneNavigator` prop passed to a VR scene to push new scenes. This method avoids the need for bridges or refs. ```tsx export function MyVRScene({ sceneNavigator }: any) { return ( sceneNavigator.push({ scene: DetailScene })}> {/* … */} ); } ``` -------------------------------- ### StreamingAudioManager API Reference Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md This section details the methods available for managing real-time PCM audio streaming using the StreamingAudioManager. ```APIDOC ## StreamingAudioManager Runtime PCM audio streaming. Feed raw float32 PCM chunks from any source (TTS, synthesizer, game engine audio) and play them in real time. ### Import ```typescript import { StreamingAudioManager } from "@reactvision/react-viro"; ``` ### API #### `create(playerId: string): void` **Description:** Creates a named audio player. **Method:** `create` **Parameters:** * `playerId` (string) - Required - A unique identifier for the audio player. #### `beginStreaming(playerId: string, sampleRate: number, channels: number): void` **Description:** Begins the audio streaming configuration for a player. Must be called before `pushSamples`. **Method:** `beginStreaming` **Parameters:** * `playerId` (string) - Required - The identifier of the audio player. * `sampleRate` (number) - Required - The sample rate of the audio stream (e.g., 22050, 44100, 48000). * `channels` (number) - Required - The number of audio channels (1 for mono, 2 for stereo). #### `play(playerId: string): void` **Description:** Starts playback for the specified audio player. **Method:** `play` **Parameters:** * `playerId` (string) - Required - The identifier of the audio player. #### `pause(playerId: string): void` **Description:** Pauses playback for the specified audio player. **Method:** `pause` **Parameters:** * `playerId` (string) - Required - The identifier of the audio player. #### `setVolume(playerId: string, volume: number): void` **Description:** Sets the playback volume for the specified audio player. **Method:** `setVolume` **Parameters:** * `playerId` (string) - Required - The identifier of the audio player. * `volume` (number) - Required - The volume level, ranging from 0.0 (silent) to 1.0 (maximum). #### `setMuted(playerId: string, muted: boolean): void` **Description:** Mutes or unmutes the specified audio player. **Method:** `setMuted` **Parameters:** * `playerId` (string) - Required - The identifier of the audio player. * `muted` (boolean) - Required - `true` to mute, `false` to unmute. #### `pushSamples(playerId: string, base64Samples: string): void` **Description:** Pushes audio data to the player's buffer. Samples should be base64-encoded float32 PCM, interleaved, and little-endian. **Method:** `pushSamples` **Parameters:** * `playerId` (string) - Required - The identifier of the audio player. * `base64Samples` (string) - Required - The base64-encoded audio sample data. #### `destroy(playerId: string): void` **Description:** Releases all resources associated with the specified audio player. **Method:** `destroy` **Parameters:** * `playerId` (string) - Required - The identifier of the audio player. ### Correct Call Order Example ```typescript // 1. Create StreamingAudioManager.create("voice"); // 2. Configure stream format StreamingAudioManager.beginStreaming("voice", 22050, 1); // 3. Pre-fill buffer BEFORE play StreamingAudioManager.pushSamples("voice", firstChunkBase64); // 4. Start playback StreamingAudioManager.play("voice"); // 5. Continue pushing on each chunk StreamingAudioManager.pushSamples("voice", nextChunkBase64); // 6. Clean up StreamingAudioManager.destroy("voice"); ``` > **Critical:** Call `pushSamples` before `play`. If the ring buffer is empty when playback starts, the audio thread will underrun. ### Encoding Samples Example ```typescript function float32ToBase64(samples: Float32Array): string { const bytes = new Uint8Array(samples.buffer); let binary = ""; bytes.forEach((b) => (binary += String.fromCharCode(b))); return btoa(binary); } ``` ``` -------------------------------- ### Enable VR Renderer Flags Source: https://github.com/reactvision/viro/blob/main/docs/QUEST_SETUP.md Use `passthroughEnabled`, `handTrackingEnabled`, `hdrEnabled`, and `bloomEnabled` props on `ViroXRSceneNavigator` to control VR rendering features. These props are automatically passed to the native `ViroVRSceneNavigator`. ```tsx ``` -------------------------------- ### Verify ARCore Weak Linking Flags Source: https://github.com/reactvision/viro/blob/main/readmes/IOS_CONFIGURATION.md Check your app target's xcconfig file for weak framework flags related to ARCore. This confirms that ARCore frameworks are set to be weakly linked. ```bash # Check your app target's xcconfig for weak framework flags cat ios/Pods/Target\ Support\ Files/Pods-YourApp/Pods-YourApp.debug.xcconfig | grep ARCore ``` -------------------------------- ### iOS Camera Permission Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md Add the camera usage description to your iOS Info.plist file. This informs the user why the camera permission is needed. ```xml NSCameraUsageDescription Used to display live camera feed in AR scenes ``` -------------------------------- ### Typical AR Selfie Pattern Source: https://github.com/reactvision/viro/blob/main/docs/ViroCameraTexture.md A common pattern for creating an AR selfie experience. It uses `ViroCameraTexture` to capture the front camera feed and displays it on a `ViroQuad` positioned in front of the user. The `onCameraReady` callback is used to control the visibility of the quad. ```tsx import React, { useState } from 'react'; import { ViroARScene, ViroARSceneNavigator, ViroQuad, ViroCameraTexture, ViroMaterials, } from '@reactvision/react-viro'; ViroMaterials.createMaterials({ selfie: { lightingModel: 'Constant' }, }); function SelfieARScene() { const [ready, setReady] = useState(false); return ( {/* A portrait-ratio quad floating in front of the user */} setReady(true)} onError={(e) => console.error('Camera error:', e.nativeEvent.error)} /> ); } export default function App() { return ( ); } ``` -------------------------------- ### ViroWorldMeshConfig and ViroWorldMeshStats Types Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Defines the configuration options for world mesh generation and the statistics returned when the mesh is updated. Use `worldMeshConfig` for fine-grained control over physics and debug drawing. ```typescript type ViroWorldMeshConfig = { // Physics mesh simplification physicsCellSize?: number; // Vertex clustering cell (default: 0.10 m). 0 = disabled. physicsMaxTriangles?: number; // Stride decimation limit. 0 = no limit. // Debug wireframe debugDrawEnabled?: boolean; // default: true debugDrawDepthTest?: boolean; // occluded by real surfaces (default: true) debugDrawMaxTriangles?: number; // default: 1000 debugDrawLineThickness?: number; // default: 0.001 m }; type ViroWorldMeshStats = { vertexCount: number; triangleCount: number; source: "lidar" | "monocular" | "plane"; }; ``` -------------------------------- ### Import ViroGameLoop Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Import the ViroGameLoop component from the @reactvision/react-viro package. ```tsx import { ViroGameLoop } from "@reactvision/react-viro"; ``` -------------------------------- ### Physics Usage with ViroARScene Source: https://github.com/reactvision/viro/blob/main/docs/PLATFORM_EXTENSIONS.md Enables physics simulation in your AR scene by adding a `physicsWorld` prop. This is required for JS physics objects to interact with the AR environment. ```tsx ```