jsc3d-rails 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);