Уроки Коммент.

Создание игр » DirectX 9, Featured » Загрузка модели из 3d MAX – binary

Загрузка модели из 3d MAX – binary

загрузка модели из 3d studio maxЗагрузку модели из 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, Оптимизация, Создание движка

6 комментариев на "Загрузка модели из 3d MAX – binary"
  1. Antony пишет:

    Большое спасибо!
    На основе этой статьи (и др. про экспорт из 3d max) я буду писать загрузчик из редактора, с которым сам работаю. Надеюсь, что получиться.

    1. Вячеслав пишет:

      Я тоже надеюсь ) Сегодня, чуть попозже, ещё выложу апдейт кода экспортера из 3d max – нашёл там вчера небольшие помарки.

  2. Antony пишет:

    По поводу апдейта кода экспортёра – это будет отдельная статья/новость? Чтобы не пропустить.

    1. Вячеслав пишет:

      Нет, апдейт я залил ещё 1го числа вечером. Ссылку в той статье (линк на проект с исходным кодом), соответственно, поменял на новую.

  3. YuRik пишет:

    Вячеслав, спасибо за статью и продолжайте писать!

    1. Вячеслав пишет:

      YuRik, писать продолжаю. Спасибо, что поддерживаете моё начинание ;-)

Оставить комментарий

*

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>