在进行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));这样一个处理过程,这个过程在我看来应该查询两个面共同的边,也就是知道那两个面相邻。