Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_gltf/src/vertex_attributes.rs
6595 views
1
use bevy_mesh::{Mesh, MeshVertexAttribute, VertexAttributeValues as Values, VertexFormat};
2
use bevy_platform::collections::HashMap;
3
use gltf::{
4
accessor::{DataType, Dimensions},
5
mesh::util::{ReadColors, ReadJoints, ReadTexCoords, ReadWeights},
6
};
7
use thiserror::Error;
8
9
use crate::convert_coordinates::ConvertCoordinates;
10
11
/// Represents whether integer data requires normalization
12
#[derive(Copy, Clone)]
13
struct Normalization(bool);
14
15
impl Normalization {
16
fn apply_either<T, U>(
17
self,
18
value: T,
19
normalized_ctor: impl Fn(T) -> U,
20
unnormalized_ctor: impl Fn(T) -> U,
21
) -> U {
22
if self.0 {
23
normalized_ctor(value)
24
} else {
25
unnormalized_ctor(value)
26
}
27
}
28
}
29
30
/// An error that occurs when accessing buffer data
31
#[derive(Error, Debug)]
32
pub(crate) enum AccessFailed {
33
#[error("Malformed vertex attribute data")]
34
MalformedData,
35
#[error("Unsupported vertex attribute format")]
36
UnsupportedFormat,
37
}
38
39
/// Helper for reading buffer data
40
struct BufferAccessor<'a> {
41
accessor: gltf::Accessor<'a>,
42
buffer_data: &'a Vec<Vec<u8>>,
43
normalization: Normalization,
44
}
45
46
impl<'a> BufferAccessor<'a> {
47
/// Creates an iterator over the elements in this accessor
48
fn iter<T: gltf::accessor::Item>(self) -> Result<gltf::accessor::Iter<'a, T>, AccessFailed> {
49
gltf::accessor::Iter::new(self.accessor, |buffer: gltf::Buffer| {
50
self.buffer_data.get(buffer.index()).map(Vec::as_slice)
51
})
52
.ok_or(AccessFailed::MalformedData)
53
}
54
55
/// Applies the element iterator to a constructor or fails if normalization is required
56
fn with_no_norm<T: gltf::accessor::Item, U>(
57
self,
58
ctor: impl Fn(gltf::accessor::Iter<'a, T>) -> U,
59
) -> Result<U, AccessFailed> {
60
if self.normalization.0 {
61
return Err(AccessFailed::UnsupportedFormat);
62
}
63
self.iter().map(ctor)
64
}
65
66
/// Applies the element iterator and the normalization flag to a constructor
67
fn with_norm<T: gltf::accessor::Item, U>(
68
self,
69
ctor: impl Fn(gltf::accessor::Iter<'a, T>, Normalization) -> U,
70
) -> Result<U, AccessFailed> {
71
let normalized = self.normalization;
72
self.iter().map(|v| ctor(v, normalized))
73
}
74
}
75
76
/// An enum of the iterators user by different vertex attribute formats
77
enum VertexAttributeIter<'a> {
78
// For reading native WGPU formats
79
F32(gltf::accessor::Iter<'a, f32>),
80
U32(gltf::accessor::Iter<'a, u32>),
81
F32x2(gltf::accessor::Iter<'a, [f32; 2]>),
82
U32x2(gltf::accessor::Iter<'a, [u32; 2]>),
83
F32x3(gltf::accessor::Iter<'a, [f32; 3]>),
84
U32x3(gltf::accessor::Iter<'a, [u32; 3]>),
85
F32x4(gltf::accessor::Iter<'a, [f32; 4]>),
86
U32x4(gltf::accessor::Iter<'a, [u32; 4]>),
87
S16x2(gltf::accessor::Iter<'a, [i16; 2]>, Normalization),
88
U16x2(gltf::accessor::Iter<'a, [u16; 2]>, Normalization),
89
S16x4(gltf::accessor::Iter<'a, [i16; 4]>, Normalization),
90
U16x4(gltf::accessor::Iter<'a, [u16; 4]>, Normalization),
91
S8x2(gltf::accessor::Iter<'a, [i8; 2]>, Normalization),
92
U8x2(gltf::accessor::Iter<'a, [u8; 2]>, Normalization),
93
S8x4(gltf::accessor::Iter<'a, [i8; 4]>, Normalization),
94
U8x4(gltf::accessor::Iter<'a, [u8; 4]>, Normalization),
95
// Additional on-disk formats used for RGB colors
96
U16x3(gltf::accessor::Iter<'a, [u16; 3]>, Normalization),
97
U8x3(gltf::accessor::Iter<'a, [u8; 3]>, Normalization),
98
}
99
100
impl<'a> VertexAttributeIter<'a> {
101
/// Creates an iterator over the elements in a vertex attribute accessor
102
fn from_accessor(
103
accessor: gltf::Accessor<'a>,
104
buffer_data: &'a Vec<Vec<u8>>,
105
) -> Result<VertexAttributeIter<'a>, AccessFailed> {
106
let normalization = Normalization(accessor.normalized());
107
let format = (accessor.data_type(), accessor.dimensions());
108
let acc = BufferAccessor {
109
accessor,
110
buffer_data,
111
normalization,
112
};
113
match format {
114
(DataType::F32, Dimensions::Scalar) => acc.with_no_norm(VertexAttributeIter::F32),
115
(DataType::U32, Dimensions::Scalar) => acc.with_no_norm(VertexAttributeIter::U32),
116
(DataType::F32, Dimensions::Vec2) => acc.with_no_norm(VertexAttributeIter::F32x2),
117
(DataType::U32, Dimensions::Vec2) => acc.with_no_norm(VertexAttributeIter::U32x2),
118
(DataType::F32, Dimensions::Vec3) => acc.with_no_norm(VertexAttributeIter::F32x3),
119
(DataType::U32, Dimensions::Vec3) => acc.with_no_norm(VertexAttributeIter::U32x3),
120
(DataType::F32, Dimensions::Vec4) => acc.with_no_norm(VertexAttributeIter::F32x4),
121
(DataType::U32, Dimensions::Vec4) => acc.with_no_norm(VertexAttributeIter::U32x4),
122
(DataType::I16, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::S16x2),
123
(DataType::U16, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::U16x2),
124
(DataType::I16, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::S16x4),
125
(DataType::U16, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::U16x4),
126
(DataType::I8, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::S8x2),
127
(DataType::U8, Dimensions::Vec2) => acc.with_norm(VertexAttributeIter::U8x2),
128
(DataType::I8, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::S8x4),
129
(DataType::U8, Dimensions::Vec4) => acc.with_norm(VertexAttributeIter::U8x4),
130
(DataType::U16, Dimensions::Vec3) => acc.with_norm(VertexAttributeIter::U16x3),
131
(DataType::U8, Dimensions::Vec3) => acc.with_norm(VertexAttributeIter::U8x3),
132
_ => Err(AccessFailed::UnsupportedFormat),
133
}
134
}
135
136
/// Materializes values for any supported format of vertex attribute
137
fn into_any_values(self, convert_coordinates: bool) -> Result<Values, AccessFailed> {
138
match self {
139
VertexAttributeIter::F32(it) => Ok(Values::Float32(it.collect())),
140
VertexAttributeIter::U32(it) => Ok(Values::Uint32(it.collect())),
141
VertexAttributeIter::F32x2(it) => Ok(Values::Float32x2(it.collect())),
142
VertexAttributeIter::U32x2(it) => Ok(Values::Uint32x2(it.collect())),
143
VertexAttributeIter::F32x3(it) => Ok(if convert_coordinates {
144
// The following f32x3 values need to be converted to the correct coordinate system
145
// - Positions
146
// - Normals
147
//
148
// See <https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview>
149
Values::Float32x3(it.map(ConvertCoordinates::convert_coordinates).collect())
150
} else {
151
Values::Float32x3(it.collect())
152
}),
153
VertexAttributeIter::U32x3(it) => Ok(Values::Uint32x3(it.collect())),
154
VertexAttributeIter::F32x4(it) => Ok(if convert_coordinates {
155
// The following f32x4 values need to be converted to the correct coordinate system
156
// - Tangents
157
//
158
// See <https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes-overview>
159
Values::Float32x4(it.map(ConvertCoordinates::convert_coordinates).collect())
160
} else {
161
Values::Float32x4(it.collect())
162
}),
163
VertexAttributeIter::U32x4(it) => Ok(Values::Uint32x4(it.collect())),
164
VertexAttributeIter::S16x2(it, n) => {
165
Ok(n.apply_either(it.collect(), Values::Snorm16x2, Values::Sint16x2))
166
}
167
VertexAttributeIter::U16x2(it, n) => {
168
Ok(n.apply_either(it.collect(), Values::Unorm16x2, Values::Uint16x2))
169
}
170
VertexAttributeIter::S16x4(it, n) => {
171
Ok(n.apply_either(it.collect(), Values::Snorm16x4, Values::Sint16x4))
172
}
173
VertexAttributeIter::U16x4(it, n) => {
174
Ok(n.apply_either(it.collect(), Values::Unorm16x4, Values::Uint16x4))
175
}
176
VertexAttributeIter::S8x2(it, n) => {
177
Ok(n.apply_either(it.collect(), Values::Snorm8x2, Values::Sint8x2))
178
}
179
VertexAttributeIter::U8x2(it, n) => {
180
Ok(n.apply_either(it.collect(), Values::Unorm8x2, Values::Uint8x2))
181
}
182
VertexAttributeIter::S8x4(it, n) => {
183
Ok(n.apply_either(it.collect(), Values::Snorm8x4, Values::Sint8x4))
184
}
185
VertexAttributeIter::U8x4(it, n) => {
186
Ok(n.apply_either(it.collect(), Values::Unorm8x4, Values::Uint8x4))
187
}
188
_ => Err(AccessFailed::UnsupportedFormat),
189
}
190
}
191
192
/// Materializes RGBA values, converting compatible formats to Float32x4
193
fn into_rgba_values(self) -> Result<Values, AccessFailed> {
194
match self {
195
VertexAttributeIter::U8x3(it, Normalization(true)) => Ok(Values::Float32x4(
196
ReadColors::RgbU8(it).into_rgba_f32().collect(),
197
)),
198
VertexAttributeIter::U16x3(it, Normalization(true)) => Ok(Values::Float32x4(
199
ReadColors::RgbU16(it).into_rgba_f32().collect(),
200
)),
201
VertexAttributeIter::F32x3(it) => Ok(Values::Float32x4(
202
ReadColors::RgbF32(it).into_rgba_f32().collect(),
203
)),
204
VertexAttributeIter::U8x4(it, Normalization(true)) => Ok(Values::Float32x4(
205
ReadColors::RgbaU8(it).into_rgba_f32().collect(),
206
)),
207
VertexAttributeIter::U16x4(it, Normalization(true)) => Ok(Values::Float32x4(
208
ReadColors::RgbaU16(it).into_rgba_f32().collect(),
209
)),
210
s => s.into_any_values(false),
211
}
212
}
213
214
/// Materializes joint index values, converting compatible formats to Uint16x4
215
fn into_joint_index_values(self) -> Result<Values, AccessFailed> {
216
match self {
217
VertexAttributeIter::U8x4(it, Normalization(false)) => {
218
Ok(Values::Uint16x4(ReadJoints::U8(it).into_u16().collect()))
219
}
220
s => s.into_any_values(false),
221
}
222
}
223
224
/// Materializes joint weight values, converting compatible formats to Float32x4
225
fn into_joint_weight_values(self) -> Result<Values, AccessFailed> {
226
match self {
227
VertexAttributeIter::U8x4(it, Normalization(true)) => {
228
Ok(Values::Float32x4(ReadWeights::U8(it).into_f32().collect()))
229
}
230
VertexAttributeIter::U16x4(it, Normalization(true)) => {
231
Ok(Values::Float32x4(ReadWeights::U16(it).into_f32().collect()))
232
}
233
s => s.into_any_values(false),
234
}
235
}
236
237
/// Materializes texture coordinate values, converting compatible formats to Float32x2
238
fn into_tex_coord_values(self) -> Result<Values, AccessFailed> {
239
match self {
240
VertexAttributeIter::U8x2(it, Normalization(true)) => Ok(Values::Float32x2(
241
ReadTexCoords::U8(it).into_f32().collect(),
242
)),
243
VertexAttributeIter::U16x2(it, Normalization(true)) => Ok(Values::Float32x2(
244
ReadTexCoords::U16(it).into_f32().collect(),
245
)),
246
s => s.into_any_values(false),
247
}
248
}
249
}
250
251
enum ConversionMode {
252
Any,
253
Rgba,
254
JointIndex,
255
JointWeight,
256
TexCoord,
257
}
258
259
#[derive(Error, Debug)]
260
pub(crate) enum ConvertAttributeError {
261
#[error("Vertex attribute {0} has format {1:?} but expected {3:?} for target attribute {2}")]
262
WrongFormat(String, VertexFormat, String, VertexFormat),
263
#[error("{0} in accessor {1}")]
264
AccessFailed(AccessFailed, usize),
265
#[error("Unknown vertex attribute {0}")]
266
UnknownName(String),
267
}
268
269
pub(crate) fn convert_attribute(
270
semantic: gltf::Semantic,
271
accessor: gltf::Accessor,
272
buffer_data: &Vec<Vec<u8>>,
273
custom_vertex_attributes: &HashMap<Box<str>, MeshVertexAttribute>,
274
convert_coordinates: bool,
275
) -> Result<(MeshVertexAttribute, Values), ConvertAttributeError> {
276
if let Some((attribute, conversion, convert_coordinates)) = match &semantic {
277
gltf::Semantic::Positions => Some((
278
Mesh::ATTRIBUTE_POSITION,
279
ConversionMode::Any,
280
convert_coordinates,
281
)),
282
gltf::Semantic::Normals => Some((
283
Mesh::ATTRIBUTE_NORMAL,
284
ConversionMode::Any,
285
convert_coordinates,
286
)),
287
gltf::Semantic::Tangents => Some((
288
Mesh::ATTRIBUTE_TANGENT,
289
ConversionMode::Any,
290
convert_coordinates,
291
)),
292
gltf::Semantic::Colors(0) => Some((Mesh::ATTRIBUTE_COLOR, ConversionMode::Rgba, false)),
293
gltf::Semantic::TexCoords(0) => {
294
Some((Mesh::ATTRIBUTE_UV_0, ConversionMode::TexCoord, false))
295
}
296
gltf::Semantic::TexCoords(1) => {
297
Some((Mesh::ATTRIBUTE_UV_1, ConversionMode::TexCoord, false))
298
}
299
gltf::Semantic::Joints(0) => Some((
300
Mesh::ATTRIBUTE_JOINT_INDEX,
301
ConversionMode::JointIndex,
302
false,
303
)),
304
gltf::Semantic::Weights(0) => Some((
305
Mesh::ATTRIBUTE_JOINT_WEIGHT,
306
ConversionMode::JointWeight,
307
false,
308
)),
309
gltf::Semantic::Extras(name) => custom_vertex_attributes
310
.get(name.as_str())
311
.map(|attr| (*attr, ConversionMode::Any, false)),
312
_ => None,
313
} {
314
let raw_iter = VertexAttributeIter::from_accessor(accessor.clone(), buffer_data);
315
let converted_values = raw_iter.and_then(|iter| match conversion {
316
ConversionMode::Any => iter.into_any_values(convert_coordinates),
317
ConversionMode::Rgba => iter.into_rgba_values(),
318
ConversionMode::TexCoord => iter.into_tex_coord_values(),
319
ConversionMode::JointIndex => iter.into_joint_index_values(),
320
ConversionMode::JointWeight => iter.into_joint_weight_values(),
321
});
322
match converted_values {
323
Ok(values) => {
324
let loaded_format = VertexFormat::from(&values);
325
if attribute.format == loaded_format {
326
Ok((attribute, values))
327
} else {
328
Err(ConvertAttributeError::WrongFormat(
329
semantic.to_string(),
330
loaded_format,
331
attribute.name.to_string(),
332
attribute.format,
333
))
334
}
335
}
336
Err(err) => Err(ConvertAttributeError::AccessFailed(err, accessor.index())),
337
}
338
} else {
339
Err(ConvertAttributeError::UnknownName(semantic.to_string()))
340
}
341
}
342
343