jsc3d-js-rails 1.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1041 @@
1
+ /**
2
+ * @preserve Copyright (c) 2011~2013 Humu <humu2009@gmail.com>
3
+ * This file is part of jsc3d project, which is freely distributable under the
4
+ * terms of the MIT license.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ */
24
+
25
+
26
+ /**
27
+ @namespace JSC3D
28
+ */
29
+ var JSC3D = JSC3D || {};
30
+
31
+
32
+ /**
33
+ * Lacked Features / Issue List:
34
+ * 1. Wireframe rendering is not implemented yet.
35
+ * 2. Does not support data updating.
36
+ * 3. Picking does not work correctly on old Firefox (tested on FF6, 8). This may be related with some defect in FF's frame-buffer binding.
37
+ * 4. Each 1st frame is not presented properly when switching from 'standard' to other definitions on old Firefox. There will be a blank frame then.
38
+ */
39
+
40
+ /**
41
+ * @class WebGLRenderBackend
42
+ *
43
+ * This class implements an optional WebGL render back-end for {JSC3D.Viewer}. If enabled, it takes
44
+ * place of {JSC3D.Viewer}'s default software rendering module and provides high performance rendering.
45
+ */
46
+ JSC3D.WebGLRenderBackend = function(canvas, releaseLocalBuffers) {
47
+ this.canvas = canvas;
48
+ // IE11 only has a partial implementation of WebGL API, thus some special treatments are required
49
+ // to avoid usage of unsupported methods and properties.
50
+ this.isIE11 = (JSC3D.PlatformInfo.browser == 'ie') && (parseInt(JSC3D.PlatformInfo.version) >= 11);
51
+ this.gl = canvas.getContext('experimental-webgl', {/*antialias: false,*/ preserveDrawingBuffer: true/*this is necessary since we need to read back pixels for picking*/}) || canvas.getContext('webgl');
52
+ if(!this.gl)
53
+ throw 'JSC3D.WebGLRenderBackend constructor failed: Cannot get WebGL context!';
54
+ this.definition = 'standard';
55
+ this.bkgColors = [0, 0];
56
+ this.bkgTexture = null;
57
+ this.backFB = null;
58
+ this.pickingFB = null;
59
+ this.pickingResult = new Uint8Array(4);
60
+ this.releaseLocalBuffers = releaseLocalBuffers || false;
61
+
62
+ this.screen_vs = '#ifdef GL_ES \n' +
63
+ ' precision mediump float; \n' +
64
+ '#endif \n' +
65
+ '\n' +
66
+ 'attribute vec2 a_position; \n' +
67
+ 'varying vec2 v_texCoord; \n' +
68
+ '\n' +
69
+ 'void main(void) { \n' +
70
+ ' v_texCoord = vec2(0.5, 0.5) * a_position + vec2(0.5, 0.5); \n' +
71
+ ' gl_Position = vec4(a_position, 1.0, 1.0); \n' +
72
+ '}';
73
+ this.screen_fs = '#ifdef GL_ES \n' +
74
+ ' precision mediump float; \n' +
75
+ '#endif \n' +
76
+ '\n' +
77
+ 'uniform sampler2D s_screenTexture; \n' +
78
+ 'varying vec2 v_texCoord; \n' +
79
+ '\n' +
80
+ 'void main(void) { \n' +
81
+ ' gl_FragColor = texture2D(s_screenTexture, v_texCoord); \n' +
82
+ '}';
83
+ this.gradient_background_vs = '#ifdef GL_ES \n' +
84
+ ' precision mediump float; \n' +
85
+ '#endif \n' +
86
+ '\n' +
87
+ 'uniform vec3 u_color1; \n' +
88
+ 'uniform vec3 u_color2; \n' +
89
+ 'attribute vec2 a_position; \n' +
90
+ 'varying vec4 v_color; \n' +
91
+ '\n' +
92
+ 'void main(void) { \n' +
93
+ ' v_color = vec4(a_position.y > 0.0 ? u_color1 : u_color2, 1.0); \n' +
94
+ ' gl_Position = vec4(a_position, 1.0, 1.0); \n' +
95
+ '}';
96
+ this.gradient_background_fs = '#ifdef GL_ES \n' +
97
+ ' precision mediump float; \n' +
98
+ '#endif \n' +
99
+ '\n' +
100
+ 'varying vec4 v_color; \n' +
101
+ '\n' +
102
+ 'void main(void) { \n' +
103
+ ' gl_FragColor = v_color;' +
104
+ '}';
105
+ this.frame_vs = '#ifdef GL_ES \n' +
106
+ ' precision mediump float; \n' +
107
+ '#endif \n' +
108
+ '\n' +
109
+ 'uniform bool u_isPoint; \n' +
110
+ 'uniform mat4 u_transformMatrix; \n' +
111
+ 'attribute vec3 a_position; \n' +
112
+ '\n' +
113
+ 'void main(void) { \n' +
114
+ ' if(u_isPoint) { \n' +
115
+ ' gl_PointSize = 2.0; \n' +
116
+ ' } \n' +
117
+ ' gl_Position = u_transformMatrix * vec4(a_position, 1.0); \n' +
118
+ '}';
119
+ this.frame_fs = '#ifdef GL_ES \n' +
120
+ ' precision mediump float; \n' +
121
+ '#endif \n' +
122
+ '\n' +
123
+ 'uniform vec3 u_materialColor; \n' +
124
+ '\n' +
125
+ 'void main(void) { \n' +
126
+ ' gl_FragColor = vec4(u_materialColor, 1.0); \n' +
127
+ '}';
128
+ this.solid_vs = '#ifdef GL_ES \n' +
129
+ ' precision mediump float; \n' +
130
+ '#endif \n' +
131
+ '\n' +
132
+ 'uniform bool u_isLit; \n' +
133
+ 'uniform bool u_isCast; \n' +
134
+ 'uniform bool u_hasTexture; \n' +
135
+ 'uniform mat3 u_rotationMatrix; \n' +
136
+ 'uniform mat4 u_transformMatrix; \n' +
137
+ 'attribute vec3 a_position; \n' +
138
+ 'attribute vec3 a_normal; \n' +
139
+ 'attribute vec2 a_texCoord; \n' +
140
+ 'varying vec3 v_normal; \n' +
141
+ 'varying vec2 v_texCoord; \n' +
142
+ '\n' +
143
+ 'void main(void) { \n' +
144
+ ' if(u_isLit) { \n' +
145
+ ' v_normal = u_rotationMatrix * a_normal; \n' +
146
+ ' } \n' +
147
+ ' if(u_hasTexture) { \n' +
148
+ ' v_texCoord = a_texCoord; \n' +
149
+ ' } \n' +
150
+ ' gl_Position = u_transformMatrix * vec4(a_position, 1.0); \n' +
151
+ '}';
152
+ this.solid_fs = '#ifdef GL_ES \n' +
153
+ ' precision mediump float; \n' +
154
+ '#endif \n' +
155
+ '\n' +
156
+ 'uniform bool u_isLit; \n' +
157
+ 'uniform bool u_isCast; \n' +
158
+ 'uniform bool u_hasTexture; \n' +
159
+ 'uniform float u_opacity; \n' +
160
+ 'uniform sampler2D s_palette; \n' +
161
+ 'uniform sampler2D s_texture; \n' +
162
+ 'uniform sampler2D s_sphereTexture; \n' +
163
+ 'varying vec3 v_normal; \n' +
164
+ 'varying vec2 v_texCoord; \n' +
165
+ '\n' +
166
+ 'void main(void) { \n' +
167
+ ' vec4 materialColor = u_isLit ? vec4(texture2D(s_palette, vec2(abs(v_normal.z), 0.0)).rgb, u_opacity) : vec4(1.0, 1.0, 1.0, u_opacity); \n' +
168
+ ' if(u_isCast) { \n' +
169
+ ' gl_FragColor = materialColor * texture2D(s_sphereTexture, vec2(0.5, -0.5) * v_normal.xy + vec2(0.5, 0.5)); \n' +
170
+ ' } \n' +
171
+ ' else { \n' +
172
+ ' gl_FragColor = u_hasTexture ? (materialColor * texture2D(s_texture, v_texCoord)) : materialColor; \n' +
173
+ ' } \n' +
174
+ '}';
175
+ this.picking_vs = '#ifdef GL_ES \n' +
176
+ ' precision mediump float; \n' +
177
+ '#endif \n' +
178
+ '\n' +
179
+ 'uniform mat4 u_transformMatrix; \n' +
180
+ 'attribute vec3 a_position; \n' +
181
+ '\n' +
182
+ 'void main(void) { \n' +
183
+ ' gl_Position = u_transformMatrix * vec4(a_position, 1.0); \n' +
184
+ '}';
185
+ this.picking_fs = '#ifdef GL_ES \n' +
186
+ ' precision mediump float; \n' +
187
+ '#endif \n' +
188
+ '\n' +
189
+ 'uniform vec3 u_pickingId; \n' +
190
+ '\n' +
191
+ 'void main(void) { \n' +
192
+ ' gl_FragColor = vec4(u_pickingId, 1.0); \n' +
193
+ '}';
194
+
195
+ function createProgram(gl, vSrc, fSrc) {
196
+ var vShader = gl.createShader(gl.VERTEX_SHADER);
197
+ gl.shaderSource(vShader, vSrc);
198
+ gl.compileShader(vShader);
199
+ if(!gl.getShaderParameter(vShader, gl.COMPILE_STATUS)) {
200
+ if(JSC3D.console)
201
+ JSC3D.console.logWarning('Error occured in shader compilation: ' + gl.getShaderInfoLog(vShader) + ' The corresponding program will be ignored.');
202
+ return null;
203
+ }
204
+
205
+ var fShader = gl.createShader(gl.FRAGMENT_SHADER);
206
+ gl.shaderSource(fShader, fSrc);
207
+ gl.compileShader(fShader);
208
+ if(!gl.getShaderParameter(fShader, gl.COMPILE_STATUS)) {
209
+ if(JSC3D.console)
210
+ JSC3D.console.logWarning('Error occured in shader compilation: ' + gl.getShaderInfoLog(fShader) + ' The corresponding program will be ignored.');
211
+ return null;
212
+ }
213
+
214
+ var program = gl.createProgram();
215
+ gl.attachShader(program, vShader);
216
+ gl.attachShader(program, fShader);
217
+ gl.linkProgram(program);
218
+ if(!gl.getProgramParameter(program, gl.LINK_STATUS)) {
219
+ if(JSC3D.console)
220
+ JSC3D.console.logWarning('Error occured when generating program: ' + gl.getProgramInfoLog(program) + ' This program will be ignored.');
221
+ return null;
222
+ }
223
+
224
+ program.attributes = {};
225
+ var numOfAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
226
+ for(var i=0; i<numOfAttribs; i++) {
227
+ var attrib = gl.getActiveAttrib(program, i);
228
+ program.attributes[attrib.name] = gl.getAttribLocation(program, attrib.name);
229
+ }
230
+
231
+ program.uniforms = {};
232
+ var numOfUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
233
+ for(var i=0; i<numOfUniforms; i++) {
234
+ var uniform = gl.getActiveUniform(program, i);
235
+ program.uniforms[uniform.name] = gl.getUniformLocation(program, uniform.name);
236
+ }
237
+
238
+ return program;
239
+ }
240
+
241
+ this.programs = {
242
+ screen: createProgram(this.gl, this.screen_vs, this.screen_fs),
243
+ gradient_background: createProgram(this.gl, this.gradient_background_vs, this.gradient_background_fs),
244
+ frame: createProgram(this.gl, this.frame_vs, this.frame_fs),
245
+ solid: createProgram(this.gl, this.solid_vs, this.solid_fs),
246
+ picking: createProgram(this.gl, this.picking_vs, this.picking_fs)
247
+ };
248
+
249
+ this.canvasBoard = this.gl.createBuffer();
250
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.canvasBoard);
251
+ this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1]), this.gl.STATIC_DRAW);
252
+ this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
253
+ };
254
+
255
+ JSC3D.WebGLRenderBackend.prototype.setBackgroundColors = function(color1, color2) {
256
+ this.bkgColors = [new Float32Array([(color1 & 0xff0000) / 16777216, (color1 & 0xff00) / 65536, (color1 & 0xff) / 256])];
257
+ if(color1 != color2)
258
+ this.bkgColors.push(new Float32Array([(color2 & 0xff0000) / 16777216, (color2 & 0xff00) / 65536, (color2 & 0xff) / 256]));
259
+ };
260
+
261
+ JSC3D.WebGLRenderBackend.prototype.setBackgroundImage = function(img) {
262
+ var gl = this.gl;
263
+
264
+ this.bkgTexture = gl.createTexture();
265
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
266
+ gl.bindTexture(gl.TEXTURE_2D, this.bkgTexture);
267
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
268
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
269
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
270
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
271
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
272
+ gl.bindTexture(gl.TEXTURE_2D, null);
273
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
274
+ };
275
+
276
+ JSC3D.WebGLRenderBackend.prototype.beginFrame = function(definition) {
277
+ var gl = this.gl;
278
+
279
+ function prepareFB(gl, fbo, w, h) {
280
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
281
+
282
+ // create a render buffer object and set it as the depth attachment of the fbo
283
+ var depthAttachment = gl.createRenderbuffer();
284
+ gl.bindRenderbuffer(gl.RENDERBUFFER, depthAttachment);
285
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, w, h);
286
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthAttachment);
287
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
288
+
289
+ // create a texture object and set it as the color attachment of the fbo
290
+ var colorAttachment = gl.createTexture();
291
+ //gl.activeTexture(gl.TEXTURE0);
292
+ gl.bindTexture(gl.TEXTURE_2D, colorAttachment);
293
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorAttachment, 0);
294
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
295
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
296
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
297
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
298
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
299
+ gl.bindTexture(gl.TEXTURE_2D, null);
300
+
301
+ fbo.width = w;
302
+ fbo.height = h;
303
+ fbo.texture = colorAttachment;
304
+ fbo.depthRB = depthAttachment;
305
+ }
306
+
307
+ function destroyFB(gl, fbo) {
308
+ gl.deleteRenderbuffer(fbo.depthRB);
309
+ gl.deleteTexture(fbo.texture);
310
+ gl.deleteFramebuffer(fbo);
311
+ }
312
+
313
+ // create the picking frame-buffer
314
+ if(!this.pickingFB) {
315
+ this.pickingFB = gl.createFramebuffer();
316
+ prepareFB(gl, this.pickingFB, this.canvas.width, this.canvas.height);
317
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
318
+ }
319
+
320
+ var frameWidth = this.canvas.width;
321
+ var frameHeight = this.canvas.height;
322
+ switch(definition) {
323
+ case 'low':
324
+ frameWidth = frameWidth >> 1;
325
+ frameHeight = frameHeight >> 1;
326
+ break;
327
+ case 'high':
328
+ frameWidth *= 2;
329
+ frameHeight *= 2;
330
+ break;
331
+ case 'standard':
332
+ default:
333
+ break;
334
+ }
335
+
336
+ /*
337
+ * For definitions other than 'standard', drawings will be generated in the back frame-buffer
338
+ * and then resampled to be applied to canvas.
339
+ */
340
+ if(frameWidth != this.canvas.width) {
341
+ if(!this.backFB) {
342
+ // create the back frame-buffer and bind it as render target
343
+ this.backFB = gl.createFramebuffer();
344
+ prepareFB(gl, this.backFB, frameWidth, frameHeight);
345
+ }
346
+ else if(this.definition != definition) {
347
+ // reallocate storage for the back frame-buffer as definition has changed, then bind it
348
+ // as render target
349
+ prepareFB(gl, this.backFB, frameWidth, frameHeight);
350
+ }
351
+ else {
352
+ // bind the back frame-buffer as render target
353
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.backFB);
354
+ }
355
+ }
356
+ else if(this.backFB) {
357
+ // delete and destroy the back frame-buffer as it is no longer needed under 'standard' definition
358
+ destroyFB(gl, this.backFB);
359
+ this.backFB = null;
360
+ }
361
+
362
+ this.definition = definition;
363
+
364
+ gl.viewport(0, 0, frameWidth, frameHeight);
365
+
366
+ /*
367
+ * Clear canvas with the given background.
368
+ */
369
+ if(this.bkgTexture) {
370
+ /*
371
+ * Apply background texture.
372
+ */
373
+ gl.frontFace(gl.CCW);
374
+ gl.disable(gl.DEPTH_TEST);
375
+ gl.clear(gl.DEPTH_BUFFER_BIT);
376
+ gl.useProgram(this.programs.screen);
377
+ gl.activeTexture(gl.TEXTURE0);
378
+ gl.bindTexture(gl.TEXTURE_2D, this.bkgTexture);
379
+ gl.uniform1i(this.programs.screen.uniforms['s_screenTexture'], 0);
380
+ gl.enableVertexAttribArray(0);
381
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.canvasBoard);
382
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
383
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
384
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
385
+ gl.bindTexture(gl.TEXTURE_2D, null);
386
+ }
387
+ else if(this.bkgColors.length > 1) {
388
+ /*
389
+ * Draw gradient background.
390
+ */
391
+ gl.frontFace(gl.CCW);
392
+ gl.disable(gl.DEPTH_TEST);
393
+ gl.clear(gl.DEPTH_BUFFER_BIT);
394
+ gl.useProgram(this.programs.gradient_background);
395
+ gl.uniform3fv(this.programs.gradient_background.uniforms['u_color1'], this.bkgColors[0]);
396
+ gl.uniform3fv(this.programs.gradient_background.uniforms['u_color2'], this.bkgColors[1]);
397
+ gl.enableVertexAttribArray(0);
398
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.canvasBoard);
399
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
400
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
401
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
402
+ }
403
+ else {
404
+ /*
405
+ * Clear canvas with a single background color.
406
+ */
407
+ var color = this.bkgColors[0];
408
+ gl.clearColor(color[0], color[1], color[2], 1);
409
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
410
+ }
411
+ };
412
+
413
+ JSC3D.WebGLRenderBackend.prototype.endFrame = function() {
414
+ var gl = this.gl;
415
+
416
+ // unbind any additional frame-buffer and redirect latter output to canvas
417
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
418
+
419
+ switch(this.definition) {
420
+ case 'low':
421
+ case 'high':
422
+ if(this.backFB) {
423
+ // resample the drawings in the back frame-buffer and apply it to canvas
424
+ gl.viewport(0, 0, this.canvas.width, this.canvas.height);
425
+ gl.frontFace(gl.CCW);
426
+ gl.disable(gl.DEPTH_TEST);
427
+ gl.useProgram(this.programs.screen);
428
+ gl.activeTexture(gl.TEXTURE0);
429
+ gl.bindTexture(gl.TEXTURE_2D, this.backFB.texture);
430
+ gl.uniform1i(this.programs.screen.uniforms['s_screenTexture'], 0);
431
+ gl.enableVertexAttribArray(0);
432
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.canvasBoard);
433
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
434
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
435
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
436
+ gl.bindTexture(gl.TEXTURE_2D, null);
437
+ }
438
+ break;
439
+ case 'standard':
440
+ default:
441
+ break;
442
+ }
443
+
444
+ gl.flush();
445
+ };
446
+
447
+ JSC3D.WebGLRenderBackend.prototype.render = function(renderList, transformMatrix, normalMatrix, renderMode, defaultMaterial, sphereMap) {
448
+ var gl = this.gl;
449
+
450
+ var transformMat4Flattened = new Float32Array([
451
+ transformMatrix.m00, transformMatrix.m10, transformMatrix.m20, 0,
452
+ transformMatrix.m01, transformMatrix.m11, transformMatrix.m21, 0,
453
+ transformMatrix.m02, transformMatrix.m12, transformMatrix.m22, 0,
454
+ transformMatrix.m03, transformMatrix.m13, transformMatrix.m23, 1
455
+ ]);
456
+
457
+ var normalMat3Flattened = new Float32Array([
458
+ normalMatrix.m00, normalMatrix.m10, normalMatrix.m20,
459
+ normalMatrix.m01, normalMatrix.m11, normalMatrix.m21,
460
+ normalMatrix.m02, normalMatrix.m12, normalMatrix.m22
461
+ ]);
462
+
463
+ // render the color pass
464
+ this.renderColorPass(renderList, transformMat4Flattened, normalMat3Flattened, renderMode, defaultMaterial, sphereMap);
465
+
466
+ // render the picking pass
467
+ if(this.pickingFB) {
468
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.pickingFB);
469
+ this.renderPickingPass(renderList, transformMat4Flattened, defaultMaterial);
470
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
471
+ }
472
+ };
473
+
474
+ JSC3D.WebGLRenderBackend.prototype.pick = function(x, y) {
475
+ if(!this.pickingFB)
476
+ return 0;
477
+
478
+ var gl = this.gl;
479
+
480
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this.pickingFB);
481
+ gl.readPixels(x, this.pickingFB.height - y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, this.pickingResult);
482
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
483
+
484
+ return this.pickingResult[0] << 16 | this.pickingResult[1] << 8 | this.pickingResult[2];
485
+ };
486
+
487
+ JSC3D.WebGLRenderBackend.prototype.renderColorPass = function(renderList, transformMat4, normalMat3, renderMode, defaultMaterial, sphereMap) {
488
+ if(sphereMap && sphereMap.hasData() && !sphereMap.compiled)
489
+ this.compileTexture(sphereMap);
490
+
491
+ var gl = this.gl;
492
+
493
+ gl.disable(gl.BLEND);
494
+ gl.enable(gl.DEPTH_TEST);
495
+ gl.depthMask(true);
496
+ gl.frontFace(gl.CCW);
497
+
498
+ var curProgram = null;
499
+ var isBlendEnabled = false;
500
+
501
+ for(var i=0; i<renderList.length; i++) {
502
+ var mesh = renderList[i];
503
+ if(mesh.isTrivial() || !mesh.visible)
504
+ continue;
505
+
506
+ var material = mesh.material || defaultMaterial;
507
+ var texture = mesh.hasTexture() ? mesh.texture : null;
508
+ var isTransparent = (material.transparency > 0) || (texture && texture.hasTransparency);
509
+ var opacity = 1 - material.transparency;
510
+
511
+ if(!material.compiled)
512
+ this.compileMaterial(material);
513
+ if(texture && !texture.compiled)
514
+ this.compileTexture(texture);
515
+
516
+ if(mesh.isDoubleSided)
517
+ gl.disable(gl.CULL_FACE);
518
+ else
519
+ gl.enable(gl.CULL_FACE);
520
+
521
+ // switch blend mode
522
+ if(isTransparent != isBlendEnabled) {
523
+ if(isTransparent) {
524
+ gl.depthMask(false);
525
+ gl.enable(gl.BLEND);
526
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE);
527
+ }
528
+ else {
529
+ gl.depthMask(true);
530
+ gl.disable(gl.BLEND);
531
+ }
532
+ isBlendEnabled = isTransparent;
533
+ }
534
+
535
+ var isSphereMapped = mesh.isEnvironmentCast && (sphereMap != null);
536
+
537
+ // resolve current render mode and chose a right program
538
+ var rmode = mesh.renderMode || renderMode;
539
+ var program;
540
+ switch(rmode) {
541
+ case 'point':
542
+ case 'wireframe':
543
+ program = this.programs.frame;
544
+ break;
545
+ case 'flat':
546
+ case 'smooth':
547
+ program = this.programs.solid;
548
+ break;
549
+ case 'texture':
550
+ case 'textureflat':
551
+ if(!texture)
552
+ rmode = 'flat';
553
+ program = this.programs.solid;
554
+ break;
555
+ case 'texturesmooth':
556
+ if(!texture && !isSphereMapped)
557
+ rmode = 'smooth';
558
+ program = this.programs.solid;
559
+ break;
560
+ default:
561
+ rmode = 'flat';
562
+ program = this.programs.solid;
563
+ break;
564
+ }
565
+
566
+ if(!mesh.compiled || mesh.compiled.remderMode != rmode)
567
+ this.compileMesh(mesh, rmode);
568
+
569
+ if(curProgram != program) {
570
+ gl.useProgram(program);
571
+ curProgram = program;
572
+ }
573
+
574
+ // render this mesh with the correct render mode
575
+ switch(rmode) {
576
+ case 'point':
577
+ gl.uniform1i(program.uniforms['u_isPoint'], rmode == 'point');
578
+ gl.uniform3fv(program.uniforms['u_materialColor'], material.compiled.diffColor);
579
+ gl.uniformMatrix4fv(program.uniforms['u_transformMatrix'], false, transformMat4);
580
+ gl.enableVertexAttribArray(program.attributes['a_position']);
581
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.coords);
582
+ gl.vertexAttribPointer(program.attributes['a_position'], 3, gl.FLOAT, false, 0, 0);
583
+ gl.drawArrays(gl.POINTS, 0, mesh.compiled.coordCount);
584
+ break;
585
+ case 'wireframe':
586
+ //TODO: implement this
587
+ break;
588
+ case 'flat':
589
+ case 'smooth':
590
+ gl.uniform1i(program.uniforms['u_isLit'], true);
591
+ gl.uniform1i(program.uniforms['u_isCast'], false);
592
+ gl.uniform1i(program.uniforms['u_hasTexture'], false);
593
+ gl.uniform1f(program.uniforms['u_opacity'], opacity);
594
+ gl.uniformMatrix3fv(program.uniforms['u_rotationMatrix'], false, normalMat3);
595
+ gl.uniformMatrix4fv(program.uniforms['u_transformMatrix'], false, transformMat4);
596
+ gl.activeTexture(gl.TEXTURE0);
597
+ gl.bindTexture(gl.TEXTURE_2D, material.compiled.palette);
598
+ gl.uniform1i(program.uniforms['s_palette'], 0);
599
+ gl.enableVertexAttribArray(program.attributes['a_position']);
600
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.coords);
601
+ gl.vertexAttribPointer(program.attributes['a_position'], 3, gl.FLOAT, false, 0, 0);
602
+ gl.enableVertexAttribArray(program.attributes['a_normal']);
603
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.normals);
604
+ gl.vertexAttribPointer(program.attributes['a_normal'], 3, gl.FLOAT, false, 0, 0);
605
+ gl.disableVertexAttribArray(program.attributes['a_texCoord']);
606
+ gl.drawArrays(gl.TRIANGLES, 0, mesh.compiled.coordCount);
607
+ break;
608
+ case 'texture':
609
+ gl.uniform1i(program.uniforms['u_isLit'], false);
610
+ gl.uniform1i(program.uniforms['u_isCast'], false);
611
+ gl.uniform1i(program.uniforms['u_hasTexture'], true);
612
+ gl.uniform1f(program.uniforms['u_opacity'], opacity);
613
+ gl.uniformMatrix3fv(program.uniforms['u_rotationMatrix'], false, normalMat3);
614
+ gl.uniformMatrix4fv(program.uniforms['u_transformMatrix'], false, transformMat4);
615
+ gl.activeTexture(gl.TEXTURE1);
616
+ gl.bindTexture(gl.TEXTURE_2D, texture.compiled.tex);
617
+ gl.uniform1i(program.uniforms['s_texture'], 1);
618
+ gl.enableVertexAttribArray(program.attributes['a_position']);
619
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.coords);
620
+ gl.vertexAttribPointer(program.attributes['a_position'], 3, gl.FLOAT, false, 0, 0);
621
+ gl.disableVertexAttribArray(program.attributes['a_normal']);
622
+ gl.enableVertexAttribArray(program.attributes['a_texCoord']);
623
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.texcoords);
624
+ gl.vertexAttribPointer(program.attributes['a_texCoord'], 2, gl.FLOAT, false, 0, 0);
625
+ gl.drawArrays(gl.TRIANGLES, 0, mesh.compiled.coordCount);
626
+ break;
627
+ case 'textureflat':
628
+ gl.uniform1i(program.uniforms['u_isLit'], true);
629
+ gl.uniform1i(program.uniforms['u_isCast'], false);
630
+ gl.uniform1i(program.uniforms['u_hasTexture'], true);
631
+ gl.uniform1f(program.uniforms['u_opacity'], opacity);
632
+ gl.uniformMatrix3fv(program.uniforms['u_rotationMatrix'], false, normalMat3);
633
+ gl.uniformMatrix4fv(program.uniforms['u_transformMatrix'], false, transformMat4);
634
+ gl.activeTexture(gl.TEXTURE0);
635
+ gl.bindTexture(gl.TEXTURE_2D, material.compiled.palette);
636
+ gl.uniform1i(program.uniforms['s_palette'], 0);
637
+ gl.activeTexture(gl.TEXTURE1);
638
+ gl.bindTexture(gl.TEXTURE_2D, texture.compiled.tex);
639
+ gl.uniform1i(program.uniforms['s_texture'], 1);
640
+ gl.enableVertexAttribArray(program.attributes['a_position']);
641
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.coords);
642
+ gl.vertexAttribPointer(program.attributes['a_position'], 3, gl.FLOAT, false, 0, 0);
643
+ gl.enableVertexAttribArray(program.attributes['a_normal']);
644
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.normals);
645
+ gl.vertexAttribPointer(program.attributes['a_normal'], 3, gl.FLOAT, false, 0, 0);
646
+ gl.enableVertexAttribArray(program.attributes['a_texCoord']);
647
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.texcoords);
648
+ gl.vertexAttribPointer(program.attributes['a_texCoord'], 2, gl.FLOAT, false, 0, 0);
649
+ gl.drawArrays(gl.TRIANGLES, 0, mesh.compiled.coordCount);
650
+ break;
651
+ case 'texturesmooth':
652
+ gl.uniform1i(program.uniforms['u_isLit'], true);
653
+ gl.uniform1i(program.uniforms['u_isCast'], isSphereMapped);
654
+ gl.uniform1i(program.uniforms['u_hasTexture'], !isSphereMapped);
655
+ gl.uniform1f(program.uniforms['u_opacity'], opacity);
656
+ gl.uniformMatrix3fv(program.uniforms['u_rotationMatrix'], false, normalMat3);
657
+ gl.uniformMatrix4fv(program.uniforms['u_transformMatrix'], false, transformMat4);
658
+ gl.activeTexture(gl.TEXTURE0);
659
+ gl.bindTexture(gl.TEXTURE_2D, material.compiled.palette);
660
+ gl.uniform1i(program.uniforms['s_palette'], 0);
661
+ if(!isSphereMapped) {
662
+ gl.activeTexture(gl.TEXTURE1);
663
+ gl.bindTexture(gl.TEXTURE_2D, texture.compiled.tex);
664
+ gl.uniform1i(program.uniforms['s_texture'], 1);
665
+ }
666
+ else {
667
+ gl.activeTexture(gl.TEXTURE2);
668
+ gl.bindTexture(gl.TEXTURE_2D, sphereMap.compiled.tex);
669
+ gl.uniform1i(program.uniforms['s_sphereTexture'], 2);
670
+ }
671
+ gl.enableVertexAttribArray(program.attributes['a_position']);
672
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.coords);
673
+ gl.vertexAttribPointer(program.attributes['a_position'], 3, gl.FLOAT, false, 0, 0);
674
+ gl.enableVertexAttribArray(program.attributes['a_normal']);
675
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.normals);
676
+ gl.vertexAttribPointer(program.attributes['a_normal'], 3, gl.FLOAT, false, 0, 0);
677
+ if(!isSphereMapped) {
678
+ gl.enableVertexAttribArray(program.attributes['a_texCoord']);
679
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.texcoords);
680
+ gl.vertexAttribPointer(program.attributes['a_texCoord'], 2, gl.FLOAT, false, 0, 0);
681
+ }
682
+ else
683
+ gl.disableVertexAttribArray(program.attributes['a_texCoord']);
684
+ gl.drawArrays(gl.TRIANGLES, 0, mesh.compiled.coordCount);
685
+ break;
686
+ default:
687
+ break;
688
+ }
689
+ }
690
+ };
691
+
692
+ JSC3D.WebGLRenderBackend.prototype.renderPickingPass = function(renderList, transformMat4, defaultMaterial) {
693
+ var gl = this.gl;
694
+
695
+ gl.disable(gl.BLEND);
696
+ gl.enable(gl.DEPTH_TEST);
697
+ gl.depthMask(true);
698
+ gl.frontFace(gl.CCW);
699
+
700
+ gl.viewport(0, 0, this.pickingFB.width, this.pickingFB.height);
701
+ gl.clearColor(0, 0, 0, 1);
702
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
703
+
704
+ gl.useProgram(this.programs.picking);
705
+
706
+ for(var i=0; i<renderList.length; i++) {
707
+ var mesh = renderList[i];
708
+ if(mesh.isTrivial() || !mesh.visible)
709
+ continue;
710
+
711
+ // skip the mesh if it is totally transparent
712
+ var material = mesh.material || defaultMaterial;
713
+ if(material.transparency > 0.99)
714
+ continue;
715
+
716
+ if(mesh.isDoubleSided)
717
+ gl.disable(gl.CULL_FACE);
718
+ else
719
+ gl.enable(gl.CULL_FACE);
720
+
721
+ gl.uniformMatrix4fv(this.programs.picking.uniforms['u_transformMatrix'], false, transformMat4);
722
+ gl.uniform3fv(this.programs.picking.uniforms['u_pickingId'], mesh.compiled.pickingId);
723
+ gl.enableVertexAttribArray(this.programs.picking.attributes['a_position']);
724
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.coords);
725
+ gl.vertexAttribPointer(this.programs.picking.attributes['a_position'], 3, gl.FLOAT, false, 0, 0);
726
+
727
+ switch(mesh.compiled.remderMode) {
728
+ case 'point':
729
+ gl.drawArrays(gl.POINTS, 0, mesh.compiled.coordCount);
730
+ break;
731
+ case 'wireframe':
732
+ //TODO: implement this
733
+ break;
734
+ case 'flat':
735
+ case 'smooth':
736
+ case 'texture':
737
+ case 'textureflat':
738
+ case 'texturesmooth':
739
+ default:
740
+ gl.drawArrays(gl.TRIANGLES, 0, mesh.compiled.coordCount);
741
+ break;
742
+ }
743
+ }
744
+ };
745
+
746
+ JSC3D.WebGLRenderBackend.prototype.compileMesh = function(mesh, renderMode) {
747
+ if(mesh.isTrivial())
748
+ return false;
749
+
750
+ renderMode = mesh.renderMode || renderMode;
751
+
752
+ var gl = this.gl;
753
+
754
+ var needFlat = (renderMode == 'flat') || (renderMode == 'textureflat');
755
+
756
+ var hasTrianglesOnly = mesh.indexBuffer.length == 4 * mesh.faceCount;
757
+ var hasTextureCoords = mesh.texCoordBuffer && mesh.texCoordBuffer.length >= 2;
758
+
759
+ var ibuf = mesh.indexBuffer;
760
+ var vbuf = mesh.vertexBuffer;
761
+ var tbuf = mesh.texCoordBuffer;
762
+ var tibuf = mesh.texCoordIndexBuffer || ibuf;
763
+ var nbuf = mesh.vertexNormalBuffer;
764
+ var nibuf = mesh.vertexNormalIndexBuffer || ibuf;
765
+ var fnbuf = mesh.faceNormalBuffer;
766
+ var numOfFaces = mesh.faceCount;
767
+
768
+ if (!mesh.compiled) {
769
+ /*
770
+ * Rebuild all primitives from scratch.
771
+ */
772
+
773
+ mesh.compiled = {
774
+ isIndexed: false
775
+ };
776
+
777
+ mesh.compiled.pickingId = new Float32Array([
778
+ (mesh.internalId & 0xff0000) / 16777216, (mesh.internalId & 0xff00) / 65536, (mesh.internalId & 0xff) / 256
779
+ ]);
780
+
781
+ var triangles, edges, coords, normals, texcoords;
782
+ if(hasTrianglesOnly) {
783
+ coords = new Float32Array(9 * numOfFaces);
784
+ normals = new Float32Array(9 * numOfFaces);
785
+ if(hasTextureCoords)
786
+ texcoords = new Float32Array(6 * numOfFaces);
787
+ var v0, v1, v2;
788
+ var n0, n1, n2;
789
+ var t0, t1, t2;
790
+ for(var i=0, j=0, k=0, faceIndex=0; i<ibuf.length; i+=4, j+=9, k+=6, faceIndex++) {
791
+ v0 = ibuf[i ] * 3;
792
+ v1 = ibuf[i + 1] * 3;
793
+ v2 = ibuf[i + 2] * 3;
794
+ coords[j ] = vbuf[v0 ];
795
+ coords[j + 1] = vbuf[v0 + 1];
796
+ coords[j + 2] = vbuf[v0 + 2];
797
+ coords[j + 3] = vbuf[v1 ];
798
+ coords[j + 4] = vbuf[v1 + 1];
799
+ coords[j + 5] = vbuf[v1 + 2];
800
+ coords[j + 6] = vbuf[v2 ];
801
+ coords[j + 7] = vbuf[v2 + 1];
802
+ coords[j + 8] = vbuf[v2 + 2];
803
+
804
+ if(needFlat) {
805
+ n0 = faceIndex * 3;
806
+ normals[j ] = normals[j + 3] = normals[j + 6] = fnbuf[n0 ];
807
+ normals[j + 1] = normals[j + 4] = normals[j + 7] = fnbuf[n0 + 1];
808
+ normals[j + 2] = normals[j + 5] = normals[j + 8] = fnbuf[n0 + 2];
809
+
810
+ }
811
+ else {
812
+ n0 = nibuf[i ] * 3;
813
+ n1 = nibuf[i + 1] * 3;
814
+ n2 = nibuf[i + 2] * 3;
815
+ normals[j ] = nbuf[n0 ];
816
+ normals[j + 1] = nbuf[n0 + 1];
817
+ normals[j + 2] = nbuf[n0 + 2];
818
+ normals[j + 3] = nbuf[n1 ];
819
+ normals[j + 4] = nbuf[n1 + 1];
820
+ normals[j + 5] = nbuf[n1 + 2];
821
+ normals[j + 6] = nbuf[n2 ];
822
+ normals[j + 7] = nbuf[n2 + 1];
823
+ normals[j + 8] = nbuf[n2 + 2];
824
+ }
825
+
826
+ if(hasTextureCoords) {
827
+ t0 = tibuf[i ] * 2;
828
+ t1 = tibuf[i + 1] * 2;
829
+ t2 = tibuf[i + 2] * 2;
830
+ texcoords[k ] = tbuf[t0 ];
831
+ texcoords[k + 1] = tbuf[t0 + 1];
832
+ texcoords[k + 2] = tbuf[t1 ];
833
+ texcoords[k + 3] = tbuf[t1 + 1];
834
+ texcoords[k + 4] = tbuf[t2 ];
835
+ texcoords[k + 5] = tbuf[t2 + 1];
836
+ }
837
+ }
838
+ }
839
+ else {
840
+ coords = [];
841
+ normals = [];
842
+ if(hasTextureCoords)
843
+ texcoords = [];
844
+ var v0, v1, v2;
845
+ var n0, n1, n2;
846
+ var t0, t1, t2;
847
+ for(var i=0, j=0; i<numOfFaces; i++) {
848
+ v0 = ibuf[j ] * 3;
849
+ v1 = ibuf[j + 1] * 3;
850
+ n0 = nibuf[j ] * 3;
851
+ n1 = nibuf[j + 1] * 3;
852
+ if(hasTextureCoords) {
853
+ t0 = tibuf[j ] * 2;
854
+ t1 = tibuf[j + 1] * 2;
855
+ }
856
+ j += 2;
857
+ while(ibuf[j] >= 0) {
858
+ v2 = ibuf[j] * 3;
859
+ coords.push(vbuf[v0], vbuf[v0 + 1], vbuf[v0 + 2], vbuf[v1], vbuf[v1 + 1], vbuf[v1 + 2], vbuf[v2], vbuf[v2 + 1], vbuf[v2 + 2]);
860
+ v1 = v2;
861
+ if(needFlat) {
862
+ n0 = i * 3;
863
+ normals.push(fnbuf[n0], fnbuf[n0 + 1], fnbuf[n0 + 2], fnbuf[n0], fnbuf[n0 + 1], fnbuf[n0 + 2], fnbuf[n0], fnbuf[n0 + 1], fnbuf[n0 + 2]);
864
+ }
865
+ else {
866
+ n2 = nibuf[j] * 3;
867
+ normals.push(nbuf[n0], nbuf[n0 + 1], nbuf[n0 + 2], nbuf[n1], nbuf[n1 + 1], nbuf[n1 + 2], nbuf[n2], nbuf[n2 + 1], nbuf[n2 + 2]);
868
+ n1 = n2;
869
+ }
870
+ if(hasTextureCoords) {
871
+ t2 = tibuf[j] * 2;
872
+ texcoords.push(tbuf[t0], tbuf[t0 + 1], tbuf[t1], tbuf[t1 + 1], tbuf[t2], tbuf[t2 + 1]);
873
+ t1 = t2;
874
+ }
875
+ j++;
876
+ }
877
+ j++;
878
+ }
879
+ coords = new Float32Array(coords);
880
+ normals = new Float32Array(normals);
881
+ if(hasTextureCoords)
882
+ texcoords = new Float32Array(texcoords);
883
+ }
884
+
885
+ mesh.compiled.coords = gl.createBuffer();
886
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.coords);
887
+ gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW);
888
+ mesh.compiled.normals = gl.createBuffer();
889
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.normals);
890
+ gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
891
+ if(hasTextureCoords) {
892
+ mesh.compiled.texcoords = gl.createBuffer();
893
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.texcoords);
894
+ gl.bufferData(gl.ARRAY_BUFFER, texcoords, gl.STATIC_DRAW);
895
+ }
896
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
897
+
898
+ mesh.compiled.faceCount = numOfFaces;
899
+ mesh.compiled.coordCount = coords.length / 3;
900
+ }
901
+ else {
902
+ /*
903
+ * Do not need to rebuild, just update normal data.
904
+ */
905
+
906
+ var isFlat = (mesh.compiled.remderMode == 'flat') || (mesh.compiled.remderMode == 'textureflat');
907
+ if(isFlat != needFlat) {
908
+ var normals;
909
+ if(hasTrianglesOnly) {
910
+ normals = new Float32Array(9 * numOfFaces);
911
+ var n0, n1, n2
912
+ for(var i=0, j=0, faceIndex=0; i<ibuf.length; i+=4, j+=9, faceIndex++) {
913
+ if(needFlat) {
914
+ n0 = faceIndex * 3;
915
+ normals[j ] = normals[j + 3] = normals[j + 6] = fnbuf[n0 ];
916
+ normals[j + 1] = normals[j + 4] = normals[j + 7] = fnbuf[n0 + 1];
917
+ normals[j + 2] = normals[j + 5] = normals[j + 8] = fnbuf[n0 + 2];
918
+
919
+ }
920
+ else {
921
+ n0 = nibuf[i ] * 3;
922
+ n1 = nibuf[i + 1] * 3;
923
+ n2 = nibuf[i + 2] * 3;
924
+ normals[j ] = nbuf[n0 ];
925
+ normals[j + 1] = nbuf[n0 + 1];
926
+ normals[j + 2] = nbuf[n0 + 2];
927
+ normals[j + 3] = nbuf[n1 ];
928
+ normals[j + 4] = nbuf[n1 + 1];
929
+ normals[j + 5] = nbuf[n1 + 2];
930
+ normals[j + 6] = nbuf[n2 ];
931
+ normals[j + 7] = nbuf[n2 + 1];
932
+ normals[j + 8] = nbuf[n2 + 2];
933
+ }
934
+ }
935
+ }
936
+ else {
937
+ normals = [];
938
+ var n0, n1, n2;
939
+ for(var i=0, j=0; i<numOfFaces; i++) {
940
+ n0 = nibuf[j++] * 3;
941
+ n1 = nibuf[j++] * 3;
942
+ while(ibuf[j] >= 0) {
943
+ if(needFlat) {
944
+ n0 = i * 3;
945
+ normals.push(fnbuf[n0], fnbuf[n0 + 1], fnbuf[n0 + 2], fnbuf[n0], fnbuf[n0 + 1], fnbuf[n0 + 2], fnbuf[n0], fnbuf[n0 + 1], fnbuf[n0 + 2]);
946
+ }
947
+ else {
948
+ n2 = nibuf[j] * 3;
949
+ normals.push(nbuf[n0], nbuf[n0 + 1], nbuf[n0 + 2], nbuf[n1], nbuf[n1 + 1], nbuf[n1 + 2], nbuf[n2], nbuf[n2 + 1], nbuf[n2 + 2]);
950
+ n1 = n2;
951
+ }
952
+ j++;
953
+ }
954
+ j++;
955
+ }
956
+ normals = new Float32Array(normals);
957
+ }
958
+
959
+ if(this.isIE11) {
960
+ // IE11 does not support bufferSubData() for buffer content update. So the normal VBO has to be reallocated for the new data.
961
+ gl.deleteBuffer(mesh.compiled.normals);
962
+ mesh.compiled.normals = gl.createBuffer();
963
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.normals);
964
+ gl.bufferData(gl.ARRAY_BUFFER, normals, gl.STATIC_DRAW);
965
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
966
+ }
967
+ else {
968
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.compiled.normals);
969
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, normals);
970
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
971
+ }
972
+ }
973
+ }
974
+
975
+ mesh.compiled.remderMode = renderMode;
976
+
977
+ return true;
978
+ };
979
+
980
+ JSC3D.WebGLRenderBackend.prototype.compileMaterial = function(material) {
981
+ var gl = this.gl;
982
+
983
+ material.compiled = {
984
+ diffColor: new Float32Array([(material.diffuseColor & 0xff0000) / 16777216, (material.diffuseColor & 0xff00) / 65536, (material.diffuseColor & 0xff) / 256])
985
+ };
986
+
987
+ var rgba = new Uint8Array((new Uint32Array(material.getPalette())).buffer);
988
+ // the sequence should be converted from BGRA to RGBA by swapping each 1st and 3rd components
989
+ //TODO: this only works on Little-Endian platforms. We shall also take into account the case for Big-Endian.
990
+ for(var i=0; i<rgba.length; i+=4) {
991
+ var tmp = rgba[i];
992
+ rgba[i] = rgba[i + 2];
993
+ rgba[i + 2] = tmp;
994
+ }
995
+
996
+ material.compiled.palette = gl.createTexture();
997
+ gl.bindTexture(gl.TEXTURE_2D, material.compiled.palette);
998
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, rgba);
999
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
1000
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
1001
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
1002
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
1003
+ gl.bindTexture(gl.TEXTURE_2D, null);
1004
+
1005
+ return true;
1006
+ };
1007
+
1008
+ JSC3D.WebGLRenderBackend.prototype.compileTexture = function(texture, genMipmap) {
1009
+ if(!texture.hasData())
1010
+ return false;
1011
+
1012
+ genMipmap = genMipmap || texture.hasMipmap();
1013
+
1014
+ var gl = this.gl;
1015
+
1016
+ texture.compiled = {
1017
+ hasMipmap: genMipmap
1018
+ };
1019
+
1020
+ var rgba = new Uint8Array((new Uint32Array(texture.data)).buffer);
1021
+ // convert the sequence from BGRA to RGBA by swapping each 1st and 3rd components
1022
+ //TODO: also take into account the case for Big-Endian?
1023
+ for(var i=0; i<rgba.length; i+=4) {
1024
+ var tmp = rgba[i];
1025
+ rgba[i] = rgba[i + 2];
1026
+ rgba[i + 2] = tmp;
1027
+ }
1028
+
1029
+ texture.compiled.tex = gl.createTexture();
1030
+ gl.bindTexture(gl.TEXTURE_2D, texture.compiled.tex);
1031
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, texture.width, texture.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, rgba);
1032
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
1033
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, genMipmap ? gl.LINEAR_MIPMAP_NEAREST : gl.LINEAR);
1034
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
1035
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
1036
+ if(genMipmap)
1037
+ gl.generateMipmap(gl.TEXTURE_2D);
1038
+ gl.bindTexture(gl.TEXTURE_2D, null);
1039
+
1040
+ return true;
1041
+ };