1 module Engine.Texture;
2 
3 import Engine.Atlas;
4 import derelict.freeimage.freeimage;
5 import std.stdio;
6 import derelict.opengl3.gl;
7 import Engine.math;
8 import Engine.Buffer;
9 import Engine.Material;
10 
11 struct UV {
12 	public float U1;
13 	public float V1;
14 	public float U2;
15 	public float V2;
16 }
17 
18 interface ITexture {
19 	@property GLenum id();
20 	@property recti rect();
21 	@property ubyte pixelSize();
22 	@property GLenum format();
23 
24 	package static GLuint currentTexture = -1;
25 
26 	@property final int width() {
27 		return rect.max.x - rect.min.x;
28 	}
29 
30 	@property final int height() {
31 		return rect.max.y - rect.min.y;
32 	}
33 
34 	final void Bind() {
35 		if (id != currentTexture) {
36 			currentTexture = id;
37 			glBindTexture(GL_TEXTURE_2D,id);
38 		}
39 	}
40 
41 
42 
43 }
44 
45 class DynamicTexture : Texture {
46 
47 	Buffer buffer;
48 	int nextIndex = 1;
49 	int index = 0;
50 	int pboMode = 2;
51 
52 	this(ITexture texture) {
53 		super(texture);
54 		this.buffer = new Buffer(this.rect.Dx() * this.rect.Dy() * this.pixelSize,GL_PIXEL_UNPACK_BUFFER,GL_STREAM_DRAW);
55 	}
56 
57 	final void Update(T)(void delegate(T[] arr) update) {
58         buffer.Update(update);
59 		this.Bind();
60 		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.Dx(), rect.Dy(), format, GL_UNSIGNED_BYTE, null);
61 	}
62 
63 	final void CopyToBuffer() {
64 		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rect.Dx(), rect.Dy(), format, GL_UNSIGNED_BYTE, null);
65 	}
66 
67 }
68 
69 class Texture : ITexture {
70 	@property GLenum id() { return _id;};
71 	@property recti rect() { return _rect;};
72 	@property ubyte pixelSize() { return _pixelSize;};
73 	@property GLenum format() { return _format; };
74 
75 	package GLenum _id;
76 	package recti _rect;
77 	package ubyte _pixelSize;
78 	package GLenum _format;
79 
80 	package Material material;
81 
82     package static immutable bool littleEndian;
83 
84 	static this() {
85 		DerelictFI.load();
86 		littleEndian = cast(bool)FreeImage_IsLittleEndian();
87 	}
88 
89 	final Material GetMaterial() {
90 		if (material is null) {
91 			material = new Material(this);
92 		}
93 		return material;
94 	}
95 
96 	package this() {}
97 
98 	this(ITexture texture) {
99 		this._id = texture.id;
100 		this._rect = texture.rect;
101 		this._pixelSize = texture.pixelSize;
102 		this._format = texture.format;
103 		//this._pixelType = texture.pixelType;
104 	}
105 
106 	this(ubyte[] buffer, int width, int height) {
107 		this._rect = recti(width,height);
108 		this._pixelSize = 2;
109 		this._format = GL_RGBA;
110 		//generate an OpenGL texture ID for this texture
111 		glGenTextures(1, &_id);
112 		//bind to the new texture ID
113 		glBindTexture(GL_TEXTURE_2D, id);
114 	
115 		glTexImage2D(GL_TEXTURE_2D, 0, this._format, width, height,
116 					 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, cast(void*)buffer);
117 
118 		SetFiltering(GL_LINEAR,GL_LINEAR);
119 	}
120 
121 	this(int width, int height) {
122 		this._rect = recti(width,height);
123 		this._pixelSize = 4*4;
124 		this._format = GL_RGBA32F;
125 		//generate an OpenGL texture ID for this texture
126 		glGenTextures(1, &_id);
127 		//bind to the new texture ID
128 		glBindTexture(GL_TEXTURE_2D, id);
129 
130 		glTexImage2D(GL_TEXTURE_2D, 0, this._format, width, height,
131 					 0, 0, GL_FLOAT, null);
132 
133 		SetFiltering(GL_NEAREST,GL_NEAREST);
134 	}
135 
136 	this(string filename) {
137 		auto fif = FreeImage_GetFileType(filename.ptr, 0);
138 		//if still unknown, try to guess the file format from the file extension
139 		if(fif == FIF_UNKNOWN) 
140 			fif = FreeImage_GetFIFFromFilename(filename.ptr);
141 		//if still unkown, return failure
142 		if(fif == FIF_UNKNOWN)
143 			throw new Exception("Unkown format " ~ filename);
144 		
145 		if(!FreeImage_FIFSupportsReading(fif)) {
146 			throw new Exception("Cannot read the file " ~ filename);
147 		}
148 		auto dib = FreeImage_Load(fif, filename.ptr, 0);
149 		if (dib == null) {
150 			throw new Exception("Cannot load the file " ~ filename);
151 		}
152 		
153 		scope(exit)  FreeImage_Unload(dib);
154 			
155 		//retrieve the image data
156 		auto pixels = FreeImage_GetBits(dib);
157 		
158 		//get the image width and height
159 		auto width = FreeImage_GetWidth(dib);
160 		auto height = FreeImage_GetHeight(dib);
161 
162 		_rect = recti(width,height);
163 
164 		//if this somehow one of these failed (they shouldn't), return failure
165 		if((pixels is null) || (width == 0) || (height == 0))
166 			throw new Exception("Unexcepted error there is no width/height/pixels.");
167 
168 		auto colorType = FreeImage_GetColorType(dib);
169 		//generate an OpenGL texture ID for this texture
170 		glGenTextures(1, &_id);
171 		//bind to the new texture ID
172 		glBindTexture(GL_TEXTURE_2D, id);
173 
174 
175 		//store the texture data for OpenGL use
176 		switch(colorType) { 
177 			case FIC_RGB:
178 				this._format = GL_RGB;
179 				GLenum inFormat;
180 				if (littleEndian) {
181 					inFormat = GL_BGR;
182 				} else {
183 					inFormat = GL_RGB;
184 				}
185 				glTexImage2D(GL_TEXTURE_2D, 0, this._format, width, height,
186 							 0,inFormat, GL_UNSIGNED_BYTE, pixels);
187 				this._pixelSize = 3;
188 				break;
189 			case FIC_RGBALPHA:
190 				this._format = GL_RGBA;
191 				GLenum inFormat;
192 				if (littleEndian) {
193 					inFormat = GL_BGRA;
194 				} else {
195 					inFormat = GL_RGBA;
196 				}
197 				glTexImage2D(GL_TEXTURE_2D, 0, this._format, width, height,
198 							 0, inFormat, GL_UNSIGNED_BYTE, pixels);
199 				this._pixelSize = 4;
200 				break;
201 			default:
202 				throw new Exception("Cannot this handle pixel type.");
203 				break;
204 		}
205 	
206 		SetFiltering(GL_NEAREST,GL_NEAREST);	
207 	}
208 
209 	UV RectUV(recti rect) {
210 		auto w = cast(double)width;
211 		auto h = cast(double)height;
212 		return UV((cast(double)rect.min.x)/w,
213 			(cast(double)rect.min.y)/h,
214 			(cast(double)rect.max.x)/w,
215 			(cast(double)rect.max.y)/h);
216 		//return vec2(cast(double)width = r.
217 	}
218 
219 	void SetFiltering(GLenum mag, GLenum min) {		
220 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag);
221 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min);
222 	}
223 
224 
225 
226 }