@@ -27,12 +27,11 @@ function mesh(primitive::AbstractGeometry; pointtype=Point, facetype=GLTriangleF
27
27
if _f isa AbstractVector{<: MultiFace }
28
28
if facetype isa MultiFace
29
29
# drop faces that facetype doesn't include
30
- names = propertynames (facetype)
31
- _f = map (f -> MultiFace {names} (getproperty .((f,), names)), _f)
30
+ _f = simplify_faces (facetype, _f)
32
31
else
33
32
# drop faces for vertex attributes that aren't given
34
33
names = (:position , keys (vertex_attributes)... )
35
- _f2 = map (f -> MultiFace { names} ( getproperty .((f,), names)) , _f)
34
+ _f2 = simplify_faces ( names, _f)
36
35
37
36
# and remap to a simple face type so that decompose can handle the rest
38
37
_f, mappings = merge_vertex_indices (_f2)
@@ -47,7 +46,6 @@ function mesh(primitive::AbstractGeometry; pointtype=Point, facetype=GLTriangleF
47
46
return Mesh (positions, f; vertex_attributes... )
48
47
end
49
48
50
-
51
49
const SimpleMesh{N, T, FT} = Mesh{N, T, FT, (:position ,), Tuple{Vector{Point{N, T}}}, Vector{FT}}
52
50
const SimpleTriangleMesh{N} = SimpleMesh{N, Float32, GLTriangleFace}
53
51
@@ -123,41 +121,133 @@ function volume(mesh::Mesh)
123
121
return sum (volume, mesh)
124
122
end
125
123
124
+ # TODO : Is this ok as "public" function?
125
+ # MultiFace(f1, f2, f3) + (o1, o2, o3) = MultiFace(f1 + o1, f2 + o2, f3 + o3)
126
+ function Base.:+ (f:: MultiFace{N, T, FT, Names, M} , o:: NTuple{M, T} ) where {N, T, FT, Names, M}
127
+ return MultiFace {Names} (ntuple (m -> f[m] + o[m], M))
128
+ end
129
+
126
130
function Base. merge (meshes:: AbstractVector{<:Mesh} )
127
131
return if isempty (meshes)
128
132
return Mesh (Point3f[], GLTriangleFace[])
129
133
elseif length (meshes) == 1
130
134
return meshes[1 ]
131
135
else
132
- ps = reduce (vcat, coordinates .(meshes))
133
- fs = reduce (vcat, faces .(meshes))
134
- idx = length (faces (meshes[1 ]))
135
- offset = length (coordinates (meshes[1 ]))
136
- for mesh in Iterators. drop (meshes, 1 )
137
- N = length (faces (mesh))
138
- for i = idx .+ (1 : N)
139
- fs[i] = fs[i] .+ offset
140
- end
141
- idx += N
142
- offset += length (coordinates (mesh))
136
+
137
+ m1 = meshes[1 ]
138
+
139
+ # Check that all meshes use the same VertexAttributes
140
+ # Could also do this via typing the function, but maybe error is nice?
141
+ names = propertynames (m1. vertex_attributes)
142
+ idx = findfirst (m -> propertynames (m. vertex_attributes) != names, meshes)
143
+ if idx != = nothing
144
+ error (
145
+ " Cannot merge meshes with different vertex attributes. " *
146
+ " First missmatch between meshes[1] with $names and " *
147
+ " meshes[$idx ] with $(propertynames (meshes[idx])) ."
148
+ )
143
149
end
144
- return Mesh (ps, fs)
145
- end
146
- end
147
150
148
- function Base. merge (meshes:: AbstractVector{T} ) where T <: MetaMesh
149
- isempty (meshes) && return T (Point3f[], GLTriangleFace[])
150
- big_mesh = merge (map (Mesh, meshes))
151
- big_meta = deepcopy (meta (meshes[1 ]))
152
- for mesh in Iterators. drop (meshes, 1 )
153
- mm = meta (mesh)
154
- for (k, v) in pairs (mm)
155
- append! (big_meta[k], v)
151
+ # We can't merge MultiFace with standard faces because MutliFace allows
152
+ # desynchronizes vertex indices that normal faces assume synchronized.
153
+ is_multi = facetype (m1) <: MultiFace
154
+
155
+ if all (m -> is_multi == (facetype (m) <: MultiFace ), meshes)
156
+
157
+ # All the same kind of face, can just merge
158
+
159
+ new_attribs = NamedTuple {names} (map (names) do name
160
+ return mapreduce (m -> getproperty (m, name), vcat, meshes)
161
+ end )
162
+ fs = reduce (vcat, faces .(meshes))
163
+
164
+ # TODO : is the type difference in offset bad?
165
+ idx = length (faces (m1))
166
+ offset = is_multi ? length .(values (vertex_attributes (m1))) : length (coordinates (m1))
167
+ views = isempty (m1. views) ? [1 : idx] : copy (m1. views)
168
+
169
+ for mesh in Iterators. drop (meshes, 1 )
170
+ # update face indices
171
+ N = length (faces (mesh))
172
+ for i = idx .+ (1 : N)
173
+ fs[i] = fs[i] + offset
174
+ end
175
+
176
+ # add views
177
+ if isempty (mesh. views)
178
+ push! (views, idx+ 1 : idx+ N)
179
+ else
180
+ append! (views, (view + idx for view in mesh. views))
181
+ end
182
+
183
+ idx += N
184
+ if is_multi
185
+ offset = offset .+ length .(values (vertex_attributes (mesh)))
186
+ else
187
+ offset += length (coordinates (mesh))
188
+ end
189
+ end
190
+
191
+ return Mesh (new_attribs, fs, views)
192
+
193
+ else
194
+
195
+ # TODO : We can probably simplify this to `merge(merge_vertex_indices.(meshes))`
196
+ # but need to check performance
197
+
198
+
199
+ # Varying ace types, need to convert MultiFace
200
+ new_attribs = NamedTuple {names} (similar .(values (vertex_attributes (m1)), 0 ))
201
+ FT = facetype (m1) <: MultiFace ? eltype (facetype (m1)) : facetype (m1)
202
+ remapped_faces = []
203
+ new_views = UnitRange{Int}[]
204
+ vertex_index_counter = eltype (FT)(1 )
205
+
206
+ for mesh in meshes
207
+ # convert MultiFace mesh to normal faces, synchronizing vertex indices
208
+ attribs, fs, views = merge_vertex_indices (
209
+ vertex_attributes (mesh), faces (mesh), mesh. views, vertex_index_counter)
210
+
211
+ # increment first vertex index used by faces of the next iteration
212
+ vertex_index_counter += length (attribs[1 ])
213
+
214
+ # update merged data
215
+ for name in names
216
+ append! (new_attribs[name], attribs[name])
217
+ end
218
+
219
+ push! (remapped_faces, fs)
220
+
221
+ if isempty (views)
222
+ push! (new_views, 1 : length (fs))
223
+ else
224
+ append! (new_views, views)
225
+ end
226
+ end
227
+
228
+ # We did MultiFace -> normal face, now equalize normal face types
229
+ new_faces = reduce (vcat, remapped_faces)
230
+
231
+ return Mesh (new_attribs, new_faces, new_views)
156
232
end
233
+
157
234
end
158
- return MetaMesh (big_mesh, big_meta)
159
235
end
160
236
237
+ # TODO : Probably not our problem
238
+ # function Base.merge(meshes::AbstractVector{T}) where T <: MetaMesh
239
+ # isempty(meshes) && return T(Point3f[], GLTriangleFace[])
240
+ # big_mesh = merge(map(Mesh, meshes))
241
+ # big_meta = deepcopy(meta(meshes[1]))
242
+ # for mesh in Iterators.drop(meshes, 1)
243
+ # mm = meta(mesh)
244
+ # for (k, v) in pairs(mm)
245
+ # append!(big_meta[k], v)
246
+ # end
247
+ # end
248
+ # return MetaMesh(big_mesh, big_meta)
249
+ # end
250
+
161
251
# TODO : naming
162
252
# synchronize_vertex_attributes
163
253
# merge_vertex_(attribute)_indices
@@ -170,10 +260,28 @@ function merge_vertex_indices(mesh)
170
260
return Mesh (attribs, fs, views)
171
261
end
172
262
263
+ function merge_vertex_indices (
264
+ attribs:: NamedTuple{Names} ,
265
+ faces:: AbstractVector{<: FT} ,
266
+ views:: Vector{UnitRange{Int}} ,
267
+ vertex_index_counter = nothing
268
+ ) where {Names, FT <: AbstractFace }
269
+
270
+ if FT <: MultiFace
271
+ error (
272
+ " Failed to call correct method. This likely happened because vertex " *
273
+ " attributes names $Names do not match face name $(propertynames (first (faces))) ."
274
+ )
275
+ end
276
+
277
+ return attribs, faces, views
278
+ end
279
+
173
280
function merge_vertex_indices (
174
281
attribs:: NamedTuple{Names} ,
175
282
faces:: AbstractVector{<: MultiFace{N, T, FT, Names}} ,
176
- views:: Vector{UnitRange}
283
+ views:: Vector{UnitRange{Int}} ,
284
+ vertex_index_counter = T (1 ) # TODO : test 0 vs 1 base
177
285
) where {Names, N, T, FT}
178
286
179
287
# Note: typing checks for matching Names
@@ -186,17 +294,16 @@ function merge_vertex_indices(
186
294
187
295
new_attribs = NamedTuple ((Pair (k, similar (v, 0 )) for (k, v) in pairs (attribs)))
188
296
new_faces = similar (faces, FT, 0 )
189
- new_views = UnitRange[]
297
+ new_views = UnitRange{Int} []
190
298
299
+ # TODO : this depends on T in Face (1 based -> 1, 0 based -> 0)
191
300
for idxs in views
192
- # TODO : this depends on T in Face (1 based -> 1, 0 based -> 0)
193
- vertex_index_counter = T (length (new_attribs[1 ]) + 1 )
194
-
195
301
# Generate new face from current view, with the first vertex_index
196
302
# corresponding to the first vertex attribute added in this iteration
197
303
face_view = view (faces, idxs)
198
304
new_faces_in_view, vertex_map = merge_vertex_indices (face_view, vertex_index_counter)
199
-
305
+ vertex_index_counter += length (vertex_map)
306
+
200
307
# update vertex attributes
201
308
for (name, indices) in pairs (vertex_map)
202
309
append! (new_attribs[name], view (attribs[name], indices))
@@ -211,6 +318,17 @@ function merge_vertex_indices(
211
318
return new_attribs, new_faces, new_views
212
319
end
213
320
321
+ function merge_vertex_indices (
322
+ faces:: AbstractVector{FT} ,
323
+ vertex_index_counter = T (1 )
324
+ ) where {N, T, FT <: AbstractFace{N, T} }
325
+
326
+ @assert ! (FT <: MultiFace ) " Dispatch failed?"
327
+
328
+ N_vert = mapreduce (f -> max (f), max, faces)
329
+ return faces, (1 : N_vert) .+ vertex_index_counter
330
+ end
331
+
214
332
function merge_vertex_indices (
215
333
faces:: AbstractVector{<: MultiFace{N, T, FT, Names, N_Attrib}} ,
216
334
vertex_index_counter = T (1 )
0 commit comments