From 069c684376bc00111b6470ff468a2fd78d5c616e Mon Sep 17 00:00:00 2001 From: liqiang Date: Mon, 20 Oct 2025 17:11:55 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0BabylonReactNative?= =?UTF-8?q?=E6=8C=87=E5=AF=BC=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: liqiang --- en/BabylonReactNative.md | 405 +++++++++++++++++++++++++++++++++++ en/README_EN.md | 2 +- zh-cn/BabylonReactNative.md | 408 ++++++++++++++++++++++++++++++++++++ zh-cn/README.md | 1 + 4 files changed, 815 insertions(+), 1 deletion(-) create mode 100644 en/BabylonReactNative.md create mode 100644 zh-cn/BabylonReactNative.md diff --git a/en/BabylonReactNative.md b/en/BabylonReactNative.md new file mode 100644 index 00000000..7990774c --- /dev/null +++ b/en/BabylonReactNative.md @@ -0,0 +1,405 @@ +> Template version: v0.2.2 + +

+

BabylonReactNative

+

+

+ + Supported platforms + + + License + +

+ +> [!TIP] [Github address](https://github.com/react-native-oh-library/BabylonReactNative) + +## Installation and Usage + +Find the matching version information in the release address of a third-party library: [@react-native-oh-tpl/babylonjs-react-native and @react-native-oh-tpl/babylonjs-react-native-harmony Releases](https://github.com/react-native-oh-library/BabylonReactNative/releases). + +Go to the project directory and execute the following instruction: + + + + + +#### **npm** + +```bash +npm install @react-native-oh-tpl/babylonjs-react-native +npm install @react-native-oh-tpl/babylonjs-react-native-harmony +``` + +#### **yarn** + +```bash +yarn add @react-native-oh-tpl/babylonjs-react-native +yarn add @react-native-oh-tpl/babylonjs-react-native-harmony +``` + + + +The following code shows the basic use scenario of the repository: + +> [!WARNING] The name of the imported repository remains unchanged. + +```js +import React, { useState, FunctionComponent, useEffect, useCallback } from 'react'; +import { StatusBar, Button, View, Text, ViewProps, Image } from 'react-native'; +import { SafeAreaView } from '@react-native-oh-tpl/react-native-safe-area-context'; + +import { EngineView, useEngine, EngineViewCallbacks } from '@babylonjs/react-native'; + import { Scene, Vector3, ArcRotateCamera, Camera, WebXRSessionManager, SceneLoader, TransformNode, DeviceSourceManager, DeviceType, PointerInput, WebXRTrackingState, IMouseEvent } from '@babylonjs/core'; +import '@babylonjs/loaders'; +import Slider from '@react-native-community/slider'; + +const EngineScreen: FunctionComponent = (props: ViewProps) => { + const defaultScale = 1; + const enableSnapshots = false; + // const enableSnapshots = true; + + const engine = useEngine(); + const [toggleView, setToggleView] = useState(false); + const [camera, setCamera] = useState(); + const [rootNode, setRootNode] = useState(); + const [scene, setScene] = useState(); + const [xrSession, setXrSession] = useState(); + const [scale, setScale] = useState(defaultScale); + const [snapshotData, setSnapshotData] = useState(); + const [engineViewCallbacks, setEngineViewCallbacks] = useState(); + const [trackingState, setTrackingState] = useState(); + + useEffect(() => { + if (engine) { + const scene = new Scene(engine); + setScene(scene); + scene.createDefaultCamera(true); + (scene.activeCamera as ArcRotateCamera).beta -= Math.PI / 8; + setCamera(scene.activeCamera!); + scene.createDefaultLight(true); + const rootNode = new TransformNode('Root Container', scene); + setRootNode(rootNode); + + const deviceSourceManager = new DeviceSourceManager(engine); + const handlePointerInput = (event: IMouseEvent) => { + if (event.inputIndex === PointerInput.Move && event.movementX) { + rootNode.rotate(Vector3.Down(), event.movementX * 0.005); + }; + }; + + deviceSourceManager.onDeviceConnectedObservable.add(device => { + if (device.deviceType === DeviceType.Touch) { + const touch = deviceSourceManager.getDeviceSource(device.deviceType, device.deviceSlot)!; + touch.onInputChangedObservable.add(touchEvent => { + handlePointerInput(touchEvent); + }); + } else if (device.deviceType === DeviceType.Mouse) { + const mouse = deviceSourceManager.getDeviceSource(device.deviceType, device.deviceSlot)!; + mouse.onInputChangedObservable.add(mouseEvent => { + if (mouse.getInput(PointerInput.LeftClick)) { + handlePointerInput(mouseEvent); + } + }); + } + }); + + const transformContainer = new TransformNode('Transform Container', scene); + transformContainer.parent = rootNode; + transformContainer.scaling.scaleInPlace(0.2); + transformContainer.position.y -= .2; + + scene.beforeRender = function () { + transformContainer.rotate(Vector3.Up(), 0.005 * scene.getAnimationRatio()); + }; + + SceneLoader.ImportMeshAsync('', 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxAnimated/glTF-Binary/BoxAnimated.glb').then(result => { + const mesh = result.meshes[0]; + mesh.parent = transformContainer; + }); + } + }, [engine]); + + useEffect(() => { + if (rootNode) { + rootNode.scaling = new Vector3(scale, scale, scale); + } + }, [rootNode, scale]); + + const trackingStateToString = (trackingState: WebXRTrackingState | undefined) : string => { + return trackingState === undefined ? '' : WebXRTrackingState[trackingState]; + }; + + const onToggleXr = useCallback(() => { + (async () => { + if (xrSession) { + await xrSession.exitXRAsync(); + } else { + if (rootNode !== undefined && scene !== undefined) { + const xr = await scene.createDefaultXRExperienceAsync({ disableDefaultUI: true, disableTeleportation: true }) + const session = await xr.baseExperience.enterXRAsync('immersive-ar', 'unbounded', xr.renderTarget); + setXrSession(session); + session.onXRSessionEnded.add(() => { + setXrSession(undefined); + setTrackingState(undefined); + }) + + setTrackingState(xr.baseExperience.camera.trackingState); + xr.baseExperience.camera.onTrackingStateChanged.add((newTrackingState) => { + setTrackingState(newTrackingState); + }); + + // TODO: Figure out why getFrontPosition stopped working + //box.position = (scene.activeCamera as TargetCamera).getFrontPosition(2); + const cameraRay = scene.activeCamera!.getForwardRay(1); + rootNode.position = cameraRay.origin.add(cameraRay.direction.scale(cameraRay.length)); + rootNode.rotate(Vector3.Up(), 3.14159); + } + } + })(); + }, [rootNode, scene, xrSession]); + + const onInitialized = useCallback(async(engineViewCallbacks: EngineViewCallbacks) => { + setEngineViewCallbacks(engineViewCallbacks); + }, [engine]); + + const onSnapshot = useCallback(async () => { + if (engineViewCallbacks) { + setSnapshotData('data:image/jpeg;base64,' + await engineViewCallbacks.takeSnapshot()); + } + }, [engineViewCallbacks]); + + return ( + <> + +