Implement native asset pipeline and cache (#64)
Some checks failed
Native code generation / deterministic (push) Failing after 2m18s
Imaging and meshing gate / native (push) Failing after 1m30s
JPEG 2000 feature / linux (push) Successful in 2m40s
Native Rust workspace compile / compile (push) Failing after 57s
Skia feature / linux (push) Successful in 31m8s
Some checks failed
Native code generation / deterministic (push) Failing after 2m18s
Imaging and meshing gate / native (push) Failing after 1m30s
JPEG 2000 feature / linux (push) Successful in 2m40s
Native Rust workspace compile / compile (push) Failing after 57s
Skia feature / linux (push) Successful in 31m8s
This commit is contained in:
719
crates/libremetaverse/src/asset_material.rs
Normal file
719
crates/libremetaverse/src/asset_material.rs
Normal file
@@ -0,0 +1,719 @@
|
||||
//! GLTF material asset and simulator material-override encoding.
|
||||
|
||||
#![allow(
|
||||
clippy::assigning_clones,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::collapsible_if,
|
||||
clippy::float_cmp,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::must_use_candidate,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::should_implement_trait
|
||||
)]
|
||||
|
||||
use crate::{Error, assets::GltfAlphaMode};
|
||||
use libremetaverse_structured_data::{OSD, OSDMap};
|
||||
use libremetaverse_types::{AssetType, Color4, UUID, Vector2, Vector3};
|
||||
use serde_json::{Value, json};
|
||||
use std::collections::HashMap;
|
||||
|
||||
const FLOAT_EPSILON: f32 = 1.192_092_9e-7;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct GltfTextureTransform {
|
||||
pub offset: Vector2,
|
||||
pub rotation: f32,
|
||||
pub scale: Vector2,
|
||||
}
|
||||
|
||||
impl GltfTextureTransform {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
offset: Vector2::zero(),
|
||||
rotation: 0.0,
|
||||
scale: Vector2 { x: 1.0, y: 1.0 },
|
||||
}
|
||||
}
|
||||
pub fn equals_with_gltf_texture_transform(&self, other: Self) -> bool {
|
||||
*self == other
|
||||
}
|
||||
pub fn equals_with_object(&self, _obj: Option<libremetaverse_types::compat::Object>) -> bool {
|
||||
false
|
||||
}
|
||||
pub fn get_hash_code(&self) -> i32 {
|
||||
(self.offset.x.to_bits()
|
||||
^ self.offset.y.to_bits()
|
||||
^ self.rotation.to_bits()
|
||||
^ self.scale.x.to_bits()
|
||||
^ self.scale.y.to_bits())
|
||||
.cast_signed()
|
||||
}
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.offset.x.abs() <= FLOAT_EPSILON
|
||||
&& self.offset.y.abs() <= FLOAT_EPSILON
|
||||
&& self.rotation.abs() <= FLOAT_EPSILON
|
||||
&& (self.scale.x - 1.0).abs() <= FLOAT_EPSILON
|
||||
&& (self.scale.y - 1.0).abs() <= FLOAT_EPSILON
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AssetMaterial {
|
||||
asset_id: UUID,
|
||||
asset_data: Vec<u8>,
|
||||
name: String,
|
||||
texture_ids: Vec<UUID>,
|
||||
texture_transforms: Vec<GltfTextureTransform>,
|
||||
base_color_factor: Color4,
|
||||
emissive_factor: Vector3,
|
||||
metallic_factor: f32,
|
||||
roughness_factor: f32,
|
||||
alpha_cutoff: f32,
|
||||
alpha_mode: GltfAlphaMode,
|
||||
double_sided: bool,
|
||||
override_alpha_mode: bool,
|
||||
override_double_sided: bool,
|
||||
}
|
||||
|
||||
impl AssetMaterial {
|
||||
pub const TEXTURE_BASE_COLOR: i32 = 0;
|
||||
pub const TEXTURE_NORMAL: i32 = 1;
|
||||
pub const TEXTURE_METALLIC_ROUGHNESS: i32 = 2;
|
||||
pub const TEXTURE_EMISSIVE: i32 = 3;
|
||||
pub const TEXTURE_COUNT: i32 = 4;
|
||||
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
asset_id: UUID::zero(),
|
||||
asset_data: Vec::new(),
|
||||
name: String::new(),
|
||||
texture_ids: vec![UUID::zero(); Self::TEXTURE_COUNT as usize],
|
||||
texture_transforms: vec![GltfTextureTransform::default(); Self::TEXTURE_COUNT as usize],
|
||||
base_color_factor: Color4 {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
},
|
||||
emissive_factor: Vector3::zero(),
|
||||
metallic_factor: 1.0,
|
||||
roughness_factor: 1.0,
|
||||
alpha_cutoff: 0.5,
|
||||
alpha_mode: GltfAlphaMode::Opaque,
|
||||
double_sided: false,
|
||||
override_alpha_mode: false,
|
||||
override_double_sided: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gltf_override_null_uuid() -> UUID {
|
||||
UUID::new_with_string("ffffffff-ffff-ffff-ffff-ffffffffffff".into())
|
||||
.expect("constant UUID is valid")
|
||||
}
|
||||
|
||||
pub fn new_with_constructor() -> Result<Self, Error> {
|
||||
Ok(Self::default())
|
||||
}
|
||||
|
||||
pub fn new_with_uuid_bytes(asset_id: UUID, asset_data: Vec<u8>) -> Result<Self, Error> {
|
||||
if asset_data.len() > crate::asset_models::MAX_ASSET_BYTES {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
let mut material = Self {
|
||||
asset_id,
|
||||
asset_data,
|
||||
..Self::default()
|
||||
};
|
||||
if !material.decode()? {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
Ok(material)
|
||||
}
|
||||
|
||||
fn slot(slot: i32) -> Result<usize, Error> {
|
||||
usize::try_from(slot)
|
||||
.ok()
|
||||
.filter(|slot| *slot < Self::TEXTURE_COUNT as usize)
|
||||
.ok_or(Error::Argument)
|
||||
}
|
||||
|
||||
pub fn set_texture_id(
|
||||
&mut self,
|
||||
slot: i32,
|
||||
id: UUID,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
let slot = Self::slot(slot)?;
|
||||
self.texture_ids[slot] = if for_override.unwrap_or(false) && id == UUID::zero() {
|
||||
Self::gltf_override_null_uuid()
|
||||
} else {
|
||||
id
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_texture_offset(&mut self, slot: i32, offset: Vector2) -> Result<(), Error> {
|
||||
self.texture_transforms[Self::slot(slot)?].offset = offset;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_texture_scale(&mut self, slot: i32, scale: Vector2) -> Result<(), Error> {
|
||||
self.texture_transforms[Self::slot(slot)?].scale = scale;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_texture_rotation(&mut self, slot: i32, rotation: f32) -> Result<(), Error> {
|
||||
self.texture_transforms[Self::slot(slot)?].rotation = rotation;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_base_color_factor_with_color4_boolean(
|
||||
&mut self,
|
||||
mut color: Color4,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
if for_override.unwrap_or(false) && color == Self::default().base_color_factor {
|
||||
color.a -= FLOAT_EPSILON;
|
||||
}
|
||||
self.base_color_factor = color;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_emissive_factor_with_vector3_boolean(
|
||||
&mut self,
|
||||
mut color: Vector3,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
if for_override.unwrap_or(false) && color == Vector3::zero() {
|
||||
color.x += FLOAT_EPSILON;
|
||||
}
|
||||
self.emissive_factor = color;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_metallic_factor_with_single_boolean(
|
||||
&mut self,
|
||||
metallic: f32,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
if !metallic.is_finite() {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
self.metallic_factor = if for_override.unwrap_or(false) {
|
||||
metallic.min(1.0 - FLOAT_EPSILON)
|
||||
} else {
|
||||
metallic
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_roughness_factor_with_single_boolean(
|
||||
&mut self,
|
||||
roughness: f32,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
if !roughness.is_finite() {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
self.roughness_factor = if for_override.unwrap_or(false) {
|
||||
roughness.min(1.0 - FLOAT_EPSILON)
|
||||
} else {
|
||||
roughness
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_alpha_cutoff_with_single_boolean(
|
||||
&mut self,
|
||||
cutoff: f32,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
if !cutoff.is_finite() {
|
||||
return Err(Error::Argument);
|
||||
}
|
||||
self.alpha_cutoff = if for_override.unwrap_or(false) && cutoff == 0.5 {
|
||||
cutoff - FLOAT_EPSILON
|
||||
} else {
|
||||
cutoff
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_alpha_mode_with_gltf_alpha_mode_boolean(
|
||||
&mut self,
|
||||
mode: GltfAlphaMode,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
self.alpha_mode = mode;
|
||||
self.override_alpha_mode = for_override.unwrap_or(false);
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_double_sided_with_boolean_boolean(
|
||||
&mut self,
|
||||
double_sided: bool,
|
||||
for_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
self.double_sided = double_sided;
|
||||
self.override_double_sided = for_override.unwrap_or(false);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_base_material(&mut self) -> Result<(), Error> {
|
||||
let transforms = self.texture_transforms.clone();
|
||||
let asset_id = self.asset_id;
|
||||
*self = Self::default();
|
||||
self.asset_id = asset_id;
|
||||
self.texture_transforms = transforms;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_override(&mut self, value: Self) -> Result<(), Error> {
|
||||
let sentinel = Self::gltf_override_null_uuid();
|
||||
for slot in 0..Self::TEXTURE_COUNT as usize {
|
||||
if value.texture_ids[slot] == sentinel {
|
||||
self.texture_ids[slot] = UUID::zero();
|
||||
} else if value.texture_ids[slot] != UUID::zero() {
|
||||
self.texture_ids[slot] = value.texture_ids[slot];
|
||||
}
|
||||
if !value.texture_transforms[slot].is_default() {
|
||||
self.texture_transforms[slot] = value.texture_transforms[slot];
|
||||
}
|
||||
}
|
||||
let defaults = Self::default();
|
||||
if value.base_color_factor != defaults.base_color_factor {
|
||||
self.base_color_factor = value.base_color_factor;
|
||||
}
|
||||
if value.emissive_factor != defaults.emissive_factor {
|
||||
self.emissive_factor = value.emissive_factor;
|
||||
}
|
||||
if value.metallic_factor != defaults.metallic_factor {
|
||||
self.metallic_factor = value.metallic_factor;
|
||||
}
|
||||
if value.roughness_factor != defaults.roughness_factor {
|
||||
self.roughness_factor = value.roughness_factor;
|
||||
}
|
||||
if value.alpha_cutoff != defaults.alpha_cutoff {
|
||||
self.alpha_cutoff = value.alpha_cutoff;
|
||||
}
|
||||
if value.override_alpha_mode {
|
||||
self.alpha_mode = value.alpha_mode;
|
||||
}
|
||||
if value.override_double_sided {
|
||||
self.double_sided = value.double_sided;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alpha_name(mode: GltfAlphaMode) -> &'static str {
|
||||
match mode {
|
||||
GltfAlphaMode::Opaque => "OPAQUE",
|
||||
GltfAlphaMode::Blend => "BLEND",
|
||||
GltfAlphaMode::Mask => "MASK",
|
||||
}
|
||||
}
|
||||
fn parse_alpha(value: Option<&str>) -> Result<GltfAlphaMode, Error> {
|
||||
match value.unwrap_or("OPAQUE") {
|
||||
"OPAQUE" => Ok(GltfAlphaMode::Opaque),
|
||||
"BLEND" => Ok(GltfAlphaMode::Blend),
|
||||
"MASK" => Ok(GltfAlphaMode::Mask),
|
||||
_ => Err(Error::Argument),
|
||||
}
|
||||
}
|
||||
|
||||
fn texture_json(
|
||||
&self,
|
||||
slot: usize,
|
||||
images: &mut Vec<Value>,
|
||||
textures: &mut Vec<Value>,
|
||||
) -> Option<Value> {
|
||||
let id = self.texture_ids[slot];
|
||||
if id == UUID::zero() || id == Self::gltf_override_null_uuid() {
|
||||
return None;
|
||||
}
|
||||
let index = images.len();
|
||||
images.push(json!({"uri": id.to_string()}));
|
||||
textures.push(json!({"source": index}));
|
||||
let mut info = json!({"index": index});
|
||||
let transform = self.texture_transforms[slot];
|
||||
if !transform.is_default() {
|
||||
info["extensions"] = json!({"KHR_texture_transform": {"offset": [transform.offset.x, transform.offset.y], "scale": [transform.scale.x, transform.scale.y], "rotation": transform.rotation}});
|
||||
}
|
||||
Some(info)
|
||||
}
|
||||
|
||||
pub fn to_json(&self) -> Result<String, Error> {
|
||||
let mut images = Vec::new();
|
||||
let mut textures = Vec::new();
|
||||
let base = self.texture_json(
|
||||
Self::TEXTURE_BASE_COLOR as usize,
|
||||
&mut images,
|
||||
&mut textures,
|
||||
);
|
||||
let normal = self.texture_json(Self::TEXTURE_NORMAL as usize, &mut images, &mut textures);
|
||||
let metallic = self.texture_json(
|
||||
Self::TEXTURE_METALLIC_ROUGHNESS as usize,
|
||||
&mut images,
|
||||
&mut textures,
|
||||
);
|
||||
let emissive =
|
||||
self.texture_json(Self::TEXTURE_EMISSIVE as usize, &mut images, &mut textures);
|
||||
let mut pbr = json!({"baseColorFactor": [self.base_color_factor.r,self.base_color_factor.g,self.base_color_factor.b,self.base_color_factor.a], "metallicFactor": self.metallic_factor, "roughnessFactor": self.roughness_factor});
|
||||
if let Some(value) = base {
|
||||
pbr["baseColorTexture"] = value;
|
||||
}
|
||||
if let Some(value) = metallic {
|
||||
pbr["metallicRoughnessTexture"] = value;
|
||||
}
|
||||
let mut material = json!({"name": self.name, "pbrMetallicRoughness": pbr, "emissiveFactor": [self.emissive_factor.x,self.emissive_factor.y,self.emissive_factor.z], "alphaMode": Self::alpha_name(self.alpha_mode), "alphaCutoff": self.alpha_cutoff, "doubleSided": self.double_sided});
|
||||
if let Some(value) = normal {
|
||||
material["normalTexture"] = value;
|
||||
}
|
||||
if let Some(value) = emissive {
|
||||
material["emissiveTexture"] = value;
|
||||
}
|
||||
let mut root = json!({"asset":{"version":"2.0"},"materials":[material]});
|
||||
if !images.is_empty() {
|
||||
root["images"] = Value::Array(images);
|
||||
root["textures"] = Value::Array(textures);
|
||||
root["extensionsUsed"] = json!(["KHR_texture_transform"]);
|
||||
}
|
||||
serde_json::to_string(&root).map_err(|_| Error::InvalidOperation)
|
||||
}
|
||||
|
||||
pub fn encode(&mut self) -> Result<(), Error> {
|
||||
self.asset_data = self.to_json()?.into_bytes();
|
||||
Ok(())
|
||||
}
|
||||
pub fn decode(&mut self) -> Result<bool, Error> {
|
||||
let root: Value = serde_json::from_slice(&self.asset_data).map_err(|_| Error::Argument)?;
|
||||
let mat = root
|
||||
.get("materials")
|
||||
.and_then(Value::as_array)
|
||||
.and_then(|v| v.first())
|
||||
.and_then(Value::as_object)
|
||||
.ok_or(Error::Argument)?;
|
||||
self.name = mat
|
||||
.get("name")
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or_default()
|
||||
.to_owned();
|
||||
let pbr = mat.get("pbrMetallicRoughness").and_then(Value::as_object);
|
||||
if let Some(values) = pbr
|
||||
.and_then(|v| v.get("baseColorFactor"))
|
||||
.and_then(Value::as_array)
|
||||
{
|
||||
if values.len() == 4 {
|
||||
self.base_color_factor = Color4 {
|
||||
r: number(&values[0])?,
|
||||
g: number(&values[1])?,
|
||||
b: number(&values[2])?,
|
||||
a: number(&values[3])?,
|
||||
};
|
||||
}
|
||||
}
|
||||
self.metallic_factor = pbr
|
||||
.and_then(|v| v.get("metallicFactor"))
|
||||
.map(number)
|
||||
.transpose()?
|
||||
.unwrap_or(1.0);
|
||||
self.roughness_factor = pbr
|
||||
.and_then(|v| v.get("roughnessFactor"))
|
||||
.map(number)
|
||||
.transpose()?
|
||||
.unwrap_or(1.0);
|
||||
if let Some(values) = mat.get("emissiveFactor").and_then(Value::as_array) {
|
||||
if values.len() == 3 {
|
||||
self.emissive_factor = Vector3 {
|
||||
x: number(&values[0])?,
|
||||
y: number(&values[1])?,
|
||||
z: number(&values[2])?,
|
||||
};
|
||||
}
|
||||
}
|
||||
self.alpha_mode = Self::parse_alpha(mat.get("alphaMode").and_then(Value::as_str))?;
|
||||
self.alpha_cutoff = mat
|
||||
.get("alphaCutoff")
|
||||
.map(number)
|
||||
.transpose()?
|
||||
.unwrap_or(0.5);
|
||||
self.double_sided = mat
|
||||
.get("doubleSided")
|
||||
.and_then(Value::as_bool)
|
||||
.unwrap_or(false);
|
||||
let textures = root.get("textures").and_then(Value::as_array);
|
||||
let images = root.get("images").and_then(Value::as_array);
|
||||
for (slot, info) in [
|
||||
(0, pbr.and_then(|v| v.get("baseColorTexture"))),
|
||||
(1, mat.get("normalTexture")),
|
||||
(2, pbr.and_then(|v| v.get("metallicRoughnessTexture"))),
|
||||
(3, mat.get("emissiveTexture")),
|
||||
] {
|
||||
if let Some(info) = info {
|
||||
self.decode_texture(slot, info, textures, images)?;
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn decode_texture(
|
||||
&mut self,
|
||||
slot: usize,
|
||||
info: &Value,
|
||||
textures: Option<&Vec<Value>>,
|
||||
images: Option<&Vec<Value>>,
|
||||
) -> Result<(), Error> {
|
||||
let index = info
|
||||
.get("index")
|
||||
.and_then(Value::as_u64)
|
||||
.and_then(|v| usize::try_from(v).ok())
|
||||
.ok_or(Error::Argument)?;
|
||||
let source = textures
|
||||
.and_then(|v| v.get(index))
|
||||
.and_then(|v| v.get("source"))
|
||||
.and_then(Value::as_u64)
|
||||
.and_then(|v| usize::try_from(v).ok())
|
||||
.ok_or(Error::Argument)?;
|
||||
let uri = images
|
||||
.and_then(|v| v.get(source))
|
||||
.and_then(|v| v.get("uri"))
|
||||
.and_then(Value::as_str)
|
||||
.ok_or(Error::Argument)?;
|
||||
self.texture_ids[slot] = UUID::new_with_string(uri.to_owned())?;
|
||||
if let Some(transform) = info.pointer("/extensions/KHR_texture_transform") {
|
||||
if let Some(v) = transform.get("offset").and_then(Value::as_array) {
|
||||
if v.len() == 2 {
|
||||
self.texture_transforms[slot].offset = Vector2 {
|
||||
x: number(&v[0])?,
|
||||
y: number(&v[1])?,
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some(v) = transform.get("scale").and_then(Value::as_array) {
|
||||
if v.len() == 2 {
|
||||
self.texture_transforms[slot].scale = Vector2 {
|
||||
x: number(&v[0])?,
|
||||
y: number(&v[1])?,
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some(v) = transform.get("rotation") {
|
||||
self.texture_transforms[slot].rotation = number(v)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn to_override_osd(&self) -> Result<OSDMap, Error> {
|
||||
let defaults = Self::default();
|
||||
let mut map = HashMap::new();
|
||||
let mut tex = Vec::new();
|
||||
let mut last = None;
|
||||
for (index, id) in self.texture_ids.iter().enumerate() {
|
||||
if *id != UUID::zero() {
|
||||
last = Some(index);
|
||||
}
|
||||
}
|
||||
if let Some(last) = last {
|
||||
for id in &self.texture_ids[..=last] {
|
||||
tex.push(OSD::UUID(*id));
|
||||
}
|
||||
map.insert("tex".into(), OSD::Array(tex));
|
||||
}
|
||||
if self.base_color_factor != defaults.base_color_factor {
|
||||
map.insert("bc".into(), color_osd(self.base_color_factor));
|
||||
}
|
||||
if self.emissive_factor != defaults.emissive_factor {
|
||||
map.insert("ec".into(), vector3_osd(self.emissive_factor));
|
||||
}
|
||||
if self.metallic_factor != defaults.metallic_factor {
|
||||
map.insert("mf".into(), OSD::Real(f64::from(self.metallic_factor)));
|
||||
}
|
||||
if self.roughness_factor != defaults.roughness_factor {
|
||||
map.insert("rf".into(), OSD::Real(f64::from(self.roughness_factor)));
|
||||
}
|
||||
if self.override_alpha_mode {
|
||||
map.insert("am".into(), OSD::Integer(self.alpha_mode as i32));
|
||||
}
|
||||
if self.alpha_cutoff != defaults.alpha_cutoff {
|
||||
map.insert("ac".into(), OSD::Real(f64::from(self.alpha_cutoff)));
|
||||
}
|
||||
if self.override_double_sided {
|
||||
map.insert("ds".into(), OSD::Boolean(self.double_sided));
|
||||
}
|
||||
let transforms: Vec<_> = self
|
||||
.texture_transforms
|
||||
.iter()
|
||||
.map(|t| {
|
||||
OSD::Map(HashMap::from([
|
||||
("o".into(), vector2_osd(t.offset)),
|
||||
("s".into(), vector2_osd(t.scale)),
|
||||
("r".into(), OSD::Real(f64::from(t.rotation))),
|
||||
]))
|
||||
})
|
||||
.collect();
|
||||
if self.texture_transforms.iter().any(|v| !v.is_default()) {
|
||||
map.insert("ti".into(), OSD::Array(transforms));
|
||||
}
|
||||
OSDMap::new_with_dictionary(map)
|
||||
}
|
||||
|
||||
pub fn from_override_osd(data: OSDMap) -> Result<Self, Error> {
|
||||
let map = data.snapshot();
|
||||
let mut value = Self::default();
|
||||
if let Some(OSD::Array(ids)) = map.get("tex") {
|
||||
for (slot, id) in ids.iter().take(Self::TEXTURE_COUNT as usize).enumerate() {
|
||||
value.texture_ids[slot] = id.as_uuid()?;
|
||||
}
|
||||
}
|
||||
if let Some(v) = map.get("bc") {
|
||||
value.base_color_factor = v.as_color4()?;
|
||||
if value.base_color_factor == Self::default().base_color_factor {
|
||||
value.base_color_factor.a -= FLOAT_EPSILON;
|
||||
}
|
||||
}
|
||||
if let Some(v) = map.get("ec") {
|
||||
value.emissive_factor = v.as_vector3()?;
|
||||
if value.emissive_factor == Vector3::zero() {
|
||||
value.emissive_factor.x += FLOAT_EPSILON;
|
||||
}
|
||||
}
|
||||
if let Some(v) = map.get("mf") {
|
||||
value.metallic_factor = (v.as_real()? as f32).min(1.0 - FLOAT_EPSILON);
|
||||
}
|
||||
if let Some(v) = map.get("rf") {
|
||||
value.roughness_factor = (v.as_real()? as f32).min(1.0 - FLOAT_EPSILON);
|
||||
}
|
||||
if let Some(v) = map.get("am") {
|
||||
value.alpha_mode = match v.as_integer()? {
|
||||
0 => GltfAlphaMode::Opaque,
|
||||
1 => GltfAlphaMode::Blend,
|
||||
2 => GltfAlphaMode::Mask,
|
||||
_ => return Err(Error::Argument),
|
||||
};
|
||||
value.override_alpha_mode = true;
|
||||
}
|
||||
if let Some(v) = map.get("ac") {
|
||||
value.alpha_cutoff = v.as_real()? as f32;
|
||||
if value.alpha_cutoff == 0.5 {
|
||||
value.alpha_cutoff -= FLOAT_EPSILON;
|
||||
}
|
||||
}
|
||||
if let Some(v) = map.get("ds") {
|
||||
value.double_sided = v.as_boolean()?;
|
||||
value.override_double_sided = true;
|
||||
}
|
||||
if let Some(OSD::Array(values)) = map.get("ti") {
|
||||
for (slot, transform) in values.iter().take(Self::TEXTURE_COUNT as usize).enumerate() {
|
||||
if let OSD::Map(t) = transform {
|
||||
if let Some(v) = t.get("o") {
|
||||
value.texture_transforms[slot].offset = v.as_vector2()?;
|
||||
}
|
||||
if let Some(v) = t.get("s") {
|
||||
value.texture_transforms[slot].scale = v.as_vector2()?;
|
||||
}
|
||||
if let Some(v) = t.get("r") {
|
||||
value.texture_transforms[slot].rotation = v.as_real()? as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub const fn asset_type(&self) -> AssetType {
|
||||
AssetType::Material
|
||||
}
|
||||
pub fn alpha_cutoff(&self) -> f32 {
|
||||
self.alpha_cutoff
|
||||
}
|
||||
pub fn set_alpha_cutoff(&mut self, value: f32) {
|
||||
self.alpha_cutoff = value;
|
||||
}
|
||||
pub fn alpha_mode(&self) -> GltfAlphaMode {
|
||||
self.alpha_mode
|
||||
}
|
||||
pub fn set_alpha_mode(&mut self, value: GltfAlphaMode) {
|
||||
self.alpha_mode = value;
|
||||
}
|
||||
pub fn base_color_factor(&self) -> Color4 {
|
||||
self.base_color_factor
|
||||
}
|
||||
pub fn set_base_color_factor(&mut self, value: Color4) {
|
||||
self.base_color_factor = value;
|
||||
}
|
||||
pub fn double_sided(&self) -> bool {
|
||||
self.double_sided
|
||||
}
|
||||
pub fn set_double_sided(&mut self, value: bool) {
|
||||
self.double_sided = value;
|
||||
}
|
||||
pub fn emissive_factor(&self) -> Vector3 {
|
||||
self.emissive_factor
|
||||
}
|
||||
pub fn set_emissive_factor(&mut self, value: Vector3) {
|
||||
self.emissive_factor = value;
|
||||
}
|
||||
pub fn metallic_factor(&self) -> f32 {
|
||||
self.metallic_factor
|
||||
}
|
||||
pub fn set_metallic_factor(&mut self, value: f32) {
|
||||
self.metallic_factor = value;
|
||||
}
|
||||
pub fn roughness_factor(&self) -> f32 {
|
||||
self.roughness_factor
|
||||
}
|
||||
pub fn set_roughness_factor(&mut self, value: f32) {
|
||||
self.roughness_factor = value;
|
||||
}
|
||||
pub fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
pub fn set_name(&mut self, value: String) {
|
||||
self.name = value;
|
||||
}
|
||||
pub fn override_alpha_mode(&self) -> bool {
|
||||
self.override_alpha_mode
|
||||
}
|
||||
pub fn set_override_alpha_mode(&mut self, value: bool) {
|
||||
self.override_alpha_mode = value;
|
||||
}
|
||||
pub fn override_double_sided(&self) -> bool {
|
||||
self.override_double_sided
|
||||
}
|
||||
pub fn set_override_double_sided(&mut self, value: bool) {
|
||||
self.override_double_sided = value;
|
||||
}
|
||||
pub fn texture_ids(&self) -> Vec<UUID> {
|
||||
self.texture_ids.clone()
|
||||
}
|
||||
pub fn set_texture_ids(&mut self, value: Vec<UUID>) {
|
||||
self.texture_ids = value;
|
||||
}
|
||||
pub fn texture_transforms(&self) -> Vec<GltfTextureTransform> {
|
||||
self.texture_transforms.clone()
|
||||
}
|
||||
pub fn set_texture_transforms(&mut self, value: Vec<GltfTextureTransform>) {
|
||||
self.texture_transforms = value;
|
||||
}
|
||||
}
|
||||
|
||||
fn number(value: &Value) -> Result<f32, Error> {
|
||||
value
|
||||
.as_f64()
|
||||
.map(|v| v as f32)
|
||||
.filter(|v| v.is_finite())
|
||||
.ok_or(Error::Argument)
|
||||
}
|
||||
fn vector2_osd(value: Vector2) -> OSD {
|
||||
OSD::Array(vec![
|
||||
OSD::Real(f64::from(value.x)),
|
||||
OSD::Real(f64::from(value.y)),
|
||||
])
|
||||
}
|
||||
fn vector3_osd(value: Vector3) -> OSD {
|
||||
OSD::Array(vec![
|
||||
OSD::Real(f64::from(value.x)),
|
||||
OSD::Real(f64::from(value.y)),
|
||||
OSD::Real(f64::from(value.z)),
|
||||
])
|
||||
}
|
||||
fn color_osd(value: Color4) -> OSD {
|
||||
OSD::Array(vec![
|
||||
OSD::Real(f64::from(value.r)),
|
||||
OSD::Real(f64::from(value.g)),
|
||||
OSD::Real(f64::from(value.b)),
|
||||
OSD::Real(f64::from(value.a)),
|
||||
])
|
||||
}
|
||||
Reference in New Issue
Block a user