diff --git a/.gitignore b/.gitignore index 24f06e4..6a2bf47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,30 +1,90 @@ -# ---> Node -# Logs -logs +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: *.log -npm-debug.log* +local_settings.py -# Runtime data -pids -*.pid -*.seed +# Flask stuff: +instance/ +.webassets-cache -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +# Scrapy stuff: +.scrapy -# Coverage directory used by tools like istanbul -coverage +# Sphinx documentation +docs/_build/ -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt +# PyBuilder +target/ -# node-waf configuration -.lock-wscript +# Jupyter Notebook +.ipynb_checkpoints -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release +# pyenv +.python-version -# Dependency directory -# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git -node_modules +# celery beat schedule file +celerybeat-schedule +# dotenv +.env + +# virtualenv +.venv/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject diff --git a/ModelConverter.js b/ModelConverter.js deleted file mode 100644 index b5b7834..0000000 --- a/ModelConverter.js +++ /dev/null @@ -1,175 +0,0 @@ -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/ModelConverter.py b/ModelConverter.py new file mode 100755 index 0000000..83c0228 --- /dev/null +++ b/ModelConverter.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 + +import sys +from functools import reduce + +class Vertex: + def __init__(self, x = None, y = None, z = None): + self.x = x + self.y = y + self.z = z + + def from_array(self, arr): + self.x = arr[0] if len(arr) > 0 else None + self.y = arr[1] if len(arr) > 1 else None + self.z = arr[2] if len(arr) > 2 else None + return self + +Normal = Vertex +TexCoord = Vertex + +class FaceVertex: + def __init__(self, vertex = None, texture = None, normal = None): + self.vertex = vertex + self.texture = texture + self.normal = normal + + def from_array(self, arr): + self.vertex = arr[0] if len(arr) > 0 else None + self.texture = arr[1] if len(arr) > 1 else None + self.normal = arr[2] if len(arr) > 2 else None + return self + +class Face: + def __init__(self, a = None, b = None, c = None, mtl = None): + self.a = a + self.b = b + self.c = c + self.mtl = mtl + + # Expects array of array + def from_array(self, arr): + self.a = FaceVertex().from_array(arr[0]) + self.b = FaceVertex().from_array(arr[1]) + self.c = FaceVertex().from_array(arr[2]) + return self + +class ModelParser: + + def __init__(self): + self.vertices = [] + self.normals = [] + self.tex_coords = [] + self.faces = [] + + def add_vertex(self, vertex): + self.vertices.append(vertex) + + def add_tex_coord(self, tex_coord): + self.tex_coords.append(tex_coord) + + def add_normal(self, normal): + self.normals.append(normal) + + def addFace(self, face): + self.faces.append(face) + + def parse_line(self, string): + pass + + def parse_file(self, path): + with open(path) as f: + for line in f.readlines(): + line = line.rstrip() + self.parse_line(line) + +class OBJParser(ModelParser): + + def __init__(self): + super().__init__() + self.materials = [] + + def parse_line(self, string): + split = string.split(' ') + first = split[0] + split = split[1:] + + if first == 'usemtl': + self.currentMaterial = split[0] + elif first == 'v': + self.add_vertex(Vertex().from_array(split)) + elif first == 'vn': + self.add_normal(Normal().from_array(split)) + elif first == 'vt': + self.add_tex_coord(TexCoord().from_array(split)) + elif first == 'f': + splits = list(map(lambda x: x.split('/'), split)) + self.addFace(Face().from_array(splits)) + +class Exporter: + def __init__(self, model): + self.model = model + +class OBJExporter(Exporter): + def __init__(self, model): + super().__init__(model) + + def __str__(self): + str = "" + + for vertex in self.model.vertices: + str += "n " + ' '.join([vertex.x, vertex.y, vertex.z]) + "\n" + + str += "\n" + + for tex_coord in self.model.tex_coords: + str += "vt " + ' '.join([tex_coord.x, tex_coord.y]) + "\n" + + str += "\n" + + for normal in self.model.normals: + str += "vn " + ' '.join([normal.x, normal.y, normal.z]) + "\n" + + str += "\n" + + for face in self.model.faces: + str += "f " + arr = [] + for v in [face.a, face.b, face.c]: + sub_arr = [] + sub_arr.append(v.vertex) + if v.normal is None: + if v.texture is not None: + sub_arr.append('') + sub_arr.append(v.texture) + elif v.texture is not None: + sub_arr.append(v.texture) + if v.normal is not None: + sub_arr.append(v.normal) + arr.append('/'.join(sub_arr)) + + str += ' '.join(arr) + '\n' + + return str + + +if __name__ == '__main__': + + parser = OBJParser() + parser.parse_file('./examples/cube.obj') + + exporter = OBJExporter(parser) + str = str(exporter) + + print(str) + +