diff --git a/ModelConverter.js b/ModelConverter.js new file mode 100644 index 0000000..b5b7834 --- /dev/null +++ b/ModelConverter.js @@ -0,0 +1,175 @@ +const fs = require('fs'); + +class Vertex { + constructor(x,y,z) { + this.x = x; + this.y = y; + this.z = z; + } + + fromArray(arr) { + this.x = arr[0]; + this.y = arr[1]; + this.z = arr[2]; + return this; + } +} +const Normal = Vertex; +const TexCoord = Vertex; + +class Face { + constructor(a,b,c,aTex,bTex,cTex,aNorm,bNorm,cNorm,mtl) { + this.a = a; + this.b = b; + this.c = c; + + this.aTex = aTex; + this.bTex = bTex; + this.cTex = cTex; + + this.aNorm = aNorm; + this.bNorm = bNorm; + this.cNorm = cNorm; + + this.mtl = mtl; + } + + fromArray(arr) { + this.a = arr[0]; + this.aTex = arr[1]; + this.aNorm = arr[2]; + + this.b = arr[3]; + this.bTex = arr[4]; + this.bNorm = arr[5]; + + this.c = arr[6]; + this.cTex = arr[7]; + this.cNorm = arr[8]; + ; + this.mtl = arr[9]; + return this; + } +} + + +class ModelParser { + + constructor() { + if (this.constructor === ModelParser) { + throw new Error("Can't instanciate abstract class ModelParser"); + } + this.vertices = []; + this.normals = []; + this.texCoords = []; + this.faces = []; + } + + addVertex(vertex) { + this.vertices.push(vertex); + } + + addTexCoord(texCoord) { + this.texCoords.push(texCoord); + } + + addNormal(normal) { + this.normals.push(normal); + } + + addFace(face) { + this.faces.push(face); + } + + parseLine(string) { + if (this.constructor === ModelParser) { + throw new Error("Can't call abstract method ModelParser.parseLine"); + } + } + + parseFile(path) { + fs + .readFileSync(path, 'utf-8') + .split('\n') + // .map((l) => l.slice(0,-1)) + .map((a) => this.parseLine(a)); + } + +} + +class OBJParser extends ModelParser { + + constructor() { + super(); + this.materials = []; + } + + parseLine(string) { + let split = string.split(' '); + + switch (split.shift()) { + case 'usemtl': this.currentMaterial = split[0]; break; + + case 'v': this.addVertex (new Vertex(). fromArray(split)); break; + case 'vn': this.addNormal (new Normal(). fromArray(split)); break; + case 'vt': this.addTexCoord(new TexCoord().fromArray(split)); break; + case 'f': this.addFace (new Face() .fromArray(split.map(s => s.split('/')).reduce((a,b) => a.concat(b), []))); + + } + } + +} + +class Exporter { + constructor(model) { + this.model = model; + } +} + +class OBJExporter extends Exporter { + constructor(model) { + super(model); + } + + toString() { + let string = ""; + + for (let vertex of this.model.vertices) { + string += "n " + [vertex.x, vertex.y, vertex.z].join(' ') + "\n"; + } + + string += "\n"; + + for (let texCoord of this.model.texCoords) { + string += "vt " + [texCoord.x, texCoord.y].join(' ') + "\n"; + } + + string += "\n" + + for (let normal of this.model.normals) { + string += "vn " + [normal.x, normal.y, normal.z].join(' ') + "\n"; + } + + string += "\n"; + + for (let face of this.model.faces) { + string += "f "; + string += ['a', 'b', 'c'].map(a => [face[a], face[a + 'Tex'], face[a + 'Norm']].join('/')).join(' '); + string += '\n'; + } + + return string; + } +} + +if (require.main === module) { + + let parser = new OBJParser(); + parser.parseFile('./examples/cube.obj'); + + let exporter = new OBJExporter(parser); + let string = exporter.toString(); + + console.log(string); + +} diff --git a/examples/cube.obj b/examples/cube.obj new file mode 100644 index 0000000..214d29f --- /dev/null +++ b/examples/cube.obj @@ -0,0 +1,33 @@ +v -0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 0.500000 -0.500000 +v 0.500000 0.500000 -0.500000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 + +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 + +vn 0.000000 0.000000 1.000000 +vn 0.000000 1.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 -1.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn -1.000000 0.000000 0.000000 + +f 1/1/1 2/2/1 3/3/1 +f 3/3/1 2/2/1 4/4/1 +f 3/1/2 4/2/2 5/3/2 +f 5/3/2 4/2/2 6/4/2 +f 5/4/3 6/3/3 7/2/3 +f 7/2/3 6/3/3 8/1/3 +f 7/1/4 8/2/4 1/3/4 +f 1/3/4 8/2/4 2/4/4 +f 2/1/5 8/2/5 4/3/5 +f 4/3/5 8/2/5 6/4/5 +f 7/1/6 1/2/6 5/3/6 +f 5/3/6 1/2/6 3/4/6