index.html 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>ObJS</title>
  5. <link rel='shortcut icon' href='favicon.ico'/>
  6. <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
  7. <meta charset='utf-8'/>
  8. <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
  9. <link rel='author' href='I&ntilde;igo Valentin'/>
  10. <link rel='publisher' href='I&ntilde;igo Valentin'/>
  11. <meta name='description' content='Demo of ObJS - 3D viewer in pure Javascript'/>
  12. <!-- Script file -->
  13. <script type="text/javascript">
  14. /********************************************************************
  15. * The ObJS object initializes the canvas, assigning it to the HTML *
  16. * element, and set the required mouse events. *
  17. * #parameters: none *
  18. * #return: the ObJS object. *
  19. ********************************************************************/
  20. function ObJS(){
  21. /********************************************************************
  22. * Parameters for costumization. *
  23. ********************************************************************/
  24. var backColor = "rgba(200, 200, 200, 1)";
  25. var vertColor = "rgba(10, 10, 10, 1)";
  26. var edgeColor = "rgba(100, 70, 70, 1)";
  27. var faceColor = "rgba(180, 180, 255, .6)";
  28. var faceAlpha = .6;
  29. var textColor = "rgba(0, 0, 0, 1)";
  30. var vertSize = 4; //Make it even: at small scales will look better
  31. var rotationSpeed = 90; //Smaller = faster. 100 is a good speed
  32. var zoomSpeed = 0.161; //0-1. 0.2 or 0.3 are good speeds
  33. var moveSpeed = .5;
  34. /********************************************************************
  35. * Variable containing this object, to be refered from nested *
  36. * functions. *
  37. ********************************************************************/
  38. var thisObJS;
  39. /********************************************************************
  40. * Parameter used to scale the measures in the .obj file. Can be *
  41. * integer or float, greater than 0. 50 is a good default. *
  42. ********************************************************************/
  43. var scale;
  44. /********************************************************************
  45. * Variable to determine if the canvas has been initialized. *
  46. ********************************************************************/
  47. var canvasInitialized = false;
  48. /********************************************************************
  49. * Variable to determine if the mouse button is being holded down. *
  50. ********************************************************************/
  51. var mDown = false;
  52. var pinch = false;
  53. var pinchScale = 0;
  54. /********************************************************************
  55. * Variables containing the canvas and its context. *
  56. ********************************************************************/
  57. var canvas;
  58. var topCanvas;
  59. var frontCanvas;
  60. var sideCanvas;
  61. var ctx;
  62. var topCtx;
  63. var frontCtx;
  64. var sideCtx;
  65. /********************************************************************
  66. * Variables containing the number of elements. *
  67. ********************************************************************/
  68. var totalVert;
  69. var totalMaterial = 0;
  70. var totalFace
  71. /********************************************************************
  72. * Arrays containing the vertices, faces and materials. *
  73. ********************************************************************/
  74. var material;
  75. var vert;
  76. var face;
  77. /********************************************************************
  78. * Booleans to determine wich elements are to draw. *
  79. ********************************************************************/
  80. var dVerts = true;
  81. var dEdges = true;
  82. var dFaces = true;
  83. var dBackg = true;
  84. /********************************************************************
  85. * Booleans to indicate if mtl file should be used. *
  86. ********************************************************************/
  87. var hasMtl = true;
  88. var useMtl = true;
  89. /********************************************************************
  90. * Global variables that will contain the position of the mouse in *
  91. * the previous frame to calculate rotation. *
  92. ********************************************************************/
  93. var pX = null;
  94. var pY = null;
  95. /********************************************************************
  96. * Function that initializes the canvas, assignin it to the HTML *
  97. * element, and set the required mouse events. *
  98. * #parameters: *
  99. * canv (canvas): canvas to use. If omited, the one named *
  100. * named "ObJSView" will be used. *
  101. * #return: nothing *
  102. * #scope: public *
  103. ********************************************************************/
  104. this.initCanvas = function(canv){
  105. if (!canvasInitialized){
  106. if (canv != null){
  107. canvas = canv;
  108. }
  109. else{
  110. canvas = document.getElementById("ObJSView");
  111. }
  112. topCanvas = document.getElementById("ObJSTop");
  113. frontCanvas = document.getElementById("ObJSFront");
  114. sideCanvas = document.getElementById("ObJSSide");
  115. ctx = canvas.getContext("2d");
  116. topCtx = topCanvas.getContext("2d");
  117. frontCtx = frontCanvas.getContext("2d");
  118. sideCtx = sideCanvas.getContext("2d");
  119. ctx.translate(canvas.width / 2, canvas.height / 2);
  120. canvas.onmousedown = function(e){
  121. pX = e.offsetX == undefined?e.layerX:e.offsetX;
  122. pY = e.offsetY == undefined?e.layerY:e.offsetY;
  123. mDown = true;
  124. };
  125. canvas.onmouseup = function(e){
  126. if(mDown){
  127. mouseClick(e);
  128. }
  129. pX = null;
  130. pY = null;
  131. mDown = false;
  132. };
  133. canvas.onmousemove = function(e){
  134. if(!mDown){
  135. return;
  136. }
  137. var x = e.offsetX == undefined?e.layerX:e.offsetX;
  138. var y = e.offsetY == undefined?e.layerY:e.offsetY;
  139. if (e.shiftKey){
  140. thisObJS.moveHorizontal(x - pX);
  141. thisObJS.moveVertical(y - pY);
  142. }
  143. else{
  144. thisObJS.rotateY(x - pX);
  145. thisObJS.rotateX(y - pY);
  146. }
  147. pX = x;
  148. pY = y;
  149. return false;
  150. };
  151. canvas.onmouseout = function(e){
  152. if(mDown){
  153. mDown = false;
  154. }
  155. }
  156. canvas.addEventListener('mousewheel',function(e){
  157. e.preventDefault();
  158. if (event.wheelDelta > 0){
  159. thisObJS.zoom(1);
  160. }
  161. else{
  162. thisObJS.zoom(-1);
  163. }
  164. return false;
  165. }, false);
  166. canvas.addEventListener('touchstart',function(e){
  167. if(e.touches.length == 2) {
  168. pinch = true;
  169. pinchScale = Math.sqrt((e.touches[0].clientX-e.touches[1].clientX) * (e.touches[0].clientX-e.touches[1].clientX) + (e.touches[0].clientY-e.touches[1].clientY) * (e.touches[0].clientY-e.touches[1].clientY));
  170. }
  171. return false;
  172. }, false);
  173. canvas.addEventListener('touchend',function(e){
  174. if(pinch){
  175. pinch = false;
  176. }
  177. return false;
  178. }, false);
  179. canvas.addEventListener('touchcancel',function(e){
  180. if(pinch){
  181. pinch = false;
  182. }
  183. return false;
  184. }, false);
  185. canvas.addEventListener('touchleave',function(e){
  186. if(pinch){
  187. pinch = false;
  188. }
  189. return false;
  190. }, false);
  191. canvas.addEventListener('touchmove', function(e){
  192. e.preventDefault();
  193. if(pinch){
  194. var dist = Math.sqrt((e.touches[0].clientX-e.touches[1].clientX) * (e.touches[0].clientX-e.touches[1].clientX) + (e.touches[0].clientY-e.touches[1].clientY) * (e.touches[0].clientY-e.touches[1].clientY));
  195. if (dist > pinchScale){
  196. thisObJS.zoom(1);
  197. }
  198. else if (dist < pinchScale){
  199. thisObJS.zoom(-1);
  200. }
  201. pinchScale = dist;
  202. }
  203. else{
  204. var x = e.touches[0].clientX
  205. var y = e.touches[0].clientY;
  206. thisObJS.rotateY(x - pX);
  207. thisObJS.rotateX(y - pY);
  208. pX = x;
  209. pY = y;
  210. }
  211. return false;
  212. }, false);
  213. canvasInitialized = true;
  214. }
  215. };
  216. /********************************************************************
  217. * Main function, called every time a file is to be loaded. *
  218. * #parameters: *
  219. * file (string): name of the file, without path or extension. *
  220. * #return: nothing *
  221. * #scope: public *
  222. ********************************************************************/
  223. this.load = function(file){
  224. if(!canvasInitialized){
  225. initCanvas();
  226. }
  227. //Get obj file
  228. var xmlhttp = new XMLHttpRequest();
  229. xmlhttp.open("GET", file, false);
  230. xmlhttp.send();
  231. var fileContent = xmlhttp.responseText;
  232. //Get mtl file
  233. xmlhttp = new XMLHttpRequest();
  234. xmlhttp.open("GET",file.substring(0, file.lastIndexOf(".")) + ".mtl", false);
  235. xmlhttp.send();
  236. var mtlContent = xmlhttp.responseText;
  237. initArrays();
  238. readFile(fileContent);
  239. readMtlFile(mtlContent);
  240. draw();
  241. drawSides();
  242. };
  243. /********************************************************************
  244. * Getters that determine if an element is being drawn. *
  245. ********************************************************************/
  246. this.isBackgroundDrawn = function(){
  247. return dBackg;
  248. };
  249. this.ivertexDrawn = function(){
  250. return dVerts;
  251. };
  252. this.isEdgeDrawn = function(){
  253. return dEdges;
  254. };
  255. this.ifaceDrawn = function(){
  256. return dFaces;
  257. };
  258. this.isMaterialUsed = function(){
  259. return useMtl;
  260. };
  261. /********************************************************************
  262. * Getters that determine if the model has associated materials. *
  263. ********************************************************************/
  264. this.hasMaterial = function(){
  265. return hasMtl;
  266. };
  267. /********************************************************************
  268. * Getters for the color of the elements. *
  269. ********************************************************************/
  270. this.getBackgroundColor = function (){
  271. var str = backColor;
  272. var r = str.substring(5, str.indexOf(', '));
  273. str = str.substring(str.indexOf(', ') + 2);
  274. var g = str.substring(0, str.indexOf(', '));
  275. str = str.substring(str.indexOf(', ') + 2);
  276. var b = str.substring(0, str.indexOf(', '));
  277. var a = 1;
  278. return {red: r, green: g, blue: b, alpha: a};
  279. };
  280. this.gevertexColor = function (){
  281. var str = vertColor;
  282. var r = str.substring(5, str.indexOf(', '));
  283. str = str.substring(str.indexOf(', ') + 2);
  284. var g = str.substring(0, str.indexOf(', '));
  285. str = str.substring(str.indexOf(', ') + 2);
  286. var b = str.substring(0, str.indexOf(', '));
  287. var a = 1;
  288. return {red: r, green: g, blue: b, alpha: a};
  289. };
  290. this.getEdgeColor = function (){
  291. var str = edgeColor;
  292. var r = str.substring(5, str.indexOf(', '));
  293. str = str.substring(str.indexOf(', ') + 2);
  294. var g = str.substring(0, str.indexOf(', '));
  295. str = str.substring(str.indexOf(', ') + 2);
  296. var b = str.substring(0, str.indexOf(', '));
  297. var a = 1;
  298. return {red: r, green: g, blue: b, alpha: a};
  299. };
  300. this.gefaceColor = function (){
  301. var str = faceColor;
  302. var r = str.substring(5, str.indexOf(', '));
  303. str = str.substring(str.indexOf(', ') + 2);
  304. var g = str.substring(0, str.indexOf(', '));
  305. str = str.substring(str.indexOf(', ') + 2);
  306. var b = str.substring(0, str.indexOf(', '));
  307. str = str.substring(str.indexOf(', ') + 2);
  308. var a = str.substring(0, str.indexOf(')'));
  309. return {red: r, green: g, blue: b, alpha: a};
  310. };
  311. /********************************************************************
  312. * Functions switching the elements drawn (dVerts, dEdges, dFaces). *
  313. * #parameters: *
  314. * on (boolean): indicating if the element is to be drawn. *
  315. * #return: nothing *
  316. * #scope: public *
  317. ********************************************************************/
  318. this.drawVertizes = function(on){
  319. if (on == true || on == false){
  320. dVerts = on;
  321. draw();
  322. }
  323. };
  324. this.drawEdges = function(on){
  325. if (on == true || on == false){
  326. dEdges = on;
  327. draw();
  328. }
  329. };
  330. this.drawFaces = function(on){
  331. if (on == true || on == false){
  332. dFaces = on;
  333. draw();
  334. }
  335. };
  336. this.drawBackground = function(on){
  337. if (on == true || on == false){
  338. dBackg = on;
  339. draw();
  340. }
  341. };
  342. /********************************************************************
  343. * Functions switching the color of the elements. *
  344. * #parameters: *
  345. * code (string): hex color value, in #fff or #ffffff form. If *
  346. * wrong, a message will be printed to console, *
  347. * and nothing will be changed. *
  348. * #return: nothing *
  349. * #scope: public *
  350. ********************************************************************/
  351. this.severtizesColor = function(code){
  352. if (isColor(code)){
  353. var color = hexToRgb(code);
  354. vertColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
  355. draw();
  356. }
  357. else{
  358. console.log("severtizesColor(" + code + ") ERROR: " + code + " not a valid hex color");
  359. }
  360. };
  361. this.setEdgesColor = function(code){
  362. if (isColor(code)){
  363. var color = hexToRgb(code);
  364. edgeColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
  365. draw();
  366. }
  367. else{
  368. console.log("setEdgesColor(" + code + ") ERROR: " + code + " not a valid hex color");
  369. }
  370. };
  371. this.sefacesColor = function(code){
  372. if (isColor(code)){
  373. var color = hexToRgb(code);
  374. faceColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
  375. draw();
  376. }
  377. else{
  378. console.log("sefacesColor(" + code + ") ERROR: " + code + " not a valid hex color");
  379. }
  380. };
  381. this.setBackgroundColor = function(code){
  382. if (isColor(code)){
  383. var color = hexToRgb(code);
  384. backColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
  385. draw();
  386. }
  387. else{
  388. console.log("setBackgroundColor(" + code + ") ERROR: " + code + " not a valid hex color");
  389. }
  390. };
  391. /********************************************************************
  392. * Function switching the use of mtl file for color. *
  393. * #parameters: *
  394. * value (boolean): indicating if the element is to be drawn. *
  395. * #return: nothing *
  396. * #scope: public *
  397. ********************************************************************/
  398. this.linkMaterial = function(value){
  399. useMtl = value;
  400. draw();
  401. }
  402. /********************************************************************
  403. * Function switching the alpha value of the faces. *
  404. * #parameters: *
  405. * percent (int): Value (0-100) of alpha. Must be processed (0-1) *
  406. * #return: nothing *
  407. * #scope: public *
  408. ********************************************************************/
  409. this.setAlpha = function(percent){
  410. if (percent >= 0 && percent <= 100){
  411. faceAlpha = percent / 100;
  412. faceColor = faceColor.substring(0, faceColor.lastIndexOf(",") + 2) + faceAlpha + ")";
  413. draw();
  414. }
  415. else{
  416. console.log("setAlpha(" + percent + "); ERROR: Only values between 0 and 100 are allowed");
  417. }
  418. };
  419. /********************************************************************
  420. * Function switching the rotation speed. *
  421. * #parameters: *
  422. * percent (int): Value (0-10) of speed. *
  423. * #return: nothing *
  424. * #scope: public *
  425. ********************************************************************/
  426. this.setRotationSpeed = function(percent){
  427. if (percent >= 0 && percent <= 10){
  428. rotationSpeed = Math.round((10 - percent) * 30);
  429. if (rotationSpeed == 0){
  430. rotationSpeed = 1;
  431. }
  432. }
  433. else{
  434. console.log("setRotationSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
  435. }
  436. }
  437. /********************************************************************
  438. * Functions that rotate elements in the canvas along the *
  439. * correspondent axys, calculating the new position for each vertex *
  440. * in the vert array. *
  441. * #parameters: *
  442. * des (int): distance to rotate. *
  443. * #return: nothing *
  444. * #scope: public *
  445. ********************************************************************/
  446. this.rotateY = function(des){
  447. for (var i = 0; i < vert.length; i ++) {
  448. var x = vert[i][0];
  449. var z = vert[i][2];
  450. vert[i][0] = x * Math.cos(des / rotationSpeed) - z * Math.sin(des / rotationSpeed);
  451. vert[i][2] = z * Math.cos(des / rotationSpeed) + x * Math.sin(des / rotationSpeed);
  452. }
  453. draw();
  454. };
  455. this.rotateX = function(des){
  456. for (var i = 0; i < vert.length; i ++) {
  457. var y = vert[i][1];
  458. var z = vert[i][2];
  459. vert[i][1] = y * Math.cos(des / rotationSpeed) - z * Math.sin(des / rotationSpeed);
  460. vert[i][2] = z * Math.cos(des / rotationSpeed) + y * Math.sin(des / rotationSpeed);
  461. }
  462. draw();
  463. };
  464. /********************************************************************
  465. * Function switching the movement speed of the model. *
  466. * #parameters: *
  467. * percent (int): Value (0-10) of speed. *
  468. * #return: nothing *
  469. * #scope: public *
  470. ********************************************************************/
  471. this.setMovementSpeed = function(percent){
  472. if (percent >= 0 && percent <= 10){
  473. moveSpeed = ((percent * 0.8) / 10) + 0.01;
  474. }
  475. else{
  476. console.log("setMoveSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
  477. }
  478. }
  479. /********************************************************************
  480. * Functions that move elements in the canvas along the selected *
  481. * direction, calculating the new position for each vertex in the *
  482. * vert array. *
  483. * #parameters: *
  484. * des (int): distance to move. *
  485. * #return: nothing *
  486. * #scope: public *
  487. ********************************************************************/
  488. this.moveVertical = function(des){
  489. for (var i = 0; i < vert.length; i ++){
  490. vert[i][1] = (vert[i][1] * 1) + des * (moveSpeed / 50);
  491. }
  492. draw();
  493. };
  494. this.moveHorizontal = function(des){
  495. for (var i = 0; i < vert.length; i ++){
  496. vert[i][0] = (vert[i][0] * 1) + des * (moveSpeed / 50);
  497. }
  498. draw();
  499. };
  500. /********************************************************************
  501. * Function switching the zoom speede of the faces. *
  502. * #parameters: *
  503. * percent (int): Value (0-100) of speed. *
  504. * #return: nothing *
  505. * #scope: public *
  506. ********************************************************************/
  507. this.setZoomSpeed = function(percent){
  508. if (percent >= 0 && percent <= 10){
  509. zoomSpeed = ((percent * 0.8) / 10) + 0.01;
  510. }
  511. else{
  512. console.log("setZoomSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
  513. }
  514. }
  515. /********************************************************************
  516. * Function that changes the scale variable, witch results in a *
  517. * zoom in or zoom out effect. *
  518. * #parameters: none *
  519. * #return: nothing *
  520. * #scope: public *
  521. ********************************************************************/
  522. this.zoom = function(val){
  523. scale = scale + val * scale * zoomSpeed;
  524. draw();
  525. };
  526. /********************************************************************
  527. * Function that clears the arrays and the canvas. Use to unload a *
  528. * model. *
  529. * #parameters: none *
  530. * #return: nothing *
  531. * #scope: public *
  532. ********************************************************************/
  533. this.clear = function(){
  534. initArrays();
  535. clearCanvas();
  536. };
  537. /********************************************************************
  538. * Function that turns a hex color code in RGB. *
  539. * stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb *
  540. * #parameters: *
  541. * hex (string): color code, for example "#33ff99" *
  542. * #return: (Array) r, g, b, with their correspondent color. *
  543. * #scope: private *
  544. ********************************************************************/
  545. var hexToRgb = function(hex) {
  546. var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  547. return result ? {
  548. r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16)
  549. } : null;
  550. };
  551. /********************************************************************
  552. * Function that checks if a string is a valid hexadecimal color *
  553. * code, in the format '#ffffff' or '#fff' *
  554. * #parameters: *
  555. * code (string): Code to check. *
  556. * #return: (Boolean) True if valid color code, false otherwise. *
  557. * #scope: private *
  558. ********************************************************************/
  559. var isColor = function(code){
  560. if (code[0] != '#'){
  561. return false;
  562. }
  563. if (code.length != 4 && code.length != 7){
  564. return false
  565. }
  566. for (var i = 1; i < code.length; i ++){
  567. if (code[i] < '0' || (code[i] > 9 && code[i] < 'A') || (code[i] > 'Z' && code[i] < 'a') || code[i] > 'z'){
  568. return false;
  569. }
  570. }
  571. return true;
  572. };
  573. /********************************************************************
  574. * Function that clears the canvas and gets it ready to draw on it. *
  575. * #parameters: none *
  576. * #return: nothing *
  577. * #scope: private *
  578. ********************************************************************/
  579. var clearCanvas = function(){
  580. ctx.clearRect(0, 0, canvas.width, canvas.height);
  581. if (dBackg){
  582. ctx.fillStyle = backColor;
  583. }
  584. else{
  585. ctx.fillStyle = "#ffffff";
  586. }
  587. ctx.fillRect(-canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
  588. };
  589. /********************************************************************
  590. * Function initializing the arrays obj, vert and face as empty *
  591. * arrays. *
  592. * #parameters: none *
  593. * #return: nothing *
  594. * #scope: private *
  595. ********************************************************************/
  596. var initArrays = function(){
  597. material = null;
  598. vert = null;
  599. face = null;
  600. material = new Array();
  601. vert = new Array();
  602. face = new Array();
  603. };
  604. /********************************************************************
  605. * Function tat populates the vert[] array with and array of three *
  606. * elements, containig the coordinates, and the face[] array with *
  607. * the vertices forming the face, plus one element indicating the *
  608. * codename of the material. *
  609. * #parameters: *
  610. * text (string): the content of the obj file. *
  611. * #return: nothing *
  612. * #scope: private *
  613. ********************************************************************/
  614. var readFile = function(text){
  615. var line;
  616. var v = 0;
  617. var f = 0;
  618. var m = 0;
  619. var max = 0;
  620. var j;
  621. var dist;
  622. var fac;
  623. var mat = "null";
  624. text = text + "\n";
  625. while (text.indexOf("\n") != -1){
  626. line = text.substring(0, text.indexOf("\n"));
  627. if (line.substring(0, 2) == "v "){
  628. vert[v] = new Array(3);
  629. line = line.substring(2);
  630. vert[v][0] = line.substring(0, line.indexOf(" "));
  631. line = line.substring(line.indexOf(" ") + 1);
  632. vert[v][1] = line.substring(0, line.indexOf(" "));
  633. line = line.substring(line.indexOf(" ") + 1);
  634. vert[v][2] = line;
  635. dist = Math.sqrt(vert[v][0] * vert[v][0] + vert[v][1] * vert[v][1] + vert[v][2] * vert[v][2]);
  636. if (dist > max){
  637. max = dist;
  638. }
  639. v = v + 1;
  640. }
  641. else if (line.substring(0,2) == "f "){
  642. line = line.substring(2);
  643. face[f] = new Array();
  644. j = 0;
  645. while(line.indexOf(" ") != -1){
  646. face[f][j] = line.substring(0, line.indexOf(" "));
  647. if (face[f][j].indexOf('/') != -1){
  648. face[f][j] = face[f][j].substring(0, face[f][j].indexOf('/'));
  649. }
  650. face[f][j] = face[f][j] - 1;
  651. line = line.substring(line.indexOf(" ") + 1);
  652. j = j + 1;
  653. }
  654. face[f][j] = line;
  655. if (face[f][j].indexOf('/') != -1){
  656. face[f][j] = face[f][j].substring(0, face[f][j].indexOf('/'));
  657. }
  658. face[f][j] = face[f][j] - 1;
  659. face[f][j + 1] = mat;
  660. f = f + 1;
  661. }
  662. else if (line.substring(0, 6) == "usemtl"){
  663. mat = line.substring(7);
  664. }
  665. text = text.substring(text.indexOf("\n") + 1);
  666. }
  667. scale = calculateScale(max);
  668. totalVert = vert.length;
  669. totalFace = face.length;
  670. };
  671. /********************************************************************
  672. * Function tat populates the material[] array with an array *
  673. * containig, four values: the material name, and the three RGB *
  674. * values. *
  675. * #parameters: *
  676. * text (string): the content of the mtl file. *
  677. * #return: nothing *
  678. * #scope: private *
  679. ********************************************************************/
  680. var readMtlFile = function(text){
  681. text = text + "\n";
  682. i = -1;
  683. while (text.indexOf("\n") != -1){
  684. line = text.substring(0, text.indexOf("\n"));
  685. if (line.substring(0, 6) == "newmtl"){
  686. i = i + 1;
  687. material[i] = new Array(4);
  688. material[i][0] = line.substring(line.indexOf(" ") + 1);
  689. }
  690. //Kd, ddifuse color, usefull
  691. //Ka, ambient color, not implemented
  692. //Ks, specular color, not implemented
  693. //d or TR, transparency, not now
  694. else if (line.substring(0, 3) == "Kd "){
  695. line = line.substring(3);
  696. material[i][1] = line.substring(0, line.indexOf(" "));
  697. line = line.substring(line.indexOf(" ") + 1);
  698. material[i][2] = line.substring(0, line.indexOf(" "));
  699. line = line.substring(line.indexOf(" ") + 1);
  700. material[i][3] = line;
  701. }
  702. text = text.substring(text.indexOf("\n") + 1);
  703. }
  704. totalMaterial = material.length;
  705. if (totalMaterial == 0){
  706. hasMtl = false;
  707. }
  708. };
  709. /********************************************************************
  710. * Function that automatically calculates the best scale for the *
  711. * modelermined number of elements, containing the verices that *
  712. * form a face, so the bigest dimension from the object can fit in *
  713. * the smallest dimension of the canvas. *
  714. * #parameters: *
  715. * text (int): the farthest vertex, from the center. *
  716. * #return: (int) The scale to be aplied. *
  717. * #scope: private *
  718. ********************************************************************/
  719. var calculateScale = function(max){
  720. var dim;
  721. if (canvas.width < canvas.height){
  722. dim = canvas.width;
  723. }
  724. else{
  725. dim = canvas.height;
  726. }
  727. var sc = Math.round((0.95 * dim) / (2 * max));
  728. return sc;
  729. };
  730. /********************************************************************
  731. * Function that compares the average Z coordinate of two faces. *
  732. * Must be used when sorting the face array. *
  733. * #parameters: *
  734. * a (Array): One face. *
  735. * b (Array): The other face. *
  736. * #return: (int) -1, 1 or 0. *
  737. * #scope: private *
  738. ********************************************************************/
  739. var faceComparator = function(a,b){
  740. var aZ;
  741. var bZ;
  742. var i = 0;
  743. var sum = 0.0;
  744. while (i < a.length - 1){
  745. sum = sum + parseFloat(vert[a[i]][2]);
  746. i = i + 1;
  747. }
  748. aZ = sum / parseFloat(i);
  749. i = 0;
  750. sum = 0;
  751. while (i < b.length - 1){
  752. sum = sum + parseFloat(1 * vert[b[i]][2]);
  753. i = i + 1;
  754. }
  755. bZ = sum / parseFloat(i);
  756. if (aZ < bZ) return -1;
  757. if (aZ > bZ) return 1;
  758. return 0;
  759. };
  760. /********************************************************************
  761. * Function that writes text in the corner of the canvas. Must be *
  762. * called when everithing has been drawn. *
  763. * #parameters: none *
  764. * #return: nothing *
  765. * #scope: private *
  766. ********************************************************************/
  767. var writeCredits = function(){
  768. /*ctx.fillStyle = textColor;
  769. ctx.font = "12px Arial";
  770. var h = canvas.height / 2 - 14;
  771. var w = 10 - (canvas.width / 2);
  772. ctx.fillText("ObJS: " + totalVert + " vertices " + totalFace + " faces " + totalMaterial + " materials", w, h);*/
  773. document.getElementById('details_verts').innerHTML = totalVert + ' vertices';
  774. document.getElementById('details_faces').innerHTML = totalFace + ' faces';
  775. document.getElementById('details_materials').innerHTML = totalMaterial + ' materials';
  776. };
  777. var drawSides = function(){
  778. var sScale = scale * 0.2;
  779. // TOP
  780. topCtx.clearRect(0, 0, canvas.width, canvas.height);
  781. topCtx.fillStyle = "#ffffff";
  782. topCtx.fillRect(-topCanvas.width / 2, -topCanvas.height / 2, topCanvas.width, canvas.height);
  783. var des = Math.PI / 2;
  784. for (var i = 0; i < vert.length; i ++) {
  785. var y = vert[i][1];
  786. var z = vert[i][2];
  787. vert[i][1] = y * Math.cos(des) - z * Math.sin(des);
  788. vert[i][2] = z * Math.cos(des) + y * Math.sin(des);
  789. }
  790. for (var i = 0; i < face.length; i++){
  791. topCtx.strokeStyle = '#00000099';
  792. topCtx.beginPath();
  793. topCtx.moveTo(50 + (sScale * vert[face[i][0]][0]), 50 + (sScale * vert[face[i][0]][1]));
  794. for (var j = 1; j < face[i].length - 1; j++) {
  795. topCtx.lineTo(50 + (sScale * vert[face[i][j]][0]), 50 + (sScale * vert[face[i][j]][1]));
  796. //topCtx.stroke();
  797. }
  798. topCtx.closePath();
  799. if (useMtl){
  800. //Get material with the same name as the one in the last element of the face
  801. j = 0;
  802. while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
  803. j = j + 1;
  804. }
  805. if (material[j][0] == face[i][face[i].length - 1]){
  806. topCtx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + 1 + ")";
  807. }
  808. else{
  809. topCtx.fillStyle = faceColor;
  810. }
  811. }
  812. else{
  813. topCtx.fillStyle = faceColor;
  814. }
  815. topCtx.stroke();
  816. if(dFaces){
  817. topCtx.fill();
  818. }
  819. }
  820. //restore
  821. des = Math.PI / -2;
  822. for (var i = 0; i < vert.length; i ++) {
  823. var y = vert[i][1];
  824. var z = vert[i][2];
  825. vert[i][1] = y * Math.cos(des) - z * Math.sin(des);
  826. vert[i][2] = z * Math.cos(des) + y * Math.sin(des);
  827. }
  828. // FRONT
  829. frontCtx.clearRect(0, 0, canvas.width, canvas.height);
  830. frontCtx.fillStyle = "#ffffff";
  831. frontCtx.fillRect(-frontCanvas.width / 2, -frontCanvas.height / 2, frontCanvas.width, canvas.height);
  832. des = Math.PI / 2;
  833. for (var i = 0; i < face.length; i++){
  834. frontCtx.strokeStyle = '#00000099';
  835. frontCtx.beginPath();
  836. frontCtx.moveTo(50 + (sScale * vert[face[i][0]][0]), 50 + (sScale * vert[face[i][0]][1]));
  837. for (var j = 1; j < face[i].length - 1; j++) {
  838. frontCtx.lineTo(50 + (sScale * vert[face[i][j]][0]), 50 + (sScale * vert[face[i][j]][1]));
  839. //frontCtx.stroke();
  840. }
  841. frontCtx.closePath();
  842. if (useMtl){
  843. //Get material with the same name as the one in the last element of the face
  844. j = 0;
  845. while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
  846. j = j + 1;
  847. }
  848. if (material[j][0] == face[i][face[i].length - 1]){
  849. frontCtx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + 1 + ")";
  850. }
  851. else{
  852. frontCtx.fillStyle = faceColor;
  853. }
  854. }
  855. else{
  856. frontCtx.fillStyle = faceColor;
  857. }
  858. frontCtx.stroke();
  859. if(dFaces){
  860. frontCtx.fill();
  861. }
  862. }
  863. // SIDE
  864. sideCtx.clearRect(0, 0, canvas.width, canvas.height);
  865. sideCtx.fillStyle = "#ffffff";
  866. sideCtx.fillRect(-sideCanvas.width / 2, -sideCanvas.height / 2, sideCanvas.width, canvas.height);
  867. des = Math.PI / 2;
  868. for (var i = 0; i < vert.length; i ++) {
  869. var x = vert[i][0];
  870. var z = vert[i][2];
  871. vert[i][0] = x * Math.cos(des) - z * Math.sin(des);
  872. vert[i][2] = z * Math.cos(des) + x * Math.sin(des);
  873. }
  874. for (var i = 0; i < face.length; i++){
  875. sideCtx.strokeStyle = '#00000099';
  876. sideCtx.beginPath();
  877. sideCtx.moveTo(50 + (sScale * vert[face[i][0]][0]), 50 + (sScale * vert[face[i][0]][1]));
  878. for (var j = 1; j < face[i].length - 1; j++) {
  879. sideCtx.lineTo(50 + (sScale * vert[face[i][j]][0]), 50 + (sScale * vert[face[i][j]][1]));
  880. //sideCtx.stroke();
  881. }
  882. sideCtx.closePath();
  883. if (useMtl){
  884. //Get material with the same name as the one in the last element of the face
  885. j = 0;
  886. while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
  887. j = j + 1;
  888. }
  889. if (material[j][0] == face[i][face[i].length - 1]){
  890. sideCtx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + 1 + ")";
  891. }
  892. else{
  893. sideCtx.fillStyle = faceColor;
  894. }
  895. }
  896. else{
  897. sideCtx.fillStyle = faceColor;
  898. }
  899. sideCtx.stroke();
  900. if(dFaces){
  901. sideCtx.fill();
  902. }
  903. }
  904. //Restore
  905. des = Math.PI / -2;
  906. for (var i = 0; i < vert.length; i ++) {
  907. var x = vert[i][0];
  908. var z = vert[i][2];
  909. vert[i][0] = x * Math.cos(des) - z * Math.sin(des);
  910. vert[i][2] = z * Math.cos(des) + x * Math.sin(des);
  911. }
  912. };
  913. /********************************************************************
  914. * Function that draws th vertices, faces and edges in the canvas. *
  915. * #parameters: none *
  916. * #return: nothing *
  917. * #scope: private *
  918. ********************************************************************/
  919. var draw = function(){
  920. face = face.sort(faceComparator).reverse();
  921. clearCanvas();
  922. for (var i = 0; i < face.length; i++){
  923. ctx.strokeStyle = edgeColor;
  924. ctx.fillStyle = vertColor;
  925. ctx.beginPath();
  926. ctx.moveTo(scale * vert[face[i][0]][0], scale * vert[face[i][0]][1]);
  927. for (var j = 1; j < face[i].length - 1; j++) {
  928. ctx.lineTo(scale * vert[face[i][j]][0], scale * vert[face[i][j]][1]);
  929. if(dEdges){
  930. ctx.stroke();
  931. }
  932. if(dVerts){
  933. var x = scale * vert[face[i][j]][0] - (vertSize / 2);
  934. var w = vertSize;
  935. var y = scale * vert[face[i][j]][1] - (vertSize / 2);
  936. var h = vertSize;
  937. ctx.fillRect(x, y, w, h);
  938. }
  939. }
  940. ctx.closePath();
  941. if (useMtl){
  942. //Get material with the same name as the one in the last element of the face
  943. j = 0;
  944. while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
  945. j = j + 1;
  946. }
  947. if (material[j][0] == face[i][face[i].length - 1]){
  948. ctx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + faceAlpha + ")";
  949. }
  950. else{
  951. ctx.fillStyle = faceColor;
  952. }
  953. }
  954. else{
  955. ctx.fillStyle = faceColor;
  956. }
  957. if(dFaces){
  958. ctx.fill();
  959. }
  960. }
  961. writeCredits();
  962. };
  963. /********************************************************************
  964. * TODO: Function used when something in the canvas is clicked. *
  965. * #parameters: none *
  966. * #return: nothing *
  967. * #scope: private *
  968. ********************************************************************/
  969. var mouseClick = function(){
  970. };
  971. thisObJS = this;
  972. return this;
  973. }
  974. </script>
  975. <style>
  976. body{
  977. text-align: center;
  978. background-color: #002200;
  979. }
  980. h1{
  981. margin-bottom: 0;
  982. color: #ffffff;
  983. }
  984. h2{
  985. font-size: 110%;
  986. font-style: italic;
  987. margin-top: 0;
  988. color: #ffffff;
  989. }
  990. section{
  991. display: inline-block;
  992. vertical-align: top;
  993. height: 526px;
  994. margin: 0;
  995. border: 2px solid #000000;
  996. }
  997. section h3{
  998. height: 24px;
  999. margin: 0;
  1000. border-bottom: 2px solid #000000;
  1001. background-color: #cceeff;
  1002. }
  1003. section#sideViews{
  1004. width: 120px;
  1005. background-color: #e0f5ff;
  1006. border-top-left-radius: 20px;
  1007. border-bottom-left-radius: 20px;
  1008. }
  1009. section#sideViews h3{
  1010. border-top-left-radius: 20px;
  1011. }
  1012. section#sideViews h4{
  1013. margin: 10px auto 0 auto;
  1014. }
  1015. section#sideViews canvas{
  1016. border: 0.1em solid #000000;
  1017. margin: 0 10px 10px 10px;
  1018. background-color: #ffffff;
  1019. }
  1020. section#sideViews div#details{
  1021. margin-top: 20px;
  1022. }
  1023. section#sideViews div#details span{
  1024. font-size: 70%;
  1025. display: block;
  1026. color: #444444;
  1027. font-weight: bold;
  1028. }
  1029. section#controls{
  1030. text-align: center;
  1031. width: 260px;
  1032. background-color: #e0f5ff;
  1033. border-top-right-radius: 20px;
  1034. border-bottom-right-radius: 20px;
  1035. }
  1036. section#controls h3{
  1037. border-top-right-radius: 20px;
  1038. margin-bottom: 20px;
  1039. }
  1040. section#controls table#colors{
  1041. margin: 20px auto;
  1042. border-collapse: collapse;
  1043. }
  1044. section#controls table#colors td{
  1045. border: 1px solid #000000;
  1046. }
  1047. section#controls table#colors td img{
  1048. width: 30px;
  1049. height: 30px;
  1050. }
  1051. section#controls table#colors td input[type="color"]{
  1052. width: 30px;
  1053. height: 30px;
  1054. padding: 1px;
  1055. }
  1056. section#controls input[type="range"]{
  1057. display: block;
  1058. width: 80%;
  1059. margin: auto auto 15px auto;
  1060. }
  1061. canvas#ObJSView{
  1062. width: 500px;
  1063. height: 500px;
  1064. }
  1065. td#ObJSControl input[type="color"]{
  1066. width: 1.3em;
  1067. height: 1.3em;
  1068. padding: 0;
  1069. vertical-align: middle;
  1070. }
  1071. td#ObJSControl input[type="range"]{
  1072. vertical-align: middle;
  1073. }
  1074. </style>
  1075. </head>
  1076. <body onload="objs = new ObJS(); objs.initCanvas();objs.load(document.getElementById('objSelector').value);">
  1077. <h1>
  1078. ObJS
  1079. </h1>
  1080. <h2>
  1081. by I&ntilde;igo Valentin
  1082. </h2>
  1083. <section id='sideViews'>
  1084. <h3>
  1085. Details
  1086. </h3>
  1087. <h4>
  1088. Top
  1089. </h4>
  1090. <canvas id="ObJSTop" width="100" height="100">
  1091. Your browser does not support the HTML5 canvas tag.
  1092. </canvas>
  1093. <h4>
  1094. Front
  1095. </h4>
  1096. <canvas id="ObJSFront" width="100" height="100">
  1097. Your browser does not support the HTML5 canvas tag.
  1098. </canvas>
  1099. <h4>
  1100. Side
  1101. </h4>
  1102. <canvas id="ObJSSide" width="100" height="100">
  1103. Your browser does not support the HTML5 canvas tag.
  1104. </canvas>
  1105. <div id='details'>
  1106. <span id='details_verts'></span>
  1107. <span id='details_faces'></span>
  1108. <span id='details_materials'></span>
  1109. </div>
  1110. </section>
  1111. <section id='view'>
  1112. <h3>
  1113. View
  1114. </h3>
  1115. <canvas id="ObJSView" width="500" height="500">
  1116. Your browser does not support the HTML5 canvas tag.
  1117. </canvas>
  1118. </section>
  1119. <section id='controls'>
  1120. <h3>
  1121. Controls
  1122. </h3>
  1123. Choose a model:
  1124. <select id="objSelector" onChange="objs.load(this.value)">
  1125. <option value="obj/rainbow.obj" selected="selected">Rainbow</option>
  1126. <option value="obj/cube.obj">Cube</option>
  1127. <option value="obj/sphere.obj">Sphere</option>
  1128. <option value="obj/star.obj">Star</option>
  1129. <option value="obj/monkey.obj">Monkey</option>
  1130. <option value="obj/bolt.obj">Bolt</option>
  1131. <option value="obj/multiple.obj">Multiple Objects</option>
  1132. <option value="obj/seaplane.obj">Seaplane (Can freeze browser)</option>
  1133. </select>
  1134. <table id='colors'>
  1135. <tr>
  1136. <td>
  1137. </td>
  1138. <td>
  1139. <img src='img/eye.png'/>
  1140. </td>
  1141. <td>
  1142. <img src='img/color.png'/>
  1143. </td>
  1144. </tr>
  1145. <tr>
  1146. <td>
  1147. <img src='img/vert.png'/>
  1148. </td>
  1149. <td>
  1150. <input type="checkbox" value="verts" checked onChange="objs.drawVertizes(this.checked);"/>
  1151. </td>
  1152. <td>
  1153. <input type="color" value="#0A0A0A" onChange="objs.severtizesColor(this.value);"/>
  1154. </td>
  1155. </tr>
  1156. <tr>
  1157. <td>
  1158. <img src='img/line.png'/>
  1159. </td>
  1160. <td>
  1161. <input type="checkbox" value="edges" checked onChange="objs.drawEdges(this.checked);"/>
  1162. </td>
  1163. <td>
  1164. <input type="color" value="#644646" onChange="objs.setEdgesColor(this.value);"/>
  1165. </td>
  1166. </tr>
  1167. <tr>
  1168. <td>
  1169. <img src='img/face.png'/>
  1170. </td>
  1171. <td>
  1172. <input type="checkbox" value="faces" checked onChange="objs.drawFaces(this.checked);"/>
  1173. </td>
  1174. <td>
  1175. <input type="color" value="#B4B4FF" onChange="objs.sefacesColor(this.value);"/>
  1176. <!-- <input type="checkbox" checked onChange="objs.linkMaterial(this.checked);"/>Use materials -->
  1177. </td>
  1178. </tr>
  1179. <tr>
  1180. <td>
  1181. <img src='img/bg.png'/>
  1182. </td>
  1183. <td>
  1184. <input type="checkbox" value="backg" checked onChange="objs.drawBackground(this.checked);"/>
  1185. </td>
  1186. <td>
  1187. <input type="color" value="#C8C8C8" onChange="objs.setBackgroundColor(this.value)"/>
  1188. </td>
  1189. </tr>
  1190. </table>
  1191. Alpha:
  1192. <input type="range" min="0" max="100" step="1" value="60" onInput="objs.setAlpha(this.value);" onChange="objs.setAlpha(this.value);"/>
  1193. Rotation speed:
  1194. <input type="range" min="0" max="10" step="1" value="3" onInput="objs.setRotationSpeed(this.value);" onChange="objs.setRotationSpeed(this.value);"/>
  1195. Zoom speed:
  1196. <input type="range" min="0" max="10" step="1" value="2" onInput="objs.setZoomSpeed(this.value);" onChange="objs.setZoomSpeed(this.value);"/>
  1197. Movement speed:
  1198. <input type="range" min="0" max="10" step="1" value="5" onInput="objs.setMovementSpeed(this.value);" onChange="objs.setMovementSpeed(this.value);"/>
  1199. </section>
  1200. </body>
  1201. </html>