1use crate::core::base::*;
2use crate::core::error::*;
3use crate::core::geometry::*;
4use crate::core::interaction::*;
5use crate::core::param_set::*;
6use crate::core::transform::*;
7
8pub trait TextureMapping2D {
9 fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f);
10}
11
12pub struct UVMapping2D {
13 pub su: Float,
14 pub sv: Float,
15 pub du: Float,
16 pub dv: Float,
17}
18
19impl UVMapping2D {
20 pub fn new(su: Float, sv: Float, du: Float, dv: Float) -> Self {
21 UVMapping2D { su, sv, du, dv }
22 }
23}
24
25impl TextureMapping2D for UVMapping2D {
26 fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
27 let dstdx = Vector2f::new(self.su * si.dudx, self.sv * si.dvdx);
29 let dstdy = Vector2f::new(self.su * si.dudy, self.sv * si.dvdy);
30 let st = Point2f::new(self.su * si.uv[0] + self.du, self.sv * si.uv[1] + self.dv);
31 return (st, dstdx, dstdy);
32 }
33}
34
35pub struct SphericalMapping2D {
36 world_to_texture: Transform,
37}
38
39impl SphericalMapping2D {
40 pub fn new(world_to_texture: &Transform) -> Self {
41 SphericalMapping2D {
42 world_to_texture: *world_to_texture,
43 }
44 }
45 fn sphere(&self, p: &Point3f) -> Point2f {
46 let vec =
47 (self.world_to_texture.transform_point(p) - Point3f::new(0.0, 0.0, 0.0)).normalize();
48 let theta = spherical_theta(&vec);
49 let phi = spherical_phi(&vec);
50 return Point2f::new(theta * INV_PI, phi * INV_2_PI);
51 }
52}
53
54impl TextureMapping2D for SphericalMapping2D {
55 fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
56 let st = self.sphere(&si.p);
57 const DELTA: Float = 0.1;
59 let st_delta_x = self.sphere(&(si.p + DELTA * si.dpdx));
60 let mut dstdx = (st_delta_x - st) * (1.0 / DELTA);
61 let st_delta_y = self.sphere(&(si.p + DELTA * si.dpdy));
62 let mut dstdy = (st_delta_y - st) * (1.0 / DELTA);
63
64 if dstdx[1] > 0.5 {
66 dstdx[1] = 1.0 - dstdx[1];
67 } else if dstdx[1] < -0.5 {
68 dstdx[1] = -(dstdx[1] + 1.0);
69 }
70
71 if dstdy[1] > 0.5 {
72 dstdy[1] = 1.0 - dstdy[1];
73 } else if dstdy[1] < -0.5 {
74 dstdy[1] = -(dstdy[1] + 1.0);
75 }
76
77 return (st, dstdx, dstdy);
78 }
79}
80
81pub struct CylindricalMapping2D {
82 world_to_texture: Transform,
83}
84
85impl CylindricalMapping2D {
86 pub fn new(world_to_texture: &Transform) -> Self {
87 CylindricalMapping2D {
88 world_to_texture: *world_to_texture,
89 }
90 }
91 fn cylinder(&self, p: &Point3f) -> Point2f {
92 let vec =
93 (self.world_to_texture.transform_point(p) - Point3f::new(0.0, 0.0, 0.0)).normalize();
94 return Point2f::new((PI + Float::atan2(vec.y, vec.x)) * INV_2_PI, vec.z);
95 }
96}
97
98impl TextureMapping2D for CylindricalMapping2D {
99 fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
100 let st = self.cylinder(&si.p);
101 const DELTA: Float = 0.1;
103 let st_delta_x = self.cylinder(&(si.p + DELTA * si.dpdx));
104 let mut dstdx = (st_delta_x - st) * (1.0 / DELTA);
105 let st_delta_y = self.cylinder(&(si.p + DELTA * si.dpdy));
106 let mut dstdy = (st_delta_y - st) * (1.0 / DELTA);
107
108 if dstdx[1] > 0.5 {
110 dstdx[1] = 1.0 - dstdx[1];
111 } else if dstdx[1] < -0.5 {
112 dstdx[1] = -(dstdx[1] + 1.0);
113 }
114
115 if dstdy[1] > 0.5 {
116 dstdy[1] = 1.0 - dstdy[1];
117 } else if dstdy[1] < -0.5 {
118 dstdy[1] = -(dstdy[1] + 1.0);
119 }
120
121 return (st, dstdx, dstdy);
122 }
123}
124
125pub struct PlanarMapping2D {
126 vs: Vector3f,
127 vt: Vector3f,
128 ds: Float,
129 dt: Float,
130}
131
132impl PlanarMapping2D {
133 pub fn new(vs: &Vector3f, vt: &Vector3f, ds: Float, dt: Float) -> Self {
134 PlanarMapping2D {
135 vs: *vs,
136 vt: *vt,
137 ds,
138 dt,
139 }
140 }
141}
142
143impl TextureMapping2D for PlanarMapping2D {
144 fn map(&self, si: &SurfaceInteraction) -> (Point2f, Vector2f, Vector2f) {
145 let vec = si.p;
146
147 let st = Vector2f::new(
148 self.ds + Vector3f::dot(&vec, &self.vs),
149 self.dt + Vector3f::dot(&vec, &self.vt),
150 );
151
152 let dstdx = Vector2f::new(
153 Vector3f::dot(&si.dpdx, &self.vs),
154 Vector3f::dot(&si.dpdx, &self.vt),
155 );
156
157 let dstdy = Vector2f::new(
158 Vector3f::dot(&si.dpdy, &self.vs),
159 Vector3f::dot(&si.dpdy, &self.vt),
160 );
161
162 return (st, dstdx, dstdy);
163 }
164}
165
166pub fn create_texture_mapping2d(
167 tex2world: &Transform,
168 tp: &TextureParams,
169) -> Result<Box<dyn TextureMapping2D>, PbrtError> {
170 let mapping = tp.find_string("mapping", "uv");
171 match mapping.as_ref() {
172 "uv" => {
173 let su = tp.find_float("uscale", 1.0);
174 let sv = tp.find_float("vscale", 1.0);
175 let du = tp.find_float("udelta", 0.0);
176 let dv = tp.find_float("vdelta", 0.0);
177 return Ok(Box::new(UVMapping2D::new(su, sv, du, dv)));
178 }
179 "spherical" => {
180 let it = tex2world.inverse();
181 return Ok(Box::new(SphericalMapping2D::new(&it)));
182 }
183 "cylindrical" => {
184 let it = tex2world.inverse();
185 return Ok(Box::new(CylindricalMapping2D::new(&it)));
186 }
187 "planar" => {
188 let v1 = tp.find_vector3f("v1", &Vector3f::new(1.0, 0.0, 0.0));
189 let v2 = tp.find_vector3f("v2", &Vector3f::new(0.0, 1.0, 0.0));
190 let du = tp.find_float("udelta", 0.0);
191 let dv = tp.find_float("vdelta", 0.0);
192 return Ok(Box::new(PlanarMapping2D::new(&v1, &v2, du, dv)));
193 }
194 _ => {
195 let msg = format!("2D texture mapping \"{}\" unknown", mapping);
196 return Err(PbrtError::error(&msg));
197 }
198 }
199}