PageRenderTime 360ms CodeModel.GetById 100ms app.highlight 158ms RepoModel.GetById 85ms app.codeStats 0ms

/Graphics/OpenTK/OpenTKTexture.cs

#
C# | 355 lines | 249 code | 23 blank | 83 comment | 24 complexity | c707a99c88e30443d1c7245c6f9155bc MD5 | raw file
  1using System;
  2using System.Runtime.InteropServices;
  3using Delta.Graphics.BaseOpenGL;
  4using Delta.Platforms.Windows;
  5using Delta.Utilities;
  6using Delta.Utilities.Datatypes;
  7using Delta.Utilities.Graphics;
  8using OpenTK.Graphics.OpenGL;
  9
 10namespace Delta.Graphics.OpenTK
 11{
 12	/// <summary>
 13	/// OpenTK texture implementation. Helpful links:
 14	/// http://www.opentk.com/project/TexLib
 15	/// http://www.opentk.com/node/750
 16	/// http://www.opentk.com/node/1624
 17	/// http://people.mozilla.com/~vladimir/jsvec/WebGL-spec.html
 18	/// </summary>
 19	internal class OpenTKTexture : BaseOpenGLTexture
 20	{
 21		#region Constructors
 22		/// <summary>
 23		/// Creates a OpenGL Texture, all the magic happens in the Load methods
 24		/// </summary>
 25		/// <param name="setContentName">Content image name to load</param>
 26		protected OpenTKTexture(string setContentName)
 27			: base(setContentName)
 28		{
 29		}
 30
 31		/// <summary>
 32		/// Create texture with given RGB data, this can be used to create
 33		/// textures programmatically (e.g. mini-maps). You can also use
 34		/// RenderToTexture to render onto textures, which can also be used in
 35		/// Materials and Shaders. RenderToTexture is much more efficient if you
 36		/// want to change the texture data and should always be used for post
 37		/// screen shaders. This constructor is only useful for static data.
 38		/// </summary>
 39		/// <param name="setRgbData">RGB data (24 bit per pixel, no alpha)</param>
 40		/// <param name="setSize">Size of the texture in pixels,
 41		/// Width * Height must match rgbData.Length / 3</param>
 42		/// <param name="setMode">Blend mode to use for this texture</param>
 43		/// <param name="setUseLinearFiltering">True if we want the normal linear
 44		/// filtering enabled or false for sharp blocky looking textures.</param>
 45		/// <param name="setIsRgba">
 46		/// Set true whether setByteData is RGBA or RGB.
 47		/// </param>
 48		protected OpenTKTexture(byte[] setRgbData, Size setSize,
 49			BlendMode setMode, bool setUseLinearFiltering, bool setIsRgba)
 50			: base(setRgbData, setSize, setMode, setUseLinearFiltering, setIsRgba)
 51		{
 52		}
 53		#endregion
 54
 55		#region Methods (Private)
 56
 57		#region SetTextureParameters
 58		/// <summary>
 59		/// Set texture parameters for wrap mode and texture filtering.
 60		/// </summary>
 61		protected override void SetTextureParameters(int wrapMode,
 62			int magFilterMode, int minFilterMode)
 63		{
 64			// Creating a texture without setting the filter states is problematic
 65			// in OpenGL (due to the default values 
 66			// http://www.opengl.org/wiki/Common_Mistakes#Creating_a_Texture)
 67			// Since we cannot be sure when and how this constant is going to be 
 68			// created, it is preferred to set the parameters explicitly here
 69			if (IsCubeMap)
 70			{
 71				GL.TexParameter(TextureTarget.TextureCubeMap,
 72					TextureParameterName.TextureWrapS, wrapMode);
 73				GL.TexParameter(TextureTarget.TextureCubeMap,
 74					TextureParameterName.TextureWrapT, wrapMode);
 75				GL.TexParameter(TextureTarget.TextureCubeMap,
 76					TextureParameterName.TextureMagFilter, magFilterMode);
 77				GL.TexParameter(TextureTarget.TextureCubeMap,
 78					TextureParameterName.TextureMinFilter, minFilterMode);
 79			}
 80			else
 81			{
 82				GL.TexParameter(TextureTarget.Texture2D,
 83					TextureParameterName.TextureWrapS, wrapMode);
 84				GL.TexParameter(TextureTarget.Texture2D,
 85					TextureParameterName.TextureWrapT, wrapMode);
 86				GL.TexParameter(TextureTarget.Texture2D,
 87					TextureParameterName.TextureMagFilter, magFilterMode);
 88				GL.TexParameter(TextureTarget.Texture2D,
 89					TextureParameterName.TextureMinFilter, minFilterMode);
 90			}
 91		}
 92		#endregion
 93
 94		#region FillTextureData
 95		/// <summary>
 96		/// Fill texture data that has already been loaded in a specific format
 97		/// and upload it to the GPU for use in rendering.
 98		/// </summary>
 99		protected override bool FillTextureData(Size fullImageSize,
100			byte[] imageData, int dataLengthPerTexture, int minimumMipmapByteSize,
101			int numberOfMipmaps, bool isDxt1, bool isDxt3, bool isDxt5,
102			bool isAtc, bool isAtcA, bool isAtcI,
103			bool isRgba4Texture, bool isPvrTexture, bool isRgb5A1Texture)
104		{
105
106			ErrorCode error = GL.GetError();
107			if (error != ErrorCode.NoError)
108			{
109				Log.Warning(
110					"There was an OpenGL error before, you should " +
111					"investigate: '" + error + "'. Trying to load a texture '" +
112					this +
113					"' here (and if anything fails with this fails we want to report " +
114					"that error, not any previous error, which should be fixed " +
115					"elsewhere).");
116			}
117			if (isPvrTexture)
118			{
119				Log.Warning(
120					"Sorry, PVR textures are not supported in OpenTK: " +
121					this +
122					". Please use the content system to generate texture files " +
123					"that can be used here (usually DDS on Windows)");
124				return false;
125			}
126			if (isAtc || isAtcA || isAtcI)
127			{
128				Log.Warning(
129					"Sorry, ATC textures are not supported in OpenTK: " +
130					this +
131					". Please use the content system to generate texture files " +
132					"that can be used here (usually DDS on Windows)");
133				return false;
134			}
135
136			// Check if power of two.
137			//disabled because content is not using atlas yet.
138			//if (MathHelper.IsPowerOfTwo(fullImageSize.Width) == false ||
139			//  MathHelper.IsPowerOfTwo(fullImageSize.Height) == false)
140			//{
141			//  Log.Info("This texture is not power of two and might cause "+
142			//    "rendering problems, this platform supports non-power of "+
143			//    "two textures, but this can cause problems on other platforms. "+
144			//    "Please only create power of two textures! "+
145			//    "Image Size=" + fullImageSize);
146			//}
147
148			// Create the texture in OpenGL and use the textureHandle from now on.
149			GL.GenTextures(1, out textureHandle);
150
151			// By default the pixel format is Rgb for non alpha and Rgba for alpha
152			// PixelInternalFormat: Rgba is 0x1908, Rgb is 0x1907.
153			PixelInternalFormat formatUsed =
154				HasAlpha
155					? PixelInternalFormat.Rgba
156					: PixelInternalFormat.Rgb;
157
158			int imageWidth = (int)fullImageSize.Width;
159			int imageHeight = (int)fullImageSize.Height;
160
161			// Bind the texture, which is usually a 2D texture (except for cube maps)
162			if (IsCubeMap)
163			{
164				GL.BindTexture(TextureTarget.TextureCubeMap, textureHandle);
165				GL.Enable(EnableCap.TextureCubeMap);
166			}
167			else
168			{
169				GL.BindTexture(TextureTarget.Texture2D, textureHandle);
170			}
171
172			// Note: We always force clamp to fully utilize atlas texturing
173			// Use ClampToEdge or we get black lines at the edges.
174			int wrapMode = (int)All.ClampToEdge;
175			if (AllowTiling)
176			{
177				wrapMode = (int)All.Repeat;
178			}
179			// Note: Using just bilinear filtering, not trilinear for better
180			// performance on slow devices. For additional information see:
181			// http://www.ds.arch.tue.nl/joran/wup/Default.menu?menu=6
182			// Note: on PC trilinear is the same speed as LinearMipmapNearest
183			SetTextureParameters(wrapMode,
184				UseLinearFiltering
185					? (int)All.Linear
186					: (int)All.Nearest,
187				UseLinearFiltering
188					? numberOfMipmaps > 1
189					  	? (int)All.LinearMipmapLinear
190					  	: (int)All.Linear
191					: numberOfMipmaps > 1
192					  	? (int)All.NearestMipmapNearest
193					  	: (int)All.Nearest);
194
195			// Use glTexImage2D instead of glCompressedTexImage2D for uncompressed
196			// DDS files (RGB 888 or RGBA 8888)
197			GCHandle handle = GCHandle.Alloc(imageData, GCHandleType.Pinned);
198			try
199			{
200				IntPtr dataPointer = handle.AddrOfPinnedObject();
201				if (isDxt1 || isDxt3 || isDxt5)
202				{
203					// Note: CompressedRgbS3tcDxt1Ext seems always to be the same as
204					// CompressedRgbaS3tcDxt1Ext, if the image uses alpha is decided
205					// by the dds, not by this flag.
206					//PixelInternalFormat.CompressedRgbaS3tcDxt1Ext :
207					formatUsed =
208						isDxt1
209							? PixelInternalFormat.CompressedRgbS3tcDxt1Ext
210							: isDxt3
211							  	? PixelInternalFormat.CompressedRgbaS3tcDxt3Ext
212							  	: PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
213
214					// Width and height must be multiple of 4x4. For more help see:
215					// http://www.khronos.org/opengles/sdk/1.1/docs/man/glCompressedTexImage2D.xml
216					int mipmapWidth = (imageWidth / 4) * 4;
217					int mipmapHeight = (imageHeight / 4) * 4;
218
219					if (IsCubeMap)
220					{
221						// For cube maps we need to load all 6 cube faces
222						for (int face = 0; face < 6; face++)
223						{
224							GL.CompressedTexImage2D(
225								TextureTarget.TextureCubeMapPositiveX + face, 0,
226								formatUsed, mipmapWidth, mipmapHeight, 0,
227								dataLengthPerTexture, dataPointer);
228							dataPointer += dataLengthPerTexture;
229						}
230					}
231					else
232					{
233						GL.CompressedTexImage2D(TextureTarget.Texture2D, 0, formatUsed,
234							mipmapWidth, mipmapHeight, 0, dataLengthPerTexture,
235							dataPointer);
236
237						int mipmapByteSize = dataLengthPerTexture;
238						for (int i = 1; i < numberOfMipmaps; i++)
239						{
240							// Move our data pointer along.
241							dataPointer += mipmapByteSize;
242							mipmapByteSize /= 4;
243							mipmapWidth /= 2;
244							mipmapHeight /= 2;
245							if (mipmapByteSize < minimumMipmapByteSize)
246							{
247								mipmapByteSize = minimumMipmapByteSize;
248							}
249							if (mipmapWidth < 1)
250							{
251								mipmapWidth = 1;
252							}
253							if (mipmapHeight < 1)
254							{
255								mipmapHeight = 1;
256							}
257							GL.CompressedTexImage2D(TextureTarget.Texture2D, i, formatUsed,
258								mipmapWidth, mipmapHeight, 0, mipmapByteSize, dataPointer);
259						} // for
260					} // else
261				} // if (ddsFormat)
262				else
263				{
264					PixelType pixelType =
265						isRgb5A1Texture
266							? PixelType.UnsignedShort5551
267							: isRgba4Texture
268							  	? PixelType.UnsignedShort4444
269							  	: PixelType.UnsignedByte;
270
271					GL.TexImage2D(TextureTarget.Texture2D, 0,
272						// Use the same for internal format (Rgba or Rgb)
273						formatUsed, imageWidth, imageHeight, 0,
274						// And the same for the pixel format of the incoming data
275						(PixelFormat)formatUsed, pixelType, dataPointer);
276
277					int mipmapByteSize = dataLengthPerTexture;
278					int mipmapWidth = imageWidth;
279					int mipmapHeight = imageHeight;
280					if (mipmapByteSize > 0 &&
281					    numberOfMipmaps > 1)
282					{
283						for (int i = 1; i < numberOfMipmaps; i++)
284						{
285							// Move our data pointer along.
286							dataPointer += mipmapByteSize;
287							mipmapByteSize /= 4;
288							mipmapWidth /= 2;
289							mipmapHeight /= 2;
290							if (mipmapWidth < 1)
291							{
292								mipmapWidth = 1;
293							}
294							if (mipmapHeight < 1)
295							{
296								mipmapHeight = 1;
297							}
298
299							// Use the same for internal format (Rgba or Rgb)
300							// And the same for the pixel format of the incoming data
301							GL.TexImage2D(TextureTarget.Texture2D, i,
302								formatUsed, mipmapWidth, mipmapHeight, 0,
303								(PixelFormat)formatUsed, pixelType, dataPointer);
304						} // for
305					} // if
306				} // else
307			} // try
308			finally
309			{
310				handle.Free();
311			}
312
313			return true;
314		}
315		#endregion
316
317		#region LoadPngImageData
318		/// <summary>
319		/// Helper method to load png image data, which is not allowed on all
320		/// platforms (only on Windows to be precise).
321		/// </summary>
322		/// <param name="filename">Filename to load the png from</param>
323		/// <param name="totalImageSize">Total image size from the png data</param>
324		/// <returns>The RGB or RGBA image data.</returns>
325		protected override byte[] LoadPngImageData(string filename,
326			out Size totalImageSize)
327		{
328			bool totalImageHasAlpha;
329			byte[] data = BitmapHelper.GetRGBAImageData(filename,
330				out totalImageSize, out totalImageHasAlpha);
331			if (totalImageHasAlpha != HasAlpha)
332			{
333				Log.Info(
334					"Loaded Png Image might cause problems '" + filename + "' has " +
335					(totalImageHasAlpha
336					 	? "alpha"
337					 	: "no alpha") + " (switching to " +
338					"this), but the given blend mode '" + BlendMode + "' from the " +
339					"content system says we have " +
340					(HasAlpha
341					 	? "alpha"
342					 	: "no alpha"));
343				BlendMode =
344					totalImageHasAlpha
345						? BlendMode.Translucency
346						: BlendMode.Opaque;
347			}
348			return data;
349		}
350		#endregion
351
352		#endregion
353	}
354}
355