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 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 unsafe {
318 Self::new_unchecked::<T>(
319 context,
320 width,
321 height,
322 min_filter,
323 mag_filter,
324 mipmap,
325 wrap_s,
326 wrap_t,
327 wrap_r,
328 |texture| {
329 context.tex_storage_2d(
330 crate::context::TEXTURE_CUBE_MAP,
331 texture.number_of_mip_maps() as i32,
332 T::internal_format(),
333 width as i32,
334 height as i32,
335 )
336 },
337 )
338 }
339 }
340
341 pub fn fill<T: TextureDataType>(
349 &self,
350 right_data: &[T],
351 left_data: &[T],
352 top_data: &[T],
353 bottom_data: &[T],
354 front_data: &[T],
355 back_data: &[T],
356 ) {
357 check_data_length::<T>(
358 self.width,
359 self.height,
360 1,
361 self.data_byte_size,
362 right_data.len(),
363 );
364 check_data_length::<T>(
365 self.width,
366 self.height,
367 1,
368 self.data_byte_size,
369 left_data.len(),
370 );
371 check_data_length::<T>(
372 self.width,
373 self.height,
374 1,
375 self.data_byte_size,
376 top_data.len(),
377 );
378 check_data_length::<T>(
379 self.width,
380 self.height,
381 1,
382 self.data_byte_size,
383 bottom_data.len(),
384 );
385 check_data_length::<T>(
386 self.width,
387 self.height,
388 1,
389 self.data_byte_size,
390 front_data.len(),
391 );
392 check_data_length::<T>(
393 self.width,
394 self.height,
395 1,
396 self.data_byte_size,
397 back_data.len(),
398 );
399 self.bind();
400 for i in 0..6 {
401 let data = match i {
402 0 => right_data,
403 1 => left_data,
404 2 => top_data,
405 3 => bottom_data,
406 4 => front_data,
407 5 => back_data,
408 _ => unreachable!(),
409 };
410 unsafe {
411 self.context.tex_sub_image_2d(
412 crate::context::TEXTURE_CUBE_MAP_POSITIVE_X + i as u32,
413 0,
414 0,
415 0,
416 self.width as i32,
417 self.height as i32,
418 format_from_data_type::<T>(),
419 T::data_type(),
420 crate::context::PixelUnpackData::Slice(Some(to_byte_slice(data))),
421 );
422 }
423 }
424 self.generate_mip_maps();
425 }
426
427 pub fn new_from_equirectangular<T: PrimitiveDataType + TextureDataType>(
431 context: &Context,
432 cpu_texture: &CpuTexture,
433 ) -> Self {
434 let texture_size = cpu_texture.width / 4;
435 let texture = Self::new_empty::<[T; 4]>(
436 context,
437 texture_size,
438 texture_size,
439 Interpolation::Linear,
440 Interpolation::Linear,
441 Some(Mipmap::default()),
442 Wrapping::ClampToEdge,
443 Wrapping::ClampToEdge,
444 Wrapping::ClampToEdge,
445 );
446
447 {
448 let map = Texture2D::new(context, cpu_texture);
449 let fragment_shader_source = "
450 uniform sampler2D equirectangularMap;
451 uniform vec3 direction;
452 uniform vec3 up;
453
454 in vec2 uvs;
455
456 layout (location = 0) out vec4 outColor;
457
458 void main()
459 {
460 vec3 right = cross(direction, up);
461 vec3 dir = normalize(up * (uvs.y - 0.5) * 2.0 + right * (uvs.x - 0.5) * 2.0 + direction);
462 vec2 uv = vec2(0.1591 * atan(dir.z, dir.x) + 0.5, 0.3183 * asin(dir.y) + 0.5);
463 outColor = texture(equirectangularMap, uv);
464 }";
465
466 let program = Program::from_source(
467 context,
468 full_screen_vertex_shader_source(),
469 fragment_shader_source,
470 )
471 .expect("Failed compiling shader");
472
473 for side in CubeMapSide::iter() {
474 let viewport = Viewport::new_at_origo(texture_size, texture_size);
475 texture
476 .as_color_target(&[side], None)
477 .clear(ClearState::default())
478 .write::<CoreError>(|| {
479 program.use_texture("equirectangularMap", &map);
480 program.use_uniform("direction", side.direction());
481 program.use_uniform("up", side.up());
482 full_screen_draw(context, &program, RenderStates::default(), viewport);
483 Ok(())
484 })
485 .unwrap();
486 }
487 }
488 texture
489 }
490
491 pub fn as_color_target<'a>(
500 &'a self,
501 sides: &'a [CubeMapSide],
502 mip_level: Option<u32>,
503 ) -> ColorTarget<'a> {
504 ColorTarget::new_texture_cube_map(&self.context, self, sides, mip_level)
505 }
506
507 pub fn width(&self) -> u32 {
509 self.width
510 }
511
512 pub fn height(&self) -> u32 {
514 self.height
515 }
516
517 pub fn number_of_mip_maps(&self) -> u32 {
519 self.number_of_mip_maps
520 }
521
522 pub(in crate::core) fn generate_mip_maps(&self) {
523 if self.number_of_mip_maps > 1 {
524 self.bind();
525 unsafe {
526 self.context
527 .generate_mipmap(crate::context::TEXTURE_CUBE_MAP);
528 }
529 }
530 }
531
532 pub(in crate::core) fn bind_as_color_target(
533 &self,
534 side: CubeMapSide,
535 channel: u32,
536 mip_level: u32,
537 ) {
538 unsafe {
539 self.context.framebuffer_texture_2d(
540 crate::context::DRAW_FRAMEBUFFER,
541 crate::context::COLOR_ATTACHMENT0 + channel,
542 side.to_const(),
543 Some(self.id),
544 mip_level as i32,
545 );
546 }
547 }
548
549 pub(in crate::core) fn bind(&self) {
550 unsafe {
551 self.context
552 .bind_texture(crate::context::TEXTURE_CUBE_MAP, Some(self.id));
553 }
554 }
555
556 pub unsafe fn new_unchecked<T: TextureDataType>(
567 context: &Context,
568 width: u32,
569 height: u32,
570 min_filter: Interpolation,
571 mag_filter: Interpolation,
572 mipmap: Option<Mipmap>,
573 wrap_s: Wrapping,
574 wrap_t: Wrapping,
575 wrap_r: Wrapping,
576 callback: impl FnOnce(&Self),
577 ) -> Self {
578 let id = generate(context);
579 let number_of_mip_maps = calculate_number_of_mip_maps::<T>(mipmap, width, height, None);
580 let texture = Self {
581 context: context.clone(),
582 id,
583 width,
584 height,
585 number_of_mip_maps,
586 data_byte_size: std::mem::size_of::<T>(),
587 };
588 texture.bind();
589 set_parameters(
590 context,
591 crate::context::TEXTURE_CUBE_MAP,
592 min_filter,
593 mag_filter,
594 if number_of_mip_maps == 1 {
595 None
596 } else {
597 mipmap
598 },
599 wrap_s,
600 wrap_t,
601 Some(wrap_r),
602 );
603 callback(&texture);
604 texture.generate_mip_maps();
605 texture
606 }
607}
608
609impl Drop for TextureCubeMap {
610 fn drop(&mut self) {
611 unsafe {
612 self.context.delete_texture(self.id);
613 }
614 }
615}