2021SC@SDUSC
目录
分析概括
本周开始我们开始正式进行源代码分析工作,基于上周讨论结果,本周我计划从modle模块开始分析。modle模块主要实现通过三角形建模形成相应三维模型并进行渲染绑定的功能,其中涉及多个头文件,本次主要分析其中的object类。
基础知识
QUuid数据结构分析
Uuid是Universally Unique Identifier的缩写,它是在一定的范围内唯一的机器生成的标识符,具有随机性与唯一性。QUuid类是QT Uuid的简称,主要实现Uuid的生成、比较、转换等功能。具体格式为xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
例:QUuid Id;//定义一个随机生成标识符Id
Pair类型分析
pair定义于头文件utility中,主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型,可使用first、second的成员函数调用Pair型变量的第一二个数据。
例:pair<T1,T2> p1;
pair<T1,T2> p2(v1,v2);
T1 x=p1.first;
T2 y=p1.second;
QRectF类分析
QRectF类使用坐标的浮点精度在平面中定义一个矩形,QRectF::right()和QRectF::bottom()函数可返回矩形右坐标和底部坐标。
结构体与类的分析
object类分析
1、ObjectNode结构体
此结构体主要用于定义对象上任意节点的相关属性。使用QUuid类可定义唯一ID码,使用Vector3D可定义物体三维坐标。
struct ObjectNode
{
QUuid partId;
QUuid nodeId;//节点ID
QVector3D origin;//初始位置坐标
float radius = 0;//半径范围
QColor color;
float colorSolubility = 0;//颜色溶解性
float metalness = 0;//金属性
float roughness = 1.0;//粗糙度
QUuid materialId;//材料ID
bool countershaded = false;//遮挡性,判断是否被遮挡
QUuid mirrorFromPartId;
QUuid mirroredByPartId;
BoneMark boneMark = BoneMark::None;
QVector3D direction;//方向向量
bool joined = true;//判断是否连接
};
2、Object类
Object类主要初始化定义建模的三角形面片的相关属性,主要包括节点信息、边缘信息、法向、颜色、透明度等。
class Object
{
public:
std::vector<ObjectNode> nodes;
std::vector<std::pair<std::pair<QUuid, QUuid>, std::pair<QUuid, QUuid>>> edges;//pair是一个二元集合,定义了一个边(存储两侧点)信息的edges容器
std::vector<QVector3D> vertices;
std::vector<std::pair<QUuid, QUuid>> vertexSourceNodes;//存放初始点的容器
std::vector<std::vector<size_t>> triangleAndQuads;
std::vector<std::vector<size_t>> triangles;//容器的嵌套
std::vector<QVector3D> triangleNormals;//三角面片法向向量
std::vector<QColor> triangleColors;//三件面片颜色
bool alphaEnabled = false;//透明度变量
quint64 meshId = 0;
const std::vector<std::pair<QUuid, QUuid>> *triangleSourceNodes() const
{
if (!m_hasTriangleSourceNodes)
return nullptr;
return &m_triangleSourceNodes;
}
void setTriangleSourceNodes(const std::vector<std::pair<QUuid, QUuid>> &sourceNodes)
{
Q_ASSERT(sourceNodes.size() == triangles.size());
m_triangleSourceNodes = sourceNodes;
m_hasTriangleSourceNodes = true;
}
const std::vector<std::vector<QVector2D>> *triangleVertexUvs() const
{
if (!m_hasTriangleVertexUvs)
return nullptr;
return &m_triangleVertexUvs;
}
void setTriangleVertexUvs(const std::vector<std::vector<QVector2D>> &uvs)
{
Q_ASSERT(uvs.size() == triangles.size());
m_triangleVertexUvs = uvs;
m_hasTriangleVertexUvs = true;
}
const std::vector<std::vector<QVector3D>> *triangleVertexNormals() const
{
if (!m_hasTriangleVertexNormals)
return nullptr;
return &m_triangleVertexNormals;
}
void setTriangleVertexNormals(const std::vector<std::vector<QVector3D>> &normals)
{
Q_ASSERT(normals.size() == triangles.size());
m_triangleVertexNormals = normals;
m_hasTriangleVertexNormals = true;
}
const std::vector<QVector3D> *triangleTangents() const
{
if (!m_hasTriangleTangents)
return nullptr;
return &m_triangleTangents;
}
void setTriangleTangents(const std::vector<QVector3D> &tangents)
{
Q_ASSERT(tangents.size() == triangles.size());
m_triangleTangents = tangents;
m_hasTriangleTangents = true;
}
const std::map<QUuid, std::vector<QRectF>> *partUvRects() const
{
if (!m_hasPartUvRects)
return nullptr;
return &m_partUvRects;
}
void setPartUvRects(const std::map<QUuid, std::vector<QRectF>> &uvRects)
{
m_partUvRects = uvRects;
m_hasPartUvRects = true;
}
const std::vector<std::pair<std::pair<size_t, size_t>, std::pair<size_t, size_t>>> *triangleLinks() const
{
if (!m_hasTriangleLinks)
return nullptr;
return &m_triangleLinks;
}
void setTriangleLinks(const std::vector<std::pair<std::pair<size_t, size_t>, std::pair<size_t, size_t>>> &triangleLinks)
{
m_triangleLinks = triangleLinks;
m_hasTriangleLinks = true;
}
static void buildInterpolatedNodes(const std::vector<ObjectNode> &nodes,
const std::vector<std::pair<std::pair<QUuid, QUuid>, std::pair<QUuid, QUuid>>> &edges,
std::vector<std::tuple<QVector3D, float, size_t>> *targetNodes);
private:
bool m_hasTriangleSourceNodes = false;
std::vector<std::pair<QUuid, QUuid>> m_triangleSourceNodes;//初始节点
bool m_hasTriangleVertexUvs = false;
std::vector<std::vector<QVector2D>> m_triangleVertexUvs;//UV坐标
bool m_hasTriangleVertexNormals = false;
std::vector<std::vector<QVector3D>> m_triangleVertexNormals;//法向向量
bool m_hasTriangleTangents = false;
std::vector<QVector3D> m_triangleTangents;//切线向量
bool m_hasPartUvRects = false;
std::map<QUuid, std::vector<QRectF>> m_partUvRects;//不同零件的UV平面矩形
bool m_hasTriangleLinks = false;
std::vector<std::pair<std::pair<size_t, size_t>, std::pair<size_t, size_t>>> m_triangleLinks;
};
该类中的成员函数大多用于定义形成面片的点以及切线等信息,将大的模型按零件分层,在不同零件中再按照三角面片分出不同基础单元进行初始化,object类中相关函数定义如下:
triangleSourceNodes();setTriangleSourceNodes() | 初始化三面面片源节点 |
triangleVertexUvs();setTriangleVertexUvs() | 初始化三面面片顶点UV |
triangleVertexNormals();setTriangleVertexNormals() | 初始化三角面片法向 |
triangleTangents();setTriangleTangents() | 初始化三面面片切线 |
partUvRects();setPartUvRects() | 初始化不同零件的UV矩形 |
triangleLinks();setTriangleLinks() | 初始化面片间的关联关系 |
buildInterpolatedNodes() | 插入节点 |
buildInterpolatedNodes函数分析
buildInterpolatedNodes主要用于细分两点形成边之间的点,在边之间插入一系列关联点,同时根据比例关系推导出新增点的半径,具体分析如下:
void Object::buildInterpolatedNodes(const std::vector<ObjectNode> &nodes,
const std::vector<std::pair<std::pair<QUuid, QUuid>, std::pair<QUuid, QUuid>>> &edges,
std::vector<std::tuple<QVector3D, float, size_t>> *targetNodes)//tuple可看作泛化的std::pair,targetNodes为新增的目标点
{
targetNodes->clear();
std::map<std::pair<QUuid, QUuid>, size_t> nodeMap;
for (size_t nodeIndex = 0; nodeIndex < nodes.size(); ++nodeIndex)
{//按序存储节点在nodeMap中
const auto &it = nodes[nodeIndex];
nodeMap.insert({{it.partId, it.nodeId}, nodeIndex});//插入节点信息(所在部件序号与节点序号)以及节点在map中的存储位置
targetNodes->push_back(std::make_tuple(it.origin, it.radius, nodeIndex));//将节点位置半径以及节点在结构体中的位置插入其中
}
for (const auto &it: edges)
{//遍历每一条边
//从第一条边开始,遍历到最后一条边后跳出循环
auto findFirst = nodeMap.find(it.first);
if (findFirst == nodeMap.end())
continue;
auto findSecond = nodeMap.find(it.second);
if (findSecond == nodeMap.end())
continue;
const auto &firstNode = nodes[findFirst->second];
const auto &secondNode = nodes[findSecond->second];
//计算构成边的点之间的距离并细分
float length = (firstNode.origin - secondNode.origin).length();
float segments = length / 0.02;
if (qFuzzyIsNull(segments))
continue;
if (segments > 100)
segments = 100;
//在两点形成的边之间细分点,同时根据比例关系求出细分点的半径
float segmentLength = 1.0f / segments;
float offset = segmentLength;
while (offset < 1.0f) {
float radius = firstNode.radius * (1.0f - offset) + secondNode.radius * offset;
targetNodes->push_back(std::make_tuple(
firstNode.origin * (1.0f - offset) + secondNode.origin * offset,
radius,
offset <= 0.5 ? findFirst->second : findSecond->second
));
offset += segmentLength;
}
}
}
如有错误,欢迎指正。