Заключение
Выглядит все это в итоге где-то так:
Обратите внимание на чайник. Он не наклонен под углом 45 градусов, т. к. я перевел его вершины в локальную (чайника) систему координат, в которой он имеет нормальную ориентацию.
Обезьяны на рисунке - это модель, взятая из файла примера 3DStudioMax. Дерево также из библиотеки обёектов.
Текстуры наложены "от балды" и без особого смысла.
Важные теоретические сведения по С++ на примерах:
C++ Копировать
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();//Конец файла?
Если ничего не понятно, то читайте Бьерн Страуструп "Язык программирования С++. Специальное издание" и Вам станет все понятным или еще менее понятным :).
Код двух функций:
C++ Копировать
#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; } } //---------------------------------------------------------------------------