1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
//! Exposes the OpenGL [`Texture`](struct.Texture.html) family of objects and
//! related types.

use std::marker::PhantomData;
use gl;
use gl::types::*;
use types::GLObject;

/// A type of OpenGL texture.
///
/// OpenGL supports a multitude of different types of textures. The simplest
/// of these is a [`Texture2d`](type.Texture2d.html), which is, as the name
/// suggests, a flat, 2-dimensional image. Textures serve two primary purposes
/// in OpenGL: to be used in a [`Shader`](../shader/struct.Shader.html) to
/// be used while drawing (such as to paint a polygon with an image), or
/// to be used as an attachment for a [`Framebuffer`]
/// (../framebuffer/struct.Framebuffer.html), which can be used for
/// postprocessing effects (such as applying a gaussian blur to a scene).
///
/// In glitter, the `Texture` type has a generic type parameter, which is used
/// to represent, at the type level, what type of image data a specific
/// texture contains. For simplicity, the [`Texture2d`](type.Texture2d.html)
/// and [`TextureCubeMap`](type.TextureCubeMap.html) type aliases are provided.
///
/// All textures will be automatically deleted after going out of scope.
///
/// For details about the different types of textures
/// read the documentation for the [`TextureBindingTarget`]
/// (enum.TextureBindingTarget.html) enum. For more details about the
/// generic type parameter of the `Texture` type, read the documentation for
/// the [`TextureType`](trait.TextureType.html) trait.
///
/// # See also
/// [`gl.build_texture_2d`](trait.ContextTextureBuilderExt.html#method.build_texture_2d):
/// Build a new [`Texture2d`](type.Texture2d.html).
///
/// [`gl.gen_texture`](../context/texture_context/trait.ContextTextureExt.html#method.gen_texture):
/// Create a new, uninitialized texture. Note that this is currently the only
/// way to create textures that aren't 2D.
///
/// [`context::texture_units`](../context/texture_units/index.html): The module
/// with details details about binding a texture in a context.
pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}

/// An OpenGL texture with 2-dimensional image data.
///
/// See the documentation for [`Texture`](struct.Texture.html) for
/// more details about textures in glitter, and [`TextureBindingTarget`]
/// (enum.TextureBindingTarget) for details about the different types
/// of textures.
pub type Texture2d = Texture<Tx2d>;

/// An OpenGL texture used to hold a cubemap texture, made up of 6
/// 2-dimensional images (one for each face of a cube).
///
/// See the documentation for [`Texture`](struct.Texture.html) for
/// more details about textures in glitter, and [`TextureBindingTarget`]
/// (enum.TextureBindingTarget) for details about the different types
/// of textures.
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl<T: TextureType> GLObject for Texture<T> {
    type Id = GLuint;

    unsafe fn from_raw(id: Self::Id) -> Self {
        Texture {
            gl_id: id,
            phantom: PhantomData
        }
    }

    fn id(&self) -> Self::Id {
        self.gl_id
    }
}



/// A trait implemented for types that are used to represent all of the
/// possible 2D images that make up a specific implementation of
/// [`TextureType`](trait.TextureType.html). For more details, read the
/// [`TextureType`](trait.TextureType.html) documentation.
pub trait ImageTargetType {
    /// Get the raw OpenGL enum value for an image target.
    fn gl_enum(&self) -> GLenum;
}

/// A trait implemented for a type that represent a type of texture (such
/// as 2D textures or cube map textures).  For example, [`TxCubeMap`]
/// (struct.TxCubeMap.html) is a type that implements `TextureType`, and
/// it represents cube map textures.
pub trait TextureType {
    /// The type that is used to indicate all of the possible target 2D images
    /// for this type of texture. The associated `ImageTargetType` in the impl
    /// for [`TxCubeMap`](struct.TxCubeMap.html), for example, is
    /// [`TxCubeMapImageTarget`] (enum.TxCubeMapImageTarget.html), which is an
    /// enum with six variants, one for each of the six 2-dimensional images
    /// that make up a cube map.
    type ImageTargetType: ImageTargetType;

    /// The actual variant that represents this type of texture. The
    /// `target()` method impl for [`TxCubeMap`](struct.TxCubeMap.html), for
    /// example, returns `TextureBindingTarget::CubeMap`.
    fn target() -> TextureBindingTarget;
}

/// The [`TextureType`](trait.TextureType.html) for 2-dimensional textures.
pub struct Tx2d;

/// The possible image targets for `GL_TEXTURE_2D` (only one variant,
/// since this *is* the 2D texture).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tx2dImageTarget {
    /// The only possible target for a 2-dimensional texture.
    Texture2d = gl::TEXTURE_2D as isize
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

/// The [`TextureType`](trait.TextureType.html) for cubemap textures.
pub struct TxCubeMap;

gl_enum! {
    /// The possible 2D image targets for a cubemap texture.
    pub gl_enum TxCubeMapImageTarget {
        /// The positive-X image target face of a cubemap.
        pub const CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,

        /// The negative-X image target face of a cubemap.
        pub const CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,

        /// The positive-Y image target face of a cubemap.
        pub const CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,

        /// The negative-Y image target face of a cubemap.
        pub const CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,

        /// The positive-Z image target face of a cubemap.
        pub const CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,

        /// The negative-Z image target face of a cubemap.
        pub const CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}



/// Represents all of the possible types of OpenGL textures.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureBindingTarget {
    /// A 2-dimensional texture, which can be thought of as a 2D grid of colors.
    Texture2d = gl::TEXTURE_2D as isize,

    /// A cubemap texture, which is a texture made up of six 2-dimensional
    /// images, each of which represent a face of a cube. This type of texture
    /// is especially useful for skyboxes.
    TextureCubeMap = gl::TEXTURE_CUBE_MAP as isize
}

impl TextureBindingTarget {
    /// Convert a `TextureBindingTarget` into a raw OpenGL enum value.
    pub fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

/// This is a unit type that is used to be coerced into select enum variants.
///
/// In OpenGL, the `GL_TEXTURE_2D` enum value is used for multiple purposes. In
/// particular, it can be used as *both* a valid argument for [`glBindTexture`]
/// (http://docs.gl/es2/glBindTexture) (where it represents a possible texture
/// binding, which maps to [`TextureBindingTarget::Texture2d`]
/// (enum.TextureBindingTarget.html) in glitter) *and* for [`glTexImage2D`]
/// (http://docs.gl/es2/glTexImage2D) (where it represents the possible
/// *2D images* that make up a particular texture type, which maps to
/// the [`ImageTargetType`](trait.ImageTargetType.html) trait and
/// the [`Tx2dImageTarget`](enum.Tx2dImageTarget.html) enum in glitter).
///
/// Thus, in order to be able to use the constant `glitter::TEXTURE_2D` as an
/// argument to the [`image_2d`](../context/texture_context/trait.ContextTextureExt.html#method.image_2d)
/// method, `glitter::TEXTURE_2D` must be of type [`TextureBindingTarget`]
/// (enum.TextureBindingTarget.html). But, in order to be able to it as an
/// argument to a method that takes [`Tx2dImageTarget`]
/// (enum.Tx2dImageTarget.html), it must be of type [`Tx2dImageTarget`]
// / (enum.Tx2dImageTarget.html). Obviously, glitter::TEXTURE_2D` can't be
/// *both* types, so this type exists solely to be the type of
/// `glitter::TEXTURE_2D`, and it implements `From` so that it can be converted
/// to either type freely.

// HACK: Done to allow glitter::TEXTURE_2D as both a
//       TextureBindingTarget and Tx2dImageTarget
pub struct VariantTexture2d;

impl From<VariantTexture2d> for TextureBindingTarget {
    fn from(_: VariantTexture2d) -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

impl From<VariantTexture2d> for Tx2dImageTarget {
    fn from(_: VariantTexture2d) -> Tx2dImageTarget {
        Tx2dImageTarget::Texture2d
    }
}

impl ImageTargetType for VariantTexture2d {
    fn gl_enum(&self) -> GLenum {
        gl::TEXTURE_2D
    }
}

/// This constant is designed to be used in glitter wherever the constant
/// `GL_TEXTURE_CUBE_MAP` is used in plain OpenGL code.
pub const TEXTURE_CUBE_MAP : TextureBindingTarget =
    TextureBindingTarget::TextureCubeMap;


/// This constant is designed to be used in glitter wherever the constant
/// `GL_TEXTURE_2D` is used in plain OpenGL code.
///
/// See the documentation for [`VariantTexture2d`](struct.VariantTexture2d.html)
/// for details about how this works.
pub const TEXTURE_2D : VariantTexture2d = VariantTexture2d;


/// Represents the different forms of texture filtering, which determines
/// how a texture will be sampled when drawn.

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureFilter {
    /// When texturing a pixel, return the texel that is nearest to the center
    /// of the pixel.
    Nearest,

    /// When texturing a pixel, return a weighted average of the four texels
    /// nearest to center of the pixel.
    Linear
}

/// Represents the different forms of texture filtering when using mipmaps.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureMipmapFilter {
    /// Ignore mipmap values, and texture a pixel using a standard
    /// [`TextureFilter`](enum.TextureFilter.html).
    Filter(TextureFilter),

    /// Select the two mipmaps that are closest to the size of pixel
    /// being filled, and sample each one according to `criterion`.
    /// Finally, the result will be computed either by taking the
    /// weighted average of each texel, or by selecting the value
    /// from the closer texel, according to `mipmap`.
    MipmapFilter {
        /// The method to use to select the texels from a mipmap.
        criterion: TextureFilter,

        /// The method to use to select the mipmaps.
        mipmap: TextureFilter
    }
}

/// When texturing a pixel, select the texel that is closest
/// to the center of the pixel.
pub const NEAREST : TextureFilter = TextureFilter::Nearest;

/// When texturing a pixel, select the four texels that
/// are closest to the center of the pixel, and compute the
/// result by taking a weighted average of each texel.
pub const LINEAR : TextureFilter = TextureFilter::Linear;

/// When texturing a pixel, select the mipmap that is nearest
/// in size to the pixel, and select the texel that is
/// closest to the center of the pixel.
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };

/// When texturing a pixel, select the mipmap that is nearest
/// in size to the pixel, select the four texels that are closest
/// to the center of the pixel, and compute the result by taking
/// the weighted average of each texel.
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };

/// When texturing a pixel, select the two mipmaps that are nearest
/// in size to the pixel, select the texel in each that is closest
/// to the center of the pixel, and compute the result by taking
/// the weighted average of each texel.
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };

/// When texturing a pixel, select the two mipmaps that are nearest
/// in size to the pixel. For each, select the four texels that are
/// closest to the center of the pixel, and compute the weighted average.
/// Finally, take the resulting two weighted averages of the texels,
/// and take the weighted average of both based on the mipmaps.
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    /// Convert a `TextureFilter` into a raw OpenGL enum value
    pub fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    /// Convert a `TextureMipmapFilter` into a raw OpenGL enum value
    pub fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

gl_enum! {
    /// The wrapping modes when drawing a texture.
    pub gl_enum TextureWrapMode {
        /// Wrap a texture by clamping it within the range `[1/2x, 1 - 1/2x]`,
        /// where `x` is the dimension of the texture being clamped.
        pub const ClampToEdge as CLAMP_TO_EDGE = gl::CLAMP_TO_EDGE,

        /// Wrap a texture by repeating it front-to-back, then back-to-front,
        /// then repeating.
        pub const MirroredRepeat as MIRRORED_REPEAT = gl::MIRRORED_REPEAT,

        /// Wrap a texture by repeating it over and over again.
        pub const Repeat as REPEAT = gl::REPEAT
    }
}