-
Notifications
You must be signed in to change notification settings - Fork 0
/
obj2json.html
146 lines (141 loc) · 5.58 KB
/
obj2json.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<title>Convert OBJ to JSON</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<script>
/**
* Converts the contents of an obj file to a dictionary
* containing the "vertices," "indices," and "texels"
* and "normals" as lists. This is a generator and will
* continue to yield new objects until the end of the file.
* @param {string} obj_text
*/
async function* parseObj(obj_text) {
let verts = [];
let texels = [];
let inds = [];
let textureFile = null;
let mtlFile = null;
let indexOffset = 1; // obj indices start at 1
for (let line of obj_text.split('\n')) {
// remove comments and break up on whitespace
let data = line.split('#', 1)[0].trim().split(/\s+/);
// skip empty lines
if (data.length === 0) { continue }
switch (data[0]) {
case 'mtllib':
// sets the corresponding mtl file that will be used for all obj's textures
mtlFile = data[1];
break;
case "usemtl":
let useMtl = data[1];
await fetch(mtlFile)
.then(response => response.text())
.then(text => {
let record = false;
for (let l of text.split('\n')) {
if (l.endsWith(useMtl)) {
record = true;
}
if (l.startsWith("map_Kd") && record) {
// reverse split on '/' and take the last element
let texture = l.split('/');
textureFile = texture.pop();
record = false;
break;
}
}
});
break;
case 'o':
// marks the start of a new object
if (verts.length > 0) {
yield {"vertices": verts, "indices": inds, "texels": texels, "texture": textureFile};
indexOffset += verts.length/3;
verts = [];
texels = [];
inds = [];
}
break;
case 'v':
// vertex line
if (data.length !== 4)
throw `All vertices must be 3D, vertex ${verts.length/3} is ${data.length-1}D`;
verts.push(...data.slice(1).map(x => +x));
break;
case 'vt':
// texture coordinates
if (data.length !== 3)
throw `All texture coordinates must be 2D, texel ${texels.length/2} is ${data.length-1}D`;
texels.push(...data.slice(1).map(x => +x));
break;
case 'f':
// face line
if (data.length !== 4)
throw `All faces must be triangles, face ${inds.length/3} has ${data.length-1} vertices`;
inds.push(...data.slice(1).map(x => parseInt(x.split('/', 1)[0])-indexOffset));
break;
default:
// all other lines are ignored
console.debug('ignoring', line);
}
}
// return the results
yield {"vertices": verts, "indices": inds, "texels": texels, "texture": textureFile};
}
/** Save ('download') a text file with the given filename. */
function save(text, filename) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
/** Get a filename without the extension. */
function filenameWithoutExt(filename) {
let dot = filename.lastIndexOf(".");
return dot > 0 ? filename.substr(0, dot) : filename;
}
/** Convert an OBJ file 2 a JSON file. */
function objFile2json(file) {
// Get the new file name (remove extension and use .json)
filename = `${filenameWithoutExt(file.name)}.json`;
let zip = new JSZip();
// Read the file as text
file.text().then(async (data) => {
// Process OBJ data, convert to JSON, and save
// save(JSON.stringify(obj2dict(data)), filename);
const parseObjGen = parseObj(data);
let i = 0;
while (true) {
let obj = await parseObjGen.next();
if (obj.done) { break; }
console.log(i)
const filename = `${filenameWithoutExt(file.name)}_${i++}.json`
// save(JSON.stringify(obj.value), `${filenameWithoutExt(file.name)}_${i++}.json`);
zip.file(filename, JSON.stringify(obj.value));
}
zip.generateAsync({type:"blob"})
.then(function(content) {
// see FileSaver.js
saveAs(content, `${filenameWithoutExt(file.name)}.zip`);
});
}).catch(alert);
}
</script>
</head>
<body>
<h1>Convert OBJ to JSON</h1>
<div>
<input type="file" id="file">
<input type="button" value="Convert!"
onclick="objFile2json(document.getElementById('file').files[0])">
</div>
</body>
</html>