import { NativeModules, Platform } from 'react-native'; import { ARObjectModel, ARSessionOptions, ARFeatures, ARModelFormat, ARModelMaterial, ARLightConfiguration, ARShadowConfiguration, ARLightEstimation, ARLightEstimationMode } from './types'; import { NativeARModule } from './NativeARModule'; import { ARHapticFeedback } from './ARHapticFeedback'; // Check if the native module exists, otherwise show a helpful error message const LINKING_ERROR = `The package 'react-native-ar' doesn't seem to be linked. Make sure: \n\n` + '- You have run "npm install" or "yarn"\n' + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n'; const isTurboModuleEnabled = global.__turboModuleProxy != null; const NativeAR = isTurboModuleEnabled ? NativeARModule : NativeModules.ARModule ? NativeModules.ARModule : new Proxy( {}, { get() { throw new Error(LINKING_ERROR); }, } ); /** * Manager for controlling AR sessions */ export class ARSessionManager { /** * Starts the AR session * @param options Optional configuration for the AR session */ static startSession(options: ARSessionOptions = {}): Promise { return NativeAR.startSession(options); } /** * Pauses the AR session */ static pauseSession(): Promise { return NativeAR.pauseSession(); } /** * Resets the AR session */ static resetSession(): Promise { return NativeAR.resetSession(); } /** * Checks if AR is supported on the device */ static isSupported(): Promise { return NativeAR.isSupported(); } /** * Adds a 3D object to the scene with haptic feedback * @param model Model information */ static addObject(model: ARObjectModel): Promise { return NativeAR.addObject(model) .then((objectId: string) => { // Trigger haptic feedback when object is placed ARHapticFeedback.objectPlaced(); return objectId; }); } /** * Removes a 3D object from the scene with haptic feedback * @param id Object ID to remove */ static removeObject(id: string): Promise { return NativeAR.removeObject(id) .then(() => { // Trigger haptic feedback when object is removed ARHapticFeedback.objectRemoved(); }); } /** * Moves an object to a new position with haptic feedback * @param id Object ID to move * @param position New position */ static moveObject(id: string, position: { x: number; y: number; z: number }): Promise { return NativeAR.moveObject(id, position) .then(() => { // Subtle haptic feedback when object is moved ARHapticFeedback.objectMoved(); }); } /** * Rotates an object with haptic feedback * @param id Object ID to rotate * @param rotation New rotation in radians */ static rotateObject(id: string, rotation: { x: number; y: number; z: number }): Promise { return NativeAR.rotateObject(id, rotation) .then(() => { // Subtle haptic feedback when object is rotated ARHapticFeedback.objectRotated(); }); } /** * Scales an object with haptic feedback * @param id Object ID to scale * @param scale New scale factor */ static scaleObject(id: string, scale: number): Promise { return NativeAR.scaleObject(id, scale) .then(() => { // Subtle haptic feedback when object is scaled ARHapticFeedback.objectScaled(); }); } /** * Gets information about the object's current properties * @param id Object ID * @returns Current properties of the object */ static getObjectProperties(id: string): Promise<{ position: { x: number; y: number; z: number }; rotation: { x: number; y: number; z: number }; scale: number; }> { return NativeAR.getObjectProperties(id); } /** * Returns information about which AR features are supported on the current device * @returns Object containing feature support information */ static getSupportedFeatures(): Promise { return NativeAR.getSupportedFeatures(); } /** * Sets a property on the AR session * @param propertyName Name of the property to set * @param value Value to set */ static setSessionProperty(propertyName: string, value: any): Promise { return NativeAR.setSessionProperty(propertyName, value); } /** * Gets the current value of a session property * @param propertyName Name of the property to get * @returns Current value of the property */ static getSessionProperty(propertyName: string): Promise { return NativeAR.getSessionProperty(propertyName); } /** * For Android: Prompts the user to install ARCore if it's not already installed * @returns Promise that resolves to true if ARCore is installed or the user has agreed to install it */ static promptARCoreInstall(): Promise { if (Platform.OS !== 'android') { return Promise.resolve(true); } return NativeAR.promptARCoreInstall(); } /** * Loads a 3D model for use in the AR scene * This preloads the model to improve performance when it's actually placed in the scene * @param modelInfo Information about the 3D model to load * @returns Promise that resolves to a model ID that can be used for placement */ static preloadModel(modelInfo: ARObjectModel): Promise { if (!modelInfo.uri) { return Promise.reject(new Error('Model URI is required')); } // Automatically determine format if not provided if (!modelInfo.format) { const extension = modelInfo.uri.split('.').pop()?.toLowerCase(); switch (extension) { case 'glb': modelInfo.format = ARModelFormat.GLB; break; case 'gltf': modelInfo.format = ARModelFormat.GLTF; break; case 'obj': modelInfo.format = ARModelFormat.OBJ; break; case 'usdz': modelInfo.format = ARModelFormat.USDZ; break; case 'scnassets': modelInfo.format = ARModelFormat.SCNASSETS; break; default: console.warn(`Unknown model format for ${modelInfo.uri}, defaulting to GLB`); modelInfo.format = ARModelFormat.GLB; } } return NativeAR.preloadModel(modelInfo); } /** * Checks if a model is loaded and ready for use * @param modelId ID of the model returned from preloadModel * @returns Promise that resolves to true if the model is loaded */ static isModelLoaded(modelId: string): Promise { return NativeAR.isModelLoaded(modelId); } /** * Unloads a preloaded model to free up memory * @param modelId ID of the model to unload * @returns Promise that resolves when the model is unloaded */ static unloadModel(modelId: string): Promise { return NativeAR.unloadModel(modelId); } /** * Places a preloaded model in the AR scene with haptic feedback * @param modelId ID of the preloaded model * @param position Position to place the model * @param options Additional options for placement (rotation, scale) * @returns Promise that resolves to the object ID of the placed model */ static placeModel( modelId: string, position: { x: number; y: number; z: number }, options?: { rotation?: { x: number; y: number; z: number }; scale?: number; } ): Promise { return NativeAR.placeModel(modelId, position, options || {}) .then((objectId: string) => { // Provide success haptic feedback when model is placed ARHapticFeedback.objectPlaced(); return objectId; }); } /** * Retrieves information about the available animations for a model * @param modelId ID of the model returned from preloadModel * @returns Promise that resolves to an array of animation names */ static getModelAnimations(modelId: string): Promise { return NativeAR.getModelAnimations(modelId); } /** * Plays an animation on a placed model * @param objectId ID of the placed object * @param animationName Name of the animation to play * @param options Optional animation parameters (loop, speed) * @returns Promise that resolves when the animation starts */ static playAnimation( objectId: string, animationName: string, options?: { loop?: boolean; speed?: number } ): Promise { return NativeAR.playAnimation(objectId, animationName, options || {}); } /** * Stops all animations on a placed model * @param objectId ID of the placed object * @returns Promise that resolves when the animations are stopped */ static stopAnimations(objectId: string): Promise { return NativeAR.stopAnimations(objectId); } /** * Updates the material properties of a placed model * @param objectId ID of the placed object * @param nodeName Name of the node/mesh to update (use '' for the entire model) * @param material Material properties to update * @returns Promise that resolves when the material is updated */ static updateMaterial( objectId: string, nodeName: string, material: ARModelMaterial ): Promise { return NativeAR.updateMaterial(objectId, nodeName, material); } /** * Gets information about a 3D model, such as node names and materials * @param modelId ID of the preloaded model * @returns Promise that resolves to model information */ static getModelInfo(modelId: string): Promise<{ nodes: string[]; materials: Record; boundingBox: { min: { x: number; y: number; z: number }; max: { x: number; y: number; z: number }; }; }> { return NativeAR.getModelInfo(modelId); } /** * Gets the current light estimation data from the AR session * @returns Promise that resolves to light estimation data */ static getLightEstimation(): Promise { return NativeAR.getLightEstimation(); } /** * Sets the light estimation mode for the AR session * @param mode Light estimation mode */ static setLightEstimationMode(mode: ARLightEstimationMode): Promise { return NativeAR.setLightEstimationMode(mode); } /** * Adds a custom light to the AR scene * @param light Light configuration * @returns Promise that resolves to a unique ID for the light */ static addLight(light: ARLightConfiguration): Promise { return NativeAR.addLight(light); } /** * Updates an existing light's properties * @param lightId ID of the light to update * @param properties Updated light properties */ static updateLight( lightId: string, properties: Partial ): Promise { return NativeAR.updateLight(lightId, properties); } /** * Removes a custom light from the scene * @param lightId ID of the light to remove */ static removeLight(lightId: string): Promise { return NativeAR.removeLight(lightId); } /** * Gets all custom lights in the scene * @returns Promise that resolves to an array of light IDs and their configurations */ static getLights(): Promise<{id: string, config: ARLightConfiguration}[]> { return NativeAR.getLights(); } /** * Configures shadow rendering for the AR scene * @param configuration Shadow configuration */ static configureShadows(configuration: ARShadowConfiguration): Promise { return NativeAR.configureShadows(configuration); } /** * Gets the current shadow configuration * @returns Promise that resolves to the current shadow configuration */ static getShadowConfiguration(): Promise { return NativeAR.getShadowConfiguration(); } /** * Creates an environment probe at the specified position * Environment probes capture the surroundings for realistic reflections * @param position Position of the probe * @param size Size of the probe's capture area * @returns Promise that resolves to the probe ID */ static createEnvironmentProbe( position: { x: number; y: number; z: number }, size?: { width: number; height: number; depth: number } ): Promise { return NativeAR.createEnvironmentProbe(position, size || { width: 5, height: 5, depth: 5 }); } /** * Updates an environment probe's position * @param probeId ID of the probe to update * @param position New position for the probe */ static updateEnvironmentProbe( probeId: string, position: { x: number; y: number; z: number } ): Promise { return NativeAR.updateEnvironmentProbe(probeId, position); } /** * Removes an environment probe * @param probeId ID of the probe to remove */ static removeEnvironmentProbe(probeId: string): Promise { return NativeAR.removeEnvironmentProbe(probeId); } /** * Sets whether objects should cast shadows * @param objectId ID of the object * @param castsShadow Whether the object should cast shadows */ static setObjectCastsShadow(objectId: string, castsShadow: boolean): Promise { return NativeAR.setObjectCastsShadow(objectId, castsShadow); } /** * Sets whether objects should receive shadows * @param objectId ID of the object * @param receivesShadow Whether the object should receive shadows */ static setObjectReceivesShadow(objectId: string, receivesShadow: boolean): Promise { return NativeAR.setObjectReceivesShadow(objectId, receivesShadow); } /** * Performs a hit test at the given position and returns information about what was hit * @param position Position to test * @returns Promise that resolves to hit test results */ static async performHitTest(position: { x: number; y: number; z: number }): Promise> { try { // Call the native module to perform hit test return await NativeAR.performHitTest(position); } catch (error) { console.error('Error performing hit test:', error); // Return a simplified result if the native method fails return [{ type: position.y < 0.3 ? 'horizontal' : 'vertical', distance: 1.0, position: { ...position } }]; } } }