mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-15 06:42:31 +00:00
* fix(model): share decoded textures and skin matrices, bound texture pixels, index OBJ materials Texture::decode expanded any image to float RGBA without a size check. Repeated mtllib lines reparsed the library and decoded its textures again; glTF textures with a non-default sampler each copied their image's pixels; every instance of a skin built its own joint matrices. OBJ usemtl and faces scanned the growing material and primitive lists, and contour grouping kept every ancestor of every contour. * address review: keep largest contours and dedupe mtllib by set * address review: contour depth counts containers, glTF wrap copies and FBX textures share the import pixel budget * address review: holes go to the innermost outline; glTF wrap variants keyed by decoded image * address review: create every outline slot before attaching holes * Import: a model's left-out parts reach the user as an import warning (Model::warnings, Importer::probe_noting) * Models: left-out textures are import warnings; the import texture budget is 2^29 pixels * Import: a model's left-out parts reach the user as an import warning (Model::warnings, Importer::probe_noting) * Import: a model's left-out parts reach the user as an import warning * FBX textures move to #2955 (it reworks the same code)
38 lines
1.8 KiB
Rust
38 lines
1.8 KiB
Rust
//! Wavefront OBJ geometry and MTL materials, read with `craft-mesh` (`craft_mesh::obj`).
|
|
//!
|
|
//! The material library and the textures it names are read through the caller's resolver: a
|
|
//! texture is looked up by the name as written (a name containing spaces is one name), then by
|
|
//! its file name (an absolute path from another machine, #1274), then by the final token (the
|
|
//! options convention, `-bm 1 file.png`). Faces without normals get smooth normals within a
|
|
//! smoothing group and flat ones where smoothing is off.
|
|
|
|
use crate::scene::{MissingNormals, error, limits, to_model};
|
|
use crate::{Model, Result};
|
|
|
|
pub use craft_mesh::srgb_to_linear;
|
|
|
|
/// A material reference resolved against the model's folder: the name as written, then its file
|
|
/// name, then the final token.
|
|
fn resolve_reference(resolve: &dyn Fn(&str) -> Option<Vec<u8>>, name: &str) -> Option<Vec<u8>> {
|
|
let mut tried: Vec<&str> = vec![];
|
|
for candidate in [name, craft_mesh::obj::sibling_name(name).unwrap_or(name), name.split_whitespace().last().unwrap_or(name)] {
|
|
if tried.contains(&candidate) {
|
|
continue;
|
|
}
|
|
tried.push(candidate);
|
|
if let Some(b) = resolve(candidate) {
|
|
return Some(b);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Parse OBJ text (and its MTL libraries and textures through `resolve`).
|
|
pub fn parse(bytes: &[u8], resolve: &dyn Fn(&str) -> Option<Vec<u8>>) -> Result<Model> {
|
|
// `craft-mesh` splits `mtllib` names and tries each complete library path itself. Applying
|
|
// texture-option fallback here first can mistake the last library for the whole directive.
|
|
let mut library = |name: &str| resolve(name);
|
|
let scene = craft_mesh::obj::read(bytes, &limits(), &mut library).map_err(error)?;
|
|
to_model(scene, MissingNormals::BySmoothing, &|im| resolve_reference(resolve, im.uri.as_deref()?.trim()))
|
|
}
|