Finished work on the shared side. Now moving to host code. May god have mercy on my soul

This commit is contained in:
Wito Wiala 2026-05-19 19:58:21 +01:00
parent 5b4928e1aa
commit 050698c1d0
49 changed files with 818 additions and 357 deletions

View file

@ -9,7 +9,7 @@ use_f64 = []
use_gpu = ["dep:wgpu"]
use_nvtx = ["dep:nvtx"]
cuda = ["dep:cudarc", "dep:cust", "dep:cust_raw", "dep:cuda-runtime-sys"]
vulkan = ["ash", "gpu-allocator"]
vulkan = ["dep:ash", "dep:gpu-allocator", "shared/vulkan"]
ash = ["dep:ash"]
gpu-allocator = ["dep:gpu-allocator"]
jemalloc = ["jemallocator"]

View file

@ -0,0 +1,13 @@
#![allow(unused)]
[package]
name = "ptex-filter"
version = "0.1.0"
edition = "2021"
[build-dependencies]
cc = "1.0"
pkg-config = "0.3"
[dependencies]
ptex = "0.3.0"

View file

@ -0,0 +1,17 @@
fn main() {
println!("cargo:rerun-if-changed=cpp/ptex_filter_wrapper.cpp");
let home = std::env::var("HOME").unwrap();
let ptex_include = format!("{}/.local/include", home);
let ptex_lib = format!("{}/.local/lib", home);
cc::Build::new()
.cpp(true)
.file("cpp/ptex_filter_wrapper.cpp")
.include(&ptex_include)
.flag("-std=c++17")
.compile("ptex_filter_wrapper");
println!("cargo:rustc-link-search=native={}", ptex_lib);
println!("cargo:rustc-link-lib=Ptex");
}

View file

@ -0,0 +1,65 @@
#include "ptex_filter_wrapper.h"
#include <Ptexture.h>
extern "C" {
PtexFilterHandle ptex_filter_create(PtexTextureHandle texture, const PtexFilterOptions* opts) {
Ptex::PtexTexture* tex = static_cast<Ptex::PtexTexture*>(texture);
if (!tex || !opts) return nullptr;
Ptex::PtexFilter::Options ptex_opts;
ptex_opts.filter = static_cast<Ptex::PtexFilter::FilterType>(opts->filter);
ptex_opts.lerp = opts->lerp;
ptex_opts.sharpness = opts->sharpness;
ptex_opts.noedgeblend = opts->noedgeblend != 0;
return Ptex::PtexFilter::getFilter(tex, ptex_opts);
}
void ptex_filter_eval(
PtexFilterHandle filter,
float* result,
int32_t first_channel,
int32_t num_channels,
int32_t face_id,
float u, float v,
float dudx, float dvdx,
float dudy, float dvdy
) {
Ptex::PtexFilter* f = static_cast<Ptex::PtexFilter*>(filter);
if (f && result) {
f->eval(result, first_channel, num_channels, face_id, u, v, dudx, dvdx, dudy, dvdy);
}
}
void ptex_filter_release(PtexFilterHandle filter) {
Ptex::PtexFilter* f = static_cast<Ptex::PtexFilter*>(filter);
if (f) {
f->release();
}
}
PtexTextureHandle ptex_texture_open(const char* filename, char** error_str) {
Ptex::String error;
Ptex::PtexTexture* tex = Ptex::PtexTexture::open(filename, error);
if (!tex && error_str) {
*error_str = strdup(error.c_str());
}
return tex;
}
void ptex_texture_release(PtexTextureHandle texture) {
Ptex::PtexTexture* tex = static_cast<Ptex::PtexTexture*>(texture);
if (tex) {
tex->release();
}
}
int32_t ptex_texture_num_channels(PtexTextureHandle texture) {
Ptex::PtexTexture* tex = static_cast<Ptex::PtexTexture*>(texture);
return tex ? tex->numChannels() : 0;
}
}

View file

@ -0,0 +1,51 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
typedef void* PtexTextureHandle;
typedef void* PtexFilterHandle;
typedef enum {
PTEX_FILTER_POINT = 0,
PTEX_FILTER_BILINEAR = 1,
PTEX_FILTER_BOX = 2,
PTEX_FILTER_GAUSSIAN = 3,
PTEX_FILTER_BICUBIC = 4,
PTEX_FILTER_BSPLINE = 5,
PTEX_FILTER_CATMULLROM = 6,
PTEX_FILTER_MITCHELL = 7
} PtexFilterType;
typedef struct {
PtexFilterType filter;
int32_t lerp;
float sharpness;
int32_t noedgeblend;
} PtexFilterOptions;
PtexTextureHandle ptex_texture_open(const char* filename, char** error_str);
void ptex_filter_release(PtexFilterHandle filter);
int32_t ptex_texture_num_channels(PtexTextureHandle texture);
PtexFilterHandle ptex_filter_create(PtexTextureHandle texture, const PtexFilterOptions* opts);
void ptex_filter_eval(
PtexFilterHandle filter,
float* result,
int32_t first_channel,
int32_t num_channels,
int32_t face_id,
float u, float v,
float dudx, float dvdx,
float dudy, float dvdy
);
void ptex_filter_release(PtexFilterHandle filter);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,60 @@
use std::ffi::c_void;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PtexFilterType {
Point = 0,
Bilinear = 1,
Box = 2,
Gaussian = 3,
Bicubic = 4,
BSpline = 5,
CatmullRom = 6,
Mitchell = 7,
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct PtexFilterOptions {
pub filter: PtexFilterType,
pub lerp: i32,
pub sharpness: f32,
pub noedgeblend: i32,
}
impl Default for PtexFilterOptions {
fn default() -> Self {
Self {
filter: PtexFilterType::BSpline,
lerp: 1,
sharpness: 0.0,
noedgeblend: 0,
}
}
}
extern "C" {
pub fn ptex_filter_create(texture: *mut c_void, opts: *const PtexFilterOptions) -> *mut c_void;
pub fn ptex_filter_eval(
filter: *mut c_void,
result: *mut f32,
first_channel: i32,
num_channels: i32,
face_id: i32,
u: f32,
v: f32,
dudx: f32,
dvdx: f32,
dudy: f32,
dvdy: f32,
);
pub fn ptex_filter_release(filter: *mut c_void);
pub fn ptex_texture_open(
filename: *const std::ffi::c_char,
error_str: *mut *mut std::ffi::c_char,
) -> *mut c_void;
pub fn ptex_texture_release(texture: *mut c_void);
pub fn ptex_texture_num_channels(texture: *mut c_void) -> i32;
}

View file

@ -0,0 +1,66 @@
#![allow(unused)]
#![allow(dead_code)]
mod ffi;
pub use ffi::{ptex_filter_create, PtexFilterOptions, PtexFilterType};
use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr::NonNull;
pub struct PtexFilter {
handle: NonNull<c_void>,
_marker: PhantomData<*mut ()>,
}
impl PtexFilter {
/// Creates a new Ptex filter pointer
///
/// # Safety
pub unsafe fn new(texture_ptr: *mut c_void, opts: &PtexFilterOptions) -> Option<Self> {
let handle = ffi::ptex_filter_create(texture_ptr, opts);
NonNull::new(handle).map(|h| Self {
handle: h,
_marker: PhantomData,
})
}
pub fn eval(
&self,
face_id: i32,
u: f32,
v: f32,
dudx: f32,
dvdx: f32,
dudy: f32,
dvdy: f32,
num_channels: i32,
) -> [f32; 4] {
let mut result = [0.0f32; 4];
unsafe {
ffi::ptex_filter_eval(
self.handle.as_ptr(),
result.as_mut_ptr(),
0,
num_channels.min(4),
face_id,
u,
v,
dudx,
dvdx,
dudy,
dvdy,
);
}
result
}
}
impl Drop for PtexFilter {
fn drop(&mut self) {
unsafe {
ffi::ptex_filter_release(self.handle.as_ptr());
}
}
}

View file

@ -5,8 +5,12 @@ edition = "2024"
[dependencies]
bitflags = "2.10.0"
half = "2.7.1"
bytemuck = { version = "1.24.0", features = ["derive"] }
enum_dispatch = "0.3.13"
ash = { version = "0.38", optional = true }
parking_lot = { version = "0.12.5", optional = true }
gpu-allocator = { version = "0.28", features = ["vulkan"], optional = true }
num-traits = { version = "0.2.19", default-features = false, features = ["libm"] }
cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", default-features = false, optional = true }
@ -14,3 +18,4 @@ cuda_std = { git = "https://github.com/Rust-GPU/Rust-CUDA", branch = "main", def
use_f64 = []
cuda = ["cuda_std"]
cpu_debug = []
vulkan = ["dep:ash", "dep:gpu-allocator", "dep:parking_lot"]

View file

@ -16,13 +16,13 @@ use num_traits::Float as NumFloat;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MeasuredBxDFData {
pub wavelengths: Ptr<Float>,
pub spectra: PiecewiseLinear2D<3>,
pub ndf: PiecewiseLinear2D<0>,
pub vndf: PiecewiseLinear2D<2>,
pub sigma: PiecewiseLinear2D<0>,
pub isotropic: bool,
pub luminance: PiecewiseLinear2D<2>,
pub wavelengths: Ptr<Float>,
pub spectra: Ptr<PiecewiseLinear2D<3>>,
pub ndf: Ptr<PiecewiseLinear2D<0>>,
pub vndf: Ptr<PiecewiseLinear2D<2>>,
pub sigma: Ptr<PiecewiseLinear2D<0>>,
pub luminance: Ptr<PiecewiseLinear2D<2>>,
}
#[repr(C)]

View file

@ -10,7 +10,7 @@ use crate::core::sampler::CameraSample;
use crate::core::scattering::refract;
use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::{lerp, quadratic, square};
use crate::{Float, GVec, Ptr, PI};
use crate::{Float, GVec, Ptr, PI, gvec};
use num_traits::Float as NumFloat;
#[repr(C)]
@ -32,7 +32,7 @@ pub struct ExitPupilSample {
pub const EXIT_PUPIL_SAMPLES: usize = 64;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
pub struct RealisticCamera {
pub base: CameraBase,
pub focus_distance: Float,
@ -75,7 +75,8 @@ impl RealisticCamera {
if curvature_radius == 0.0 {
aperture_diameter /= 1000.0;
if set_aperture_diameter > aperture_diameter {
println!("Aperture is larger than possible")
// println!("Aperture is larger than possible")
aperture_diameter = -1.;
} else {
aperture_diameter = set_aperture_diameter;
}
@ -113,6 +114,10 @@ impl RealisticCamera {
}
}
unsafe fn lens(&self, idx: usize) -> &LensElementInterface {
unsafe { &*self.element_interfaces.as_ptr().add(idx) }
}
pub fn compute_cardinal_points(r_in: Ray, r_out: Ray) -> (Float, Float) {
let tf = -r_out.o.x() / r_out.d.x();
let tp = (r_in.o.x() - r_out.o.x()) / r_out.d.x();
@ -162,14 +167,12 @@ impl RealisticCamera {
);
}
let delta = (pz[1] - z + pz[0] - c.sqrt()) / 2.;
let last_interface = unsafe { self.element_interfaces.add(self.n_elements) };
let last_interface = unsafe { self.lens(self.n_elements - 1) };
last_interface.thickness + delta
}
pub fn bound_exit_pupil(&self, film_x_0: Float, film_x_1: Float) -> Bounds2f {
let interface_array = unsafe {
core::slice::from_raw_parts(self.element_interfaces.as_raw(), self.n_elements as usize)
};
let interface_array = self.element_interfaces.as_slice();
Self::compute_exit_pupil_bounds(interface_array, film_x_0, film_x_1)
}
@ -191,7 +194,6 @@ impl RealisticCamera {
let trace_lenses_from_film = |_ray: Ray, _place: Option<Ray>| true;
for i in 0..n_samples {
// Find location of sample points on $x$ segment and rear lens element
//
let p_film = Point3f::new(
lerp((i as Float + 0.5) / n_samples as Float, film_x_0, film_x_1),
0.,
@ -272,7 +274,7 @@ impl RealisticCamera {
);
for i in (0..self.n_elements - 1).rev() {
let element: &LensElementInterface = unsafe { &self.element_interfaces.add(i) };
let element: &LensElementInterface = unsafe { self.lens(i) };
// Update ray from film accounting for interaction with _element_
element_z -= element.thickness;
@ -311,7 +313,7 @@ impl RealisticCamera {
// Update ray path for element interface interaction
if !is_stop {
let eta_i = element.eta;
let interface_i = unsafe { self.element_interfaces.add(i - 1) };
let interface_i = unsafe { self.lens(i) };
let eta_t = if i > 0 && interface_i.eta != 0. {
interface_i.eta
} else {
@ -372,21 +374,21 @@ impl RealisticCamera {
}
pub fn lens_rear_z(&self) -> Float {
let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) };
let last_interface = unsafe { self.lens(self.n_elements - 1) };
last_interface.thickness
}
pub fn lens_front_z(&self) -> Float {
let mut z_sum = 0.;
for i in 0..self.n_elements {
let element = unsafe { self.element_interfaces.add(i) };
let element = unsafe { self.lens(i) };
z_sum += element.thickness;
}
z_sum
}
pub fn rear_element_radius(&self) -> Float {
let last_interface = unsafe { self.element_interfaces.add(self.n_elements - 1) };
let last_interface = unsafe { self.lens(self.n_elements - 1) };
last_interface.aperture_radius
}
}

View file

@ -1,9 +1,7 @@
use crate::core::geometry::{Bounds3f, Ray, Vector3f};
use crate::core::pbrt::Float;
use crate::core::primitive::{Primitive, PrimitiveTrait};
use crate::core::shape::ShapeIntersection;
use crate::{Float, Ptr, GVec, gvec};
use crate::utils::Ptr;
#[repr(C)]
#[derive(Debug, Clone, Copy)]
@ -18,11 +16,11 @@ pub struct LinearBVHNode {
#[repr(C)]
#[derive(Debug, Clone)]
pub struct BVHAggregate {
pub max_prims_in_node: u32,
pub primitives: GVec<Primitive>,
pub primitive_count: u32,
pub nodes: GVec<LinearBVHNode>,
pub node_count: u32,
pub max_prims_in_node: u32,
pub primitive_count: u32,
pub primitives: GVec<Primitive>,
pub nodes: GVec<LinearBVHNode>,
}
impl BVHAggregate {

View file

@ -118,7 +118,7 @@ pub struct CameraBase {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
#[enum_dispatch(CameraTrait)]
pub enum Camera {
Perspective(PerspectiveCamera),

View file

@ -1,18 +1,15 @@
use crate::core::geometry::Point2f;
use crate::core::spectrum::Spectrum;
use crate::utils::find_interval;
use crate::utils::math::{clamp, evaluate_polynomial, lerp, SquareMatrix, SquareMatrix3f};
use crate::{Float, GVec, Ptr};
use core::any::TypeId;
use core::fmt;
use core::ops::{
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
};
use crate::Float;
use crate::core::geometry::Point2f;
use crate::core::spectrum::Spectrum;
use crate::utils::Ptr;
use crate::utils::find_interval;
use crate::utils::math::{SquareMatrix, SquareMatrix3f, clamp, evaluate_polynomial, lerp};
use num_traits::Float as NumFloat;
use enum_dispatch::enum_dispatch;
use num_traits::Float as NumFloat;
#[repr(C)]
#[derive(Debug, Default, Clone, Copy)]
@ -311,17 +308,33 @@ impl RGB {
pub fn min_component_index(&self) -> u32 {
if self.r < self.g {
if self.r < self.b { 0 } else { 2 }
if self.r < self.b {
0
} else {
if self.g < self.b { 1 } else { 2 }
2
}
} else {
if self.g < self.b {
1
} else {
2
}
}
}
pub fn max_component_index(&self) -> u32 {
if self.r > self.g {
if self.r > self.b { 0 } else { 2 }
if self.r > self.b {
0
} else {
if self.g > self.b { 1 } else { 2 }
2
}
} else {
if self.g > self.b {
1
} else {
2
}
}
}
@ -1086,10 +1099,10 @@ impl Mul<Float> for Coeffs {
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct RGBToSpectrumTable {
pub z_nodes: GVec<Float>,
pub coeffs: GVec<Coeffs>,
pub z_nodes: Ptr<Float>,
pub coeffs: Ptr<Coeffs>,
pub n_nodes: u32,
}
@ -1114,7 +1127,11 @@ impl RGBToSpectrumTable {
// Identify the primary bucket (c) based on the dominant axis
let c_idx = if rgb[0] > rgb[1] {
if rgb[0] > rgb[2] { 0 } else { 2 }
if rgb[0] > rgb[2] {
0
} else {
2
}
} else if rgb[1] > rgb[2] {
1
} else {
@ -1136,7 +1153,7 @@ impl RGBToSpectrumTable {
let x = coord_a / z;
let y = coord_b / z;
let z_nodes = &self.z_nodes;
let z_nodes = unsafe { core::slice::from_raw_parts(self.z_nodes.as_raw(), RES as usize) };
let zi = find_interval(RES, |i| z_nodes[i as usize] < z) as usize;
let dz = (z - z_nodes[zi]) / (z_nodes[zi + 1] - z_nodes[zi]);
let x_float = x * (RES - 1) as Float;

View file

@ -17,7 +17,7 @@ use crate::utils::math::{wrap_equal_area_square, SquareMatrix};
use crate::utils::sampling::VarianceEstimator;
use crate::utils::transform::AnimatedTransform;
use crate::utils::{gpu_array_from_fn, AtomicFloat};
use crate::{gvec_with_capacity, Array2D, Float, Ptr};
use crate::{gvec_from_slice, gvec_with_capacity, Array2D, Float, GVec, Ptr};
use num_traits::Float as NumFloat;
#[repr(C)]
@ -84,7 +84,7 @@ impl RGBFilm {
filter_integral,
output_rgbf_from_sensor_rgb,
pixels,
};
}
}
pub fn base(&self) -> &FilmBase {
@ -465,7 +465,7 @@ impl SpectralFilm {
}
SpectralFilm {
base,
base: *base,
colorspace: colorspace.clone(),
lambda_min,
lambda_max,
@ -474,12 +474,7 @@ impl SpectralFilm {
write_fp16,
filter_integral: base.filter.integral(),
output_rgbf_from_sensor_rgb: SquareMatrix::identity(),
pixels: Array2D {
values: pixels.values.as_mut_ptr(),
extent: base.pixel_bounds,
},
pixels: Array2D::from_slice(base.pixel_bounds, pixels.as_slice()),
bucket_sums,
weight_sums,
bucket_splats,
@ -522,9 +517,9 @@ impl SpectralFilm {
#[derive(Debug, Copy, Clone)]
pub struct PixelSensor {
pub xyz_from_sensor_rgb: SquareMatrix<Float, 3>,
pub r_bar: DenselySampledSpectrum,
pub g_bar: DenselySampledSpectrum,
pub b_bar: DenselySampledSpectrum,
pub r_bar: Ptr<DenselySampledSpectrum>,
pub g_bar: Ptr<DenselySampledSpectrum>,
pub b_bar: Ptr<DenselySampledSpectrum>,
pub imaging_ratio: Float,
}

View file

@ -2,7 +2,7 @@ use crate::core::geometry::{Bounds2f, Bounds2i, Point2f, Point2i, Vector2f};
use crate::filters::*;
use crate::utils::math::{gaussian, gaussian_integral, lerp, sample_tent, windowed_sinc};
use crate::utils::sampling::PiecewiseConstant2D;
use crate::{Array2D, Float};
use crate::{Array2D, Float, Ptr};
use enum_dispatch::enum_dispatch;
pub struct FilterSample {
@ -11,7 +11,7 @@ pub struct FilterSample {
}
#[repr(C)]
#[derive(Clone, Debug, Copy)]
#[derive(Clone, Debug)]
pub struct FilterSampler {
pub domain: Bounds2f,
pub distrib: PiecewiseConstant2D,
@ -66,9 +66,26 @@ pub trait FilterTrait {
#[enum_dispatch(FilterTrait)]
#[derive(Clone, Copy, Debug)]
pub enum Filter {
Box(BoxFilter),
Gaussian(GaussianFilter),
Mitchell(MitchellFilter),
LanczosSinc(LanczosSincFilter),
Triangle(TriangleFilter),
Box(Ptr<BoxFilter>),
Gaussian(Ptr<GaussianFilter>),
Mitchell(Ptr<MitchellFilter>),
LanczosSinc(Ptr<LanczosSincFilter>),
Triangle(Ptr<TriangleFilter>),
}
impl<T: FilterTrait> FilterTrait for Ptr<T> {
fn radius(&self) -> Vector2f {
unsafe { self.as_ref().radius() }
}
fn integral(&self) -> Float {
unsafe { self.as_ref().integral() }
}
fn evaluate(&self, p: Point2f) -> Float {
unsafe { self.as_ref().evaluate(p) }
}
fn sample(&self, p: Point2f) -> FilterSample {
unsafe { self.as_ref().sample(p) }
}
}

View file

@ -1,8 +1,7 @@
use crate::core::color::{ColorEncoding, ColorEncodingTrait, LINEAR};
use crate::core::geometry::{Bounds2f, Point2f, Point2fi, Point2i};
use crate::utils::math::{f16_to_f32_software, lerp, square};
use crate::Float;
use crate::GVec;
use crate::{gvec_with_capacity, Float, GVec};
use core::hash;
use core::ops::{Deref, DerefMut};
use num_traits::Float as NumFloat;
@ -69,7 +68,7 @@ impl PixelFormat {
#[repr(C)]
#[derive(Clone, Debug)]
pub struct Pixels {
pixels: GVec<u8>,
data: GVec<u8>,
format: PixelFormat,
}
@ -95,17 +94,17 @@ impl Pixels {
}
pub unsafe fn read_u8(&self, texel_offset: usize) -> u8 {
*self.data.as_ptr().add(texel_offset)
unsafe { *self.data.as_ptr().add(texel_offset) }
}
pub unsafe fn read_f16(&self, texel_offset: usize) -> u16 {
let byte_offset = texel_offset * 2;
*(self.data.as_ptr().add(byte_offset) as *const u16)
unsafe { *(self.data.as_ptr().add(byte_offset) as *const u16) }
}
pub unsafe fn read_f32(&self, texel_offset: usize) -> f32 {
let byte_offset = texel_offset * 4;
*(self.data.as_ptr().add(byte_offset) as *const f32)
unsafe { *(self.data.as_ptr().add(byte_offset) as *const f32) }
}
pub unsafe fn read(&self, texel_offset: usize, encoding: &ColorEncoding) -> Float {
@ -117,17 +116,17 @@ impl Pixels {
}
pub unsafe fn write_u8(&mut self, texel_offset: usize, val: u8) {
*self.data.as_mut_ptr().add(texel_offset) = val;
unsafe { *self.data.as_mut_ptr().add(texel_offset) = val };
}
pub unsafe fn write_f16(&mut self, texel_offset: usize, val: u16) {
let byte_offset = texel_offset * 2;
*(self.data.as_mut_ptr().add(byte_offset) as *mut u16) = val;
unsafe { *(self.data.as_mut_ptr().add(byte_offset) as *mut u16) = val };
}
pub unsafe fn write_f32(&mut self, texel_offset: usize, val: f32) {
let byte_offset = texel_offset * 4;
*(self.data.as_mut_ptr().add(byte_offset) as *mut f32) = val;
unsafe { *(self.data.as_mut_ptr().add(byte_offset) as *mut f32) = val };
}
pub fn empty(texel_count: usize, format: PixelFormat) -> Self {
@ -194,6 +193,14 @@ impl Pixels {
}
}
#[derive(Clone, Debug)]
pub struct ImageBase {
pub format: PixelFormat,
pub encoding: ColorEncoding,
pub resolution: Point2i,
pub n_channels: i32,
}
#[derive(Clone, Debug)]
pub struct Image {
pub format: PixelFormat,
@ -299,6 +306,15 @@ impl Image {
true
}
pub fn base(&self) -> ImageBase {
ImageBase {
format: self.format,
encoding: self.encoding,
resolution: self.resolution,
n_channels: self.n_channels,
}
}
pub fn get_channel(&self, p: Point2i, c: i32) -> Float {
self.get_channel_with_wrap(p, c, WrapMode::Clamp.into())
}
@ -329,11 +345,11 @@ impl Image {
self.get_channel_with_wrap(pi, c, wrap_mode)
}
pub fn get_channels_array<const N: usize>(&self, p: Point2i) -> [Float; N] {
self.get_channels_array_with_wrap(p, WrapMode::Clamp.into())
pub fn get_channels<const N: usize>(&self, p: Point2i) -> [Float; N] {
self.get_channels_with_wrap(p, WrapMode::Clamp.into())
}
pub fn get_channels_array_with_wrap<const N: usize>(
pub fn get_channels_with_wrap<const N: usize>(
&self,
mut p: Point2i,
wrap_mode: WrapMode2D,

View file

@ -142,7 +142,7 @@ impl MajorantGrid {
let idx = x + self.res.x() * (y + self.res.y() * z);
unsafe {
*self.voxels.as_ptr().add(idx as usize) = v;
*self.voxels.as_mut_ptr().add(idx as usize) = v;
}
}
@ -229,7 +229,7 @@ pub struct DDAMajorantIterator {
sigma_t: SampledSpectrum,
t_min: Float,
t_max: Float,
grid: MajorantGrid,
grid: Ptr<MajorantGrid>,
next_crossing_t: [Float; 3],
delta_t: [Float; 3],
step: [i32; 3],
@ -249,7 +249,7 @@ impl DDAMajorantIterator {
t_min,
t_max,
sigma_t: *sigma_t,
grid: *grid,
grid: Ptr::from(&*grid),
next_crossing_t: [0.0; 3],
delta_t: [0.0; 3],
step: [0; 3],
@ -452,9 +452,9 @@ pub enum Medium {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct HomogeneousMedium {
pub sigma_a_spec: DenselySampledSpectrum,
pub sigma_s_spec: DenselySampledSpectrum,
pub le_spec: DenselySampledSpectrum,
pub sigma_a_spec: Ptr<DenselySampledSpectrum>,
pub sigma_s_spec: Ptr<DenselySampledSpectrum>,
pub le_spec: Ptr<DenselySampledSpectrum>,
pub phase: HGPhaseFunction,
}
@ -496,15 +496,15 @@ impl MediumTrait for HomogeneousMedium {
pub struct GridMedium {
pub bounds: Bounds3f,
pub render_from_medium: Transform,
pub sigma_a_spec: DenselySampledSpectrum,
pub sigma_s_spec: DenselySampledSpectrum,
pub density_grid: SampledGrid<Float>,
pub sigma_a_spec: Ptr<DenselySampledSpectrum>,
pub sigma_s_spec: Ptr<DenselySampledSpectrum>,
pub density_grid: Ptr<SampledGrid<Float>>,
pub phase: HGPhaseFunction,
pub temperature_grid: Option<SampledGrid<Float>>,
pub le_spec: DenselySampledSpectrum,
pub le_scale: SampledGrid<Float>,
pub temperature_grid: Ptr<SampledGrid<Float>>,
pub le_spec: Ptr<DenselySampledSpectrum>,
pub le_scale: Ptr<SampledGrid<Float>>,
pub is_emissive: bool,
pub majorant_grid: MajorantGrid,
pub majorant_grid: Ptr<MajorantGrid>,
}
impl MediumTrait for GridMedium {
@ -527,8 +527,8 @@ impl MediumTrait for GridMedium {
};
let le = if scale > 0.0 {
let raw_emission = if let Some(temp_grid) = &self.temperature_grid {
let temp = temp_grid.lookup(p);
let raw_emission = if !self.temperature_grid.is_null() {
let temp = self.temperature_grid.lookup(p);
BlackbodySpectrum::new(temp).sample(lambda)
} else {
self.le_spec.sample(lambda)
@ -592,10 +592,10 @@ pub struct RGBGridMedium {
pub phase: HGPhaseFunction,
pub le_scale: Float,
pub sigma_scale: Float,
pub sigma_a_grid: SampledGrid<RGBUnboundedSpectrum>,
pub sigma_s_grid: SampledGrid<RGBUnboundedSpectrum>,
pub le_grid: SampledGrid<RGBIlluminantSpectrum>,
pub majorant_grid: MajorantGrid,
pub sigma_a_grid: Ptr<SampledGrid<RGBUnboundedSpectrum>>,
pub sigma_s_grid: Ptr<SampledGrid<RGBUnboundedSpectrum>>,
pub le_grid: Ptr<SampledGrid<RGBIlluminantSpectrum>>,
pub majorant_grid: Ptr<MajorantGrid>,
}
impl MediumTrait for RGBGridMedium {

View file

@ -213,6 +213,23 @@ pub enum Primitive {
Geometric(GeometricPrimitive),
Transformed(TransformedPrimitive),
Animated(AnimatedPrimitive),
BVH(BVHAggregate),
BVH(Ptr<BVHAggregate>),
KdTree(KdTreeAggregate),
}
impl<T: PrimitiveTrait> PrimitiveTrait for Ptr<T> {
fn bounds(&self) -> Bounds3f {
unsafe { self.as_ref().bounds() }
}
fn intersect(&self, r: &Ray, t_max: Option<Float>) -> Option<ShapeIntersection> {
unsafe { self.as_ref().intersect(r, t_max) }
}
fn intersect_p(&self, r: &Ray, t_max: Option<Float>) -> bool {
unsafe { self.as_ref().intersect_p(r, t_max) }
}
}

View file

@ -100,7 +100,7 @@ pub struct HaltonSampler {
pub mult_inverse: [u64; 2],
pub halton_index: u64,
pub dim: u32,
pub digit_permutations: GVec<DigitPermutation>,
pub digit_permutations: Ptr<DigitPermutation>,
}
#[allow(clippy::derivable_impls)]
@ -114,7 +114,7 @@ impl Default for HaltonSampler {
mult_inverse: [0; 2],
halton_index: 0,
dim: 0,
digit_permutations: gvec(),
digit_permutations: Ptr::default(),
}
}
}

View file

@ -1,11 +1,11 @@
use crate::Float;
use crate::{Float, Ptr};
use crate::core::color::{RGB, XYZ};
use enum_dispatch::enum_dispatch;
pub use crate::spectra::*;
#[enum_dispatch]
pub trait SpectrumTrait: Copy {
pub trait SpectrumTrait {
fn evaluate(&self, lambda: Float) -> Float;
fn sample(&self, lambda: &SampledWavelengths) -> SampledSpectrum {
SampledSpectrum::from_fn(|i| self.evaluate(lambda[i]))
@ -16,10 +16,10 @@ pub trait SpectrumTrait: Copy {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct StandardSpectra {
pub x: DenselySampledSpectrum,
pub y: DenselySampledSpectrum,
pub z: DenselySampledSpectrum,
pub d65: DenselySampledSpectrum,
pub x: Ptr<DenselySampledSpectrum>,
pub y: Ptr<DenselySampledSpectrum>,
pub z: Ptr<DenselySampledSpectrum>,
pub d65: Ptr<DenselySampledSpectrum>,
}
unsafe impl Send for StandardSpectra {}
@ -30,14 +30,23 @@ unsafe impl Sync for StandardSpectra {}
#[derive(Debug, Clone, Copy)]
pub enum Spectrum {
Constant(ConstantSpectrum),
Dense(DenselySampledSpectrum),
Piecewise(PiecewiseLinearSpectrum),
Dense(Ptr<DenselySampledSpectrum>),
Piecewise(Ptr<PiecewiseLinearSpectrum>),
Blackbody(BlackbodySpectrum),
RGBAlbedo(RGBAlbedoSpectrum),
RGBIlluminant(RGBIlluminantSpectrum),
RGBUnbounded(RGBUnboundedSpectrum),
}
impl<T: SpectrumTrait> SpectrumTrait for Ptr<T> {
fn evaluate(&self, lambda: Float) -> Float {
unsafe { self.as_ref().evaluate(lambda) }
}
fn max_value(&self) -> Float {
unsafe { self.as_ref().max_value() }
}
}
impl Spectrum {
pub fn std_illuminant_d65() -> Self {
unimplemented!("Use crate::spectra::default_illuminant() on host")

View file

@ -1,10 +1,10 @@
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
use crate::core::geometry::{Point2f, Vector2f};
use crate::utils::math::{gaussian, gaussian_integral};
use crate::Float;
use crate::{Ptr, Float};
#[repr(C)]
#[derive(Clone, Debug, Copy)]
#[derive(Clone, Debug)]
pub struct GaussianFilter {
pub radius: Vector2f,
pub sigma: Float,

View file

@ -1,10 +1,11 @@
use crate::core::filter::{FilterSampler, FilterSample, FilterTrait};
use crate::core::geometry::{Point2f, Vector2f};
use crate::utils::math::{lerp, windowed_sinc};
use crate::utils::rng::Rng;
use crate::Float;
#[repr(C)]
#[derive(Clone, Debug, Copy)]
#[derive(Clone, Debug)]
pub struct LanczosSincFilter {
pub radius: Vector2f,
pub tau: Float,
@ -14,15 +15,34 @@ pub struct LanczosSincFilter {
impl LanczosSincFilter {
pub fn new(radius: Vector2f, tau: Float) -> Self {
let sampler = FilterSampler::new(radius, move |p: Point2f| {
let evaluate = move |p: Point2f| -> Float {
windowed_sinc(p.x(), radius.x(), tau) * windowed_sinc(p.y(), radius.y(), tau)
});
Self {
radius,
tau,
sampler,
};
let sampler = FilterSampler::new(radius, evaluate);
let sqrt_samples = 64u32;
let n_samples = sqrt_samples * sqrt_samples;
let area = (2.0 * radius.x()) * (2.0 * radius.y());
let mut sum = 0.0;
let mut rng = Rng::new(0);
for y in 0..sqrt_samples {
for x in 0..sqrt_samples {
let u = Point2f::new(
(x as Float + rng.uniform::<Float>()) / sqrt_samples as Float,
(y as Float + rng.uniform::<Float>()) / sqrt_samples as Float,
);
let p = Point2f::new(
lerp(u.x(), -radius.x(), radius.x()),
lerp(u.y(), -radius.y(), radius.y()),
);
sum += evaluate(p);
}
}
let integral = sum / n_samples as Float * area;
Self { radius, tau, sampler, integral }
}
}
impl FilterTrait for LanczosSincFilter {

View file

@ -1,10 +1,10 @@
use crate::core::filter::{FilterSampler, FilterSample, FilterTrait};
use crate::core::filter::{FilterSample, FilterSampler, FilterTrait};
use crate::core::geometry::{Point2f, Vector2f};
use crate::Float;
use num_traits::Float as NumFloat;
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub struct MitchellFilter {
pub radius: Vector2f,
pub b: Float,
@ -12,8 +12,7 @@ pub struct MitchellFilter {
pub sampler: FilterSampler,
}
impl MitchellFilter {
pub fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
pub fn mitchell_1d_eval(b: Float, c: Float, x: Float) -> Float {
let x = x.abs();
if x <= 1.0 {
((12.0 - 9.0 * b - 6.0 * c) * x.powi(3)
@ -29,15 +28,12 @@ impl MitchellFilter {
} else {
0.0
}
}
fn mitchell_1d(&self, x: Float) -> Float {
Self::mitchell_1d_eval(self.b, self.c, x)
}
}
impl MitchellFilter {
pub fn new(radius: Vector2f, b: Float, c: Float) -> Self {
let sampler = FilterSampler::new(radius, move |p: Point2f| {
mitchell_1d(p.x() / radius.x(), b, c) * mitchell_1d(p.y() / radius.y(), b, c)
mitchell_1d_eval(p.x() / radius.x(), b, c) * mitchell_1d_eval(p.y() / radius.y(), b, c)
});
Self {
radius,
@ -46,6 +42,10 @@ impl MitchellFilter {
sampler,
}
}
fn mitchell_1d(&self, x: Float) -> Float {
mitchell_1d_eval(self.b, self.c, x)
}
}
impl FilterTrait for MitchellFilter {

View file

@ -1,4 +1,5 @@
#![allow(unused_imports, dead_code)]
#![feature(allocator_api)]
#![feature(associated_type_defaults)]
#![no_std]
extern crate alloc;
@ -17,5 +18,5 @@ pub mod textures;
pub mod utils;
pub use core::pbrt::*;
pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, GVec, Gbox};
pub use utils::{Transform, Ptr, Array2D, PBRTOptions};
pub use utils::alloc::{gbox, gvec, gvec_from_slice, gvec_with_capacity, GVec, GBox};
pub use utils::{Array2D, PBRTOptions, Ptr, Transform};

View file

@ -1,6 +1,6 @@
use crate::core::color::{RGB, XYZ};
use crate::core::geometry::*;
use crate::core::image::{Image, ImageAccess};
use crate::core::image::Image;
use crate::core::interaction::{
Interaction, InteractionTrait, MediumInteraction, SurfaceInteraction,
};
@ -171,8 +171,8 @@ impl LightTrait for DiffuseAreaLight {
fn bounds(&self) -> Option<LightBounds> {
let mut phi = 0.;
if !self.image.is_null() {
for y in 0..self.image.base.resolution.y() {
for x in 0..self.image.base.resolution.x() {
for y in 0..self.image.resolution().y() {
for x in 0..self.image.resolution().x() {
for c in 0..3 {
phi += self.image.get_channel(Point2i::new(x, y), c);
}

View file

@ -20,22 +20,6 @@ pub struct DistantLight {
}
impl DistantLight {
pub fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self {
let base = LightBase::new(
LightType::DeltaDirection,
render_from_light,
MediumInterface::empty(),
);
let lemit = lookup_spectrum(&le);
Self {
base,
lemit: Ptr::from(&lemit.device()),
scale,
scene_center: Point3f::default(),
scene_radius: 0.,
}
}
pub fn sample_li_base(
&self,
ctx_p: Point3f,

View file

@ -1,5 +1,5 @@
use crate::core::geometry::{Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f};
use crate::core::image::{Image, ImageAccess};
use crate::core::image::Image;
use crate::core::light::{
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
};

View file

@ -3,7 +3,7 @@ use crate::core::geometry::{
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector2f, Vector3f,
};
use crate::core::geometry::{Frame, VectorLike};
use crate::core::image::{Image, ImageAccess, PixelFormat, WrapMode};
use crate::core::image::{Image, PixelFormat, WrapMode};
use crate::core::interaction::InteractionBase;
use crate::core::interaction::{Interaction, SimpleInteraction};
use crate::core::light::{
@ -15,7 +15,7 @@ use crate::spectra::{DenselySampledSpectrum, SampledSpectrum, SampledWavelengths
use crate::spectra::{RGBColorSpace, RGBIlluminantSpectrum};
use crate::utils::math::{clamp, equal_area_sphere_to_square, equal_area_square_to_sphere, square};
use crate::utils::sampling::{
sample_uniform_sphere, uniform_sphere_pdf, AliasTable, DevicePiecewiseConstant2D,
sample_uniform_sphere, uniform_sphere_pdf, AliasTable, PiecewiseConstant2D,
WindowedPiecewiseConstant2D,
};
use crate::utils::{Ptr, Transform};
@ -151,7 +151,7 @@ impl ImageInfiniteLight {
pub fn new(
render_from_light: Transform,
scale: Float,
image: Ptr<HostImage>,
image: Ptr<Image>,
image_color_space: Ptr<RGBColorSpace>,
distrib: Ptr<PiecewiseConstant2D>,
compensated_distrib: Ptr<PiecewiseConstant2D>,
@ -176,7 +176,11 @@ impl ImageInfiniteLight {
fn image_le(&self, uv: Point2f, lambda: &SampledWavelengths) -> SampledSpectrum {
let mut rgb = RGB::default();
for c in 0..3 {
rgb[c] = self.image.get_ch(uv, c, WrapMode::OctahedralSphere.into());
rgb[c] = self.image.lookup_nearest_channel_with_wrap(
uv,
c,
WrapMode::OctahedralSphere.into(),
);
}
let spec = RGBIlluminantSpectrum::new(&self.image_color_space, rgb.clamp_zero());
self.scale * spec.sample(lambda)
@ -297,7 +301,7 @@ pub struct PortalInfiniteLight {
pub scale: Float,
pub portal: [Point3f; 4],
pub portal_frame: Frame,
pub distribution: WindowedPiecewiseConstant2D,
pub distribution: Ptr<WindowedPiecewiseConstant2D>,
pub scene_center: Point3f,
pub scene_radius: Float,
}
@ -306,7 +310,7 @@ impl PortalInfiniteLight {
pub fn new(
render_from_light: Transform,
scale: Float,
image: Ptr<HostImage>,
image: Ptr<Image>,
image_color_space: Ptr<RGBColorSpace>,
portal: [Point3f; 4],
portal_frame: Frame,
@ -324,7 +328,7 @@ impl PortalInfiniteLight {
scale,
portal,
portal_frame,
distribution: *distribution,
distribution,
scene_center: Point3f::default(),
scene_radius: 0.0,
}

View file

@ -18,25 +18,6 @@ pub struct PointLight {
pub i: Ptr<DenselySampledSpectrum>,
}
impl PointLight {
pub fn new(
render_from_light: Transform,
medium_interface: MediumInterface,
le: Spectrum,
scale: Float,
) -> Self {
let base = LightBase::new(
LightType::DeltaPosition,
render_from_light,
medium_interface,
);
let iemit = lookup_spectrum(&le);
let i = Ptr::from(&iemit.device());
Self { base, scale, i }
}
}
impl LightTrait for PointLight {
fn base(&self) -> &LightBase {
&self.base

View file

@ -3,7 +3,7 @@ use crate::core::color::RGB;
use crate::core::geometry::{
Bounds2f, Bounds3f, Normal3f, Point2f, Point2i, Point3f, Ray, Vector3f, VectorLike, cos_theta,
};
use crate::core::image::{Image, ImageAccess};
use crate::core::image::Image;
use crate::core::light::{
LightBase, LightBounds, LightLiSample, LightSampleContext, LightTrait, LightType,
};
@ -13,7 +13,7 @@ use crate::spectra::{SampledSpectrum, SampledWavelengths};
use crate::utils::math::{radians, square};
use crate::{
spectra::{RGBColorSpace, RGBIlluminantSpectrum},
utils::{Ptr, Transform, sampling::DevicePiecewiseConstant2D},
utils::{Ptr, Transform, sampling::PiecewiseConstant2D},
};
use num_traits::Float as NumFloat;
@ -28,7 +28,7 @@ pub struct ProjectionLight {
pub light_from_screen: Transform,
pub a: Float,
pub image: Ptr<Image>,
pub distrib: Ptr<DevicePiecewiseConstant2D>,
pub distrib: Ptr<PiecewiseConstant2D>,
pub image_color_space: Ptr<RGBColorSpace>,
}

View file

@ -251,7 +251,7 @@ pub struct Alias {
pub struct PowerLightSampler {
pub lights: Ptr<Light>,
pub lights_len: u32,
pub alias_table: AliasTable,
pub alias_table: Ptr<AliasTable>,
}
unsafe impl Send for PowerLightSampler {}

View file

@ -19,31 +19,6 @@ pub struct SpotLight {
}
impl SpotLight {
pub fn new(
render_from_light: Transform,
_medium_interface: MediumInterface,
le: Spectrum,
scale: shared::Float,
cos_falloff_start: Float,
total_width: Float,
) -> Self {
let base = LightBase::new(
LightType::DeltaPosition,
render_from_light,
MediumInterface::empty(),
);
let i = lookup_spectrum(&le);
let iemit = Ptr::from(&i.device());
Self {
base,
iemit,
scale,
cos_falloff_end: radians(total_width).cos(),
cos_falloff_start: radians(cos_falloff_start).cos(),
}
}
pub fn i(&self, w: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
let cos_theta = w.z(); // assuming normalized in light space
let falloff = crate::utils::math::smooth_step(

View file

@ -61,7 +61,7 @@ impl BilinearPatchShape {
#[inline(always)]
fn get_vertex_indices(&self) -> [usize; 4] {
unsafe {
let base_ptr = self.mesh.vertex_indices.add((self.blp_index as usize) * 4);
let base_ptr = self.mesh.vertex_indices.as_ptr().add((self.blp_index as usize) * 4);
[
*base_ptr.add(0) as usize,
*base_ptr.add(1) as usize,
@ -73,47 +73,29 @@ impl BilinearPatchShape {
#[inline(always)]
fn get_points(&self) -> [Point3f; 4] {
let mesh = self.mesh();
let [v0, v1, v2, v3] = self.get_vertex_indices();
unsafe {
[
*self.mesh.p.add(v0),
*self.mesh.p.add(v1),
*self.mesh.p.add(v2),
*self.mesh.p.add(v3),
]
}
[mesh.p[v0], mesh.p[v1], mesh.p[v2], mesh.p[v3]]
}
#[inline(always)]
fn get_uvs(&self) -> Option<[Point2f; 4]> {
if self.mesh.uv.is_null() {
let mesh = self.mesh();
if mesh.uv.is_empty() {
return None;
}
let [v0, v1, v2, v3] = self.get_vertex_indices();
unsafe {
Some([
*self.mesh.uv.add(v0),
*self.mesh.uv.add(v1),
*self.mesh.uv.add(v2),
*self.mesh.uv.add(v3),
])
}
Some([mesh.uv[v0], mesh.uv[v1], mesh.uv[v2], mesh.uv[v3]])
}
#[inline(always)]
fn get_shading_normals(&self) -> Option<[Normal3f; 4]> {
if self.mesh.n.is_null() {
let mesh = self.mesh();
if mesh.n.is_empty() {
return None;
}
let [v0, v1, v2, v3] = self.get_vertex_indices();
unsafe {
Some([
*self.mesh.n.add(v0),
*self.mesh.n.add(v1),
*self.mesh.n.add(v2),
*self.mesh.n.add(v3),
])
}
Some([mesh.n[v0], mesh.n[v1], mesh.n[v2], mesh.n[v3]])
}
#[cfg(not(target_os = "cuda"))]

View file

@ -1,10 +1,9 @@
use crate::core::geometry::{Normal3f, Point2f, Point3f, Vector3f};
use crate::utils::sampling::PiecewiseConstant2D;
use crate::utils::Transform;
use crate::{Float, Gvec};
use crate::{gvec_from_slice, gvec_with_capacity, Float, GVec, Ptr, Transform};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
pub struct TriangleMesh {
pub p: GVec<Point3f>,
pub n: GVec<Normal3f>,
@ -19,7 +18,7 @@ pub struct TriangleMesh {
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct BilinearPatchMesh {
pub p: GVec<Point3f>,
pub n: GVec<Normal3f>,
@ -121,7 +120,7 @@ impl BilinearPatchMesh {
p: &[Point3f],
n: &[Normal3f],
uv: &[Point2f],
image_distribution: Option<PiecewiseConstant2D>,
image_distribution: Option<&PiecewiseConstant2D>,
) -> Self {
let n_patches = (vertex_indices.len() / 4) as u32;
let n_vertices = p.len() as u32;

View file

@ -41,8 +41,11 @@ impl TriangleShape {
pub const MIN_SPHERICAL_SAMPLE_AREA: Float = 3e-4;
pub const MAX_SPHERICAL_SAMPLE_AREA: Float = 6.22;
fn mesh(&self) -> &TriangleMesh {
&*self.mesh
}
fn get_vertex_indices(&self) -> [usize; 3] {
let mesh = unsafe { &*self.mesh };
let mesh = self.mesh();
let base = (self.tri_index as usize) * 3;
[
mesh.vertex_indices[base] as usize,
@ -52,13 +55,13 @@ impl TriangleShape {
}
fn get_points(&self) -> [Point3f; 3] {
let mesh = unsafe { &*self.mesh };
let mesh = self.mesh();
let [v0, v1, v2] = self.get_vertex_indices();
[mesh.p[v0], mesh.p[v1], mesh.p[v2]]
}
fn get_shading_normals(&self) -> Option<[Normal3f; 3]> {
let mesh = unsafe { &*self.mesh };
let mesh = self.mesh();
if mesh.n.is_empty() {
return None;
}
@ -67,7 +70,7 @@ impl TriangleShape {
}
fn get_tangents(&self) -> Option<[Vector3f; 3]> {
let mesh = unsafe { &*self.mesh };
let mesh = self.mesh();
if mesh.s.is_empty() {
return None;
}
@ -75,6 +78,16 @@ impl TriangleShape {
Some([mesh.s[v0], mesh.s[v1], mesh.s[v2]])
}
fn get_uvs(&self) -> Option<[Point2f; 3]> {
let mesh = self.mesh();
if mesh.s.is_empty() {
return None;
}
let [v0, v1, v2] = self.get_vertex_indices();
Some([mesh.uv[v0], mesh.uv[v1], mesh.uv[v2]])
}
pub fn new(mesh: Ptr<TriangleMesh>, tri_index: i32) -> Self {
Self { mesh, tri_index }
}
@ -172,8 +185,8 @@ impl TriangleShape {
flip_normal,
);
isect.face_index = if !self.mesh.face_indices.is_null() {
unsafe { *self.mesh.face_indices.add(self.tri_index as usize) }
isect.face_index = if !self.mesh.face_indices.is_empty() {
unsafe { *self.mesh.face_indices.as_ptr().add(self.tri_index as usize) }
} else {
0
};
@ -181,7 +194,7 @@ impl TriangleShape {
isect.common.n = ng;
isect.shading.n = ng;
if !self.mesh.p.is_null() || !self.mesh.s.is_null() {
if !self.mesh.p.is_empty() || !self.mesh.s.is_empty() {
self.compute_shading_geometry(&mut isect, &ti, uv, dpdu, determinant, degenerate);
}
isect

View file

@ -71,7 +71,7 @@ pub struct RGBColorSpace {
pub xyz_from_rgb: SquareMatrix3f,
pub rgb_from_xyz: SquareMatrix3f,
pub illuminant: Ptr<DenselySampledSpectrum>,
pub rgb_to_spectrum_table: Ptr<RGBToSpectrumTable>,
pub rgb_to_spectrum_table: RGBToSpectrumTable,
}
unsafe impl Send for RGBColorSpace {}

View file

@ -85,7 +85,7 @@ impl RGBIlluminantSpectrum {
Self {
scale,
rsp,
illuminant: Ptr::from(&illuminant),
illuminant,
}
}
}

View file

@ -180,11 +180,13 @@ pub struct PiecewiseLinearSpectrum {
impl PiecewiseLinearSpectrum {
#[inline(always)]
pub fn count(&self) -> usize {
if self.values.is_empty() {
0
} else {
(self.lambda_max - self.lambda_min + 1) as usize
self.count.try_into().unwrap()
}
#[inline(always)]
pub fn lambda(&self, idx: u32) -> Float {
unsafe { *self.lambdas.as_ptr().add(idx as usize) }
}
#[inline(always)]
@ -263,7 +265,7 @@ impl SpectrumTrait for PiecewiseLinearSpectrum {
for i in 0..n {
unsafe {
let val = *self.values.add(i as usize);
let val = *self.values.as_ptr().add(i as usize);
if val > max_val {
max_val = val;
}

View file

@ -18,7 +18,7 @@ pub struct GPUSpectrumImageTexture {
pub scale: Float,
pub invert: bool,
pub is_single_channel: bool,
pub color_space: RGBColorSpace,
pub color_space: Ptr<RGBColorSpace>,
pub spectrum_type: SpectrumType,
}

View file

@ -1,7 +1,8 @@
#![feature(allocator_api)]
extern crate alloc;
use alloc::alloc::Global;
use alloc::vec::Vec;
use alloc::boxed::Box;
use core::alloc::{AllocError, Allocator, Layout};
use core::ptr::NonNull;

View file

@ -76,6 +76,22 @@ impl<T: Default + Clone> Array2D<T> {
values.resize(n, val);
Self { extent, values }
}
pub fn from_slice(extent: Bounds2i, slice: &[T]) -> Self {
assert_eq!(slice.len(), extent.area() as usize);
Self {
extent,
values: gvec_from_slice(slice),
}
}
pub fn get(&self, p: Point2i) -> &T {
&self[p]
}
pub fn get_linear_mut(&mut self, index: usize) -> &mut T {
unsafe { &mut *self.values.as_mut_ptr().add(index) }
}
}
impl<T> Index<(i32, i32)> for Array2D<T> {
@ -93,8 +109,25 @@ impl<T> IndexMut<(i32, i32)> for Array2D<T> {
}
}
impl<T> Index<Point2i> for Array2D<T> {
type Output = T;
fn index(&self, p: Point2i) -> &T {
let offset =
(p.y() - self.extent.p_min.y()) * self.stride() + (p.x() - self.extent.p_min.x());
&self.values[offset as usize]
}
}
impl<T> IndexMut<Point2i> for Array2D<T> {
fn index_mut(&mut self, p: Point2i) -> &mut T {
let offset =
(p.y() - self.extent.p_min.y()) * self.stride() + (p.x() - self.extent.p_min.x());
&mut self.values[offset as usize]
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct SampledGrid<T> {
pub values: GVec<T>,
pub values_len: u32,
@ -106,7 +139,7 @@ pub struct SampledGrid<T> {
unsafe impl<T: Sync> Sync for SampledGrid<T> {}
unsafe impl<T: Send> Send for SampledGrid<T> {}
impl<T> SampledGrid<T> {
impl<T: core::clone::Clone> SampledGrid<T> {
pub fn new(slice: &[T], nx: i32, ny: i32, nz: i32) -> Self {
assert_eq!(slice.len(), (nx * ny * nz) as usize);
Self {
@ -129,7 +162,7 @@ impl<T> SampledGrid<T> {
}
pub fn is_valid(&self) -> bool {
!self.values.is_null() && self.nx > 0 && self.ny > 0 && self.nz > 0
!self.values.is_empty() && self.nx > 0 && self.ny > 0 && self.nz > 0
}
pub fn bytes_allocated(&self) -> u32 {
@ -168,7 +201,7 @@ impl<T> SampledGrid<T> {
let idx = (p.z() * self.ny + p.y()) * self.nx + p.x();
unsafe {
let val = &*self.values.add(idx as usize);
let val = &*self.values.as_ptr().add(idx as usize);
convert(val)
}
}

View file

@ -1,11 +1,10 @@
use crate::core::color::{RGB, XYZ};
use crate::core::geometry::{Lerp, MulAdd, Point, Point2f, Point2i, Vector, Vector3f, VectorLike};
use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI_OVER_4};
use crate::utils::gpu_array_from_fn;
use crate::utils::hash::{hash_buffer, mix_bits};
use crate::utils::math::permutation_element;
use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV};
use crate::utils::{gpu_array_from_fn, Ptr};
use crate::{gvec, gvec_with_capacity, GVec, Ptr};
use core::fmt::{self, Display, Write};
use core::iter::{Product, Sum};
use core::mem;
@ -770,13 +769,23 @@ pub fn inverse_radical_inverse(mut inverse: u64, base: u64, n_digits: u64) -> u6
// Digit scrambling
#[repr(C)]
#[derive(Default, Debug, Clone)]
#[derive(Debug, Clone)]
pub struct DigitPermutation {
pub base: i32,
pub n_digits: u32,
pub permutations: GVec<u16>,
}
impl Default for DigitPermutation {
fn default() -> Self {
Self {
base: i32::default(),
n_digits: u32::default(),
permutations: gvec(),
}
}
}
impl DigitPermutation {
pub fn new(base: i32, seed: u64) -> Self {
assert!(base < 65536);
@ -804,9 +813,9 @@ impl DigitPermutation {
}
Self {
device,
permutations,
base,
n_digits,
permutations,
}
}
@ -818,11 +827,7 @@ impl DigitPermutation {
}
}
pub fn scrambled_radical_inverse(
base_index: u32,
mut a: u64,
perm: &DigitPermutation,
) -> Float {
pub fn scrambled_radical_inverse(base_index: u32, mut a: u64, perm: &DigitPermutation) -> Float {
let base = PRIMES[base_index as usize] as u64;
let limit = (u64::MAX / base).saturating_sub(base);

View file

@ -1,3 +1,4 @@
pub mod alloc;
pub mod complex;
pub mod containers;
pub mod hash;
@ -16,6 +17,7 @@ pub mod transform;
pub use options::PBRTOptions;
pub use ptr::Ptr;
pub use transform::{AnimatedTransform, Transform, TransformGeneric};
pub use containers::Array2D;
use crate::Float;
use core::sync::atomic::{AtomicU32, Ordering};

View file

@ -50,7 +50,6 @@ impl<T> Ptr<T> {
unsafe { &*self.ptr }
}
/// Get as Option - safe for optional fields
#[inline(always)]
pub fn get<'a>(self) -> Option<&'a T> {
if self.is_null() {
@ -141,3 +140,12 @@ impl<T> From<*const T> for Ptr<T> {
Self { ptr }
}
}
impl<T> From<Option<&T>> for Ptr<T> {
fn from(opt: Option<&T>) -> Self {
match opt {
Some(r) => Ptr::from(r),
None => Ptr::null(),
}
}
}

View file

@ -1,14 +1,14 @@
use crate::core::geometry::{
Bounds2f, Frame, Point2f, Point2i, Point3f, Vector2f, Vector2i, Vector3f, VectorLike,
};
use crate::core::image::Image;
use crate::utils::find_interval;
use crate::utils::math::{
catmull_rom_weights, clamp, difference_of_products, evaluate_polynomial, lerp, logistic,
newton_bisection, safe_sqrt, square, sum_of_products,
};
use crate::utils::ptr::Ptr;
use crate::utils::rng::Rng;
use crate::{gvec_from_slice, gvec_with_capacity, Array2D, GVec};
use crate::{gvec, gvec_from_slice, gvec_with_capacity, Array2D, GVec, Ptr};
use crate::{Float, INV_2_PI, INV_4_PI, INV_PI, ONE_MINUS_EPSILON, PI, PI_OVER_2, PI_OVER_4};
use num_traits::Float as NumFloat;
use num_traits::Num;
@ -751,9 +751,11 @@ impl PiecewiseConstant1D {
F: Fn(Float) -> Float,
{
let delta = (max - min) / n as Float;
let values: Vec<Float> = (0..n)
.map(|i| f(min + (i as Float + 0.5) * delta))
.collect();
let delta = (max - min) / n as Float;
let mut values = gvec_with_capacity(n);
for i in 0..n {
values.push(f(min + (i as Float + 0.5) * delta));
}
Self::new_with_bounds(&values, min, max)
}
@ -830,12 +832,12 @@ impl PiecewiseConstant2D {
assert_eq!(data.len(), n_u * n_v);
let mut conditionals = gvec_with_capacity(n_v);
let mut marginal_func = GVec::with_capacity(n_v);
let mut marginal_func = gvec_with_capacity(n_v);
for v in 0..n_v {
let row = &data[v * n_u..(v + 1) * n_u];
let row = data[v * n_u..(v + 1) * n_u].to_vec();
let conditional =
PiecewiseConstant1D::new_with_bounds(row, domain.p_min.x(), domain.p_max.x());
PiecewiseConstant1D::new_with_bounds(&row, domain.p_min.x(), domain.p_max.x());
marginal_func.push(conditional.integral());
conditionals.push(conditional);
}
@ -846,37 +848,41 @@ impl PiecewiseConstant2D {
domain.p_max.y(),
);
Self {
conditionals,
marginal,
n_u: n_u.try_into().unwrap(),
n_v: n_v.try_into().unwrap(),
}
}
pub fn from_image(image: &Image) -> Self {
let res = image.resolution();
let n_u = res.x() as usize;
let n_v = res.y() as usize;
let mut data = Vec::with_capacity(n_u * n_v);
let mut data = gvec_with_capacity(n_u * n_v);
for v in 0..n_v {
for u in 0..n_u {
data.push(
image
.get_channels(Point2i::new(u as i32, v as i32))
.average(),
);
data.push(image.get_channels_average(Point2i::new(u as i32, v as i32)));
}
}
Self::from_slice(&data, n_u, n_v, Bounds2f::unit())
}
Self {
conditionals,
marginal,
n_u: n_u as u32,
n_v: n_v as u32,
}
PiecewiseConstant2D::from_slice(&data, n_u, n_v, Bounds2f::unit())
}
pub fn integral(&self) -> Float {
self.marginal.integral()
}
pub fn pdf(&self, p: Point2f) -> Float {
let u_offset = ((p.x() * self.n_u as Float) as usize).min(self.n_u as usize - 1);
let v_offset = ((p.y() * self.n_v as Float) as usize).min(self.n_v as usize - 1);
let conditional = unsafe { &*self.conditionals.as_ptr().add(v_offset) };
let func_val = unsafe { *conditional.func.as_ptr().add(u_offset) };
func_val / self.integral()
}
pub fn sample(&self, u: Point2f) -> (Point2f, Float, Point2i) {
let (d1, pdf1, off_y) = self.marginal.sample(u.y());
let (d0, pdf0, off_x) = self.conditionals[off_y].sample(u.x());
@ -890,7 +896,7 @@ impl PiecewiseConstant2D {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
pub struct SummedAreaTable {
pub sum: Array2D<f64>,
}
@ -964,7 +970,7 @@ impl SummedAreaTable {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
pub struct WindowedPiecewiseConstant2D {
pub sat: SummedAreaTable,
pub func: Array2D<Float>,
@ -989,7 +995,8 @@ impl WindowedPiecewiseConstant2D {
};
let nx = self.func.x_size();
let px_val = Self::sample_bisection(px, u.x(), b.p_min.x(), b.p_max.x(), nx);
let px_val =
Self::sample_bisection(px, u.x(), b.p_min.x(), b.p_max.x(), nx.try_into().unwrap());
let nx_f = nx as Float;
@ -1017,7 +1024,8 @@ impl WindowedPiecewiseConstant2D {
};
let ny = self.func.y_size();
let py_val = Self::sample_bisection(py, u.y(), b.p_min.y(), b.p_max.y(), ny);
let py_val =
Self::sample_bisection(py, u.y(), b.p_min.y(), b.p_max.y(), ny.try_into().unwrap());
let p = Point2f::new(px_val, py_val);
@ -1117,8 +1125,8 @@ impl AliasTable {
index: usize,
}
let mut under = Vec::with_capacity(n);
let mut over = Vec::with_capacity(n);
let mut under = gvec_with_capacity(n);
let mut over = gvec_with_capacity(n);
for (i, bin) in bins.iter().enumerate() {
let p_hat = (bin.p as f64) * (n as f64);
@ -1201,7 +1209,7 @@ impl AliasTable {
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Clone)]
pub struct PiecewiseLinear2D<const N: usize> {
pub size: Vector2i,
pub inv_patch_size: Vector2f,
@ -1233,7 +1241,8 @@ impl<const N: usize> PiecewiseLinear2D<N> {
let mut param_size = [0u32; N];
let mut param_strides = [0u32; N];
let owned_param_values: [Vec<Float>; N] = gpu_array_from_fn(|i| param_values[i].to_vec());
let owned_param_values: [GVec<Float>; N] =
core::array::from_fn(|i| gvec_from_slice(param_values[i]));
let mut slices: u32 = 1;
for i in (0..N).rev() {
@ -1244,16 +1253,23 @@ impl<const N: usize> PiecewiseLinear2D<N> {
}
let n_values = (x_size * y_size) as usize;
let mut new_data = vec![0.0; slices as usize * n_values];
let mut new_data = gvec_with_capacity(slices as usize * n_values);
new_data.resize(slices as usize * n_values, 0.0);
let mut marginal_cdf = if build_cdf {
vec![0.0; slices as usize * y_size as usize]
let mut v = gvec_with_capacity(slices as usize * y_size as usize);
v.resize(slices as usize * y_size as usize, 0.0);
v
} else {
Vec::new()
gvec()
};
let mut conditional_cdf = if build_cdf {
vec![0.0; slices as usize * n_values]
let mut v = gvec_with_capacity(slices as usize * n_values);
v.resize(slices as usize * n_values, 0.0);
v
} else {
Vec::new()
gvec()
};
let mut data_offset = 0;
@ -1314,7 +1330,6 @@ impl<const N: usize> PiecewiseLinear2D<N> {
inv_patch_size,
param_size,
param_strides,
storage,
data: new_data,
marginal_cdf,
conditional_cdf,
@ -1336,7 +1351,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
let conditional_offset = slice_offset * conditional_size;
let fetch_marginal = |idx: u32| {
self.lookup(
self.marginal_cdf,
&self.marginal_cdf,
marginal_offset + idx,
marginal_size,
&param_weights,
@ -1346,13 +1361,13 @@ impl<const N: usize> PiecewiseLinear2D<N> {
let marginal_cdf_row = fetch_marginal(row);
sample[1] -= marginal_cdf_row;
let r0 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_offset + (row + 1) * self.size.x() as u32 - 1,
conditional_size,
&param_weights,
);
let r1 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_offset + (row + 2) * self.size.x() as u32 - 1,
conditional_size,
&param_weights,
@ -1369,13 +1384,13 @@ impl<const N: usize> PiecewiseLinear2D<N> {
let conditional_row_offset = conditional_offset + row * self.size.x() as u32;
let fetch_conditional = |idx: u32| {
let v0 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_row_offset + idx,
conditional_size,
&param_weights,
);
let v1 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_row_offset + idx + self.size.x() as u32,
conditional_size,
&param_weights,
@ -1387,16 +1402,16 @@ impl<const N: usize> PiecewiseLinear2D<N> {
});
sample[0] -= fetch_conditional(col);
let offset = conditional_row_offset + col;
let v00 = self.lookup(self.data, offset, slice_size, &param_weights);
let v10 = self.lookup(self.data, offset + 1, slice_size, &param_weights);
let v00 = self.lookup(&self.data, offset, slice_size, &param_weights);
let v10 = self.lookup(&self.data, offset + 1, slice_size, &param_weights);
let v01 = self.lookup(
self.data,
&self.data,
offset + self.size.x() as u32,
slice_size,
&param_weights,
);
let v11 = self.lookup(
self.data,
&self.data,
offset + self.size.x() as u32 + 1,
slice_size,
&param_weights,
@ -1433,16 +1448,16 @@ impl<const N: usize> PiecewiseLinear2D<N> {
let frac = Point2f::new(p.x() - col as Float, p.y() - row as Float);
let slice_size = (self.size.x() * self.size.y()) as u32;
let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32;
let v00 = self.lookup(self.data, offset, slice_size, &param_weights);
let v10 = self.lookup(self.data, offset + 1, slice_size, &param_weights);
let v00 = self.lookup(&self.data, offset, slice_size, &param_weights);
let v10 = self.lookup(&self.data, offset + 1, slice_size, &param_weights);
let v01 = self.lookup(
self.data,
&self.data,
offset + self.size.x() as u32,
slice_size,
&param_weights,
);
let v11 = self.lookup(
self.data,
&self.data,
offset + self.size.x() as u32 + 1,
slice_size,
&param_weights,
@ -1456,26 +1471,26 @@ impl<const N: usize> PiecewiseLinear2D<N> {
u[0] = w1.x() * (c0 + 0.5 * w1.x() * (c1 - c0));
let conditional_row_offset = slice_offset * slice_size + (row * self.size.x()) as u32;
let v0 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_row_offset + col as u32,
slice_size,
&param_weights,
);
let v1 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_row_offset + col as u32 + self.size.x() as u32,
slice_size,
&param_weights,
);
u[0] += (1.0 - u.y()) * v0 + u.y() * v1;
let r0 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_row_offset + self.size.x() as u32 - 1,
slice_size,
&param_weights,
);
let r1 = self.lookup(
self.conditional_cdf,
&self.conditional_cdf,
conditional_row_offset + self.size.x() as u32 * 2 - 1,
slice_size,
&param_weights,
@ -1484,7 +1499,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
u[1] = w1.y() * (r0 + 0.5 * w1.y() * (r1 - r0));
let marginal_offset = slice_offset * self.size.y() as u32 + row as u32;
u[1] += self.lookup(
self.marginal_cdf,
&self.marginal_cdf,
marginal_offset,
self.size.y() as u32,
&param_weights,
@ -1508,16 +1523,16 @@ impl<const N: usize> PiecewiseLinear2D<N> {
let w0 = Point2f::new(1.0 - w1.x(), 1.0 - w1.y());
let slice_size = (self.size.x() * self.size.y()) as u32;
let offset = slice_offset * slice_size + (row * self.size.x() + col) as u32;
let v00 = self.lookup(self.data, offset, slice_size, &param_weights);
let v10 = self.lookup(self.data, offset + 1, slice_size, &param_weights);
let v00 = self.lookup(&self.data, offset, slice_size, &param_weights);
let v10 = self.lookup(&self.data, offset + 1, slice_size, &param_weights);
let v01 = self.lookup(
self.data,
&self.data,
offset + self.size.x() as u32,
slice_size,
&param_weights,
);
let v11 = self.lookup(
self.data,
&self.data,
offset + self.size.x() as u32 + 1,
slice_size,
&param_weights,
@ -1529,8 +1544,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
#[inline(always)]
fn get_param_value(&self, dim: usize, idx: usize) -> Float {
// Safety: Bounds checking against param_size ensures this is valid
unsafe { *self.param_values[dim].add(idx) }
unsafe { *self.param_values[dim].as_ptr().add(idx) }
}
fn get_slice_info(&self, params: [Float; N]) -> (u32, [(Float, Float); N]) {
@ -1570,7 +1584,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
fn lookup(
&self,
data: Ptr<Float>,
data: &GVec<Float>,
i0: u32,
size: u32,
param_weight: &[(Float, Float); N],
@ -1592,7 +1606,7 @@ impl<const N: usize> PiecewiseLinear2D<N> {
current_mask >>= 1;
}
let idx = (i0 + offset) as usize;
let val = unsafe { *data.add(idx) };
let val = unsafe { *data.as_ptr().add(idx) };
result += weight * val;
}
result

View file

@ -3,7 +3,6 @@ use crate::core::spectrum::spectrum_to_photometric;
use crate::core::texture::FloatTexture;
use crate::utils::{Arena, FileLoc, ParameterDictionary};
use anyhow::Result;
use shared::Float;
use shared::core::geometry::{Point3f, VectorLike};
use shared::core::light::{Light, LightBase, LightType};
use shared::core::medium::{Medium, MediumInterface};
@ -13,6 +12,29 @@ use shared::core::texture::SpectrumType;
use shared::lights::DistantLight;
use shared::spectra::RGBColorSpace;
use shared::utils::{Ptr, Transform};
use shared::Float;
trait CreateDistantLight {
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self;
}
impl CreateDistantLight for DistantLight {
fn new(render_from_light: Transform, le: Spectrum, scale: Float) -> Self {
let base = LightBase::new(
LightType::DeltaDirection,
render_from_light,
MediumInterface::empty(),
);
let lemit = lookup_spectrum(&le);
Self {
base,
lemit: Ptr::from(&lemit.device()),
scale,
scene_center: Point3f::default(),
scene_radius: 0.,
}
}
}
pub fn create(
render_from_light: Transform,

View file

@ -13,6 +13,35 @@ use shared::lights::PointLight;
use shared::spectra::RGBColorSpace;
use shared::{Float, PI, Ptr, Transform};
pub trait CreatePointLight {
fn new(
render_from_light: Transform,
medium_interface: MediumInterface,
le: Spectrum,
scale: Float,
arena: &Arena,
) -> Self;
}
impl CreatePointLight for PointLight {
fn new(
render_from_light: Transform,
medium_interface: MediumInterface,
le: Spectrum,
scale: Float,
arena: &Arena,
) -> Self {
let base = LightBase::new(
LightType::DeltaPosition,
render_from_light,
medium_interface,
);
let iemit = lookup_spectrum(&le);
let i = arena.alloc((*iemit).clone());
Self { base, scale, i }
}
}
pub fn create(
render_from_light: Transform,

View file

@ -13,7 +13,45 @@ use shared::lights::SpotLight;
use shared::spectra::RGBColorSpace;
use shared::utils::math::radians;
use shared::utils::{Ptr, Transform};
use shared::{Float, PI, Ptr, Transform};
use shared::{Float, Ptr, Transform, PI};
trait CreateSpotLight {
fn new(
render_from_light: Transform,
_medium_interface: MediumInterface,
le: Spectrum,
scale: Float,
cos_falloff_start: Float,
total_width: Float,
) -> Self;
}
impl CreateSpotLight for SpotLight {
fn new(
render_from_light: Transform,
_medium_interface: MediumInterface,
le: Spectrum,
scale: Float,
cos_falloff_start: Float,
total_width: Float,
) -> Self {
let base = LightBase::new(
LightType::DeltaPosition,
render_from_light,
MediumInterface::empty(),
);
let i = lookup_spectrum(&le);
let iemit = Ptr::from(&i.device());
Self {
base,
iemit,
scale,
cos_falloff_end: radians(total_width).cos(),
cos_falloff_start: radians(cos_falloff_start).cos(),
}
}
}
pub fn create(
render_from_light: Transform,

View file

@ -45,10 +45,10 @@ pub fn cie_d65() -> Spectrum {
pub fn get_spectra_context() -> StandardSpectra {
StandardSpectra {
x: CIE_X_DATA,
y: CIE_Y_DATA,
z: CIE_Z_DATA,
d65: CIE_D65_DATA,
x: Ptr::from(&*CIE_X_DATA),
y: Ptr::from(&*CIE_Y_DATA),
z: Ptr::from(&*CIE_Z_DATA),
d65: Ptr::from(&*CIE_D65_DATA),
}
}