Skip to content

Commit

Permalink
Merge pull request #91 from spelufo/ply-normals
Browse files Browse the repository at this point in the history
Ply normals
  • Loading branch information
sjkelly authored Jun 14, 2024
2 parents 1dfb00f + 413a70a commit 31857a3
Showing 1 changed file with 83 additions and 22 deletions.
105 changes: 83 additions & 22 deletions src/io/ply.jl
Original file line number Diff line number Diff line change
@@ -1,58 +1,78 @@
function save(f::Stream{format"PLY_BINARY"}, msh::AbstractMesh)
io = stream(f)
points = decompose(Point{3, Float32}, msh)
faces = decompose(GLTriangleFace, msh)

points = coordinates(msh)
point_normals = normals(msh)
meshfaces = faces(msh)
n_points = length(points)
n_faces = length(faces)
n_faces = length(meshfaces)

# write the header
write(io, "ply\n")
write(io, "format binary_little_endian 1.0\n")
write(io, "element vertex $n_points\n")
write(io, "property float x\nproperty float y\nproperty float z\n")
if !isnothing(point_normals)
write(io, "property float nx\nproperty float ny\nproperty float nz\n")
end
write(io, "element face $n_faces\n")
write(io, "property list uchar int vertex_index\n")
write(io, "end_header\n")

# write the vertices and faces
write(io, points)

for f in faces
write(io, convert(UInt8, 3))
write(io, raw.(f)...)
if isnothing(point_normals)
write(io, points)
else
for (v, n) in zip(points, point_normals)
write(io, v)
write(io, n)
end
end

for f in meshfaces
write(io, convert(UInt8, length(f)))
write(io, raw.(ZeroIndex.(f))...)
end
close(io)
end

function save(f::Stream{format"PLY_ASCII"}, msh::AbstractMesh)
io = stream(f)
points = coordinates(msh)
point_normals = normals(msh)
meshfaces = faces(msh)

n_faces = length(points)
n_points = length(meshfaces)
n_points = length(points)
n_faces = length(meshfaces)

# write the header
write(io, "ply\n")
write(io, "format ascii 1.0\n")
write(io, "element vertex $n_faces\n")
write(io, "element vertex $n_points\n")
write(io, "property float x\nproperty float y\nproperty float z\n")
write(io, "element face $n_points\n")
if !isnothing(point_normals)
write(io, "property float nx\nproperty float ny\nproperty float nz\n")
end
write(io, "element face $n_faces\n")
write(io, "property list uchar int vertex_index\n")
write(io, "end_header\n")

# write the vertices and faces
for v in points
println(io, join(Point{3, Float32}(v), " "))
if isnothing(point_normals)
for v in points
println(io, join(Point{3, Float32}(v), " "))
end
else
for (v, n) in zip(points, point_normals)
println(io, join([v n], " "))
end
end
for f in meshfaces
println(io, length(f), " ", join(raw.(ZeroIndex.(f)), " "))
end
close(io)
end

function load(fs::Stream{format"PLY_ASCII"}; facetype=GLTriangleFace, pointtype=Point3f)
function load(fs::Stream{format"PLY_ASCII"}; facetype=GLTriangleFace, pointtype=Point3f, normalstype=Vec3f)
io = stream(fs)
n_points = 0
n_faces = 0
Expand All @@ -62,9 +82,12 @@ function load(fs::Stream{format"PLY_ASCII"}; facetype=GLTriangleFace, pointtype=
# read the header
line = readline(io)

has_normals = false
while !startswith(line, "end_header")
if startswith(line, "element vertex")
n_points = parse(Int, split(line)[3])
elseif startswith(line, "property float nx") || startswith(line, "property double nx")
has_normals = true
elseif startswith(line, "element face")
n_faces = parse(Int, split(line)[3])
elseif startswith(line, "property")
Expand All @@ -75,12 +98,17 @@ function load(fs::Stream{format"PLY_ASCII"}; facetype=GLTriangleFace, pointtype=

faceeltype = eltype(facetype)
points = Array{pointtype}(undef, n_points)
point_normals = Array{normalstype}(undef, n_points)
#faces = Array{FaceType}(undef, n_faces)
faces = facetype[]

# read the data
for i = 1:n_points
points[i] = pointtype(parse.(eltype(pointtype), split(readline(io)))) # line looks like: "-0.018 0.038 0.086"
numbers = parse.(eltype(pointtype), split(readline(io)))
points[i] = pointtype(numbers[1:3])
if has_normals && length(numbers) >= 6
point_normals[i] = pointtype(numbers[4:6])
end
end

for i = 1:n_faces
Expand All @@ -92,10 +120,14 @@ function load(fs::Stream{format"PLY_ASCII"}; facetype=GLTriangleFace, pointtype=
push!(faces, convert_simplex(facetype, QuadFace{faceeltype}(reinterpret(ZeroIndex{UInt32}, parse.(UInt32, line))))...) # line looks like: "4 0 1 2 3"
end
end
return Mesh(points, faces)
if has_normals
return Mesh(meta(points; normals=point_normals), faces)
else
return Mesh(points, faces)
end
end

function load(fs::Stream{format"PLY_BINARY"}; facetype=GLTriangleFace, pointtype=Point3f)
function load(fs::Stream{format"PLY_BINARY"}; facetype=GLTriangleFace, pointtype=Point3f, normalstype=Vec3f)
io = stream(fs)
n_points = 0
n_faces = 0
Expand All @@ -105,9 +137,30 @@ function load(fs::Stream{format"PLY_BINARY"}; facetype=GLTriangleFace, pointtype
# read the header
line = readline(io)

has_normals = false
has_doubles = Float32
xtype = Float32; ytype = Float32; ztype = Float32
nxtype = Float32; nytype = Float32; nztype = Float32
while !startswith(line, "end_header")
if startswith(line, "element vertex")
n_points = parse(Int, split(line)[3])
elseif startswith(line, "property double x")
xtype = Float64
elseif startswith(line, "property double y")
ytype = Float64
elseif startswith(line, "property double z")
ztype = Float64
elseif startswith(line, "property float n")
has_normals = true
elseif startswith(line, "property double nx")
has_normals = true
nxtype = Float64
elseif startswith(line, "property double ny")
has_normals = true
nytype = Float64
elseif startswith(line, "property double nz")
has_normals = true
nztype = Float64
elseif startswith(line, "element face")
n_faces = parse(Int, split(line)[3])
elseif startswith(line, "property")
Expand All @@ -118,12 +171,16 @@ function load(fs::Stream{format"PLY_BINARY"}; facetype=GLTriangleFace, pointtype

faceeltype = eltype(facetype)
points = Array{pointtype}(undef, n_points)
point_normals = Array{normalstype}(undef, n_points)
#faces = Array{FaceType}(undef, n_faces)
faces = facetype[]

# read the data
for i = 1:n_points
points[i] = pointtype(read(io, Float32), read(io, Float32), read(io, Float32))
points[i] = pointtype(read(io, xtype), read(io, ytype), read(io, ztype))
if has_normals
point_normals[i] = normalstype(read(io, nxtype), read(io, nytype), read(io, nztype))
end
end

for i = 1:n_faces
Expand All @@ -136,5 +193,9 @@ function load(fs::Stream{format"PLY_BINARY"}; facetype=GLTriangleFace, pointtype
end
end

return Mesh(points, faces)
end
if has_normals
return Mesh(meta(points; normals=point_normals), faces)
else
return Mesh(points, faces)
end
end

0 comments on commit 31857a3

Please sign in to comment.