1 /*
2 Copyright (c) 2013 Andrey Penechko
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license the "Software" to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 module anchovy.graphics.texture;
30 
31 public import derelict.opengl3.gl3;
32 
33 import std.conv, std.file;
34 import std.stdio;
35 import std.string;
36 
37 import anchovy.core.types;
38 import anchovy.graphics.glerrors;
39 import anchovy.graphics.bitmap;
40 
41 //version = debugTexture;
42 
43 enum TextureTarget : uint
44 {
45 	target1d = GL_TEXTURE_1D,
46 	target2d = GL_TEXTURE_2D,
47 	target3d = GL_TEXTURE_3D,
48 	targetRectangle = GL_TEXTURE_RECTANGLE,
49 	targetBuffer = GL_TEXTURE_BUFFER,
50 	targetCubeMap = GL_TEXTURE_CUBE_MAP,
51 	target1dArray = GL_TEXTURE_1D_ARRAY,
52 	target2dArray = GL_TEXTURE_2D_ARRAY,
53 	targetCubeMapArray = GL_TEXTURE_CUBE_MAP_ARRAY,
54 	target2dMultisample = GL_TEXTURE_2D_MULTISAMPLE,
55 	target2dMultisampleArray = GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
56 }
57 
58 enum TextureFormat : uint
59 {
60 	r = GL_RED,
61 	rg = GL_RG,
62 	rgb = GL_RGB,
63 	rgba = GL_RGBA,
64 }
65 
66 class Texture
67 {
68 	this(string filename, TextureTarget target, TextureFormat format)
69 	{
70 		texTarget = target;
71 		texFormat = format;
72 		loadFromFile(filename);
73 	}
74 	
75 	this(Bitmap textureData, TextureTarget target, TextureFormat format)
76 	{
77 		texTarget = target;
78 		texFormat = format;
79 		loadFromData(textureData);
80 	}
81 	
82 	~this()
83 	{
84 		unload();
85 	}
86 	
87 	void validateBind(uint textureUnit = 0)
88 	{
89 		if (!isValid) reload();
90 
91 		texUnit = GL_TEXTURE0 + textureUnit;
92 
93 		glBindTexture(texTarget, glTextureHandle);
94 			checkGlError;
95 	}
96 
97 	void bind(uint textureUnit = 0)
98 	{
99 		texUnit = GL_TEXTURE0 + textureUnit;
100 
101 		glBindTexture(texTarget, glTextureHandle);
102 			checkGlError;
103 	}
104 	
105 	void unbind()
106 	{
107 		glBindTexture(texTarget, 0);
108 	}
109 
110 	uvec2 size() @property
111 	{
112 		return _bitmap.size;
113 	}
114 	
115 	ref const(ubyte[]) data()
116 	{
117 		return _bitmap.data;
118 	}
119 
120 	private void reload()
121 	{
122 		if (!isHandleCreated) genTexture();
123 		
124 		bind;
125 		if (_bitmap.size != lastSize)
126 		{
127 			glTexImage2D(texTarget, 0, texFormat, _bitmap.size.x, _bitmap.size.y, 0, texFormat, GL_UNSIGNED_BYTE, null);
128 				checkGlError;
129 			glTexImage2D(texTarget, 0, texFormat, _bitmap.size.x, _bitmap.size.y, 0, texFormat, GL_UNSIGNED_BYTE, _bitmap.data.ptr);
130 				checkGlError;
131 
132 			lastSize = _bitmap.size;
133 		}
134 		else
135 		{
136 			glTexSubImage2D(texTarget, 0, 0, 0, _bitmap.size.x, _bitmap.size.y,  texFormat, GL_UNSIGNED_BYTE, _bitmap.data.ptr);
137 				checkGlError;
138 		}
139 
140 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
141 			checkGlError;
142 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
143 			checkGlError;
144 		unbind;
145 
146 		isValid = true;
147 	}
148 
149 	void invalidate()
150 	{
151 		isValid = false;
152 	}
153 
154 	void onBitmapChanged()
155 	{
156 		invalidate();
157 	}
158 
159 	void bitmap(Bitmap newBitmap) @property
160 	{
161 		if (newBitmap == _bitmap) return;
162 
163 		loadFromData(newBitmap);
164 	}
165 
166 	Bitmap bitmap() @property
167 	{
168 		return _bitmap;
169 	}
170 	
171 	/////////////////
172 	//Private methods
173 	/////////////////
174 	
175 	private void genTexture()
176 	{
177 		glGenTextures(1, &glTextureHandle);
178 			checkGlError;
179 
180 		isHandleCreated = true;
181 	}
182 	
183 	/// Loads image from file in RGBA8 format
184 	private void loadFromFile(string filename)
185 	{
186 		if (!exists(filename)) throw new Exception("File not found: " ~ filename);
187 
188 		if (_bitmap)
189 		{
190 			_bitmap.dataChanged.disconnect(&onBitmapChanged);
191 		}
192 
193 		_bitmap = createBitmapFromFile(filename);
194 
195 		_bitmap.dataChanged.connect(&onBitmapChanged);
196 
197 		invalidate();
198 	}
199 	
200 	private void loadFromData(Bitmap textureData)
201 	{
202 		if (_bitmap)
203 		{
204 			_bitmap.dataChanged.disconnect(&onBitmapChanged);
205 		}
206 
207 		this._bitmap = textureData;
208 		_bitmap.dataChanged.connect(&onBitmapChanged);
209 
210 		invalidate();
211 	}
212 	
213 	private void unload()
214 	{
215 		glDeleteTextures(1, &glTextureHandle);
216 	}
217 	
218 private:
219 	TextureFormat texFormat;
220 	uint glTextureHandle;
221 	TextureTarget texTarget;
222 	uvec2 lastSize;
223 	bool isValid = false;
224 	bool isHandleCreated = false;
225 
226 	uint texUnit = 0;
227 	Bitmap _bitmap;
228 }