jsc3d-rails 1.0

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