Skip to content

Commit a142134

Browse files
author
Emile
committed
Add onFrame callback
Update Three.js example to use onFrame callback Add start/stop rotation on touch to Three.js example
1 parent 554fa31 commit a142134

File tree

9 files changed

+122
-44
lines changed

9 files changed

+122
-44
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,6 @@ android/app/libs
5656
fastlane/report.xml
5757
fastlane/Preview.html
5858
fastlane/screenshots
59+
60+
# Visual Studio Code
61+
.vscode

android/src/main/java/fr/greweb/rnwebgl/RNWebGLView.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,15 @@ public synchronized void onDrawFrame(GL10 unused) {
6565
}
6666
mEventQueue.clear();
6767

68+
// Call frame event after each frame is drawn
69+
WritableMap event = Arguments.createMap();
70+
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "frame", event);
71+
6872
// ctxId may be unset if we get here (on the GL thread) before RNWebGLContextCreate(...) is
6973
// called on the JS thread to create the RNWebGL context and save its id (see above in
7074
// the implementation of `onSurfaceCreated(...)`)
7175
if (ctxId > 0) {
72-
RNWebGLContextFlush(ctxId);
76+
RNWebGLContextFlush(ctxId);
7377
}
7478
}
7579

android/src/main/java/fr/greweb/rnwebgl/RNWebGLViewManager.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public RNWebGLView createViewInstance(ThemedReactContext context) {
2525
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
2626
return MapBuilder.of(
2727
"surfaceCreate",
28-
MapBuilder.of("registrationName", "onSurfaceCreate"));
28+
MapBuilder.of("registrationName", "onSurfaceCreate"),
29+
"frame",
30+
MapBuilder.of("registrationName", "onFrame"));
2931
}
3032
}

example/ios/example.xcodeproj/project.pbxproj

+38
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@
103103
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
104104
remoteInfo = React;
105105
};
106+
149FF1741FA75B0B0075DD13 /* PBXContainerItemProxy */ = {
107+
isa = PBXContainerItemProxy;
108+
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
109+
proxyType = 2;
110+
remoteGlobalIDString = 3DBE0D001F3B181A0099AA32;
111+
remoteInfo = fishhook;
112+
};
113+
149FF1761FA75B0B0075DD13 /* PBXContainerItemProxy */ = {
114+
isa = PBXContainerItemProxy;
115+
containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
116+
proxyType = 2;
117+
remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32;
118+
remoteInfo = "fishhook-tvOS";
119+
};
106120
2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = {
107121
isa = PBXContainerItemProxy;
108122
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
@@ -420,6 +434,8 @@
420434
children = (
421435
139FDEF41B06529B00C62182 /* libRCTWebSocket.a */,
422436
3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */,
437+
149FF1751FA75B0B0075DD13 /* libfishhook.a */,
438+
149FF1771FA75B0B0075DD13 /* libfishhook-tvOS.a */,
423439
);
424440
name = Products;
425441
sourceTree = "<group>";
@@ -620,8 +636,12 @@
620636
TargetAttributes = {
621637
00E356ED1AD99517003FC87E = {
622638
CreatedOnToolsVersion = 6.2;
639+
DevelopmentTeam = V485XZT2WB;
623640
TestTargetID = 13B07F861A680F5B00A75B9A;
624641
};
642+
13B07F861A680F5B00A75B9A = {
643+
DevelopmentTeam = V485XZT2WB;
644+
};
625645
2D02E47A1E0B4A5D006451C7 = {
626646
CreatedOnToolsVersion = 8.2.1;
627647
ProvisioningStyle = Automatic;
@@ -761,6 +781,20 @@
761781
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
762782
sourceTree = BUILT_PRODUCTS_DIR;
763783
};
784+
149FF1751FA75B0B0075DD13 /* libfishhook.a */ = {
785+
isa = PBXReferenceProxy;
786+
fileType = archive.ar;
787+
path = libfishhook.a;
788+
remoteRef = 149FF1741FA75B0B0075DD13 /* PBXContainerItemProxy */;
789+
sourceTree = BUILT_PRODUCTS_DIR;
790+
};
791+
149FF1771FA75B0B0075DD13 /* libfishhook-tvOS.a */ = {
792+
isa = PBXReferenceProxy;
793+
fileType = archive.ar;
794+
path = "libfishhook-tvOS.a";
795+
remoteRef = 149FF1761FA75B0B0075DD13 /* PBXContainerItemProxy */;
796+
sourceTree = BUILT_PRODUCTS_DIR;
797+
};
764798
341611631F48BB72004A5873 /* libthird-party.a */ = {
765799
isa = PBXReferenceProxy;
766800
fileType = archive.ar;
@@ -1049,6 +1083,7 @@
10491083
isa = XCBuildConfiguration;
10501084
buildSettings = {
10511085
BUNDLE_LOADER = "$(TEST_HOST)";
1086+
DEVELOPMENT_TEAM = V485XZT2WB;
10521087
GCC_PREPROCESSOR_DEFINITIONS = (
10531088
"DEBUG=1",
10541089
"$(inherited)",
@@ -1070,6 +1105,7 @@
10701105
buildSettings = {
10711106
BUNDLE_LOADER = "$(TEST_HOST)";
10721107
COPY_PHASE_STRIP = NO;
1108+
DEVELOPMENT_TEAM = V485XZT2WB;
10731109
INFOPLIST_FILE = exampleTests/Info.plist;
10741110
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
10751111
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
@@ -1088,6 +1124,7 @@
10881124
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
10891125
CURRENT_PROJECT_VERSION = 1;
10901126
DEAD_CODE_STRIPPING = NO;
1127+
DEVELOPMENT_TEAM = V485XZT2WB;
10911128
INFOPLIST_FILE = example/Info.plist;
10921129
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
10931130
OTHER_LDFLAGS = (
@@ -1105,6 +1142,7 @@
11051142
buildSettings = {
11061143
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
11071144
CURRENT_PROJECT_VERSION = 1;
1145+
DEVELOPMENT_TEAM = V485XZT2WB;
11081146
INFOPLIST_FILE = example/Info.plist;
11091147
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
11101148
OTHER_LDFLAGS = (

example/src/ThreeCube.js

+50-36
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ import THREE from "./three";
66

77
export default class App extends React.Component {
88
requestId: *;
9+
rotating = true;
10+
renderer;
11+
scene;
12+
camera;
13+
cube;
14+
915
componentWillUnmount() {
1016
cancelAnimationFrame(this.requestId);
1117
}
12-
onContextCreate = (gl: WebGLRenderingContext) => {
13-
const rngl = gl.getExtension("RN");
1418

19+
onContextCreate = (gl: WebGLRenderingContext) => {
1520
const { drawingBufferWidth: width, drawingBufferHeight: height } = gl;
16-
const renderer = new THREE.WebGLRenderer({
21+
this.renderer = new THREE.WebGLRenderer({
1722
canvas: {
1823
width,
1924
height,
@@ -24,53 +29,62 @@ export default class App extends React.Component {
2429
},
2530
context: gl
2631
});
27-
renderer.setSize(width, height);
28-
renderer.setClearColor(0x000000, 1);
32+
this.renderer.setSize(width, height);
33+
this.renderer.setClearColor(0x000000, 1);
2934

30-
let camera, scene;
31-
let cube;
35+
this.init(width, height);
36+
// animate();
37+
};
38+
toggleRotation = () => {
39+
this.rotating = !this.rotating;
40+
};
3241

33-
function init() {
34-
camera = new THREE.PerspectiveCamera(75, width / height, 1, 1100);
35-
camera.position.y = 150;
36-
camera.position.z = 500;
37-
scene = new THREE.Scene();
42+
init = (width, height) => {
43+
this.camera = new THREE.PerspectiveCamera(75, width / height, 1, 1100);
44+
this.camera.position.y = 150;
45+
this.camera.position.z = 500;
46+
this.scene = new THREE.Scene();
3847

39-
let geometry = new THREE.BoxGeometry(200, 200, 200);
40-
for (let i = 0; i < geometry.faces.length; i += 2) {
41-
let hex = Math.random() * 0xffffff;
42-
geometry.faces[i].color.setHex(hex);
43-
geometry.faces[i + 1].color.setHex(hex);
44-
}
48+
let geometry = new THREE.BoxGeometry(200, 200, 200);
49+
for (let i = 0; i < geometry.faces.length; i += 2) {
50+
let hex = Math.random() * 0xffffff;
51+
geometry.faces[i].color.setHex(hex);
52+
geometry.faces[i + 1].color.setHex(hex);
53+
}
4554

46-
let material = new THREE.MeshBasicMaterial({
47-
vertexColors: THREE.FaceColors,
48-
overdraw: 0.5
49-
});
55+
let material = new THREE.MeshBasicMaterial({
56+
vertexColors: THREE.FaceColors,
57+
overdraw: 0.5
58+
});
5059

51-
cube = new THREE.Mesh(geometry, material);
52-
cube.position.y = 150;
53-
scene.add(cube);
54-
}
55-
const animate = () => {
56-
this.requestId = requestAnimationFrame(animate);
57-
renderer.render(scene, camera);
60+
this.cube = new THREE.Mesh(geometry, material);
61+
this.cube.position.y = 150;
62+
this.scene.add(this.cube);
63+
};
5864

59-
cube.rotation.y += 0.05;
65+
animate = () => {
66+
if (this.renderer) {
67+
// this.requestId = requestAnimationFrame(animate);
68+
this.renderer.render(this.scene, this.camera);
6069

61-
gl.flush();
62-
rngl.endFrame();
63-
};
70+
if (this.rotating) {
71+
this.cube.rotation.y += 0.05;
72+
}
6473

65-
init();
66-
animate();
74+
const rngl = this.renderer.context.getExtension("RN");
75+
rngl.endFrame();
76+
}
6777
};
78+
6879
render() {
6980
return (
70-
<View style={styles.container}>
81+
<View style={styles.container} onTouchStart={this.toggleRotation}>
7182
<WebGLView
7283
style={styles.webglView}
7384
onContextCreate={this.onContextCreate}
85+
onFrame={() => {
86+
this.animate();
87+
}}
7488
/>
7589
</View>
7690
);

ios/RNWebGLView.h

+1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
- (instancetype)initWithManager:(RNWebGLViewManager *)mgr;
88

99
@property (nonatomic, copy) RCTDirectEventBlock onSurfaceCreate;
10+
@property (nonatomic, copy) RCTBubblingEventBlock onFrame;
1011

1112
@end

ios/RNWebGLView.m

+3
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ - (void)draw
236236
[EAGLContext setCurrentContext:_eaglCtx];
237237
RNWebGLContextSetDefaultFramebuffer(_ctxId, _msaaFramebuffer);
238238
RNWebGLContextFlush(_ctxId);
239+
240+
// onFrame callback
241+
self.onFrame(@{});
239242

240243
// Present current state of view buffers
241244
// TODO(nikki): This should happen exactly at `endFrame()` in the queue

ios/RNWebGLViewManager.m

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ - (UIView *)view
1111
}
1212

1313
RCT_EXPORT_VIEW_PROPERTY(onSurfaceCreate, RCTDirectEventBlock);
14+
RCT_EXPORT_VIEW_PROPERTY(onFrame, RCTBubblingEventBlock);
1415
RCT_EXPORT_VIEW_PROPERTY(msaaSamples, NSNumber);
1516

1617
@end

src/WebGLView.js

+18-6
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,18 @@ const getGl = (ctxId: number): ?WebGLRenderingContext => {
3737
return gl;
3838
};
3939

40-
export default class WebGLView extends React.Component {
41-
props: {
42-
onContextCreate: (gl: WebGLRenderingContext) => void,
43-
onContextFailure: (e: Error) => void,
44-
msaaSamples: number
45-
};
40+
type Props = {
41+
onContextCreate: (gl: WebGLRenderingContext) => void,
42+
onContextFailure: (e: Error) => void,
43+
onFrame: () => void,
44+
msaaSamples: number
45+
};
46+
47+
export default class WebGLView extends React.Component<Props> {
4648
static propTypes = {
4749
onContextCreate: PropTypes.func,
4850
onContextFailure: PropTypes.func,
51+
onFrame: PropTypes.func,
4952
msaaSamples: PropTypes.number,
5053
...ViewPropTypes
5154
};
@@ -58,6 +61,7 @@ export default class WebGLView extends React.Component {
5861
const {
5962
onContextCreate, // eslint-disable-line no-unused-vars
6063
onContextFailure, // eslint-disable-line no-unused-vars
64+
onFrame, // eslint-disable-line no-unused-vars
6165
msaaSamples,
6266
...viewProps
6367
} = this.props;
@@ -69,6 +73,7 @@ export default class WebGLView extends React.Component {
6973
<WebGLView.NativeView
7074
style={{ flex: 1, backgroundColor: "transparent" }}
7175
onSurfaceCreate={this.onSurfaceCreate}
76+
onFrame={this.onFrame}
7277
msaaSamples={Platform.OS === "ios" ? msaaSamples : undefined}
7378
/>
7479
</View>
@@ -100,6 +105,13 @@ export default class WebGLView extends React.Component {
100105
}
101106
};
102107

108+
onFrame = (): void => {
109+
if (!this.props.onFrame) {
110+
return;
111+
}
112+
this.props.onFrame();
113+
};
114+
103115
static NativeView = requireNativeComponent("RNWebGLView", WebGLView, {
104116
nativeOnly: { onSurfaceCreate: true }
105117
});

0 commit comments

Comments
 (0)