PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Graphics/OpenTK/OpenTKTexture.cs

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