Внимание!

Эта публикация перенесена в раздел уроков по адресу Загрузка .X (DirectX) файлов в OpenGL.
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

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

Загрузка трехмерных моделей

Для загрузки из файла трехмерных объектов (Meshs) в OpenGl , нет встроенных решений. Так, как OpenGL- это кросс-платформенная библиотека, которая была написана для вывода графики, а не для работы с файловой системой, потоками....

Поэтому хочу поведать, как грузить удобный, и простой для понимания загрузки, DirectX-совский .X файл. Мы будем использовать связку C# + Tao.Framework, поэтому желательно почитать пару уроков, как ее использовать.

[spoiler]
Небольшое отступление

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


Файлы трехмерных моделей в формате .X

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

Этот формат хранит информацию 3D объекта:

- координаты вершин(Vertex), последовательность их загрузки(Face);
- координаты нормалей(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 объекта(мэша)
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; - указывает какой тип полигона 
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 не удобен в плане необходимости лицензии.
Для того, чтобы не было мороки с малоизвестной всем программой, я модифицировал загрузчик для большей универсальности. Так, что он сможет грузит .x файлы экспортированные через Blender.

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

Единственное, что может показаться недостатком, так это то что придется установить Python(точнее его интерпретатор).
Сразу хочу предупредить, что этой мой пробный загрузчик и не исключены ошибки и малопроизводительные решения, а точнее должны быть)). Но как говориться: "нет программ без ошибок, просто плохо искали"))).


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

Приступим к делу. Сперва создаем проект оконного приложения С#.
- Добавим в references(ссылки) Tao.FreeGlut, Tao.DevIl, Tao.OpenGl, Tao.Platform.Windows




- Подключим библиотеки (директива using):

/*http://esate.ru, Flashhell*/


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




- Прописуем создания класса прям в 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)//считаем количество объектов и материалов для [B]выделения на них памяти[/B]
        {
           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;// тоже принцип что и в [B]GetFirstWord[/B]
        while (str[start] == ' ')
        {
           start++;
        }
        if (start > 0)
        {
           str = str.Substring(start, (str.Length - start));
        }
        string[] temp = str.Split(' ');
        return temp[1];// только возращаем второе слово
      }
      private void LoadMesh()//функция загрузки [B]Mesh[/B]-а
      {
        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-ом и пару управляющих элементов. Здесь и здесь можно почитать как это сделать. В исходники выложены готовая форма с рабочим классом, и со всем необходим: x-файлами, текстурами. Статья была написана с трудом и без сна :), если обнаружите банальные ошибки пожалуйста не обижайтесь.

При загрузке файла из примера-исходника подвигайте ползунки, т.к. размеры модели бывают разные, с ними иногда баг выходит не понятный))), но это не связано с загрузчиком, скорее с контролами.

Скачать исходные коды.

Исходный код урока

Надеюсь Вам пригодиться, в будущем планирую написать урок о сохранении Mesh в собственный двоичный формат и загрузки с него. Но это если этот урок хоть кому то пригодиться или следующий станет востребованный.
0       2120        07.01.2011        20

Внимание!

Эта публикация перенесена в раздел уроков по адресу Загрузка .X (DirectX) файлов в OpenGL.
К ней прикреплена новая отдельная ветка комментариев форума, которую вы можетет найти после текста публикации.
Обсуждение публикации рекуомендуется вести по новому адресу, который указан выше.

0  
07.01.2011 00:00:00
Отличный урок!
0  
07.01.2011 00:00:00
0  
08.01.2011 00:00:00
мм? просто из любопытства, а отличии между.Х и .ASE какие??)
0  
08.01.2011 00:00:00
Формат хранения. Все форматы хранят примерно по одинаковому принципу, просто у одних в бинарном формате, у других в текстовом.
Все сводится к разбору спецификации и написанию парсера.

А готовые решения (или база от чего оттолкнуться) — это гуд.
0  
09.01.2011 00:00:00
а я свой формат писал =)) и буду писать, по мне так лучше все свое
0  
14.01.2011 00:00:00
Прикольно, но .x формат принято использовать в дх, и загружать его там проще…
0  
14.01.2011 00:00:00
В dx есть готовый класс для загрузки. Указал путь к файлу и все дела, загружать даже и не надо в принципе).
Любой формат которые удобнее, тот грузим. И принято или не принято, чтобы загружать Mesh в OpenGL надо самому искать решения этой задачи.
А можно поинтересоваться, что по Вашему мнению принято использовать с OpenGL?!
0  
15.01.2011 00:00:00
Ну, для полноты картины теперь не хватает только загрузчика файлов *.FBX (экспорт по умолчанию из последних 3d studio max).
Благо к ним есть целый SDK с примерами, чем не мог похвастаться *.3ds.
0  
15.01.2011 00:00:00
Flashhell, я имею ввиду, что microsoft далала этот формат для DirectX. В общем можно использовать формат .x для хранения моделей и в OpenGL. И вообще хорошо, когда ваша программа поддерживает много расширений… Определённого формата для хранения моделей и анимации в GL нет, по этому всё зависит от программиста.
0  
03.02.2011 00:00:00
а какой источник?
0  
03.02.2011 00:00:00
В смысле?
0  
04.02.2011 00:00:00
Ты брал за основу информацию с какого-то сайта? Или ты сам придумал этот способ?
0  
04.02.2011 00:00:00
loadImage(string imageUrl)//функция открытия картинки(текстуры)
MakeGlTexture(int Format, IntPtr pixels, int w, int h);//привязка текстуры
Функции открытия и привязки текстуры с , а загрузчик сам писал, отталкиваясь от примера — .
А что?
0  
04.02.2011 00:00:00
Ищу сайт с хорошими уроками…
0  
04.02.2011 00:00:00
а самому написать никак??
0  
07.02.2011 00:00:00
хе-хе))
0  
04.02.2011 00:00:00
Можно сделать так: создать свой формат и сделать для него загрузчик, потом сделать конвертер всех современных форматов в свой, тем самым экономим очень и очень много времени и написания лишнего кода (хотя может именно так все и делают =)) )
0  
10.12.2012 00:00:00
хай, ну ТЗ нет. просто я зарылся в работе сейчас, а месяц назад, когда Flashhell'a пригласил потестить, я активно каждый день что нить добавлял или правил на новом сайте, и на тот момент конешн в плане тестирования было раздолье. А сейчас так сказать спад моей активности, но можно просто залезть, поюзать что есть, поизучать, обратить внимание на косяки (в основном косяки раздела «е-сеть», там аналог социальной сети, но тоже не доделан еще).
Можно создавать группы, темы на форуме и т.д. Полностью другой движок.
Но очень много нового функционала еще нет. Ну и много нвого есть =) Можно поделится впечатлениями.
Так теперь еще живая лента, чат и т.д. Есть плюшки интересные.
0  
10.12.2012 00:00:00
ну и сразу после восстановления пароля — заполняй профиль, добавлялся в друзья, в общем начинай юзать есеть
0  
11.12.2012 00:00:00
Окай!
Буду ковырять на выходных и когда на работе скучно становится ;-)
^