Browse Source

More GLTF

Panagiotis Christopoulos Charitos 7 years ago
parent
commit
2cae521c22
2 changed files with 304 additions and 38 deletions
  1. 286 36
      tools/gltf_exporter/Exporter.cpp
  2. 18 2
      tools/gltf_exporter/Exporter.h

+ 286 - 36
tools/gltf_exporter/Exporter.cpp

@@ -45,23 +45,164 @@ Error Exporter::load()
 	return Error::NONE;
 }
 
-void Exporter::getAttributeInfo(
-	const tinygltf::Primitive& primitive, CString attribName, const U8*& buff, U32& stride, U32& count) const
+void Exporter::getAttributeInfo(const tinygltf::Primitive& primitive,
+	CString attribName,
+	const U8*& buff,
+	U32& stride,
+	U32& count,
+	Format& fmt) const
 {
 	const tinygltf::Accessor& accessor = m_model.accessors[primitive.attributes.find(attribName.cstr())->second];
 	count = accessor.count;
 	const tinygltf::BufferView& view = m_model.bufferViews[accessor.bufferView];
 	buff = reinterpret_cast<const U8*>(&(m_model.buffers[view.buffer].data[accessor.byteOffset + view.byteOffset]));
 	stride = view.byteStride;
+	ANKI_ASSERT(stride > 0);
+
+	const Bool normalized = accessor.normalized;
+	switch(accessor.componentType)
+	{
+	case TINYGLTF_COMPONENT_TYPE_BYTE:
+		switch(accessor.type)
+		{
+		case TINYGLTF_TYPE_SCALAR:
+			fmt = (normalized) ? Format::R8_SNORM : Format::R8_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC2:
+			fmt = (normalized) ? Format::R8G8_SNORM : Format::R8G8_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC3:
+			fmt = (normalized) ? Format::R8G8B8_SNORM : Format::R8G8B8_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC4:
+			fmt = (normalized) ? Format::R8G8B8A8_SNORM : Format::R8G8B8A8_SINT;
+			break;
+		default:
+			ANKI_ASSERT(!"TODO");
+		}
+		break;
+	case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
+		switch(accessor.type)
+		{
+		case TINYGLTF_TYPE_SCALAR:
+			fmt = (normalized) ? Format::R8_UNORM : Format::R8_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC2:
+			fmt = (normalized) ? Format::R8G8_UNORM : Format::R8G8_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC3:
+			fmt = (normalized) ? Format::R8G8B8_UNORM : Format::R8G8B8_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC4:
+			fmt = (normalized) ? Format::R8G8B8A8_UNORM : Format::R8G8B8A8_UINT;
+			break;
+		default:
+			ANKI_ASSERT(!"TODO");
+		}
+		break;
+	case TINYGLTF_COMPONENT_TYPE_SHORT:
+		switch(accessor.type)
+		{
+		case TINYGLTF_TYPE_SCALAR:
+			fmt = (normalized) ? Format::R16_SNORM : Format::R16_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC2:
+			fmt = (normalized) ? Format::R16G16_SNORM : Format::R16G16_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC3:
+			fmt = (normalized) ? Format::R16G16B16_SNORM : Format::R16G16B16_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC4:
+			fmt = (normalized) ? Format::R16G16B16A16_SNORM : Format::R16G16B16A16_SINT;
+			break;
+		default:
+			ANKI_ASSERT(!"TODO");
+		}
+		break;
+	case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
+		switch(accessor.type)
+		{
+		case TINYGLTF_TYPE_SCALAR:
+			fmt = (normalized) ? Format::R16_UNORM : Format::R16_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC2:
+			fmt = (normalized) ? Format::R16G16_UNORM : Format::R16G16_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC3:
+			fmt = (normalized) ? Format::R16G16B16_UNORM : Format::R16G16B16_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC4:
+			fmt = (normalized) ? Format::R16G16B16A16_UNORM : Format::R16G16B16A16_UINT;
+			break;
+		default:
+			ANKI_ASSERT(!"TODO");
+		}
+		break;
+	case TINYGLTF_COMPONENT_TYPE_INT:
+		switch(accessor.type)
+		{
+		case TINYGLTF_TYPE_SCALAR:
+			fmt = Format::R32_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC2:
+			fmt = Format::R32G32_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC3:
+			fmt = Format::R32G32B32_SINT;
+			break;
+		case TINYGLTF_TYPE_VEC4:
+			fmt = Format::R32G32B32A32_SINT;
+			break;
+		default:
+			ANKI_ASSERT(!"TODO");
+		}
+		break;
+	case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
+		switch(accessor.type)
+		{
+		case TINYGLTF_TYPE_SCALAR:
+			fmt = Format::R32_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC2:
+			fmt = Format::R32G32_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC3:
+			fmt = Format::R32G32B32_UINT;
+			break;
+		case TINYGLTF_TYPE_VEC4:
+			fmt = Format::R32G32B32A32_UINT;
+			break;
+		default:
+			ANKI_ASSERT(!"TODO");
+		}
+		break;
+	case TINYGLTF_COMPONENT_TYPE_FLOAT:
+		switch(accessor.type)
+		{
+		case TINYGLTF_TYPE_SCALAR:
+			fmt = Format::R32_SFLOAT;
+			break;
+		case TINYGLTF_TYPE_VEC2:
+			fmt = Format::R32G32_SFLOAT;
+			break;
+		case TINYGLTF_TYPE_VEC3:
+			fmt = Format::R32G32B32_SFLOAT;
+			break;
+		case TINYGLTF_TYPE_VEC4:
+			fmt = Format::R32G32B32A32_SFLOAT;
+			break;
+		default:
+			ANKI_ASSERT(!"TODO");
+		}
+		break;
+	default:
+		ANKI_ASSERT(!"TODO");
+	}
 }
 
 Error Exporter::exportMesh(const tinygltf::Mesh& mesh)
 {
-	if(mesh.primitives.size() != 1)
-	{
-		ANKI_LOGE("Can't handle this");
-		return Error::USER_DATA;
-	}
+	EXPORT_ASSERT(mesh.primitives.size() == 1);
 	const tinygltf::Primitive& primitive = mesh.primitives[0];
 
 	// Get indices
@@ -71,6 +212,7 @@ Error Exporter::exportMesh(const tinygltf::Mesh& mesh)
 		const tinygltf::BufferView& bufferView = m_model.bufferViews[accessor.bufferView];
 		const tinygltf::Buffer& buffer = m_model.buffers[bufferView.buffer];
 
+		EXPORT_ASSERT((accessor.count % 3) == 0);
 		indices.create(accessor.count);
 
 		switch(accessor.componentType)
@@ -88,7 +230,7 @@ Error Exporter::exportMesh(const tinygltf::Mesh& mesh)
 			}
 			break;
 		default:
-			ANKI_ASSERT(!"TODO");
+			EXPORT_ASSERT(!"TODO");
 			break;
 		}
 	}
@@ -98,21 +240,13 @@ Error Exporter::exportMesh(const tinygltf::Mesh& mesh)
 	Vec3 minPos, maxPos;
 	U32 vertCount;
 	{
-		if(primitive.attributes.find("POSITION") != primitive.attributes.end())
-		{
-			ANKI_LOGE("Positions are missing for mesh %s", mesh.name.c_str());
-			return Error::USER_DATA;
-		}
+		EXPORT_ASSERT(primitive.attributes.find("POSITION") != primitive.attributes.end());
 
 		const U8* posBuff;
 		U32 posStride;
-		getAttributeInfo(primitive, "POSITION", posBuff, posStride, vertCount);
-
-		if(posStride < sizeof(Vec3))
-		{
-			ANKI_LOGE("Position stride is wrong for mesh %s", mesh.name.c_str());
-			return Error::USER_DATA;
-		}
+		Format fmt;
+		getAttributeInfo(primitive, "POSITION", posBuff, posStride, vertCount, fmt);
+		EXPORT_ASSERT(posStride == sizeof(Vec3) && fmt == Format::R32G32B32_SFLOAT);
 
 		positions.create(vertCount);
 		for(U v = 0; v < vertCount; ++v)
@@ -126,29 +260,21 @@ Error Exporter::exportMesh(const tinygltf::Mesh& mesh)
 	// Get normals and UVs
 	DynamicArrayAuto<Vec3> normals(m_alloc);
 	DynamicArrayAuto<Vec2> uvs(m_alloc);
-
 	{
-		if(primitive.attributes.find("NORMAL") != primitive.attributes.end())
-		{
-			ANKI_LOGE("Normals are missing for mesh %s", mesh.name.c_str());
-			return Error::USER_DATA;
-		}
-		if(primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end())
-		{
-			ANKI_LOGE("Texcoords are missing for mesh %s", mesh.name.c_str());
-			return Error::USER_DATA;
-		}
+		EXPORT_ASSERT(primitive.attributes.find("NORMAL") != primitive.attributes.end());
+		EXPORT_ASSERT(primitive.attributes.find("TEXCOORD_0") != primitive.attributes.end());
 
 		const U8* normalBuff;
 		U32 normalStride;
 		U32 count;
-		getAttributeInfo(primitive, "NORMAL", normalBuff, normalStride, count);
-		ANKI_ASSERT(count == vertCount);
+		Format fmt;
+		getAttributeInfo(primitive, "NORMAL", normalBuff, normalStride, count, fmt);
+		EXPORT_ASSERT(count == vertCount && fmt == Format::R32G32B32_SFLOAT);
 
 		const U8* uvBuff;
 		U32 uvStride;
-		getAttributeInfo(primitive, "TEXCOORD_0", uvBuff, uvStride, count);
-		ANKI_ASSERT(count == vertCount);
+		getAttributeInfo(primitive, "TEXCOORD_0", uvBuff, uvStride, count, fmt);
+		EXPORT_ASSERT(count == vertCount && fmt == Format::R32G32_SFLOAT);
 
 		normals.create(vertCount);
 		uvs.create(vertCount);
@@ -160,6 +286,125 @@ Error Exporter::exportMesh(const tinygltf::Mesh& mesh)
 		}
 	}
 
+	// Fix normals
+	for(U v = 0; v < vertCount; ++v)
+	{
+		const Vec3& pos = positions[v];
+		Vec3& normal = normals[v];
+
+		for(U prevV = 0; prevV < v; ++prevV)
+		{
+			const Vec3& otherPos = positions[prevV];
+
+			// Check the positions dist
+			const F32 posDist = (otherPos - pos).getLength();
+			if(posDist > EPSILON)
+			{
+				continue;
+			}
+
+			// Check angle of the normals
+			Vec3& otherNormal = normals[prevV];
+			const F32 cosAng = otherNormal.dot(normal);
+			if(cosAng > m_normalsMergeCosAngle)
+			{
+				continue;
+			}
+
+			// Merge normals
+			const Vec3 newNormal = (otherNormal + normal).getNormalized();
+			normal = newNormal;
+			otherNormal = newNormal;
+		}
+	}
+
+	// Compute tangents
+	DynamicArrayAuto<Vec4> tangents(m_alloc);
+	{
+		DynamicArrayAuto<Vec3> bitangents(m_alloc);
+
+		tangents.create(vertCount, Vec4(0.0f));
+		bitangents.create(vertCount, Vec3(0.0f));
+
+		for(U i = 0; i < indices.getSize(); i += 3)
+		{
+			const U i0 = indices[i + 0];
+			const U i1 = indices[i + 1];
+			const U i2 = indices[i + 2];
+
+			const Vec3& v0 = positions[i0];
+			const Vec3& v1 = positions[i1];
+			const Vec3& v2 = positions[i2];
+			const Vec3 edge01 = v1 - v0;
+			const Vec3 edge02 = v2 - v0;
+
+			const Vec2 uvedge01 = uvs[i1] - uvs[i0];
+			const Vec2 uvedge02 = uvs[i2] - uvs[i0];
+
+			F32 det = (uvedge01.y() * uvedge02.x()) - (uvedge01.x() * uvedge02.y());
+			det = (isZero(det)) ? 0.0001f : (1.0f / det);
+
+			Vec3 t = (edge02 * uvedge01.y() - edge01 * uvedge02.y()) * det;
+			Vec3 b = (edge02 * uvedge01.x() - edge01 * uvedge02.x()) * det;
+			t.normalize();
+			b.normalize();
+
+			tangents[i0] += Vec4(t, 0.0f);
+			tangents[i1] += Vec4(t, 0.0f);
+			tangents[i2] += Vec4(t, 0.0f);
+
+			bitangents[i0] += b;
+			bitangents[i1] += b;
+			bitangents[i2] += b;
+		}
+
+		for(U i = 0; i < vertCount; ++i)
+		{
+			Vec3 t = Vec3(tangents[i].xyz());
+			const Vec3& n = normals[i];
+			Vec3& b = bitangents[i];
+
+			t.normalize();
+			b.normalize();
+
+			const F32 w = ((n.cross(t)).dot(b) < 0.0f) ? 1.0f : -1.0f;
+			tangents[i] = Vec4(t, w);
+		}
+	}
+
+	// Load bone info
+	DynamicArrayAuto<UVec4> boneIds(m_alloc);
+	DynamicArrayAuto<Vec4> weights(m_alloc);
+
+	if(primitive.attributes.find("JOINTS_0") != primitive.attributes.end()
+		&& primitive.attributes.find("WEIGHTS_0") != primitive.attributes.end())
+	{
+		const U8* bonesBuff;
+		U32 bonesStride;
+		U32 count;
+		Format fmt;
+		getAttributeInfo(primitive, "JOINTS_0", bonesBuff, bonesStride, count, fmt);
+		EXPORT_ASSERT(count == vertCount && fmt == Format::R16G16B16A16_UINT);
+
+		const U8* weightsBuff;
+		U32 weightsStride;
+		getAttributeInfo(primitive, "WEIGHTS_0", weightsBuff, weightsStride, count, fmt);
+		EXPORT_ASSERT(count == vertCount && fmt == Format::R32G32B32A32_SFLOAT);
+
+		boneIds.create(vertCount);
+		weights.create(vertCount);
+
+		for(U v = 0; v < vertCount; ++v)
+		{
+			boneIds[v] = UVec4(*reinterpret_cast<const U16*>(&bonesBuff[v * bonesStride]),
+				*reinterpret_cast<const U16*>(&bonesBuff[v * bonesStride + sizeof(U16)]),
+				*reinterpret_cast<const U16*>(&bonesBuff[v * bonesStride + sizeof(U16) * 2]),
+				*reinterpret_cast<const U16*>(&bonesBuff[v * bonesStride + sizeof(U16) * 3]));
+
+			weights[v] = Vec4(reinterpret_cast<const F32*>(&weights[v * weightsStride]));
+		}
+	}
+
 	return Error::NONE;
 }
 
@@ -167,7 +412,12 @@ Error Exporter::exportAll()
 {
 	for(const tinygltf::Mesh& mesh : m_model.meshes)
 	{
-		ANKI_CHECK(exportMesh(mesh));
+		const Error err = exportMesh(mesh);
+		if(err)
+		{
+			ANKI_LOGE("Failed to load mesh %s", mesh.name.c_str());
+			return err;
+		}
 	}
 
 	return Error::NONE;

+ 18 - 2
tools/gltf_exporter/Exporter.h

@@ -21,6 +21,8 @@ public:
 	StringAuto m_rpath{m_alloc};
 	StringAuto m_texrpath{m_alloc};
 
+	F32 m_normalsMergeCosAngle = cos(toRad(30.0));
+
 	tinygltf::TinyGLTF m_loader;
 	tinygltf::Model m_model;
 
@@ -32,8 +34,22 @@ public:
 private:
 	Error exportMesh(const tinygltf::Mesh& mesh);
 
-	void getAttributeInfo(
-		const tinygltf::Primitive& primitive, CString attribName, const U8*& buff, U32& stride, U32& count) const;
+	void getAttributeInfo(const tinygltf::Primitive& primitive,
+		CString attribName,
+		const U8*& buff,
+		U32& stride,
+		U32& count,
+		Format& fmt) const;
 };
 
+#define EXPORT_ASSERT(expr) \
+	do \
+	{ \
+		if(!(expr)) \
+		{ \
+			ANKI_LOGE(#expr); \
+			return Error::USER_DATA; \
+		} \
+	} while(false)
+
 } // end namespace anki