jsc3d-rails 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE +21 -0
  4. data/README.md +33 -0
  5. data/jsc3d-rails.gemspec +23 -0
  6. data/lib/generators/jquery/install/install_generator.rb +59 -0
  7. data/lib/jsc3d-rails.rb +1 -0
  8. data/lib/jsc3d/rails.rb +8 -0
  9. data/lib/jsc3d/rails/engine.rb +6 -0
  10. data/lib/jsc3d/rails/version.rb +6 -0
  11. data/vendor/assets/javascripts/bin/jsc3d.min.js +139 -0
  12. data/vendor/assets/javascripts/bin/jsc3d_ie.min.js +133 -0
  13. data/vendor/assets/javascripts/build/compile.bat +1 -0
  14. data/vendor/assets/javascripts/build/compile.sh +2 -0
  15. data/vendor/assets/javascripts/build/gendoc.bat +2 -0
  16. data/vendor/assets/javascripts/build/gendoc.sh +2 -0
  17. data/vendor/assets/javascripts/docs/files.html +247 -0
  18. data/vendor/assets/javascripts/docs/index.html +349 -0
  19. data/vendor/assets/javascripts/docs/symbols/JSC3D.AABB.html +436 -0
  20. data/vendor/assets/javascripts/docs/symbols/JSC3D.BinaryStream.html +1051 -0
  21. data/vendor/assets/javascripts/docs/symbols/JSC3D.LoaderSelector.html +455 -0
  22. data/vendor/assets/javascripts/docs/symbols/JSC3D.Material.html +430 -0
  23. data/vendor/assets/javascripts/docs/symbols/JSC3D.Math3D.html +468 -0
  24. data/vendor/assets/javascripts/docs/symbols/JSC3D.Matrix3x4.html +682 -0
  25. data/vendor/assets/javascripts/docs/symbols/JSC3D.Mesh.html +628 -0
  26. data/vendor/assets/javascripts/docs/symbols/JSC3D.ObjLoader.html +428 -0
  27. data/vendor/assets/javascripts/docs/symbols/JSC3D.PickInfo.html +332 -0
  28. data/vendor/assets/javascripts/docs/symbols/JSC3D.Scene.html +571 -0
  29. data/vendor/assets/javascripts/docs/symbols/JSC3D.StlLoader.html +473 -0
  30. data/vendor/assets/javascripts/docs/symbols/JSC3D.Texture.html +589 -0
  31. data/vendor/assets/javascripts/docs/symbols/JSC3D.Viewer.html +1156 -0
  32. data/vendor/assets/javascripts/docs/symbols/JSC3D.html +329 -0
  33. data/vendor/assets/javascripts/docs/symbols/_global_.html +279 -0
  34. data/vendor/assets/javascripts/docs/symbols/src/jsc3d.js.html +4804 -0
  35. data/vendor/assets/javascripts/jsc3d.console.js +146 -0
  36. data/vendor/assets/javascripts/jsc3d.js +4796 -0
  37. data/vendor/assets/javascripts/jsc3d_ie.js +4487 -0
  38. data/vendor/assets/javascripts/tools/closure/COPYING +202 -0
  39. data/vendor/assets/javascripts/tools/closure/README +261 -0
  40. data/vendor/assets/javascripts/tools/closure/USAGE +87 -0
  41. data/vendor/assets/javascripts/tools/closure/compiler.jar +0 -0
  42. data/vendor/assets/javascripts/tools/doc_generator/README.txt +146 -0
  43. data/vendor/assets/javascripts/tools/doc_generator/app/frame.js +33 -0
  44. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Chain.js +102 -0
  45. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Dumper.js +144 -0
  46. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Hash.js +84 -0
  47. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Link.js +148 -0
  48. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Namespace.js +10 -0
  49. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Opt.js +134 -0
  50. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Reflection.js +26 -0
  51. data/vendor/assets/javascripts/tools/doc_generator/app/frame/String.js +93 -0
  52. data/vendor/assets/javascripts/tools/doc_generator/app/frame/Testrun.js +129 -0
  53. data/vendor/assets/javascripts/tools/doc_generator/app/handlers/FOODOC.js +26 -0
  54. data/vendor/assets/javascripts/tools/doc_generator/app/handlers/XMLDOC.js +26 -0
  55. data/vendor/assets/javascripts/tools/doc_generator/app/handlers/XMLDOC/DomReader.js +159 -0
  56. data/vendor/assets/javascripts/tools/doc_generator/app/handlers/XMLDOC/XMLDoc.js +16 -0
  57. data/vendor/assets/javascripts/tools/doc_generator/app/handlers/XMLDOC/XMLParse.js +292 -0
  58. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC.js +101 -0
  59. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/DocComment.js +200 -0
  60. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/DocTag.js +294 -0
  61. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/JsDoc.js +126 -0
  62. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/JsPlate.js +109 -0
  63. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/Lang.js +144 -0
  64. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/Parser.js +107 -0
  65. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/PluginManager.js +33 -0
  66. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/Symbol.js +643 -0
  67. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/SymbolSet.js +229 -0
  68. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/TextStream.js +41 -0
  69. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/Token.js +18 -0
  70. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/TokenReader.js +332 -0
  71. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/TokenStream.js +133 -0
  72. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/Util.js +32 -0
  73. data/vendor/assets/javascripts/tools/doc_generator/app/lib/JSDOC/Walker.js +459 -0
  74. data/vendor/assets/javascripts/tools/doc_generator/app/main.js +111 -0
  75. data/vendor/assets/javascripts/tools/doc_generator/app/plugins/commentSrcJson.js +20 -0
  76. data/vendor/assets/javascripts/tools/doc_generator/app/plugins/frameworkPrototype.js +16 -0
  77. data/vendor/assets/javascripts/tools/doc_generator/app/plugins/functionCall.js +10 -0
  78. data/vendor/assets/javascripts/tools/doc_generator/app/plugins/publishSrcHilite.js +62 -0
  79. data/vendor/assets/javascripts/tools/doc_generator/app/plugins/symbolLink.js +9 -0
  80. data/vendor/assets/javascripts/tools/doc_generator/app/plugins/tagParamConfig.js +31 -0
  81. data/vendor/assets/javascripts/tools/doc_generator/app/plugins/tagSynonyms.js +43 -0
  82. data/vendor/assets/javascripts/tools/doc_generator/app/run.js +348 -0
  83. data/vendor/assets/javascripts/tools/doc_generator/app/t/TestDoc.js +144 -0
  84. data/vendor/assets/javascripts/tools/doc_generator/app/t/runner.js +13 -0
  85. data/vendor/assets/javascripts/tools/doc_generator/app/test.js +307 -0
  86. data/vendor/assets/javascripts/tools/doc_generator/app/test/addon.js +24 -0
  87. data/vendor/assets/javascripts/tools/doc_generator/app/test/anon_inner.js +14 -0
  88. data/vendor/assets/javascripts/tools/doc_generator/app/test/augments.js +31 -0
  89. data/vendor/assets/javascripts/tools/doc_generator/app/test/augments2.js +26 -0
  90. data/vendor/assets/javascripts/tools/doc_generator/app/test/borrows.js +41 -0
  91. data/vendor/assets/javascripts/tools/doc_generator/app/test/borrows2.js +23 -0
  92. data/vendor/assets/javascripts/tools/doc_generator/app/test/config.js +22 -0
  93. data/vendor/assets/javascripts/tools/doc_generator/app/test/constructs.js +18 -0
  94. data/vendor/assets/javascripts/tools/doc_generator/app/test/encoding.js +10 -0
  95. data/vendor/assets/javascripts/tools/doc_generator/app/test/encoding_other.js +12 -0
  96. data/vendor/assets/javascripts/tools/doc_generator/app/test/event.js +54 -0
  97. data/vendor/assets/javascripts/tools/doc_generator/app/test/functions_anon.js +39 -0
  98. data/vendor/assets/javascripts/tools/doc_generator/app/test/functions_nested.js +33 -0
  99. data/vendor/assets/javascripts/tools/doc_generator/app/test/global.js +13 -0
  100. data/vendor/assets/javascripts/tools/doc_generator/app/test/globals.js +25 -0
  101. data/vendor/assets/javascripts/tools/doc_generator/app/test/ignore.js +10 -0
  102. data/vendor/assets/javascripts/tools/doc_generator/app/test/inner.js +16 -0
  103. data/vendor/assets/javascripts/tools/doc_generator/app/test/jsdoc_test.js +477 -0
  104. data/vendor/assets/javascripts/tools/doc_generator/app/test/lend.js +33 -0
  105. data/vendor/assets/javascripts/tools/doc_generator/app/test/memberof.js +19 -0
  106. data/vendor/assets/javascripts/tools/doc_generator/app/test/memberof_constructor.js +17 -0
  107. data/vendor/assets/javascripts/tools/doc_generator/app/test/name.js +19 -0
  108. data/vendor/assets/javascripts/tools/doc_generator/app/test/namespace_nested.js +23 -0
  109. data/vendor/assets/javascripts/tools/doc_generator/app/test/nocode.js +13 -0
  110. data/vendor/assets/javascripts/tools/doc_generator/app/test/oblit_anon.js +20 -0
  111. data/vendor/assets/javascripts/tools/doc_generator/app/test/overview.js +20 -0
  112. data/vendor/assets/javascripts/tools/doc_generator/app/test/param_inline.js +37 -0
  113. data/vendor/assets/javascripts/tools/doc_generator/app/test/params_optional.js +8 -0
  114. data/vendor/assets/javascripts/tools/doc_generator/app/test/prototype.js +17 -0
  115. data/vendor/assets/javascripts/tools/doc_generator/app/test/prototype_nested.js +9 -0
  116. data/vendor/assets/javascripts/tools/doc_generator/app/test/prototype_oblit.js +13 -0
  117. data/vendor/assets/javascripts/tools/doc_generator/app/test/prototype_oblit_constructor.js +24 -0
  118. data/vendor/assets/javascripts/tools/doc_generator/app/test/public.js +10 -0
  119. data/vendor/assets/javascripts/tools/doc_generator/app/test/shared.js +42 -0
  120. data/vendor/assets/javascripts/tools/doc_generator/app/test/shared2.js +2 -0
  121. data/vendor/assets/javascripts/tools/doc_generator/app/test/shortcuts.js +22 -0
  122. data/vendor/assets/javascripts/tools/doc_generator/app/test/static_this.js +13 -0
  123. data/vendor/assets/javascripts/tools/doc_generator/app/test/synonyms.js +31 -0
  124. data/vendor/assets/javascripts/tools/doc_generator/app/test/tosource.js +23 -0
  125. data/vendor/assets/javascripts/tools/doc_generator/app/test/variable_redefine.js +14 -0
  126. data/vendor/assets/javascripts/tools/doc_generator/changes.txt +75 -0
  127. data/vendor/assets/javascripts/tools/doc_generator/conf/sample.conf +31 -0
  128. data/vendor/assets/javascripts/tools/doc_generator/java/build.xml +36 -0
  129. data/vendor/assets/javascripts/tools/doc_generator/java/build_1.4.xml +36 -0
  130. data/vendor/assets/javascripts/tools/doc_generator/java/classes/js.jar +0 -0
  131. data/vendor/assets/javascripts/tools/doc_generator/java/src/JsDebugRun.java +21 -0
  132. data/vendor/assets/javascripts/tools/doc_generator/java/src/JsRun.java +21 -0
  133. data/vendor/assets/javascripts/tools/doc_generator/jsdebug.jar +0 -0
  134. data/vendor/assets/javascripts/tools/doc_generator/jsrun.jar +0 -0
  135. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/allclasses.tmpl +17 -0
  136. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/allfiles.tmpl +56 -0
  137. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/class.tmpl +646 -0
  138. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/index.tmpl +39 -0
  139. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/publish.js +184 -0
  140. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/static/default.css +165 -0
  141. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/static/header.html +2 -0
  142. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/static/index.html +19 -0
  143. data/vendor/assets/javascripts/tools/doc_generator/templates/jsdoc/symbol.tmpl +35 -0
  144. metadata +234 -0
@@ -0,0 +1,4487 @@
1
+ /**
2
+ @preserve Copyright (c) 2012 Humu humu2009@gmail.com
3
+ jsc3d is freely distributable under the terms of the MIT license.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+ **/
23
+
24
+
25
+ /**
26
+ @namespace JSC3D
27
+ */
28
+ var JSC3D = JSC3D || {};
29
+
30
+
31
+ /**
32
+ @class Viewer
33
+
34
+ Viewer is the main class of JSC3D. It provides presentation of and interaction with a simple static 3D scene
35
+ which can either be given as the url of the scene file, or be manually constructed and passed in. It
36
+ also provides some settings to adjust the mode and quality of the rendering.<br /><br />
37
+
38
+ Viewer should be constructed with an existing canvas object where to perform the rendering.<br /><br />
39
+
40
+ Viewer provides 3 way to specify the scene:<br />
41
+ 1. Use setParameter() method before initilization and set 'SceneUrl' parameter with a valid url
42
+ that describes where to load the scene. <br />
43
+ 2. Use replaceSceneFromUrl() method, passing in a valid url to load/replace scene at runtime.<br />
44
+ 3. Use replaceScene() method, passing in a manually constructed scene object to replace the current one
45
+ at runtime.<br />
46
+ */
47
+ JSC3D.Viewer = function(canvas, parameters) {
48
+ if(parameters)
49
+ this.params = {
50
+ SceneUrl: parameters.SceneUrl || '',
51
+ InitRotationX: parameters.InitRotationX || 0,
52
+ InitRotationY: parameters.InitRotationY || 0,
53
+ InitRotationZ: parameters.InitRotationZ || 0,
54
+ ModelColor: parameters.ModelColor || '#caa618',
55
+ BackgroundColor1: parameters.BackgroundColor1 || '#ffffff',
56
+ BackgroundColor2: parameters.BackgroundColor2 || '#383840',
57
+ RenderMode: parameters.RenderMode || 'flat',
58
+ Definition: parameters.Definition || 'standard',
59
+ MipMapping: parameters.MipMapping || 'off',
60
+ SphereMapUrl: parameters.SphereMapUrl || ''
61
+ };
62
+ else
63
+ this.params = {
64
+ SceneUrl: '',
65
+ InitRotationX: 0,
66
+ InitRotationY: 0,
67
+ InitRotationZ: 0,
68
+ ModelColor: '#caa618',
69
+ BackgroundColor1: '#ffffff',
70
+ BackgroundColor2: '#383840',
71
+ RenderMode: 'flat',
72
+ Definition: 'standard',
73
+ MipMapping: 'off',
74
+ SphereMapUrl: ''
75
+ };
76
+
77
+ this.canvas = canvas;
78
+ this.ctx = null;
79
+ this.canvasData = null;
80
+ this.bkgColorBuffer = null;
81
+ this.colorBuffer = null;
82
+ this.zBuffer = null;
83
+ this.selectionBuffer = null;
84
+ this.frameWidth = canvas.width;
85
+ this.frameHeight = canvas.height;
86
+ this.scene = null;
87
+ this.defaultMaterial = null;
88
+ this.sphereMap = null;
89
+ this.isLoaded = false;
90
+ this.isFailed = false;
91
+ this.errorMsg = '';
92
+ this.initRotX = 0;
93
+ this.initRotY = 0;
94
+ this.initRotZ = 0;
95
+ this.zoomFactor = 1;
96
+ this.rotMatrix = new JSC3D.Matrix3x4;
97
+ this.transformMatrix = new JSC3D.Matrix3x4;
98
+ this.sceneUrl = '';
99
+ this.modelColor = 0xcaa618;
100
+ this.bkgColor1 = 0xffffff;
101
+ this.bkgColor2 = 0x383840;
102
+ this.renderMode = 'flat';
103
+ this.definition = 'standard';
104
+ this.isMipMappingOn = false;
105
+ this.sphereMapUrl = '';
106
+ this.buttonStates = {};
107
+ this.keyStates = {};
108
+ this.mouseX = 0;
109
+ this.mouseY = 0;
110
+ this.onmousedown = null;
111
+ this.onmouseup = null;
112
+ this.onmousemove = null;
113
+ this.beforeupdate = null;
114
+ this.afterupdate = null;
115
+ this.mouseUsage = 'default';
116
+ this.isDefaultInputHandlerEnabled = true;
117
+
118
+ // setup input handlers
119
+ var self = this;
120
+ canvas.attachEvent('onmousedown', function(e){self.mouseDownHandler(e);});
121
+ canvas.attachEvent('onmouseup', function(e){self.mouseUpHandler(e);});
122
+ canvas.attachEvent('onmousemove', function(e){self.mouseMoveHandler(e);});
123
+ document.attachEvent('onkeydown', function(e){self.keyDownHandler(e);});
124
+ document.attachEvent('onkeyup', function(e){self.keyUpHandler(e);});
125
+ };
126
+
127
+ /**
128
+ Set the initial value for a parameter to parameterize viewer before initialization.<br />
129
+ Available parameters are:<br />
130
+ '<b>SceneUrl</b>': url string that describes where to load the scene, default: '';<br />
131
+ '<b>InitRotationX</b>': initial rotation angle around x-axis for the whole scene, default: 0;<br />
132
+ '<b>InitRotationY</b>': initial rotation angle around y-axis for the whole scene, default: 0;<br />
133
+ '<b>InitRotationZ</b>': initial rotation angle around z-axis for the whole scene, default: 0;<br />
134
+ '<b>ModelColor</b>': fallback color for all meshes, default: '#caa618';<br />
135
+ '<b>BackgroundColor1</b>': color at the top of the background, default: '#ffffff';<br />
136
+ '<b>BackgroundColor2</b>': color at the bottom of the background, default: '#383840';<br />
137
+ '<b>RenderMode</b>': render mode, default: 'flat';<br />
138
+ '<b>Definition</b>': quality level of rendering, default: 'standard';<br />
139
+ '<b>MipMapping</b>': turn on/off mip-mapping, default: 'off';<br />
140
+ '<b>SphereMapUrl</b>': url string that describes where to load the image used for sphere mapping, default: ''.<br />
141
+ @param {string} name name of the parameter to set.
142
+ @param value new value for the parameter.
143
+ */
144
+ JSC3D.Viewer.prototype.setParameter = function(name, value) {
145
+ this.params[name] = value;
146
+ };
147
+
148
+ /**
149
+ Initialize viewer for rendering and interactions.
150
+ */
151
+ JSC3D.Viewer.prototype.init = function() {
152
+ this.sceneUrl = this.params['SceneUrl'];
153
+ this.initRotX = parseFloat(this.params['InitRotationX']);
154
+ this.initRotY = parseFloat(this.params['InitRotationY']);
155
+ this.initRotZ = parseFloat(this.params['InitRotationZ']);
156
+ this.modelColor = parseInt('0x' + this.params['ModelColor'].substring(1));
157
+ this.bkgColor1 = parseInt('0x' + this.params['BackgroundColor1'].substring(1));
158
+ this.bkgColor2 = parseInt('0x' + this.params['BackgroundColor2'].substring(1));
159
+ this.renderMode = this.params['RenderMode'].toLowerCase();
160
+ this.definition = this.params['Definition'].toLowerCase();
161
+ this.isMipMappingOn = this.params['MipMapping'].toLowerCase() == 'on';
162
+ this.sphereMapUrl = this.params['SphereMapUrl'];
163
+
164
+ try {
165
+ this.ctx = this.canvas.getContext('2d');
166
+ this.canvasData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
167
+ }
168
+ catch(e) {
169
+ this.ctx = null;
170
+ this.canvasData = null;
171
+ }
172
+
173
+ if(this.canvas.width <= 2 || this.canvas.height <= 2)
174
+ this.definition = 'standard';
175
+
176
+ switch(this.definition) {
177
+ case 'low':
178
+ this.frameWidth = ~~((this.canvas.width + 1) / 2);
179
+ this.frameHeight = ~~((this.canvas.height + 1) / 2);
180
+ break;
181
+ case 'high':
182
+ this.frameWidth = this.canvas.width * 2;
183
+ this.frameHeight = this.canvas.height * 2;
184
+ break;
185
+ case 'standard':
186
+ default:
187
+ this.frameWidth = this.canvas.width;
188
+ this.frameHeight = this.canvas.height;
189
+ break;
190
+ }
191
+
192
+ this.zoomFactor = 1;
193
+ this.rotMatrix.identity();
194
+ this.transformMatrix.identity();
195
+ this.isLoaded = false;
196
+ this.isFailed = false;
197
+ this.errorMsg = '';
198
+ this.scene = null;
199
+ // allocate memory storage for frame buffers
200
+ this.colorBuffer = new Array(this.frameWidth * this.frameHeight);
201
+ this.zBuffer = new Array(this.frameWidth * this.frameHeight);
202
+ this.selectionBuffer = new Array(this.frameWidth * this.frameHeight);
203
+ this.bkgColorBuffer = new Array(this.frameWidth * this.frameHeight);
204
+ this.generateBackground();
205
+ // create a default material for rendring of meshes that don't have one
206
+ this.defaultMaterial = new JSC3D.Material;
207
+ this.defaultMaterial.ambientColor = 0;
208
+ this.defaultMaterial.diffuseColor = this.modelColor;
209
+ this.defaultMaterial.transparency = 0;
210
+ this.defaultMaterial.simulateSpecular = true;
211
+ this.drawBackground();
212
+
213
+ // load scene if any
214
+ this.loadScene();
215
+
216
+ // load sphere mapping image if any
217
+ this.setSphereMapFromUrl(this.sphereMapUrl);
218
+ };
219
+
220
+ /**
221
+ Ask viewer to render a new frame or just repaint last frame.
222
+ @param {boolean} repaintOnly true to repaint last frame; false(default) to render a new frame.
223
+ */
224
+ JSC3D.Viewer.prototype.update = function(repaintOnly) {
225
+ if(this.isFailed) {
226
+ this.reportError(this.errorMsg);
227
+ return;
228
+ }
229
+
230
+ if(this.beforeupdate != null && (typeof this.beforeupdate) == 'function')
231
+ this.beforeupdate();
232
+
233
+ if(this.scene) {
234
+ if(!repaintOnly && this.colorBuffer != null) {
235
+ this.beginScene();
236
+ this.render();
237
+ this.endScene();
238
+ }
239
+
240
+ this.paint();
241
+ }
242
+ else {
243
+ this.drawBackground();
244
+ }
245
+
246
+ if(this.afterupdate != null && (typeof this.afterupdate) == 'function')
247
+ this.afterupdate();
248
+ };
249
+
250
+ /**
251
+ Rotate the scene with given angles around Cardinal axes.
252
+ @param {float} rotX rotation angle around x-axis in degrees.
253
+ @param {float} rotY rotation angle around y-axis in degrees.
254
+ @param {float} rotZ rotation angle around z-axis in degrees.
255
+ */
256
+ JSC3D.Viewer.prototype.rotate = function(rotX, rotY, rotZ) {
257
+ this.rotMatrix.rotateAboutXAxis(rotX);
258
+ this.rotMatrix.rotateAboutYAxis(rotY);
259
+ this.rotMatrix.rotateAboutZAxis(rotZ);
260
+ };
261
+
262
+ /**
263
+ Set render mode.<br />
264
+ Available render modes are:<br />
265
+ '<b>point</b>': render meshes as point clouds;<br />
266
+ '<b>wireframe</b>': render meshes as wireframe;<br />
267
+ '<b>flat</b>': render meshes as solid objects using flat shading;<br />
268
+ '<b>smooth</b>': render meshes as solid objects using smooth shading;<br />
269
+ '<b>texture</b>': render meshes as solid textured objects, no lighting will be apllied;<br />
270
+ '<b>textureflat</b>': render meshes as solid textured objects, lighting will be calculated per face;<br />
271
+ '<b>texturesmooth</b>': render meshes as solid textured objects, lighting will be calculated per vertex and interpolated.<br />
272
+ @param {string} mode new render mode.
273
+ */
274
+ JSC3D.Viewer.prototype.setRenderMode = function(mode) {
275
+ this.params['RenderMode'] = mode;
276
+ this.renderMode = mode;
277
+ };
278
+
279
+ /**
280
+ Set quality level of rendering.<br />
281
+ Available quality levels are:<br />
282
+ '<b>low</b>': low-quality rendering will be applied, with highest performance;<br />
283
+ '<b>standard</b>': normal-quality rendering will be applied, with modest performace;<br />
284
+ '<b>high</b>': high-quality rendering will be applied, with lowest performace.<br />
285
+ @params {string} definition new quality level.
286
+ */
287
+ JSC3D.Viewer.prototype.setDefinition = function(definition) {
288
+ if(this.canvas.width <= 2 || this.canvas.height <= 2)
289
+ definition = 'standard';
290
+
291
+ if(definition == this.definition)
292
+ return;
293
+
294
+ this.params['Definition'] = definition;
295
+ this.definition = definition;
296
+
297
+ var oldFrameWidth = this.frameWidth;
298
+
299
+ switch(this.definition) {
300
+ case 'low':
301
+ this.frameWidth = ~~((this.canvas.width + 1) / 2);
302
+ this.frameHeight = ~~((this.canvas.height + 1) / 2);
303
+ break;
304
+ case 'high':
305
+ this.frameWidth = this.canvas.width * 2;
306
+ this.frameHeight = this.canvas.height * 2;
307
+ break;
308
+ case 'standard':
309
+ default:
310
+ this.frameWidth = this.canvas.width;
311
+ this.frameHeight = this.canvas.height;
312
+ break;
313
+ }
314
+
315
+ var newSize = this.frameWidth * this.frameHeight;
316
+ if(this.colorBuffer.length < newSize)
317
+ this.colorBuffer = new Array(newSize);
318
+
319
+ if(this.zBuffer.length < newSize)
320
+ this.zBuffer = new Array(newSize);
321
+
322
+ if(this.selectionBuffer.length < newSize)
323
+ this.selectionBuffer = new Array(newSize);
324
+
325
+ if(this.bkgColorBuffer.length < newSize)
326
+ this.bkgColorBuffer = new Array(newSize);
327
+
328
+ this.generateBackground();
329
+
330
+ // zoom factor should be adjusted,
331
+ // otherwise there would be an abrupt zoom-in or zoom-out on next frame
332
+ this.zoomFactor *= this.frameWidth / oldFrameWidth;
333
+ };
334
+
335
+ /**
336
+ Specify a new image from the given url which will be used for applying sphere mapping.
337
+ @param {string} sphereMapUrl url string that describes where to load the image.
338
+ */
339
+ JSC3D.Viewer.prototype.setSphereMapFromUrl = function(sphereMapUrl) {
340
+ if(sphereMapUrl == '') {
341
+ this.sphereMap = null;
342
+ return;
343
+ }
344
+
345
+ this.params['SphereMapUrl'] = sphereMapUrl;
346
+ this.sphereMapUrl = sphereMapUrl;
347
+
348
+ var self = this;
349
+ var newSphereMap = new JSC3D.Texture;
350
+
351
+ newSphereMap.onready = function() {
352
+ self.sphereMap = newSphereMap;
353
+ self.update();
354
+ };
355
+
356
+ newSphereMap.createFromUrl(this.sphereMapUrl);
357
+ };
358
+
359
+ /**
360
+ Enable/Disable the default mouse and key event handling routines.
361
+ @param {boolean} enabled true to enable the default handler; false to disable them.
362
+ */
363
+ JSC3D.Viewer.prototype.enableDefaultInputHandler = function(enabled) {
364
+ this.isDefaultInputHandlerEnabled = enabled;
365
+ };
366
+
367
+ /**
368
+ Set control of mouse pointer.
369
+ Available options are:<br />
370
+ '<b>default</b>': default mouse control will be used;<br />
371
+ '<b>free</b>': this tells {JSC3D.Viewer} a user-defined mouse control will be adopted.
372
+ This is often used together with viewer.enableDefaultInputHandler(false)
373
+ and viewer.onmousedown, viewer.onmouseup and/or viewer.onmousemove overridden.<br />
374
+ '<b>rotate</b>': mouse will be used to rotate the scene;<br />
375
+ '<b>zoom</b>': mouse will be used to do zooming.<br />
376
+ @param {String} usage control of mouse pointer to be set.
377
+ */
378
+ JSC3D.Viewer.prototype.setMouseUsage = function(usage) {
379
+ this.mouseUsage = usage;
380
+ };
381
+
382
+ /**
383
+ Load a new scene from the given url to replace the current scene.
384
+ @param {string} sceneUrl url string that describes where to load the new scene.
385
+ */
386
+ JSC3D.Viewer.prototype.replaceSceneFromUrl = function(sceneUrl) {
387
+ this.params['SceneUrl'] = sceneUrl;
388
+ this.sceneUrl = sceneUrl;
389
+ this.isFailed = this.isLoaded = false;
390
+ this.loadScene();
391
+ };
392
+
393
+ /**
394
+ Replace the current scene with a given scene.
395
+ @param {JSC3D.Scene} scene the given scene.
396
+ */
397
+ JSC3D.Viewer.prototype.replaceScene = function(scene) {
398
+ this.params['SceneUrl'] = '';
399
+ this.sceneUrl = '';
400
+ this.isFailed = false;
401
+ this.isLoaded = true;
402
+ this.errorMsg = '';
403
+ this.setupScene(scene);
404
+ };
405
+
406
+ /**
407
+ Get the current scene.
408
+ @returns {JSC3D.Scene} the current scene.
409
+ */
410
+ JSC3D.Viewer.prototype.getScene = function() {
411
+ return this.scene;
412
+ };
413
+
414
+ /**
415
+ Query information at a given position on the canvas.
416
+ @param {float} clientX client x coordinate on the current page.
417
+ @param {float} clientY client y coordinate on the current page.
418
+ @returns {JSC3D.PickInfo} a PickInfo object which hold the result.
419
+ */
420
+ JSC3D.Viewer.prototype.pick = function(clientX, clientY) {
421
+ var pickInfo = new JSC3D.PickInfo;
422
+
423
+ var canvasRect = this.canvas.getBoundingClientRect();
424
+ var canvasX = clientX - canvasRect.left;
425
+ var canvasY = clientY - canvasRect.top;
426
+
427
+ var frameX = canvasX;
428
+ var frameY = canvasY;
429
+ if( this.selectionBuffer != null &&
430
+ canvasX >= 0 && canvasX < this.canvas.width &&
431
+ canvasY >= 0 && canvasY < this.canvas.height ) {
432
+ switch(this.definition) {
433
+ case 'low':
434
+ frameX = ~~(frameX / 2);
435
+ frameY = ~~(frameY / 2);
436
+ break;
437
+ case 'high':
438
+ frameX *= 2;
439
+ frameY *= 2;
440
+ break;
441
+ case 'standard':
442
+ default:
443
+ break;
444
+ }
445
+
446
+ var pickedId = this.selectionBuffer[frameY * this.frameWidth + frameX];
447
+ if(pickedId > 0) {
448
+ var meshes = this.scene.getChildren();
449
+ for(var i=0; i<meshes.length; i++) {
450
+ if(meshes[i].internalId == pickedId) {
451
+ pickInfo.mesh = meshes[i];
452
+ break;
453
+ }
454
+ }
455
+ }
456
+ }
457
+
458
+ pickInfo.canvasX = canvasX;
459
+ pickInfo.canvasY = canvasY;
460
+ if(pickInfo.mesh)
461
+ pickInfo.depth = this.zBuffer[frameY * this.frameWidth + frameX];
462
+
463
+ return pickInfo;
464
+ };
465
+
466
+ /**
467
+ Paint onto canvas.
468
+ @private
469
+ */
470
+ JSC3D.Viewer.prototype.paint = function() {
471
+ if(!this.canvasData)
472
+ return;
473
+
474
+ this.ctx.putImageData(this.canvasData, 0, 0);
475
+ };
476
+
477
+ /**
478
+ The mouse-down event handling routine.
479
+ @private
480
+ */
481
+ JSC3D.Viewer.prototype.mouseDownHandler = function(e) {
482
+ if(this.onmousedown) {
483
+ var info = this.pick(e.clientX, e.clientY);
484
+ this.onmousedown(info.canvasX, info.canvasY, e.button, info.depth, info.mesh);
485
+ }
486
+
487
+ if(!this.isDefaultInputHandlerEnabled)
488
+ return;
489
+
490
+ this.buttonStates[e.button] = true;
491
+ this.mouseX = e.clientX;
492
+ this.mouseY = e.clientY;
493
+ };
494
+
495
+ /**
496
+ The mouse-up event handling routine.
497
+ @private
498
+ */
499
+ JSC3D.Viewer.prototype.mouseUpHandler = function(e) {
500
+ if(this.onmouseup) {
501
+ var info = this.pick(e.clientX, e.clientY);
502
+ this.onmouseup(info.canvasX, info.canvasY, e.button, info.depth, info.mesh);
503
+ }
504
+
505
+ if(!this.isDefaultInputHandlerEnabled)
506
+ return;
507
+
508
+ var isDragging = this.buttonStates[1] == true;
509
+ var isShiftDown = this.keyStates[16] == true;
510
+ if(isDragging) {
511
+ if(((isShiftDown && this.mouseUsage == 'default') || this.mouseUsage == 'zoom') && e.clientY != this.mouseY) {
512
+ this.zoomFactor *= this.mouseY < e.clientY ?
513
+ Math.pow(1.11, ~~(1 + (e.clientY - this.mouseY) * 8 / this.canvas.height)) :
514
+ Math.pow(0.9, ~~(1 + (this.mouseY - e.clientY) * 8 / this.canvas.height));
515
+ }
516
+ else if(this.mouseUsage == 'default' || this.mouseUsage == 'rotate') {
517
+ var rotX = (e.clientY - this.mouseY) * 180 / this.canvas.width;
518
+ var rotY = (e.clientX - this.mouseX) * 180 / this.canvas.height;
519
+ this.rotMatrix.rotateAboutXAxis(rotX);
520
+ this.rotMatrix.rotateAboutYAxis(rotY);
521
+ }
522
+ this.mouseX = e.clientX;
523
+ this.mouseY = e.clientY;
524
+ this.update();
525
+ }
526
+
527
+ this.buttonStates[e.button] = false;
528
+ };
529
+
530
+ /**
531
+ The mouse-move event handling routine.
532
+ @private
533
+ */
534
+ JSC3D.Viewer.prototype.mouseMoveHandler = function(e) {
535
+ if(this.onmousemove) {
536
+ var info = this.pick(e.clientX, e.clientY);
537
+ this.onmousemove(info.canvasX, info.canvasY, e.button, info.depth, info.mesh);
538
+ }
539
+
540
+ if(!this.isDefaultInputHandlerEnabled)
541
+ return;
542
+
543
+ /*
544
+ var isDragging = this.buttonStates[1] == true;
545
+ var isShiftDown = this.keyStates[16] == true;
546
+ if(isDragging) {
547
+ if(isShiftDown) {
548
+ this.zoomFactor *= this.mouseY <= e.clientY ? 1.11 : 0.9;
549
+ }
550
+ else {
551
+ var rotX = (e.clientY - this.mouseY) * 360 / this.canvas.width;
552
+ var rotY = (e.clientX - this.mouseX) * 360 / this.canvas.height;
553
+ this.rotMatrix.rotateAboutXAxis(rotX);
554
+ this.rotMatrix.rotateAboutYAxis(rotY);
555
+ }
556
+ this.mouseX = e.clientX;
557
+ this.mouseY = e.clientY;
558
+ this.update();
559
+ }
560
+ */
561
+ };
562
+
563
+ /**
564
+ The key-down event handling routine.
565
+ @private
566
+ */
567
+ JSC3D.Viewer.prototype.keyDownHandler = function(e) {
568
+ this.keyStates[e.keyCode] = true;
569
+ };
570
+
571
+ /**
572
+ The key-up event handling routine.
573
+ @private
574
+ */
575
+ JSC3D.Viewer.prototype.keyUpHandler = function(e) {
576
+ this.keyStates[e.keyCode] = false;
577
+ };
578
+
579
+ /**
580
+ Internally load a scene.
581
+ @private
582
+ */
583
+ JSC3D.Viewer.prototype.loadScene = function() {
584
+ this.scene = null;
585
+ this.isLoaded = false;
586
+
587
+ if(this.sceneUrl == '')
588
+ return false;
589
+
590
+ var lastSlashAt = this.sceneUrl.lastIndexOf('/');
591
+ if(lastSlashAt == -1)
592
+ lastSlashAt = this.sceneUrl.lastIndexOf('\\');
593
+
594
+ var fileName = this.sceneUrl.substring(lastSlashAt + 1);
595
+ var lastDotAt = fileName.lastIndexOf('.');
596
+ if(lastDotAt == -1)
597
+ return false;
598
+
599
+ var fileExtName = fileName.substring(lastDotAt + 1);
600
+ var loader = JSC3D.LoaderSelector.getLoader(fileExtName);
601
+ if(!loader)
602
+ return false;
603
+
604
+ var self = this;
605
+
606
+ loader.onload = function(scene) {
607
+ self.setupScene(scene);
608
+ };
609
+
610
+ loader.onerror = function(errorMsg) {
611
+ self.scene = null;
612
+ self.isLoaded = false;
613
+ self.isFailed = true;
614
+ self.errorMsg = errorMsg;
615
+ self.update();
616
+ };
617
+
618
+ loader.onprogress = function(task, prog) {
619
+ self.reportProgress(task, prog);
620
+ };
621
+
622
+ loader.onresource = function(resource) {
623
+ if((resource instanceof JSC3D.Texture) && self.isMipMappingOn && !resource.hasMipmap())
624
+ resource.generateMipmaps();
625
+ self.update();
626
+ };
627
+
628
+ loader.loadFromUrl(this.sceneUrl);
629
+
630
+ return true;
631
+ };
632
+
633
+ /**
634
+ Prepare for rendering of a new scene.
635
+ @private
636
+ */
637
+ JSC3D.Viewer.prototype.setupScene = function(scene) {
638
+ scene.init();
639
+ if(!scene.isEmpty()) {
640
+ var d = scene.aabb.lengthOfDiagonal();
641
+ var w = this.frameWidth;
642
+ var h = this.frameHeight;
643
+ this.zoomFactor = (d == 0) ? 1 : (w < h ? w : h) / d;
644
+ }
645
+
646
+ this.rotMatrix.identity();
647
+ this.rotMatrix.rotateAboutXAxis(this.initRotX);
648
+ this.rotMatrix.rotateAboutYAxis(this.initRotY);
649
+ this.rotMatrix.rotateAboutZAxis(this.initRotZ);
650
+ this.scene = scene;
651
+ this.isLoaded = true;
652
+ this.isFailed = false;
653
+ this.errorMsg = '';
654
+ this.update();
655
+ };
656
+
657
+ /**
658
+ Show progress and some informations about current time-cosuming task.
659
+ @param {string} task text information about current task.
660
+ @param {float} progress progress of current task. this should be a number between 0 and 1.
661
+ */
662
+ JSC3D.Viewer.prototype.reportProgress = function(task, progress) {
663
+ if(!this.ctx)
664
+ return;
665
+
666
+ this.drawBackground();
667
+
668
+ this.ctx.save();
669
+
670
+ var r = 255 - ((this.bkgColor1 & 0xff0000) >> 16);
671
+ var g = 255 - ((this.bkgColor1 & 0xff00) >> 8);
672
+ var b = 255 - (this.bkgColor1 & 0xff);
673
+ var style = '#' + r.toString(16) + g.toString(16) + b.toString(16);
674
+ this.ctx.strokeStyle = style;
675
+ this.ctx.fillStyle = style;
676
+
677
+ var barX = 40;
678
+ var barY = this.canvas.height * 0.38;
679
+ var barWidth = this.canvas.width - barX * 2;
680
+ var barHeight = 20;
681
+ this.ctx.strokeRect(barX, barY, barWidth, barHeight);
682
+ this.ctx.fillRect(barX+2, barY+2, (barWidth-4)*progress, barHeight-4);
683
+
684
+ this.ctx.font = '12px Courier New';
685
+ this.ctx.textAlign = 'left';
686
+ this.ctx.fillText(task, barX, barY-4, barWidth);
687
+
688
+ this.ctx.restore();
689
+ };
690
+
691
+ /**
692
+ Show informations about a fatal error.
693
+ @param {string} message text information about this error.
694
+ */
695
+ JSC3D.Viewer.prototype.reportError = function(message) {
696
+ if(!this.ctx)
697
+ return;
698
+
699
+ this.drawBackground();
700
+
701
+ this.ctx.save();
702
+
703
+ var msgX = 40;
704
+ var msgY = this.canvas.height * 0.38 - 4;
705
+ var r = 255 - ((this.bkgColor1 & 0xff0000) >> 16);
706
+ var g = 255 - ((this.bkgColor1 & 0xff00) >> 8);
707
+ var b = 255 - (this.bkgColor1 & 0xff);
708
+ var style = '#' + r.toString(16) + g.toString(16) + b.toString(16);
709
+ this.ctx.fillStyle = style;
710
+ this.ctx.font = '16px Courier New';
711
+ this.ctx.textAlign = 'left';
712
+ this.ctx.fillText(message, msgX, msgY);
713
+
714
+ this.ctx.restore();
715
+ };
716
+
717
+ /**
718
+ Fill the background color buffer.
719
+ @private
720
+ */
721
+ JSC3D.Viewer.prototype.generateBackground = function() {
722
+ var w = this.frameWidth;
723
+ var h = this.frameHeight;
724
+ var pixels = this.bkgColorBuffer;
725
+
726
+ var r1 = (this.bkgColor1 & 0xff0000) >> 16;
727
+ var g1 = (this.bkgColor1 & 0xff00) >> 8;
728
+ var b1 = this.bkgColor1 & 0xff;
729
+ var r2 = (this.bkgColor2 & 0xff0000) >> 16;
730
+ var g2 = (this.bkgColor2 & 0xff00) >> 8;
731
+ var b2 = this.bkgColor2 & 0xff;
732
+
733
+ var pix = 0;
734
+ for(var i=0; i<h; i++) {
735
+ var r = (r1 + i * (r2 - r1) / h) & 0xff;
736
+ var g = (g1 + i * (g2 - g1) / h) & 0xff;
737
+ var b = (b1 + i * (b2 - b1) / h) & 0xff;
738
+
739
+ for(var j=0; j<w; j++) {
740
+ pixels[pix++] = r << 16 | g << 8 | b;
741
+ }
742
+ }
743
+ };
744
+
745
+ /**
746
+ Draw background onto canvas.
747
+ @private
748
+ */
749
+ JSC3D.Viewer.prototype.drawBackground = function() {
750
+ if(!this.canvasData)
751
+ return;
752
+
753
+ this.beginScene();
754
+ this.endScene();
755
+
756
+ this.paint();
757
+ };
758
+
759
+ /**
760
+ Begin to render a new frame.
761
+ @private
762
+ */
763
+ JSC3D.Viewer.prototype.beginScene = function() {
764
+ var cbuf = this.colorBuffer;
765
+ var zbuf = this.zBuffer;
766
+ var sbuf = this.selectionBuffer;
767
+ var bbuf = this.bkgColorBuffer;
768
+ var size = this.frameWidth * this.frameHeight;
769
+ var MIN_Z = -Number.MAX_VALUE;
770
+
771
+ for(var i=0; i<size; i++) {
772
+ cbuf[i] = bbuf[i];
773
+ zbuf[i] = MIN_Z;
774
+ sbuf[i] = 0;
775
+ }
776
+ };
777
+
778
+ /**
779
+ End for rendering of a frame.
780
+ @private
781
+ */
782
+ JSC3D.Viewer.prototype.endScene = function() {
783
+ var data = this.canvasData.data;
784
+ var width = this.canvas.width;
785
+ var height = this.canvas.height;
786
+ var cbuf = this.colorBuffer;
787
+ var cwidth = this.frameWidth;
788
+ var cheight = this.frameHeight;
789
+ var csize = cwidth * cheight;
790
+
791
+ switch(this.definition) {
792
+ case 'low':
793
+ var halfWidth = width >> 1;
794
+ var surplus = cwidth - halfWidth;
795
+ var src = 0, dest = 0;
796
+ for(var i=0; i<height; i++) {
797
+ for(var j=0; j<width; j++) {
798
+ var color = cbuf[src];
799
+ data[dest] = (color & 0xff0000) >> 16;
800
+ data[dest + 1] = (color & 0xff00) >> 8;
801
+ data[dest + 2] = color & 0xff;
802
+ data[dest + 3] = 0xff;
803
+ src += (j & 1);
804
+ dest += 4;
805
+ }
806
+ src += (i & 1) ? surplus : -halfWidth;
807
+ }
808
+ break;
809
+ case 'high':
810
+ var src = 0, dest = 0;
811
+ for(var i=0; i<height; i++) {
812
+ for(var j=0; j<width; j++) {
813
+ var color0 = cbuf[src];
814
+ var color1 = cbuf[src + 1];
815
+ var color2 = cbuf[src + cwidth];
816
+ var color3 = cbuf[src + cwidth + 1];
817
+ data[dest] = ((color0 & 0xff0000) + (color1 & 0xff0000) + (color2 & 0xff0000) + (color3 & 0xff0000)) >> 18;
818
+ data[dest + 1] = ((color0 & 0xff00) + (color1 & 0xff00) + (color2 & 0xff00) + (color3 & 0xff00)) >> 10;
819
+ data[dest + 2] = ((color0 & 0xff) + (color1 & 0xff) + (color2 & 0xff) + (color3 & 0xff)) >> 2;
820
+ data[dest + 3] = 0xff;
821
+ src += 2;
822
+ dest += 4;
823
+ }
824
+ src += cwidth;
825
+ }
826
+ break;
827
+ case 'standard':
828
+ default:
829
+ for(var src=0, dest=0; src<csize; src++, dest+=4) {
830
+ var color = cbuf[src];
831
+ data[dest] = (color & 0xff0000) >> 16;
832
+ data[dest + 1] = (color & 0xff00) >> 8;
833
+ data[dest + 2] = color & 0xff;
834
+ data[dest + 3] = 0xff;
835
+ }
836
+ break;
837
+ }
838
+ };
839
+
840
+ /**
841
+ Render a new frame.
842
+ @private
843
+ */
844
+ JSC3D.Viewer.prototype.render = function() {
845
+ if(this.scene.isEmpty())
846
+ return;
847
+
848
+ var aabb = this.scene.aabb;
849
+
850
+ // calculate transformation matrix
851
+ this.transformMatrix.identity();
852
+ this.transformMatrix.translate(-(aabb.minX+aabb.maxX)/2, -(aabb.minY+aabb.maxY)/2, -(aabb.minZ+aabb.maxZ)/2);
853
+ this.transformMatrix.multiply(this.rotMatrix);
854
+ this.transformMatrix.scale(this.zoomFactor, -this.zoomFactor, this.zoomFactor);
855
+ this.transformMatrix.translate(this.frameWidth/2, this.frameHeight/2, 0);
856
+
857
+ // sort, transform and render the scene
858
+ var renderList = this.sortScene(this.transformMatrix);
859
+ for(var i=0; i<renderList.length; i++) {
860
+ var mesh = renderList[i];
861
+
862
+ if(!mesh.isTrivial()) {
863
+ JSC3D.Math3D.transformVectors(this.transformMatrix, mesh.vertexBuffer, mesh.transformedVertexBuffer);
864
+
865
+ if(mesh.visible) {
866
+ switch(this.renderMode) {
867
+ case 'point':
868
+ this.renderPoint(mesh);
869
+ break;
870
+ case 'wireframe':
871
+ this.renderWireframe(mesh);
872
+ break;
873
+ case 'flat':
874
+ this.renderSolidFlat(mesh);
875
+ break;
876
+ case 'smooth':
877
+ this.renderSolidSmooth(mesh);
878
+ break;
879
+ case 'texture':
880
+ if(mesh.hasTexture())
881
+ this.renderSolidTexture(mesh);
882
+ else
883
+ this.renderSolidFlat(mesh);
884
+ break;
885
+ case 'textureflat':
886
+ if(mesh.hasTexture())
887
+ this.renderTextureFlat(mesh);
888
+ else
889
+ this.renderSolidFlat(mesh);
890
+ break;
891
+ case 'texturesmooth':
892
+ if(mesh.isEnvironmentCast && this.sphereMap != null && this.sphereMap.hasData())
893
+ this.renderSolidSphereMapped(mesh);
894
+ else if(mesh.hasTexture())
895
+ this.renderTextureSmooth(mesh);
896
+ else
897
+ this.renderSolidSmooth(mesh);
898
+ break;
899
+ default:
900
+ this.renderSolidFlat(mesh);
901
+ break;
902
+ }
903
+ }
904
+ }
905
+ }
906
+ };
907
+
908
+ /**
909
+ Sort meshes inside the scene into a render list. The sorting criterion is a mixture of trnasparency and depth.
910
+ This routine is necessary to ensure a correct rendering order.
911
+ @private
912
+ */
913
+ JSC3D.Viewer.prototype.sortScene = function(mat) {
914
+ var renderList = [];
915
+
916
+ var meshes = this.scene.getChildren();
917
+ for(var i=0; i<meshes.length; i++) {
918
+ var mesh = meshes[i];
919
+ if(!mesh.isTrivial()) {
920
+ renderList.push(mesh);
921
+ var meshCenter = mesh.aabb.center();
922
+ JSC3D.Math3D.transformVectors(mat, meshCenter, meshCenter);
923
+ var meshMaterial = mesh.material ? mesh.material : this.defaultMaterial;
924
+ mesh.sortKey = {
925
+ depth: meshCenter[2],
926
+ isTransparnt: (meshMaterial.transparency > 0) || (mesh.hasTexture() ? mesh.texture.hasTransparency : false)
927
+ };
928
+ }
929
+ }
930
+
931
+ renderList.sort(
932
+ function(mesh0, mesh1) {
933
+ // opaque meshes should always be prior to transparent ones to be rendered
934
+ if(!mesh0.sortKey.isTransparnt && mesh1.sortKey.isTransparnt)
935
+ return -1;
936
+
937
+ // opaque meshes should always be prior to transparent ones to be rendered
938
+ if(mesh0.sortKey.isTransparnt && !mesh1.sortKey.isTransparnt)
939
+ return 1;
940
+
941
+ // transparent meshes should be rendered from far to near
942
+ if(mesh0.sortKey.isTransparnt)
943
+ return mesh0.sortKey.depth - mesh1.sortKey.depth;
944
+
945
+ // opaque meshes should be rendered form near to far
946
+ return mesh1.sortKey.depth - mesh0.sortKey.depth;
947
+ } );
948
+
949
+ return renderList;
950
+ };
951
+
952
+ /**
953
+ Render the given mesh as points.
954
+ @private
955
+ */
956
+ JSC3D.Viewer.prototype.renderPoint = function(mesh) {
957
+ var w = this.frameWidth;
958
+ var h = this.frameHeight;
959
+ var xbound = w - 1;
960
+ var ybound = h - 1;
961
+ var ibuf = mesh.indexBuffer;
962
+ var vbuf = mesh.transformedVertexBuffer;
963
+ var nbuf = mesh.transformedVertexNormalZBuffer;
964
+ var cbuf = this.colorBuffer;
965
+ var zbuf = this.zBuffer;
966
+ var sbuf = this.selectionBuffer;
967
+ var numOfVertices = vbuf.length / 3;
968
+ var id = mesh.internalId;
969
+ var color = mesh.material ? mesh.material.diffuseColor : this.defaultMaterial.diffuseColor;
970
+
971
+ if(!nbuf || nbuf.length < numOfVertices) {
972
+ mesh.transformedVertexNormalZBuffer = new Array(numOfVertices);
973
+ nbuf = mesh.transformedVertexNormalZBuffer;
974
+ }
975
+
976
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.vertexNormalBuffer, nbuf);
977
+
978
+ for(var i=0, j=0; i<numOfVertices; i++, j+=3) {
979
+ var xformedNz = nbuf[i];
980
+ if(mesh.isDoubleSided)
981
+ xformedNz = xformedNz > 0 ? xformedNz : -xformedNz;
982
+ if(xformedNz > 0) {
983
+ var x = ~~(vbuf[j] + 0.5);
984
+ var y = ~~(vbuf[j + 1] + 0.5);
985
+ var z = vbuf[j + 2];
986
+ if(x >=0 && x < xbound && y >=0 && y < ybound) {
987
+ var pix = y * w + x;
988
+ if(z > zbuf[pix]) {
989
+ zbuf[pix] = z;
990
+ cbuf[pix] = color;
991
+ sbuf[pix] = id;
992
+ }
993
+ pix++;
994
+ if(z > zbuf[pix]) {
995
+ zbuf[pix] = z;
996
+ cbuf[pix] = color;
997
+ sbuf[pix] = id;
998
+ }
999
+ pix += xbound;
1000
+ if(z > zbuf[pix]) {
1001
+ zbuf[pix] = z;
1002
+ cbuf[pix] = color;
1003
+ sbuf[pix] = id;
1004
+ }
1005
+ pix++;
1006
+ if(z > zbuf[pix]) {
1007
+ zbuf[pix] = z;
1008
+ cbuf[pix] = color;
1009
+ sbuf[pix] = id;
1010
+ }
1011
+ }
1012
+ }
1013
+ }
1014
+ };
1015
+
1016
+ /**
1017
+ Render the given mesh as wireframe.
1018
+ @private
1019
+ */
1020
+ JSC3D.Viewer.prototype.renderWireframe = function(mesh) {
1021
+ var w = this.frameWidth;
1022
+ var h = this.frameHeight;
1023
+ var xbound = w - 1;
1024
+ var ybound = h - 1;
1025
+ var ibuf = mesh.indexBuffer;
1026
+ var vbuf = mesh.transformedVertexBuffer;
1027
+ var nbuf = mesh.transformedFaceNormalZBuffer;
1028
+ var cbuf = this.colorBuffer;
1029
+ var zbuf = this.zBuffer;
1030
+ var sbuf = this.selectionBuffer;
1031
+ var numOfFaces = mesh.faceCount;
1032
+ var id = mesh.internalId;
1033
+ var color = mesh.material ? mesh.material.diffuseColor : this.defaultMaterial.diffuseColor;
1034
+
1035
+ if(!nbuf || nbuf.length < numOfFaces) {
1036
+ mesh.transformedFaceNormalZBuffer = new Array(numOfFaces);
1037
+ nbuf = mesh.transformedFaceNormalZBuffer;
1038
+ }
1039
+
1040
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.faceNormalBuffer, nbuf);
1041
+
1042
+ var i = 0, j = 0;
1043
+ while(i < numOfFaces) {
1044
+ var xformedNz = nbuf[i++];
1045
+ if(mesh.isDoubleSided)
1046
+ xformedNz = xformedNz > 0 ? xformedNz : -xformedNz;
1047
+ if(xformedNz < 0) {
1048
+ do {
1049
+ } while (ibuf[j++] != -1);
1050
+ }
1051
+ else {
1052
+ var vStart, v0, v1;
1053
+ v0 = ibuf[j++] * 3;
1054
+ v1 = ibuf[j++] * 3;
1055
+ vStart = v0;
1056
+
1057
+ var isClosed = false;
1058
+ while(!isClosed) {
1059
+ var x0 = ~~(vbuf[v0] + 0.5);
1060
+ var y0 = ~~(vbuf[v0 + 1] + 0.5);
1061
+ var z0 = vbuf[v0 + 2];
1062
+ var x1 = ~~(vbuf[v1] + 0.5);
1063
+ var y1 = ~~(vbuf[v1 + 1] + 0.5);
1064
+ var z1 = vbuf[v1 + 2];
1065
+
1066
+ var dx = x1 - x0;
1067
+ var dy = y1 - y0;
1068
+ var dz = z1 - z0;
1069
+
1070
+ var dd;
1071
+ var xInc, yInc, zInc;
1072
+ if(Math.abs(dx) > Math.abs(dy)) {
1073
+ dd = dx;
1074
+ xInc = dx > 0 ? 1 : -1;
1075
+ yInc = dx != 0 ? xInc * dy / dx : 0;
1076
+ zInc = dx != 0 ? xInc * dz / dx : 0;
1077
+ }
1078
+ else {
1079
+ dd = dy;
1080
+ yInc = dy > 0 ? 1 : -1;
1081
+ xInc = dy != 0 ? yInc * dx / dy : 0;
1082
+ zInc = dy != 0 ? yInc * dz / dy : 0;
1083
+ }
1084
+
1085
+ var x = x0;
1086
+ var y = y0;
1087
+ var z = z0;
1088
+
1089
+ if(dd < 0) {
1090
+ x = x1;
1091
+ y = y1;
1092
+ z = z1;
1093
+ dd = -dd;
1094
+ xInc = -xInc;
1095
+ yInc = -yInc;
1096
+ zInc = -zInc;
1097
+ }
1098
+
1099
+ for(var k=0; k<dd; k++) {
1100
+ if(x >=0 && x < xbound && y >=0 && y < ybound) {
1101
+ var pix = (~~y) * w + (~~x);
1102
+ if(z > zbuf[pix]) {
1103
+ zbuf[pix] = z;
1104
+ cbuf[pix] = color;
1105
+ sbuf[pix] = id;
1106
+ }
1107
+ }
1108
+
1109
+ x += xInc;
1110
+ y += yInc;
1111
+ z += zInc;
1112
+ }
1113
+
1114
+ if(v1 == vStart) {
1115
+ isClosed = true;
1116
+ }
1117
+ else {
1118
+ v0 = v1;
1119
+
1120
+ if(ibuf[j] != -1) {
1121
+ v1 = ibuf[j++] * 3;
1122
+ }
1123
+ else {
1124
+ v1 = vStart;
1125
+ }
1126
+ }
1127
+ }
1128
+
1129
+ j++;
1130
+ }
1131
+ }
1132
+ };
1133
+
1134
+ /**
1135
+ Render the given mesh as solid object, using flat shading.
1136
+ @private
1137
+ */
1138
+ JSC3D.Viewer.prototype.renderSolidFlat = function(mesh) {
1139
+ var w = this.frameWidth;
1140
+ var h = this.frameHeight;
1141
+ var ibuf = mesh.indexBuffer;
1142
+ var vbuf = mesh.transformedVertexBuffer;
1143
+ var nbuf = mesh.transformedFaceNormalZBuffer;
1144
+ var cbuf = this.colorBuffer;
1145
+ var zbuf = this.zBuffer;
1146
+ var sbuf = this.selectionBuffer;
1147
+ var numOfFaces = mesh.faceCount;
1148
+ var id = mesh.internalId;
1149
+ var material = mesh.material ? mesh.material : this.defaultMaterial;
1150
+ var palette = material.getPalette();
1151
+ var isOpaque = material.transparency == 0;
1152
+ var trans = material.transparency * 255;
1153
+ var opaci = 255 - trans;
1154
+
1155
+ // skip this mesh if it is completely transparent
1156
+ if(material.transparency == 1)
1157
+ return;
1158
+
1159
+ if(!nbuf || nbuf.length < numOfFaces) {
1160
+ mesh.transformedFaceNormalZBuffer = new Array(numOfFaces);
1161
+ nbuf = mesh.transformedFaceNormalZBuffer;
1162
+ }
1163
+
1164
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.faceNormalBuffer, nbuf);
1165
+
1166
+ var Xs = new Array(3);
1167
+ var Ys = new Array(3);
1168
+ var Zs = new Array(3);
1169
+ var i = 0, j = 0;
1170
+ while(i < numOfFaces) {
1171
+ var xformedNz = nbuf[i++];
1172
+ if(mesh.isDoubleSided)
1173
+ xformedNz = xformedNz > 0 ? xformedNz : -xformedNz;
1174
+ if(xformedNz < 0) {
1175
+ do {
1176
+ } while (ibuf[j++] != -1);
1177
+ }
1178
+ else {
1179
+ var color = palette[~~(xformedNz * 255)];
1180
+
1181
+ var v0, v1, v2;
1182
+ v0 = ibuf[j++] * 3;
1183
+ v1 = ibuf[j++] * 3;
1184
+
1185
+ do {
1186
+ v2 = ibuf[j++] * 3;
1187
+
1188
+ Xs[0] = ~~(vbuf[v0] + 0.5);
1189
+ Ys[0] = ~~(vbuf[v0 + 1] + 0.5);
1190
+ Zs[0] = vbuf[v0 + 2];
1191
+ Xs[1] = ~~(vbuf[v1] + 0.5);
1192
+ Ys[1] = ~~(vbuf[v1 + 1] + 0.5);
1193
+ Zs[1] = vbuf[v1 + 2];
1194
+ Xs[2] = ~~(vbuf[v2] + 0.5);
1195
+ Ys[2] = ~~(vbuf[v2 + 1] + 0.5);
1196
+ Zs[2] = vbuf[v2 + 2];
1197
+
1198
+ var high = Ys[0] < Ys[1] ? 0 : 1;
1199
+ high = Ys[high] < Ys[2] ? high : 2;
1200
+ var low = Ys[0] > Ys[1] ? 0 : 1;
1201
+ low = Ys[low] > Ys[2] ? low : 2;
1202
+ var mid = 3 - low - high;
1203
+
1204
+ if(high != low) {
1205
+ var x0 = Xs[low];
1206
+ var z0 = Zs[low];
1207
+ var dy0 = Ys[low] - Ys[high];
1208
+ dy0 = dy0 != 0 ? dy0 : 1;
1209
+ var xStep0 = (Xs[low] - Xs[high]) / dy0;
1210
+ var zStep0 = (Zs[low] - Zs[high]) / dy0;
1211
+
1212
+ var x1 = Xs[low];
1213
+ var z1 = Zs[low];
1214
+ var dy1 = Ys[low] - Ys[mid];
1215
+ dy1 = dy1 != 0 ? dy1 : 1;
1216
+ var xStep1 = (Xs[low] - Xs[mid]) / dy1;
1217
+ var zStep1 = (Zs[low] - Zs[mid]) / dy1;
1218
+
1219
+ var x2 = Xs[mid];
1220
+ var z2 = Zs[mid];
1221
+ var dy2 = Ys[mid] - Ys[high];
1222
+ dy2 = dy2 != 0 ? dy2 : 1;
1223
+ var xStep2 = (Xs[mid] - Xs[high]) / dy2;
1224
+ var zStep2 = (Zs[mid] - Zs[high]) / dy2;
1225
+
1226
+ var linebase = Ys[low] * w;
1227
+ for(var y=Ys[low]; y>Ys[high]; y--) {
1228
+ if(y >=0 && y < h) {
1229
+ var xLeft = ~~x0;
1230
+ var zLeft = z0;
1231
+ var xRight, zRight;
1232
+ if(y > Ys[mid]) {
1233
+ xRight = ~~x1;
1234
+ zRight = z1;
1235
+ }
1236
+ else {
1237
+ xRight = ~~x2;
1238
+ zRight = z2;
1239
+ }
1240
+
1241
+ if(xLeft > xRight) {
1242
+ var temp;
1243
+ temp = xLeft;
1244
+ xLeft = xRight;
1245
+ xRight = temp;
1246
+ temp = zLeft;
1247
+ zLeft = zRight;
1248
+ zRight = temp;
1249
+ }
1250
+
1251
+ if(xLeft < 0)
1252
+ xLeft = 0;
1253
+ if(xRight >= w)
1254
+ xRight = w - 1;
1255
+
1256
+ var zInc = (xLeft != xRight) ? ((zRight - zLeft) / (xRight - xLeft)) : 1;
1257
+ var pix = linebase + xLeft;
1258
+ if(isOpaque) {
1259
+ for(var x=xLeft, z=zLeft; x<=xRight; x++, z+=zInc) {
1260
+ if(z > zbuf[pix]) {
1261
+ zbuf[pix] = z;
1262
+ cbuf[pix] = color;
1263
+ sbuf[pix] = id;
1264
+ }
1265
+ pix++;
1266
+ }
1267
+ }
1268
+ else {
1269
+ for(var x=xLeft, z=zLeft; x<xRight; x++, z+=zInc) {
1270
+ if(z > zbuf[pix]) {
1271
+ var foreColor = color;
1272
+ var backColor = cbuf[pix];
1273
+ var rr = ((backColor & 0xff0000) * trans + (foreColor & 0xff0000) * opaci) >> 8;
1274
+ var gg = ((backColor & 0xff00) * trans + (foreColor & 0xff00) * opaci) >> 8;
1275
+ var bb = ((backColor & 0xff) * trans + (foreColor & 0xff) * opaci) >> 8;
1276
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
1277
+ sbuf[pix] = id;
1278
+ }
1279
+ pix++;
1280
+ }
1281
+ }
1282
+ }
1283
+
1284
+ // step up to next scanline
1285
+ //
1286
+ x0 -= xStep0;
1287
+ z0 -= zStep0;
1288
+ if(y > Ys[mid]) {
1289
+ x1 -= xStep1;
1290
+ z1 -= zStep1;
1291
+ }
1292
+ else {
1293
+ x2 -= xStep2;
1294
+ z2 -= zStep2;
1295
+ }
1296
+ linebase -= w;
1297
+ }
1298
+ }
1299
+
1300
+ v1 = v2;
1301
+ } while (ibuf[j] != -1);
1302
+
1303
+ j++;
1304
+ }
1305
+ }
1306
+ };
1307
+
1308
+ /**
1309
+ Render the given mesh as solid object, using smooth shading.
1310
+ @private
1311
+ */
1312
+ JSC3D.Viewer.prototype.renderSolidSmooth = function(mesh) {
1313
+ var w = this.frameWidth;
1314
+ var h = this.frameHeight;
1315
+ var ibuf = mesh.indexBuffer;
1316
+ var vbuf = mesh.transformedVertexBuffer;
1317
+ var vnbuf = mesh.transformedVertexNormalZBuffer;
1318
+ var fnbuf = mesh.transformedFaceNormalZBuffer;
1319
+ var cbuf = this.colorBuffer;
1320
+ var zbuf = this.zBuffer;
1321
+ var sbuf = this.selectionBuffer;
1322
+ var numOfFaces = mesh.faceCount;
1323
+ var numOfVertices = vbuf.length / 3;
1324
+ var id = mesh.internalId;
1325
+ var material = mesh.material ? mesh.material : this.defaultMaterial;
1326
+ var palette = material.getPalette();
1327
+ var isOpaque = material.transparency == 0;
1328
+ var trans = material.transparency * 255;
1329
+ var opaci = 255 - trans;
1330
+
1331
+ // skip this mesh if it is completely transparent
1332
+ if(material.transparency == 1)
1333
+ return;
1334
+
1335
+ if(!vnbuf || vnbuf.length < numOfVertices) {
1336
+ mesh.transformedVertexNormalZBuffer = new Array(numOfVertices);
1337
+ vnbuf = mesh.transformedVertexNormalZBuffer;
1338
+ }
1339
+
1340
+ if(!fnbuf || fnbuf.length < numOfFaces) {
1341
+ mesh.transformedFaceNormalZBuffer = new Array(numOfFaces);
1342
+ fnbuf = mesh.transformedFaceNormalZBuffer;
1343
+ }
1344
+
1345
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.vertexNormalBuffer, vnbuf);
1346
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.faceNormalBuffer, fnbuf);
1347
+
1348
+ var isDoubleSided = mesh.isDoubleSided;
1349
+
1350
+ var Xs = new Array(3);
1351
+ var Ys = new Array(3);
1352
+ var Zs = new Array(3);
1353
+ var Ns = new Array(3);
1354
+ var i = 0, j = 0;
1355
+ while(i < numOfFaces) {
1356
+ var xformedFNz = fnbuf[i++];
1357
+ if(isDoubleSided)
1358
+ xformedFNz = xformedFNz > 0 ? xformedFNz : -xformedFNz;
1359
+ if(xformedFNz < 0) {
1360
+ do {
1361
+ } while (ibuf[j++] != -1);
1362
+ }
1363
+ else {
1364
+ var i0, i1, i2;
1365
+ var v0, v1, v2;
1366
+ i0 = ibuf[j++];
1367
+ v0 = i0 * 3;
1368
+ i1 = ibuf[j++];
1369
+ v1 = i1 * 3;
1370
+
1371
+ do {
1372
+ i2 = ibuf[j++];
1373
+ v2 = i2 * 3;
1374
+
1375
+ Xs[0] = ~~(vbuf[v0] + 0.5);
1376
+ Ys[0] = ~~(vbuf[v0 + 1] + 0.5);
1377
+ Zs[0] = vbuf[v0 + 2];
1378
+ Xs[1] = ~~(vbuf[v1] + 0.5);
1379
+ Ys[1] = ~~(vbuf[v1 + 1] + 0.5);
1380
+ Zs[1] = vbuf[v1 + 2];
1381
+ Xs[2] = ~~(vbuf[v2] + 0.5);
1382
+ Ys[2] = ~~(vbuf[v2 + 1] + 0.5);
1383
+ Zs[2] = vbuf[v2 + 2];
1384
+
1385
+ Ns[0] = vnbuf[i0];
1386
+ Ns[1] = vnbuf[i1];
1387
+ Ns[2] = vnbuf[i2];
1388
+ if(isDoubleSided) {
1389
+ if(Ns[0] < 0)
1390
+ Ns[0] = -Ns[0];
1391
+ if(Ns[1] < 0)
1392
+ Ns[1] = -Ns[1];
1393
+ if(Ns[2] < 0)
1394
+ Ns[2] = -Ns[2];
1395
+ }
1396
+
1397
+ var high = Ys[0] < Ys[1] ? 0 : 1;
1398
+ high = Ys[high] < Ys[2] ? high : 2;
1399
+ var low = Ys[0] > Ys[1] ? 0 : 1;
1400
+ low = Ys[low] > Ys[2] ? low : 2;
1401
+ var mid = 3 - low - high;
1402
+
1403
+ if(high != low) {
1404
+ var x0 = Xs[low];
1405
+ var z0 = Zs[low];
1406
+ var n0 = Ns[low] * 255;
1407
+ var dy0 = Ys[low] - Ys[high];
1408
+ dy0 = dy0 != 0 ? dy0 : 1;
1409
+ var xStep0 = (Xs[low] - Xs[high]) / dy0;
1410
+ var zStep0 = (Zs[low] - Zs[high]) / dy0;
1411
+ var nStep0 = (Ns[low] - Ns[high]) * 255 / dy0;
1412
+
1413
+ var x1 = Xs[low];
1414
+ var z1 = Zs[low];
1415
+ var n1 = Ns[low] * 255;
1416
+ var dy1 = Ys[low] - Ys[mid];
1417
+ dy1 = dy1 != 0 ? dy1 : 1;
1418
+ var xStep1 = (Xs[low] - Xs[mid]) / dy1;
1419
+ var zStep1 = (Zs[low] - Zs[mid]) / dy1;
1420
+ var nStep1 = (Ns[low] - Ns[mid]) * 255 / dy1;
1421
+
1422
+ var x2 = Xs[mid];
1423
+ var z2 = Zs[mid];
1424
+ var n2 = Ns[mid] * 255;
1425
+ var dy2 = Ys[mid] - Ys[high];
1426
+ dy2 = dy2 != 0 ? dy2 : 1;
1427
+ var xStep2 = (Xs[mid] - Xs[high]) / dy2;
1428
+ var zStep2 = (Zs[mid] - Zs[high]) / dy2;
1429
+ var nStep2 = (Ns[mid] - Ns[high]) * 255 / dy2;
1430
+
1431
+ var linebase = Ys[low] * w;
1432
+ for(var y=Ys[low]; y>Ys[high]; y--) {
1433
+ if(y >=0 && y < h) {
1434
+ var xLeft = ~~x0;
1435
+ var zLeft = z0;
1436
+ var nLeft = n0;
1437
+ var xRight, zRight, nRight;
1438
+ if(y > Ys[mid]) {
1439
+ xRight = ~~x1;
1440
+ zRight = z1;
1441
+ nRight = n1;
1442
+ }
1443
+ else {
1444
+ xRight = ~~x2;
1445
+ zRight = z2;
1446
+ nRight = n2;
1447
+ }
1448
+
1449
+ if(xLeft > xRight) {
1450
+ var temp;
1451
+ temp = xLeft;
1452
+ xLeft = xRight;
1453
+ xRight = temp;
1454
+ temp = zLeft;
1455
+ zLeft = zRight;
1456
+ zRight = temp;
1457
+ temp = nLeft;
1458
+ nLeft = nRight;
1459
+ nRight = temp;
1460
+ }
1461
+
1462
+ var zInc = (xLeft != xRight) ? ((zRight - zLeft) / (xRight - xLeft)) : 1;
1463
+ var nInc = (xLeft != xRight) ? ((nRight - nLeft) / (xRight - xLeft)) : 1;
1464
+ if(xLeft < 0) {
1465
+ zLeft -= xLeft * zInc;
1466
+ nLeft -= xLeft * nInc;
1467
+ xLeft = 0;
1468
+ }
1469
+ if(xRight >= w) {
1470
+ xRight = w - 1;
1471
+ }
1472
+ var pix = linebase + xLeft;
1473
+ if(isOpaque) {
1474
+ for(var x=xLeft, z=zLeft, n=nLeft; x<=xRight; x++, z+=zInc, n+=nInc) {
1475
+ if(z > zbuf[pix]) {
1476
+ zbuf[pix] = z;
1477
+ cbuf[pix] = palette[n > 0 ? (~~n) : 0];
1478
+ sbuf[pix] = id;
1479
+ }
1480
+ pix++;
1481
+ }
1482
+ }
1483
+ else {
1484
+ for(var x=xLeft, z=zLeft, n=nLeft; x<xRight; x++, z+=zInc, n+=nInc) {
1485
+ if(z > zbuf[pix]) {
1486
+ var foreColor = palette[n > 0 ? (~~n) : 0];
1487
+ var backColor = cbuf[pix];
1488
+ var rr = ((backColor & 0xff0000) * trans + (foreColor & 0xff0000) * opaci) >> 8;
1489
+ var gg = ((backColor & 0xff00) * trans + (foreColor & 0xff00) * opaci) >> 8;
1490
+ var bb = ((backColor & 0xff) * trans + (foreColor & 0xff) * opaci) >> 8;
1491
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
1492
+ sbuf[pix] = id;
1493
+ }
1494
+ pix++;
1495
+ }
1496
+ }
1497
+ }
1498
+
1499
+ // step up to next scanline
1500
+ //
1501
+ x0 -= xStep0;
1502
+ z0 -= zStep0;
1503
+ n0 -= nStep0;
1504
+ if(y > Ys[mid]) {
1505
+ x1 -= xStep1;
1506
+ z1 -= zStep1;
1507
+ n1 -= nStep1;
1508
+ }
1509
+ else {
1510
+ x2 -= xStep2;
1511
+ z2 -= zStep2;
1512
+ n2 -= nStep2;
1513
+ }
1514
+ linebase -= w;
1515
+ }
1516
+ }
1517
+
1518
+ v1 = v2;
1519
+ i1 = i2;
1520
+ } while (ibuf[j] != -1);
1521
+
1522
+ j++;
1523
+ }
1524
+ }
1525
+ };
1526
+
1527
+ /**
1528
+ Render the given mesh as textured object, with no lightings.
1529
+ @private
1530
+ */
1531
+ JSC3D.Viewer.prototype.renderSolidTexture = function(mesh) {
1532
+ var w = this.frameWidth;
1533
+ var h = this.frameHeight;
1534
+ var ibuf = mesh.indexBuffer;
1535
+ var vbuf = mesh.transformedVertexBuffer;
1536
+ var nbuf = mesh.transformedFaceNormalZBuffer;
1537
+ var cbuf = this.colorBuffer;
1538
+ var zbuf = this.zBuffer;
1539
+ var sbuf = this.selectionBuffer;
1540
+ var numOfFaces = mesh.faceCount;
1541
+ var id = mesh.internalId;
1542
+ var texture = mesh.texture;
1543
+ var isOpaque = !texture.hasTransparency;
1544
+ var tbuf = mesh.texCoordBuffer;
1545
+ var tibuf = mesh.texCoordIndexBuffer;
1546
+ var tdata = texture.data;
1547
+ var tdim = texture.width;
1548
+ var tbound = tdim - 1;
1549
+ var mipmaps = texture.hasMipmap() ? texture.mipmaps : null;
1550
+ var mipentries = mipmaps ? texture.mipentries : null;
1551
+
1552
+ if(!nbuf || nbuf.length < numOfFaces) {
1553
+ mesh.transformedFaceNormalZBuffer = new Array(numOfFaces);
1554
+ nbuf = mesh.transformedFaceNormalZBuffer;
1555
+ }
1556
+
1557
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.faceNormalBuffer, nbuf);
1558
+
1559
+ var Xs = new Array(3);
1560
+ var Ys = new Array(3);
1561
+ var Zs = new Array(3);
1562
+ var THs = new Array(3);
1563
+ var TVs = new Array(3);
1564
+ var i = 0, j = 0;
1565
+ while(i < numOfFaces) {
1566
+ var xformedNz = nbuf[i++];
1567
+ if(mesh.isDoubleSided)
1568
+ xformedNz = xformedNz > 0 ? xformedNz : -xformedNz;
1569
+ if(xformedNz < 0) {
1570
+ do {
1571
+ } while (ibuf[j++] != -1);
1572
+ }
1573
+ else {
1574
+ var v0, v1, v2;
1575
+ var t0, t1, t2;
1576
+ v0 = ibuf[j] * 3;
1577
+ t0 = tibuf[j] * 2;
1578
+ j++;
1579
+ v1 = ibuf[j] * 3;
1580
+ t1 = tibuf[j] * 2;
1581
+ j++;
1582
+
1583
+ // select an appropriate mip-map level for texturing
1584
+ //
1585
+ if(mipmaps) {
1586
+ v2 = ibuf[j] * 3;
1587
+ t2 = tibuf[j] * 2;
1588
+
1589
+ tdim = texture.width;
1590
+
1591
+ Xs[0] = vbuf[v0];
1592
+ Ys[0] = vbuf[v0 + 1];
1593
+ Xs[1] = vbuf[v1];
1594
+ Ys[1] = vbuf[v1 + 1];
1595
+ Xs[2] = vbuf[v2];
1596
+ Ys[2] = vbuf[v2 + 1];
1597
+
1598
+ THs[0] = tbuf[t0] * tdim;
1599
+ TVs[0] = tbuf[t0 + 1] * tdim;
1600
+ THs[1] = tbuf[t1] * tdim;
1601
+ TVs[1] = tbuf[t1 + 1] * tdim;
1602
+ THs[2] = tbuf[t2] * tdim;
1603
+ TVs[2] = tbuf[t2 + 1] * tdim;
1604
+
1605
+ var faceArea = (Xs[1] - Xs[0]) * (Ys[2] - Ys[0]) - (Ys[1] - Ys[0]) * (Xs[2] - Xs[0]);
1606
+ if(faceArea < 0)
1607
+ faceArea = -faceArea;
1608
+ faceArea += 1;
1609
+ var texArea = (THs[1] - THs[0]) * (TVs[2] - TVs[0]) - (TVs[1] - TVs[0]) * (THs[2] - THs[0]);
1610
+ if(texArea < 0)
1611
+ texArea = -texArea;
1612
+ var mipRatio = texArea / faceArea;
1613
+
1614
+ var level = 0;
1615
+ if(mipRatio < mipentries[1])
1616
+ level = 0;
1617
+ else if(mipRatio >= mipentries[mipentries.length - 1]) {
1618
+ level = mipentries.length - 1;
1619
+ tdim = 1;
1620
+ }
1621
+ else {
1622
+ while(mipRatio >= mipentries[level+1]) {
1623
+ level++;
1624
+ tdim /= 2;
1625
+ }
1626
+ }
1627
+
1628
+ tdata = mipmaps[level];
1629
+ tbound = tdim - 1;
1630
+ }
1631
+
1632
+ do {
1633
+ v2 = ibuf[j] * 3;
1634
+ t2 = tibuf[j] * 2;
1635
+ j++;
1636
+
1637
+ Xs[0] = ~~(vbuf[v0] + 0.5);
1638
+ Ys[0] = ~~(vbuf[v0 + 1] + 0.5);
1639
+ Zs[0] = vbuf[v0 + 2];
1640
+ Xs[1] = ~~(vbuf[v1] + 0.5);
1641
+ Ys[1] = ~~(vbuf[v1 + 1] + 0.5);
1642
+ Zs[1] = vbuf[v1 + 2];
1643
+ Xs[2] = ~~(vbuf[v2] + 0.5);
1644
+ Ys[2] = ~~(vbuf[v2 + 1] + 0.5);
1645
+ Zs[2] = vbuf[v2 + 2];
1646
+
1647
+ THs[0] = tbuf[t0] * tdim;
1648
+ TVs[0] = tbuf[t0 + 1] * tdim;
1649
+ THs[1] = tbuf[t1] * tdim;
1650
+ TVs[1] = tbuf[t1 + 1] * tdim;
1651
+ THs[2] = tbuf[t2] * tdim;
1652
+ TVs[2] = tbuf[t2 + 1] * tdim;
1653
+
1654
+ var high = Ys[0] < Ys[1] ? 0 : 1;
1655
+ high = Ys[high] < Ys[2] ? high : 2;
1656
+ var low = Ys[0] > Ys[1] ? 0 : 1;
1657
+ low = Ys[low] > Ys[2] ? low : 2;
1658
+ var mid = 3 - low - high;
1659
+
1660
+ if(high != low) {
1661
+ var x0 = Xs[low];
1662
+ var z0 = Zs[low];
1663
+ var th0 = THs[low];
1664
+ var tv0 = TVs[low];
1665
+ var dy0 = Ys[low] - Ys[high];
1666
+ dy0 = dy0 != 0 ? dy0 : 1;
1667
+ var xStep0 = (Xs[low] - Xs[high]) / dy0;
1668
+ var zStep0 = (Zs[low] - Zs[high]) / dy0;
1669
+ var thStep0 = (THs[low] - THs[high]) / dy0;
1670
+ var tvStep0 = (TVs[low] - TVs[high]) / dy0;
1671
+
1672
+ var x1 = Xs[low];
1673
+ var z1 = Zs[low];
1674
+ var th1 = THs[low];
1675
+ var tv1 = TVs[low];
1676
+ var dy1 = Ys[low] - Ys[mid];
1677
+ dy1 = dy1 != 0 ? dy1 : 1;
1678
+ var xStep1 = (Xs[low] - Xs[mid]) / dy1;
1679
+ var zStep1 = (Zs[low] - Zs[mid]) / dy1;
1680
+ var thStep1 = (THs[low] - THs[mid]) / dy1;
1681
+ var tvStep1 = (TVs[low] - TVs[mid]) / dy1;
1682
+
1683
+ var x2 = Xs[mid];
1684
+ var z2 = Zs[mid];
1685
+ var th2 = THs[mid];
1686
+ var tv2 = TVs[mid];
1687
+ var dy2 = Ys[mid] - Ys[high];
1688
+ dy2 = dy2 != 0 ? dy2 : 1;
1689
+ var xStep2 = (Xs[mid] - Xs[high]) / dy2;
1690
+ var zStep2 = (Zs[mid] - Zs[high]) / dy2;
1691
+ var thStep2 = (THs[mid] - THs[high]) / dy2;
1692
+ var tvStep2 = (TVs[mid] - TVs[high]) / dy2;
1693
+
1694
+ var linebase = Ys[low] * w;
1695
+ for(var y=Ys[low]; y>Ys[high]; y--) {
1696
+ if(y >=0 && y < h) {
1697
+ var xLeft = ~~x0;
1698
+ var zLeft = z0;
1699
+ var thLeft = th0;
1700
+ var tvLeft = tv0;
1701
+ var xRight, zRight, thRight, tvRight;
1702
+ if(y > Ys[mid]) {
1703
+ xRight = ~~x1;
1704
+ zRight = z1;
1705
+ thRight = th1;
1706
+ tvRight = tv1;
1707
+ }
1708
+ else {
1709
+ xRight = ~~x2;
1710
+ zRight = z2;
1711
+ thRight = th2;
1712
+ tvRight = tv2;
1713
+ }
1714
+
1715
+ if(xLeft > xRight) {
1716
+ var temp;
1717
+ temp = xLeft;
1718
+ xLeft = xRight;
1719
+ xRight = temp;
1720
+ temp = zLeft;
1721
+ zLeft = zRight;
1722
+ zRight = temp;
1723
+ temp = thLeft;
1724
+ thLeft = thRight;
1725
+ thRight = temp;
1726
+ temp = tvLeft;
1727
+ tvLeft = tvRight;
1728
+ tvRight = temp;
1729
+ }
1730
+
1731
+ var zInc = (xLeft != xRight) ? ((zRight - zLeft) / (xRight - xLeft)) : 1;
1732
+ var thInc = (xLeft != xRight) ? ((thRight - thLeft) / (xRight - xLeft)) : 1;
1733
+ var tvInc = (xLeft != xRight) ? ((tvRight - tvLeft) / (xRight - xLeft)) : 1;
1734
+
1735
+ if(xLeft < 0) {
1736
+ zLeft -= xLeft * zInc;
1737
+ thLeft -= xLeft * thInc;
1738
+ tvLeft -= xLeft * tvInc;
1739
+ xLeft = 0;
1740
+ }
1741
+ if(xRight >= w)
1742
+ xRight = w - 1;
1743
+
1744
+ var pix = linebase + xLeft;
1745
+ if(isOpaque) {
1746
+ for(var x=xLeft, z=zLeft, th=thLeft, tv=tvLeft; x<=xRight; x++, z+=zInc, th+=thInc, tv+=tvInc) {
1747
+ if(z > zbuf[pix]) {
1748
+ zbuf[pix] = z;
1749
+ cbuf[pix] = tdata[(tv & tbound) * tdim + (th & tbound)];
1750
+ sbuf[pix] = id;
1751
+ }
1752
+ pix++;
1753
+ }
1754
+ }
1755
+ else {
1756
+ for(var x=xLeft, z=zLeft, th=thLeft, tv=tvLeft; x<xRight; x++, z+=zInc, th+=thInc, tv+=tvInc) {
1757
+ if(z > zbuf[pix]) {
1758
+ var foreColor = tdata[(tv & tbound) * tdim + (th & tbound)];
1759
+ var backColor = cbuf[pix];
1760
+ var opaci = (foreColor >> 24) & 0xff;
1761
+ var trans = 255 - opaci;
1762
+ var rr = ((backColor & 0xff0000) * trans + (foreColor & 0xff0000) * opaci) >> 8;
1763
+ var gg = ((backColor & 0xff00) * trans + (foreColor & 0xff00) * opaci) >> 8;
1764
+ var bb = ((backColor & 0xff) * trans + (foreColor & 0xff) * opaci) >> 8;
1765
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
1766
+ sbuf[pix] = id;
1767
+ }
1768
+ pix++;
1769
+ }
1770
+ }
1771
+ }
1772
+
1773
+ // step up to next scanline
1774
+ //
1775
+ x0 -= xStep0;
1776
+ z0 -= zStep0;
1777
+ th0 -= thStep0;
1778
+ tv0 -= tvStep0;
1779
+ if(y > Ys[mid]) {
1780
+ x1 -= xStep1;
1781
+ z1 -= zStep1;
1782
+ th1 -= thStep1;
1783
+ tv1 -= tvStep1;
1784
+ }
1785
+ else {
1786
+ x2 -= xStep2;
1787
+ z2 -= zStep2;
1788
+ th2 -= thStep2;
1789
+ tv2 -= tvStep2;
1790
+ }
1791
+ linebase -= w;
1792
+ }
1793
+ }
1794
+
1795
+ v1 = v2;
1796
+ t1 = t2;
1797
+ } while (ibuf[j] != -1);
1798
+
1799
+ j++;
1800
+ }
1801
+ }
1802
+ };
1803
+
1804
+ /**
1805
+ Render the given mesh as textured object. Lighting will be calculated per face.
1806
+ @private
1807
+ */
1808
+ JSC3D.Viewer.prototype.renderTextureFlat = function(mesh) {
1809
+ var w = this.frameWidth;
1810
+ var h = this.frameHeight;
1811
+ var ibuf = mesh.indexBuffer;
1812
+ var vbuf = mesh.transformedVertexBuffer;
1813
+ var nbuf = mesh.transformedFaceNormalZBuffer;
1814
+ var cbuf = this.colorBuffer;
1815
+ var zbuf = this.zBuffer;
1816
+ var sbuf = this.selectionBuffer;
1817
+ var numOfFaces = mesh.faceCount;
1818
+ var id = mesh.internalId;
1819
+ var material = mesh.material ? mesh.material : this.defaultMaterial;
1820
+ var palette = material.getPalette();
1821
+ var texture = mesh.texture;
1822
+ var isOpaque = (material.transparency == 0) && !texture.hasTransparency;
1823
+ var matOpacity = ~~((1 - material.transparency) * 255);
1824
+ var tbuf = mesh.texCoordBuffer;
1825
+ var tibuf = mesh.texCoordIndexBuffer;
1826
+ var tdata = texture.data;
1827
+ var tdim = texture.width;
1828
+ var tbound = tdim - 1;
1829
+ var mipmaps = texture.hasMipmap() ? texture.mipmaps : null;
1830
+ var mipentries = mipmaps ? texture.mipentries : null;
1831
+
1832
+ // skip this mesh if it is completely transparent
1833
+ if(material.transparency == 1)
1834
+ return;
1835
+
1836
+ if(!nbuf || nbuf.length < numOfFaces) {
1837
+ mesh.transformedFaceNormalZBuffer = new Array(numOfFaces);
1838
+ nbuf = mesh.transformedFaceNormalZBuffer;
1839
+ }
1840
+
1841
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.faceNormalBuffer, nbuf);
1842
+
1843
+ var Xs = new Array(3);
1844
+ var Ys = new Array(3);
1845
+ var Zs = new Array(3);
1846
+ var THs = new Array(3);
1847
+ var TVs = new Array(3);
1848
+ var i = 0, j = 0;
1849
+ while(i < numOfFaces) {
1850
+ var xformedNz = nbuf[i++];
1851
+ if(mesh.isDoubleSided)
1852
+ xformedNz = xformedNz > 0 ? xformedNz : -xformedNz;
1853
+ if(xformedNz < 0) {
1854
+ do {
1855
+ } while (ibuf[j++] != -1);
1856
+ }
1857
+ else {
1858
+ var color = palette[~~(xformedNz * 255)];
1859
+
1860
+ var v0, v1, v2;
1861
+ var t0, t1, t2;
1862
+ v0 = ibuf[j] * 3;
1863
+ t0 = tibuf[j] * 2;
1864
+ j++;
1865
+ v1 = ibuf[j] * 3;
1866
+ t1 = tibuf[j] * 2;
1867
+ j++;
1868
+
1869
+ if(mipmaps) {
1870
+ v2 = ibuf[j] * 3;
1871
+ t2 = tibuf[j] * 2;
1872
+
1873
+ tdim = texture.width;
1874
+
1875
+ Xs[0] = vbuf[v0];
1876
+ Ys[0] = vbuf[v0 + 1];
1877
+ Xs[1] = vbuf[v1];
1878
+ Ys[1] = vbuf[v1 + 1];
1879
+ Xs[2] = vbuf[v2];
1880
+ Ys[2] = vbuf[v2 + 1];
1881
+
1882
+ THs[0] = tbuf[t0] * tdim;
1883
+ TVs[0] = tbuf[t0 + 1] * tdim;
1884
+ THs[1] = tbuf[t1] * tdim;
1885
+ TVs[1] = tbuf[t1 + 1] * tdim;
1886
+ THs[2] = tbuf[t2] * tdim;
1887
+ TVs[2] = tbuf[t2 + 1] * tdim;
1888
+
1889
+ var faceArea = (Xs[1] - Xs[0]) * (Ys[2] - Ys[0]) - (Ys[1] - Ys[0]) * (Xs[2] - Xs[0]);
1890
+ if(faceArea < 0)
1891
+ faceArea = -faceArea;
1892
+ faceArea += 1;
1893
+ var texArea = (THs[1] - THs[0]) * (TVs[2] - TVs[0]) - (TVs[1] - TVs[0]) * (THs[2] - THs[0]);
1894
+ if(texArea < 0)
1895
+ texArea = -texArea;
1896
+ var mipRatio = texArea / faceArea;
1897
+
1898
+ var level = 0;
1899
+ if(mipRatio < mipentries[1])
1900
+ level = 0;
1901
+ else if(mipRatio >= mipentries[mipentries.length - 1]) {
1902
+ level = mipentries.length - 1;
1903
+ tdim = 1;
1904
+ }
1905
+ else {
1906
+ while(mipRatio >= mipentries[level+1]) {
1907
+ level++;
1908
+ tdim /= 2;
1909
+ }
1910
+ }
1911
+
1912
+ tdata = mipmaps[level];
1913
+ tbound = tdim - 1;
1914
+ }
1915
+
1916
+ do {
1917
+ v2 = ibuf[j] * 3;
1918
+ t2 = tibuf[j] * 2;
1919
+ j++;
1920
+
1921
+ Xs[0] = ~~(vbuf[v0] + 0.5);
1922
+ Ys[0] = ~~(vbuf[v0 + 1] + 0.5);
1923
+ Zs[0] = vbuf[v0 + 2];
1924
+ Xs[1] = ~~(vbuf[v1] + 0.5);
1925
+ Ys[1] = ~~(vbuf[v1 + 1] + 0.5);
1926
+ Zs[1] = vbuf[v1 + 2];
1927
+ Xs[2] = ~~(vbuf[v2] + 0.5);
1928
+ Ys[2] = ~~(vbuf[v2 + 1] + 0.5);
1929
+ Zs[2] = vbuf[v2 + 2];
1930
+
1931
+ THs[0] = tbuf[t0] * tdim;
1932
+ TVs[0] = tbuf[t0 + 1] * tdim;
1933
+ THs[1] = tbuf[t1] * tdim;
1934
+ TVs[1] = tbuf[t1 + 1] * tdim;
1935
+ THs[2] = tbuf[t2] * tdim;
1936
+ TVs[2] = tbuf[t2 + 1] * tdim;
1937
+
1938
+ var high = Ys[0] < Ys[1] ? 0 : 1;
1939
+ high = Ys[high] < Ys[2] ? high : 2;
1940
+ var low = Ys[0] > Ys[1] ? 0 : 1;
1941
+ low = Ys[low] > Ys[2] ? low : 2;
1942
+ var mid = 3 - low - high;
1943
+
1944
+ if(high != low) {
1945
+ var x0 = Xs[low];
1946
+ var z0 = Zs[low];
1947
+ var th0 = THs[low];
1948
+ var tv0 = TVs[low];
1949
+ var dy0 = Ys[low] - Ys[high];
1950
+ dy0 = dy0 != 0 ? dy0 : 1;
1951
+ var xStep0 = (Xs[low] - Xs[high]) / dy0;
1952
+ var zStep0 = (Zs[low] - Zs[high]) / dy0;
1953
+ var thStep0 = (THs[low] - THs[high]) / dy0;
1954
+ var tvStep0 = (TVs[low] - TVs[high]) / dy0;
1955
+
1956
+ var x1 = Xs[low];
1957
+ var z1 = Zs[low];
1958
+ var th1 = THs[low];
1959
+ var tv1 = TVs[low];
1960
+ var dy1 = Ys[low] - Ys[mid];
1961
+ dy1 = dy1 != 0 ? dy1 : 1;
1962
+ var xStep1 = (Xs[low] - Xs[mid]) / dy1;
1963
+ var zStep1 = (Zs[low] - Zs[mid]) / dy1;
1964
+ var thStep1 = (THs[low] - THs[mid]) / dy1;
1965
+ var tvStep1 = (TVs[low] - TVs[mid]) / dy1;
1966
+
1967
+ var x2 = Xs[mid];
1968
+ var z2 = Zs[mid];
1969
+ var th2 = THs[mid];
1970
+ var tv2 = TVs[mid];
1971
+ var dy2 = Ys[mid] - Ys[high];
1972
+ dy2 = dy2 != 0 ? dy2 : 1;
1973
+ var xStep2 = (Xs[mid] - Xs[high]) / dy2;
1974
+ var zStep2 = (Zs[mid] - Zs[high]) / dy2;
1975
+ var thStep2 = (THs[mid] - THs[high]) / dy2;
1976
+ var tvStep2 = (TVs[mid] - TVs[high]) / dy2;
1977
+
1978
+ var linebase = Ys[low] * w;
1979
+ for(var y=Ys[low]; y>Ys[high]; y--) {
1980
+ if(y >=0 && y < h) {
1981
+ var xLeft = ~~x0;
1982
+ var zLeft = z0;
1983
+ var thLeft = th0;
1984
+ var tvLeft = tv0;
1985
+ var xRight, zRight, thRight, tvRight;
1986
+ if(y > Ys[mid]) {
1987
+ xRight = ~~x1;
1988
+ zRight = z1;
1989
+ thRight = th1;
1990
+ tvRight = tv1;
1991
+ }
1992
+ else {
1993
+ xRight = ~~x2;
1994
+ zRight = z2;
1995
+ thRight = th2;
1996
+ tvRight = tv2;
1997
+ }
1998
+
1999
+ if(xLeft > xRight) {
2000
+ var temp;
2001
+ temp = xLeft;
2002
+ xLeft = xRight;
2003
+ xRight = temp;
2004
+ temp = zLeft;
2005
+ zLeft = zRight;
2006
+ zRight = temp;
2007
+ temp = thLeft;
2008
+ thLeft = thRight;
2009
+ thRight = temp;
2010
+ temp = tvLeft;
2011
+ tvLeft = tvRight;
2012
+ tvRight = temp;
2013
+ }
2014
+
2015
+ var zInc = (xLeft != xRight) ? ((zRight - zLeft) / (xRight - xLeft)) : 1;
2016
+ var thInc = (xLeft != xRight) ? ((thRight - thLeft) / (xRight - xLeft)) : 1;
2017
+ var tvInc = (xLeft != xRight) ? ((tvRight - tvLeft) / (xRight - xLeft)) : 1;
2018
+
2019
+ if(xLeft < 0) {
2020
+ zLeft -= xLeft * zInc;
2021
+ thLeft -= xLeft * thInc;
2022
+ tvLeft -= xLeft * tvInc;
2023
+ xLeft = 0;
2024
+ }
2025
+ if(xRight >= w)
2026
+ xRight = w - 1;
2027
+
2028
+ var pix = linebase + xLeft;
2029
+ if(isOpaque) {
2030
+ for(var x=xLeft, z=zLeft, th=thLeft, tv=tvLeft; x<=xRight; x++, z+=zInc, th+=thInc, tv+=tvInc) {
2031
+ if(z > zbuf[pix]) {
2032
+ zbuf[pix] = z;
2033
+ var texel = tdata[(tv & tbound) * tdim + (th & tbound)];
2034
+ var rr = (((color & 0xff0000) >> 16) * ((texel & 0xff0000) >> 8));
2035
+ var gg = (((color & 0xff00) >> 8) * ((texel & 0xff00) >> 8));
2036
+ var bb = ((color & 0xff) * (texel & 0xff)) >> 8;
2037
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
2038
+ sbuf[pix] = id;
2039
+ }
2040
+ pix++;
2041
+ }
2042
+ }
2043
+ else {
2044
+ for(var x=xLeft, z=zLeft, th=thLeft, tv=tvLeft; x<xRight; x++, z+=zInc, th+=thInc, tv+=tvInc) {
2045
+ if(z > zbuf[pix]) {
2046
+ var foreColor = tdata[(tv & tbound) * tdim + (th & tbound)];
2047
+ var backColor = cbuf[pix];
2048
+ var opaci = (((foreColor >> 24) & 0xff) * (matOpacity & 0xff)) >> 8;
2049
+ var rr = (((color & 0xff0000) >> 16) * ((foreColor & 0xff0000) >> 8));
2050
+ var gg = (((color & 0xff00) >> 8) * ((foreColor & 0xff00) >> 8));
2051
+ var bb = ((color & 0xff) * (foreColor & 0xff)) >> 8;
2052
+ if(opaci > 250) {
2053
+ zbuf[pix] = z;
2054
+ }
2055
+ else {
2056
+ var trans = 255 - opaci;
2057
+ rr = (rr * opaci + (backColor & 0xff0000) * trans) >> 8;
2058
+ gg = (gg * opaci + (backColor & 0xff00) * trans) >> 8;
2059
+ bb = (bb * opaci + (backColor & 0xff) * trans) >> 8;
2060
+ }
2061
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
2062
+ sbuf[pix] = id;
2063
+ }
2064
+ pix++;
2065
+ }
2066
+ }
2067
+ }
2068
+
2069
+ // step up to next scanline
2070
+ //
2071
+ x0 -= xStep0;
2072
+ z0 -= zStep0;
2073
+ th0 -= thStep0;
2074
+ tv0 -= tvStep0;
2075
+ if(y > Ys[mid]) {
2076
+ x1 -= xStep1;
2077
+ z1 -= zStep1;
2078
+ th1 -= thStep1;
2079
+ tv1 -= tvStep1;
2080
+ }
2081
+ else {
2082
+ x2 -= xStep2;
2083
+ z2 -= zStep2;
2084
+ th2 -= thStep2;
2085
+ tv2 -= tvStep2;
2086
+ }
2087
+ linebase -= w;
2088
+ }
2089
+ }
2090
+
2091
+ v1 = v2;
2092
+ t1 = t2;
2093
+ } while (ibuf[j] != -1);
2094
+
2095
+ j++;
2096
+ }
2097
+ }
2098
+ };
2099
+
2100
+ /**
2101
+ Render the given mesh as textured object. Lighting will be calculated per vertex and then inerpolated between and inside scanlines.
2102
+ @private
2103
+ */
2104
+ JSC3D.Viewer.prototype.renderTextureSmooth = function(mesh) {
2105
+ var w = this.frameWidth;
2106
+ var h = this.frameHeight;
2107
+ var ibuf = mesh.indexBuffer;
2108
+ var vbuf = mesh.transformedVertexBuffer;
2109
+ var vnbuf = mesh.transformedVertexNormalZBuffer;
2110
+ var fnbuf = mesh.transformedFaceNormalZBuffer;
2111
+ var cbuf = this.colorBuffer;
2112
+ var zbuf = this.zBuffer;
2113
+ var sbuf = this.selectionBuffer;
2114
+ var numOfFaces = mesh.faceCount;
2115
+ var id = mesh.internalId;
2116
+ var numOfVertices = vbuf.length / 3;
2117
+ var material = mesh.material ? mesh.material : this.defaultMaterial;
2118
+ var palette = material.getPalette();
2119
+ var texture = mesh.texture;
2120
+ var isOpaque = (material.transparency == 0) && !texture.hasTransparency;
2121
+ var matOpacity = ~~((1 - material.transparency) * 255);
2122
+ var tbuf = mesh.texCoordBuffer;
2123
+ var tibuf = mesh.texCoordIndexBuffer;
2124
+ var tdata = texture.data;
2125
+ var tdim = texture.width;
2126
+ var tbound = tdim - 1;
2127
+ var mipmaps = texture.hasMipmap() ? texture.mipmaps : null;
2128
+ var mipentries = mipmaps ? texture.mipentries : null;
2129
+
2130
+ // skip this mesh if it is completely transparent
2131
+ if(material.transparency == 1)
2132
+ return;
2133
+
2134
+ if(!vnbuf || vnbuf.length < numOfVertices) {
2135
+ mesh.transformedVertexNormalZBuffer = new Array(numOfVertices);
2136
+ vnbuf = mesh.transformedVertexNormalZBuffer;
2137
+ }
2138
+
2139
+ if(!fnbuf || fnbuf.length < numOfFaces) {
2140
+ mesh.transformedFaceNormalZBuffer = new Array(numOfFaces);
2141
+ fnbuf = mesh.transformedFaceNormalZBuffer;
2142
+ }
2143
+
2144
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.vertexNormalBuffer, vnbuf);
2145
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.faceNormalBuffer, fnbuf);
2146
+
2147
+ var isDoubleSided = mesh.isDoubleSided;
2148
+
2149
+ var Xs = new Array(3);
2150
+ var Ys = new Array(3);
2151
+ var Zs = new Array(3);
2152
+ var Ns = new Array(3);
2153
+ var THs = new Array(3);
2154
+ var TVs = new Array(3);
2155
+ var i = 0, j = 0;
2156
+ while(i < numOfFaces) {
2157
+ var xformedFNz = fnbuf[i++];
2158
+ if(isDoubleSided)
2159
+ xformedFNz = xformedFNz > 0 ? xformedFNz : -xformedFNz;
2160
+ if(xformedFNz < 0) {
2161
+ do {
2162
+ } while (ibuf[j++] != -1);
2163
+ }
2164
+ else {
2165
+ var i0, i1, i2;
2166
+ var v0, v1, v2;
2167
+ var t0, t1, t2;
2168
+ i0 = ibuf[j];
2169
+ v0 = i0 * 3;
2170
+ t0 = tibuf[j] * 2;
2171
+ j++;
2172
+ i1 = ibuf[j];
2173
+ v1 = i1 * 3;
2174
+ t1 = tibuf[j] * 2;
2175
+ j++;
2176
+
2177
+ if(mipmaps) {
2178
+ v2 = ibuf[j] * 3;
2179
+ t2 = tibuf[j] * 2;
2180
+
2181
+ tdim = texture.width;
2182
+
2183
+ Xs[0] = vbuf[v0];
2184
+ Ys[0] = vbuf[v0 + 1];
2185
+ Xs[1] = vbuf[v1];
2186
+ Ys[1] = vbuf[v1 + 1];
2187
+ Xs[2] = vbuf[v2];
2188
+ Ys[2] = vbuf[v2 + 1];
2189
+
2190
+ THs[0] = tbuf[t0] * tdim;
2191
+ TVs[0] = tbuf[t0 + 1] * tdim;
2192
+ THs[1] = tbuf[t1] * tdim;
2193
+ TVs[1] = tbuf[t1 + 1] * tdim;
2194
+ THs[2] = tbuf[t2] * tdim;
2195
+ TVs[2] = tbuf[t2 + 1] * tdim;
2196
+
2197
+ var faceArea = (Xs[1] - Xs[0]) * (Ys[2] - Ys[0]) - (Ys[1] - Ys[0]) * (Xs[2] - Xs[0]);
2198
+ if(faceArea < 0)
2199
+ faceArea = -faceArea;
2200
+ faceArea += 1;
2201
+ var texArea = (THs[1] - THs[0]) * (TVs[2] - TVs[0]) - (TVs[1] - TVs[0]) * (THs[2] - THs[0]);
2202
+ if(texArea < 0)
2203
+ texArea = -texArea;
2204
+ var mipRatio = texArea / faceArea;
2205
+
2206
+ var level = 0;
2207
+ if(mipRatio < mipentries[1])
2208
+ level = 0;
2209
+ else if(mipRatio >= mipentries[mipentries.length - 1]) {
2210
+ level = mipentries.length - 1;
2211
+ tdim = 1;
2212
+ }
2213
+ else {
2214
+ while(mipRatio >= mipentries[level+1]) {
2215
+ level++;
2216
+ tdim /= 2;
2217
+ }
2218
+ }
2219
+
2220
+ tdata = mipmaps[level];
2221
+ tbound = tdim - 1;
2222
+ }
2223
+
2224
+ do {
2225
+ i2 = ibuf[j];
2226
+ v2 = i2 * 3;
2227
+ t2 = tibuf[j] * 2;
2228
+ j++;
2229
+
2230
+ Xs[0] = ~~(vbuf[v0] + 0.5);
2231
+ Ys[0] = ~~(vbuf[v0 + 1] + 0.5);
2232
+ Zs[0] = vbuf[v0 + 2];
2233
+ Xs[1] = ~~(vbuf[v1] + 0.5);
2234
+ Ys[1] = ~~(vbuf[v1 + 1] + 0.5);
2235
+ Zs[1] = vbuf[v1 + 2];
2236
+ Xs[2] = ~~(vbuf[v2] + 0.5);
2237
+ Ys[2] = ~~(vbuf[v2 + 1] + 0.5);
2238
+ Zs[2] = vbuf[v2 + 2];
2239
+
2240
+ THs[0] = tbuf[t0] * tdim;
2241
+ TVs[0] = tbuf[t0 + 1] * tdim;
2242
+ THs[1] = tbuf[t1] * tdim;
2243
+ TVs[1] = tbuf[t1 + 1] * tdim;
2244
+ THs[2] = tbuf[t2] * tdim;
2245
+ TVs[2] = tbuf[t2 + 1] * tdim;
2246
+
2247
+ Ns[0] = vnbuf[i0];
2248
+ Ns[1] = vnbuf[i1];
2249
+ Ns[2] = vnbuf[i2];
2250
+ if(isDoubleSided) {
2251
+ if(Ns[0] < 0)
2252
+ Ns[0] = -Ns[0];
2253
+ if(Ns[1] < 0)
2254
+ Ns[1] = -Ns[1];
2255
+ if(Ns[2] < 0)
2256
+ Ns[2] = -Ns[2];
2257
+ }
2258
+
2259
+ var high = Ys[0] < Ys[1] ? 0 : 1;
2260
+ high = Ys[high] < Ys[2] ? high : 2;
2261
+ var low = Ys[0] > Ys[1] ? 0 : 1;
2262
+ low = Ys[low] > Ys[2] ? low : 2;
2263
+ var mid = 3 - low - high;
2264
+
2265
+ if(high != low) {
2266
+ var x0 = Xs[low];
2267
+ var z0 = Zs[low];
2268
+ var th0 = THs[low];
2269
+ var tv0 = TVs[low];
2270
+ var n0 = Ns[low] * 255;
2271
+ var dy0 = Ys[low] - Ys[high];
2272
+ dy0 = dy0 != 0 ? dy0 : 1;
2273
+ var xStep0 = (Xs[low] - Xs[high]) / dy0;
2274
+ var zStep0 = (Zs[low] - Zs[high]) / dy0;
2275
+ var thStep0 = (THs[low] - THs[high]) / dy0;
2276
+ var tvStep0 = (TVs[low] - TVs[high]) / dy0;
2277
+ var nStep0 = (Ns[low] - Ns[high]) * 255 / dy0;
2278
+
2279
+ var x1 = Xs[low];
2280
+ var z1 = Zs[low];
2281
+ var th1 = THs[low];
2282
+ var tv1 = TVs[low];
2283
+ var n1 = Ns[low] * 255;
2284
+ var dy1 = Ys[low] - Ys[mid];
2285
+ dy1 = dy1 != 0 ? dy1 : 1;
2286
+ var xStep1 = (Xs[low] - Xs[mid]) / dy1;
2287
+ var zStep1 = (Zs[low] - Zs[mid]) / dy1;
2288
+ var thStep1 = (THs[low] - THs[mid]) / dy1;
2289
+ var tvStep1 = (TVs[low] - TVs[mid]) / dy1;
2290
+ var nStep1 = (Ns[low] - Ns[mid]) * 255 / dy1;
2291
+
2292
+ var x2 = Xs[mid];
2293
+ var z2 = Zs[mid];
2294
+ var th2 = THs[mid];
2295
+ var tv2 = TVs[mid];
2296
+ var n2 = Ns[mid] * 255;
2297
+ var dy2 = Ys[mid] - Ys[high];
2298
+ dy2 = dy2 != 0 ? dy2 : 1;
2299
+ var xStep2 = (Xs[mid] - Xs[high]) / dy2;
2300
+ var zStep2 = (Zs[mid] - Zs[high]) / dy2;
2301
+ var thStep2 = (THs[mid] - THs[high]) / dy2;
2302
+ var tvStep2 = (TVs[mid] - TVs[high]) / dy2;
2303
+ var nStep2 = (Ns[mid] - Ns[high]) * 255 / dy2;
2304
+
2305
+ var linebase = Ys[low] * w;
2306
+ for(var y=Ys[low]; y>Ys[high]; y--) {
2307
+ if(y >=0 && y < h) {
2308
+ var xLeft = ~~x0;
2309
+ var zLeft = z0;
2310
+ var thLeft = th0;
2311
+ var tvLeft = tv0;
2312
+ var nLeft = n0;
2313
+ var xRight, zRight, thRight, tvRight, nRight;
2314
+ if(y > Ys[mid]) {
2315
+ xRight = ~~x1;
2316
+ zRight = z1;
2317
+ thRight = th1;
2318
+ tvRight = tv1;
2319
+ nRight = n1;
2320
+ }
2321
+ else {
2322
+ xRight = ~~x2;
2323
+ zRight = z2;
2324
+ thRight = th2;
2325
+ tvRight = tv2;
2326
+ nRight = n2;
2327
+ }
2328
+
2329
+ if(xLeft > xRight) {
2330
+ var temp;
2331
+ temp = xLeft;
2332
+ xLeft = xRight;
2333
+ xRight = temp;
2334
+ temp = zLeft;
2335
+ zLeft = zRight;
2336
+ zRight = temp;
2337
+ temp = thLeft;
2338
+ thLeft = thRight;
2339
+ thRight = temp;
2340
+ temp = tvLeft;
2341
+ tvLeft = tvRight;
2342
+ tvRight = temp;
2343
+ temp = nLeft;
2344
+ nLeft = nRight;
2345
+ nRight = temp;
2346
+ }
2347
+
2348
+ var zInc = (xLeft != xRight) ? ((zRight - zLeft) / (xRight - xLeft)) : 1;
2349
+ var thInc = (xLeft != xRight) ? ((thRight - thLeft) / (xRight - xLeft)) : 1;
2350
+ var tvInc = (xLeft != xRight) ? ((tvRight - tvLeft) / (xRight - xLeft)) : 1;
2351
+ var nInc = (xLeft != xRight) ? ((nRight - nLeft) / (xRight - xLeft)) : 0;
2352
+
2353
+ if(xLeft < 0) {
2354
+ zLeft -= xLeft * zInc;
2355
+ thLeft -= xLeft * thInc;
2356
+ tvLeft -= xLeft * tvInc;
2357
+ nLeft -= xLeft * nInc;
2358
+ xLeft = 0;
2359
+ }
2360
+ if(xRight >= w)
2361
+ xRight = w - 1;
2362
+
2363
+ var pix = linebase + xLeft;
2364
+ if(isOpaque) {
2365
+ for(var x=xLeft, z=zLeft, n=nLeft, th=thLeft, tv=tvLeft; x<=xRight; x++, z+=zInc, n+=nInc, th+=thInc, tv+=tvInc) {
2366
+ if(z > zbuf[pix]) {
2367
+ zbuf[pix] = z;
2368
+ var color = palette[n > 0 ? (~~n) : 0];
2369
+ var texel = tdata[(tv & tbound) * tdim + (th & tbound)];
2370
+ var rr = (((color & 0xff0000) >> 16) * ((texel & 0xff0000) >> 8));
2371
+ var gg = (((color & 0xff00) >> 8) * ((texel & 0xff00) >> 8));
2372
+ var bb = ((color & 0xff) * (texel & 0xff)) >> 8;
2373
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
2374
+ sbuf[pix] = id;
2375
+ }
2376
+ pix++;
2377
+ }
2378
+ }
2379
+ else {
2380
+ for(var x=xLeft, z=zLeft, n=nLeft, th=thLeft, tv=tvLeft; x<xRight; x++, z+=zInc, n+=nInc, th+=thInc, tv+=tvInc) {
2381
+ if(z > zbuf[pix]) {
2382
+ var color = palette[n > 0 ? (~~n) : 0];
2383
+ var foreColor = tdata[(tv & tbound) * tdim + (th & tbound)];
2384
+ var backColor = cbuf[pix];
2385
+ var opaci = (((foreColor >> 24) & 0xff) * (matOpacity & 0xff)) >> 8;
2386
+ var rr = (((color & 0xff0000) >> 16) * ((foreColor & 0xff0000) >> 8));
2387
+ var gg = (((color & 0xff00) >> 8) * ((foreColor & 0xff00) >> 8));
2388
+ var bb = ((color & 0xff) * (foreColor & 0xff)) >> 8;
2389
+ if(opaci > 250) {
2390
+ zbuf[pix] = z;
2391
+ }
2392
+ else {
2393
+ var trans = 255 - opaci;
2394
+ rr = (rr * opaci + (backColor & 0xff0000) * trans) >> 8;
2395
+ gg = (gg * opaci + (backColor & 0xff00) * trans) >> 8;
2396
+ bb = (bb * opaci + (backColor & 0xff) * trans) >> 8;
2397
+ }
2398
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
2399
+ sbuf[pix] = id;
2400
+ }
2401
+ pix++;
2402
+ }
2403
+ }
2404
+ }
2405
+
2406
+ // step up to next scanline
2407
+ //
2408
+ x0 -= xStep0;
2409
+ z0 -= zStep0;
2410
+ th0 -= thStep0;
2411
+ tv0 -= tvStep0;
2412
+ n0 -= nStep0;
2413
+ if(y > Ys[mid]) {
2414
+ x1 -= xStep1;
2415
+ z1 -= zStep1;
2416
+ th1 -= thStep1;
2417
+ tv1 -= tvStep1;
2418
+ n1 -= nStep1;
2419
+ }
2420
+ else {
2421
+ x2 -= xStep2;
2422
+ z2 -= zStep2;
2423
+ th2 -= thStep2;
2424
+ tv2 -= tvStep2;
2425
+ n2 -= nStep2;
2426
+ }
2427
+ linebase -= w;
2428
+ }
2429
+ }
2430
+
2431
+ i1 = i2;
2432
+ v1 = v2;
2433
+ t1 = t2;
2434
+ } while (ibuf[j] != -1);
2435
+
2436
+ j++;
2437
+ }
2438
+ }
2439
+ };
2440
+
2441
+ /**
2442
+ Render the given mesh as solid object with sphere mapping. Lighting will be calculated per vertex and then inerpolated between and inside scanlines.
2443
+ @private
2444
+ */
2445
+ JSC3D.Viewer.prototype.renderSolidSphereMapped = function(mesh) {
2446
+ var w = this.frameWidth;
2447
+ var h = this.frameHeight;
2448
+ var ibuf = mesh.indexBuffer;
2449
+ var vbuf = mesh.transformedVertexBuffer;
2450
+ var vnbuf = mesh.transformedVertexNormalBuffer;
2451
+ var fnbuf = mesh.transformedFaceNormalZBuffer;
2452
+ var cbuf = this.colorBuffer;
2453
+ var zbuf = this.zBuffer;
2454
+ var sbuf = this.selectionBuffer;
2455
+ var numOfFaces = mesh.faceCount;
2456
+ var numOfVertices = vbuf.length / 3;
2457
+ var id = mesh.internalId;
2458
+ var material = mesh.material ? mesh.material : this.defaultMaterial;
2459
+ var palette = material.getPalette();
2460
+ var sphereMap = this.sphereMap;
2461
+ var sdata = sphereMap.data;
2462
+ var sdim = sphereMap.width;
2463
+ var sbound = sdim - 1;
2464
+ var isOpaque = material.transparency == 0;
2465
+ var trans = material.transparency * 255;
2466
+ var opaci = 255 - trans;
2467
+
2468
+ // skip this mesh if it is completely transparent
2469
+ if(material.transparency == 1)
2470
+ return;
2471
+
2472
+ if(!vnbuf || vnbuf.length < numOfVertices * 3) {
2473
+ mesh.transformedVertexNormalBuffer = new Array(numOfVertices * 3);
2474
+ vnbuf = mesh.transformedVertexNormalBuffer;
2475
+ }
2476
+
2477
+ if(!fnbuf || fnbuf.length < numOfFaces) {
2478
+ mesh.transformedFaceNormalZBuffer = new Array(numOfFaces);
2479
+ fnbuf = mesh.transformedFaceNormalZBuffer;
2480
+ }
2481
+
2482
+ JSC3D.Math3D.transformVectors(this.rotMatrix, mesh.vertexNormalBuffer, vnbuf);
2483
+ JSC3D.Math3D.transformVectorZs(this.rotMatrix, mesh.faceNormalBuffer, fnbuf);
2484
+
2485
+ var isDoubleSided = mesh.isDoubleSided;
2486
+
2487
+ var Xs = new Array(3);
2488
+ var Ys = new Array(3);
2489
+ var Zs = new Array(3);
2490
+ var NXs = new Array(3);
2491
+ var NYs = new Array(3);
2492
+ var NZs = new Array(3);
2493
+ var i = 0, j = 0;
2494
+ while(i < numOfFaces) {
2495
+ var xformedFNz = fnbuf[i++];
2496
+ if(isDoubleSided)
2497
+ xformedFNz = xformedFNz > 0 ? xformedFNz : -xformedFNz;
2498
+ if(xformedFNz < 0) {
2499
+ do {
2500
+ } while (ibuf[j++] != -1);
2501
+ }
2502
+ else {
2503
+ var v0, v1, v2;
2504
+ v0 = ibuf[j++] * 3;
2505
+ v1 = ibuf[j++] * 3;
2506
+
2507
+ do {
2508
+ v2 = ibuf[j++] * 3;
2509
+
2510
+ Xs[0] = ~~(vbuf[v0] + 0.5);
2511
+ Ys[0] = ~~(vbuf[v0 + 1] + 0.5);
2512
+ Zs[0] = vbuf[v0 + 2];
2513
+ Xs[1] = ~~(vbuf[v1] + 0.5);
2514
+ Ys[1] = ~~(vbuf[v1 + 1] + 0.5);
2515
+ Zs[1] = vbuf[v1 + 2];
2516
+ Xs[2] = ~~(vbuf[v2] + 0.5);
2517
+ Ys[2] = ~~(vbuf[v2 + 1] + 0.5);
2518
+ Zs[2] = vbuf[v2 + 2];
2519
+
2520
+ NXs[0] = vnbuf[v0];
2521
+ NYs[0] = vnbuf[v0 + 1];
2522
+ NZs[0] = vnbuf[v0 + 2];
2523
+ NXs[1] = vnbuf[v1];
2524
+ NYs[1] = vnbuf[v1 + 1];
2525
+ NZs[1] = vnbuf[v1 + 2];
2526
+ NXs[2] = vnbuf[v2];
2527
+ NYs[2] = vnbuf[v2 + 1];
2528
+ NZs[2] = vnbuf[v2 + 2];
2529
+ if(isDoubleSided) {
2530
+ if(NZs[0] < 0)
2531
+ NZs[0] = -NZs[0];
2532
+ if(NZs[1] < 0)
2533
+ NZs[1] = -NZs[1];
2534
+ if(NZs[2] < 0)
2535
+ NZs[2] = -NZs[2];
2536
+ }
2537
+
2538
+ var high = Ys[0] < Ys[1] ? 0 : 1;
2539
+ high = Ys[high] < Ys[2] ? high : 2;
2540
+ var low = Ys[0] > Ys[1] ? 0 : 1;
2541
+ low = Ys[low] > Ys[2] ? low : 2;
2542
+ var mid = 3 - low - high;
2543
+
2544
+ if(high != low) {
2545
+ var x0 = Xs[low];
2546
+ var z0 = Zs[low];
2547
+ var n0 = NZs[low] * 255;
2548
+ var sh0 = ((NXs[low] / 2 + 0.5) * sdim) & sbound;
2549
+ var sv0 = ((0.5 - NYs[low] / 2) * sdim) & sbound;
2550
+ var dy0 = Ys[low] - Ys[high];
2551
+ dy0 = dy0 != 0 ? dy0 : 1;
2552
+ var xStep0 = (Xs[low] - Xs[high]) / dy0;
2553
+ var zStep0 = (Zs[low] - Zs[high]) / dy0;
2554
+ var nStep0 = (NZs[low] - NZs[high]) * 255 / dy0;
2555
+ var shStep0 = (((NXs[low] - NXs[high]) / 2) * sdim) / dy0;
2556
+ var svStep0 = (((NYs[high] - NYs[low]) / 2) * sdim) / dy0;
2557
+
2558
+ var x1 = Xs[low];
2559
+ var z1 = Zs[low];
2560
+ var n1 = NZs[low] * 255;
2561
+ var sh1 = ((NXs[low] / 2 + 0.5) * sdim) & sbound;
2562
+ var sv1 = ((0.5 - NYs[low] / 2) * sdim) & sbound;
2563
+ var dy1 = Ys[low] - Ys[mid];
2564
+ dy1 = dy1 != 0 ? dy1 : 1;
2565
+ var xStep1 = (Xs[low] - Xs[mid]) / dy1;
2566
+ var zStep1 = (Zs[low] - Zs[mid]) / dy1;
2567
+ var nStep1 = (NZs[low] - NZs[mid]) * 255 / dy1;
2568
+ var shStep1 = (((NXs[low] - NXs[mid]) / 2) * sdim) / dy1;
2569
+ var svStep1 = (((NYs[mid] - NYs[low]) / 2) * sdim) / dy1;
2570
+
2571
+ var x2 = Xs[mid];
2572
+ var z2 = Zs[mid];
2573
+ var n2 = NZs[mid] * 255;
2574
+ var sh2 = ((NXs[mid] / 2 + 0.5) * sdim) & sbound;
2575
+ var sv2 = ((0.5 - NYs[mid] / 2) * sdim) & sbound;
2576
+ var dy2 = Ys[mid] - Ys[high];
2577
+ dy2 = dy2 != 0 ? dy2 : 1;
2578
+ var xStep2 = (Xs[mid] - Xs[high]) / dy2;
2579
+ var zStep2 = (Zs[mid] - Zs[high]) / dy2;
2580
+ var nStep2 = (NZs[mid] - NZs[high]) * 255 / dy2;
2581
+ var shStep2 = (((NXs[mid] - NXs[high]) / 2) * sdim) / dy2;
2582
+ var svStep2 = (((NYs[high] - NYs[mid]) / 2) * sdim) / dy2;
2583
+
2584
+ var linebase = Ys[low] * w;
2585
+ for(var y=Ys[low]; y>Ys[high]; y--) {
2586
+ if(y >=0 && y < h) {
2587
+ var xLeft = ~~x0;
2588
+ var zLeft = z0;
2589
+ var nLeft = n0;
2590
+ var shLeft = sh0;
2591
+ var svLeft = sv0;
2592
+ var xRight, zRight, nRight, shRight, svRight;
2593
+ if(y > Ys[mid]) {
2594
+ xRight = ~~x1;
2595
+ zRight = z1;
2596
+ nRight = n1;
2597
+ shRight = sh1;
2598
+ svRight = sv1;
2599
+ }
2600
+ else {
2601
+ xRight = ~~x2;
2602
+ zRight = z2;
2603
+ nRight = n2;
2604
+ shRight = sh2;
2605
+ svRight = sv2;
2606
+ }
2607
+
2608
+ if(xLeft > xRight) {
2609
+ var temp;
2610
+ temp = xLeft;
2611
+ xLeft = xRight;
2612
+ xRight = temp;
2613
+ temp = zLeft;
2614
+ zLeft = zRight;
2615
+ zRight = temp;
2616
+ temp = nLeft;
2617
+ nLeft = nRight;
2618
+ nRight = temp;
2619
+ temp = shLeft;
2620
+ shLeft = shRight;
2621
+ shRight = temp;
2622
+ temp = svLeft;
2623
+ svLeft = svRight;
2624
+ svRight = temp;
2625
+ }
2626
+
2627
+ var zInc = (xLeft != xRight) ? ((zRight - zLeft) / (xRight - xLeft)) : 1;
2628
+ var nInc = (xLeft != xRight) ? ((nRight - nLeft) / (xRight - xLeft)) : 1;
2629
+ var shInc = (xLeft != xRight) ? ((shRight - shLeft) / (xRight - xLeft)) : 1;
2630
+ var svInc = (xLeft != xRight) ? ((svRight - svLeft) / (xRight - xLeft)) : 1;
2631
+ if(xLeft < 0) {
2632
+ zLeft -= xLeft * zInc;
2633
+ nLeft -= xLeft * nInc;
2634
+ shLeft -= shLeft * shInc;
2635
+ svLeft -= svLeft * svInc;
2636
+ xLeft = 0;
2637
+ }
2638
+ if(xRight >= w) {
2639
+ xRight = w - 1;
2640
+ }
2641
+ var pix = linebase + xLeft;
2642
+ if(isOpaque) {
2643
+ for(var x=xLeft, z=zLeft, n=nLeft, sh=shLeft, sv=svLeft; x<=xRight; x++, z+=zInc, n+=nInc, sh+=shInc, sv+=svInc) {
2644
+ if(z > zbuf[pix]) {
2645
+ zbuf[pix] = z;
2646
+ var color = palette[n > 0 ? (~~n) : 0];
2647
+ var stexel = sdata[(sv & sbound) * sdim + (sh & sbound)];
2648
+ var rr = (((color & 0xff0000) >> 16) * ((stexel & 0xff0000) >> 8));
2649
+ var gg = (((color & 0xff00) >> 8) * ((stexel & 0xff00) >> 8));
2650
+ var bb = ((color & 0xff) * (stexel & 0xff)) >> 8;
2651
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
2652
+ sbuf[pix] = id;
2653
+ }
2654
+ pix++;
2655
+ }
2656
+ }
2657
+ else {
2658
+ for(var x=xLeft, z=zLeft, n=nLeft, sh=shLeft, sv=svLeft; x<xRight; x++, z+=zInc, n+=nInc, sh+=shInc, sv+=svInc) {
2659
+ if(z > zbuf[pix]) {
2660
+ var color = palette[n > 0 ? (~~n) : 0];
2661
+ var foreColor = sdata[(sv & sbound) * sdim + (sh & sbound)];
2662
+ var backColor = cbuf[pix];
2663
+ var rr = (((color & 0xff0000) >> 16) * ((foreColor & 0xff0000) >> 8));
2664
+ var gg = (((color & 0xff00) >> 8) * ((foreColor & 0xff00) >> 8));
2665
+ var bb = ((color & 0xff) * (foreColor & 0xff)) >> 8;
2666
+ rr = (rr * opaci + (backColor & 0xff0000) * trans) >> 8;
2667
+ gg = (gg * opaci + (backColor & 0xff00) * trans) >> 8;
2668
+ bb = (bb * opaci + (backColor & 0xff) * trans) >> 8;
2669
+ cbuf[pix] = (rr & 0xff0000) | (gg & 0xff00) | (bb & 0xff);
2670
+ sbuf[pix] = id;
2671
+ }
2672
+ pix++;
2673
+ }
2674
+ }
2675
+ }
2676
+
2677
+ // step up to next scanline
2678
+ //
2679
+ x0 -= xStep0;
2680
+ z0 -= zStep0;
2681
+ n0 -= nStep0;
2682
+ sh0 -= shStep0;
2683
+ sv0 -= svStep0;
2684
+ if(y > Ys[mid]) {
2685
+ x1 -= xStep1;
2686
+ z1 -= zStep1;
2687
+ n1 -= nStep1;
2688
+ sh1 -= shStep1;
2689
+ sv1 -= svStep1;
2690
+ }
2691
+ else {
2692
+ x2 -= xStep2;
2693
+ z2 -= zStep2;
2694
+ n2 -= nStep2;
2695
+ sh2 -= shStep2;
2696
+ sv2 -= svStep2;
2697
+ }
2698
+ linebase -= w;
2699
+ }
2700
+ }
2701
+
2702
+ v1 = v2;
2703
+ } while (ibuf[j] != -1);
2704
+
2705
+ j++;
2706
+ }
2707
+ }
2708
+ };
2709
+
2710
+ JSC3D.Viewer.prototype.params = null;
2711
+ JSC3D.Viewer.prototype.canvas = null;
2712
+ JSC3D.Viewer.prototype.ctx = null;
2713
+ JSC3D.Viewer.prototype.canvasData = null;
2714
+ JSC3D.Viewer.prototype.bkgColorBuffer = null;
2715
+ JSC3D.Viewer.prototype.colorBuffer = null;
2716
+ JSC3D.Viewer.prototype.zBuffer = null;
2717
+ JSC3D.Viewer.prototype.selectionBuffer = null;
2718
+ JSC3D.Viewer.prototype.frameWidth = 0;
2719
+ JSC3D.Viewer.prototype.frameHeight = 0;
2720
+ JSC3D.Viewer.prototype.scene = null;
2721
+ JSC3D.Viewer.prototype.defaultMaterial = null;
2722
+ JSC3D.Viewer.prototype.sphereMap = null;
2723
+ JSC3D.Viewer.prototype.isLoaded = false;
2724
+ JSC3D.Viewer.prototype.isFailed = false;
2725
+ JSC3D.Viewer.prototype.errorMsg = '';
2726
+ JSC3D.Viewer.prototype.initRotX = 0;
2727
+ JSC3D.Viewer.prototype.initRotY = 0;
2728
+ JSC3D.Viewer.prototype.initRotZ = 0;
2729
+ JSC3D.Viewer.prototype.zoomFactor = 1;
2730
+ JSC3D.Viewer.prototype.rotMatrix = null;
2731
+ JSC3D.Viewer.prototype.transformMatrix = null;
2732
+ JSC3D.Viewer.prototype.sceneUrl = '';
2733
+ JSC3D.Viewer.prototype.modelColor = 0xcaa618;
2734
+ JSC3D.Viewer.prototype.bkgColor1 = 0xffffff;
2735
+ JSC3D.Viewer.prototype.bkgColor2 = 0xffff80;
2736
+ JSC3D.Viewer.prototype.renderMode = 'flat';
2737
+ JSC3D.Viewer.prototype.definition = 'standard';
2738
+ JSC3D.Viewer.prototype.isMipMappingOn = false;
2739
+ JSC3D.Viewer.prototype.sphereMapUrl = '';
2740
+ JSC3D.Viewer.prototype.buttonStates = null;
2741
+ JSC3D.Viewer.prototype.keyStates = null;
2742
+ JSC3D.Viewer.prototype.mouseX = 0;
2743
+ JSC3D.Viewer.prototype.mouseY = 0;
2744
+ JSC3D.Viewer.prototype.onmousedown = null;
2745
+ JSC3D.Viewer.prototype.onmouseup = null;
2746
+ JSC3D.Viewer.prototype.onmousemove = null;
2747
+ JSC3D.Viewer.prototype.beforeupdate = null;
2748
+ JSC3D.Viewer.prototype.afterupdate = null;
2749
+ JSC3D.Viewer.prototype.mouseUsage = 'default';
2750
+ JSC3D.Viewer.prototype.isDefaultInputHandlerEnabled = false;
2751
+
2752
+
2753
+ /**
2754
+ @class PickInfo
2755
+
2756
+ PickInfo is used as the return value of JSC3D.Viewer's pick() method, holding picking values at a given position
2757
+ on the canvas.
2758
+ */
2759
+ JSC3D.PickInfo = function() {
2760
+ this.canvasX = 0;
2761
+ this.canvasY = 0;
2762
+ this.depth = -Infinity;
2763
+ this.mesh = null;
2764
+ };
2765
+
2766
+
2767
+ /**
2768
+ @class Scene
2769
+
2770
+ This class implements scene that contains a group of meshes that forms the world.
2771
+ */
2772
+ JSC3D.Scene = function(name) {
2773
+ this.name = name || '';
2774
+ this.aabb = null;
2775
+ this.children = [];
2776
+ this.maxChildId = 1;
2777
+ };
2778
+
2779
+ /**
2780
+ Initialize the scene.
2781
+ */
2782
+ JSC3D.Scene.prototype.init = function() {
2783
+ if(this.isEmpty())
2784
+ return;
2785
+
2786
+ for(var i=0; i<this.children.length; i++)
2787
+ this.children[i].init();
2788
+
2789
+ if(!this.aabb) {
2790
+ this.aabb = new JSC3D.AABB;
2791
+ this.calcAABB();
2792
+ }
2793
+ };
2794
+
2795
+ /**
2796
+ See if the scene is empty.
2797
+ @returns {boolean} true if it contains no meshes; false if it has any.
2798
+ */
2799
+ JSC3D.Scene.prototype.isEmpty = function() {
2800
+ return (this.children.length == 0);
2801
+ };
2802
+
2803
+ /**
2804
+ Add a mesh to the scene.
2805
+ @param {JSC3D.Mesh} mesh the mesh to be added.
2806
+ */
2807
+ JSC3D.Scene.prototype.addChild = function(mesh) {
2808
+ mesh.internalId = this.maxChildId++;
2809
+ this.children.push(mesh);
2810
+ };
2811
+
2812
+ /**
2813
+ Remove a mesh from the scene.
2814
+ @param {JSC3D.Mesh} mesh the mesh to be added.
2815
+ */
2816
+ JSC3D.Scene.prototype.removeChild = function(mesh) {
2817
+ for(var i=0; i<this.children.length; i++) {
2818
+ if(this.children[i] == mesh) {
2819
+ this.children.splice(i, 1);
2820
+ break;
2821
+ }
2822
+ }
2823
+ };
2824
+
2825
+ /**
2826
+ Get all meshes in the scene.
2827
+ @returns {Array} meshes as an array.
2828
+ */
2829
+ JSC3D.Scene.prototype.getChildren = function() {
2830
+ return this.children;
2831
+ };
2832
+
2833
+ /**
2834
+ Calculate AABB of the scene.
2835
+ @private
2836
+ */
2837
+ JSC3D.Scene.prototype.calcAABB = function() {
2838
+ this.aabb.minX = this.aabb.minY = this.aabb.minZ = Number.MAX_VALUE;
2839
+ this.aabb.maxX = this.aabb.maxY = this.aabb.maxZ = -Number.MAX_VALUE;
2840
+ for(var i=0; i<this.children.length; i++) {
2841
+ var child = this.children[i];
2842
+ if(!child.isTrivial()) {
2843
+ var minX = child.aabb.minX;
2844
+ var minY = child.aabb.minY;
2845
+ var minZ = child.aabb.minZ;
2846
+ var maxX = child.aabb.maxX;
2847
+ var maxY = child.aabb.maxY;
2848
+ var maxZ = child.aabb.maxZ;
2849
+ if(this.aabb.minX > minX)
2850
+ this.aabb.minX = minX;
2851
+ if(this.aabb.minY > minY)
2852
+ this.aabb.minY = minY;
2853
+ if(this.aabb.minZ > minZ)
2854
+ this.aabb.minZ = minZ;
2855
+ if(this.aabb.maxX < maxX)
2856
+ this.aabb.maxX = maxX;
2857
+ if(this.aabb.maxY < maxY)
2858
+ this.aabb.maxY = maxY;
2859
+ if(this.aabb.maxZ < maxZ)
2860
+ this.aabb.maxZ = maxZ;
2861
+ }
2862
+ }
2863
+ };
2864
+
2865
+ JSC3D.Scene.prototype.name = '';
2866
+ JSC3D.Scene.prototype.aabb = null;
2867
+ JSC3D.Scene.prototype.children = null;
2868
+ JSC3D.Scene.prototype.maxChildId = 1;
2869
+
2870
+
2871
+ /**
2872
+ @class Mesh
2873
+
2874
+ This class implements mesh that is used as an expression of 3D object and the basic primitive for rendering. <br />
2875
+ A mesh basically consists of a sequence of faces, and optioanlly a material, a texture mapping and other attributes and metadata.<br />
2876
+ A face consists of 3 or more coplanary vertex that should be descript in counter-clockwise order.<br />
2877
+ A texture mapping includes a valid texture object with a sequence of texture coordinats specified per vertex.<br />
2878
+ */
2879
+ JSC3D.Mesh = function(name, visible, material, texture, isDoubleSided, isEnvironmentCast, coordBuffer, indexBuffer, texCoordBuffer, texCoordIndexBuffer) {
2880
+ this.name = name || '';
2881
+ this.metadata = '';
2882
+ this.visible = (visible != undefined) ? visible : true;
2883
+ this.aabb = null;
2884
+ this.vertexBuffer = coordBuffer || null;
2885
+ this.indexBuffer = indexBuffer || null;
2886
+ this.vertexNormalBuffer = null;
2887
+ this.faceNormalBuffer = null;
2888
+ this.material = material || null;
2889
+ this.texture = texture || null;
2890
+ this.faceCount = 0;
2891
+ this.isDoubleSided = isDoubleSided || false;
2892
+ this.isEnvironmentCast = isEnvironmentCast || false;
2893
+ this.internalId = 0;
2894
+ this.texCoordBuffer = texCoordBuffer || null;
2895
+ this.texCoordIndexBuffer = texCoordIndexBuffer || null;
2896
+ this.transformedVertexBuffer = null;
2897
+ this.transformedVertexNormalZBuffer = null;
2898
+ this.transformedFaceNormalZBuffer = null;
2899
+ this.transformedVertexNormalBuffer = null;
2900
+ };
2901
+
2902
+ /**
2903
+ Initialize the mesh.
2904
+ */
2905
+ JSC3D.Mesh.prototype.init = function() {
2906
+ if(this.isTrivial()) {
2907
+ return;
2908
+ }
2909
+
2910
+ if(this.faceCount == 0) {
2911
+ this.calcFaceCount();
2912
+ if(this.faceCount == 0)
2913
+ return;
2914
+ }
2915
+
2916
+ if(!this.aabb) {
2917
+ this.aabb = new JSC3D.AABB;
2918
+ this.calcAABB();
2919
+ }
2920
+
2921
+ if(!this.faceNormalBuffer) {
2922
+ this.faceNormalBuffer = new Array(this.faceCount * 3);
2923
+ this.calcFaceNormals();
2924
+ }
2925
+
2926
+ if(!this.vertexNormalBuffer) {
2927
+ this.vertexNormalBuffer = new Array(this.vertexBuffer.length);
2928
+ this.calcVertexNormals();
2929
+ }
2930
+
2931
+ this.normalizeFaceNormals();
2932
+
2933
+ this.transformedVertexBuffer = new Array(this.vertexBuffer.length);
2934
+ };
2935
+
2936
+ /**
2937
+ See if the mesh is a trivial mesh. A trivial mesh should be omited in any calculations and rendering.
2938
+ @returns {boolean} true if it is trivial; false if not.
2939
+ */
2940
+ JSC3D.Mesh.prototype.isTrivial = function() {
2941
+ return ( !this.vertexBuffer || this.vertexBuffer.length < 3 ||
2942
+ !this.indexBuffer || this.indexBuffer.length < 3 );
2943
+ };
2944
+
2945
+ /**
2946
+ Set material for the mesh.
2947
+ @param {JSC3D.Material} material the material object.
2948
+ */
2949
+ JSC3D.Mesh.prototype.setMaterial = function(material) {
2950
+ this.material = material;
2951
+ };
2952
+
2953
+ /**
2954
+ Set texture for the mesh.
2955
+ @param {JSC3D.Texture} texture the texture object.
2956
+ */
2957
+ JSC3D.Mesh.prototype.setTexture = function(texture) {
2958
+ this.texture = texture;
2959
+ };
2960
+
2961
+ /**
2962
+ See if the mesh has valid texture mapping.
2963
+ @returns {boolean} true if it has valid texture mapping; false if not.
2964
+ */
2965
+ JSC3D.Mesh.prototype.hasTexture = function() {
2966
+ return ( (this.texCoordBuffer != null) && (this.texCoordBuffer.length >= 2) &&
2967
+ (this.texCoordIndexBuffer != null) && (this.texCoordIndexBuffer.length >= 3) &&
2968
+ (this.texCoordIndexBuffer.length >= this.indexBuffer.length) &&
2969
+ (this.texture != null) && this.texture.hasData() );
2970
+ };
2971
+
2972
+ /**
2973
+ Calculate count of faces.
2974
+ @private
2975
+ */
2976
+ JSC3D.Mesh.prototype.calcFaceCount = function() {
2977
+ this.faceCount = 0;
2978
+
2979
+ var ibuf = this.indexBuffer;
2980
+ if(ibuf[ibuf.length - 1] != -1)
2981
+ ibuf.push(-1);
2982
+
2983
+ for(var i=0; i<ibuf.length; i++) {
2984
+ if(ibuf[i] == -1)
2985
+ this.faceCount++;
2986
+ }
2987
+ };
2988
+
2989
+ /**
2990
+ Calculate AABB of the mesh.
2991
+ @private
2992
+ */
2993
+ JSC3D.Mesh.prototype.calcAABB = function() {
2994
+ var minX = minY = minZ = Number.MAX_VALUE;
2995
+ var maxX = maxY = maxZ = -Number.MAX_VALUE;
2996
+
2997
+ var vbuf = this.vertexBuffer;
2998
+ for(var i=0; i<vbuf.length; i+=3) {
2999
+ var x = vbuf[i];
3000
+ var y = vbuf[i + 1];
3001
+ var z = vbuf[i + 2];
3002
+
3003
+ if(x < minX)
3004
+ minX = x;
3005
+ if(x > maxX)
3006
+ maxX = x;
3007
+ if(y < minY)
3008
+ minY = y;
3009
+ if(y > maxY)
3010
+ maxY = y;
3011
+ if(z < minZ)
3012
+ minZ = z;
3013
+ if(z > maxZ)
3014
+ maxZ = z;
3015
+ }
3016
+
3017
+ this.aabb.minX = minX;
3018
+ this.aabb.minY = minY;
3019
+ this.aabb.minZ = minZ;
3020
+ this.aabb.maxX = maxX;
3021
+ this.aabb.maxY = maxY;
3022
+ this.aabb.maxZ = maxZ;
3023
+ };
3024
+
3025
+ /**
3026
+ Calculate per face normals. The reault remain un-normalized for later vertex normal calculations.
3027
+ @private
3028
+ */
3029
+ JSC3D.Mesh.prototype.calcFaceNormals = function() {
3030
+ var vbuf = this.vertexBuffer;
3031
+ var ibuf = this.indexBuffer;
3032
+ var nbuf = this.faceNormalBuffer;
3033
+ var i = 0, j = 0;
3034
+ while(i < ibuf.length) {
3035
+ var index = ibuf[i++] * 3;
3036
+ var x0 = vbuf[index];
3037
+ var y0 = vbuf[index + 1];
3038
+ var z0 = vbuf[index + 2];
3039
+
3040
+ index = ibuf[i++] * 3;
3041
+ var x1 = vbuf[index];
3042
+ var y1 = vbuf[index + 1];
3043
+ var z1 = vbuf[index + 2];
3044
+
3045
+ index = ibuf[i++] * 3;
3046
+ var x2 = vbuf[index];
3047
+ var y2 = vbuf[index + 1];
3048
+ var z2 = vbuf[index + 2];
3049
+
3050
+ var dx1 = x1 - x0;
3051
+ var dy1 = y1 - y0;
3052
+ var dz1 = z1 - z0;
3053
+ var dx2 = x2 - x0;
3054
+ var dy2 = y2 - y0;
3055
+ var dz2 = z2 - z0;
3056
+
3057
+ var nx = dy1 * dz2 - dz1 * dy2;
3058
+ var ny = dz1 * dx2 - dx1 * dz2;
3059
+ var nz = dx1 * dy2 - dy1 * dx2;
3060
+
3061
+ nbuf[j++] = nx;
3062
+ nbuf[j++] = ny;
3063
+ nbuf[j++] = nz;
3064
+
3065
+ do {
3066
+ } while (ibuf[i++] != -1);
3067
+ }
3068
+ };
3069
+
3070
+ /**
3071
+ Calculate per vertex normals.
3072
+ @private
3073
+ */
3074
+ JSC3D.Mesh.prototype.calcVertexNormals = function() {
3075
+ if(!this.faceNormalBuffer) {
3076
+ this.faceNormalBuffer = new Array(this.faceCount * 3);
3077
+ this.calcFaceNormals();
3078
+ }
3079
+
3080
+ var vbuf = this.vertexBuffer;
3081
+ var ibuf = this.indexBuffer;
3082
+ var fnbuf = this.faceNormalBuffer;
3083
+ var vnbuf = this.vertexNormalBuffer;
3084
+ for(var i=0; i<vnbuf.length; i++) {
3085
+ vnbuf[i] = 0;
3086
+ }
3087
+
3088
+ var numOfVertices = vbuf.length / 3;
3089
+
3090
+ var i = 0, j = 0, k = 0;
3091
+ while(i < ibuf.length) {
3092
+ k = ibuf[i++];
3093
+ if(k == -1) {
3094
+ j += 3;
3095
+ }
3096
+ else {
3097
+ var index = k * 3;
3098
+ vnbuf[index ] += fnbuf[j];
3099
+ vnbuf[index + 1] += fnbuf[j + 1];
3100
+ vnbuf[index + 2] += fnbuf[j + 2];
3101
+ }
3102
+ }
3103
+
3104
+ for(var i=0, j=0; i<vnbuf.length; i+=3, j++) {
3105
+ var nx = vnbuf[i];
3106
+ var ny = vnbuf[i + 1];
3107
+ var nz = vnbuf[i + 2];
3108
+ var len = Math.sqrt(nx * nx + ny * ny + nz * nz);
3109
+ if(len > 0) {
3110
+ nx /= len;
3111
+ ny /= len;
3112
+ nz /= len;
3113
+ }
3114
+
3115
+ vnbuf[i ] = nx;
3116
+ vnbuf[i + 1] = ny;
3117
+ vnbuf[i + 2] = nz;
3118
+ }
3119
+ };
3120
+
3121
+ /**
3122
+ Normalize face normals.
3123
+ @private
3124
+ */
3125
+ JSC3D.Mesh.prototype.normalizeFaceNormals = function() {
3126
+ var nbuf = this.faceNormalBuffer;
3127
+
3128
+ for(var i=0; i<nbuf.length; i+=3) {
3129
+ var nx = nbuf[i];
3130
+ var ny = nbuf[i + 1];
3131
+ var nz = nbuf[i + 2];
3132
+ var len = Math.sqrt(nx * nx + ny * ny + nz * nz);
3133
+ if(len > 0) {
3134
+ nx /= len;
3135
+ ny /= len;
3136
+ nz /= len;
3137
+ }
3138
+
3139
+ nbuf[i ] = nx;
3140
+ nbuf[i + 1] = ny;
3141
+ nbuf[i + 2] = nz;
3142
+ }
3143
+ };
3144
+
3145
+ JSC3D.Mesh.prototype.checkValid = function() {
3146
+ //TODO: not implemented yet
3147
+ };
3148
+
3149
+ JSC3D.Mesh.prototype.name = '';
3150
+ JSC3D.Mesh.prototype.metadata = '';
3151
+ JSC3D.Mesh.prototype.visible = false;
3152
+ JSC3D.Mesh.prototype.aabb = null;
3153
+ JSC3D.Mesh.prototype.vertexBuffer = null;
3154
+ JSC3D.Mesh.prototype.indexBuffer = null;
3155
+ JSC3D.Mesh.prototype.vertexNormalBuffer = null;
3156
+ JSC3D.Mesh.prototype.faceNormalBuffer = null;
3157
+ JSC3D.Mesh.prototype.texCoordBuffer = null;
3158
+ JSC3D.Mesh.prototype.texCoordIndexBuffer = null;
3159
+ JSC3D.Mesh.prototype.material = null;
3160
+ JSC3D.Mesh.prototype.texture = null;
3161
+ JSC3D.Mesh.prototype.faceCount = 0;
3162
+ JSC3D.Mesh.prototype.isDoubleSided = false;
3163
+ JSC3D.Mesh.prototype.isEnvironmentCast = false;
3164
+ JSC3D.Mesh.prototype.internalId = 0;
3165
+ JSC3D.Mesh.prototype.transformedVertexBuffer = null;
3166
+ JSC3D.Mesh.prototype.transformedVertexNormalZBuffer = null;
3167
+ JSC3D.Mesh.prototype.transformedFaceNormalZBuffer = null;
3168
+ JSC3D.Mesh.prototype.transformedVertexNormalBuffer = null;
3169
+
3170
+
3171
+ /**
3172
+ @class Material
3173
+
3174
+ This class implements material which describes the feel and look of a mesh.
3175
+ */
3176
+ JSC3D.Material = function(name, ambientColor, diffuseColor, transparency, simulateSpecular) {
3177
+ this.name = name || '';
3178
+ this.ambientColor = ambientColor || 0;
3179
+ this.diffuseColor = diffuseColor || 0x7f7f7f;
3180
+ this.transparency = transparency || 0;
3181
+ this.simulateSpecular = simulateSpecular || false;
3182
+ this.palette = null;
3183
+ };
3184
+
3185
+ /**
3186
+ Get the palette of the material used for shadings.
3187
+ @return {Array} palette of the material as an array.
3188
+ */
3189
+ JSC3D.Material.prototype.getPalette = function() {
3190
+ if(!this.palette) {
3191
+ this.palette = new Array(256);
3192
+ this.generatePalette();
3193
+ }
3194
+
3195
+ return this.palette;
3196
+ };
3197
+
3198
+ /**
3199
+ @private
3200
+ */
3201
+ JSC3D.Material.prototype.generatePalette = function() {
3202
+ var ambientR = (this.ambientColor & 0xff0000) >> 16;
3203
+ var ambientG = (this.ambientColor & 0xff00) >> 8;
3204
+ var ambientB = this.ambientColor & 0xff;
3205
+ var diffuseR = (this.diffuseColor & 0xff0000) >> 16;
3206
+ var diffuseG = (this.diffuseColor & 0xff00) >> 8;
3207
+ var diffuseB = this.diffuseColor & 0xff;
3208
+
3209
+ if(this.simulateSpecular) {
3210
+ var i = 0;
3211
+ while(i < 204) {
3212
+ var r = ambientR + i * diffuseR / 204;
3213
+ var g = ambientG + i * diffuseG / 204;
3214
+ var b = ambientB + i * diffuseB / 204;
3215
+ if(r > 255)
3216
+ r = 255;
3217
+ if(g > 255)
3218
+ g = 255;
3219
+ if(b > 255)
3220
+ b = 255;
3221
+
3222
+ this.palette[i++] = r << 16 | g << 8 | b;
3223
+ }
3224
+
3225
+ while(i < 256) {
3226
+ var r = ambientR + diffuseR + (i - 204) * (255 - diffuseR) / 82;
3227
+ var g = ambientG + diffuseG + (i - 204) * (255 - diffuseG) / 82;
3228
+ var b = ambientB + diffuseB + (i - 204) * (255 - diffuseB) / 82;
3229
+ if(r > 255)
3230
+ r = 255;
3231
+ if(g > 255)
3232
+ g = 255;
3233
+ if(b > 255)
3234
+ b = 255;
3235
+
3236
+ this.palette[i++] = r << 16 | g << 8 | b;
3237
+ }
3238
+ }
3239
+ else {
3240
+ var i = 0;
3241
+ while(i < 256) {
3242
+ var r = ambientR + i * diffuseR / 256;
3243
+ var g = ambientG + i * diffuseG / 256;
3244
+ var b = ambientB + i * diffuseB / 256;
3245
+ if(r > 255)
3246
+ r = 255;
3247
+ if(g > 255)
3248
+ g = 255;
3249
+ if(b > 255)
3250
+ b = 255;
3251
+
3252
+ this.palette[i++] = r << 16 | g << 8 | b;
3253
+ }
3254
+ }
3255
+ };
3256
+
3257
+ JSC3D.Material.prototype.name = '';
3258
+ JSC3D.Material.prototype.ambientColor = 0;
3259
+ JSC3D.Material.prototype.diffuseColor = 0x7f7f7f;
3260
+ JSC3D.Material.prototype.transparency = 0;
3261
+ JSC3D.Material.prototype.simulateSpecular = false;
3262
+ JSC3D.Material.prototype.palette = null;
3263
+
3264
+
3265
+ /**
3266
+ @class Texture
3267
+
3268
+ This class implements texture which describes the surface details for a mesh.
3269
+ */
3270
+ JSC3D.Texture = function(name, onready) {
3271
+ this.name = name || '';
3272
+ this.width = 0;
3273
+ this.height = 0;
3274
+ this.data = null;
3275
+ this.mipmaps = null;
3276
+ this.mipentries = null;
3277
+ this.hasTransparency = false;
3278
+ this.srcUrl = '';
3279
+ this.onready = (onready && typeof(onready) == 'function') ? onready : null;
3280
+ };
3281
+
3282
+ /**
3283
+ Load an image and extract texture data from it.
3284
+ @param {string} imageUrl where to load the image.
3285
+ @param {boolean} useMipmap set true to generate mip-maps; false(default) not to generate mip-maps.
3286
+ */
3287
+ JSC3D.Texture.prototype.createFromUrl = function(imageUrl, useMipmap) {
3288
+ var self = this;
3289
+ var img = new Image;
3290
+
3291
+ img.onload = function() {
3292
+ self.data = null;
3293
+ self.mipmaps = null;
3294
+ self.mipentries = null;
3295
+ self.width = 0;
3296
+ self.height = 0;
3297
+ self.hasTransparency = false;
3298
+ self.srcUrl = '';
3299
+ self.createFromImage(this, useMipmap);
3300
+ };
3301
+
3302
+ img.onerror = function() {
3303
+ self.data = null;
3304
+ self.mipmaps = null;
3305
+ self.mipentries = null;
3306
+ self.width = 0;
3307
+ self.height = 0;
3308
+ self.hasTransparency = false;
3309
+ self.srcUrl = '';
3310
+ };
3311
+
3312
+ img.src = imageUrl;
3313
+ };
3314
+
3315
+ /**
3316
+ Extract texture data from an exsisting image.
3317
+ @param {Image} image image as datasource of the texture.
3318
+ @param {boolean} useMipmap set true to generate mip-maps; false(default) not to generate mip-maps.
3319
+ */
3320
+ JSC3D.Texture.prototype.createFromImage = function(image, useMipmap) {
3321
+ if(image.width <=0 || image.height <=0)
3322
+ return;
3323
+
3324
+ if(JSC3D.Texture.cv_is_locked) {
3325
+ var self = this;
3326
+ setTimeout( function() {
3327
+ self.createFromImage(image);
3328
+ }, JSC3D.Texture.cv_lock_cycle );
3329
+
3330
+ return;
3331
+ }
3332
+
3333
+ var cv = JSC3D.Texture.cv;
3334
+ if(!cv) {
3335
+ try {
3336
+ cv = document.createElement('canvas');
3337
+ JSC3D.Texture.cv = cv;
3338
+ }
3339
+ catch(e) {
3340
+ return;
3341
+ }
3342
+ }
3343
+
3344
+ var dim = image.width > image.height ? image.width : image.height;
3345
+ if(dim <= 32)
3346
+ dim = 32;
3347
+ else if(dim <= 64)
3348
+ dim = 64;
3349
+ else if(dim <= 128)
3350
+ dim = 128;
3351
+ //else if(dim <= 256)
3352
+ // dim = 256;
3353
+ //else
3354
+ // dim = 512;
3355
+ else
3356
+ dim = 256;
3357
+
3358
+ try {
3359
+ JSC3D.Texture.cv_is_locked = true;
3360
+
3361
+ var self = this;
3362
+
3363
+ if(cv.width != dim || cv.height != dim)
3364
+ cv.width = cv.height = dim;
3365
+
3366
+ setTimeout( function() {
3367
+ var ctx = cv.getContext('2d');
3368
+ ctx.drawImage(image, 0, 0, dim, dim);
3369
+
3370
+ setTimeout( function() {
3371
+ var imgData = ctx.getImageData(0, 0, dim, dim);
3372
+ var data = imgData.data;
3373
+
3374
+ var size = data.length / 4;
3375
+ self.data = new Array(size);
3376
+ var alpha;
3377
+ for(var i=0, j=0; i<size; i++, j+=4) {
3378
+ alpha = data[j + 3];
3379
+ self.data[i] = alpha << 24 | data[j] << 16 | data[j+1] << 8 | data[j+2];
3380
+ if(alpha < 250)
3381
+ self.hasTransparency = true;
3382
+ }
3383
+
3384
+ self.width = dim;
3385
+ self.height = dim;
3386
+
3387
+ cv.width = 1;
3388
+ cv.height = 1;
3389
+
3390
+ JSC3D.Texture.cv_is_locked = false;
3391
+
3392
+ self.mipmaps = null;
3393
+ if(useMipmap)
3394
+ self.generateMipmaps();
3395
+
3396
+ self.srcUrl = image.src;
3397
+
3398
+ if(self.onready != null && (typeof self.onready) == 'function')
3399
+ self.onready();
3400
+
3401
+ }, JSC3D.Texture.cv_lock_cycle );
3402
+
3403
+ }, JSC3D.Texture.cv_lock_cycle );
3404
+ }
3405
+ catch(e) {
3406
+ JSC3D.Texture.cv_is_locked = false;
3407
+ return;
3408
+ }
3409
+ };
3410
+
3411
+ /**
3412
+ See if this texture contains texel data.
3413
+ @returns {boolean} true if it has texel data; false if not.
3414
+ */
3415
+ JSC3D.Texture.prototype.hasData = function() {
3416
+ return (this.data != null);
3417
+ };
3418
+
3419
+ /**
3420
+ Generate mip-maps for the texture.
3421
+ */
3422
+ JSC3D.Texture.prototype.generateMipmaps = function() {
3423
+ if(this.width <= 1 || this.data == null || this.mipmaps != null)
3424
+ return;
3425
+
3426
+ this.mipmaps = [this.data];
3427
+ this.mipentries = [1];
3428
+
3429
+ var numOfMipLevels = 1 + ~~(0.1 + Math.log(this.width) * Math.LOG2E);
3430
+ var dim = this.width >> 1;
3431
+ for(var level=1; level<numOfMipLevels; level++) {
3432
+ var map = new Array(dim * dim);
3433
+ var uppermap = this.mipmaps[level - 1];
3434
+ var upperdim = dim << 1;
3435
+
3436
+ var src = 0, dest = 0;
3437
+ for(var i=0; i<dim; i++) {
3438
+ for(var j=0; j<dim; j++) {
3439
+ var texel0 = uppermap[src];
3440
+ var texel1 = uppermap[src + 1];
3441
+ var texel2 = uppermap[src + upperdim];
3442
+ var texel3 = uppermap[src + upperdim + 1];
3443
+ var a = ( ((texel0 & 0xff000000) >>> 2) + ((texel1 & 0xff000000) >>> 2) + ((texel2 & 0xff000000) >>> 2) + ((texel3 & 0xff000000) >>> 2) ) & 0xff000000;
3444
+ var r = ( ((texel0 & 0xff0000) + (texel1 & 0xff0000) + (texel2 & 0xff0000) + (texel3 & 0xff0000)) >> 2 ) & 0xff0000;
3445
+ var g = ( ((texel0 & 0xff00) + (texel1 & 0xff00) + (texel2 & 0xff00) + (texel3 & 0xff00)) >> 2 ) & 0xff00;
3446
+ var b = ( ((texel0 & 0xff) + (texel1 & 0xff) + (texel2 & 0xff) + (texel3 & 0xff)) >> 2 ) & 0xff;
3447
+ map[dest] = a + r + g + b;
3448
+ src += 2;
3449
+ dest++;
3450
+ }
3451
+ src += upperdim;
3452
+ }
3453
+
3454
+ this.mipmaps.push(map);
3455
+ this.mipentries.push(Math.pow(4, level));
3456
+ dim = dim >> 1;
3457
+ }
3458
+ };
3459
+
3460
+ /**
3461
+ See if this texture has mip-maps.
3462
+ @returns {boolean} true if it has mip-maps; false if not.
3463
+ */
3464
+ JSC3D.Texture.prototype.hasMipmap = function() {
3465
+ return (this.mipmaps != null);
3466
+ };
3467
+
3468
+ JSC3D.Texture.prototype.name = '';
3469
+ JSC3D.Texture.prototype.data = null;
3470
+ JSC3D.Texture.prototype.mipmaps = null;
3471
+ JSC3D.Texture.prototype.mipentries = null;
3472
+ JSC3D.Texture.prototype.width = 0;
3473
+ JSC3D.Texture.prototype.height = 0;
3474
+ JSC3D.Texture.prototype.hasTransparency = false;
3475
+ JSC3D.Texture.prototype.srcUrl = '';
3476
+ JSC3D.Texture.prototype.onready = null;
3477
+ JSC3D.Texture.cv = null;
3478
+ JSC3D.Texture.cv_is_locked = false;
3479
+ JSC3D.Texture.cv_lock_cycle = 100; // milliseconds
3480
+
3481
+
3482
+ /**
3483
+ @class AABB
3484
+
3485
+ This class implements the Axis-Aligned Bounding Box to measure spacial enclosure.
3486
+ */
3487
+ JSC3D.AABB = function() {
3488
+ this.minX = this.maxX = 0;
3489
+ this.minY = this.maxY = 0;
3490
+ this.minZ = this.maxZ = 0;
3491
+ };
3492
+
3493
+ /**
3494
+ Get the center coordinates of the AABB.
3495
+ @returns {Array} center coordinates as an array.
3496
+ */
3497
+ JSC3D.AABB.prototype.center = function() {
3498
+ return [(this.minX + this.maxX) / 2, (this.minY + this.maxY) / 2, (this.minZ + this.maxZ) / 2];
3499
+ };
3500
+
3501
+ /**
3502
+ Get the length of the diagonal of the AABB.
3503
+ @returns {float} length of the diagonal.
3504
+ */
3505
+ JSC3D.AABB.prototype.lengthOfDiagonal = function() {
3506
+ var xx = this.maxX - this.minX;
3507
+ var yy = this.maxY - this.minY;
3508
+ var zz = this.maxZ - this.minZ;
3509
+ return Math.sqrt(xx * xx + yy * yy + zz * zz);
3510
+ };
3511
+
3512
+
3513
+ /**
3514
+ @class Matrix3x4
3515
+
3516
+ This class implements 3x4 matrix and mass operations for 3D transformations.
3517
+ */
3518
+ JSC3D.Matrix3x4 = function() {
3519
+ this.m00 = 1; this.m01 = 0; this.m02 = 0; this.m03 = 0;
3520
+ this.m10 = 0; this.m11 = 1; this.m12 = 0; this.m13 = 0;
3521
+ this.m20 = 0; this.m21 = 0; this.m22 = 1; this.m23 = 0;
3522
+ };
3523
+
3524
+ /**
3525
+ Make the matrix an identical matrix.
3526
+ */
3527
+ JSC3D.Matrix3x4.prototype.identity = function() {
3528
+ this.m00 = 1; this.m01 = 0; this.m02 = 0; this.m03 = 0;
3529
+ this.m10 = 0; this.m11 = 1; this.m12 = 0; this.m13 = 0;
3530
+ this.m20 = 0; this.m21 = 0; this.m22 = 1; this.m23 = 0;
3531
+ };
3532
+
3533
+ /**
3534
+ Scale the matrix using scaling factors on each axial directions.
3535
+ @param {float} sx scaling factors on x-axis.
3536
+ @param {float} sy scaling factors on y-axis.
3537
+ @param {float} sz scaling factors on z-axis.
3538
+ */
3539
+ JSC3D.Matrix3x4.prototype.scale = function(sx, sy, sz) {
3540
+ this.m00 *= sx; this.m01 *= sx; this.m02 *= sx; this.m03 *= sx;
3541
+ this.m10 *= sy; this.m11 *= sy; this.m12 *= sy; this.m13 *= sy;
3542
+ this.m20 *= sz; this.m21 *= sz; this.m22 *= sz; this.m23 *= sz;
3543
+ };
3544
+
3545
+ /**
3546
+ Translate the matrix using translations on each axial directions.
3547
+ @param {float} tx translations on x-axis.
3548
+ @param {float} ty translations on y-axis.
3549
+ @param {float} tz translations on z-axis.
3550
+ */
3551
+ JSC3D.Matrix3x4.prototype.translate = function(tx, ty, tz) {
3552
+ this.m03 += tx;
3553
+ this.m13 += ty;
3554
+ this.m23 += tz;
3555
+ };
3556
+
3557
+ /**
3558
+ Rotate the matrix an arbitrary angle about the x-axis.
3559
+ @param {float} angle rotation angle in degrees.
3560
+ */
3561
+ JSC3D.Matrix3x4.prototype.rotateAboutXAxis = function(angle) {
3562
+ if(angle != 0) {
3563
+ angle *= Math.PI / 180;
3564
+ var cosA = Math.cos(angle);
3565
+ var sinA = Math.sin(angle);
3566
+
3567
+ var m10 = cosA * this.m10 - sinA * this.m20;
3568
+ var m11 = cosA * this.m11 - sinA * this.m21;
3569
+ var m12 = cosA * this.m12 - sinA * this.m22;
3570
+ var m13 = cosA * this.m13 - sinA * this.m23;
3571
+ var m20 = cosA * this.m20 + sinA * this.m10;
3572
+ var m21 = cosA * this.m21 + sinA * this.m11;
3573
+ var m22 = cosA * this.m22 + sinA * this.m12;
3574
+ var m23 = cosA * this.m23 + sinA * this.m13;
3575
+
3576
+ this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m13 = m13;
3577
+ this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
3578
+ }
3579
+ };
3580
+
3581
+ /**
3582
+ Rotate the matrix an arbitrary angle about the y-axis.
3583
+ @param {float} angle rotation angle in degrees.
3584
+ */
3585
+ JSC3D.Matrix3x4.prototype.rotateAboutYAxis = function(angle) {
3586
+ if(angle != 0) {
3587
+ angle *= Math.PI / 180;
3588
+ var cosA = Math.cos(angle);
3589
+ var sinA = Math.sin(angle);
3590
+
3591
+ var m00 = cosA * this.m00 + sinA * this.m20;
3592
+ var m01 = cosA * this.m01 + sinA * this.m21;
3593
+ var m02 = cosA * this.m02 + sinA * this.m22;
3594
+ var m03 = cosA * this.m03 + sinA * this.m23;
3595
+ var m20 = cosA * this.m20 - sinA * this.m00;
3596
+ var m21 = cosA * this.m21 - sinA * this.m01;
3597
+ var m22 = cosA * this.m22 - sinA * this.m02;
3598
+ var m23 = cosA * this.m23 - sinA * this.m03;
3599
+
3600
+ this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
3601
+ this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
3602
+ }
3603
+ };
3604
+
3605
+ /**
3606
+ Rotate the matrix an arbitrary angle about the z-axis.
3607
+ @param {float} angle rotation angle in degrees.
3608
+ */
3609
+ JSC3D.Matrix3x4.prototype.rotateAboutZAxis = function(angle) {
3610
+ if(angle != 0) {
3611
+ angle *= Math.PI / 180;
3612
+ var cosA = Math.cos(angle);
3613
+ var sinA = Math.sin(angle);
3614
+
3615
+ var m10 = cosA * this.m10 + sinA * this.m00;
3616
+ var m11 = cosA * this.m11 + sinA * this.m01;
3617
+ var m12 = cosA * this.m12 + sinA * this.m02;
3618
+ var m13 = cosA * this.m13 + sinA * this.m03;
3619
+ var m00 = cosA * this.m00 - sinA * this.m10;
3620
+ var m01 = cosA * this.m01 - sinA * this.m11;
3621
+ var m02 = cosA * this.m02 - sinA * this.m12;
3622
+ var m03 = cosA * this.m03 - sinA * this.m13;
3623
+
3624
+ this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
3625
+ this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m13 = m13;
3626
+ }
3627
+ };
3628
+
3629
+ /**
3630
+ Multiply the matrix by another matrix.
3631
+ @param {JSC3D.Matrix3x4} mult another matrix to be multiplied on this.
3632
+ */
3633
+ JSC3D.Matrix3x4.prototype.multiply = function(mult) {
3634
+ var m00 = mult.m00 * this.m00 + mult.m01 * this.m10 + mult.m02 * this.m20;
3635
+ var m01 = mult.m00 * this.m01 + mult.m01 * this.m11 + mult.m02 * this.m21;
3636
+ var m02 = mult.m00 * this.m02 + mult.m01 * this.m12 + mult.m02 * this.m22;
3637
+ var m03 = mult.m00 * this.m03 + mult.m01 * this.m13 + mult.m02 * this.m23 + mult.m03;
3638
+ var m10 = mult.m10 * this.m00 + mult.m11 * this.m10 + mult.m12 * this.m20;
3639
+ var m11 = mult.m10 * this.m01 + mult.m11 * this.m11 + mult.m12 * this.m21;
3640
+ var m12 = mult.m10 * this.m02 + mult.m11 * this.m12 + mult.m12 * this.m22;
3641
+ var m13 = mult.m10 * this.m03 + mult.m11 * this.m13 + mult.m12 * this.m23 + mult.m13;
3642
+ var m20 = mult.m20 * this.m00 + mult.m21 * this.m10 + mult.m22 * this.m20;
3643
+ var m21 = mult.m20 * this.m01 + mult.m21 * this.m11 + mult.m22 * this.m21;
3644
+ var m22 = mult.m20 * this.m02 + mult.m21 * this.m12 + mult.m22 * this.m22;
3645
+ var m23 = mult.m20 * this.m03 + mult.m21 * this.m13 + mult.m22 * this.m23 + mult.m23;
3646
+
3647
+ this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
3648
+ this.m10 = m10; this.m11 = m11; this.m12 = m12; this.m13 = m13;
3649
+ this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
3650
+ };
3651
+
3652
+
3653
+ /**
3654
+ @class Math3D
3655
+
3656
+ This class provides some utility methods for 3D mathematics.
3657
+ */
3658
+ JSC3D.Math3D = {
3659
+
3660
+ /**
3661
+ Transform vectors using the given matrix.
3662
+ @param {JSC3D.Matrix3x4} mat the transformation matrix.
3663
+ @param {Array} vecs a batch of vectors to be transform.
3664
+ @param {Array} xfvecs holds the transformed vetors.
3665
+ */
3666
+ transformVectors: function(mat, vecs, xfvecs) {
3667
+ for(var i=0; i<vecs.length; i+=3) {
3668
+ var x = vecs[i];
3669
+ var y = vecs[i + 1];
3670
+ var z = vecs[i + 2];
3671
+ xfvecs[i] = mat.m00 * x + mat.m01 * y + mat.m02 * z + mat.m03;
3672
+ xfvecs[i + 1] = mat.m10 * x + mat.m11 * y + mat.m12 * z + mat.m13;
3673
+ xfvecs[i + 2] = mat.m20 * x + mat.m21 * y + mat.m22 * z + mat.m23;
3674
+ }
3675
+ },
3676
+
3677
+ /**
3678
+ Transform vectors' z components using the given matrix.
3679
+ @param {JSC3D.Matrix3x4} mat the transformation matrix.
3680
+ @param {Array} vecs a batch of vectors to be transform.
3681
+ @param {Array} xfveczs holds the transformed z components of the input vectors.
3682
+ */
3683
+ transformVectorZs: function(mat, vecs, xfveczs) {
3684
+ var num = vecs.length / 3;
3685
+ var i = 0, j = 0
3686
+ while(i < num) {
3687
+ xfveczs[i] = mat.m20 * vecs[j] + mat.m21 * vecs[j + 1] + mat.m22 * vecs[j + 2] + mat.m23;
3688
+ i++;
3689
+ j += 3;
3690
+ }
3691
+ }
3692
+ };
3693
+
3694
+
3695
+ /**
3696
+ @class LoaderSelector
3697
+ */
3698
+ JSC3D.LoaderSelector = {
3699
+
3700
+ /**
3701
+ Register a scene loader for a specific file format, using the file extesion name for lookup.
3702
+ @param {string} fileExtName extension name for the specific file format.
3703
+ @param {Function} loaderCtor constructor of the loader class.
3704
+ */
3705
+ registerLoader: function(fileExtName, loaderCtor) {
3706
+ if((typeof loaderCtor) == 'function') {
3707
+ JSC3D.LoaderSelector.loaderTable[fileExtName] = loaderCtor;
3708
+ }
3709
+ },
3710
+
3711
+ /**
3712
+ Get the proper loader for a target file format using the file extension name.
3713
+ @param {string} fileExtName file extension name for the specific format.
3714
+ @returns {object} loader object for the specific format; null if not found.
3715
+ */
3716
+ getLoader: function(fileExtName) {
3717
+ var loaderCtor = JSC3D.LoaderSelector.loaderTable[fileExtName.toLowerCase()];
3718
+ if(!loaderCtor)
3719
+ return null;
3720
+
3721
+ var loaderInst;
3722
+ try {
3723
+ loaderInst = new loaderCtor();
3724
+ }
3725
+ catch(e) {
3726
+ loaderInst = null;
3727
+ }
3728
+
3729
+ return loaderInst;
3730
+ },
3731
+
3732
+ loaderTable: {}
3733
+ };
3734
+
3735
+
3736
+ /**
3737
+ @class ObjLoader
3738
+
3739
+ This class implements a scene loader from a wavefront obj file.
3740
+ */
3741
+ JSC3D.ObjLoader = function(onload, onerror, onprogress, onresource) {
3742
+ this.onload = (onload && typeof(onload) == 'function') ? onload : null;
3743
+ this.onerror = (onerror && typeof(onerror) == 'function') ? onerror : null;
3744
+ this.onprogress = (onprogress && typeof(onprogress) == 'function') ? onprogress : null;
3745
+ this.onresource = (onresource && typeof(onresource) == 'function') ? onresource : null;
3746
+ this.requestCount = 0;
3747
+ };
3748
+
3749
+ /**
3750
+ Load scene from a given obj file.
3751
+ @param {string} urlName a string that specifies where to fetch the obj file.
3752
+ */
3753
+ JSC3D.ObjLoader.prototype.loadFromUrl = function(urlName) {
3754
+ var urlPath = '';
3755
+ var fileName = urlName;
3756
+
3757
+ var lastSlashAt = urlName.lastIndexOf('/');
3758
+ if(lastSlashAt == -1)
3759
+ lastSlashAt = urlName.lastIndexOf('\\');
3760
+ if(lastSlashAt != -1) {
3761
+ urlPath = urlName.substring(0, lastSlashAt+1);
3762
+ fileName = urlName.substring(lastSlashAt+1);
3763
+ }
3764
+
3765
+ this.requestCount = 0;
3766
+ this.loadObjFile(urlPath, fileName);
3767
+ };
3768
+
3769
+ /**
3770
+ Load scene from the obj file using the given url path and file name.
3771
+ @private
3772
+ */
3773
+ JSC3D.ObjLoader.prototype.loadObjFile = function(urlPath, fileName) {
3774
+ var urlName = urlPath + fileName;
3775
+ var self = this;
3776
+ var xhr = window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject('MSXML2.XMLHTTP.3.0');
3777
+ xhr.open('GET', urlName, true);
3778
+
3779
+ xhr.onreadystatechange = function() {
3780
+ if(this.readyState == 4) {
3781
+ if(this.status == 200 || this.status == 0) {
3782
+ if(self.onload) {
3783
+ if(self.onprogress)
3784
+ self.onprogress('Loading obj file ...', 1);
3785
+ var scene = new JSC3D.Scene;
3786
+ var mtllibs = self.parseObj(scene, this.responseText);
3787
+ if(mtllibs.length > 0) {
3788
+ for(var i=0; i<mtllibs.length; i++)
3789
+ self.loadMtlFile(scene, urlPath, mtllibs[i]);
3790
+ }
3791
+ if(--self.requestCount == 0)
3792
+ self.onload(scene);
3793
+ }
3794
+ }
3795
+ else if(self.onerror) {
3796
+ self.requestCount--;
3797
+ self.onerror('Failed to load obj file \'' + urlName + '\'.');
3798
+ }
3799
+ }
3800
+ };
3801
+
3802
+ if(this.onprogress && xhr.onprogress) {
3803
+ this.onprogress('Loading obj file ...', 0);
3804
+ xhr.onprogress = function(event) {
3805
+ self.onprogress('Loading obj file ...', event.position / event.totalSize);
3806
+ };
3807
+ }
3808
+
3809
+ this.requestCount++;
3810
+ xhr.send();
3811
+ };
3812
+
3813
+ /**
3814
+ Load materials and textures from an mtl file and set them to corresponding meshes.
3815
+ @private
3816
+ */
3817
+ JSC3D.ObjLoader.prototype.loadMtlFile = function(scene, urlPath, fileName) {
3818
+ var urlName = urlPath + fileName;
3819
+ var self = this;
3820
+ var xhr = window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject('MSXML2.XMLHTTP.3.0');
3821
+ xhr.open('GET', urlName, true);
3822
+
3823
+ xhr.onreadystatechange = function() {
3824
+ if(this.readyState == 4) {
3825
+ if(this.status == 200 || this.status == 0) {
3826
+ if(self.onprogress)
3827
+ self.onprogress('Loading mtl file ...', 1);
3828
+ var mtls = self.parseMtl(this.responseText);
3829
+ var textures = {};
3830
+ var meshes = scene.getChildren();
3831
+ for(var i=0; i<meshes.length; i++) {
3832
+ var mesh = meshes[i];
3833
+ if(mesh.mtl != null && mesh.mtllib != null && mesh.mtllib == fileName) {
3834
+ var mtl = mtls[mesh.mtl];
3835
+ if(mtl != null) {
3836
+ if(mtl.material != null)
3837
+ mesh.setMaterial(mtl.material);
3838
+ if(mtl.textureFileName != '') {
3839
+ if(!textures[mtl.textureFileName])
3840
+ textures[mtl.textureFileName] = [mesh];
3841
+ else
3842
+ textures[mtl.textureFileName].push(mesh);
3843
+ }
3844
+ }
3845
+ }
3846
+ }
3847
+ for(var textureFileName in textures)
3848
+ self.setupTexture(textures[textureFileName], urlPath + textureFileName);
3849
+ }
3850
+ else {
3851
+ //TODO: when failed to load an mtl file ...
3852
+ }
3853
+ if(--self.requestCount == 0)
3854
+ self.onload(scene);
3855
+ }
3856
+ };
3857
+
3858
+ if(this.onprogress && xhr.onprogress) {
3859
+ this.onprogress('Loading mtl file ...', 0);
3860
+ xhr.onprogress = function(event) {
3861
+ self.onprogress('Loading mtl file ...', event.position / event.totalSize);
3862
+ };
3863
+ }
3864
+
3865
+ this.requestCount++;
3866
+ xhr.send();
3867
+ };
3868
+
3869
+ /**
3870
+ Parse contents of the obj file, generating the scene and returning all required mtllibs.
3871
+ @private
3872
+ */
3873
+ JSC3D.ObjLoader.prototype.parseObj = function(scene, data) {
3874
+ var meshes = {};
3875
+ var mtllibs = [];
3876
+ var namePrefix = 'obj-';
3877
+ var meshIndex = 0;
3878
+ var curMesh = null;
3879
+ var curMtllibName = '';
3880
+ var curMtlName = '';
3881
+
3882
+ var tempVertexBuffer = []; // temporary buffer as container for all vertices
3883
+ var tempTexCoordBuffer = []; // temporary buffer as container for all vertex texture coords
3884
+
3885
+ // create a default mesh to hold all faces that are not associated with any mtl.
3886
+ var defaultMeshName = namePrefix + meshIndex++;
3887
+ var defaultMesh = new JSC3D.Mesh;
3888
+ defaultMesh.name = defaultMeshName;
3889
+ defaultMesh.indexBuffer = [];
3890
+ meshes['nomtl'] = defaultMesh;
3891
+ curMesh = defaultMesh;
3892
+
3893
+ var lines = data.split("\n");
3894
+ for(var i=0; i<lines.length; i++) {
3895
+ var line = lines[i];
3896
+ var tokens = line.split(/[ \t]+/);
3897
+ if(tokens.length > 0) {
3898
+ var keyword = tokens[0];
3899
+ switch(keyword) {
3900
+ case 'v':
3901
+ if(tokens.length > 3) {
3902
+ for(var j=1; j<4; j++) {
3903
+ tempVertexBuffer.push( parseFloat(tokens[j]) );
3904
+ }
3905
+ }
3906
+ break;
3907
+ case 'vn':
3908
+ // ignore vertex normals
3909
+ break;
3910
+ case 'vt':
3911
+ if(tokens.length > 2) {
3912
+ tempTexCoordBuffer.push( parseFloat(tokens[1]) );
3913
+ tempTexCoordBuffer.push( 1 - parseFloat(tokens[2]) );
3914
+ }
3915
+ break;
3916
+ case 'f':
3917
+ if(tokens.length > 3) {
3918
+ for(var j=1; j<tokens.length; j++) {
3919
+ var refs = tokens[j].split('/');
3920
+ curMesh.indexBuffer.push( parseInt(refs[0]) - 1 );
3921
+ if(refs.length > 1 && refs[1] != '') {
3922
+ if(!curMesh.texCoordIndexBuffer)
3923
+ curMesh.texCoordIndexBuffer = [];
3924
+ curMesh.texCoordIndexBuffer.push( parseInt(refs[1]) - 1 );
3925
+ }
3926
+ }
3927
+ curMesh.indexBuffer.push(-1); // mark the end of vertex index sequence for the face
3928
+ if(curMesh.texCoordIndexBuffer)
3929
+ curMesh.texCoordIndexBuffer.push(-1); // mark the end of vertex tex coord index sequence for the face
3930
+ }
3931
+ break;
3932
+ case 'mtllib':
3933
+ if(tokens.length > 1) {
3934
+ curMtllibName = tokens[1];
3935
+ mtllibs.push(curMtllibName);
3936
+ }
3937
+ else
3938
+ curMtllibName = '';
3939
+ break;
3940
+ case 'usemtl':
3941
+ if(tokens.length > 1 && tokens[1] != '' && curMtllibName != '') {
3942
+ curMtlName = tokens[1];
3943
+ var meshid = curMtllibName + '-' + curMtlName;
3944
+ var mesh = meshes[meshid];
3945
+ if(!mesh) {
3946
+ // create a new mesh to hold faces using the same mtl
3947
+ mesh = new JSC3D.Mesh;
3948
+ mesh.name = namePrefix + meshIndex++;
3949
+ mesh.indexBuffer = [];
3950
+ mesh.mtllib = curMtllibName;
3951
+ mesh.mtl = curMtlName;
3952
+ meshes[meshid] = mesh;
3953
+ }
3954
+ curMesh = mesh;
3955
+ }
3956
+ else {
3957
+ curMtlName = '';
3958
+ curMesh = defaultMesh;
3959
+ }
3960
+ break;
3961
+ case '#':
3962
+ // ignore comments
3963
+ default:
3964
+ break;
3965
+ }
3966
+ }
3967
+ }
3968
+
3969
+ var viBuffer = tempVertexBuffer.length >= 3 ? (new Array(tempVertexBuffer.length / 3)) : null;
3970
+ var tiBuffer = tempTexCoordBuffer.length >= 2 ? (new Array(tempTexCoordBuffer.length / 2)) : null;
3971
+
3972
+ for(var id in meshes) {
3973
+ var mesh = meshes[id];
3974
+
3975
+ // split vertices into the mesh, the indices are also re-calculated
3976
+ if(tempVertexBuffer.length >= 3 && mesh.indexBuffer.length > 0) {
3977
+ for(var i=0; i<viBuffer.length; i++)
3978
+ viBuffer[i] = -1;
3979
+
3980
+ mesh.vertexBuffer = [];
3981
+ var oldVI = 0, newVI = 0;
3982
+ for(var i=0; i<mesh.indexBuffer.length; i++) {
3983
+ oldVI = mesh.indexBuffer[i];
3984
+ if(oldVI != -1) {
3985
+ if(viBuffer[oldVI] == -1) {
3986
+ var v = oldVI * 3;
3987
+ mesh.vertexBuffer.push(tempVertexBuffer[v]);
3988
+ mesh.vertexBuffer.push(tempVertexBuffer[v + 1]);
3989
+ mesh.vertexBuffer.push(tempVertexBuffer[v + 2]);
3990
+ mesh.indexBuffer[i] = newVI;
3991
+ viBuffer[oldVI] = newVI;
3992
+ newVI++;
3993
+ }
3994
+ else {
3995
+ mesh.indexBuffer[i] = viBuffer[oldVI];
3996
+ }
3997
+ }
3998
+ }
3999
+ }
4000
+
4001
+ // split vertex texture coords into the mesh, the indices for tex coords are re-calculated as well
4002
+ if(tempTexCoordBuffer.length >= 2 && mesh.texCoordIndexBuffer != null && mesh.texCoordIndexBuffer.length > 0) {
4003
+ for(var i=0; i<tiBuffer.length; i++)
4004
+ tiBuffer[i] = -1;
4005
+
4006
+ mesh.texCoordBuffer = [];
4007
+ var oldTI = 0, newTI = 0;
4008
+ for(var i=0; i<mesh.texCoordIndexBuffer.length; i++) {
4009
+ oldTI = mesh.texCoordIndexBuffer[i];
4010
+ if(oldTI != -1) {
4011
+ if(tiBuffer[oldTI] == -1) {
4012
+ var t = oldTI * 2;
4013
+ mesh.texCoordBuffer.push(tempTexCoordBuffer[t]);
4014
+ mesh.texCoordBuffer.push(tempTexCoordBuffer[t + 1]);
4015
+ mesh.texCoordIndexBuffer[i] = newTI;
4016
+ tiBuffer[oldTI] = newTI;
4017
+ newTI++;
4018
+ }
4019
+ else {
4020
+ mesh.texCoordIndexBuffer[i] = tiBuffer[oldTI];
4021
+ }
4022
+ }
4023
+ }
4024
+ }
4025
+
4026
+ // add mesh to scene
4027
+ if(!mesh.isTrivial())
4028
+ scene.addChild(mesh);
4029
+ }
4030
+
4031
+ return mtllibs;
4032
+ };
4033
+
4034
+ /**
4035
+ Parse contents of an mtl file, returning all materials and textures defined in it.
4036
+ @private
4037
+ */
4038
+ JSC3D.ObjLoader.prototype.parseMtl = function(data) {
4039
+ var mtls = {};
4040
+ var curMtlName = '';
4041
+
4042
+ var lines = data.split("\n");
4043
+ for(var i=0; i<lines.length; i++) {
4044
+ var line = lines[i];
4045
+ var tokens = line.split(/[ \t]+/);
4046
+ if(tokens.length > 0) {
4047
+ var keyword = tokens[0];
4048
+ switch(keyword) {
4049
+ case 'newmtl':
4050
+ curMtlName = tokens[1];
4051
+ var mtl = {};
4052
+ mtl.material = new JSC3D.Material;
4053
+ mtl.textureFileName = '';
4054
+ mtls[curMtlName] = mtl;
4055
+ break;
4056
+ case 'Ka':
4057
+ /*
4058
+ if(tokens.length == 4 && !isNaN(tokens[1])) {
4059
+ var ambientR = (parseFloat(tokens[1]) * 255) & 0xff;
4060
+ var ambientG = (parseFloat(tokens[2]) * 255) & 0xff;
4061
+ var ambientB = (parseFloat(tokens[3]) * 255) & 0xff;
4062
+ var mtl = mtls[curMtlName];
4063
+ if(mtl != null)
4064
+ mtl.material.ambientColor = (ambientR << 16) | (ambientG << 8) | ambientB;
4065
+ }
4066
+ */
4067
+ break;
4068
+ case 'Kd':
4069
+ if(tokens.length == 4 && !isNaN(tokens[1])) {
4070
+ var diffuseR = (parseFloat(tokens[1]) * 255) & 0xff;
4071
+ var diffuseG = (parseFloat(tokens[2]) * 255) & 0xff;
4072
+ var diffuseB = (parseFloat(tokens[3]) * 255) & 0xff;
4073
+ var mtl = mtls[curMtlName];
4074
+ if(mtl != null)
4075
+ mtl.material.diffuseColor = (diffuseR << 16) | (diffuseG << 8) | diffuseB;
4076
+ }
4077
+ break;
4078
+ case 'Ks':
4079
+ // ignore specular reflectivity definition
4080
+ break;
4081
+ case 'd':
4082
+ if(tokens.length == 2 && !isNaN(tokens[1])) {
4083
+ var opacity = parseFloat(tokens[1]);
4084
+ var mtl = mtls[curMtlName];
4085
+ if(mtl != null)
4086
+ mtl.material.transparency = 1 - opacity;
4087
+ }
4088
+ break;
4089
+ case 'illum':
4090
+ /*
4091
+ if(tokens.length == 2 && tokens[1] == '2') {
4092
+ var mtl = mtls[curMtlName];
4093
+ if(mtl != null)
4094
+ mtl.material.simulateSpecular = true;
4095
+ }
4096
+ */
4097
+ break;
4098
+ case 'map_Kd':
4099
+ if(tokens.length == 2) {
4100
+ var texFileName = tokens[1];
4101
+ var mtl = mtls[curMtlName];
4102
+ if(mtl != null)
4103
+ mtl.textureFileName = texFileName;
4104
+ }
4105
+ break;
4106
+ case '#':
4107
+ // ignore any comments
4108
+ default:
4109
+ break;
4110
+ }
4111
+ }
4112
+ }
4113
+
4114
+ return mtls;
4115
+ };
4116
+
4117
+ /**
4118
+ Asynchronously load a texture from a given url and set it to corresponding meshes when done.
4119
+ @private
4120
+ */
4121
+ JSC3D.ObjLoader.prototype.setupTexture = function(meshList, textureUrlName) {
4122
+ var self = this;
4123
+ var texture = new JSC3D.Texture;
4124
+
4125
+ texture.onready = function() {
4126
+ for(var i=0; i<meshList.length; i++)
4127
+ meshList[i].setTexture(this);
4128
+ if(self.onresource)
4129
+ self.onresource(this);
4130
+ };
4131
+
4132
+ texture.createFromUrl(textureUrlName);
4133
+ };
4134
+
4135
+ JSC3D.ObjLoader.prototype.onload = null;
4136
+ JSC3D.ObjLoader.prototype.onerror = null;
4137
+ JSC3D.ObjLoader.prototype.onprogress = null;
4138
+ JSC3D.ObjLoader.prototype.onresource = null;
4139
+ JSC3D.ObjLoader.prototype.requestCount = 0;
4140
+
4141
+ JSC3D.LoaderSelector.registerLoader('obj', JSC3D.ObjLoader);
4142
+
4143
+
4144
+ /**
4145
+ @class StlLoader
4146
+
4147
+ This class implements a scene loader from an STL file. Both binary and ASCII STL files are supported.
4148
+ */
4149
+ JSC3D.StlLoader = function(onload, onerror, onprogress, onresource) {
4150
+ this.onload = (onload && typeof(onload) == 'function') ? onload : null;
4151
+ this.onerror = (onerror && typeof(onerror) == 'function') ? onerror : null;
4152
+ this.onprogress = (onprogress && typeof(onprogress) == 'function') ? onprogress : null;
4153
+ this.onresource = (onresource && typeof(onresource) == 'function') ? onresource : null;
4154
+ this.decimalPrecision = 3;
4155
+ };
4156
+
4157
+ /**
4158
+ Load scene from a given STL file.
4159
+ @param {string} urlName a string that specifies where to fetch the STL file.
4160
+ */
4161
+ JSC3D.StlLoader.prototype.loadFromUrl = function(urlName) {
4162
+ /*
4163
+ we assume this is an ASCII STL file and load it in that manner.
4164
+ if it is not, we load the same file a second time and treat it as a binary stream.
4165
+ */
4166
+
4167
+ var self = this;
4168
+ var xhr = window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject('MSXML2.XMLHTTP.3.0');
4169
+ xhr.open('GET', urlName, true);
4170
+
4171
+ xhr.onreadystatechange = function() {
4172
+ if(this.readyState == 4) {
4173
+ if(this.status == 200 || this.status == 0) {
4174
+ if(self.onload) {
4175
+ if(self.onprogress)
4176
+ self.onprogress('Loading stl file ...', 1);
4177
+ if(this.responseText.substring(0, 5).toLowerCase() == 'solid') {
4178
+ var scene = new JSC3D.Scene;
4179
+ self.parseStlAscii(scene, this.responseText);
4180
+ self.onload(scene);
4181
+ }
4182
+ else {
4183
+ // this is not an ASCII STL file, reload it as a binary STL file
4184
+ self.loadBinaryFromUrl(urlName);
4185
+ }
4186
+ }
4187
+ }
4188
+ else if(self.onerror) {
4189
+ self.onerror('Failed to load stl file \'' + urlName + '\'.');
4190
+ }
4191
+ }
4192
+ };
4193
+
4194
+ if(this.onprogress && xhr.onprogress) {
4195
+ this.onprogress('Loading stl file ...', 0);
4196
+ xhr.onprogress = function(event) {
4197
+ self.onprogress('Loading stl file ...', event.position / event.totalSize);
4198
+ };
4199
+ }
4200
+
4201
+ xhr.send();
4202
+ };
4203
+
4204
+ /**
4205
+ Load scene from a given binary STL file.
4206
+ @private
4207
+ */
4208
+ JSC3D.StlLoader.prototype.loadBinaryFromUrl = function(urlName) {
4209
+ var self = this;
4210
+ var xhr = window.XMLHttpRequest ? new XMLHttpRequest : new ActiveXObject('MSXML2.XMLHTTP.3.0');
4211
+ xhr.open('GET', urlName, true);
4212
+ xhr.setRequestHeader("Accept-Charset", "x-user-defined");
4213
+
4214
+ xhr.onreadystatechange = function() {
4215
+ if(this.readyState == 4) {
4216
+ if(this.status == 200 || this.status == 0) {
4217
+ if(self.onload) {
4218
+ if(self.onprogress)
4219
+ self.onprogress('Loading binary stl file ...', 1);
4220
+ var scene = new JSC3D.Scene;
4221
+ // Hack !!!!!!
4222
+ // As javascript cannot access contents of the XHR's responseBody property which hold the raw binary data returned by the request,
4223
+ // we need to generate and execute a vbscript function to extract those data to a javascript array before we can parse them.
4224
+ var data_convert_function =
4225
+ 'Function BinaryToArray(Binary)\n' +
4226
+ ' Dim length\n' +
4227
+ ' length = LenB(Binary)\n' +
4228
+ ' ReDim byteArray(length)\n' +
4229
+ ' Dim i\n' +
4230
+ ' For i = 1 To length\n' +
4231
+ ' byteArray(i-1) = AscB(MidB(Binary, i, 1))\n' +
4232
+ ' Next\n' +
4233
+ ' BinaryToArray = byteArray\n' +
4234
+ 'End Function';
4235
+ try {
4236
+ execScript(data_convert_function, 'vbscript');
4237
+ self.parseStlBinary(scene, BinaryToArray(this.responseBody).toArray());
4238
+ } catch(e) {}
4239
+ self.onload(scene);
4240
+ }
4241
+ }
4242
+ else if(self.onerror) {
4243
+ self.onerror('Failed to load binary stl file \'' + urlName + '\'.');
4244
+ }
4245
+ }
4246
+ };
4247
+
4248
+ if(this.onprogress && xhr.onprogress) {
4249
+ this.onprogress('Loading binary stl file ...', 0);
4250
+ xhr.onprogress = function(event) {
4251
+ self.onprogress('Loading binary stl file ...', event.position / event.totalSize);
4252
+ };
4253
+ }
4254
+
4255
+ xhr.send();
4256
+ };
4257
+
4258
+ /**
4259
+ Set decimal precision that defines the threshold to detect and weld vertices that coincide.
4260
+ @param {number} precision the decimal preciison.
4261
+ */
4262
+ JSC3D.StlLoader.prototype.setDecimalPrecision = function(precision) {
4263
+ this.decimalPrecision = precision;
4264
+ };
4265
+
4266
+ /**
4267
+ Parse contents of an ASCII STL file and generate the scene.
4268
+ Submitted by Triffid Hunter.
4269
+ @private
4270
+ */
4271
+ JSC3D.StlLoader.prototype.parseStlAscii = function(scene, data) {
4272
+ var mesh = new JSC3D.Mesh;
4273
+ mesh.vertexBuffer = [];
4274
+ mesh.indexBuffer = [];
4275
+ mesh.faceNormalBuffer = [];
4276
+
4277
+ var facePattern = 'facet\\s+normal\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+' +
4278
+ 'outer\\s+loop\\s+' +
4279
+ 'vertex\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+' +
4280
+ 'vertex\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+' +
4281
+ 'vertex\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+([-+]?\\b(?:[0-9]*\\.)?[0-9]+(?:[eE][-+]?[0-9]+)?\\b)\\s+' +
4282
+ 'endloop\\s+' +
4283
+ 'endfacet';
4284
+ var faceRegExp = new RegExp(facePattern, 'ig');
4285
+ var matches = data.match(faceRegExp);
4286
+
4287
+ if(matches) {
4288
+ var numOfFaces = matches.length;
4289
+
4290
+ mesh.faceCount = numOfFaces;
4291
+ var v2i = {};
4292
+
4293
+ // reset regexp for vertex extraction
4294
+ faceRegExp.lastIndex = 0;
4295
+ faceRegExp.global = false;
4296
+
4297
+ // read faces
4298
+ for (var r = faceRegExp.exec(data); r != null; r = faceRegExp.exec(data)) {
4299
+ mesh.faceNormalBuffer.push(parseFloat(r[1]), parseFloat(r[2]), parseFloat(r[3]));
4300
+
4301
+ for (var i = 0; i < 3; i++) {
4302
+ var x = parseFloat(r[4 + (i * 3)]);
4303
+ var y = parseFloat(r[5 + (i * 3)]);
4304
+ var z = parseFloat(r[6 + (i * 3)]);
4305
+
4306
+ // weld vertices by the given decimal precision
4307
+ var vertKey = x.toFixed(this.decimalPrecision) + '-' + y.toFixed(this.decimalPrecision) + '-' + z.toFixed(this.decimalPrecision);
4308
+ var vi = v2i[vertKey];
4309
+ if(vi == undefined) {
4310
+ vi = mesh.vertexBuffer.length / 3;
4311
+ v2i[vertKey] = vi;
4312
+ mesh.vertexBuffer.push(x);
4313
+ mesh.vertexBuffer.push(y);
4314
+ mesh.vertexBuffer.push(z);
4315
+ }
4316
+ mesh.indexBuffer.push(vi);
4317
+ }
4318
+
4319
+ // mark the end of the indices of a face
4320
+ mesh.indexBuffer.push(-1);
4321
+ }
4322
+ }
4323
+
4324
+ // add mesh to scene
4325
+ if(!mesh.isTrivial())
4326
+ scene.addChild(mesh);
4327
+ };
4328
+
4329
+ /**
4330
+ Parse contents of a binary STL file and generate the scene.
4331
+ @private
4332
+ */
4333
+ JSC3D.StlLoader.prototype.parseStlBinary = function(scene, data) {
4334
+ var UINT16_BYTES = 2;
4335
+ var UINT32_BYTES = 4;
4336
+ var FLOAT_BYTES = 4;
4337
+ var HEADER_BYTES = 80;
4338
+ var FACE_COUNT_BYTES = UINT32_BYTES;
4339
+ var FACE_NORMAL_BYTES = FLOAT_BYTES * 3;
4340
+ var FACE_VERTICES = 3;
4341
+ var VERTEX_BYTES = FLOAT_BYTES * 3;
4342
+ var ATTRIB_BYTE_COUNT_BYTES = UINT16_BYTES;
4343
+
4344
+ // 84 is the minimun length of a valid binary stl file
4345
+ if(data.length < HEADER_BYTES + FACE_COUNT_BYTES)
4346
+ return;
4347
+
4348
+ var cur = 0;
4349
+
4350
+ // skip 80-byte's stl file header
4351
+ cur += HEADER_BYTES;
4352
+
4353
+ // read face count
4354
+ var numOfFaces = this.readUInt32LittleEndian(data, cur);
4355
+ cur += UINT32_BYTES;
4356
+
4357
+ var expectedLen = HEADER_BYTES + FACE_COUNT_BYTES +
4358
+ (FACE_NORMAL_BYTES + VERTEX_BYTES * FACE_VERTICES + ATTRIB_BYTE_COUNT_BYTES) * numOfFaces;
4359
+
4360
+ // file is not complete
4361
+ if(data.length < expectedLen)
4362
+ return;
4363
+
4364
+ var mesh = new JSC3D.Mesh;
4365
+ mesh.faceCount = numOfFaces;
4366
+ mesh.vertexBuffer = [];
4367
+ mesh.indexBuffer = [];
4368
+ mesh.faceNormalBuffer = [];
4369
+ var v2i = {};
4370
+
4371
+ // read faces
4372
+ for(var i=0; i<numOfFaces; i++) {
4373
+ // read normal vector of a face
4374
+ mesh.faceNormalBuffer.push(this.readFloatLittleEndian(data, cur));
4375
+ cur += FLOAT_BYTES;
4376
+ mesh.faceNormalBuffer.push(this.readFloatLittleEndian(data, cur));
4377
+ cur += FLOAT_BYTES;
4378
+ mesh.faceNormalBuffer.push(this.readFloatLittleEndian(data, cur));
4379
+ cur += FLOAT_BYTES;
4380
+
4381
+ // read all 3 vertices of a face
4382
+ for(var j=0; j<FACE_VERTICES; j++) {
4383
+ // read coords of a vertex
4384
+ var x, y, z;
4385
+ x = this.readFloatLittleEndian(data, cur);
4386
+ cur += FLOAT_BYTES;
4387
+ y = this.readFloatLittleEndian(data, cur);
4388
+ cur += FLOAT_BYTES;
4389
+ z = this.readFloatLittleEndian(data, cur);
4390
+ cur += FLOAT_BYTES;
4391
+
4392
+ // weld vertices by the given decimal precision
4393
+ var vertKey = x.toFixed(this.decimalPrecision) + '-' + y.toFixed(this.decimalPrecision) + '-' + z.toFixed(this.decimalPrecision);
4394
+ var vi = v2i[vertKey];
4395
+ if(vi != undefined) {
4396
+ mesh.indexBuffer.push(vi);
4397
+ }
4398
+ else {
4399
+ vi = mesh.vertexBuffer.length / 3;
4400
+ v2i[vertKey] = vi;
4401
+ mesh.vertexBuffer.push(x);
4402
+ mesh.vertexBuffer.push(y);
4403
+ mesh.vertexBuffer.push(z);
4404
+ mesh.indexBuffer.push(vi);
4405
+ }
4406
+ }
4407
+
4408
+ // mark the end of the indices of a face
4409
+ mesh.indexBuffer.push(-1);
4410
+
4411
+ // skip 2-bytes' 'attribute byte count' field, since we do not deal with any additional attribs
4412
+ cur += ATTRIB_BYTE_COUNT_BYTES;
4413
+
4414
+ }
4415
+
4416
+ // add mesh to scene
4417
+ if(!mesh.isTrivial())
4418
+ scene.addChild(mesh);
4419
+ };
4420
+
4421
+ /**
4422
+ @private
4423
+ */
4424
+ JSC3D.StlLoader.prototype.readUInt32LittleEndian = function(data, start) {
4425
+ var rv = 0, f = 1;
4426
+ for (var i=0; i<4; i++) {
4427
+ rv += (data[start + i] * f);
4428
+ f *= 256;
4429
+ }
4430
+
4431
+ return rv;
4432
+ };
4433
+
4434
+ /**
4435
+ @private
4436
+ */
4437
+ JSC3D.StlLoader.prototype.readFloatLittleEndian = function(data, start) {
4438
+ var mLen = 23;
4439
+ var eLen = 8; // 4 * 8 - 23 - 1
4440
+ var eMax = 255; // (1 << eLen) - 1;
4441
+ var eBias = 127; // eMax >> 1;
4442
+
4443
+ var i = 3;
4444
+ var d = -1;
4445
+ var s = data[start + i];
4446
+ i += d;
4447
+ var bits = -7;
4448
+ var e = s & ((1 << (-bits)) - 1);
4449
+ s >>= -bits;
4450
+ bits += eLen
4451
+ while(bits > 0) {
4452
+ e = e * 256 + data[start + i];
4453
+ i += d;
4454
+ bits -= 8;
4455
+ }
4456
+
4457
+ var m = e & ((1 << (-bits)) - 1);
4458
+ e >>= -bits;
4459
+ bits += mLen;
4460
+ while(bits > 0) {
4461
+ m = m * 256 + data[start + i];
4462
+ i += d;
4463
+ bits -= 8;
4464
+ }
4465
+
4466
+ switch(e) {
4467
+ case 0: // 0 or denormalized number
4468
+ e = 1 - eBias;
4469
+ break;
4470
+ case eMax: // NaN or +/-Infinity
4471
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
4472
+ default: // normalized number
4473
+ m = m + Math.pow(2, mLen);
4474
+ e = e - eBias;
4475
+ break;
4476
+ }
4477
+
4478
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
4479
+ };
4480
+
4481
+ JSC3D.StlLoader.prototype.onload = null;
4482
+ JSC3D.StlLoader.prototype.onerror = null;
4483
+ JSC3D.StlLoader.prototype.onprogress = null;
4484
+ JSC3D.StlLoader.prototype.onresource = null;
4485
+ JSC3D.StlLoader.prototype.decimalPrecision = 3;
4486
+
4487
+ JSC3D.LoaderSelector.registerLoader('stl', JSC3D.StlLoader);