Skip to content

Commit a0b47e8

Browse files
committed
Geom.ParseObj is a new function that will parse a Wavefront OBJ file into model data that can be consumed by the Mesh Game Object.
1 parent bd2c56e commit a0b47e8

2 files changed

Lines changed: 303 additions & 1 deletion

File tree

src/geom/ParseObj.js

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
/**
2+
* @author Richard Davey <rich@photonstorm.com>
3+
* @copyright 2020 Photon Storm Ltd.
4+
* @license {@link https://opensource.org/licenses/MIT|MIT License}
5+
*/
6+
7+
var flip = true;
8+
9+
var defaultModelName = 'untitled';
10+
var currentGroup = '';
11+
var currentMaterial = '';
12+
13+
/**
14+
* @ignore
15+
*/
16+
function stripComments (line)
17+
{
18+
var idx = line.indexOf('#');
19+
20+
return (idx > -1) ? line.substring(0, idx) : line;
21+
}
22+
23+
/**
24+
* @ignore
25+
*/
26+
function currentModel (result)
27+
{
28+
if (result.models.length === 0)
29+
{
30+
result.models.push({
31+
faces: [],
32+
name: defaultModelName,
33+
textureCoords: [],
34+
vertexNormals: [],
35+
vertices: []
36+
});
37+
}
38+
39+
currentGroup = '';
40+
41+
return result.models[result.models.length - 1];
42+
}
43+
44+
/**
45+
* @ignore
46+
*/
47+
function parseObject (lineItems, result)
48+
{
49+
var modelName = lineItems.length >= 2 ? lineItems[1] : defaultModelName;
50+
51+
result.models.push({
52+
faces: [],
53+
name: modelName,
54+
textureCoords: [],
55+
vertexNormals: [],
56+
vertices: []
57+
});
58+
59+
currentGroup = '';
60+
}
61+
62+
/**
63+
* @ignore
64+
*/
65+
function parseGroup (lineItems)
66+
{
67+
if (lineItems.length === 2)
68+
{
69+
currentGroup = lineItems[1];
70+
}
71+
}
72+
73+
/**
74+
* @ignore
75+
*/
76+
function parseVertexCoords (lineItems, result)
77+
{
78+
var len = lineItems.length;
79+
80+
var x = (len >= 2) ? parseFloat(lineItems[1]) : 0;
81+
var y = (len >= 3) ? parseFloat(lineItems[2]) : 0;
82+
var z = (len >= 4) ? parseFloat(lineItems[3]) : 0;
83+
84+
currentModel(result).vertices.push({ x: x, y: y, z: z });
85+
}
86+
87+
/**
88+
* @ignore
89+
*/
90+
function parseTextureCoords (lineItems, result)
91+
{
92+
var len = lineItems.length;
93+
94+
var u = (len >= 2) ? parseFloat(lineItems[1]) : 0;
95+
var v = (len >= 3) ? parseFloat(lineItems[2]) : 0;
96+
var w = (len >= 4) ? parseFloat(lineItems[3]) : 0;
97+
98+
if (isNaN(u))
99+
{
100+
u = 0;
101+
}
102+
103+
if (isNaN(v))
104+
{
105+
v = 0;
106+
}
107+
108+
if (isNaN(w))
109+
{
110+
w = 0;
111+
}
112+
113+
if (flip)
114+
{
115+
v = 1 - v;
116+
}
117+
118+
currentModel(result).textureCoords.push({ u: u, v: v, w: w });
119+
}
120+
121+
/**
122+
* @ignore
123+
*/
124+
function parseVertexNormal (lineItems, result)
125+
{
126+
var len = lineItems.length;
127+
128+
var x = (len >= 2) ? parseFloat(lineItems[1]) : 0;
129+
var y = (len >= 3) ? parseFloat(lineItems[2]) : 0;
130+
var z = (len >= 4) ? parseFloat(lineItems[3]) : 0;
131+
132+
currentModel(result).vertexNormals.push({ x: x, y: y, z: z });
133+
}
134+
135+
/**
136+
* @ignore
137+
*/
138+
function parsePolygon (lineItems, result)
139+
{
140+
var totalVertices = lineItems.length - 1;
141+
142+
if (totalVertices < 3)
143+
{
144+
return;
145+
}
146+
147+
var face = {
148+
group: currentGroup,
149+
material: currentMaterial,
150+
vertices: []
151+
};
152+
153+
for (var i = 0; i < totalVertices; i++)
154+
{
155+
var vertexString = lineItems[i + 1];
156+
var vertexValues = vertexString.split('/');
157+
var vvLen = vertexValues.length;
158+
159+
if (vvLen < 1 || vvLen > 3)
160+
{
161+
continue;
162+
}
163+
164+
var vertexIndex = 0;
165+
var textureCoordsIndex = 0;
166+
var vertexNormalIndex = 0;
167+
168+
vertexIndex = parseInt(vertexValues[0], 10);
169+
170+
if (vvLen > 1 && vertexValues[1] !== '')
171+
{
172+
textureCoordsIndex = parseInt(vertexValues[1], 10);
173+
}
174+
175+
if (vvLen > 2)
176+
{
177+
vertexNormalIndex = parseInt(vertexValues[2], 10);
178+
}
179+
180+
if (vertexIndex !== 0)
181+
{
182+
// Negative vertex indices refer to the nth last defined vertex
183+
// convert these to postive indices for simplicity
184+
if (vertexIndex < 0)
185+
{
186+
vertexIndex = currentModel(result).vertices.length + 1 + vertexIndex;
187+
}
188+
189+
textureCoordsIndex -= 1;
190+
vertexIndex -= 1;
191+
vertexNormalIndex -= 1;
192+
193+
face.vertices.push({
194+
textureCoordsIndex: textureCoordsIndex,
195+
vertexIndex: vertexIndex,
196+
vertexNormalIndex: vertexNormalIndex
197+
});
198+
}
199+
}
200+
201+
currentModel(result).faces.push(face);
202+
}
203+
204+
/**
205+
* @ignore
206+
*/
207+
function parseMtlLib (lineItems, result)
208+
{
209+
if (lineItems.length >= 2)
210+
{
211+
result.materialLibraries.push(lineItems[1]);
212+
}
213+
}
214+
215+
/**
216+
* @ignore
217+
*/
218+
function parseUseMtl (lineItems)
219+
{
220+
if (lineItems.length >= 2)
221+
{
222+
currentMaterial = lineItems[1];
223+
}
224+
}
225+
226+
/**
227+
* Parses a Wavefront OBJ File, extracting the models from it and returning them
228+
* in an array.
229+
*
230+
* @function Phaser.Geom.ParseObj
231+
* @since 3.50.0
232+
*
233+
* @param {string} data - The OBJ File data as a raw string.
234+
* @param {boolean} [flipUV=true] -
235+
*
236+
* @return {array} An array of model data.
237+
*/
238+
var ParseObj = function (data, flipUV)
239+
{
240+
if (flipUV === undefined) { flipUV = true; }
241+
242+
flip = flipUV;
243+
244+
// Store results in here
245+
var result = {
246+
materialLibraries: [],
247+
models: []
248+
};
249+
250+
currentGroup = '';
251+
currentMaterial = '';
252+
253+
var lines = data.split('\n');
254+
255+
for (var i = 0; i < lines.length; i++)
256+
{
257+
var line = stripComments(lines[i]);
258+
259+
var lineItems = line.replace(/\s\s+/g, ' ').trim().split(' ');
260+
261+
switch (lineItems[0].toLowerCase())
262+
{
263+
case 'o':
264+
// Start A New Model
265+
parseObject(lineItems, result);
266+
break;
267+
case 'g':
268+
// Start a new polygon group
269+
parseGroup(lineItems);
270+
break;
271+
case 'v':
272+
// Define a vertex for the current model
273+
parseVertexCoords(lineItems, result);
274+
break;
275+
case 'vt':
276+
// Texture Coords
277+
parseTextureCoords(lineItems, result);
278+
break;
279+
case 'vn':
280+
// Define a vertex normal for the current model
281+
parseVertexNormal(lineItems, result);
282+
break;
283+
case 'f':
284+
// Define a Face/Polygon
285+
parsePolygon(lineItems, result);
286+
break;
287+
case 'mtllib':
288+
// Reference to a material library file (.mtl)
289+
parseMtlLib(lineItems, result);
290+
break;
291+
case 'usemtl':
292+
// Sets the current material to be applied to polygons defined from this point forward
293+
parseUseMtl(lineItems);
294+
break;
295+
}
296+
}
297+
298+
return result;
299+
};
300+
301+
module.exports = ParseObj;

src/geom/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ var Extend = require('../utils/object/Extend');
1212
*/
1313

1414
var Geom = {
15-
15+
1616
Circle: require('./circle'),
1717
Ellipse: require('./ellipse'),
1818
Intersects: require('./intersects'),
1919
Line: require('./line'),
20+
ParseObj: require('./ParseObj'),
2021
Point: require('./point'),
2122
Polygon: require('./polygon'),
2223
Rectangle: require('./rectangle'),

0 commit comments

Comments
 (0)