Wednesday February 5, 2025
Home
|
Contact
|
Support
|
OBJ 3D File Formats The bits and bytes...
>>
OBJ 3D File Formats
The bits and bytes...
OBJ File Format (.obj)
The `obj` file format is a text based format.
Example of what the file format looks like in a text editor:
Obj format overview
(
triangle only obj file
)
.
obj file
:
mtllib tree
.
mtl
o Icosphere
v 0.089624 1.419387 0.052847
vt 0.818181 0.000000
vn 0.5499 0.7413 0.3847
usemtl Material.001
f 1
/
1
/
1 14
/
2
/
1 13
/
3
/
1
.
mtl file
:
newmtl Material.001
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.217401 0.800000 0.176586
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
Example obj file loader (in Javascript)
The following `obj` file loader is in a single file. Also converts non-triangle (quad) mesh elements to triangles.
Output for the example OBJ Loader - loads in a 3d model of a Stadium
readObj
= function(
txt
)
{
console
.
log
(
'readObj...'
);
let objects
= [];
let materials
= [];
objects
.
push
( {
'name'
:
'test'
,
'v'
:[],
'vt'
:[],
'vn'
:[],
'f'
:[],
'usemtl'
:
''
} );
let data
= {
'v'
:[],
'n'
:[],
'f'
:[],
'm'
:[] };
txt
=
txt
.
replaceAll
(
' '
,
' '
);
let lines
=
txt
.
split
(
'\n'
);
console
.
log
(
'num lines:'
,
lines
.
length
);
let curmat
= -
1
;
for (
let i
=
0
;
i
<
lines
.
length
;
i
++)
{
let line
=
lines
[
i
];
line
=
line
.
trim
()
if (
line
.
length
<
2
) continue;
if (
line
[
0
] ==
'#'
) continue;
let parts
=
line
.
split
(
' '
);
if (
parts
.
length
<
1
) continue;
try{
switch(
parts
[
0
] )
{
case
'usemtl'
:
{
let matname
=
parts
[
1
];
if ( !
materials
.
includes
(
matname
) )
{
materials
.
push
(
matname
);
}
curmat
=
materials
.
indexOf
(
matname
);
//console.log( 'curmat:', curmat );
}
break;
case
'v'
:
// v 0.089624 1.419387 0.052847
{
objects
.
reverse
()[
0
].
v
.
push
(
parts
[
1
] );
objects
.
reverse
()[
0
].
v
.
push
(
parts
[
2
] );
objects
.
reverse
()[
0
].
v
.
push
(
parts
[
3
] );
}
break;
case
'vt'
:
// vt 0.818181 0.000000
{
objects
.
reverse
()[
0
].
vt
.
push
(
parts
[
1
] );
objects
.
reverse
()[
0
].
vt
.
push
(
parts
[
2
] );
}
break;
case
'vn'
:
// vn 0.5499 0.7413 0.3847
{
objects
.
reverse
()[
0
].
vn
.
push
(
parts
[
1
] );
objects
.
reverse
()[
0
].
vn
.
push
(
parts
[
2
] );
objects
.
reverse
()[
0
].
vn
.
push
(
parts
[
3
] );
}
break;
case
'f'
:
// f 1/1/1 14/2/1 13/3/1
{
// f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...
// f v1//vn1 v2//vn2 v3//vn3 ... (double slashes)
/*
If you have 4 indices, e.g.:
0 1 2 3
The division into two triangles would be one with the first 3 indices, and one with the first, third, and fourth. In this example:
0 1 2
0 2 3
Let's try some ASCII art to illustrate this:
3-------2
| /|
| / |
| / |
|/ |
0-------1
*/
let i0
=
parts
[
1
].
split
(
'/'
)[
0
]-
1
;
let i1
=
parts
[
2
].
split
(
'/'
)[
0
]-
1
;
let i2
=
parts
[
3
].
split
(
'/'
)[
0
]-
1
;
let i3
=
parts
.
length
==
4
?
undefined
: (
parts
[
4
].
split
(
'/'
)[
0
]-
1
);
let n0
=
parts
[
1
].
split
(
'/'
)[
2
]-
1
;
let n1
=
parts
[
2
].
split
(
'/'
)[
2
]-
1
;
let n2
=
parts
[
3
].
split
(
'/'
)[
2
]-
1
;
let n3
=
parts
.
length
==
4
?
undefined
: (
parts
[
4
].
split
(
'/'
)[
2
]-
1
);
if (
parts
[
1
].
includes
(
'//'
) )
{
n0
=
parts
[
1
].
split
(
'//'
)[
1
]-
1
;
n1
=
parts
[
2
].
split
(
'//'
)[
1
]-
1
;
n2
=
parts
[
3
].
split
(
'//'
)[
1
]-
1
;
n3
=
parts
.
length
==
4
?
undefined
: (
parts
[
4
].
split
(
'//'
)[
1
]-
1
);
}
if (
i3
==
undefined
)
// triangles
{
let v
= []
for (
let g
=
0
;
g
<
3
;
g
++)
{
let idx
= [
i0
,
i1
,
i2
][
g
];
let x0
=
objects
.
reverse
()[
0
].
v
[ (
3
*
idx
)+
0
];
let y0
=
objects
.
reverse
()[
0
].
v
[ (
3
*
idx
)+
1
];
let z0
=
objects
.
reverse
()[
0
].
v
[ (
3
*
idx
)+
2
];
v
.
push
( {
x
:
x0
,
y
:
y0
,
z
:
z0
} );
}
data
.
v
.
push
(
v
[
0
].
x
);
data
.
v
.
push
(
v
[
0
].
y
);
data
.
v
.
push
(
v
[
0
].
z
);
data
.
v
.
push
(
v
[
1
].
x
);
data
.
v
.
push
(
v
[
1
].
y
);
data
.
v
.
push
(
v
[
1
].
z
);
data
.
v
.
push
(
v
[
2
].
x
);
data
.
v
.
push
(
v
[
2
].
y
);
data
.
v
.
push
(
v
[
2
].
z
);
data
.
m
.
push
(
curmat
);
data
.
m
.
push
(
curmat
);
data
.
m
.
push
(
curmat
);
data
.
f
.
push
(
data
.
f
.
length
);
data
.
f
.
push
(
data
.
f
.
length
);
data
.
f
.
push
(
data
.
f
.
length
);
let n
= [];
for (
let g
=
0
;
g
<[
n0
,
n1
,
n2
].
length
;
g
++)
{
let nx0
=
objects
.
reverse
()[
0
].
vn
[
3
*[
n0
,
n1
,
n2
][
g
]+
0
];
let ny0
=
objects
.
reverse
()[
0
].
vn
[
3
*[
n0
,
n1
,
n2
][
g
]+
1
];
let nz0
=
objects
.
reverse
()[
0
].
vn
[
3
*[
n0
,
n1
,
n2
][
g
]+
2
];
n
.
push
( {
x
:
nx0
,
y
:
ny0
,
z
:
nz0
} );
}
data
.
n
.
push
(
n
[
0
].
x
);
data
.
n
.
push
(
n
[
0
].
y
);
data
.
n
.
push
(
n
[
0
].
z
);
data
.
n
.
push
(
n
[
1
].
x
);
data
.
n
.
push
(
n
[
1
].
y
);
data
.
n
.
push
(
n
[
1
].
z
);
data
.
n
.
push
(
n
[
2
].
x
);
data
.
n
.
push
(
n
[
2
].
y
);
data
.
n
.
push
(
n
[
2
].
z
);
}
else
// quads
{
let v
= [];
for (
let g
=
0
;
g
<
4
;
g
++)
{
let idx
= [
i0
,
i1
,
i2
,
i3
][
g
];
let x0
=
objects
.
reverse
()[
0
].
v
[ (
3
*
idx
)+
0
];
let y0
=
objects
.
reverse
()[
0
].
v
[ (
3
*
idx
)+
1
];
let z0
=
objects
.
reverse
()[
0
].
v
[ (
3
*
idx
)+
2
];
v
.
push
( {
x
:
x0
,
y
:
y0
,
z
:
z0
} );
}
data
.
v
.
push
(
v
[
0
].
x
);
data
.
v
.
push
(
v
[
0
].
y
);
data
.
v
.
push
(
v
[
0
].
z
);
data
.
v
.
push
(
v
[
1
].
x
);
data
.
v
.
push
(
v
[
1
].
y
);
data
.
v
.
push
(
v
[
1
].
z
);
data
.
v
.
push
(
v
[
2
].
x
);
data
.
v
.
push
(
v
[
2
].
y
);
data
.
v
.
push
(
v
[
2
].
z
);
data
.
m
.
push
(
curmat
);
data
.
m
.
push
(
curmat
);
data
.
m
.
push
(
curmat
);
data
.
f
.
push
(
data
.
f
.
length
);
data
.
f
.
push
(
data
.
f
.
length
);
data
.
f
.
push
(
data
.
f
.
length
);
data
.
v
.
push
(
v
[
0
].
x
);
data
.
v
.
push
(
v
[
0
].
y
);
data
.
v
.
push
(
v
[
0
].
z
);
data
.
v
.
push
(
v
[
2
].
x
);
data
.
v
.
push
(
v
[
2
].
y
);
data
.
v
.
push
(
v
[
2
].
z
);
data
.
v
.
push
(
v
[
3
].
x
);
data
.
v
.
push
(
v
[
3
].
y
);
data
.
v
.
push
(
v
[
3
].
z
);
data
.
m
.
push
(
curmat
);
data
.
m
.
push
(
curmat
);
data
.
m
.
push
(
curmat
);
data
.
f
.
push
(
data
.
f
.
length
);
data
.
f
.
push
(
data
.
f
.
length
);
data
.
f
.
push
(
data
.
f
.
length
);
let n
= [];
for (
let g
=
0
;
g
<
4
;
g
++)
{
let idx
= [
n0
,
n1
,
n2
,
n3
][
g
];
let nx0
=
objects
.
reverse
()[
0
].
vn
[ (
3
*
idx
)+
0
];
let ny0
=
objects
.
reverse
()[
0
].
vn
[ (
3
*
idx
)+
1
];
let nz0
=
objects
.
reverse
()[
0
].
vn
[ (
3
*
idx
)+
2
];
n
.
push
( {
x
:
nx0
,
y
:
ny0
,
z
:
nz0
} );
}
data
.
n
.
push
(
n
[
0
].
x
);
data
.
n
.
push
(
n
[
0
].
y
);
data
.
n
.
push
(
n
[
0
].
z
);
data
.
n
.
push
(
n
[
1
].
x
);
data
.
n
.
push
(
n
[
1
].
y
);
data
.
n
.
push
(
n
[
1
].
z
);
data
.
n
.
push
(
n
[
2
].
x
);
data
.
n
.
push
(
n
[
2
].
y
);
data
.
n
.
push
(
n
[
2
].
z
);
data
.
n
.
push
(
n
[
0
].
x
);
data
.
n
.
push
(
n
[
0
].
y
);
data
.
n
.
push
(
n
[
0
].
z
);
data
.
n
.
push
(
n
[
2
].
x
);
data
.
n
.
push
(
n
[
2
].
y
);
data
.
n
.
push
(
n
[
2
].
z
);
data
.
n
.
push
(
n
[
3
].
x
);
data
.
n
.
push
(
n
[
3
].
y
);
data
.
n
.
push
(
n
[
3
].
z
);
}
}
break;
}
// end switch(..)
}catch(
e
){ }
}
// for
console
.
log
(
'dump:'
,
data
.
m
.
slice
(
0
,
20
) );
return [
data
];
}
// readObj(..)
Resources and Examples
• WebGPU Example [
LINK
]
• Notebook `obj` to `json` Example [
https://notebook.xbdev.net/index.php?page=objtojson&
]
Advert (Support Website)
Visitor:
Copyright (c) 2002-2025 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.