Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update MeshIO for GeometryBasics refactor #97

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/MeshIO.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
12 changes: 5 additions & 7 deletions src/io/gts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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} )
Expand All @@ -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 )
Expand Down
12 changes: 6 additions & 6 deletions src/io/ifs.jl
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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(verts, faces)
end

function save(fs::Stream{format"IFS"}, msh::AbstractMesh; meshname = "mesh")
Expand All @@ -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")
Expand Down
55 changes: 19 additions & 36 deletions src/io/obj.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
##############################

function load(io::Stream{format"OBJ"}; facetype=GLTriangleFace,
pointtype=Point3f, normaltype=Vec3f, uvtype=Any)
pointtype=Point3f, normaltype=Vec3f, uvtype=Any)

points, v_normals, uv, faces = pointtype[], normaltype[], uvtype[], facetype[]
f_uv_n_faces = (faces, facetype[], facetype[])
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))
Expand Down Expand Up @@ -58,40 +57,19 @@ 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

else # we have vertex indexing - no need to remap

if !isempty(v_normals)
point_attributes[:normals] = v_normals
end
if !isempty(uv)
point_attributes[:uv] = uv
end

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 Mesh(meta(points; point_attributes...), faces)
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 ....""
Expand All @@ -111,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])
Expand All @@ -122,7 +105,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
Expand Down
4 changes: 2 additions & 2 deletions src/io/ply.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/io/stl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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
56 changes: 0 additions & 56 deletions src/util.jl

This file was deleted.

52 changes: 20 additions & 32 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ 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)


mktempdir() do tmpdir
Expand Down Expand Up @@ -54,6 +56,8 @@ end
@test mesh_loaded == uvn_mesh
end
end


@testset "Real world files" begin

@testset "STL" begin
Expand All @@ -64,23 +68,26 @@ 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(msh.normals) == 2484
@test length(normals(msh)) == 2484
@test test_face_indices(msh)

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 msh.normals == msh1.normals
@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)
Expand Down Expand Up @@ -133,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
Expand Down Expand Up @@ -172,29 +180,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
Loading