Войти
ПрограммированиеСтатьиГрафика

Формат 3DS: Первый шаг. (4 стр)

Заключение

Выглядит все это в итоге где-то так:

Изображение

Обратите внимание на чайник. Он не наклонен под углом 45 градусов, т. к. я перевел его вершины в локальную (чайника) систему координат, в которой он имеет нормальную ориентацию.

Обезьяны на рисунке - это модель, взятая из файла примера 3DStudioMax. Дерево также из библиотеки объектов.

Текстуры наложены "от балды" и без особого смысла.

Важные теоретические сведения по С++ на примерах:

ifstream ifs;//Переменная типа файловый поток чтения, где ifs просто имя переменной
//Открыть файл с именем fname, как бинарный для чтения и поставить файловый указатель в начало
ifs.open(fname.c_str(), ios_base::in | ios_base::binary | ios_base::beg);
ifs.is_open();//Файл открыт?
ifs.read((char*)&chunk_id,2);//Считать в переменную  chunk_id 2 байта; причем часть кода
//(char*)& является обязательной и преобразует адрес переменной в указатель на символ
ifs.ignore(chunk_len-6);//Пропустить chunk_len-6 байт; перейти на  chunk_len-6 байт вперед
ifs.seekg(ifs.tellg()-6);//Поставить файловый указатель на позицию ifs.tellg()-6
ifs.tellg();//Получить текущую позицию файлового указателя
ifs.eof();//Конец файла?

Если ничего не понятно, то читайте Бьерн Страуструп "Язык программирования С++. Специальное издание" и Вам станет все понятным или еще менее понятным :).

Код двух функций:

#include <ifstream.h>
#include <string.h>
//---------------------------------------------------------------------------
unsigned int T3DMesh::FindChunk(ifstream& ifs, unsigned short id, bool isParent)

/* Данная функция получает ссылку на файловый поток, идентификатор искомого блока и 
   логическую переменную указывающую как начинается поиск, с родительского блока или
   внутри его. Это нужно для возможности поиска подблоков одного и того же типа 
   в одном родительском блоке (в перспективе) */
{
    unsigned short chunk_id;//Идентификатор блока
    unsigned int chunk_len;//Смещение к следующему блоку (от chunk_id)
    //Найдем нужный блок в файле *.3DS
    //Если файловый указатель указывает на родительский chunk, то
    //нужно прочитать заголовочные данные родительского блока
    if(isParent)
    {
        //Читаем идентификатор родительского блока и его длину
        ifs.read((char*)&chunk_id,2);
        ifs.read((char*)&chunk_len,4);
        //Если это блок объекта, то нужно пропустить строку имени объекта
        if(chunk_id==0x4000)
        {
            char ch;
            do
            {
                ifs.read((char*)&ch,1);
            }while(ch!='\0' && !ifs.eof());
        }
    }
    //Начинаем поиск внутри родительского блока
    do
    {
        //Читаем идентифекатор очередного блока и его длинну
        ifs.read((char*)&chunk_id,2);
        ifs.read((char*)&chunk_len,4);
        //Пропускаем все кроме блока объекта
        if(chunk_id!=id) ifs.ignore(chunk_len-6);
        else
        {
            //Вернемся к началу найденого блока и вернем его позицию
            ifs.seekg(ifs.tellg()-6);
            return (ifs.tellg());
        }
    }while(!ifs.eof());
    return false;
}
//---------------------------------------------------------------------------
bool T3DMesh::LoadMeshFrom3DS(string fname)
{
    //Переменные и константы
    const MAIN3DS=0x4D4D;//Идентификатор *.3DS файла
    const EDIT3DS=0x3D3D;//Блок редактора (this is the start of the editor config)
    const EDIT_OBJECT=0x4000;//Объект (сетка, источник света, камера)
    //------ sub defines of EDIT_OBJECT
    const OBJ_TRIMESH=0x4100;
    //------ sub defines of OBJ_TRIMESH
    const TRI_VERTEXLIST=0x4110;
    const TRI_VERTEXOPTIONS=0x4111;
    const TRI_MAPPINGCOORS=0x4140;
    const TRI_MAPPINGSTANDARD=0x4170;
    const TRI_FACELIST=0x4120;
    const TRI_SMOOTH=0x4150;
    const TRI_MATERIAL=0x4130;
    const TRI_LOCAL=0x4160;
    const TRI_VISIBLE=0x4165;
    //Все остальные блоки нас не волнуют
    unsigned short chunk_id;//Идентификатор блока
    unsigned int chunk_pos;//Позиция начала блока
    unsigned int chunk_temppos;//Временная позиция начала блока
    unsigned int chunk_len;//Смещение к следующему блоку (от chunk_id)

    //Откроем файл 3DS
    ifstream ifs;
    ifs.open(fname.c_str(),ios_base::in | ios_base::binary
        | ios_base::beg);
    if(!ifs.is_open())
    {
        //Не удалось открыть файл
        return false;
    }
    else
    {
        //Проверим является ли файл *.3DS формата
        ifs.read((char*)&chunk_id,2);
        ifs.read((char*)&chunk_len,4);
        if(chunk_id!=MAIN3DS) return false;
        ifs.seekg(ifs.tellg()-6);
        //Следующий должен быть блок редактора
        chunk_pos=FindChunk(ifs,EDIT3DS);
        if(chunk_pos==0) return false;
        //Пропускаем все кроме блока объекта
        chunk_pos=FindChunk(ifs,EDIT_OBJECT);
        if(chunk_pos==0) return false;
        //Читаем имя объекта (если оно нам нужно)
        //...
        //Пропускаем все кроме блока объекта
        chunk_pos=FindChunk(ifs,OBJ_TRIMESH);
        if(chunk_pos==0) return false;
        //Запомним позицию блока OBJ_TRIMESH
        chunk_temppos=chunk_pos;
        //Если мы дошли до сюда, то мы нашли объект-сетку. Считаем его
        //---Дойдем до блока вершин---
        chunk_pos=FindChunk(ifs,TRI_VERTEXLIST);
        if(chunk_pos==0) return false;
        ifs.ignore(6);
        //Считаем вершины предварительно выделив память
        ifs.read((char*)&nVertexs,2);
        //Выделим память
            //Если уже выделяли уничтожим и выделим снова
            if(Vertexs) delete[] Vertexs;
            if(TexCoords) delete[] TexCoords;
            if(Triangles) delete[] Triangles;
        Vertexs=new TPointR3[nVertexs];
        //Считаем
        for(int i=0;i<nVertexs;i++)
        {
            ifs.read((char*)&(Vertexs[i].r0),4);
            ifs.read((char*)&(Vertexs[i].r2),4);//y и z поменяны местами
            ifs.read((char*)&(Vertexs[i].r1),4);
        }
        //---Дойдем до списка текстурных вершин---
        ifs.seekg(chunk_temppos);
        chunk_pos=FindChunk(ifs,TRI_MAPPINGCOORS);
        if(chunk_pos==0) return false;
        ifs.ignore(6);
        //Считаем текстурные вершины предварительно выделив память
        unsigned short nTexCoords;
        ifs.read((char*)&nTexCoords,2);
        //Вылелим память
        TexCoords=new TPointR2[nTexCoords];
        //Считаем
        for(int i=0;i<nTexCoords;i++)
        {
            ifs.read((char*)&(TexCoords[i].r0),4);
            ifs.read((char*)&(TexCoords[i].r1),4);
        }
        //---Дойдем до списка граней---
        ifs.seekg(chunk_temppos);
        chunk_pos=FindChunk(ifs,TRI_FACELIST);
        if(chunk_pos==0) return false;
        ifs.ignore(6);
        //Считаем грани предварительно выделив память
        ifs.read((char*)&nTriangles,2);
        //Вылелим память
        Triangles=new TFace3DS[nTriangles];
        //Считаем
        for(int i=0;i<nTriangles;i++)
        {
            ifs.read((char*)&(Triangles[i].v0),2);
            ifs.read((char*)&(Triangles[i].v1),2);
            ifs.read((char*)&(Triangles[i].v2),2);
            ifs.ignore(2);
        }
        //---Дойдем к данным о локальной системе объекта---
        ifs.seekg(chunk_temppos);
        chunk_pos=FindChunk(ifs,TRI_LOCAL);
        if(chunk_pos==0) return false;
        ifs.ignore(6);
        //Совершим необходимые преобразования над вершинами объекта
        float Local[12]={0.0f};
        float x0,x1,x2;
        ifs.read((char*)&Local,sizeof(float)*12);
        for(int i=0;i<nVertexs;i++)//y и z поменяны местами
        {
            //Надо сначала сдвинуть его назад, то есть на вектор
            //-offset (НЕ на offset),
            Vertexs[i].r0-=Local[9];
            Vertexs[i].r2-=Local[10];
            Vertexs[i].r1-=Local[11];
            //а потом применить матрицу поворота rotmatr
            //Матрица записана построчно
            //Не забудьте - в ней y и z тоже везде обменены местами!
            x0=Vertexs[i].r0;
            x1=Vertexs[i].r1;
            x2=Vertexs[i].r2;
            Vertexs[i].r0=Local[0]*x0+Local[2]*x1+Local[1]*x2;
            Vertexs[i].r2=Local[3]*x0+Local[5]*x1+Local[4]*x2;
            Vertexs[i].r1=Local[6]*x0+Local[8]*x1+Local[7]*x2;
        }
        //---Дойдем к данным о материале объекта---
        ifs.seekg(chunk_temppos);
        chunk_pos=FindChunk(ifs,TRI_MATERIAL);
        if(chunk_pos==0) return false;
        ifs.ignore(6);
        //Считаем название первого попавшегося материала 
        //(он у нас один для всего объекта)

        //...

        //Все хорошо - объект считан
        return true;
    }
}
//---------------------------------------------------------------------------
Страницы: 1 2 3 4

#3DS, #игровой объект, #импорт, #модель, #экспорт

8 мая 2003

Комментарии [65]