在进行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)obje

最低0.47元/天 解锁文章
1127

被折叠的 条评论
为什么被折叠?



