Creating a Cylinder
Posted In 20 Nov 2015 Tagged With CPP Graphics
简介
使用代码生成圆柱体,可用来表示Spot Light的凸包围体
当圆柱体的某一端闭合时,会产生重复顶点。 可参考Assimp[0]的做法,来剔除潜在重复顶点。
实现
void CreateCylinder(const std::string &name, float topR, float bottomR, float height, int segH, int segV)
{
if (segH < 2 || segV < 3)
{
LOGW << "SegH or SegV tooooo small!";
return;
}
// output result
std::vector<float> vertices;
std::vector<unsigned int> indices;
// allocate some space.
vertices.reserve((segV * segH + 2) * 3);
indices.reserve((segV * segH * 2) * 3);
float avgRadian = PI * 2.0f / segV;
float currentHeight = height / 2.0f;
float currentRadius = topR;
float heightStep = height / (segH - 1);
float radiusSetp = (topR - bottomR) / (segH - 1);
std::vector<unsigned int> previousRing;
previousRing.reserve(segV * 3);
std::vector<unsigned int> currentRing;
currentRing.reserve(segV * 3);
unsigned int index = 0;
auto AddVertex = [¤tRing, &vertices, &index](float x, float y, float z) -> unsigned int
{
vertices.insert(vertices.end(), { x, y, z });
return index++;
};
for (int h = 0; h < segH; h++)
{
// fill current ring
for (int v = 0; v < segV; v++)
{
float radian = avgRadian * v;
currentRing.push_back(AddVertex(
cos(radian) * currentRadius, currentHeight, sin(radian) * currentRadius));
}
if (previousRing.size() > 0)
{
// has previous ring, we connect them to triangles.
for (unsigned int i = 0; i < currentRing.size() - 1; i++)
{
indices.insert(indices.end(), {
previousRing[i], previousRing[i + 1], currentRing[i + 1],
currentRing[i + 1], currentRing[i], previousRing[i]
});
}
indices.insert(indices.end(), {
previousRing.back(), previousRing.front(), currentRing.front(),
currentRing.front(), currentRing.back(), previousRing.back()
});
}
else
{
// don't have previous ring, then we're on top.
// close this ring.
unsigned int center = AddVertex(0, currentHeight, 0);
for (unsigned int i = 0; i < currentRing.size() - 1; i++)
{
indices.insert(indices.end(), { center, currentRing[i + 1], currentRing[i] });
}
indices.insert(indices.end(), { center, currentRing.front(), currentRing.back() });
}
previousRing = currentRing;
// this way the memory allocated earlier won't be destoried.
currentRing.erase(currentRing.begin(), currentRing.end());
currentHeight -= heightStep;
currentRadius -= radiusSetp;
}
// close bottom ring
unsigned int center = AddVertex(0, -height / 2.0f, 0);
for (unsigned int i = 0; i < previousRing.size() - 1; i++)
{
indices.insert(indices.end(), { center, previousRing[i], previousRing[i + 1] });
}
indices.insert(indices.end(), { center, previousRing.back(), previousRing.front() });
}
附录
[0] Assimp. JoinVerticesProcess.cpp.
[1] Andreaskahler. Creating a icosphere.