Recent

Author Topic: OPenGL Texture loader  (Read 5057 times)

KemBill

  • Jr. Member
  • **
  • Posts: 74
OPenGL Texture loader
« on: November 21, 2017, 08:02:02 pm »
in reply to Marcov comment to my previous thread.

So here is the code I use to load textures (the whole class)

Code: Pascal  [Select][+][-]
  1. unit gltexture;
  2.  
  3. {$mode delphi}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, dglopengl, Graphics, GraphType, Interfaces;
  9.  
  10. type
  11.  
  12.   { TTextureLoader }
  13.  
  14.   TTextureLoader = class(TObject)
  15.   private
  16.     fChecker: GLuint;
  17.     fList: TList;
  18.     procedure MakeChecker;
  19.     procedure FlipVertical(const px: TRawImage);
  20.     procedure AddTextureToList(filename: string; Texture: GLuint);
  21.     function FindTextureFromList(filename: string; var Texture: GLuint): boolean;
  22.     function CreateTexture(Width, Height: integer; Alpha: boolean;
  23.       Data: Pointer): integer;
  24.     procedure LoadFromBMP(Filename: string; var Texture: GLuint);
  25.     procedure LoadFromJPG(Filename: string; var Texture: GLuint);
  26.     procedure LoadFromPNG(Filename: string; var Texture: GLuint);
  27.   public
  28.     constructor Create;
  29.     destructor Destroy; override;
  30.     function LoadTexture(const filename: string): GLuint;
  31.     procedure FreeTexture(const id: GLuint);
  32.   end;
  33.  
  34. implementation
  35.  
  36. { TTextureLoader }
  37. type
  38.  
  39.   PTextureRef = ^TTextureRef;
  40.  
  41.   TTextureRef = record
  42.     Name: shortstring;
  43.     id: GlUint;
  44.   end;
  45.  
  46. procedure TTextureLoader.MakeChecker;
  47. const
  48.   size = 64;
  49. var
  50.   i, j: integer;
  51.   c: glubyte;
  52.   t: array[0..size - 1, 0..size - 1, 0..3] of glubyte;
  53. begin
  54.   for i := 0 to size - 1 do
  55.   begin
  56.     for j := 0 to size - 1 do
  57.     begin
  58.       c := 255;
  59.       if ((i and 8) xor (j and 8)) > 0 then
  60.         c := 0;
  61.       t[i, j, 0] := c;
  62.       t[i, j, 1] := c;
  63.       t[i, j, 2] := c;
  64.       t[i, j, 3] := 255;
  65.     end;
  66.   end;
  67.   fChecker := CreateTexture(size, size, true, @t);
  68. end;
  69.  
  70. procedure TTextureLoader.FlipVertical(const px: TRawImage);
  71. var
  72.   p: array of byte;
  73.   i, half: integer;
  74.   LoPtr, HiPtr: PInteger;
  75.   bpl: integer;
  76. begin
  77.   bpl := px.Description.BytesPerLine;
  78.   if px.Description.Height < 3 then
  79.     exit;
  80.   half := (px.Description.Height div 2);
  81.   LoPtr := PInteger(px.Data);
  82.   HiPtr := PInteger(px.Data + ((px.Description.Height - 1) * bpl));
  83.   setlength(p, bpl);
  84.   for i := 1 to half do
  85.   begin
  86.     System.Move(LoPtr^, p[0], bpl); //(src, dst,sz)
  87.     System.Move(HiPtr^, LoPtr^, bpl); //(src, dst,sz)
  88.     System.Move(p[0], HiPtr^, bpl); //(src, dst,sz)
  89.     Inc(PByte(LoPtr), bpl);
  90.     Dec(PByte(HiPtr), bpl);
  91.   end;
  92. end;
  93.  
  94. procedure TTextureLoader.AddTextureToList(filename: string; Texture: GLuint);
  95. var
  96.   p: PTextureRef;
  97. begin
  98.   new(p);
  99.   p^.Name := filename;
  100.   p^.id := Texture;
  101.   fList.add(p);
  102. end;
  103.  
  104. function TTextureLoader.FindTextureFromList(filename: string;
  105.   var Texture: GLuint): boolean;
  106. var
  107.   i: integer;
  108.   p: PTextureRef;
  109. begin
  110.   Result := False;
  111.   i := 0;
  112.   while i < fList.Count do
  113.   begin
  114.     p := PTextureRef(fList.Items[i]);
  115.     if p^.Name = filename then
  116.     begin
  117.       texture := p^.id;
  118.       Result := True;
  119.       exit;
  120.     end;
  121.     Inc(i);
  122.   end;
  123. end;
  124.  
  125. function TTextureLoader.CreateTexture(Width, Height: integer;
  126.   Alpha: boolean; Data: Pointer): integer;
  127. var
  128.   Texture: GLuint;
  129. begin
  130.   glGenTextures(1, @Texture);
  131.   glBindTexture(GL_TEXTURE_2D, Texture);
  132.   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  133.   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  134.   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  135.  
  136.   if Alpha then
  137.     gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, Width, Height, GL_RGBA,
  138.       GL_UNSIGNED_BYTE, Data)
  139.   else
  140.     gluBuild2DMipmaps(GL_TEXTURE_2D, 3, Width, Height, GL_RGB, GL_UNSIGNED_BYTE, Data);
  141.  
  142.   Result := Texture;
  143. end;
  144.  
  145. procedure TTextureLoader.LoadFromBMP(Filename: string; var Texture: GLuint);
  146. var
  147.   bmp: TBitmap;
  148.   PixelRowPtr: PInteger;
  149.   useAlpha: boolean;
  150.  
  151. begin
  152.   if not fileexists(filename) then
  153.     exit;
  154.   bmp := TBitmap.Create;
  155.   try
  156.     bmp.LoadFromFile(Filename);
  157.     PixelRowPtr := PInteger(bmp.RawImage.Data);
  158.     useAlpha := False;
  159.     if bmp.RawImage.Description.BitsPerPixel div 8 = 4 then
  160.       useAlpha := True;
  161.     FlipVertical(bmp.RawImage);
  162.     Texture := CreateTexture(bmp.Width, bmp.Height, useAlpha, PixelRowPtr);
  163.   finally
  164.     bmp.Free;
  165.   end;
  166. end;
  167.  
  168. procedure TTextureLoader.LoadFromJPG(Filename: string; var Texture: GLuint);
  169. var
  170.   jpg: TJPEGImage;
  171.   PixelRowPtr: PInteger;
  172.   useAlpha: boolean;
  173. begin
  174.   if not fileexists(filename) then
  175.     exit;
  176.   jpg := TJPEGImage.Create;
  177.   try
  178.     jpg.LoadFromFile(Filename);
  179.     PixelRowPtr := PInteger(jpg.RawImage.Data);
  180.     useAlpha := False;
  181.     if jpg.RawImage.Description.BitsPerPixel div 8 = 4 then
  182.       useAlpha := True;
  183.     FlipVertical(jpg.RawImage);
  184.     Texture := CreateTexture(jpg.Width, jpg.Height, useAlpha, PixelRowPtr);
  185.   finally
  186.     jpg.Free;
  187.   end;
  188. end;
  189.  
  190. procedure TTextureLoader.LoadFromPNG(Filename: string; var Texture: GLuint);
  191. var
  192.   png: TPortableNetworkGraphic;
  193.   PixelRowPtr: PInteger;
  194.   useAlpha: boolean;
  195. begin
  196.   if not fileexists(filename) then
  197.     exit;
  198.   png := TPortableNetworkGraphic.Create;
  199.   try
  200.     png.LoadFromFile(Filename);
  201.     PixelRowPtr := PInteger(png.RawImage.Data);
  202.     useAlpha := False;
  203.     if png.RawImage.Description.BitsPerPixel div 8 = 4 then
  204.       useAlpha := True;
  205.     FlipVertical(png.RawImage);
  206.     Texture := CreateTexture(png.Width, png.Height, useAlpha, PixelRowPtr);
  207.   finally
  208.     png.Free;
  209.   end;
  210. end;
  211.  
  212. constructor TTextureLoader.Create;
  213. begin
  214.   MakeChecker;
  215.   fList := TList.Create;
  216. end;
  217.  
  218. destructor TTextureLoader.Destroy;
  219. var
  220.   p: PTextureRef;
  221.   i: integer;
  222. begin
  223.   for i := 0 to flist.Count - 1 do
  224.   begin
  225.     p := PTextureRef(fList.Items[i]);
  226.     glDeleteTextures(1, @p^.id);
  227.     dispose(p);
  228.   end;
  229.   flist.free;
  230.   inherited Destroy;
  231. end;
  232.  
  233. function TTextureLoader.LoadTexture(const filename: string): GLuint;
  234. var
  235.   ext: string;
  236. begin
  237.  
  238.   if FindTextureFromList(filename, Result) then
  239.     exit;
  240.  
  241.   Result := fChecker;
  242.  
  243.   ext := UpperCase(ExtractFileExt(filename));
  244.   if ext = '.BMP' then
  245.     LoadFromBMP(Filename, Result)
  246.   else if ext = '.JPG' then
  247.     LoadFromJPG(Filename, Result)
  248.   else if ext = '.PNG' then
  249.     LoadFromPNG(Filename, Result);
  250.  
  251.   AddTextureToList(filename, Result);
  252. end;
  253.  
  254. procedure TTextureLoader.FreeTexture(const id: GLuint);
  255. var
  256.   p: PTextureRef;
  257.   i: integer;
  258. begin
  259.   if glIsTexture(id) = GL_TRUE then
  260.   begin
  261.     for i := 0 to fList.Count - 1 do
  262.     begin
  263.       p := PTextureRef(fList.Items[i]);
  264.       if p^.id = id then
  265.         dispose(p);
  266.     end;
  267.     glDeleteTextures(1, @id);
  268.   end;
  269. end;
  270.  
  271. end.
  272.  

in the main program
Code: Pascal  [Select][+][-]
  1.   // before drawing
  2.   //texture := textureloader.LoadTexture('test.bmp');
  3.   [...]
  4.   glEnable(GL_TEXTURE_2D);
  5.   glBegin(GL_QUADS);
  6.     // Front Face
  7.     glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
  8.     glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
  9.     glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0);
  10.     glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0);
  11.     // Back Face
  12.     glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
  13.     glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
  14.     glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
  15.     glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
  16.     // Top Face
  17.     glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
  18.     glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0,  1.0);
  19.     glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0,  1.0);
  20.     glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
  21.     // Bottom Face
  22.     glTexCoord2f(1.0, 1.0); glVertex3f(-1.0, -1.0, -1.0);
  23.     glTexCoord2f(0.0, 1.0); glVertex3f( 1.0, -1.0, -1.0);
  24.     glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
  25.     glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
  26.     // Right face
  27.     glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0, -1.0);
  28.     glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0, -1.0);
  29.     glTexCoord2f(0.0, 1.0); glVertex3f( 1.0,  1.0,  1.0);
  30.     glTexCoord2f(0.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
  31.     // Left Face
  32.     glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0, -1.0);
  33.     glTexCoord2f(1.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
  34.     glTexCoord2f(1.0, 1.0); glVertex3f(-1.0,  1.0,  1.0);
  35.     glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0, -1.0);
  36.   glEnd();

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11453
  • FPC developer.
Re: OPenGL Texture loader
« Reply #1 on: November 21, 2017, 09:31:28 pm »
Ok, so a 3D scene. Don't know that very well, but try something like this

Code: Pascal  [Select][+][-]
  1.   // Front Face
  2.  
  3. if bitmapistopdown then // when you would normally flip
  4.   begin
  5.    // note that the y coordinates  of the first command are inverted here.
  6.     glTexCoord2f(0.0, 1.0); glVertex3f(-1.0, -1.0,  1.0);
  7.     glTexCoord2f(1.0, 1.0); glVertex3f( 1.0, -1.0,  1.0);
  8.     glTexCoord2f(1.0, 0.0); glVertex3f( 1.0,  1.0,  1.0);
  9.     glTexCoord2f(0.0, 0.0); glVertex3f(-1.0,  1.0,  1.0);
  10. end
  11. else
  12. begin  
  13.   // original, unmodified statemeents.
  14.     glTexCoord2f(0.0, 0.0); glVertex3f(-1.0, -1.0,  1.0);
  15.     glTexCoord2f(1.0, 0.0); glVertex3f( 1.0, -1.0,  1.0);
  16.     glTexCoord2f(1.0, 1.0); glVertex3f( 1.0,  1.0,  1.0);
  17.     glTexCoord2f(0.0, 1.0); glVertex3f(-1.0,  1.0,  1.0);
  18. end  
  19.  

Play with the condition a bit (maybe the IF condition needs a not)

KemBill

  • Jr. Member
  • **
  • Posts: 74
Re: OPenGL Texture loader
« Reply #2 on: November 21, 2017, 10:06:15 pm »
thanks for the notice, in my case I prefer transform the texture instead of performing tests during the render loop.

To the other readers, the loader is fully fonctional, the class prevents loading the same texture multiple times and replace the texture by a checkerboard if something bad happen (ie, non existant file)

feel free to comment  ;)

RAW

  • Hero Member
  • *****
  • Posts: 868
Re: OPenGL Texture loader
« Reply #3 on: November 23, 2017, 09:09:30 pm »
Thanks for sharing...

If I remember right @Akira1364 posted something about  "it's not a good idea to use (glBegin) and (glEnd)"...
[VAOs, VBOs, IBOs)]

Yeah... I don't know anything about OpenGL... just the first thing that comes to my mind...  :)
Windows 7 Pro (x64 Sp1) & Windows XP Pro (x86 Sp3).

Handoko

  • Hero Member
  • *****
  • Posts: 5154
  • My goal: build my own game engine using Lazarus
Re: OPenGL Texture loader
« Reply #4 on: November 24, 2017, 09:22:59 am »
Yes, you're right. Akira1364 is good in using OpenGL and I learned a lot from him unfortunately I still haven't fully understood the modern method of using OpenGL, which he said.

He told me to:
Quote
use vertex array instead of pure immediate mode (glBegin/glEnd)
http://forum.lazarus.freepascal.org/index.php/topic,35313.msg256940.html#msg256940

I checked, it is true that vertex array already available on OpenGL 1.1 1977:
https://en.wikipedia.org/wiki/OpenGL#OpenGL_1.1

For someone who interested to learn modern OpenGL, try this:
http://forum.lazarus.freepascal.org/index.php/topic,35649.0.html

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: OPenGL Texture loader
« Reply #5 on: November 24, 2017, 09:52:31 am »
If I remember right @Akira1364 posted something about  "it's not a good idea to use (glBegin) and (glEnd)"...
[VAOs, VBOs, IBOs)]
If speed is your concern, yes. Otherwise, it's fine (but the functions themselves have been marked deprecated, no idea if already obsolete in latest version). Count the number of function calls between and including glBegin and glEnd, there are just too much overhead. With VBA, you only call the function once, setting up all required data in an array previously. With VBO, things are even better, the result of that function call are stored for reuse in video card memory (super fast!). VAO are just array of VBO. I've never learned IBO so I don't know how it works.

Bad Sector

  • Jr. Member
  • **
  • Posts: 69
    • Runtime Terror
Re: OPenGL Texture loader
« Reply #6 on: November 24, 2017, 03:11:06 pm »
glBegin, glEnd and other functions from the so-called compatibility profile might not be supported in some newer OpenGL implementations, but the chances of encountering such an implementation are extremely low - especially for a desktop application. Existing implementations aren't going to remove them.

What you *may* face, however, is lack of the OpenGL compatibility profile. This is a concept that only exists since OpenGL 3.2, so if you target previous versions of OpenGL (which can coexist on the same system as an OpenGL 3.2 or later implementation that doesn't implement the compatibility profile) you can still use glBegin, glEnd, etc but you cannot use newer stuff.

In practice the vast majority of computers out there will have full OpenGL support. A breakdown is something like this:

  • All windows systems support the full OpenGL (4.6, compatibility profile)
  • Mac systems have only core profile up to version 4.1, meaning you get either OpenGL 2.1 (which has glBegin, etc) or OpenGL 4.1 but without the compatibility profile (so no glBegin, etc)
  • Linux systems depend on your drivers: Nvidia and AMD proprietary drivers provides the full OpenGL up to 4.6, Intel and the open source drivers (those based on Mesa) are like Mac and only provide either OpenGL 2.1 or core profile version 4.5.

Note of course that all the above depend on drivers (e.g. in Windows if you have an Intel GPU you get up to OpenGL version 4.5). Also note that Mesa will soon have 4.6 and there was recently a bit of work to add support for the GL_ARB_compatibility extension (the precursor to the compatibility profile, which hopefully means that full support for the compatibility profile will eventually be added - after all they have most of the functionality there and you can even enable a gimped version of it, it is just that not everything is tied together properly to have full support).

So the only systems where you wont be able to use glBegin, glEnd, etc are Mac and Linux when using the open source Mesa based drivers and those *only* if you want to use them with the newer features (e.g. you want to mix calls to glBegin, glEnd, etc with calls to compute shaders). All systems allow you to use up to version 2.1 just fine (and keep in mind that version 2.1 was good enough to create Rage and the Wolfenstein The New Order, so it isn't like it is weak or anything).

In practice glBegin and glEnd are perfectly fine to use, especially for making tools (where Lazarus itself also shines). For example i made this texture painting tool a few years ago in an afternoon using Lazarus and it uses glBegin/glEnd for the viewport. Also my 3D world editor uses "deprecated" calls all over the place since they are *way* more convenient to use (which is why i started using OpenGL in the first place and why i consider OpenGL to still be the best 3D graphics API).
Kostas "Bad Sector" Michalopoulos
Runtime Terror

 

TinyPortal © 2005-2018