Back to... GLOBE_3D

Source file : gl-textures.adb


-------------------------------------------------------------------------
--  GL.Textures - GL Textures model
--
--  Copyright (c) Rod Kay 2016
--  AUSTRALIA
--  Permission granted to use this software, without any warranty,
--  for any purpose, provided this copyright note remains attached
--  and unmodified if sources are distributed further.
-------------------------------------------------------------------------

with GL.IO,
     GL.Errors,
     Ada.Text_IO;

use Ada.Text_IO;

package body GL.Textures is

   -- Names

   function new_texture_Name return texture_Name
   is
      the_Name : aliased texture_Name;
   begin
      GL.GenTextures (1,  the_Name'Unchecked_Access);
      return the_Name;
   end new_texture_Name;

   procedure free (the_texture_Name : in texture_Name)
   is
      the_Name : aliased texture_Name := the_texture_Name;
   begin
      GL.DeleteTextures (1, the_Name'Unchecked_Access);
   end free;

   -- Coordinates
   --

   function to_texture_Coordinates_xz (the_Points  : in GL.Geometry.Vertex_array;
                                       Transform_S : in texture_Transform;          -- Transforms point X ordinate.
                                       Transform_T : in texture_Transform)          -- Transforms point Z ordinate.
                                       return p_Coordinate_2D_array
   is
      the_Coords : constant p_Coordinate_2D_array := new Coordinate_2D_array (1 .. the_Points'Last);
   begin
      for Each in the_Points'Range loop
         declare
            the_Vertex : GL.Geometry.Vertex renames the_Points (Each);
         begin
            the_Coords (Each).S :=       (the_Vertex (0) + Transform_S.Offset) * Transform_S.Scale;
            the_Coords (Each).T := 1.0 - (the_Vertex (2) + Transform_T.Offset) * Transform_T.Scale;
         end;
      end loop;

      return the_Coords;
   end to_texture_Coordinates_xz;

   function to_texture_Coordinates_xz (the_Points  : in GL.Geometry.Vertex_array;
                                       Transform_S : in texture_Transform;          -- Transforms point X ordinate.
                                       Transform_T : in texture_Transform)          -- Transforms point Z ordinate.
                                       return Coordinate_2D_array
   is
      the_Coords : Coordinate_2D_array (1 .. the_Points'Last);
   begin
      for Each in the_Points'Range loop
         declare
            the_Vertex : GL.Geometry.Vertex renames the_Points (Each);
         begin
            the_Coords (Each).S :=         (the_Vertex (0) + Transform_S.Offset) * Transform_S.Scale;
            the_Coords (Each).T := 1.0  -  (the_Vertex (2) + Transform_T.Offset) * Transform_T.Scale;
         end;
      end loop;

      return the_Coords;
   end to_texture_Coordinates_xz;

   -- xz_Generator

   overriding
   function to_Coordinates (Self : in xz_Generator;   the_Vertices : in GL.Geometry.Vertex_array) return GL.Textures.p_Coordinate_2D_array
   is
   begin
      return to_texture_Coordinates_xz (the_Vertices, Self.Transform_S, Self.Transform_T);
   end to_Coordinates;

   overriding
   function to_Coordinates (Self : in xz_Generator;   the_Vertices : in GL.Geometry.Vertex_array) return GL.Textures.Coordinate_2D_array
   is
   begin
      return to_texture_Coordinates_xz (the_Vertices, Self.Transform_S, Self.Transform_T);
   end to_Coordinates;

   -- texture objects

   function new_Texture (image_Filename : in String) return Object
   is
      the_Texture : Object;
   begin
      the_Texture.Name := new_texture_Name;
      GL.IO.Load (image_Filename,  Integer (the_Texture.Name),  blending_hint => the_Texture.is_Transparent);

      -- tbd: if not found, look in 'global' and 'level' zip files also, ala gautiers 'globe_3d.textures'.
      return the_Texture;
   end new_Texture;

   procedure destroy (Self : in out Object)
   is
   begin
      if Self.Pool = null then
         free (Self.Name);
      else
         free (Self.Pool.all, Self);
      end if;
   end destroy;

   procedure set_Name (Self : in out Object;   To : in GL.Uint)
   is
   begin
      Self.Name := To;
   end set_Name;

   function Name (Self : in Object) return GL.Uint
   is
   begin
      return Self.Name;
   end Name;

   function  is_Transparent (Self : in Object) return Boolean
   is
   begin
      return Self.is_Transparent;
   end is_Transparent;

   procedure enable (Self : in out Object)
   is
   begin
      pragma Assert (Self.Name > 0);

      GL.Enable      (GL.TEXTURE_2D);
      GL.BindTexture (GL.TEXTURE_2D, Self.Name);
   end enable;

   -- Pool
   --

   null_Image : array (1 .. 10_000_000) of aliased GL.Ubyte := (others => 0);

   -- tbd: add texture properties as 'in' parameters to habdle different types of textures.
   --
   function new_Texture (From : access Pool;   min_Width  : in Positive;
                                               min_Height : in Positive) return Object
   is
      the_Texture : aliased Object;

      Size_width  : constant Size := to_Size (min_Width);
      Size_height : constant Size := to_Size (min_Height);

      unused_texture_List : p_pool_texture_List := From.unused_Textures_for_size (Size_width, Size_height);
   begin
      if unused_texture_List = null then
         unused_texture_List                                     := new pool_texture_List;
         From.unused_Textures_for_size (Size_width, Size_height) := unused_texture_List;
      end if;

      -- Search for existing, but unused, object.
      --
      if unused_texture_List.Last > 0 then -- An existing unused texture has been found.
         the_Texture              := unused_texture_List.Textures (unused_texture_List.Last);
         unused_texture_List.Last := unused_texture_List.Last - 1;

         enable (the_Texture);

         GL.TexImage2D  (GL.TEXTURE_2D,  0,  GL.RGBA,
                         power_of_2_Ceiling (min_Width), power_of_2_Ceiling (min_Height),
                         0,
                         --gl.RGBA, gl.GL_UNSIGNED_BYTE, null);    -- nb: actual image is not initialised.
                         GL.RGBA, GL.GL_UNSIGNED_BYTE, null_Image (null_Image'First)'Access);    -- NB: Actual image is not initialised.
      else
         -- No existing, unused texture found, so create a new one.
         --
         the_Texture.Width  := Size_width;
         the_Texture.Height := Size_height;

         the_Texture.Pool := From.all'Access;

         the_Texture.Name := new_texture_Name;
         enable (the_Texture);

         PixelStore ( UNPACK_ALIGNMENT, 1 );                        -- tbd: these properties are tailored for impostors
         --TexParameter ( TEXTURE_2D, TEXTURE_WRAP_S, REPEAT );       --      make them user settable !
         --TexParameter ( TEXTURE_2D, TEXTURE_WRAP_T, REPEAT );
          -- TexParameter ( TEXTURE_2D, TEXTURE_WRAP_S, CLAMP );       --      make them user settable !
          -- TexParameter ( TEXTURE_2D, TEXTURE_WRAP_T, CLAMP );
         TexParameter ( TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE );       --      make them user settable !
         TexParameter ( TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE );

         --TexParameter (TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST);
         --TexParameter (TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST);
         TexParameter ( TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR);
         TexParameter ( TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR);

         TexEnv ( TEXTURE_ENV, TEXTURE_ENV_MODE, MODULATE );
         --TexEnv ( TEXTURE_ENV, TEXTURE_ENV_MODE, DECAL );

         GL.TexImage2D (GL.TEXTURE_2D,  0,  GL.RGBA,
                        power_of_2_Ceiling (min_Width), power_of_2_Ceiling (min_Height),
                        0,
                        --gl.RGBA, gl.GL_UNSIGNED_BYTE, null);    -- nb: actual image is not initialised.
                        GL.RGBA, GL.GL_UNSIGNED_BYTE, null_Image (null_Image'First)'Access);    -- NB: Actual image is not initialised.

         GL.Errors.Log;
      end if;

      return the_Texture;
   end new_Texture;

   procedure free (Self : in out Pool;   the_Texture : in Object)
   is
   begin
      if the_Texture.Name = 0 then
         return;
      end if;

      declare
         unused_texture_List : constant p_pool_texture_List := Self.unused_Textures_for_size (the_Texture.Width, the_Texture.Height);
      begin
         unused_texture_List.Last                                := unused_texture_List.Last + 1;
         unused_texture_List.Textures (unused_texture_List.Last) := the_Texture;
      end;
   end free;

   procedure vacuum (Self : in out Pool)
   is
   begin
      for each_Width in Self.unused_Textures_for_size'Range (1)
      loop
         for each_Height in Self.unused_Textures_for_size'Range (2)
         loop
            declare
               unused_texture_List : constant p_pool_texture_List := Self.unused_Textures_for_size (each_Width, each_Height);
            begin
               if unused_texture_List /= null
               then
                  for Each in 1 .. unused_texture_List.Last
                  loop
                     free (unused_texture_List.Textures (Each).Name);
                  end loop;

                  unused_texture_List.Last := 0;
               end if;
            end;
         end loop;
      end loop;
   end vacuum;

   function to_Size (From : in Positive) return Size
   is
   begin
      if    From <= 2    then  return s2;
      elsif From <= 4    then  return s4;
      elsif From <= 8    then  return s8;
      elsif From <= 16   then  return s16;
      elsif From <= 32   then  return s32;
      elsif From <= 64   then  return s64;
      elsif From <= 128  then  return s128;
      elsif From <= 256  then  return s256;
      elsif From <= 512  then  return s512;
      elsif From <= 1024 then  return s1024;
      elsif From <= 2048 then  return s2048;
      end if;

      Put_Line ("to_Size: From: " & Positive'Image (From));

      raise Constraint_Error;
   end to_Size;

   function power_of_2_Ceiling (From : in Positive) return GL.Sizei
   is
   begin
      if    From <= 2    then  return 2;
      elsif From <= 4    then  return 4;
      elsif From <= 8    then  return 8;
      elsif From <= 16   then  return 16;
      elsif From <= 32   then  return 32;
      elsif From <= 64   then  return 64;
      elsif From <= 128  then  return 128;
      elsif From <= 256  then  return 256;
      elsif From <= 512  then  return 512;
      elsif From <= 1024 then  return 1024;
      elsif From <= 2048 then  return 2048;
      end if;

      raise Constraint_Error;
   end power_of_2_Ceiling;

   function Size_width  (Self : in Object) return Size
   is
   begin
      return Self.Width;
   end Size_width;

   function Size_height (Self : in Object) return Size
   is
   begin
      return Self.Height;
   end Size_height;

end GL.Textures;

GLOBE_3D: Ada library for real-time 3D rendering. Ada programming.