import React from 'react'; import { requireNativeComponent, ViewStyle, NativeSyntheticEvent, UIManager, findNodeHandle, Platform } from 'react-native'; import { ARTrackingState, ARPlaneEvent, ARObjectEvent, ARTapEvent, ARDepthUpdateEvent, ARObjectModel, ARLightEstimationEvent, ARLightEstimationMode, ARLightConfiguration, ARShadowConfiguration, ARShadowQuality } from './types'; 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 ComponentName = 'ARView'; // Check if the native module exists through UIManager const isTurboModuleEnabled = global.__turboModuleProxy != null; const ARViewNative = isTurboModuleEnabled ? require('./NativeARModule').default.getViewManager(ComponentName) : UIManager.getViewManagerConfig(ComponentName) != null ? requireNativeComponent(ComponentName) : () => { throw new Error(LINKING_ERROR); }; export interface ARViewProps { /** * Style for the AR view */ style?: ViewStyle; /** * Callback for tracking state changes */ onTrackingUpdated?: (state: ARTrackingState) => void; /** * Callback when a new plane is detected */ onPlaneDetected?: (event: ARPlaneEvent) => void; /** * Callback when an object is placed */ onObjectPlaced?: (event: ARObjectEvent) => void; /** * Callback when the user taps */ onTap?: (event: ARTapEvent) => void; /** * Whether to place an object when the user taps */ placeOnTap?: boolean; /** * Scale factor for placed objects */ objectScale?: number; /** * Whether to enable depth-based occlusion */ enableDepthOcclusion?: boolean; /** * Whether to visualize depth information for debugging */ occlusionDebugMode?: boolean; /** * Callback with depth information at the center of the screen */ onDepthUpdated?: (event: ARDepthUpdateEvent) => void; /** * Whether to show AR feature points */ showFeaturePoints?: boolean; /** * Whether to visualize detected planes */ showPlanes?: boolean; /** * Default 3D model to place on tap * If not provided, a default cube model will be used */ defaultModel?: ARObjectModel; /** * Whether to allow model manipulation (rotation, scaling) */ allowModelManipulation?: boolean; /** * Whether to enable model shadows (deprecated, use shadowConfiguration instead) */ enableShadows?: boolean; /** * Advanced shadow configuration for rendering */ shadowConfiguration?: ARShadowConfiguration; /** * Maximum number of models that can be placed * Set to 0 for unlimited */ maxModels?: number; /** * Whether to automatically estimate lighting for realistic rendering * (deprecated, use lightingConfiguration instead) */ enableLightEstimation?: boolean; /** * Advanced lighting configuration for adaptive lighting */ lightingConfiguration?: { /** * Light estimation mode for environmental lighting */ estimationMode?: ARLightEstimationMode; /** * Additional manual lights to add to the scene */ additionalLights?: ARLightConfiguration[]; /** * Whether to debug visualize the lights */ debugVisualization?: boolean; /** * Whether to use environment probes for PBR reflections */ useEnvironmentProbes?: boolean; }; /** * Callback for light estimation updates */ onLightEstimationUpdated?: (event: ARLightEstimationEvent) => void; /** * Callback when model manipulation starts (selection, moving, etc.) */ onModelManipulationStart?: (objectId: string) => void; /** * Callback when model manipulation ends */ onModelManipulationEnd?: (objectId: string) => void; /** * Callback when a model fails to load */ onModelLoadError?: (error: {modelUri: string, message: string}) => void; } interface NativeARViewProps extends ARViewProps { onTrackingUpdatedNative?: (event: NativeSyntheticEvent) => void; onPlaneDetectedNative?: (event: NativeSyntheticEvent) => void; onObjectPlacedNative?: (event: NativeSyntheticEvent) => void; onTapNative?: (event: NativeSyntheticEvent) => void; onDepthUpdatedNative?: (event: NativeSyntheticEvent) => void; onLightEstimationUpdatedNative?: (event: NativeSyntheticEvent) => void; onModelManipulationStartNative?: (event: NativeSyntheticEvent<{objectId: string}>) => void; onModelManipulationEndNative?: (event: NativeSyntheticEvent<{objectId: string}>) => void; onModelLoadErrorNative?: (event: NativeSyntheticEvent<{modelUri: string, message: string}>) => void; } /** * A React Native component that displays AR content */ export const ARView: React.FC = ({ onTrackingUpdated, onPlaneDetected, onObjectPlaced, onTap, onDepthUpdated, onLightEstimationUpdated, onModelManipulationStart, onModelManipulationEnd, onModelLoadError, placeOnTap = true, objectScale = 1.0, enableDepthOcclusion = false, occlusionDebugMode = false, showFeaturePoints = false, showPlanes = false, defaultModel = undefined, allowModelManipulation = true, enableShadows = true, shadowConfiguration = undefined, maxModels = 0, enableLightEstimation = true, lightingConfiguration = undefined, ...props }) => { const viewRef = React.useRef(null); const onTrackingUpdatedNative = (event: NativeSyntheticEvent) => { onTrackingUpdated?.(event.nativeEvent); }; const onPlaneDetectedNative = (event: NativeSyntheticEvent) => { onPlaneDetected?.(event.nativeEvent); }; const onObjectPlacedNative = (event: NativeSyntheticEvent) => { onObjectPlaced?.(event.nativeEvent); }; const onTapNative = (event: NativeSyntheticEvent) => { onTap?.(event.nativeEvent); }; const onDepthUpdatedNative = (event: NativeSyntheticEvent) => { onDepthUpdated?.(event.nativeEvent); }; const onLightEstimationUpdatedNative = (event: NativeSyntheticEvent) => { onLightEstimationUpdated?.(event.nativeEvent); }; const onModelManipulationStartNative = (event: NativeSyntheticEvent<{objectId: string}>) => { onModelManipulationStart?.(event.nativeEvent.objectId); }; const onModelManipulationEndNative = (event: NativeSyntheticEvent<{objectId: string}>) => { onModelManipulationEnd?.(event.nativeEvent.objectId); }; const onModelLoadErrorNative = (event: NativeSyntheticEvent<{modelUri: string, message: string}>) => { onModelLoadError?.(event.nativeEvent); }; // Preload the default model if provided React.useEffect(() => { if (defaultModel && viewRef.current) { // This would call a native method to preload the model const nodeHandle = findNodeHandle(viewRef.current); if (nodeHandle && UIManager.dispatchViewManagerCommand) { UIManager.dispatchViewManagerCommand( nodeHandle, // @ts-ignore - Command name mapping handled by native side UIManager.getViewManagerConfig(ComponentName).Commands.preloadDefaultModel, [defaultModel] ); } } }, [defaultModel]); return ( ); };