Crate wow_m2

Crate wow_m2 

Source
Expand description

A parser for World of Warcraft M2 model files with version conversion support

This library allows parsing, validating, and converting M2 model files between different versions of the World of Warcraft client, with automatic format detection for SKIN and ANIM dependencies.

§Format Evolution

M2 files evolved significantly across WoW versions:

  • Classic/TBC: Embedded SKIN and ANIM data within M2
  • WotLK: External SKIN files introduced
  • Cataclysm/MoP: External ANIM files (raw format)
  • Legion+: ANIM files use chunked format with MAOF magic

§Example

use wow_m2::{
    M2Model, M2Format, SkinFile, AnimFile, AnimFormat, M2Version, M2Converter,
    CoordinateSystem, CoordinateTransformer, transform_position,
    skinning::{M2Skinner, SkinningOptions}
};

// Load a model with format detection
let model_format = M2Model::load("path/to/model.m2").unwrap();
let model = model_format.model();

// Print model info
println!("Model name: {:?}", model.name);
println!("Model version: {:?}", model.header.version());
println!("Vertices: {}", model.vertices.len());
println!("Is chunked format: {}", model_format.is_chunked());

// Transform vertices using skinning system
let mut skinner = M2Skinner::new(&model.bones, SkinningOptions::default());
skinner.calculate_bind_pose();
let skinned_vertices = skinner.skin_vertices(&model.vertices);
for (i, vertex) in skinned_vertices.iter().enumerate() {
    println!("Skinned vertex {}: {:?}", i, vertex);
}

// Transform coordinates for Blender
let transformer = CoordinateTransformer::new(CoordinateSystem::Blender);
for vertex in &model.vertices {
    let blender_pos = transformer.transform_position(vertex.position);
    println!("WoW: {:?} → Blender: {:?}", vertex.position, blender_pos);
}

// Convert to a different version
let converter = M2Converter::new();
let converted = converter.convert(model, M2Version::MoP).unwrap();

// Save the converted model
converted.save("path/to/converted.m2").unwrap();

// Load SKIN files with automatic format detection
let skin_file = SkinFile::load("path/to/model00.skin").unwrap();
match &skin_file {
    SkinFile::New(skin) => println!("New format SKIN with version {}", skin.header.version),
    SkinFile::Old(skin) => println!("Old format SKIN with {} indices", skin.indices.len()),
}

// Access data regardless of format
let indices = skin_file.indices();
let submeshes = skin_file.submeshes();
println!("SKIN has {} indices and {} submeshes", indices.len(), submeshes.len());

// Load ANIM files with automatic format detection
let anim_file = AnimFile::load("path/to/model0-0.anim").unwrap();
match &anim_file.format {
    AnimFormat::Legacy => println!("Legacy format with {} sections", anim_file.sections.len()),
    AnimFormat::Modern => println!("Modern format with {} sections", anim_file.sections.len()),
}

§Vertex Skinning

The library provides a comprehensive vertex skinning system that transforms vertices from their bind pose using bone weights and transformations:

use wow_m2::{M2Model, skinning::{M2Skinner, SkinningOptions}};

// Load a model
let model_format = M2Model::load("path/to/model.m2").unwrap();
let model = model_format.model();

// Create skinner with options
let options = SkinningOptions {
    normalize_weights: true,
    weight_threshold: 0.001,
    validate_bone_indices: true,
    handle_invalid_indices: true,
};
let mut skinner = M2Skinner::new(&model.bones, options);

// Calculate bind pose (rest position)
skinner.calculate_bind_pose();

// Transform all vertices
let skinned_vertices = skinner.skin_vertices(&model.vertices);
println!("Transformed {} vertices", skinned_vertices.len());

// Or transform individual vertices
for vertex in &model.vertices {
    let skinned_pos = skinner.skin_single_vertex(vertex);
    println!("Original: {:?} → Skinned: {:?}", vertex.position, skinned_pos);
}

§Vertex Validation

The library supports different validation modes when parsing vertex data to handle corruption in older WoW models while preserving valid static geometry:

use wow_m2::{ValidationMode, chunks::vertex::M2Vertex};
use std::io::Cursor;

let mut cursor = Cursor::new(data);

// Strict mode: fixes all zero-weight vertices (legacy behavior)
// Use when you need all vertices to be animated
let vertex = M2Vertex::parse_with_validation(
    &mut cursor, 256, Some(34), ValidationMode::Strict
);

// Permissive mode: preserves valid static geometry, fixes corruption (default)
// Best balance between fixing corruption and preserving intentional data
let vertex = M2Vertex::parse_with_validation(
    &mut cursor, 256, Some(34), ValidationMode::Permissive
);

// No validation: preserves all original data
// Use when you need exact original data, even if corrupted
let vertex = M2Vertex::parse_with_validation(
    &mut cursor, 256, Some(34), ValidationMode::None
);

Re-exports§

pub use anim::AnimFile;
pub use anim::AnimFormat;
pub use anim::AnimMetadata;
pub use anim::AnimSection;
pub use anim::MemoryUsage;
pub use animation::AnimSequence;
pub use animation::AnimationManager;
pub use animation::AnimationManagerBuilder;
pub use animation::AnimationState;
pub use animation::BoneFlags;
pub use animation::BoneTransformComputer;
pub use animation::ComputedBone;
pub use animation::Fixedi16;
pub use animation::Lerp;
pub use animation::Mat4 as AnimMat4;
pub use animation::Quat;
pub use animation::ResolvedBone;
pub use animation::ResolvedTrack;
pub use animation::Vec3 as AnimVec3;
pub use chunks::particle_emitter::M2ParticleEmitter;
pub use chunks::particle_emitter::M2ParticleEmitterType;
pub use chunks::particle_emitter::M2ParticleFlags;
pub use chunks::vertex::ValidationMode;
pub use converter::M2Converter;
pub use coordinate::CoordinateSystem;
pub use coordinate::CoordinateTransformer;
pub use coordinate::transform_position;
pub use coordinate::transform_quaternion;
pub use error::M2Error;
pub use error::Result;
pub use file_resolver::FileResolver;
pub use file_resolver::ListfileResolver;
pub use file_resolver::PathResolver;
pub use model::M2Format;
pub use model::M2Model;
pub use model::parse_m2;
pub use model_animation_resolver::M2ModelAnimationExt;
pub use model_animation_resolver::ResolvedBoneAnimation;
pub use model_enhanced::AnimationInfo;
pub use model_enhanced::BoneInfo;
pub use model_enhanced::BoundingBox;
pub use model_enhanced::EnhancedModelData;
pub use model_enhanced::MaterialInfo;
pub use model_enhanced::ModelStats;
pub use model_enhanced::TextureInfo;
pub use particles::EmissionType;
pub use particles::EmitterParams;
pub use particles::Particle;
pub use particles::ParticleEmitter;
pub use particles::TEXELS_PER_PARTICLE;
pub use skin::OldSkin;
pub use skin::Skin;
pub use skin::SkinFile;
pub use skin::load_skin;
pub use skin::parse_skin;
pub use skinning::BoneTransform;
pub use skinning::M2Skinner;
pub use skinning::SkinningOptions;
pub use version::M2Version;

Modules§

anim
animation
M2 Animation System
chunks
common
converter
coordinate
Coordinate system transformations for World of Warcraft models
embedded_skin
Support for parsing embedded skin data from pre-WotLK M2 models
error
file_resolver
FileDataID resolution system for M2 chunked format
header
io_ext
model
model_animation_resolver
model_enhanced
Enhanced M2 model parser with comprehensive data extraction methods
particles
Particle system for M2 models
skin
skinning
Vertex skinning system for M2 models
version

Structs§

BlpTexture
Parsed information from BLP file. The structure of the type strictly follows how the file is stored on the disk for easy encoding/decoding and further transformations.

Constants§

VERSION
Library version