Merge branch 'python' of tforgione/model-converter into master

This commit is contained in:
tforgione 2016-11-21 13:32:53 +00:00 committed by Gogs
commit 35f8dd75ec
3 changed files with 237 additions and 196 deletions

102
.gitignore vendored
View File

@ -1,30 +1,90 @@
# ---> Node # Byte-compiled / optimized / DLL files
# Logs __pycache__/
logs *.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 *.log
npm-debug.log* local_settings.py
# Runtime data # Flask stuff:
pids instance/
*.pid .webassets-cache
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover # Scrapy stuff:
lib-cov .scrapy
# Coverage directory used by tools like istanbul # Sphinx documentation
coverage docs/_build/
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) # PyBuilder
.grunt target/
# node-waf configuration # Jupyter Notebook
.lock-wscript .ipynb_checkpoints
# Compiled binary addons (http://nodejs.org/api/addons.html) # pyenv
build/Release .python-version
# Dependency directory # celery beat schedule file
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git celerybeat-schedule
node_modules
# dotenv
.env
# virtualenv
.venv/
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject

View File

@ -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);
}

156
ModelConverter.py Executable file
View File

@ -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)