diff --git a/src/io/gts.jl b/src/io/gts.jl index 9c3c33b..88c421e 100644 --- a/src/io/gts.jl +++ b/src/io/gts.jl @@ -12,15 +12,13 @@ function parseGtsLine( s::AbstractString, C, T=eltype(C) ) end end -function load( st::Stream{format"GTS"}, MeshType=GLNormalMesh ) +function load( st::Stream{format"GTS"}; facetype=GLTriangleFace, pointtype=Point) io = stream(st) head = readline( io ) - FT = facetype(MeshType) - VT = vertextype(MeshType) nVertices, nEdges, nFacets = parseGtsLine( head, Tuple{Int,Int,Int} ) iV = iE = iF = 1 - vertices = Vector{VT}(undef, nVertices) + vertices = Vector{pointtype}(undef, nVertices) edges = Vector{Vector{Int}}(undef, nEdges) facets = Vector{Vector{Int}}(undef, nFacets) for full_line::String in eachline(io) @@ -30,7 +28,7 @@ function load( st::Stream{format"GTS"}, MeshType=GLNormalMesh ) if !startswith(line, "#") && !isempty(line) && !all(iscntrl, line) #ignore comments if iV <= nVertices - vertices[iV] = parseGtsLine( line, VT ) + vertices[iV] = parseGtsLine( line, pointtype ) iV += 1 elseif iV > nVertices && iE <= nEdges edges[iE] = parseGtsLine( line, Array{Int} ) @@ -41,8 +39,8 @@ function load( st::Stream{format"GTS"}, MeshType=GLNormalMesh ) end # if end # if end # for - faces = [ FT( union( edges[facets[i][1]], edges[facets[i][2]], edges[facets[i][3]] ) ) for i in 1:length(facets) ] # orientation not guaranteed - return MeshType( vertices, faces ) + faces = [ facetype( union( edges[facets[i][1]], edges[facets[i][2]], edges[facets[i][3]] ) ) for i in 1:length(facets) ] # orientation not guaranteed + return Mesh( vertices, faces ) end function save( st::Stream{format"GTS"}, mesh::AbstractMesh ) diff --git a/src/io/ifs.jl b/src/io/ifs.jl index 2a6c67d..d50a81b 100644 --- a/src/io/ifs.jl +++ b/src/io/ifs.jl @@ -1,4 +1,4 @@ -function load(fs::Stream{format"IFS"}, MeshType = GLNormalMesh) +function load(fs::Stream{format"IFS"}; facetype=GLTriangleFace, pointtype=Point3f) io = stream(fs) function str() n = read(io, UInt32) @@ -11,15 +11,15 @@ function load(fs::Stream{format"IFS"}, MeshType = GLNormalMesh) end nverts = read(io, UInt32) verts_float = read(io, Float32, nverts * 3) - verts = reinterpret(Point3f0, verts_float) + verts = reinterpret(pointtype, verts_float) tris = str() if tris != "TRIANGLES\0" error("$(filename(fs)) does not seem to be of format IFS") end nfaces = read(io, UInt32) faces_int = read(io, UInt32, nfaces * 3) - faces = reinterpret(GLTriangle, faces_int) - MeshType(vertices = verts, faces = faces) + faces = reinterpret(facetype, faces_int) + return GeometryBasics.mesh(vertices = verts, faces = faces) end function save(fs::Stream{format"IFS"}, msh::AbstractMesh; meshname = "mesh") @@ -29,8 +29,8 @@ function save(fs::Stream{format"IFS"}, msh::AbstractMesh; meshname = "mesh") write(io, UInt32(length(s0))) write(io, s0) end - vts = decompose(Point3f0, msh) - fcs = decompose(GLTriangle, msh) + vts = decompose(Point3f, msh) + fcs = decompose(GLTriangleFace, msh) # write the header write0str("IFS") diff --git a/src/io/obj.jl b/src/io/obj.jl index 4101bf3..af25d59 100644 --- a/src/io/obj.jl +++ b/src/io/obj.jl @@ -5,11 +5,10 @@ ############################## function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, - pointtype=Point3f, normaltype=Vec3f, uvtype=Vec2f) + pointtype=Point3f, normaltype=Vec3f, uvtype=Any) points, v_normals, uv, faces = pointtype[], normaltype[], uvtype[], Any[] - last_command = "" - attrib_type = nothing + for full_line in eachline(stream(io)) # read a line, remove newline and leading/trailing whitespaces line = strip(chomp(full_line)) @@ -54,7 +53,7 @@ function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, 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)) + append!(faces, GeometryBasics.UVNormalFace.(pos_faces, uv_faces, normal_faces)) end else append!(faces, triangulated_faces(facetype, lines)) @@ -65,22 +64,15 @@ function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace, end end - vertex_attributes = Dict{Symbol, Any}() - - # TODO: add GeometryBasics convenience for dropping nothing vertex attributes? - if !isempty(v_normals) - vertex_attributes[:normal] = v_normals - end - - if !isempty(uv) - vertex_attributes[:uv] = uv - 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) - return GeometryBasics.mesh(points, faces, facetype = facetype; vertex_attributes...) + return GeometryBasics.mesh( + points, faces, facetype = facetype; + uv = isempty(uv) ? nothing : uv, + normal = isempty(v_normals) ? nothing : v_normals + ) end # of form "faces v1 v2 v3 ...."" @@ -111,7 +103,7 @@ function save(f::Stream{format"OBJ"}, mesh::AbstractMesh) end end - if hasproperty(mesh, :normals) + if hasproperty(mesh, :normal) for n in decompose_normals(mesh) println(io, "vn ", n[1], " ", n[2], " ", n[3]) end diff --git a/src/io/ply.jl b/src/io/ply.jl index f4a537e..d44f116 100644 --- a/src/io/ply.jl +++ b/src/io/ply.jl @@ -121,7 +121,7 @@ function load(fs::Stream{format"PLY_ASCII"}; facetype=GLTriangleFace, pointtype= end end if has_normals - return Mesh(meta(points; normals=point_normals), faces) + return Mesh(points, faces; normal = point_normals) else return Mesh(points, faces) end @@ -194,7 +194,7 @@ function load(fs::Stream{format"PLY_BINARY"}; facetype=GLTriangleFace, pointtype end if has_normals - return Mesh(meta(points; normals=point_normals), faces) + return Mesh(points, faces; normal = point_normals) else return Mesh(points, faces) end diff --git a/test/runtests.jl b/test/runtests.jl index 70ae35b..b3bf0b9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,8 @@ 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,6 +25,8 @@ end ] uvn_mesh = merge(map(uv_normal_mesh, mesh)) mesh = merge(map(triangle_mesh, mesh)) + empty!(uvn_mesh.views) + empty!(mesh.views) mktempdir() do tmpdir @@ -54,6 +58,8 @@ end @test mesh_loaded == uvn_mesh end end + + @testset "Real world files" begin @testset "STL" begin @@ -67,7 +73,7 @@ end @test msh isa GLNormalMesh @test length(faces(msh)) == 828 @test length(coordinates(msh)) == 2484 - @test length(msh.normals) == 2484 + @test length(normals(msh)) == 2484 @test test_face_indices(msh) mktempdir() do tmpdir @@ -76,7 +82,7 @@ end @test msh1 isa GLNormalMesh @test faces(msh) == faces(msh1) @test coordinates(msh) == coordinates(msh1) - @test msh.normals == msh1.normals + @test normals(msh) == normals(msh1) end msh = load(joinpath(tf, "binary_stl_from_solidworks.STL")) @@ -172,29 +178,9 @@ end end @testset "GTS" begin # TODO: FileIO upstream - #msh = load(joinpath(tf, "sphere5.gts")) - #@test typeof(msh) == GLNormalMesh - #test_face_indices(msh) - end - - @testset "Index remapping" begin - pos_faces = GLTriangleFace[(5, 6, 7), (5, 6, 8), (5, 7, 8)] - normal_faces = GLTriangleFace[(5, 6, 7), (3, 6, 8), (5, 7, 8)] - uv_faces = GLTriangleFace[(1, 2, 3), (4, 2, 5), (1, 3, 1)] - - # unique combinations -> new indices - # 551 662 773 534 885 881 1 2 3 4 5 6 (or 0..5 with 0 based indices) - faces, maps = MeshIO.merge_vertex_attribute_indices(pos_faces, normal_faces, uv_faces) - - @test length(faces) == 3 - @test faces == GLTriangleFace[(1, 2, 3), (4, 2, 5), (1, 3, 6)] - - # maps are structured as map[new_index] = old_index, so they grab the - # first/second/third index of the unique combinations above - # maps = (pos_map, normal_map, uv_map) - @test maps[1] == [5, 6, 7, 5, 8, 8] - @test maps[2] == [5, 6, 7, 3, 8, 8] - @test maps[3] == [1, 2, 3, 4, 5, 1] + # msh = load(joinpath(tf, "sphere5.gts")) + # @test typeof(msh) == GLNormalMesh + # test_face_indices(msh) end end end