rpfm_lib/files/rigidmodel/vertices/
mod.rs1use getset::{Getters, MutGetters, Setters};
72use half::f16;
73use nalgebra::{Vector2, Vector4};
74use serde::{Deserialize, Serialize};
75
76use crate::binary::{ReadBytes, WriteBytes};
77use crate::error::{Result, RLibError};
78
79use super::materials::MaterialType;
80
81#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
90#[repr(u16)]
91pub enum VertexFormat {
92 #[default]
94 Static = 0,
95 Collision = 1,
97 Weighted = 3,
99 Cinematic = 4,
101 Grass = 5,
103 Tree = 6,
105 Billboard = 7,
107 Rope = 11,
109 Uk8 = 8,
111 Uk12 = 12,
113 ClothSim = 25,
115}
116
117#[derive(Clone, Debug, Default, PartialEq, Getters, MutGetters, Setters, Serialize, Deserialize)]
140#[getset(get = "pub", get_mut = "pub", set = "pub")]
141pub struct Vertex {
142
143 position: Vector4<f32>,
146
147 texture_coordinate: Vector2<f32>,
150
151 texture_coordinate2: Vector2<f32>,
157
158 normal: Vector4<f32>,
161
162 tangent: Vector4<f32>,
165
166 bitangent: Vector4<f32>,
169
170 color: Vector4<f32>,
172
173 bone_indices: Vector4<u8>,
177
178 weights: Vector4<f32>,
181
182 uk_1: Vector4<u8>,
184
185 position2: Vector4<f32>,
189
190 uk_2: Vector2<f32>,
192
193 tree_colour_wind: Vector4<f32>,
195
196 uk_4: f32,
198}
199
200impl TryFrom<u16> for VertexFormat {
205 type Error = RLibError;
206 fn try_from(value: u16) -> Result<Self> {
207 match value {
208 _ if value == Self::Static as u16 => Ok(Self::Static),
209 _ if value == Self::Collision as u16 => Ok(Self::Collision),
210 _ if value == Self::Weighted as u16 => Ok(Self::Weighted),
211 _ if value == Self::Cinematic as u16 => Ok(Self::Cinematic),
212 _ if value == Self::Grass as u16 => Ok(Self::Grass),
213 _ if value == Self::Tree as u16 => Ok(Self::Tree),
214 _ if value == Self::Billboard as u16 => Ok(Self::Billboard),
215 _ if value == Self::Rope as u16 => Ok(Self::Rope),
216 _ if value == Self::Uk8 as u16 => Ok(Self::Uk8),
217 _ if value == Self::Uk12 as u16 => Ok(Self::Uk12),
218 _ if value == Self::ClothSim as u16 => Ok(Self::ClothSim),
219 _ => Err(RLibError::DecodingRigidModelUnknownVertexFormat(value))
220 }
221 }
222}
223
224impl From<VertexFormat> for u16 {
225 fn from(value: VertexFormat) -> u16 {
226 value as u16
227 }
228}
229
230impl Vertex {
231
232 pub fn read<R: ReadBytes>(data: &mut R, version: u32, vtype: VertexFormat, mtype: MaterialType) -> Result<Self> {
252 let mut v = Self::default();
253 match vtype {
254 VertexFormat::Static => match static_layout(version, mtype) {
255 MaterialType::TiledDirtmap |
256 MaterialType::ShipAmbientmap |
257 MaterialType::Decal |
258 MaterialType::DecalDirtmap |
259 MaterialType::Dirtmap |
260 MaterialType::Cloth |
261 MaterialType::Unlit |
262 MaterialType::WaterDisplaceVolume |
263 MaterialType::Uk54 |
264 MaterialType::DefaultMaterial => {
265 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
266 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
267 v.texture_coordinate2 = data.read_vector_2_f32_from_vector_2_f16()?;
268 v.normal = data.read_vector_4_f32_normal_from_vector_4_u8()?;
269 v.tangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
270 v.bitangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
271 v.uk_1 = data.read_vector_4_u8()?;
272 },
273 MaterialType::RsTerrain => {
274 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
275 v.normal = data.read_vector_4_f32_normal_from_vector_4_f16()?;
276 },
277 MaterialType::WeightedTextureBlend => {
278 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
279 v.normal = data.read_vector_4_f32_normal_from_vector_4_f16()?;
280 },
281 MaterialType::ProjectedDecalV4 => {
282 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
283 },
284 MaterialType::RiverOutfield |
285 MaterialType::RsRiver => {
286 v.position = data.read_vector_4_f32()?;
287 v.normal = data.read_vector_4_f32()?;
288 },
289 MaterialType::ProjectedDecal |
290 MaterialType::ProjectedDecalV2 |
291 MaterialType::ProjectedDecalV3 => {
292 v.position = data.read_vector_4_f32()?;
293 },
294 MaterialType::StaticPointLight |
296 MaterialType::Uk56 |
297 MaterialType::Uk57 => {},
298 MaterialType::TextureCombo |
299 MaterialType::TextureComboVertexWind => {
300 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
301 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
302 v.normal = data.read_vector_4_f32_normal_from_vector_4_u8()?;
303 v.tangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
304 v.bitangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
305 v.uk_1 = data.read_vector_4_u8()?;
306 },
307 MaterialType::Uk27 => {
308 v.position = data.read_vector_4_f32_from_vec_3_f32()?;
309 v.texture_coordinate = Vector2::new(data.read_f32()?, data.read_f32()?);
310 v.texture_coordinate2 = Vector2::new(data.read_f32()?, data.read_f32()?);
311 v.normal = data.read_vector_4_f32_normal_from_vector_4_u8()?;
312 v.tangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
313 v.bitangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
314 v.uk_1 = data.read_vector_4_u8()?;
315 },
316 MaterialType::DebugGeometry => {
317 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
318 v.normal = data.read_vector_4_f32_normal_from_vector_4_f16()?;
319 v.color = data.read_vector_4_f32_pct_from_vector_4_u8()?;
320 },
321 MaterialType::BowWave => {
322 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
323 v.position2 = data.read_vector_4_f32_normal_from_vector_4_f16()?;
324 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
325 v.uk_4 = data.read_f32()?;
326 },
327 MaterialType::CustomTerrain => {
328 v.position = data.read_vector_4_f32()?;
329 v.uk_1 = data.read_vector_4_u8()?;
330 v.normal = data.read_vector_4_f32_normal_from_vector_4_u8()?;
331 v.tangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
332 v.texture_coordinate = Vector2::new(data.read_f32()?, data.read_f32()?);
333 },
334 MaterialType::TerrainBlend => {
335 v.position = data.read_vector_4_f32()?;
336 v.normal = data.read_vector_4_f32()?;
337 },
338 MaterialType::AlphaBlend => {
339 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
340 v.texture_coordinate2 = data.read_vector_2_f32_from_vector_2_f16()?;
341 }
342 _ => return Err(RLibError::DecodingRigidModelUnsupportedVertexFormatForMaterial(vtype.into(), mtype.into()))
343 },
344
345 VertexFormat::Collision => {
346 v.position = data.read_vector_4_f32_from_vec_3_f32()?;
347 v.normal = data.read_vector_4_f32_from_vec_3_f32()?;
348 }
349
350 VertexFormat::Weighted => {
351 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
352
353 let bone_indices = data.read_vector_2_u8()?;
354 v.bone_indices = Vector4::new(bone_indices.x, bone_indices.y, 0, 0);
355
356 let weights = data.read_vector_2_f32_pct_from_vector_2_u8()?;
357 v.weights = Vector4::new(weights.x, weights.y, 0.0, 0.0);
358
359 v.normal = data.read_vector_4_f32_normal_from_vector_4_u8()?;
360 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
361 v.tangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
362 v.bitangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
363
364 if version >= 8 {
365 v.uk_1 = data.read_vector_4_u8()?;
366 }
367 }
368 VertexFormat::Cinematic => {
369 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
370
371 v.bone_indices = data.read_vector_4_u8()?;
373 v.weights = data.read_vector_4_f32_pct_from_vector_4_u8()?;
374 v.normal = data.read_vector_4_f32_normal_from_vector_4_u8()?;
375 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
376 v.tangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
377 v.bitangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
378
379 if version >= 8 {
380 v.uk_1 = data.read_vector_4_u8()?;
381 }
382 }
383 VertexFormat::Grass => {
384 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
385 v.texture_coordinate = Vector2::new(data.read_f32()?, data.read_f32()?);
386 v.normal = data.read_vector_4_f32_normal_from_vector_4_u8()?;
387 v.tangent = data.read_vector_4_f32_normal_from_vector_4_u8()?;
388 v.uk_1 = data.read_vector_4_u8()?;
389 }
390 VertexFormat::Tree => {
391 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
392 v.position2 = data.read_vector_4_f32_normal_from_vector_4_f16()?;
393 v.normal = data.read_vector_4_f32_normal_from_vector_4_f16()?;
394 v.tangent = data.read_vector_4_f32_normal_from_vector_4_f16()?;
395 v.bitangent = data.read_vector_4_f32_normal_from_vector_4_f16()?;
396 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
397 v.texture_coordinate2 = data.read_vector_2_f32_from_vector_2_f16()?;
398 v.uk_2 = data.read_vector_2_f32_from_vector_2_f16()?;
399 v.tree_colour_wind = read_vector_4_f32_from_vector_4_f16(data)?;
400 }
401 VertexFormat::Billboard |
402 VertexFormat::Uk8 => {
403 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
404 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
405 }
406 VertexFormat::Uk12 if version <= 6 => {
407 v.position = read_vector_4_f32_from_vector_4_f16(data)?;
408 v.normal = read_vector_4_f32_from_vector_4_f16(data)?;
409 }
410 VertexFormat::Uk12 => {
411 v.position = data.read_vector_4_f32_normal_from_vector_4_f16()?;
412 v.texture_coordinate = data.read_vector_2_f32_from_vector_2_f16()?;
413 v.texture_coordinate2 = data.read_vector_2_f32_from_vector_2_f16()?;
414 v.uk_1 = data.read_vector_4_u8()?;
415 }
416 _ => return Err(RLibError::DecodingRigidModelUnknownVertexFormat(vtype.into()))
417 }
418
419 Ok(v)
420 }
421
422 pub fn write<W: WriteBytes>(&self, data: &mut W, version: u32, vtype: VertexFormat, mtype: MaterialType) -> Result<()> {
442 match vtype {
443 VertexFormat::Static => match static_layout(version, mtype) {
444 MaterialType::TiledDirtmap |
445 MaterialType::ShipAmbientmap |
446 MaterialType::Decal |
447 MaterialType::DecalDirtmap |
448 MaterialType::Dirtmap |
449 MaterialType::Cloth |
450 MaterialType::Unlit |
451 MaterialType::WaterDisplaceVolume |
452 MaterialType::Uk54 |
453 MaterialType::DefaultMaterial => {
454 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
455 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
456 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate2)?;
457 data.write_vector_4_f32_normal_as_vector_4_u8(self.normal)?;
458 data.write_vector_4_f32_normal_as_vector_4_u8(self.tangent)?;
459 data.write_vector_4_f32_normal_as_vector_4_u8(self.bitangent)?;
460
461 data.write_vector_4_u8(self.uk_1)?;
463 }
464 MaterialType::RsTerrain => {
465 data.write_vector_4_f32_normal_as_vector_4_f16(*self.position())?;
466 data.write_vector_4_f32_normal_as_vector_4_f16(*self.normal())?;
467 }
468 MaterialType::WeightedTextureBlend => {
469 data.write_vector_4_f32_normal_as_vector_4_f16(*self.position())?;
470 data.write_vector_4_f32_normal_as_vector_4_f16(*self.normal())?;
471 }
472 MaterialType::ProjectedDecalV4 => {
473 data.write_vector_4_f32_normal_as_vector_4_f16(*self.position())?;
474 }
475 MaterialType::RiverOutfield |
476 MaterialType::RsRiver => {
477 data.write_vector_4_f32(*self.position())?;
478 data.write_vector_4_f32(*self.normal())?;
479 }
480 MaterialType::ProjectedDecal |
481 MaterialType::ProjectedDecalV2 |
482 MaterialType::ProjectedDecalV3 => {
483 data.write_vector_4_f32(*self.position())?;
484 }
485 MaterialType::StaticPointLight |
486 MaterialType::Uk56 |
487 MaterialType::Uk57 => {}
488 MaterialType::TextureCombo |
489 MaterialType::TextureComboVertexWind => {
490 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
491 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
492 data.write_vector_4_f32_normal_as_vector_4_u8(self.normal)?;
493 data.write_vector_4_f32_normal_as_vector_4_u8(self.tangent)?;
494 data.write_vector_4_f32_normal_as_vector_4_u8(self.bitangent)?;
495 data.write_vector_4_u8(self.uk_1)?;
496 }
497 MaterialType::Uk27 => {
498 data.write_vector_4_f32_to_vector_3_f32(self.position)?;
499 data.write_f32(self.texture_coordinate.x)?;
500 data.write_f32(self.texture_coordinate.y)?;
501 data.write_f32(self.texture_coordinate2.x)?;
502 data.write_f32(self.texture_coordinate2.y)?;
503 data.write_vector_4_f32_normal_as_vector_4_u8(self.normal)?;
504 data.write_vector_4_f32_normal_as_vector_4_u8(self.tangent)?;
505 data.write_vector_4_f32_normal_as_vector_4_u8(self.bitangent)?;
506 data.write_vector_4_u8(self.uk_1)?;
507 }
508 MaterialType::DebugGeometry => {
509 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
510 data.write_vector_4_f32_normal_as_vector_4_f16(self.normal)?;
511 data.write_vector_4_f32_pct_as_vector_4_u8(self.color)?;
512 }
513 MaterialType::BowWave => {
514 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
515 data.write_vector_4_f32_normal_as_vector_4_f16(self.position2)?;
516 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
517 data.write_f32(self.uk_4)?;
518 }
519 MaterialType::CustomTerrain => {
520 data.write_vector_4_f32(self.position)?;
521 data.write_vector_4_u8(self.uk_1)?;
522 data.write_vector_4_f32_normal_as_vector_4_u8(self.normal)?;
523 data.write_vector_4_f32_normal_as_vector_4_u8(self.tangent)?;
524 data.write_f32(self.texture_coordinate.x)?;
525 data.write_f32(self.texture_coordinate.y)?;
526 }
527 MaterialType::TerrainBlend => {
528 data.write_vector_4_f32(*self.position())?;
529 data.write_vector_4_f32(*self.normal())?;
530 }
531 MaterialType::AlphaBlend => {
532 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
533 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate2)?;
534 }
535 _ => return Err(RLibError::DecodingRigidModelUnsupportedVertexFormatForMaterial(vtype.into(), mtype.into()))
536 },
537
538 VertexFormat::Collision => {
539 data.write_vector_4_f32_to_vector_3_f32(self.position)?;
540 data.write_vector_4_f32_to_vector_3_f32(self.normal)?;
541 }
542
543 VertexFormat::Weighted => {
544 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
545 data.write_vector_2_u8(Vector2::new(self.bone_indices.x, self.bone_indices.y))?;
546 data.write_vector_2_f32_pct_as_vector_2_u8(Vector2::new(self.weights.x, self.weights.y))?;
547 data.write_vector_4_f32_normal_as_vector_4_u8(self.normal)?;
548 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
549 data.write_vector_4_f32_normal_as_vector_4_u8(self.tangent)?;
550 data.write_vector_4_f32_normal_as_vector_4_u8(self.bitangent)?;
551
552 if version >= 8 {
553 data.write_vector_4_u8(self.uk_1)?;
554 }
555 }
556 VertexFormat::Cinematic => {
557 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
558 data.write_vector_4_u8(self.bone_indices)?;
559 data.write_vector_4_f32_pct_as_vector_4_u8(self.weights)?;
560 data.write_vector_4_f32_normal_as_vector_4_u8(self.normal)?;
561 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
562 data.write_vector_4_f32_normal_as_vector_4_u8(self.tangent)?;
563 data.write_vector_4_f32_normal_as_vector_4_u8(self.bitangent)?;
564
565 if version >= 8 {
566 data.write_vector_4_u8(self.uk_1)?;
567 }
568 }
569 VertexFormat::Grass => {
570 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
571 data.write_f32(self.texture_coordinate.x)?;
572 data.write_f32(self.texture_coordinate.y)?;
573 data.write_vector_4_f32_normal_as_vector_4_u8(self.normal)?;
574 data.write_vector_4_f32_normal_as_vector_4_u8(self.tangent)?;
575 data.write_vector_4_u8(self.uk_1)?;
576 }
577 VertexFormat::Tree => {
578 data.write_vector_4_f32_normal_as_vector_4_f16(self.position)?;
579 data.write_vector_4_f32_normal_as_vector_4_f16(self.position2)?;
580 data.write_vector_4_f32_normal_as_vector_4_f16(self.normal)?;
581 data.write_vector_4_f32_normal_as_vector_4_f16(self.tangent)?;
582 data.write_vector_4_f32_normal_as_vector_4_f16(self.bitangent)?;
583 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
584 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate2)?;
585 data.write_vector_2_f32_as_vector_2_f16(self.uk_2)?;
586 write_vector_4_f32_as_vector_4_f16(data, self.tree_colour_wind)?;
587 }
588 VertexFormat::Billboard |
589 VertexFormat::Uk8 => {
590 data.write_vector_4_f32_normal_as_vector_4_f16(*self.position())?;
591 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
592 }
593 VertexFormat::Uk12 if version <= 6 => {
594 write_vector_4_f32_as_vector_4_f16(data, self.position)?;
595 write_vector_4_f32_as_vector_4_f16(data, self.normal)?;
596 }
597 VertexFormat::Uk12 => {
598 data.write_vector_4_f32_normal_as_vector_4_f16(*self.position())?;
599 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate)?;
600 data.write_vector_2_f32_as_vector_2_f16(self.texture_coordinate2)?;
601 data.write_vector_4_u8(self.uk_1)?;
602 }
603 _ => return Err(RLibError::DecodingRigidModelUnknownVertexFormat(vtype.into()))
604 }
605
606 Ok(())
607 }
608}
609fn static_layout(version: u32, mtype: MaterialType) -> MaterialType {
615 match mtype {
616 MaterialType::TerrainBlend if version <= 6 => MaterialType::DefaultMaterial,
617 _ => mtype,
618 }
619}
620
621fn read_vector_4_f32_from_vector_4_f16<R: ReadBytes>(data: &mut R) -> Result<Vector4<f32>> {
623 Ok(Vector4::new(data.read_f16()?.to_f32(), data.read_f16()?.to_f32(), data.read_f16()?.to_f32(), data.read_f16()?.to_f32()))
624}
625
626fn write_vector_4_f32_as_vector_4_f16<W: WriteBytes>(data: &mut W, value: Vector4<f32>) -> Result<()> {
628 for component in value.iter() {
629 data.write_f16(f16::from_f32(*component))?;
630 }
631
632 Ok(())
633}
634
635