| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271 |
- <!DOCTYPE html>
- <html>
- <head>
- <title>ObJS</title>
- <link rel='shortcut icon' href='favicon.ico'/>
- <meta content='text/html; charset=utf-8' http-equiv='content-type'/>
- <meta charset='utf-8'/>
- <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1'/>
- <link rel='author' href='Iñigo Valentin'/>
- <link rel='publisher' href='Iñigo Valentin'/>
- <meta name='description' content='Demo of ObJS - 3D viewer in pure Javascript'/>
- <!-- Script file -->
- <script type="text/javascript">
- /********************************************************************
- * The ObJS object initializes the canvas, assigning it to the HTML *
- * element, and set the required mouse events. *
- * #parameters: none *
- * #return: the ObJS object. *
- ********************************************************************/
- function ObJS(){
- /********************************************************************
- * Parameters for costumization. *
- ********************************************************************/
- var backColor = "rgba(200, 200, 200, 1)";
- var vertColor = "rgba(10, 10, 10, 1)";
- var edgeColor = "rgba(100, 70, 70, 1)";
- var faceColor = "rgba(180, 180, 255, .6)";
- var faceAlpha = .6;
- var textColor = "rgba(0, 0, 0, 1)";
- var vertSize = 4; //Make it even: at small scales will look better
- var rotationSpeed = 90; //Smaller = faster. 100 is a good speed
- var zoomSpeed = 0.161; //0-1. 0.2 or 0.3 are good speeds
- var moveSpeed = .5;
- /********************************************************************
- * Variable containing this object, to be refered from nested *
- * functions. *
- ********************************************************************/
- var thisObJS;
- /********************************************************************
- * Parameter used to scale the measures in the .obj file. Can be *
- * integer or float, greater than 0. 50 is a good default. *
- ********************************************************************/
- var scale;
- /********************************************************************
- * Variable to determine if the canvas has been initialized. *
- ********************************************************************/
- var canvasInitialized = false;
- /********************************************************************
- * Variable to determine if the mouse button is being holded down. *
- ********************************************************************/
- var mDown = false;
- var pinch = false;
- var pinchScale = 0;
- /********************************************************************
- * Variables containing the canvas and its context. *
- ********************************************************************/
- var canvas;
- var topCanvas;
- var frontCanvas;
- var sideCanvas;
- var ctx;
- var topCtx;
- var frontCtx;
- var sideCtx;
- /********************************************************************
- * Variables containing the number of elements. *
- ********************************************************************/
- var totalVert;
- var totalMaterial = 0;
- var totalFace
- /********************************************************************
- * Arrays containing the vertices, faces and materials. *
- ********************************************************************/
- var material;
- var vert;
- var face;
- /********************************************************************
- * Booleans to determine wich elements are to draw. *
- ********************************************************************/
- var dVerts = true;
- var dEdges = true;
- var dFaces = true;
- var dBackg = true;
- /********************************************************************
- * Booleans to indicate if mtl file should be used. *
- ********************************************************************/
- var hasMtl = true;
- var useMtl = true;
- /********************************************************************
- * Global variables that will contain the position of the mouse in *
- * the previous frame to calculate rotation. *
- ********************************************************************/
- var pX = null;
- var pY = null;
- /********************************************************************
- * Function that initializes the canvas, assignin it to the HTML *
- * element, and set the required mouse events. *
- * #parameters: *
- * canv (canvas): canvas to use. If omited, the one named *
- * named "ObJSView" will be used. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.initCanvas = function(canv){
- if (!canvasInitialized){
- if (canv != null){
- canvas = canv;
- }
- else{
- canvas = document.getElementById("ObJSView");
- }
- topCanvas = document.getElementById("ObJSTop");
- frontCanvas = document.getElementById("ObJSFront");
- sideCanvas = document.getElementById("ObJSSide");
- ctx = canvas.getContext("2d");
- topCtx = topCanvas.getContext("2d");
- frontCtx = frontCanvas.getContext("2d");
- sideCtx = sideCanvas.getContext("2d");
- ctx.translate(canvas.width / 2, canvas.height / 2);
- canvas.onmousedown = function(e){
- pX = e.offsetX == undefined?e.layerX:e.offsetX;
- pY = e.offsetY == undefined?e.layerY:e.offsetY;
- mDown = true;
- };
- canvas.onmouseup = function(e){
- if(mDown){
- mouseClick(e);
- }
- pX = null;
- pY = null;
- mDown = false;
- };
- canvas.onmousemove = function(e){
- if(!mDown){
- return;
- }
- var x = e.offsetX == undefined?e.layerX:e.offsetX;
- var y = e.offsetY == undefined?e.layerY:e.offsetY;
- if (e.shiftKey){
- thisObJS.moveHorizontal(x - pX);
- thisObJS.moveVertical(y - pY);
- }
- else{
- thisObJS.rotateY(x - pX);
- thisObJS.rotateX(y - pY);
- }
- pX = x;
- pY = y;
- return false;
- };
- canvas.onmouseout = function(e){
- if(mDown){
- mDown = false;
- }
- }
- canvas.addEventListener('mousewheel',function(e){
- e.preventDefault();
- if (event.wheelDelta > 0){
- thisObJS.zoom(1);
- }
- else{
- thisObJS.zoom(-1);
- }
- return false;
- }, false);
- canvas.addEventListener('touchstart',function(e){
- if(e.touches.length == 2) {
- pinch = true;
- 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));
- }
- return false;
- }, false);
- canvas.addEventListener('touchend',function(e){
- if(pinch){
- pinch = false;
- }
- return false;
- }, false);
- canvas.addEventListener('touchcancel',function(e){
- if(pinch){
- pinch = false;
- }
- return false;
- }, false);
- canvas.addEventListener('touchleave',function(e){
- if(pinch){
- pinch = false;
- }
- return false;
- }, false);
- canvas.addEventListener('touchmove', function(e){
- e.preventDefault();
- if(pinch){
- 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));
- if (dist > pinchScale){
- thisObJS.zoom(1);
- }
- else if (dist < pinchScale){
- thisObJS.zoom(-1);
- }
- pinchScale = dist;
- }
- else{
- var x = e.touches[0].clientX
- var y = e.touches[0].clientY;
- thisObJS.rotateY(x - pX);
- thisObJS.rotateX(y - pY);
- pX = x;
- pY = y;
- }
- return false;
- }, false);
- canvasInitialized = true;
- }
- };
- /********************************************************************
- * Main function, called every time a file is to be loaded. *
- * #parameters: *
- * file (string): name of the file, without path or extension. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.load = function(file){
- if(!canvasInitialized){
- initCanvas();
- }
- //Get obj file
- var xmlhttp = new XMLHttpRequest();
- xmlhttp.open("GET", file, false);
- xmlhttp.send();
- var fileContent = xmlhttp.responseText;
- //Get mtl file
- xmlhttp = new XMLHttpRequest();
- xmlhttp.open("GET",file.substring(0, file.lastIndexOf(".")) + ".mtl", false);
- xmlhttp.send();
- var mtlContent = xmlhttp.responseText;
- initArrays();
- readFile(fileContent);
- readMtlFile(mtlContent);
- draw();
- drawSides();
- };
- /********************************************************************
- * Getters that determine if an element is being drawn. *
- ********************************************************************/
- this.isBackgroundDrawn = function(){
- return dBackg;
- };
- this.ivertexDrawn = function(){
- return dVerts;
- };
- this.isEdgeDrawn = function(){
- return dEdges;
- };
- this.ifaceDrawn = function(){
- return dFaces;
- };
- this.isMaterialUsed = function(){
- return useMtl;
- };
- /********************************************************************
- * Getters that determine if the model has associated materials. *
- ********************************************************************/
- this.hasMaterial = function(){
- return hasMtl;
- };
- /********************************************************************
- * Getters for the color of the elements. *
- ********************************************************************/
- this.getBackgroundColor = function (){
- var str = backColor;
- var r = str.substring(5, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var g = str.substring(0, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var b = str.substring(0, str.indexOf(', '));
- var a = 1;
- return {red: r, green: g, blue: b, alpha: a};
- };
- this.gevertexColor = function (){
- var str = vertColor;
- var r = str.substring(5, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var g = str.substring(0, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var b = str.substring(0, str.indexOf(', '));
- var a = 1;
- return {red: r, green: g, blue: b, alpha: a};
- };
- this.getEdgeColor = function (){
- var str = edgeColor;
- var r = str.substring(5, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var g = str.substring(0, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var b = str.substring(0, str.indexOf(', '));
- var a = 1;
- return {red: r, green: g, blue: b, alpha: a};
- };
- this.gefaceColor = function (){
- var str = faceColor;
- var r = str.substring(5, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var g = str.substring(0, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var b = str.substring(0, str.indexOf(', '));
- str = str.substring(str.indexOf(', ') + 2);
- var a = str.substring(0, str.indexOf(')'));
- return {red: r, green: g, blue: b, alpha: a};
- };
- /********************************************************************
- * Functions switching the elements drawn (dVerts, dEdges, dFaces). *
- * #parameters: *
- * on (boolean): indicating if the element is to be drawn. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.drawVertizes = function(on){
- if (on == true || on == false){
- dVerts = on;
- draw();
- }
- };
- this.drawEdges = function(on){
- if (on == true || on == false){
- dEdges = on;
- draw();
- }
- };
- this.drawFaces = function(on){
- if (on == true || on == false){
- dFaces = on;
- draw();
- }
- };
- this.drawBackground = function(on){
- if (on == true || on == false){
- dBackg = on;
- draw();
- }
- };
- /********************************************************************
- * Functions switching the color of the elements. *
- * #parameters: *
- * code (string): hex color value, in #fff or #ffffff form. If *
- * wrong, a message will be printed to console, *
- * and nothing will be changed. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.severtizesColor = function(code){
- if (isColor(code)){
- var color = hexToRgb(code);
- vertColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
- draw();
- }
- else{
- console.log("severtizesColor(" + code + ") ERROR: " + code + " not a valid hex color");
- }
- };
- this.setEdgesColor = function(code){
- if (isColor(code)){
- var color = hexToRgb(code);
- edgeColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
- draw();
- }
- else{
- console.log("setEdgesColor(" + code + ") ERROR: " + code + " not a valid hex color");
- }
- };
- this.sefacesColor = function(code){
- if (isColor(code)){
- var color = hexToRgb(code);
- faceColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
- draw();
- }
- else{
- console.log("sefacesColor(" + code + ") ERROR: " + code + " not a valid hex color");
- }
- };
- this.setBackgroundColor = function(code){
- if (isColor(code)){
- var color = hexToRgb(code);
- backColor = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", 1)";
- draw();
- }
- else{
- console.log("setBackgroundColor(" + code + ") ERROR: " + code + " not a valid hex color");
- }
- };
- /********************************************************************
- * Function switching the use of mtl file for color. *
- * #parameters: *
- * value (boolean): indicating if the element is to be drawn. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.linkMaterial = function(value){
- useMtl = value;
- draw();
- }
- /********************************************************************
- * Function switching the alpha value of the faces. *
- * #parameters: *
- * percent (int): Value (0-100) of alpha. Must be processed (0-1) *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.setAlpha = function(percent){
- if (percent >= 0 && percent <= 100){
- faceAlpha = percent / 100;
- faceColor = faceColor.substring(0, faceColor.lastIndexOf(",") + 2) + faceAlpha + ")";
- draw();
- }
- else{
- console.log("setAlpha(" + percent + "); ERROR: Only values between 0 and 100 are allowed");
- }
- };
- /********************************************************************
- * Function switching the rotation speed. *
- * #parameters: *
- * percent (int): Value (0-10) of speed. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.setRotationSpeed = function(percent){
- if (percent >= 0 && percent <= 10){
- rotationSpeed = Math.round((10 - percent) * 30);
- if (rotationSpeed == 0){
- rotationSpeed = 1;
- }
- }
- else{
- console.log("setRotationSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
- }
- }
- /********************************************************************
- * Functions that rotate elements in the canvas along the *
- * correspondent axys, calculating the new position for each vertex *
- * in the vert array. *
- * #parameters: *
- * des (int): distance to rotate. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.rotateY = function(des){
- for (var i = 0; i < vert.length; i ++) {
- var x = vert[i][0];
- var z = vert[i][2];
- vert[i][0] = x * Math.cos(des / rotationSpeed) - z * Math.sin(des / rotationSpeed);
- vert[i][2] = z * Math.cos(des / rotationSpeed) + x * Math.sin(des / rotationSpeed);
- }
- draw();
- };
- this.rotateX = function(des){
- for (var i = 0; i < vert.length; i ++) {
- var y = vert[i][1];
- var z = vert[i][2];
- vert[i][1] = y * Math.cos(des / rotationSpeed) - z * Math.sin(des / rotationSpeed);
- vert[i][2] = z * Math.cos(des / rotationSpeed) + y * Math.sin(des / rotationSpeed);
- }
- draw();
- };
- /********************************************************************
- * Function switching the movement speed of the model. *
- * #parameters: *
- * percent (int): Value (0-10) of speed. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.setMovementSpeed = function(percent){
- if (percent >= 0 && percent <= 10){
- moveSpeed = ((percent * 0.8) / 10) + 0.01;
- }
- else{
- console.log("setMoveSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
- }
- }
- /********************************************************************
- * Functions that move elements in the canvas along the selected *
- * direction, calculating the new position for each vertex in the *
- * vert array. *
- * #parameters: *
- * des (int): distance to move. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.moveVertical = function(des){
- for (var i = 0; i < vert.length; i ++){
- vert[i][1] = (vert[i][1] * 1) + des * (moveSpeed / 50);
- }
- draw();
- };
- this.moveHorizontal = function(des){
- for (var i = 0; i < vert.length; i ++){
- vert[i][0] = (vert[i][0] * 1) + des * (moveSpeed / 50);
- }
- draw();
- };
- /********************************************************************
- * Function switching the zoom speede of the faces. *
- * #parameters: *
- * percent (int): Value (0-100) of speed. *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.setZoomSpeed = function(percent){
- if (percent >= 0 && percent <= 10){
- zoomSpeed = ((percent * 0.8) / 10) + 0.01;
- }
- else{
- console.log("setZoomSpeed(" + percent + "); ERROR: Only values between 0 and 10 are allowed");
- }
- }
- /********************************************************************
- * Function that changes the scale variable, witch results in a *
- * zoom in or zoom out effect. *
- * #parameters: none *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.zoom = function(val){
- scale = scale + val * scale * zoomSpeed;
- draw();
- };
- /********************************************************************
- * Function that clears the arrays and the canvas. Use to unload a *
- * model. *
- * #parameters: none *
- * #return: nothing *
- * #scope: public *
- ********************************************************************/
- this.clear = function(){
- initArrays();
- clearCanvas();
- };
- /********************************************************************
- * Function that turns a hex color code in RGB. *
- * stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb *
- * #parameters: *
- * hex (string): color code, for example "#33ff99" *
- * #return: (Array) r, g, b, with their correspondent color. *
- * #scope: private *
- ********************************************************************/
- var hexToRgb = function(hex) {
- var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- return result ? {
- r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16)
- } : null;
- };
- /********************************************************************
- * Function that checks if a string is a valid hexadecimal color *
- * code, in the format '#ffffff' or '#fff' *
- * #parameters: *
- * code (string): Code to check. *
- * #return: (Boolean) True if valid color code, false otherwise. *
- * #scope: private *
- ********************************************************************/
- var isColor = function(code){
- if (code[0] != '#'){
- return false;
- }
- if (code.length != 4 && code.length != 7){
- return false
- }
- for (var i = 1; i < code.length; i ++){
- if (code[i] < '0' || (code[i] > 9 && code[i] < 'A') || (code[i] > 'Z' && code[i] < 'a') || code[i] > 'z'){
- return false;
- }
- }
- return true;
- };
- /********************************************************************
- * Function that clears the canvas and gets it ready to draw on it. *
- * #parameters: none *
- * #return: nothing *
- * #scope: private *
- ********************************************************************/
- var clearCanvas = function(){
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- if (dBackg){
- ctx.fillStyle = backColor;
- }
- else{
- ctx.fillStyle = "#ffffff";
- }
- ctx.fillRect(-canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
- };
- /********************************************************************
- * Function initializing the arrays obj, vert and face as empty *
- * arrays. *
- * #parameters: none *
- * #return: nothing *
- * #scope: private *
- ********************************************************************/
- var initArrays = function(){
- material = null;
- vert = null;
- face = null;
- material = new Array();
- vert = new Array();
- face = new Array();
- };
- /********************************************************************
- * Function tat populates the vert[] array with and array of three *
- * elements, containig the coordinates, and the face[] array with *
- * the vertices forming the face, plus one element indicating the *
- * codename of the material. *
- * #parameters: *
- * text (string): the content of the obj file. *
- * #return: nothing *
- * #scope: private *
- ********************************************************************/
- var readFile = function(text){
- var line;
- var v = 0;
- var f = 0;
- var m = 0;
- var max = 0;
- var j;
- var dist;
- var fac;
- var mat = "null";
- text = text + "\n";
- while (text.indexOf("\n") != -1){
- line = text.substring(0, text.indexOf("\n"));
- if (line.substring(0, 2) == "v "){
- vert[v] = new Array(3);
- line = line.substring(2);
- vert[v][0] = line.substring(0, line.indexOf(" "));
- line = line.substring(line.indexOf(" ") + 1);
- vert[v][1] = line.substring(0, line.indexOf(" "));
- line = line.substring(line.indexOf(" ") + 1);
- vert[v][2] = line;
- dist = Math.sqrt(vert[v][0] * vert[v][0] + vert[v][1] * vert[v][1] + vert[v][2] * vert[v][2]);
- if (dist > max){
- max = dist;
- }
- v = v + 1;
- }
- else if (line.substring(0,2) == "f "){
- line = line.substring(2);
- face[f] = new Array();
- j = 0;
- while(line.indexOf(" ") != -1){
- face[f][j] = line.substring(0, line.indexOf(" "));
- if (face[f][j].indexOf('/') != -1){
- face[f][j] = face[f][j].substring(0, face[f][j].indexOf('/'));
- }
- face[f][j] = face[f][j] - 1;
- line = line.substring(line.indexOf(" ") + 1);
- j = j + 1;
- }
- face[f][j] = line;
- if (face[f][j].indexOf('/') != -1){
- face[f][j] = face[f][j].substring(0, face[f][j].indexOf('/'));
- }
- face[f][j] = face[f][j] - 1;
- face[f][j + 1] = mat;
- f = f + 1;
- }
- else if (line.substring(0, 6) == "usemtl"){
- mat = line.substring(7);
- }
- text = text.substring(text.indexOf("\n") + 1);
- }
- scale = calculateScale(max);
- totalVert = vert.length;
- totalFace = face.length;
- };
- /********************************************************************
- * Function tat populates the material[] array with an array *
- * containig, four values: the material name, and the three RGB *
- * values. *
- * #parameters: *
- * text (string): the content of the mtl file. *
- * #return: nothing *
- * #scope: private *
- ********************************************************************/
- var readMtlFile = function(text){
- text = text + "\n";
- i = -1;
- while (text.indexOf("\n") != -1){
- line = text.substring(0, text.indexOf("\n"));
- if (line.substring(0, 6) == "newmtl"){
- i = i + 1;
- material[i] = new Array(4);
- material[i][0] = line.substring(line.indexOf(" ") + 1);
- }
- //Kd, ddifuse color, usefull
- //Ka, ambient color, not implemented
- //Ks, specular color, not implemented
- //d or TR, transparency, not now
- else if (line.substring(0, 3) == "Kd "){
- line = line.substring(3);
- material[i][1] = line.substring(0, line.indexOf(" "));
- line = line.substring(line.indexOf(" ") + 1);
- material[i][2] = line.substring(0, line.indexOf(" "));
- line = line.substring(line.indexOf(" ") + 1);
- material[i][3] = line;
- }
- text = text.substring(text.indexOf("\n") + 1);
- }
- totalMaterial = material.length;
- if (totalMaterial == 0){
- hasMtl = false;
- }
- };
- /********************************************************************
- * Function that automatically calculates the best scale for the *
- * modelermined number of elements, containing the verices that *
- * form a face, so the bigest dimension from the object can fit in *
- * the smallest dimension of the canvas. *
- * #parameters: *
- * text (int): the farthest vertex, from the center. *
- * #return: (int) The scale to be aplied. *
- * #scope: private *
- ********************************************************************/
- var calculateScale = function(max){
- var dim;
- if (canvas.width < canvas.height){
- dim = canvas.width;
- }
- else{
- dim = canvas.height;
- }
- var sc = Math.round((0.95 * dim) / (2 * max));
- return sc;
- };
- /********************************************************************
- * Function that compares the average Z coordinate of two faces. *
- * Must be used when sorting the face array. *
- * #parameters: *
- * a (Array): One face. *
- * b (Array): The other face. *
- * #return: (int) -1, 1 or 0. *
- * #scope: private *
- ********************************************************************/
- var faceComparator = function(a,b){
- var aZ;
- var bZ;
- var i = 0;
- var sum = 0.0;
- while (i < a.length - 1){
- sum = sum + parseFloat(vert[a[i]][2]);
- i = i + 1;
- }
- aZ = sum / parseFloat(i);
- i = 0;
- sum = 0;
- while (i < b.length - 1){
- sum = sum + parseFloat(1 * vert[b[i]][2]);
- i = i + 1;
- }
- bZ = sum / parseFloat(i);
- if (aZ < bZ) return -1;
- if (aZ > bZ) return 1;
- return 0;
- };
- /********************************************************************
- * Function that writes text in the corner of the canvas. Must be *
- * called when everithing has been drawn. *
- * #parameters: none *
- * #return: nothing *
- * #scope: private *
- ********************************************************************/
- var writeCredits = function(){
- /*ctx.fillStyle = textColor;
- ctx.font = "12px Arial";
- var h = canvas.height / 2 - 14;
- var w = 10 - (canvas.width / 2);
- ctx.fillText("ObJS: " + totalVert + " vertices " + totalFace + " faces " + totalMaterial + " materials", w, h);*/
- document.getElementById('details_verts').innerHTML = totalVert + ' vertices';
- document.getElementById('details_faces').innerHTML = totalFace + ' faces';
- document.getElementById('details_materials').innerHTML = totalMaterial + ' materials';
- };
- var drawSides = function(){
- var sScale = scale * 0.2;
- // TOP
- topCtx.clearRect(0, 0, canvas.width, canvas.height);
- topCtx.fillStyle = "#ffffff";
- topCtx.fillRect(-topCanvas.width / 2, -topCanvas.height / 2, topCanvas.width, canvas.height);
- var des = Math.PI / 2;
- for (var i = 0; i < vert.length; i ++) {
- var y = vert[i][1];
- var z = vert[i][2];
- vert[i][1] = y * Math.cos(des) - z * Math.sin(des);
- vert[i][2] = z * Math.cos(des) + y * Math.sin(des);
- }
- for (var i = 0; i < face.length; i++){
- topCtx.strokeStyle = '#00000099';
- topCtx.beginPath();
- topCtx.moveTo(50 + (sScale * vert[face[i][0]][0]), 50 + (sScale * vert[face[i][0]][1]));
- for (var j = 1; j < face[i].length - 1; j++) {
- topCtx.lineTo(50 + (sScale * vert[face[i][j]][0]), 50 + (sScale * vert[face[i][j]][1]));
- //topCtx.stroke();
- }
- topCtx.closePath();
- if (useMtl){
- //Get material with the same name as the one in the last element of the face
- j = 0;
- while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
- j = j + 1;
- }
- if (material[j][0] == face[i][face[i].length - 1]){
- topCtx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + 1 + ")";
- }
- else{
- topCtx.fillStyle = faceColor;
- }
- }
- else{
- topCtx.fillStyle = faceColor;
- }
- topCtx.stroke();
- if(dFaces){
- topCtx.fill();
- }
- }
- //restore
- des = Math.PI / -2;
- for (var i = 0; i < vert.length; i ++) {
- var y = vert[i][1];
- var z = vert[i][2];
- vert[i][1] = y * Math.cos(des) - z * Math.sin(des);
- vert[i][2] = z * Math.cos(des) + y * Math.sin(des);
- }
- // FRONT
- frontCtx.clearRect(0, 0, canvas.width, canvas.height);
- frontCtx.fillStyle = "#ffffff";
- frontCtx.fillRect(-frontCanvas.width / 2, -frontCanvas.height / 2, frontCanvas.width, canvas.height);
- des = Math.PI / 2;
- for (var i = 0; i < face.length; i++){
- frontCtx.strokeStyle = '#00000099';
- frontCtx.beginPath();
- frontCtx.moveTo(50 + (sScale * vert[face[i][0]][0]), 50 + (sScale * vert[face[i][0]][1]));
- for (var j = 1; j < face[i].length - 1; j++) {
- frontCtx.lineTo(50 + (sScale * vert[face[i][j]][0]), 50 + (sScale * vert[face[i][j]][1]));
- //frontCtx.stroke();
- }
- frontCtx.closePath();
- if (useMtl){
- //Get material with the same name as the one in the last element of the face
- j = 0;
- while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
- j = j + 1;
- }
- if (material[j][0] == face[i][face[i].length - 1]){
- frontCtx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + 1 + ")";
- }
- else{
- frontCtx.fillStyle = faceColor;
- }
- }
- else{
- frontCtx.fillStyle = faceColor;
- }
- frontCtx.stroke();
- if(dFaces){
- frontCtx.fill();
- }
- }
- // SIDE
- sideCtx.clearRect(0, 0, canvas.width, canvas.height);
- sideCtx.fillStyle = "#ffffff";
- sideCtx.fillRect(-sideCanvas.width / 2, -sideCanvas.height / 2, sideCanvas.width, canvas.height);
- des = Math.PI / 2;
- for (var i = 0; i < vert.length; i ++) {
- var x = vert[i][0];
- var z = vert[i][2];
- vert[i][0] = x * Math.cos(des) - z * Math.sin(des);
- vert[i][2] = z * Math.cos(des) + x * Math.sin(des);
- }
- for (var i = 0; i < face.length; i++){
- sideCtx.strokeStyle = '#00000099';
- sideCtx.beginPath();
- sideCtx.moveTo(50 + (sScale * vert[face[i][0]][0]), 50 + (sScale * vert[face[i][0]][1]));
- for (var j = 1; j < face[i].length - 1; j++) {
- sideCtx.lineTo(50 + (sScale * vert[face[i][j]][0]), 50 + (sScale * vert[face[i][j]][1]));
- //sideCtx.stroke();
- }
- sideCtx.closePath();
- if (useMtl){
- //Get material with the same name as the one in the last element of the face
- j = 0;
- while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
- j = j + 1;
- }
- if (material[j][0] == face[i][face[i].length - 1]){
- sideCtx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + 1 + ")";
- }
- else{
- sideCtx.fillStyle = faceColor;
- }
- }
- else{
- sideCtx.fillStyle = faceColor;
- }
- sideCtx.stroke();
- if(dFaces){
- sideCtx.fill();
- }
- }
- //Restore
- des = Math.PI / -2;
- for (var i = 0; i < vert.length; i ++) {
- var x = vert[i][0];
- var z = vert[i][2];
- vert[i][0] = x * Math.cos(des) - z * Math.sin(des);
- vert[i][2] = z * Math.cos(des) + x * Math.sin(des);
- }
- };
- /********************************************************************
- * Function that draws th vertices, faces and edges in the canvas. *
- * #parameters: none *
- * #return: nothing *
- * #scope: private *
- ********************************************************************/
- var draw = function(){
- face = face.sort(faceComparator).reverse();
- clearCanvas();
- for (var i = 0; i < face.length; i++){
- ctx.strokeStyle = edgeColor;
- ctx.fillStyle = vertColor;
- ctx.beginPath();
- ctx.moveTo(scale * vert[face[i][0]][0], scale * vert[face[i][0]][1]);
- for (var j = 1; j < face[i].length - 1; j++) {
- ctx.lineTo(scale * vert[face[i][j]][0], scale * vert[face[i][j]][1]);
- if(dEdges){
- ctx.stroke();
- }
- if(dVerts){
- var x = scale * vert[face[i][j]][0] - (vertSize / 2);
- var w = vertSize;
- var y = scale * vert[face[i][j]][1] - (vertSize / 2);
- var h = vertSize;
- ctx.fillRect(x, y, w, h);
- }
- }
- ctx.closePath();
- if (useMtl){
- //Get material with the same name as the one in the last element of the face
- j = 0;
- while (material[j][0] != face[i][face[i].length - 1] && j < material.length){
- j = j + 1;
- }
- if (material[j][0] == face[i][face[i].length - 1]){
- ctx.fillStyle = "rgba(" + Math.round(255 * material[j][1]) + ", " + Math.round(255 * material[j][2]) + ", " + Math.round(255 * material[j][3]) + ", " + faceAlpha + ")";
- }
- else{
- ctx.fillStyle = faceColor;
- }
- }
- else{
- ctx.fillStyle = faceColor;
- }
- if(dFaces){
- ctx.fill();
- }
- }
- writeCredits();
- };
- /********************************************************************
- * TODO: Function used when something in the canvas is clicked. *
- * #parameters: none *
- * #return: nothing *
- * #scope: private *
- ********************************************************************/
- var mouseClick = function(){
- };
- thisObJS = this;
- return this;
- }
- </script>
- <style>
- body{
- text-align: center;
- background-color: #002200;
- }
- h1{
- margin-bottom: 0;
- color: #ffffff;
- }
- h2{
- font-size: 110%;
- font-style: italic;
- margin-top: 0;
- color: #ffffff;
- }
- section{
- display: inline-block;
- vertical-align: top;
- height: 526px;
- margin: 0;
- border: 2px solid #000000;
- }
- section h3{
- height: 24px;
- margin: 0;
- border-bottom: 2px solid #000000;
- background-color: #cceeff;
- }
- section#sideViews{
- width: 120px;
- background-color: #e0f5ff;
- border-top-left-radius: 20px;
- border-bottom-left-radius: 20px;
- }
- section#sideViews h3{
- border-top-left-radius: 20px;
- }
- section#sideViews h4{
- margin: 10px auto 0 auto;
- }
- section#sideViews canvas{
- border: 0.1em solid #000000;
- margin: 0 10px 10px 10px;
- background-color: #ffffff;
- }
- section#sideViews div#details{
- margin-top: 20px;
- }
- section#sideViews div#details span{
- font-size: 70%;
- display: block;
- color: #444444;
- font-weight: bold;
- }
- section#controls{
- text-align: center;
- width: 260px;
- background-color: #e0f5ff;
- border-top-right-radius: 20px;
- border-bottom-right-radius: 20px;
- }
- section#controls h3{
- border-top-right-radius: 20px;
- margin-bottom: 20px;
- }
- section#controls table#colors{
- margin: 20px auto;
- border-collapse: collapse;
- }
- section#controls table#colors td{
- border: 1px solid #000000;
- }
- section#controls table#colors td img{
- width: 30px;
- height: 30px;
- }
- section#controls table#colors td input[type="color"]{
- width: 30px;
- height: 30px;
- padding: 1px;
- }
- section#controls input[type="range"]{
- display: block;
- width: 80%;
- margin: auto auto 15px auto;
- }
- canvas#ObJSView{
- width: 500px;
- height: 500px;
- }
- td#ObJSControl input[type="color"]{
- width: 1.3em;
- height: 1.3em;
- padding: 0;
- vertical-align: middle;
- }
- td#ObJSControl input[type="range"]{
- vertical-align: middle;
- }
- </style>
- </head>
- <body onload="objs = new ObJS(); objs.initCanvas();objs.load(document.getElementById('objSelector').value);">
- <h1>
- ObJS
- </h1>
- <h2>
- by Iñigo Valentin
- </h2>
- <section id='sideViews'>
- <h3>
- Details
- </h3>
- <h4>
- Top
- </h4>
- <canvas id="ObJSTop" width="100" height="100">
- Your browser does not support the HTML5 canvas tag.
- </canvas>
- <h4>
- Front
- </h4>
- <canvas id="ObJSFront" width="100" height="100">
- Your browser does not support the HTML5 canvas tag.
- </canvas>
- <h4>
- Side
- </h4>
- <canvas id="ObJSSide" width="100" height="100">
- Your browser does not support the HTML5 canvas tag.
- </canvas>
- <div id='details'>
- <span id='details_verts'></span>
- <span id='details_faces'></span>
- <span id='details_materials'></span>
- </div>
- </section>
- <section id='view'>
- <h3>
- View
- </h3>
- <canvas id="ObJSView" width="500" height="500">
- Your browser does not support the HTML5 canvas tag.
- </canvas>
- </section>
- <section id='controls'>
- <h3>
- Controls
- </h3>
- Choose a model:
- <select id="objSelector" onChange="objs.load(this.value)">
- <option value="obj/rainbow.obj" selected="selected">Rainbow</option>
- <option value="obj/cube.obj">Cube</option>
- <option value="obj/sphere.obj">Sphere</option>
- <option value="obj/star.obj">Star</option>
- <option value="obj/monkey.obj">Monkey</option>
- <option value="obj/bolt.obj">Bolt</option>
- <option value="obj/multiple.obj">Multiple Objects</option>
- <option value="obj/seaplane.obj">Seaplane (Can freeze browser)</option>
- </select>
- <table id='colors'>
- <tr>
- <td>
- </td>
- <td>
- <img src='img/eye.png'/>
- </td>
- <td>
- <img src='img/color.png'/>
- </td>
- </tr>
- <tr>
- <td>
- <img src='img/vert.png'/>
- </td>
- <td>
- <input type="checkbox" value="verts" checked onChange="objs.drawVertizes(this.checked);"/>
- </td>
- <td>
- <input type="color" value="#0A0A0A" onChange="objs.severtizesColor(this.value);"/>
- </td>
- </tr>
- <tr>
- <td>
- <img src='img/line.png'/>
- </td>
- <td>
- <input type="checkbox" value="edges" checked onChange="objs.drawEdges(this.checked);"/>
- </td>
- <td>
- <input type="color" value="#644646" onChange="objs.setEdgesColor(this.value);"/>
- </td>
- </tr>
- <tr>
- <td>
- <img src='img/face.png'/>
- </td>
- <td>
- <input type="checkbox" value="faces" checked onChange="objs.drawFaces(this.checked);"/>
- </td>
- <td>
- <input type="color" value="#B4B4FF" onChange="objs.sefacesColor(this.value);"/>
- <!-- <input type="checkbox" checked onChange="objs.linkMaterial(this.checked);"/>Use materials -->
- </td>
- </tr>
- <tr>
- <td>
- <img src='img/bg.png'/>
- </td>
- <td>
- <input type="checkbox" value="backg" checked onChange="objs.drawBackground(this.checked);"/>
- </td>
- <td>
- <input type="color" value="#C8C8C8" onChange="objs.setBackgroundColor(this.value)"/>
- </td>
- </tr>
- </table>
- Alpha:
- <input type="range" min="0" max="100" step="1" value="60" onInput="objs.setAlpha(this.value);" onChange="objs.setAlpha(this.value);"/>
- Rotation speed:
- <input type="range" min="0" max="10" step="1" value="3" onInput="objs.setRotationSpeed(this.value);" onChange="objs.setRotationSpeed(this.value);"/>
- Zoom speed:
- <input type="range" min="0" max="10" step="1" value="2" onInput="objs.setZoomSpeed(this.value);" onChange="objs.setZoomSpeed(this.value);"/>
- Movement speed:
- <input type="range" min="0" max="10" step="1" value="5" onInput="objs.setMovementSpeed(this.value);" onChange="objs.setMovementSpeed(this.value);"/>
- </section>
- </body>
- </html>
|