@thi.ng/geom-io-obj 0.3.87 → 0.3.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/api.js +0 -1
- package/package.json +9 -6
- package/parser.js +163 -162
package/CHANGELOG.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/geom-io-obj",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.88",
|
|
4
4
|
"description": "Wavefront OBJ parser (& exporter soon)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,12 +35,13 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/errors": "^2.4.
|
|
38
|
-
"@thi.ng/vectors": "^7.8.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/errors": "^2.4.6",
|
|
40
|
+
"@thi.ng/vectors": "^7.8.9"
|
|
39
41
|
},
|
|
40
42
|
"devDependencies": {
|
|
41
43
|
"@microsoft/api-extractor": "^7.38.3",
|
|
44
|
+
"esbuild": "^0.19.8",
|
|
42
45
|
"rimraf": "^5.0.5",
|
|
43
46
|
"tools": "^0.0.1",
|
|
44
47
|
"typedoc": "^0.25.4",
|
|
@@ -82,5 +85,5 @@
|
|
|
82
85
|
"status": "alpha",
|
|
83
86
|
"year": 2016
|
|
84
87
|
},
|
|
85
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
86
89
|
}
|
package/parser.js
CHANGED
|
@@ -1,176 +1,177 @@
|
|
|
1
1
|
import { assert } from "@thi.ng/errors/assert";
|
|
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
|
-
break;
|
|
55
|
-
case 2:
|
|
56
|
-
opts.uvs && (face.uv = []);
|
|
57
|
-
for (let i = 1; i < n; i++) {
|
|
58
|
-
const f = line[i].split("/");
|
|
59
|
-
addID(face.v, f[0], nv);
|
|
60
|
-
face.uv && addID(face.uv, f[1], nuv);
|
|
61
|
-
}
|
|
62
|
-
break;
|
|
63
|
-
case 3:
|
|
64
|
-
opts.uvs && items[1].length && (face.uv = []);
|
|
65
|
-
opts.normals && items[2].length && (face.n = []);
|
|
66
|
-
for (let i = 1; i < n; i++) {
|
|
67
|
-
const f = line[i].split("/");
|
|
68
|
-
addID(face.v, f[0], nv);
|
|
69
|
-
face.uv && addID(face.uv, f[1], nuv);
|
|
70
|
-
face.n && addID(face.n, f[2], nn);
|
|
71
|
-
}
|
|
72
|
-
break;
|
|
73
|
-
default:
|
|
2
|
+
const parseOBJ = (src, opts) => {
|
|
3
|
+
opts = {
|
|
4
|
+
normals: true,
|
|
5
|
+
uvs: true,
|
|
6
|
+
objects: true,
|
|
7
|
+
groups: true,
|
|
8
|
+
comments: false,
|
|
9
|
+
tessellate: false,
|
|
10
|
+
...opts
|
|
11
|
+
};
|
|
12
|
+
const vertices = [];
|
|
13
|
+
const normals = [];
|
|
14
|
+
const uvs = [];
|
|
15
|
+
const result = {
|
|
16
|
+
vertices,
|
|
17
|
+
normals,
|
|
18
|
+
uvs,
|
|
19
|
+
objects: [],
|
|
20
|
+
mtlLibs: [],
|
|
21
|
+
comments: []
|
|
22
|
+
};
|
|
23
|
+
let faces;
|
|
24
|
+
let currGroup;
|
|
25
|
+
let nextID = 0;
|
|
26
|
+
const newGroup = (id, force = false) => {
|
|
27
|
+
id = id || `group-${nextID++}`;
|
|
28
|
+
(force || opts.groups) && result.objects[result.objects.length - 1].groups.push(
|
|
29
|
+
currGroup = {
|
|
30
|
+
id,
|
|
31
|
+
smooth: false,
|
|
32
|
+
faces: faces = [],
|
|
33
|
+
lines: [],
|
|
34
|
+
mtl: currGroup ? currGroup.mtl : void 0
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
const newObject = (id, force = false) => {
|
|
39
|
+
(force || opts.objects) && result.objects.push({ id: id || `object-${nextID++}`, groups: [] });
|
|
40
|
+
newGroup("default", force);
|
|
41
|
+
};
|
|
42
|
+
const readFace = (line) => {
|
|
43
|
+
const face = { v: [] };
|
|
44
|
+
const n = line.length;
|
|
45
|
+
const nv = vertices.length;
|
|
46
|
+
const nuv = uvs.length;
|
|
47
|
+
const nn = normals.length;
|
|
48
|
+
const items = line[1].split("/");
|
|
49
|
+
switch (items.length) {
|
|
50
|
+
case 1:
|
|
51
|
+
for (let i = 1; i < n; i++) {
|
|
52
|
+
addID(face.v, line[i], nv);
|
|
74
53
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
54
|
+
break;
|
|
55
|
+
case 2:
|
|
56
|
+
opts.uvs && (face.uv = []);
|
|
57
|
+
for (let i = 1; i < n; i++) {
|
|
58
|
+
const f = line[i].split("/");
|
|
59
|
+
addID(face.v, f[0], nv);
|
|
60
|
+
face.uv && addID(face.uv, f[1], nuv);
|
|
82
61
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (l[0] === "#") {
|
|
93
|
-
comments && result.comments.push(l.substring(1).trim());
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
const items = l.trim().split(/\s+/g);
|
|
97
|
-
const len = items.length;
|
|
98
|
-
switch (items[0]) {
|
|
99
|
-
case "v": {
|
|
100
|
-
assert(len > 3, `invalid vertex @ line ${i}`);
|
|
101
|
-
const v = readVec3(items);
|
|
102
|
-
vertices.push(xform ? xform(v) : v);
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
case "vn": {
|
|
106
|
-
assert(len > 3, `invalid normal @ line ${i}`);
|
|
107
|
-
const v = readVec3(items);
|
|
108
|
-
normals.push(xform ? xform(v) : v);
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
case "vt": {
|
|
112
|
-
assert(len > 2, `invalid uv @ line ${i}`);
|
|
113
|
-
const v = readVec2(items);
|
|
114
|
-
uvs.push(xformUV ? xformUV(v) : v);
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
case "f": {
|
|
118
|
-
assert(len > 3, `invalid face @ line ${i}`);
|
|
119
|
-
const f = readFace(items);
|
|
120
|
-
tessellate && f.v.length > 3
|
|
121
|
-
? faces.push(...tessellateFace(f))
|
|
122
|
-
: faces.push(f);
|
|
123
|
-
break;
|
|
124
|
-
}
|
|
125
|
-
case "l":
|
|
126
|
-
assert(len > 2, `invalid polyline @ line ${i}`);
|
|
127
|
-
currGroup.lines.push(readPolyLine(items));
|
|
128
|
-
break;
|
|
129
|
-
case "o":
|
|
130
|
-
opts.objects && newObject(items[1]);
|
|
131
|
-
break;
|
|
132
|
-
case "g":
|
|
133
|
-
opts.groups && newGroup(items[1]);
|
|
134
|
-
break;
|
|
135
|
-
case "s":
|
|
136
|
-
currGroup.smooth = items[1] !== "0" && items[1] !== "off";
|
|
137
|
-
break;
|
|
138
|
-
case "mtllib":
|
|
139
|
-
result.mtlLibs.push(items[1]);
|
|
140
|
-
break;
|
|
141
|
-
case "usemtl":
|
|
142
|
-
currGroup.mtl = items[1];
|
|
143
|
-
break;
|
|
144
|
-
default:
|
|
145
|
-
console.log(`ignoring token: ${items[0]} @ line ${i}`);
|
|
62
|
+
break;
|
|
63
|
+
case 3:
|
|
64
|
+
opts.uvs && items[1].length && (face.uv = []);
|
|
65
|
+
opts.normals && items[2].length && (face.n = []);
|
|
66
|
+
for (let i = 1; i < n; i++) {
|
|
67
|
+
const f = line[i].split("/");
|
|
68
|
+
addID(face.v, f[0], nv);
|
|
69
|
+
face.uv && addID(face.uv, f[1], nuv);
|
|
70
|
+
face.n && addID(face.n, f[2], nn);
|
|
146
71
|
}
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
}
|
|
75
|
+
return face;
|
|
76
|
+
};
|
|
77
|
+
const readPolyLine = (items) => {
|
|
78
|
+
const nv = vertices.length;
|
|
79
|
+
const verts = [];
|
|
80
|
+
for (let i = 1, n = items.length; i < n; i++) {
|
|
81
|
+
addID(verts, items[i], nv);
|
|
82
|
+
}
|
|
83
|
+
return verts;
|
|
84
|
+
};
|
|
85
|
+
newObject("default", true);
|
|
86
|
+
const { xform, xformUV, tessellate, comments } = opts;
|
|
87
|
+
const lines = src.split(/[\n\r]+/g);
|
|
88
|
+
for (let i = 0, n = lines.length; i < n; i++) {
|
|
89
|
+
const l = lines[i];
|
|
90
|
+
if (!l.length)
|
|
91
|
+
continue;
|
|
92
|
+
if (l[0] === "#") {
|
|
93
|
+
comments && result.comments.push(l.substring(1).trim());
|
|
94
|
+
continue;
|
|
147
95
|
}
|
|
148
|
-
|
|
96
|
+
const items = l.trim().split(/\s+/g);
|
|
97
|
+
const len = items.length;
|
|
98
|
+
switch (items[0]) {
|
|
99
|
+
case "v": {
|
|
100
|
+
assert(len > 3, `invalid vertex @ line ${i}`);
|
|
101
|
+
const v = readVec3(items);
|
|
102
|
+
vertices.push(xform ? xform(v) : v);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case "vn": {
|
|
106
|
+
assert(len > 3, `invalid normal @ line ${i}`);
|
|
107
|
+
const v = readVec3(items);
|
|
108
|
+
normals.push(xform ? xform(v) : v);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case "vt": {
|
|
112
|
+
assert(len > 2, `invalid uv @ line ${i}`);
|
|
113
|
+
const v = readVec2(items);
|
|
114
|
+
uvs.push(xformUV ? xformUV(v) : v);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case "f": {
|
|
118
|
+
assert(len > 3, `invalid face @ line ${i}`);
|
|
119
|
+
const f = readFace(items);
|
|
120
|
+
tessellate && f.v.length > 3 ? faces.push(...tessellateFace(f)) : faces.push(f);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case "l":
|
|
124
|
+
assert(len > 2, `invalid polyline @ line ${i}`);
|
|
125
|
+
currGroup.lines.push(readPolyLine(items));
|
|
126
|
+
break;
|
|
127
|
+
case "o":
|
|
128
|
+
opts.objects && newObject(items[1]);
|
|
129
|
+
break;
|
|
130
|
+
case "g":
|
|
131
|
+
opts.groups && newGroup(items[1]);
|
|
132
|
+
break;
|
|
133
|
+
case "s":
|
|
134
|
+
currGroup.smooth = items[1] !== "0" && items[1] !== "off";
|
|
135
|
+
break;
|
|
136
|
+
case "mtllib":
|
|
137
|
+
result.mtlLibs.push(items[1]);
|
|
138
|
+
break;
|
|
139
|
+
case "usemtl":
|
|
140
|
+
currGroup.mtl = items[1];
|
|
141
|
+
break;
|
|
142
|
+
default:
|
|
143
|
+
console.log(`ignoring token: ${items[0]} @ line ${i}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
149
147
|
};
|
|
150
148
|
const addID = (acc, x, num) => {
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
const v = parseInt(x);
|
|
150
|
+
acc.push(v < 0 ? v + num : v - 1);
|
|
153
151
|
};
|
|
154
152
|
const readVec2 = (items) => [
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
parseFloat(items[1]),
|
|
154
|
+
parseFloat(items[2])
|
|
157
155
|
];
|
|
158
156
|
const readVec3 = (items) => [
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
157
|
+
parseFloat(items[1]),
|
|
158
|
+
parseFloat(items[2]),
|
|
159
|
+
parseFloat(items[3])
|
|
162
160
|
];
|
|
163
161
|
const tessellateFace = (face) => {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
162
|
+
const { v, uv, n } = face;
|
|
163
|
+
const v0 = v[0];
|
|
164
|
+
const uv0 = uv && uv[0];
|
|
165
|
+
const n0 = n && n[0];
|
|
166
|
+
const acc = [];
|
|
167
|
+
for (let i = 1, num = v.length - 1; i < num; i++) {
|
|
168
|
+
const tri = { v: [v0, v[i], v[i + 1]] };
|
|
169
|
+
uv && (tri.uv = [uv0, uv[i], uv[i + 1]]);
|
|
170
|
+
n && (tri.n = [n0, n[i], n[i + 1]]);
|
|
171
|
+
acc.push(tri);
|
|
172
|
+
}
|
|
173
|
+
return acc;
|
|
174
|
+
};
|
|
175
|
+
export {
|
|
176
|
+
parseOBJ
|
|
176
177
|
};
|