diff --git a/src/io/ifs.jl b/src/io/ifs.jl index d50a81b..fedc173 100644 --- a/src/io/ifs.jl +++ b/src/io/ifs.jl @@ -19,7 +19,7 @@ function load(fs::Stream{format"IFS"}; facetype=GLTriangleFace, pointtype=Point3 nfaces = read(io, UInt32) faces_int = read(io, UInt32, nfaces * 3) faces = reinterpret(facetype, faces_int) - return GeometryBasics.mesh(vertices = verts, faces = faces) + return GeometryBasics.Mesh(verts, faces) end function save(fs::Stream{format"IFS"}, msh::AbstractMesh; meshname = "mesh") diff --git a/src/io/obj.jl b/src/io/obj.jl index af25d59..66bf130 100644 --- a/src/io/obj.jl +++ b/src/io/obj.jl @@ -7,7 +7,8 @@ function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, pointtype=Point3f, normaltype=Vec3f, uvtype=Any) - points, v_normals, uv, faces = pointtype[], normaltype[], uvtype[], Any[] + points, v_normals, uv, faces = pointtype[], normaltype[], uvtype[], facetype[] + f_uv_n_faces = (faces, facetype[], facetype[]) for full_line in eachline(stream(io)) # read a line, remove newline and leading/trailing whitespaces @@ -41,22 +42,14 @@ 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.UVNormalFace.(pos_faces, uv_faces, normal_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 @@ -64,9 +57,13 @@ function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, end end - # 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) + if !isempty(f_uv_n_faces[2]) && (f_uv_n_faces[2] != faces) + uv = FaceView(uv, f_uv_n_faces[2]) + end + + if !isempty(f_uv_n_faces[3]) && (f_uv_n_faces[3] != faces) + v_normals = FaceView(v_normals, f_uv_n_faces[3]) + end return GeometryBasics.mesh( points, faces, facetype = facetype; @@ -92,6 +89,11 @@ function _typemax(::Type{OffsetInteger{O, T}}) where {O, T} end function save(f::Stream{format"OBJ"}, mesh::AbstractMesh) + # TODO: allow saving with faceviews (i.e. build the / or // syntax) + if any(v -> v isa FaceView, values(vertex_attributes(mesh))) + mesh = GeometryBasics.clear_faceviews(mesh) + end + io = stream(f) for p in decompose(Point3f, mesh) println(io, "v ", p[1], " ", p[2], " ", p[3]) diff --git a/test/runtests.jl b/test/runtests.jl index b3bf0b9..51db14c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,8 +3,6 @@ using Test const tf = joinpath(dirname(@__FILE__), "testfiles") using MeshIO -using GeometryBasics: GLNormalMesh - function test_face_indices(mesh) for face in faces(mesh) for index in face @@ -23,8 +21,8 @@ end Rect3f(Vec3f(baselen), Vec3f(baselen, dirlen, baselen)), Rect3f(Vec3f(baselen), Vec3f(baselen, baselen, dirlen)) ] - uvn_mesh = merge(map(uv_normal_mesh, mesh)) - mesh = merge(map(triangle_mesh, mesh)) + uvn_mesh = GeometryBasics.clear_faceviews(merge(map(uv_normal_mesh, mesh))) + mesh = GeometryBasics.clear_faceviews(merge(map(triangle_mesh, mesh))) empty!(uvn_mesh.views) empty!(mesh.views) @@ -70,7 +68,8 @@ end @test test_face_indices(msh) msh = load(joinpath(tf, "binary.stl")) - @test msh isa GLNormalMesh + @test msh isa Mesh{D, Float32, GLTriangleFace} where D + @test all(v -> v isa AbstractVector, values(vertex_attributes(msh))) @test length(faces(msh)) == 828 @test length(coordinates(msh)) == 2484 @test length(normals(msh)) == 2484 @@ -79,14 +78,16 @@ end mktempdir() do tmpdir save(File{format"STL_BINARY"}(joinpath(tmpdir, "test.stl")), msh) msh1 = load(joinpath(tmpdir, "test.stl")) - @test msh1 isa GLNormalMesh + @test msh1 isa Mesh{D, Float32, GLTriangleFace} where D + @test all(v -> v isa AbstractVector, values(vertex_attributes(msh1))) @test faces(msh) == faces(msh1) @test coordinates(msh) == coordinates(msh1) @test normals(msh) == normals(msh1) end msh = load(joinpath(tf, "binary_stl_from_solidworks.STL")) - @test msh isa GLNormalMesh + @test msh isa Mesh{D, Float32, GLTriangleFace} where D + @test all(v -> v isa AbstractVector, values(vertex_attributes(msh))) @test length(faces(msh)) == 12 @test length(coordinates(msh)) == 36 @test test_face_indices(msh) @@ -139,8 +140,9 @@ end @testset "OBJ" begin msh = load(joinpath(tf, "test.obj")) @test length(faces(msh)) == 3954 - @test length(coordinates(msh)) == 2519 - @test length(normals(msh)) == 2519 + @test length(coordinates(msh)) == 2248 + @test length(normals(msh)) == 2240 + @test length(texturecoordinates(msh)) == 2220 @test test_face_indices(msh) msh = load(joinpath(tf, "cube.obj")) # quads