This commit is way too large. So, added Upload trait back again, but only for Textures, which are the most complex types. Fixing Medium and PixelSensor creation on host side. Can´t really find a satisfying way of keeping construction and creation separate, so for now, regrettably, putting them in the same place. Added some types to parameter parsing. Continuing fixup caused by creation of GVec and GBox
This commit is contained in:
parent
44099dffa9
commit
a14960562c
29 changed files with 1038 additions and 406 deletions
|
|
@ -40,8 +40,8 @@ impl CoatedDiffuseMaterial {
|
||||||
thickness: Ptr<GPUFloatTexture>,
|
thickness: Ptr<GPUFloatTexture>,
|
||||||
albedo: Ptr<GPUSpectrumTexture>,
|
albedo: Ptr<GPUSpectrumTexture>,
|
||||||
g: Ptr<GPUFloatTexture>,
|
g: Ptr<GPUFloatTexture>,
|
||||||
eta: Ptr<Spectrum>,
|
|
||||||
displacement: Ptr<GPUFloatTexture>,
|
displacement: Ptr<GPUFloatTexture>,
|
||||||
|
eta: Ptr<Spectrum>,
|
||||||
normal_map: Ptr<Image>,
|
normal_map: Ptr<Image>,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
|
|
@ -153,7 +153,6 @@ impl MaterialTrait for CoatedDiffuseMaterial {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct CoatedConductorMaterial {
|
pub struct CoatedConductorMaterial {
|
||||||
normal_map: Ptr<Image>,
|
|
||||||
displacement: Ptr<GPUFloatTexture>,
|
displacement: Ptr<GPUFloatTexture>,
|
||||||
interface_uroughness: Ptr<GPUFloatTexture>,
|
interface_uroughness: Ptr<GPUFloatTexture>,
|
||||||
interface_vroughness: Ptr<GPUFloatTexture>,
|
interface_vroughness: Ptr<GPUFloatTexture>,
|
||||||
|
|
@ -166,6 +165,7 @@ pub struct CoatedConductorMaterial {
|
||||||
conductor_eta: Ptr<GPUSpectrumTexture>,
|
conductor_eta: Ptr<GPUSpectrumTexture>,
|
||||||
k: Ptr<GPUSpectrumTexture>,
|
k: Ptr<GPUSpectrumTexture>,
|
||||||
reflectance: Ptr<GPUSpectrumTexture>,
|
reflectance: Ptr<GPUSpectrumTexture>,
|
||||||
|
normal_map: Ptr<Image>,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
|
|
@ -175,12 +175,10 @@ pub struct CoatedConductorMaterial {
|
||||||
impl CoatedConductorMaterial {
|
impl CoatedConductorMaterial {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
normal_map: Ptr<Image>,
|
|
||||||
displacement: Ptr<GPUFloatTexture>,
|
displacement: Ptr<GPUFloatTexture>,
|
||||||
interface_uroughness: Ptr<GPUFloatTexture>,
|
interface_uroughness: Ptr<GPUFloatTexture>,
|
||||||
interface_vroughness: Ptr<GPUFloatTexture>,
|
interface_vroughness: Ptr<GPUFloatTexture>,
|
||||||
thickness: Ptr<GPUFloatTexture>,
|
thickness: Ptr<GPUFloatTexture>,
|
||||||
interface_eta: Ptr<Spectrum>,
|
|
||||||
g: Ptr<GPUFloatTexture>,
|
g: Ptr<GPUFloatTexture>,
|
||||||
albedo: Ptr<GPUSpectrumTexture>,
|
albedo: Ptr<GPUSpectrumTexture>,
|
||||||
conductor_uroughness: Ptr<GPUFloatTexture>,
|
conductor_uroughness: Ptr<GPUFloatTexture>,
|
||||||
|
|
@ -188,6 +186,8 @@ impl CoatedConductorMaterial {
|
||||||
conductor_eta: Ptr<GPUSpectrumTexture>,
|
conductor_eta: Ptr<GPUSpectrumTexture>,
|
||||||
k: Ptr<GPUSpectrumTexture>,
|
k: Ptr<GPUSpectrumTexture>,
|
||||||
reflectance: Ptr<GPUSpectrumTexture>,
|
reflectance: Ptr<GPUSpectrumTexture>,
|
||||||
|
normal_map: Ptr<Image>,
|
||||||
|
interface_eta: Ptr<Spectrum>,
|
||||||
max_depth: u32,
|
max_depth: u32,
|
||||||
n_samples: u32,
|
n_samples: u32,
|
||||||
remap_roughness: bool,
|
remap_roughness: bool,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub trait CreateRGBToSpectrumTable {
|
pub trait CreateRGBToSpectrumTable {
|
||||||
fn from_data(z_nodes: &[Float], coeffs: &[Float]) -> Self;
|
fn new(z_nodes: &[Float], coeffs: &[Float]) -> Self;
|
||||||
fn load(base_dir: &Path, name: &str) -> Result<Self> where Self: Sized;
|
fn load(base_dir: &Path, name: &str) -> Result<Self> where Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -349,4 +349,20 @@ impl HostImage {
|
||||||
pub fn into_image(self) -> Image {
|
pub fn into_image(self) -> Image {
|
||||||
self.inner
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolution(&self) -> Point2i {
|
||||||
|
self.inner.resolution()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn n_channels(&self) -> i32 {
|
||||||
|
self.inner.n_channels()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encoding(&self) -> ColorEncoding {
|
||||||
|
self.inner.encoding
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> PixelFormat {
|
||||||
|
self.inner.format()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,14 +102,13 @@ pub fn create_light(
|
||||||
arena,
|
arena,
|
||||||
),
|
),
|
||||||
"diffuse" => Err(anyhow!(
|
"diffuse" => Err(anyhow!(
|
||||||
"{}: \"diffuse\" is an area light; use create_area_light with a shape",
|
"{}: \"diffuse\" is an area light. Use create_area_light with a shape",
|
||||||
loc
|
loc
|
||||||
)),
|
)),
|
||||||
_ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)),
|
_ => Err(anyhow!("{}: unknown light type \"{}\"", loc, name)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a diffuse area light bound to a specific shape
|
|
||||||
pub fn create_area_light(
|
pub fn create_area_light(
|
||||||
render_from_light: Transform,
|
render_from_light: Transform,
|
||||||
medium: Option<Medium>,
|
medium: Option<Medium>,
|
||||||
|
|
@ -119,15 +118,10 @@ pub fn create_area_light(
|
||||||
alpha_tex: &FloatTexture,
|
alpha_tex: &FloatTexture,
|
||||||
colorspace: Option<&RGBColorSpace>,
|
colorspace: Option<&RGBColorSpace>,
|
||||||
arena: &mut Arena,
|
arena: &mut Arena,
|
||||||
) -> Result<Light> {
|
) -> Result<Ptr<Light>> {
|
||||||
crate::lights::diffuse::create(
|
let light = crate::lights::diffuse::create(
|
||||||
render_from_light,
|
render_from_light, medium, parameters, loc,
|
||||||
medium,
|
shape, alpha_tex, colorspace, arena,
|
||||||
parameters,
|
)?;
|
||||||
loc,
|
Ok(arena.alloc(light))
|
||||||
shape,
|
|
||||||
alpha_tex,
|
|
||||||
colorspace,
|
|
||||||
arena,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,164 +1,610 @@
|
||||||
use shared::core::geometry::{Bounds3f, Point3i};
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use shared::core::medium::{GridMedium, HGPhaseFunction, HomogeneousMedium, RGBGridMedium};
|
use crate::spectra::SRGB;
|
||||||
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
use shared::core::geometry::{Bounds3f, Point3f, Point3i};
|
||||||
|
use shared::core::medium::{
|
||||||
|
GridMedium, HGPhaseFunction, HomogeneousMedium, MajorantGrid, Medium, RGBGridMedium,
|
||||||
|
};
|
||||||
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
use shared::core::spectrum::{Spectrum, SpectrumTrait};
|
||||||
use shared::spectra::{RGBIlluminantSpectrum, RGBUnboundedSpectrum, DenselySampledSpectrum};
|
use shared::core::texture::SpectrumType;
|
||||||
|
use shared::spectra::{
|
||||||
|
ConstantSpectrum, DenselySampledSpectrum, RGBIlluminantSpectrum, RGBUnboundedSpectrum,
|
||||||
|
};
|
||||||
use shared::utils::containers::SampledGrid;
|
use shared::utils::containers::SampledGrid;
|
||||||
use shared::{Float, Transform, core::medium::MajorantGrid};
|
use shared::{Float, Ptr, Transform};
|
||||||
|
|
||||||
pub trait RGBGridMediumCreator {
|
struct MeasuredSS {
|
||||||
fn new(
|
name: &'static str,
|
||||||
bounds: &Bounds3f,
|
sigma_prime_s: [Float; 3],
|
||||||
render_from_medium: &Transform,
|
sigma_a: [Float; 3],
|
||||||
g: Float,
|
|
||||||
sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
|
|
||||||
sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
|
|
||||||
sigma_scale: Float,
|
|
||||||
le_grid: SampledGrid<RGBIlluminantSpectrum>,
|
|
||||||
le_scale: Float,
|
|
||||||
) -> Self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RGBGridMediumCreator for RGBGridMedium {
|
// From "A Practical Model for Subsurface Light Transport"
|
||||||
#[allow(clippy::too_many_arguments)]
|
// Jensen, Marschner, Levoy, Hanrahan — SIGGRAPH 2001
|
||||||
fn new(
|
// and "Acquiring Scattering Properties of Participating Media by Dilution"
|
||||||
bounds: &Bounds3f,
|
// Narasimhan, Gupta, Donner, Ramamoorthi, Nayar, Jensen — SIGGRAPH 2006
|
||||||
render_from_medium: &Transform,
|
static SUBSURFACE_TABLE: &[MeasuredSS] = &[
|
||||||
g: Float,
|
MeasuredSS {
|
||||||
sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
|
name: "Apple",
|
||||||
sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
|
sigma_prime_s: [2.29, 2.39, 1.97],
|
||||||
sigma_scale: Float,
|
sigma_a: [0.0030, 0.0034, 0.046],
|
||||||
le_grid: SampledGrid<RGBIlluminantSpectrum>,
|
},
|
||||||
le_scale: Float,
|
MeasuredSS {
|
||||||
) -> Self {
|
name: "Chicken1",
|
||||||
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16));
|
sigma_prime_s: [0.15, 0.21, 0.38],
|
||||||
for z in 0..majorant_grid.res.x() {
|
sigma_a: [0.015, 0.077, 0.19],
|
||||||
for y in 0..majorant_grid.res.y() {
|
},
|
||||||
for x in 0..majorant_grid.res.x() {
|
MeasuredSS {
|
||||||
let bounds = majorant_grid.voxel_bounds(x, y, z);
|
name: "Chicken2",
|
||||||
let convert = |s: &RGBUnboundedSpectrum| s.max_value();
|
sigma_prime_s: [0.19, 0.25, 0.32],
|
||||||
let max_sigma_t = sigma_a_grid.max_value_convert(bounds, convert)
|
sigma_a: [0.018, 0.088, 0.20],
|
||||||
+ sigma_s_grid.max_value_convert(bounds, convert);
|
},
|
||||||
majorant_grid.set(x, y, z, sigma_scale * max_sigma_t);
|
MeasuredSS {
|
||||||
}
|
name: "Cream",
|
||||||
}
|
sigma_prime_s: [7.38, 5.47, 3.15],
|
||||||
}
|
sigma_a: [0.0002, 0.0028, 0.0163],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Ketchup",
|
||||||
|
sigma_prime_s: [0.18, 0.07, 0.03],
|
||||||
|
sigma_a: [0.061, 0.97, 1.45],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Marble",
|
||||||
|
sigma_prime_s: [2.19, 2.62, 3.00],
|
||||||
|
sigma_a: [0.0021, 0.0041, 0.0071],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Potato",
|
||||||
|
sigma_prime_s: [0.68, 0.70, 0.55],
|
||||||
|
sigma_a: [0.0024, 0.0090, 0.12],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Skimmilk",
|
||||||
|
sigma_prime_s: [0.70, 1.22, 1.90],
|
||||||
|
sigma_a: [0.0014, 0.0025, 0.0142],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Skin1",
|
||||||
|
sigma_prime_s: [0.74, 0.88, 1.01],
|
||||||
|
sigma_a: [0.032, 0.17, 0.48],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Skin2",
|
||||||
|
sigma_prime_s: [1.09, 1.59, 1.79],
|
||||||
|
sigma_a: [0.013, 0.070, 0.145],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Spectralon",
|
||||||
|
sigma_prime_s: [11.6, 20.4, 14.9],
|
||||||
|
sigma_a: [0.00, 0.00, 0.00],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Wholemilk",
|
||||||
|
sigma_prime_s: [2.55, 3.21, 3.77],
|
||||||
|
sigma_a: [0.0011, 0.0024, 0.014],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Lowfat Milk",
|
||||||
|
sigma_prime_s: [0.89187, 1.5136, 2.532],
|
||||||
|
sigma_a: [0.002875, 0.00575, 0.0115],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Reduced Milk",
|
||||||
|
sigma_prime_s: [2.4858, 3.1669, 4.5214],
|
||||||
|
sigma_a: [0.0025556, 0.0051111, 0.012778],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Regular Milk",
|
||||||
|
sigma_prime_s: [4.5513, 5.8294, 7.136],
|
||||||
|
sigma_a: [0.0015333, 0.0046, 0.019933],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Espresso",
|
||||||
|
sigma_prime_s: [0.72378, 0.84557, 1.0247],
|
||||||
|
sigma_a: [4.7984, 6.5751, 8.8493],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Mint Mocha Coffee",
|
||||||
|
sigma_prime_s: [0.31602, 0.38538, 0.48131],
|
||||||
|
sigma_a: [3.772, 5.8228, 7.82],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Lowfat Soy Milk",
|
||||||
|
sigma_prime_s: [0.30576, 0.34233, 0.61664],
|
||||||
|
sigma_a: [0.0014375, 0.0071875, 0.035937],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Regular Soy Milk",
|
||||||
|
sigma_prime_s: [0.59223, 0.73866, 1.4693],
|
||||||
|
sigma_a: [0.0019167, 0.0095833, 0.065167],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Lowfat Chocolate Milk",
|
||||||
|
sigma_prime_s: [0.64925, 0.83916, 1.1057],
|
||||||
|
sigma_a: [0.0115, 0.0368, 0.1564],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Regular Chocolate Milk",
|
||||||
|
sigma_prime_s: [1.4585, 2.1289, 2.9527],
|
||||||
|
sigma_a: [0.010063, 0.043125, 0.14375],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Coke",
|
||||||
|
sigma_prime_s: [8.9053e-05, 8.372e-05, 0.0],
|
||||||
|
sigma_a: [0.10014, 0.16503, 0.2468],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Pepsi",
|
||||||
|
sigma_prime_s: [6.1697e-05, 4.2564e-05, 0.0],
|
||||||
|
sigma_a: [0.091641, 0.14158, 0.20729],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Sprite",
|
||||||
|
sigma_prime_s: [6.0306e-06, 6.4139e-06, 6.5504e-06],
|
||||||
|
sigma_a: [0.001886, 0.0018308, 0.0020025],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Gatorade",
|
||||||
|
sigma_prime_s: [0.0024574, 0.003007, 0.0037325],
|
||||||
|
sigma_a: [0.024794, 0.019289, 0.008878],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Chardonnay",
|
||||||
|
sigma_prime_s: [1.7982e-05, 1.3758e-05, 1.2023e-05],
|
||||||
|
sigma_a: [0.010782, 0.011855, 0.023997],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "White Zinfandel",
|
||||||
|
sigma_prime_s: [1.7501e-05, 1.9069e-05, 1.288e-05],
|
||||||
|
sigma_a: [0.012072, 0.016184, 0.019843],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Merlot",
|
||||||
|
sigma_prime_s: [2.1129e-05, 0.0, 0.0],
|
||||||
|
sigma_a: [0.11632, 0.25191, 0.29434],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Budweiser Beer",
|
||||||
|
sigma_prime_s: [2.4356e-05, 2.4079e-05, 1.0564e-05],
|
||||||
|
sigma_a: [0.011492, 0.024911, 0.057786],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Coors Light Beer",
|
||||||
|
sigma_prime_s: [5.0922e-05, 4.301e-05, 0.0],
|
||||||
|
sigma_a: [0.006164, 0.013984, 0.034983],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Clorox",
|
||||||
|
sigma_prime_s: [0.0024035, 0.0031373, 0.003991],
|
||||||
|
sigma_a: [0.0033542, 0.014892, 0.026297],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Apple Juice",
|
||||||
|
sigma_prime_s: [0.00013612, 0.00015836, 0.000227],
|
||||||
|
sigma_a: [0.012957, 0.023741, 0.052184],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Cranberry Juice",
|
||||||
|
sigma_prime_s: [0.00010402, 0.00011646, 7.8139e-05],
|
||||||
|
sigma_a: [0.039437, 0.094223, 0.12426],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Grape Juice",
|
||||||
|
sigma_prime_s: [5.382e-05, 0.0, 0.0],
|
||||||
|
sigma_a: [0.10404, 0.23958, 0.29325],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Ruby Grapefruit Juice",
|
||||||
|
sigma_prime_s: [0.011002, 0.010927, 0.011036],
|
||||||
|
sigma_a: [0.085867, 0.18314, 0.25262],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "White Grapefruit Juice",
|
||||||
|
sigma_prime_s: [0.22826, 0.23998, 0.32748],
|
||||||
|
sigma_a: [0.0138, 0.018831, 0.056781],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Shampoo",
|
||||||
|
sigma_prime_s: [0.0007176, 0.0008303, 0.0009016],
|
||||||
|
sigma_a: [0.014107, 0.045693, 0.061717],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Strawberry Shampoo",
|
||||||
|
sigma_prime_s: [0.00015671, 0.00015947, 1.518e-05],
|
||||||
|
sigma_a: [0.01449, 0.05796, 0.075823],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Head & Shoulders Shampoo",
|
||||||
|
sigma_prime_s: [0.023805, 0.028804, 0.034306],
|
||||||
|
sigma_a: [0.084621, 0.15688, 0.20365],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Lemon Tea Powder",
|
||||||
|
sigma_prime_s: [0.040224, 0.045264, 0.051081],
|
||||||
|
sigma_a: [2.4288, 4.5757, 7.2127],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Orange Powder",
|
||||||
|
sigma_prime_s: [0.00015617, 0.00017482, 0.0001762],
|
||||||
|
sigma_a: [0.001449, 0.003441, 0.007863],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Pink Lemonade Powder",
|
||||||
|
sigma_prime_s: [0.00012103, 0.00013073, 0.00012528],
|
||||||
|
sigma_a: [0.001165, 0.002366, 0.003195],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Cappuccino Powder",
|
||||||
|
sigma_prime_s: [1.8436, 2.5851, 2.1662],
|
||||||
|
sigma_a: [35.844, 49.547, 61.084],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Salt Powder",
|
||||||
|
sigma_prime_s: [0.027333, 0.032451, 0.031979],
|
||||||
|
sigma_a: [0.28415, 0.3257, 0.34148],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Sugar Powder",
|
||||||
|
sigma_prime_s: [0.00022272, 0.00025513, 0.000271],
|
||||||
|
sigma_a: [0.012638, 0.031051, 0.050124],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Suisse Mocha Powder",
|
||||||
|
sigma_prime_s: [2.7979, 3.5452, 4.3365],
|
||||||
|
sigma_a: [17.502, 27.004, 35.433],
|
||||||
|
},
|
||||||
|
MeasuredSS {
|
||||||
|
name: "Pacific Ocean Surface Water",
|
||||||
|
sigma_prime_s: [0.0001764, 0.00032095, 0.00019617],
|
||||||
|
sigma_a: [0.031845, 0.031324, 0.030147],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
Self {
|
fn get_medium_scattering_properties(name: &str) -> Option<(Spectrum, Spectrum)> {
|
||||||
bounds: *bounds,
|
SUBSURFACE_TABLE.iter().find(|m| m.name == name).map(|m| {
|
||||||
render_from_medium: *render_from_medium,
|
let sigma_a = Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(&SRGB, m.sigma_a.into()));
|
||||||
le_grid,
|
let sigma_s =
|
||||||
le_scale,
|
Spectrum::RGBUnbounded(RGBUnboundedSpectrum::new(&SRGB, m.sigma_prime_s.into()));
|
||||||
phase: HGPhaseFunction::new(g),
|
(sigma_a, sigma_s)
|
||||||
sigma_a_grid,
|
})
|
||||||
sigma_s_grid,
|
|
||||||
sigma_scale,
|
|
||||||
majorant_grid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GridMediumCreator {
|
pub trait CreateMedium {
|
||||||
fn new(
|
fn create(
|
||||||
bounds: &Bounds3f,
|
name: &str,
|
||||||
render_from_medium: &Transform,
|
parameters: &ParameterDictionary,
|
||||||
sigma_a: &Spectrum,
|
render_from_medium: Transform,
|
||||||
sigma_s: &Spectrum,
|
loc: &FileLoc,
|
||||||
sigma_scale: Float,
|
arena: &Arena,
|
||||||
g: Float,
|
) -> Result<Ptr<Medium>> {
|
||||||
density_grid: SampledGrid<Float>,
|
let medium = match name {
|
||||||
temperature_grid: Option<SampledGrid<Float>>,
|
"homogeneous" => create_homogeneous(parameters, loc, arena)?,
|
||||||
le: &Spectrum,
|
"uniformgrid" => create_grid(parameters, render_from_medium, loc, arena)?,
|
||||||
le_scale: SampledGrid<Float>,
|
"rgbgrid" => create_rgb_grid(parameters, render_from_medium, loc, arena)?,
|
||||||
) -> Self;
|
// "cloud" => create_cloud(parameters, render_from_medium, loc, arena)?,
|
||||||
}
|
// "nanovdb" => create_nanovdb(parameters, render_from_medium, loc, arena)?,
|
||||||
|
_ => bail!("{}: unknown medium \"{}\"", loc, name),
|
||||||
impl GridMediumCreator for GridMedium {
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn new(
|
|
||||||
bounds: &Bounds3f,
|
|
||||||
render_from_medium: &Transform,
|
|
||||||
sigma_a: &Spectrum,
|
|
||||||
sigma_s: &Spectrum,
|
|
||||||
sigma_scale: Float,
|
|
||||||
g: Float,
|
|
||||||
density_grid: SampledGrid<Float>,
|
|
||||||
temperature_grid: Option<SampledGrid<Float>>,
|
|
||||||
le: &Spectrum,
|
|
||||||
le_scale: SampledGrid<Float>,
|
|
||||||
) -> Self {
|
|
||||||
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(sigma_a);
|
|
||||||
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(sigma_s);
|
|
||||||
|
|
||||||
sigma_a_spec.scale(sigma_scale);
|
|
||||||
sigma_s_spec.scale(sigma_scale);
|
|
||||||
|
|
||||||
let le_spec = DenselySampledSpectrum::from_spectrum(le);
|
|
||||||
|
|
||||||
let mut majorant_grid = MajorantGrid::new(*bounds, Point3i::new(16, 16, 16)).device;
|
|
||||||
let is_emissive = if temperature_grid.is_some() {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
Spectrum::Dense(le_spec.device()).max_value() > 0.
|
|
||||||
};
|
};
|
||||||
|
Ok(arena.alloc(medium))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for z in 0..majorant_grid.res.z() {
|
fn create_homogeneous(
|
||||||
for y in 0..majorant_grid.res.y() {
|
parameters: &ParameterDictionary,
|
||||||
for x in 0..majorant_grid.res.x() {
|
loc: &FileLoc,
|
||||||
let bounds = majorant_grid.voxel_bounds(x, y, z);
|
arena: &Arena,
|
||||||
majorant_grid.set(x, y, z, density_grid.max_value(bounds));
|
) -> Result<Medium> {
|
||||||
}
|
let preset = parameters.get_one_string("preset", "")?;
|
||||||
|
|
||||||
|
let (mut sigma_a, mut sigma_s) = if !preset.is_empty() {
|
||||||
|
match get_medium_scattering_properties(&preset) {
|
||||||
|
Some(props) => (Some(props.0), Some(props.1)),
|
||||||
|
None => {
|
||||||
|
log::warn!("{}: material preset \"{}\" not found", loc, preset);
|
||||||
|
(None, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
if sigma_a.is_none() {
|
||||||
bounds: *bounds,
|
sigma_a = parameters
|
||||||
render_from_medium: *render_from_medium,
|
.get_one_spectrum("sigma_a", None, SpectrumType::Unbounded)
|
||||||
sigma_a_spec: sigma_a_spec.device(),
|
.or(Some(Spectrum::Constant(ConstantSpectrum::new(1.0))));
|
||||||
sigma_s_spec: sigma_s_spec.device(),
|
}
|
||||||
density_grid,
|
|
||||||
phase: HGPhaseFunction::new(g),
|
if sigma_s.is_none() {
|
||||||
temperature_grid,
|
sigma_s = parameters
|
||||||
le_spec: le_spec.device(),
|
.get_one_spectrum("sigma_s", None, SpectrumType::Unbounded)
|
||||||
le_scale,
|
.or(Some(Spectrum::Constant(ConstantSpectrum::new(1.0))));
|
||||||
is_emissive,
|
}
|
||||||
majorant_grid,
|
|
||||||
|
let sigma_a = sigma_a.unwrap();
|
||||||
|
let sigma_s = sigma_s.unwrap();
|
||||||
|
|
||||||
|
let le = parameters
|
||||||
|
.get_one_spectrum("Le", None, SpectrumType::Illuminant)
|
||||||
|
.filter(|s| s.max_value() > 0.0);
|
||||||
|
|
||||||
|
let mut le_scale = parameters.get_one_float("Lescale", 1.0)?;
|
||||||
|
let le = match le {
|
||||||
|
Some(s) => {
|
||||||
|
le_scale /= spectrum_to_photometric(s);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
None => Spectrum::Constant(ConstantSpectrum::new(0.0)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let sigma_scale = parameters.get_one_float("scale", 1.0)?;
|
||||||
|
let g = parameters.get_one_float("g", 0.0)?;
|
||||||
|
|
||||||
|
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
|
||||||
|
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
|
||||||
|
let mut le_spec = DenselySampledSpectrum::from_spectrum(&le);
|
||||||
|
|
||||||
|
sigma_a_spec.scale(sigma_scale);
|
||||||
|
sigma_s_spec.scale(sigma_scale);
|
||||||
|
le_spec.scale(le_scale);
|
||||||
|
|
||||||
|
Ok(Medium::Homogeneous(HomogeneousMedium {
|
||||||
|
sigma_a_spec: arena.alloc(sigma_a_spec),
|
||||||
|
sigma_s_spec: arena.alloc(sigma_s_spec),
|
||||||
|
le_spec: arena.alloc(le_spec),
|
||||||
|
phase: HGPhaseFunction::new(g),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_grid(
|
||||||
|
parameters: &ParameterDictionary,
|
||||||
|
render_from_medium: shared::Transform,
|
||||||
|
loc: &FileLoc,
|
||||||
|
arena: &Arena,
|
||||||
|
) -> Result<Medium> {
|
||||||
|
let density = parameters.get_float_array("density")?;
|
||||||
|
if density.is_empty() {
|
||||||
|
bail!("{}: no \"density\" value provided for grid medium", loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
let temperature = parameters.get_float_array("temperature")?;
|
||||||
|
if !temperature.is_empty() && temperature.len() != density.len() {
|
||||||
|
bail!(
|
||||||
|
"{}: different number of samples ({} vs {}) for \"density\" and \"temperature\"",
|
||||||
|
loc,
|
||||||
|
density.len(),
|
||||||
|
temperature.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let nx = parameters.get_one_int("nx", 1)?;
|
||||||
|
let ny = parameters.get_one_int("ny", 1)?;
|
||||||
|
let nz = parameters.get_one_int("nz", 1)?;
|
||||||
|
if density.len() as i32 != nx * ny * nz {
|
||||||
|
bail!(
|
||||||
|
"{}: grid medium has {} density values; expected nx*ny*nz = {}",
|
||||||
|
loc,
|
||||||
|
density.len(),
|
||||||
|
nx * ny * nz
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let density_grid = SampledGrid::new(&density, nx, ny, nz);
|
||||||
|
|
||||||
|
let temperature_grid = if !temperature.is_empty() {
|
||||||
|
Some(SampledGrid::new(&temperature, nx, ny, nz))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let le = parameters.get_one_spectrum("Le", None, SpectrumType::Illuminant);
|
||||||
|
if le.is_some() && !temperature.is_empty() {
|
||||||
|
bail!(
|
||||||
|
"{}: both \"Le\" and \"temperature\" values were provided",
|
||||||
|
loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (le, le_norm) = match le.filter(|s| s.max_value() > 0.0) {
|
||||||
|
Some(s) => (s, 1.0 / spectrum_to_photometric(s)),
|
||||||
|
None => (Spectrum::Constant(ConstantSpectrum::new(0.0)), 1.0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let le_scale_values = parameters.get_float_array("Lescale")?;
|
||||||
|
let le_grid = if le_scale_values.is_empty() {
|
||||||
|
SampledGrid::new(&[le_norm], 1, 1, 1)
|
||||||
|
} else {
|
||||||
|
if le_scale_values.len() as i32 != nx * ny * nz {
|
||||||
|
bail!(
|
||||||
|
"{}: expected {} values for \"Lescale\" but got {}",
|
||||||
|
loc,
|
||||||
|
nx * ny * nz,
|
||||||
|
le_scale_values.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let scaled: Vec<Float> = le_scale_values.iter().map(|v| v * le_norm).collect();
|
||||||
|
SampledGrid::new(&scaled, nx, ny, nz)
|
||||||
|
};
|
||||||
|
|
||||||
|
let p0 = parameters.get_one_point3f("p0", Point3f::new(0.0, 0.0, 0.0))?;
|
||||||
|
let p1 = parameters.get_one_point3f("p1", Point3f::new(1.0, 1.0, 1.0))?;
|
||||||
|
let bounds = Bounds3f::from_points(p0, p1);
|
||||||
|
|
||||||
|
let g = parameters.get_one_float("g", 0.0)?;
|
||||||
|
|
||||||
|
let sigma_a = parameters
|
||||||
|
.get_one_spectrum("sigma_a", None, SpectrumType::Unbounded)
|
||||||
|
.unwrap_or(Spectrum::Constant(ConstantSpectrum::new(1.0)));
|
||||||
|
|
||||||
|
let sigma_s = parameters
|
||||||
|
.get_one_spectrum("sigma_s", None, SpectrumType::Unbounded)
|
||||||
|
.unwrap_or(Spectrum::Constant(ConstantSpectrum::new(1.0)));
|
||||||
|
|
||||||
|
let sigma_scale = parameters.get_one_float("scale", 1.0)?;
|
||||||
|
|
||||||
|
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
|
||||||
|
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
|
||||||
|
sigma_a_spec.scale(sigma_scale);
|
||||||
|
sigma_s_spec.scale(sigma_scale);
|
||||||
|
|
||||||
|
let le_spec = DenselySampledSpectrum::from_spectrum(&le);
|
||||||
|
|
||||||
|
let is_emissive = if temperature_grid.is_some() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
le.max_value() > 0.0
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut majorant_grid = MajorantGrid::new(bounds, Point3i::new(16, 16, 16));
|
||||||
|
for z in 0..majorant_grid.res.z() {
|
||||||
|
for y in 0..majorant_grid.res.y() {
|
||||||
|
for x in 0..majorant_grid.res.x() {
|
||||||
|
let voxel_bounds = majorant_grid.voxel_bounds(x, y, z);
|
||||||
|
majorant_grid.set(x, y, z, density_grid.max_value(voxel_bounds));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Medium::Grid(GridMedium {
|
||||||
|
bounds,
|
||||||
|
render_from_medium,
|
||||||
|
sigma_a_spec: arena.alloc(sigma_a_spec),
|
||||||
|
sigma_s_spec: arena.alloc(sigma_s_spec),
|
||||||
|
density_grid: arena.alloc(density_grid),
|
||||||
|
phase: HGPhaseFunction::new(g),
|
||||||
|
temperature_grid: arena.alloc_opt(temperature_grid),
|
||||||
|
le_spec: arena.alloc(le_spec),
|
||||||
|
le_scale: arena.alloc(le_grid),
|
||||||
|
is_emissive,
|
||||||
|
majorant_grid: arena.alloc(majorant_grid),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HomogeneousMediumCreator {
|
fn create_rgb_grid(
|
||||||
fn new(
|
parameters: &ParameterDictionary,
|
||||||
sigma_a: Spectrum,
|
render_from_medium: shared::Transform,
|
||||||
sigma_s: Spectrum,
|
loc: &FileLoc,
|
||||||
sigma_scale: Float,
|
arena: &Arena,
|
||||||
le: Spectrum,
|
) -> Result<Medium> {
|
||||||
le_scale: Float,
|
let sigma_a_rgb = parameters.get_rgb_array("sigma_a")?;
|
||||||
g: Float,
|
let sigma_s_rgb = parameters.get_rgb_array("sigma_s")?;
|
||||||
) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HomogeneousMediumCreator for HomogeneousMedium {
|
if sigma_a_rgb.is_empty() && sigma_s_rgb.is_empty() {
|
||||||
fn new(
|
bail!("{}: RGB grid requires \"sigma_a\" and/or \"sigma_s\"", loc);
|
||||||
sigma_a: Spectrum,
|
}
|
||||||
sigma_s: Spectrum,
|
|
||||||
sigma_scale: Float,
|
|
||||||
le: Spectrum,
|
|
||||||
le_scale: Float,
|
|
||||||
g: Float,
|
|
||||||
) -> Self {
|
|
||||||
let mut sigma_a_spec = DenselySampledSpectrum::from_spectrum(&sigma_a);
|
|
||||||
let mut sigma_s_spec = DenselySampledSpectrum::from_spectrum(&sigma_s);
|
|
||||||
let mut le_spec = DenselySampledSpectrum::from_spectrum(&le);
|
|
||||||
|
|
||||||
sigma_a_spec.scale(sigma_scale);
|
let n_density = if !sigma_a_rgb.is_empty() {
|
||||||
sigma_s_spec.scale(sigma_scale);
|
if !sigma_s_rgb.is_empty() && sigma_a_rgb.len() != sigma_s_rgb.len() {
|
||||||
le_spec.scale(le_scale);
|
bail!(
|
||||||
|
"{}: different number of samples ({} vs {}) for \"sigma_a\" and \"sigma_s\"",
|
||||||
|
loc,
|
||||||
|
sigma_a_rgb.len(),
|
||||||
|
sigma_s_rgb.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sigma_a_rgb.len()
|
||||||
|
} else {
|
||||||
|
sigma_s_rgb.len()
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
let le_rgb = parameters.get_rgb_array("Le")?;
|
||||||
sigma_a_spec: sigma_a_spec.device(),
|
if !le_rgb.is_empty() && sigma_a_rgb.is_empty() {
|
||||||
sigma_s_spec: sigma_s_spec.device(),
|
bail!(
|
||||||
le_spec: le_spec.device(),
|
"{}: RGB grid requires \"sigma_a\" if \"Le\" is provided",
|
||||||
phase: HGPhaseFunction::new(g),
|
loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if !le_rgb.is_empty() && le_rgb.len() != n_density {
|
||||||
|
bail!(
|
||||||
|
"{}: expected {} values for \"Le\" but got {}",
|
||||||
|
loc,
|
||||||
|
n_density,
|
||||||
|
le_rgb.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let nx = parameters.get_one_int("nx", 1)?;
|
||||||
|
let ny = parameters.get_one_int("ny", 1)?;
|
||||||
|
let nz = parameters.get_one_int("nz", 1)?;
|
||||||
|
if n_density as i32 != nx * ny * nz {
|
||||||
|
bail!(
|
||||||
|
"{}: RGB grid medium has {} values; expected nx*ny*nz = {}",
|
||||||
|
loc,
|
||||||
|
n_density,
|
||||||
|
nx * ny * nz
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cs = parameters.colorspace();
|
||||||
|
|
||||||
|
let sigma_a_grid = if !sigma_a_rgb.is_empty() {
|
||||||
|
let spectra: Vec<RGBUnboundedSpectrum> = sigma_a_rgb
|
||||||
|
.iter()
|
||||||
|
.map(|rgb| RGBUnboundedSpectrum::new(cs, *rgb))
|
||||||
|
.collect();
|
||||||
|
Some(SampledGrid::new(&spectra, nx, ny, nz))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let sigma_s_grid = if !sigma_s_rgb.is_empty() {
|
||||||
|
let spectra: Vec<RGBUnboundedSpectrum> = sigma_s_rgb
|
||||||
|
.iter()
|
||||||
|
.map(|rgb| RGBUnboundedSpectrum::new(cs, *rgb))
|
||||||
|
.collect();
|
||||||
|
Some(SampledGrid::new(&spectra, nx, ny, nz))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let le_grid = if !le_rgb.is_empty() {
|
||||||
|
let spectra: Vec<RGBIlluminantSpectrum> = le_rgb
|
||||||
|
.iter()
|
||||||
|
.map(|rgb| RGBIlluminantSpectrum::new(cs, *rgb))
|
||||||
|
.collect();
|
||||||
|
Some(SampledGrid::new(&spectra, nx, ny, nz))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let p0 = parameters.get_one_point3f("p0", Point3f::new(0.0, 0.0, 0.0))?;
|
||||||
|
let p1 = parameters.get_one_point3f("p1", Point3f::new(1.0, 1.0, 1.0))?;
|
||||||
|
let bounds = Bounds3f::from_points(p0, p1);
|
||||||
|
|
||||||
|
let le_scale = parameters.get_one_float("Lescale", 1.0)?;
|
||||||
|
let g = parameters.get_one_float("g", 0.0)?;
|
||||||
|
let sigma_scale = parameters.get_one_float("scale", 1.0)?;
|
||||||
|
|
||||||
|
// Build majorant grid from combined sigma_a + sigma_s maxima
|
||||||
|
let mut majorant_grid = MajorantGrid::new(bounds, Point3i::new(16, 16, 16));
|
||||||
|
let convert = |s: &RGBUnboundedSpectrum| s.max_value();
|
||||||
|
for z in 0..majorant_grid.res.z() {
|
||||||
|
for y in 0..majorant_grid.res.y() {
|
||||||
|
for x in 0..majorant_grid.res.x() {
|
||||||
|
let voxel_bounds = majorant_grid.voxel_bounds(x, y, z);
|
||||||
|
let max_a = sigma_a_grid
|
||||||
|
.as_ref()
|
||||||
|
.map(|g| g.max_value_convert(voxel_bounds, convert))
|
||||||
|
.unwrap_or(1.0);
|
||||||
|
let max_s = sigma_s_grid
|
||||||
|
.as_ref()
|
||||||
|
.map(|g| g.max_value_convert(voxel_bounds, convert))
|
||||||
|
.unwrap_or(1.0);
|
||||||
|
majorant_grid.set(x, y, z, sigma_scale * (max_a + max_s));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Medium::RGBGrid(RGBGridMedium {
|
||||||
|
bounds,
|
||||||
|
render_from_medium,
|
||||||
|
phase: HGPhaseFunction::new(g),
|
||||||
|
le_scale,
|
||||||
|
sigma_scale,
|
||||||
|
sigma_a_grid: arena.alloc_opt(sigma_a_grid),
|
||||||
|
sigma_s_grid: arena.alloc_opt(sigma_s_grid),
|
||||||
|
le_grid: arena.alloc_opt(le_grid),
|
||||||
|
majorant_grid: arena.alloc(majorant_grid),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -556,7 +556,7 @@ impl ParserTarget for BasicSceneBuilder {
|
||||||
self.current_accelerator
|
self.current_accelerator
|
||||||
.take()
|
.take()
|
||||||
.expect("Accelerator not set before WorldBegin"),
|
.expect("Accelerator not set before WorldBegin"),
|
||||||
arena,
|
&arena,
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
use crate::utils::parallel::{run_async, AsyncJob};
|
use crate::utils::parallel::{run_async, AsyncJob};
|
||||||
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary};
|
||||||
use crate::utils::resolve_filename;
|
use crate::utils::resolve_filename;
|
||||||
use crate::{Arena, FileLoc};
|
use crate::{Arena, ArenaUpload, FileLoc, Upload};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use shared::core::camera::{Camera, CameraTransform};
|
use shared::core::camera::{Camera, CameraTransform};
|
||||||
|
|
@ -556,7 +556,7 @@ impl BasicScene {
|
||||||
loaded_shapes: &[Vec<Shape>],
|
loaded_shapes: &[Vec<Shape>],
|
||||||
shape_entities: &[ShapeSceneEntity],
|
shape_entities: &[ShapeSceneEntity],
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
arena: &mut Arena,
|
arena: &Arena,
|
||||||
) -> HashMap<usize, Vec<Light>> {
|
) -> HashMap<usize, Vec<Light>> {
|
||||||
let light_state = self.light_state.lock();
|
let light_state = self.light_state.lock();
|
||||||
let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new();
|
let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new();
|
||||||
|
|
@ -672,6 +672,53 @@ impl BasicScene {
|
||||||
(primitives, area_lights)
|
(primitives, area_lights)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_primitives_inner(
|
||||||
|
shapes: Vec<Ptr<Shape>>,
|
||||||
|
mtl: Material,
|
||||||
|
alpha_tex: &Option<Arc<FloatTexture>>,
|
||||||
|
mi: MediumInterface,
|
||||||
|
al_params: Option<&AreaLightEntity>,
|
||||||
|
render_from_light: Transform,
|
||||||
|
film_cs: Option<&RGBColorSpace>,
|
||||||
|
arena: &mut Arena,
|
||||||
|
area_lights: &mut Vec<Arc<Light>>,
|
||||||
|
) -> Vec<(Ptr<Shape>, Ptr<Light>, Ptr<GPUFloatTexture>)> {
|
||||||
|
shapes
|
||||||
|
.into_iter()
|
||||||
|
.map(|shape| {
|
||||||
|
let area_light_ptr = al_params
|
||||||
|
.and_then(|al_entity| {
|
||||||
|
let cs = al_entity.parameters.color_space.as_deref().or(film_cs);
|
||||||
|
let default_alpha = Arc::new(FloatTexture::default());
|
||||||
|
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
||||||
|
crate::core::light::create_area_light(
|
||||||
|
render_from_light,
|
||||||
|
None,
|
||||||
|
&al_entity.parameters,
|
||||||
|
&al_entity.loc,
|
||||||
|
&shape,
|
||||||
|
alpha_ref,
|
||||||
|
cs,
|
||||||
|
arena,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.unwrap_or(Ptr::null());
|
||||||
|
|
||||||
|
if !area_light_ptr.is_null() {
|
||||||
|
area_lights.push(Arc::new(unsafe { &*area_light_ptr.as_raw() }.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let alpha_ptr = alpha_tex
|
||||||
|
.as_ref()
|
||||||
|
.map(|t| arena.upload(t.as_ref()))
|
||||||
|
.unwrap_or(Ptr::null());
|
||||||
|
|
||||||
|
(shape, area_light_ptr, alpha_ptr)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
fn build_primitives_for_entity(
|
fn build_primitives_for_entity(
|
||||||
entity: &ShapeSceneEntity,
|
entity: &ShapeSceneEntity,
|
||||||
textures: &NamedTextures,
|
textures: &NamedTextures,
|
||||||
|
|
@ -727,52 +774,30 @@ impl BasicScene {
|
||||||
|
|
||||||
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
|
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
|
||||||
|
|
||||||
for shape in shapes {
|
let built = Self::build_primitives_inner(
|
||||||
let area_light = al_params.and_then(|al_entity| {
|
shapes,
|
||||||
let colorspace_ref = al_entity.parameters.color_space.as_deref().or(film_cs);
|
mtl,
|
||||||
let default_alpha = Arc::new(FloatTexture::default());
|
&alpha_tex,
|
||||||
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
mi.clone(),
|
||||||
|
al_params,
|
||||||
crate::core::light::create_area_light(
|
*entity.render_from_object,
|
||||||
*entity.render_from_object,
|
film_cs,
|
||||||
None,
|
arena,
|
||||||
&al_entity.parameters,
|
area_lights,
|
||||||
&al_entity.loc,
|
);
|
||||||
&shape,
|
|
||||||
alpha_ref,
|
|
||||||
colorspace_ref,
|
|
||||||
arena,
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
});
|
|
||||||
|
|
||||||
let uploaded_light = area_light
|
|
||||||
.as_ref()
|
|
||||||
.map(|l| l.upload(arena))
|
|
||||||
.unwrap_or(Ptr::null());
|
|
||||||
|
|
||||||
if let Some(ref light) = area_light {
|
|
||||||
area_lights.push(Arc::new(light.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let shape_ptr = shape.upload(arena);
|
|
||||||
|
|
||||||
let prim =
|
|
||||||
if uploaded_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
|
||||||
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
|
|
||||||
} else {
|
|
||||||
Primitive::Geometric(GeometricPrimitive::new(
|
|
||||||
shape_ptr,
|
|
||||||
mtl.upload(arena),
|
|
||||||
uploaded_light,
|
|
||||||
mi.clone(),
|
|
||||||
alpha_tex
|
|
||||||
.as_ref()
|
|
||||||
.map(|t| t.upload(arena))
|
|
||||||
.unwrap_or(Ptr::null()),
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
|
for (shape, light_ptr, alpha_ptr) in built {
|
||||||
|
let prim = if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
||||||
|
Primitive::Simple(SimplePrimitive::new(shape, Ptr::from(&mtl)))
|
||||||
|
} else {
|
||||||
|
Primitive::Geometric(GeometricPrimitive::new(
|
||||||
|
shape,
|
||||||
|
arena.alloc(mtl),
|
||||||
|
light_ptr,
|
||||||
|
mi.clone(),
|
||||||
|
alpha_ptr,
|
||||||
|
))
|
||||||
|
};
|
||||||
primitives.push(prim);
|
primitives.push(prim);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -839,56 +864,33 @@ impl BasicScene {
|
||||||
|
|
||||||
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
|
let al_params = entity.light_index.map(|idx| &light_state.area_lights[idx]);
|
||||||
|
|
||||||
for shape in shapes {
|
let built = Self::build_primitives_inner(
|
||||||
let area_light = al_params.and_then(|al_entity| {
|
shapes,
|
||||||
let colorspace_ref = al_entity.parameters.color_space.as_deref().or(film_cs);
|
mtl,
|
||||||
let default_alpha = Arc::new(FloatTexture::default());
|
&alpha_tex,
|
||||||
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
mi.clone(),
|
||||||
|
al_params,
|
||||||
crate::core::light::create_area_light(
|
entity.transformed_base.render_from_object.start_transform,
|
||||||
entity.transformed_base.render_from_object.start_transform,
|
film_cs,
|
||||||
None,
|
arena,
|
||||||
&al_entity.parameters,
|
area_lights,
|
||||||
&al_entity.loc,
|
);
|
||||||
&shape,
|
|
||||||
alpha_ref,
|
|
||||||
colorspace_ref,
|
|
||||||
arena,
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
});
|
|
||||||
|
|
||||||
let uploaded_light = area_light
|
|
||||||
.as_ref()
|
|
||||||
.map(|l| l.upload(arena))
|
|
||||||
.unwrap_or(Ptr::null());
|
|
||||||
|
|
||||||
if let Some(ref light) = area_light {
|
|
||||||
area_lights.push(Arc::new(light.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let shape_ptr = shape.upload(arena);
|
|
||||||
|
|
||||||
|
for (shape, light_ptr, alpha_ptr) in built {
|
||||||
let base_prim =
|
let base_prim =
|
||||||
if uploaded_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
if light_ptr.is_null() && !mi.is_medium_transition() && alpha_tex.is_none() {
|
||||||
Primitive::Simple(SimplePrimitive::new(shape_ptr, Ptr::from(&mtl)))
|
Primitive::Simple(SimplePrimitive::new(shape, Ptr::from(&mtl)))
|
||||||
} else {
|
} else {
|
||||||
Primitive::Geometric(GeometricPrimitive::new(
|
Primitive::Geometric(GeometricPrimitive::new(
|
||||||
shape_ptr,
|
shape,
|
||||||
mtl.upload(arena),
|
arena.alloc(mtl),
|
||||||
uploaded_light,
|
light_ptr,
|
||||||
mi.clone(),
|
mi.clone(),
|
||||||
alpha_tex
|
alpha_ptr,
|
||||||
.as_ref()
|
|
||||||
.map(|t| t.upload(arena))
|
|
||||||
.unwrap_or(Ptr::null()),
|
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
let base_ptr = arena.alloc(base_prim);
|
|
||||||
|
|
||||||
primitives.push(Primitive::Animated(AnimatedPrimitive {
|
primitives.push(Primitive::Animated(AnimatedPrimitive {
|
||||||
primitive: base_ptr,
|
primitive: arena.alloc(base_prim),
|
||||||
render_from_primitive: arena.alloc(entity.transformed_base.render_from_object),
|
render_from_primitive: arena.alloc(entity.transformed_base.render_from_object),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -937,10 +939,7 @@ impl BasicScene {
|
||||||
.get(&shape_ctx.entity_index)
|
.get(&shape_ctx.entity_index)
|
||||||
.and_then(|lights| lights.get(shape_ctx.shape_index));
|
.and_then(|lights| lights.get(shape_ctx.shape_index));
|
||||||
|
|
||||||
let area_light = shape_lights_opt
|
let light_ptr = arena.alloc_opt(shape_lights_opt);
|
||||||
.map(|l| l.upload(arena))
|
|
||||||
.unwrap_or(Ptr::null());
|
|
||||||
|
|
||||||
let shape_ptr = shape_ctx.shape;
|
let shape_ptr = shape_ctx.shape;
|
||||||
let prim = if area_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none()
|
let prim = if area_light.is_null() && !mi.is_medium_transition() && alpha_tex.is_none()
|
||||||
{
|
{
|
||||||
|
|
@ -948,10 +947,10 @@ impl BasicScene {
|
||||||
} else {
|
} else {
|
||||||
Primitive::Geometric(GeometricPrimitive::new(
|
Primitive::Geometric(GeometricPrimitive::new(
|
||||||
shape_ptr,
|
shape_ptr,
|
||||||
mtl.upload(arena),
|
arena.alloc(mtl),
|
||||||
area_light,
|
area_light,
|
||||||
mi.clone(),
|
mi.clone(),
|
||||||
alpha_tex.upload(arena),
|
arena.upload(alpha_tex),
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::shapes::{BilinearPatchMesh, TriangleMesh};
|
use crate::shapes::*;
|
||||||
use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename};
|
use crate::utils::resolve_filename;
|
||||||
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use shared::core::shape::*;
|
use shared::core::shape::*;
|
||||||
use shared::Ptr;
|
|
||||||
use shared::shapes::*;
|
use shared::shapes::*;
|
||||||
use shared::utils::Transform;
|
use shared::{Transform, Ptr};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -116,8 +116,8 @@ impl ShapeFactory for Shape {
|
||||||
global_store.push(host_arc.clone());
|
global_store.push(host_arc.clone());
|
||||||
drop(global_store);
|
drop(global_store);
|
||||||
|
|
||||||
let n_tris = host_arc.device.n_triangles;
|
let n_tris = host_arc..n_triangles;
|
||||||
let mesh_ptr = Ptr::from(&host_arc.device);
|
let mesh_ptr = Ptr::from(&host_arc);
|
||||||
let shapes: Vec<Ptr<Shape>> = (0..n_tris)
|
let shapes: Vec<Ptr<Shape>> = (0..n_tris)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let tri_shape = Shape::Triangle(TriangleShape {
|
let tri_shape = Shape::Triangle(TriangleShape {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ pub fn get_spectrum_cache() -> &'static InternCache<DenselySampledSpectrum> {
|
||||||
|
|
||||||
pub fn spectrum_to_photometric(s: Spectrum) -> Float {
|
pub fn spectrum_to_photometric(s: Spectrum) -> Float {
|
||||||
let effective_spectrum = match s {
|
let effective_spectrum = match s {
|
||||||
Spectrum::RGBIlluminant(ill) => &Spectrum::Dense(*ill.illuminant),
|
Spectrum::RGBIlluminant(ill) => &Spectrum::Dense(ill.illuminant),
|
||||||
_ => &s,
|
_ => &s,
|
||||||
};
|
};
|
||||||
effective_spectrum.inner_product(&cie_y())
|
effective_spectrum.inner_product(&cie_y())
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::textures::*;
|
use crate::textures::*;
|
||||||
use crate::utils::mipmap::{MIPMapFilterOptions, MIPMap};
|
|
||||||
use crate::utils::TextureParameterDictionary;
|
use crate::utils::TextureParameterDictionary;
|
||||||
use crate::{Arena, FileLoc};
|
use crate::{Arena, FileLoc};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
@ -9,8 +8,8 @@ use shared::core::geometry::Vector3f;
|
||||||
use shared::core::image::WrapMode;
|
use shared::core::image::WrapMode;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::core::texture::{
|
use shared::core::texture::{
|
||||||
CylindricalMapping, PlanarMapping, SphericalMapping, TextureEvalContext, TextureMapping2D,
|
CylindricalMapping, GPUFloatTexture, GPUSpectrumTexture, PlanarMapping, SphericalMapping,
|
||||||
UVMapping, GPUFloatTexture, GPUSpectrumTexture
|
TextureEvalContext, TextureMapping2D, UVMapping,
|
||||||
};
|
};
|
||||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||||
use shared::textures::*;
|
use shared::textures::*;
|
||||||
|
|
@ -30,6 +29,7 @@ pub trait SpectrumTextureTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[enum_dispatch(FloatTextureTrait)]
|
||||||
pub enum FloatTexture {
|
pub enum FloatTexture {
|
||||||
Constant(FloatConstantTexture),
|
Constant(FloatConstantTexture),
|
||||||
Checkerboard(FloatCheckerboardTexture),
|
Checkerboard(FloatCheckerboardTexture),
|
||||||
|
|
@ -40,7 +40,7 @@ pub enum FloatTexture {
|
||||||
Mix(FloatMixTexture),
|
Mix(FloatMixTexture),
|
||||||
DirectionMix(FloatDirectionMixTexture),
|
DirectionMix(FloatDirectionMixTexture),
|
||||||
// #[device(custom = "upload_image", variant_type = "GPUFloatImageTexture")]
|
// #[device(custom = "upload_image", variant_type = "GPUFloatImageTexture")]
|
||||||
Scaled(FloatScaledTexture)
|
Scaled(FloatScaledTexture),
|
||||||
Image(FloatImageTexture),
|
Image(FloatImageTexture),
|
||||||
Bilerp(FloatBilerpTexture),
|
Bilerp(FloatBilerpTexture),
|
||||||
}
|
}
|
||||||
|
|
@ -107,14 +107,11 @@ impl FloatTexture {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
#[enum_dispatch(SpectrumTextureTrait)]
|
||||||
pub enum SpectrumTexture {
|
pub enum SpectrumTexture {
|
||||||
Constant(SpectrumConstantTexture),
|
Constant(SpectrumConstantTexture),
|
||||||
Checkerboard(SpectrumCheckerboardTexture),
|
Checkerboard(SpectrumCheckerboardTexture),
|
||||||
Dots(SpectrumDotsTexture),
|
Dots(SpectrumDotsTexture),
|
||||||
// #[device(
|
|
||||||
// custom = "upload_spectrum_image",
|
|
||||||
// variant_type = "GPUSpectrumImageTexture"
|
|
||||||
// )]
|
|
||||||
Image(SpectrumImageTexture),
|
Image(SpectrumImageTexture),
|
||||||
Bilerp(SpectrumBilerpTexture),
|
Bilerp(SpectrumBilerpTexture),
|
||||||
Scaled(SpectrumScaledTexture),
|
Scaled(SpectrumScaledTexture),
|
||||||
|
|
@ -135,12 +132,11 @@ impl SpectrumTexture {
|
||||||
scale: inner.base.scale,
|
scale: inner.base.scale,
|
||||||
invert: inner.base.invert,
|
invert: inner.base.invert,
|
||||||
is_single_channel: inner.base.mipmap.is_single_channel(),
|
is_single_channel: inner.base.mipmap.is_single_channel(),
|
||||||
color_space: inner
|
color_space: arena.alloc(
|
||||||
.base
|
inner.base.mipmap.color_space
|
||||||
.mipmap
|
.clone()
|
||||||
.color_space
|
.unwrap_or_else(crate::spectra::default_colorspace),
|
||||||
.clone()
|
),
|
||||||
.unwrap_or_else(crate::spectra::default_colorspace),
|
|
||||||
spectrum_type: inner.spectrum_type,
|
spectrum_type: inner.spectrum_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -266,3 +262,4 @@ pub struct TexInfo {
|
||||||
pub wrap_mode: WrapMode,
|
pub wrap_mode: WrapMode,
|
||||||
pub encoding: ColorEncoding,
|
pub encoding: ColorEncoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,10 @@ pub static REC2020_COEFFS: Lazy<&[Float]> =
|
||||||
Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));
|
Lazy::new(|| strip_to_len(REC2020_COEFFS_BYTES, COEFFS_LEN));
|
||||||
|
|
||||||
pub static SRGB_TABLE: Lazy<RGBToSpectrumTable> =
|
pub static SRGB_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(SRGB_SCALE, SRGB_COEFFS));
|
Lazy::new(|| RGBToSpectrumTable::new(&SRGB_SCALE, &SRGB_COEFFS));
|
||||||
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTable> =
|
pub static DCI_P3_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(DCI_P3_SCALE.to_vec(), DCI_P3_COEFFS.to_vec()));
|
Lazy::new(|| RGBToSpectrumTable::new(&DCI_P3_SCALE, &DCI_P3_COEFFS));
|
||||||
pub static REC2020_TABLE: Lazy<RGBToSpectrumTable> =
|
pub static REC2020_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(REC2020_SCALE.to_vec(), REC2020_COEFFS.to_vec()));
|
Lazy::new(|| RGBToSpectrumTable::new(&REC2020_SCALE, &REC2020_COEFFS));
|
||||||
pub static ACES_TABLE: Lazy<RGBToSpectrumTable> =
|
pub static ACES_TABLE: Lazy<RGBToSpectrumTable> =
|
||||||
Lazy::new(|| RGBToSpectrumTableData::new(ACES_SCALE.to_vec(), ACES_COEFFS.to_vec()));
|
Lazy::new(|| RGBToSpectrumTable::new(&ACES_SCALE, &ACES_COEFFS));
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
use super::RayIntegratorTrait;
|
|
||||||
use super::base::IntegratorBase;
|
use super::base::IntegratorBase;
|
||||||
use crate::Arena;
|
use super::RayIntegratorTrait;
|
||||||
use crate::core::camera::InitMetadata;
|
use crate::core::camera::InitMetadata;
|
||||||
use crate::core::film::FilmTrait;
|
use crate::core::film::FilmTrait;
|
||||||
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
use crate::core::image::{Image, ImageIO, ImageMetadata};
|
||||||
use crate::globals::get_options;
|
use crate::globals::get_options;
|
||||||
use crate::spectra::get_spectra_context;
|
use crate::spectra::get_spectra_context;
|
||||||
|
use crate::Arena;
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use shared::Float;
|
|
||||||
use shared::core::camera::{Camera, CameraTrait};
|
use shared::core::camera::{Camera, CameraTrait};
|
||||||
use shared::core::geometry::{Bounds2i, Point2i, VectorLike};
|
use shared::core::geometry::{Bounds2i, Point2i, VectorLike};
|
||||||
use shared::core::sampler::get_camera_sample;
|
use shared::core::sampler::get_camera_sample;
|
||||||
use shared::core::sampler::{Sampler, SamplerTrait};
|
use shared::core::sampler::{Sampler, SamplerTrait};
|
||||||
use shared::spectra::SampledSpectrum;
|
use shared::spectra::SampledSpectrum;
|
||||||
|
use shared::Float;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -213,7 +213,7 @@ pub fn render<T>(
|
||||||
let film_image = film.get_image(&film_metadata, splat_scale);
|
let film_image = film.get_image(&film_metadata, splat_scale);
|
||||||
|
|
||||||
let (mse_values, _mse_debug_img) =
|
let (mse_values, _mse_debug_img) =
|
||||||
film_image.mse(film_image.all_channels_desc(), ref_img, false);
|
film_image.mse(&film_image.all_channels_desc(), ref_img, false);
|
||||||
let mse_avg = mse_values.average();
|
let mse_avg = mse_values.average();
|
||||||
|
|
||||||
if let Some(file) = &mut mse_out_file {
|
if let Some(file) = &mut mse_out_file {
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,4 @@ pub mod utils;
|
||||||
|
|
||||||
#[cfg(feature = "cuda")]
|
#[cfg(feature = "cuda")]
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
pub use utils::{Arena, FileLoc, ParameterDictionary};
|
pub use utils::{Arena, FileLoc, ParameterDictionary, Upload, ArenaUpload};
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use crate::core::light::lookup_spectrum;
|
||||||
use crate::core::spectrum::spectrum_to_photometric;
|
use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::core::texture::FloatTexture;
|
use crate::core::texture::FloatTexture;
|
||||||
use crate::utils::resolve_filename;
|
use crate::utils::resolve_filename;
|
||||||
|
use crate::utils::upload::ArenaUpload;
|
||||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use shared::core::geometry::Point2i;
|
use shared::core::geometry::Point2i;
|
||||||
|
|
@ -12,11 +13,10 @@ use shared::core::light::{Light, LightBase, LightType};
|
||||||
use shared::core::medium::{Medium, MediumInterface};
|
use shared::core::medium::{Medium, MediumInterface};
|
||||||
use shared::core::shape::{Shape, ShapeTrait};
|
use shared::core::shape::{Shape, ShapeTrait};
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::GPUFloatTexture;
|
|
||||||
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
use shared::core::texture::{SpectrumType, TextureEvalContext};
|
||||||
use shared::lights::DiffuseAreaLight;
|
use shared::lights::DiffuseAreaLight;
|
||||||
use shared::spectra::RGBColorSpace;
|
use shared::spectra::RGBColorSpace;
|
||||||
use shared::utils::{Ptr, Transform};
|
use shared::utils::Transform;
|
||||||
use shared::{Float, PI};
|
use shared::{Float, PI};
|
||||||
|
|
||||||
pub fn create(
|
pub fn create(
|
||||||
|
|
@ -37,51 +37,49 @@ pub fn create(
|
||||||
let two_sided = params.get_one_bool("twosided", false)?;
|
let two_sided = params.get_one_bool("twosided", false)?;
|
||||||
|
|
||||||
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
let filename = resolve_filename(¶ms.get_one_string("filename", "")?);
|
||||||
let (image, image_color_space) = if !filename.is_empty() {
|
let (image, image_color_space): (Option<Image>, Option<RGBColorSpace>) =
|
||||||
if l.is_some() {
|
if !filename.is_empty() {
|
||||||
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
|
if l.is_some() {
|
||||||
}
|
return Err(anyhow!("{}: both \"L\" and \"filename\" specified", loc));
|
||||||
|
}
|
||||||
|
|
||||||
let im = Image::read(Path::new(&filename), None)?;
|
let im = Image::read(Path::new(&filename), None)?;
|
||||||
|
|
||||||
if im.image.has_any_infinite_pixels() {
|
if im.image.has_any_infinite_pixels() {
|
||||||
return Err(anyhow!("{}: image has infinite pixel values", loc));
|
return Err(anyhow!("{}: image has infinite pixel values", loc));
|
||||||
}
|
}
|
||||||
if im.image.has_any_nan_pixels() {
|
if im.image.has_any_nan_pixels() {
|
||||||
return Err(anyhow!("{}: image has NaN pixel values", loc));
|
return Err(anyhow!("{}: image has NaN pixel values", loc));
|
||||||
}
|
}
|
||||||
|
|
||||||
let channel_desc = im
|
let channel_desc = im
|
||||||
.image
|
.image
|
||||||
.get_channel_desc(&["R", "G", "B"])
|
.get_channel_desc(&["R", "G", "B"])
|
||||||
.map_err(|_| anyhow!("{}: image must have R, G, B channels", loc))?;
|
.map_err(|_| anyhow!("{}: image must have R, G, B channels", loc))?;
|
||||||
|
|
||||||
let image = im.image.select_channels(&channel_desc);
|
let image = im.image.select_channels(&channel_desc);
|
||||||
let cs = im.metadata.get_colorspace();
|
let cs = im.metadata.get_colorspace();
|
||||||
|
|
||||||
(Some(image), cs)
|
(Some(image), cs)
|
||||||
} else {
|
} else {
|
||||||
if l.is_none() {
|
if l.is_none() {
|
||||||
l = Some(illum_spec);
|
l = Some(illum_spec);
|
||||||
}
|
}
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let l_for_scale = l.as_ref().unwrap_or(&illum_spec);
|
let l_for_scale = l.as_ref().unwrap_or(&illum_spec);
|
||||||
scale /= spectrum_to_photometric(*l_for_scale);
|
scale /= spectrum_to_photometric(*l_for_scale);
|
||||||
|
|
||||||
let phi_v = params.get_one_float("power", -1.0)?;
|
let phi_v = params.get_one_float("power", -1.0)?;
|
||||||
if phi_v > 0.0 {
|
if phi_v > 0.0 {
|
||||||
// k_e is the emissive power of the light as defined by the spectral
|
|
||||||
// distribution and texture and is used to normalize the emitted
|
|
||||||
// radiance such that the user-defined power will be the actual power
|
|
||||||
// emitted by the light.
|
|
||||||
|
|
||||||
let mut k_e: Float = 1.0;
|
let mut k_e: Float = 1.0;
|
||||||
|
|
||||||
if let Some(ref img) = image {
|
if let Some(ref img) = image {
|
||||||
// Get the appropriate luminance vector from the image colour space
|
let lum_vec = image_color_space
|
||||||
let lum_vec = image_color_space.unwrap().luminance_vector();
|
.as_ref()
|
||||||
|
.expect("image present but no color space")
|
||||||
|
.luminance_vector();
|
||||||
|
|
||||||
let mut sum_k_e = 0.0;
|
let mut sum_k_e = 0.0;
|
||||||
let res = img.resolution();
|
let res = img.resolution();
|
||||||
|
|
@ -91,7 +89,6 @@ pub fn create(
|
||||||
let r = img.get_channel(Point2i::new(x, y), 0);
|
let r = img.get_channel(Point2i::new(x, y), 0);
|
||||||
let g = img.get_channel(Point2i::new(x, y), 1);
|
let g = img.get_channel(Point2i::new(x, y), 1);
|
||||||
let b = img.get_channel(Point2i::new(x, y), 2);
|
let b = img.get_channel(Point2i::new(x, y), 2);
|
||||||
|
|
||||||
sum_k_e += r * lum_vec[0] + g * lum_vec[1] + b * lum_vec[2];
|
sum_k_e += r * lum_vec[0] + g * lum_vec[1] + b * lum_vec[2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,21 +97,15 @@ pub fn create(
|
||||||
|
|
||||||
let side_factor = if two_sided { 2.0 } else { 1.0 };
|
let side_factor = if two_sided { 2.0 } else { 1.0 };
|
||||||
k_e *= side_factor * shape.area() * PI;
|
k_e *= side_factor * shape.area() * PI;
|
||||||
|
|
||||||
// now multiply up scale to hit the target power
|
|
||||||
scale *= phi_v / k_e;
|
scale *= phi_v / k_e;
|
||||||
}
|
}
|
||||||
|
|
||||||
let alpha_ptr = alpha.upload(arena);
|
// Upload alpha texture to GPU and check for constant-zero
|
||||||
let is_constant_zero = match &*alpha_ptr {
|
let alpha_ptr = arena.upload(alpha);
|
||||||
GPUFloatTexture::Constant(tex) => tex.evaluate(&TextureEvalContext::default()) == 0.0,
|
let light_type = if alpha_ptr.is_constant_zero() {
|
||||||
_ => false,
|
LightType::DeltaPosition
|
||||||
};
|
|
||||||
|
|
||||||
let (light_type, _) = if is_constant_zero {
|
|
||||||
(LightType::DeltaPosition, None)
|
|
||||||
} else {
|
} else {
|
||||||
(LightType::Area, Some(alpha))
|
LightType::Area
|
||||||
};
|
};
|
||||||
|
|
||||||
let mi = match medium {
|
let mi = match medium {
|
||||||
|
|
@ -127,6 +118,7 @@ pub fn create(
|
||||||
}
|
}
|
||||||
None => MediumInterface::default(),
|
None => MediumInterface::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let base = LightBase::new(light_type, render_from_light, mi);
|
let base = LightBase::new(light_type, render_from_light, mi);
|
||||||
|
|
||||||
if let Some(ref img) = image {
|
if let Some(ref img) = image {
|
||||||
|
|
@ -143,30 +135,20 @@ pub fn create(
|
||||||
|
|
||||||
let is_triangle_or_bilinear = matches!(*shape, Shape::Triangle(_) | Shape::BilinearPatch(_));
|
let is_triangle_or_bilinear = matches!(*shape, Shape::Triangle(_) | Shape::BilinearPatch(_));
|
||||||
if render_from_light.has_scale(None) && !is_triangle_or_bilinear {
|
if render_from_light.has_scale(None) && !is_triangle_or_bilinear {
|
||||||
println!(
|
eprintln!(
|
||||||
"Scaling detected in rendering to light space transformation! \
|
"Warning: scaling detected in rendering-to-light transform; \
|
||||||
Proceed at your own risk; your image may have errors."
|
image may have errors."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let shape_ptr = shape.upload(arena);
|
|
||||||
let image_ptr = image
|
|
||||||
.as_ref()
|
|
||||||
.map(|img| arena.alloc(*img.device()))
|
|
||||||
.unwrap_or(Ptr::null());
|
|
||||||
let colorspace_ptr = image_color_space
|
|
||||||
.map(|cs| cs.upload(arena))
|
|
||||||
.unwrap_or(Ptr::null());
|
|
||||||
let lemit_ptr = arena.alloc(lookup_spectrum(l_for_scale).device());
|
|
||||||
|
|
||||||
let specific = DiffuseAreaLight {
|
let specific = DiffuseAreaLight {
|
||||||
base,
|
base,
|
||||||
area: shape.area(),
|
area: shape.area(),
|
||||||
shape: shape_ptr,
|
shape: arena.alloc(*shape),
|
||||||
alpha: alpha_ptr,
|
alpha: alpha_ptr,
|
||||||
image: image_ptr,
|
image: arena.alloc(image),
|
||||||
colorspace: colorspace_ptr,
|
colorspace: arena.alloc_opt(image_color_space),
|
||||||
lemit: lemit_ptr,
|
lemit: arena.alloc((*lookup_spectrum(l_for_scale)).clone()),
|
||||||
two_sided,
|
two_sided,
|
||||||
scale,
|
scale,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ impl CreateDistantLight for DistantLight {
|
||||||
let lemit = lookup_spectrum(&le);
|
let lemit = lookup_spectrum(&le);
|
||||||
Self {
|
Self {
|
||||||
base,
|
base,
|
||||||
lemit: Ptr::from(&lemit.device()),
|
lemit: Ptr::from(&*lemit),
|
||||||
scale,
|
scale,
|
||||||
scene_center: Point3f::default(),
|
scene_center: Point3f::default(),
|
||||||
scene_radius: 0.,
|
scene_radius: 0.,
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ pub fn create(
|
||||||
|
|
||||||
let image_ptr = if !image.is_null() {
|
let image_ptr = if !image.is_null() {
|
||||||
let distrib = PiecewiseConstant2D::from_image(&image);
|
let distrib = PiecewiseConstant2D::from_image(&image);
|
||||||
let distrib_ptr = distrib.upload(arena);
|
let distrib_ptr = arena.alloc(distrib);
|
||||||
let img_ptr = image.upload(arena);
|
let img_ptr = arena.alloc(image);
|
||||||
(img_ptr, distrib_ptr)
|
(img_ptr, distrib_ptr)
|
||||||
} else {
|
} else {
|
||||||
(Ptr::null(), Ptr::null())
|
(Ptr::null(), Ptr::null())
|
||||||
|
|
@ -108,7 +108,7 @@ pub fn create(
|
||||||
|
|
||||||
let specific = GoniometricLight {
|
let specific = GoniometricLight {
|
||||||
base,
|
base,
|
||||||
iemit: arena.alloc(iemit.device()),
|
iemit: arena.alloc(*iemit),
|
||||||
scale,
|
scale,
|
||||||
image: image_ptr.0,
|
image: image_ptr.0,
|
||||||
distrib: image_ptr.1,
|
distrib: image_ptr.1,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::core::spectrum::spectrum_to_photometric;
|
||||||
use crate::spectra::get_spectra_context;
|
use crate::spectra::get_spectra_context;
|
||||||
use crate::utils::resolve_filename;
|
use crate::utils::resolve_filename;
|
||||||
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
use crate::utils::sampling::{PiecewiseConstant2D, WindowedPiecewiseConstant2D};
|
||||||
use crate::{Arena, FileLoc, ParameterDictionary};
|
use crate::{Arena, FileLoc, ParameterDictionary, ArenaUpload, Upload};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use shared::core::camera::CameraTransform;
|
use shared::core::camera::CameraTransform;
|
||||||
|
|
@ -63,7 +63,7 @@ pub fn create(
|
||||||
}
|
}
|
||||||
|
|
||||||
let lemit = lookup_spectrum(&spectrum);
|
let lemit = lookup_spectrum(&spectrum);
|
||||||
let light = UniformInfiniteLight::new(render_from_light, scale, lemit.upload(arena));
|
let light = UniformInfiniteLight::new(render_from_light, scale, arena.alloc(*lemit));
|
||||||
return Ok(Light::InfiniteUniform(light));
|
return Ok(Light::InfiniteUniform(light));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,9 +140,9 @@ fn create_image_light(
|
||||||
render_from_light,
|
render_from_light,
|
||||||
scale,
|
scale,
|
||||||
image_ptr,
|
image_ptr,
|
||||||
image_cs.upload(arena),
|
arena.alloc(image_cs),
|
||||||
distrib.upload(arena),
|
arena.alloc(distrib),
|
||||||
compensated_distrib.upload(arena),
|
arena.alloc(compensated_distrib),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Light::InfiniteImage(light))
|
Ok(Light::InfiniteImage(light))
|
||||||
|
|
@ -198,11 +198,11 @@ fn create_portal_light(
|
||||||
let light = PortalInfiniteLight::new(
|
let light = PortalInfiniteLight::new(
|
||||||
render_from_light,
|
render_from_light,
|
||||||
scale,
|
scale,
|
||||||
remapped.upload(arena),
|
arena.alloc(remapped),
|
||||||
image_cs.upload(arena),
|
arena.alloc(image_cs),
|
||||||
portal,
|
portal,
|
||||||
portal_frame,
|
portal_frame,
|
||||||
distribution.upload(arena),
|
arena.alloc(distribution),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Light::InfinitePortal(light))
|
Ok(Light::InfinitePortal(light))
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,6 @@ pub fn create(
|
||||||
None => MediumInterface::default(),
|
None => MediumInterface::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let specific = PointLight::new(final_render, mi, l, scale);
|
let specific = PointLight::new(final_render, mi, l, scale, arena);
|
||||||
Ok(Light::Point(specific))
|
Ok(Light::Point(specific))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl CreateSpotLight for SpotLight {
|
||||||
);
|
);
|
||||||
|
|
||||||
let i = lookup_spectrum(&le);
|
let i = lookup_spectrum(&le);
|
||||||
let iemit = Ptr::from(&i.device());
|
let iemit = arena.alloc(i);
|
||||||
Self {
|
Self {
|
||||||
base,
|
base,
|
||||||
iemit,
|
iemit,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::core::texture::SpectrumTexture;
|
||||||
use crate::globals::get_options;
|
use crate::globals::get_options;
|
||||||
use crate::spectra::data::get_named_spectrum;
|
use crate::spectra::data::get_named_spectrum;
|
||||||
use crate::utils::TextureParameterDictionary;
|
use crate::utils::TextureParameterDictionary;
|
||||||
use crate::{Arena, FileLoc};
|
use crate::{Arena, FileLoc, Upload, ArenaUpload};
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
|
|
@ -56,15 +56,15 @@ impl CreateMaterial for CoatedDiffuseMaterial {
|
||||||
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
||||||
|
|
||||||
let specific = CoatedDiffuseMaterial::new(
|
let specific = CoatedDiffuseMaterial::new(
|
||||||
reflectance.upload(arena),
|
arena.upload(reflectance),
|
||||||
u_roughness.upload(arena),
|
arena.upload(u_roughness),
|
||||||
v_roughness.upload(arena),
|
arena.upload(v_roughness),
|
||||||
thickness.upload(arena),
|
arena.upload(thickness),
|
||||||
albedo.upload(arena),
|
arena.upload(albedo),
|
||||||
g.upload(arena),
|
arena.upload(g),
|
||||||
eta.upload(arena),
|
arena.upload(displacement),
|
||||||
displacement.upload(arena),
|
arena.alloc(eta),
|
||||||
normal_map.upload(arena),
|
arena.alloc(normal_map),
|
||||||
remap_roughness,
|
remap_roughness,
|
||||||
max_depth as u32,
|
max_depth as u32,
|
||||||
n_samples as u32,
|
n_samples as u32,
|
||||||
|
|
@ -154,19 +154,19 @@ impl CreateMaterial for CoatedConductorMaterial {
|
||||||
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
let remap_roughness = parameters.get_one_bool("remaproughness", true)?;
|
||||||
|
|
||||||
let material = Self::new(
|
let material = Self::new(
|
||||||
normal_map.upload(arena),
|
arena.upload(displacement)
|
||||||
displacement.upload(arena),
|
arena.upload(interface_u_roughness),
|
||||||
interface_u_roughness.upload(arena),
|
arena.upload(interface_v_roughness),
|
||||||
interface_v_roughness.upload(arena),
|
arena.upload(thickness),
|
||||||
thickness.upload(arena),
|
arena.upload(g),
|
||||||
interface_eta.upload(arena),
|
arena.upload(albedo),
|
||||||
g.upload(arena),
|
arena.upload(conductor_u_roughness),
|
||||||
albedo.upload(arena),
|
arena.upload(conductor_v_roughness),
|
||||||
conductor_u_roughness.upload(arena),
|
arena.upload(conductor_eta),
|
||||||
conductor_v_roughness.upload(arena),
|
arena.upload(k),
|
||||||
conductor_eta.upload(arena),
|
arena.upload(reflectance),
|
||||||
k.upload(arena),
|
arena.alloc(normal_map),
|
||||||
reflectance.upload(arena),
|
arena.alloc(interface_eta),
|
||||||
max_depth as u32,
|
max_depth as u32,
|
||||||
n_samples as u32,
|
n_samples as u32,
|
||||||
remap_roughness,
|
remap_roughness,
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,15 @@ use crate::core::image::Image;
|
||||||
use crate::core::material::CreateMaterial;
|
use crate::core::material::CreateMaterial;
|
||||||
use crate::core::texture::SpectrumTexture;
|
use crate::core::texture::SpectrumTexture;
|
||||||
use crate::spectra::get_colorspace_device;
|
use crate::spectra::get_colorspace_device;
|
||||||
use crate::{Arena, FileLoc};
|
|
||||||
use crate::utils::TextureParameterDictionary;
|
use crate::utils::TextureParameterDictionary;
|
||||||
|
use crate::{Arena, FileLoc, Upload, ArenaUpload};
|
||||||
|
use anyhow::Result;
|
||||||
use shared::bxdfs::HairBxDF;
|
use shared::bxdfs::HairBxDF;
|
||||||
use shared::core::material::Material;
|
use shared::core::material::Material;
|
||||||
use shared::core::spectrum::Spectrum;
|
use shared::core::spectrum::Spectrum;
|
||||||
use shared::core::texture::SpectrumType;
|
use shared::core::texture::SpectrumType;
|
||||||
use shared::materials::complex::*;
|
use shared::materials::complex::*;
|
||||||
use shared::textures::SpectrumConstantTexture;
|
use shared::textures::SpectrumConstantTexture;
|
||||||
use anyhow::Result;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -30,8 +30,7 @@ impl CreateMaterial for HairMaterial {
|
||||||
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
|
let pheomelanin = parameters.get_float_texture_or_null("pheomelanin")?;
|
||||||
let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
|
let has_melanin = eumelanin.is_some() || pheomelanin.is_some();
|
||||||
|
|
||||||
// Default distribution if nothing is spceified
|
let sigma_a = if sigma_a.is_none() && reflectance.is_none() && !has_melanin {
|
||||||
let sigma_a = if sigma_a.is_none() && !reflectance.is_none() && !has_melanin {
|
|
||||||
let stdcs = get_colorspace_device();
|
let stdcs = get_colorspace_device();
|
||||||
let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs);
|
let default_rgb = HairBxDF::sigma_a_from_concentration(1.3, 0.0, stdcs);
|
||||||
let spectrum = Spectrum::RGBUnbounded(default_rgb);
|
let spectrum = Spectrum::RGBUnbounded(default_rgb);
|
||||||
|
|
@ -45,21 +44,23 @@ impl CreateMaterial for HairMaterial {
|
||||||
let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
|
let beta_m = parameters.get_float_texture("beta_m", 0.3)?;
|
||||||
let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
|
let beta_n = parameters.get_float_texture("beta_n", 0.3)?;
|
||||||
let alpha = parameters.get_float_texture("alpha", 2.)?;
|
let alpha = parameters.get_float_texture("alpha", 2.)?;
|
||||||
|
|
||||||
let material = HairMaterial::new(
|
let material = HairMaterial::new(
|
||||||
sigma_a.upload(arena),
|
arena.upload(sigma_a),
|
||||||
reflectance.upload(arena),
|
arena.upload(reflectance),
|
||||||
eumelanin.upload(arena),
|
arena.upload(eumelanin),
|
||||||
pheomelanin.upload(arena),
|
arena.upload(pheomelanin),
|
||||||
eta.upload(arena),
|
arena.upload(eta),
|
||||||
beta_m.upload(arena),
|
arena.upload(beta_m),
|
||||||
beta_n.upload(arena),
|
arena.upload(beta_n),
|
||||||
alpha.upload(arena),
|
arena.upload(alpha),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Material::Hair(material))
|
Ok(Material::Hair(material))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl CreateMaterial for SubsurfaceMaterial {
|
impl CreateMaterial for SubsurfaceMaterial {
|
||||||
fn create(
|
fn create(
|
||||||
_parameters: &TextureParameterDictionary,
|
_parameters: &TextureParameterDictionary,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ impl CreateHaltonSampler for HaltonSampler {
|
||||||
randomize: RandomizeStrategy,
|
randomize: RandomizeStrategy,
|
||||||
seed: u64,
|
seed: u64,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (_, digit_permutations) = compute_radical_inverse_permutations(seed);
|
let digit_permutations = compute_radical_inverse_permutations(seed);
|
||||||
let mut base_scales = [0u64; 2];
|
let mut base_scales = [0u64; 2];
|
||||||
let mut base_exponents = [0u64; 2];
|
let mut base_exponents = [0u64; 2];
|
||||||
let bases = [2, 3];
|
let bases = [2, 3];
|
||||||
|
|
|
||||||
|
|
@ -268,11 +268,19 @@ impl TriQuadMesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TriangleMesh {
|
pub trait ReadTriangleMesh {
|
||||||
pub fn from_ply<P: AsRef<Path>>(
|
pub fn from_ply<P: AsRef<Path>>(
|
||||||
filename: P,
|
filename: P,
|
||||||
render_from_object: &Transform,
|
render_from_object: &Transform,
|
||||||
reverse_orientation: bool,
|
reverse_orientation: bool,
|
||||||
|
) -> AnyResult<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadTriangleMesh for TriangleMesh {
|
||||||
|
fn from_ply<P: AsRef<Path>>(
|
||||||
|
filename: P,
|
||||||
|
render_from_object: &Transform,
|
||||||
|
reverse_orientation: bool,
|
||||||
) -> AnyResult<Self> {
|
) -> AnyResult<Self> {
|
||||||
let mesh = TriQuadMesh::read_ply(filename)?;
|
let mesh = TriQuadMesh::read_ply(filename)?;
|
||||||
Ok(mesh.into_triangle_mesh(render_from_object, reverse_orientation))
|
Ok(mesh.into_triangle_mesh(render_from_object, reverse_orientation))
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ impl CreateRGBColorSpace for RGBColorSpace {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let stdspec = get_spectra_context();
|
let stdspec = get_spectra_context();
|
||||||
let illum_ptr = Ptr::from(illuminant);
|
let illum_ptr = Ptr::from(illuminant);
|
||||||
let illum_spectrum = Spectrum::Dense(illuminant);
|
let illum_spectrum = Spectrum::Dense(illum_ptr);
|
||||||
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
|
let w_xyz: XYZ = illum_spectrum.to_xyz(&stdspec);
|
||||||
let w = w_xyz.xy();
|
let w = w_xyz.xy();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ use std::sync::LazyLock;
|
||||||
|
|
||||||
pub mod colorspace;
|
pub mod colorspace;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod dense;
|
|
||||||
pub mod piecewise;
|
pub mod piecewise;
|
||||||
|
|
||||||
pub static CIE_X_DATA: LazyLock<DenselySampledSpectrum> =
|
pub static CIE_X_DATA: LazyLock<DenselySampledSpectrum> =
|
||||||
|
|
@ -57,7 +56,7 @@ pub static SRGB: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let b = Point2f::new(0.15, 0.06);
|
let b = Point2f::new(0.15, 0.06);
|
||||||
let table_ptr = Ptr::from(&*SRGB_TABLE);
|
let table_ptr = Ptr::from(&*SRGB_TABLE);
|
||||||
|
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
|
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
|
|
@ -66,7 +65,7 @@ pub static DCI_P3: LazyLock<Arc<RGBColorSpace>> = LazyLock::new(|| {
|
||||||
let g = Point2f::new(0.265, 0.690);
|
let g = Point2f::new(0.265, 0.690);
|
||||||
let b = Point2f::new(0.150, 0.060);
|
let b = Point2f::new(0.150, 0.060);
|
||||||
let table_ptr = Ptr::from(&*DCI_P3_TABLE);
|
let table_ptr = Ptr::from(&*DCI_P3_TABLE);
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
|
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
||||||
|
|
@ -75,7 +74,7 @@ pub static REC2020: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
||||||
let g = Point2f::new(0.170, 0.797);
|
let g = Point2f::new(0.170, 0.797);
|
||||||
let b = Point2f::new(0.131, 0.046);
|
let b = Point2f::new(0.131, 0.046);
|
||||||
let table_ptr = Ptr::from(&*REC2020_TABLE);
|
let table_ptr = Ptr::from(&*REC2020_TABLE);
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
|
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
||||||
|
|
@ -84,7 +83,7 @@ pub static ACES: LazyLock<Arc<RGBColorSpaceData>> = LazyLock::new(|| {
|
||||||
let g = Point2f::new(0.0000, 1.0000);
|
let g = Point2f::new(0.0000, 1.0000);
|
||||||
let b = Point2f::new(0.0001, -0.0770);
|
let b = Point2f::new(0.0001, -0.0770);
|
||||||
let table_ptr = Ptr::from(&ACES_TABLE);
|
let table_ptr = Ptr::from(&ACES_TABLE);
|
||||||
Arc::new(RGBColorSpace::new(r, g, b, illum, table_ptr))
|
Arc::new(RGBColorSpace::new(r, g, b, &illum, &table_ptr))
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -134,6 +133,11 @@ pub fn default_colorspace_arc() -> Arc<RGBColorSpace> {
|
||||||
Arc::new(default_colorspace())
|
Arc::new(default_colorspace())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_colorspace_ref() -> &'static RGBColorSpace {
|
||||||
|
static CS: OnceLock<RGBColorSpace> = OnceLock::new();
|
||||||
|
CS.get_or_init(|| stdcs.srgb)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn default_illuminant() -> Spectrum {
|
pub fn default_illuminant() -> Spectrum {
|
||||||
Spectrum::Dense(default_colorspace().illuminant)
|
Spectrum::Dense(default_colorspace().illuminant)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ use shared::utils::Transform;
|
||||||
use shared::Float;
|
use shared::Float;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
// use crate::utils::{FileLoc, TextureParameterDictionary};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ImageTextureBase {
|
pub struct ImageTextureBase {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ pub mod containers;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod math;
|
|
||||||
pub mod mipmap;
|
pub mod mipmap;
|
||||||
pub mod parallel;
|
pub mod parallel;
|
||||||
pub mod parameters;
|
pub mod parameters;
|
||||||
|
|
@ -19,6 +18,7 @@ pub use parameters::{
|
||||||
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
|
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
|
||||||
};
|
};
|
||||||
pub use strings::*;
|
pub use strings::*;
|
||||||
|
pub use upload::{Upload, ArenaUpload};
|
||||||
|
|
||||||
#[cfg(feature = "vulkan")]
|
#[cfg(feature = "vulkan")]
|
||||||
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
|
pub type Arena = arena::Arena<backend::vulkan::VulkanAllocator>;
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,18 @@ impl PBRTParameter for bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PBRTParameter for RGB {
|
||||||
|
type Raw = Float;
|
||||||
|
const TYPE_NAME: &'static str = "rgb";
|
||||||
|
const N_PER_ITEM: usize = 3;
|
||||||
|
fn convert(v: &[Self::Raw]) -> Self {
|
||||||
|
RGB::new(v[0], v[1], v[2])
|
||||||
|
}
|
||||||
|
fn get_values(param: &ParsedParameter) -> &[Self::Raw] {
|
||||||
|
¶m.floats
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PBRTParameter for Float {
|
impl PBRTParameter for Float {
|
||||||
type Raw = Float;
|
type Raw = Float;
|
||||||
const TYPE_NAME: &'static str = "float";
|
const TYPE_NAME: &'static str = "float";
|
||||||
|
|
@ -281,6 +293,12 @@ impl ParameterDictionary {
|
||||||
Ok(dict)
|
Ok(dict)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn colorspace(&self) -> &RGBColorSpace {
|
||||||
|
self.color_space
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or_else(|| crate::spectra::default_colorspace_ref())
|
||||||
|
}
|
||||||
|
|
||||||
fn check_parameter_types(&self) -> Result<()> {
|
fn check_parameter_types(&self) -> Result<()> {
|
||||||
for p in &self.params {
|
for p in &self.params {
|
||||||
match p.type_name.as_str() {
|
match p.type_name.as_str() {
|
||||||
|
|
@ -444,6 +462,10 @@ impl ParameterDictionary {
|
||||||
self.lookup_single(name, def)
|
self.lookup_single(name, def)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_one_rgb(&self, name: &str, def: RGB) -> Result<RGB> {
|
||||||
|
self.lookup_single(name, def)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> {
|
pub fn get_float_array(&self, name: &str) -> Result<Vec<Float>> {
|
||||||
self.lookup_array(name)
|
self.lookup_array(name)
|
||||||
}
|
}
|
||||||
|
|
@ -476,6 +498,10 @@ impl ParameterDictionary {
|
||||||
self.lookup_array(name)
|
self.lookup_array(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rgb_array(&self, name: &str) -> Result<Vec<RGB>> {
|
||||||
|
self.lookup_array(name)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_one_spectrum(
|
pub fn get_one_spectrum(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
|
|
|
||||||
160
src/utils/upload.rs
Normal file
160
src/utils/upload.rs
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
use crate::core::texture::{FloatTexture, SpectrumTexture};
|
||||||
|
use crate::Arena;
|
||||||
|
use shared::core::texture::{GPUFloatTexture, GPUSpectrumTexture};
|
||||||
|
use shared::textures::*;
|
||||||
|
use shared::Ptr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub trait Upload {
|
||||||
|
type Target;
|
||||||
|
fn upload(self, arena: &Arena) -> Self::Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_float(tex: &FloatTexture, arena: &Arena) -> GPUFloatTexture {
|
||||||
|
match tex {
|
||||||
|
FloatTexture::Constant(t) => GPUFloatTexture::Constant(*t),
|
||||||
|
FloatTexture::Bilerp(t) => GPUFloatTexture::Bilerp(*t),
|
||||||
|
FloatTexture::Checkerboard(t) => GPUFloatTexture::Checkerboard(*t),
|
||||||
|
FloatTexture::Dots(t) => GPUFloatTexture::Dots(*t),
|
||||||
|
FloatTexture::FBm(t) => GPUFloatTexture::FBm(*t),
|
||||||
|
FloatTexture::Windy(t) => GPUFloatTexture::Windy(*t),
|
||||||
|
FloatTexture::Wrinkled(t) => GPUFloatTexture::Wrinkled(*t),
|
||||||
|
|
||||||
|
FloatTexture::Scaled(t) => {
|
||||||
|
let tex = arena.alloc(convert_float(&t.tex, arena));
|
||||||
|
let scale = arena.alloc(convert_float(&t.scale, arena));
|
||||||
|
GPUFloatTexture::Scaled(GPUFloatScaledTexture { tex, scale })
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatTexture::Mix(t) => {
|
||||||
|
let tex1 = arena.alloc(convert_float(&t.tex1, arena));
|
||||||
|
let tex2 = arena.alloc(convert_float(&t.tex2, arena));
|
||||||
|
let amount = arena.alloc(convert_float(&t.amount, arena));
|
||||||
|
GPUFloatTexture::Mix(GPUFloatMixTexture { tex1, tex2, amount })
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatTexture::DirectionMix(t) => {
|
||||||
|
let tex1 = arena.alloc(convert_float(&t.tex1, arena));
|
||||||
|
let tex2 = arena.alloc(convert_float(&t.tex2, arena));
|
||||||
|
GPUFloatTexture::DirectionMix(GPUFloatDirectionMixTexture {
|
||||||
|
tex1,
|
||||||
|
tex2,
|
||||||
|
dir: t.dir,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
FloatTexture::Image(t) => {
|
||||||
|
let tex_obj = arena.get_texture_object(&t.base.mipmap);
|
||||||
|
GPUFloatTexture::Image(GPUFloatImageTexture {
|
||||||
|
mapping: t.base.mapping,
|
||||||
|
tex_obj,
|
||||||
|
scale: t.base.scale,
|
||||||
|
invert: t.base.invert,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_spectrum(tex: &SpectrumTexture, arena: &Arena) -> GPUSpectrumTexture {
|
||||||
|
match tex {
|
||||||
|
SpectrumTexture::Constant(t) => GPUSpectrumTexture::Constant(*t),
|
||||||
|
SpectrumTexture::Bilerp(t) => GPUSpectrumTexture::Bilerp(*t),
|
||||||
|
SpectrumTexture::Checkerboard(t) => GPUSpectrumTexture::Checkerboard(*t),
|
||||||
|
SpectrumTexture::Dots(t) => GPUSpectrumTexture::Dots(*t),
|
||||||
|
SpectrumTexture::Marble(t) => GPUSpectrumTexture::Marble(*t),
|
||||||
|
|
||||||
|
SpectrumTexture::Scaled(t) => {
|
||||||
|
let tex = arena.alloc(convert_spectrum(&t.tex, arena));
|
||||||
|
let scale = arena.alloc(convert_float(&t.scale, arena));
|
||||||
|
GPUSpectrumTexture::Scaled(GPUSpectrumScaledTexture { tex, scale })
|
||||||
|
}
|
||||||
|
|
||||||
|
SpectrumTexture::Mix(t) => {
|
||||||
|
let tex1 = arena.alloc(convert_spectrum(&t.tex1, arena));
|
||||||
|
let tex2 = arena.alloc(convert_spectrum(&t.tex2, arena));
|
||||||
|
let amount = arena.alloc(convert_float(&t.amount, arena));
|
||||||
|
GPUSpectrumTexture::Mix(GPUSpectrumMixTexture { tex1, tex2, amount })
|
||||||
|
}
|
||||||
|
|
||||||
|
SpectrumTexture::DirectionMix(t) => {
|
||||||
|
let tex1 = arena.alloc(convert_spectrum(&t.tex1, arena));
|
||||||
|
let tex2 = arena.alloc(convert_spectrum(&t.tex2, arena));
|
||||||
|
GPUSpectrumTexture::DirectionMix(GPUSpectrumDirectionMixTexture {
|
||||||
|
tex1,
|
||||||
|
tex2,
|
||||||
|
dir: t.dir,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
SpectrumTexture::Image(t) => {
|
||||||
|
let tex_obj = arena.get_texture_object(&t.base.mipmap);
|
||||||
|
GPUSpectrumTexture::Image(GPUSpectrumImageTexture {
|
||||||
|
mapping: t.base.mapping,
|
||||||
|
tex_obj,
|
||||||
|
scale: t.base.scale,
|
||||||
|
invert: t.base.invert,
|
||||||
|
is_single_channel: t.base.mipmap.is_single_channel(),
|
||||||
|
spectrum_type: t.spectrum_type,
|
||||||
|
|
||||||
|
color_space: arena.alloc(
|
||||||
|
t.base
|
||||||
|
.mipmap
|
||||||
|
.color_space
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(crate::spectra::default_colorspace),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload for Arc<FloatTexture> {
|
||||||
|
type Target = Ptr<GPUFloatTexture>;
|
||||||
|
fn upload(self, arena: &Arena) -> Self::Target {
|
||||||
|
arena.alloc(convert_float(&self, arena))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload for Arc<SpectrumTexture> {
|
||||||
|
type Target = Ptr<GPUSpectrumTexture>;
|
||||||
|
fn upload(self, arena: &Arena) -> Self::Target {
|
||||||
|
arena.alloc(convert_spectrum(&self, arena))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload for &FloatTexture {
|
||||||
|
type Target = Ptr<GPUFloatTexture>;
|
||||||
|
fn upload(self, arena: &Arena) -> Self::Target {
|
||||||
|
arena.alloc(convert_float(&self, arena))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload for &SpectrumTexture {
|
||||||
|
type Target = Ptr<GPUSpectrumTexture>;
|
||||||
|
fn upload(self, arena: &Arena) -> Self::Target {
|
||||||
|
arena.alloc(convert_spectrum(&self, arena))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Upload for Option<Arc<FloatTexture>> {
|
||||||
|
type Target = Ptr<GPUFloatTexture>;
|
||||||
|
fn upload(self, arena: &Arena) -> Self::Target {
|
||||||
|
arena.alloc_opt(self.map(|v| convert_float(&v, arena)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Upload for Option<Arc<SpectrumTexture>> {
|
||||||
|
type Target = Ptr<GPUSpectrumTexture>;
|
||||||
|
fn upload(self, arena: &Arena) -> Self::Target {
|
||||||
|
arena.alloc_opt(self.map(|v| convert_spectrum(&v, arena)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ArenaUpload {
|
||||||
|
fn upload<T: Upload>(&self, value: T) -> T::Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArenaUpload for Arena {
|
||||||
|
fn upload<T: Upload>(&self, value: T) -> T::Target {
|
||||||
|
value.upload(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue