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
720 lines
24 KiB
Rust
720 lines
24 KiB
Rust
//! 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)),
|
|
])
|
|
}
|