这一章讲解当模型不参考本身的uv的情况:继续上一章AddMesh过程的讲解,AddMesh中将模型的数据组织为atlas内部的数据,并且是原始数据的拷贝,而不是引用,并将mesh交给了子线程处理。
// atlas中添加mesh【顶点、法线、uv】
AddMeshError AddMesh(Atlas *atlas, const MeshDecl &meshDecl, uint32_t meshCountHint)
{
......
// 获取一个任务组句柄
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;
}
上面的过程中将 task.userData = mesh; 将mesh给了一个子任务,并且调度run了一下:
// 运行任务
void run(TaskGroupHandle handle, const Task &task)
{
XA_DEBUG_ASSERT(handle.value != UINT32_MAX);
// 获取任务,锁定处理,
TaskGroup &group = m_groups[handle.value];
group.queueLock.lock();
group.queue.push_back(task); // 任务队列
group.queueLock.unlock();
group.ref++; // 任务数量递增
// Wake up a worker to run this task. 唤醒所有的任务处理线程
for (uint32_t i = 0; i < m_workers.size(); i++) {
// 设置唤醒标志
m_workers[i].wakeup = true;
// 通知线程运行
m_workers[i].cv.notify_one();
}
}
上面的过程就是将任务放到任务队列当中,同时将线程在阻塞状态变为运行状态,线程运行后会执行,任务绑定的处理函数runAddMeshTask,
// 线程函数
static void workerThread(TaskScheduler *scheduler, Worker *worker, uint32_t threadIndex)
{
// 当前形成索引
m_threadIndex = threadIndex;
// 锁定
std::unique_lock<std::mutex> lock(worker->mutex);
// 处理过程
for (;;) {
// 等待通知信号,检测wakeup是否为true,如果为true就等通知了,通知了就下运行
worker->cv.wait(lock, [=]{ return worker->wakeup.load(); });
// 设置为false,下一次循环会变为阻塞
worker->wakeup = false;
for (;;) {
if (scheduler->m_shutdown) // 线程停止了
return;
// Look for a task in any of the groups and run it.
// 查找任务并执行
TaskGroup *group = nullptr;
Task *task = nullptr;
for (uint32_t i = 0; i < scheduler->m_maxGroups; i++) {
// 获取一个任务组
group = &scheduler->m_groups[i];
if (group->free || group->ref == 0) // 没有任务
continue;
// 锁定队列
group->queueLock.lock();
// 遍历任务队列
if (group->queueHead < group->queue.size()) {
// 拿出一个任务
task = &group->queue[group->queueHead++];
// 解锁队列
group->queueLock.unlock();
break;
}
// 解锁队列
group->queueLock.unlock();
}
// 如果任务不存在
if (!task)
break;
// 处理任务【组数据,用户数据】
task->func(group->userData, task->userData);
// 处理完成后递减
group->ref--;
}
}
}
};
runAddMeshTask的处理如下:
//addmesh后的的处理,查询三角面有公共顶点的过程,有功能顶点就将他们串联起来
void createColocals()
{
if (m_epsilon <= FLT_EPSILON)
createColocalsHash();
else
createColocalsBVH();
}
// 使用hash的方式串联重复的索引
void createColocalsHash()
{
// 顶点数量
const uint32_t vertexCount = m_positions.size();
// 顶点哈希
HashMap<Vector3> positionToVertexMap(MemTag::Default, vertexCount);
// 每一个顶点对应一个<顶点,哈希值>,相同哈希值,相同的点处理??
for (uint32_t i = 0; i < vertexCount; i++)
positionToVertexMap.add(m_positions[i]);
// 临时存储查重数据
Array<uint32_t> colocals(MemTag::MeshColocals);
// 将相同的顶点按照升序的方式串联起来
m_nextColocalVertex.resize(vertexCount);
m_nextColocalVertex.fillBytes(0xff); // 填充无效数据
m_firstColocalVertex.resize(vertexCount);
m_firstColocalVertex.fillBytes(0xff); // 填充无效数据
for (uint32_t i = 0; i < vertexCount; i++) {
if (m_nextColocalVertex[i] != UINT32_MAX)
continue; // Already linked. 已经连接了
// Find other vertices colocal to this one.
// 找到与此顶点共轴的其他顶点
colocals.clear();
colocals.push_back(i); // Always add this vertex. 已经添加了这个顶点
// otherVertex是m_positions[i]在positionToVertexMap中keys、next的位置
uint32_t otherVertex = positionToVertexMap.get(m_positions[i]);
while (otherVertex != UINT32_MAX) { // 有重复的坐标
if (otherVertex != i && equal(m_positions[i], m_positions[otherVertex], m_epsilon) && m_nextColocalVertex[otherVertex] == UINT32_MAX)
colocals.push_back(otherVertex); // 添加一个索引
// 获取下一个相同的哈希值的坐标的位置
otherVertex = positionToVertexMap.getNext(m_positions[i], otherVertex);
}
if (colocals.size() == 1) { // 只有一个相同的值
// No colocals for this vertex. 第一个和下一个都指向一个
m_nextColocalVertex[i] = i;
m_firstColocalVertex[i] = i;
continue;
}
// Link in ascending order. 相同的顶点值按照其在内存中的位置索引升序排序
insertionSort(colocals.data(), colocals.size());
for (uint32_t j = 0; j < colocals.size(); j++) {
// 将相同的顶点索引位置,按照升序串联起来
m_nextColocalVertex[colocals[j]] = colocals[(j + 1) % colocals.size()];
m_firstColocalVertex[colocals[j]] = colocals[0]; //
}
XA_DEBUG_ASSERT(m_nextColocalVertex[i] != UINT32_MAX);
}
}
以以上是AddMesh的过程