Skip to content

Commit 8ed0d2d

Browse files
authored
Create dedicated XR view (#173)
1 parent f858dad commit 8ed0d2d

File tree

9 files changed

+140
-14
lines changed

9 files changed

+140
-14
lines changed

Modules/@babylonjs/react-native/android/src/main/cpp/BabylonNativeInterop.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ extern "C" JNIEXPORT void JNICALL Java_com_babylonreactnative_BabylonNativeInter
8282
Babylon::ResetView();
8383
}
8484

85+
extern "C" JNIEXPORT void JNICALL Java_com_babylonreactnative_BabylonNativeInterop_00024BabylonNative_updateXRView(JNIEnv* env, jclass obj, jobject surface)
86+
{
87+
ANativeWindow* windowPtr{};
88+
if (surface)
89+
{
90+
windowPtr = ANativeWindow_fromSurface(env, surface);
91+
}
92+
Babylon::UpdateXRView(windowPtr);
93+
}
94+
95+
extern "C" JNIEXPORT jboolean JNICALL Java_com_babylonreactnative_BabylonNativeInterop_00024BabylonNative_isXRActive(JNIEnv* env, jclass obj)
96+
{
97+
return Babylon::IsXRActive();
98+
}
99+
85100
extern "C" JNIEXPORT void JNICALL Java_com_babylonreactnative_BabylonNativeInterop_00024BabylonNative_setTouchButtonState(JNIEnv* env, jclass obj, jint pointerId, jboolean isDown, jint x, jint y)
86101
{
87102
Babylon::SetTouchButtonState(static_cast<uint32_t>(pointerId), isDown, static_cast<uint32_t>(x), static_cast<uint32_t>(y));

Modules/@babylonjs/react-native/android/src/main/java/com/babylonreactnative/BabylonNativeInterop.java

+10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ private static class BabylonNative {
2525
public static native void updateView(Surface surface);
2626
public static native void renderView();
2727
public static native void resetView();
28+
public static native void updateXRView(Surface surface);
29+
public static native boolean isXRActive();
2830
public static native void setTouchButtonState(int pointerId, boolean isDown, int x, int y);
2931
public static native void setTouchPosition(int pointerId, int x, int y);
3032
}
@@ -91,6 +93,14 @@ public static void resetView() {
9193
BabylonNative.resetView();
9294
}
9395

96+
public static void updateXRView(Surface surface) {
97+
BabylonNative.updateXRView(surface);
98+
}
99+
100+
public static boolean isXRActive() {
101+
return BabylonNative.isXRActive();
102+
}
103+
94104
public static void reportMotionEvent(MotionEvent motionEvent) {
95105
int maskedAction = motionEvent.getActionMasked();
96106
boolean isPointerDown = maskedAction == MotionEvent.ACTION_DOWN || maskedAction == MotionEvent.ACTION_POINTER_DOWN;

Modules/@babylonjs/react-native/android/src/main/java/com/babylonreactnative/EngineView.java

+53-11
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,54 @@
1212
import android.view.SurfaceHolder;
1313
import android.view.SurfaceView;
1414
import android.view.View;
15+
import android.widget.FrameLayout;
1516

1617
import com.facebook.react.bridge.ReactContext;
1718
import com.facebook.react.uimanager.UIManagerModule;
1819
import com.facebook.react.uimanager.events.EventDispatcher;
1920

2021
import java.io.ByteArrayOutputStream;
2122

22-
public final class EngineView extends SurfaceView implements SurfaceHolder.Callback, View.OnTouchListener {
23+
public final class EngineView extends FrameLayout implements SurfaceHolder.Callback, View.OnTouchListener {
24+
private static final FrameLayout.LayoutParams childViewLayoutParams = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
25+
private final SurfaceView primarySurfaceView;
26+
private final SurfaceView xrSurfaceView;
2327
private final EventDispatcher reactEventDispatcher;
2428

2529
public EngineView(ReactContext reactContext) {
2630
super(reactContext);
27-
this.getHolder().addCallback(this);
31+
32+
this.primarySurfaceView = new SurfaceView(reactContext);
33+
this.primarySurfaceView.setLayoutParams(EngineView.childViewLayoutParams);
34+
this.primarySurfaceView.getHolder().addCallback(this);
35+
this.addView(this.primarySurfaceView);
36+
37+
this.xrSurfaceView = new SurfaceView(reactContext);
38+
this.xrSurfaceView.setLayoutParams(childViewLayoutParams);
39+
this.xrSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
40+
@Override
41+
public void surfaceCreated(SurfaceHolder holder) {
42+
// surfaceChanged is also called when the surface is created, so just do all the handling there
43+
}
44+
45+
@Override
46+
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
47+
BabylonNativeInterop.updateXRView(holder.getSurface());
48+
}
49+
50+
@Override
51+
public void surfaceDestroyed(SurfaceHolder holder) {
52+
BabylonNativeInterop.updateXRView(null);
53+
}
54+
});
55+
this.xrSurfaceView.setVisibility(View.INVISIBLE);
56+
this.addView(this.xrSurfaceView);
57+
2858
this.setOnTouchListener(this);
29-
this.reactEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
3059

31-
setWillNotDraw(false);
60+
this.setWillNotDraw(false);
61+
62+
this.reactEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
3263
}
3364

3465
@Override
@@ -51,6 +82,18 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
5182
return true;
5283
}
5384

85+
@Override
86+
protected void onDraw(Canvas canvas) {
87+
if (BabylonNativeInterop.isXRActive()) {
88+
this.xrSurfaceView.setVisibility(View.VISIBLE);
89+
} else {
90+
this.xrSurfaceView.setVisibility(View.INVISIBLE);
91+
}
92+
93+
BabylonNativeInterop.renderView();
94+
invalidate();
95+
}
96+
5497
@TargetApi(24)
5598
public void takeSnapshot() {
5699
// Only supported on API level 24 and up, return a blank image.
@@ -70,8 +113,13 @@ public void takeSnapshot() {
70113
helperThread.start();
71114
final Handler helperThreadHandler = new Handler(helperThread.getLooper());
72115

116+
SurfaceView surfaceView = this.primarySurfaceView;
117+
if (BabylonNativeInterop.isXRActive()) {
118+
surfaceView = this.xrSurfaceView;
119+
}
120+
73121
// Request the pixel copy.
74-
PixelCopy.request(this, bitmap, (copyResult) -> {
122+
PixelCopy.request(surfaceView, bitmap, (copyResult) -> {
75123
// If the pixel copy was a success then convert the image to a base 64 encoded jpeg and fire the event.
76124
String encoded = "";
77125
if (copyResult == PixelCopy.SUCCESS) {
@@ -87,10 +135,4 @@ public void takeSnapshot() {
87135
helperThread.quitSafely();
88136
}, helperThreadHandler);
89137
}
90-
91-
@Override
92-
protected void onDraw(Canvas canvas) {
93-
BabylonNativeInterop.renderView();
94-
invalidate();
95-
}
96138
}

Modules/@babylonjs/react-native/ios/BabylonNativeInterop.h

+2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,7 @@
88
+ (void)updateView:(MTKView*)mtkView;
99
+ (void)renderView;
1010
+ (void)resetView;
11+
+ (void)updateXRView:(MTKView*)mtkView;
12+
+ (bool)isXRActive;
1113
+ (void)reportTouchEvent:(MTKView*)mtkView touches:(NSSet<UITouch*>*)touches event:(UIEvent*)event;
1214
@end

Modules/@babylonjs/react-native/ios/BabylonNativeInterop.mm

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ + (void)resetView {
7070
Babylon::ResetView();
7171
}
7272

73+
+ (void)updateXRView:(MTKView*)mtkView {
74+
Babylon::UpdateXRView((__bridge void*)mtkView);
75+
}
76+
77+
+ (bool)isXRActive {
78+
return Babylon::IsXRActive();
79+
}
80+
7381
+ (void)reportTouchEvent:(MTKView*)mtkView touches:(NSSet<UITouch*>*)touches event:(UIEvent*)event {
7482
for (UITouch* touch in touches) {
7583
if (touch.view == mtkView) {

Modules/@babylonjs/react-native/ios/EngineViewManager.mm

+15-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ @interface EngineView : MTKView
1515
@end
1616

1717
@implementation EngineView {
18-
RCTBridge* bridge;
18+
const RCTBridge* bridge;
19+
MTKView* xrView;
1920
}
2021

2122
- (instancetype)init:(RCTBridge*)_bridge {
@@ -25,6 +26,13 @@ - (instancetype)init:(RCTBridge*)_bridge {
2526
super.translatesAutoresizingMaskIntoConstraints = false;
2627
super.colorPixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
2728
super.depthStencilPixelFormat = MTLPixelFormatDepth32Float;
29+
30+
xrView = [[MTKView alloc] initWithFrame:self.bounds device:self.device];
31+
xrView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
32+
xrView.userInteractionEnabled = false;
33+
xrView.hidden = true;
34+
[self addSubview:xrView];
35+
[BabylonNativeInterop updateXRView:xrView];
2836
}
2937
return self;
3038
}
@@ -51,6 +59,12 @@ - (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event {
5159
}
5260

5361
- (void)drawRect:(CGRect)rect {
62+
if ([BabylonNativeInterop isXRActive]) {
63+
xrView.hidden = false;
64+
} else {
65+
xrView.hidden = true;
66+
}
67+
5468
[BabylonNativeInterop renderView];
5569
}
5670

Modules/@babylonjs/react-native/shared/BabylonNative.cpp

+34-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ namespace Babylon
3333
: m_env{ Napi::Attach<facebook::jsi::Runtime&>(jsiRuntime) }
3434
, m_jsDispatcher{ std::move(jsDispatcher) }
3535
, m_isRunning{ std::make_shared<bool>(true) }
36+
, m_isXRActive{ std::make_shared<bool>(false) }
3637
{
3738
// Initialize a JS promise that will be returned by whenInitialized, and completed when NativeEngine is initialized.
3839
CreateInitPromise();
@@ -41,7 +42,8 @@ namespace Babylon
4142
JsRuntime::CreateForJavaScript(m_env, CreateJsRuntimeDispatcher(m_env, jsiRuntime, m_jsDispatcher, m_isRunning));
4243

4344
// Initialize Babylon Native plugins
44-
Plugins::NativeXr::Initialize(m_env);
45+
m_nativeXr.emplace(Plugins::NativeXr::Initialize(m_env));
46+
m_nativeXr->SetSessionStateChangedCallback([isXRActive{ m_isXRActive }](bool isSessionActive) { *isXRActive = isSessionActive; });
4547
Plugins::NativeCapture::Initialize(m_env);
4648
m_nativeInput = &Plugins::NativeInput::CreateForJavaScript(m_env);
4749

@@ -144,6 +146,16 @@ namespace Babylon
144146
m_nativeInput->TouchMove(pointerId, x, y);
145147
}
146148

149+
bool IsXRActive()
150+
{
151+
return *m_isXRActive;
152+
}
153+
154+
void UpdateXRView(void* windowPtr)
155+
{
156+
m_nativeXr->UpdateWindow(windowPtr);
157+
}
158+
147159
jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& prop) override
148160
{
149161
const auto propName{ prop.utf8(runtime) };
@@ -183,6 +195,9 @@ namespace Babylon
183195
std::shared_ptr<bool> m_isRunning{};
184196
std::once_flag m_isGraphicsInitialized{};
185197
Plugins::NativeInput* m_nativeInput{};
198+
std::optional<Plugins::NativeXr> m_nativeXr{};
199+
200+
std::shared_ptr<bool> m_isXRActive{};
186201
};
187202

188203
namespace
@@ -270,4 +285,22 @@ namespace Babylon
270285
nativeModule->SetTouchPosition(pointerId, x, y);
271286
}
272287
}
288+
289+
bool IsXRActive()
290+
{
291+
if (auto nativeModule{ g_nativeModule.lock() })
292+
{
293+
return nativeModule->IsXRActive();
294+
}
295+
296+
return false;
297+
}
298+
299+
void UpdateXRView(void* windowPtr)
300+
{
301+
if (auto nativeModule{ g_nativeModule.lock() })
302+
{
303+
nativeModule->UpdateXRView(windowPtr);
304+
}
305+
}
273306
}

Modules/@babylonjs/react-native/shared/BabylonNative.h

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ namespace Babylon
1515
void SetMousePosition(uint32_t x, uint32_t y);
1616
void SetTouchButtonState(uint32_t pointerId, bool isDown, uint32_t x, uint32_t y);
1717
void SetTouchPosition(uint32_t pointerId, uint32_t x, uint32_t y);
18+
bool IsXRActive();
19+
void UpdateXRView(void* windowPtr);
1820

1921
extern const uint32_t LEFT_MOUSE_BUTTON_ID;
2022
extern const uint32_t MIDDLE_MOUSE_BUTTON_ID;

0 commit comments

Comments
 (0)