-
Notifications
You must be signed in to change notification settings - Fork 31
/
obj.jl
105 lines (100 loc) · 3.94 KB
/
obj.jl
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
##############################
#
# obj-Files
#
##############################
function load{MT <: AbstractMesh}(io::Stream{format"OBJ"}, MeshType::Type{MT} = GLNormalMesh)
io = stream(io)
lineNumber = 1
Tv,Tn,Tuv,Tf = vertextype(MT), normaltype(MT), texturecoordinatetype(MT), facetype(MT)
v,n,uv,f = Tv[], Tn[], Tuv[], Tf[]
f_uv_n_faces = (f, GLTriangle[], GLTriangle[])
last_command = ""
attrib_type = nothing
for line in eachline(io)
# read a line, remove newline and leading/trailing whitespaces
line = strip(chomp(line))
!isascii(line) && error("non valid ascii in obj")
if !startswith(line, "#") && !isempty(line) && !all(iscntrl, line) #ignore comments
lines = split(line)
command = shift!(lines) #first is the command, rest the data
if "v" == command # mesh always has vertices
push!(v, Point{3, Float32}(parse.(Float32, lines))) # should automatically convert to the right type in vertices(mesh)
elseif "vn" == command && hasnormals(MT)
push!(n, Normal{3, Float32}(parse.(Float32, lines)))
elseif "vt" == command && hastexturecoordinates(MT)
if length(lines) == 2
push!(uv, UV{Float32}(parse.(Float32, lines)))
elseif length(lines) == 3
push!(uv, UVW{Float32}(parse.(Float32, lines)))
else
error("Unknown UVW coordinate: $lines")
end
elseif "f" == command #mesh always has faces
if any(x->contains(x, "//"), lines)
fs = process_face_normal(lines)
elseif any(x->contains(x, "/"), lines)
fs = process_face_uv_or_normal(lines)
else
push!(f, triangulated_faces(Tf, lines)...)
continue
end
for i = 1:length(first(fs))
push!(f_uv_n_faces[i], triangulated_faces(Tf, getindex.(fs, i))...)
end
else
#TODO
end
end
# read next line
lineNumber += 1
end
attributes = Dict{Symbol, Any}()
!isempty(f) && (attributes[:faces] = f)
!isempty(v) && (attributes[:vertices] = v)
if !isempty(n)
n = if !isempty(f_uv_n_faces[3])
_n = similar(v, eltype(n))
for (vf, nf) in zip(f, f_uv_n_faces[3])
for (vidx, nidx) in zip(vf, nf)
_n[vidx] = n[nidx]
end
end
_n
else
# these are not per vertex normals, which we
# can't deal with at the moment
if length(v) != length(n)
normals(v, f, Tn)
else
n
end
end
attributes[:normals] = n
end
if !isempty(uv)
uv = if !isempty(f_uv_n_faces[2])
_uv = similar(v, eltype(uv))
for (vf, uvf) in zip(f, f_uv_n_faces[2])
for (vidx, uvidx) in zip(vf, uvf)
_uv[vidx] = uv[uvidx]
end
end
_uv
else
uv
end
attributes[:texturecoordinates] = uv
end
return MT(GeometryTypes.homogenousmesh(attributes))
end
# of form "f v1 v2 v3 ....""
process_face{S <: AbstractString}(lines::Vector{S}) = (lines,) # just put it in the same format as the others
# of form "f v1//vn1 v2//vn2 v3//vn3 ..."
process_face_normal{S <: AbstractString}(lines::Vector{S}) = split.(lines, "//")
# of form "f v1/vt1 v2/vt2 v3/vt3 ..." or of form "f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3 ...."
process_face_uv_or_normal{S <: AbstractString}(lines::Vector{S}) = split.(lines, '/')
function triangulated_faces{Tf}(::Type{Tf}, vertex_indices::Vector{<:AbstractString})
poly_face = Face{length(vertex_indices), UInt32}(parse.(UInt32, vertex_indices))
decompose(Tf, poly_face)
end