|
|
@@ -1,58 +1,1271 @@
|
|
|
<!DOCTYPE html>
|
|
|
<html>
|
|
|
- <head>
|
|
|
- <meta content="text/html; charset=windows-1252" http-equiv="content-type"/>
|
|
|
- <title>ObJS</title>
|
|
|
- <!-- Script file -->
|
|
|
- <script type="text/javascript" src="script/ObJS.js"></script>
|
|
|
- </head>
|
|
|
- <body onload="objs = new ObJS(); objs.initCanvas();objs.load(document.getElementById('objSelector').value);">
|
|
|
- <table>
|
|
|
- <tr>
|
|
|
- <td>
|
|
|
- <canvas id="ObJSCanvas" width="500" height="500">
|
|
|
- Your browser does not support the HTML5 canvas tag.
|
|
|
- </canvas>
|
|
|
- </td>
|
|
|
- <td id="ObJSControl">
|
|
|
- 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>
|
|
|
- <br/><br/><br/>
|
|
|
- <input type="checkbox" value="backg" checked onChange="objs.drawBackground(this.checked);"/>Background
|
|
|
- <input type="color" value="#C8C8C8" onChange="objs.setBackgroundColor(this.value)"/>
|
|
|
- <br/>
|
|
|
- <input type="checkbox" value="verts" checked onChange="objs.drawVertizes(this.checked);"/>Vertices
|
|
|
- <input type="color" value="#0A0A0A" onChange="objs.setVertizesColor(this.value);"/>
|
|
|
- <br/>
|
|
|
- <input type="checkbox" value="edges" checked onChange="objs.drawEdges(this.checked);"/>Edges
|
|
|
- <input type="color" value="#644646" onChange="objs.setEdgesColor(this.value);"/>
|
|
|
- <br/>
|
|
|
- <input type="checkbox" value="faces" checked onChange="objs.drawFaces(this.checked);"/>Faces
|
|
|
- <input type="color" value="#B4B4FF" onChange="objs.setFacesColor(this.value);"/>
|
|
|
- <input type="checkbox" checked onChange="objs.linkMaterial(this.checked);"/>Use materials
|
|
|
- <br/><br/>
|
|
|
- Alpha:<br/>
|
|
|
- 0% <input type="range" min="0" max="100" step="1" value="60" onInput="objs.setAlpha(this.value);" onChange="objs.setAlpha(this.value);"/>100%
|
|
|
- <br/><br/>
|
|
|
- Rotation speed:<br/>
|
|
|
- 0% <input type="range" min="0" max="10" step="1" value="3" onInput="objs.setRotationSpeed(this.value);" onChange="objs.setRotationSpeed(this.value);"/>100%
|
|
|
- <br/><br/>
|
|
|
- Zoom speed:<br/>
|
|
|
- 0% <input type="range" min="0" max="10" step="1" value="2" onInput="objs.setZoomSpeed(this.value);" onChange="objs.setZoomSpeed(this.value);"/>100%
|
|
|
- <br/><br/>
|
|
|
- Movement speed:<br/>
|
|
|
- 0% <input type="range" min="0" max="10" step="1" value="5" onInput="objs.setMovementSpeed(this.value);" onChange="objs.setMovementSpeed(this.value);"/>100%
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
- </body>
|
|
|
-</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>
|