tinygltf介绍

 源码地址:点击跳转吗

tinygltf 是一个轻量级的、用 C++ 编写的开源库,用于加载和保存 glTF 文件格式(JSON 和二进制格式)。glTF(GL Transmission Format)是一个开放的、专为高效传输和加载 3D 内容而设计的文件格式,广泛应用于网页、游戏、虚拟现实(VR)和增强现实(AR)等领域。

tinygltf 的主要特点:

  1. 轻量级和高效

    • tinygltf 被设计为轻量级的库,提供了简单的 API 和高效的实现,适合需要快速加载 glTF 文件的应用。
    • 它支持 glTF 2.0 标准,能够处理包括模型、纹理、动画、材质等在内的完整 3D 内容。
  2. 支持格式

    • tinygltf 支持加载和保存两种主要的 glTF 文件格式:JSON 格式(.gltf)和二进制格式(.glb)。其中,.glb 是将 glTF 文件内容和资源(如纹理)打包为一个二进制文件的格式。
    • 它还支持读取 glTF 文件中的网格、节点、材质、纹理、动画等数据。
  3. 跨平台

    • 作为一个 C++ 库,tinygltf 支持多平台,包括 Windows、Linux 和 macOS。
  4. 易于集成

    • tinygltf 是一个单头文件库,这使得它非常容易集成到其他项目中,无需复杂的构建配置。
  5. 无外部依赖

    • tinygltf 不依赖于任何外部库,只有标准 C++ 库的支持,因此它的依赖非常少,简化了集成和使用的复杂性。

核心功能:

  • 加载 glTF 文件:从磁盘加载 .gltf 或 .glb 文件,解析其中的 3D 内容。
  • 保存 glTF 文件:将 3D 内容保存为 .gltf 或 .glb 格式。
  • 处理 纹理、网格、动画:解析文件中的材质、纹理、网格、骨骼动画、相机和灯光等元素。
  • 支持扩展tinygltf 支持 glTF 文件格式的扩展,允许开发者使用自定义的功能。
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "glad/glad.h"
#include "tiny_gltf.h"

int main(int argc, char *argv[])
{
	tinygltf::Model model;
	tinygltf::TinyGLTF loader;
	std::string err;
	std::string warn;

	bool ret = loader.LoadBinaryFromFile(&model, &err, &warn, "zhiche.glb");

	if (!warn.empty()) qWarning() << warn.c_str();
	if (!err.empty()) qCritical() << err.c_str();
	if (!ret) {
		qCritical() << " load error!";
		throw std::runtime_error("Failed to load glTF model!");
	}

	qDebug() << "meshes.size: " << model.meshes.size();

	glm::vec3 translation(0.0);
	glm::vec3 rotation(0.0, 0.0, 0.0);
	glm::vec3 scale(1.0);
	/** @note Assume one Node just one Mesh */
	int nodeIndex = -1;
	QMap<int, QString> s_meshNames;
	QMap<int, QVector<glm::vec3>> s_vertices;
	QMap<int, QVector<unsigned int>> s_indices;
	QMap<int, QVector<glm::vec2>> s_texcoords;
	QMap<int, std::tuple<glm::vec3, glm::vec3, glm::vec3>> s_transforms;
	QMap<int, Material> s_materials;
	QMap<int, QVector<glm::vec3>> s_normals;

	for (const auto& node : model.nodes)
	{// #Node
		if (!node.translation.empty())
			translation = glm::vec3(node.translation[0], node.translation[1], node.translation[2]);
		if (!node.rotation.empty())
		{
			const auto& quaternion = glm::quat(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
			rotation = glm::eulerAngles(quaternion);
		}
		if (!node.scale.empty())
			scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]);

		++nodeIndex;

		// !Assume one Node just one Mesh
		const auto& mesh = model.meshes[node.mesh];// #Mesh
		int meshIndex = nodeIndex;
		s_meshNames.insert(meshIndex, QString::fromStdString(mesh.name));
		s_transforms.insert(meshIndex, std::make_tuple(translation, rotation, scale));

		for (const auto& primitive : mesh.primitives)
		{// #Primitive
			if (primitive.attributes.find("POSITION") != primitive.attributes.end())
			{// #Vertex
				const auto& positionAccessor = model.accessors[primitive.attributes.at("POSITION")];
				const auto& positionBufferView = model.bufferViews[positionAccessor.bufferView];
				const auto& positionBuffer = model.buffers[positionBufferView.buffer];

				QVector<glm::vec3> _vertices;
				const auto* vertices = reinterpret_cast<const float*>(&positionBuffer.data[positionBufferView.byteOffset + positionAccessor.byteOffset]);
				for (size_t i = 0; i < positionAccessor.count; ++i)
				{
					const auto& P = glm::vec3(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);
					_vertices << P;
				}
				s_vertices.insert(meshIndex, _vertices);
			}

			if (primitive.attributes.find("NORMAL") != primitive.attributes.end())
			{// #Normal
				const auto& normalAccessor = model.accessors[primitive.attributes.at("NORMAL")];
				const auto& normalBufferView = model.bufferViews[normalAccessor.bufferView];
				const auto& normalBuffer = model.buffers[normalBufferView.buffer];

				QVector<glm::vec3> _normals;
				const float* normals = reinterpret_cast<const float*>(&normalBuffer.data[normalBufferView.byteOffset + normalAccessor.byteOffset]);
				for (size_t i = 0; i < normalAccessor.count; ++i)
					_normals << glm::vec3(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]);
				s_normals.insert(meshIndex, _normals);
			}

			if (primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end())
			{// #Texcoord
				const auto& texcoordAccessor = model.accessors[primitive.attributes.at("TEXCOORD_0")];
				const auto& texcoordBufferView = model.bufferViews[texcoordAccessor.bufferView];
				const auto& texcoordBuffer = model.buffers[texcoordBufferView.buffer];

				QVector<glm::vec2> _texcoords;
				const float* texcoords = reinterpret_cast<const float*>(&texcoordBuffer.data[texcoordBufferView.byteOffset + texcoordAccessor.byteOffset]);
				for (size_t i = 0; i < texcoordAccessor.count; ++i)
					_texcoords << glm::vec2(texcoords[i * 2], texcoords[i * 2 + 1]);
				s_texcoords.insert(meshIndex, _texcoords);
			}

			if (primitive.indices >= 0)
			{// #Index
				const auto& indexAccessor = model.accessors[primitive.indices];
				const auto& indexBufferView = model.bufferViews[indexAccessor.bufferView];
				const auto& indexBuffer = model.buffers[indexBufferView.buffer];

				QVector<GLuint> _indices;
				if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
				{
					const unsigned short* indices = reinterpret_cast<const unsigned short*>(&indexBuffer.data[indexBufferView.byteOffset + indexAccessor.byteOffset]);
					for (size_t i = 0; i < indexAccessor.count; ++i)
						_indices << indices[i];
				} else if (indexAccessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
				{
					const unsigned int* indices = reinterpret_cast<const unsigned int*>(&indexBuffer.data[indexBufferView.byteOffset + indexAccessor.byteOffset]);
					for (size_t i = 0; i < indexAccessor.count; ++i)
						_indices << indices[i];
				}
				s_indices.insert(meshIndex, _indices);
			}

			if (primitive.material >= 0 && primitive.material < model.materials.size())
			{
				Material _material;

				const auto& material = model.materials[primitive.material];
				if (material.pbrMetallicRoughness.baseColorFactor.size() == 4)
				{// #Material (if size is not 4, it means bad datas in glTF file)
					float r = material.pbrMetallicRoughness.baseColorFactor[0];
					float g = material.pbrMetallicRoughness.baseColorFactor[1];
					float b = material.pbrMetallicRoughness.baseColorFactor[2];
					float a = material.pbrMetallicRoughness.baseColorFactor[3];
					_material.setBaseColor(QColor::fromRgbF(r, g, b, a));
				}

				if (material.pbrMetallicRoughness.metallicFactor > 0.0f)// [0, 1]
					_material.setMetallic(material.pbrMetallicRoughness.metallicFactor);
				else
					_material.setMetallic(0);// no metallic

				if (material.pbrMetallicRoughness.roughnessFactor > 0.0f)// [0, 1]
					_material.setRoughness(material.pbrMetallicRoughness.roughnessFactor);
				else
					_material.setRoughness(0);// no roughness

				s_materials.insert(meshIndex, _material);
			}
			else
				qInfo() << "read glTF no Material: define Default. # modelType";
		}
	}// end for #Node

	return 0;
}

demo:点击跳转

觉得有帮助的话,打赏一下呗。。

           

需要商务合作(定制程序)的欢迎私信!! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值