├── samples └── demo │ ├── male02.ctm │ ├── loader.js │ ├── demo.html │ ├── glMatrix-0.9.5.min.js │ ├── lzma.js │ └── ctm.js ├── LICENSE.txt ├── README.md └── src ├── libs └── lzma.js └── ctm.js /samples/demo/male02.ctm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jcmellado/js-openctm/HEAD/samples/demo/male02.ctm -------------------------------------------------------------------------------- /samples/demo/loader.js: -------------------------------------------------------------------------------- 1 | 2 | importScripts("lzma.js", "ctm.js"); 3 | 4 | self.onmessage = function(event){ 5 | self.postMessage( new CTM.File( new CTM.Stream(event.data) ) ); 6 | self.close(); 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Juan Mellado 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | 22 | OpenCTM 23 | ======= 24 | Copyright (c) 2009-2010 Marcus Geelnard 25 | 26 | This software is provided 'as-is', without any express or implied 27 | warranty. In no event will the authors be held liable for any damages 28 | arising from the use of this software. 29 | 30 | Permission is granted to anyone to use this software for any purpose, 31 | including commercial applications, and to alter it and redistribute it 32 | freely, subject to the following restrictions: 33 | 34 | 1. The origin of this software must not be misrepresented; you must not 35 | claim that you wrote the original software. If you use this software 36 | in a product, an acknowledgment in the product documentation would be 37 | appreciated but is not required. 38 | 39 | 2. Altered source versions must be plainly marked as such, and must not 40 | be misrepresented as being the original software. 41 | 42 | 3. This notice may not be removed or altered from any source 43 | distribution. 44 | 45 | 46 | js-lzma 47 | ======= 48 | Copyright (c) 2011 Juan Mellado 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy 51 | of this software and associated documentation files (the "Software"), to deal 52 | in the Software without restriction, including without limitation the rights 53 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 54 | copies of the Software, and to permit persons to whom the Software is 55 | furnished to do so, subject to the following conditions: 56 | 57 | The above copyright notice and this permission notice shall be included in 58 | all copies or substantial portions of the Software. 59 | 60 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 61 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 62 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 63 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 64 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 65 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 66 | THE SOFTWARE. 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **js-openctm** is a JavaScript library for reading OpenCTM files. 2 | 3 | [OpenCTM](http://openctm.sourceforge.net) is a file format for storing 3D triangle meshes created by Marcus Geelnard. 4 | 5 | Some parts of this library are a direct traslation to JavaScript from original C source code found on the OpenCTM SDK. 6 | 7 | ### Demo ### 8 | 9 | - [Live demo!](https://jcmellado.github.io/showcase/js/js-openctm/demo/index.html) 10 | 11 | - [Using Three.js to render](http://alteredqualia.com/three/examples/webgl_loader_ctm_materials.html) 12 | 13 | ### Video ### 14 | 15 | [js-openctm](https://jcmellado.github.io/showcase/videos/js-openctm_%20WebGL%20demo.mp4) 16 | 17 | ### How to use it? ### 18 | 19 | **1)** Retrieve your `.ctm` file from the web, as usual: 20 | 21 | ``` 22 | function retrieve(url){ 23 | var request = new XMLHttpRequest(); 24 | request.open("GET", url, false); 25 | request.overrideMimeType("text/plain; charset=x-user-defined"); 26 | request.send(); 27 | if ( (200 !== request.status) && (0 !== request.status) ){ 28 | throw new Error(request.status + " Retrieving " + url); 29 | } 30 | return request; 31 | }; 32 | 33 | var request = retrieve(""); 34 | ``` 35 | 36 | **2)** Create a `CTM.Stream` object from the retrieved file: 37 | 38 | ``` 39 | var stream = new CTM.Stream(request.responseText); 40 | ``` 41 | 42 | **3)** Create a `CTM.File` object from the stream: 43 | 44 | ``` 45 | var file = new CTM.File(stream); 46 | ``` 47 | 48 | ### What do you get? ### 49 | 50 | `CTM.File` objects have two properties: 51 | 52 | * `header`, an object with the following properties: 53 | * `fileFormat`: File format version (always 5) 54 | * `compressionMethod`: Compression method (RAW, MG1 or MG2) 55 | * `vertexCount`: Vertex count 56 | * `triangleCount`: Triangle count 57 | * `uvMapCount`: UV map count 58 | * `attrMapCount`: Attribute map count 59 | * `flags`: Boolean flags (bit 0 means that normals are present) 60 | * `comment`: File comment 61 | 62 | * `body`, an object with the following properties: 63 | * `indices`: A `Uint32Array` buffer with 3 x `header.triangleCount` elements 64 | * `vertices`: A `Float32Array` buffer with `header.vertexCount` elements 65 | * `normals`: (optional) A `Float32Array` buffer with `header.vertexCount` elements 66 | * `uvMaps`: (optional) An array with `header.uvMapCount` elements, each of them with the following properties: 67 | * `name`: UV map name 68 | * `filename`: UV map ?le name 69 | * `uv`: A `Float32Array` buffer with 2 x `header.vertexCount` elements 70 | * `attrMaps`: (optional) A array with `header.attrMapCount` elements, each of them with the following properties: 71 | * `name`: Attribute map name 72 | * `attr`: A `Float32Array` buffer with 4 x `header.vertexCount` elements 73 | 74 | ### How to draw the mesh? ### 75 | 76 | Using WebGL, of course. 77 | 78 | ``` 79 | ... 80 | indicesBuffer = gl.createBuffer(); 81 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer); 82 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(file.body.indices), gl.STATIC_DRAW); 83 | 84 | verticesBuffer = gl.createBuffer(); 85 | gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer); 86 | gl.bufferData(gl.ARRAY_BUFFER, file.body.vertices, gl.STATIC_DRAW); 87 | 88 | if (file.body.normals){ 89 | normalsBuffer = gl.createBuffer(); 90 | gl.bindBuffer(gl.ARRAY_BUFFER, normalsBuffer); 91 | gl.bufferData(gl.ARRAY_BUFFER,file.body.normals, gl.STATIC_DRAW); 92 | } 93 | 94 | if (file.body.uvMaps){ 95 | uvBuffer = gl.createBuffer(); 96 | gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer); 97 | gl.bufferData(gl.ARRAY_BUFFER, file.body.uvMaps[0].uv, gl.STATIC_DRAW); 98 | } 99 | ... 100 | ``` 101 | 102 | No idea? Try [http://learningwebgl.com](http://learningwebgl.com/blog/?page_id=1217). 103 | 104 | ### Dependencies ### 105 | [js-lzma](https://github.com/jcmellado/js-lzma): A port of the LZMA decompression algorithm to JavaScript. 106 | 107 | ### Limitations ### 108 | 109 | - Textures are not packed on `.ctm` files, you must retrieve them on your own. 110 | 111 | - Indices are exposed like a `Uint32Array` buffer, but please, remember that WebGL `drawElements` function works with `Uint16Array`, you must split them on your own. 112 | 113 | ### What about writer function? ### 114 | 115 | Sorry, don't. This library is part of another project of mine that only needed the reader algorithm. 116 | -------------------------------------------------------------------------------- /samples/demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | js-openctm - Demo 5 | 6 | 7 | 8 | 9 | 24 | 25 | 36 | 37 | 354 | 355 | 356 | 357 | 358 |
359 | 360 |

361 | male02.ctm - 842,718 vertices - 1,685,024 triangles - 6.00 MB - No texture

362 | Unpacked using js-openctm - Rendered using WebGL

363 | 3D model courtesy of Cyberware
364 |
365 | 366 | 367 | -------------------------------------------------------------------------------- /samples/demo/glMatrix-0.9.5.min.js: -------------------------------------------------------------------------------- 1 | // glMatrix v0.9.5 2 | glMatrixArrayType=typeof Float32Array!="undefined"?Float32Array:typeof WebGLFloatArray!="undefined"?WebGLFloatArray:Array;var vec3={};vec3.create=function(a){var b=new glMatrixArrayType(3);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2]}return b};vec3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];return b};vec3.add=function(a,b,c){if(!c||a==c){a[0]+=b[0];a[1]+=b[1];a[2]+=b[2];return a}c[0]=a[0]+b[0];c[1]=a[1]+b[1];c[2]=a[2]+b[2];return c}; 3 | vec3.subtract=function(a,b,c){if(!c||a==c){a[0]-=b[0];a[1]-=b[1];a[2]-=b[2];return a}c[0]=a[0]-b[0];c[1]=a[1]-b[1];c[2]=a[2]-b[2];return c};vec3.negate=function(a,b){b||(b=a);b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];return b};vec3.scale=function(a,b,c){if(!c||a==c){a[0]*=b;a[1]*=b;a[2]*=b;return a}c[0]=a[0]*b;c[1]=a[1]*b;c[2]=a[2]*b;return c}; 4 | vec3.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=Math.sqrt(c*c+d*d+e*e);if(g){if(g==1){b[0]=c;b[1]=d;b[2]=e;return b}}else{b[0]=0;b[1]=0;b[2]=0;return b}g=1/g;b[0]=c*g;b[1]=d*g;b[2]=e*g;return b};vec3.cross=function(a,b,c){c||(c=a);var d=a[0],e=a[1];a=a[2];var g=b[0],f=b[1];b=b[2];c[0]=e*b-a*f;c[1]=a*g-d*b;c[2]=d*f-e*g;return c};vec3.length=function(a){var b=a[0],c=a[1];a=a[2];return Math.sqrt(b*b+c*c+a*a)};vec3.dot=function(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]}; 5 | vec3.direction=function(a,b,c){c||(c=a);var d=a[0]-b[0],e=a[1]-b[1];a=a[2]-b[2];b=Math.sqrt(d*d+e*e+a*a);if(!b){c[0]=0;c[1]=0;c[2]=0;return c}b=1/b;c[0]=d*b;c[1]=e*b;c[2]=a*b;return c};vec3.lerp=function(a,b,c,d){d||(d=a);d[0]=a[0]+c*(b[0]-a[0]);d[1]=a[1]+c*(b[1]-a[1]);d[2]=a[2]+c*(b[2]-a[2]);return d};vec3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+"]"};var mat3={}; 6 | mat3.create=function(a){var b=new glMatrixArrayType(9);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9]}return b};mat3.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];return b};mat3.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=1;a[5]=0;a[6]=0;a[7]=0;a[8]=1;return a}; 7 | mat3.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[5];a[1]=a[3];a[2]=a[6];a[3]=c;a[5]=a[7];a[6]=d;a[7]=e;return a}b[0]=a[0];b[1]=a[3];b[2]=a[6];b[3]=a[1];b[4]=a[4];b[5]=a[7];b[6]=a[2];b[7]=a[5];b[8]=a[8];return b};mat3.toMat4=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=0;b[4]=a[3];b[5]=a[4];b[6]=a[5];b[7]=0;b[8]=a[6];b[9]=a[7];b[10]=a[8];b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; 8 | mat3.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+"]"};var mat4={};mat4.create=function(a){var b=new glMatrixArrayType(16);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15]}return b}; 9 | mat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=a[12];b[13]=a[13];b[14]=a[14];b[15]=a[15];return b};mat4.identity=function(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1;return a}; 10 | mat4.transpose=function(a,b){if(!b||a==b){var c=a[1],d=a[2],e=a[3],g=a[6],f=a[7],h=a[11];a[1]=a[4];a[2]=a[8];a[3]=a[12];a[4]=c;a[6]=a[9];a[7]=a[13];a[8]=d;a[9]=g;a[11]=a[14];a[12]=e;a[13]=f;a[14]=h;return a}b[0]=a[0];b[1]=a[4];b[2]=a[8];b[3]=a[12];b[4]=a[1];b[5]=a[5];b[6]=a[9];b[7]=a[13];b[8]=a[2];b[9]=a[6];b[10]=a[10];b[11]=a[14];b[12]=a[3];b[13]=a[7];b[14]=a[11];b[15]=a[15];return b}; 11 | mat4.determinant=function(a){var b=a[0],c=a[1],d=a[2],e=a[3],g=a[4],f=a[5],h=a[6],i=a[7],j=a[8],k=a[9],l=a[10],o=a[11],m=a[12],n=a[13],p=a[14];a=a[15];return m*k*h*e-j*n*h*e-m*f*l*e+g*n*l*e+j*f*p*e-g*k*p*e-m*k*d*i+j*n*d*i+m*c*l*i-b*n*l*i-j*c*p*i+b*k*p*i+m*f*d*o-g*n*d*o-m*c*h*o+b*n*h*o+g*c*p*o-b*f*p*o-j*f*d*a+g*k*d*a+j*c*h*a-b*k*h*a-g*c*l*a+b*f*l*a}; 12 | mat4.inverse=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=a[4],h=a[5],i=a[6],j=a[7],k=a[8],l=a[9],o=a[10],m=a[11],n=a[12],p=a[13],r=a[14],s=a[15],A=c*h-d*f,B=c*i-e*f,t=c*j-g*f,u=d*i-e*h,v=d*j-g*h,w=e*j-g*i,x=k*p-l*n,y=k*r-o*n,z=k*s-m*n,C=l*r-o*p,D=l*s-m*p,E=o*s-m*r,q=1/(A*E-B*D+t*C+u*z-v*y+w*x);b[0]=(h*E-i*D+j*C)*q;b[1]=(-d*E+e*D-g*C)*q;b[2]=(p*w-r*v+s*u)*q;b[3]=(-l*w+o*v-m*u)*q;b[4]=(-f*E+i*z-j*y)*q;b[5]=(c*E-e*z+g*y)*q;b[6]=(-n*w+r*t-s*B)*q;b[7]=(k*w-o*t+m*B)*q;b[8]=(f*D-h*z+j*x)*q; 13 | b[9]=(-c*D+d*z-g*x)*q;b[10]=(n*v-p*t+s*A)*q;b[11]=(-k*v+l*t-m*A)*q;b[12]=(-f*C+h*y-i*x)*q;b[13]=(c*C-d*y+e*x)*q;b[14]=(-n*u+p*B-r*A)*q;b[15]=(k*u-l*B+o*A)*q;return b};mat4.toRotationMat=function(a,b){b||(b=mat4.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];b[4]=a[4];b[5]=a[5];b[6]=a[6];b[7]=a[7];b[8]=a[8];b[9]=a[9];b[10]=a[10];b[11]=a[11];b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b}; 14 | mat4.toMat3=function(a,b){b||(b=mat3.create());b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[4];b[4]=a[5];b[5]=a[6];b[6]=a[8];b[7]=a[9];b[8]=a[10];return b};mat4.toInverseMat3=function(a,b){var c=a[0],d=a[1],e=a[2],g=a[4],f=a[5],h=a[6],i=a[8],j=a[9],k=a[10],l=k*f-h*j,o=-k*g+h*i,m=j*g-f*i,n=c*l+d*o+e*m;if(!n)return null;n=1/n;b||(b=mat3.create());b[0]=l*n;b[1]=(-k*d+e*j)*n;b[2]=(h*d-e*f)*n;b[3]=o*n;b[4]=(k*c-e*i)*n;b[5]=(-h*c+e*g)*n;b[6]=m*n;b[7]=(-j*c+d*i)*n;b[8]=(f*c-d*g)*n;return b}; 15 | mat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2],f=a[3],h=a[4],i=a[5],j=a[6],k=a[7],l=a[8],o=a[9],m=a[10],n=a[11],p=a[12],r=a[13],s=a[14];a=a[15];var A=b[0],B=b[1],t=b[2],u=b[3],v=b[4],w=b[5],x=b[6],y=b[7],z=b[8],C=b[9],D=b[10],E=b[11],q=b[12],F=b[13],G=b[14];b=b[15];c[0]=A*d+B*h+t*l+u*p;c[1]=A*e+B*i+t*o+u*r;c[2]=A*g+B*j+t*m+u*s;c[3]=A*f+B*k+t*n+u*a;c[4]=v*d+w*h+x*l+y*p;c[5]=v*e+w*i+x*o+y*r;c[6]=v*g+w*j+x*m+y*s;c[7]=v*f+w*k+x*n+y*a;c[8]=z*d+C*h+D*l+E*p;c[9]=z*e+C*i+D*o+E*r;c[10]=z* 16 | g+C*j+D*m+E*s;c[11]=z*f+C*k+D*n+E*a;c[12]=q*d+F*h+G*l+b*p;c[13]=q*e+F*i+G*o+b*r;c[14]=q*g+F*j+G*m+b*s;c[15]=q*f+F*k+G*n+b*a;return c};mat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1];b=b[2];c[0]=a[0]*d+a[4]*e+a[8]*b+a[12];c[1]=a[1]*d+a[5]*e+a[9]*b+a[13];c[2]=a[2]*d+a[6]*e+a[10]*b+a[14];return c}; 17 | mat4.multiplyVec4=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2];b=b[3];c[0]=a[0]*d+a[4]*e+a[8]*g+a[12]*b;c[1]=a[1]*d+a[5]*e+a[9]*g+a[13]*b;c[2]=a[2]*d+a[6]*e+a[10]*g+a[14]*b;c[3]=a[3]*d+a[7]*e+a[11]*g+a[15]*b;return c}; 18 | mat4.translate=function(a,b,c){var d=b[0],e=b[1];b=b[2];if(!c||a==c){a[12]=a[0]*d+a[4]*e+a[8]*b+a[12];a[13]=a[1]*d+a[5]*e+a[9]*b+a[13];a[14]=a[2]*d+a[6]*e+a[10]*b+a[14];a[15]=a[3]*d+a[7]*e+a[11]*b+a[15];return a}var g=a[0],f=a[1],h=a[2],i=a[3],j=a[4],k=a[5],l=a[6],o=a[7],m=a[8],n=a[9],p=a[10],r=a[11];c[0]=g;c[1]=f;c[2]=h;c[3]=i;c[4]=j;c[5]=k;c[6]=l;c[7]=o;c[8]=m;c[9]=n;c[10]=p;c[11]=r;c[12]=g*d+j*e+m*b+a[12];c[13]=f*d+k*e+n*b+a[13];c[14]=h*d+l*e+p*b+a[14];c[15]=i*d+o*e+r*b+a[15];return c}; 19 | mat4.scale=function(a,b,c){var d=b[0],e=b[1];b=b[2];if(!c||a==c){a[0]*=d;a[1]*=d;a[2]*=d;a[3]*=d;a[4]*=e;a[5]*=e;a[6]*=e;a[7]*=e;a[8]*=b;a[9]*=b;a[10]*=b;a[11]*=b;return a}c[0]=a[0]*d;c[1]=a[1]*d;c[2]=a[2]*d;c[3]=a[3]*d;c[4]=a[4]*e;c[5]=a[5]*e;c[6]=a[6]*e;c[7]=a[7]*e;c[8]=a[8]*b;c[9]=a[9]*b;c[10]=a[10]*b;c[11]=a[11]*b;c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15];return c}; 20 | mat4.rotate=function(a,b,c,d){var e=c[0],g=c[1];c=c[2];var f=Math.sqrt(e*e+g*g+c*c);if(!f)return null;if(f!=1){f=1/f;e*=f;g*=f;c*=f}var h=Math.sin(b),i=Math.cos(b),j=1-i;b=a[0];f=a[1];var k=a[2],l=a[3],o=a[4],m=a[5],n=a[6],p=a[7],r=a[8],s=a[9],A=a[10],B=a[11],t=e*e*j+i,u=g*e*j+c*h,v=c*e*j-g*h,w=e*g*j-c*h,x=g*g*j+i,y=c*g*j+e*h,z=e*c*j+g*h;e=g*c*j-e*h;g=c*c*j+i;if(d){if(a!=d){d[12]=a[12];d[13]=a[13];d[14]=a[14];d[15]=a[15]}}else d=a;d[0]=b*t+o*u+r*v;d[1]=f*t+m*u+s*v;d[2]=k*t+n*u+A*v;d[3]=l*t+p*u+B* 21 | v;d[4]=b*w+o*x+r*y;d[5]=f*w+m*x+s*y;d[6]=k*w+n*x+A*y;d[7]=l*w+p*x+B*y;d[8]=b*z+o*e+r*g;d[9]=f*z+m*e+s*g;d[10]=k*z+n*e+A*g;d[11]=l*z+p*e+B*g;return d};mat4.rotateX=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[4],g=a[5],f=a[6],h=a[7],i=a[8],j=a[9],k=a[10],l=a[11];if(c){if(a!=c){c[0]=a[0];c[1]=a[1];c[2]=a[2];c[3]=a[3];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[4]=e*b+i*d;c[5]=g*b+j*d;c[6]=f*b+k*d;c[7]=h*b+l*d;c[8]=e*-d+i*b;c[9]=g*-d+j*b;c[10]=f*-d+k*b;c[11]=h*-d+l*b;return c}; 22 | mat4.rotateY=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[0],g=a[1],f=a[2],h=a[3],i=a[8],j=a[9],k=a[10],l=a[11];if(c){if(a!=c){c[4]=a[4];c[5]=a[5];c[6]=a[6];c[7]=a[7];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[0]=e*b+i*-d;c[1]=g*b+j*-d;c[2]=f*b+k*-d;c[3]=h*b+l*-d;c[8]=e*d+i*b;c[9]=g*d+j*b;c[10]=f*d+k*b;c[11]=h*d+l*b;return c}; 23 | mat4.rotateZ=function(a,b,c){var d=Math.sin(b);b=Math.cos(b);var e=a[0],g=a[1],f=a[2],h=a[3],i=a[4],j=a[5],k=a[6],l=a[7];if(c){if(a!=c){c[8]=a[8];c[9]=a[9];c[10]=a[10];c[11]=a[11];c[12]=a[12];c[13]=a[13];c[14]=a[14];c[15]=a[15]}}else c=a;c[0]=e*b+i*d;c[1]=g*b+j*d;c[2]=f*b+k*d;c[3]=h*b+l*d;c[4]=e*-d+i*b;c[5]=g*-d+j*b;c[6]=f*-d+k*b;c[7]=h*-d+l*b;return c}; 24 | mat4.frustum=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=e*2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=e*2/i;f[6]=0;f[7]=0;f[8]=(b+a)/h;f[9]=(d+c)/i;f[10]=-(g+e)/j;f[11]=-1;f[12]=0;f[13]=0;f[14]=-(g*e*2)/j;f[15]=0;return f};mat4.perspective=function(a,b,c,d,e){a=c*Math.tan(a*Math.PI/360);b=a*b;return mat4.frustum(-b,b,-a,a,c,d,e)}; 25 | mat4.ortho=function(a,b,c,d,e,g,f){f||(f=mat4.create());var h=b-a,i=d-c,j=g-e;f[0]=2/h;f[1]=0;f[2]=0;f[3]=0;f[4]=0;f[5]=2/i;f[6]=0;f[7]=0;f[8]=0;f[9]=0;f[10]=-2/j;f[11]=0;f[12]=-(a+b)/h;f[13]=-(d+c)/i;f[14]=-(g+e)/j;f[15]=1;return f}; 26 | mat4.lookAt=function(a,b,c,d){d||(d=mat4.create());var e=a[0],g=a[1];a=a[2];var f=c[0],h=c[1],i=c[2];c=b[1];var j=b[2];if(e==b[0]&&g==c&&a==j)return mat4.identity(d);var k,l,o,m;c=e-b[0];j=g-b[1];b=a-b[2];m=1/Math.sqrt(c*c+j*j+b*b);c*=m;j*=m;b*=m;k=h*b-i*j;i=i*c-f*b;f=f*j-h*c;if(m=Math.sqrt(k*k+i*i+f*f)){m=1/m;k*=m;i*=m;f*=m}else f=i=k=0;h=j*f-b*i;l=b*k-c*f;o=c*i-j*k;if(m=Math.sqrt(h*h+l*l+o*o)){m=1/m;h*=m;l*=m;o*=m}else o=l=h=0;d[0]=k;d[1]=h;d[2]=c;d[3]=0;d[4]=i;d[5]=l;d[6]=j;d[7]=0;d[8]=f;d[9]= 27 | o;d[10]=b;d[11]=0;d[12]=-(k*e+i*g+f*a);d[13]=-(h*e+l*g+o*a);d[14]=-(c*e+j*g+b*a);d[15]=1;return d};mat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+", "+a[4]+", "+a[5]+", "+a[6]+", "+a[7]+", "+a[8]+", "+a[9]+", "+a[10]+", "+a[11]+", "+a[12]+", "+a[13]+", "+a[14]+", "+a[15]+"]"};quat4={};quat4.create=function(a){var b=new glMatrixArrayType(4);if(a){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3]}return b};quat4.set=function(a,b){b[0]=a[0];b[1]=a[1];b[2]=a[2];b[3]=a[3];return b}; 28 | quat4.calculateW=function(a,b){var c=a[0],d=a[1],e=a[2];if(!b||a==b){a[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e));return a}b[0]=c;b[1]=d;b[2]=e;b[3]=-Math.sqrt(Math.abs(1-c*c-d*d-e*e));return b};quat4.inverse=function(a,b){if(!b||a==b){a[0]*=1;a[1]*=1;a[2]*=1;return a}b[0]=-a[0];b[1]=-a[1];b[2]=-a[2];b[3]=a[3];return b};quat4.length=function(a){var b=a[0],c=a[1],d=a[2];a=a[3];return Math.sqrt(b*b+c*c+d*d+a*a)}; 29 | quat4.normalize=function(a,b){b||(b=a);var c=a[0],d=a[1],e=a[2],g=a[3],f=Math.sqrt(c*c+d*d+e*e+g*g);if(f==0){b[0]=0;b[1]=0;b[2]=0;b[3]=0;return b}f=1/f;b[0]=c*f;b[1]=d*f;b[2]=e*f;b[3]=g*f;return b};quat4.multiply=function(a,b,c){c||(c=a);var d=a[0],e=a[1],g=a[2];a=a[3];var f=b[0],h=b[1],i=b[2];b=b[3];c[0]=d*b+a*f+e*i-g*h;c[1]=e*b+a*h+g*f-d*i;c[2]=g*b+a*i+d*h-e*f;c[3]=a*b-d*f-e*h-g*i;return c}; 30 | quat4.multiplyVec3=function(a,b,c){c||(c=b);var d=b[0],e=b[1],g=b[2];b=a[0];var f=a[1],h=a[2];a=a[3];var i=a*d+f*g-h*e,j=a*e+h*d-b*g,k=a*g+b*e-f*d;d=-b*d-f*e-h*g;c[0]=i*a+d*-b+j*-h-k*-f;c[1]=j*a+d*-f+k*-b-i*-h;c[2]=k*a+d*-h+i*-f-j*-b;return c};quat4.toMat3=function(a,b){b||(b=mat3.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c=c*i;var l=d*h;d=d*i;e=e*i;f=g*f;h=g*h;g=g*i;b[0]=1-(l+e);b[1]=k-g;b[2]=c+h;b[3]=k+g;b[4]=1-(j+e);b[5]=d-f;b[6]=c-h;b[7]=d+f;b[8]=1-(j+l);return b}; 31 | quat4.toMat4=function(a,b){b||(b=mat4.create());var c=a[0],d=a[1],e=a[2],g=a[3],f=c+c,h=d+d,i=e+e,j=c*f,k=c*h;c=c*i;var l=d*h;d=d*i;e=e*i;f=g*f;h=g*h;g=g*i;b[0]=1-(l+e);b[1]=k-g;b[2]=c+h;b[3]=0;b[4]=k+g;b[5]=1-(j+e);b[6]=d-f;b[7]=0;b[8]=c-h;b[9]=d+f;b[10]=1-(j+l);b[11]=0;b[12]=0;b[13]=0;b[14]=0;b[15]=1;return b};quat4.slerp=function(a,b,c,d){d||(d=a);var e=c;if(a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3]<0)e=-1*c;d[0]=1-c*a[0]+e*b[0];d[1]=1-c*a[1]+e*b[1];d[2]=1-c*a[2]+e*b[2];d[3]=1-c*a[3]+e*b[3];return d}; 32 | quat4.str=function(a){return"["+a[0]+", "+a[1]+", "+a[2]+", "+a[3]+"]"}; -------------------------------------------------------------------------------- /samples/demo/lzma.js: -------------------------------------------------------------------------------- 1 | 2 | var LZMA = LZMA || {}; 3 | 4 | LZMA.OutWindow = function(){ 5 | this._windowSize = 0; 6 | }; 7 | 8 | LZMA.OutWindow.prototype.create = function(windowSize){ 9 | if ( (!this._buffer) || (this._windowSize !== windowSize) ){ 10 | this._buffer = []; 11 | } 12 | this._windowSize = windowSize; 13 | this._pos = 0; 14 | this._streamPos = 0; 15 | }; 16 | 17 | LZMA.OutWindow.prototype.flush = function(){ 18 | var size = this._pos - this._streamPos; 19 | if (size !== 0){ 20 | while(size --){ 21 | this._stream.writeByte(this._buffer[this._streamPos ++]); 22 | } 23 | if (this._pos >= this._windowSize){ 24 | this._pos = 0; 25 | } 26 | this._streamPos = this._pos; 27 | } 28 | }; 29 | 30 | LZMA.OutWindow.prototype.releaseStream = function(){ 31 | this.flush(); 32 | this._stream = null; 33 | }; 34 | 35 | LZMA.OutWindow.prototype.setStream = function(stream){ 36 | this.releaseStream(); 37 | this._stream = stream; 38 | }; 39 | 40 | LZMA.OutWindow.prototype.init = function(solid){ 41 | if (!solid){ 42 | this._streamPos = 0; 43 | this._pos = 0; 44 | } 45 | }; 46 | 47 | LZMA.OutWindow.prototype.copyBlock = function(distance, len){ 48 | var pos = this._pos - distance - 1; 49 | if (pos < 0){ 50 | pos += this._windowSize; 51 | } 52 | while(len --){ 53 | if (pos >= this._windowSize){ 54 | pos = 0; 55 | } 56 | this._buffer[this._pos ++] = this._buffer[pos ++]; 57 | if (this._pos >= this._windowSize){ 58 | this.flush(); 59 | } 60 | } 61 | }; 62 | 63 | LZMA.OutWindow.prototype.putByte = function(b){ 64 | this._buffer[this._pos ++] = b; 65 | if (this._pos >= this._windowSize){ 66 | this.flush(); 67 | } 68 | }; 69 | 70 | LZMA.OutWindow.prototype.getByte = function(distance){ 71 | var pos = this._pos - distance - 1; 72 | if (pos < 0){ 73 | pos += this._windowSize; 74 | } 75 | return this._buffer[pos]; 76 | }; 77 | 78 | LZMA.RangeDecoder = function(){ 79 | }; 80 | 81 | LZMA.RangeDecoder.prototype.setStream = function(stream){ 82 | this._stream = stream; 83 | }; 84 | 85 | LZMA.RangeDecoder.prototype.releaseStream = function(){ 86 | this._stream = null; 87 | }; 88 | 89 | LZMA.RangeDecoder.prototype.init = function(){ 90 | var i = 5; 91 | 92 | this._code = 0; 93 | this._range = -1; 94 | 95 | while(i --){ 96 | this._code = (this._code << 8) | this._stream.readByte(); 97 | } 98 | }; 99 | 100 | LZMA.RangeDecoder.prototype.decodeDirectBits = function(numTotalBits){ 101 | var result = 0, i = numTotalBits, t; 102 | 103 | while(i --){ 104 | this._range >>>= 1; 105 | t = (this._code - this._range) >>> 31; 106 | this._code -= this._range & (t - 1); 107 | result = (result << 1) | (1 - t); 108 | 109 | if ( (this._range & 0xff000000) === 0){ 110 | this._code = (this._code << 8) | this._stream.readByte(); 111 | this._range <<= 8; 112 | } 113 | } 114 | 115 | return result; 116 | }; 117 | 118 | LZMA.RangeDecoder.prototype.decodeBit = function(probs, index){ 119 | var prob = probs[index], 120 | newBound = (this._range >>> 11) * prob; 121 | 122 | if ( (this._code ^ 0x80000000) < (newBound ^ 0x80000000) ){ 123 | this._range = newBound; 124 | probs[index] += (2048 - prob) >>> 5; 125 | if ( (this._range & 0xff000000) === 0){ 126 | this._code = (this._code << 8) | this._stream.readByte(); 127 | this._range <<= 8; 128 | } 129 | return 0; 130 | } 131 | 132 | this._range -= newBound; 133 | this._code -= newBound; 134 | probs[index] -= prob >>> 5; 135 | if ( (this._range & 0xff000000) === 0){ 136 | this._code = (this._code << 8) | this._stream.readByte(); 137 | this._range <<= 8; 138 | } 139 | return 1; 140 | }; 141 | 142 | LZMA.initBitModels = function(probs, len){ 143 | while(len --){ 144 | probs[len] = 1024; 145 | } 146 | }; 147 | 148 | LZMA.BitTreeDecoder = function(numBitLevels){ 149 | this._models = []; 150 | this._numBitLevels = numBitLevels; 151 | }; 152 | 153 | LZMA.BitTreeDecoder.prototype.init = function(){ 154 | LZMA.initBitModels(this._models, 1 << this._numBitLevels); 155 | }; 156 | 157 | LZMA.BitTreeDecoder.prototype.decode = function(rangeDecoder){ 158 | var m = 1, i = this._numBitLevels; 159 | 160 | while(i --){ 161 | m = (m << 1) | rangeDecoder.decodeBit(this._models, m); 162 | } 163 | return m - (1 << this._numBitLevels); 164 | }; 165 | 166 | LZMA.BitTreeDecoder.prototype.reverseDecode = function(rangeDecoder){ 167 | var m = 1, symbol = 0, i = 0, bit; 168 | 169 | for (; i < this._numBitLevels; ++ i){ 170 | bit = rangeDecoder.decodeBit(this._models, m); 171 | m = (m << 1) | bit; 172 | symbol |= bit << i; 173 | } 174 | return symbol; 175 | }; 176 | 177 | LZMA.reverseDecode2 = function(models, startIndex, rangeDecoder, numBitLevels){ 178 | var m = 1, symbol = 0, i = 0, bit; 179 | 180 | for (; i < numBitLevels; ++ i){ 181 | bit = rangeDecoder.decodeBit(models, startIndex + m); 182 | m = (m << 1) | bit; 183 | symbol |= bit << i; 184 | } 185 | return symbol; 186 | }; 187 | 188 | LZMA.LenDecoder = function(){ 189 | this._choice = []; 190 | this._lowCoder = []; 191 | this._midCoder = []; 192 | this._highCoder = new LZMA.BitTreeDecoder(8); 193 | this._numPosStates = 0; 194 | }; 195 | 196 | LZMA.LenDecoder.prototype.create = function(numPosStates){ 197 | for (; this._numPosStates < numPosStates; ++ this._numPosStates){ 198 | this._lowCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); 199 | this._midCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); 200 | } 201 | }; 202 | 203 | LZMA.LenDecoder.prototype.init = function(){ 204 | var i = this._numPosStates; 205 | LZMA.initBitModels(this._choice, 2); 206 | while(i --){ 207 | this._lowCoder[i].init(); 208 | this._midCoder[i].init(); 209 | } 210 | this._highCoder.init(); 211 | }; 212 | 213 | LZMA.LenDecoder.prototype.decode = function(rangeDecoder, posState){ 214 | if (rangeDecoder.decodeBit(this._choice, 0) === 0){ 215 | return this._lowCoder[posState].decode(rangeDecoder); 216 | } 217 | if (rangeDecoder.decodeBit(this._choice, 1) === 0){ 218 | return 8 + this._midCoder[posState].decode(rangeDecoder); 219 | } 220 | return 16 + this._highCoder.decode(rangeDecoder); 221 | }; 222 | 223 | LZMA.Decoder2 = function(){ 224 | this._decoders = []; 225 | }; 226 | 227 | LZMA.Decoder2.prototype.init = function(){ 228 | LZMA.initBitModels(this._decoders, 0x300); 229 | }; 230 | 231 | LZMA.Decoder2.prototype.decodeNormal = function(rangeDecoder){ 232 | var symbol = 1; 233 | 234 | do{ 235 | symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); 236 | }while(symbol < 0x100); 237 | 238 | return symbol & 0xff; 239 | }; 240 | 241 | LZMA.Decoder2.prototype.decodeWithMatchByte = function(rangeDecoder, matchByte){ 242 | var symbol = 1, matchBit, bit; 243 | 244 | do{ 245 | matchBit = (matchByte >> 7) & 1; 246 | matchByte <<= 1; 247 | bit = rangeDecoder.decodeBit(this._decoders, ( (1 + matchBit) << 8) + symbol); 248 | symbol = (symbol << 1) | bit; 249 | if (matchBit !== bit){ 250 | while(symbol < 0x100){ 251 | symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); 252 | } 253 | break; 254 | } 255 | }while(symbol < 0x100); 256 | 257 | return symbol & 0xff; 258 | }; 259 | 260 | LZMA.LiteralDecoder = function(){ 261 | }; 262 | 263 | LZMA.LiteralDecoder.prototype.create = function(numPosBits, numPrevBits){ 264 | var i; 265 | 266 | if (this._coders 267 | && (this._numPrevBits === numPrevBits) 268 | && (this._numPosBits === numPosBits) ){ 269 | return; 270 | } 271 | this._numPosBits = numPosBits; 272 | this._posMask = (1 << numPosBits) - 1; 273 | this._numPrevBits = numPrevBits; 274 | 275 | this._coders = []; 276 | 277 | i = 1 << (this._numPrevBits + this._numPosBits); 278 | while(i --){ 279 | this._coders[i] = new LZMA.Decoder2(); 280 | } 281 | }; 282 | 283 | LZMA.LiteralDecoder.prototype.init = function(){ 284 | var i = 1 << (this._numPrevBits + this._numPosBits); 285 | while(i --){ 286 | this._coders[i].init(); 287 | } 288 | }; 289 | 290 | LZMA.LiteralDecoder.prototype.getDecoder = function(pos, prevByte){ 291 | return this._coders[( (pos & this._posMask) << this._numPrevBits) 292 | + ( (prevByte & 0xff) >>> (8 - this._numPrevBits) )]; 293 | }; 294 | 295 | LZMA.Decoder = function(){ 296 | this._outWindow = new LZMA.OutWindow(); 297 | this._rangeDecoder = new LZMA.RangeDecoder(); 298 | this._isMatchDecoders = []; 299 | this._isRepDecoders = []; 300 | this._isRepG0Decoders = []; 301 | this._isRepG1Decoders = []; 302 | this._isRepG2Decoders = []; 303 | this._isRep0LongDecoders = []; 304 | this._posSlotDecoder = []; 305 | this._posDecoders = []; 306 | this._posAlignDecoder = new LZMA.BitTreeDecoder(4); 307 | this._lenDecoder = new LZMA.LenDecoder(); 308 | this._repLenDecoder = new LZMA.LenDecoder(); 309 | this._literalDecoder = new LZMA.LiteralDecoder(); 310 | this._dictionarySize = -1; 311 | this._dictionarySizeCheck = -1; 312 | 313 | this._posSlotDecoder[0] = new LZMA.BitTreeDecoder(6); 314 | this._posSlotDecoder[1] = new LZMA.BitTreeDecoder(6); 315 | this._posSlotDecoder[2] = new LZMA.BitTreeDecoder(6); 316 | this._posSlotDecoder[3] = new LZMA.BitTreeDecoder(6); 317 | }; 318 | 319 | LZMA.Decoder.prototype.setDictionarySize = function(dictionarySize){ 320 | if (dictionarySize < 0){ 321 | return false; 322 | } 323 | if (this._dictionarySize !== dictionarySize){ 324 | this._dictionarySize = dictionarySize; 325 | this._dictionarySizeCheck = Math.max(this._dictionarySize, 1); 326 | this._outWindow.create( Math.max(this._dictionarySizeCheck, 4096) ); 327 | } 328 | return true; 329 | }; 330 | 331 | LZMA.Decoder.prototype.setLcLpPb = function(lc, lp, pb){ 332 | var numPosStates = 1 << pb; 333 | 334 | if (lc > 8 || lp > 4 || pb > 4){ 335 | return false; 336 | } 337 | 338 | this._literalDecoder.create(lp, lc); 339 | 340 | this._lenDecoder.create(numPosStates); 341 | this._repLenDecoder.create(numPosStates); 342 | this._posStateMask = numPosStates - 1; 343 | 344 | return true; 345 | }; 346 | 347 | LZMA.Decoder.prototype.init = function(){ 348 | var i = 4; 349 | 350 | this._outWindow.init(false); 351 | 352 | LZMA.initBitModels(this._isMatchDecoders, 192); 353 | LZMA.initBitModels(this._isRep0LongDecoders, 192); 354 | LZMA.initBitModels(this._isRepDecoders, 12); 355 | LZMA.initBitModels(this._isRepG0Decoders, 12); 356 | LZMA.initBitModels(this._isRepG1Decoders, 12); 357 | LZMA.initBitModels(this._isRepG2Decoders, 12); 358 | LZMA.initBitModels(this._posDecoders, 114); 359 | 360 | this._literalDecoder.init(); 361 | 362 | while(i --){ 363 | this._posSlotDecoder[i].init(); 364 | } 365 | 366 | this._lenDecoder.init(); 367 | this._repLenDecoder.init(); 368 | this._posAlignDecoder.init(); 369 | this._rangeDecoder.init(); 370 | }; 371 | 372 | LZMA.Decoder.prototype.decode = function(inStream, outStream, outSize){ 373 | var state = 0, rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0, nowPos64 = 0, prevByte = 0, 374 | posState, decoder2, len, distance, posSlot, numDirectBits; 375 | 376 | this._rangeDecoder.setStream(inStream); 377 | this._outWindow.setStream(outStream); 378 | 379 | this.init(); 380 | 381 | while(outSize < 0 || nowPos64 < outSize){ 382 | posState = nowPos64 & this._posStateMask; 383 | 384 | if (this._rangeDecoder.decodeBit(this._isMatchDecoders, (state << 4) + posState) === 0){ 385 | decoder2 = this._literalDecoder.getDecoder(nowPos64 ++, prevByte); 386 | 387 | if (state >= 7){ 388 | prevByte = decoder2.decodeWithMatchByte(this._rangeDecoder, this._outWindow.getByte(rep0) ); 389 | }else{ 390 | prevByte = decoder2.decodeNormal(this._rangeDecoder); 391 | } 392 | this._outWindow.putByte(prevByte); 393 | 394 | state = state < 4? 0: state - (state < 10? 3: 6); 395 | 396 | }else{ 397 | 398 | if (this._rangeDecoder.decodeBit(this._isRepDecoders, state) === 1){ 399 | len = 0; 400 | if (this._rangeDecoder.decodeBit(this._isRepG0Decoders, state) === 0){ 401 | if (this._rangeDecoder.decodeBit(this._isRep0LongDecoders, (state << 4) + posState) === 0){ 402 | state = state < 7? 9: 11; 403 | len = 1; 404 | } 405 | }else{ 406 | if (this._rangeDecoder.decodeBit(this._isRepG1Decoders, state) === 0){ 407 | distance = rep1; 408 | }else{ 409 | if (this._rangeDecoder.decodeBit(this._isRepG2Decoders, state) === 0){ 410 | distance = rep2; 411 | }else{ 412 | distance = rep3; 413 | rep3 = rep2; 414 | } 415 | rep2 = rep1; 416 | } 417 | rep1 = rep0; 418 | rep0 = distance; 419 | } 420 | if (len === 0){ 421 | len = 2 + this._repLenDecoder.decode(this._rangeDecoder, posState); 422 | state = state < 7? 8: 11; 423 | } 424 | }else{ 425 | rep3 = rep2; 426 | rep2 = rep1; 427 | rep1 = rep0; 428 | 429 | len = 2 + this._lenDecoder.decode(this._rangeDecoder, posState); 430 | state = state < 7? 7: 10; 431 | 432 | posSlot = this._posSlotDecoder[len <= 5? len - 2: 3].decode(this._rangeDecoder); 433 | if (posSlot >= 4){ 434 | 435 | numDirectBits = (posSlot >> 1) - 1; 436 | rep0 = (2 | (posSlot & 1) ) << numDirectBits; 437 | 438 | if (posSlot < 14){ 439 | rep0 += LZMA.reverseDecode2(this._posDecoders, 440 | rep0 - posSlot - 1, this._rangeDecoder, numDirectBits); 441 | }else{ 442 | rep0 += this._rangeDecoder.decodeDirectBits(numDirectBits - 4) << 4; 443 | rep0 += this._posAlignDecoder.reverseDecode(this._rangeDecoder); 444 | if (rep0 < 0){ 445 | if (rep0 === -1){ 446 | break; 447 | } 448 | return false; 449 | } 450 | } 451 | }else{ 452 | rep0 = posSlot; 453 | } 454 | } 455 | 456 | if (rep0 >= nowPos64 || rep0 >= this._dictionarySizeCheck){ 457 | return false; 458 | } 459 | 460 | this._outWindow.copyBlock(rep0, len); 461 | nowPos64 += len; 462 | prevByte = this._outWindow.getByte(0); 463 | } 464 | } 465 | 466 | this._outWindow.flush(); 467 | this._outWindow.releaseStream(); 468 | this._rangeDecoder.releaseStream(); 469 | 470 | return true; 471 | }; 472 | 473 | LZMA.Decoder.prototype.setDecoderProperties = function(properties){ 474 | var value, lc, lp, pb, dictionarySize; 475 | 476 | if (properties.size < 5){ 477 | return false; 478 | } 479 | 480 | value = properties.readByte(); 481 | lc = value % 9; 482 | value = ~~(value / 9); 483 | lp = value % 5; 484 | pb = ~~(value / 5); 485 | 486 | if ( !this.setLcLpPb(lc, lp, pb) ){ 487 | return false; 488 | } 489 | 490 | dictionarySize = properties.readByte(); 491 | dictionarySize |= properties.readByte() << 8; 492 | dictionarySize |= properties.readByte() << 16; 493 | dictionarySize += properties.readByte() * 16777216; 494 | 495 | return this.setDictionarySize(dictionarySize); 496 | }; 497 | 498 | LZMA.decompress = function(properties, inStream, outStream, outSize){ 499 | var decoder = new LZMA.Decoder(); 500 | 501 | if ( !decoder.setDecoderProperties(properties) ){ 502 | throw "Incorrect stream properties"; 503 | } 504 | 505 | if ( !decoder.decode(inStream, outStream, outSize) ){ 506 | throw "Error in data stream"; 507 | } 508 | 509 | return true; 510 | }; 511 | -------------------------------------------------------------------------------- /src/libs/lzma.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Juan Mellado 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | References: 25 | - "LZMA SDK" by Igor Pavlov 26 | http://www.7-zip.org/sdk.html 27 | */ 28 | 29 | var LZMA = LZMA || {}; 30 | 31 | LZMA.OutWindow = function(){ 32 | this._windowSize = 0; 33 | }; 34 | 35 | LZMA.OutWindow.prototype.create = function(windowSize){ 36 | if ( (!this._buffer) || (this._windowSize !== windowSize) ){ 37 | this._buffer = []; 38 | } 39 | this._windowSize = windowSize; 40 | this._pos = 0; 41 | this._streamPos = 0; 42 | }; 43 | 44 | LZMA.OutWindow.prototype.flush = function(){ 45 | var size = this._pos - this._streamPos; 46 | if (size !== 0){ 47 | while(size --){ 48 | this._stream.writeByte(this._buffer[this._streamPos ++]); 49 | } 50 | if (this._pos >= this._windowSize){ 51 | this._pos = 0; 52 | } 53 | this._streamPos = this._pos; 54 | } 55 | }; 56 | 57 | LZMA.OutWindow.prototype.releaseStream = function(){ 58 | this.flush(); 59 | this._stream = null; 60 | }; 61 | 62 | LZMA.OutWindow.prototype.setStream = function(stream){ 63 | this.releaseStream(); 64 | this._stream = stream; 65 | }; 66 | 67 | LZMA.OutWindow.prototype.init = function(solid){ 68 | if (!solid){ 69 | this._streamPos = 0; 70 | this._pos = 0; 71 | } 72 | }; 73 | 74 | LZMA.OutWindow.prototype.copyBlock = function(distance, len){ 75 | var pos = this._pos - distance - 1; 76 | if (pos < 0){ 77 | pos += this._windowSize; 78 | } 79 | while(len --){ 80 | if (pos >= this._windowSize){ 81 | pos = 0; 82 | } 83 | this._buffer[this._pos ++] = this._buffer[pos ++]; 84 | if (this._pos >= this._windowSize){ 85 | this.flush(); 86 | } 87 | } 88 | }; 89 | 90 | LZMA.OutWindow.prototype.putByte = function(b){ 91 | this._buffer[this._pos ++] = b; 92 | if (this._pos >= this._windowSize){ 93 | this.flush(); 94 | } 95 | }; 96 | 97 | LZMA.OutWindow.prototype.getByte = function(distance){ 98 | var pos = this._pos - distance - 1; 99 | if (pos < 0){ 100 | pos += this._windowSize; 101 | } 102 | return this._buffer[pos]; 103 | }; 104 | 105 | LZMA.RangeDecoder = function(){ 106 | }; 107 | 108 | LZMA.RangeDecoder.prototype.setStream = function(stream){ 109 | this._stream = stream; 110 | }; 111 | 112 | LZMA.RangeDecoder.prototype.releaseStream = function(){ 113 | this._stream = null; 114 | }; 115 | 116 | LZMA.RangeDecoder.prototype.init = function(){ 117 | var i = 5; 118 | 119 | this._code = 0; 120 | this._range = -1; 121 | 122 | while(i --){ 123 | this._code = (this._code << 8) | this._stream.readByte(); 124 | } 125 | }; 126 | 127 | LZMA.RangeDecoder.prototype.decodeDirectBits = function(numTotalBits){ 128 | var result = 0, i = numTotalBits, t; 129 | 130 | while(i --){ 131 | this._range >>>= 1; 132 | t = (this._code - this._range) >>> 31; 133 | this._code -= this._range & (t - 1); 134 | result = (result << 1) | (1 - t); 135 | 136 | if ( (this._range & 0xff000000) === 0){ 137 | this._code = (this._code << 8) | this._stream.readByte(); 138 | this._range <<= 8; 139 | } 140 | } 141 | 142 | return result; 143 | }; 144 | 145 | LZMA.RangeDecoder.prototype.decodeBit = function(probs, index){ 146 | var prob = probs[index], 147 | newBound = (this._range >>> 11) * prob; 148 | 149 | if ( (this._code ^ 0x80000000) < (newBound ^ 0x80000000) ){ 150 | this._range = newBound; 151 | probs[index] += (2048 - prob) >>> 5; 152 | if ( (this._range & 0xff000000) === 0){ 153 | this._code = (this._code << 8) | this._stream.readByte(); 154 | this._range <<= 8; 155 | } 156 | return 0; 157 | } 158 | 159 | this._range -= newBound; 160 | this._code -= newBound; 161 | probs[index] -= prob >>> 5; 162 | if ( (this._range & 0xff000000) === 0){ 163 | this._code = (this._code << 8) | this._stream.readByte(); 164 | this._range <<= 8; 165 | } 166 | return 1; 167 | }; 168 | 169 | LZMA.initBitModels = function(probs, len){ 170 | while(len --){ 171 | probs[len] = 1024; 172 | } 173 | }; 174 | 175 | LZMA.BitTreeDecoder = function(numBitLevels){ 176 | this._models = []; 177 | this._numBitLevels = numBitLevels; 178 | }; 179 | 180 | LZMA.BitTreeDecoder.prototype.init = function(){ 181 | LZMA.initBitModels(this._models, 1 << this._numBitLevels); 182 | }; 183 | 184 | LZMA.BitTreeDecoder.prototype.decode = function(rangeDecoder){ 185 | var m = 1, i = this._numBitLevels; 186 | 187 | while(i --){ 188 | m = (m << 1) | rangeDecoder.decodeBit(this._models, m); 189 | } 190 | return m - (1 << this._numBitLevels); 191 | }; 192 | 193 | LZMA.BitTreeDecoder.prototype.reverseDecode = function(rangeDecoder){ 194 | var m = 1, symbol = 0, i = 0, bit; 195 | 196 | for (; i < this._numBitLevels; ++ i){ 197 | bit = rangeDecoder.decodeBit(this._models, m); 198 | m = (m << 1) | bit; 199 | symbol |= bit << i; 200 | } 201 | return symbol; 202 | }; 203 | 204 | LZMA.reverseDecode2 = function(models, startIndex, rangeDecoder, numBitLevels){ 205 | var m = 1, symbol = 0, i = 0, bit; 206 | 207 | for (; i < numBitLevels; ++ i){ 208 | bit = rangeDecoder.decodeBit(models, startIndex + m); 209 | m = (m << 1) | bit; 210 | symbol |= bit << i; 211 | } 212 | return symbol; 213 | }; 214 | 215 | LZMA.LenDecoder = function(){ 216 | this._choice = []; 217 | this._lowCoder = []; 218 | this._midCoder = []; 219 | this._highCoder = new LZMA.BitTreeDecoder(8); 220 | this._numPosStates = 0; 221 | }; 222 | 223 | LZMA.LenDecoder.prototype.create = function(numPosStates){ 224 | for (; this._numPosStates < numPosStates; ++ this._numPosStates){ 225 | this._lowCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); 226 | this._midCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3); 227 | } 228 | }; 229 | 230 | LZMA.LenDecoder.prototype.init = function(){ 231 | var i = this._numPosStates; 232 | LZMA.initBitModels(this._choice, 2); 233 | while(i --){ 234 | this._lowCoder[i].init(); 235 | this._midCoder[i].init(); 236 | } 237 | this._highCoder.init(); 238 | }; 239 | 240 | LZMA.LenDecoder.prototype.decode = function(rangeDecoder, posState){ 241 | if (rangeDecoder.decodeBit(this._choice, 0) === 0){ 242 | return this._lowCoder[posState].decode(rangeDecoder); 243 | } 244 | if (rangeDecoder.decodeBit(this._choice, 1) === 0){ 245 | return 8 + this._midCoder[posState].decode(rangeDecoder); 246 | } 247 | return 16 + this._highCoder.decode(rangeDecoder); 248 | }; 249 | 250 | LZMA.Decoder2 = function(){ 251 | this._decoders = []; 252 | }; 253 | 254 | LZMA.Decoder2.prototype.init = function(){ 255 | LZMA.initBitModels(this._decoders, 0x300); 256 | }; 257 | 258 | LZMA.Decoder2.prototype.decodeNormal = function(rangeDecoder){ 259 | var symbol = 1; 260 | 261 | do{ 262 | symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); 263 | }while(symbol < 0x100); 264 | 265 | return symbol & 0xff; 266 | }; 267 | 268 | LZMA.Decoder2.prototype.decodeWithMatchByte = function(rangeDecoder, matchByte){ 269 | var symbol = 1, matchBit, bit; 270 | 271 | do{ 272 | matchBit = (matchByte >> 7) & 1; 273 | matchByte <<= 1; 274 | bit = rangeDecoder.decodeBit(this._decoders, ( (1 + matchBit) << 8) + symbol); 275 | symbol = (symbol << 1) | bit; 276 | if (matchBit !== bit){ 277 | while(symbol < 0x100){ 278 | symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); 279 | } 280 | break; 281 | } 282 | }while(symbol < 0x100); 283 | 284 | return symbol & 0xff; 285 | }; 286 | 287 | LZMA.LiteralDecoder = function(){ 288 | }; 289 | 290 | LZMA.LiteralDecoder.prototype.create = function(numPosBits, numPrevBits){ 291 | var i; 292 | 293 | if (this._coders 294 | && (this._numPrevBits === numPrevBits) 295 | && (this._numPosBits === numPosBits) ){ 296 | return; 297 | } 298 | this._numPosBits = numPosBits; 299 | this._posMask = (1 << numPosBits) - 1; 300 | this._numPrevBits = numPrevBits; 301 | 302 | this._coders = []; 303 | 304 | i = 1 << (this._numPrevBits + this._numPosBits); 305 | while(i --){ 306 | this._coders[i] = new LZMA.Decoder2(); 307 | } 308 | }; 309 | 310 | LZMA.LiteralDecoder.prototype.init = function(){ 311 | var i = 1 << (this._numPrevBits + this._numPosBits); 312 | while(i --){ 313 | this._coders[i].init(); 314 | } 315 | }; 316 | 317 | LZMA.LiteralDecoder.prototype.getDecoder = function(pos, prevByte){ 318 | return this._coders[( (pos & this._posMask) << this._numPrevBits) 319 | + ( (prevByte & 0xff) >>> (8 - this._numPrevBits) )]; 320 | }; 321 | 322 | LZMA.Decoder = function(){ 323 | this._outWindow = new LZMA.OutWindow(); 324 | this._rangeDecoder = new LZMA.RangeDecoder(); 325 | this._isMatchDecoders = []; 326 | this._isRepDecoders = []; 327 | this._isRepG0Decoders = []; 328 | this._isRepG1Decoders = []; 329 | this._isRepG2Decoders = []; 330 | this._isRep0LongDecoders = []; 331 | this._posSlotDecoder = []; 332 | this._posDecoders = []; 333 | this._posAlignDecoder = new LZMA.BitTreeDecoder(4); 334 | this._lenDecoder = new LZMA.LenDecoder(); 335 | this._repLenDecoder = new LZMA.LenDecoder(); 336 | this._literalDecoder = new LZMA.LiteralDecoder(); 337 | this._dictionarySize = -1; 338 | this._dictionarySizeCheck = -1; 339 | 340 | this._posSlotDecoder[0] = new LZMA.BitTreeDecoder(6); 341 | this._posSlotDecoder[1] = new LZMA.BitTreeDecoder(6); 342 | this._posSlotDecoder[2] = new LZMA.BitTreeDecoder(6); 343 | this._posSlotDecoder[3] = new LZMA.BitTreeDecoder(6); 344 | }; 345 | 346 | LZMA.Decoder.prototype.setDictionarySize = function(dictionarySize){ 347 | if (dictionarySize < 0){ 348 | return false; 349 | } 350 | if (this._dictionarySize !== dictionarySize){ 351 | this._dictionarySize = dictionarySize; 352 | this._dictionarySizeCheck = Math.max(this._dictionarySize, 1); 353 | this._outWindow.create( Math.max(this._dictionarySizeCheck, 4096) ); 354 | } 355 | return true; 356 | }; 357 | 358 | LZMA.Decoder.prototype.setLcLpPb = function(lc, lp, pb){ 359 | var numPosStates = 1 << pb; 360 | 361 | if (lc > 8 || lp > 4 || pb > 4){ 362 | return false; 363 | } 364 | 365 | this._literalDecoder.create(lp, lc); 366 | 367 | this._lenDecoder.create(numPosStates); 368 | this._repLenDecoder.create(numPosStates); 369 | this._posStateMask = numPosStates - 1; 370 | 371 | return true; 372 | }; 373 | 374 | LZMA.Decoder.prototype.init = function(){ 375 | var i = 4; 376 | 377 | this._outWindow.init(false); 378 | 379 | LZMA.initBitModels(this._isMatchDecoders, 192); 380 | LZMA.initBitModels(this._isRep0LongDecoders, 192); 381 | LZMA.initBitModels(this._isRepDecoders, 12); 382 | LZMA.initBitModels(this._isRepG0Decoders, 12); 383 | LZMA.initBitModels(this._isRepG1Decoders, 12); 384 | LZMA.initBitModels(this._isRepG2Decoders, 12); 385 | LZMA.initBitModels(this._posDecoders, 114); 386 | 387 | this._literalDecoder.init(); 388 | 389 | while(i --){ 390 | this._posSlotDecoder[i].init(); 391 | } 392 | 393 | this._lenDecoder.init(); 394 | this._repLenDecoder.init(); 395 | this._posAlignDecoder.init(); 396 | this._rangeDecoder.init(); 397 | }; 398 | 399 | LZMA.Decoder.prototype.decode = function(inStream, outStream, outSize){ 400 | var state = 0, rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0, nowPos64 = 0, prevByte = 0, 401 | posState, decoder2, len, distance, posSlot, numDirectBits; 402 | 403 | this._rangeDecoder.setStream(inStream); 404 | this._outWindow.setStream(outStream); 405 | 406 | this.init(); 407 | 408 | while(outSize < 0 || nowPos64 < outSize){ 409 | posState = nowPos64 & this._posStateMask; 410 | 411 | if (this._rangeDecoder.decodeBit(this._isMatchDecoders, (state << 4) + posState) === 0){ 412 | decoder2 = this._literalDecoder.getDecoder(nowPos64 ++, prevByte); 413 | 414 | if (state >= 7){ 415 | prevByte = decoder2.decodeWithMatchByte(this._rangeDecoder, this._outWindow.getByte(rep0) ); 416 | }else{ 417 | prevByte = decoder2.decodeNormal(this._rangeDecoder); 418 | } 419 | this._outWindow.putByte(prevByte); 420 | 421 | state = state < 4? 0: state - (state < 10? 3: 6); 422 | 423 | }else{ 424 | 425 | if (this._rangeDecoder.decodeBit(this._isRepDecoders, state) === 1){ 426 | len = 0; 427 | if (this._rangeDecoder.decodeBit(this._isRepG0Decoders, state) === 0){ 428 | if (this._rangeDecoder.decodeBit(this._isRep0LongDecoders, (state << 4) + posState) === 0){ 429 | state = state < 7? 9: 11; 430 | len = 1; 431 | } 432 | }else{ 433 | if (this._rangeDecoder.decodeBit(this._isRepG1Decoders, state) === 0){ 434 | distance = rep1; 435 | }else{ 436 | if (this._rangeDecoder.decodeBit(this._isRepG2Decoders, state) === 0){ 437 | distance = rep2; 438 | }else{ 439 | distance = rep3; 440 | rep3 = rep2; 441 | } 442 | rep2 = rep1; 443 | } 444 | rep1 = rep0; 445 | rep0 = distance; 446 | } 447 | if (len === 0){ 448 | len = 2 + this._repLenDecoder.decode(this._rangeDecoder, posState); 449 | state = state < 7? 8: 11; 450 | } 451 | }else{ 452 | rep3 = rep2; 453 | rep2 = rep1; 454 | rep1 = rep0; 455 | 456 | len = 2 + this._lenDecoder.decode(this._rangeDecoder, posState); 457 | state = state < 7? 7: 10; 458 | 459 | posSlot = this._posSlotDecoder[len <= 5? len - 2: 3].decode(this._rangeDecoder); 460 | if (posSlot >= 4){ 461 | 462 | numDirectBits = (posSlot >> 1) - 1; 463 | rep0 = (2 | (posSlot & 1) ) << numDirectBits; 464 | 465 | if (posSlot < 14){ 466 | rep0 += LZMA.reverseDecode2(this._posDecoders, 467 | rep0 - posSlot - 1, this._rangeDecoder, numDirectBits); 468 | }else{ 469 | rep0 += this._rangeDecoder.decodeDirectBits(numDirectBits - 4) << 4; 470 | rep0 += this._posAlignDecoder.reverseDecode(this._rangeDecoder); 471 | if (rep0 < 0){ 472 | if (rep0 === -1){ 473 | break; 474 | } 475 | return false; 476 | } 477 | } 478 | }else{ 479 | rep0 = posSlot; 480 | } 481 | } 482 | 483 | if (rep0 >= nowPos64 || rep0 >= this._dictionarySizeCheck){ 484 | return false; 485 | } 486 | 487 | this._outWindow.copyBlock(rep0, len); 488 | nowPos64 += len; 489 | prevByte = this._outWindow.getByte(0); 490 | } 491 | } 492 | 493 | this._outWindow.flush(); 494 | this._outWindow.releaseStream(); 495 | this._rangeDecoder.releaseStream(); 496 | 497 | return true; 498 | }; 499 | 500 | LZMA.Decoder.prototype.setDecoderProperties = function(properties){ 501 | var value, lc, lp, pb, dictionarySize; 502 | 503 | if (properties.size < 5){ 504 | return false; 505 | } 506 | 507 | value = properties.readByte(); 508 | lc = value % 9; 509 | value = ~~(value / 9); 510 | lp = value % 5; 511 | pb = ~~(value / 5); 512 | 513 | if ( !this.setLcLpPb(lc, lp, pb) ){ 514 | return false; 515 | } 516 | 517 | dictionarySize = properties.readByte(); 518 | dictionarySize |= properties.readByte() << 8; 519 | dictionarySize |= properties.readByte() << 16; 520 | dictionarySize += properties.readByte() * 16777216; 521 | 522 | return this.setDictionarySize(dictionarySize); 523 | }; 524 | 525 | LZMA.decompress = function(properties, inStream, outStream, outSize){ 526 | var decoder = new LZMA.Decoder(); 527 | 528 | if ( !decoder.setDecoderProperties(properties) ){ 529 | throw "Incorrect stream properties"; 530 | } 531 | 532 | if ( !decoder.decode(inStream, outStream, outSize) ){ 533 | throw "Error in data stream"; 534 | } 535 | 536 | return true; 537 | }; 538 | 539 | LZMA.decompressFile = function(inStream, outStream){ 540 | var decoder = new LZMA.Decoder(), outSize; 541 | 542 | if ( !decoder.setDecoderProperties(inStream) ){ 543 | throw "Incorrect stream properties"; 544 | } 545 | 546 | outSize = inStream.readByte(); 547 | outSize |= inStream.readByte() << 8; 548 | outSize |= inStream.readByte() << 16; 549 | outSize += inStream.readByte() * 16777216; 550 | 551 | inStream.readByte(); 552 | inStream.readByte(); 553 | inStream.readByte(); 554 | inStream.readByte(); 555 | 556 | if ( !decoder.decode(inStream, outStream, outSize) ){ 557 | throw "Error in data stream"; 558 | } 559 | 560 | return true; 561 | }; 562 | -------------------------------------------------------------------------------- /src/ctm.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Juan Mellado 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | References: 25 | - "OpenCTM: The Open Compressed Triangle Mesh file format" by Marcus Geelnard 26 | http://openctm.sourceforge.net/ 27 | */ 28 | 29 | var CTM = CTM || {}; 30 | 31 | CTM.CompressionMethod = { 32 | RAW: 0x00574152, 33 | MG1: 0x0031474d, 34 | MG2: 0x0032474d 35 | }; 36 | 37 | CTM.Flags = { 38 | NORMALS: 0x00000001 39 | }; 40 | 41 | CTM.File = function(stream){ 42 | this.load(stream); 43 | }; 44 | 45 | CTM.File.prototype.load = function(stream){ 46 | this.header = new CTM.FileHeader(stream); 47 | 48 | this.body = new CTM.FileBody(this.header); 49 | 50 | this.getReader().read(stream, this.body); 51 | }; 52 | 53 | CTM.File.prototype.getReader = function(){ 54 | var reader; 55 | 56 | switch(this.header.compressionMethod){ 57 | case CTM.CompressionMethod.RAW: 58 | reader = new CTM.ReaderRAW(); 59 | break; 60 | case CTM.CompressionMethod.MG1: 61 | reader = new CTM.ReaderMG1(); 62 | break; 63 | case CTM.CompressionMethod.MG2: 64 | reader = new CTM.ReaderMG2(); 65 | break; 66 | } 67 | 68 | return reader; 69 | }; 70 | 71 | CTM.FileHeader = function(stream){ 72 | stream.readInt32(); //magic "OCTM" 73 | this.fileFormat = stream.readInt32(); 74 | this.compressionMethod = stream.readInt32(); 75 | this.vertexCount = stream.readInt32(); 76 | this.triangleCount = stream.readInt32(); 77 | this.uvMapCount = stream.readInt32(); 78 | this.attrMapCount = stream.readInt32(); 79 | this.flags = stream.readInt32(); 80 | this.comment = stream.readString(); 81 | }; 82 | 83 | CTM.FileHeader.prototype.hasNormals = function(){ 84 | return this.flags & CTM.Flags.NORMALS; 85 | }; 86 | 87 | CTM.FileBody = function(header){ 88 | var i = header.triangleCount * 3, 89 | v = header.vertexCount * 3, 90 | n = header.hasNormals()? header.vertexCount * 3: 0, 91 | u = header.vertexCount * 2, 92 | a = header.vertexCount * 4, 93 | j = 0; 94 | 95 | var data = new ArrayBuffer( 96 | (i + v + n + (u * header.uvMapCount) + (a * header.attrMapCount) ) * 4); 97 | 98 | this.indices = new Uint32Array(data, 0, i); 99 | 100 | this.vertices = new Float32Array(data, i * 4, v); 101 | 102 | if ( header.hasNormals() ){ 103 | this.normals = new Float32Array(data, (i + v) * 4, n); 104 | } 105 | 106 | if (header.uvMapCount){ 107 | this.uvMaps = []; 108 | for (j = 0; j < header.uvMapCount; ++ j){ 109 | this.uvMaps[j] = {uv: new Float32Array(data, 110 | (i + v + n + (j * u) ) * 4, u) }; 111 | } 112 | } 113 | 114 | if (header.attrMapCount){ 115 | this.attrMaps = []; 116 | for (j = 0; j < header.attrMapCount; ++ j){ 117 | this.attrMaps[j] = {attr: new Float32Array(data, 118 | (i + v + n + (u * header.uvMapCount) + (j * a) ) * 4, a) }; 119 | } 120 | } 121 | }; 122 | 123 | CTM.FileMG2Header = function(stream){ 124 | stream.readInt32(); //magic "MG2H" 125 | this.vertexPrecision = stream.readFloat32(); 126 | this.normalPrecision = stream.readFloat32(); 127 | this.lowerBoundx = stream.readFloat32(); 128 | this.lowerBoundy = stream.readFloat32(); 129 | this.lowerBoundz = stream.readFloat32(); 130 | this.higherBoundx = stream.readFloat32(); 131 | this.higherBoundy = stream.readFloat32(); 132 | this.higherBoundz = stream.readFloat32(); 133 | this.divx = stream.readInt32(); 134 | this.divy = stream.readInt32(); 135 | this.divz = stream.readInt32(); 136 | 137 | this.sizex = (this.higherBoundx - this.lowerBoundx) / this.divx; 138 | this.sizey = (this.higherBoundy - this.lowerBoundy) / this.divy; 139 | this.sizez = (this.higherBoundz - this.lowerBoundz) / this.divz; 140 | }; 141 | 142 | CTM.ReaderRAW = function(){ 143 | }; 144 | 145 | CTM.ReaderRAW.prototype.read = function(stream, body){ 146 | this.readIndices(stream, body.indices); 147 | this.readVertices(stream, body.vertices); 148 | 149 | if (body.normals){ 150 | this.readNormals(stream, body.normals); 151 | } 152 | if (body.uvMaps){ 153 | this.readUVMaps(stream, body.uvMaps); 154 | } 155 | if (body.attrMaps){ 156 | this.readAttrMaps(stream, body.attrMaps); 157 | } 158 | }; 159 | 160 | CTM.ReaderRAW.prototype.readIndices = function(stream, indices){ 161 | stream.readInt32(); //magic "INDX" 162 | stream.readArrayInt32(indices); 163 | }; 164 | 165 | CTM.ReaderRAW.prototype.readVertices = function(stream, vertices){ 166 | stream.readInt32(); //magic "VERT" 167 | stream.readArrayFloat32(vertices); 168 | }; 169 | 170 | CTM.ReaderRAW.prototype.readNormals = function(stream, normals){ 171 | stream.readInt32(); //magic "NORM" 172 | stream.readArrayFloat32(normals); 173 | }; 174 | 175 | CTM.ReaderRAW.prototype.readUVMaps = function(stream, uvMaps){ 176 | var i = 0; 177 | for (; i < uvMaps.length; ++ i){ 178 | stream.readInt32(); //magic "TEXC" 179 | 180 | uvMaps[i].name = stream.readString(); 181 | uvMaps[i].filename = stream.readString(); 182 | stream.readArrayFloat32(uvMaps[i].uv); 183 | } 184 | }; 185 | 186 | CTM.ReaderRAW.prototype.readAttrMaps = function(stream, attrMaps){ 187 | var i = 0; 188 | for (; i < attrMaps.length; ++ i){ 189 | stream.readInt32(); //magic "ATTR" 190 | 191 | attrMaps[i].name = stream.readString(); 192 | stream.readArrayFloat32(attrMaps[i].attr); 193 | } 194 | }; 195 | 196 | CTM.ReaderMG1 = function(){ 197 | }; 198 | 199 | CTM.ReaderMG1.prototype.read = function(stream, body){ 200 | this.readIndices(stream, body.indices); 201 | this.readVertices(stream, body.vertices); 202 | 203 | if (body.normals){ 204 | this.readNormals(stream, body.normals); 205 | } 206 | if (body.uvMaps){ 207 | this.readUVMaps(stream, body.uvMaps); 208 | } 209 | if (body.attrMaps){ 210 | this.readAttrMaps(stream, body.attrMaps); 211 | } 212 | }; 213 | 214 | CTM.ReaderMG1.prototype.readIndices = function(stream, indices){ 215 | stream.readInt32(); //magic "INDX" 216 | var size = stream.readInt32(); //packed size 217 | 218 | var interleaved = new CTM.InterleavedStream(indices, 3); 219 | CTM.decompress(stream, size, interleaved); 220 | 221 | CTM.restoreIndices(indices, indices.length); 222 | }; 223 | 224 | CTM.ReaderMG1.prototype.readVertices = function(stream, vertices){ 225 | stream.readInt32(); //magic "VERT" 226 | var size = stream.readInt32(); //packed size 227 | 228 | var interleaved = new CTM.InterleavedStream(vertices, 1); 229 | CTM.decompress(stream, size, interleaved); 230 | }; 231 | 232 | CTM.ReaderMG1.prototype.readNormals = function(stream, normals){ 233 | stream.readInt32(); //magic "NORM" 234 | var size = stream.readInt32(); //packed size 235 | 236 | var interleaved = new CTM.InterleavedStream(normals, 3); 237 | CTM.decompress(stream, size, interleaved); 238 | }; 239 | 240 | CTM.ReaderMG1.prototype.readUVMaps = function(stream, uvMaps){ 241 | var i = 0; 242 | for (; i < uvMaps.length; ++ i){ 243 | stream.readInt32(); //magic "TEXC" 244 | 245 | uvMaps[i].name = stream.readString(); 246 | uvMaps[i].filename = stream.readString(); 247 | 248 | var size = stream.readInt32(); //packed size 249 | 250 | var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2); 251 | CTM.decompress(stream, size, interleaved); 252 | } 253 | }; 254 | 255 | CTM.ReaderMG1.prototype.readAttrMaps = function(stream, attrMaps){ 256 | var i = 0; 257 | for (; i < attrMaps.length; ++ i){ 258 | stream.readInt32(); //magic "ATTR" 259 | 260 | attrMaps[i].name = stream.readString(); 261 | 262 | var size = stream.readInt32(); //packed size 263 | 264 | var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4); 265 | CTM.decompress(stream, size, interleaved); 266 | } 267 | }; 268 | 269 | CTM.ReaderMG2 = function(){ 270 | }; 271 | 272 | CTM.ReaderMG2.prototype.read = function(stream, body){ 273 | this.MG2Header = new CTM.FileMG2Header(stream); 274 | 275 | this.readVertices(stream, body.vertices); 276 | this.readIndices(stream, body.indices); 277 | 278 | if (body.normals){ 279 | this.readNormals(stream, body); 280 | } 281 | if (body.uvMaps){ 282 | this.readUVMaps(stream, body.uvMaps); 283 | } 284 | if (body.attrMaps){ 285 | this.readAttrMaps(stream, body.attrMaps); 286 | } 287 | }; 288 | 289 | CTM.ReaderMG2.prototype.readVertices = function(stream, vertices){ 290 | stream.readInt32(); //magic "VERT" 291 | var size = stream.readInt32(); //packed size 292 | 293 | var interleaved = new CTM.InterleavedStream(vertices, 3); 294 | CTM.decompress(stream, size, interleaved); 295 | 296 | var gridIndices = this.readGridIndices(stream, vertices); 297 | 298 | CTM.restoreVertices(vertices, this.MG2Header, gridIndices, this.MG2Header.vertexPrecision); 299 | }; 300 | 301 | CTM.ReaderMG2.prototype.readGridIndices = function(stream, vertices){ 302 | stream.readInt32(); //magic "GIDX" 303 | var size = stream.readInt32(); //packed size 304 | 305 | var gridIndices = new Uint32Array(vertices.length / 3); 306 | 307 | var interleaved = new CTM.InterleavedStream(gridIndices, 1); 308 | CTM.decompress(stream, size, interleaved); 309 | 310 | CTM.restoreGridIndices(gridIndices, gridIndices.length); 311 | 312 | return gridIndices; 313 | }; 314 | 315 | CTM.ReaderMG2.prototype.readIndices = function(stream, indices){ 316 | stream.readInt32(); //magic "INDX" 317 | var size = stream.readInt32(); //packed size 318 | 319 | var interleaved = new CTM.InterleavedStream(indices, 3); 320 | CTM.decompress(stream, size, interleaved); 321 | 322 | CTM.restoreIndices(indices, indices.length); 323 | }; 324 | 325 | CTM.ReaderMG2.prototype.readNormals = function(stream, body){ 326 | stream.readInt32(); //magic "NORM" 327 | var size = stream.readInt32(); //packed size 328 | 329 | var interleaved = new CTM.InterleavedStream(body.normals, 3); 330 | CTM.decompress(stream, size, interleaved); 331 | 332 | var smooth = CTM.calcSmoothNormals(body.indices, body.vertices); 333 | 334 | CTM.restoreNormals(body.normals, smooth, this.MG2Header.normalPrecision); 335 | }; 336 | 337 | CTM.ReaderMG2.prototype.readUVMaps = function(stream, uvMaps){ 338 | var i = 0; 339 | for (; i < uvMaps.length; ++ i){ 340 | stream.readInt32(); //magic "TEXC" 341 | 342 | uvMaps[i].name = stream.readString(); 343 | uvMaps[i].filename = stream.readString(); 344 | 345 | var precision = stream.readFloat32(); 346 | 347 | var size = stream.readInt32(); //packed size 348 | 349 | var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2); 350 | CTM.decompress(stream, size, interleaved); 351 | 352 | CTM.restoreMap(uvMaps[i].uv, 2, precision); 353 | } 354 | }; 355 | 356 | CTM.ReaderMG2.prototype.readAttrMaps = function(stream, attrMaps){ 357 | var i = 0; 358 | for (; i < attrMaps.length; ++ i){ 359 | stream.readInt32(); //magic "ATTR" 360 | 361 | attrMaps[i].name = stream.readString(); 362 | 363 | var precision = stream.readFloat32(); 364 | 365 | var size = stream.readInt32(); //packed size 366 | 367 | var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4); 368 | CTM.decompress(stream, size, interleaved); 369 | 370 | CTM.restoreMap(attrMaps[i].attr, 4, precision); 371 | } 372 | }; 373 | 374 | CTM.decompress = function(stream, size, interleaved){ 375 | var offset = stream.offset; 376 | LZMA.decompress(stream, stream, interleaved, interleaved.data.length); 377 | stream.offset = offset + 5 + size; 378 | }; 379 | 380 | CTM.restoreIndices = function(indices, len){ 381 | var i = 3; 382 | if (len > 0){ 383 | indices[2] += indices[0]; 384 | indices[1] += indices[0]; 385 | } 386 | for (; i < len; i += 3){ 387 | indices[i] += indices[i - 3]; 388 | 389 | if (indices[i] === indices[i - 3]){ 390 | indices[i + 1] += indices[i - 2]; 391 | }else{ 392 | indices[i + 1] += indices[i]; 393 | } 394 | 395 | indices[i + 2] += indices[i]; 396 | } 397 | }; 398 | 399 | CTM.restoreGridIndices = function(gridIndices, len){ 400 | var i = 1; 401 | for (; i < len; ++ i){ 402 | gridIndices[i] += gridIndices[i - 1]; 403 | } 404 | }; 405 | 406 | CTM.restoreVertices = function(vertices, grid, gridIndices, precision){ 407 | var gridIdx, delta, x, y, z, 408 | intVertices = new Uint32Array(vertices.buffer, vertices.byteOffset, vertices.length), 409 | ydiv = grid.divx, zdiv = ydiv * grid.divy, 410 | prevGridIdx = 0x7fffffff, prevDelta = 0, 411 | i = 0, j = 0, len = gridIndices.length; 412 | 413 | for (; i < len; j += 3){ 414 | x = gridIdx = gridIndices[i ++]; 415 | 416 | z = ~~(x / zdiv); 417 | x -= ~~(z * zdiv); 418 | y = ~~(x / ydiv); 419 | x -= ~~(y * ydiv); 420 | 421 | delta = intVertices[j]; 422 | if (gridIdx === prevGridIdx){ 423 | delta += prevDelta; 424 | } 425 | 426 | vertices[j] = grid.lowerBoundx + 427 | x * grid.sizex + precision * delta; 428 | vertices[j + 1] = grid.lowerBoundy + 429 | y * grid.sizey + precision * intVertices[j + 1]; 430 | vertices[j + 2] = grid.lowerBoundz + 431 | z * grid.sizez + precision * intVertices[j + 2]; 432 | 433 | prevGridIdx = gridIdx; 434 | prevDelta = delta; 435 | } 436 | }; 437 | 438 | CTM.restoreNormals = function(normals, smooth, precision){ 439 | var ro, phi, theta, sinPhi, 440 | nx, ny, nz, by, bz, len, 441 | intNormals = new Uint32Array(normals.buffer, normals.byteOffset, normals.length), 442 | i = 0, k = normals.length, 443 | PI_DIV_2 = 3.141592653589793238462643 * 0.5; 444 | 445 | for (; i < k; i += 3){ 446 | ro = intNormals[i] * precision; 447 | phi = intNormals[i + 1]; 448 | 449 | if (phi === 0){ 450 | normals[i] = smooth[i] * ro; 451 | normals[i + 1] = smooth[i + 1] * ro; 452 | normals[i + 2] = smooth[i + 2] * ro; 453 | }else{ 454 | 455 | if (phi <= 4){ 456 | theta = (intNormals[i + 2] - 2) * PI_DIV_2; 457 | }else{ 458 | theta = ( (intNormals[i + 2] * 4 / phi) - 2) * PI_DIV_2; 459 | } 460 | 461 | phi *= precision * PI_DIV_2; 462 | sinPhi = ro * Math.sin(phi); 463 | 464 | nx = sinPhi * Math.cos(theta); 465 | ny = sinPhi * Math.sin(theta); 466 | nz = ro * Math.cos(phi); 467 | 468 | bz = smooth[i + 1]; 469 | by = smooth[i] - smooth[i + 2]; 470 | 471 | len = Math.sqrt(2 * bz * bz + by * by); 472 | if (len > 1e-20){ 473 | by /= len; 474 | bz /= len; 475 | } 476 | 477 | normals[i] = smooth[i] * nz + 478 | (smooth[i + 1] * bz - smooth[i + 2] * by) * ny - bz * nx; 479 | normals[i + 1] = smooth[i + 1] * nz - 480 | (smooth[i + 2] + smooth[i] ) * bz * ny + by * nx; 481 | normals[i + 2] = smooth[i + 2] * nz + 482 | (smooth[i] * by + smooth[i + 1] * bz) * ny + bz * nx; 483 | } 484 | } 485 | }; 486 | 487 | CTM.restoreMap = function(map, count, precision){ 488 | var delta, value, 489 | intMap = new Uint32Array(map.buffer, map.byteOffset, map.length), 490 | i = 0, j, len = map.length; 491 | 492 | for (; i < count; ++ i){ 493 | delta = 0; 494 | 495 | for (j = i; j < len; j += count){ 496 | value = intMap[j]; 497 | 498 | delta += value & 1? -( (value + 1) >> 1): value >> 1; 499 | 500 | map[j] = delta * precision; 501 | } 502 | } 503 | }; 504 | 505 | CTM.calcSmoothNormals = function(indices, vertices){ 506 | var smooth = new Float32Array(vertices.length), 507 | indx, indy, indz, nx, ny, nz, 508 | v1x, v1y, v1z, v2x, v2y, v2z, len, 509 | i, k; 510 | 511 | for (i = 0, k = indices.length; i < k;){ 512 | indx = indices[i ++] * 3; 513 | indy = indices[i ++] * 3; 514 | indz = indices[i ++] * 3; 515 | 516 | v1x = vertices[indy] - vertices[indx]; 517 | v2x = vertices[indz] - vertices[indx]; 518 | v1y = vertices[indy + 1] - vertices[indx + 1]; 519 | v2y = vertices[indz + 1] - vertices[indx + 1]; 520 | v1z = vertices[indy + 2] - vertices[indx + 2]; 521 | v2z = vertices[indz + 2] - vertices[indx + 2]; 522 | 523 | nx = v1y * v2z - v1z * v2y; 524 | ny = v1z * v2x - v1x * v2z; 525 | nz = v1x * v2y - v1y * v2x; 526 | 527 | len = Math.sqrt(nx * nx + ny * ny + nz * nz); 528 | if (len > 1e-10){ 529 | nx /= len; 530 | ny /= len; 531 | nz /= len; 532 | } 533 | 534 | smooth[indx] += nx; 535 | smooth[indx + 1] += ny; 536 | smooth[indx + 2] += nz; 537 | smooth[indy] += nx; 538 | smooth[indy + 1] += ny; 539 | smooth[indy + 2] += nz; 540 | smooth[indz] += nx; 541 | smooth[indz + 1] += ny; 542 | smooth[indz + 2] += nz; 543 | } 544 | 545 | for (i = 0, k = smooth.length; i < k; i += 3){ 546 | len = Math.sqrt(smooth[i] * smooth[i] + 547 | smooth[i + 1] * smooth[i + 1] + 548 | smooth[i + 2] * smooth[i + 2]); 549 | 550 | if(len > 1e-10){ 551 | smooth[i] /= len; 552 | smooth[i + 1] /= len; 553 | smooth[i + 2] /= len; 554 | } 555 | } 556 | 557 | return smooth; 558 | }; 559 | 560 | CTM.isLittleEndian = (function(){ 561 | var buffer = new ArrayBuffer(2), 562 | bytes = new Uint8Array(buffer), 563 | ints = new Uint16Array(buffer); 564 | 565 | bytes[0] = 1; 566 | 567 | return ints[0] === 1; 568 | }()); 569 | 570 | CTM.InterleavedStream = function(data, count){ 571 | this.data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); 572 | this.offset = CTM.isLittleEndian? 3: 0; 573 | this.count = count * 4; 574 | this.len = this.data.length; 575 | }; 576 | 577 | CTM.InterleavedStream.prototype.writeByte = function(value){ 578 | this.data[this.offset] = value; 579 | 580 | this.offset += this.count; 581 | if (this.offset >= this.len){ 582 | 583 | this.offset -= this.len - 4; 584 | if (this.offset >= this.count){ 585 | 586 | this.offset -= this.count + (CTM.isLittleEndian? 1: -1); 587 | } 588 | } 589 | }; 590 | 591 | CTM.Stream = function(data){ 592 | this.data = data; 593 | this.offset = 0; 594 | }; 595 | 596 | CTM.Stream.prototype.TWO_POW_MINUS23 = Math.pow(2, -23); 597 | 598 | CTM.Stream.prototype.TWO_POW_MINUS126 = Math.pow(2, -126); 599 | 600 | CTM.Stream.prototype.readByte = function(){ 601 | return this.data.charCodeAt(this.offset ++) & 0xff; 602 | }; 603 | 604 | CTM.Stream.prototype.readInt32 = function(){ 605 | var i = this.readByte(); 606 | i |= this.readByte() << 8; 607 | i |= this.readByte() << 16; 608 | return i | (this.readByte() << 24); 609 | }; 610 | 611 | CTM.Stream.prototype.readFloat32 = function(){ 612 | var m = this.readByte(); 613 | m += this.readByte() << 8; 614 | 615 | var b1 = this.readByte(); 616 | var b2 = this.readByte(); 617 | 618 | m += (b1 & 0x7f) << 16; 619 | var e = ( (b2 & 0x7f) << 1) | ( (b1 & 0x80) >>> 7); 620 | var s = b2 & 0x80? -1: 1; 621 | 622 | if (e === 255){ 623 | return m !== 0? NaN: s * Infinity; 624 | } 625 | if (e > 0){ 626 | return s * (1 + (m * this.TWO_POW_MINUS23) ) * Math.pow(2, e - 127); 627 | } 628 | if (m !== 0){ 629 | return s * m * this.TWO_POW_MINUS126; 630 | } 631 | return s * 0; 632 | }; 633 | 634 | CTM.Stream.prototype.readString = function(){ 635 | var len = this.readInt32(); 636 | 637 | this.offset += len; 638 | 639 | return this.data.substr(this.offset - len, len); 640 | }; 641 | 642 | CTM.Stream.prototype.readArrayInt32 = function(array){ 643 | var i = 0, len = array.length; 644 | 645 | while(i < len){ 646 | array[i ++] = this.readInt32(); 647 | } 648 | 649 | return array; 650 | }; 651 | 652 | CTM.Stream.prototype.readArrayFloat32 = function(array){ 653 | var i = 0, len = array.length; 654 | 655 | while(i < len){ 656 | array[i ++] = this.readFloat32(); 657 | } 658 | 659 | return array; 660 | }; 661 | -------------------------------------------------------------------------------- /samples/demo/ctm.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011 Juan Mellado 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | /* 24 | References: 25 | - "OpenCTM: The Open Compressed Triangle Mesh file format" by Marcus Geelnard 26 | http://openctm.sourceforge.net/ 27 | */ 28 | 29 | var CTM = CTM || {}; 30 | 31 | CTM.CompressionMethod = { 32 | RAW: 0x00574152, 33 | MG1: 0x0031474d, 34 | MG2: 0x0032474d 35 | }; 36 | 37 | CTM.Flags = { 38 | NORMALS: 0x00000001 39 | }; 40 | 41 | CTM.File = function(stream){ 42 | this.load(stream); 43 | }; 44 | 45 | CTM.File.prototype.load = function(stream){ 46 | this.header = new CTM.FileHeader(stream); 47 | 48 | this.body = new CTM.FileBody(this.header); 49 | 50 | this.getReader().read(stream, this.body); 51 | }; 52 | 53 | CTM.File.prototype.getReader = function(){ 54 | var reader; 55 | 56 | switch(this.header.compressionMethod){ 57 | case CTM.CompressionMethod.RAW: 58 | reader = new CTM.ReaderRAW(); 59 | break; 60 | case CTM.CompressionMethod.MG1: 61 | reader = new CTM.ReaderMG1(); 62 | break; 63 | case CTM.CompressionMethod.MG2: 64 | reader = new CTM.ReaderMG2(); 65 | break; 66 | } 67 | 68 | return reader; 69 | }; 70 | 71 | CTM.FileHeader = function(stream){ 72 | stream.readInt32(); //magic "OCTM" 73 | this.fileFormat = stream.readInt32(); 74 | this.compressionMethod = stream.readInt32(); 75 | this.vertexCount = stream.readInt32(); 76 | this.triangleCount = stream.readInt32(); 77 | this.uvMapCount = stream.readInt32(); 78 | this.attrMapCount = stream.readInt32(); 79 | this.flags = stream.readInt32(); 80 | this.comment = stream.readString(); 81 | }; 82 | 83 | CTM.FileHeader.prototype.hasNormals = function(){ 84 | return this.flags & CTM.Flags.NORMALS; 85 | }; 86 | 87 | CTM.FileBody = function(header){ 88 | var i = header.triangleCount * 3, 89 | v = header.vertexCount * 3, 90 | n = header.hasNormals()? header.vertexCount * 3: 0, 91 | u = header.vertexCount * 2, 92 | a = header.vertexCount * 4, 93 | j = 0; 94 | 95 | var data = new ArrayBuffer( 96 | (i + v + n + (u * header.uvMapCount) + (a * header.attrMapCount) ) * 4); 97 | 98 | this.indices = new Uint32Array(data, 0, i); 99 | 100 | this.vertices = new Float32Array(data, i * 4, v); 101 | 102 | if ( header.hasNormals() ){ 103 | this.normals = new Float32Array(data, (i + v) * 4, n); 104 | } 105 | 106 | if (header.uvMapCount){ 107 | this.uvMaps = []; 108 | for (j = 0; j < header.uvMapCount; ++ j){ 109 | this.uvMaps[j] = {uv: new Float32Array(data, 110 | (i + v + n + (j * u) ) * 4, u) }; 111 | } 112 | } 113 | 114 | if (header.attrMapCount){ 115 | this.attrMaps = []; 116 | for (j = 0; j < header.attrMapCount; ++ j){ 117 | this.attrMaps[j] = {attr: new Float32Array(data, 118 | (i + v + n + (u * header.uvMapCount) + (j * a) ) * 4, a) }; 119 | } 120 | } 121 | }; 122 | 123 | CTM.FileMG2Header = function(stream){ 124 | stream.readInt32(); //magic "MG2H" 125 | this.vertexPrecision = stream.readFloat32(); 126 | this.normalPrecision = stream.readFloat32(); 127 | this.lowerBoundx = stream.readFloat32(); 128 | this.lowerBoundy = stream.readFloat32(); 129 | this.lowerBoundz = stream.readFloat32(); 130 | this.higherBoundx = stream.readFloat32(); 131 | this.higherBoundy = stream.readFloat32(); 132 | this.higherBoundz = stream.readFloat32(); 133 | this.divx = stream.readInt32(); 134 | this.divy = stream.readInt32(); 135 | this.divz = stream.readInt32(); 136 | 137 | this.sizex = (this.higherBoundx - this.lowerBoundx) / this.divx; 138 | this.sizey = (this.higherBoundy - this.lowerBoundy) / this.divy; 139 | this.sizez = (this.higherBoundz - this.lowerBoundz) / this.divz; 140 | }; 141 | 142 | CTM.ReaderRAW = function(){ 143 | }; 144 | 145 | CTM.ReaderRAW.prototype.read = function(stream, body){ 146 | this.readIndices(stream, body.indices); 147 | this.readVertices(stream, body.vertices); 148 | 149 | if (body.normals){ 150 | this.readNormals(stream, body.normals); 151 | } 152 | if (body.uvMaps){ 153 | this.readUVMaps(stream, body.uvMaps); 154 | } 155 | if (body.attrMaps){ 156 | this.readAttrMaps(stream, body.attrMaps); 157 | } 158 | }; 159 | 160 | CTM.ReaderRAW.prototype.readIndices = function(stream, indices){ 161 | stream.readInt32(); //magic "INDX" 162 | stream.readArrayInt32(indices); 163 | }; 164 | 165 | CTM.ReaderRAW.prototype.readVertices = function(stream, vertices){ 166 | stream.readInt32(); //magic "VERT" 167 | stream.readArrayFloat32(vertices); 168 | }; 169 | 170 | CTM.ReaderRAW.prototype.readNormals = function(stream, normals){ 171 | stream.readInt32(); //magic "NORM" 172 | stream.readArrayFloat32(normals); 173 | }; 174 | 175 | CTM.ReaderRAW.prototype.readUVMaps = function(stream, uvMaps){ 176 | var i = 0; 177 | for (; i < uvMaps.length; ++ i){ 178 | stream.readInt32(); //magic "TEXC" 179 | 180 | uvMaps[i].name = stream.readString(); 181 | uvMaps[i].filename = stream.readString(); 182 | stream.readArrayFloat32(uvMaps[i].uv); 183 | } 184 | }; 185 | 186 | CTM.ReaderRAW.prototype.readAttrMaps = function(stream, attrMaps){ 187 | var i = 0; 188 | for (; i < attrMaps.length; ++ i){ 189 | stream.readInt32(); //magic "ATTR" 190 | 191 | attrMaps[i].name = stream.readString(); 192 | stream.readArrayFloat32(attrMaps[i].attr); 193 | } 194 | }; 195 | 196 | CTM.ReaderMG1 = function(){ 197 | }; 198 | 199 | CTM.ReaderMG1.prototype.read = function(stream, body){ 200 | this.readIndices(stream, body.indices); 201 | this.readVertices(stream, body.vertices); 202 | 203 | if (body.normals){ 204 | this.readNormals(stream, body.normals); 205 | } 206 | if (body.uvMaps){ 207 | this.readUVMaps(stream, body.uvMaps); 208 | } 209 | if (body.attrMaps){ 210 | this.readAttrMaps(stream, body.attrMaps); 211 | } 212 | }; 213 | 214 | CTM.ReaderMG1.prototype.readIndices = function(stream, indices){ 215 | stream.readInt32(); //magic "INDX" 216 | var size = stream.readInt32(); //packed size 217 | 218 | var interleaved = new CTM.InterleavedStream(indices, 3); 219 | CTM.decompress(stream, size, interleaved); 220 | 221 | CTM.restoreIndices(indices, indices.length); 222 | }; 223 | 224 | CTM.ReaderMG1.prototype.readVertices = function(stream, vertices){ 225 | stream.readInt32(); //magic "VERT" 226 | var size = stream.readInt32(); //packed size 227 | 228 | var interleaved = new CTM.InterleavedStream(vertices, 1); 229 | CTM.decompress(stream, size, interleaved); 230 | }; 231 | 232 | CTM.ReaderMG1.prototype.readNormals = function(stream, normals){ 233 | stream.readInt32(); //magic "NORM" 234 | var size = stream.readInt32(); //packed size 235 | 236 | var interleaved = new CTM.InterleavedStream(normals, 3); 237 | CTM.decompress(stream, size, interleaved); 238 | }; 239 | 240 | CTM.ReaderMG1.prototype.readUVMaps = function(stream, uvMaps){ 241 | var i = 0; 242 | for (; i < uvMaps.length; ++ i){ 243 | stream.readInt32(); //magic "TEXC" 244 | 245 | uvMaps[i].name = stream.readString(); 246 | uvMaps[i].filename = stream.readString(); 247 | 248 | var size = stream.readInt32(); //packed size 249 | 250 | var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2); 251 | CTM.decompress(stream, size, interleaved); 252 | } 253 | }; 254 | 255 | CTM.ReaderMG1.prototype.readAttrMaps = function(stream, attrMaps){ 256 | var i = 0; 257 | for (; i < attrMaps.length; ++ i){ 258 | stream.readInt32(); //magic "ATTR" 259 | 260 | attrMaps[i].name = stream.readString(); 261 | 262 | var size = stream.readInt32(); //packed size 263 | 264 | var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4); 265 | CTM.decompress(stream, size, interleaved); 266 | } 267 | }; 268 | 269 | CTM.ReaderMG2 = function(){ 270 | }; 271 | 272 | CTM.ReaderMG2.prototype.read = function(stream, body){ 273 | this.MG2Header = new CTM.FileMG2Header(stream); 274 | 275 | this.readVertices(stream, body.vertices); 276 | this.readIndices(stream, body.indices); 277 | 278 | if (body.normals){ 279 | this.readNormals(stream, body); 280 | } 281 | if (body.uvMaps){ 282 | this.readUVMaps(stream, body.uvMaps); 283 | } 284 | if (body.attrMaps){ 285 | this.readAttrMaps(stream, body.attrMaps); 286 | } 287 | }; 288 | 289 | CTM.ReaderMG2.prototype.readVertices = function(stream, vertices){ 290 | stream.readInt32(); //magic "VERT" 291 | var size = stream.readInt32(); //packed size 292 | 293 | var interleaved = new CTM.InterleavedStream(vertices, 3); 294 | CTM.decompress(stream, size, interleaved); 295 | 296 | var gridIndices = this.readGridIndices(stream, vertices); 297 | 298 | CTM.restoreVertices(vertices, this.MG2Header, gridIndices, this.MG2Header.vertexPrecision); 299 | }; 300 | 301 | CTM.ReaderMG2.prototype.readGridIndices = function(stream, vertices){ 302 | stream.readInt32(); //magic "GIDX" 303 | var size = stream.readInt32(); //packed size 304 | 305 | var gridIndices = new Uint32Array(vertices.length / 3); 306 | 307 | var interleaved = new CTM.InterleavedStream(gridIndices, 1); 308 | CTM.decompress(stream, size, interleaved); 309 | 310 | CTM.restoreGridIndices(gridIndices, gridIndices.length); 311 | 312 | return gridIndices; 313 | }; 314 | 315 | CTM.ReaderMG2.prototype.readIndices = function(stream, indices){ 316 | stream.readInt32(); //magic "INDX" 317 | var size = stream.readInt32(); //packed size 318 | 319 | var interleaved = new CTM.InterleavedStream(indices, 3); 320 | CTM.decompress(stream, size, interleaved); 321 | 322 | CTM.restoreIndices(indices, indices.length); 323 | }; 324 | 325 | CTM.ReaderMG2.prototype.readNormals = function(stream, body){ 326 | stream.readInt32(); //magic "NORM" 327 | var size = stream.readInt32(); //packed size 328 | 329 | var interleaved = new CTM.InterleavedStream(body.normals, 3); 330 | CTM.decompress(stream, size, interleaved); 331 | 332 | var smooth = CTM.calcSmoothNormals(body.indices, body.vertices); 333 | 334 | CTM.restoreNormals(body.normals, smooth, this.MG2Header.normalPrecision); 335 | }; 336 | 337 | CTM.ReaderMG2.prototype.readUVMaps = function(stream, uvMaps){ 338 | var i = 0; 339 | for (; i < uvMaps.length; ++ i){ 340 | stream.readInt32(); //magic "TEXC" 341 | 342 | uvMaps[i].name = stream.readString(); 343 | uvMaps[i].filename = stream.readString(); 344 | 345 | var precision = stream.readFloat32(); 346 | 347 | var size = stream.readInt32(); //packed size 348 | 349 | var interleaved = new CTM.InterleavedStream(uvMaps[i].uv, 2); 350 | CTM.decompress(stream, size, interleaved); 351 | 352 | CTM.restoreMap(uvMaps[i].uv, 2, precision); 353 | } 354 | }; 355 | 356 | CTM.ReaderMG2.prototype.readAttrMaps = function(stream, attrMaps){ 357 | var i = 0; 358 | for (; i < attrMaps.length; ++ i){ 359 | stream.readInt32(); //magic "ATTR" 360 | 361 | attrMaps[i].name = stream.readString(); 362 | 363 | var precision = stream.readFloat32(); 364 | 365 | var size = stream.readInt32(); //packed size 366 | 367 | var interleaved = new CTM.InterleavedStream(attrMaps[i].attr, 4); 368 | CTM.decompress(stream, size, interleaved); 369 | 370 | CTM.restoreMap(attrMaps[i].attr, 4, precision); 371 | } 372 | }; 373 | 374 | CTM.decompress = function(stream, size, interleaved){ 375 | var offset = stream.offset; 376 | LZMA.decompress(stream, stream, interleaved, interleaved.data.length); 377 | stream.offset = offset + 5 + size; 378 | }; 379 | 380 | CTM.restoreIndices = function(indices, len){ 381 | var i = 3; 382 | if (len > 0){ 383 | indices[2] += indices[0]; 384 | indices[1] += indices[0]; 385 | } 386 | for (; i < len; i += 3){ 387 | indices[i] += indices[i - 3]; 388 | 389 | if (indices[i] === indices[i - 3]){ 390 | indices[i + 1] += indices[i - 2]; 391 | }else{ 392 | indices[i + 1] += indices[i]; 393 | } 394 | 395 | indices[i + 2] += indices[i]; 396 | } 397 | }; 398 | 399 | CTM.restoreGridIndices = function(gridIndices, len){ 400 | var i = 1; 401 | for (; i < len; ++ i){ 402 | gridIndices[i] += gridIndices[i - 1]; 403 | } 404 | }; 405 | 406 | CTM.restoreVertices = function(vertices, grid, gridIndices, precision){ 407 | var gridIdx, delta, x, y, z, 408 | intVertices = new Uint32Array(vertices.buffer, vertices.byteOffset, vertices.length), 409 | ydiv = grid.divx, zdiv = ydiv * grid.divy, 410 | prevGridIdx = 0x7fffffff, prevDelta = 0, 411 | i = 0, j = 0, len = gridIndices.length; 412 | 413 | for (; i < len; j += 3){ 414 | x = gridIdx = gridIndices[i ++]; 415 | 416 | z = ~~(x / zdiv); 417 | x -= ~~(z * zdiv); 418 | y = ~~(x / ydiv); 419 | x -= ~~(y * ydiv); 420 | 421 | delta = intVertices[j]; 422 | if (gridIdx === prevGridIdx){ 423 | delta += prevDelta; 424 | } 425 | 426 | vertices[j] = grid.lowerBoundx + 427 | x * grid.sizex + precision * delta; 428 | vertices[j + 1] = grid.lowerBoundy + 429 | y * grid.sizey + precision * intVertices[j + 1]; 430 | vertices[j + 2] = grid.lowerBoundz + 431 | z * grid.sizez + precision * intVertices[j + 2]; 432 | 433 | prevGridIdx = gridIdx; 434 | prevDelta = delta; 435 | } 436 | }; 437 | 438 | CTM.restoreNormals = function(normals, smooth, precision){ 439 | var ro, phi, theta, sinPhi, 440 | nx, ny, nz, by, bz, len, 441 | intNormals = new Uint32Array(normals.buffer, normals.byteOffset, normals.length), 442 | i = 0, k = normals.length, 443 | PI_DIV_2 = 3.141592653589793238462643 * 0.5; 444 | 445 | for (; i < k; i += 3){ 446 | ro = intNormals[i] * precision; 447 | phi = intNormals[i + 1]; 448 | 449 | if (phi === 0){ 450 | normals[i] = smooth[i] * ro; 451 | normals[i + 1] = smooth[i + 1] * ro; 452 | normals[i + 2] = smooth[i + 2] * ro; 453 | }else{ 454 | 455 | if (phi <= 4){ 456 | theta = (intNormals[i + 2] - 2) * PI_DIV_2; 457 | }else{ 458 | theta = ( (intNormals[i + 2] * 4 / phi) - 2) * PI_DIV_2; 459 | } 460 | 461 | phi *= precision * PI_DIV_2; 462 | sinPhi = ro * Math.sin(phi); 463 | 464 | nx = sinPhi * Math.cos(theta); 465 | ny = sinPhi * Math.sin(theta); 466 | nz = ro * Math.cos(phi); 467 | 468 | bz = smooth[i + 1]; 469 | by = smooth[i] - smooth[i + 2]; 470 | 471 | len = Math.sqrt(2 * bz * bz + by * by); 472 | if (len > 1e-20){ 473 | by /= len; 474 | bz /= len; 475 | } 476 | 477 | normals[i] = smooth[i] * nz + 478 | (smooth[i + 1] * bz - smooth[i + 2] * by) * ny - bz * nx; 479 | normals[i + 1] = smooth[i + 1] * nz - 480 | (smooth[i + 2] + smooth[i] ) * bz * ny + by * nx; 481 | normals[i + 2] = smooth[i + 2] * nz + 482 | (smooth[i] * by + smooth[i + 1] * bz) * ny + bz * nx; 483 | } 484 | } 485 | }; 486 | 487 | CTM.restoreMap = function(map, count, precision){ 488 | var delta, value, 489 | intMap = new Uint32Array(map.buffer, map.byteOffset, map.length), 490 | i = 0, j, len = map.length; 491 | 492 | for (; i < count; ++ i){ 493 | delta = 0; 494 | 495 | for (j = i; j < len; j += count){ 496 | value = intMap[j]; 497 | 498 | delta += value & 1? -( (value + 1) >> 1): value >> 1; 499 | 500 | map[j] = delta * precision; 501 | } 502 | } 503 | }; 504 | 505 | CTM.calcSmoothNormals = function(indices, vertices){ 506 | var smooth = new Float32Array(vertices.length), 507 | indx, indy, indz, nx, ny, nz, 508 | v1x, v1y, v1z, v2x, v2y, v2z, len, 509 | i, k; 510 | 511 | for (i = 0, k = indices.length; i < k;){ 512 | indx = indices[i ++] * 3; 513 | indy = indices[i ++] * 3; 514 | indz = indices[i ++] * 3; 515 | 516 | v1x = vertices[indy] - vertices[indx]; 517 | v2x = vertices[indz] - vertices[indx]; 518 | v1y = vertices[indy + 1] - vertices[indx + 1]; 519 | v2y = vertices[indz + 1] - vertices[indx + 1]; 520 | v1z = vertices[indy + 2] - vertices[indx + 2]; 521 | v2z = vertices[indz + 2] - vertices[indx + 2]; 522 | 523 | nx = v1y * v2z - v1z * v2y; 524 | ny = v1z * v2x - v1x * v2z; 525 | nz = v1x * v2y - v1y * v2x; 526 | 527 | len = Math.sqrt(nx * nx + ny * ny + nz * nz); 528 | if (len > 1e-10){ 529 | nx /= len; 530 | ny /= len; 531 | nz /= len; 532 | } 533 | 534 | smooth[indx] += nx; 535 | smooth[indx + 1] += ny; 536 | smooth[indx + 2] += nz; 537 | smooth[indy] += nx; 538 | smooth[indy + 1] += ny; 539 | smooth[indy + 2] += nz; 540 | smooth[indz] += nx; 541 | smooth[indz + 1] += ny; 542 | smooth[indz + 2] += nz; 543 | } 544 | 545 | for (i = 0, k = smooth.length; i < k; i += 3){ 546 | len = Math.sqrt(smooth[i] * smooth[i] + 547 | smooth[i + 1] * smooth[i + 1] + 548 | smooth[i + 2] * smooth[i + 2]); 549 | 550 | if(len > 1e-10){ 551 | smooth[i] /= len; 552 | smooth[i + 1] /= len; 553 | smooth[i + 2] /= len; 554 | } 555 | } 556 | 557 | return smooth; 558 | }; 559 | 560 | CTM.isLittleEndian = (function(){ 561 | var buffer = new ArrayBuffer(2), 562 | bytes = new Uint8Array(buffer), 563 | ints = new Uint16Array(buffer); 564 | 565 | bytes[0] = 1; 566 | 567 | return ints[0] === 1; 568 | }()); 569 | 570 | CTM.InterleavedStream = function(data, count){ 571 | this.data = new Uint8Array(data.buffer, data.byteOffset, data.byteLength); 572 | this.offset = CTM.isLittleEndian? 3: 0; 573 | this.count = count * 4; 574 | this.len = this.data.length; 575 | }; 576 | 577 | CTM.InterleavedStream.prototype.writeByte = function(value){ 578 | this.data[this.offset] = value; 579 | 580 | this.offset += this.count; 581 | if (this.offset >= this.len){ 582 | 583 | this.offset -= this.len - 4; 584 | if (this.offset >= this.count){ 585 | 586 | this.offset -= this.count + (CTM.isLittleEndian? 1: -1); 587 | } 588 | } 589 | }; 590 | 591 | CTM.Stream = function(data){ 592 | this.data = data; 593 | this.offset = 0; 594 | }; 595 | 596 | CTM.Stream.prototype.TWO_POW_MINUS23 = Math.pow(2, -23); 597 | 598 | CTM.Stream.prototype.TWO_POW_MINUS126 = Math.pow(2, -126); 599 | 600 | CTM.Stream.prototype.readByte = function(){ 601 | return this.data.charCodeAt(this.offset ++) & 0xff; 602 | }; 603 | 604 | CTM.Stream.prototype.readInt32 = function(){ 605 | var i = this.readByte(); 606 | i |= this.readByte() << 8; 607 | i |= this.readByte() << 16; 608 | return i | (this.readByte() << 24); 609 | }; 610 | 611 | CTM.Stream.prototype.readFloat32 = function(){ 612 | var m = this.readByte(); 613 | m += this.readByte() << 8; 614 | 615 | var b1 = this.readByte(); 616 | var b2 = this.readByte(); 617 | 618 | m += (b1 & 0x7f) << 16; 619 | var e = ( (b2 & 0x7f) << 1) | ( (b1 & 0x80) >>> 7); 620 | var s = b2 & 0x80? -1: 1; 621 | 622 | if (e === 255){ 623 | return m !== 0? NaN: s * Infinity; 624 | } 625 | if (e > 0){ 626 | return s * (1 + (m * this.TWO_POW_MINUS23) ) * Math.pow(2, e - 127); 627 | } 628 | if (m !== 0){ 629 | return s * m * this.TWO_POW_MINUS126; 630 | } 631 | return s * 0; 632 | }; 633 | 634 | CTM.Stream.prototype.readString = function(){ 635 | var len = this.readInt32(); 636 | 637 | this.offset += len; 638 | 639 | return this.data.substr(this.offset - len, len); 640 | }; 641 | 642 | CTM.Stream.prototype.readArrayInt32 = function(array){ 643 | var i = 0, len = array.length; 644 | 645 | while(i < len){ 646 | array[i ++] = this.readInt32(); 647 | } 648 | 649 | return array; 650 | }; 651 | 652 | CTM.Stream.prototype.readArrayFloat32 = function(array){ 653 | var i = 0, len = array.length; 654 | 655 | while(i < len){ 656 | array[i ++] = this.readFloat32(); 657 | } 658 | 659 | return array; 660 | }; 661 | --------------------------------------------------------------------------------