Skip to content

Commit 197fe21

Browse files
kelsettido64
andauthored
feat(test app): add new RNTA-based test app (#641)
* baseline: adding RNTA vanilla, it works * wire in the babylon stuff * a couple of temp files * minor cleanup, bump to latest 73 and RNTA * add a quick readme * Update Apps/BRNPlayground/macos/Podfile Co-authored-by: Tommy Nguyen <[email protected]> * Update Apps/BRNPlayground/postinstall.js Co-authored-by: Tommy Nguyen <[email protected]> * Update Apps/BRNPlayground/.gitignore Co-authored-by: Tommy Nguyen <[email protected]> * get CMake to build * add xcworkspace * bump to latest RN and RNTA --------- Co-authored-by: Tommy Nguyen <[email protected]>
1 parent 8ede856 commit 197fe21

26 files changed

+20247
-0
lines changed

Apps/BRNPlayground/.gitignore

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
*.binlog
2+
*.hprof
3+
*.zip
4+
.DS_Store
5+
.gradle/
6+
.idea/
7+
.vs/
8+
.xcode.env
9+
Pods/
10+
build/
11+
dist/
12+
local.properties
13+
msbuild.binlog
14+
node_modules/

Apps/BRNPlayground/.watchmanconfig

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

Apps/BRNPlayground/App.tsx

+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
/**
2+
* Generated with the TypeScript template
3+
* https://github.com/react-native-community/react-native-template-typescript
4+
*
5+
* @format
6+
*/
7+
8+
import React, {
9+
useState,
10+
FunctionComponent,
11+
useEffect,
12+
useCallback,
13+
} from "react";
14+
import {
15+
SafeAreaView,
16+
StatusBar,
17+
Button,
18+
View,
19+
Text,
20+
ViewProps,
21+
Image,
22+
} from "react-native";
23+
24+
import {
25+
EngineView,
26+
useEngine,
27+
EngineViewCallbacks,
28+
} from "@babylonjs/react-native";
29+
import {
30+
Scene,
31+
Vector3,
32+
ArcRotateCamera,
33+
Camera,
34+
WebXRSessionManager,
35+
SceneLoader,
36+
TransformNode,
37+
DeviceSourceManager,
38+
DeviceType,
39+
PointerInput,
40+
WebXRTrackingState,
41+
IMouseEvent,
42+
} from "@babylonjs/core";
43+
import "@babylonjs/loaders";
44+
import Slider from "@react-native-community/slider";
45+
46+
const EngineScreen: FunctionComponent<ViewProps> = (props: ViewProps) => {
47+
const defaultScale = 1;
48+
const enableSnapshots = false;
49+
50+
const engine = useEngine();
51+
const [toggleView, setToggleView] = useState(false);
52+
const [camera, setCamera] = useState<Camera>();
53+
const [rootNode, setRootNode] = useState<TransformNode>();
54+
const [scene, setScene] = useState<Scene>();
55+
const [xrSession, setXrSession] = useState<WebXRSessionManager>();
56+
const [scale, setScale] = useState<number>(defaultScale);
57+
const [snapshotData, setSnapshotData] = useState<string>();
58+
const [engineViewCallbacks, setEngineViewCallbacks] =
59+
useState<EngineViewCallbacks>();
60+
const [trackingState, setTrackingState] = useState<WebXRTrackingState>();
61+
62+
useEffect(() => {
63+
if (engine) {
64+
const scene = new Scene(engine);
65+
setScene(scene);
66+
scene.createDefaultCamera(true);
67+
(scene.activeCamera as ArcRotateCamera).beta -= Math.PI / 8;
68+
setCamera(scene.activeCamera!);
69+
scene.createDefaultLight(true);
70+
const rootNode = new TransformNode("Root Container", scene);
71+
setRootNode(rootNode);
72+
73+
const deviceSourceManager = new DeviceSourceManager(engine);
74+
const handlePointerInput = (event: IMouseEvent) => {
75+
if (event.inputIndex === PointerInput.Move && event.movementX) {
76+
rootNode.rotate(Vector3.Down(), event.movementX * 0.005);
77+
}
78+
};
79+
80+
deviceSourceManager.onDeviceConnectedObservable.add((device) => {
81+
if (device.deviceType === DeviceType.Touch) {
82+
const touch = deviceSourceManager.getDeviceSource(
83+
device.deviceType,
84+
device.deviceSlot
85+
)!;
86+
touch.onInputChangedObservable.add((touchEvent) => {
87+
handlePointerInput(touchEvent);
88+
});
89+
} else if (device.deviceType === DeviceType.Mouse) {
90+
const mouse = deviceSourceManager.getDeviceSource(
91+
device.deviceType,
92+
device.deviceSlot
93+
)!;
94+
mouse.onInputChangedObservable.add((mouseEvent) => {
95+
if (mouse.getInput(PointerInput.LeftClick)) {
96+
handlePointerInput(mouseEvent);
97+
}
98+
});
99+
}
100+
});
101+
102+
const transformContainer = new TransformNode(
103+
"Transform Container",
104+
scene
105+
);
106+
transformContainer.parent = rootNode;
107+
transformContainer.scaling.scaleInPlace(0.2);
108+
transformContainer.position.y -= 0.2;
109+
110+
scene.beforeRender = function () {
111+
transformContainer.rotate(
112+
Vector3.Up(),
113+
0.005 * scene.getAnimationRatio()
114+
);
115+
};
116+
117+
SceneLoader.ImportMeshAsync(
118+
"",
119+
"https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxAnimated/glTF-Binary/BoxAnimated.glb"
120+
).then((result) => {
121+
const mesh = result.meshes[0];
122+
mesh.parent = transformContainer;
123+
});
124+
}
125+
}, [engine]);
126+
127+
useEffect(() => {
128+
if (rootNode) {
129+
rootNode.scaling = new Vector3(scale, scale, scale);
130+
}
131+
}, [rootNode, scale]);
132+
133+
const trackingStateToString = (
134+
trackingState: WebXRTrackingState | undefined
135+
): string => {
136+
return trackingState === undefined ? "" : WebXRTrackingState[trackingState];
137+
};
138+
139+
const onToggleXr = useCallback(() => {
140+
(async () => {
141+
if (xrSession) {
142+
await xrSession.exitXRAsync();
143+
} else {
144+
if (rootNode !== undefined && scene !== undefined) {
145+
const xr = await scene.createDefaultXRExperienceAsync({
146+
disableDefaultUI: true,
147+
disableTeleportation: true,
148+
});
149+
const session = await xr.baseExperience.enterXRAsync(
150+
"immersive-ar",
151+
"unbounded",
152+
xr.renderTarget
153+
);
154+
setXrSession(session);
155+
session.onXRSessionEnded.add(() => {
156+
setXrSession(undefined);
157+
setTrackingState(undefined);
158+
});
159+
160+
setTrackingState(xr.baseExperience.camera.trackingState);
161+
xr.baseExperience.camera.onTrackingStateChanged.add(
162+
(newTrackingState) => {
163+
setTrackingState(newTrackingState);
164+
}
165+
);
166+
167+
// TODO: Figure out why getFrontPosition stopped working
168+
//box.position = (scene.activeCamera as TargetCamera).getFrontPosition(2);
169+
const cameraRay = scene.activeCamera!.getForwardRay(1);
170+
rootNode.position = cameraRay.origin.add(
171+
cameraRay.direction.scale(cameraRay.length)
172+
);
173+
rootNode.rotate(Vector3.Up(), 3.14159);
174+
}
175+
}
176+
})();
177+
}, [rootNode, scene, xrSession]);
178+
179+
const onInitialized = useCallback(
180+
async (engineViewCallbacks: EngineViewCallbacks) => {
181+
setEngineViewCallbacks(engineViewCallbacks);
182+
},
183+
[engine]
184+
);
185+
186+
const onSnapshot = useCallback(async () => {
187+
if (engineViewCallbacks) {
188+
setSnapshotData(
189+
"data:image/jpeg;base64," + (await engineViewCallbacks.takeSnapshot())
190+
);
191+
}
192+
}, [engineViewCallbacks]);
193+
194+
return (
195+
<>
196+
<View style={props.style}>
197+
<Button
198+
title="Toggle EngineView"
199+
onPress={() => {
200+
setToggleView(!toggleView);
201+
}}
202+
/>
203+
<Button
204+
title={xrSession ? "Stop XR" : "Start XR"}
205+
onPress={onToggleXr}
206+
/>
207+
{!toggleView && (
208+
<View style={{ flex: 1 }}>
209+
{enableSnapshots && (
210+
<View style={{ flex: 1 }}>
211+
<Button title={"Take Snapshot"} onPress={onSnapshot} />
212+
<Image style={{ flex: 1 }} source={{ uri: snapshotData }} />
213+
</View>
214+
)}
215+
<EngineView
216+
camera={camera}
217+
onInitialized={onInitialized}
218+
displayFrameRate={true}
219+
antiAliasing={2}
220+
/>
221+
<Slider
222+
style={{
223+
position: "absolute",
224+
minHeight: 50,
225+
margin: 10,
226+
left: 0,
227+
right: 0,
228+
bottom: 0,
229+
}}
230+
minimumValue={0.2}
231+
maximumValue={2}
232+
step={0.01}
233+
value={defaultScale}
234+
onValueChange={setScale}
235+
/>
236+
<Text style={{ color: "yellow", position: "absolute", margin: 3 }}>
237+
{trackingStateToString(trackingState)}
238+
</Text>
239+
</View>
240+
)}
241+
{toggleView && (
242+
<View
243+
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
244+
>
245+
<Text style={{ fontSize: 24 }}>EngineView has been removed.</Text>
246+
<Text style={{ fontSize: 12 }}>
247+
Render loop stopped, but engine is still alive.
248+
</Text>
249+
</View>
250+
)}
251+
</View>
252+
</>
253+
);
254+
};
255+
256+
const App = () => {
257+
const [toggleScreen, setToggleScreen] = useState(false);
258+
259+
return (
260+
<>
261+
<StatusBar barStyle="dark-content" />
262+
<SafeAreaView style={{ flex: 1, backgroundColor: "white" }}>
263+
{!toggleScreen && <EngineScreen style={{ flex: 1 }} />}
264+
{toggleScreen && (
265+
<View
266+
style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
267+
>
268+
<Text style={{ fontSize: 24 }}>EngineScreen has been removed.</Text>
269+
<Text style={{ fontSize: 12 }}>
270+
Engine has been disposed, and will be recreated.
271+
</Text>
272+
</View>
273+
)}
274+
<Button
275+
title="Toggle EngineScreen"
276+
onPress={() => {
277+
setToggleScreen(!toggleScreen);
278+
}}
279+
/>
280+
</SafeAreaView>
281+
</>
282+
);
283+
};
284+
285+
export default App;

Apps/BRNPlayground/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# BabylonReactNative Playground App
2+
3+
This is a new example app for BabylonReactNative, based on `react-native-test-app`.
4+
5+
It's still getting fully wired up as a replacement, so if you are seeing this message it means that it's not in its final form yet!
6+
7+
To run it (on an Android device):
8+
9+
1. `npm i`
10+
2. `npm run android`
11+
12+
(assuming that you have your setup working for running [RN Android apps on device](https://reactnative.dev/docs/running-on-device?platform=android))
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
buildscript {
2+
def androidTestAppDir = "../node_modules/react-native-test-app/android"
3+
apply(from: "${androidTestAppDir}/dependencies.gradle")
4+
5+
repositories {
6+
mavenCentral()
7+
google()
8+
}
9+
10+
dependencies {
11+
getReactNativeDependencies().each { dependency ->
12+
classpath(dependency)
13+
}
14+
}
15+
}
16+
17+
allprojects {
18+
repositories {
19+
maven {
20+
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
21+
url("${rootDir}/../node_modules/react-native/android")
22+
}
23+
mavenCentral()
24+
google()
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Project-wide Gradle settings.
2+
3+
# IDE (e.g. Android Studio) users:
4+
# Gradle settings configured through the IDE *will override*
5+
# any settings specified in this file.
6+
7+
# For more details on how to configure your build environment visit
8+
# http://www.gradle.org/docs/current/userguide/build_environment.html
9+
10+
# Specifies the JVM arguments used for the Gradle Daemon. The setting is
11+
# particularly useful for configuring JVM memory settings for build performance.
12+
# This does not affect the JVM settings for the Gradle client VM.
13+
# The default is `-Xmx512m -XX:MaxMetaspaceSize=256m`.
14+
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
15+
16+
# When configured, Gradle will fork up to org.gradle.workers.max JVMs to execute
17+
# projects in parallel. To learn more about parallel task execution, see the
18+
# section on Gradle build performance:
19+
# https://docs.gradle.org/current/userguide/performance.html#parallel_execution.
20+
# Default is `false`.
21+
#org.gradle.parallel=true
22+
23+
# AndroidX package structure to make it clearer which packages are bundled with the
24+
# Android operating system, and which are packaged with your app's APK
25+
# https://developer.android.com/topic/libraries/support-library/androidx-rn
26+
android.useAndroidX=true
27+
# Automatically convert third-party libraries to use AndroidX
28+
android.enableJetifier=true
29+
# Jetifier randomly fails on these libraries
30+
android.jetifier.ignorelist=hermes-android
31+
32+
# Use this property to specify which architecture you want to build.
33+
# You can also override it from the CLI using
34+
# ./gradlew <task> -PreactNativeArchitectures=x86_64
35+
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
36+
37+
# Use this property to enable support to the new architecture.
38+
# This will allow you to use TurboModules and the Fabric render in
39+
# your application. You should enable this flag either if you want
40+
# to write custom TurboModules/Fabric components OR use libraries that
41+
# are providing them.
42+
# Note that this is incompatible with web debugging.
43+
#newArchEnabled=true
44+
45+
# Uncomment the line below if building react-native from source
46+
#ANDROID_NDK_VERSION=26.1.10909125
47+
48+
# Version of Kotlin to build against.
49+
#KOTLIN_VERSION=1.8.22
Binary file not shown.

0 commit comments

Comments
 (0)