1use crate::core::texture::*;
2
3#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
4pub enum CubeMapSide {
8 Top,
10 Bottom,
12 Right,
14 Left,
16 Front,
18 Back,
20}
21
22pub struct CubeMapSideIterator {
26 index: usize,
27}
28
29impl CubeMapSideIterator {
30 pub fn new() -> Self {
34 Self { index: 0 }
35 }
36}
37
38impl Default for CubeMapSideIterator {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl Iterator for CubeMapSideIterator {
45 type Item = CubeMapSide;
46 fn next(&mut self) -> Option<Self::Item> {
47 self.index += 1;
48 match self.index {
49 1 => Some(CubeMapSide::Right),
50 2 => Some(CubeMapSide::Left),
51 3 => Some(CubeMapSide::Top),
52 4 => Some(CubeMapSide::Bottom),
53 5 => Some(CubeMapSide::Front),
54 6 => Some(CubeMapSide::Back),
55 _ => None,
56 }
57 }
58}
59
60impl CubeMapSide {
61 pub fn iter() -> CubeMapSideIterator {
65 CubeMapSideIterator::new()
66 }
67
68 pub(in crate::core) fn to_const(self) -> u32 {
69 match self {
70 CubeMapSide::Right => crate::context::TEXTURE_CUBE_MAP_POSITIVE_X,
71 CubeMapSide::Left => crate::context::TEXTURE_CUBE_MAP_NEGATIVE_X,
72 CubeMapSide::Top => crate::context::TEXTURE_CUBE_MAP_POSITIVE_Y,
73 CubeMapSide::Bottom => crate::context::TEXTURE_CUBE_MAP_NEGATIVE_Y,
74 CubeMapSide::Front => crate::context::TEXTURE_CUBE_MAP_POSITIVE_Z,
75 CubeMapSide::Back => crate::context::TEXTURE_CUBE_MAP_NEGATIVE_Z,
76 }
77 }
78
79 pub fn up(&self) -> Vec3 {
81 match self {
82 CubeMapSide::Right => vec3(0.0, -1.0, 0.0),
83 CubeMapSide::Left => vec3(0.0, -1.0, 0.0),
84 CubeMapSide::Top => vec3(0.0, 0.0, 1.0),
85 CubeMapSide::Bottom => vec3(0.0, 0.0, -1.0),
86 CubeMapSide::Front => vec3(0.0, -1.0, 0.0),
87 CubeMapSide::Back => vec3(0.0, -1.0, 0.0),
88 }
89 }
90
91 pub fn direction(&self) -> Vec3 {
93 match self {
94 CubeMapSide::Right => vec3(1.0, 0.0, 0.0),
95 CubeMapSide::Left => vec3(-1.0, 0.0, 0.0),
96 CubeMapSide::Top => vec3(0.0, 1.0, 0.0),
97 CubeMapSide::Bottom => vec3(0.0, -1.0, 0.0),
98 CubeMapSide::Front => vec3(0.0, 0.0, 1.0),
99 CubeMapSide::Back => vec3(0.0, 0.0, -1.0),
100 }
101 }
102}
103
104pub struct TextureCubeMap {
108 context: Context,
109 id: crate::context::Texture,
110 width: u32,
111 height: u32,
112 number_of_mip_maps: u32,
113 data_byte_size: usize,
114}
115
116impl TextureCubeMap {
117 pub fn new(
124 context: &Context,
125 right: &CpuTexture,
126 left: &CpuTexture,
127 top: &CpuTexture,
128 bottom: &CpuTexture,
129 front: &CpuTexture,
130 back: &CpuTexture,
131 ) -> Self {
132 match &front.data {
133 TextureData::RU8(front_data) => Self::new_with_data(
134 context,
135 front,
136 right.wrap_s,
137 ru8_data(right),
138 ru8_data(left),
139 ru8_data(top),
140 ru8_data(bottom),
141 front_data,
142 ru8_data(back),
143 ),
144 TextureData::RgU8(front_data) => Self::new_with_data(
145 context,
146 front,
147 right.wrap_s,
148 rgu8_data(right),
149 rgu8_data(left),
150 rgu8_data(top),
151 rgu8_data(bottom),
152 front_data,
153 rgu8_data(back),
154 ),
155 TextureData::RgbU8(front_data) => Self::new_with_data(
156 context,
157 front,
158 right.wrap_s,
159 rgbu8_data(right),
160 rgbu8_data(left),
161 rgbu8_data(top),
162 rgbu8_data(bottom),
163 front_data,
164 rgbu8_data(back),
165 ),
166 TextureData::RgbaU8(front_data) => Self::new_with_data(
167 context,
168 front,
169 right.wrap_s,
170 rgbau8_data(right),
171 rgbau8_data(left),
172 rgbau8_data(top),
173 rgbau8_data(bottom),
174 front_data,
175 rgbau8_data(back),
176 ),
177 TextureData::RF16(front_data) => Self::new_with_data(
178 context,
179 front,
180 right.wrap_s,
181 rf16_data(right),
182 rf16_data(left),
183 rf16_data(top),
184 rf16_data(bottom),
185 front_data,
186 rf16_data(back),
187 ),
188 TextureData::RgF16(front_data) => Self::new_with_data(
189 context,
190 front,
191 right.wrap_s,
192 rgf16_data(right),
193 rgf16_data(left),
194 rgf16_data(top),
195 rgf16_data(bottom),
196 front_data,
197 rgf16_data(back),
198 ),
199 TextureData::RgbF16(front_data) => Self::new_with_data(
200 context,
201 front,
202 right.wrap_s,
203 rgbf16_data(right),
204 rgbf16_data(left),
205 rgbf16_data(top),
206 rgbf16_data(bottom),
207 front_data,
208 rgbf16_data(back),
209 ),
210 TextureData::RgbaF16(front_data) => Self::new_with_data(
211 context,
212 front,
213 right.wrap_s,
214 rgbaf16_data(right),
215 rgbaf16_data(left),
216 rgbaf16_data(top),
217 rgbaf16_data(bottom),
218 front_data,
219 rgbaf16_data(back),
220 ),
221 TextureData::RF32(front_data) => Self::new_with_data(
222 context,
223 front,
224 right.wrap_s,
225 rf32_data(right),
226 rf32_data(left),
227 rf32_data(top),
228 rf32_data(bottom),
229 front_data,
230 rf32_data(back),
231 ),
232 TextureData::RgF32(front_data) => Self::new_with_data(
233 context,
234 front,
235 right.wrap_s,
236 rgf32_data(right),
237 rgf32_data(left),
238 rgf32_data(top),
239 rgf32_data(bottom),
240 front_data,
241 rgf32_data(back),
242 ),
243 TextureData::RgbF32(front_data) => Self::new_with_data(
244 context,
245 front,
246 right.wrap_s,
247 rgbf32_data(right),
248 rgbf32_data(left),
249 rgbf32_data(top),
250 rgbf32_data(bottom),
251 front_data,
252 rgbf32_data(back),
253 ),
254 TextureData::RgbaF32(front_data) => Self::new_with_data(
255 context,
256 front,
257 right.wrap_s,
258 rgbaf32_data(right),
259 rgbaf32_data(left),
260 rgbaf32_data(top),
261 rgbaf32_data(bottom),
262 front_data,
263 rgbaf32_data(back),
264 ),
265 }
266 }
267
268 fn new_with_data<T: TextureDataType>(
269 context: &Context,
270 cpu_texture: &CpuTexture,
271 wrap_r: Wrapping,
272 right_data: &[T],
273 left_data: &[T],
274 top_data: &[T],
275 bottom_data: &[T],
276 front_data: &[T],
277 back_data: &[T],
278 ) -> Self {
279 let mut texture = Self::new_empty::<T>(
280 context,
281 cpu_texture.width,
282 cpu_texture.height,
283 cpu_texture.min_filter,
284 cpu_texture.mag_filter,
285 cpu_texture.mipmap,
286 cpu_texture.wrap_s,
287 cpu_texture.wrap_t,
288 wrap_r,
289 );
290 texture.fill(
291 right_data,
292 left_data,
293 top_data,
294 bottom_data,
295 front_data,
296 back_data,
297 );
298 texture
299 }
300
301 pub fn new_empty<T: TextureDataType>(
307 context: &Context,
308 width: u32,
309 height: u32,
310 min_filter: Interpolation,
311 mag_filter: Interpolation,
312 mipmap: Option<Mipmap>,
313 wrap_s: Wrapping,
314 wrap_t: Wrapping,
315 wrap_r: Wrapping,
316 ) -> Self {
317 let id = generate(context);
318 let number_of_mip_maps = calculate_number_of_mip_maps::<T>(mipmap, width, height, None);
319 let texture = Self {
320 context: context.clone(),
321 id,
322 width,
323 height,
324 number_of_mip_maps,
325 data_byte_size: std::mem::size_of::<T>(),
326 };
327 texture.bind();
328 set_parameters(
329 context,
330 crate::context::TEXTURE_CUBE_MAP,
331 min_filter,
332 mag_filter,
333 if number_of_mip_maps == 1 {
334 None
335 } else {
336 mipmap
337 },
338 wrap_s,
339 wrap_t,
340 Some(wrap_r),
341 );
342 unsafe {
343 context.tex_storage_2d(
344 crate::context::TEXTURE_CUBE_MAP,
345 number_of_mip_maps as i32,
346 T::internal_format(),
347 width as i32,
348 height as i32,
349 );
350 }
351 texture.generate_mip_maps();
352 texture
353 }
354
355 pub fn fill<T: TextureDataType>(
363 &mut self,
364 right_data: &[T],
365 left_data: &[T],
366 top_data: &[T],
367 bottom_data: &[T],
368 front_data: &[T],
369 back_data: &[T],
370 ) {
371 check_data_length::<T>(
372 self.width,
373 self.height,
374 1,
375 self.data_byte_size,
376 right_data.len(),
377 );
378 check_data_length::<T>(
379 self.width,
380 self.height,
381 1,
382 self.data_byte_size,
383 left_data.len(),
384 );
385 check_data_length::<T>(
386 self.width,
387 self.height,
388 1,
389 self.data_byte_size,
390 top_data.len(),
391 );
392 check_data_length::<T>(
393 self.width,
394 self.height,
395 1,
396 self.data_byte_size,
397 bottom_data.len(),
398 );
399 check_data_length::<T>(
400 self.width,
401 self.height,
402 1,
403 self.data_byte_size,
404 front_data.len(),
405 );
406 check_data_length::<T>(
407 self.width,
408 self.height,
409 1,
410 self.data_byte_size,
411 back_data.len(),
412 );
413 self.bind();
414 for i in 0..6 {
415 let data = match i {
416 0 => right_data,
417 1 => left_data,
418 2 => top_data,
419 3 => bottom_data,
420 4 => front_data,
421 5 => back_data,
422 _ => unreachable!(),
423 };
424 unsafe {
425 self.context.tex_sub_image_2d(
426 crate::context::TEXTURE_CUBE_MAP_POSITIVE_X + i as u32,
427 0,
428 0,
429 0,
430 self.width as i32,
431 self.height as i32,
432 format_from_data_type::<T>(),
433 T::data_type(),
434 crate::context::PixelUnpackData::Slice(to_byte_slice(data)),
435 );
436 }
437 }
438 self.generate_mip_maps();
439 }
440
441 pub fn new_from_equirectangular<T: PrimitiveDataType + TextureDataType>(
445 context: &Context,
446 cpu_texture: &CpuTexture,
447 ) -> Self {
448 let texture_size = cpu_texture.width / 4;
449 let mut texture = Self::new_empty::<[T; 4]>(
450 context,
451 texture_size,
452 texture_size,
453 Interpolation::Linear,
454 Interpolation::Linear,
455 Some(Mipmap::default()),
456 Wrapping::ClampToEdge,
457 Wrapping::ClampToEdge,
458 Wrapping::ClampToEdge,
459 );
460
461 {
462 let map = Texture2D::new(context, cpu_texture);
463 let fragment_shader_source = "
464 uniform sampler2D equirectangularMap;
465 uniform vec3 direction;
466 uniform vec3 up;
467
468 in vec2 uvs;
469
470 layout (location = 0) out vec4 outColor;
471
472 void main()
473 {
474 vec3 right = cross(direction, up);
475 vec3 dir = normalize(up * (uvs.y - 0.5) * 2.0 + right * (uvs.x - 0.5) * 2.0 + direction);
476 vec2 uv = vec2(0.1591 * atan(dir.z, dir.x) + 0.5, 0.3183 * asin(dir.y) + 0.5);
477 outColor = texture(equirectangularMap, uv);
478 }";
479
480 let program = Program::from_source(
481 context,
482 full_screen_vertex_shader_source(),
483 &fragment_shader_source,
484 )
485 .expect("Failed compiling shader");
486
487 for side in CubeMapSide::iter() {
488 let viewport = Viewport::new_at_origo(texture_size, texture_size);
489 texture
490 .as_color_target(&[side], None)
491 .clear(ClearState::default())
492 .write::<CoreError>(|| {
493 program.use_texture("equirectangularMap", &map);
494 program.use_uniform("direction", side.direction());
495 program.use_uniform("up", side.up());
496 full_screen_draw(context, &program, RenderStates::default(), viewport);
497 Ok(())
498 })
499 .unwrap();
500 }
501 }
502 texture
503 }
504
505 pub fn as_color_target<'a>(
514 &'a mut self,
515 sides: &'a [CubeMapSide],
516 mip_level: Option<u32>,
517 ) -> ColorTarget<'a> {
518 ColorTarget::new_texture_cube_map(&self.context, self, sides, mip_level)
519 }
520
521 pub fn width(&self) -> u32 {
523 self.width
524 }
525
526 pub fn height(&self) -> u32 {
528 self.height
529 }
530
531 pub fn number_of_mip_maps(&self) -> u32 {
533 self.number_of_mip_maps
534 }
535
536 pub(in crate::core) fn generate_mip_maps(&self) {
537 if self.number_of_mip_maps > 1 {
538 self.bind();
539 unsafe {
540 self.context
541 .generate_mipmap(crate::context::TEXTURE_CUBE_MAP);
542 }
543 }
544 }
545
546 pub(in crate::core) fn bind_as_color_target(
547 &self,
548 side: CubeMapSide,
549 channel: u32,
550 mip_level: u32,
551 ) {
552 unsafe {
553 self.context.framebuffer_texture_2d(
554 crate::context::DRAW_FRAMEBUFFER,
555 crate::context::COLOR_ATTACHMENT0 + channel,
556 side.to_const(),
557 Some(self.id),
558 mip_level as i32,
559 );
560 }
561 }
562
563 pub(in crate::core) fn bind(&self) {
564 unsafe {
565 self.context
566 .bind_texture(crate::context::TEXTURE_CUBE_MAP, Some(self.id));
567 }
568 }
569}
570
571impl Drop for TextureCubeMap {
572 fn drop(&mut self) {
573 unsafe {
574 self.context.delete_texture(self.id);
575 }
576 }
577}