UNPKG

12.4 kBJavaScriptView Raw
1import React from 'react';
2import PropTypes from 'prop-types';
3import {
4 NativeModules,
5 Platform,
6 View,
7 ViewPropTypes,
8 findNodeHandle,
9 requireNativeComponent,
10} from 'react-native';
11
12import Constants from './Constants';
13
14// A component that acts as an OpenGL render target.
15
16export default class GLView extends React.Component {
17 static propTypes = {
18 // Called when the OpenGL context is created, with the context object as a
19 // parameter. The context object has an API mirroring WebGL's
20 // WebGLRenderingContext.
21 onContextCreate: PropTypes.func,
22
23 // [iOS only] Number of samples for Apple's built-in multisampling.
24 msaaSamples: PropTypes.number,
25
26 // A ref callback for the native GLView
27 nativeRef_EXPERIMENTAL: PropTypes.func,
28
29 ...ViewPropTypes,
30 };
31
32 static defaultProps = {
33 msaaSamples: 4,
34 };
35
36 render() {
37 const {
38 onContextCreate, // eslint-disable-line no-unused-vars
39 msaaSamples,
40 ...viewProps
41 } = this.props;
42
43 // NOTE: Removing `backgroundColor: 'transparent'` causes a performance
44 // regression. Not sure why yet...
45 return (
46 <View {...viewProps}>
47 <GLView.NativeView
48 ref={this._setNativeRef}
49 style={{ flex: 1, backgroundColor: 'transparent' }}
50 onSurfaceCreate={this._onSurfaceCreate}
51 msaaSamples={Platform.OS === 'ios' ? msaaSamples : undefined}
52 />
53 </View>
54 );
55 }
56
57 _setNativeRef = nativeRef => {
58 if (this.props.nativeRef_EXPERIMENTAL) {
59 this.props.nativeRef_EXPERIMENTAL(nativeRef);
60 }
61 this.nativeRef = nativeRef;
62 };
63
64 _onSurfaceCreate = ({ nativeEvent: { exglCtxId } }) => {
65 const gl = getGl(exglCtxId);
66 if (this.props.onContextCreate) {
67 this.props.onContextCreate(gl);
68 }
69 };
70
71 static NativeView = requireNativeComponent('ExponentGLView', GLView, {
72 nativeOnly: { onSurfaceCreate: true },
73 });
74
75 startARSessionAsync() {
76 return NativeModules.ExponentGLViewManager.startARSessionAsync(
77 findNodeHandle(this.nativeRef)
78 );
79 }
80}
81
82// JavaScript WebGL types to wrap around native objects
83
84class WebGLRenderingContext {}
85
86const idToObject = {};
87
88class WebGLObject {
89 constructor(id) {
90 if (idToObject[id]) {
91 throw new Error(
92 `WebGL object with underlying EXGLObjectId '${id}' already exists!`
93 );
94 }
95 this.id = id; // Native GL object id
96 }
97 toString() {
98 return `[WebGLObject ${this.id}]`;
99 }
100}
101
102const wrapObject = (type, id) => {
103 const found = idToObject[id];
104 if (found) {
105 return found;
106 }
107 return (idToObject[id] = new type(id));
108};
109
110class WebGLBuffer extends WebGLObject {}
111
112class WebGLFramebuffer extends WebGLObject {}
113
114class WebGLProgram extends WebGLObject {}
115
116class WebGLRenderbuffer extends WebGLObject {}
117
118class WebGLShader extends WebGLObject {}
119
120class WebGLTexture extends WebGLObject {}
121
122class WebGLUniformLocation {
123 constructor(id) {
124 this.id = id; // Native GL object id
125 }
126}
127
128class WebGLActiveInfo {
129 constructor(obj) {
130 Object.assign(this, obj);
131 }
132}
133
134class WebGLShaderPrecisionFormat {
135 constructor(obj) {
136 Object.assign(this, obj);
137 }
138}
139
140// Many functions need wrapping/unwrapping of arguments and return value. We
141// handle each case specifically so we can write the tightest code for
142// better performance.
143const wrapMethods = gl => {
144 const wrap = (methodNames, wrapper) =>
145 (Array.isArray(methodNames) ? methodNames : [methodNames]).forEach(
146 methodName => (gl[methodName] = wrapper(gl[methodName]))
147 );
148
149 // We can be slow in `gl.getParameter(...)` since it's a blocking call anyways
150 const getParameterTypes = {
151 [gl.ARRAY_BUFFER_BINDING]: WebGLBuffer,
152 [gl.ELEMENT_ARRAY_BUFFER_BINDING]: WebGLBuffer,
153 [gl.CURRENT_PROGRAM]: WebGLProgram,
154 [gl.FRAMEBUFFER_BINDING]: WebGLFramebuffer,
155 [gl.RENDERBUFFER_BINDING]: WebGLRenderbuffer,
156 [gl.TEXTURE_BINDING_2D]: WebGLTexture,
157 [gl.TEXTURE_BINDING_CUBE_MAP]: WebGLTexture,
158 };
159 wrap('getParameter', orig => pname => {
160 let ret = orig.call(gl, pname);
161 if (pname === gl.VERSION) {
162 // Wrap native version name
163 ret = `WebGL 1.0 (Expo-${Platform.OS}-${Constants.expoVersion}) (${ret})`;
164 }
165 const type = getParameterTypes[pname];
166 return type ? wrapObject(type, ret) : ret;
167 });
168
169 // Buffers
170 wrap('bindBuffer', orig => (target, buffer) =>
171 orig.call(gl, target, buffer && buffer.id)
172 );
173 wrap('createBuffer', orig => () => wrapObject(WebGLBuffer, orig.call(gl)));
174 wrap('deleteBuffer', orig => buffer => orig.call(gl, buffer && buffer.id));
175 wrap('isBuffer', orig => buffer =>
176 buffer instanceof WebGLBuffer && orig.call(gl, buffer.id)
177 );
178
179 // Framebuffers
180 wrap('bindFramebuffer', orig => (target, framebuffer) =>
181 orig.call(gl, target, framebuffer && framebuffer.id)
182 );
183 wrap('createFramebuffer', orig => () =>
184 wrapObject(WebGLFramebuffer, orig.call(gl))
185 );
186 wrap('deleteFramebuffer', orig => framebuffer =>
187 orig.call(gl, framebuffer && framebuffer.id)
188 );
189 wrap('framebufferRenderbuffer', orig => (target, attachment, rbtarget, rb) =>
190 orig.call(gl, target, attachment, rbtarget, rb && rb.id)
191 );
192 wrap(
193 'framebufferTexture2D',
194 orig => (target, attachment, textarget, tex, level) =>
195 orig.call(gl, target, attachment, textarget, tex && tex.id, level)
196 );
197 wrap('isFramebuffer', orig => framebuffer =>
198 framebuffer instanceof WebGLFramebuffer && orig.call(gl, framebuffer.id)
199 );
200
201 // Renderbuffers
202 wrap('bindRenderbuffer', orig => (target, renderbuffer) =>
203 orig.call(gl, target, renderbuffer && renderbuffer.id)
204 );
205 wrap('createRenderbuffer', orig => () =>
206 wrapObject(WebGLRenderbuffer, orig.call(gl))
207 );
208 wrap('deleteRenderbuffer', orig => renderbuffer =>
209 orig.call(gl, renderbuffer && renderbuffer.id)
210 );
211 wrap('isRenderbuffer', orig => renderbuffer =>
212 renderbuffer instanceof WebGLRenderbuffer && orig.call(gl, renderbuffer.id)
213 );
214
215 // Textures
216 wrap('bindTexture', orig => (target, texture) =>
217 orig.call(gl, target, texture && texture.id)
218 );
219 wrap('createTexture', orig => () => wrapObject(WebGLTexture, orig.call(gl)));
220 wrap('deleteTexture', orig => texture =>
221 orig.call(gl, texture && texture.id)
222 );
223 wrap('isTexture', orig => texture =>
224 texture instanceof WebGLTexture && orig.call(gl, texture.id)
225 );
226
227 // Programs and shaders
228 wrap('attachShader', orig => (program, shader) =>
229 orig.call(gl, program && program.id, shader && shader.id)
230 );
231 wrap('bindAttribLocation', orig => (program, index, name) =>
232 orig.call(gl, program && program.id, index, name)
233 );
234 wrap('compileShader', orig => shader => orig.call(gl, shader && shader.id));
235 wrap('createProgram', orig => () => wrapObject(WebGLProgram, orig.call(gl)));
236 wrap('createShader', orig => type =>
237 wrapObject(WebGLShader, orig.call(gl, type))
238 );
239 wrap('deleteProgram', orig => program =>
240 orig.call(gl, program && program.id)
241 );
242 wrap('deleteShader', orig => shader => orig.call(gl, shader && shader.id));
243 wrap('detachShader', orig => (program, shader) =>
244 orig.call(gl, program && program.id, shader && shader.id)
245 );
246 wrap('getAttachedShaders', orig => program =>
247 orig.call(gl, program && program.id).map(id => wrapObject(WebGLShader, id))
248 );
249 wrap('getProgramParameter', orig => (program, pname) =>
250 orig.call(gl, program && program.id, pname)
251 );
252 wrap('getProgramInfoLog', orig => program =>
253 orig.call(gl, program && program.id)
254 );
255 wrap('getShaderParameter', orig => (shader, pname) =>
256 orig.call(gl, shader && shader.id, pname)
257 );
258 wrap('getShaderPrecisionFormat', orig => (shadertype, precisiontype) =>
259 new WebGLShaderPrecisionFormat(orig.call(gl, shadertype, precisiontype))
260 );
261 wrap('getShaderInfoLog', orig => shader =>
262 orig.call(gl, shader && shader.id)
263 );
264 wrap('getShaderSource', orig => shader => orig.call(gl, shader && shader.id));
265 wrap('linkProgram', orig => program => orig.call(gl, program && program.id));
266 wrap('shaderSource', orig => (shader, source) =>
267 orig.call(gl, shader && shader.id, source)
268 );
269 wrap('useProgram', orig => program => orig.call(gl, program && program.id));
270 wrap('validateProgram', orig => program =>
271 orig.call(gl, program && program.id)
272 );
273 wrap('isShader', orig => shader =>
274 shader instanceof WebGLShader && orig.call(gl, shader.id)
275 );
276 wrap('isProgram', orig => program =>
277 program instanceof WebGLProgram && orig.call(gl, program.id)
278 );
279
280 // Uniforms and attributes
281 wrap('getActiveAttrib', orig => (program, index) =>
282 new WebGLActiveInfo(orig.call(gl, program && program.id, index))
283 );
284 wrap('getActiveUniform', orig => (program, index) =>
285 new WebGLActiveInfo(orig.call(gl, program && program.id, index))
286 );
287 wrap('getAttribLocation', orig => (program, name) =>
288 orig.call(gl, program && program.id, name)
289 );
290 wrap('getUniform', orig => (program, location) =>
291 orig.call(gl, program && program.id, location && location.id)
292 );
293 wrap('getUniformLocation', orig => (program, name) =>
294 new WebGLUniformLocation(orig.call(gl, program && program.id, name))
295 );
296 wrap(['uniform1f', 'uniform1i'], orig => (loc, x) =>
297 orig.call(gl, loc && loc.id, x)
298 );
299 wrap(['uniform2f', 'uniform2i'], orig => (loc, x, y) =>
300 orig.call(gl, loc && loc.id, x, y)
301 );
302 wrap(['uniform3f', 'uniform3i'], orig => (loc, x, y, z) =>
303 orig.call(gl, loc && loc.id, x, y, z)
304 );
305 wrap(['uniform4f', 'uniform4i'], orig => (loc, x, y, z, w) =>
306 orig.call(gl, loc && loc.id, x, y, z, w)
307 );
308 wrap(
309 ['uniform1fv', 'uniform2fv', 'uniform3fv', 'uniform4fv'],
310 orig => (loc, val) => orig.call(gl, loc && loc.id, new Float32Array(val))
311 );
312 wrap(
313 ['uniform1iv', 'uniform2iv', 'uniform3iv', 'uniform4iv'],
314 orig => (loc, val) => orig.call(gl, loc && loc.id, new Int32Array(val))
315 );
316 wrap(
317 ['uniformMatrix2fv', 'uniformMatrix3fv', 'uniformMatrix4fv'],
318 orig => (loc, transpose, val) =>
319 orig.call(gl, loc && loc.id, transpose, new Float32Array(val))
320 );
321 wrap(
322 [
323 'vertexAttrib1fv',
324 'vertexAttrib2fv',
325 'vertexAttrib3fv',
326 'vertexAttrib4fv',
327 ],
328 orig => (index, val) => orig.call(gl, index, new Float32Array(val))
329 );
330};
331
332// Get the GL interface from an EXGLContextID and do JS-side setup
333const getGl = exglCtxId => {
334 const gl = global.__EXGLContexts[exglCtxId];
335 gl.__exglCtxId = exglCtxId;
336 delete global.__EXGLContexts[exglCtxId];
337 if (Object.setPrototypeOf) {
338 Object.setPrototypeOf(gl, global.WebGLRenderingContext.prototype);
339 } else {
340 gl.__proto__ = global.WebGLRenderingContext.prototype; // eslint-disable-line no-proto
341 }
342
343 wrapMethods(gl);
344
345 // No canvas yet...
346 gl.canvas = null;
347
348 // Drawing buffer width/height
349 // TODO(nikki): Make this dynamic
350 const viewport = gl.getParameter(gl.VIEWPORT);
351 gl.drawingBufferWidth = viewport[2];
352 gl.drawingBufferHeight = viewport[3];
353
354 // Enable/disable logging of all GL function calls
355 let enableLogging = false;
356 Object.defineProperty(gl, 'enableLogging', {
357 configurable: true,
358 get() {
359 return enableLogging;
360 },
361 set(enable) {
362 if (enable === enableLogging) {
363 return;
364 }
365 if (enable) {
366 Object.keys(gl).forEach(key => {
367 if (typeof gl[key] === 'function') {
368 const original = gl[key];
369 gl[key] = (...args) => {
370 console.log(`EXGL: ${key}(${args.join(', ')})`);
371 const r = original.apply(gl, args);
372 console.log(`EXGL: = ${r}`);
373 return r;
374 };
375 gl[key].original = original;
376 }
377 });
378 } else {
379 Object.keys(gl).forEach(key => {
380 if (typeof gl[key] === 'function' && gl[key].original) {
381 gl[key] = gl[key].original;
382 }
383 });
384 }
385 enableLogging = enable;
386 },
387 });
388
389 return gl;
390};
391
392global.WebGLRenderingContext = WebGLRenderingContext;
393global.WebGLObject = WebGLObject;
394global.WebGLBuffer = WebGLBuffer;
395global.WebGLFramebuffer = WebGLFramebuffer;
396global.WebGLProgram = WebGLProgram;
397global.WebGLRenderbuffer = WebGLRenderbuffer;
398global.WebGLShader = WebGLShader;
399global.WebGLTexture = WebGLTexture;
400global.WebGLUniformLocation = WebGLUniformLocation;
401global.WebGLActiveInfo = WebGLActiveInfo;
402global.WebGLShaderPrecisionFormat = WebGLShaderPrecisionFormat;