1 /*
2 Copyright (c) 2017-2018 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 Permission is hereby granted, free of charge, to any person or organization
6 obtaining a copy of the software and accompanying documentation covered by
7 this license (the "Software") to use, reproduce, display, distribute,
8 execute, and transmit the Software, and to prepare derivative works of the
9 Software, and to permit third-parties to whom the Software is furnished to
10 do so, all subject to the following:
11 
12 The copyright notices in the Software and this entire statement, including
13 the above license grant, this restriction and the following disclaimer,
14 must be included in all copies of the Software, in whole or in part, and
15 all derivative works of the Software, unless such copies or derivative
16 works are solely in the form of machine-executable object code generated by
17 a source language processor.
18 
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26 */
27 
28 module dagon.graphics.texture;
29 
30 import std.stdio;
31 import std.math;
32 
33 import dlib.core.memory;
34 import dlib.image.color;
35 import dlib.image.image;
36 import dlib.image.hdri;
37 import dlib.math.vector;
38 
39 import dagon.core.libs;
40 import dagon.core.ownership;
41 
42 class Texture: Owner
43 {
44     SuperImage image;
45 
46     GLuint tex;
47     GLenum format;
48     GLint intFormat;
49     GLenum type;
50 
51     int width;
52     int height;
53     int numMipmapLevels;
54 
55     Vector2f translation;
56     Vector2f scale;
57     float rotation;
58 
59     bool useMipmapFiltering = true;
60     bool useLinearFiltering = true;
61 
62     protected bool mipmapGenerated = false;
63 
64     this(Owner o)
65     {
66         super(o);
67         translation = Vector2f(0.0f, 0.0f);
68         scale = Vector2f(1.0f, 1.0f);
69         rotation = 0.0f;
70     }
71 
72     this(SuperImage img, Owner o, bool genMipmaps = false)
73     {
74         super(o);
75         translation = Vector2f(0.0f, 0.0f);
76         scale = Vector2f(1.0f, 1.0f);
77         rotation = 0.0f;
78         createFromImage(img, genMipmaps);
79     }
80 
81     void createFromImage(SuperImage img, bool genMipmaps = true)
82     {
83         releaseGLTexture();
84 
85         image = img;
86         width = img.width;
87         height = img.height;
88 
89         if (!pixelFormatToTextureFormat(cast(PixelFormat)img.pixelFormat, format, intFormat, type))
90             writefln("Unsupported pixel format %s", img.pixelFormat);
91 
92         glGenTextures(1, &tex);
93         glActiveTexture(GL_TEXTURE0);
94         glBindTexture(GL_TEXTURE_2D, tex);
95 
96         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
97         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
98         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
99         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
100 
101         //if (DerelictGL.isExtSupported("GL_EXT_texture_filter_anisotropic"))
102         //    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f);
103 
104         glTexImage2D(GL_TEXTURE_2D, 0, intFormat, width, height, 0, format, type, cast(void*)img.data.ptr);
105 
106         useMipmapFiltering = genMipmaps;
107         if (useMipmapFiltering)
108         {
109             glGenerateMipmap(GL_TEXTURE_2D);
110             mipmapGenerated = true;
111         }
112 
113         glBindTexture(GL_TEXTURE_2D, 0);
114     }
115 
116     void bind()
117     {
118         if (glIsTexture(tex))
119         {
120             glBindTexture(GL_TEXTURE_2D, tex);
121 
122             if (!mipmapGenerated && useMipmapFiltering)
123             {
124                 glGenerateMipmap(GL_TEXTURE_2D);
125                 mipmapGenerated = true;
126             }
127 
128             if (useMipmapFiltering)
129                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
130             else if (useLinearFiltering)
131                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
132             else
133             {
134                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
135                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
136             }
137         }
138     }
139 
140     void unbind()
141     {
142         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
143         glBindTexture(GL_TEXTURE_2D, 0);
144     }
145 
146     bool valid()
147     {
148         return cast(bool)glIsTexture(tex);
149     }
150 
151     Color4f sample(float u, float v)
152     {
153         if (image)
154         {
155             int x = cast(int)floor(u * width);
156             int y = cast(int)floor(v * height);
157             return image[x, y];
158         }
159         else
160             return Color4f(0, 0, 0, 0);
161     }
162 
163     void release()
164     {
165         releaseGLTexture();
166         if (image)
167         {
168             Delete(image);
169             image = null;
170         }
171     }
172 
173     void releaseGLTexture()
174     {
175         if (glIsTexture(tex))
176             glDeleteTextures(1, &tex);
177     }
178 
179     ~this()
180     {
181         release();
182     }
183 }
184 
185 bool pixelFormatToTextureFormat(uint pixelFormat, out GLenum textureFormat, out GLint textureInternalFormat, out GLenum pixelType)
186 {
187     switch (pixelFormat)
188     {
189         case PixelFormat.L8:         textureInternalFormat = GL_R8;      textureFormat = GL_RED;  pixelType = GL_UNSIGNED_BYTE; break;
190         case PixelFormat.LA8:        textureInternalFormat = GL_RG8;     textureFormat = GL_RG;   pixelType = GL_UNSIGNED_BYTE; break;
191         case PixelFormat.RGB8:       textureInternalFormat = GL_RGB8;    textureFormat = GL_RGB;  pixelType = GL_UNSIGNED_BYTE; break;
192         case PixelFormat.RGBA8:      textureInternalFormat = GL_RGBA8;   textureFormat = GL_RGBA; pixelType = GL_UNSIGNED_BYTE; break;
193         case FloatPixelFormat.RGBAF32: textureInternalFormat = GL_RGBA32F; textureFormat = GL_RGBA; pixelType = GL_FLOAT; break;
194         default:
195             return false;
196     }
197 
198     return true;
199 }