Загрузка .X (DirectX) файлов в OpenGL

Блоговая публикация пользователя: Flashhell Эта публикация была перенесена из личного блога пользователя в общие разделы уровок сайта.

Загрузка файлов трехмерных моделей в формате .X (DirectX)

Так как OpenGL - это кросс-платформенная библиотека, которая была написана для вывода графики, а не для работы с файловой системой и потоками, поэтому в ней нет встроенных решений для загрузки трехмерных объектов (Meshs) из файла.
В это уроке будет подробно рассмотрен несложный способ загрузки файла в формате .X (DirectX). Для этого будет использована связка C# + Tao.Framework, поэтому желательно почитать пару уроков, как ее использовать.

Обратите внимание!

После установки Tao все библиотеки из C:\Program Files\TaoFramework\lib нужно скопировать в C:\Windows\System32, иначе библиотек не будет в системе, а Visual Studio не будет их видеть.

.X файл - формат файла для хранения 3D-объектов (Meshs). Файл может быть записан как текстовый - ".X" или бинарный - ".bin".

Формат X хранит информацию 3D-объекта:
  • координаты вершин (Vertex), последовательность их загрузки (Face);
  • координаты нормалейv(Vertex), последовательность их загрузки (Face);
  • текстурные координаты, описание материалов и названия текстур.
Vertex - это точка в трехмерном пространстве, содержащая в себе координаты X Y Z.

Normals - это вектор направленный вверх от полигона, предназначенный для расчета света.

Полигон - это простая фигура сложенная в трехмерном пространстве из Vertex-ов.(например: треугольник, прямоугольник).

Вот выглядит простой куб, экспортированный в файл формата .X с помощью CyberMotion 3D-Designer (v12.0):

Код:
/*http://esate.ru, Flashhell*/

xof 0302txt 0064

Material NameMaterial { // Material - ключевое слово, 
1.0;1.0;1.0;1.000000;; // NameMaterial - имя материала
0.000000;
1.0;1.0;1.0;;
0.000000;0.000000;0.000000;;
}
Material Basic {
1.0;1.0;1.0;1.000000;;
0.000000;
1.0;1.0;1.0;;
0.000000;0.000000;0.000000;;
}
Mesh Box { // Mesh - ключевое слово; Box - имя 3D-объекта (Meshs)
8;     // количество Vertex-ов
-33.5;32.5;-33.5;,     // сами Vertex-ы
32.5;32.5;-33.5;,
32.5;-33.5;-33.5;,
-33.5;-33.5;-33.5;,
-33.5;32.5;32.5;,
32.5;32.5;32.5;,
32.5;-33.5;32.5;,
-33.5;-33.5;32.5;;
                   // Mesh Face
12;                 // количество Face-ов
3;1,2,0;,           // указывает какой тип полигона 
3;2,3,0;,           // используется (3 triangles, 4 quads...) 1,2,0;, = Face-ы
3;4,6,5;,
3;4,7,6;,
3;0,4,1;,
3;4,5,1;,
3;1,5,2;,
3;5,6,2;,
3;2,6,3;,
3;6,7,3;,
3;3,7,0;,
3;7,4,0;;

MeshMaterialList {
0;
12;
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0;;
}
MeshNormals {
12;             // количество нормалей Vertex-ы 
0.0;0.0;-1.0;, // Vertex-ы нормалей
0.0;0.0;-1.0;,
0.0;0.0;1.0;,
0.0;0.0;1.0;,
0.0;1.0;0.0;,
0.0;1.0;0.0;,
1.0;0.0;0.0;,
1.0;0.0;0.0;,
0.0;-1.0;0.0;,
0.0;-1.0;0.0;,
-1.0;0.0;0.0;,
-1.0;0.0;0.0;;
12;               // количество Фэйсов (Face)
3;0,0,0;,         // Фэйсы (Face)
3;1,1,1;,
3;2,2,2;,
3;3,3,3;,
3;4,4,4;,
3;5,5,5;,
3;6,6,6;,
3;7,7,7;,
3;8,8,8;,
3;9,9,9;,
3;10,10,10;,
3;11,11,11;;
}

}

А вот выглядит простой куб, но уже текстурированный (с материалами) в .X-файле, экспортированный с помощью CyberMotion 3D-Designer (v12.0):

Код:
/*http://esate.ru, Flashhell*/

xof 0302txt 0064
Material Material_2 { // Material - это ключевое слово, а Material_2 это его имя
1.0;1.0;1.0;1.000000;;
0.000000;
1.0;1.0;1.0;;
0.852088;0.852088;0.852088;;
TextureFilename {"cube_uv.jpg";} // имя текстуры
}
Material Basic {
1.0;1.0;1.0;1.000000;;
0.000000;
1.0;1.0;1.0;;
0.000000;0.000000;0.000000;;
}
Mesh Box_2 {
24;
-50.0;13.895662;-50.0;,
50.0;13.895662;-50.0;,
50.0;-87.104338;-50.0;,
-50.0;-87.104338;-50.0;,
-50.0;13.895662;50.0;,
50.0;13.895662;50.0;,
50.0;-87.104338;50.0;,
-50.0;-87.104338;50.0;,
50.0;13.895662;-50.0;,
50.0;-87.104338;-50.0;,
-50.0;13.895662;-50.0;,
-50.0;-87.104338;-50.0;,
-50.0;13.895662;50.0;,
50.0;-87.104338;50.0;,
50.0;13.895662;50.0;,
-50.0;-87.104338;50.0;,
-50.0;13.895662;-50.0;,
-50.0;13.895662;50.0;,
50.0;13.895662;-50.0;,
50.0;13.895662;50.0;,
50.0;-87.104338;-50.0;,
50.0;-87.104338;50.0;,
-50.0;-87.104338;-50.0;,
-50.0;-87.104338;50.0;;

12;
3;8,9,10;,
3;9,11,10;,
3;12,13,14;,
3;12,15,13;,
3;16,17,18;,
3;17,19,18;,
3;1,5,20;,
3;5,21,20;,
3;2,6,22;,
3;6,23,22;,
3;3,7,0;,
3;7,4,0;;

MeshMaterialList {
1;
12;
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0;;
{Material_2} // указание на использованный материал, по его имени
}
MeshNormals {
12;
0.0;0.0;-1.0;,
0.0;0.0;-1.0;,
0.0;0.0;1.0;,
0.0;0.0;1.0;,
0.0;1.0;0.0;,
0.0;1.0;0.0;,
1.0;0.0;0.0;,
1.0;0.0;0.0;,
0.0;-1.0;0.0;,
0.0;-1.0;0.0;,
-1.0;0.0;0.0;,
-1.0;0.0;0.0;;
12;
3;0,0,0;,
3;1,1,1;,
3;2,2,2;,
3;3,3,3;,
3;4,4,4;,
3;5,5,5;,
3;6,6,6;,
3;7,7,7;,
3;8,8,8;,
3;9,9,9;,
3;10,10,10;,
3;11,11,11;;
}

MeshTextureCoords { // текстурные координаты
24;
0.244999;0.34667;
0.510002;0.34667;
0.489998;0.673335;
0.244999;0.65333;
0.020005;0.34667;
0.734997;0.34667;
0.489998;0.979995;
0.020005;0.65333;
0.489998;0.34667;
0.489998;0.65333;
0.265003;0.34667;
0.265003;0.65333;
0.979995;0.34667;
0.755001;0.65333;
0.755001;0.34667;
0.979995;0.65333;
0.265003;0.326665;
0.265003;0.020005;
0.489998;0.326665;
0.489998;0.020005;
0.510002;0.65333;
0.734997;0.65333;
0.265003;0.673335;
0.265003;0.979995;;
}

}



Рассмотрев структуру файла внимательней, можно понять, какие слова являются ключевыми и как разграничиваются записи данных.

Загрузчик был специально создан под программу CyberMotion 3D-Designer, но CyberMotion 3D-Designer не совсем удобен, так как для его использования необходима лицензия.

Поэтому загрузчик был модифицирован для большей универсальности. Так что он сможет загружать -файлы, экспортированные через Blender.

Blender - бесплатная программа, которую легко найти и скачать, кроме того, она работает с многими известными форматами.

Единственное, что может показаться недостатком, так это то, что придется установить Python (точнее его интерпретатор).

Внимание! Данный загрузчик - пробный и не исключены ошибки и малопроизводительные решения.

Код загрузчика

Сначала создаем проект оконного приложения С#.

Добавим в References (ссылки) Tao.FreeGlut, Tao.DevIl, Tao.OpenGl, Tao.Platform.Windows.

Уроки OpenGL различных тематик: Добавление библиотек в проект Рисунок 1. Добавление библиотек в проект.
Подключим библиотеки (директива using):

Код:
/*http://esate.ru, Flashhell*/

using System.IO;
using Tao.FreeGlut;
using Tao.OpenGl;
using Tao.Platform.Windows;
using Tao.DevIl;


Уроки OpenGL различных тематик: Подключение библиотек Рисунок 2. Подключение библиотек.
Прописываем создание класса в Form1 (или как Вы его назвали) .cs:

Код:
/*http://esate.ru, Flashhell*/

public partial class Form1 : Form
    {}
public class mesh
    {}


Добавим глобальные переменные:

Код:
/*http://esate.ru, Flashhell*/

    public class mesh
    {
        int list;
        string[,] MaterialName;
        Mesh[] Meshs;
        StreamReader sw;
        int imageId;
        int count;
        int schetMaterial = 0;
        int kolMaterial = 0;
        uint mGlTextureObject = 0;
        short kolObject = 0;
        public string buf = "";
}


Первое, что необходимо нашему классу - это функция для получения первого слова в строке, а потом уже от полученного слова будут работать другие функции. Здесь лучше использовать вариант функции с классом загрузки .ASE файлов anModelLoader, или написать свою. Какую использовать, решать Вам:

Код:
/*http://esate.ru, Flashhell*/

        private string GetFirstWord(string word)
        {
            if (word == "") return ""; // если строка пуста, то выходим из функции
            int start = 0; // с этого (пока нулевого) символа начнем вырезку начальных пробелов
            while(word[start]==' ') // если пробелы есть, считаем до первой буквы
            {
                start++;
            }
            if(start>0) // если пробелы есть 
            {
                word = word.Substring(start,(word.Length-start)); // отрезаем пробелы
            }
            string[] s = word.Split(' '); // split-ом делим на подмассивы с разделителем пробел = ' ', 
                                     // чтобы получить массив слов без пробелов
            return s[0]; / /возвращаем первый элемент массива, то есть первое слово
        }

Теперь, когда есть базовая функция, можем приступить к функции определения ключевых слов:

Код:
/*http://esate.ru, Flashhell*/

        public bool Read()
        {
            string temp = sw.ReadLine(); // читаем строку из потока, поток (sw) мы создадим в другой функции
            if (temp == null)           // если ничего не зачитали с потока, выходим из функции
                return false;
            string buf2 = temp;     // записываем полученную строку, чтоб при необходимости к ней обратиться
            //buf = temp;
            temp = GetFirstWord(temp); // получаем первое слово в строке и перезаписываем temp
            switch (temp)     // проверяем на совпадение с ключевыми словами
            {
                case "Mesh":
                    count++;
                    LoadMesh();    /*каждому ключевому слову будет соответствовать функция загрузки определенного параметра объекта, каждую из которых мы вскоре рассмотрим*/ 
                    break;
                case "MeshNormals":
                    LoadNormals();
                    break;
                case "MeshTextureCoords":
                    LoadTextureCoords();
                    break;
                case "Material":
                    LoadMaterial(buf2,false);
                    break;
                case "MeshMaterialList": // сложные и мало понятные манипуляции 
                                        // для получения соответствующего Material-а для объекта
                    string tempo = "";
                    string str;
                    bool flag = false;
                    while (flag == false)
                    {
                        str = sw.ReadLine();
                        if (GetFirstWord(str) == "Material")
                        {
                            LoadMaterial(str,true);
                        }
                        if (str == @"}")
                            break;
                        string word = GetFirstWord(str);
                        if (word == @"}")
                            break;
                        for (int i = 0; i < kolMaterial; i++)
                        {
                            tempo = MaterialName[i, 0];
                            tempo = @"{" + tempo + @"}";
                            if (tempo == str)
                            {
                                                        Meshs[count].fileTexture = @"путь к текстурам" + MaterialName[i, 1];
/*
используйте следующие методы получения пути исполняемого файла, чтобы прикрепить к нему папку с файлами текстур
System.Reflection.Assembly assem = System.Reflection.Assembly.GetEntryAssembly();
string path = Path.GetDirectoryName(assem.Location);
*/
                                flag = true;
                                break;
                            }
                        }

                    }
                    break;
            }
            return true;
        }
        


Напишем функцию открытия файла:

Код:
/*http://esate.ru, Flashhell*/

        public void Open(string url)
        {
            sw = new StreamReader(url, Encoding.Default); // открываем поток чтения файла
            string temp;
            while ((temp = sw.ReadLine()) != null) // считаем количество объектов и материалов для выделения на них памяти
            {
                temp = GetFirstWord(temp);
                if (temp == "Mesh")
                    kolObject++;
                if (temp == "Material")
                    kolMaterial++;
            }
            sw.Close();
            Meshs = new Mesh[kolObject]; // выделяем память
            MaterialName = new string[kolMaterial, 2];
            count = -1;
            sw = new StreamReader(url, Encoding.Default); // снова открываем уже прочтенный поток
        }
       


Создаем конструкторы класса:

Код:
/*http://esate.ru, Flashhell*/

        public mesh()
        {

        }
        public mesh(string url) // основной конструктор для открытия и чтения
        {
            Open(url);
            while (Read() == true) // читаем по строке пока поток не кончиться
            {
            }
            DrawInMemory();
        }


Теперь создадим отдельные функции для загрузки определенных ключевыми словами (Mesh, MeshNormals, Material, MeshTextureCoords) данных и вложенных в них данных (Vertex, Face), но сначала создадим структуры, куда будем это все записывать:

Код:
/*http://esate.ru, Flashhell*/

    public struct Mesh
    {
        public string fileTexture;
        public Vertex[] MeshVertex;
        public int[] MeshFace;
        public Vertex[] MeshNormals;
        public int[] MeshFaceNormals;
        public float[,] TextCoords;
    }
    public struct Vertex
    {
        private float xx, yy, zz;
        public Vertex(float x, float y, float z)
        {
            xx = x;
            yy = y;
            zz = z;
        }
        #region prop
        public float X
        {
            get
            {
                return xx;
            }
            set
            {
                xx = value;
            }
        }
        public float Y
        {
            get
            {
                return yy;
            }
            set
            {
                yy = value;
            }
        }
        public float Z
        {
            get
            {
                return zz;
            }
            set
            {
                zz = value;
            }
        }
        #endregion


    }


Функции:

Код:
/*http://esate.ru, Flashhell*/

        private string LoadFileNameTexture(string StrTextureFilename) // функция загрузки имени файла текстуры
        {
            string[] temp = StrTextureFilename.Split('\"'); // имя файла взято в кавычки, поэтому делим на массивы отталкиваясь от этого
            return temp[1]; // возвращаем второй элемент массива, то есть то, что находится за первой кавычкой и до последней
        }
        private string LoadNameMaterial(string str) // функция загрузки имени Material-а
        {
            int start = 0; // тот же принцип, что и в GetFirstWord
            while (str[start] == ' ')
            {
                start++;
            }
            if (start > 0)
            {
                str = str.Substring(start, (str.Length - start));
            }
            string[] temp = str.Split(' ');
            return temp[1]; // только возращаем второе слово
        }
        private void LoadMesh() // функция загрузки Mesh-а
        {
            string temp = sw.ReadLine();
            int kol = Convert.ToInt32(temp.Replace(";", "")); // определяем количество строк данных
            Meshs[count].MeshVertex = new Vertex[kol]; // выделяем память под Vertex-ы подобъекта
            LoadVertex(Meshs[count].MeshVertex); // вызываем функцию загрузки Vertex-сных (геометрических) данных
            while ((temp = sw.ReadLine()) == null || temp == "") // пропускаем пустые строки
            {

            }
            kol = Convert.ToInt32(temp.Replace(";", "")); // определяем количество строк данных
            Meshs[count].MeshFace = new int[kol * 3]; // выделяем память под Face подобъекта
            LoadFace(Meshs[count].MeshFace, kol); // вызываем загрузку Face-ов

        }
        private void LoadNormals()
        {
            string temp = sw.ReadLine();
            int kol = Convert.ToInt32(temp.Replace(";", "")); // определяем количество строк данных
            Meshs[count].MeshNormals = new Vertex[kol]; // выделяем память
            LoadVertex(Meshs[count].MeshNormals); // вызываем функцию загрузки Vertex-сных(Геометрических) данных
            while ((temp = sw.ReadLine()) == null || temp == "") // пропускаем пустые строки
            {

            }
            kol = Convert.ToInt32(temp.Replace(";", ""));
            Meshs[count].MeshFaceNormals = new int[kol * 3]; // выделяем память
            LoadFace(Meshs[count].MeshFaceNormals, kol); // вызываем загрузку Face-ов

        }
        private void LoadTextureCoords()
        {
            string temp = sw.ReadLine();
            int kol = Convert.ToInt32(temp.Replace(";", "")); // определяем количество строк данных
            Meshs[count].TextCoords = new float[kol, 2]; // выделяем память
            LoadCoords(Meshs[count].TextCoords); // вызываем загрузку текстурных координат
        }
        private void LoadVertex(Vertex[] MeshVertex)
        {
             /*так как память мы выделяли под просчитанный размер, будем
считывать до конца выделенной памяти
Пример записи [B]Вертексов[/B]: -50.0;13.895662;-50.0;,  */
            for (int i = 0; i < MeshVertex.Length; i++)
            {
                 // заменяем точки в представлении числа с плавающей точкой,
                 // на запятые, чтобы правильно выполнилась функция 
                 // преобразования строки в дробное число 
                string temp = sw.ReadLine().Replace('.', ',');
                string[] ver = temp.Split(';');//разбиваем на под массивы
                MeshVertex[i].X = Convert.ToSingle(ver[0]);
                MeshVertex[i].Y = Convert.ToSingle(ver[1]);
                MeshVertex[i].Z = Convert.ToSingle(ver[2]);
            }
        }
        private void LoadFace(int[] Face, int KolString)
        {
            for (int i = 0; i < KolString; i++)
            {
                 /*Face начинаются с цифры указывающей на тип полигона, но нам нужны только tringles(и в основном только он используются, об 
исключениях я, к сожалению, не знаю).
Пример записи [B]Фэйсов[/B]: 3;0,0,0;,
*/
                string temp = sw.ReadLine();
                temp = temp.Remove(0, 2); // Так, что отбрасываем первые два символа
                temp = temp.Remove(temp.Length - 2); // и последнее точка с запятой (;,) нам не нужны
                string[] face = temp.Split(',');
                for (int y = 0; y < 3; y++)
                {
                    Face[i * 3 + y] = Convert.ToInt32(face[y]);
                }
            }
        }
        private void LoadCoords(float[,] TextureCoords)
        {
/*
TextureCoords.Length / 2 - так как у нас двумерный массив и его длина равна столбцы * на строки, поэтому делим на 2
Пример записи [B]текстурных координат[/B]: 0.244999;0.34667;;,
*/
            for (int i = 0; i < TextureCoords.Length / 2; i++)
            {
                string temp = sw.ReadLine().Replace('.', ',');
                string[] ver = temp.Split(';');
                TextureCoords[i, 0] = Convert.ToSingle(ver[0]);
                TextureCoords[i, 1] = Convert.ToSingle(ver[1]);
            }
        }
        private void LoadMaterial(string MaterialStr,bool flag)
        {
            MaterialName[schetMaterial, 0] = LoadNameMaterial(MaterialStr);
            while (true)
            {
                string tmp = sw.ReadLine();
                if (tmp == @"}")
                    break;
                string word = GetFirstWord(tmp);
                if (word == @"}")
                    break;
                if ("TextureFilename" == word)
                {
                    MaterialName[schetMaterial, 1] = LoadFileNameTexture(tmp);
                    if (flag == true)
                        Meshs[count].fileTexture = @"путь к текстурам" + MaterialName[schetMaterial, 1];
/*
используйте следующие методы получения пути исполняемого файла,
чтобы к ниму прикрепить папку с файлами текстур
System.Reflection.Assembly assem = System.Reflection.Assembly.GetEntryAssembly();
string path = Path.GetDirectoryName(assem.Location);
*/
                    schetMaterial++;
                    break;
                }
            }
        }


Теперь напишем функции для отрисовки геометрии, а также открытия и привязки текстуры:

Код:
/*http://esate.ru, Flashhell*/

        public void Draw() // вызов дисплейного списка
        {
            Gl.glCallList(list);
        }
        public void DrawInMemory() // создание дисплейного списка
        {
            int nom_l = Gl.glGenLists(1);
             // генерируем новый дисплейный список
            Vertex temp;
            Gl.glNewList(nom_l, Gl.GL_COMPILE);
            Gl.glPushMatrix();
            for (int i = 0; i < Meshs.Length; i++)
            {
                Gl.glBegin(Gl.GL_TRIANGLES);
                if (Meshs[i].fileTexture != null)
                {
                    loadImage(Meshs[i].fileTexture);
                    Gl.glEnable(Gl.GL_TEXTURE_2D);
                     // включаем режим текстурирования, указывая индификатор mGlTextureObject
                    Gl.glBindTexture(Gl.GL_TEXTURE_2D, mGlTextureObject);
                    for (int j = 0; j < Meshs[i].MeshFace.Length; j++)
                    {
                        int temps = Meshs[i].MeshFace[j];
                        Gl.glTexCoord2f(Meshs[i].TextCoords[temps, 0], Meshs[i].TextCoords[temps, 1]);
                        temp = Meshs[i].MeshNormals[Meshs[i].MeshFaceNormals[j]];
                        Gl.glNormal3f(temp.X, temp.Y, temp.Z);
                        temp = Meshs[i].MeshVertex[temps];
                        Gl.glVertex3f(temp.X, temp.Y, temp.Z);
                    }
                }
                else
                {
                    for (int j = 0; j < Meshs[i].MeshFace.Length; j++)
                    {
                        temp = Meshs[i].MeshNormals[Meshs[i].MeshFaceNormals[j]];
                        Gl.glNormal3f(temp.X, temp.Y, temp.Z);
                        temp = Meshs[i].MeshVertex[Meshs[i].MeshFace[j]];
                        Gl.glVertex3f(temp.X, temp.Y, temp.Z);
                    }
                }
                Gl.glEnd();
                Gl.glDisable(Gl.GL_TEXTURE_2D);
            }
            Gl.glPopMatrix();
            Gl.glEndList();
            list = nom_l;
        }
        private static uint MakeGlTexture(int Format, IntPtr pixels, int w, int h)
        {
             // индетефекатор текстурного объекта
            uint texObject;

             // генерируем текстурный объект
            Gl.glGenTextures(1, out texObject);

             // устанавливаем режим упаковки пикселей
            Gl.glPixelStorei(Gl.GL_UNPACK_ALIGNMENT, 1);

             // создаем привязку к только что созданной текстуре
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, texObject);

             // устанавливаем режим фильтрации и повторения текстуры
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
            Gl.glTexEnvf(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_REPLACE);

             // создаем RGB или RGBA текстуру
            switch (Format)
            {
                case Gl.GL_RGB:
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, w, h, 0, Gl.GL_RGB, Gl.GL_UNSIGNED_BYTE, pixels);
                    break;

                case Gl.GL_RGBA:
                    Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, w, h, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, pixels);
                    break;
            }

             // возвращаем индентификатор текстурного объекта

            return texObject;
        }
        private void loadImage(string imageUrl)
        {
            Il.ilGenImages(1, out imageId);
             // делаем изображение текущим
            Il.ilBindImage(imageId);
            if (Il.ilLoadImage(imageUrl))
            {
                 // если загрузка прошла успешно
                 // сохраняем размеры изображения
                int width = Il.ilGetInteger(Il.IL_IMAGE_WIDTH);
                int height = Il.ilGetInteger(Il.IL_IMAGE_HEIGHT);

                 // определяем число бит на пиксель
                int bitspp = Il.ilGetInteger(Il.IL_IMAGE_BITS_PER_PIXEL);

                switch (bitspp) // в зависимости оп полученного результата
                {
                     // создаем текстуру используя режим GL_RGB или GL_RGBA
                    case 24:
                        mGlTextureObject = MakeGlTexture(Gl.GL_RGB, Il.ilGetData(), width, height);
                        break;
                    case 32:
                        mGlTextureObject = MakeGlTexture(Gl.GL_RGBA, Il.ilGetData(), width, height);
                        break;
                }

                 // активируем флаг, сигнализирующий загрузку текстуры
                 // очищаем память
                Il.ilDeleteImages(1, ref imageId);


            }
        }
        


Класс готов, для его теста нужна форма с OpenGL Control и паруой управляющих элементов.

Здесь и здесь можно почитать, как это сделать. 

Нет доступа к просмотру комментариев.

^