1 | import React from 'react';
|
2 | import PropTypes from 'prop-types';
|
3 | import {
|
4 | NativeModules,
|
5 | Platform,
|
6 | View,
|
7 | ViewPropTypes,
|
8 | findNodeHandle,
|
9 | requireNativeComponent,
|
10 | } from 'react-native';
|
11 |
|
12 | import Constants from './Constants';
|
13 |
|
14 |
|
15 |
|
16 | export default class GLView extends React.Component {
|
17 | static propTypes = {
|
18 |
|
19 |
|
20 |
|
21 | onContextCreate: PropTypes.func,
|
22 |
|
23 |
|
24 | msaaSamples: PropTypes.number,
|
25 |
|
26 |
|
27 | nativeRef_EXPERIMENTAL: PropTypes.func,
|
28 |
|
29 | ...ViewPropTypes,
|
30 | };
|
31 |
|
32 | static defaultProps = {
|
33 | msaaSamples: 4,
|
34 | };
|
35 |
|
36 | render() {
|
37 | const {
|
38 | onContextCreate,
|
39 | msaaSamples,
|
40 | ...viewProps
|
41 | } = this.props;
|
42 |
|
43 |
|
44 |
|
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 |
|
83 |
|
84 | class WebGLRenderingContext {}
|
85 |
|
86 | const idToObject = {};
|
87 |
|
88 | class 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;
|
96 | }
|
97 | toString() {
|
98 | return `[WebGLObject ${this.id}]`;
|
99 | }
|
100 | }
|
101 |
|
102 | const wrapObject = (type, id) => {
|
103 | const found = idToObject[id];
|
104 | if (found) {
|
105 | return found;
|
106 | }
|
107 | return (idToObject[id] = new type(id));
|
108 | };
|
109 |
|
110 | class WebGLBuffer extends WebGLObject {}
|
111 |
|
112 | class WebGLFramebuffer extends WebGLObject {}
|
113 |
|
114 | class WebGLProgram extends WebGLObject {}
|
115 |
|
116 | class WebGLRenderbuffer extends WebGLObject {}
|
117 |
|
118 | class WebGLShader extends WebGLObject {}
|
119 |
|
120 | class WebGLTexture extends WebGLObject {}
|
121 |
|
122 | class WebGLUniformLocation {
|
123 | constructor(id) {
|
124 | this.id = id;
|
125 | }
|
126 | }
|
127 |
|
128 | class WebGLActiveInfo {
|
129 | constructor(obj) {
|
130 | Object.assign(this, obj);
|
131 | }
|
132 | }
|
133 |
|
134 | class WebGLShaderPrecisionFormat {
|
135 | constructor(obj) {
|
136 | Object.assign(this, obj);
|
137 | }
|
138 | }
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | const wrapMethods = gl => {
|
144 | const wrap = (methodNames, wrapper) =>
|
145 | (Array.isArray(methodNames) ? methodNames : [methodNames]).forEach(
|
146 | methodName => (gl[methodName] = wrapper(gl[methodName]))
|
147 | );
|
148 |
|
149 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
333 | const 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;
|
341 | }
|
342 |
|
343 | wrapMethods(gl);
|
344 |
|
345 |
|
346 | gl.canvas = null;
|
347 |
|
348 |
|
349 |
|
350 | const viewport = gl.getParameter(gl.VIEWPORT);
|
351 | gl.drawingBufferWidth = viewport[2];
|
352 | gl.drawingBufferHeight = viewport[3];
|
353 |
|
354 |
|
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 |
|
392 | global.WebGLRenderingContext = WebGLRenderingContext;
|
393 | global.WebGLObject = WebGLObject;
|
394 | global.WebGLBuffer = WebGLBuffer;
|
395 | global.WebGLFramebuffer = WebGLFramebuffer;
|
396 | global.WebGLProgram = WebGLProgram;
|
397 | global.WebGLRenderbuffer = WebGLRenderbuffer;
|
398 | global.WebGLShader = WebGLShader;
|
399 | global.WebGLTexture = WebGLTexture;
|
400 | global.WebGLUniformLocation = WebGLUniformLocation;
|
401 | global.WebGLActiveInfo = WebGLActiveInfo;
|
402 | global.WebGLShaderPrecisionFormat = WebGLShaderPrecisionFormat;
|