├── .gitignore ├── README.md ├── examples ├── assets │ └── images │ │ └── test-uvmap.jpg ├── js │ ├── picogl │ │ ├── gl-matrix.js │ │ ├── picogl-utils.js │ │ └── picogl.min.js │ └── threejs │ │ ├── THREE.DumbJsonLoader.js │ │ ├── THREE.FBXLoader.js │ │ ├── THREE.OBJLoader.js │ │ ├── THREE.OBJLoader2.js │ │ └── inflate.min.js ├── obj2prwm.html ├── picogl-prwm-loader.html ├── prwm-inspector.html ├── svg2prwm.html ├── three-buffergeometry-to-prwm.html ├── three-prwm-loader-benchmark.html └── three-prwm-loader.html ├── generate-prwm-models.sh ├── implementations ├── obj2prwm │ ├── LICENSE │ ├── README.md │ ├── build │ │ ├── obj2prwm.dev.js │ │ └── obj2prwm.min.js │ ├── cli.js │ ├── index.js │ ├── package.json │ └── utils │ │ └── compute-normals.js ├── picogl-prwm-loader │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── build │ │ ├── PRWMLoader.dev.js │ │ └── PRWMLoader.min.js │ ├── index.js │ └── package.json ├── prwm │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── build │ │ ├── prwm.dev.js │ │ └── prwm.min.js │ ├── eyeball.js │ ├── index.js │ ├── package.json │ ├── prwm │ │ ├── attribute-types.js │ │ ├── decode.js │ │ └── encode.js │ ├── test │ │ ├── assets │ │ │ ├── cube-BE.prwm │ │ │ ├── cube-LE.prwm │ │ │ └── cube.js │ │ ├── attribute-types.js │ │ ├── decode.js │ │ ├── encode.js │ │ ├── fullround.js │ │ └── is-big-endian.js │ └── utils │ │ └── is-big-endian-platform.js ├── svg2prwm │ ├── LICENSE │ ├── README.md │ ├── build │ │ ├── svg2prwm.dev.js │ │ └── svg2prwm.min.js │ ├── cli.js │ ├── index.js │ └── package.json ├── three-buffergeometry-to-prwm │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── build │ │ ├── three-buffergeometry-to-prwm.dev.js │ │ └── three-buffergeometry-to-prwm.min.js │ ├── index.js │ └── package.json └── three-prwm-loader │ ├── .gitignore │ ├── PRWMLoader.js │ ├── README.md │ ├── build │ ├── three-prwm-loader.dev.js │ └── three-prwm-loader.min.js │ ├── index.js │ └── package.json ├── legacy ├── vprb.md └── vprm.md ├── models ├── ascii-obj │ ├── cerberus.obj │ ├── faceted-nefertiti.obj │ ├── smooth-nefertiti.obj │ ├── smooth-suzanne.obj │ ├── stanford-bunny.obj │ ├── stanford-dragon.obj │ ├── unit-cube.obj │ └── vr_controller_vive_1_5.tri.obj ├── binary-fbx │ └── faceted-nefertiti.fbx ├── json │ ├── cube.json │ ├── faceted-heavy-suzanne.json │ ├── faceted-nefertiti.json │ ├── smooth-heavy-suzanne.json │ └── smooth-nefertiti.json ├── prwm │ ├── bowtie-logo.be.prwm │ ├── bowtie-logo.le.prwm │ ├── cerberus.be.prwm │ ├── cerberus.le.prwm │ ├── cube.be.prwm │ ├── cube.le.prwm │ ├── faceted-nefertiti.be.prwm │ ├── faceted-nefertiti.le.prwm │ ├── github-logo.be.prwm │ ├── github-logo.le.prwm │ ├── nodejs-logo.be.prwm │ ├── nodejs-logo.le.prwm │ ├── smooth-nefertiti.be.prwm │ ├── smooth-nefertiti.le.prwm │ ├── smooth-suzanne.be.prwm │ ├── smooth-suzanne.le.prwm │ ├── stanford-bunny.be.prwm │ ├── stanford-bunny.le.prwm │ ├── stanford-dragon.be.prwm │ ├── stanford-dragon.le.prwm │ ├── stylus-logo.be.prwm │ ├── stylus-logo.le.prwm │ ├── vive-controller.be.prwm │ └── vive-controller.le.prwm └── svg │ ├── bowtie.svg │ ├── github.svg │ ├── nodejs.svg │ └── stylus.svg ├── rebuild-all-modules.sh └── specifications └── prwm.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | .idea 40 | testbed/ 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Packed Raw WebGL Model (PRWM) 2 | 3 | Packed Raw WebGL Model is a binary file format for nD geometries specifically designed for JavaScript and WebGL with a strong focus on fast parsing (from 1ms to 0.1ms in Chrome 59 on a MBP Late 2013). 4 | 5 | The format support indexed (`drawElements`) and non-indexed (`drawArrays`) geometries. It is designed to allow any number of custom attributes, doesn't force the use of any pre-defined attributes and supports integer attributes (WebGL2's `vertexAttribIPointer`). However it doesn't support any type of bone-based animations. 6 | 7 | ## Why is it fast ? 8 | 9 | Aside from a few metadatas (name of the attributes, number of values, encoding types, etc.), all the WebGL `array buffer` and `element array buffer` typed arrays are encoded *as is* in the PRWM file. Since typed arrays can be instantiated as windowed views over an existing arraybuffer and since arraybuffers can be retrieved directly by the browser with Fetch or XMLHttpRequest (level 2), this means that the file can be decoded virtually instantly. To put it another way, **instantiating the typed arrays for the WebGL buffers from a PRWM file doesn't require us to actually read their content in our javascript code**. 10 | 11 | An interesting side effect is that for the same number of attributes (i.e. positions, normals and uvs) the parsing time is the same for a cube encoded in a 300 bytes files than for a complex geometry encoded in a 50 megabytes file. 12 | 13 | ## The Endianness issue 14 | 15 | Endianness ([wikipedia](https://en.wikipedia.org/wiki/Endianness)) refers to the way multi-byte values, such as integers on 16 bits and floats on 32 bits for example, are represented in the computer memory. In Big-Endian byte order, the most important byte is written first and the least important byte is written last, so the 16 bits integer `1` would be represented as `00 01` (read left to right). In Little-Endian this is the opposite, the least important byte comes in first and the most important byte comes in last, so the same 16 bits integer value would be represented as `01 00` (read right to left). Trying to interpret the Little-Endian representation as Big-Endian would give us `256` instead of `1`. 16 | 17 | The issue is that we can't define the endianness of a typed array or an arraybuffer in javascript, whether it is one or the other is implementation dependent ("*the alternative that is most efficient for the implementation*"). So if a client on a platform using the Big-Endian byte order receives an arraybuffer originally encoded in Little-Endian and naively creates a windowed typed array over it, all of its data will be corrupted. 18 | 19 | In a PRWM file, the endianness of the file is encoded in its header, so the decoder is able to fallback on a less efficient code path reading and converting each values manually. Obviously this is not optimal and pretty much invalidate the advantage of this file format. Please see the guidelines below on how to avoid this issue. 20 | 21 | ## Guidelines 22 | 23 | When using PRWM files, it is strongly recommended to : 24 | 25 | * always provide Little-Endian and Big-Endian versions of the files 26 | * to check the endianness of the client platform and request the correct file 27 | 28 | The reference encoder/decoder provides a method to check the endianness of the platform. 29 | 30 | ## Specifications 31 | 32 | * [PRWM version 1 Specifications](https://github.com/kchapelier/PRWM/blob/master/specifications/prwm.md) 33 | 34 | ## Implementations 35 | 36 | ### prwm 37 | 38 | [github](https://github.com/kchapelier/PRWM/tree/master/implementations/prwm) / [npm](https://www.npmjs.com/package/prwm) 39 | 40 | The reference encoder/decoder for the file format. 41 | 42 | ### obj2prwm 43 | 44 | [github](https://github.com/kchapelier/PRWM/tree/master/implementations/obj2prwm) / [npm](https://www.npmjs.com/package/obj2prwm) 45 | 46 | A module and simple CLI utility to convert OBJ files to PRWM files. 47 | 48 | ### svg2prwm 49 | 50 | [github](https://github.com/kchapelier/PRWM/tree/master/implementations/svg2prwm) / [npm](https://www.npmjs.com/package/svg2prwm) 51 | 52 | A module and simple CLI utility to convert SVG files to PRWM files. 53 | 54 | ### three-buffergeometry-to-prwm 55 | 56 | [github](https://github.com/kchapelier/PRWM/tree/master/implementations/three-buffergeometry-to-prwm) / [npm](https://www.npmjs.com/package/three-buffergeometry-to-prwm) / [online example](http://www.kchapelier.com/prwm/examples/three-buffergeometry-to-prwm.html) 57 | 58 | A module to create a PRWM file out of any instance of THREE.BufferGeometry. Useful for procedural generation application 59 | 60 | ### three-prwm-loader 61 | 62 | [github](https://github.com/kchapelier/PRWM/tree/master/implementations/three-prwm-loader) / [npm](https://www.npmjs.com/package/three-prwm-loader) / [online example](http://www.kchapelier.com/prwm/examples/three-prwm-loader.html) / [online benchmark](http://www.kchapelier.com/prwm/examples/three-prwm-loader-benchmark.html) 63 | 64 | Merged in Three.js since r86: [github](https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/PRWMLoader.js) / [online example](https://threejs.org/examples/?q=prwm#webgl_loader_prwm) 65 | 66 | A loader for the Three.js engine. 67 | 68 | ### picogl-prwm-loader 69 | 70 | [github](https://github.com/kchapelier/PRWM/tree/master/implementations/picogl-prwm-loader) / [npm](https://www.npmjs.com/package/picogl-prwm-loader) / [online example](http://www.kchapelier.com/prwm/examples/picogl-prwm-loader.html) 71 | 72 | A loader for PicoGL.js 73 | 74 | ## Example files 75 | 76 | * [Several valid PRWM files](https://github.com/kchapelier/PRWM/tree/master/models/prwm) 77 | 78 | ## Online tools 79 | 80 | ### PRWM Inspector 81 | 82 | http://www.kchapelier.com/prwm/examples/prwm-inspector.html 83 | 84 | Drag and drop a PRWM file to analyze its content. 85 | 86 | ### Convert OBJ to PRWM 87 | 88 | http://www.kchapelier.com/prwm/examples/obj2prwm.html 89 | 90 | A basic GUI for obj2prwm. Drag and drop an OBJ file (with triangles only) and convert it to PRWM. 91 | 92 | ### Convert SVG to PRWM 93 | 94 | http://www.kchapelier.com/prwm/examples/svg2prwm.html 95 | 96 | A basic GUI for svg2prwm. Drag and drop an SVG file and convert its path to PRWM. 97 | 98 | ## Legacy 99 | 100 | Variable Precision Model (VPRM) and Variable Precision Bundle (VPRB) were older file format specifications with a focus on file size. They had a fixed endianness. They supported custom 24 bits typed array for attributes values and the indices values were encoded on the least possible amount of bits to save space (i.e. an indexed cube geometry with 24 vertices would have its indices encoded with 5 bits per value). 101 | 102 | The prototypes for the encoder and the decoder were growing in complexity while the gain on the file size wasn't really impressive. Finally the parsing was relatively slow as each attribute value and index had to be parsed individually. 103 | 104 | The early draft of those specifications are still available [here](https://github.com/kchapelier/PRWM/blob/master/legacy/). 105 | -------------------------------------------------------------------------------- /examples/assets/images/test-uvmap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/examples/assets/images/test-uvmap.jpg -------------------------------------------------------------------------------- /examples/js/threejs/THREE.DumbJsonLoader.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | THREE.DumbJSONLoader = function ( manager ) { 4 | 5 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 6 | this.mappingAttributes = { 7 | vertices: ['position', Float32Array, 3], 8 | normals: ['normal', Float32Array, 3], 9 | uvs: ['uv', Float32Array, 2] 10 | }; 11 | 12 | }; 13 | 14 | THREE.DumbJSONLoader.prototype = { 15 | 16 | constructor: THREE.DumbJSONLoader, 17 | 18 | load: function ( url, onLoad, onProgress, onError ) { 19 | 20 | var scope = this; 21 | 22 | var loader = new THREE.FileLoader( scope.manager ); 23 | loader.setResponseType( 'json' ); 24 | loader.load( url, function ( json ) { 25 | onLoad( scope.parse( json ) ); 26 | }, onProgress, onError ); 27 | 28 | }, 29 | 30 | parse: function ( json ) { 31 | 32 | console.time( 'DumbJSONLoader' ); 33 | 34 | var attributesKey = Object.keys(json), 35 | bufferGeometry = new THREE.BufferGeometry(), 36 | mapping, 37 | attribute, 38 | i; 39 | 40 | for (i = 0; i < attributesKey.length; i++) { 41 | if (attributesKey[i] !== 'indices' && !!this.mappingAttributes[attributesKey[i]]) { 42 | mapping = this.mappingAttributes[attributesKey[i]]; 43 | attribute = json[attributesKey[i]]; 44 | bufferGeometry.addAttribute( 45 | mapping[0], 46 | new THREE.BufferAttribute(new mapping[1](attribute), mapping[2]) 47 | ); 48 | } 49 | } 50 | 51 | bufferGeometry.setIndex(new THREE.BufferAttribute(new Uint32Array(json.indices), 1)); 52 | 53 | console.timeEnd( 'DumbJSONLoader' ); 54 | 55 | return bufferGeometry; 56 | 57 | } 58 | 59 | }; 60 | -------------------------------------------------------------------------------- /examples/js/threejs/inflate.min.js: -------------------------------------------------------------------------------- 1 | /** @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License */(function() {'use strict';var m=this;function q(c,d){var a=c.split("."),b=m;!(a[0]in b)&&b.execScript&&b.execScript("var "+a[0]);for(var e;a.length&&(e=a.shift());)!a.length&&void 0!==d?b[e]=d:b=b[e]?b[e]:b[e]={}};var s="undefined"!==typeof Uint8Array&&"undefined"!==typeof Uint16Array&&"undefined"!==typeof Uint32Array&&"undefined"!==typeof DataView;function t(c){var d=c.length,a=0,b=Number.POSITIVE_INFINITY,e,f,g,h,k,l,p,n,r,K;for(n=0;na&&(a=c[n]),c[n]>=1;K=g<<16|n;for(r=l;r>>=1;switch(c){case 0:var d=this.input,a=this.a,b=this.c,e=this.b,f=d.length,g=void 0,h=void 0,k=b.length,l=void 0;this.d=this.f=0;if(a+1>=f)throw Error("invalid uncompressed block header: LEN");g=d[a++]|d[a++]<<8;if(a+1>=f)throw Error("invalid uncompressed block header: NLEN");h=d[a++]|d[a++]<<8;if(g===~h)throw Error("invalid uncompressed block header: length verify");if(a+g>d.length)throw Error("input buffer is broken");switch(this.i){case w:for(;e+ 4 | g>b.length;){l=k-e;g-=l;if(s)b.set(d.subarray(a,a+l),e),e+=l,a+=l;else for(;l--;)b[e++]=d[a++];this.b=e;b=this.e();e=this.b}break;case v:for(;e+g>b.length;)b=this.e({p:2});break;default:throw Error("invalid inflate mode");}if(s)b.set(d.subarray(a,a+g),e),e+=g,a+=g;else for(;g--;)b[e++]=d[a++];this.a=a;this.b=e;this.c=b;break;case 1:this.j(z,A);break;case 2:B(this);break;default:throw Error("unknown BTYPE: "+c);}}return this.n()}; 5 | var C=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],D=s?new Uint16Array(C):C,E=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,258,258],F=s?new Uint16Array(E):E,G=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0],H=s?new Uint8Array(G):G,I=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],J=s?new Uint16Array(I):I,L=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13, 6 | 13],M=s?new Uint8Array(L):L,N=new (s?Uint8Array:Array)(288),O,P;O=0;for(P=N.length;O=O?8:255>=O?9:279>=O?7:8;var z=t(N),Q=new (s?Uint8Array:Array)(30),R,S;R=0;for(S=Q.length;R=g)throw Error("input buffer is broken");a|=e[f++]<>>d;c.d=b-d;c.a=f;return h} 7 | function T(c,d){for(var a=c.f,b=c.d,e=c.input,f=c.a,g=e.length,h=d[0],k=d[1],l,p;b=g);)a|=e[f++]<>>16;c.f=a>>p;c.d=b-p;c.a=f;return l&65535} 8 | function B(c){function d(a,c,b){var d,e=this.q,f,g;for(g=0;gf)b>=e&&(this.b=b,a=this.e(),b=this.b),a[b++]=f;else{g=f-257;k=F[g];0=e&&(this.b=b,a=this.e(),b=this.b);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; 10 | u.prototype.z=function(c,d){var a=this.c,b=this.b;this.o=c;for(var e=a.length,f,g,h,k;256!==(f=T(this,c));)if(256>f)b>=e&&(a=this.e(),e=a.length),a[b++]=f;else{g=f-257;k=F[g];0e&&(a=this.e(),e=a.length);for(;k--;)a[b]=a[b++-h]}for(;8<=this.d;)this.d-=8,this.a--;this.b=b}; 11 | u.prototype.e=function(){var c=new (s?Uint8Array:Array)(this.b-32768),d=this.b-32768,a,b,e=this.c;if(s)c.set(e.subarray(32768,c.length));else{a=0;for(b=c.length;aa;++a)e[a]=e[d+a];this.b=32768;return e}; 12 | u.prototype.A=function(c){var d,a=this.input.length/this.a+1|0,b,e,f,g=this.input,h=this.c;c&&("number"===typeof c.p&&(a=c.p),"number"===typeof c.v&&(a+=c.v));2>a?(b=(g.length-this.a)/this.o[2],f=258*(b/2)|0,e=fd&&(this.c.length=d),c=this.c);return this.buffer=c};function U(c,d){var a,b;this.input=c;this.a=0;if(d||!(d={}))d.index&&(this.a=d.index),d.verify&&(this.B=d.verify);a=c[this.a++];b=c[this.a++];switch(a&15){case V:this.method=V;break;default:throw Error("unsupported compression method");}if(0!==((a<<8)+b)%31)throw Error("invalid fcheck flag:"+((a<<8)+b)%31);if(b&32)throw Error("fdict flag is not supported");this.r=new u(c,{index:this.a,bufferSize:d.bufferSize,bufferType:d.bufferType,resize:d.resize})} 15 | U.prototype.k=function(){var c=this.input,d,a;d=this.r.k();this.a=this.r.a;if(this.B){a=(c[this.a++]<<24|c[this.a++]<<16|c[this.a++]<<8|c[this.a++])>>>0;var b=d;if("string"===typeof b){var e=b.split(""),f,g;f=0;for(g=e.length;f>>0;b=e}for(var h=1,k=0,l=b.length,p,n=0;0>>0)throw Error("invalid adler-32 checksum");}return d};var V=8;q("Zlib.Inflate",U);q("Zlib.Inflate.prototype.decompress",U.prototype.k);var W={ADAPTIVE:x.t,BLOCK:x.u},X,Y,Z,$;if(Object.keys)X=Object.keys(W);else for(Y in X=[],Z=0,W)X[Z++]=Y;Z=0;for($=X.length;Z<$;++Z)Y=X[Z],q("Zlib.Inflate.BufferType."+Y,W[Y]);}).call(this); 16 | -------------------------------------------------------------------------------- /examples/obj2prwm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OBJ2PRWM 5 | 6 | 76 | 77 | 78 |
79 | Drag and drop an OBJ file to convert it. 80 |
81 |
82 |
83 |

84 | Source file: Test 85 |

86 |
87 | 91 |
92 |
93 | 97 |
98 |
99 | 103 |
104 |
105 | 109 |
110 |
111 | 115 |
116 | 117 | 118 |
119 |
120 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /examples/prwm-inspector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | PRWM Inspector 5 | 6 | 88 | 89 | 90 |
91 | Drag and drop a PRWM file to inspect it. 92 |
93 |
94 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /examples/svg2prwm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SVG2PRWM 5 | 6 | 82 | 83 | 84 |
85 | Drag and drop an SVG file to convert it. 86 |
87 |
88 |
89 |

90 | Source file: Test 91 |

92 |
93 | 97 |
98 |
99 | 103 |
104 |
105 | 109 |
110 |
111 | 115 |
116 |
117 | 124 |
125 | 126 | 127 |
128 |
129 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /examples/three-buffergeometry-to-prwm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 48 | 49 | 50 | 51 | 52 | 55 |
56 | Generating a PRWM file from an existing WebGL geometry is relatively simple, 57 | making it a perfect fit for procedurally generated geometries.

58 | In this example the geometry is generated by extruding a shape using the tools built in Three.js.

59 | 60 | Specifications and implementations 61 |
62 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /examples/three-prwm-loader-benchmark.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
56 |
57 | The times abiove only account for the actual parsing time. It does not include the download.

58 | DumbJSONLoader is a custom simplified JSON loader for THREE.js.

59 | 60 | Specifications and implementations 61 |
62 | 63 | 64 | 89 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /examples/three-prwm-loader.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 53 | 54 | 55 | 56 | 57 |
58 | Little-Endian 59 | Cube 60 | Smooth Nefertiti 61 | Faceted Nefertiti 62 | Smooth Suzanne 63 | Vive Controller 64 | Cerberus 65 |
66 | 67 | Stanford Bunny 68 | Stanford Dragon 69 | Github Logo 70 | Node.js Logo 71 | Stylus Logo 72 |
73 | Big-Endian 74 | Cube 75 | Smooth Nefertiti 76 | Faceted Nefertiti 77 | Smooth Suzanne 78 | Vive Controller 79 | Cerberus 80 |
81 | 82 | Stanford Bunny 83 | Stanford Dragon 84 | Github Logo 85 | Node.js Logo 86 | Stylus Logo 87 |
88 |
89 | The parsing of PRWM file is especially fast when the endianness of the file is the same as the endianness 90 | of the client platform. This platform endianness is .

91 | See your console for stats.

92 | Benchmark comparing different loaders

93 | Specifications and implementations 94 |
95 | 254 | 255 | 256 | -------------------------------------------------------------------------------- /generate-prwm-models.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/unit-cube.obj -o ./models/prwm/cube.le.prwm --positions --normals --indexed -q 4 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/unit-cube.obj -o ./models/prwm/cube.be.prwm --positions --normals --indexed -q --be 5 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/smooth-suzanne.obj -o ./models/prwm/smooth-suzanne.le.prwm --positions --normals --indexed -q 6 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/smooth-suzanne.obj -o ./models/prwm/smooth-suzanne.be.prwm --positions --normals --indexed -q --be 7 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/faceted-nefertiti.obj -o ./models/prwm/faceted-nefertiti.le.prwm --positions --normals -q 8 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/faceted-nefertiti.obj -o ./models/prwm/faceted-nefertiti.be.prwm --positions --normals -q --be 9 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/smooth-nefertiti.obj -o ./models/prwm/smooth-nefertiti.le.prwm --positions --normals --indexed -q 10 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/smooth-nefertiti.obj -o ./models/prwm/smooth-nefertiti.be.prwm --positions --normals --indexed -q --be 11 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/vr_controller_vive_1_5.tri.obj -o ./models/prwm/vive-controller.le.prwm --positions --normals --indexed -q 12 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/vr_controller_vive_1_5.tri.obj -o ./models/prwm/vive-controller.be.prwm --positions --normals --indexed -q --be 13 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/cerberus.obj -o ./models/prwm/cerberus.le.prwm --positions --normals --indexed -q 14 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/cerberus.obj -o ./models/prwm/cerberus.be.prwm --positions --normals --indexed -q --be 15 | 16 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/stanford-bunny.obj -o ./models/prwm/stanford-bunny.le.prwm --positions --normals --indexed -q 17 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/stanford-bunny.obj -o ./models/prwm/stanford-bunny.be.prwm --positions --normals --indexed --be -q 18 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/stanford-dragon.obj -o ./models/prwm/stanford-dragon.le.prwm --positions --normals -q 19 | ./implementations/obj2prwm/cli.js -i ./models/ascii-obj/stanford-dragon.obj -o ./models/prwm/stanford-dragon.be.prwm --positions --normals --be -q 20 | 21 | ./implementations/svg2prwm/cli.js -i ./models/svg/github.svg -o ./models/prwm/github-logo.le.prwm --normals --simplify 0.4 --scale 0.9 -q 22 | ./implementations/svg2prwm/cli.js -i ./models/svg/github.svg -o ./models/prwm/github-logo.be.prwm --normals --simplify 0.4 --scale 0.9 -q --be 23 | ./implementations/svg2prwm/cli.js -i ./models/svg/bowtie.svg -o ./models/prwm/bowtie-logo.le.prwm --normals --simplify 0.4 --scale 0.9 -q 24 | ./implementations/svg2prwm/cli.js -i ./models/svg/bowtie.svg -o ./models/prwm/bowtie-logo.be.prwm --normals --simplify 0.4 --scale 0.9 -q --be 25 | ./implementations/svg2prwm/cli.js -i ./models/svg/nodejs.svg -o ./models/prwm/nodejs-logo.le.prwm --normals --simplify 0.4 --scale 0.9 -q 26 | ./implementations/svg2prwm/cli.js -i ./models/svg/nodejs.svg -o ./models/prwm/nodejs-logo.be.prwm --normals --simplify 0.4 --scale 0.9 -q --be 27 | ./implementations/svg2prwm/cli.js -i ./models/svg/stylus.svg -o ./models/prwm/stylus-logo.le.prwm --normals --simplify 0.4 --scale 0.9 -q 28 | ./implementations/svg2prwm/cli.js -i ./models/svg/stylus.svg -o ./models/prwm/stylus-logo.be.prwm --normals --simplify 0.4 --scale 0.9 -q --be 29 | -------------------------------------------------------------------------------- /implementations/obj2prwm/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Kevin Chapelier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /implementations/obj2prwm/README.md: -------------------------------------------------------------------------------- 1 | # obj2prwm 2 | 3 | CLI tool to convert WaveFront OBJ files into PRWM files. 4 | 5 | Packed Raw WebGL Model is a binary file format for nD geometries specifically designed for JavaScript and WebGL with a strong focus on fast parsing (from 1ms to 0.1ms in Chrome 59 on a MBP Late 2013). More information on this [here](https://github.com/kchapelier/PRWM). 6 | 7 | Currently this tool only support the conversion of OBJ containing a single model made of triangles. 8 | 9 | ## Installing 10 | 11 | With [npm](http://npmjs.org) do: 12 | 13 | ``` 14 | npm install obj2prwm -g 15 | ``` 16 | 17 | ## CLI Usage 18 | 19 | ### obj2prwm -i inputFile -o outputFile [OPTIONS] 20 | 21 | **Options** 22 | 23 | * **--positions :** Include the vertices positions in the destination file. 24 | * **--normals :** Include the normals in the destination file. Missing normals will be generated. 25 | * **--uvs :** Include the UVs in the destination file. 26 | * **--indexed :** Indicate that the geometry stored in the destination file must be indexed. (recommended) 27 | * **--be :** Indicate that the destination file must be in Big Endian byte order. By default the destination file is in Little Endian. 28 | * **-q, --quiet :** Quiet mode. Silence the output to the console. 29 | 30 | ## API 31 | 32 | ### obj2prwm.convert(objString, options) 33 | 34 | **Arguments** 35 | 36 | * **objString :** The obj file as a string. 37 | * **options :** Options 38 | * **positions :** Include the vertices positions in the destination file. 39 | * **normals :** Include the normals in the destination file. Missing normals will be generated. 40 | * **uvs :** Include the UVs in the destination file. 41 | * **indexed :** Indicate that the geometry stored in the destination file must be indexed. (recommended) 42 | * **bigEndian :** Indicate that the destination file must be in Big Endian byte order. By default the destination file is in Little Endian. 43 | * **quiet :** Quiet mode. Silence the output to the console. 44 | 45 | ## Example 46 | 47 | ``` 48 | $ obj2prwm -i original.obj -o destination.prwm --positions --normals --uvs --indexed 49 | 50 | * Reading original.obj 51 | * Parsing WaveFront OBJ data 52 | * Formatting data 53 | * Writing destination.prwm 54 | 55 | Operation completed in 0.16s. 56 | Original OBJ file size : 1502.48kB 57 | Generated indexed PRWM file size : 386.42kB 58 | Individual vertices : 12147 59 | ``` 60 | 61 | ## Changelog 62 | 63 | ### 1.1.1 (2017.12.26) : 64 | 65 | * Update `wavefront-obj-parser` and other dependencies. 66 | 67 | ### 1.1.0 (2017.08.19) : 68 | 69 | * Allow to use the module programmatically. 70 | * Generate missing normals. 71 | 72 | ### 1.0.0 (2017.06.10) : 73 | 74 | * First release. 75 | 76 | ## Roadmap 77 | 78 | * Support OBJ with quads 79 | * Support the generation of tangents 80 | 81 | ## License 82 | 83 | MIT 84 | -------------------------------------------------------------------------------- /implementations/obj2prwm/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var fs = require('fs'), 6 | yargs = require('yargs'), 7 | obj2prwm = require('./index'); 8 | 9 | var argv = yargs.usage('Usage: obj2prwm -i inputFile -o outputFile [options]') 10 | .describe('i', 'Input file') 11 | .alias('i', 'in') 12 | .describe('o', 'Output file') 13 | .alias('o', 'out') 14 | .describe('positions', 'Include the vertices positions in output file') 15 | .describe('uvs', 'Include the UV in output file') 16 | .describe('normals', 'Include the normals in output file. Missing normals will be generated.') 17 | .describe('indexed', 'Whether the geometry should be indexed') 18 | .describe('be', 'Write the output file in big endian') 19 | .describe('q', 'Quiet mode. Silence the output.') 20 | .alias('q', 'quiet') 21 | .boolean(['uvs','positions','normals', 'indexed', 'quiet', 'be']) 22 | .demandOption(['o', 'i']) 23 | .help('h') 24 | .alias('h', 'help') 25 | .argv; 26 | 27 | var now = Date.now(), 28 | inFd = fs.openSync(argv.in, 'r'), 29 | outFd = fs.openSync(argv.out, 'w'), 30 | options = { 31 | positions: !!argv.positions, 32 | uvs: !!argv.uvs, 33 | normals: !!argv.normals, 34 | indexed: !!argv.indexed, 35 | bigEndian: !!argv.be, 36 | quiet: !!argv.quiet 37 | }, 38 | log = options.quiet ? function noop() {} : function log(s) { console.log(s) }; 39 | 40 | 41 | log(' * Reading ' + argv.in); 42 | var objString = fs.readFileSync(inFd, 'utf8'); 43 | 44 | var arrayBuffer = obj2prwm.convert(objString, options); 45 | 46 | log(' * Writing ' + argv.out); 47 | fs.writeFileSync(outFd, new Buffer(arrayBuffer), { flag: 'w' }); 48 | log(''); 49 | log('Operation completed in ' + ((Date.now() - now) / 1000).toFixed(2) + 's.'); 50 | log('Original OBJ file size : ' + (Buffer.byteLength(objString, 'utf8') / 1024).toFixed(2) + 'kB'); 51 | log('Generated ' + (options.indexed ? 'indexed' : 'non-indexed') + ' PRWM file size : ' + (arrayBuffer.byteLength / 1024).toFixed(2) + 'kB'); 52 | log('Individual vertices : ' + obj2prwm.getNumberOfVertices()); 53 | log(''); 54 | 55 | 56 | -------------------------------------------------------------------------------- /implementations/obj2prwm/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var objParser = require('wavefront-obj-parser'), 4 | computeNormals = require('./utils/compute-normals'), 5 | prwm = require('prwm'); 6 | 7 | function serializeIndexed (objData, usePositions, useNormals, useUvs) { 8 | var nbPolygons = objData.vertexPositionIndices.length / 4, // the parser always return indices by group of 4 to support quads 9 | indicesMapping = {}, 10 | indices = [], 11 | vertices = [], 12 | normals = [], 13 | uvs = [], 14 | i, 15 | k, 16 | vertexIndex, 17 | normalIndex, 18 | uvIndex, 19 | mapped, 20 | index, 21 | nextIndex = 0; 22 | 23 | var mustGenerateNewNormals = useNormals && (!objData.vertexNormals|| objData.vertexNormals.length === 0); 24 | 25 | for (i = 0; i < nbPolygons; i++) { 26 | for (k = 0; k < 3; k++) { // assume we don't have actual quads in the models 27 | vertexIndex = objData.vertexPositionIndices[i * 4 + k]; 28 | normalIndex = objData.vertexNormalIndices[i * 4 + k]; 29 | uvIndex = objData.vertexUVIndices[i * 4 + k]; 30 | 31 | mapped = (usePositions ? vertexIndex + ':' : ':') + (useNormals ? normalIndex + ':' : ':') + (useUvs ? uvIndex + ':' : ':'); 32 | 33 | index = indicesMapping[mapped]; 34 | 35 | if (typeof index === 'undefined') { 36 | index = nextIndex; 37 | indicesMapping[mapped] = index; 38 | nextIndex++; 39 | 40 | if (usePositions) { 41 | vertices.push( 42 | objData.vertexPositions[vertexIndex * 3], 43 | objData.vertexPositions[vertexIndex * 3 + 1], 44 | objData.vertexPositions[vertexIndex * 3 + 2] 45 | ); 46 | } 47 | 48 | if (useNormals && !mustGenerateNewNormals) { 49 | normals.push( 50 | objData.vertexNormals[normalIndex * 3], 51 | objData.vertexNormals[normalIndex * 3 + 1], 52 | objData.vertexNormals[normalIndex * 3 + 2] 53 | ); 54 | } 55 | 56 | if (useUvs) { 57 | uvs.push( 58 | objData.vertexUVs[uvIndex * 2], 59 | objData.vertexUVs[uvIndex * 2 + 1] 60 | ); 61 | } 62 | } 63 | 64 | indices.push(index); 65 | } 66 | } 67 | 68 | if (mustGenerateNewNormals) { 69 | computeNormals(indices, vertices, normals); 70 | } 71 | 72 | return { 73 | indices: indices, 74 | vertices: vertices, 75 | normals: normals, 76 | uvs: uvs 77 | }; 78 | } 79 | 80 | function serializeNonIndexed (objData, usePositions, useNormals, useUvs) { 81 | var nbPolygons = objData.vertexPositionIndices.length / 4, // the parser always return indices by group of 4 to support quads 82 | vertices = [], 83 | normals = [], 84 | uvs = [], 85 | i, 86 | k, 87 | vertexIndex, 88 | normalIndex, 89 | uvIndex; 90 | 91 | var mustGenerateNewNormals = useNormals && (!objData.vertexNormals|| objData.vertexNormals.length === 0); 92 | 93 | for (i = 0; i < nbPolygons; i++) { 94 | for (k = 0; k < 3; k++) { // assume we don't have actual quads in the models 95 | if (usePositions) { 96 | vertexIndex = objData.vertexPositionIndices[i * 4 + k]; 97 | 98 | vertices.push( 99 | objData.vertexPositions[vertexIndex * 3], 100 | objData.vertexPositions[vertexIndex * 3 + 1], 101 | objData.vertexPositions[vertexIndex * 3 + 2] 102 | ); 103 | } 104 | 105 | if (useNormals && !mustGenerateNewNormals) { 106 | normalIndex = objData.vertexNormalIndices[i * 4 + k]; 107 | 108 | normals.push( 109 | objData.vertexNormals[normalIndex * 3], 110 | objData.vertexNormals[normalIndex * 3 + 1], 111 | objData.vertexNormals[normalIndex * 3 + 2] 112 | ); 113 | } 114 | 115 | if (useUvs) { 116 | uvIndex = objData.vertexUVIndices[i * 4 + k]; 117 | 118 | uvs.push( 119 | objData.vertexUVs[uvIndex * 2], 120 | objData.vertexUVs[uvIndex * 2 + 1] 121 | ); 122 | } 123 | } 124 | } 125 | 126 | if (mustGenerateNewNormals) { 127 | computeNormals(null, vertices, normals); 128 | } 129 | 130 | return { 131 | indices: null, 132 | vertices: vertices, 133 | normals: normals, 134 | uvs: uvs 135 | }; 136 | } 137 | 138 | var nbVertices = null; 139 | 140 | module.exports = { 141 | convert: function (objString, options) { 142 | var log = options.quiet ? function noop() {} : function log(s) { console.log(s) }; 143 | 144 | log(' * Parsing WaveFront OBJ data'); 145 | var objData = objParser(objString); 146 | 147 | log(' * Formatting data'); 148 | var serialized = options.indexed ? serializeIndexed(objData, options.positions, options.normals, options.uvs) : serializeNonIndexed(objData, options.positions, options.normals, options.uvs); 149 | 150 | var attributes = {}; 151 | 152 | nbVertices = 0; 153 | 154 | if (options.positions) { 155 | attributes['position'] = { cardinality: 3, normalized: false, values: new Float32Array(serialized.vertices) }; 156 | nbVertices = serialized.vertices.length / 3; 157 | } 158 | 159 | if (options.normals) { 160 | attributes['normal'] = { cardinality: 3, normalized: false, values: new Float32Array(serialized.normals) }; 161 | nbVertices = serialized.normals.length / 3; 162 | } 163 | 164 | if (options.uvs) { 165 | attributes['uv'] = { cardinality: 2, normalized: false, values: new Float32Array(serialized.uvs) }; 166 | nbVertices = serialized.uvs.length / 2; 167 | } 168 | 169 | return prwm.encode( 170 | attributes, 171 | serialized.indices ? (nbVertices > 0xFFFF ? new Uint32Array(serialized.indices) : new Uint16Array(serialized.indices)) : null, 172 | options.bigEndian 173 | ); 174 | }, 175 | getNumberOfVertices: function () { 176 | return nbVertices; 177 | } 178 | }; 179 | 180 | -------------------------------------------------------------------------------- /implementations/obj2prwm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obj2prwm", 3 | "version": "1.1.1", 4 | "description": "CLI tool to convert WaveFront OBJ files into PRWM files.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build-dev": "browserify ./index.js -d --s obj2prwm > ./build/obj2prwm.dev.js", 8 | "build-min": "browserify ./index.js --s obj2prwm | uglifyjs > ./build/obj2prwm.min.js" 9 | }, 10 | "bin": { 11 | "obj2prwm": "cli.js" 12 | }, 13 | "files": [ 14 | "index.js", 15 | "cli.js", 16 | "utils/compute-normals.js" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/kchapelier/PRWM.git" 21 | }, 22 | "keywords": [ 23 | "wavefront", 24 | "obj", 25 | "prwm" 26 | ], 27 | "author": "Kevin Chapelier", 28 | "license": "MIT", 29 | "readmeFilename": "README.md", 30 | "bugs": { 31 | "url": "https://github.com/kchapelier/PRWM/issues" 32 | }, 33 | "homepage": "https://github.com/kchapelier/PRWM", 34 | "dependencies": { 35 | "prwm": "^1.2.0", 36 | "wavefront-obj-parser": "~2.0.1", 37 | "yargs": "~10.0.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /implementations/obj2prwm/utils/compute-normals.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | // based on https://github.com/kchapelier/procjam2015/blob/master/src/utils/meshes/compute-vertex-normals.js 4 | // which itself is a rewrite of Three.js function to compute normals 5 | 6 | var normalizeNormals = function normalizeNormals (normals) { 7 | var i, x, y, z, n; 8 | 9 | for (i = 0; i < normals.length; i+= 3) { 10 | x = normals[i]; 11 | y = normals[i + 1]; 12 | z = normals[i + 2]; 13 | 14 | n = 1.0 / Math.sqrt(x * x + y * y + z * z); 15 | 16 | normals[i]*= n; 17 | normals[i + 1]*= n; 18 | normals[i + 2]*= n; 19 | } 20 | }; 21 | 22 | var computeVertexNormals = function computeVertexNormals (indices, positions, normals) { 23 | var pA = [0,0,0], 24 | pB = [0,0,0], 25 | pC = [0,0,0], 26 | cb = [0,0,0], 27 | ab = [0,0,0], 28 | vA, 29 | vB, 30 | vC, 31 | cbx, 32 | cby, 33 | cbz, 34 | i; 35 | 36 | normals.length = positions.length; 37 | 38 | for (i = 0; i < normals.length; i++) { 39 | normals[i] = 0; 40 | } 41 | 42 | if (indices) { 43 | for (i = 0; i < indices.length; i += 3) { 44 | 45 | vA = indices[i] * 3; 46 | vB = indices[i + 1] * 3; 47 | vC = indices[i + 2] * 3; 48 | 49 | /* 50 | pA.fromArray( positions, vA ); 51 | pB.fromArray( positions, vB ); 52 | pC.fromArray( positions, vC ); 53 | */ 54 | 55 | pA[0] = positions[vA]; 56 | pA[1] = positions[vA + 1]; 57 | pA[2] = positions[vA + 2]; 58 | 59 | pB[0] = positions[vB]; 60 | pB[1] = positions[vB + 1]; 61 | pB[2] = positions[vB + 2]; 62 | 63 | pC[0] = positions[vC]; 64 | pC[1] = positions[vC + 1]; 65 | pC[2] = positions[vC + 2]; 66 | 67 | /* 68 | cb.subVectors( pC, pB ); 69 | ab.subVectors( pA, pB ); 70 | */ 71 | 72 | cb[0] = pC[0] - pB[0]; 73 | cb[1] = pC[1] - pB[1]; 74 | cb[2] = pC[2] - pB[2]; 75 | 76 | ab[0] = pA[0] - pB[0]; 77 | ab[1] = pA[1] - pB[1]; 78 | ab[2] = pA[2] - pB[2]; 79 | 80 | /* 81 | cb.cross( ab ); 82 | */ 83 | 84 | cbx = cb[0]; 85 | cby = cb[1]; 86 | cbz = cb[2]; 87 | 88 | cb[0] = cby * ab[2] - cbz * ab[1]; 89 | cb[1] = cbz * ab[0] - cbx * ab[2]; 90 | cb[2] = cbx * ab[1] - cby * ab[0]; 91 | 92 | normals[vA] += cb[0]; 93 | normals[vA + 1] += cb[1]; 94 | normals[vA + 2] += cb[2]; 95 | 96 | normals[vB] += cb[0]; 97 | normals[vB + 1] += cb[1]; 98 | normals[vB + 2] += cb[2]; 99 | 100 | normals[vC] += cb[0]; 101 | normals[vC + 1] += cb[1]; 102 | normals[vC + 2] += cb[2]; 103 | 104 | } 105 | } else { 106 | for (i = 0; i < positions.length; i += 9) { 107 | 108 | /* 109 | pA.fromArray( positions, i ); 110 | pB.fromArray( positions, i + 3 ); 111 | pC.fromArray( positions, i + 6 ); 112 | */ 113 | 114 | pA[0] = positions[i]; 115 | pA[1] = positions[i + 1]; 116 | pA[2] = positions[i + 2]; 117 | 118 | pB[0] = positions[i + 3]; 119 | pB[1] = positions[i + 4]; 120 | pB[2] = positions[i + 5]; 121 | 122 | pC[0] = positions[i + 6]; 123 | pC[1] = positions[i + 7]; 124 | pC[2] = positions[i + 8]; 125 | 126 | /* 127 | cb.subVectors( pC, pB ); 128 | ab.subVectors( pA, pB ); 129 | */ 130 | 131 | cb[0] = pC[0] - pB[0]; 132 | cb[1] = pC[1] - pB[1]; 133 | cb[2] = pC[2] - pB[2]; 134 | 135 | ab[0] = pA[0] - pB[0]; 136 | ab[1] = pA[1] - pB[1]; 137 | ab[2] = pA[2] - pB[2]; 138 | 139 | /* 140 | cb.cross( ab ); 141 | */ 142 | 143 | cbx = cb[0]; 144 | cby = cb[1]; 145 | cbz = cb[2]; 146 | 147 | cb[0] = cby * ab[2] - cbz * ab[1]; 148 | cb[1] = cbz * ab[0] - cbx * ab[2]; 149 | cb[2] = cbx * ab[1] - cby * ab[0]; 150 | 151 | normals[ i ] = cb[0]; 152 | normals[ i + 1 ] = cb[1]; 153 | normals[ i + 2 ] = cb[2]; 154 | 155 | normals[ i + 3 ] = cb[0]; 156 | normals[ i + 4 ] = cb[1]; 157 | normals[ i + 5 ] = cb[2]; 158 | 159 | normals[ i + 6 ] = cb[0]; 160 | normals[ i + 7 ] = cb[1]; 161 | normals[ i + 8 ] = cb[2]; 162 | } 163 | } 164 | 165 | normalizeNormals(normals); 166 | }; 167 | 168 | module.exports = computeVertexNormals; 169 | -------------------------------------------------------------------------------- /implementations/picogl-prwm-loader/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | .idea 40 | -------------------------------------------------------------------------------- /implementations/picogl-prwm-loader/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Kevin Chapelier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /implementations/picogl-prwm-loader/README.md: -------------------------------------------------------------------------------- 1 | # picogl-prwm-loader 2 | 3 | A PRWM loader for PicoGL.js 4 | 5 | Packed Raw WebGL Model is a binary file format for nD geometries specifically designed for JavaScript and WebGL with a strong focus on fast parsing (from 1ms to 0.1ms in Chrome 59 on a MBP Late 2013). More information on this [here](https://github.com/kchapelier/PRWM). 6 | 7 | ## Installing 8 | 9 | With [npm](http://npmjs.org) do: 10 | 11 | ``` 12 | npm install picogl-prwm-loader --save 13 | ``` 14 | 15 | ## Example 16 | 17 | ```js 18 | var PRWMLoader = require('picogl-prwm-loader'), 19 | PicoGL = require('picogl'); 20 | 21 | var app = PicoGL.createApp(document.getElementById('canvas')); 22 | 23 | var loader = new PRWMLoader(PicoGL, app).setVerbosity(true); 24 | 25 | loader.load( 26 | './models/mymodel.prwm', 27 | { 28 | position: 0, 29 | normal: 1, 30 | uv: 2 31 | }, 32 | function onComplete (vertexArray) { 33 | console.log(vertexArray) 34 | } 35 | ); 36 | ``` 37 | 38 | [Online example](http://www.kchapelier.com/prwm/examples/picogl-prwm-loader.html) 39 | 40 | ## API 41 | 42 | ### new PRWMLoader(picoGL, app) 43 | 44 | Instantiate a loader for PRWM files. 45 | 46 | **Arguments** 47 | 48 | * picoGL : The PicoGL namespace. 49 | * app : The PicoGL App instance. 50 | 51 | ### loader.setVerbosity(verbose) 52 | 53 | Set the verbosity of the loader. 54 | 55 | **Arguments** 56 | 57 | * verbose : Whether the loader should be verbose (true) or silent (false). 58 | 59 | ### loader.parse(arrayBuffer, attributeMapping, offset) 60 | 61 | Parse a PRWM file passed as an ArrayBuffer and directly return an instance of PicoGL's VertexArray. 62 | 63 | **Arguments** 64 | 65 | * arrayBuffer: ArrayBuffer containing the PRWM data. 66 | * attributeMapping: Literal object with attribute name => attribute index mapping. 67 | * offset: Offset (in bytes) at which the PRWM file content is located in the ArrayBuffer. Must be a multiple of 4. 68 | 69 | ### loader.load(url, attributeMapping, onSuccess) 70 | 71 | Parse a remote PRWM file and return an instance of PicoGL's VertexArray (through a callback) 72 | 73 | **Arguments** 74 | 75 | * url: Url of the PRWM file. 76 | * attributeMapping: Literal object with attribute name => attribute index mapping. 77 | * onSuccess: Callback called with the VertexArray on success. 78 | 79 | ### PRWMLoader.isBigEndianPlatform() 80 | 81 | Return true if the endianness of the platform is Big Endian. 82 | 83 | ## Changelog 84 | 85 | ### 1.1.1 (2017.12.26) : 86 | 87 | * Add `offset` argument in `parse()`. 88 | 89 | ### 1.1.0 (2017.08.19) : 90 | 91 | * Use [new methods](https://github.com/tsherif/picogl.js/pull/55) from PicoGL 0.6.6 to fully support all attributes types. 92 | 93 | ### 1.0.0 (2017.08.15) : 94 | 95 | * First release. 96 | 97 | ## License 98 | 99 | MIT 100 | -------------------------------------------------------------------------------- /implementations/picogl-prwm-loader/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var prwm = require('prwm'); 4 | 5 | /** 6 | * Instantiate a loader for PRWM files 7 | * @param {object} picoGL PicoGL namespace 8 | * @param {object} app PicoGL App instance 9 | * @constructor 10 | */ 11 | var PRWMLoader = function PRWMLoader (picoGL, app) { 12 | this.picoGL = picoGL; 13 | this.app = app; 14 | this.verbose = false; 15 | }; 16 | 17 | PRWMLoader.prototype.picoGL = null; 18 | PRWMLoader.prototype.app = null; 19 | PRWMLoader.prototype.verbose = null; 20 | 21 | /** 22 | * Set the verbosity of the loader. 23 | * @param {bool} [verbose=false] Whether the loader should be verbose (true) or silent (false). 24 | * @returns {PRWMLoader} Instance of the PRWMLoader for method chaining 25 | */ 26 | PRWMLoader.prototype.setVerbosity = function (verbose) { 27 | this.verbose = !!verbose; 28 | return this; 29 | }; 30 | 31 | /** 32 | * Return the PicoGL attribute type for a given typed array 33 | * @param {ArrayBufferView} typedArray 34 | * @return {int} Attribute type 35 | * @protected 36 | */ 37 | PRWMLoader.prototype.getAttributeTypeForTypedArray = function (typedArray) { 38 | var typedArrayName = typedArray.constructor.name, 39 | result; 40 | 41 | switch (typedArrayName) { 42 | case 'Int8Array': 43 | result = this.picoGL.BYTE; 44 | break; 45 | case 'Uint8Array': 46 | result = this.picoGL.UNSIGNED_BYTE; 47 | break; 48 | case 'Int16Array': 49 | result = this.picoGL.SHORT; 50 | break; 51 | case 'Uint16Array': 52 | result = this.picoGL.UNSIGNED_SHORT; 53 | break; 54 | case 'Int32Array': 55 | result = this.picoGL.INT; 56 | break; 57 | case 'Uint32Array': 58 | result = this.picoGL.UNSIGNED_INT; 59 | break; 60 | case 'Float32Array': 61 | result = this.picoGL.FLOAT; 62 | break; 63 | default: 64 | throw new Error('PRWMLoader: Unrecognized typedArray: "' + typedArrayName + '"'); 65 | } 66 | 67 | return result; 68 | }; 69 | 70 | /** 71 | * Parse a PRWM file passed as an ArrayBuffer and directly return an instance of PicoGL's VertexArray 72 | * @param {ArrayBuffer} arrayBuffer ArrayBuffer containing the PRWM data 73 | * @param {object} attributeMapping Literal object with attribute name => attribute index mapping 74 | * @param {int} [offset=0] Offset (in bytes) at which the PRWM file content is located in the ArrayBuffer. Must be a multiple of 4. 75 | * @returns {object} Instance of PicoGL's VertexArray 76 | */ 77 | PRWMLoader.prototype.parse = function (arrayBuffer, attributeMapping, offset) { 78 | var attributeKeys = Object.keys(attributeMapping), 79 | decodeStart = performance.now(), 80 | data = prwm.decode(arrayBuffer, offset), 81 | timeToDecode = (performance.now() - decodeStart).toFixed(3); 82 | 83 | if (this.verbose) { 84 | // console.log(data); 85 | console.log('Model decoded in ' + timeToDecode + 'ms'); 86 | console.log('Model file size: ' + (arrayBuffer.byteLength / 1024).toFixed(2) + 'kB'); 87 | console.log('Model type: ' + (data.indices ? 'indexed geometry' : 'non-indexed geometry')); 88 | console.log('# of vertices: ' + data.attributes.position.values.length / data.attributes.position.cardinality); 89 | console.log('# of polygons: ' + (data.indices ? data.indices.length / 3 : data.attributes.position.values.length / data.attributes.position.cardinality / 3)); 90 | } 91 | 92 | var vertexArray = this.app.createVertexArray(), 93 | vertexBuffer, 94 | attributeIndex, 95 | attributeName, 96 | attributeType, 97 | attributeCardinality, 98 | i; 99 | 100 | for (i = 0; i < attributeKeys.length; i++) { 101 | attributeName = attributeKeys[i]; 102 | attributeIndex = attributeMapping[attributeName]; 103 | attributeType = this.getAttributeTypeForTypedArray(data.attributes[attributeName].values); 104 | attributeCardinality = data.attributes[attributeName].cardinality; 105 | vertexBuffer = this.app.createVertexBuffer(attributeType, attributeCardinality, data.attributes[attributeName].values); 106 | 107 | // vertexArray.attributeBuffer() is not in doc, so avoid using directly (even though its tempting) 108 | 109 | if (data.attributes[attributeName].type === prwm.Int) { 110 | vertexArray.vertexIntegerAttributeBuffer(attributeIndex, vertexBuffer); 111 | } else if (data.attributes[attributeName].normalized) { 112 | vertexArray.vertexNormalizedAttributeBuffer(attributeIndex, vertexBuffer); 113 | } else { 114 | vertexArray.vertexAttributeBuffer(attributeIndex, vertexBuffer); 115 | } 116 | } 117 | 118 | if (data.indices !== null) { 119 | attributeType = data.indices.BYTES_PER_ELEMENT === 2 ? this.picoGL.UNSIGNED_SHORT : this.picoGL.UNSIGNED_INT; 120 | vertexArray.indexBuffer(this.app.createIndexBuffer(attributeType, 3, data.indices)); 121 | } 122 | 123 | return vertexArray; 124 | }; 125 | 126 | /** 127 | * Parse a remote PRWM file and return an instance of PicoGL's VertexArray (through a callback) 128 | * @param {string} url Url of the PRWM file 129 | * @param {object} attributeMapping Literal object with attribute name => attribute index mapping 130 | * @param {function} onSuccess Callback called with the VertexArray on success 131 | */ 132 | PRWMLoader.prototype.load = function (url, attributeMapping, onSuccess) { 133 | var self = this, 134 | xhr = new XMLHttpRequest(); 135 | 136 | xhr.open('GET', url, true); 137 | xhr.responseType = 'arraybuffer'; 138 | xhr.onload = function () { 139 | if (self.verbose) { 140 | console.log('--- ' + url); 141 | } 142 | 143 | onSuccess(self.parse(this.response, attributeMapping)); 144 | }; 145 | 146 | xhr.send(null); 147 | }; 148 | 149 | /** 150 | * Check if the endianness of the platform is big-endian (most significant bit first) 151 | * @returns {boolean} True if big-endian, false if little-endian 152 | */ 153 | PRWMLoader.isBigEndianPlatform = function () { 154 | return prwm.isBigEndianPlatform(); 155 | }; 156 | 157 | module.exports = PRWMLoader; 158 | -------------------------------------------------------------------------------- /implementations/picogl-prwm-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "picogl-prwm-loader", 3 | "version": "1.1.1", 4 | "description": "PRWM loader for PicoGL.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "build-dev": "browserify ./index.js -d --s PRWMLoader > ./build/PRWMLoader.dev.js", 8 | "build-min": "browserify ./index.js --s PRWMLoader | uglifyjs > ./build/PRWMLoader.min.js" 9 | }, 10 | "files": [ 11 | "index.js" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/kchapelier/PRWM.git" 16 | }, 17 | "keywords": [ 18 | "prwm", 19 | "picogl", 20 | "picogl.js" 21 | ], 22 | "author": "Kevin Chapelier", 23 | "license": "MIT", 24 | "readmeFilename": "README.md", 25 | "bugs": { 26 | "url": "https://github.com/kchapelier/PRWM/issues" 27 | }, 28 | "homepage": "https://github.com/kchapelier/PRWM", 29 | "dependencies": { 30 | "prwm": "^1.2.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /implementations/prwm/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | .idea 40 | -------------------------------------------------------------------------------- /implementations/prwm/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Kevin Chapelier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /implementations/prwm/README.md: -------------------------------------------------------------------------------- 1 | # prwm 2 | 3 | The reference encoding / decoding library for the PRWM file format. 4 | 5 | Packed Raw WebGL Model is a binary file format for nD geometries specifically designed for JavaScript and WebGL with a strong focus on fast parsing (from 1ms to 0.1ms in Chrome 59 on a MBP Late 2013). More information on this [here](https://github.com/kchapelier/PRWM). 6 | 7 | ## Installing 8 | 9 | With [npm](http://npmjs.org) do: 10 | 11 | ``` 12 | npm install prwm 13 | ``` 14 | 15 | ## Usage 16 | 17 | ### Encoding 18 | 19 | ``` 20 | var prwm = require('prwm'); 21 | 22 | // this will encode a prwm file with a simple square, non-indexed 23 | 24 | var arrayBuffer = prwm.encode({ 25 | position: { 26 | cardinality: 3, 27 | normalized: false, 28 | values: new Float32Array([ 29 | -1.0, -1.0, 0.0, 30 | 1.0, -1.0, 0.0, 31 | 1.0, 1.0, 0.0, 32 | -1.0, -1.0, 0.0, 33 | 1.0, 1.0, 0.0, 34 | -1.0, 1.0, 0.0 35 | ]) 36 | }, 37 | uv: { 38 | cardinality: 2, 39 | normalized: false, 40 | values: new Int8Array([ 41 | 0, 0 42 | 1, 0, 43 | 1, 1, 44 | 0, 0, 45 | 1, 1, 46 | 0, 1 47 | ]) 48 | } 49 | }, null, false); 50 | 51 | console.log(arrayBuffer); 52 | ``` 53 | 54 | ``` 55 | var prwm = require('prwm'); 56 | 57 | // this will encode a prwm file with a simple square, indexed 58 | 59 | var arrayBuffer = prwm.encode({ 60 | position: { 61 | cardinality: 3, 62 | normalized: false, 63 | values: new Float32Array([ 64 | -1.0, -1.0, 0.0, 65 | 1.0, -1.0, 0.0, 66 | 1.0, 1.0, 0.0, 67 | -1.0, 1.0, 0.0 68 | ]) 69 | }, 70 | uv: { 71 | cardinality: 2, 72 | normalized: false, 73 | values: new Int8Array([ 74 | 0, 0 75 | 1, 0, 76 | 1, 1, 77 | 0, 1 78 | ]) 79 | } 80 | }, new Uint16Array([0,1,2,0,2,3]), false); 81 | 82 | console.log(arrayBuffer); 83 | ``` 84 | 85 | ``` 86 | var prwm = require('prwm'); 87 | 88 | // this will encode a prwm file with a simple square, indexed, and flag its position attribute to be considered 89 | // as integers values using WebGL2's vertexAttribIPointer 90 | 91 | var arrayBuffer = prwm.encode({ 92 | position: { 93 | cardinality: 3, 94 | type: prwm.Int, 95 | normalized: false, 96 | values: new Int32Array([ 97 | -1.0, -1.0, 0.0, 98 | 1.0, -1.0, 0.0, 99 | 1.0, 1.0, 0.0, 100 | -1.0, 1.0, 0.0 101 | ]) 102 | }, 103 | uv: { 104 | cardinality: 2, 105 | type: prwm.Float, 106 | normalized: false, 107 | values: new Int8Array([ 108 | 0, 0 109 | 1, 0, 110 | 1, 1, 111 | 0, 1 112 | ]) 113 | } 114 | }, new Uint16Array([0,1,2,0,2,3]), false); 115 | 116 | console.log(arrayBuffer); 117 | ``` 118 | 119 | ### Decoding 120 | 121 | ``` 122 | var prwm = require('prwm'); 123 | 124 | // assume that arrayBuffer is an ArrayBuffer with the content of a valid prwm file 125 | 126 | var data = prwm.decode(arrayBuffer); 127 | 128 | console.log(data); // a json containing all the attributes and indices 129 | ``` 130 | 131 | ## API 132 | 133 | ### prwm.isBigEndianPlatform() 134 | 135 | Check if the platform is natively using the Big Endian byte order. 136 | 137 | Return true for Big Endian, false for Little Endian. 138 | 139 | ### prwm.encode(attributes, indices, bigEndian) 140 | 141 | * **attributes :** A list of attribute, represented as an object literal where the name of the property is the name of the attribute. Each attribute is defined by its cardinality (the number of components per vertex attribute, either 1, 2, 3 or 4), its type (Float or Integer), whether it is normalized and its values as a typed array. 142 | * **indices :** The indices of the geometry, can be either null (for non-indexed property), an Uint16Array or an Uint32Array. 143 | * **bigEndian :** Whether to generate the file in Big Endian byte order. 144 | 145 | ### prwm.decode(prwmData, offset) 146 | 147 | * **prwmData :** An ArrayBuffer with the content of a PRWM file. 148 | * **offset :** Offset (in bytes) at which the PRWM file content is located in the ArrayBuffer. Must be a multiple of 4. Defaults to 0. 149 | 150 | ## Changelog 151 | 152 | ### 1.2.0 (2017.12.03) : 153 | 154 | * Add `offset` parameter to decode function. 155 | 156 | ### 1.1.0 (2017.08.15) : 157 | 158 | * Add endianness of the file in the decoded data. 159 | * Reduce the weight of the npm package. 160 | 161 | ### 1.0.0 (2017.06.09) : 162 | 163 | * First implementation, with full support for the first version of the format. 164 | 165 | ## Roadmap 166 | 167 | * More tests 168 | 169 | ## License 170 | 171 | MIT 172 | -------------------------------------------------------------------------------- /implementations/prwm/build/prwm.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.prwm=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o>7),indicesType=flags>>6&1,bigEndian=(flags>>5&1)===1,attributesNumber=flags&31,valuesNumber=0,indicesNumber=0;if(bigEndian){valuesNumber=(array[2]<<16)+(array[3]<<8)+array[4];indicesNumber=(array[5]<<16)+(array[6]<<8)+array[7]}else{valuesNumber=array[2]+(array[3]<<8)+(array[4]<<16);indicesNumber=array[5]+(array[6]<<8)+(array[7]<<16)}if(version===0){throw new Error("PRWM decoder: Invalid format version: 0")}else if(version!==1){throw new Error("PRWM decoder: Unsupported format version: "+version)}if(!indexedGeometry){if(indicesType!==0){throw new Error("PRWM decoder: Indices type must be set to 0 for non-indexed geometries")}else if(indicesNumber!==0){throw new Error("PRWM decoder: Number of indices must be set to 0 for non-indexed geometries")}}var pos=8;var attributes={},attributeName,char,attributeNormalized,attributeType,cardinality,encodingType,arrayType,values,i;for(i=0;i>7&1;attributeNormalized=!!(flags>>6&1);cardinality=(flags>>4&3)+1;encodingType=flags&15;arrayType=InvertedEncodingTypes[encodingType];pos++;pos=Math.ceil(pos/4)*4;values=copyFromBuffer(buffer,arrayType,pos,cardinality*valuesNumber,bigEndian);pos+=arrayType.BYTES_PER_ELEMENT*cardinality*valuesNumber;attributes[attributeName]={type:attributeType,normalized:attributeNormalized,cardinality:cardinality,values:values}}pos=Math.ceil(pos/4)*4;var indices=null;if(indexedGeometry){indices=copyFromBuffer(buffer,indicesType===1?Uint32Array:Uint16Array,pos,indicesNumber,bigEndian)}return{version:version,bigEndian:bigEndian,attributes:attributes,indices:indices}}module.exports=decode},{"../utils/is-big-endian-platform":5}],4:[function(require,module,exports){"use strict";var isBigEndianPlatform=require("../utils/is-big-endian-platform"),attributeTypes=require("./attribute-types");var EncodingTypes={Float32Array:1,Int8Array:3,Int16Array:4,Int32Array:6,Uint8Array:7,Uint16Array:8,Uint32Array:10};var setMethods={Uint16Array:"setUint16",Uint32Array:"setUint32",Int16Array:"setInt16",Int32Array:"setInt32",Float32Array:"setFloat32"};function copyToBuffer(sourceTypedArray,destinationArrayBuffer,position,bigEndian){var length=sourceTypedArray.length,bytesPerElement=sourceTypedArray.BYTES_PER_ELEMENT;var writeArray=new sourceTypedArray.constructor(destinationArrayBuffer,position,length);if(bigEndian===isBigEndianPlatform()||bytesPerElement===1){writeArray.set(sourceTypedArray.subarray(0,length))}else{var writeView=new DataView(destinationArrayBuffer,position,length*bytesPerElement),setMethod=setMethods[sourceTypedArray.constructor.name],littleEndian=!bigEndian,i=0;for(i=0;i31){throw new Error("PRWM encoder: The model can have at most 31 attributes")}for(i=0;i>16&255;array[3]=valuesNumber>>8&255;array[4]=valuesNumber&255;array[5]=indicesNumber>>16&255;array[6]=indicesNumber>>8&255;array[7]=indicesNumber&255}else{array[2]=valuesNumber&255;array[3]=valuesNumber>>8&255;array[4]=valuesNumber>>16&255;array[5]=indicesNumber&255;array[6]=indicesNumber>>8&255;array[7]=indicesNumber>>16&255}var pos=8;for(i=0;i ./build/prwm.dev.js", 9 | "build-min": "browserify ./index.js --s prwm | uglifyjs > ./build/prwm.min.js" 10 | }, 11 | "files": [ 12 | "index.js", 13 | "utils/is-big-endian-platform.js", 14 | "prwm/attribute-types.js", 15 | "prwm/decode.js", 16 | "prwm/encode.js" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/kchapelier/PRWM.git" 21 | }, 22 | "keywords": [ 23 | "prwm" 24 | ], 25 | "author": "Kevin Chapelier", 26 | "license": "MIT", 27 | "readmeFilename": "README.md", 28 | "bugs": { 29 | "url": "https://github.com/kchapelier/PRWM/issues" 30 | }, 31 | "homepage": "https://github.com/kchapelier/PRWM", 32 | "devDependencies": { 33 | "chai": "^4.0.1", 34 | "mocha": "^3.4.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /implementations/prwm/prwm/attribute-types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | Float: 0, 5 | Int: 1 6 | }; 7 | -------------------------------------------------------------------------------- /implementations/prwm/prwm/decode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var isBigEndianPlatform = require('../utils/is-big-endian-platform'); 4 | 5 | // match the values defined in the spec to the TypedArray types 6 | var InvertedEncodingTypes = [ 7 | null, 8 | Float32Array, 9 | null, 10 | Int8Array, 11 | Int16Array, 12 | null, 13 | Int32Array, 14 | Uint8Array, 15 | Uint16Array, 16 | null, 17 | Uint32Array 18 | ]; 19 | 20 | // define the method to use on a DataView, corresponding the TypedArray type 21 | var getMethods = { 22 | Uint16Array: 'getUint16', 23 | Uint32Array: 'getUint32', 24 | Int16Array: 'getInt16', 25 | Int32Array: 'getInt32', 26 | Float32Array: 'getFloat32' 27 | }; 28 | 29 | function copyFromBuffer (sourceArrayBuffer, viewType, position, length, fromBigEndian) { 30 | var bytesPerElement = viewType.BYTES_PER_ELEMENT, 31 | result; 32 | 33 | if (fromBigEndian === isBigEndianPlatform() || bytesPerElement === 1) { 34 | result = new viewType(sourceArrayBuffer, position, length); 35 | } else { 36 | var readView = new DataView(sourceArrayBuffer, position, length * bytesPerElement), 37 | getMethod = getMethods[viewType.name], 38 | littleEndian = !fromBigEndian; 39 | 40 | result = new viewType(length); 41 | 42 | for (var i = 0; i < length; i++) { 43 | result[i] = readView[getMethod](i * bytesPerElement, littleEndian); 44 | } 45 | } 46 | 47 | return result; 48 | } 49 | 50 | function decode (buffer, offset) { 51 | offset = offset || 0; 52 | 53 | var array = new Uint8Array(buffer, offset), 54 | version = array[0], 55 | flags = array[1], 56 | indexedGeometry = !!(flags >> 7), 57 | indicesType = flags >> 6 & 0x01, 58 | bigEndian = (flags >> 5 & 0x01) === 1, 59 | attributesNumber = flags & 0x1F, 60 | valuesNumber = 0, 61 | indicesNumber = 0; 62 | 63 | if (bigEndian) { 64 | valuesNumber = (array[2] << 16) + (array[3] << 8) + array[4]; 65 | indicesNumber = (array[5] << 16) + (array[6] << 8) + array[7]; 66 | } else { 67 | valuesNumber = array[2] + (array[3] << 8) + (array[4] << 16); 68 | indicesNumber = array[5] + (array[6] << 8) + (array[7] << 16); 69 | } 70 | 71 | /** PRELIMINARY CHECKS **/ 72 | 73 | if (offset / 4 % 1 !== 0) { 74 | throw new Error('PRWM decoder: Offset should be a multiple of 4, received ' + offset); 75 | } 76 | 77 | if (version === 0) { 78 | throw new Error('PRWM decoder: Invalid format version: 0'); 79 | } else if (version !== 1) { 80 | throw new Error('PRWM decoder: Unsupported format version: ' + version); 81 | } 82 | 83 | if (!indexedGeometry) { 84 | if (indicesType !== 0) { 85 | throw new Error('PRWM decoder: Indices type must be set to 0 for non-indexed geometries'); 86 | } else if (indicesNumber !== 0) { 87 | throw new Error('PRWM decoder: Number of indices must be set to 0 for non-indexed geometries'); 88 | } 89 | } 90 | 91 | /** PARSING **/ 92 | 93 | var pos = 8; 94 | 95 | var attributes = {}, 96 | attributeName, 97 | char, 98 | attributeNormalized, 99 | attributeType, 100 | cardinality, 101 | encodingType, 102 | arrayType, 103 | values, 104 | i; 105 | 106 | for (i = 0; i < attributesNumber; i++) { 107 | attributeName = ''; 108 | 109 | while (pos < array.length) { 110 | char = array[pos]; 111 | pos++; 112 | 113 | if (char === 0) { 114 | break; 115 | } else { 116 | attributeName += String.fromCharCode(char); 117 | } 118 | } 119 | 120 | flags = array[pos]; 121 | 122 | attributeType = flags >> 7 & 0x01; 123 | attributeNormalized = !!(flags >> 6 & 0x01); 124 | cardinality = (flags >> 4 & 0x03) + 1; 125 | encodingType = flags & 0x0F; 126 | arrayType = InvertedEncodingTypes[encodingType]; 127 | 128 | pos++; 129 | 130 | // padding to next multiple of 4 131 | pos = Math.ceil(pos / 4) * 4; 132 | 133 | values = copyFromBuffer(buffer, arrayType, pos + offset, cardinality * valuesNumber, bigEndian); 134 | 135 | pos+= arrayType.BYTES_PER_ELEMENT * cardinality * valuesNumber; 136 | 137 | attributes[attributeName] = { 138 | type: attributeType, 139 | normalized: attributeNormalized, 140 | cardinality: cardinality, 141 | values: values 142 | }; 143 | } 144 | 145 | pos = Math.ceil(pos / 4) * 4; 146 | 147 | var indices = null; 148 | 149 | if (indexedGeometry) { 150 | indices = copyFromBuffer( 151 | buffer, 152 | indicesType === 1 ? Uint32Array : Uint16Array, 153 | pos + offset, 154 | indicesNumber, 155 | bigEndian 156 | ); 157 | } 158 | 159 | return { 160 | version: version, 161 | bigEndian: bigEndian, 162 | attributes: attributes, 163 | indices: indices 164 | }; 165 | } 166 | 167 | module.exports = decode; 168 | -------------------------------------------------------------------------------- /implementations/prwm/prwm/encode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var isBigEndianPlatform = require('../utils/is-big-endian-platform'), 4 | attributeTypes = require('./attribute-types'); 5 | 6 | // match the TypedArray type with the value defined in the spec 7 | var EncodingTypes = { 8 | Float32Array: 1, 9 | Int8Array: 3, 10 | Int16Array: 4, 11 | Int32Array: 6, 12 | Uint8Array: 7, 13 | Uint16Array: 8, 14 | Uint32Array: 10 15 | }; 16 | 17 | // define the method to use on a DataView, corresponding the TypedArray type 18 | var setMethods = { 19 | Uint16Array: 'setUint16', 20 | Uint32Array: 'setUint32', 21 | Int16Array: 'setInt16', 22 | Int32Array: 'setInt32', 23 | Float32Array: 'setFloat32' 24 | }; 25 | 26 | function copyToBuffer (sourceTypedArray, destinationArrayBuffer, position, bigEndian) { 27 | var length = sourceTypedArray.length, 28 | bytesPerElement = sourceTypedArray.BYTES_PER_ELEMENT; 29 | 30 | var writeArray = new sourceTypedArray.constructor(destinationArrayBuffer, position, length); 31 | 32 | if (bigEndian === isBigEndianPlatform() || bytesPerElement === 1) { 33 | // desired endianness is the same as the platform, or the endianness doesn't matter (1 byte) 34 | writeArray.set(sourceTypedArray.subarray(0, length)); 35 | } else { 36 | var writeView = new DataView(destinationArrayBuffer, position, length * bytesPerElement), 37 | setMethod = setMethods[sourceTypedArray.constructor.name], 38 | littleEndian = !bigEndian, 39 | i = 0; 40 | 41 | for (i = 0; i < length; i++) { 42 | writeView[setMethod](i * bytesPerElement, sourceTypedArray[i], littleEndian); 43 | } 44 | } 45 | 46 | return writeArray; 47 | } 48 | 49 | function encode (attributes, indices, bigEndian) { 50 | var attributeKeys = attributes ? Object.keys(attributes) : [], 51 | indexedGeometry = !!indices, 52 | i, j; 53 | 54 | /** PRELIMINARY CHECKS **/ 55 | 56 | // this is not supposed to catch all the possible errors, only some of the gotchas 57 | 58 | if (attributeKeys.length === 0) { 59 | throw new Error('PRWM encoder: The model must have at least one attribute'); 60 | } 61 | 62 | if (attributeKeys.length > 31) { 63 | throw new Error('PRWM encoder: The model can have at most 31 attributes'); 64 | } 65 | 66 | for (i = 0; i < attributeKeys.length; i++) { 67 | if (!EncodingTypes.hasOwnProperty(attributes[attributeKeys[i]].values.constructor.name)) { 68 | throw new Error('PRWM encoder: Unsupported attribute values type: ' + attributes[attributeKeys[i]].values.constructor.name); 69 | } 70 | } 71 | 72 | if (indexedGeometry && indices.constructor.name !== 'Uint16Array' && indices.constructor.name !== 'Uint32Array') { 73 | throw new Error('PRWM encoder: The indices must be represented as an Uint16Array or an Uint32Array'); 74 | } 75 | 76 | /** GET THE TYPE OF INDICES AS WELL AS THE NUMBER OF INDICES AND ATTRIBUTE VALUES **/ 77 | 78 | var valuesNumber = attributes[attributeKeys[0]].values.length / attributes[attributeKeys[0]].cardinality | 0, 79 | indicesNumber = indexedGeometry ? indices.length : 0, 80 | indicesType = indexedGeometry && indices.constructor.name === 'Uint32Array' ? 1 : 0; 81 | 82 | /** GET THE FILE LENGTH **/ 83 | 84 | var totalLength = 8, 85 | attributeKey, 86 | attribute, 87 | attributeType, 88 | attributeNormalized; 89 | 90 | for (i = 0; i < attributeKeys.length; i++) { 91 | attributeKey = attributeKeys[i]; 92 | attribute = attributes[attributeKey]; 93 | totalLength += attributeKey.length + 2; // NUL byte + flag byte + padding 94 | totalLength = Math.ceil(totalLength / 4) * 4; // padding 95 | totalLength += attribute.values.byteLength; 96 | } 97 | 98 | if (indexedGeometry) { 99 | totalLength = Math.ceil(totalLength / 4) * 4; 100 | totalLength += indices.byteLength; 101 | } 102 | 103 | /** INITIALIZE THE BUFFER */ 104 | 105 | var buffer = new ArrayBuffer(totalLength), 106 | array = new Uint8Array(buffer); 107 | 108 | /** HEADER **/ 109 | 110 | array[0] = 1; 111 | array[1] = ( 112 | indexedGeometry << 7 | 113 | indicesType << 6 | 114 | (bigEndian ? 1 : 0) << 5 | 115 | attributeKeys.length & 0x1F 116 | ); 117 | 118 | if (bigEndian) { 119 | array[2] = valuesNumber >> 16 & 0xFF; 120 | array[3] = valuesNumber >> 8 & 0xFF; 121 | array[4] = valuesNumber & 0xFF; 122 | 123 | array[5] = indicesNumber >> 16 & 0xFF; 124 | array[6] = indicesNumber >> 8 & 0xFF; 125 | array[7] = indicesNumber & 0xFF; 126 | } else { 127 | array[2] = valuesNumber & 0xFF; 128 | array[3] = valuesNumber >> 8 & 0xFF; 129 | array[4] = valuesNumber >> 16 & 0xFF; 130 | 131 | array[5] = indicesNumber & 0xFF; 132 | array[6] = indicesNumber >> 8 & 0xFF; 133 | array[7] = indicesNumber >> 16 & 0xFF; 134 | } 135 | 136 | 137 | var pos = 8; 138 | 139 | /** ATTRIBUTES **/ 140 | 141 | for (i = 0; i < attributeKeys.length; i++) { 142 | attributeKey = attributeKeys[i]; 143 | attribute = attributes[attributeKey]; 144 | attributeType = typeof attribute.type === 'undefined' ? attributeTypes.Float : attribute.type; 145 | attributeNormalized = (!!attribute.normalized ? 1 : 0); 146 | 147 | /*** WRITE ATTRIBUTE HEADER ***/ 148 | 149 | for (j = 0; j < attributeKey.length; j++, pos++) { 150 | array[pos] = (attributeKey.charCodeAt(j) & 0x7F) || 0x5F; // default to underscore 151 | } 152 | 153 | pos++; 154 | 155 | array[pos] = ( 156 | attributeType << 7 | 157 | attributeNormalized << 6 | 158 | ((attribute.cardinality - 1) & 0x03) << 4 | 159 | EncodingTypes[attribute.values.constructor.name] & 0x0F 160 | ); 161 | 162 | pos++; 163 | 164 | 165 | // padding to next multiple of 4 166 | pos = Math.ceil(pos / 4) * 4; 167 | 168 | /*** WRITE ATTRIBUTE VALUES ***/ 169 | 170 | var attributesWriteArray = copyToBuffer(attribute.values, buffer, pos, bigEndian); 171 | 172 | pos += attributesWriteArray.byteLength; 173 | } 174 | 175 | /*** WRITE INDICES VALUES ***/ 176 | 177 | if (indexedGeometry) { 178 | pos = Math.ceil(pos / 4) * 4; 179 | 180 | copyToBuffer(indices, buffer, pos, bigEndian); 181 | } 182 | 183 | return buffer; 184 | } 185 | 186 | module.exports = encode; 187 | -------------------------------------------------------------------------------- /implementations/prwm/test/assets/cube-BE.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/implementations/prwm/test/assets/cube-BE.prwm -------------------------------------------------------------------------------- /implementations/prwm/test/assets/cube-LE.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/implementations/prwm/test/assets/cube-LE.prwm -------------------------------------------------------------------------------- /implementations/prwm/test/assets/cube.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function (bigEndian) { 4 | return { 5 | version: 1, 6 | bigEndian: bigEndian, 7 | attributes: { 8 | position: { 9 | type: 0, 10 | normalized: false, 11 | cardinality: 3, 12 | values: new Float32Array([-0.5,-0.5,0.5,-0.5,0.5,0.5,-0.5,0.5,-0.5,-0.5,-0.5,-0.5,0.5,-0.5,-0.5,0.5,0.5,-0.5,0.5,0.5,0.5,0.5,-0.5,0.5,0.5,-0.5,-0.5,0.5,-0.5,0.5,-0.5,-0.5,0.5,-0.5,-0.5,-0.5,0.5,0.5,0.5,0.5,0.5,-0.5,-0.5,0.5,-0.5,-0.5,0.5,0.5,-0.5,-0.5,-0.5,-0.5,0.5,-0.5,0.5,0.5,-0.5,0.5,-0.5,-0.5,0.5,-0.5,0.5,0.5,0.5,0.5,-0.5,0.5,0.5,-0.5,-0.5,0.5]) 13 | }, 14 | normal: { 15 | type: 0, 16 | normalized: false, 17 | cardinality: 3, 18 | values: new Float32Array([-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,1,0,0,1,0,0,1,0,0,1]) 19 | } 20 | }, 21 | indices: new Uint16Array([0,1,2,0,2,3,4,5,6,4,6,7,8,9,10,8,10,11,12,13,14,12,14,15,16,17,18,16,18,19,20,21,22,20,22,23]) 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /implementations/prwm/test/attribute-types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var lib = require('../index'), 4 | should = require('chai').should(); 5 | 6 | describe('Attribute types', function () { 7 | it('should contain the values defined by the spec', function () { 8 | lib.Float.should.equal(0); 9 | lib.Int.should.equal(1); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /implementations/prwm/test/decode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var lib = require('../index'), 4 | fs = require('fs'), 5 | should = require('chai').should(); 6 | 7 | var cubeBEBuffer = fs.readFileSync(__dirname + '/assets/cube-BE.prwm'), 8 | cubeLEBuffer = fs.readFileSync(__dirname + '/assets/cube-LE.prwm'), 9 | expectedCubeBEData = require('./assets/cube')(true), 10 | expectedCubeLEData = require('./assets/cube')(false); 11 | 12 | // see https://github.com/nodejs/node/issues/11132#issuecomment-277157700 13 | var cubeBE = new Uint8Array(cubeBEBuffer).buffer, 14 | cubeLE = new Uint8Array(cubeLEBuffer).buffer; 15 | 16 | // copy with bad versions 17 | var cubeVersion0Array = new Uint8Array(cubeBEBuffer), 18 | cubeVersion2Array = new Uint8Array(cubeBEBuffer), 19 | cubeVersion0 = cubeVersion0Array.buffer, 20 | cubeVersion2 = cubeVersion2Array.buffer; 21 | 22 | cubeVersion0Array[0] = 0; 23 | cubeVersion2Array[0] = 2; 24 | 25 | describe('decode()', function () { 26 | it('should properly read a big endian file', function () { 27 | var data = lib.decode(cubeBE); 28 | 29 | data.should.deep.equal(expectedCubeBEData); 30 | }); 31 | 32 | it('should properly read a little endian file', function () { 33 | var data = lib.decode(cubeLE); 34 | 35 | data.should.deep.equal(expectedCubeLEData); 36 | }); 37 | 38 | it('should properly read a big endian file from an offset', function () { 39 | var offset = 12; 40 | var tmp = new Uint8Array(offset + cubeBE.byteLength); 41 | tmp.set(new Uint8Array(cubeBE), offset); 42 | var offsetCubeBE = tmp.buffer; 43 | 44 | var data = lib.decode(offsetCubeBE, offset); 45 | 46 | data.should.deep.equal(expectedCubeBEData); 47 | }); 48 | 49 | it('should properly read a little endian file from an offset', function () { 50 | var offset = 16; 51 | var tmp = new Uint8Array(offset + cubeLE.byteLength); 52 | tmp.set(new Uint8Array(cubeLE), offset); 53 | var offsetCubeLE = tmp.buffer; 54 | 55 | var data = lib.decode(offsetCubeLE, offset); 56 | 57 | data.should.deep.equal(expectedCubeLEData); 58 | }); 59 | 60 | it('should throw an error if the version is 0', function () { 61 | (function () { lib.decode(cubeVersion0); }).should.throw('PRWM decoder: Invalid format version: 0'); 62 | }); 63 | 64 | it('should throw an error if the version is not the same as the lib', function () { 65 | (function () { lib.decode(cubeVersion2); }).should.throw('PRWM decoder: Unsupported format version: 2'); 66 | }); 67 | 68 | it('should throw an error if the given offset is not a multiple of 4', function () { 69 | (function () { lib.decode(cubeLE, 5); }).should.throw('PRWM decoder: Offset should be a multiple of 4, received 5'); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /implementations/prwm/test/encode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var lib = require('../index'), 4 | should = require('chai').should(); 5 | 6 | function generateAttributes (nbAttributes) { 7 | var attr = {}, 8 | i = 1; 9 | 10 | for (; i <= nbAttributes; i++) { 11 | attr['attribute' + i] = { cardinality: 1, values: new Uint8Array([0, 1]) }; 12 | } 13 | 14 | return attr; 15 | } 16 | 17 | describe('encode()', function () { 18 | it('should throw an error if no attribute is provided', function () { 19 | (function () { 20 | lib.encode(null, null, false); 21 | }).should.throw('PRWM encoder: The model must have at least one attribute'); 22 | 23 | (function () { 24 | lib.encode({}, null, false); 25 | }).should.throw('PRWM encoder: The model must have at least one attribute'); 26 | }); 27 | 28 | it('should throw an error if more than 31 attributes are provided', function () { 29 | (function () { 30 | lib.encode(generateAttributes(32), null, false); 31 | }).should.throw('PRWM encoder: The model can have at most 31 attributes'); 32 | }); 33 | 34 | it('should not throw error if the number of attribute is between 1 and 31', function () { 35 | (function () { 36 | lib.encode(generateAttributes(1), null, false); 37 | }).should.not.throw(); 38 | 39 | (function () { 40 | lib.encode(generateAttributes(31), null, false); 41 | }).should.not.throw(); 42 | }); 43 | 44 | it('should return an ArrayBuffer', function () { 45 | lib.encode(generateAttributes(1), null, false).should.be.an('ArrayBuffer'); 46 | }); 47 | 48 | it('should not support Float64Array as attribute values', function () { 49 | (function () { 50 | lib.encode({ 51 | position: { 52 | cardinality: 3, 53 | values: new Float64Array([0, 1, 2]) 54 | } 55 | }, null, false); 56 | }).should.throw('PRWM encoder: Unsupported attribute values type: Float64Array'); 57 | }); 58 | 59 | it('should not support non-typed-array as attribute values', function () { 60 | (function () { 61 | lib.encode({ 62 | position: { 63 | cardinality: 3, 64 | values: [0, 1, 2] 65 | } 66 | }, null, false); 67 | }).should.throw('PRWM encoder: Unsupported attribute values type: Array'); 68 | }); 69 | 70 | it('should not support indices other than Uint16Array and Uint32Array', function () { 71 | (function () { 72 | lib.encode(generateAttributes(1), new Uint8Array([0, 1]), false); 73 | }).should.throw('PRWM encoder: The indices must be represented as an Uint16Array or an Uint32Array'); 74 | 75 | (function () { 76 | lib.encode(generateAttributes(1), [0, 1], false); 77 | }).should.throw('PRWM encoder: The indices must be represented as an Uint16Array or an Uint32Array'); 78 | }); 79 | 80 | it('should specify it is the version 1 of the format', function () { 81 | var arrayBuffer = lib.encode(generateAttributes(1), null, false); 82 | 83 | var array = new Uint8Array(arrayBuffer); 84 | 85 | array[5].should.equal(0); 86 | array[6].should.equal(0); 87 | array[7].should.equal(0); 88 | }); 89 | 90 | it('should set the number of indices to 0 for non-indexed geometry', function () { 91 | var arrayBuffer = lib.encode(generateAttributes(1), null, false); 92 | 93 | var array = new Uint8Array(arrayBuffer); 94 | 95 | array[5].should.equal(0); 96 | array[6].should.equal(0); 97 | array[7].should.equal(0); 98 | }); 99 | 100 | it('should set the number of indices to the correct value for indexed geometry', function () { 101 | var arrayBuffer = lib.encode(generateAttributes(1), new Uint16Array([0, 1]), false), 102 | array = new Uint8Array(arrayBuffer); 103 | 104 | array[5].should.equal(2); 105 | array[6].should.equal(0); 106 | array[7].should.equal(0); 107 | 108 | arrayBuffer = lib.encode(generateAttributes(1), new Uint16Array([0, 1]), true); 109 | array = new Uint8Array(arrayBuffer); 110 | 111 | array[5].should.equal(0); 112 | array[6].should.equal(0); 113 | array[7].should.equal(2); 114 | }); 115 | 116 | it('should set the number of attribute values to the correct value', function () { 117 | var arrayBuffer = lib.encode({ attrib: { cardinality: 1, values: new Uint8Array([0, 1]) } }, null, false), 118 | array = new Uint8Array(arrayBuffer); 119 | 120 | array[2].should.equal(2); 121 | array[3].should.equal(0); 122 | array[4].should.equal(0); 123 | 124 | arrayBuffer = lib.encode({ attrib: { cardinality: 2, values: new Uint8Array([0, 1]) } }, null, false); 125 | array = new Uint8Array(arrayBuffer); 126 | 127 | array[2].should.equal(1); 128 | array[3].should.equal(0); 129 | array[4].should.equal(0); 130 | 131 | arrayBuffer = lib.encode({ attrib: { cardinality: 1, values: new Uint8Array([0, 1]) } }, null, true); 132 | array = new Uint8Array(arrayBuffer); 133 | 134 | array[2].should.equal(0); 135 | array[3].should.equal(0); 136 | array[4].should.equal(2); 137 | 138 | arrayBuffer = lib.encode({ attrib: { cardinality: 2, values: new Uint8Array([0, 1]) } }, null, true); 139 | array = new Uint8Array(arrayBuffer); 140 | 141 | array[2].should.equal(0); 142 | array[3].should.equal(0); 143 | array[4].should.equal(1); 144 | }); 145 | }); 146 | -------------------------------------------------------------------------------- /implementations/prwm/test/is-big-endian.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var lib = require('../index'), 4 | should = require('chai').should(); 5 | 6 | describe('isBigEndianPlatform()', function () { 7 | it('should return a boolean', function () { 8 | lib.isBigEndianPlatform().should.be.a('boolean'); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /implementations/prwm/utils/is-big-endian-platform.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var bigEndianPlatform = null; 4 | 5 | /** 6 | * Check if the endianness of the platform is big-endian (most significant bit first) 7 | * @returns {boolean} True if big-endian, false if little-endian 8 | */ 9 | function isBigEndianPlatform () { 10 | if (bigEndianPlatform === null) { 11 | var buffer = new ArrayBuffer(2), 12 | uint8Array = new Uint8Array(buffer), 13 | uint16Array = new Uint16Array(buffer); 14 | 15 | uint8Array[0] = 0xAA; // set first byte 16 | uint8Array[1] = 0xBB; // set second byte 17 | bigEndianPlatform = (uint16Array[0] === 0xAABB); 18 | } 19 | 20 | return bigEndianPlatform; 21 | } 22 | 23 | module.exports = isBigEndianPlatform; 24 | -------------------------------------------------------------------------------- /implementations/svg2prwm/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Kevin Chapelier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /implementations/svg2prwm/README.md: -------------------------------------------------------------------------------- 1 | # svg2prwm 2 | 3 | CLI tool to convert SVG files into PRWM files. 4 | 5 | Packed Raw WebGL Model is a binary file format for nD geometries specifically designed for JavaScript and WebGL with a strong focus on fast parsing (from 1ms to 0.1ms in Chrome 59 on a MBP Late 2013). More information on this [here](https://github.com/kchapelier/PRWM). 6 | 7 | Currently this tool only support the conversion of SVG containing a path. 8 | 9 | ## Installing 10 | 11 | With [npm](http://npmjs.org) do: 12 | 13 | ``` 14 | npm install svg2prwm -g 15 | ``` 16 | 17 | ## CLI Usage 18 | 19 | ### svg2prwm -i inputFile -o outputFile [OPTIONS] 20 | 21 | **Options** 22 | 23 | * **--normals :** Generate a set of normals. 24 | * **--uvs :** Generate a set of uvs. 25 | * **--separateTriangles :** Make sure no vertex is shared between multiple triangles. Produces larger files. 26 | * **--scale :** Scale used for the bezier curves. A higher value means a better resolution. 27 | * **--simplify :** Simplification amount. A higher value means a more simplified mesh. 28 | * **--be :** Indicate that the destination file must be in Big Endian byte order. By default the destination file is in Little Endian. 29 | * **-q, --quiet :** Quiet mode. Silence the output to the console. 30 | 31 | ## API 32 | 33 | ### svg2prwm.convert(svgString, options) 34 | 35 | **Arguments** 36 | 37 | * **svgString :** The svg file as a string. 38 | * **options :** Options 39 | * **normals :** Generate a set of normals. 40 | * **uvs :** Generate a set of uvs. 41 | * **separateTriangles :** Make sure no vertex is shared between multiple triangles. Produces larger files. 42 | * **scale :** Scale used for the bezier curves. A higher value means a better resolution. 43 | * **simplify :** Simplification amount. A higher value means a more simplified mesh. 44 | * **bigEndian :** Indicate that the destination file must be in Big Endian byte order. By default the destination file is in Little Endian. 45 | * **quiet :** Quiet mode. Silence the output to the console. 46 | 47 | ## Example 48 | 49 | ``` 50 | $ svg2prwm -i original.svg -o destination.prwm --normals --scale 1 --simplify 0.5 51 | 52 | * Reading original.svg 53 | * Writing destination.prwm 54 | 55 | Operation completed in 0.20s. 56 | Original SVG file size : 6.56kB 57 | Generated indexed PRWM file size : 5.38kB 58 | Individual vertices : 263 59 | ``` 60 | 61 | ## Changelog 62 | 63 | ### 1.0.1 (2017.12.26) : 64 | 65 | * Update dependencies. 66 | 67 | ### 1.0.0 (2017.08.19) : 68 | 69 | * First release. 70 | 71 | ## License 72 | 73 | MIT 74 | -------------------------------------------------------------------------------- /implementations/svg2prwm/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | "use strict"; 4 | 5 | var fs = require('fs'), 6 | yargs = require('yargs'), 7 | svg2prwm = require('./index'); 8 | 9 | var argv = yargs.usage('Usage: obj2prwm -i inputFile -o outputFile [options]') 10 | .describe('i', 'Input file') 11 | .alias('i', 'in') 12 | .describe('o', 'Output file') 13 | .alias('o', 'out') 14 | .describe('simplify', 'Simplification amount, a higher value means a more simplified mesh') 15 | .describe('scale', 'Scale used for the bezier curves, a higher value means a better resolution') 16 | .describe('normals', 'Generate normals') 17 | .describe('uvs', 'Generate uvs') 18 | .describe('separateTriangles', 'Separate all triangles so that no vertex is shared') 19 | .describe('be', 'Write the output file in big endian') 20 | .describe('q', 'Quiet mode. Silence the output.') 21 | .alias('q', 'quiet') 22 | .boolean(['quiet', 'be']) 23 | .demandOption(['o', 'i']) 24 | .help('h') 25 | .alias('h', 'help') 26 | .argv; 27 | 28 | var now = Date.now(), 29 | inFd = fs.openSync(argv.in, 'r'), 30 | outFd = fs.openSync(argv.out, 'w'), 31 | options = { 32 | normals: !!argv.normals, 33 | uvs: !!argv.uvs, 34 | separateTriangles: argv.separateTriangles, 35 | simplify: typeof argv.simplify !== 'undefined' ? parseFloat(argv.simplify) : 0, 36 | scale: typeof argv.scale !== 'undefined' ? parseFloat(argv.scale) : 1, 37 | bigEndian: !!argv.be, 38 | quiet: !!argv.quiet 39 | }, 40 | log = options.quiet ? function noop() {} : function log(s) { console.log(s) }; 41 | 42 | 43 | log(' * Reading ' + argv.in); 44 | var svgString = fs.readFileSync(inFd, 'utf8'); 45 | 46 | var arrayBuffer = svg2prwm.convert(svgString, options); 47 | 48 | log(' * Writing ' + argv.out); 49 | fs.writeFileSync(outFd, new Buffer(arrayBuffer), { flag: 'w' }); 50 | log(''); 51 | log('Operation completed in ' + ((Date.now() - now) / 1000).toFixed(2) + 's.'); 52 | log('Original SVG file size : ' + (Buffer.byteLength(svgString, 'utf8') / 1024).toFixed(2) + 'kB'); 53 | log('Generated PRWM file size : ' + (arrayBuffer.byteLength / 1024).toFixed(2) + 'kB'); 54 | log('Individual vertices : ' + svg2prwm.getNumberOfVertices()); 55 | log(''); 56 | 57 | 58 | -------------------------------------------------------------------------------- /implementations/svg2prwm/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var extractSvgPath = require('extract-svg-path').parse; 4 | var createMesh = require('svg-mesh-3d'); 5 | var prwm = require('prwm'); 6 | var reindex = require('mesh-reindex'); 7 | var unindex = require('unindex-mesh'); 8 | 9 | var nbVertices = null; 10 | 11 | module.exports = { 12 | convert: function (svgString, options) { 13 | var mesh = createMesh(extractSvgPath(svgString), { 14 | normalize: true, 15 | simplify: options.simplify, 16 | scale: options.scale 17 | }); 18 | 19 | if (options.separateTriangles) { 20 | mesh = reindex(unindex(mesh.positions, mesh.cells)); 21 | } 22 | 23 | var attributes = {}; 24 | 25 | var positions = new Float32Array(mesh.positions.length * 3); 26 | 27 | for (var i = 0; i < mesh.positions.length; i++) { 28 | positions[i * 3] = mesh.positions[i][0]; 29 | positions[i * 3 + 1] = mesh.positions[i][1]; 30 | positions[i * 3 + 2] = mesh.positions[i][2]; 31 | } 32 | 33 | attributes.position = { 34 | cardinality: 3, 35 | normalized: false, 36 | values: positions 37 | }; 38 | 39 | nbVertices = positions.length / 3; 40 | 41 | if (options.normals) { 42 | // saves a few kB by using a Int8Array instead of a Float32Array 43 | var normals = new Int8Array(mesh.positions.length * 3); 44 | 45 | for (i = 0; i < mesh.positions.length; i++) { 46 | normals[i * 3] = 0; 47 | normals[i * 3 + 1] = 0; 48 | normals[i * 3 + 2] = 1; 49 | } 50 | 51 | attributes.normal = { 52 | cardinality: 3, 53 | normalized: false, 54 | values: normals 55 | }; 56 | } 57 | 58 | if (options.uvs) { 59 | var uvs = new Float32Array(mesh.positions.length * 2); 60 | 61 | for (i = 0; i < mesh.positions.length; i++) { 62 | uvs[i * 2] = (1 + mesh.positions[i][0]) / 2; 63 | uvs[i * 2 + 1] = (1 + mesh.positions[i][1]) / 2; 64 | } 65 | 66 | attributes.uv = { 67 | cardinality: 2, 68 | normalized: false, 69 | values: uvs 70 | }; 71 | } 72 | 73 | var indices = null; 74 | 75 | indices = mesh.positions.length <= 0xFFFF ? new Uint16Array(mesh.cells.length * 3) : new Uint32Array(mesh.cells.length * 3); 76 | 77 | for (i = 0; i < mesh.cells.length; i++) { 78 | indices[i * 3] = mesh.cells[i][1]; 79 | indices[i * 3 + 1] = mesh.cells[i][0]; 80 | indices[i * 3 + 2] = mesh.cells[i][2]; 81 | } 82 | 83 | return prwm.encode( 84 | attributes, 85 | indices, 86 | options.bigEndian 87 | ); 88 | }, 89 | getNumberOfVertices: function () { 90 | return nbVertices; 91 | } 92 | }; 93 | -------------------------------------------------------------------------------- /implementations/svg2prwm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svg2prwm", 3 | "version": "1.0.1", 4 | "description": "CLI tool to convert SVG files into PRWM files.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build-dev": "browserify ./index.js -d --s svg2prwm > ./build/svg2prwm.dev.js", 8 | "build-min": "browserify ./index.js --s svg2prwm | uglifyjs > ./build/svg2prwm.min.js" 9 | }, 10 | "bin": { 11 | "svg2prwm": "cli.js" 12 | }, 13 | "files": [ 14 | "index.js", 15 | "cli.js" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/kchapelier/PRWM.git" 20 | }, 21 | "keywords": [ 22 | "svg", 23 | "prwm" 24 | ], 25 | "author": "Kevin Chapelier", 26 | "license": "MIT", 27 | "readmeFilename": "README.md", 28 | "bugs": { 29 | "url": "https://github.com/kchapelier/PRWM/issues" 30 | }, 31 | "homepage": "https://github.com/kchapelier/PRWM", 32 | "dependencies": { 33 | "extract-svg-path": "~2.1.0", 34 | "mesh-reindex": "~1.0.0", 35 | "prwm": "^1.2.0", 36 | "svg-mesh-3d": "~1.1.0", 37 | "unindex-mesh": "~2.0.0", 38 | "yargs": "~10.0.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /implementations/three-buffergeometry-to-prwm/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | .idea 40 | -------------------------------------------------------------------------------- /implementations/three-buffergeometry-to-prwm/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Kevin Chapelier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /implementations/three-buffergeometry-to-prwm/README.md: -------------------------------------------------------------------------------- 1 | # three-buffergeometry-to-prwm 2 | 3 | Takes a THREE.BufferGeometry and returns an ArrayBuffer containing a PRWM file. 4 | 5 | Packed Raw WebGL Model is a binary file format for nD geometries specifically designed for JavaScript and WebGL with a strong focus on fast parsing (from 1ms to 0.1ms in Chrome 59 on a MBP Late 2013). More information on this [here](https://github.com/kchapelier/PRWM). 6 | 7 | Mostly a convenience wrapper around [prwm](https://www.npmjs.com/package/prwm). 8 | 9 | ## Installing 10 | 11 | With [npm](http://npmjs.org) do: 12 | 13 | ``` 14 | npm install three-buffergeometry-to-prwm 15 | ``` 16 | 17 | ## Example 18 | 19 | ```js 20 | var threeBufferGeometryToPrwm = require('three-buffergeometry-to-prwm'); 21 | 22 | console.log(threeBufferGeometryToPrwm(bufferGeometry)); 23 | ``` 24 | 25 | ## API 26 | 27 | ```js 28 | threeBufferGeometryToPrwm(bufferGeometry, bigEndian); 29 | ``` 30 | 31 | ### Arguments 32 | 33 | * geometry : an instance of THREE.BufferGeometry (can be indexed or non-indexed). 34 | * bigEndian : whether the endianness of the generated file should be Big Endian. 35 | 36 | ## Changelog 37 | 38 | ### 1.0.0 (2017.06.10) : 39 | 40 | * First release. 41 | 42 | ## Roadmap 43 | 44 | * Tests 45 | 46 | ## License 47 | 48 | MIT 49 | -------------------------------------------------------------------------------- /implementations/three-buffergeometry-to-prwm/build/three-buffergeometry-to-prwm.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.threeBuffergeometryToPrwm=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o>7),indicesType=flags>>6&1,bigEndian=(flags>>5&1)===1,attributesNumber=flags&31,valuesNumber=0,indicesNumber=0;if(bigEndian){valuesNumber=(array[2]<<16)+(array[3]<<8)+array[4];indicesNumber=(array[5]<<16)+(array[6]<<8)+array[7]}else{valuesNumber=array[2]+(array[3]<<8)+(array[4]<<16);indicesNumber=array[5]+(array[6]<<8)+(array[7]<<16)}if(offset/4%1!==0){throw new Error("PRWM decoder: Offset should be a multiple of 4, received "+offset)}if(version===0){throw new Error("PRWM decoder: Invalid format version: 0")}else if(version!==1){throw new Error("PRWM decoder: Unsupported format version: "+version)}if(!indexedGeometry){if(indicesType!==0){throw new Error("PRWM decoder: Indices type must be set to 0 for non-indexed geometries")}else if(indicesNumber!==0){throw new Error("PRWM decoder: Number of indices must be set to 0 for non-indexed geometries")}}var pos=8;var attributes={},attributeName,char,attributeNormalized,attributeType,cardinality,encodingType,arrayType,values,i;for(i=0;i>7&1;attributeNormalized=!!(flags>>6&1);cardinality=(flags>>4&3)+1;encodingType=flags&15;arrayType=InvertedEncodingTypes[encodingType];pos++;pos=Math.ceil(pos/4)*4;values=copyFromBuffer(buffer,arrayType,pos+offset,cardinality*valuesNumber,bigEndian);pos+=arrayType.BYTES_PER_ELEMENT*cardinality*valuesNumber;attributes[attributeName]={type:attributeType,normalized:attributeNormalized,cardinality:cardinality,values:values}}pos=Math.ceil(pos/4)*4;var indices=null;if(indexedGeometry){indices=copyFromBuffer(buffer,indicesType===1?Uint32Array:Uint16Array,pos+offset,indicesNumber,bigEndian)}return{version:version,bigEndian:bigEndian,attributes:attributes,indices:indices}}module.exports=decode},{"../utils/is-big-endian-platform":6}],5:[function(require,module,exports){"use strict";var isBigEndianPlatform=require("../utils/is-big-endian-platform"),attributeTypes=require("./attribute-types");var EncodingTypes={Float32Array:1,Int8Array:3,Int16Array:4,Int32Array:6,Uint8Array:7,Uint16Array:8,Uint32Array:10};var setMethods={Uint16Array:"setUint16",Uint32Array:"setUint32",Int16Array:"setInt16",Int32Array:"setInt32",Float32Array:"setFloat32"};function copyToBuffer(sourceTypedArray,destinationArrayBuffer,position,bigEndian){var length=sourceTypedArray.length,bytesPerElement=sourceTypedArray.BYTES_PER_ELEMENT;var writeArray=new sourceTypedArray.constructor(destinationArrayBuffer,position,length);if(bigEndian===isBigEndianPlatform()||bytesPerElement===1){writeArray.set(sourceTypedArray.subarray(0,length))}else{var writeView=new DataView(destinationArrayBuffer,position,length*bytesPerElement),setMethod=setMethods[sourceTypedArray.constructor.name],littleEndian=!bigEndian,i=0;for(i=0;i31){throw new Error("PRWM encoder: The model can have at most 31 attributes")}for(i=0;i>16&255;array[3]=valuesNumber>>8&255;array[4]=valuesNumber&255;array[5]=indicesNumber>>16&255;array[6]=indicesNumber>>8&255;array[7]=indicesNumber&255}else{array[2]=valuesNumber&255;array[3]=valuesNumber>>8&255;array[4]=valuesNumber>>16&255;array[5]=indicesNumber&255;array[6]=indicesNumber>>8&255;array[7]=indicesNumber>>16&255}var pos=8;for(i=0;i ./build/three-buffergeometry-to-prwm.dev.js", 8 | "build-min": "browserify ./index.js --s threeBuffergeometryToPrwm | uglifyjs > ./build/three-buffergeometry-to-prwm.min.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/kchapelier/PRWM.git" 13 | }, 14 | "keywords": [ 15 | "three", 16 | "threejs", 17 | "three.js", 18 | "prwm" 19 | ], 20 | "author": "Kevin Chapelier", 21 | "license": "MIT", 22 | "readmeFilename": "README.md", 23 | "bugs": { 24 | "url": "https://github.com/kchapelier/PRWM/issues" 25 | }, 26 | "homepage": "https://github.com/kchapelier/PRWM", 27 | "dependencies": { 28 | "prwm": "^1.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /implementations/three-prwm-loader/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | .idea 40 | 41 | generate-npm-module.js -------------------------------------------------------------------------------- /implementations/three-prwm-loader/PRWMLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Kevin Chapelier / https://github.com/kchapelier 3 | * See https://github.com/kchapelier/PRWM for more informations about this file format 4 | */ 5 | 6 | ( function ( THREE ) { 7 | 8 | 'use strict'; 9 | 10 | var bigEndianPlatform = null; 11 | 12 | /** 13 | * Check if the endianness of the platform is big-endian (most significant bit first) 14 | * @returns {boolean} True if big-endian, false if little-endian 15 | */ 16 | function isBigEndianPlatform() { 17 | 18 | if ( bigEndianPlatform === null ) { 19 | 20 | var buffer = new ArrayBuffer( 2 ), 21 | uint8Array = new Uint8Array( buffer ), 22 | uint16Array = new Uint16Array( buffer ); 23 | 24 | uint8Array[ 0 ] = 0xAA; // set first byte 25 | uint8Array[ 1 ] = 0xBB; // set second byte 26 | bigEndianPlatform = ( uint16Array[ 0 ] === 0xAABB ); 27 | 28 | } 29 | 30 | return bigEndianPlatform; 31 | 32 | } 33 | 34 | // match the values defined in the spec to the TypedArray types 35 | var InvertedEncodingTypes = [ 36 | null, 37 | Float32Array, 38 | null, 39 | Int8Array, 40 | Int16Array, 41 | null, 42 | Int32Array, 43 | Uint8Array, 44 | Uint16Array, 45 | null, 46 | Uint32Array 47 | ]; 48 | 49 | // define the method to use on a DataView, corresponding the TypedArray type 50 | var getMethods = { 51 | Uint16Array: 'getUint16', 52 | Uint32Array: 'getUint32', 53 | Int16Array: 'getInt16', 54 | Int32Array: 'getInt32', 55 | Float32Array: 'getFloat32', 56 | Float64Array: 'getFloat64' 57 | }; 58 | 59 | 60 | function copyFromBuffer( sourceArrayBuffer, viewType, position, length, fromBigEndian ) { 61 | 62 | var bytesPerElement = viewType.BYTES_PER_ELEMENT, 63 | result; 64 | 65 | if ( fromBigEndian === isBigEndianPlatform() || bytesPerElement === 1 ) { 66 | 67 | result = new viewType( sourceArrayBuffer, position, length ); 68 | 69 | } else { 70 | 71 | var readView = new DataView( sourceArrayBuffer, position, length * bytesPerElement ), 72 | getMethod = getMethods[ viewType.name ], 73 | littleEndian = ! fromBigEndian, 74 | i = 0; 75 | 76 | result = new viewType( length ); 77 | 78 | for ( ; i < length; i ++ ) { 79 | 80 | result[ i ] = readView[ getMethod ]( i * bytesPerElement, littleEndian ); 81 | 82 | } 83 | 84 | } 85 | 86 | return result; 87 | 88 | } 89 | 90 | 91 | function decodePrwm( buffer, offset ) { 92 | offset = offset || 0; 93 | 94 | var array = new Uint8Array( buffer, offset ), 95 | version = array[ 0 ], 96 | flags = array[ 1 ], 97 | indexedGeometry = !! ( flags >> 7 & 0x01 ), 98 | indicesType = flags >> 6 & 0x01, 99 | bigEndian = ( flags >> 5 & 0x01 ) === 1, 100 | attributesNumber = flags & 0x1F, 101 | valuesNumber = 0, 102 | indicesNumber = 0; 103 | 104 | if ( bigEndian ) { 105 | 106 | valuesNumber = ( array[ 2 ] << 16 ) + ( array[ 3 ] << 8 ) + array[ 4 ]; 107 | indicesNumber = ( array[ 5 ] << 16 ) + ( array[ 6 ] << 8 ) + array[ 7 ]; 108 | 109 | } else { 110 | 111 | valuesNumber = array[ 2 ] + ( array[ 3 ] << 8 ) + ( array[ 4 ] << 16 ); 112 | indicesNumber = array[ 5 ] + ( array[ 6 ] << 8 ) + ( array[ 7 ] << 16 ); 113 | 114 | } 115 | 116 | /** PRELIMINARY CHECKS **/ 117 | 118 | if ( offset / 4 % 1 !== 0 ) { 119 | 120 | throw new Error( 'PRWM decoder: Offset should be a multiple of 4, received ' + offset ); 121 | 122 | } 123 | 124 | if ( version === 0 ) { 125 | 126 | throw new Error( 'PRWM decoder: Invalid format version: 0' ); 127 | 128 | } else if ( version !== 1 ) { 129 | 130 | throw new Error( 'PRWM decoder: Unsupported format version: ' + version ); 131 | 132 | } 133 | 134 | if ( ! indexedGeometry ) { 135 | 136 | if ( indicesType !== 0 ) { 137 | 138 | throw new Error( 'PRWM decoder: Indices type must be set to 0 for non-indexed geometries' ); 139 | 140 | } else if ( indicesNumber !== 0 ) { 141 | 142 | throw new Error( 'PRWM decoder: Number of indices must be set to 0 for non-indexed geometries' ); 143 | 144 | } 145 | 146 | } 147 | 148 | /** PARSING **/ 149 | 150 | var pos = 8; 151 | 152 | var attributes = {}, 153 | attributeName, 154 | char, 155 | attributeType, 156 | cardinality, 157 | encodingType, 158 | arrayType, 159 | values, 160 | indices, 161 | i; 162 | 163 | for ( i = 0; i < attributesNumber; i ++ ) { 164 | 165 | attributeName = ''; 166 | 167 | while ( pos < array.length ) { 168 | 169 | char = array[ pos ]; 170 | pos ++; 171 | 172 | if ( char === 0 ) { 173 | 174 | break; 175 | 176 | } else { 177 | 178 | attributeName += String.fromCharCode( char ); 179 | 180 | } 181 | 182 | } 183 | 184 | flags = array[ pos ]; 185 | 186 | attributeType = flags >> 7 & 0x01; 187 | cardinality = ( flags >> 4 & 0x03 ) + 1; 188 | encodingType = flags & 0x0F; 189 | arrayType = InvertedEncodingTypes[ encodingType ]; 190 | 191 | pos ++; 192 | 193 | // padding to next multiple of 4 194 | pos = Math.ceil( pos / 4 ) * 4; 195 | 196 | values = copyFromBuffer( buffer, arrayType, pos + offset, cardinality * valuesNumber, bigEndian ); 197 | 198 | pos += arrayType.BYTES_PER_ELEMENT * cardinality * valuesNumber; 199 | 200 | attributes[ attributeName ] = { 201 | type: attributeType, 202 | cardinality: cardinality, 203 | values: values 204 | }; 205 | 206 | } 207 | 208 | pos = Math.ceil( pos / 4 ) * 4; 209 | 210 | indices = null; 211 | 212 | if ( indexedGeometry ) { 213 | 214 | indices = copyFromBuffer( 215 | buffer, 216 | indicesType === 1 ? Uint32Array : Uint16Array, 217 | pos + offset, 218 | indicesNumber, 219 | bigEndian 220 | ); 221 | 222 | } 223 | 224 | return { 225 | version: version, 226 | attributes: attributes, 227 | indices: indices 228 | }; 229 | 230 | } 231 | 232 | // Define the public interface 233 | 234 | THREE.PRWMLoader = function PRWMLoader( manager ) { 235 | 236 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 237 | 238 | }; 239 | 240 | THREE.PRWMLoader.prototype = { 241 | 242 | constructor: THREE.PRWMLoader, 243 | 244 | load: function ( url, onLoad, onProgress, onError ) { 245 | 246 | var scope = this; 247 | 248 | url = url.replace( /\*/g, isBigEndianPlatform() ? 'be' : 'le' ); 249 | 250 | var loader = new THREE.FileLoader( scope.manager ); 251 | loader.setPath( scope.path ); 252 | loader.setResponseType( 'arraybuffer' ); 253 | 254 | loader.load( url, function ( arrayBuffer ) { 255 | 256 | onLoad( scope.parse( arrayBuffer ) ); 257 | 258 | }, onProgress, onError ); 259 | 260 | }, 261 | 262 | setPath: function ( value ) { 263 | 264 | this.path = value; 265 | return this; 266 | 267 | }, 268 | 269 | parse: function ( arrayBuffer, offset ) { 270 | 271 | console.time( 'PRWMLoader' ); 272 | 273 | var data = decodePrwm( arrayBuffer, offset ), 274 | attributesKey = Object.keys( data.attributes ), 275 | bufferGeometry = new THREE.BufferGeometry(), 276 | attribute, 277 | i; 278 | 279 | for ( i = 0; i < attributesKey.length; i ++ ) { 280 | 281 | attribute = data.attributes[ attributesKey[ i ] ]; 282 | bufferGeometry.addAttribute( attributesKey[ i ], new THREE.BufferAttribute( attribute.values, attribute.cardinality, attribute.normalized ) ); 283 | 284 | } 285 | 286 | if ( data.indices !== null ) { 287 | 288 | bufferGeometry.setIndex( new THREE.BufferAttribute( data.indices, 1 ) ); 289 | 290 | } 291 | 292 | console.timeEnd( 'PRWMLoader' ); 293 | 294 | return bufferGeometry; 295 | 296 | } 297 | 298 | }; 299 | 300 | THREE.PRWMLoader.isBigEndianPlatform = function () { 301 | 302 | return isBigEndianPlatform(); 303 | 304 | }; 305 | 306 | } )( THREE ); 307 | -------------------------------------------------------------------------------- /implementations/three-prwm-loader/README.md: -------------------------------------------------------------------------------- 1 | # three-prwm-loader 2 | 3 | A PRWM loader for Three.js 4 | 5 | Packed Raw WebGL Model is a binary file format for nD geometries specifically designed for JavaScript and WebGL with a strong focus on fast parsing (from 1ms to 0.1ms in Chrome 59 on a MBP Late 2013). More information on this [here](https://github.com/kchapelier/PRWM). 6 | 7 | ## Installing 8 | 9 | With [npm](http://npmjs.org) do: 10 | 11 | ``` 12 | npm install three-prwm-loader --save 13 | ``` 14 | 15 | ## Example 16 | 17 | ```js 18 | var PRWMLoader = require('three-prwm-loader')(THREE); 19 | 20 | // instantiate a loader 21 | var loader = new PRWMLoader(); 22 | 23 | // load a resource 24 | loader.load('./models/mymodel.le.prwm', function onLoad (bufferGeometry) { 25 | var mesh = new THREE.Mesh(bufferGeometry, new THREE.MeshNormalMaterial()); 26 | 27 | // add mesh to scene, etc. 28 | // scene.add(mesh); 29 | // ... 30 | }); 31 | ``` 32 | 33 | [Online example](http://www.kchapelier.com/prwm/examples/three-prwm-loader.html) 34 | 35 | [Benchmark](http://www.kchapelier.com/prwm/examples/three-prwm-loader-benchmark.html) 36 | 37 | ## API 38 | 39 | ### new PRWMLoader([manager]) 40 | 41 | Instantiate a loader for PRWM files. 42 | 43 | **Arguments** 44 | 45 | * manager: An instance of [THREE.LoadingManager](https://threejs.org/docs/#api/loaders/managers/LoadingManager), will use THREE.DefaultLoadingManager by default. 46 | 47 | ### loader.parse(arrayBuffer [, offset]) 48 | 49 | Parse a PRWM file passed as an ArrayBuffer and directly return an instance of THREE.BufferGeometry. 50 | 51 | **Arguments** 52 | 53 | * arrayBuffer: ArrayBuffer containing the PRWM data. 54 | * offset: Offset (in bytes) at which the PRWM file content is located in the ArrayBuffer. Must be a multiple of 4. 55 | 56 | ### loader.load(url, onLoad [, onProgress[, onError]]) 57 | 58 | Parse a remote PRWM file and return an instance of THREE.BufferGeometry (through a callback). 59 | 60 | **Arguments** 61 | 62 | * url: Url of the PRWM file to load. Any `*` character will be replaced by `le` or `be` depending on the platform endianness. (see the [guidelines](https://github.com/kchapelier/PRWM/#guidelines)) 63 | * onLoad: Will be called when load completes. The argument will be the loaded BufferGeometry. 64 | * onProgress: Will be called while load progresses. The argument will be the XMLHttpRequest instance, which contains .total and .loaded bytes. 65 | * onError: Will be called when load errors. 66 | 67 | ### PRWMLoader.isBigEndianPlatform() 68 | 69 | Return true if the endianness of the platform is Big Endian. 70 | 71 | ## Changelog 72 | 73 | ### 1.1.1 (2019.04.07) : 74 | 75 | * Retrieve changes from Three.js repository, implement setPath. 76 | 77 | ### 1.1.0 (2017.12.26) : 78 | 79 | * Add `offset` argument in `parse()`. 80 | 81 | ### 1.0.0 (2017.10.22) : 82 | 83 | * First release on npm. 84 | 85 | ## License 86 | 87 | MIT 88 | -------------------------------------------------------------------------------- /implementations/three-prwm-loader/build/three-prwm-loader.min.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.PRWMLoaderWrapper=f()}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i>7&1),indicesType=flags>>6&1,bigEndian=(flags>>5&1)===1,attributesNumber=flags&31,valuesNumber=0,indicesNumber=0;if(bigEndian){valuesNumber=(array[2]<<16)+(array[3]<<8)+array[4];indicesNumber=(array[5]<<16)+(array[6]<<8)+array[7]}else{valuesNumber=array[2]+(array[3]<<8)+(array[4]<<16);indicesNumber=array[5]+(array[6]<<8)+(array[7]<<16)}if(offset/4%1!==0){throw new Error("PRWM decoder: Offset should be a multiple of 4, received "+offset)}if(version===0){throw new Error("PRWM decoder: Invalid format version: 0")}else if(version!==1){throw new Error("PRWM decoder: Unsupported format version: "+version)}if(!indexedGeometry){if(indicesType!==0){throw new Error("PRWM decoder: Indices type must be set to 0 for non-indexed geometries")}else if(indicesNumber!==0){throw new Error("PRWM decoder: Number of indices must be set to 0 for non-indexed geometries")}}var pos=8;var attributes={},attributeName,char,attributeType,cardinality,encodingType,arrayType,values,indices,i;for(i=0;i>7&1;cardinality=(flags>>4&3)+1;encodingType=flags&15;arrayType=InvertedEncodingTypes[encodingType];pos++;pos=Math.ceil(pos/4)*4;values=copyFromBuffer(buffer,arrayType,pos+offset,cardinality*valuesNumber,bigEndian);pos+=arrayType.BYTES_PER_ELEMENT*cardinality*valuesNumber;attributes[attributeName]={type:attributeType,cardinality:cardinality,values:values}}pos=Math.ceil(pos/4)*4;indices=null;if(indexedGeometry){indices=copyFromBuffer(buffer,indicesType===1?Uint32Array:Uint16Array,pos+offset,indicesNumber,bigEndian)}return{version:version,attributes:attributes,indices:indices}}var PRWMLoader=function PRWMLoader(manager){this.manager=manager!==undefined?manager:THREE.DefaultLoadingManager};PRWMLoader.prototype={constructor:THREE.PRWMLoader,load:function(url,onLoad,onProgress,onError){var scope=this;url=url.replace(/\*/g,isBigEndianPlatform()?"be":"le");var loader=new THREE.FileLoader(scope.manager);loader.setPath(scope.path);loader.setResponseType("arraybuffer");loader.load(url,function(arrayBuffer){onLoad(scope.parse(arrayBuffer))},onProgress,onError)},setPath:function(value){this.path=value;return this},parse:function(arrayBuffer,offset){var data=decodePrwm(arrayBuffer,offset),attributesKey=Object.keys(data.attributes),bufferGeometry=new THREE.BufferGeometry,attribute,i;for(i=0;i> 7 & 0x01 ), 83 | indicesType = flags >> 6 & 0x01, 84 | bigEndian = ( flags >> 5 & 0x01 ) === 1, 85 | attributesNumber = flags & 0x1F, 86 | valuesNumber = 0, 87 | indicesNumber = 0; 88 | 89 | if ( bigEndian ) { 90 | valuesNumber = ( array[ 2 ] << 16 ) + ( array[ 3 ] << 8 ) + array[ 4 ]; 91 | indicesNumber = ( array[ 5 ] << 16 ) + ( array[ 6 ] << 8 ) + array[ 7 ]; 92 | } else { 93 | valuesNumber = array[ 2 ] + ( array[ 3 ] << 8 ) + ( array[ 4 ] << 16 ); 94 | indicesNumber = array[ 5 ] + ( array[ 6 ] << 8 ) + ( array[ 7 ] << 16 ); 95 | } 96 | 97 | /** PRELIMINARY CHECKS **/ 98 | 99 | if ( offset / 4 % 1 !== 0 ) { 100 | throw new Error( 'PRWM decoder: Offset should be a multiple of 4, received ' + offset ); 101 | } 102 | 103 | if ( version === 0 ) { 104 | throw new Error( 'PRWM decoder: Invalid format version: 0' ); 105 | } else if ( version !== 1 ) { 106 | throw new Error( 'PRWM decoder: Unsupported format version: ' + version ); 107 | } 108 | 109 | if ( ! indexedGeometry ) { 110 | if ( indicesType !== 0 ) { 111 | throw new Error( 'PRWM decoder: Indices type must be set to 0 for non-indexed geometries' ); 112 | } else if ( indicesNumber !== 0 ) { 113 | throw new Error( 'PRWM decoder: Number of indices must be set to 0 for non-indexed geometries' ); 114 | } 115 | } 116 | 117 | /** PARSING **/ 118 | 119 | var pos = 8; 120 | 121 | var attributes = {}, 122 | attributeName, 123 | char, 124 | attributeType, 125 | cardinality, 126 | encodingType, 127 | arrayType, 128 | values, 129 | indices, 130 | i; 131 | 132 | for ( i = 0; i < attributesNumber; i ++ ) { 133 | attributeName = ''; 134 | 135 | while ( pos < array.length ) { 136 | char = array[ pos ]; 137 | pos ++; 138 | 139 | if ( char === 0 ) { 140 | break; 141 | } else { 142 | attributeName += String.fromCharCode( char ); 143 | } 144 | } 145 | 146 | flags = array[ pos ]; 147 | 148 | attributeType = flags >> 7 & 0x01; 149 | cardinality = ( flags >> 4 & 0x03 ) + 1; 150 | encodingType = flags & 0x0F; 151 | arrayType = InvertedEncodingTypes[ encodingType ]; 152 | 153 | pos ++; 154 | 155 | // padding to next multiple of 4 156 | pos = Math.ceil( pos / 4 ) * 4; 157 | 158 | values = copyFromBuffer( buffer, arrayType, pos + offset, cardinality * valuesNumber, bigEndian ); 159 | 160 | pos += arrayType.BYTES_PER_ELEMENT * cardinality * valuesNumber; 161 | 162 | attributes[ attributeName ] = { 163 | type: attributeType, 164 | cardinality: cardinality, 165 | values: values 166 | }; 167 | } 168 | 169 | pos = Math.ceil( pos / 4 ) * 4; 170 | 171 | indices = null; 172 | 173 | if ( indexedGeometry ) { 174 | indices = copyFromBuffer( 175 | buffer, 176 | indicesType === 1 ? Uint32Array : Uint16Array, 177 | pos + offset, 178 | indicesNumber, 179 | bigEndian 180 | ); 181 | } 182 | 183 | return { 184 | version: version, 185 | attributes: attributes, 186 | indices: indices 187 | }; 188 | } 189 | 190 | // Define the public interface 191 | 192 | var PRWMLoader = function PRWMLoader( manager ) { 193 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 194 | }; 195 | 196 | PRWMLoader.prototype = { 197 | constructor: THREE.PRWMLoader, 198 | 199 | load: function ( url, onLoad, onProgress, onError ) { 200 | var scope = this; 201 | 202 | url = url.replace( /\*/g, isBigEndianPlatform() ? 'be' : 'le' ); 203 | 204 | var loader = new THREE.FileLoader( scope.manager ); 205 | loader.setPath( scope.path ); 206 | loader.setResponseType( 'arraybuffer' ); 207 | 208 | loader.load( url, function ( arrayBuffer ) { 209 | onLoad( scope.parse( arrayBuffer ) ); 210 | }, onProgress, onError ); 211 | }, 212 | 213 | setPath: function ( value ) { 214 | this.path = value; 215 | return this; 216 | }, 217 | 218 | parse: function ( arrayBuffer, offset ) { 219 | var data = decodePrwm( arrayBuffer, offset ), 220 | attributesKey = Object.keys( data.attributes ), 221 | bufferGeometry = new THREE.BufferGeometry(), 222 | attribute, 223 | i; 224 | 225 | for ( i = 0; i < attributesKey.length; i ++ ) { 226 | attribute = data.attributes[ attributesKey[ i ] ]; 227 | bufferGeometry.addAttribute( attributesKey[ i ], new THREE.BufferAttribute( attribute.values, attribute.cardinality, attribute.normalized ) ); 228 | } 229 | 230 | if ( data.indices !== null ) { 231 | bufferGeometry.setIndex( new THREE.BufferAttribute( data.indices, 1 ) ); 232 | } 233 | 234 | return bufferGeometry; 235 | } 236 | }; 237 | 238 | PRWMLoader.isBigEndianPlatform = function () { 239 | return isBigEndianPlatform(); 240 | }; 241 | 242 | return PRWMLoader; 243 | 244 | }; 245 | -------------------------------------------------------------------------------- /implementations/three-prwm-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-prwm-loader", 3 | "version": "1.1.1", 4 | "description": "PRWM loader for Three.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "build-dev": "browserify ./index.js -d --s PRWMLoaderWrapper > ./build/three-prwm-loader.dev.js", 8 | "build-min": "browserify ./index.js --s PRWMLoaderWrapper | uglifyjs > ./build/three-prwm-loader.min.js" 9 | }, 10 | "files": [ 11 | "index.js" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/kchapelier/PRWM.git" 16 | }, 17 | "keywords": [ 18 | "three", 19 | "threejs", 20 | "three.js", 21 | "prwm" 22 | ], 23 | "author": "Kevin Chapelier", 24 | "license": "MIT", 25 | "readmeFilename": "README.md", 26 | "bugs": { 27 | "url": "https://github.com/kchapelier/PRWM/issues" 28 | }, 29 | "homepage": "https://github.com/kchapelier/PRWM", 30 | "dependencies": {} 31 | } 32 | -------------------------------------------------------------------------------- /legacy/vprb.md: -------------------------------------------------------------------------------- 1 | 2 | # Variable Precision Bundle (VPrB) Specifications (version 1 / draft 2017-04-22) 3 | 4 | * All numerical values are stored in big-endian byte order. 5 | 6 | The general structure of the file is the following : **One header** (1 bytes) followed by **one or more file blocks** (varying byte count). 7 | 8 | 9 | 10 | 11 | ## Header 12 | 13 | * **Version** : 1 byte (0 to 255) 14 | * **Reserved** : 1 bits (0 to 1) 15 | * **Number of files** : 7 bits (0 to 127) 16 | 17 | ### Version 18 | 19 | Indicates the version of the specification to apply while decoding this bundle. 20 | 21 | A value of 0 should be treated as an error by the decoder. 22 | 23 | ### Number of files 24 | 25 | Indicates the number of files included in the bundle. 26 | 27 | A value of 0 should be treated as an error by the decoder. 28 | 29 | 30 | 31 | 32 | ## File block 33 | 34 | ### File header 35 | 36 | * **File type** : 4 bits (0 to 15) 37 | * **Reserved** : 4 bits 38 | * **Name** : An ASCII encoded C-string (one byte per character, terminated with a NUL character) 39 | 40 | #### File type 41 | 42 | * **0** : VPRM file 43 | * **1 to 15** : Reserved 44 | 45 | ### File data 46 | 47 | The remaining of the file block is filled with the file content. 48 | -------------------------------------------------------------------------------- /legacy/vprm.md: -------------------------------------------------------------------------------- 1 | 2 | # Variable Precision Model (VPrM) Specifications (version 1 / draft 2017-05-14) 3 | 4 | * All numerical values are stored in big-endian byte order. 5 | * All signed integer values are stored in two's complement format. 6 | * All float values are stored following the IEEE 754 spec. 7 | * The format supports two main types of geometries : point clouds and triangle meshes. 8 | * The format is designed to allow any number of custom attributes and doesn't force the use of any pre-defined attributes. 9 | * The format doesn't support any type of bone-based animations, morphing is achievable with custom attributes. 10 | 11 | The general structure of the file is the following : **One header** (5 bytes) followed by **one or more attribute blocks** (varying byte count) followed by **up to one indices block** (varying byte count). 12 | 13 | 14 | 15 | 16 | ## Header 17 | 18 | * **Version** : 1 byte (0 to 255) 19 | * **Mesh type** : 1 bit 20 | * **Indexing scheme** : 1 bit 21 | * **Number of attributes per vertex** : 6 bits (0 to 63) 22 | * **Number of elements** : 3 bytes (0 to 16777215) 23 | 24 | ### Version 25 | 26 | Indicates the version of the specification to apply while decoding this model. 27 | 28 | A value of 0 should be treated as an error by the decoder. 29 | 30 | ### Mesh type 31 | 32 | * **0** : Point cloud 33 | * **1** : Triangle mesh 34 | 35 | ### Indexing scheme 36 | 37 | * **0** : One index per vertex 38 | * **1** : One index per attribute per vertex (similar to the scheme used in the Wavefront OBJ format) 39 | 40 | ### Number of attributes per vertex 41 | 42 | Indicates the number of attributes per vertex. 43 | 44 | A value of 0 should be treated as an error by the decoder. 45 | 46 | It should be noted that most OpenGL implementations are currently limited to 16 attributes per vertex. 47 | 48 | #### Number of elements 49 | 50 | Indicates the number of elements. The number of points in point cloud and the number of triangles in triangle mesh. 51 | 52 | 53 | 54 | 55 | ## Attribute block 56 | 57 | ### Attribute header 58 | 59 | * **Name** : An ASCII encoded C-string (one byte per character, terminated with a NUL character) 60 | * **Type** : 2 bits (0 to 3) 61 | * **Cardinality** : 2 bits (0 to 3) 62 | * **Encoding type** : 4 bits (0 to 15) 63 | * **Number of values** : 3 bytes (0 to 16.777.215) 64 | 65 | #### Type 66 | 67 | * **0** : Signed Integer 68 | * **1** : Unsigned integer 69 | * **2** : Single-precision float 70 | * **3** : Double-precision float 71 | 72 | #### Cardinality 73 | 74 | Indicates the number of elements per value. O is scalar, 1 is a 2d vector, 2 is a 3d vector and 3 a 4d vector. 75 | 76 | Some examples in OpenGL : 77 | 78 | * An attribute with a type of 2 and a cardinality of 0 would be mapped to a float 79 | * An attribute with a type of 0 and a cardinality of 1 would be mapped to a ivec2 80 | * An attribute with a type of 1 and a cardinality of 2 would be mapped to a uvec3 81 | * An attribute with a type of 3 and a cardinality of 3 would be mapped to a dvec4 82 | 83 | It should be noted that double-precision floats are not supported in GLSL ES. 84 | 85 | #### Encoding type 86 | 87 | This indicates how each elements are stored in the file and doesn't necessarily match the aforementioned type. 88 | 89 | * **0** : Reserved 90 | * **1** : Signed 32bit float (4 bytes) 91 | * **2** : Signed 64bit float (8 bytes) 92 | * **3** : Signed 8bit integer (1 byte) 93 | * **4** : Signed 16bit integer (2 bytes) 94 | * **5** : Signed 24bit integer (3 bytes) 95 | * **6** : Signed 32bit integer (4 bytes) 96 | * **7** : Unsigned 8bit integer (1 bytes) 97 | * **8** : Unsigned 16bit integer (2 bytes) 98 | * **9** : Unsigned 24bit integer (3 bytes) 99 | * **10** : Unsigned 32bit integer (4 bytes) 100 | * **11 to 15** : Reserved 101 | 102 | ### Attribute values 103 | 104 | The values of the attributes are encoded sequentially with the specified encoding type, making up the rest of the attribute block. 105 | 106 | While not directly encoded in the attribute header, the total length of the attribute values can be obtain as such : 107 | 108 | `Attribute values length = Cardinality * Number of values * Encoding type byte count` 109 | 110 | 111 | 112 | 113 | ## Indices block 114 | 115 | The content of this space is highly dependent on the `Mesh type` and `Indexing scheme`. 116 | 117 | The number of bits per index must be deduced from the number of values of each attributes as such : 118 | 119 | `Bits per index = max(0, ceil(log2(Number of values + 1)))` 120 | 121 | Any remaining/not-set bits in the last byte should be set to 0 by the encoder and should be ignored by the decoder. 122 | 123 | ### Point clouds with one index per vertex 124 | 125 | Each group of attributes are interpreted as a single point, no indices are needed. 126 | 127 | The presence of an Indexes block for point clouds with one index per vertex should be treated as an error by the decoder. 128 | 129 | ### Point clouds with one index per attribute per vertex 130 | 131 | The indexes are ordered by point and by attribute as such : 132 | 133 | ``` 134 | point1att1index, point1att2index, point2att1index, point2att2index, ... 135 | ``` 136 | 137 | ### Triangle mesh with one index per vertex 138 | 139 | The indexes are ordered by face and by vertex as such : 140 | 141 | ``` 142 | face1vert1index, face1vert2index, face1vert3index, face2vert1index, face2vert2index, face2vert3index, ... 143 | ``` 144 | 145 | ### Triangle mesh with one index per attribute per vertex 146 | 147 | The indexes are ordered by face, by vertex and by attribute as such : 148 | 149 | ``` 150 | face1vert1att1index, face1vert1att2index, face1vert2att1index, face1vert2att2index, face1vert3att1index, face1vert3att2index, 151 | face2vert1att1index, face2vert1att2index, face2vert2att1index, face2vert2att2index, face2vert3att1index, face2vert3att2index, ... 152 | ``` 153 | -------------------------------------------------------------------------------- /models/ascii-obj/unit-cube.obj: -------------------------------------------------------------------------------- 1 | # Unit-volume cube with the same texture coordinates on each face. 2 | # 3 | # Created by Morgan McGuire and released into the Public Domain on 4 | # July 16, 2011. 5 | # 6 | # http://graphics.cs.williams.edu/data 7 | # 8 | # Added comments - Scott Kuhl 9 | # Corrected face normals - Scott Kuhl 10 | # Renamed texture and material file - Scott Kuhl 11 | 12 | mtllib cube.mtl 13 | 14 | # Vertices 15 | v -0.5 0.5 -0.5 16 | v -0.5 0.5 0.5 17 | v 0.5 0.5 0.5 18 | v 0.5 0.5 -0.5 19 | v -0.5 -0.5 -0.5 20 | v -0.5 -0.5 0.5 21 | v 0.5 -0.5 0.5 22 | v 0.5 -0.5 -0.5 23 | 24 | # Texture coordinates 25 | vt 0 1 26 | vt 0 0 27 | vt 1 0 28 | vt 1 1 29 | 30 | # Normals 31 | vn -1 0 0 32 | vn 1 0 0 33 | vn 0 -1 0 34 | vn 0 1 0 35 | vn 0 0 -1 36 | vn 0 0 1 37 | 38 | 39 | g cube 40 | usemtl default 41 | 42 | # Faces (vertex/texcoord/normal) 43 | # -X face 44 | f 6/4/1 2/3/1 1/2/1 45 | f 6/4/1 1/2/1 5/1/1 46 | # +X face 47 | f 8/4/2 4/3/2 3/2/2 48 | f 8/4/2 3/2/2 7/1/2 49 | # -Y face 50 | f 8/4/3 7/3/3 6/2/3 51 | f 8/4/3 6/2/3 5/1/3 52 | # +Y face 53 | f 3/4/4 4/3/4 1/2/4 54 | f 3/4/4 1/2/4 2/1/4 55 | # -Z face 56 | f 5/4/5 1/3/5 4/2/5 57 | f 5/4/5 4/2/5 8/1/5 58 | # +Z face 59 | f 7/4/6 3/3/6 2/2/6 60 | f 7/4/6 2/2/6 6/1/6 61 | -------------------------------------------------------------------------------- /models/binary-fbx/faceted-nefertiti.fbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/binary-fbx/faceted-nefertiti.fbx -------------------------------------------------------------------------------- /models/json/cube.json: -------------------------------------------------------------------------------- 1 | { 2 | "vertices":[1,1,1,1,1,-1,1,-1,1,1,-1,-1,-1,1,-1,-1,1,1,-1,-1,-1,-1,-1,1,-1,1,-1,1,1,-1,-1,1,1,1,1,1,-1,-1,1,1,-1,1,-1,-1,-1,1,-1,-1,-1,1,1,1,1,1,-1,-1,1,1,-1,1,1,1,-1,-1,1,-1,1,-1,-1,-1,-1,-1], 3 | "normals":[1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1], 4 | "uvs":[0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0,0,1,1,1,0,0,1,0], 5 | "indices":[0,2,1,2,3,1,4,6,5,6,7,5,8,10,9,10,11,9,12,14,13,14,15,13,16,18,17,18,19,17,20,22,21,22,23,21] 6 | } 7 | -------------------------------------------------------------------------------- /models/prwm/bowtie-logo.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/bowtie-logo.be.prwm -------------------------------------------------------------------------------- /models/prwm/bowtie-logo.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/bowtie-logo.le.prwm -------------------------------------------------------------------------------- /models/prwm/cerberus.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/cerberus.be.prwm -------------------------------------------------------------------------------- /models/prwm/cerberus.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/cerberus.le.prwm -------------------------------------------------------------------------------- /models/prwm/cube.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/cube.be.prwm -------------------------------------------------------------------------------- /models/prwm/cube.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/cube.le.prwm -------------------------------------------------------------------------------- /models/prwm/faceted-nefertiti.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/faceted-nefertiti.be.prwm -------------------------------------------------------------------------------- /models/prwm/faceted-nefertiti.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/faceted-nefertiti.le.prwm -------------------------------------------------------------------------------- /models/prwm/github-logo.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/github-logo.be.prwm -------------------------------------------------------------------------------- /models/prwm/github-logo.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/github-logo.le.prwm -------------------------------------------------------------------------------- /models/prwm/nodejs-logo.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/nodejs-logo.be.prwm -------------------------------------------------------------------------------- /models/prwm/nodejs-logo.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/nodejs-logo.le.prwm -------------------------------------------------------------------------------- /models/prwm/smooth-nefertiti.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/smooth-nefertiti.be.prwm -------------------------------------------------------------------------------- /models/prwm/smooth-nefertiti.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/smooth-nefertiti.le.prwm -------------------------------------------------------------------------------- /models/prwm/smooth-suzanne.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/smooth-suzanne.be.prwm -------------------------------------------------------------------------------- /models/prwm/smooth-suzanne.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/smooth-suzanne.le.prwm -------------------------------------------------------------------------------- /models/prwm/stanford-bunny.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/stanford-bunny.be.prwm -------------------------------------------------------------------------------- /models/prwm/stanford-bunny.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/stanford-bunny.le.prwm -------------------------------------------------------------------------------- /models/prwm/stanford-dragon.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/stanford-dragon.be.prwm -------------------------------------------------------------------------------- /models/prwm/stanford-dragon.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/stanford-dragon.le.prwm -------------------------------------------------------------------------------- /models/prwm/stylus-logo.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/stylus-logo.be.prwm -------------------------------------------------------------------------------- /models/prwm/stylus-logo.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/stylus-logo.le.prwm -------------------------------------------------------------------------------- /models/prwm/vive-controller.be.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/vive-controller.be.prwm -------------------------------------------------------------------------------- /models/prwm/vive-controller.le.prwm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kchapelier/PRWM/b01efa78b5beb35bb94098e67755fef957ac0c49/models/prwm/vive-controller.le.prwm -------------------------------------------------------------------------------- /models/svg/bowtie.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /models/svg/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /models/svg/nodejs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /models/svg/stylus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /rebuild-all-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd implementations 4 | 5 | cd obj2prwm 6 | npm run build-dev 7 | npm run build-min 8 | cd .. 9 | 10 | cd picogl-prwm-loader 11 | npm run build-dev 12 | npm run build-min 13 | cd .. 14 | 15 | cd prwm 16 | npm run build-dev 17 | npm run build-min 18 | cd .. 19 | 20 | cd svg2prwm 21 | npm run build-dev 22 | npm run build-min 23 | cd .. 24 | 25 | cd three-buffergeometry-to-prwm 26 | npm run build-dev 27 | npm run build-min 28 | cd .. 29 | 30 | cd .. 31 | -------------------------------------------------------------------------------- /specifications/prwm.md: -------------------------------------------------------------------------------- 1 | # Packed Raw WebGL Model (PRWM) Specifications (version 1 / final 2017-06-09) 2 | 3 | ## Conventions 4 | 5 | * All signed integer values are stored in two's complement format. 6 | * All float values are stored following the IEEE 754 spec. 7 | * The endianness of the file is defined in its header. 8 | * The format supports indexed and non-indexed geometries. 9 | * The format is designed to allow any number of custom attributes and doesn't force the use of any pre-defined attributes. 10 | * The format doesn't support any type of bone-based animations, morphing is achievable with custom attributes. 11 | * The format prioritizes fast parsing over file weight. 12 | * The format is specifically designed for WebGL and will probably prove useless for any other platforms. 13 | 14 | The general structure of the file is the following : **One header** (8 bytes) followed by **one or more attribute 15 | blocks** (varying byte count) and, in the case of an indexed geometry, **up to one indices 16 | block** (varying byte count). 17 | 18 | ## Header 19 | 20 | * **Version** : 1 byte (0 to 255) 21 | * **Indexed geometry** : 1 bit 22 | * **Indices type** : 1 bit 23 | * **Endianness** : 1 bit 24 | * **Number of attributes per vertex** : 5 bits (0 to 31) 25 | * **Number of values per attribute** : 3 bytes (0 to 16777215) 26 | * **Number of indices** : 3 bytes (0 to 16777215) 27 | 28 | ### Version 29 | 30 | Indicates the version of the specification to apply while decoding this model. 31 | 32 | A value of 0 should be treated as an error by the decoder. 33 | 34 | ### Indexed Geometry 35 | 36 | * **0** : Non-indexed geometry 37 | * **1** : Indexed geometry 38 | 39 | ### Indices type 40 | 41 | * **0** : Unsigned 16bit integer (2 bytes) 42 | * **1** : Unsigned 32bit integer (4 bytes) 43 | 44 | This should be set to 0 for non-indexed geometries, a value of 1 should be treated as an error by the decoder. 45 | 46 | ### Endianness 47 | 48 | * **0** : Little Endian 49 | * **1** : Big Endian 50 | 51 | ### Number of attributes per vertex 52 | 53 | Indicates the number of attributes per vertex. 54 | 55 | A value of 0 should be treated as an error by the decoder. 56 | 57 | It should be noted that most WebGL implementations are currently limited to 16 attributes per vertex. 58 | 59 | ### Number of values per attribute 60 | 61 | Indicates the number of values per attribute. 62 | 63 | ### Number of indices 64 | 65 | The number of indices stored in the index block. 66 | 67 | This should be set to 0 for non-indexed geometries, any other value should be treated as an error by the decoder. 68 | 69 | 70 | 71 | 72 | ## Attribute block 73 | 74 | ### Attribute header 75 | 76 | * **Name** : An ASCII encoded C-string (one byte per character, terminated with a NUL character) 77 | * **Type** : 1 bit 78 | * **Normalized** : 1 bit 79 | * **Cardinality** : 2 bits (0 to 3) 80 | * **Encoding type** : 4 bits (0 to 15) 81 | * **Padding** : 0 to 3 bytes 82 | 83 | #### Type 84 | 85 | * **0** : Float (should declare the attribute with vertexAttribPointer) 86 | * **1** : Integer (should declare the attribute with vertexAttribIPointer, if supported) 87 | 88 | #### Normalized 89 | 90 | * **0** : The attribute is not to be normalized. 91 | * **1** : The attribute should be normalized (see [vertexAttribPointer](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer)). 92 | 93 | This only affects attributes encoded as integers (see below) and typed as floats. 94 | 95 | #### Cardinality 96 | 97 | Indicates the number of components per vertex attribute. 0 is scalar, 1 is a 2d vector, 2 is a 3d vector and 3 a 4d vector. 98 | 99 | #### Encoding type 100 | 101 | This indicates how each elements are stored in the file and doesn't necessarily match the aforementioned type. 102 | 103 | * **0** : Reserved 104 | * **1** : Signed 32bit float (4 bytes) 105 | * **2** : Reserved 106 | * **3** : Signed 8bit integer (1 byte) 107 | * **4** : Signed 16bit integer (2 bytes) 108 | * **5** : Reserved 109 | * **6** : Signed 32bit integer (4 bytes) 110 | * **7** : Unsigned 8bit integer (1 bytes) 111 | * **8** : Unsigned 16bit integer (2 bytes) 112 | * **9** : Reserved 113 | * **10** : Unsigned 32bit integer (4 bytes) 114 | * **11 to 15** : Reserved 115 | 116 | #### Padding 117 | 118 | The attribute header block is to be padded with 0 values in such a way as to make the attribute values block starts at 119 | a position which is a multiple of 4. For example, if the attribute values block would start at the position `0xF1` 120 | without any padding, three padding bytes should be added to the header block as to make it start at the position `0xF4`. 121 | 122 | ### Attribute values 123 | 124 | The values of the attributes are encoded sequentially with the specified encoding type, making up the rest of the attribute block. 125 | 126 | While not directly encoded in the attribute header, the total length of the attribute values can be obtained as such : 127 | 128 | `Attribute values length = Cardinality * Encoding type byte count * Number of values per attribute` 129 | 130 | 131 | 132 | 133 | ## Indices block 134 | 135 | This block should only be present for indexed geometries. 136 | 137 | It should start with some padding to align the values on position which is a multiple of 4, if needed. 138 | 139 | While not directly encoded in the block, the total length of the indices values can be obtained as such : 140 | 141 | `Indices values length = Number of indices * Indices type byte count` 142 | 143 | --------------------------------------------------------------------------------