【DirectX12】4.用FBX_SDK读取网格数据

用FBX_SDK读取网格数据

1.前言

前一篇讲了如何配置FBX_SDK,这一篇来看如何读取模型的网格数据,效果如下:

2.导出

首先随便创建一个场景,然后导出:

在导出的时候勾选上三角算法,将网格全部以三角形面的形式存储,这样方便DirectX的API使用:

有些模型在导出三角面时会失败,我在3ds max里转化为可编辑多边形后就导出成功了。

当然也可以在代码中执行转换的操作,不过编辑器里都会转换失败,代码就也很可能转换失败,所以我还是在编辑器里导出时就转换好。

3.具体代码

DX需要的是顶点缓存和索引缓存,所以先定义一个类来缓存数据,如果以后有需要也可以方便实现自己的3D模型中转文件,这样不仅加快读取速度,并且可以避免版权问题。

所以如下,先用两个vector保存顶点和索引:

class MeshObject
{
public:
	UINT64 GetVertexDataSize()
	{
		return _vecVertex.size() * sizeof(DirectX::Vertex);
	}

	UINT8* GetVertexData()
	{
		return (UINT8*)(_vecVertex.data());
	}

	UINT64 GetIndexDataSize()
	{
		return _vecIndex.size() * sizeof(UINT32);
	}

	UINT8* GetIndexData()
	{
		return (UINT8*)(_vecIndex.data());
	}

	vector<Vertex> _vecVertex;
	vector<UINT32> _vecIndex;
};

先初始化FBX_SDK,设置iosetting,创建一个importer

// Initialize the SDK manager. This object handles all our memory management.
FbxManager* _lSdkManager = FbxManager::Create();

// Create the IO settings object.
FbxIOSettings* ios = FbxIOSettings::Create(_lSdkManager, IOSROOT);
_lSdkManager->SetIOSettings(ios);

// Create an importer using the SDK manager.
FbxImporter* lImporter = FbxImporter::Create(_lSdkManager, "");

然后用importer加载文件:

if (!lImporter->Initialize(lFilename, -1, _lSdkManager->GetIOSettings()))
{
	//error
}

接着用importer创建一个scene,然后释放importer,遍历scene的根节点,完成后再释放内存:

// Create a new scene so that it can be populated by the imported file.
FbxScene* lScene = FbxScene::Create(_lSdkManager, "myScene");

// Import the contents of the file into the scene.
lImporter->Import(lScene);

// The file is imported; so get rid of the importer.
lImporter->Destroy();

// Print the nodes of the scene and their attributes recursively.
// Note that we are not printing the root node because it should
// not contain any attributes.
MeshObject* ret = nullptr;
FbxNode* lRootNode = lScene->GetRootNode();
if (lRootNode) {
	ret = _init_mesh_object(lRootNode);
	/*for (int i = 0; i < lRootNode->GetChildCount(); i++)
		PrintNode(lRootNode->GetChild(i));*/
}
// Destroy the SDK manager and all the other objects it was handling.
_lSdkManager->Destroy();

_init_mesh_object函数,我们传入根节点,然后读取了网格数据。从root_node遍历有可能得到多个Mesh,下面的代码处理了一下,将多个Mesh放到了一起,当然也可以分开放:

DND::MeshObject* DNDFBX::_init_mesh_object(FbxNode* root_node)
{
	if (!root_node)
		return nullptr;

	MeshObject* ret = new MeshObject;
	int last_index = 0;//上一个模型的最大索引
	for (int i = 0; i < root_node->GetChildCount(); i++)
	{
		FbxNode* p_node = root_node->GetChild(i);
		for (int j = 0; j < p_node->GetNodeAttributeCount(); j++)
		{
			FbxNodeAttribute* p_attribute = p_node->GetNodeAttributeByIndex(j);
			if (p_attribute->GetAttributeType() == FbxNodeAttribute::eMesh)
			{
				FbxMesh* mesh = p_attribute->GetNode()->GetMesh();
				if (mesh == NULL)
				{
					return nullptr;
				}

				//数据是以形状存储的,会有重复的顶点,所以需要计算索引缓存
				int count_polygon = mesh->GetPolygonCount();
				//g_debug.Line(to_wstring(count_polygon));

				ret->_vecVertex.resize(last_index + count_polygon * 3);

				//已初始化标记
				vector<bool> vec_inited;
				vec_inited.resize(count_polygon * 3, false);

				int max_index = 0;
				for (int k = 0; k != count_polygon; ++k)
				{
					if (mesh->GetPolygonSize(k) != 3)
					{
						g_debug.Line(L"模型数据未三角化!");
						continue;
					}

					FbxVector4* ctrl_point = mesh->GetControlPoints();

					for (int l = 0; l != 3; ++l)
					{
						int index = mesh->GetPolygonVertex(k, l);
						if (index == -1)
						{
							g_debug.Line(L"获取顶点失败!");
							continue;
						}
						max_index = max(index, max_index);
						//依次记录顶点的索引,而顶点数据只存放一次
						//g_debug.Line(to_wstring(index));
						//g_debug.Line(String::Format(L"(%.0lf)(%.0lf)(%.0lf)", ctrl_point[index][0], ctrl_point[index][1], ctrl_point[index][2]));
						ret->_vecIndex.push_back(last_index + index);
						if (!vec_inited[index])
						{
							vec_inited[index] = true;
							ret->_vecVertex[last_index + index].position.x = (float)(ctrl_point[index][0]) * 0.01f;
							ret->_vecVertex[last_index + index].position.y = (float)(ctrl_point[index][1]) * 0.01f;
							ret->_vecVertex[last_index + index].position.z = -(float)(ctrl_point[index][2]) * 0.01f;

							ret->_vecVertex[last_index + index].color = { 1.0f, 1.0f, 1.0f, 1.0f };
						}

					}

				}
				last_index += max_index + 1;
			}
		}
	}
	ret->_vecVertex.resize(last_index + 1);
	return ret;
}

4.数据传递到DX

顶点缓存和索引缓存都是需要通过上载堆传递数据,使用com_ptr的局部变量需要在完成GPU的数据上传后才能被释放。这一部分比较麻烦,后面再详细捋一捋。

HRESULT hr = S_OK;
//-------------------------------------------------顶点缓存------------------------------------------------------------
// Create the vertex buffer.
//默认堆需要上载堆传递CPU数据

CD3DX12_HEAP_PROPERTIES heapProps0(D3D12_HEAP_TYPE_DEFAULT);
auto desc0 = CD3DX12_RESOURCE_DESC::Buffer(mesh_object->GetVertexDataSize());
hr = g_dx._device->CreateCommittedResource(
	&heapProps0,
	D3D12_HEAP_FLAG_NONE,
	&desc0,
	D3D12_RESOURCE_STATE_COPY_DEST,
	nullptr,
	IID_PPV_ARGS(&_vertexBuffer));
if (FAILED(hr))
{
	g_debug.Line(L"创建顶点缓存失败(DEFAULT)!");
	return;
}

//上载堆
com_ptr<ID3D12Resource> vertexBufferUploadHeap;

CD3DX12_HEAP_PROPERTIES heapProps1(D3D12_HEAP_TYPE_UPLOAD);
auto desc1 = CD3DX12_RESOURCE_DESC::Buffer(mesh_object->GetVertexDataSize());

hr = g_dx._device->CreateCommittedResource(
	&heapProps1,
	D3D12_HEAP_FLAG_NONE,
	&desc1,
	D3D12_RESOURCE_STATE_GENERIC_READ,
	nullptr,
	IID_PPV_ARGS(&vertexBufferUploadHeap));
if (FAILED(hr))
{
	g_debug.Line(L"创建顶点缓存失败(UPLOAD)!");
	return;
}
_vertexBuffer->SetName(L"vb0");

// Copy data to the intermediate upload heap and then schedule a copy
// from the upload heap to the vertex buffer.
//从内存复制到上载堆,再复制到GPU
D3D12_SUBRESOURCE_DATA vertexData = {};
vertexData.pData = mesh_object->GetVertexData();
vertexData.RowPitch = mesh_object->GetVertexDataSize();
vertexData.SlicePitch = vertexData.RowPitch;

UpdateSubresources<1>(g_dx._commandList.get(), _vertexBuffer.get(), vertexBufferUploadHeap.get(), 0, 0, 1, &vertexData);

auto num_barrier = CD3DX12_RESOURCE_BARRIER::Transition(_vertexBuffer.get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
g_dx._commandList->ResourceBarrier(1, &num_barrier);

// Initialize the vertex buffer view.
//创建顶点缓存视图
_vertexBufferView.BufferLocation = _vertexBuffer->GetGPUVirtualAddress();
_vertexBufferView.StrideInBytes = sizeof(DirectX::Vertex);
_vertexBufferView.SizeInBytes = mesh_object->GetVertexDataSize();

//-------------------------------------------------索引缓存------------------------------------------------------------

CD3DX12_HEAP_PROPERTIES heapProps2(D3D12_HEAP_TYPE_DEFAULT);
auto desc2 = CD3DX12_RESOURCE_DESC::Buffer(mesh_object->GetIndexDataSize());

hr = g_dx._device->CreateCommittedResource(
	&heapProps2,
	D3D12_HEAP_FLAG_NONE,
	&desc2,
	D3D12_RESOURCE_STATE_COPY_DEST,
	nullptr,
	IID_PPV_ARGS(&_indexBuffer));
if (FAILED(hr))
{
	g_debug.Line(L"创建索引缓存失败(DEFAULT)!");
	return;
}

com_ptr<ID3D12Resource> indexBufferUploadHeap;

CD3DX12_HEAP_PROPERTIES heapProps3(D3D12_HEAP_TYPE_UPLOAD);
auto desc3 = CD3DX12_RESOURCE_DESC::Buffer(mesh_object->GetIndexDataSize());

hr = g_dx._device->CreateCommittedResource(
	&heapProps3,
	D3D12_HEAP_FLAG_NONE,
	&desc3,
	D3D12_RESOURCE_STATE_GENERIC_READ,
	nullptr,
	IID_PPV_ARGS(&indexBufferUploadHeap));

_indexBuffer->SetName(L"ib0");

// Copy data to the intermediate upload heap and then schedule a copy
// from the upload heap to the index buffer.
D3D12_SUBRESOURCE_DATA indexData = {};
indexData.pData = mesh_object->GetIndexData();
indexData.RowPitch = mesh_object->GetIndexDataSize();
indexData.SlicePitch = indexData.RowPitch;

UpdateSubresources<1>(g_dx._commandList.get(), _indexBuffer.get(), indexBufferUploadHeap.get(), 0, 0, 1, &indexData);
auto num_barrier1 = CD3DX12_RESOURCE_BARRIER::Transition(_indexBuffer.get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER);
g_dx._commandList->ResourceBarrier(1, &num_barrier1);

// Describe the index buffer view.
_indexBufferView.BufferLocation = _indexBuffer->GetGPUVirtualAddress();
_indexBufferView.Format = DXGI_FORMAT_R32_UINT;
_indexBufferView.SizeInBytes = mesh_object->GetIndexDataSize();

_numIndices = mesh_object->GetIndexDataSize() / 4;    // R32_UINT (SampleAssets::StandardIndexFormat) = 4 bytes each.

5.模型位置

之所以最终绘制出的模型都在原点,是因为FBX的单个模型的顶点都是以自身坐标系的,而自身的位置属性(位移、旋转、缩放)通过以下面方式得到:

FbxDouble3 translation = pNode->LclTranslation.Get();
FbxDouble3 rotation = pNode->LclRotation.Get();
FbxDouble3 scaling = pNode->LclScaling.Get();

其后可以通过构造矩阵,在读取时就变换到整体的坐标系,比如一个大的游戏场景,有很多固定不动的物体,我们就不必分开处理,直接当成一个整体模型来处理。

如果是动态的、反复出现的模型,就需要单独保存,通过上传矩阵到着色器来计算位置。

6.结语

目前我也只是一步一步的尝试,有很多不完善的地方,此文章只是简单说一下,等以后理解得比较透彻后再详细一步一步的说明。

代码上传到了这儿:

https://gitee.com/lveyou/dnd3d

接下来做一下按键检测,来控制镜头,方便详细的观察模型,好弄清楚它们的坐标系到底是怎样的。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: FBX SDK for Unity是为Unity游戏引擎设计的一种软件开发工具包(SDK)。FBX SDK是由Autodesk开发的一套用于处理和管理FBX文件格式的工具。FBX是一种常见的3D文件格式,用于在不同的3D应用程序之间进行互操作。使用FBX SDK,开发人员可以轻松地将FBX文件导入到Unity中,以便在游戏中使用。 FBX SDK for Unity提供了一些功能,使开发人员能够更好地在Unity中处理和管理FBX文件。首先,它允许开发人员读取和写入FBX文件。开发人员可以使用FBX SDK在Unity中读取FBX文件的内容,例如模型、材质、动画等,并将其转换为Unity能够使用的数据格式。相反地,开发人员还可以使用FBX SDK将Unity中的内容导出为FBX文件,以便在其他应用程序中使用。 其次,FBX SDK for Unity还提供了一些用于编辑和处理FBX文件的工具和函数。开发人员可以使用FBX SDK更改模型的层次结构、材质属性、动画轨迹等。这使得开发人员能够更好地控制和调整FBX文件以适应他们的游戏需求。 此外,FBX SDK for Unity还提供了一些用于导入和导出FBX文件的辅助功能。开发人员可以使用FBX SDK处理FBX文件的单位、坐标系、标记和动画设置等。这些功能可以让开发人员更加方便地与FBX文件进行交互,并确保在不同应用程序之间进行正确的数据转换。 总之,FBX SDK for Unity是一个非常有用的工具包,使开发人员能够更好地在Unity中处理和管理FBX文件,并与其他3D应用程序进行交互和互操作。它为开发人员提供了更多的灵活性和控制权,以实现他们的游戏开发目标。 ### 回答2: FBX SDK for Unity是一款Unity引擎的插件,用于导入和导出FBX文件格式。FBX文件是一种用于在不同3D软件之间交换模型、动画、材质等数据的通用文件格式。 FBX SDK for Unity提供了许多有用的功能和工具,可以帮助开发者更轻松地在Unity中使用FBX文件。通过该插件,开发者可以导入FBX文件,并将其转换为Unity中可用的资源,如3D模型、骨骼动画、材质、贴图等。同时,开发者也可以将Unity中的资源导出为FBX文件,以便与其他支持FBX格式的软件进行交流和共享。 FBX SDK for Unity还支持模型的网格优化、纹理压缩和骨骼动画的精细调整。通过可以控制导入和导出设置,开发者可以根据需求对FBX文件进行自定义处理,以获得更好的效果和性能。 除此之外,FBX SDK for Unity还提供了一些扩展工具,用于处理FBX文件中的蒙皮、法线、顶点色等数据,以及支持多个FBX文件的批量操作。 总之,FBX SDK for Unity是一款功能强大的插件,提供了导入和导出FBX文件的功能,以帮助Unity开发者更好地使用FBX文件,并加强与其他3D软件的兼容性和交互性。无论是从外部软件导入资源,还是将Unity资源导出到其他软件使用,FBX SDK for Unity都是一个非常有用的工具。 ### 回答3: FBX SDK for Unity是一种用于Unity开发环境的软件开发工具包,用于处理和加载FBX文件格式。FBX文件是一种常用的3D模型和动画文件格式,广泛用于游戏开发和动画制作等领域。 通过FBX SDK for Unity,开发者可以在Unity中轻松地导入、加载和展示来自于FBX文件的3D模型和动画。这个SDK提供了一系列功能强大的工具和接口,可以处理FBX文件中的各种元素,如网格、材质、动画、骨骼等等。开发者可以使用这些工具和接口来编辑、控制和操纵FBX模型和动画,在游戏中实现各种效果和功能。 FBX SDK for Unity还支持多种模型和动画文件的转换和导出功能。这意味着开发者可以将FBX文件转换为其他格式,如Unity支持的模型和动画格式,以便在不同的开发环境中使用。同时,也可以将Unity中创建的模型和动画导出为FBX文件,用于与其他软件或平台进行交互和共享。 FBX SDK for Unity为开发者提供了更多的选择和自由,使他们能够更灵活地使用FBX文件中的资源。通过这个SDK,开发者可以更加高效地创建和操作3D模型和动画,加快开发进度,并实现更具创意和吸引力的游戏效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值