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