Создание игр » DirectX 9, Featured » Загрузка модели из 3d MAX – binary
Загрузка модели из 3d MAX – binary
Загрузку модели из 3d MAX в DirectX мы с Вами уже рассмотрели в одном из предыдущих уроков Загрузка модели из 3d MAX. При этом мы загружали модель, экспорт которой был сделан с помощью 3d max script. Однако, в уроке Экспорт из 3d max – 3d max sdk мы с Вами рассмотрели как выгрузить модель из 3d max в бинарный формат, при этом выгрузив не только базовые данные модели, но и такие важные, как вектора TBN, при этом в сразу оптимизированном виде. В этом уроке нашей задачей будет написать загрузчик всех этих данных, сохранить эти данные в нашей модели и, в следующих уроках по созданию движка для игры, научиться наиболее эффективно использовать эти данные. Загрузить модель при этом нам будет не сложнее, чем в прошлый раз, а может быть даже и немного проще, т.к. этапы оптимизации данных уже выполнены нами во время экспорта данных из 3d max.
Загрузка бинарных данных модели
Загрузка модели из бинарных данных не сложнее, чем запись модели в файл в этом бинарном формате. Особенно, если учесть, что код для выгрузки у нас уже готов – он может оказать нам большую помощь в решении этой проблемы – мы возьмём уже готовую структуру данных модели, а так же лишь частично переделаем код записи модели в файл для того, что бы вместо записи этот код мог загружать модель из файла.
В общем, самым простым решением будет взять тот код, что мы использовали для сохранения модели в файл и просто доработать его в нужных местах, научив загружать модель вместо того, что бы записывать.
Первым делом, внесём правки в исходный код хидеров меша:
struct MeshHeader { DWORD flags[16]; }; class CSubMesh { WORD m_curr_start_index; //! Идентификатор материала данного сабмеша DWORD dwMatID; //! Вертексы сабмеша std::vector<MyVertex> m_vertices; //! Индексы (для сборки граней) сабмеша std::vector<WORD> m_indices; protected: friend class CMesh; void SetStartIndex(WORD index); public: CSubMesh(DWORD iMatID = 0); ~CSubMesh(void); //! Загрузка сабмеша из потока bool Load(std::istream& in); size_t GetVertsCount(); size_t GetIdxCount(); std::vector<MyVertex>& GetVerts() { return m_vertices; } std::vector<WORD>& GetIdx() { return m_indices; } WORD GetStartIndex() { return m_curr_start_index; } }; class CMesh { std::vector<CSubMesh> m_vecSubMeshes; // index buffer LPDIRECT3DINDEXBUFFER9 m_pIB; // vertex buffer LPDIRECT3DVERTEXBUFFER9 m_pVB; void Release(); protected: friend class Graphics; LPDIRECT3DINDEXBUFFER9 GetIB() { return m_pIB; } LPDIRECT3DVERTEXBUFFER9 GetVB() { return m_pVB; } CMesh(void); virtual ~CMesh(void); virtual bool Load(const std::string& strFileName); public: HRESULT Draw(LPDIRECT3DDEVICE9 pd3dDevice); }; |
Функция чтения меша из файла стала даже короче:
bool CMesh::Load( const std::string& strFileName ) { std::string contents = GetFileAsString(strFileName); if (contents.length()==0) return false; // поток ввода из "файла" std::stringstream in(contents); MeshHeader meshHeader; int iTotalVerts = 0; int iTotalIdx = 0; unsigned int subMeshesCount = 0; in.read((char*)&meshHeader, sizeof(MeshHeader)); in.read((char*)&subMeshesCount, sizeof(unsigned int)); m_vecSubMeshes.resize(subMeshesCount); for (unsigned int i=0; i<subMeshesCount; i++) { m_vecSubMeshes[i].Load(in); iTotalVerts += m_vecSubMeshes[i].GetVertsCount(); iTotalIdx += m_vecSubMeshes[i].GetIdxCount(); } // создаём Vertex Buffer m_pVB = Graphics::get().CreateVertexBuffer(iTotalVerts*sizeof(MyVertex), D3DUSAGE_WRITEONLY, D3DPOOL_MANAGED); if (m_pVB==NULL) // если ошибка return false; // создаём Index Buffer m_pIB = Graphics::get().CreateIndexBuffer(iTotalIdx*sizeof(WORD), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED); if (m_pIB==NULL) // если ошибка { Release(); return false; } void* pData; if (FAILED(m_pVB->Lock(0, 0, &pData, 0))) { Release(); return false; } int offset = 0; char* pChData=(char*)pData; for (unsigned int i=0; i<subMeshesCount; i++) { size_t len = m_vecSubMeshes[i].GetVertsCount()*sizeof(MyVertex); memcpy(pChData, &m_vecSubMeshes[i].GetVerts()[0], len); m_vecSubMeshes[i].SetStartIndex(offset); offset += m_vecSubMeshes[i].GetVertsCount(); pChData += len; } m_pVB->Unlock(); if (FAILED(m_pIB->Lock(0, 0, &pData, 0))) { Release(); return false; } pChData=(char*)pData; for (unsigned int i=0; i<subMeshesCount; i++) { size_t len = m_vecSubMeshes[i].GetIdxCount()*sizeof(WORD); memcpy(pChData, &m_vecSubMeshes[i].GetIdx()[0], len); pChData += len; } m_pIB->Unlock(); return true; } |
Но я вынес загрузку сабмешей в отдельный класс, чтение в котором вообще элементарно:
bool CSubMesh::Load( std::istream& in ) { // это дефайн что бы было проще писать в файл #define _read(data) in.read((char*)&data, sizeof(data)) WORD iVertCount; DWORD iIdxCount; _read(dwMatID); _read(iVertCount); _read(iIdxCount); m_vertices.resize(iVertCount); m_indices.resize(iIdxCount); in.read((char*)&m_vertices[0], iVertCount*sizeof(MyVertex)); in.read((char*)&m_indices[0], iIdxCount*sizeof(WORD)); return true; } |
Загрузка модели – сабмеши
Поскольку наша модель может состоять из нескольких саб-мешей, код отрисовки модели изменился, я вынес его в класс самой загруженной модели:
HRESULT CMesh::Draw( LPDIRECT3DDEVICE9 pd3dDevice) { int startIndex=0; for (size_t i=0; i<m_vecSubMeshes.size(); i++) { HRESULT hr = pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, m_vecSubMeshes[i].GetStartIndex(), m_vecSubMeshes[i].GetVertsCount(), startIndex, m_vecSubMeshes[i].GetIdxCount()/3); startIndex += m_vecSubMeshes[i].GetIdxCount(); } return D3D_OK; } |
Пока без проверки на наличие ошибок даже.
Вот и всё, этот код полностью рабочий. Далее мы можем развивать его возможности. Потому как загрузка модели и её отображение на экране это лишь первый и очень маленький шаг на пути создания как игрового/графического движка, так и игры. Теперь наш импровизированный движок может экспортировать из макса и загружать модель с такими данными, как информация о TBN-пространстве, двух каналах текстурных координат, цвета вершин – в будущем это позволит нам рендерить очень качественную картинку, уровня не хуже, чем в самых современных играх.
Исходники прилагаю:
Исходники
Демка
Раздел: DirectX 9, Featured · Теги: Direct3D, Оптимизация, Создание движка
Большое спасибо!
На основе этой статьи (и др. про экспорт из 3d max) я буду писать загрузчик из редактора, с которым сам работаю. Надеюсь, что получиться.
Я тоже надеюсь ) Сегодня, чуть попозже, ещё выложу апдейт кода экспортера из 3d max – нашёл там вчера небольшие помарки.
По поводу апдейта кода экспортёра – это будет отдельная статья/новость? Чтобы не пропустить.
Нет, апдейт я залил ещё 1го числа вечером. Ссылку в той статье (линк на проект с исходным кодом), соответственно, поменял на новую.
Вячеслав, спасибо за статью и продолжайте писать!
YuRik, писать продолжаю. Спасибо, что поддерживаете моё начинание