IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> xatlas源码解析(六) -> 正文阅读

[游戏开发]xatlas源码解析(六)

上一章说到,facegroup有了,facegroup组成了chartgroup,那么如何使用chartgroup计算相应的chart呢,答案在runChartGroupComputeChartsTask中,如下:

// 使用ChartGroup计算Charts
static void runChartGroupComputeChartsTask(void *groupUserData, void *taskUserData)
{
	auto args = (ChartGroupComputeChartsTaskGroupArgs *)groupUserData;	// 任务所在组中的用户数据
	auto chartGroup = (ChartGroup *)taskUserData;						// 单个任务当中的用户数据,每一个任务一个chartgroup
	if (args->progress->cancel)
		return;

	XA_PROFILE_START(chartGroupComputeChartsThread)
	// 组中计算制图【调度、输入选项、进度、所有的chartgroup最终会汇聚到atlas中、边界网格、buffer?、??】
	chartGroup->computeCharts(args->taskScheduler, *args->options, args->progress, args->atlas->get(), args->boundaryGrid, args->chartBuffers, args->piecewiseParam);
	XA_PROFILE_END(chartGroupComputeChartsThread)
}

chartGroup->computeCharts中的处理过程,分段讲解:

// 使用chartgroup计算charts 【调度、输入选项、进度、所有的chartgroup最终会汇聚到atlas中、边界网格、buffer?、??】
	void computeCharts(TaskScheduler *taskScheduler, const ChartOptions &options, Progress *progress, segment::Atlas &atlas, ThreadLocal<UniformGrid2> *boundaryGrid, ThreadLocal<ChartCtorBuffers> *chartBuffers, ThreadLocal<PiecewiseParam> *piecewiseParam)
	{
		// This function may be called multiple times, so destroy existing charts.
		// 函数可能被多次调用
		for (uint32_t i = 0; i < m_charts.size(); i++) {
			m_charts[i]->~Chart();   // 销毁chart
			XA_FREE(m_charts[i]);
		}
		// Create mesh from source mesh, using only the faces in this face group.
		// 从源网格创建网格,仅使用此面组中的面。
		XA_PROFILE_START(createChartGroupMesh)
		// 重新将原来的数据组织成了一个chartgroup内部mesh
		Mesh *mesh = createMesh();
		XA_PROFILE_END(createChartGroupMesh)
。。。。。。
 }

可以看到chargroup内部又重新创建了mesh,这个创建的过程是将顶点信息、面索引等重新复制了一遍:

Mesh *createMesh()
	{
		XA_DEBUG_ASSERT(m_faceGroup != MeshFaceGroups::kInvalid);
		// Create new mesh from the source mesh, using faces that belong to this group.
		// 使用属于该组的面,从源网格创建新网格。这一组有多少个面【m_faceGroup代表第几组】
		m_faceToSourceFaceMap.reserve(m_sourceMeshFaceGroups->faceCount(m_faceGroup));
		// 遍历所有连接的面【有拓扑关系的面】
		for (MeshFaceGroups::Iterator it(m_sourceMeshFaceGroups, m_faceGroup); !it.isDone(); it.advance())
			m_faceToSourceFaceMap.push_back(it.face());   // 将面索引添加到数组中

		// Only initial meshes has ignored faces. The only flag we care about is HasNormals.  只有初始网格忽略了面。我们唯一关心的标志是有正常值。
		const uint32_t faceCount = m_faceToSourceFaceMap.size();
		XA_DEBUG_ASSERT(faceCount > 0);

		// 有些面忽略了??不知道
		const uint32_t approxVertexCount = min(faceCount * 3, m_sourceMesh->vertexCount());  // 顶点数量
		// 创建mesh【精度、近似顶点数、面数、是否有法线】
		Mesh *mesh = XA_NEW_ARGS(MemTag::Mesh, Mesh, m_sourceMesh->epsilon(), approxVertexCount, faceCount, m_sourceMesh->flags() & MeshFlags::HasNormals);

		// 顶点???
		HashMap<uint32_t, PassthroughHash<uint32_t>> sourceVertexToVertexMap(MemTag::Mesh, approxVertexCount);
		for (uint32_t f = 0; f < faceCount; f++) {
			// 获取一个面索引
			const uint32_t face = m_faceToSourceFaceMap[f];

			for (uint32_t i = 0; i < 3; i++) {
				// 找到顶点所在的索引
				const uint32_t vertex = m_sourceMesh->vertexAt(face * 3 + i);
				// 不存在就添加
				if (sourceVertexToVertexMap.get(vertex) == UINT32_MAX) {
					// 添加一个顶点
					sourceVertexToVertexMap.add(vertex);

					Vector3 normal(0.0f);
					if (m_sourceMesh->flags() & MeshFlags::HasNormals)
						normal = m_sourceMesh->normal(vertex);       // 顶点对应的法线

					// 将顶点、法线、纹理坐标添加到mesh中
					mesh->addVertex(m_sourceMesh->position(vertex), normal, m_sourceMesh->texcoord(vertex));
				}

			}
		}
		// Add faces.
		for (uint32_t f = 0; f < faceCount; f++) {
			// 获取一个面
			const uint32_t face = m_faceToSourceFaceMap[f];
			XA_DEBUG_ASSERT(!m_sourceMesh->isFaceIgnored(face));
			uint32_t indices[3];
			for (uint32_t i = 0; i < 3; i++) {
				// 获取面的顶点索引
				const uint32_t vertex = m_sourceMesh->vertexAt(face * 3 + i);
				indices[i] = sourceVertexToVertexMap.get(vertex);
				XA_DEBUG_ASSERT(indices[i] != UINT32_MAX);
			}
			// Don't copy flags - ignored faces aren't used by chart groups, they are handled by InvalidMeshGeometry.
			// 添加面索引 不复制标志-被忽略的面不被图表组使用,它们由InvalidMeshGeometry处理。
			mesh->addFace(indices);
		}

		XA_PROFILE_START(createChartGroupMeshColocals)
		// 串联相同的顶点
		mesh->createColocals();
		XA_PROFILE_END(createChartGroupMeshColocals)
		XA_PROFILE_START(createChartGroupMeshBoundaries)
		// 创建chart的边界
		mesh->createBoundaries();
		mesh->destroyEdgeMap(); // Only needed it for createBoundaries.  只是为了创建边界使用
		XA_PROFILE_END(createChartGroupMeshBoundaries)
#if XA_DEBUG_EXPORT_OBJ_CHART_GROUPS
		char filename[256];
		XA_SPRINTF(filename, sizeof(filename), "debug_mesh_%03u_chartgroup_%03u.obj", m_sourceMesh->id(), m_id);
		mesh->writeObjFile(filename);
#endif
		return mesh;
	}

这其中又通过mesh->createBoundaries();找出了哪些边是chartgroup(facegroup)的边,哪些不是边:

// 创建边界
	void createBoundaries()
	{
		// 边界数、顶点数量
		const uint32_t edgeCount = m_indices.size();
		const uint32_t vertexCount = m_positions.size();

		// 反向的边
		m_oppositeEdges.resize(edgeCount);

		// 边界框???
		m_boundaryEdges.reserve(uint32_t(edgeCount * 0.1f));

		// 边界的顶点
		m_isBoundaryVertex.resize(vertexCount);
		m_isBoundaryVertex.zeroOutMemory();


		for (uint32_t i = 0; i < edgeCount; i++)
			m_oppositeEdges[i] = UINT32_MAX;

		const uint32_t faceCount = m_indices.size() / 3;
		for (uint32_t i = 0; i < faceCount; i++) {
			if (isFaceIgnored(i))    // 是否为忽略的面
				continue;

			for (uint32_t j = 0; j < 3; j++) {
				const uint32_t edge = i * 3 + j;			
				const uint32_t vertex0 = m_indices[edge];					// 第一个点
				const uint32_t vertex1 = m_indices[i * 3 + (j + 1) % 3];	// 后一个点
				// If there is an edge with opposite winding to this one, the edge isn't on a boundary.
				// 如果有一条边的弯曲方向与此边相反,则该边不在边界上。
				const uint32_t oppositeEdge = findEdge(vertex1, vertex0);
				if (oppositeEdge != UINT32_MAX) {
					m_oppositeEdges[edge] = oppositeEdge;  // 相反的边
				} else {
					m_boundaryEdges.push_back(edge);	   // 边
					m_isBoundaryVertex.set(vertex0);	   // 边界上的顶点
					m_isBoundaryVertex.set(vertex1);	   // 边界上的顶点
				}
			}
		}
	}

下面会计算mesh中的一些信息,具体计算过程在atlas.reset(mesh, options)和atlas.compute();中

// 使用chartgroup计算charts 【调度、输入选项、进度、所有的chartgroup最终会汇聚到atlas中、边界网格、buffer?、??】
	void computeCharts(TaskScheduler *taskScheduler, const ChartOptions &options, Progress *progress, segment::Atlas &atlas, ThreadLocal<UniformGrid2> *boundaryGrid, ThreadLocal<ChartCtorBuffers> *chartBuffers, ThreadLocal<PiecewiseParam> *piecewiseParam)
{

。。。。。。


	XA_PROFILE_START(buildAtlas)
		// 将mesh和ui选项加入到图集当中
		atlas.reset(mesh, options);
		atlas.compute();
		XA_PROFILE_END(buildAtlas)
		// Update progress.
		progress->increment(faceCount());


。。。。。。
}

接着上面atlas.reset(mesh, options)中会计算mesh中每一个面的面积、连接面之间的夹角、边的长度,如下所示:

	void reset(const Mesh *mesh, const ChartOptions &options)
	{
		XA_PROFILE_START(buildAtlasInit)
		m_data.options = options;
		m_data.mesh = mesh;
		// 计算三角面积、两个面的夹角
		m_data.compute();
		XA_PROFILE_END(buildAtlasInit)
	}

// 计算邻接面的夹角、边的长度、面的面积
	void compute()
	{
		// 获取面、边数量
		const uint32_t faceCount = mesh->faceCount();
		const uint32_t edgeCount = mesh->edgeCount();

		// 面与面之间的角度
		edgeDihedralAngles.resize(edgeCount);
		// 边长
		edgeLengths.resize(edgeCount);
		// 面的面积
		faceAreas.resize(faceCount);

		// 是否使用uv
		if (options.useInputMeshUvs)
			faceUvAreas.resize(faceCount);

		// 面法线
		faceNormals.resize(faceCount);
		// 面是否已经在chart中了
		isFaceInChart.resize(faceCount);
		isFaceInChart.zeroOutMemory();

		// 遍历面
		for (uint32_t f = 0; f < faceCount; f++) {

			for (uint32_t i = 0; i < 3; i++) {
				const uint32_t edge = f * 3 + i;
				const Vector3 &p0 = mesh->position(mesh->vertexAt(meshEdgeIndex0(edge)));
				const Vector3 &p1 = mesh->position(mesh->vertexAt(meshEdgeIndex1(edge)));
				// 计算边的长度
				edgeLengths[edge] = length(p1 - p0);
				XA_DEBUG_ASSERT(edgeLengths[edge] > 0.0f);
			}

			// 计算面的面积
			faceAreas[f] = mesh->computeFaceArea(f);
			XA_DEBUG_ASSERT(faceAreas[f] > 0.0f);
			// 使用用户uv时,计算uv面积
			if (options.useInputMeshUvs)
				faceUvAreas[f] = mesh->computeFaceParametricArea(f);

			// 计算面法线
			faceNormals[f] = mesh->computeFaceNormal(f);

		}

		for (uint32_t face = 0; face < faceCount; face++) {
			for (uint32_t i = 0; i < 3; i++) {
				const uint32_t edge = face * 3 + i;   // 边
				// 找到共线的面
				const uint32_t oedge = mesh->oppositeEdge(edge);
				if (oedge == UINT32_MAX)
					edgeDihedralAngles[edge] = FLT_MAX;
				else {
					const uint32_t oface = meshEdgeFace(oedge);
					// 通过法线计算两个面的夹角
					edgeDihedralAngles[edge] = edgeDihedralAngles[oedge] = dot(faceNormals[face], faceNormals[oface]);
				}
			}
		}
	}

而atlas.compute();中的计算过程放到下一章。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:42:53  更:2022-04-04 12:43:25 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/30 11:26:56-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码