diff --git a/src/MeshIO.jl b/src/MeshIO.jl index 0f50e29..01d0c24 100644 --- a/src/MeshIO.jl +++ b/src/MeshIO.jl @@ -9,8 +9,6 @@ using FileIO: FileIO, @format_str, Stream, File, stream, skipmagic import Base.show -include("util.jl") - include("io/off.jl") include("io/ply.jl") include("io/stl.jl") diff --git a/src/io/obj.jl b/src/io/obj.jl index 5f48873..4101bf3 100644 --- a/src/io/obj.jl +++ b/src/io/obj.jl @@ -5,10 +5,9 @@ ############################## function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, - pointtype=Point3f, normaltype=Vec3f, uvtype=Any) + pointtype=Point3f, normaltype=Vec3f, uvtype=Vec2f) - points, v_normals, uv, faces = pointtype[], normaltype[], uvtype[], facetype[] - f_uv_n_faces = (faces, facetype[], facetype[]) + points, v_normals, uv, faces = pointtype[], normaltype[], uvtype[], Any[] last_command = "" attrib_type = nothing for full_line in eachline(stream(io)) @@ -43,14 +42,22 @@ function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, elseif "f" == command # mesh always has faces if any(x-> occursin("//", x), lines) fs = process_face_normal(lines) + pos_faces = triangulated_faces(facetype, getindex.(fs, 1)) + normal_faces = triangulated_faces(facetype, getindex.(fs, 2)) + append!(faces, GeometryBasics.NormalFace.(pos_faces, normal_faces)) + elseif any(x-> occursin("/", x), lines) fs = process_face_uv_or_normal(lines) + pos_faces = triangulated_faces(facetype, getindex.(fs, 1)) + uv_faces = triangulated_faces(facetype, getindex.(fs, 2)) + if length(fs[1]) == 2 + append!(faces, GeometryBasics.UVFace.(pos_faces, uv_faces)) + else + normal_faces = triangulated_faces(facetype, getindex.(fs, 3)) + append!(faces, GeometryBasics.NormalUVFace.(pos_faces, normal_faces, uv_faces)) + end else append!(faces, triangulated_faces(facetype, lines)) - continue - end - for i = 1:length(first(fs)) - append!(f_uv_n_faces[i], triangulated_faces(facetype, getindex.(fs, i))) end else #TODO @@ -58,40 +65,22 @@ function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, end end - point_attributes = Dict{Symbol, Any}() - non_empty_faces = filtertuple(!isempty, f_uv_n_faces) - - # Do we have faces with different indices for positions and normals - # (and texture coordinates) per vertex? - if length(non_empty_faces) > 1 - - # map vertices with distinct indices for possition and normal (and uv) - # to new indices, updating faces along the way - faces, attrib_maps = merge_vertex_attribute_indices(non_empty_faces) - - # Update order of vertex attributes - points = points[attrib_maps[1]] - counter = 2 - if !isempty(uv) - point_attributes[:uv] = uv[attrib_maps[counter]] - counter += 1 - end - if !isempty(v_normals) - point_attributes[:normals] = v_normals[attrib_maps[counter]] - end + vertex_attributes = Dict{Symbol, Any}() - else # we have vertex indexing - no need to remap + # TODO: add GeometryBasics convenience for dropping nothing vertex attributes? + if !isempty(v_normals) + vertex_attributes[:normal] = v_normals + end - if !isempty(v_normals) - point_attributes[:normals] = v_normals - end - if !isempty(uv) - point_attributes[:uv] = uv - end - + if !isempty(uv) + vertex_attributes[:uv] = uv end - return Mesh(meta(points; point_attributes...), faces) + # TODO: Can we avoid this conversion? + # Also, is it safe to do? Or can an obj file define different face types for different groups? + faces = convert(Vector{typeof(first(faces))}, faces) + + return GeometryBasics.mesh(points, faces, facetype = facetype; vertex_attributes...) end # of form "faces v1 v2 v3 ...."" diff --git a/src/io/stl.jl b/src/io/stl.jl index 6dc838e..d40ee98 100644 --- a/src/io/stl.jl +++ b/src/io/stl.jl @@ -84,7 +84,7 @@ function load(fs::Stream{format"STL_BINARY"}; facetype=GLTriangleFace, i += 1 end - return Mesh(meta(vertices; normals=normals), faces) + return Mesh(vertices, faces; normal = normals) end @@ -127,5 +127,5 @@ function load(fs::Stream{format"STL_ASCII"}; facetype=GLTriangleFace, push!(faces, TriangleFace{Int}(vert_idx...)) end end - return Mesh(meta(points; normals=normals), faces) + return Mesh(points, faces; normal = normals) end diff --git a/src/util.jl b/src/util.jl deleted file mode 100644 index 1c24a8f..0000000 --- a/src/util.jl +++ /dev/null @@ -1,56 +0,0 @@ -# Graphics backends like OpenGL only have one index buffer so the indices to -# positions, normals and texture coordinates cannot be different. E.g. a face -# cannot use positional indices (1, 2, 3) and normal indices (1, 1, 2). In that -# case we need to remap normals such that new_normals[1, 2, 3] = normals[[1, 1, 2]] - - -# ... -_typemin(x) = typemin(x) -_typemin(::Type{OffsetInteger{N, T}}) where {N, T} = typemin(T) - N - -merge_vertex_attribute_indices(faces...) = merge_vertex_attribute_indices(faces) - -function merge_vertex_attribute_indices(faces::Tuple) - FaceType = eltype(faces[1]) - IndexType = eltype(FaceType) - D = length(faces) - N = length(faces[1]) - - # (pos_idx, normal_idx, uv_idx, ...) -> new_idx - vertex_index_map = Dict{NTuple{D, UInt32}, IndexType}() - # faces after remapping (0 based assumed) - new_faces = sizehint!(FaceType[], N) - temp = IndexType[] # keeping track of vertex indices of a face - counter = _typemin(IndexType) - # for remaping attribs, i.e. `new_attrib = old_attrib[index2vertex[attrib_index]]` - index2vertex = ntuple(_ -> sizehint!(UInt32[], N), D) - - for i in eachindex(faces[1]) - # (pos_faces[i], normal_faces[i], uv_faces[i], ...) - attrib_faces = getindex.(faces, i) - empty!(temp) - - for j in eachindex(attrib_faces[1]) - # (pos_index, normal_idx, uv_idx, ...) - # = (pos_faces[i][j], normal_faces[i][j], uv_faces[i][j], ...) - vertex = GeometryBasics.value.(getindex.(attrib_faces, j)) # 1 based - - # if combination of indices in vertex is new, make a new index - if !haskey(vertex_index_map, vertex) - vertex_index_map[vertex] = counter - counter = IndexType(counter + 1) - push!.(index2vertex, vertex) - end - - # keep track of the (new) index for this vertex - push!(temp, vertex_index_map[vertex]) - end - - # store face with new indices - push!(new_faces, FaceType(temp...)) - end - - sizehint!(new_faces, length(new_faces)) - - return new_faces, index2vertex -end \ No newline at end of file