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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/jsc3d-js-rails.gemspec +23 -0
- data/lib/jsc3d/js/rails.rb +10 -0
- data/lib/jsc3d/js/rails/version.rb +7 -0
- data/vendor/assets/javascripts/jsc3d.3ds.js +717 -0
- data/vendor/assets/javascripts/jsc3d.console.js +147 -0
- data/vendor/assets/javascripts/jsc3d.js +5474 -0
- data/vendor/assets/javascripts/jsc3d.touch.js +1459 -0
- data/vendor/assets/javascripts/jsc3d.webgl.js +1041 -0
- data/vendor/assets/javascripts/jsc3d_ie.js +4500 -0
- metadata +106 -0
@@ -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
|
+
};
|