Line data Source code
1 : using FileIO
2 : import GeometryBasics
3 : using GeometryBasics: coordinates, meta,
4 : GLTriangleFace, Point3, Vec3, Vec2, Point, GLIndex, NgonFace
5 :
6 : # NOTE: Look at Meshes.jl and GeoIO.jl for more readers
7 :
8 : """
9 : mesh = createmesh(mobj::GeometryBasics.Mesh, template::Mesh;
10 : [position = :x])
11 :
12 : Convert `GeometryBasics.Mesh` to `Mesh`.
13 : """
14 0 : function createmesh(mobj::GeometryBasics.Mesh,
15 : template::Mesh{V, F, H, E};
16 : position=Val(:x),
17 : normal=Val(:n), texcoord=Val(:u)) where {V, F, H, E}
18 0 : n = length(coordinates(mobj))
19 0 : m = length(GeometryBasics.faces(mobj))
20 :
21 0 : mesh = similar(template)
22 :
23 0 : sizehint!(mesh, n, m)
24 :
25 0 : addvertices!(mesh, n)
26 :
27 0 : maybesetvattr(f, name::Symbol) = maybeattr(f, Val(S))
28 :
29 0 : function maybesetvattr(f, ::Val{S}) where {S}
30 0 : hasvattr(mesh, S) || return nothing
31 0 : attr = vattr(mesh, Val(S))
32 0 : isenabled(attr) && f(attr)
33 0 : nothing
34 : end
35 :
36 0 : vx = vattr(mesh, position)
37 :
38 0 : for (j, c) in enumerate(coordinates(mobj))
39 0 : vx[VHnd(j)] = c
40 0 : end
41 :
42 0 : for f in GeometryBasics.faces(mobj)
43 0 : addface!(mesh, VHnd.(convert.(Int, f))...)
44 0 : end
45 :
46 0 : hasproperty(mobj, :normal) && maybesetvattr(normal) do vn
47 0 : if isa(GeometryBasics.normals(mobj), GeometryBasics.FaceView)
48 0 : @warn "Drop vertex normals from GeometryBasics.FaceView"
49 0 : return nothing
50 : end
51 :
52 0 : for (j, n) in enumerate(GeometryBasics.normals(mobj))
53 0 : vn[VHnd(j)] = n
54 0 : end
55 : end
56 :
57 0 : hasproperty(mobj, :uv) && maybesetvattr(texcoord) do vu
58 0 : for (j, u) in enumerate(GeometryBasics.texturecoordinates(mobj))
59 0 : vu[VHnd(j)] = u
60 0 : end
61 : end
62 :
63 0 : mesh
64 : end
65 :
66 : # TODO: MeshIO loaders don't recognize all options (OFF fails for
67 : # uvtyupe=...), but I have problems passing just options...
68 : # -- currently restriction to pointstype
69 :
70 : """
71 : mesh, mobj = read(:MeshIO, filename, template::Mesh
72 : [; position=:x, exceptions=false,
73 : pointstype=Point3])
74 :
75 : Load `mesh` from file, e.g., in OFF format using
76 : [`FileIO`](https://juliaio.github.io/FileIO.jl/stable/) and
77 : [`GeometryBasics`](https://juliageometry.github.io/GeometryBasics.jl/stable/)
78 :
79 : `mobj` is the mesh as defined by `GeometryBasics` and can be used for
80 : rendering. `GeometryBasics.coordinates(mobj)` and `GeometryBasics.faces(mobj)`
81 : provide `Vector`s of vertex positions and triangle indices, respectively.
82 :
83 : !!! warning "Precondition"
84 : This method requires a **triangle mesh**!
85 :
86 : !!! warning
87 : `MeshIO.load` takes keyword arguments `uvtype` and `normaltype` only
88 : for file formats, which support these. For other formats, it just
89 : fails. We stick to the default settings (`Float32`) and pass only
90 : the vertex position type.
91 :
92 : !!! note
93 : For some file formats (e.g., OBJ), `FileIO.load` returns a `MetaMesh`.
94 : We drop the meta data, which may include material properties etc.
95 : """
96 0 : function Base.read(::Val{:MeshIO},
97 : filename::AbstractString,
98 : template::Mesh{V, F, H, E};
99 : position=Val(:x),
100 : normal=Val(:n), texcoord=Val(:u),
101 : exceptions::Bool=false,
102 : pointstype=Point3{Float32},
103 : options...) where {V, F, H, E}
104 0 : try
105 : # NOTE: MeshIO labels option "pointtype" instead of "pointstype" !!
106 0 : mobj = GeometryBasics.Mesh(FileIO.load(abspath(filename);
107 : pointtype=pointstype)) :: GeometryBasics.Mesh
108 : # NOTE: Mesh(...) drops extras from MetaMesh (e.g., from OBJ)
109 :
110 0 : createmesh(mobj, template; position, normal, texcoord), mobj
111 : catch e
112 0 : exceptions && rethrow()
113 0 : showerror(stderr, e, Base.catch_backtrace())
114 0 : nothing
115 : end
116 : end
117 :
118 0 : _len(::Type{SVector{N, T}}) where {N, T} = N
119 0 : _len(::Type{NTuple{N, T}}) where {N, T} = N
120 0 : _len(::Type{T}) where {T<:Number} = 1
121 :
122 0 : toPoint(x::V, p::Point{N, T}) where {V, N, T} =
123 : toPoint(x, Val(_len(V)), p) :: Point{N, T}
124 :
125 0 : toPoint(x, ::Val{N}, p::Point{N, T}) where {N, T} = Point{N, T}(x...)
126 :
127 0 : toPoint(x, m::Val{M}, p::Point{N, T}) where {M, N, T} =
128 : toPoint(x, m, Val(M < N), p)
129 0 : toPoint(x, ::Val{M}, ::Val{true}, _::Point{N, T}) where {M, N, T} =
130 : Point{N, T}(x..., (zero(T) for _ in M+1:N)...)
131 0 : toPoint(x, ::Val{M}, ::Val{false}, _::Point{N, T}) where {M, N, T} =
132 0 : Point{N, T}(ntuple(i -> x[i], N)...)
133 :
134 0 : toPoints(vx::Attribute{V}, pointstype::Point{N, T}, n) where {V, N, T} =
135 : [ toPoint(vx[VHnd(j)], pointstype) for j in 1:n ]
136 :
137 :
138 :
139 0 : function meshobj(mesh::Mesh;
140 : istriangulation::Union{Bool, Nothing}=nothing,
141 : position = Val(:x),
142 : uv = nothing, normals = nothing,
143 : pointstype::Type{Point{N, T}} = Point3{Float32},
144 : uvtype=Vec2{Float32},
145 : normaltype=Vec3{Float32}) where {N, T}
146 : # see, e.g., https://github.com/JuliaIO/MeshIO.jl/blob/master/src/io/obj.jl
147 0 : n = n_total(vattr(mesh))
148 0 : m = nf(mesh)
149 :
150 0 : vx = vattr(mesh, position)
151 0 : points = toPoints(vx, zero(pointstype), n)
152 :
153 0 : isnothing(istriangulation) && (istriangulation = istrianglemesh(mesh))
154 :
155 : # NOTE: This is not type-stable!
156 0 : glfaces =
157 : if istriangulation
158 0 : t = triangletable(mesh)
159 0 : [ GLTriangleFace(t[j]...) for j in 1:m ]
160 : else
161 0 : ngonfaces = Vector{NgonFace}(undef, m)
162 0 : for (i, f) in enumerate(faces(mesh))
163 0 : vs = map(v->GLIndex(Int(v)), vertices(mesh, f))
164 0 : @show vs
165 0 : ngonfaces[i] = NgonFace{length(vs), GLIndex}(vs...)
166 0 : end
167 0 : ngonfaces
168 : end
169 :
170 0 : point_attributes = Dict{Symbol, Any}()
171 :
172 0 : if uv !== nothing
173 0 : u = vattr(mesh, uv)
174 0 : point_attributes[:uv] = [ uvtype(u[VHnd(j)]...) for j in 1:n ]
175 : end
176 :
177 0 : if normals !== nothing
178 0 : point_attributes[:normals] =
179 : if normals == :auto
180 0 : GeometryBasics.normals(points, glfaces)
181 : else
182 0 : nrm = vattr(mesh, normals)
183 0 : [ normalize(normaltype(nrm[VHnd(j)]...)) for j in 1:n ]
184 : end
185 : end
186 :
187 0 : GeometryBasics.Mesh(points, glfaces; point_attributes...)
188 : end
189 :
190 : """
191 : mobj = meshobj(mesh[; istriangulation=nothing,
192 : position=:x, uv=nothing, normals=nothing,
193 : pointstype = Point3, uvtype=Vec2,
194 : normaltype=Vec3])
195 :
196 : Convert `mesh` to mesh as defined by
197 : [`GeometryBasics`](https://juliageometry.github.io/GeometryBasics.jl),
198 : which can be used for rendering, e.g., with
199 : [`Makie`](https://makie.juliaplots.org) (see `Makie.mesh`).
200 :
201 : If `istriangulation === nothing` its value is determined automatically
202 : from [`istrianglemesh`](ref). Depending on the value the less
203 : expensive [`triangletable`](@ref) call and a an array of
204 : `GeometryBasics.GLTriangleFace` can be used.
205 :
206 : Texture coordinates `uv` are passed as matrix columns or as a function
207 : that maps vertex positions. Normals are passed as a matrix. They are
208 : estimated if either `:auto` or an empty matrix is passed.
209 :
210 : The argument `normals` is used as follows:
211 :
212 : - `nothing`: don't include normals as attributes
213 : - `:auto`: have `GeometryBasics` compute normals
214 : - use provided *attribute* as normals.
215 :
216 : The dimension `ni` of the input `position` attribute may differ from the
217 : dimension `no` of the output `pointstype`:
218 :
219 : - `ni == no` copy (possibly convert types)
220 : - `ni < no` copy and fill remaining components of output with zeros
221 : - `ni > ni` copy only fist `no` dimensions of input
222 :
223 : !!! note
224 : `meshobj` does *not* call [`compacttriangletable`](@ref) and does
225 : *not* remap vertex handles to contiguous indices, i.e., the
226 : returned object may represent a mesh including unused vertices.
227 : """ meshobj
228 :
229 : # TODO: Support meshobj(geometry(...))
230 :
231 : # TODO: Refactor meshobj() and PMeshMakie
232 : # Implementations "overlap", PMeshMakie is more general
233 : # and more sophisticated
|