Skip to content

Commit fd17a83

Browse files
authored
Merge pull request #97 from JuliaIO/ff/GeometryBasics_refactor
Update MeshIO for GeometryBasics refactor
2 parents 9f452ff + 8666d91 commit fd17a83

File tree

14 files changed

+4093
-216
lines changed

14 files changed

+4093
-216
lines changed

.github/workflows/CI.yml

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
name: CI
22
on:
3-
- push
4-
- pull_request
5-
3+
push:
4+
branches:
5+
- master
6+
tags: "*"
7+
pull_request:
8+
branches:
9+
- master
610
concurrency:
711
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
812
cancel-in-progress: true
9-
1013
jobs:
1114
test:
1215
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
@@ -15,7 +18,6 @@ jobs:
1518
fail-fast: false
1619
matrix:
1720
version:
18-
- '1.3'
1921
- '1.6'
2022
- '1'
2123
os:

Project.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MeshIO"
22
uuid = "7269a6da-0436-5bbc-96c2-40638cbb6118"
33
author = "Simon Danisch"
4-
version = "0.4.13"
4+
version = "0.5.0"
55

66
[deps]
77
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
@@ -12,7 +12,7 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
1212
[compat]
1313
ColorTypes = "0.8, 0.9, 0.10, 0.11, 0.12"
1414
FileIO = "1.2.4"
15-
GeometryBasics = "0.4.1"
15+
GeometryBasics = "0.5"
1616
julia = "1.3"
1717

1818
[extras]

README.md

+22-36
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![codecov.io](http://codecov.io/github/JuliaIO/MeshIO.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaIO/MeshIO.jl?branch=master)
44
[![Coverage Status](https://coveralls.io/repos/JuliaIO/MeshIO.jl/badge.svg?branch=master&service=github)](https://coveralls.io/github/JuliaIO/MeshIO.jl?branch=master)
55

6-
This package supports loading 3D model file formats: `obj`, `stl`, `ply`, `off` and `2DM`.
6+
This package supports loading 3D model file formats: `obj`, `stl`, `ply`, `off`, `msh` and `2DM`.
77
More 3D model formats will be supported in the future.
88

99
## Installation
@@ -22,49 +22,35 @@ This means loading a mesh is as simple as this:
2222
using FileIO
2323
mesh = load("path/to/mesh.obj")
2424
```
25+
The result will usually be a [GeometryBasics](https://github.com/JuliaGeometry/GeometryBasics.jl) `Mesh`.
26+
The exception are `obj` files with non-vertex data such as material data or "group" tags, which return a `MetaMesh`.
27+
2528
Displaying a mesh can be achieved with [Makie](https://github.com/JuliaPlots/Makie.jl).
2629

2730
Functions for mesh manipulation can be found in [JuliaGeometry](https://github.com/JuliaGeometry)
2831

2932
## Additional Information
3033

31-
MeshIO now has the HomogenousMesh type. Name is still not settled, but it's supposed to be a dense mesh with all attributes either having the length of one (constant over the whole mesh) or the same length (per vertex).
32-
This meshtype holds a large variability for all the different attribute mixtures that I've encountered while trying to visualize things over at GLVisualize. This is the best type I've found so far to encode this large variability, without an explosion of functions.
33-
34-
The focus is on conversion between different mesh types and creation of different mesh types.
35-
This has led to some odd seeming design choices.
36-
First, you can get an attribute via `decompose(::Type{AttributeType}, ::Mesh)`.
37-
This will try to get this attribute, and if it has the wrong type try to convert it, or if it is not available try to create it.
38-
So `decompose(Point3{Float32}, mesh)` on a mesh with vertices of type `Point3{Float64}` will return a vector of type `Point3{Float32}`.
39-
Similarly, if you call `decompose(Normal{3, Float32}, mesh)` but the mesh doesn't have normals, it will call the function `normals(mesh.vertices, mesh.faces, Normal{3, Float32}`, which will create the normals for the mesh.
40-
As most attributes are independent, this enables us to easily create all kinds of conversions.
41-
Also, I can define `decompose` for arbitrary geometric types.
42-
`decompose{T}(Point3{T}, r::Rectangle)` can actually return the needed vertices for a rectangle.
43-
This together with `convert` enables us to create mesh primitives like this:
44-
```Julia
45-
MeshType(Cube(...))
46-
MeshType(Sphere(...))
47-
MeshType(Volume, 0.4f0) #0.4f0 => isovalue
48-
```
34+
### Usage
4935

50-
Similarly, I can pass a meshtype to an IO function, which then parses only the attributes that I really need.
51-
So passing `Mesh{Point3{Float32}, Face3{UInt32}}` to the obj importer will skip normals, uv coordinates etc, and automatically converts the given attributes to the right number type.
36+
The GeometryBasics `Mesh` supports vertex attributes with different lengths which get addressed by different faces (as of 0.5).
37+
As such MeshIO makes no effort to convert vertex attributes to a common length, indexed by one set of faces.
38+
If you need a single set of faces, e.g. for rendering, you can use `new_mesh = GeometryBasics.expand_faceviews(mesh)` to generate a fitting mesh.
5239

53-
To put this one level further, the `Face` type has the index offset relative to Julia's indexing as a parameter (e.g. `Face3{T, 0}` is 1 indexed). Also, you can index into an array with this face type, and it will convert the indexes correctly while accessing the array. So something like this always works, independent of the underlying index offset:
54-
```Julia
55-
v1, v2, v3 = vertices[face]
40+
The GeometryBasics `Mesh` allows for different element types for coordinates, normals, faces, etc.
41+
These can set when loading a mesh using keyword arguments:
42+
```julia
43+
load(filename; pointtype = Point3f, uvtype = Vec2f, facetype = GLTriangleFace, normaltype = Vec3f)
5644
```
57-
Also, the importer is sensitive to this, so if you always want to work with 0-indexed faces (like it makes sense for opengl based visualizations), you can parse the mesh already as an 0-indexed mesh, by just defining the mesh format to use `Face3{T, -1}`. (only the OBJ importer yet)
45+
Note that not every file format supports normals and uvs (texture coordinates) and thus some loaders don't accept `uvtype` and/or `normaltype`.
5846

59-
Small example to demonstrate the advantage for IO:
60-
```Julia
61-
#Export takes any mesh
62-
function write{M <: Mesh}(msh::M, fn::File{:ply_binary})
63-
# even if the native mesh format doesn't have an array of dense points or faces, the correct ones will
64-
# now be created, or converted:
65-
vts = decompose(Point3{Float32}, msh) # I know ply_binary needs Point3{Float32}
66-
fcs = decompose(Face3{Int32, -1}, msh) # And 0 indexed Int32 faces.
67-
#write code...
68-
end
69-
```
47+
The facetypes from GeometryBasics support 0 and 1-based indexing using `OffsetInteger`s.
48+
For example `GLTriangleFace` is an alias for `NgonFace{3, OffsetInteger{-1, UInt32}}`, i.e. a face containing 3 indices offset from 1-based indexing by `-1`.
49+
The raw data in a `GLTriangleFace` is 0-based so that it can be uploaded directly in a Graphics API.
50+
In Julia code it gets converted back to a 1-based Int, so that it can be used as is.
51+
52+
### Extending MeshIO
7053

54+
To implement a new file format you need to add the appropriate `load()` and `save()` methods.
55+
You also need to register the file format with [FileIO](https://juliaio.github.io/FileIO.jl/stable/registering/)
56+
For saving it may be useful to know that you can convert vertex data to specific types using the [decompose interface](https://juliageometry.github.io/GeometryBasics.jl/stable/decomposition/).

src/MeshIO.jl

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ using FileIO: FileIO, @format_str, Stream, File, stream, skipmagic
99

1010
import Base.show
1111

12-
include("util.jl")
13-
1412
include("io/off.jl")
1513
include("io/ply.jl")
1614
include("io/stl.jl")

src/io/gts.jl

+5-7
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,13 @@ function parseGtsLine( s::AbstractString, C, T=eltype(C) )
1212
end
1313
end
1414

15-
function load( st::Stream{format"GTS"}, MeshType=GLNormalMesh )
15+
function load( st::Stream{format"GTS"}; facetype=GLTriangleFace, pointtype=Point)
1616
io = stream(st)
1717
head = readline( io )
18-
FT = facetype(MeshType)
19-
VT = vertextype(MeshType)
2018

2119
nVertices, nEdges, nFacets = parseGtsLine( head, Tuple{Int,Int,Int} )
2220
iV = iE = iF = 1
23-
vertices = Vector{VT}(undef, nVertices)
21+
vertices = Vector{pointtype}(undef, nVertices)
2422
edges = Vector{Vector{Int}}(undef, nEdges)
2523
facets = Vector{Vector{Int}}(undef, nFacets)
2624
for full_line::String in eachline(io)
@@ -30,7 +28,7 @@ function load( st::Stream{format"GTS"}, MeshType=GLNormalMesh )
3028

3129
if !startswith(line, "#") && !isempty(line) && !all(iscntrl, line) #ignore comments
3230
if iV <= nVertices
33-
vertices[iV] = parseGtsLine( line, VT )
31+
vertices[iV] = parseGtsLine( line, pointtype )
3432
iV += 1
3533
elseif iV > nVertices && iE <= nEdges
3634
edges[iE] = parseGtsLine( line, Array{Int} )
@@ -41,8 +39,8 @@ function load( st::Stream{format"GTS"}, MeshType=GLNormalMesh )
4139
end # if
4240
end # if
4341
end # for
44-
faces = [ FT( union( edges[facets[i][1]], edges[facets[i][2]], edges[facets[i][3]] ) ) for i in 1:length(facets) ] # orientation not guaranteed
45-
return MeshType( vertices, faces )
42+
faces = [ facetype( union( edges[facets[i][1]], edges[facets[i][2]], edges[facets[i][3]] ) ) for i in 1:length(facets) ] # orientation not guaranteed
43+
return Mesh( vertices, faces )
4644
end
4745

4846
function save( st::Stream{format"GTS"}, mesh::AbstractMesh )

src/io/ifs.jl

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function load(fs::Stream{format"IFS"}, MeshType = GLNormalMesh)
1+
function load(fs::Stream{format"IFS"}; facetype=GLTriangleFace, pointtype=Point3f)
22
io = stream(fs)
33
function str()
44
n = read(io, UInt32)
@@ -11,15 +11,15 @@ function load(fs::Stream{format"IFS"}, MeshType = GLNormalMesh)
1111
end
1212
nverts = read(io, UInt32)
1313
verts_float = read(io, Float32, nverts * 3)
14-
verts = reinterpret(Point3f0, verts_float)
14+
verts = reinterpret(pointtype, verts_float)
1515
tris = str()
1616
if tris != "TRIANGLES\0"
1717
error("$(filename(fs)) does not seem to be of format IFS")
1818
end
1919
nfaces = read(io, UInt32)
2020
faces_int = read(io, UInt32, nfaces * 3)
21-
faces = reinterpret(GLTriangle, faces_int)
22-
MeshType(vertices = verts, faces = faces)
21+
faces = reinterpret(facetype, faces_int)
22+
return GeometryBasics.Mesh(verts, faces)
2323
end
2424

2525
function save(fs::Stream{format"IFS"}, msh::AbstractMesh; meshname = "mesh")
@@ -29,8 +29,8 @@ function save(fs::Stream{format"IFS"}, msh::AbstractMesh; meshname = "mesh")
2929
write(io, UInt32(length(s0)))
3030
write(io, s0)
3131
end
32-
vts = decompose(Point3f0, msh)
33-
fcs = decompose(GLTriangle, msh)
32+
vts = decompose(Point3f, msh)
33+
fcs = decompose(GLTriangleFace, msh)
3434

3535
# write the header
3636
write0str("IFS")

0 commit comments

Comments
 (0)