xatlas源码解析(二)

在进行xatlas分析之前,先说说xatlas将模型加载到内存后的数据组织方式,代码如下:

typedef struct {
	uint32_t flags;				// flags代表网格索引数是否超过65535
	// uint32_t if OBJZ_FLAG_INDEX32 flag is set, otherwise uint16_t.
	// See: objz_setIndexFormat
	void *indices;				// 所有的面索引buffer
	uint32_t numIndices;		// 面索引数量
	objzMaterial *materials;	// 所有的材质buffer
	uint32_t numMaterials;		// 材质数量
	objzMesh *meshes;			// 所有的网格buffer
	uint32_t numMeshes;			// 网格数量
	objzObject *objects;		// 所有对象的buffer
	uint32_t numObjects;		// 对象数量
	void *vertices; // See: objz_setVertexFormat	// 所有的顶点buffer
	uint32_t numVertices;							// 顶点数量
} objzModel;

可以看到,xatlas将模型格式化成内部结构,比较简单,便于以后的查找和访问,需要注意一个mesh中不同的面可能使用了不同的材质,上面结构中的meshs是按照材质划分的mesh,object不是按照材质划分,而是按照mesh划分的。

网格内的数据填充完成后,s_atlas.thread = new std::thread(atlasGenerateThread);就开始在线程中处理这些数据,因为界面中有一个useUvMesh标记,是否使用mesh原有的uv,所以后续的处理过程分为两种情况对待,对于使用uv的将mesh添加到 xatlas::AddUvMesh,不使用uv的调用xatlas::AddMesh,代码如下:

// 构建图集线程
static void atlasGenerateThread()
{
	// 获取模型数据
	const objzModel *model = modelGetData();
	// 第一次构建【如果data==null】
	const bool firstRun = !s_atlas.data;
	// 开始时间
	const clock_t startTime = clock();

#if USE_MIMALLOC
	if (firstRun)
		xatlas::SetAlloc(mi_realloc);   // 设置内存申请函数
#endif

	// Create xatlas context and add meshes on first run only, unless uv mesh option has changed.
	// 第一次或者是否使用uv改变了
	if (firstRun || s_atlas.useUvMeshChanged) {

		//之前展开过,这一次使用uv选项改变了
		if (s_atlas.useUvMeshChanged && s_atlas.data) {
			// 销毁数据
			xatlas::Destroy(s_atlas.data);
			s_atlas.data = nullptr;
		}

		// 申请在uv展开的过程当中使用的内存,并初始化,同时创建了整个处理过程所使用的调度线程、任务队列
		s_atlas.data = xatlas::Create();
		// 设置处理进程回调
		xatlas::SetProgressCallback(s_atlas.data, atlasProgressCallback);

		// 需要忽略的面(忽略自发光、透明材质的面)
		std::vector<uint8_t> ignoreFaces;			 // Should be bool, workaround stupid C++ specialization.
		std::vector<uint32_t> faceMaterials;		// 面材质

		// 遍历模型【一个模型会有多个对象,每一个对象按照不同部分的材质会产生不同的mesh】
		for (uint32_t i = 0; i < model->numObjects; i++) {
			// 获取一个模型
			const objzObject &object = model->objects[i];
			// 【模型的顶点都存储在了model的一个大缓存中】,每一个对象只是存储了一个偏移量
			auto v = &((const ModelVertex *)model->vertices)[object.firstVertex];

			// Ignore faces with an emissive or transparent material.
			// 忽略自发光、透明材质的面
			// 设置ignoreFace的大小,填充0数据
			ignoreFaces.resize(object.numIndices / 3);
			memset(ignoreFaces.data(), 0, ignoreFaces.size() * sizeof(uint8_t));

			// 遍历mesh数量收集需要忽略的面【一个对象上有多个子材质】
			for (uint32_t j = 0; j < object.numMeshes; j++) {
				// 获取一个mesh
				const objzMesh &mesh = model->meshes[object.firstMesh + j];
				// 获取材质,有可能存在无材质的面
				const objzMaterial *mat = mesh.materialIndex == -1 ? nullptr : &model->materials[mesh.materialIndex];
				//材质使用了自发光
				if (mat && (mat->emission[0] > 0.0f || mat->emission[1] > 0.0f || mat->emission[2] > 0.0f || mat->opacity < 1.0f)) {
					for (uint32_t k = 0; k < mesh.numIndices / 3; k++)
						ignoreFaces[(mesh.firstIndex - object.firstIndex) / 3 + k] = true;  // 每一个mesh的面??【多余的遍历,直接设置面全部为true】
				}
			}

			// AddMesh时产生的各种错误
			xatlas::AddMeshError error;
			// 使用uvmesh【模型原来有uv坐标】
			if (s_atlas.useUvMesh)
			{
				// Set face materials so charts are detected correctly with overlapping UVs.
				// 收集每一个面的材质,以便使用重叠UV正确检测chart。
				faceMaterials.resize(object.numIndices / 3);   
				// 遍历mesh数量【一个对象上有多个材质】
				for (uint32_t j = 0; j < object.numMeshes; j++) {
					// 获取一个mesh
					const objzMesh &mesh = model->meshes[object.firstMesh + j];
					// 这些面共用了一个材质
					for (uint32_t k = 0; k < mesh.numIndices / 3; k++)
						faceMaterials[(mesh.firstIndex - object.firstIndex) / 3 + k] = (uint32_t)mesh.materialIndex;
				}

				// 按照原始数据的object组织成一个UvMeshDecl,添加到s_atlas.data中
				xatlas::UvMeshDecl meshDecl;
				meshDecl.faceMaterialData = faceMaterials.data();		// 某一个对象的面材质数据
				meshDecl.vertexCount = object.numVertices;				// 某一个对象的顶点数量
				meshDecl.vertexUvData = &v->texcoord;					// 某一个对象的纹理坐标buffer
				meshDecl.vertexStride = sizeof(ModelVertex);			// 顶点属性步长
				meshDecl.indexCount = object.numIndices;				// mesh索引数量
				meshDecl.indexData = &((uint32_t *)model->indices)[object.firstIndex];	//索引数据
				meshDecl.indexFormat = xatlas::IndexFormat::UInt32;		// 索引格式
				meshDecl.indexOffset = -(int32_t)object.firstVertex;	// 某一个模型的顶点内存偏移
				error = xatlas::AddUvMesh(s_atlas.data, meshDecl);		// 添加数据
			}
			else
			{
				// 按照原始数据的object组织成一个UvMeshDecl,添加到s_atlas.data中
				xatlas::MeshDecl meshDecl;

				meshDecl.vertexCount = object.numVertices;				// 当前对象的顶点数量
				meshDecl.vertexPositionData = &v->pos;					//  当前对象顶点buffer的起始指针
				meshDecl.vertexPositionStride = sizeof(ModelVertex);	// 顶点跨度

				meshDecl.vertexNormalData = &v->normal;					// 顶点法线buffer
				meshDecl.vertexNormalStride = sizeof(ModelVertex);		// 法线跨度

				meshDecl.vertexUvData = &v->texcoord;					// uv坐标buffer
				meshDecl.vertexUvStride = sizeof(ModelVertex);			// uv跨度

				meshDecl.indexCount = object.numIndices;				// 索引数量
				meshDecl.indexData = &((uint32_t *)model->indices)[object.firstIndex];	//索引buffer
				meshDecl.indexFormat = xatlas::IndexFormat::UInt32;		// 索引格式

				meshDecl.indexOffset = -(int32_t)object.firstVertex;	// 第一个顶点

				meshDecl.faceIgnoreData = (const bool *)ignoreFaces.data(); //忽略的面数量
#if USE_MESH_DECL_FACE_MATERIAL
				faceMaterials.resize(object.numIndices / 3);
				for (uint32_t j = 0; j < object.numMeshes; j++) {
					const objzMesh &mesh = model->meshes[object.firstMesh + j];
					for (uint32_t k = 0; k < mesh.numIndices / 3; k++)
						faceMaterials[(mesh.firstIndex - object.firstIndex) / 3 + k] = (uint32_t)mesh.materialIndex;
				}
				meshDecl.faceMaterialData = faceMaterials.data();
#endif
				// 当前模型 、 模型的总数
				error = xatlas::AddMesh(s_atlas.data, meshDecl, model->numObjects); // 添加数据
			}

			// 添加不成功
			if (error != xatlas::AddMeshError::Success) {
				fprintf(stderr, "Error adding mesh: %s\n", xatlas::StringForEnum(error));
				setErrorMessage("Error adding mesh: %s", xatlas::StringForEnum(error));
				xatlas::Destroy(s_atlas.data);
				s_atlas.data = nullptr;
				// 设置状态为没有构建
				s_atlas.status.set(AtlasStatus::NotGenerated);
				return;
			}

			// Destroy context if cancelled while adding meshes.
			// 如果在这个过程中取消了就销毁数据
			if (s_atlas.status.getCancel()) {
				xatlas::Destroy(s_atlas.data);
				s_atlas.data = nullptr;
				// 重新设置状态
				s_atlas.status.set(AtlasStatus::NotGenerated);
				// 置为初始状态
				s_atlas.status.setCancel(false);
				return;
			}
		}
	}
。。。。。。
}

AddUvMesh中,主要是检查索引数据、纹理坐标是否错误,三角面数量是否错误,并将decl及其内部数据重新组织成ctx中的mesh格式。

// 添加decl数据到uvmesh中,并将uvmesh加入到ctx中
AddMeshError AddUvMesh(Atlas *atlas, const UvMeshDecl &decl)
{
	XA_DEBUG_ASSERT(atlas);
	if (!atlas) {
		XA_PRINT_WARNING("AddUvMesh: atlas is null.\n");
		return AddMeshError::Error;
	}
	// 还原结构
	Context *ctx = (Context *)atlas;
	// mesh非空就返回
	if (!ctx->meshes.isEmpty()) {
		XA_PRINT_WARNING("AddUvMesh: Meshes and UV meshes cannot be added to the same atlas.\n");
		return AddMeshError::Error;
	}

#if XA_PROFILE
	if (ctx->uvMeshInstances.isEmpty())
		internal::s_profile.addMeshRealStart = std::chrono::high_resolution_clock::now();
#endif

	XA_PROFILE_START(addMeshCopyData)

	// 索引数量
	const bool hasIndices = decl.indexCount > 0;
	const uint32_t indexCount = hasIndices ? decl.indexCount : decl.vertexCount;

	XA_PRINT("Adding UV mesh %d: %u vertices, %u triangles\n", ctx->uvMeshes.size(), decl.vertexCount, indexCount / 3);
	// Expecting triangle faces.   期望的三角形面
	if ((indexCount % 3) != 0)
		return AddMeshError::InvalidIndexCount;

	// 有索引
	if (hasIndices) {
		// Check if any index is out of range.【查看索引是否超过顶点数量】
		for (uint32_t i = 0; i < indexCount; i++) {
			const uint32_t index = DecodeIndex(decl.indexFormat, decl.indexData, decl.indexOffset, i);
			if (index >= decl.vertexCount)
				return AddMeshError::IndexOutOfRange;
		}
	}
	// Create a mesh instance.
	// 创建一个mesh实例
	internal::UvMeshInstance *meshInstance = XA_NEW(internal::MemTag::Default, internal::UvMeshInstance);
	meshInstance->mesh = nullptr;
	// 将实例添加到ctx中
	ctx->uvMeshInstances.push_back(meshInstance);

	// See if this is an instance of an already existing mesh.
	// 查看实例是否已经存在mesh??
	internal::UvMesh *mesh = nullptr;
	for (uint32_t m = 0; m < ctx->uvMeshes.size(); m++) {
		// 比较一下指针,是否decl之前存储过
		if (memcmp(&ctx->uvMeshes[m]->decl, &decl, sizeof(UvMeshDecl)) == 0) {
			// 查询到相同的meshdecl
			mesh = ctx->uvMeshes[m];
			XA_PRINT("   instance of a previous UV mesh\n");
			break;
		}
	}

	// 如果之前没有这个mesh
	if (!mesh) {
		// Copy geometry to mesh.
		// 创建uvmesh
		mesh = XA_NEW(internal::MemTag::Default, internal::UvMesh);
		// 放入到uvmesh中
		ctx->uvMeshes.push_back(mesh);
		// 添加到内部
		mesh->decl = decl;
		// 有面材质数据
		if (decl.faceMaterialData) {
			// 材质设置内存大小,并赋值
			mesh->faceMaterials.resize(decl.indexCount / 3);
			memcpy(mesh->faceMaterials.data(), decl.faceMaterialData, mesh->faceMaterials.size() * sizeof(uint32_t));
		}
		// 索引
		mesh->indices.resize(decl.indexCount);
		for (uint32_t i = 0; i < indexCount; i++)
			mesh->indices[i] = hasIndices ? DecodeIndex(decl.indexFormat, decl.indexData, decl.indexOffset, i) : i;
		// 纹理坐标
		mesh->texcoords.resize(decl.vertexCount);
		for (uint32_t i = 0; i < decl.vertexCount; i++)
			mesh->texcoords[i] = *((const internal::Vector2 *)&((const uint8_t *)decl.vertexUvData)[decl.vertexStride * i]);
		// Validate.
		// 忽略的面
		mesh->faceIgnore.resize(decl.indexCount / 3);
		mesh->faceIgnore.zeroOutMemory();
		// 最大警告数量、当前警告数量
		const uint32_t kMaxWarnings = 50;	
		uint32_t warningCount = 0;
		for (uint32_t f = 0; f < indexCount / 3; f++) {
			bool ignore = false;
			uint32_t tri[3];
			for (uint32_t i = 0; i < 3; i++)
				tri[i] = mesh->indices[f * 3 + i];
			// Check for nan UVs.  检查nan无效uv数据
			for (uint32_t i = 0; i < 3; i++) {
				const uint32_t vertex = tri[i];
				if (internal::isNan(mesh->texcoords[vertex].x) || internal::isNan(mesh->texcoords[vertex].y)) {
					ignore = true;
					if (++warningCount <= kMaxWarnings)
						XA_PRINT("   NAN texture coordinate in vertex %u\n", vertex);
					break;
				}
			}
			// Check for zero area faces.  检查面积为0的面
			if (!ignore) {
				const internal::Vector2 &v1 = mesh->texcoords[tri[0]];
				const internal::Vector2 &v2 = mesh->texcoords[tri[1]];
				const internal::Vector2 &v3 = mesh->texcoords[tri[2]];
				const float area = fabsf(((v2.x - v1.x) * (v3.y - v1.y) - (v3.x - v1.x) * (v2.y - v1.y)) * 0.5f);
				if (area <= internal::kAreaEpsilon) {
					ignore = true;
					if (++warningCount <= kMaxWarnings)
						XA_PRINT("   Zero area face: %d, indices (%d %d %d), area is %f\n", f, tri[0], tri[1], tri[2], area);
				}
			}
			// 添加到忽略的面中
			if (ignore)
				mesh->faceIgnore.set(f);
		}
		if (warningCount > kMaxWarnings)
			XA_PRINT("   %u additional warnings truncated\n", warningCount - kMaxWarnings);
	}
	// mesh放到mesh实例当中
	meshInstance->mesh = mesh;
	XA_PROFILE_END(addMeshCopyData)
	return AddMeshError::Success;
}

AddMesh中,主要是检查顶点、法线、索引数据、纹理坐标是否错误,三角面数量是否错误,并将decl及其内部数据重新组织成ctx中的mesh格式,最后用mesh创建一个任务,放到调度的任务队列中。

// atlas中添加mesh【顶点、法线、uv】
AddMeshError AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t meshCountHint)
{
	XA_DEBUG_ASSERT(atlas);

	// 没有申请内存
	if (!atlas) {
		XA_PRINT_WARNING("AddMesh: atlas is null.\n");
		return AddMeshError::Error;
	}

	// 强制转换
	Context *ctx = (Context *)atlas;

	// 如果uvmesh非空的
	if (!ctx->uvMeshes.isEmpty()) {
		XA_PRINT_WARNING("AddMesh: Meshes and UV meshes cannot be added to the same atlas.\n");
		return AddMeshError::Error;
	}

#if XA_PROFILE
	if (ctx->meshes.isEmpty())
		internal::s_profile.addMeshRealStart = std::chrono::high_resolution_clock::now();
#endif

	// Don't know how many times AddMesh will be called, so progress needs to adjusted each time.
	// 不知道AddMesh会被调用多少次,所以每次都需要调整进度。【提前设置的回调消息】
	if (!ctx->addMeshProgress) {   // 不存在就创建
		ctx->addMeshProgress = XA_NEW_ARGS(internal::MemTag::Default, internal::Progress, ProgressCategory::AddMesh, ctx->progressFunc, ctx->progressUserData, 1);
	}
	else {
		// 添加的mesh数量,按照百分比设置处理进度
		ctx->addMeshProgress->setMaxValue(internal::max(ctx->meshes.size() + 1, meshCountHint));
	}

	XA_PROFILE_START(addMeshCopyData)

	// 有索引,有三角面的索引
	const bool hasIndices = meshDecl.indexCount > 0;
	// 使用顶点数量或者使用索引
	const uint32_t indexCount = hasIndices ? meshDecl.indexCount : meshDecl.vertexCount;

	// 面数量
	uint32_t faceCount = indexCount / 3;
	// 每一个mesh中的面数量是一定的,多边形所有顶点就是一个面,四边形每一个mesh面都是4边形??
	if (meshDecl.faceVertexCount) {
		// 面数
		faceCount = meshDecl.faceCount;
		XA_PRINT("Adding mesh %d: %u vertices, %u polygons\n", ctx->meshes.size(), meshDecl.vertexCount, faceCount);
		for (uint32_t f = 0; f < faceCount; f++) {
			if (meshDecl.faceVertexCount[f] < 3)    // 检查每一个面的顶点数量都不能小于3
				return AddMeshError::InvalidFaceVertexCount;
		}
	} else {
		XA_PRINT("Adding mesh %d: %u vertices, %u triangles\n", ctx->meshes.size(), meshDecl.vertexCount, faceCount);
		// Expecting triangle faces unless otherwise specified.  除非另有说明,否则默认三角形面。
		if ((indexCount % 3) != 0)
			return AddMeshError::InvalidIndexCount;
	}
	// 是否有需要忽略的面
	uint32_t meshFlags = internal::MeshFlags::HasIgnoredFaces;

	// 法线buffer
	if (meshDecl.vertexNormalData)
		meshFlags |= internal::MeshFlags::HasNormals;

	// 使用了材质
	if (meshDecl.faceMaterialData)
		meshFlags |= internal::MeshFlags::HasMaterials;

	// 创建atlas内部的mesh【该内存是MemTag::Mesh类型的、类是internal::Mesh,顶点之间的距离如果小于epsilon就是相同的顶点,顶点数量,索引数量,标志,当前已经添加了多少网格】
	internal::Mesh *mesh = XA_NEW_ARGS(internal::MemTag::Mesh, internal::Mesh, meshDecl.epsilon, meshDecl.vertexCount, indexCount / 3, meshFlags, ctx->meshes.size());
	
	// 【将模型的顶点属性信息,重新存入内部mesh】
	for (uint32_t i = 0; i < meshDecl.vertexCount; i++) {
		// 遍历顶点
		internal::Vector3 normal(0.0f);
		internal::Vector2 texcoord(0.0f);
		// 存在法线
		if (meshDecl.vertexNormalData)
			normal = DecodeNormal(meshDecl, i);
		// 存在uv坐标
		if (meshDecl.vertexUvData)
			texcoord = DecodeUv(meshDecl, i);
		// 添加顶点、法线、uv
		mesh->addVertex(DecodePosition(meshDecl, i), normal, texcoord);
	}

	// 如果是多边形
	MeshPolygonMapping *meshPolygonMapping = nullptr;
	if (meshDecl.faceVertexCount) {
		// 多边形映射
		meshPolygonMapping = XA_NEW(internal::MemTag::Default, MeshPolygonMapping);
		// Copy MeshDecl::faceVertexCount so it can be used later when building output meshes.
		meshPolygonMapping->faceVertexCount.copyFrom(meshDecl.faceVertexCount, meshDecl.faceCount);
		// There should be at least as many triangles as polygons.
		meshPolygonMapping->triangleToPolygonMap.reserve(meshDecl.faceCount);
		meshPolygonMapping->triangleToPolygonIndicesMap.reserve(meshDecl.indexCount);
	}

	// 检查数据正确性的过程中,数据错误数量不能超过kMaxWarnings
	const uint32_t kMaxWarnings = 50;
	// 当前错误计数
	uint32_t warningCount = 0;
	// 收集每一个面的索引,方便后续处理
	internal::Array<uint32_t> triIndices;
	// 第一个面的索引
	uint32_t firstFaceIndex = 0;
	internal::Triangulator triangulator;

	// 遍历当前object的每一个面,检查每一个面的顶点是否有错误,没有错误就存储到内部mesh中
	for (uint32_t face = 0; face < faceCount; face++) {
		// Decode face indices.
		// 解码面的索引,每个面的顶点数量可能不同,单独取出来
		const uint32_t faceVertexCount = meshDecl.faceVertexCount ? (uint32_t)meshDecl.faceVertexCount[face] : 3;
		uint32_t polygon[UINT8_MAX];
		for (uint32_t i = 0; i < faceVertexCount; i++) {
			if (hasIndices) {
				polygon[i] = DecodeIndex(meshDecl.indexFormat, meshDecl.indexData, meshDecl.indexOffset, face * faceVertexCount + i);
				// Check if any index is out of range.  索引是否超出范围就返回
				if (polygon[i] >= meshDecl.vertexCount) {
					mesh->~Mesh();
					XA_FREE(mesh);
					return AddMeshError::IndexOutOfRange;
				}
			} else {
				polygon[i] = face * faceVertexCount + i;  // 没有索引
			}
		}

		// Ignore faces with degenerate or zero length edges.
		// 忽略具有退化边或零长度边的面。
		bool ignore = false;
		for (uint32_t i = 0; i < faceVertexCount; i++) {

			// 获取有(index0、index1)(index1、index2)(index2、index0)组成的边
			const uint32_t index1 = polygon[i];
			const uint32_t index2 = polygon[(i + 1) % 3];

			if (index1 == index2) {						// 索引相同,则点相同,线的长度为0
				ignore = true;							// 忽略只一个边
				if (++warningCount <= kMaxWarnings)     // 警告次数
					XA_PRINT("   Degenerate edge: index %d, index %d\n", index1, index2);
				break;									// 忽略这一个三角形
			}

			const internal::Vector3 &pos1 = mesh->position(index1);
			const internal::Vector3 &pos2 = mesh->position(index2);
			if (internal::length(pos2 - pos1) <= 0.0f) {	// 线的长度为0
				ignore = true;								// 忽略线的长度为0
				if (++warningCount <= kMaxWarnings)
					XA_PRINT("   Zero length edge: index %d position (%g %g %g), index %d position (%g %g %g)\n", index1, pos1.x, pos1.y, pos1.z, index2, pos2.x, pos2.y, pos2.z);
				break;										// 忽略这一个三角形
			}
		}
		// Ignore faces with any nan vertex attributes. 忽略具有任何nan verte顶点属性的面。
		if (!ignore) {
			for (uint32_t i = 0; i < faceVertexCount; i++) {
				// 顶点是NAN
				const internal::Vector3 &pos = mesh->position(polygon[i]);
				if (internal::isNan(pos.x) || internal::isNan(pos.y) || internal::isNan(pos.z)) {
					if (++warningCount <= kMaxWarnings)
						XA_PRINT("   NAN position in face: %d\n", face);
					ignore = true;
					break;
				}
				// 法线是NAN
				if (meshDecl.vertexNormalData) {
					const internal::Vector3 &normal = mesh->normal(polygon[i]);
					if (internal::isNan(normal.x) || internal::isNan(normal.y) || internal::isNan(normal.z)) {
						if (++warningCount <= kMaxWarnings)
							XA_PRINT("   NAN normal in face: %d\n", face);
						ignore = true;
						break;
					}
				}
				// uv是NAN
				if (meshDecl.vertexUvData) {
					const internal::Vector2 &uv = mesh->texcoord(polygon[i]);
					if (internal::isNan(uv.x) || internal::isNan(uv.y)) {
						if (++warningCount <= kMaxWarnings)
							XA_PRINT("   NAN texture coordinate in face: %d\n", face);
						ignore = true;
						break;
					}
				}
			}
		}

		// Triangulate if necessary. 必要时进行三角化。
		triIndices.clear();
		if (faceVertexCount == 3) {
			// 将索引放入到triIndices中
			triIndices.push_back(polygon[0]);
			triIndices.push_back(polygon[1]);
			triIndices.push_back(polygon[2]);
		} else {
			// 三角化多边形
			triangulator.triangulatePolygon(mesh->positions(), internal::ConstArrayView<uint32_t>(polygon, faceVertexCount), triIndices);
		}

		// Check for zero area faces.  检查三角面的面积为0
		if (!ignore) {
			for (uint32_t i = 0; i < triIndices.size(); i += 3) {
				// 拿到三角形的三个顶点
				const internal::Vector3 &a = mesh->position(triIndices[i + 0]);
				const internal::Vector3 &b = mesh->position(triIndices[i + 1]);
				const internal::Vector3 &c = mesh->position(triIndices[i + 2]);
				// 叉乘的长度代表面积
				const float area = internal::length(internal::cross(b - a, c - a)) * 0.5f;
				if (area <= internal::kAreaEpsilon) {
					ignore = true;		// 忽略面积很小的三角形
					if (++warningCount <= kMaxWarnings)
						XA_PRINT("   Zero area face: %d, area is %f\n", face, area);
					break;				// 忽略这一个三角形
				}
			}
		}

		// User face ignore.   当前的面在忽略的集合中,就忽略了
		if (meshDecl.faceIgnoreData && meshDecl.faceIgnoreData[face])
			ignore = true;

		// User material.  当前面使用的材质
		uint32_t material = UINT32_MAX;
		if (meshDecl.faceMaterialData)
			material = meshDecl.faceMaterialData[face];   

		// Add the face(s).
		// 遍历每一个面的索引,将面添加到内部mesh当中
		for (uint32_t i = 0; i < triIndices.size(); i += 3) {
			mesh->addFace(&triIndices[i], ignore, material);   // 添加一个面、这个面是否被忽略、这个面使用的材质
			// 多边形的处理方式??
			if (meshPolygonMapping)
				meshPolygonMapping->triangleToPolygonMap.push_back(face);
		}

		// 多边形的处理方式
		if (meshPolygonMapping) {
			for (uint32_t i = 0; i < triIndices.size(); i++)
				meshPolygonMapping->triangleToPolygonIndicesMap.push_back(triIndices[i]);
		}
		// ???
		firstFaceIndex += faceVertexCount;
	}

	// 最大告警数量
	if (warningCount > kMaxWarnings)
		XA_PRINT("   %u additional warnings truncated\n", warningCount - kMaxWarnings);
	XA_PROFILE_END(addMeshCopyData)

	// 添加mesh到ctx中
	ctx->meshes.push_back(mesh);
	ctx->meshPolygonMappings.push_back(meshPolygonMapping);
	ctx->paramAtlas.addMesh(mesh);   //Atlas 的参数 param

	// 获取一个任务组句柄
	if (ctx->addMeshTaskGroup.value == UINT32_MAX)  
		ctx->addMeshTaskGroup = ctx->taskScheduler->createTaskGroup(ctx);

	// 创建一个任务,并将任务与任务处理函数绑定,方便后续线程处理
	internal::Task task;
	task.userData = mesh;
	task.func = runAddMeshTask;

	// 将任务添加到这个任务组中
	ctx->taskScheduler->run(ctx->addMeshTaskGroup, task);
	// 添加成功
	return AddMeshError::Success;
}

addMesh中有如下处理,在新创建的mesh中添加索引、忽略的面、面对应的材质

// 将面添加到内部结构中,并将相同的边在m_edgeMap中关联起来
	void addFace(const uint32_t *indices, bool ignore = false, uint32_t material = UINT32_MAX)
	{
		// 是否有忽略面,如果这个面的面积是0,或者顶点是nan,则任务这个面是被忽略的
		if (m_flags & MeshFlags::HasIgnoredFaces)
			m_faceIgnore.push_back(ignore);

		// 存储当前面的材质
		if (m_flags & MeshFlags::HasMaterials)
			m_faceMaterials.push_back(material);

		// 记录当前面之前的位置
		const uint32_t firstIndex = m_indices.size();
		// 存储当前面的索引
		for (uint32_t i = 0; i < 3; i++)
			m_indices.push_back(indices[i]);    // 将索引添加到成员变量中

		for (uint32_t i = 0; i < 3; i++) {
			// 将这个面的0-1,1-2,2-0索引添加到,map当中
			const uint32_t vertex0 = m_indices[firstIndex + i];
			const uint32_t vertex1 = m_indices[firstIndex + (i + 1) % 3];
			// 将每一条边加入到m_edgeMap中,并将相同边的在m_edgeMap中按照索引串接起来(两个面共享一条边的串联起来)
			m_edgeMap.add(EdgeKey(vertex0, vertex1));
		}
	}

上面的处理过程中有一个m_edgeMap.add(EdgeKey(vertex0, vertex1));这样一个处理过程,这个过程在我看来应该查询两个面共同的边,也就是知道那两个面相邻。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值