forked from 1b-agent/opencadstudio-upstream
Render procedural checker materials correctly
This commit is contained in:
1 parent
12c1c330fc
commit
07acd1284c
9 files changed
+440
-39
No files matched your search
Generated
+3
-3
@@ -80,7 +80,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "acadrust"
|
name = "acadrust"
|
||||||
version = "0.5.5"
|
version = "0.5.5"
|
||||||
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=88fdb556dc8a292931a52b52ba42fc322dd39c23#88fdb556dc8a292931a52b52ba42fc322dd39c23"
|
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=6f3711809e39e9ffdf16a63086079b6475d97175#6f3711809e39e9ffdf16a63086079b6475d97175"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.12",
|
"ahash 0.8.12",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -895,7 +895,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cadkernel"
|
name = "cadkernel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=a3185be8189d78d6e93b4ea5b9238b184da4f3cf#a3185be8189d78d6e93b4ea5b9238b184da4f3cf"
|
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=935d90b86a99377dc95cba92e99a9b109180e2f6#935d90b86a99377dc95cba92e99a9b109180e2f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"acadrust",
|
"acadrust",
|
||||||
"cavalier_contours",
|
"cavalier_contours",
|
||||||
@@ -906,7 +906,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "cadkernel-constraints"
|
name = "cadkernel-constraints"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=a3185be8189d78d6e93b4ea5b9238b184da4f3cf#a3185be8189d78d6e93b4ea5b9238b184da4f3cf"
|
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=935d90b86a99377dc95cba92e99a9b109180e2f6#935d90b86a99377dc95cba92e99a9b109180e2f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
]
|
]
|
||||||
|
|||||||
+3
-3
@@ -30,9 +30,9 @@ glam = { version = "0.33", features = ["bytemuck"] }
|
|||||||
rfd = "0.17"
|
rfd = "0.17"
|
||||||
clap = { version = "4", features = ["derive", "string"] }
|
clap = { version = "4", features = ["derive", "string"] }
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "88fdb556dc8a292931a52b52ba42fc322dd39c23", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "6f3711809e39e9ffdf16a63086079b6475d97175", features = ["serde"] }
|
||||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "a3185be8189d78d6e93b4ea5b9238b184da4f3cf", features = ["acis", "offset"] }
|
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "935d90b86a99377dc95cba92e99a9b109180e2f6", features = ["acis", "offset"] }
|
||||||
cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "a3185be8189d78d6e93b4ea5b9238b184da4f3cf" }
|
cadkernel-constraints = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "935d90b86a99377dc95cba92e99a9b109180e2f6" }
|
||||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||||
flate2 = "1"
|
flate2 = "1"
|
||||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
|
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
|
||||||
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
||||||
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "88fdb556dc8a292931a52b52ba42fc322dd39c23", optional = true, features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "6f3711809e39e9ffdf16a63086079b6475d97175", optional = true, features = ["serde"] }
|
||||||
|
|
||||||
# Runtime IPC and serialization (host feature only).
|
# Runtime IPC and serialization (host feature only).
|
||||||
interprocess = { version = "2", optional = true }
|
interprocess = { version = "2", optional = true }
|
||||||
@@ -38,7 +38,7 @@ serde_json = "1"
|
|||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
cargo-lock = "11"
|
cargo-lock = "11"
|
||||||
# acadrust is scanned at build time to generate the embedded type registry.
|
# acadrust is scanned at build time to generate the embedded type registry.
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "88fdb556dc8a292931a52b52ba42fc322dd39c23", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "6f3711809e39e9ffdf16a63086079b6475d97175", features = ["serde"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
@@ -8,7 +8,7 @@ publish = false
|
|||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "88fdb556dc8a292931a52b52ba42fc322dd39c23", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "6f3711809e39e9ffdf16a63086079b6475d97175", features = ["serde"] }
|
||||||
bincode = "1.3"
|
bincode = "1.3"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ crate-type = ["cdylib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ocs_plugin_api = { path = "../ocs_plugin_api", features = ["host"] }
|
ocs_plugin_api = { path = "../ocs_plugin_api", features = ["host"] }
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "88fdb556dc8a292931a52b52ba42fc322dd39c23", features = ["serde"] }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "6f3711809e39e9ffdf16a63086079b6475d97175", features = ["serde"] }
|
||||||
@@ -12,4 +12,4 @@ crate-type = ["cdylib"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ocs_plugin_api = { path = "../../crates/ocs_plugin_api", features = ["host"] }
|
ocs_plugin_api = { path = "../../crates/ocs_plugin_api", features = ["host"] }
|
||||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "88fdb556dc8a292931a52b52ba42fc322dd39c23" }
|
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "6f3711809e39e9ffdf16a63086079b6475d97175" }
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
use acadrust::objects::{Material, MaterialColor, MaterialMap, ObjectType};
|
use acadrust::objects::{Material, MaterialColor, MaterialMap, ObjectType};
|
||||||
|
use acadrust::xdata::XDataValue;
|
||||||
use acadrust::{CadDocument, EntityType, Handle};
|
use acadrust::{CadDocument, EntityType, Handle};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -39,6 +40,7 @@ pub struct MeshMaterial {
|
|||||||
pub final_gather: i16,
|
pub final_gather: i16,
|
||||||
pub color_bleed_scale: f32,
|
pub color_bleed_scale: f32,
|
||||||
pub advanced_data_present: bool,
|
pub advanced_data_present: bool,
|
||||||
|
pub mapper: Option<MeshMaterialMapper>,
|
||||||
pub diffuse_map: MeshTextureMap,
|
pub diffuse_map: MeshTextureMap,
|
||||||
pub specular_map: MeshTextureMap,
|
pub specular_map: MeshTextureMap,
|
||||||
pub reflection_map: MeshTextureMap,
|
pub reflection_map: MeshTextureMap,
|
||||||
@@ -48,6 +50,82 @@ pub struct MeshMaterial {
|
|||||||
pub normal_map: MeshTextureMap,
|
pub normal_map: MeshTextureMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct MeshMaterialMapper {
|
||||||
|
pub origin: [f64; 3],
|
||||||
|
pub inverse_basis: [[f64; 3]; 3],
|
||||||
|
pub normal_basis: [[f64; 3]; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MeshMaterialMapper {
|
||||||
|
pub fn map_position(&self, high: [f32; 3], low: [f32; 3]) -> [f32; 3] {
|
||||||
|
let point = [
|
||||||
|
high[0] as f64 + low[0] as f64 - self.origin[0],
|
||||||
|
high[1] as f64 + low[1] as f64 - self.origin[1],
|
||||||
|
high[2] as f64 + low[2] as f64 - self.origin[2],
|
||||||
|
];
|
||||||
|
[
|
||||||
|
(self.inverse_basis[0][0] * point[0]
|
||||||
|
+ self.inverse_basis[0][1] * point[1]
|
||||||
|
+ self.inverse_basis[0][2] * point[2]) as f32,
|
||||||
|
(self.inverse_basis[1][0] * point[0]
|
||||||
|
+ self.inverse_basis[1][1] * point[1]
|
||||||
|
+ self.inverse_basis[1][2] * point[2]) as f32,
|
||||||
|
(self.inverse_basis[2][0] * point[0]
|
||||||
|
+ self.inverse_basis[2][1] * point[1]
|
||||||
|
+ self.inverse_basis[2][2] * point[2]) as f32,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_normal(&self, normal: [f32; 3]) -> [f32; 3] {
|
||||||
|
let mapped = [
|
||||||
|
self.normal_basis[0][0] * normal[0] as f64
|
||||||
|
+ self.normal_basis[0][1] * normal[1] as f64
|
||||||
|
+ self.normal_basis[0][2] * normal[2] as f64,
|
||||||
|
self.normal_basis[1][0] * normal[0] as f64
|
||||||
|
+ self.normal_basis[1][1] * normal[1] as f64
|
||||||
|
+ self.normal_basis[1][2] * normal[2] as f64,
|
||||||
|
self.normal_basis[2][0] * normal[0] as f64
|
||||||
|
+ self.normal_basis[2][1] * normal[1] as f64
|
||||||
|
+ self.normal_basis[2][2] * normal[2] as f64,
|
||||||
|
];
|
||||||
|
let length =
|
||||||
|
(mapped[0] * mapped[0] + mapped[1] * mapped[1] + mapped[2] * mapped[2]).sqrt();
|
||||||
|
if length > f64::EPSILON {
|
||||||
|
[
|
||||||
|
(mapped[0] / length) as f32,
|
||||||
|
(mapped[1] / length) as f32,
|
||||||
|
(mapped[2] / length) as f32,
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_bounds(&self, bounds: [f32; 6]) -> [f32; 6] {
|
||||||
|
let mut mapped = [
|
||||||
|
f32::INFINITY,
|
||||||
|
f32::INFINITY,
|
||||||
|
f32::INFINITY,
|
||||||
|
f32::NEG_INFINITY,
|
||||||
|
f32::NEG_INFINITY,
|
||||||
|
f32::NEG_INFINITY,
|
||||||
|
];
|
||||||
|
for x in [bounds[0], bounds[3]] {
|
||||||
|
for y in [bounds[1], bounds[4]] {
|
||||||
|
for z in [bounds[2], bounds[5]] {
|
||||||
|
let point = self.map_position([x, y, z], [0.0; 3]);
|
||||||
|
for axis in 0..3 {
|
||||||
|
mapped[axis] = mapped[axis].min(point[axis]);
|
||||||
|
mapped[axis + 3] = mapped[axis + 3].max(point[axis]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MeshTextureMap {
|
pub struct MeshTextureMap {
|
||||||
pub blend_factor: f32,
|
pub blend_factor: f32,
|
||||||
@@ -146,6 +224,7 @@ impl MeshMaterial {
|
|||||||
final_gather: 0,
|
final_gather: 0,
|
||||||
color_bleed_scale: 1.0,
|
color_bleed_scale: 1.0,
|
||||||
advanced_data_present: false,
|
advanced_data_present: false,
|
||||||
|
mapper: None,
|
||||||
diffuse_map: MeshTextureMap::default(),
|
diffuse_map: MeshTextureMap::default(),
|
||||||
specular_map: MeshTextureMap::default(),
|
specular_map: MeshTextureMap::default(),
|
||||||
reflection_map: MeshTextureMap::default(),
|
reflection_map: MeshTextureMap::default(),
|
||||||
@@ -210,6 +289,7 @@ impl MeshMaterial {
|
|||||||
final_gather: material.final_gather,
|
final_gather: material.final_gather,
|
||||||
color_bleed_scale: material.color_bleed_scale as f32,
|
color_bleed_scale: material.color_bleed_scale as f32,
|
||||||
advanced_data_present: material.advanced_data_present,
|
advanced_data_present: material.advanced_data_present,
|
||||||
|
mapper: None,
|
||||||
diffuse_map,
|
diffuse_map,
|
||||||
specular_map,
|
specular_map,
|
||||||
reflection_map,
|
reflection_map,
|
||||||
@@ -283,7 +363,7 @@ fn procedural_map_image(map: &MaterialMap) -> Option<Arc<MaterialImage>> {
|
|||||||
let mut rgba = Vec::with_capacity((size * size * 4) as usize);
|
let mut rgba = Vec::with_capacity((size * size * 4) as usize);
|
||||||
for y in 0..size {
|
for y in 0..size {
|
||||||
for x in 0..size {
|
for x in 0..size {
|
||||||
let color = if ((x / 8) + (y / 8)) & 1 == 0 {
|
let color = if (x < size / 2) == (y < size / 2) {
|
||||||
color1
|
color1
|
||||||
} else {
|
} else {
|
||||||
color2
|
color2
|
||||||
@@ -315,9 +395,11 @@ pub fn resolve_material_with_base(
|
|||||||
) -> MeshMaterial {
|
) -> MeshMaterial {
|
||||||
let common = entity.common();
|
let common = entity.common();
|
||||||
if common.material_flags == 1 {
|
if common.material_flags == 1 {
|
||||||
return by_block
|
let mut material = by_block
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| MeshMaterial::entity_color(entity_color));
|
.unwrap_or_else(|| MeshMaterial::entity_color(entity_color));
|
||||||
|
material.mapper = entity_material_mapper(entity);
|
||||||
|
return material;
|
||||||
}
|
}
|
||||||
let handle = if common.material_flags == 3 {
|
let handle = if common.material_flags == 3 {
|
||||||
common.material_handle
|
common.material_handle
|
||||||
@@ -331,7 +413,7 @@ pub fn resolve_material_with_base(
|
|||||||
let Some(handle) = handle else {
|
let Some(handle) = handle else {
|
||||||
return MeshMaterial::entity_color(entity_color);
|
return MeshMaterial::entity_color(entity_color);
|
||||||
};
|
};
|
||||||
match document.objects.get(&handle) {
|
let mut material = match document.objects.get(&handle) {
|
||||||
Some(ObjectType::Material(material))
|
Some(ObjectType::Material(material))
|
||||||
if material.name.eq_ignore_ascii_case("ByLayer")
|
if material.name.eq_ignore_ascii_case("ByLayer")
|
||||||
|| material.name.eq_ignore_ascii_case("ByBlock") =>
|
|| material.name.eq_ignore_ascii_case("ByBlock") =>
|
||||||
@@ -342,7 +424,58 @@ pub fn resolve_material_with_base(
|
|||||||
MeshMaterial::from_dwg(handle, material, entity_color, base_dir)
|
MeshMaterial::from_dwg(handle, material, entity_color, base_dir)
|
||||||
}
|
}
|
||||||
_ => MeshMaterial::entity_color(entity_color),
|
_ => MeshMaterial::entity_color(entity_color),
|
||||||
|
};
|
||||||
|
material.mapper = entity_material_mapper(entity);
|
||||||
|
material
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entity_material_mapper(entity: &EntityType) -> Option<MeshMaterialMapper> {
|
||||||
|
let record = entity
|
||||||
|
.common()
|
||||||
|
.extended_data
|
||||||
|
.get_record("ACAD_MATERIAL_MAPPER")?;
|
||||||
|
let mut positions = record.values.iter().filter_map(|value| match value {
|
||||||
|
XDataValue::Position3D(point) => Some([point.x, point.y, point.z]),
|
||||||
|
_ => None,
|
||||||
|
});
|
||||||
|
let origin = positions.next()?;
|
||||||
|
let x = positions.next()?;
|
||||||
|
let y = positions.next()?;
|
||||||
|
let z = positions.next()?;
|
||||||
|
let basis = glam::DMat3::from_cols(
|
||||||
|
glam::DVec3::from_array([
|
||||||
|
x[0] - origin[0],
|
||||||
|
x[1] - origin[1],
|
||||||
|
x[2] - origin[2],
|
||||||
|
]),
|
||||||
|
glam::DVec3::from_array([
|
||||||
|
y[0] - origin[0],
|
||||||
|
y[1] - origin[1],
|
||||||
|
y[2] - origin[2],
|
||||||
|
]),
|
||||||
|
glam::DVec3::from_array([
|
||||||
|
z[0] - origin[0],
|
||||||
|
z[1] - origin[1],
|
||||||
|
z[2] - origin[2],
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
if basis.determinant().abs() <= f64::EPSILON {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
let inverse = basis.inverse().to_cols_array();
|
||||||
|
Some(MeshMaterialMapper {
|
||||||
|
origin,
|
||||||
|
inverse_basis: [
|
||||||
|
[inverse[0], inverse[3], inverse[6]],
|
||||||
|
[inverse[1], inverse[4], inverse[7]],
|
||||||
|
[inverse[2], inverse[5], inverse[8]],
|
||||||
|
],
|
||||||
|
normal_basis: [
|
||||||
|
[x[0] - origin[0], x[1] - origin[1], x[2] - origin[2]],
|
||||||
|
[y[0] - origin[0], y[1] - origin[1], y[2] - origin[2]],
|
||||||
|
[z[0] - origin[0], z[1] - origin[1], z[2] - origin[2]],
|
||||||
|
],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_layer_material_with_base(
|
pub fn resolve_layer_material_with_base(
|
||||||
@@ -384,7 +517,9 @@ pub fn resolve_material_handle_with_base(
|
|||||||
fallback.clone()
|
fallback.clone()
|
||||||
}
|
}
|
||||||
Some(ObjectType::Material(material)) => {
|
Some(ObjectType::Material(material)) => {
|
||||||
MeshMaterial::from_dwg(handle, material, fallback.diffuse, base_dir)
|
let mut material = MeshMaterial::from_dwg(handle, material, fallback.diffuse, base_dir);
|
||||||
|
material.mapper = fallback.mapper;
|
||||||
|
material
|
||||||
}
|
}
|
||||||
_ => fallback.clone(),
|
_ => fallback.clone(),
|
||||||
}
|
}
|
||||||
@@ -442,3 +577,56 @@ fn load_map_image(map: &MaterialMap, base_dir: Option<&Path>) -> Option<Arc<Mate
|
|||||||
fn load_map_image(_map: &MaterialMap, _base_dir: Option<&Path>) -> Option<Arc<MaterialImage>> {
|
fn load_map_image(_map: &MaterialMap, _base_dir: Option<&Path>) -> Option<Arc<MaterialImage>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use acadrust::entities::Line;
|
||||||
|
use acadrust::objects::MaterialTexture;
|
||||||
|
use acadrust::types::Vector3;
|
||||||
|
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn procedural_checker_is_one_repeatable_two_by_two_tile() {
|
||||||
|
let mut map = MaterialMap::default();
|
||||||
|
map.texture = Some(MaterialTexture {
|
||||||
|
color1: MaterialColor {
|
||||||
|
flag: 1,
|
||||||
|
factor: 1.0,
|
||||||
|
rgb: Some(0xFF0000),
|
||||||
|
},
|
||||||
|
color2: MaterialColor {
|
||||||
|
flag: 1,
|
||||||
|
factor: 1.0,
|
||||||
|
rgb: Some(0x00FF00),
|
||||||
|
},
|
||||||
|
..MaterialTexture::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let image = procedural_map_image(&map).expect("procedural image");
|
||||||
|
let pixel = |x: usize, y: usize| &image.rgba[(y * 64 + x) * 4..][..4];
|
||||||
|
assert_eq!(pixel(0, 0), [255, 0, 0, 255]);
|
||||||
|
assert_eq!(pixel(32, 0), [0, 255, 0, 255]);
|
||||||
|
assert_eq!(pixel(0, 32), [0, 255, 0, 255]);
|
||||||
|
assert_eq!(pixel(32, 32), [255, 0, 0, 255]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn entity_material_mapper_preserves_its_coordinate_frame() {
|
||||||
|
let mut line = Line::new();
|
||||||
|
let mut record = ExtendedDataRecord::new("ACAD_MATERIAL_MAPPER");
|
||||||
|
record.values = vec![
|
||||||
|
XDataValue::Integer16(2),
|
||||||
|
XDataValue::Integer16(0),
|
||||||
|
XDataValue::Integer16(0),
|
||||||
|
XDataValue::Position3D(Vector3::new(10.0, 20.0, 30.0)),
|
||||||
|
XDataValue::Position3D(Vector3::new(10.0, 22.0, 30.0)),
|
||||||
|
XDataValue::Position3D(Vector3::new(7.0, 20.0, 30.0)),
|
||||||
|
XDataValue::Position3D(Vector3::new(10.0, 20.0, 34.0)),
|
||||||
|
];
|
||||||
|
line.common.extended_data.add_record(record);
|
||||||
|
let mapper = entity_material_mapper(&EntityType::Line(line)).expect("valid mapper");
|
||||||
|
|
||||||
|
assert_eq!(mapper.map_position([4.0, 22.0, 42.0], [0.0; 3]), [1.0, 2.0, 3.0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
+234
-24
@@ -1037,9 +1037,28 @@ fn material_key(
|
|||||||
key[34] = material.final_gather as u32;
|
key[34] = material.final_gather as u32;
|
||||||
key[35] = material.color_bleed_scale.to_bits();
|
key[35] = material.color_bleed_scale.to_bits();
|
||||||
key[36] = material.advanced_data_present as u32;
|
key[36] = material.advanced_data_present as u32;
|
||||||
|
let mapper = material_mapper_key(material);
|
||||||
|
key[37] = mapper as u32;
|
||||||
|
key[38] = (mapper >> 32) as u32;
|
||||||
|
key[39] = material.mapper.is_some() as u32;
|
||||||
MaterialBatchKey(key)
|
MaterialBatchKey(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn material_mapper_key(
|
||||||
|
material: &crate::scene::model::material_model::MeshMaterial,
|
||||||
|
) -> u64 {
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
let Some(mapper) = material.mapper else {
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
let mut hasher = rustc_hash::FxHasher::default();
|
||||||
|
mapper.origin.map(f64::to_bits).hash(&mut hasher);
|
||||||
|
for row in mapper.inverse_basis {
|
||||||
|
row.map(f64::to_bits).hash(&mut hasher);
|
||||||
|
}
|
||||||
|
hasher.finish()
|
||||||
|
}
|
||||||
|
|
||||||
fn morton_axis(mut value: u32) -> u64 {
|
fn morton_axis(mut value: u32) -> u64 {
|
||||||
value &= 0x3ff;
|
value &= 0x3ff;
|
||||||
let mut result = 0u64;
|
let mut result = 0u64;
|
||||||
@@ -1197,6 +1216,62 @@ fn material_has_textures(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn material_has_box_projection(
|
||||||
|
material: Option<&crate::scene::model::material_model::MeshMaterial>,
|
||||||
|
) -> bool {
|
||||||
|
material.is_some_and(|material| {
|
||||||
|
[
|
||||||
|
&material.diffuse_map,
|
||||||
|
&material.specular_map,
|
||||||
|
&material.reflection_map,
|
||||||
|
&material.opacity_map,
|
||||||
|
&material.bump_map,
|
||||||
|
&material.refraction_map,
|
||||||
|
&material.normal_map,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.any(|map| map.image.is_some() && map.projection == 2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triangle_mapping_normal(mesh: &MeshModel, triangle: &[u32]) -> [f32; 3] {
|
||||||
|
let vertex = |corner: usize| {
|
||||||
|
let index = triangle[corner] as usize;
|
||||||
|
(
|
||||||
|
mesh.verts[index],
|
||||||
|
mesh.verts_low.get(index).copied().unwrap_or([0.0; 3]),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let (a, a_low) = vertex(0);
|
||||||
|
let (b, b_low) = vertex(1);
|
||||||
|
let (c, c_low) = vertex(2);
|
||||||
|
let ab = [
|
||||||
|
(b[0] - a[0]) + (b_low[0] - a_low[0]),
|
||||||
|
(b[1] - a[1]) + (b_low[1] - a_low[1]),
|
||||||
|
(b[2] - a[2]) + (b_low[2] - a_low[2]),
|
||||||
|
];
|
||||||
|
let ac = [
|
||||||
|
(c[0] - a[0]) + (c_low[0] - a_low[0]),
|
||||||
|
(c[1] - a[1]) + (c_low[1] - a_low[1]),
|
||||||
|
(c[2] - a[2]) + (c_low[2] - a_low[2]),
|
||||||
|
];
|
||||||
|
let normal = [
|
||||||
|
ab[1] * ac[2] - ab[2] * ac[1],
|
||||||
|
ab[2] * ac[0] - ab[0] * ac[2],
|
||||||
|
ab[0] * ac[1] - ab[1] * ac[0],
|
||||||
|
];
|
||||||
|
let length = (normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2])
|
||||||
|
.sqrt();
|
||||||
|
if length > f32::EPSILON {
|
||||||
|
[normal[0] / length, normal[1] / length, normal[2] / length]
|
||||||
|
} else {
|
||||||
|
mesh.normals
|
||||||
|
.get(triangle[0] as usize)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or([0.0, 1.0, 0.0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_instanced_chunks(
|
fn build_instanced_chunks(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
@@ -1215,9 +1290,10 @@ fn build_instanced_chunks(
|
|||||||
let compact_vertices = !material_has_textures(material);
|
let compact_vertices = !material_has_textures(material);
|
||||||
let material_identity = if material_has_textures(material) {
|
let material_identity = if material_has_textures(material) {
|
||||||
material.map_or(0, |material| {
|
material.map_or(0, |material| {
|
||||||
material
|
let handle = material
|
||||||
.handle
|
.handle
|
||||||
.map_or(material as *const _ as usize as u64, |handle| handle.value())
|
.map_or(material as *const _ as usize as u64, |handle| handle.value());
|
||||||
|
handle ^ material_mapper_key(material).rotate_left(17)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
@@ -1229,7 +1305,8 @@ fn build_instanced_chunks(
|
|||||||
};
|
};
|
||||||
let budget = super::gpu_budget::buffer_budget(device);
|
let budget = super::gpu_budget::buffer_budget(device);
|
||||||
let needs_wire_vertices = first.include_edges && source.edge_verts.is_empty();
|
let needs_wire_vertices = first.include_edges && source.edge_verts.is_empty();
|
||||||
let split_geometry = mesh
|
let split_geometry = (first.include_faces && material_has_box_projection(material))
|
||||||
|
|| mesh
|
||||||
.verts
|
.verts
|
||||||
.len()
|
.len()
|
||||||
.saturating_mul(std::mem::size_of::<MeshVertex>())
|
.saturating_mul(std::mem::size_of::<MeshVertex>())
|
||||||
@@ -1257,21 +1334,24 @@ fn build_instanced_chunks(
|
|||||||
|
|
||||||
let has_normals = mesh.normals.len() == mesh.verts.len();
|
let has_normals = mesh.normals.len() == mesh.verts.len();
|
||||||
let bounds = mesh_bounds(mesh);
|
let bounds = mesh_bounds(mesh);
|
||||||
let vertex = |index: usize| {
|
let vertex = |index: usize, mapping_normal: Option<[f32; 3]>| {
|
||||||
let normal = if has_normals {
|
let normal = if has_normals {
|
||||||
mesh.normals[index]
|
mesh.normals[index]
|
||||||
} else {
|
} else {
|
||||||
[0.0, 1.0, 0.0]
|
[0.0, 1.0, 0.0]
|
||||||
};
|
};
|
||||||
|
let mapping_normal = mapping_normal.unwrap_or(normal);
|
||||||
let position = mesh.verts[index];
|
let position = mesh.verts[index];
|
||||||
let position_low = mesh.verts_low.get(index).copied().unwrap_or([0.0; 3]);
|
let position_low = mesh.verts_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||||
let uvs = material_uvs(
|
let uvs = material_uvs(
|
||||||
material,
|
material,
|
||||||
position,
|
position,
|
||||||
normal,
|
position_low,
|
||||||
|
mapping_normal,
|
||||||
bounds,
|
bounds,
|
||||||
position,
|
position,
|
||||||
normal,
|
position_low,
|
||||||
|
mapping_normal,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
MeshVertex {
|
MeshVertex {
|
||||||
@@ -1289,7 +1369,7 @@ fn build_instanced_chunks(
|
|||||||
};
|
};
|
||||||
let verts: Vec<_> =
|
let verts: Vec<_> =
|
||||||
if !split_geometry && (shared_vertex_buffer.is_none() || needs_wire_vertices) {
|
if !split_geometry && (shared_vertex_buffer.is_none() || needs_wire_vertices) {
|
||||||
(0..mesh.verts.len()).map(vertex).collect()
|
(0..mesh.verts.len()).map(|index| vertex(index, None)).collect()
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
@@ -1398,10 +1478,16 @@ fn build_instanced_chunks(
|
|||||||
.max(1);
|
.max(1);
|
||||||
if first.include_faces || needs_wire_vertices {
|
if first.include_faces || needs_wire_vertices {
|
||||||
for triangles in first.indices.chunks(max_triangles * 3) {
|
for triangles in first.indices.chunks(max_triangles * 3) {
|
||||||
let vertices: Vec<_> = triangles
|
let mut vertices = Vec::with_capacity(triangles.len());
|
||||||
.iter()
|
for triangle in triangles.chunks_exact(3) {
|
||||||
.map(|index| vertex(*index as usize))
|
let mapping_normal = material_has_box_projection(material)
|
||||||
.collect();
|
.then(|| triangle_mapping_normal(mesh, triangle));
|
||||||
|
vertices.extend(
|
||||||
|
triangle
|
||||||
|
.iter()
|
||||||
|
.map(|index| vertex(*index as usize, mapping_normal)),
|
||||||
|
);
|
||||||
|
}
|
||||||
let indices: Vec<_> = (0..vertices.len() as u32).collect();
|
let indices: Vec<_> = (0..vertices.len() as u32).collect();
|
||||||
let wire_indices: Vec<_> = if needs_wire_vertices {
|
let wire_indices: Vec<_> = if needs_wire_vertices {
|
||||||
indices
|
indices
|
||||||
@@ -1592,24 +1678,55 @@ fn material_is_transparent(
|
|||||||
|| material.is_some_and(|material| {
|
|| material.is_some_and(|material| {
|
||||||
material.translucence > 0.0
|
material.translucence > 0.0
|
||||||
|| (material.channel_flags as u32 & 0x08 != 0
|
|| (material.channel_flags as u32 & 0x08 != 0
|
||||||
&& material.opacity_map.image.is_some())
|
&& material
|
||||||
|
.opacity_map
|
||||||
|
.image
|
||||||
|
.as_deref()
|
||||||
|
.is_some_and(|image| !opacity_image_is_binary_cutout(image)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn opacity_image_is_binary_cutout(
|
||||||
|
image: &crate::scene::model::material_model::MaterialImage,
|
||||||
|
) -> bool {
|
||||||
|
let mut pixels = image.rgba.chunks_exact(4);
|
||||||
|
!image.rgba.is_empty()
|
||||||
|
&& pixels.all(|pixel| matches!(pixel[0], 0 | 255))
|
||||||
|
&& pixels.remainder().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
fn material_map_uv(
|
fn material_map_uv(
|
||||||
map: &crate::scene::model::material_model::MeshTextureMap,
|
map: &crate::scene::model::material_model::MeshTextureMap,
|
||||||
|
mapper: Option<&crate::scene::model::material_model::MeshMaterialMapper>,
|
||||||
local_position: [f32; 3],
|
local_position: [f32; 3],
|
||||||
|
local_position_low: [f32; 3],
|
||||||
local_normal: [f32; 3],
|
local_normal: [f32; 3],
|
||||||
local_bounds: [f32; 6],
|
local_bounds: [f32; 6],
|
||||||
model_position: [f32; 3],
|
model_position: [f32; 3],
|
||||||
|
model_position_low: [f32; 3],
|
||||||
model_normal: [f32; 3],
|
model_normal: [f32; 3],
|
||||||
model_bounds: [f32; 6],
|
model_bounds: [f32; 6],
|
||||||
) -> [f32; 2] {
|
) -> [f32; 2] {
|
||||||
let (mut position, normal, bounds) = if map.auto_transform & 4 != 0 {
|
let (mut position, position_low, mut normal, mut bounds) = if map.auto_transform & 4 != 0 {
|
||||||
(model_position, model_normal, model_bounds)
|
(
|
||||||
|
model_position,
|
||||||
|
model_position_low,
|
||||||
|
model_normal,
|
||||||
|
model_bounds,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(local_position, local_normal, local_bounds)
|
(
|
||||||
|
local_position,
|
||||||
|
local_position_low,
|
||||||
|
local_normal,
|
||||||
|
local_bounds,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
if let Some(mapper) = mapper {
|
||||||
|
position = mapper.map_position(position, position_low);
|
||||||
|
normal = mapper.map_normal(normal);
|
||||||
|
bounds = mapper.map_bounds(bounds);
|
||||||
|
}
|
||||||
if map.auto_transform & 2 != 0 {
|
if map.auto_transform & 2 != 0 {
|
||||||
for axis in 0..3 {
|
for axis in 0..3 {
|
||||||
let extent = bounds[axis + 3] - bounds[axis];
|
let extent = bounds[axis + 3] - bounds[axis];
|
||||||
@@ -1659,9 +1776,11 @@ fn material_map_uv(
|
|||||||
fn material_uvs(
|
fn material_uvs(
|
||||||
material: Option<&crate::scene::model::material_model::MeshMaterial>,
|
material: Option<&crate::scene::model::material_model::MeshMaterial>,
|
||||||
local_position: [f32; 3],
|
local_position: [f32; 3],
|
||||||
|
local_position_low: [f32; 3],
|
||||||
local_normal: [f32; 3],
|
local_normal: [f32; 3],
|
||||||
local_bounds: [f32; 6],
|
local_bounds: [f32; 6],
|
||||||
model_position: [f32; 3],
|
model_position: [f32; 3],
|
||||||
|
model_position_low: [f32; 3],
|
||||||
model_normal: [f32; 3],
|
model_normal: [f32; 3],
|
||||||
model_bounds: [f32; 6],
|
model_bounds: [f32; 6],
|
||||||
) -> [[f32; 2]; 7] {
|
) -> [[f32; 2]; 7] {
|
||||||
@@ -1674,10 +1793,13 @@ fn material_uvs(
|
|||||||
} else {
|
} else {
|
||||||
material_map_uv(
|
material_map_uv(
|
||||||
map,
|
map,
|
||||||
|
material.mapper.as_ref(),
|
||||||
local_position,
|
local_position,
|
||||||
|
local_position_low,
|
||||||
local_normal,
|
local_normal,
|
||||||
local_bounds,
|
local_bounds,
|
||||||
model_position,
|
model_position,
|
||||||
|
model_position_low,
|
||||||
model_normal,
|
model_normal,
|
||||||
model_bounds,
|
model_bounds,
|
||||||
)
|
)
|
||||||
@@ -2068,7 +2190,9 @@ pub fn build_mesh_batch_filtered(
|
|||||||
.visual_style
|
.visual_style
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(part.display_color, |style| style.edge_color(part.display_color));
|
.map_or(part.display_color, |style| style.edge_color(part.display_color));
|
||||||
let vtx = |vi: usize| {
|
let vtx = |vi: usize,
|
||||||
|
local_mapping_normal: Option<[f32; 3]>,
|
||||||
|
model_mapping_normal: Option<[f32; 3]>| {
|
||||||
let normal = if has_normals {
|
let normal = if has_normals {
|
||||||
mesh.normals[vi]
|
mesh.normals[vi]
|
||||||
} else {
|
} else {
|
||||||
@@ -2079,6 +2203,11 @@ pub fn build_mesh_batch_filtered(
|
|||||||
.get(vi)
|
.get(vi)
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or(mesh.verts[vi]);
|
.unwrap_or(mesh.verts[vi]);
|
||||||
|
let local_position_low = uv_mesh
|
||||||
|
.verts_low
|
||||||
|
.get(vi)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_else(|| mesh.verts_low.get(vi).copied().unwrap_or([0.0; 3]));
|
||||||
let local_normal = uv_mesh
|
let local_normal = uv_mesh
|
||||||
.normals
|
.normals
|
||||||
.get(vi)
|
.get(vi)
|
||||||
@@ -2087,10 +2216,12 @@ pub fn build_mesh_batch_filtered(
|
|||||||
let uv = material_uvs(
|
let uv = material_uvs(
|
||||||
material,
|
material,
|
||||||
local_position,
|
local_position,
|
||||||
local_normal,
|
local_position_low,
|
||||||
|
local_mapping_normal.unwrap_or(local_normal),
|
||||||
local_bounds,
|
local_bounds,
|
||||||
mesh.verts[vi],
|
mesh.verts[vi],
|
||||||
normal,
|
mesh.verts_low.get(vi).copied().unwrap_or([0.0; 3]),
|
||||||
|
model_mapping_normal.unwrap_or(normal),
|
||||||
model_bounds,
|
model_bounds,
|
||||||
);
|
);
|
||||||
MeshVertex {
|
MeshVertex {
|
||||||
@@ -2206,7 +2337,8 @@ pub fn build_mesh_batch_filtered(
|
|||||||
|
|
||||||
// A single mesh larger than a whole chunk: emit as triangle-soup
|
// A single mesh larger than a whole chunk: emit as triangle-soup
|
||||||
// sub-chunks (corners expanded, no vertex sharing) so each buffer fits.
|
// sub-chunks (corners expanded, no vertex sharing) so each buffer fits.
|
||||||
if mesh.verts.len() > max_verts || mesh_tris > max_tris {
|
let box_projected = part.include_faces && material_has_box_projection(material);
|
||||||
|
if mesh.verts.len() > max_verts || mesh_tris > max_tris || box_projected {
|
||||||
if !verts.is_empty() || !edge_verts.is_empty() {
|
if !verts.is_empty() || !edge_verts.is_empty() {
|
||||||
chunks.push(make_chunk(
|
chunks.push(make_chunk(
|
||||||
device,
|
device,
|
||||||
@@ -2244,10 +2376,26 @@ pub fn build_mesh_batch_filtered(
|
|||||||
let (mut sv, mut si, mut swi) = (Vec::new(), Vec::new(), Vec::new());
|
let (mut sv, mut si, mut swi) = (Vec::new(), Vec::new(), Vec::new());
|
||||||
for tri in t..end {
|
for tri in t..end {
|
||||||
let ix = &part.indices[tri * 3..tri * 3 + 3];
|
let ix = &part.indices[tri * 3..tri * 3 + 3];
|
||||||
|
let local_mapping_normal =
|
||||||
|
box_projected.then(|| triangle_mapping_normal(uv_mesh, ix));
|
||||||
|
let model_mapping_normal =
|
||||||
|
box_projected.then(|| triangle_mapping_normal(mesh, ix));
|
||||||
let b = sv.len() as u32;
|
let b = sv.len() as u32;
|
||||||
sv.push(vtx(ix[0] as usize));
|
sv.push(vtx(
|
||||||
sv.push(vtx(ix[1] as usize));
|
ix[0] as usize,
|
||||||
sv.push(vtx(ix[2] as usize));
|
local_mapping_normal,
|
||||||
|
model_mapping_normal,
|
||||||
|
));
|
||||||
|
sv.push(vtx(
|
||||||
|
ix[1] as usize,
|
||||||
|
local_mapping_normal,
|
||||||
|
model_mapping_normal,
|
||||||
|
));
|
||||||
|
sv.push(vtx(
|
||||||
|
ix[2] as usize,
|
||||||
|
local_mapping_normal,
|
||||||
|
model_mapping_normal,
|
||||||
|
));
|
||||||
if part.include_faces {
|
if part.include_faces {
|
||||||
si.extend_from_slice(&[b, b + 1, b + 2]);
|
si.extend_from_slice(&[b, b + 1, b + 2]);
|
||||||
}
|
}
|
||||||
@@ -2359,7 +2507,7 @@ pub fn build_mesh_batch_filtered(
|
|||||||
}
|
}
|
||||||
let base = verts.len() as u32;
|
let base = verts.len() as u32;
|
||||||
for i in 0..mesh.verts.len() {
|
for i in 0..mesh.verts.len() {
|
||||||
verts.push(vtx(i));
|
verts.push(vtx(i, None, None));
|
||||||
}
|
}
|
||||||
if part.include_faces {
|
if part.include_faces {
|
||||||
let fill = if is_transp { &mut transp_indices } else { &mut indices };
|
let fill = if is_transp { &mut transp_indices } else { &mut indices };
|
||||||
@@ -2459,7 +2607,69 @@ pub fn build_mesh_batch_filtered(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod texture_limit_tests {
|
mod texture_limit_tests {
|
||||||
use super::downscale_rgba_to_limit;
|
use super::{downscale_rgba_to_limit, opacity_image_is_binary_cutout};
|
||||||
|
use crate::scene::model::material_model::MaterialImage;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn binary_opacity_image_uses_cutout_rendering() {
|
||||||
|
let image = MaterialImage {
|
||||||
|
width: 2,
|
||||||
|
height: 1,
|
||||||
|
rgba: Arc::new(vec![0, 0, 0, 255, 255, 255, 255, 255]),
|
||||||
|
path: String::new(),
|
||||||
|
};
|
||||||
|
assert!(opacity_image_is_binary_cutout(&image));
|
||||||
|
|
||||||
|
let blended = MaterialImage {
|
||||||
|
rgba: Arc::new(vec![127, 127, 127, 255]),
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
path: String::new(),
|
||||||
|
};
|
||||||
|
assert!(!opacity_image_is_binary_cutout(&blended));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn object_mapped_uv_is_stable_at_large_coordinates() {
|
||||||
|
let map = crate::scene::model::material_model::MeshTextureMap::default();
|
||||||
|
let identity = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]];
|
||||||
|
let local_mapper = crate::scene::model::material_model::MeshMaterialMapper {
|
||||||
|
origin: [1.0, 2.0, 3.0],
|
||||||
|
inverse_basis: identity,
|
||||||
|
normal_basis: identity,
|
||||||
|
};
|
||||||
|
let local = super::material_map_uv(
|
||||||
|
&map,
|
||||||
|
Some(&local_mapper),
|
||||||
|
[3.0, 4.0, 5.0],
|
||||||
|
[0.0; 3],
|
||||||
|
[0.0, 0.0, 1.0],
|
||||||
|
[1.0, 2.0, 3.0, 11.0, 12.0, 13.0],
|
||||||
|
[3.0, 4.0, 5.0],
|
||||||
|
[0.0; 3],
|
||||||
|
[0.0, 0.0, 1.0],
|
||||||
|
[1.0, 2.0, 3.0, 11.0, 12.0, 13.0],
|
||||||
|
);
|
||||||
|
let shifted_mapper = crate::scene::model::material_model::MeshMaterialMapper {
|
||||||
|
origin: [1_200_001.0, -799_998.0, 3.0],
|
||||||
|
inverse_basis: identity,
|
||||||
|
normal_basis: identity,
|
||||||
|
};
|
||||||
|
let shifted = super::material_map_uv(
|
||||||
|
&map,
|
||||||
|
Some(&shifted_mapper),
|
||||||
|
[1_200_003.0, -799_996.0, 5.0],
|
||||||
|
[0.0; 3],
|
||||||
|
[0.0, 0.0, 1.0],
|
||||||
|
[1_200_001.0, -799_998.0, 3.0, 1_200_011.0, -799_988.0, 13.0],
|
||||||
|
[1_200_003.0, -799_996.0, 5.0],
|
||||||
|
[0.0; 3],
|
||||||
|
[0.0, 0.0, 1.0],
|
||||||
|
[1_200_001.0, -799_998.0, 3.0, 1_200_011.0, -799_988.0, 13.0],
|
||||||
|
);
|
||||||
|
assert_eq!(local, shifted);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn downscale_keeps_both_dimensions_within_the_limit() {
|
fn downscale_keeps_both_dimensions_within_the_limit() {
|
||||||
|
|||||||
@@ -583,6 +583,9 @@ fn fs_main(in: VertexOut) -> @location(0) vec4<f32> {
|
|||||||
}
|
}
|
||||||
material_alpha *= 1.0 - clamp(surface.ambient.a * max(surface.advanced.w, 0.0), 0.0, 0.95);
|
material_alpha *= 1.0 - clamp(surface.ambient.a * max(surface.advanced.w, 0.0), 0.0, 0.95);
|
||||||
material_alpha *= clamp(u.visual_style.y, 0.0, 1.0);
|
material_alpha *= clamp(u.visual_style.y, 0.0, 1.0);
|
||||||
|
if (u.transparency_enable > 0.5 && material_alpha <= 0.001) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
let alpha = select(1.0, material_alpha, u.transparency_enable > 0.5);
|
let alpha = select(1.0, material_alpha, u.transparency_enable > 0.5);
|
||||||
return vec4<f32>(rgb, alpha);
|
return vec4<f32>(rgb, alpha);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user